@sema-agent/server 7.132.0 → 7.134.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 +353 -0
- package/README.md +8 -9
- package/README.zh-CN.md +7 -6
- package/USAGE.md +301 -64
- package/deploy/sema-up/README.md +35 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
- package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +19 -2
- package/deploy/sema-up/chart/templates/server.yaml +19 -2
- package/deploy/sema-up/chart/values-full-single.yaml +5 -1
- package/deploy/sema-up/chart/values.yaml +34 -0
- package/deploy/sema-up/stack/compose.yaml +3 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
- package/dist/adoption/plan.d.ts +3 -2
- package/dist/adoption/plan.js +7 -5
- package/dist/adoption/runner.js +3 -10
- package/dist/adoption/sql.d.ts +3 -2
- package/dist/adoption/sql.js +3 -5
- package/dist/adoption/wire.d.ts +4 -1
- package/dist/adoption/wire.js +1 -0
- package/dist/approval-ask-audit-store.d.ts +32 -14
- package/dist/approval-ask-audit-store.js +272 -74
- package/dist/approval-ask-machine.d.ts +15 -0
- package/dist/approval-ask-machine.js +6 -0
- package/dist/approval-ask-retention.d.ts +136 -0
- package/dist/approval-ask-retention.js +102 -0
- package/dist/approval-card.d.ts +83 -23
- package/dist/approval-card.js +45 -15
- package/dist/approval-content-kind.d.ts +5 -2
- package/dist/approval-content-kind.js +16 -3
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval-gate-currency.d.ts +72 -0
- package/dist/approval-gate-currency.js +94 -0
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +55 -17
- package/dist/audit.d.ts +27 -10
- package/dist/audit.js +12 -16
- package/dist/bake-runner/main.js +3 -2
- package/dist/boot/coordinators.d.ts +6 -1
- package/dist/boot/coordinators.js +6 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -1
- package/dist/boot/execution-env.js +9 -7
- package/dist/boot/execution-lane.js +11 -0
- package/dist/boot/leader.d.ts +2 -0
- package/dist/boot/leader.js +4 -1
- package/dist/boot/memory-boundary.js +2 -2
- package/dist/boot/memory-consolidation.d.ts +5 -4
- package/dist/boot/memory-consolidation.js +7 -6
- package/dist/boot/org-delivery.d.ts +28 -0
- package/dist/boot/org-delivery.js +20 -0
- package/dist/boot/read-deny-anchor-audit.js +2 -2
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +21 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +60 -81
- package/dist/boot/runner-deps.d.ts +18 -5
- package/dist/boot/runner-deps.js +10 -0
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +4 -2
- package/dist/boot/stage-02-web-admin-pull.js +4 -1
- package/dist/boot/stage-06-runners.d.ts +6 -3
- package/dist/boot/stage-06-runners.js +20 -4
- package/dist/boot/stage-07-capability-layer.d.ts +4 -1
- package/dist/boot/stage-07-capability-layer.js +24 -3
- package/dist/boot/stage-08-reapers.d.ts +4 -1
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +5 -1
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -1
- package/dist/boot/stage-10-http-server.js +6 -6
- package/dist/boot/stores.d.ts +8 -0
- package/dist/boot/stores.js +10 -14
- package/dist/boot/web-admin.d.ts +10 -0
- package/dist/boot/web-admin.js +162 -65
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/brain.js +8 -3
- package/dist/breaker-key.d.ts +6 -0
- package/dist/breaker-key.js +29 -0
- package/dist/budget.js +5 -1
- package/dist/caller-text-echo.d.ts +11 -6
- package/dist/caller-text-echo.js +10 -12
- package/dist/canonical-json.d.ts +2 -41
- package/dist/canonical-json.js +2 -13
- package/dist/capabilities/center-plugins.d.ts +7 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +21 -0
- package/dist/capabilities/hands-lane.js +10 -0
- package/dist/capabilities/mcp-probe.d.ts +36 -4
- package/dist/capabilities/mcp-probe.js +29 -6
- package/dist/capabilities/scenarios.d.ts +63 -7
- package/dist/capabilities/scenarios.js +39 -7
- package/dist/capability-facts.d.ts +36 -4
- package/dist/capability-facts.js +30 -4
- package/dist/center-config-tier.d.ts +69 -0
- package/dist/center-config-tier.js +42 -0
- package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
- package/dist/checkpoint-ctx-seal-m28.js +19 -0
- package/dist/checkpoint-identity.d.ts +16 -0
- package/dist/checkpoint-identity.js +19 -0
- package/dist/config-catalog.d.ts +17 -10
- package/dist/config-catalog.js +43 -28
- package/dist/config-d-migration.d.ts +3 -1
- package/dist/config-d-migration.js +2 -2
- package/dist/config-dir-trust.d.ts +114 -0
- package/dist/config-dir-trust.js +388 -0
- package/dist/config-invariants.d.ts +18 -5
- package/dist/config-invariants.js +35 -3
- package/dist/config-provider.d.ts +6 -1
- package/dist/config-provider.js +6 -2
- package/dist/config-types.d.ts +71 -14
- package/dist/config-types.js +8 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +121 -59
- package/dist/delegated-session-owner.d.ts +185 -0
- package/dist/delegated-session-owner.js +119 -0
- package/dist/device-protocol.d.ts +5 -3
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.js +21 -2
- package/dist/device-ws-protocol.d.ts +70 -6
- package/dist/device-ws-protocol.js +50 -23
- package/dist/elicitation.js +3 -2
- package/dist/entry-problems.d.ts +34 -0
- package/dist/entry-problems.js +14 -0
- package/dist/execution-lane-caps.d.ts +49 -10
- package/dist/execution-lane-caps.js +43 -4
- package/dist/field-conflict.d.ts +44 -0
- package/dist/field-conflict.js +21 -0
- package/dist/file-history-disclosure.d.ts +34 -5
- package/dist/file-history-disclosure.js +2 -2
- package/dist/fleet/fleet-bus.d.ts +84 -6
- package/dist/fleet/fleet-bus.js +42 -9
- package/dist/hooks/classic-stations.d.ts +10 -5
- package/dist/hooks/classic-stations.js +4 -4
- package/dist/hooks/command-spawn.d.ts +57 -0
- package/dist/hooks/command-spawn.js +139 -0
- package/dist/hooks/hook-llm.js +2 -2
- package/dist/hooks/hook-runner.d.ts +127 -25
- package/dist/hooks/hook-runner.js +668 -287
- package/dist/hooks/hook-source.d.ts +44 -0
- package/dist/hooks/hook-source.js +40 -0
- package/dist/http/active-run-conflict.d.ts +5 -13
- package/dist/http/active-run-conflict.js +17 -27
- package/dist/http/admission.js +111 -60
- package/dist/http/approval-sweeps.js +58 -19
- package/dist/http/caller-trust.d.ts +77 -0
- package/dist/http/caller-trust.js +143 -0
- package/dist/http/checkpoint-ctx.d.ts +173 -18
- package/dist/http/checkpoint-ctx.js +171 -12
- package/dist/http/claimed-run.d.ts +4 -3
- package/dist/http/device-placement.d.ts +2 -4
- package/dist/http/device-placement.js +2 -1
- package/dist/http/dispatch.js +13 -7
- package/dist/http/durable-run-leg.d.ts +4 -32
- package/dist/http/durable-run-leg.js +37 -12
- package/dist/http/idempotency.d.ts +2 -2
- package/dist/http/idempotency.js +2 -2
- package/dist/http/owner-access.d.ts +56 -0
- package/dist/http/owner-access.js +42 -0
- package/dist/http/principal-gate.d.ts +78 -27
- package/dist/http/principal-gate.js +64 -14
- package/dist/http/readiness.d.ts +11 -5
- package/dist/http/readiness.js +10 -3
- package/dist/http/refusal-body.d.ts +12 -0
- package/dist/http/refusal-body.js +11 -0
- package/dist/http/request-guards.d.ts +16 -32
- package/dist/http/request-guards.js +31 -30
- package/dist/http/resume-ctx-read.d.ts +50 -6
- package/dist/http/resume-ctx-read.js +33 -7
- package/dist/http/resume-legs.d.ts +5 -5
- package/dist/http/resume-legs.js +163 -91
- package/dist/http/retired-service-deps.d.ts +43 -0
- package/dist/http/retired-service-deps.js +18 -0
- package/dist/http/route-ctx.d.ts +44 -37
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/a2a-serve.js +3 -4
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.js +1 -1
- package/dist/http/routes/admin-memory-optout.js +5 -1
- package/dist/http/routes/adoption.js +2 -9
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +91 -56
- package/dist/http/routes/background.js +3 -2
- package/dist/http/routes/capabilities.js +20 -4
- package/dist/http/routes/config-catalog.d.ts +3 -3
- package/dist/http/routes/config-catalog.js +1 -1
- package/dist/http/routes/devices.js +3 -17
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +6 -2
- package/dist/http/routes/fleet.js +4 -4
- package/dist/http/routes/images.js +6 -7
- package/dist/http/routes/leader.js +1 -8
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +3 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/memory-policy.d.ts +1 -1
- package/dist/http/routes/memory-policy.js +3 -2
- package/dist/http/routes/notify-wake.js +8 -21
- package/dist/http/routes/observability.js +2 -2
- package/dist/http/routes/principal-inbox.d.ts +39 -0
- package/dist/http/routes/principal-inbox.js +76 -0
- package/dist/http/routes/retention-ops.js +2 -9
- package/dist/http/routes/rules.js +9 -22
- package/dist/http/routes/runs.d.ts +5 -0
- package/dist/http/routes/runs.js +63 -115
- package/dist/http/routes/session-sync.d.ts +3 -2
- package/dist/http/routes/session-sync.js +53 -48
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions-list.js +3 -4
- package/dist/http/routes/sessions.js +33 -43
- package/dist/http/routes/side-query.js +6 -4
- package/dist/http/routes/tasks.js +71 -30
- package/dist/http/routes/trace-usage.js +7 -9
- package/dist/http/routes/workflows.d.ts +351 -0
- package/dist/http/routes/workflows.js +153 -119
- package/dist/http/run-declared-families.d.ts +16 -0
- package/dist/http/run-declared-families.js +12 -0
- package/dist/http/send.d.ts +24 -3
- package/dist/http/send.js +14 -2
- package/dist/http/server.d.ts +23 -18
- package/dist/http/server.js +20 -15
- package/dist/http/stored-body-replay.js +17 -4
- package/dist/http/submit-ctx.d.ts +4 -3
- package/dist/http/submit-ctx.js +23 -6
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +5 -4
- package/dist/http/wire-types.d.ts +17 -10
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/lane-home-probe.d.ts +78 -9
- package/dist/lane-home-probe.js +78 -12
- package/dist/leader/endpoint.d.ts +4 -1
- package/dist/leader/endpoint.js +11 -10
- package/dist/leader/wire.d.ts +4 -0
- package/dist/leader/wire.js +8 -7
- package/dist/limit-refusal.d.ts +17 -5
- package/dist/limit-refusal.js +4 -1
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +31 -10
- package/dist/mcp-assertion-header.js +10 -3
- package/dist/memory-origin-fence.js +2 -2
- package/dist/memory-project-layer.d.ts +37 -0
- package/dist/memory-project-layer.js +46 -0
- package/dist/memory-scope.d.ts +23 -16
- package/dist/memory-scope.js +24 -8
- package/dist/memory-sync-client.d.ts +27 -13
- package/dist/memory-sync-client.js +107 -18
- package/dist/model-compat.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +9 -1
- package/dist/model-entry-refusal.js +6 -1
- package/dist/model-plane-face.d.ts +51 -0
- package/dist/model-plane-face.js +24 -0
- package/dist/model-provider.d.ts +5 -0
- package/dist/model-provider.js +4 -2
- package/dist/model-route-endpoint.d.ts +8 -11
- package/dist/model-route-endpoint.js +4 -7
- package/dist/model-route-env.d.ts +22 -0
- package/dist/model-route-env.js +2 -0
- package/dist/model-route-gap.d.ts +60 -30
- package/dist/model-route-gap.js +21 -15
- package/dist/observability/corrupt-read-seat.d.ts +7 -0
- package/dist/observability/corrupt-read-seat.js +9 -0
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +18 -4
- package/dist/observability/fail-open.d.ts +35 -9
- package/dist/observability/fail-open.js +35 -9
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/prompt-manifest.d.ts +22 -25
- package/dist/observability/prompt-manifest.js +54 -50
- package/dist/observability/tool-trace.js +9 -10
- package/dist/operator-ask.d.ts +10 -0
- package/dist/operator-ask.js +3 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
- package/dist/orchestration/workflow-agent-session-index.js +9 -13
- package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
- package/dist/orchestration/workflow-completion-inbox.js +13 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
- package/dist/orchestration/workflow-notify-journal.js +27 -34
- package/dist/orchestration/workflow-park-decision.d.ts +89 -0
- package/dist/orchestration/workflow-park-decision.js +119 -0
- package/dist/org-delivery-gate.d.ts +81 -0
- package/dist/org-delivery-gate.js +59 -0
- package/dist/panel-tool-names.d.ts +23 -0
- package/dist/panel-tool-names.js +8 -0
- package/dist/parked-decide.d.ts +43 -11
- package/dist/parked-decide.js +100 -51
- package/dist/parked-revive-surface.d.ts +9 -2
- package/dist/parked-revive-surface.js +11 -8
- package/dist/permission-rule-list.d.ts +1 -1
- package/dist/permission-rule-list.js +2 -2
- package/dist/permission-rule-vocab.d.ts +1 -23
- package/dist/permission-rule-vocab.js +0 -13
- package/dist/plugins/approval-ask-store-file.d.ts +16 -1
- package/dist/plugins/approval-ask-store-file.js +21 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
- package/dist/plugins/approval-ask-store-memory.js +34 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
- package/dist/plugins/approval-ask-store-sql.js +37 -1
- package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
- package/dist/plugins/approval-payload-fingerprint.js +1 -1
- package/dist/plugins/blob-backend.d.ts +38 -25
- package/dist/plugins/blob-backend.js +29 -19
- package/dist/plugins/breaker-state-sql.d.ts +2 -2
- package/dist/plugins/breaker-state-sql.js +4 -4
- package/dist/plugins/caching-session-store.d.ts +4 -1
- package/dist/plugins/caching-session-store.js +7 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +36 -1
- package/dist/plugins/checkpoint-store-sql.js +65 -3
- package/dist/plugins/file-history-store-sql.d.ts +5 -2
- package/dist/plugins/file-history-store-sql.js +3 -1
- package/dist/plugins/file-run-store.d.ts +5 -13
- package/dist/plugins/file-run-store.js +9 -0
- package/dist/plugins/fork-routing-session-store.d.ts +9 -1
- package/dist/plugins/fork-routing-session-store.js +12 -1
- package/dist/plugins/host-platform.d.ts +2 -0
- package/dist/plugins/host-platform.js +2 -1
- package/dist/plugins/local-checkpoint-store.d.ts +15 -2
- package/dist/plugins/local-checkpoint-store.js +32 -1
- package/dist/plugins/local-session-store.d.ts +13 -10
- package/dist/plugins/local-session-store.js +23 -14
- package/dist/plugins/local-session-window.d.ts +45 -0
- package/dist/plugins/local-session-window.js +38 -0
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
- package/dist/plugins/mailbox-store-sql.d.ts +30 -48
- package/dist/plugins/mailbox-store-sql.js +77 -52
- package/dist/plugins/memory-embedder-fingerprint.js +2 -1
- package/dist/plugins/memory-run-store.d.ts +5 -13
- package/dist/plugins/memory-run-store.js +8 -0
- package/dist/plugins/one-time-migrations.d.ts +121 -5
- package/dist/plugins/one-time-migrations.js +332 -8
- package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
- package/dist/plugins/org-delivery-quota-store.js +228 -0
- package/dist/plugins/permission-rule-store-file.js +10 -6
- package/dist/plugins/permission-rule-store-sql.js +8 -8
- package/dist/plugins/pg-pool.js +39 -7
- package/dist/plugins/pg-session-storage.d.ts +18 -5
- package/dist/plugins/pg-session-storage.js +68 -23
- package/dist/plugins/remote-env-adb.d.ts +6 -12
- package/dist/plugins/remote-env-adb.js +12 -2
- package/dist/plugins/remote-env-device.d.ts +14 -7
- package/dist/plugins/remote-env-device.js +23 -15
- package/dist/plugins/remote-env-k8s.d.ts +8 -12
- package/dist/plugins/remote-env-k8s.js +23 -5
- package/dist/plugins/remote-env-local-docker.d.ts +10 -15
- package/dist/plugins/remote-env-local-docker.js +12 -9
- package/dist/plugins/remote-env-ssh.d.ts +10 -16
- package/dist/plugins/remote-env-ssh.js +17 -11
- package/dist/plugins/run-store-sql.d.ts +16 -14
- package/dist/plugins/run-store-sql.js +22 -2
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-user-file.js +2 -2
- package/dist/plugins/session-epoch-anchor.d.ts +32 -6
- package/dist/plugins/session-epoch-anchor.js +2 -3
- package/dist/plugins/session-ownership.d.ts +11 -0
- package/dist/plugins/session-ownership.js +11 -0
- package/dist/plugins/session-placement.d.ts +12 -14
- package/dist/plugins/session-placement.js +2 -15
- package/dist/plugins/session-store.d.ts +18 -8
- package/dist/plugins/session-store.js +15 -4
- package/dist/plugins/sql-driver.d.ts +74 -11
- package/dist/plugins/sql-driver.js +210 -12
- package/dist/plugins/sql-errors.d.ts +23 -0
- package/dist/plugins/sql-errors.js +21 -0
- package/dist/plugins/store-backend.d.ts +36 -1
- package/dist/plugins/store-backend.js +40 -14
- package/dist/plugins/store-contracts.d.ts +42 -0
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/tidb-pool.d.ts +6 -1
- package/dist/plugins/tidb-pool.js +44 -10
- package/dist/plugins/tidb-session-storage.d.ts +3 -2
- package/dist/plugins/tidb-session-storage.js +2 -2
- package/dist/plugins/tidb-session-store.d.ts +25 -5
- package/dist/plugins/tidb-session-store.js +67 -21
- package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
- package/dist/plugins/workflow-journal-store-sql.js +19 -6
- package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
- package/dist/plugins/workflow-run-store-sql.js +50 -9
- package/dist/posture-source.d.ts +18 -10
- package/dist/posture-source.js +4 -1
- package/dist/project-identity.d.ts +45 -6
- package/dist/project-identity.js +162 -8
- package/dist/project-layer-key.d.ts +28 -0
- package/dist/project-layer-key.js +29 -0
- package/dist/question.js +3 -2
- package/dist/read-deny-entry.d.ts +25 -0
- package/dist/read-deny-entry.js +12 -0
- package/dist/read-face-posture.d.ts +37 -11
- package/dist/read-face-posture.js +32 -3
- package/dist/request-key-closure.d.ts +7 -0
- package/dist/request-key-closure.js +9 -1
- package/dist/request-tool-axes.d.ts +12 -0
- package/dist/request-tool-axes.js +20 -0
- package/dist/request-value-closure.d.ts +5 -0
- package/dist/request-value-closure.js +11 -3
- package/dist/rule-entry-list.d.ts +26 -2
- package/dist/rule-entry-list.js +72 -12
- package/dist/rules-consent.js +3 -4
- package/dist/run-acceptance.d.ts +19 -3
- package/dist/run-acceptance.js +13 -1
- package/dist/run-local.js +37 -6
- package/dist/runs.d.ts +12 -10
- package/dist/runs.js +25 -6
- package/dist/runtime-caps-resolver.d.ts +16 -6
- package/dist/runtime-caps-resolver.js +18 -5
- package/dist/sandbox-pkg-source.d.ts +19 -1
- package/dist/sandbox-pkg-source.js +29 -7
- package/dist/sealed-key.d.ts +32 -0
- package/dist/sealed-key.js +55 -1
- package/dist/security.d.ts +95 -15
- package/dist/security.js +52 -9
- package/dist/session-error-wire.d.ts +31 -7
- package/dist/session-error-wire.js +9 -2
- package/dist/session-sync-kernel.d.ts +37 -10
- package/dist/session-sync-kernel.js +10 -0
- package/dist/session-titled-frame.d.ts +33 -0
- package/dist/session-titled-frame.js +4 -0
- package/dist/session-titler.d.ts +9 -1
- package/dist/session-titler.js +15 -4
- package/dist/spec-fields.js +21 -2
- package/dist/sql-engine-posture.d.ts +3 -1
- package/dist/task-a2a.js +4 -3
- package/dist/task-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +5 -5
- package/dist/task-mcp.js +27 -74
- package/dist/task-settings.d.ts +93 -88
- package/dist/task-settings.js +71 -63
- package/dist/text-bidi.d.ts +0 -4
- package/dist/text-bidi.js +1 -3
- package/dist/tighten-only-seats.d.ts +150 -0
- package/dist/tighten-only-seats.js +196 -0
- package/dist/tool-approval.d.ts +451 -136
- package/dist/tool-approval.js +733 -256
- package/dist/tool-rule-names.d.ts +6 -0
- package/dist/trace/core-keyset-guard.d.ts +108 -25
- package/dist/trace/exit-seat.d.ts +40 -0
- package/dist/trace/exit-seat.js +130 -0
- package/dist/trace/ledger-events.d.ts +6 -3
- package/dist/trace/project.d.ts +98 -13
- package/dist/trace/project.js +57 -32
- package/dist/trace/projection-table.d.ts +100 -0
- package/dist/trace/projection-table.js +130 -0
- package/dist/trace/redact.d.ts +57 -0
- package/dist/trace/redact.js +962 -168
- package/dist/trace/self-minted-wire.d.ts +349 -1
- package/dist/trace/self-minted-wire.js +118 -1
- package/dist/trace/wire-projection-faces.d.ts +304 -2
- package/dist/trace/wire-projection-faces.js +160 -13
- package/dist/url-refusal.js +2 -0
- package/dist/web-admin-client/apply-effective.d.ts +12 -0
- package/dist/web-admin-client/apply-effective.js +56 -5
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/read-face.js +5 -9
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +6 -8
- package/dist/web-admin-client/types.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
- package/package.json +8 -4
- package/scripts/drain-check-local.mjs +34 -2
- package/scripts/drain-check.mysql.sql +20 -2
- package/scripts/drain-check.pg.sql +20 -2
- package/dist/mcp-tool-faces.d.ts +0 -69
- package/dist/mcp-tool-faces.js +0 -106
|
@@ -228,26 +228,26 @@ diag?: {
|
|
|
228
228
|
connection: string;
|
|
229
229
|
log(msg: string, meta?: Record<string, unknown>): void;
|
|
230
230
|
}): Promise<void>;
|
|
231
|
-
/**
|
|
232
|
-
*
|
|
233
|
-
|
|
234
|
-
* injection covers a LIVE harness; a completion that lands after the leg ended had NO durable path (idle no-show,
|
|
235
|
-
* lost across restart). Bounded + redacted HERE (the store carries only egress-safe bytes; summary/result are
|
|
236
|
-
* UNTRUSTED free text). `status` collapses onto the inbox's completed|failed column; the ORIGINAL status rides
|
|
237
|
-
* the payload extras and overrides on drain (killed/cancelled reach the shell verbatim). Workflow-type
|
|
238
|
-
* notifications are the CALLER's job to filter (they have their own durable chain — double-enqueueing here would
|
|
239
|
-
* re-open the poll-served double-push).
|
|
240
|
-
*/
|
|
241
|
-
export declare function taskNotificationInboxEntry(sessionId: string, owner: string | null, n: {
|
|
231
|
+
/** 停驻收件条目的**共享字段** —— 引擎递来的完成与本服务铸的 external 通知都有的那几位。三只入参型都写成类型字面量(不是 interface):
|
|
232
|
+
* 调用方手里的是一只记录形的 payload,字面量型才与它可比(interface 没有隐式索引签名)。 */
|
|
233
|
+
type ParkedNotificationFields = {
|
|
242
234
|
task_id: string;
|
|
243
|
-
task_type: string;
|
|
244
|
-
toolUseId?: string;
|
|
245
235
|
status: string;
|
|
246
236
|
summary: string;
|
|
247
237
|
result?: string;
|
|
238
|
+
seq?: number;
|
|
239
|
+
/** 通知的来源标签(external 通知的入站体键,core 载荷同名键;live 投影过 `redactSecrets`,这里同读法)。 */
|
|
240
|
+
source?: string;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* **引擎递来的一次完成**(`onTaskNotification` 的 payload,执行腿父腿已收尾时入箱)—— 停驻收件的入参型。四处调用(bg 腿 `runs.ts`、sync 腿
|
|
244
|
+
* `routes/tasks.ts`、两条续跑腿 `resume-legs.ts`)原样交 core 的那只 payload。
|
|
245
|
+
*/
|
|
246
|
+
export type EngineNotificationParkInput = ParkedNotificationFields & {
|
|
247
|
+
task_type: string;
|
|
248
|
+
toolUseId?: string;
|
|
248
249
|
output_file?: string;
|
|
249
250
|
sessionId?: string;
|
|
250
|
-
seq?: number;
|
|
251
251
|
lines?: string[];
|
|
252
252
|
stoppedBy?: string;
|
|
253
253
|
recentSteps?: Array<{
|
|
@@ -281,7 +281,33 @@ export declare function taskNotificationInboxEntry(sessionId: string, owner: str
|
|
|
281
281
|
};
|
|
282
282
|
resupplied?: boolean;
|
|
283
283
|
attempt?: number;
|
|
284
|
-
|
|
284
|
+
/**
|
|
285
|
+
* 引擎的**一次完成**的相关 id(fill-once,`SemaTaskHandle.completionId`)—— live 投影(`trace/project.ts`)逐字透传的同一个值,
|
|
286
|
+
* 停驻收件条目照带 ⇒ 同一次完成在 live 帧与下一次开流的补投帧上同一个 id。引擎铸的不透明标识:逐字、不脱敏、不进折叠键;本仓零自铸。
|
|
287
|
+
*/
|
|
288
|
+
completionId?: string;
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* **本服务自己铸的 external 通知**(`POST /v1/sessions/:id/notify` 的 idle 半场)—— 停驻收件的入参型。类型上**不许**带 `completionId`:
|
|
292
|
+
* 那一位是引擎的一次完成的相关 id,本服务铸的通知没有它(编一个 = 凭空造一次「完成」)。
|
|
293
|
+
*/
|
|
294
|
+
export type ExternalNotificationParkInput = ParkedNotificationFields & {
|
|
295
|
+
task_type: "external";
|
|
296
|
+
completionId?: never;
|
|
297
|
+
};
|
|
298
|
+
/** external 通知 → 停驻收件条目(逐键取共享字段:运行期硬塞进来的别的键 —— 包括 `completionId` —— 一个都不过)。 */
|
|
299
|
+
export declare function externalNotificationInboxEntry(sessionId: string, owner: string | null, n: ExternalNotificationParkInput, enqueuedAt: number): WorkflowCompletionInboxEntry;
|
|
300
|
+
/**
|
|
301
|
+
* The durable half is the deployment's job: map a core `TaskNotificationPayload`
|
|
302
|
+
* (background subagent / background bash completion) onto an inbox entry, for the IDLE gap — core's in-process
|
|
303
|
+
* injection covers a LIVE harness; a completion that lands after the leg ended had NO durable path (idle no-show,
|
|
304
|
+
* lost across restart). Bounded + redacted HERE (the store carries only egress-safe bytes; summary/result are
|
|
305
|
+
* UNTRUSTED free text). `status` collapses onto the inbox's completed|failed column; the ORIGINAL status rides
|
|
306
|
+
* the payload extras and overrides on drain (killed/cancelled reach the shell verbatim). Workflow-type
|
|
307
|
+
* notifications are the CALLER's job to filter (they have their own durable chain — double-enqueueing here would
|
|
308
|
+
* re-open the poll-served double-push). External notifications enter through {@link externalNotificationInboxEntry}.
|
|
309
|
+
*/
|
|
310
|
+
export declare function taskNotificationInboxEntry(sessionId: string, owner: string | null, n: EngineNotificationParkInput, enqueuedAt: number,
|
|
285
311
|
/** lead-b (dual-ID audit): the PARENT run's durable taskId (the uuid the shell's footer rows
|
|
286
312
|
* key on) — rides the drained frame so a consumer can bind the a*-keyed child notification to its uuid-keyed
|
|
287
313
|
* parent row without a side lookup. */
|
|
@@ -341,4 +367,5 @@ export declare function resolveServedSession(p: {
|
|
|
341
367
|
}, getRun?: (taskId: string) => Promise<{
|
|
342
368
|
sessionId?: string | null;
|
|
343
369
|
} | undefined | null>): Promise<string | undefined>;
|
|
370
|
+
export {};
|
|
344
371
|
//# sourceMappingURL=workflow-completion-inbox.d.ts.map
|
|
@@ -315,6 +315,17 @@ export async function emitPendingWorkflowCompletions(inbox, sessionId, callerPri
|
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
+
export function externalNotificationInboxEntry(sessionId, owner, n, enqueuedAt) {
|
|
319
|
+
return taskNotificationInboxEntry(sessionId, owner, {
|
|
320
|
+
task_id: n.task_id,
|
|
321
|
+
task_type: "external",
|
|
322
|
+
status: n.status,
|
|
323
|
+
summary: n.summary,
|
|
324
|
+
...(n.result !== undefined ? { result: n.result } : {}),
|
|
325
|
+
...(n.seq !== undefined ? { seq: n.seq } : {}),
|
|
326
|
+
...(n.source !== undefined ? { source: n.source } : {}),
|
|
327
|
+
}, enqueuedAt);
|
|
328
|
+
}
|
|
318
329
|
export function taskNotificationInboxEntry(sessionId, owner, n, enqueuedAt, parentTaskId, priority) {
|
|
319
330
|
const cap = (s, max) => (s.length > max ? `${s.slice(0, max)}…[+${s.length - max} chars]` : s);
|
|
320
331
|
const extras = {
|
|
@@ -328,6 +339,8 @@ export function taskNotificationInboxEntry(sessionId, owner, n, enqueuedAt, pare
|
|
|
328
339
|
...(n.toolUseId ? { toolUseId: n.toolUseId } : {}),
|
|
329
340
|
...(n.status !== "completed" && n.status !== "failed" ? { status: n.status } : {}),
|
|
330
341
|
...(n.seq !== undefined ? { seq: n.seq } : {}),
|
|
342
|
+
...(n.completionId !== undefined ? { completionId: n.completionId } : {}),
|
|
343
|
+
...(n.source !== undefined ? { source: redactSecrets(n.source) } : {}),
|
|
331
344
|
...(n.lines !== undefined ? { lines: n.lines.map((l) => cap(redactSecrets(l), 1024)) } : {}),
|
|
332
345
|
...(n.stoppedBy !== undefined ? { stoppedBy: n.stoppedBy } : {}),
|
|
333
346
|
...(n.diagnostics !== undefined ? { diagnostics: cap(n.diagnostics, 4_000) } : {}),
|
|
@@ -291,39 +291,20 @@ export declare class JournalingWorkflowRunStore implements WorkflowRunStore {
|
|
|
291
291
|
* 的派生投影,写点必须与 fleet 行投影/notify journal 同一个(见 index 模块的文件头「单写者」段)。
|
|
292
292
|
* 可选:缺席 ⇒ 一位不维护(与 fleet 半场同姿势,行为逐字不变)。 */
|
|
293
293
|
agentSessionIndex?: WorkflowAgentSessionIndex | undefined);
|
|
294
|
-
/**
|
|
294
|
+
/** 本进程**亲眼看着落进索引**的指针:run → 它点名过的子会话。只用来跳过重复的 upsert,不是缓存(指针只增不删,记着的必在索引里)。
|
|
295
295
|
*
|
|
296
|
-
* 为什么需要:workflow 的进度写是每只 agent 状态变化一次,而 park 是罕见事件 ⇒
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
*
|
|
307
|
-
|
|
308
|
-
* 记不记 = 变更检测的正确性;摘不摘 = 表的有界性。**不**加「首次空集跳过」这类依赖进程内历史的特判。 */
|
|
309
|
-
private readonly indexedRows;
|
|
310
|
-
/**
|
|
311
|
-
* 写腿的**不变式守卫**(codex r1 [medium],验真后修):`indexedRows` 只保留一次**我们亲眼看着落地**
|
|
312
|
-
* 的写。持久化抛错时这条 run 的盘上状态未知(而 core 会吞掉这次失败、这条 run 往往再也不被写),
|
|
313
|
-
* 若把 memo 留着,那一条 `{runId → rows}` 就永久留在表里 —— 终态驱逐等不到它的终态写。
|
|
314
|
-
*
|
|
315
|
-
* ⚠️ 如实登记的残余:一条**最后一次写成功、随后被放弃**的 run(进程崩前没跑到终态)仍会在本进程的
|
|
316
|
-
* memo 里留一条空集。它与 fleet 活行登记簿同一量级(都随本世进程消亡),刻意**不**为它加清扫腿:
|
|
317
|
-
* 那是给一个不产生错误答案的形加机制。
|
|
318
|
-
*/
|
|
319
|
-
private forgetIndexMemoIfWriteOutcomeUnknown;
|
|
320
|
-
/** 索引的一次收敛(put/update 成功后调用)。
|
|
321
|
-
*
|
|
322
|
-
* **自吞错**:索引是观察面的派生物,它的故障绝不能把一次已成功的 run 持久化变成失败 —— 与
|
|
323
|
-
* `publishFleet` 的姿态同源。但自吞 ≠ 静默([ref]):留痕走**登记过的** fail-open tag
|
|
324
|
-
* `server.workflow-park-index.sync-failed`(P-DEBT;射程、后果与欠账的唯一属主 = 那条词表项的
|
|
325
|
-
* `note`,census 里有对应行);记录的 `parks[]` 读不出而不同步的那一臂另走 `server.workflow-park-index.parks-unreadable`(F 类,见下)。 */
|
|
326
|
-
private syncAgentSessionIndex;
|
|
296
|
+
* 为什么需要:workflow 的进度写是每只 agent 状态变化一次,而 park 是罕见事件 ⇒ 无条件 upsert 会给每一次写多打几发语句(SQL)
|
|
297
|
+
* 或多追加一行账本(file,无界增长)。判据只有一条:**这次记录点名的子会话,有没有还没亲眼看着落盘的** —— 没有就不写。
|
|
298
|
+
* 有界性:这条 run 不会再被本进程写的那一刻就摘表 —— 写到终态、CAS 输掉(owner 已不是本副本)、或这次写抛了(盘上状态未知)。 */
|
|
299
|
+
private readonly pointedSessions;
|
|
300
|
+
/** **指针先于记录**(索引模块头注的那条律):这次记录点名的子会话,指针在记录落盘之前 upsert。写不进 ⇒ 抛 —— 调用方的记录写
|
|
301
|
+
* 因此不发生,错误交给引擎 persist 的失败臂(与库写失败同一条路),不吞、不计 fail-open。
|
|
302
|
+
* 记录的 `parks[]` 读不出(在场却不是列表 / 条目形坏)⇒ 派生不出 ⇒ 不写指针,记录照写(读口据记录响亮拒);留痕走
|
|
303
|
+
* `server.workflow-park-index.parks-unreadable`(F 类)。`parks` 缺席不在这一臂:那是没有条目(读者答 `[]`),没有要写的指针。 */
|
|
304
|
+
private ensurePointersFor;
|
|
305
|
+
/** 一次记录写的整形:先指针、再记录;记录没落(`landed` 答否 / 写抛)或写到终态 ⇒ 摘表(见 {@link pointedSessions} 的有界性)。
|
|
306
|
+
* 终态判据走 fleet 半场那张**同一张**终态闭集表,不在这里手写第二份「哪些词算终态」。 */
|
|
307
|
+
private writeWithPointers;
|
|
327
308
|
/** Derive + publish the MF-Fleet workflow row from a WorkflowRun (doneCount/totalCount from agents, tokens from
|
|
328
309
|
* stats); a terminal workflow LEAVES the fleet (publish terminal then remove), a running one stays/updates. */
|
|
329
310
|
private publishFleet;
|
|
@@ -2,7 +2,7 @@ import { join } from "node:path";
|
|
|
2
2
|
import { AppendLog, 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
|
-
import { buildParkSessionRows
|
|
5
|
+
import { buildParkSessionRows } from "./workflow-agent-session-index.js";
|
|
6
6
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
7
7
|
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
8
8
|
const SUMMARY_MAX = 500;
|
|
@@ -264,41 +264,39 @@ export class JournalingWorkflowRunStore {
|
|
|
264
264
|
this.fleetBus = fleetBus;
|
|
265
265
|
this.agentSessionIndex = agentSessionIndex;
|
|
266
266
|
}
|
|
267
|
-
|
|
268
|
-
async
|
|
269
|
-
try {
|
|
270
|
-
return await write();
|
|
271
|
-
}
|
|
272
|
-
catch (e) {
|
|
273
|
-
this.indexedRows.delete(id);
|
|
274
|
-
throw e;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
async syncAgentSessionIndex(id, run) {
|
|
267
|
+
pointedSessions = new Map();
|
|
268
|
+
async ensurePointersFor(id, run) {
|
|
278
269
|
const index = this.agentSessionIndex;
|
|
279
270
|
if (index === undefined)
|
|
280
271
|
return;
|
|
281
272
|
const rows = buildParkSessionRows(id, run);
|
|
282
273
|
if (rows === undefined) {
|
|
283
|
-
this.indexedRows.delete(id);
|
|
284
274
|
recordFailOpen("server.workflow-park-index.parks-unreadable", `runId=${id}`);
|
|
285
275
|
return;
|
|
286
276
|
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
277
|
+
const known = this.pointedSessions.get(id);
|
|
278
|
+
const fresh = rows.filter((r) => known?.has(r.sessionId) !== true);
|
|
279
|
+
if (fresh.length === 0)
|
|
280
|
+
return;
|
|
281
|
+
await index.ensurePointers(id, fresh);
|
|
282
|
+
const landed = known ?? new Set();
|
|
283
|
+
for (const r of fresh)
|
|
284
|
+
landed.add(r.sessionId);
|
|
285
|
+
this.pointedSessions.set(id, landed);
|
|
286
|
+
}
|
|
287
|
+
async writeWithPointers(id, run, write, landed) {
|
|
288
|
+
await this.ensurePointersFor(id, run);
|
|
289
|
+
let result;
|
|
290
|
+
try {
|
|
291
|
+
result = await write();
|
|
297
292
|
}
|
|
298
|
-
|
|
299
|
-
this.
|
|
300
|
-
|
|
301
|
-
|
|
293
|
+
catch (e) {
|
|
294
|
+
this.pointedSessions.delete(id);
|
|
295
|
+
throw e;
|
|
296
|
+
}
|
|
297
|
+
if (!landed(result) || isTerminalWorkflowRowStatus(run.status))
|
|
298
|
+
this.pointedSessions.delete(id);
|
|
299
|
+
return result;
|
|
302
300
|
}
|
|
303
301
|
publishFleet(id, run) {
|
|
304
302
|
if (!this.fleetBus)
|
|
@@ -322,16 +320,11 @@ export class JournalingWorkflowRunStore {
|
|
|
322
320
|
runId: id,
|
|
323
321
|
scope: run.scope,
|
|
324
322
|
});
|
|
325
|
-
await this.
|
|
326
|
-
await this.syncAgentSessionIndex(id, run);
|
|
323
|
+
await this.writeWithPointers(id, run, () => this.inner.put(id, run), () => true);
|
|
327
324
|
this.publishFleet(id, run);
|
|
328
325
|
}
|
|
329
326
|
async update(id, scope, run, expect) {
|
|
330
|
-
const ok = await this.
|
|
331
|
-
if (!ok)
|
|
332
|
-
this.indexedRows.delete(id);
|
|
333
|
-
if (ok)
|
|
334
|
-
await this.syncAgentSessionIndex(id, run);
|
|
327
|
+
const ok = await this.writeWithPointers(id, run, () => this.inner.update(id, scope, run, expect), (won) => won);
|
|
335
328
|
if (ok)
|
|
336
329
|
this.publishFleet(id, run);
|
|
337
330
|
return ok;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type CheckpointToken, type ResumeOutcome } from "@sema-agent/core";
|
|
2
|
+
import { type JsonColumnReader } from "../plugins/sql-json-column.js";
|
|
3
|
+
import { type InProcessClaimFence } from "../plugins/store-contracts.js";
|
|
4
|
+
/** 一条人对审批卡的决定(引擎 `ResumeOutcome` 的 `policy_ask` 臂 —— 子代停驻只会是这一门)。 */
|
|
5
|
+
export type WorkflowParkOutcome = Extract<ResumeOutcome, {
|
|
6
|
+
gate: "policy_ask";
|
|
7
|
+
}>;
|
|
8
|
+
/** 决定行(内域形)。 */
|
|
9
|
+
export interface WorkflowParkDecision {
|
|
10
|
+
/** 子代 checkpoint 的 token —— 键。 */
|
|
11
|
+
readonly token: CheckpointToken;
|
|
12
|
+
/** 留存锚:受理那一刻的持有者 run(收割它时同批删本行)。**不是**投递目标 —— 投递目标在读时由路由判官定。 */
|
|
13
|
+
readonly runId: string;
|
|
14
|
+
/** 带着它的会话 = 受理那一刻那条 run 的发起会话(宿主)。 */
|
|
15
|
+
readonly hostSessionId: string;
|
|
16
|
+
readonly outcome: WorkflowParkOutcome;
|
|
17
|
+
/** {@link parkDecisionFingerprint}:同一决定的判据(回执幂等用)。 */
|
|
18
|
+
readonly fingerprint: string;
|
|
19
|
+
/** 受理它的那一次投递跑(回执上的 `taskId`)。 */
|
|
20
|
+
readonly deliveredTaskId: string;
|
|
21
|
+
/** 受理时刻(受理那台副本的墙钟,记录值;不当任何条件 —— 序由认领栅栏的提交序定,见文件头律二)。 */
|
|
22
|
+
readonly acceptedAtMs: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 决定行的店契约(SQL 两方言 / local 文件 / 内存三形同律)。写者只有 `/decide` 的 workflow 车道(开跑之前那一笔);读者是起跑读者与
|
|
26
|
+
* `/decide` 自己的回执幂等判;删除只有 SQL 收割的级联。
|
|
27
|
+
*/
|
|
28
|
+
export interface WorkflowParkDecisionStore {
|
|
29
|
+
/** 这只店每一笔写所在的认领栅栏 = 构造时绑定的那只 run 店。起服判官要它**就是**部署的 run 店({@link parkDecisionStoreRefusal})。 */
|
|
30
|
+
readonly claims: object;
|
|
31
|
+
/**
|
|
32
|
+
* 认领栅栏的原子段里整行写(律二,文件头):投递那一跑此刻持有宿主会话的认领且在跑 ⇒ 写(没有行插入、有行整行取代)并答 `true`;
|
|
33
|
+
* 否则什么都不写、答 `false`。
|
|
34
|
+
*/
|
|
35
|
+
record(decision: WorkflowParkDecision): Promise<boolean>;
|
|
36
|
+
get(token: CheckpointToken): Promise<WorkflowParkDecision | null>;
|
|
37
|
+
/** 带着这个会话的全部决定行(顺序不承重)。 */
|
|
38
|
+
listByHostSession(hostSessionId: string): Promise<WorkflowParkDecision[]>;
|
|
39
|
+
}
|
|
40
|
+
/** 起服判官读的那几座(`ServiceDeps` 的子集)。 */
|
|
41
|
+
export interface ParkDecisionLaneSeats {
|
|
42
|
+
readonly runStore?: object | undefined;
|
|
43
|
+
readonly workflowAgentSessionIndex?: unknown;
|
|
44
|
+
readonly workflowParkDecisions?: WorkflowParkDecisionStore | undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 🔴 **「决定店绑对了 run 店吗」的唯一判官**(`createHttpServer` 起服时调;拒 ⇒ 拒启)。判的是**递进来的对象**(按身份比),不是配置组合 ——
|
|
48
|
+
* 本服务自己那一形(SQL 后端配 `WORKFLOW_RUN_STORE=file|memory` 且开自编排)由配置不变量 `workflow-park-decision-domain` 在第 1 段、一次性迁移之前拒,走不到这里;
|
|
49
|
+
* 走到这里的不相等只来自嵌入方递错了座。
|
|
50
|
+
* 期望的绑定:workflow 子代停驻车道装了(park 索引在场)且部署有 run 店 ⇒ 决定店在场、绑的**就是**那只 run 店;否则不该有决定店。不相等的三形各一句:
|
|
51
|
+
* ①车道与 run 店都在而决定店缺席;②决定店绑的是别的 run 店(两只店各取各的:决定写进一只库、认领与读在另一只);③有决定店,而车道没装或部署没有 run 店。
|
|
52
|
+
*/
|
|
53
|
+
export declare function parkDecisionStoreRefusal(seats: ParkDecisionLaneSeats): string | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* 决定的同一性判据:判词 + 绑定两位 + 出处词(`decidedBy`)+ 载荷指纹(`updatedInput` / `answer` / `reason` / `feedback`,与 live 腿幂等回放的门⑦
|
|
56
|
+
* 同一只 {@link approvalPayloadFingerprint})。通道自报的 `approver` 不进 —— 它是归属标签,不改引擎铸的结算词。
|
|
57
|
+
* 载荷序列化不了 ⇒ 抛(两侧都源自 JSON,走到这里是结构异常;那只函数自己已经响亮 warn)。
|
|
58
|
+
*/
|
|
59
|
+
export declare function parkDecisionFingerprint(outcome: WorkflowParkOutcome): string;
|
|
60
|
+
/**
|
|
61
|
+
* 存下的决定体 → 内域形的**唯一读者**(SQL 列与 local 账本行共用)。判到本仓自己读的那几格为止(门、判词、绑定两位、出处对象);
|
|
62
|
+
* 其余键的属主是引擎,它在续跑入口 pre-CAS 再判一遍(与 checkpoint 店读 `outcome` 列同一条分工,见 `checkpoint-store-sql.ts` 的形读者注)。
|
|
63
|
+
*/
|
|
64
|
+
export declare const readWorkflowParkOutcome: JsonColumnReader<WorkflowParkOutcome>;
|
|
65
|
+
/**
|
|
66
|
+
* 单机车道的决定店(`LocalWorkflowAgentSessionIndex` 的同室孪生),绑在进程内的 run 店上({@link InProcessClaimFence}:Memory / File run 店)。
|
|
67
|
+
* `root` 在场 ⇒ 追加式 JSONL 账本(0o700 目录 + 0o600 账本,一行 = 一条决定行的整份快照,每一笔写都追加);重放按行序、**末行胜**
|
|
68
|
+
* (写序就是认领栅栏的序:每一笔都在栅栏的同步段里追加,同一宿主会话上的受理由认领串行);缺席 ⇒ 纯内存(`WORKFLOW_RUN_STORE=memory` 的易失车道)。
|
|
69
|
+
*
|
|
70
|
+
* 🔴 **坏行 fail-closed**(登记座 `workflow-park-decisions`):读不出的一行可能正是**取代**了一条旧决定的那一笔 —— 跳过它重放其余行,
|
|
71
|
+
* 会把被取代的旧决定复活成当下的决定,在下一次起跑时交给引擎。闸合上之后三个动词都响亮拒,直到有人处理那只文件;
|
|
72
|
+
* 起跑读者(`parked-decide.ts`)把这一拒读成「本店没有可供的行」(F tag `server.workflow-park-decision.store-unreadable`)—— 起跑不连带拒,写与按 token 读照旧拒。
|
|
73
|
+
* SQL 孪生同律:某一行 `outcome` 坏体 ⇒ 读口拒,起跑读者同一臂。
|
|
74
|
+
*/
|
|
75
|
+
export declare class LocalWorkflowParkDecisionStore implements WorkflowParkDecisionStore {
|
|
76
|
+
readonly claims: InProcessClaimFence;
|
|
77
|
+
private readonly fsyncEnabled;
|
|
78
|
+
private readonly ledgerPath;
|
|
79
|
+
private readonly gate;
|
|
80
|
+
private readonly rows;
|
|
81
|
+
private log;
|
|
82
|
+
constructor(claims: InProcessClaimFence, root?: string, fsyncEnabled?: boolean);
|
|
83
|
+
record(decision: WorkflowParkDecision): Promise<boolean>;
|
|
84
|
+
/** 整行写(同步):落盘**先于**内存翻转(调用方观察到的状态背后一定有耐久记录)。交回栅栏的落定记号(同步交回,型上不许让出)。 */
|
|
85
|
+
private landNow;
|
|
86
|
+
get(token: CheckpointToken): Promise<WorkflowParkDecision | null>;
|
|
87
|
+
listByHostSession(hostSessionId: string): Promise<WorkflowParkDecision[]>;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=workflow-park-decision.d.ts.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
4
|
+
import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
|
|
5
|
+
import { createObjectColumnReader, isJsonObject } from "../plugins/sql-json-column.js";
|
|
6
|
+
import { replayJsonlGuarded } from "../observability/corrupt-read-seat.js";
|
|
7
|
+
import { FENCE_LANDED } from "../plugins/store-contracts.js";
|
|
8
|
+
function seatName(v) {
|
|
9
|
+
if (v === undefined)
|
|
10
|
+
return "none";
|
|
11
|
+
const ctor = Reflect.get(v, "constructor");
|
|
12
|
+
return typeof ctor === "function" && ctor.name !== "" ? ctor.name : "an object";
|
|
13
|
+
}
|
|
14
|
+
export function parkDecisionStoreRefusal(seats) {
|
|
15
|
+
const wanted = seats.workflowAgentSessionIndex !== undefined ? seats.runStore : undefined;
|
|
16
|
+
const decisions = seats.workflowParkDecisions;
|
|
17
|
+
if (decisions?.claims === wanted)
|
|
18
|
+
return undefined;
|
|
19
|
+
const pair = `run store: ${seatName(seats.runStore)}; workflow park decision store: ${decisions === undefined ? "none" : `${seatName(decisions)} bound to ${seatName(decisions.claims)}`}`;
|
|
20
|
+
if (decisions === undefined) {
|
|
21
|
+
return (`the workflow park lane is wired (park index and run store) but no workflow park decision store is bound to that run store (${pair}) — ` +
|
|
22
|
+
"a decision row is written in the same atomic step that checks the delivering run still holds the host session, so it must live where the session claims live; " +
|
|
23
|
+
"pass the store backend's workflowParkDecisions() next to its run(); refusing to start rather than answer every workflow approval decision with a 500");
|
|
24
|
+
}
|
|
25
|
+
if (wanted === undefined) {
|
|
26
|
+
return (`a workflow park decision store is wired but the workflow park lane is not (${pair}; park index: ${seats.workflowAgentSessionIndex === undefined ? "none" : "present"}) — ` +
|
|
27
|
+
"a decision store is only read and written by that lane, which needs both the park index and the run store the decisions are fenced by; " +
|
|
28
|
+
"wire the lane (park index and run store) or drop the decision store; refusing to start rather than keep a store no lane fences");
|
|
29
|
+
}
|
|
30
|
+
return (`the workflow park decision store is not bound to this server's run store (${pair}) — ` +
|
|
31
|
+
"its writes are fenced by the run store it was built on, so a different run store means decisions recorded where the session claims are not checked; " +
|
|
32
|
+
"take runStore and workflowParkDecisions from the same store backend (run() and workflowParkDecisions()); refusing to start rather than record decisions the session claim cannot fence");
|
|
33
|
+
}
|
|
34
|
+
export function parkDecisionFingerprint(outcome) {
|
|
35
|
+
const payload = approvalPayloadFingerprint({
|
|
36
|
+
...(outcome.updatedInput !== undefined ? { updatedInput: outcome.updatedInput } : {}),
|
|
37
|
+
...(outcome.answer !== undefined ? { answer: outcome.answer } : {}),
|
|
38
|
+
...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),
|
|
39
|
+
...(outcome.feedback !== undefined ? { feedback: outcome.feedback } : {}),
|
|
40
|
+
});
|
|
41
|
+
if (payload === null)
|
|
42
|
+
throw new Error("workflow park decision: the decision payload cannot be serialized — refusing to record a decision whose identity cannot be stated");
|
|
43
|
+
const identity = JSON.stringify([outcome.decision, outcome.boundCallId, outcome.boundInputHash, outcome.hostDecision.decidedBy, payload]);
|
|
44
|
+
return createHash("sha256").update(identity).digest("hex");
|
|
45
|
+
}
|
|
46
|
+
const readOutcomeObject = createObjectColumnReader();
|
|
47
|
+
export const readWorkflowParkOutcome = (body) => {
|
|
48
|
+
if (!isJsonObject(body))
|
|
49
|
+
return { problem: "expected a JSON object" };
|
|
50
|
+
if (body.gate !== "policy_ask")
|
|
51
|
+
return { problem: "expected a policy_ask decision" };
|
|
52
|
+
if (body.decision !== "allow" && body.decision !== "deny")
|
|
53
|
+
return { problem: "decision is neither allow nor deny" };
|
|
54
|
+
if (typeof body.boundCallId !== "string" || typeof body.boundInputHash !== "string")
|
|
55
|
+
return { problem: "the binding pair is not two strings" };
|
|
56
|
+
if (!isJsonObject(body.hostDecision))
|
|
57
|
+
return { problem: "hostDecision is not an object" };
|
|
58
|
+
return readOutcomeObject(body);
|
|
59
|
+
};
|
|
60
|
+
const nonEmpty = (v) => typeof v === "string" && v !== "";
|
|
61
|
+
function readRow(raw) {
|
|
62
|
+
if (!isJsonObject(raw))
|
|
63
|
+
return undefined;
|
|
64
|
+
const { token, runId, hostSessionId, outcome, fingerprint, deliveredTaskId, acceptedAtMs } = raw;
|
|
65
|
+
if (!nonEmpty(token) || !nonEmpty(runId) || !nonEmpty(hostSessionId) || !nonEmpty(fingerprint) || !nonEmpty(deliveredTaskId))
|
|
66
|
+
return undefined;
|
|
67
|
+
if (typeof acceptedAtMs !== "number" || !Number.isSafeInteger(acceptedAtMs))
|
|
68
|
+
return undefined;
|
|
69
|
+
const o = readWorkflowParkOutcome(outcome);
|
|
70
|
+
if (o.problem !== undefined)
|
|
71
|
+
return undefined;
|
|
72
|
+
return { token: token, runId, hostSessionId, outcome: o.value, fingerprint, deliveredTaskId, acceptedAtMs };
|
|
73
|
+
}
|
|
74
|
+
const LEDGER_LINE_SHAPE = { what: "a workflow park decision row", read: readRow };
|
|
75
|
+
export class LocalWorkflowParkDecisionStore {
|
|
76
|
+
claims;
|
|
77
|
+
fsyncEnabled;
|
|
78
|
+
ledgerPath;
|
|
79
|
+
gate;
|
|
80
|
+
rows = new Map();
|
|
81
|
+
log;
|
|
82
|
+
constructor(claims, root, fsyncEnabled = true) {
|
|
83
|
+
this.claims = claims;
|
|
84
|
+
this.fsyncEnabled = fsyncEnabled;
|
|
85
|
+
if (root === undefined) {
|
|
86
|
+
this.ledgerPath = undefined;
|
|
87
|
+
this.gate = undefined;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const dir = join(root, "workflow-park-decisions");
|
|
91
|
+
ensureDir(dir);
|
|
92
|
+
this.ledgerPath = join(dir, "decisions.jsonl");
|
|
93
|
+
const { records, gate } = replayJsonlGuarded(this.ledgerPath, "workflow-park-decisions", LEDGER_LINE_SHAPE);
|
|
94
|
+
this.gate = gate;
|
|
95
|
+
for (const d of records)
|
|
96
|
+
this.rows.set(d.token, d);
|
|
97
|
+
}
|
|
98
|
+
async record(decision) {
|
|
99
|
+
this.gate?.assertUsable();
|
|
100
|
+
return this.claims.whileHoldingClaim(decision.hostSessionId, decision.deliveredTaskId, () => this.landNow(decision));
|
|
101
|
+
}
|
|
102
|
+
landNow(decision) {
|
|
103
|
+
if (this.ledgerPath !== undefined) {
|
|
104
|
+
this.log ??= new AppendLog(this.ledgerPath);
|
|
105
|
+
this.log.append(decision, this.fsyncEnabled);
|
|
106
|
+
}
|
|
107
|
+
this.rows.set(decision.token, decision);
|
|
108
|
+
return FENCE_LANDED;
|
|
109
|
+
}
|
|
110
|
+
async get(token) {
|
|
111
|
+
this.gate?.assertUsable();
|
|
112
|
+
return this.rows.get(token) ?? null;
|
|
113
|
+
}
|
|
114
|
+
async listByHostSession(hostSessionId) {
|
|
115
|
+
this.gate?.assertUsable();
|
|
116
|
+
return [...this.rows.values()].filter((d) => d.hostSessionId === hostSessionId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=workflow-park-decision.js.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 跨主体投递门 —— core `OrgDeliveryGate` 契约的 server 实现([ref] §10.2;core 7.53.0 的 core 半场)。
|
|
3
|
+
*
|
|
4
|
+
* ## 规则(一条)
|
|
5
|
+
* **一封跨主体的信只因这只门把它追加进收件人的 `principal.inbox` 而存在。** 门是唯一的跨 scope 追加持有者:成员判 →
|
|
6
|
+
* 铸信 → 收件箱上界 → 配额 → 追加,全在 {@link createOrgDeliveryGate} 交出的 `deliverCrossPrincipal` 里一体完成;
|
|
7
|
+
* 它只有两种答案 —— 一张回执,或 core 闭集 `ORG_DELIVERY_REFUSAL_CODES` 里的一个词。本仓不铸第二只拒绝词。
|
|
8
|
+
*
|
|
9
|
+
* 规则因此少了几条(而不是多了几条):
|
|
10
|
+
* · **成员判不另立名册**:「谁属于 org:acme」在一个进程里只有一个答案 —— 记忆准入 seam、memory-policy 面、共享记忆读面
|
|
11
|
+
* 读的那**同一个**目录实例(`boot/org-memory.ts` 装配的 `OrgMemoryDirectory`)。两位主体同属一个 org ⟺ 两人
|
|
12
|
+
* `lookup` 的 `org:` 键集合有交集。目录答不上来(`unavailable`)= 「部署说不出他们同 org」⇒ `not_member`
|
|
13
|
+
* (core 对这只词的定义逐字含这一臂)。
|
|
14
|
+
* · **记录只有一个铸点**:core 的纯函数 `mintCrossPrincipalRecord`(只写 `peerMeta.kind / fromScope / gateReceiptId`
|
|
15
|
+
* + 可选 `urgent` 与显示名;不写 `fromSession / fromMode / hopChain`)。本仓不另铸任何记录形。
|
|
16
|
+
* · **限流是配额店的两个原子动作,各管一件事**:`admitAttempt` 记发件人的每一次尝试(碰目录之前;界住刷目录的速率),
|
|
17
|
+
* `take` 在同一把锁下判并记 (from→to) 对偶界与收件人聚合界(投递额度)。SQL 两方言各是一个事务,见
|
|
18
|
+
* `plugins/org-delivery-quota-store.ts` 头注;File / Memory = 单进程。
|
|
19
|
+
* · **收件人的 scope 就是收件人主体串本身**:本服务的引擎 scope = `spec.principal`(`resolve-spec.ts` 落
|
|
20
|
+
* `auth.principal`;同一事实的推导见 `peer-directory.ts` 的「scope 轴」段)。门不另造编解码。
|
|
21
|
+
*
|
|
22
|
+
* ## 次序与理由
|
|
23
|
+
* 0. **发件人的尝试界**(配额店 `admitAttempt`,成没成都记一笔):满 ⇒ `rate_limited`。它必须在成员判之前 —— 成员判把调用方给的
|
|
24
|
+
* 收件人串交给组织目录去查,而目录的每主体状态按属主裁定不淘汰、它的量级只在「查的都是验过的主体」时有界(`org-memory-admission.ts`
|
|
25
|
+
* 的 `PrincipalState` 注);这一界让「一个成员拿任意串去刷目录」的速率与总量随时间线性有界(设计稿收件人查询面第四条:查询限流)。
|
|
26
|
+
* 与收件人无关 ⇒ 它的 `rate_limited` 不泄露收件人存不存在。
|
|
27
|
+
* 1. **成员判**(纯读):不同 org / 查无此人 / 目录不可用 / 收件人串过不了主体形判 / 收件人当不了收件箱的身份键(良构 Unicode ∧ 无控制字符 ∧
|
|
28
|
+
* UTF-8 字节不超过键列宽 —— 两方言收件箱写面与读面同读的唯一判官 `mailbox-store-sql.ts` 的 `mailboxKeyProblem`)⇒ 同一句 `not_member`,逐字节同 ——
|
|
29
|
+
* 门永远不是一只存在性探针(core 契约原句),也不说「是长度」。收件人那一判必须在配额之前:第 5 步的追加按同一只判官拒,拒在扣了投递额度之后就是「吃了额度没投到」。
|
|
30
|
+
* 2. **铸信**(纯函数):放在任何有副作用的步骤之前。发件人的 scope 拼不进信封的发件人位(引擎按百分号编码后 1..190 字节判,抛闭集码
|
|
31
|
+
* `cross_session.invalid_from_scope`,`CrossSessionCodecError` 由引擎包根导出)⇒ 同一句 `not_member`、一个配额名额都没吃 —— 发件人这一侧的判官只有引擎这一只,
|
|
32
|
+
* 本仓不另抄宽度公式。部署没声明收件箱消费方(`no_consumer`)或别的铸信失败 ⇒ 门抛(core:「the gate maps that to its own failure」),HTTP 面答 500,同样没吃额度。
|
|
33
|
+
* 3. **收件箱上界**(`peekCount` 读):满 ⇒ `inbox_full`,在吃配额之前判,满箱不烧发件人的额度。与引擎同主体车道的
|
|
34
|
+
* `maxQueuedPeerMessages` 同一形(入口处读一次占用);两封并发信最多越界并发数那么多,上界由配额再界一次。
|
|
35
|
+
* 4. **配额**(原子判并记):任一界满 ⇒ `rate_limited`,与第 0 步同一句(不让发件人从拒句里分辨出「收件人这一小时收了多少」)。
|
|
36
|
+
* 5. **追加**:`append(收件人 scope, PRINCIPAL_INBOX_HANDLE, 记录)`。店的预删除态拒码(`mailbox.recipient_tombstoned`)
|
|
37
|
+
* 原样转成 `recipient_tombstoned`(core 对这只词的定义:「the store's own pre-delete code, relayed」);其余错误抛。
|
|
38
|
+
* 被拒的投递在任何地方都不追加任何东西(core 契约原句);投递额度(第 4 步)在之后只有追加失败这一种「吃了额度没投到」—— 方向是收紧;
|
|
39
|
+
* 尝试界(第 0 步)按定义每次都记。
|
|
40
|
+
*
|
|
41
|
+
* ## 不是本门的威胁面(如实,[ref] §10.2-1 原句)
|
|
42
|
+
* 库内直接持有 `MailboxStore` 的宿主恒能写任何 scope —— 那是同一信任域内的事。本门保证的是**本服务的 wire 面**上没有第二条
|
|
43
|
+
* 跨 scope 写径:受理口(`POST /v1/principal-inbox/messages`)只调门,本仓源码里拼得出 `principal.inbox` 这只箱址的只有本文件
|
|
44
|
+
* (`test/org-delivery-gate-single-path.test.ts` 按源码钉)。
|
|
45
|
+
*/
|
|
46
|
+
import { type MailboxStore, type OrgDeliveryGate, type OrgDeliveryRefusalCode, type PrincipalInboxConsumer } from "@sema-agent/core";
|
|
47
|
+
import type { OrgMemoryDirectory } from "./org-memory-admission.js";
|
|
48
|
+
import type { OrgDeliveryQuotaPolicy, OrgDeliveryQuotaStore } from "./plugins/org-delivery-quota-store.js";
|
|
49
|
+
/**
|
|
50
|
+
* 跨主体配额(**唯一**的数字源;店只按它数)。默认显著紧于同主体车道(引擎 `PEER_LANE_DEFAULTS`:每发件人突发 30、
|
|
51
|
+
* 每秒回填 0.5):同一对主体每小时 10 封,一位收件人每小时合计 30 封 —— org 里一个吵闹的主体淹不了别人的箱;
|
|
52
|
+
* 一个发件人每小时 60 次尝试(成没成都算)—— 拿收件人串去问组织目录的速率有界(头注「次序」第 0 步)。
|
|
53
|
+
*/
|
|
54
|
+
export declare const ORG_DELIVERY_QUOTA: OrgDeliveryQuotaPolicy;
|
|
55
|
+
/** 收件箱上界(在箱、未被消费方确认的信封数)。与引擎同主体车道的箱上界缺省(`maxQueuedPeerMessages`)同值。 */
|
|
56
|
+
export declare const PRINCIPAL_INBOX_BOUND = 50;
|
|
57
|
+
/**
|
|
58
|
+
* 四只拒绝词各自的那**一句**(wire 上的 `error` 就是它,`routes/principal-inbox.ts` 原样中继、不手抄)。
|
|
59
|
+
* 键集 = core 闭集:core 加词 ⇒ 这张表编译红。
|
|
60
|
+
* `not_member` 一句同时服务「不同 org / 查无此人 / 目录答不上来 / 收件人串不成形 / 收件人当不了收件箱的键 / 发件人主体拼不进信封的发件人位」—— 改它要保持各臂同字节。
|
|
61
|
+
*/
|
|
62
|
+
export declare const ORG_DELIVERY_REFUSAL_MESSAGES: Readonly<Record<OrgDeliveryRefusalCode, string>>;
|
|
63
|
+
export interface OrgDeliveryGateDeps {
|
|
64
|
+
/** org 成员目录 —— **必须**与记忆准入 seam / memory-policy / 共享记忆读面同一个实例(头注)。 */
|
|
65
|
+
readonly directory: OrgMemoryDirectory;
|
|
66
|
+
/** 收件人箱所在的店(与引擎 `RunnerDeps.mailboxStore` 同一只)。 */
|
|
67
|
+
readonly mailbox: Pick<MailboxStore, "append" | "peekCount">;
|
|
68
|
+
/** 配额店(`StoreBackend.orgDeliveryQuota()`)。 */
|
|
69
|
+
readonly quota: OrgDeliveryQuotaStore;
|
|
70
|
+
/** 部署声明的收件箱消费方 —— `principalInboxConsumerOf` 读**同一份** `RunnerDeps` 的结果(core 契约:铸信要它)。 */
|
|
71
|
+
readonly consumer: PrincipalInboxConsumer;
|
|
72
|
+
/** 信的 `sentAt` 钟(缺省本进程墙钟)。配额窗不读它 —— 窗由配额店自己的钟定。 */
|
|
73
|
+
readonly nowMs?: () => number;
|
|
74
|
+
/** 回执 id 铸点(缺省 uuidv7)。 */
|
|
75
|
+
readonly mintReceiptId?: () => string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 活对象(闭包持有目录 / 两只店 / 消费方)⇒ `create*`(CLAUDE.md 工厂命名律)。
|
|
79
|
+
*/
|
|
80
|
+
export declare function createOrgDeliveryGate(deps: OrgDeliveryGateDeps): OrgDeliveryGate;
|
|
81
|
+
//# sourceMappingURL=org-delivery-gate.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CrossSessionCodecError, MAILBOX_TOMBSTONED_RECIPIENT_CODE, PRINCIPAL_INBOX_HANDLE, mintCrossPrincipalRecord, uuidv7, } from "@sema-agent/core";
|
|
2
|
+
import { principalShapeProblem } from "./security.js";
|
|
3
|
+
import { mailboxKeyProblem } from "./plugins/mailbox-store-sql.js";
|
|
4
|
+
export const ORG_DELIVERY_QUOTA = Object.freeze({ windowMs: 3_600_000, attemptsPerSender: 60, perPair: 10, perRecipient: 30 });
|
|
5
|
+
export const PRINCIPAL_INBOX_BOUND = 50;
|
|
6
|
+
export const ORG_DELIVERY_REFUSAL_MESSAGES = Object.freeze({
|
|
7
|
+
"org_delivery.not_member": "the recipient cannot be reached through the organization delivery gate",
|
|
8
|
+
"org_delivery.rate_limited": "the cross-principal delivery quota is spent for now — try again later",
|
|
9
|
+
"org_delivery.inbox_full": "the recipient's principal inbox is full — try again after the recipient has read some of it",
|
|
10
|
+
"org_delivery.recipient_tombstoned": "the recipient is being deleted — nothing was delivered",
|
|
11
|
+
});
|
|
12
|
+
const refuse = (code) => ({ ok: false, code, message: ORG_DELIVERY_REFUSAL_MESSAGES[code] });
|
|
13
|
+
async function shareAnOrg(directory, a, b) {
|
|
14
|
+
const [la, lb] = await Promise.all([directory.lookup(a), directory.lookup(b)]);
|
|
15
|
+
if (la.kind !== "granted" || lb.kind !== "granted")
|
|
16
|
+
return false;
|
|
17
|
+
return Object.keys(la.scopes).some((org) => Object.hasOwn(lb.scopes, org));
|
|
18
|
+
}
|
|
19
|
+
const principalShaped = (s) => s !== "" && principalShapeProblem(s) === undefined;
|
|
20
|
+
const fitsInboxKey = (recipient) => mailboxKeyProblem("scope", recipient) === undefined;
|
|
21
|
+
export function createOrgDeliveryGate(deps) {
|
|
22
|
+
const nowMs = deps.nowMs ?? Date.now;
|
|
23
|
+
const mintReceiptId = deps.mintReceiptId ?? uuidv7;
|
|
24
|
+
return {
|
|
25
|
+
async deliverCrossPrincipal(authedFrom, toPrincipal, body) {
|
|
26
|
+
if (!(await deps.quota.admitAttempt(authedFrom.scope, ORG_DELIVERY_QUOTA)))
|
|
27
|
+
return refuse("org_delivery.rate_limited");
|
|
28
|
+
if (!principalShaped(toPrincipal) || !fitsInboxKey(toPrincipal) || !(await shareAnOrg(deps.directory, authedFrom.scope, toPrincipal))) {
|
|
29
|
+
return refuse("org_delivery.not_member");
|
|
30
|
+
}
|
|
31
|
+
const toScope = toPrincipal;
|
|
32
|
+
const receiptId = mintReceiptId();
|
|
33
|
+
let record;
|
|
34
|
+
try {
|
|
35
|
+
record = mintCrossPrincipalRecord({ from: authedFrom, body, receiptId, sentAt: nowMs(), consumer: deps.consumer });
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (err instanceof CrossSessionCodecError && err.code === "cross_session.invalid_from_scope")
|
|
39
|
+
return refuse("org_delivery.not_member");
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
if ((await deps.mailbox.peekCount(toScope, PRINCIPAL_INBOX_HANDLE)) >= PRINCIPAL_INBOX_BOUND)
|
|
43
|
+
return refuse("org_delivery.inbox_full");
|
|
44
|
+
if (!(await deps.quota.take(authedFrom.scope, toScope, ORG_DELIVERY_QUOTA)))
|
|
45
|
+
return refuse("org_delivery.rate_limited");
|
|
46
|
+
let seq;
|
|
47
|
+
try {
|
|
48
|
+
seq = await deps.mailbox.append(toScope, PRINCIPAL_INBOX_HANDLE, record);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
if (err?.code === MAILBOX_TOMBSTONED_RECIPIENT_CODE)
|
|
52
|
+
return refuse("org_delivery.recipient_tombstoned");
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
return { ok: true, receipt: { receiptId, seq } };
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=org-delivery-gate.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 读面上「一台服务器 / 一个对端挂了哪些工具」的**名单截表律** —— 全仓唯一属主。
|
|
3
|
+
*
|
|
4
|
+
* 为什么必须是一处:名单的条数是远端可控且无界的(A2A 卡里的技能条数、MCP 服务器列出的工具条数,core 只限单个名字的
|
|
5
|
+
* 长度,不限条数),而这些读面都是**只读 GET / 可被反复触发的探测**,多身份可放大。所以读面发的是
|
|
6
|
+
* **有界表 + 真计数 + 显式旗**,而不是静默截断:一个会撒谎的读面比一个大的读面更贵。
|
|
7
|
+
* 同一个事实(「名单被截了」)在每一张读面上必须是同一个数、同一个旗键、同一个说法 —— 两处各写一遍,
|
|
8
|
+
* 下一个人只会改到其中一处。
|
|
9
|
+
*
|
|
10
|
+
* ## 契约(所有用到它的读面逐字成立)
|
|
11
|
+
* · 名单最多 {@link PANEL_MAX_TOOL_NAMES} 条,**读数序**取前缀;
|
|
12
|
+
* · 超出时另发 `toolNamesTruncated: true`;**恰好**在上限上不算截断,旗键缺席(不编 `false`);
|
|
13
|
+
* · **真数不在这里**:`toolCount` 是各读面自己的铸点(A2A 面板 = 名单长度;MCP 状态面 = §G.7 的那一只投影),本函数一个字都不碰它 ——
|
|
14
|
+
* 消费端读数用 `toolCount`,别用 `toolNames.length`。
|
|
15
|
+
*/
|
|
16
|
+
/** 每一台服务器 / 每一个对端最多回多少条工具名。 */
|
|
17
|
+
export declare const PANEL_MAX_TOOL_NAMES = 200;
|
|
18
|
+
/** 一份工具名单的有界出口形(纯数据,`build*`)。入参不被改写。 */
|
|
19
|
+
export declare function buildBoundedToolNames(names: readonly string[]): {
|
|
20
|
+
toolNames: string[];
|
|
21
|
+
toolNamesTruncated?: true;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=panel-tool-names.d.ts.map
|