@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
package/dist/http/resume-legs.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY, PAUSE_REGISTRY } from "@sema-agent/core";
|
|
4
|
-
import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
4
|
+
import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
5
|
+
import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
|
|
6
|
+
const DELIVERY_NOT_RECORDED = "workflow park decide: this delivery no longer holds the host session (its claim was released) — the decision was not recorded and this delivery was not started";
|
|
5
7
|
const sessionFaceUnavailable = async (sessionId) => {
|
|
6
8
|
throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no sessionFaceOf read (every built-in store has one) — refusing rather than resuming unverified`);
|
|
7
9
|
};
|
|
@@ -9,12 +11,14 @@ import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../se
|
|
|
9
11
|
import { parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
10
12
|
import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
|
|
11
13
|
import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
|
|
14
|
+
import { isTerminalRunStatus } from "../plugins/store-contracts.js";
|
|
12
15
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
13
16
|
import { recordResumeResourceWindow } from "../resource-window.js";
|
|
14
17
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
15
18
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../fleet/fleet-bus.js";
|
|
16
19
|
import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
|
|
17
|
-
import {
|
|
20
|
+
import { currentApprovalGateToken } from "../approval-gate-currency.js";
|
|
21
|
+
import { createApprovalCardEmitter, declaredApprovalFrameFamilies, isApprovalGateKind, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
|
|
18
22
|
import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
|
|
19
23
|
import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
|
|
20
24
|
import { serviceVersion } from "../service-version.js";
|
|
@@ -32,7 +36,7 @@ import { errorText } from "../observability/err-text.js";
|
|
|
32
36
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
33
37
|
import { verifyRoundsFromBody } from "./verify-rounds.js";
|
|
34
38
|
import { planReviewDecisionExitsPlan, storedBodyPermissionMode } from "../task-settings.js";
|
|
35
|
-
import { acceptedResumingReceipt, buildHostLegOutcome } from "./route-ctx.js";
|
|
39
|
+
import { acceptedResumingReceipt, acceptedTaskIdOf, buildHostLegOutcome } from "./route-ctx.js";
|
|
36
40
|
import { mintAndDriveDurableRun } from "./durable-run-leg.js";
|
|
37
41
|
import { createSessionsLocal } from "./routes/sessions.js";
|
|
38
42
|
import { createSessionSyncLocal } from "./routes/session-sync.js";
|
|
@@ -44,12 +48,13 @@ import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js
|
|
|
44
48
|
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
45
49
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
46
50
|
import { deploymentListAsks } from "../approval.js";
|
|
47
|
-
import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
|
|
48
|
-
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
49
|
-
import { refusalBody } from "./refusal-body.js";
|
|
51
|
+
import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnsealableAnswer } from "./resume-ctx-read.js";
|
|
52
|
+
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
53
|
+
import { refusalBody, resumeRefusalFacts } from "./refusal-body.js";
|
|
50
54
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
51
55
|
import { httpErrorRefusal } from "./send.js";
|
|
52
56
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
57
|
+
import { fieldConflictFacts } from "../field-conflict.js";
|
|
53
58
|
function legMayHaveReopenedCheckpoint(r) {
|
|
54
59
|
return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
|
|
55
60
|
}
|
|
@@ -121,6 +126,9 @@ const CHECKPOINT_ERROR_DISPOSITION = {
|
|
|
121
126
|
"steering.invalid_content": "terminal",
|
|
122
127
|
"steering.queue_full": "terminal",
|
|
123
128
|
};
|
|
129
|
+
function windowZeroSeatOf(toolApproval) {
|
|
130
|
+
return (identity) => toolApproval.boundUnattendedAsk(identity, { arm: "window_zero" });
|
|
131
|
+
}
|
|
124
132
|
export function resumeRefusalDisposition(e) {
|
|
125
133
|
const word = Object.hasOwn(CHECKPOINT_ERROR_DISPOSITION, e.code) ? CHECKPOINT_ERROR_DISPOSITION[e.code] : undefined;
|
|
126
134
|
switch (word) {
|
|
@@ -170,7 +178,7 @@ function decisionRefusalCauseOf(e, gate) {
|
|
|
170
178
|
return role === undefined ? undefined : FIELD_ROLE_CAUSE[role];
|
|
171
179
|
}
|
|
172
180
|
function notResumableAnswer() {
|
|
173
|
-
return { status: 409, body: {
|
|
181
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.not_resumable", message: "run is not in a resumable (suspended) state (already resumed, decided, or expired)" }) };
|
|
174
182
|
}
|
|
175
183
|
export function checkpointRefusalAnswer(e, at) {
|
|
176
184
|
const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, at.gate) : undefined;
|
|
@@ -182,9 +190,7 @@ export function checkpointRefusalAnswer(e, at) {
|
|
|
182
190
|
errorCode: e.code === "checkpoint.invalid_outcome" ? (refusalCause !== undefined ? WIRE_CODE_OF_DECISION_REFUSAL[refusalCause] : UNCLASSIFIED_INVALID_OUTCOME_CODE) : e.code,
|
|
183
191
|
facts: {
|
|
184
192
|
...(at.taskId !== undefined ? { taskId: at.taskId } : {}),
|
|
185
|
-
...(detail
|
|
186
|
-
...(detail?.field ? { field: detail.field } : {}),
|
|
187
|
-
...(detail?.reason !== undefined ? { reason: detail.reason } : {}),
|
|
193
|
+
...resumeRefusalFacts(detail),
|
|
188
194
|
...((e.code === "resume.usage_window_exhausted" || e.code === "resume.preflight_rejected") && typeof detail?.retryAfterMs === "number" && Number.isFinite(detail.retryAfterMs) && detail.retryAfterMs > 0
|
|
189
195
|
? { retryAfterSec: Math.max(1, Math.ceil(detail.retryAfterMs / 1000)) }
|
|
190
196
|
: {}),
|
|
@@ -194,11 +200,12 @@ export function checkpointRefusalAnswer(e, at) {
|
|
|
194
200
|
}, at.retriable);
|
|
195
201
|
}
|
|
196
202
|
export function createResumeLegs(ctx) {
|
|
197
|
-
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode,
|
|
203
|
+
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
|
|
204
|
+
const ctxOpening = (sessionId) => ({ sessionId, opener: deps.checkpointCtxSealing });
|
|
198
205
|
const resumeFleetScope = (req, auth) => req ? (gatedPrincipal(req, deps.config) ?? "default") : (auth.principal ?? "default");
|
|
199
206
|
async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider) {
|
|
200
207
|
const cs = deps.checkpointStore;
|
|
201
|
-
const token =
|
|
208
|
+
const token = await currentApprovalGateToken(cs, sessionId);
|
|
202
209
|
if (binding?.checkpointToken && binding.checkpointToken !== token) {
|
|
203
210
|
let seenUnreadable = false;
|
|
204
211
|
const seen = await cs.get(binding.checkpointToken).catch((e) => {
|
|
@@ -212,7 +219,7 @@ export function createResumeLegs(ctx) {
|
|
|
212
219
|
}
|
|
213
220
|
if (!token && !attributable && !seenUnreadable) {
|
|
214
221
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true });
|
|
215
|
-
return { status: 404, body: {
|
|
222
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
216
223
|
}
|
|
217
224
|
let currentPending;
|
|
218
225
|
if (token && decider !== undefined) {
|
|
@@ -225,7 +232,7 @@ export function createResumeLegs(ctx) {
|
|
|
225
232
|
const scopeOk = cur !== null && (decider.explicitOperator || curOwner === undefined || curOwner === decider.principal);
|
|
226
233
|
if (cur && cur.sessionId === sessionId && !scopeOk) {
|
|
227
234
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true, foreignCurrentSuppressed: true });
|
|
228
|
-
return { status: 404, body: {
|
|
235
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
229
236
|
}
|
|
230
237
|
if (cur && cur.status === "pending" && cur.sessionId === sessionId && scopeOk && curPa?.kind === "tool_approval" && curPa.toolCallId) {
|
|
231
238
|
currentPending = {
|
|
@@ -237,22 +244,24 @@ export function createResumeLegs(ctx) {
|
|
|
237
244
|
}
|
|
238
245
|
return {
|
|
239
246
|
status: 409,
|
|
240
|
-
body: {
|
|
241
|
-
error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
247
|
+
body: refusalBody({
|
|
242
248
|
errorCode: "approval_stale",
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
249
|
+
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
250
|
+
facts: {
|
|
251
|
+
...(attributable ? { terminal: "resolved" } : {}),
|
|
252
|
+
...(currentPending ? { currentPending } : {}),
|
|
253
|
+
},
|
|
254
|
+
}),
|
|
246
255
|
};
|
|
247
256
|
}
|
|
248
257
|
if (!token) {
|
|
249
258
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId });
|
|
250
|
-
return { status: 404, body: {
|
|
259
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
251
260
|
}
|
|
252
261
|
const cpPromise = cs.get(token);
|
|
253
262
|
const ctxPromise = cs.getCtx(sessionId);
|
|
254
263
|
void ctxPromise.catch(() => { });
|
|
255
|
-
const noApproval = { status: 404, body: {
|
|
264
|
+
const noApproval = { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
256
265
|
let cp;
|
|
257
266
|
try {
|
|
258
267
|
cp = await cpPromise;
|
|
@@ -263,18 +272,18 @@ export function createResumeLegs(ctx) {
|
|
|
263
272
|
deps.logger?.warn?.("approval_decide_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
|
|
264
273
|
return noApproval;
|
|
265
274
|
}
|
|
266
|
-
return { status: 409, body: {
|
|
275
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
267
276
|
}
|
|
268
277
|
if (!cp) {
|
|
269
278
|
if (!(await mayDiscloseRowError(cs, token, decider)))
|
|
270
279
|
return noApproval;
|
|
271
|
-
return { status: 404, body: {
|
|
280
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
|
|
272
281
|
}
|
|
273
282
|
if (decider !== undefined && !decider.explicitOperator) {
|
|
274
283
|
const cpOwner = decodeCheckpointScope(cp.scope);
|
|
275
284
|
if (cpOwner !== undefined && cpOwner !== decider.principal) {
|
|
276
285
|
deps.logger?.warn?.("approval_decide_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
|
|
277
|
-
return { status: 404, body: {
|
|
286
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
278
287
|
}
|
|
279
288
|
}
|
|
280
289
|
const decideGateKind = cp.gate?.kind;
|
|
@@ -287,21 +296,25 @@ export function createResumeLegs(ctx) {
|
|
|
287
296
|
: null;
|
|
288
297
|
return {
|
|
289
298
|
status: 409,
|
|
290
|
-
body: {
|
|
291
|
-
error: `task is suspended on a '${decideGateKind ?? "unknown"}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
|
|
299
|
+
body: refusalBody({
|
|
292
300
|
errorCode: "gate_not_tool_approval",
|
|
293
|
-
|
|
294
|
-
|
|
301
|
+
message: `task is suspended on a '${decideGateKind ?? "unknown"}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
|
|
302
|
+
facts: {
|
|
303
|
+
...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
|
|
304
|
+
},
|
|
305
|
+
}),
|
|
295
306
|
};
|
|
296
307
|
}
|
|
297
308
|
if (req !== undefined && !binding?.checkpointToken && !binding?.boundCallId) {
|
|
298
309
|
return {
|
|
299
310
|
status: 400,
|
|
300
|
-
body: {
|
|
301
|
-
error: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
|
|
311
|
+
body: refusalBody({
|
|
302
312
|
errorCode: "decide_requires_binding",
|
|
303
|
-
|
|
304
|
-
|
|
313
|
+
message: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
|
|
314
|
+
facts: {
|
|
315
|
+
bindingEnforced: true,
|
|
316
|
+
},
|
|
317
|
+
}),
|
|
305
318
|
};
|
|
306
319
|
}
|
|
307
320
|
const s57RowCallId = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
|
|
@@ -316,11 +329,13 @@ export function createResumeLegs(ctx) {
|
|
|
316
329
|
}
|
|
317
330
|
return {
|
|
318
331
|
status: 409,
|
|
319
|
-
body: {
|
|
320
|
-
error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
332
|
+
body: refusalBody({
|
|
321
333
|
errorCode: "approval_stale",
|
|
322
|
-
|
|
323
|
-
|
|
334
|
+
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
335
|
+
facts: {
|
|
336
|
+
...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
|
|
337
|
+
},
|
|
338
|
+
}),
|
|
324
339
|
};
|
|
325
340
|
}
|
|
326
341
|
const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
|
|
@@ -374,7 +389,7 @@ export function createResumeLegs(ctx) {
|
|
|
374
389
|
const parkedReq = {
|
|
375
390
|
token, scope: cp.scope, sessionId: cp.sessionId, pendingAction: cp.pendingAction,
|
|
376
391
|
decision, hostDecision, ...(reason !== undefined ? { reason } : {}),
|
|
377
|
-
...(binding !== undefined ? { binding:
|
|
392
|
+
...(binding !== undefined ? { binding: (({ checkpointToken: _staleGuardOnly, ...rest }) => rest)(binding) } : {}),
|
|
378
393
|
...(answer !== undefined ? { answer } : {}),
|
|
379
394
|
...(boundGrant ? { grantRemember: (rootSessionId) => boundGrant(rootSessionId) } : {}),
|
|
380
395
|
};
|
|
@@ -387,7 +402,7 @@ export function createResumeLegs(ctx) {
|
|
|
387
402
|
sessionFaceOf: deps.sessionStorage?.sessionFaceOf ? deps.sessionStorage.sessionFaceOf.bind(deps.sessionStorage) : sessionFaceUnavailable,
|
|
388
403
|
...(deps.parkedKnownAgentTypes ? { knownAgentTypes: deps.parkedKnownAgentTypes } : {}),
|
|
389
404
|
...(deps.parkedReviveInheritedGate ? { rebuildInheritedGate: deps.parkedReviveInheritedGate } : {}),
|
|
390
|
-
...(deps.toolApproval ? { approverSeatFor: deps.toolApproval
|
|
405
|
+
...(deps.toolApproval ? { approverSeatFor: windowZeroSeatOf(deps.toolApproval) } : {}),
|
|
391
406
|
...(deps.logger ? { warn: parkWarn } : {}),
|
|
392
407
|
}, parkedReq, { handle: origin.handle, row: origin.row }));
|
|
393
408
|
void ctxPromise.catch(() => { });
|
|
@@ -407,22 +422,58 @@ export function createResumeLegs(ctx) {
|
|
|
407
422
|
const plan = planWorkflowParkedDecide(origin, parkedReq);
|
|
408
423
|
if (!plan.ok)
|
|
409
424
|
return plan.deny;
|
|
410
|
-
const
|
|
425
|
+
const decisions = deps.workflowParkDecisions;
|
|
426
|
+
const runStore = deps.runStore;
|
|
427
|
+
if (decisions === undefined || runStore === undefined) {
|
|
428
|
+
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
429
|
+
}
|
|
430
|
+
const fingerprint = parkDecisionFingerprint(plan.outcome);
|
|
431
|
+
const liveReceiptOf = async (held) => {
|
|
432
|
+
if (held === null || held.fingerprint !== fingerprint)
|
|
433
|
+
return undefined;
|
|
434
|
+
const run = await runStore.getRun(held.deliveredTaskId);
|
|
435
|
+
return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId) : undefined;
|
|
436
|
+
};
|
|
437
|
+
const replay = await liveReceiptOf(await decisions.get(token));
|
|
438
|
+
if (replay !== undefined)
|
|
439
|
+
return replay;
|
|
440
|
+
let recordFailure;
|
|
441
|
+
const delivery = {
|
|
442
|
+
carried: [{ runId: plan.runId, token, outcome: plan.outcome }],
|
|
443
|
+
commitBeforeLaunch: async (deliveredTaskId) => {
|
|
444
|
+
try {
|
|
445
|
+
const recorded = await decisions.record({ token, runId: plan.runId, hostSessionId: plan.hostSessionId, outcome: plan.outcome, fingerprint, deliveredTaskId, acceptedAtMs: Date.now() });
|
|
446
|
+
if (!recorded)
|
|
447
|
+
throw new Error(DELIVERY_NOT_RECORDED);
|
|
448
|
+
}
|
|
449
|
+
catch (err) {
|
|
450
|
+
recordFailure = { err };
|
|
451
|
+
throw err;
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
};
|
|
411
455
|
const hostToken = await cs
|
|
412
456
|
.findPendingTokenBySession(plan.hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" })
|
|
413
457
|
.catch(() => undefined);
|
|
414
458
|
const admittedPayer = decodeCheckpointScope(cp.scope);
|
|
415
|
-
|
|
416
|
-
? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly,
|
|
417
|
-
: await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req,
|
|
459
|
+
const out = hostToken
|
|
460
|
+
? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, delivery, admittedPayer)
|
|
461
|
+
: await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req, delivery, plan.runId);
|
|
462
|
+
if (recordFailure !== undefined)
|
|
463
|
+
throw recordFailure.err;
|
|
464
|
+
if (acceptedTaskIdOf(out) === undefined)
|
|
465
|
+
return (await liveReceiptOf(await decisions.get(token))) ?? out;
|
|
466
|
+
return out;
|
|
418
467
|
}
|
|
419
468
|
}
|
|
420
|
-
const ctxRead0 = await readResumeCtx(ctxPromise);
|
|
469
|
+
const ctxRead0 = await readResumeCtx(ctxPromise, ctxOpening(sessionId));
|
|
421
470
|
if (ctxRead0.kind === "corrupt")
|
|
422
471
|
return resumeCtxCorruptAnswer(ctxRead0.where);
|
|
472
|
+
if (ctxRead0.kind === "unsealable")
|
|
473
|
+
return resumeCtxUnsealableAnswer(ctxRead0);
|
|
423
474
|
const ctx = ctxRead0.kind === "ok" ? ctxRead0.ctx : null;
|
|
424
475
|
if (!ctx)
|
|
425
|
-
return { status: 409, body: {
|
|
476
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
426
477
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
427
478
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
428
479
|
if (!resolved.ok)
|
|
@@ -430,10 +481,10 @@ export function createResumeLegs(ctx) {
|
|
|
430
481
|
const { spec, engineNotices } = resolved;
|
|
431
482
|
const pendingTool = cp.pendingAction.toolName;
|
|
432
483
|
if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
|
|
433
|
-
return { status: 400, body: {
|
|
484
|
+
return { status: 400, body: refusalBody({ errorCode: "request.field_conflict", message: "pending action is AskUserQuestion — approve must carry body.answer ({ answers: [{ header, selected: string[], note? }] })", facts: { ...fieldConflictFacts("ask_answer", ["decision", "answer"]) } }) };
|
|
434
485
|
}
|
|
435
486
|
if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
|
|
436
|
-
return { status: 400, body: {
|
|
487
|
+
return { status: 400, body: refusalBody({ errorCode: "request.field_conflict", message: `body.answer is only valid when approving a pending AskUserQuestion (decision: "${decision}", pending: "${pendingTool ?? "unknown"}")`, facts: { ...fieldConflictFacts("ask_answer", ["answer", "decision"]) } }) };
|
|
437
488
|
}
|
|
438
489
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
439
490
|
const pa = cp.pendingAction;
|
|
@@ -446,6 +497,7 @@ export function createResumeLegs(ctx) {
|
|
|
446
497
|
decision: decision === "approve" ? "allow" : "deny",
|
|
447
498
|
...(decision === "approve" && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
|
|
448
499
|
...(reason ? { reason } : {}),
|
|
500
|
+
...(binding?.feedback !== undefined ? { feedback: binding.feedback } : {}),
|
|
449
501
|
...(answer !== undefined ? { answer } : {}),
|
|
450
502
|
hostDecision,
|
|
451
503
|
};
|
|
@@ -508,7 +560,7 @@ export function createResumeLegs(ctx) {
|
|
|
508
560
|
const resumeLegStartedAt = Date.now();
|
|
509
561
|
const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx } = args;
|
|
510
562
|
const reparkStatus = outcome.gate === "plan_review" ? "needs_review" : "suspended";
|
|
511
|
-
const commitResumeCtx = () => deps.checkpointStore.putCtx(resumeCtx.sessionId, { ...resumeCtx.ctx, writerVersion: serviceVersion() }).catch((e) => {
|
|
563
|
+
const commitResumeCtx = () => Promise.resolve().then(() => deps.checkpointStore.putCtx(resumeCtx.sessionId, sealCheckpointCtx({ ...resumeCtx.ctx, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, resumeCtx.sessionId))).catch((e) => {
|
|
512
564
|
deps.metrics?.inc("resume_ctx_not_persisted_total");
|
|
513
565
|
deps.logger?.warn?.("resume_ctx_not_persisted", { sessionId: resumeCtx.sessionId, gate: outcome.gate, error: errorText(e), note: "this continuation runs with the body it was given; the durable rebuild input still holds the PREVIOUS body, so the NEXT continuation of this session rebuilds from that one (never wider than this leg's)" });
|
|
514
566
|
});
|
|
@@ -517,7 +569,7 @@ export function createResumeLegs(ctx) {
|
|
|
517
569
|
toolApprovalEnabled: Boolean(deps.toolApproval),
|
|
518
570
|
streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
|
|
519
571
|
backend: deps.backend,
|
|
520
|
-
|
|
572
|
+
checkpointStore: deps.checkpointStore,
|
|
521
573
|
}).active;
|
|
522
574
|
const approvalLeg = resolveApprovalLeg({
|
|
523
575
|
streamApprovalOn,
|
|
@@ -525,6 +577,7 @@ export function createResumeLegs(ctx) {
|
|
|
525
577
|
windowMarginMs: deps.config.streamAskWindowMarginMs,
|
|
526
578
|
...(typeof taskConfig.limits?.maxWalltimeMs === "number" ? { legWalltimeMs: taskConfig.limits.maxWalltimeMs } : {}),
|
|
527
579
|
nowMonotonicMs: performance.now(),
|
|
580
|
+
declaredFamilies: declaredApprovalFrameFamilies(resumeCtx.ctx.body.settings),
|
|
528
581
|
});
|
|
529
582
|
const legDeadlineMonotonic = approvalLeg.legDeadlineMonotonic;
|
|
530
583
|
const ownerQuotaDenial = args.callerInitiated === true ? ownerCostQuotaDenied(principal) : undefined;
|
|
@@ -533,6 +586,7 @@ export function createResumeLegs(ctx) {
|
|
|
533
586
|
const ownerLeaseDenial = args.callerInitiated === true && args.ownerLeaseAdmitted !== true ? await ownerFleetLeaseDenied(principal) : undefined;
|
|
534
587
|
if (ownerLeaseDenial)
|
|
535
588
|
return ownerLeaseDenial;
|
|
589
|
+
const heldDecisions = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
|
|
536
590
|
let taskId = await deps.runStore?.getActiveTaskId(sessionId);
|
|
537
591
|
let freshRunClaimed = false;
|
|
538
592
|
if (args.mintFreshRun && taskId && deps.runStore) {
|
|
@@ -562,8 +616,8 @@ export function createResumeLegs(ctx) {
|
|
|
562
616
|
const resumeTaskConfig = taskId ? { ...taskConfig, taskId } : taskConfig;
|
|
563
617
|
if (deps.toolApproval && taskId) {
|
|
564
618
|
const toolApproval = deps.toolApproval;
|
|
565
|
-
resumeTaskConfig.onAsk = approvalLeg.
|
|
566
|
-
? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId })
|
|
619
|
+
resumeTaskConfig.onAsk = approvalLeg.unattended
|
|
620
|
+
? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId }, approvalLeg.unattended)
|
|
567
621
|
: toolApproval.boundAsk({
|
|
568
622
|
owner: principal ?? null,
|
|
569
623
|
taskId,
|
|
@@ -654,6 +708,14 @@ export function createResumeLegs(ctx) {
|
|
|
654
708
|
deps.modelUsage?.register(taskId);
|
|
655
709
|
resumeManifestLease = deps.promptManifests?.register(taskId) ?? 0;
|
|
656
710
|
}
|
|
711
|
+
if (args.workflowDecision !== undefined && taskId === undefined) {
|
|
712
|
+
throw new Error("workflow decision delivery: no run row was claimed for the delivering leg — refusing to start a delivery whose decision could not be recorded against it");
|
|
713
|
+
}
|
|
714
|
+
const workflowDecision = args.workflowDecision;
|
|
715
|
+
const launchInternals = {
|
|
716
|
+
...heldDecisions,
|
|
717
|
+
...(workflowDecision !== undefined && taskId !== undefined ? { afterCheckpointResolve: () => workflowDecision.commitBeforeLaunch(taskId) } : {}),
|
|
718
|
+
};
|
|
657
719
|
commitSessionInheritEnv(deps, sessionId, resumeCtx.ctx.body);
|
|
658
720
|
if (args.onResumeCommitted)
|
|
659
721
|
await args.onResumeCommitted();
|
|
@@ -689,6 +751,7 @@ export function createResumeLegs(ctx) {
|
|
|
689
751
|
cancelLedgerAppend = vAppend;
|
|
690
752
|
const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
691
753
|
const verifyInternals = {
|
|
754
|
+
...launchInternals,
|
|
692
755
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
693
756
|
onForwardEvent: (e) => {
|
|
694
757
|
fleetPub?.onForwardEvent(e);
|
|
@@ -817,7 +880,7 @@ export function createResumeLegs(ctx) {
|
|
|
817
880
|
resumeLegLive = true;
|
|
818
881
|
const tailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
819
882
|
const stream = await legRunner.resumeStream(token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
|
|
820
|
-
...
|
|
883
|
+
...launchInternals,
|
|
821
884
|
onForwardEvent: (e) => {
|
|
822
885
|
fleetPub?.onForwardEvent(e);
|
|
823
886
|
{
|
|
@@ -1041,11 +1104,10 @@ export function createResumeLegs(ctx) {
|
|
|
1041
1104
|
const { type, ...rest } = frame;
|
|
1042
1105
|
return append(type, rest);
|
|
1043
1106
|
};
|
|
1044
|
-
const withApproval = () => deps.toolApproval && taskId && approvalLeg.active &&
|
|
1107
|
+
const withApproval = () => deps.toolApproval && taskId && approvalLeg.active && approvalLeg.unattended === undefined
|
|
1045
1108
|
? deps.toolApproval.runWithContext({
|
|
1046
1109
|
taskId,
|
|
1047
1110
|
owner: principal ?? null,
|
|
1048
|
-
emit: approvalEmit,
|
|
1049
1111
|
abortSignal: cancelCtrl.signal,
|
|
1050
1112
|
sessionId,
|
|
1051
1113
|
emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
|
|
@@ -1181,7 +1243,7 @@ export function createResumeLegs(ctx) {
|
|
|
1181
1243
|
const RESUMABLE_GATE_KIND = "resource_limit";
|
|
1182
1244
|
const preemptBinding = { gateKinds: [RESUMABLE_GATE_KIND] };
|
|
1183
1245
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, preemptBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
1184
|
-
const noSuspension = { status: 404, body: {
|
|
1246
|
+
const noSuspension = { status: 404, body: refusalBody({ errorCode: "not_found.suspension", message: "no resumable suspension for this task (already running, resumed, or expired)" }) };
|
|
1185
1247
|
if (!token)
|
|
1186
1248
|
return noSuspension;
|
|
1187
1249
|
if (decider !== undefined && !decider.explicitOperator) {
|
|
@@ -1204,12 +1266,12 @@ export function createResumeLegs(ctx) {
|
|
|
1204
1266
|
deps.logger?.warn?.("assistant_resume_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
|
|
1205
1267
|
return noSuspension;
|
|
1206
1268
|
}
|
|
1207
|
-
return { status: 409, body: {
|
|
1269
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
1208
1270
|
}
|
|
1209
1271
|
if (!cp) {
|
|
1210
1272
|
if (!(await mayDiscloseRowError(cs, token, decider)))
|
|
1211
1273
|
return noSuspension;
|
|
1212
|
-
return { status: 404, body: {
|
|
1274
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
|
|
1213
1275
|
}
|
|
1214
1276
|
if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
|
|
1215
1277
|
deps.logger?.warn?.("assistant_resume_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
|
|
@@ -1217,13 +1279,15 @@ export function createResumeLegs(ctx) {
|
|
|
1217
1279
|
}
|
|
1218
1280
|
const gateKind = cp.gate?.kind;
|
|
1219
1281
|
if (gateKind !== RESUMABLE_GATE_KIND) {
|
|
1220
|
-
return { status: 409, body: {
|
|
1282
|
+
return { status: 409, body: refusalBody({ errorCode: "gate_not_resumable", message: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a resumable resource/preempt suspension — an approval gate must be decided via POST /v1/approvals/:id/decide` }) };
|
|
1221
1283
|
}
|
|
1222
|
-
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1284
|
+
const ctxRead = await readResumeCtx(cs.getCtx(sessionId), ctxOpening(sessionId));
|
|
1223
1285
|
if (ctxRead.kind === "corrupt")
|
|
1224
1286
|
return resumeCtxCorruptAnswer(ctxRead.where);
|
|
1287
|
+
if (ctxRead.kind === "unsealable")
|
|
1288
|
+
return resumeCtxUnsealableAnswer(ctxRead);
|
|
1225
1289
|
if (ctxRead.kind === "absent")
|
|
1226
|
-
return { status: 409, body: {
|
|
1290
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1227
1291
|
const ctx = ctxRead.ctx;
|
|
1228
1292
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1229
1293
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
@@ -1234,24 +1298,28 @@ export function createResumeLegs(ctx) {
|
|
|
1234
1298
|
const verifyRounds = verifyRoundsFromBody(ctx.body);
|
|
1235
1299
|
return driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome: { gate: "resource_limit", decision: "continue" }, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
|
|
1236
1300
|
}
|
|
1237
|
-
async function startFreshHostTurn(sessionId, objective, principal, req,
|
|
1301
|
+
async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId) {
|
|
1238
1302
|
const cs = deps.checkpointStore;
|
|
1239
1303
|
if (!deps.runStore) {
|
|
1240
|
-
return { status: 501, body: {
|
|
1304
|
+
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
1241
1305
|
}
|
|
1242
|
-
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1306
|
+
const ctxRead = await readResumeCtx(cs.getCtx(sessionId), ctxOpening(sessionId));
|
|
1243
1307
|
if (ctxRead.kind === "corrupt")
|
|
1244
1308
|
return resumeCtxCorruptAnswer(ctxRead.where);
|
|
1309
|
+
if (ctxRead.kind === "unsealable")
|
|
1310
|
+
return resumeCtxUnsealableAnswer(ctxRead);
|
|
1245
1311
|
const ctx = ctxRead.kind === "ok" ? ctxRead.ctx : null;
|
|
1246
1312
|
if (!ctx) {
|
|
1247
1313
|
return {
|
|
1248
1314
|
status: 409,
|
|
1249
|
-
body: {
|
|
1250
|
-
error: "this approval waited too long to be delivered: the session this run was started from no longer keeps what a new turn would need, because that state is dropped once the session has been idle past this deployment's retention window (REAP_RUN_STALE_SEC — its current value is staleAfterSec below). Nothing was decided and the approval is still pending: resume the run yourself with the runId below, or ask whoever operates this deployment to raise REAP_RUN_STALE_SEC so approvals can sit longer before someone decides them",
|
|
1315
|
+
body: refusalBody({
|
|
1251
1316
|
errorCode: "conflict.resume_context_unavailable",
|
|
1252
|
-
runId,
|
|
1253
|
-
|
|
1254
|
-
|
|
1317
|
+
message: "this approval waited too long to be delivered: the session this run was started from no longer keeps what a new turn would need, because that state is dropped once the session has been idle past this deployment's retention window (REAP_RUN_STALE_SEC — its current value is staleAfterSec below). Nothing was decided and the approval is still pending: resume the run yourself with the runId below, or ask whoever operates this deployment to raise REAP_RUN_STALE_SEC so approvals can sit longer before someone decides them",
|
|
1318
|
+
facts: {
|
|
1319
|
+
runId,
|
|
1320
|
+
staleAfterSec: deps.config.runStaleSec,
|
|
1321
|
+
},
|
|
1322
|
+
}),
|
|
1255
1323
|
};
|
|
1256
1324
|
}
|
|
1257
1325
|
const windowDenial = await usageWindowDenialFor(principal || GLOBAL_USAGE_KEY);
|
|
@@ -1268,7 +1336,7 @@ export function createResumeLegs(ctx) {
|
|
|
1268
1336
|
fleetScope: resumeFleetScope(req, auth),
|
|
1269
1337
|
elicitOwner: principal ?? null,
|
|
1270
1338
|
source: "workflow_parked_decide",
|
|
1271
|
-
|
|
1339
|
+
workflowDecision,
|
|
1272
1340
|
});
|
|
1273
1341
|
if (out.existing) {
|
|
1274
1342
|
throw new Error(`decide host turn: createRun observed an existing row ${out.existing.taskId} for a freshly minted taskId (no idempotency key was sent)`);
|
|
@@ -1278,18 +1346,18 @@ export function createResumeLegs(ctx) {
|
|
|
1278
1346
|
const minted = out.body;
|
|
1279
1347
|
return acceptedResumingReceipt(minted.taskId, minted.sessionId);
|
|
1280
1348
|
}
|
|
1281
|
-
async function resumeWake(sessionId, message, caller, req, acceptEarly,
|
|
1349
|
+
async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor) {
|
|
1282
1350
|
const cs = deps.checkpointStore;
|
|
1283
1351
|
const wakeBinding = { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" };
|
|
1284
1352
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, wakeBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
1285
1353
|
if (!token)
|
|
1286
|
-
return { status: 404, body: {
|
|
1287
|
-
const operator = explicitOperatorOk(caller, deps.config
|
|
1354
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1355
|
+
const operator = explicitOperatorOk(caller, deps.config);
|
|
1288
1356
|
if (!operator) {
|
|
1289
1357
|
const scope = await cs.peekPendingScope(sessionId, wakeBinding).then((s) => (s === undefined ? cs.peekPendingScope(sessionId) : s)).catch(() => undefined);
|
|
1290
1358
|
const decodedScope = scope != null ? decodeCheckpointScope(scope) : undefined;
|
|
1291
1359
|
if (decodedScope !== undefined && decodedScope !== caller) {
|
|
1292
|
-
return { status: 404, body: {
|
|
1360
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1293
1361
|
}
|
|
1294
1362
|
}
|
|
1295
1363
|
let cp;
|
|
@@ -1299,22 +1367,22 @@ export function createResumeLegs(ctx) {
|
|
|
1299
1367
|
catch (e) {
|
|
1300
1368
|
if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
|
|
1301
1369
|
deps.logger?.warn?.("wake_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
|
|
1302
|
-
return { status: 404, body: {
|
|
1370
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1303
1371
|
}
|
|
1304
|
-
return { status: 409, body: {
|
|
1372
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
1305
1373
|
}
|
|
1306
1374
|
if (!cp) {
|
|
1307
1375
|
if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
|
|
1308
|
-
return { status: 404, body: {
|
|
1376
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1309
1377
|
}
|
|
1310
|
-
return { status: 404, body: {
|
|
1378
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
|
|
1311
1379
|
}
|
|
1312
1380
|
if (!operator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== caller) {
|
|
1313
|
-
return { status: 404, body: {
|
|
1381
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1314
1382
|
}
|
|
1315
1383
|
const gateKind = cp.gate?.kind;
|
|
1316
1384
|
if (gateKind !== "task_done") {
|
|
1317
|
-
return { status: 409, body: {
|
|
1385
|
+
return { status: 409, body: refusalBody({ errorCode: "wake.gate_pending", message: `cannot wake: this session is waiting on a pending gate decision (gate "${gateKind ?? "unknown"}") — resolve it through its own decide entry` }) };
|
|
1318
1386
|
}
|
|
1319
1387
|
const trusted = operator;
|
|
1320
1388
|
if (message !== undefined) {
|
|
@@ -1323,16 +1391,18 @@ export function createResumeLegs(ctx) {
|
|
|
1323
1391
|
}
|
|
1324
1392
|
catch (e) {
|
|
1325
1393
|
if (e instanceof CheckpointError && e.code === "steering.invalid_content") {
|
|
1326
|
-
return { status: 422, body: { errorCode: "steering.invalid_content",
|
|
1394
|
+
return { status: 422, body: refusalBody({ errorCode: "steering.invalid_content", message: e.message }) };
|
|
1327
1395
|
}
|
|
1328
1396
|
throw e;
|
|
1329
1397
|
}
|
|
1330
1398
|
}
|
|
1331
|
-
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1399
|
+
const ctxRead = await readResumeCtx(cs.getCtx(sessionId), ctxOpening(sessionId));
|
|
1332
1400
|
if (ctxRead.kind === "corrupt")
|
|
1333
1401
|
return resumeCtxCorruptAnswer(ctxRead.where);
|
|
1402
|
+
if (ctxRead.kind === "unsealable")
|
|
1403
|
+
return resumeCtxUnsealableAnswer(ctxRead);
|
|
1334
1404
|
if (ctxRead.kind === "absent")
|
|
1335
|
-
return { status: 409, body: {
|
|
1405
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1336
1406
|
const ctx = ctxRead.ctx;
|
|
1337
1407
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1338
1408
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
@@ -1352,10 +1422,10 @@ export function createResumeLegs(ctx) {
|
|
|
1352
1422
|
verifyRounds: undefined,
|
|
1353
1423
|
resumeCtx: { sessionId, ctx },
|
|
1354
1424
|
mintFreshRun: { source: "wake" },
|
|
1425
|
+
...(workflowDecision !== undefined ? { workflowDecision } : {}),
|
|
1355
1426
|
...(engineNotices.length > 0 ? { engineNotices } : {}),
|
|
1356
1427
|
...(acceptEarly === true ? { acceptEarly: true } : {}),
|
|
1357
1428
|
...(req !== undefined ? { callerInitiated: true } : {}),
|
|
1358
|
-
...(workflowParkedResume !== undefined ? { workflowParkedResume } : {}),
|
|
1359
1429
|
...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
|
|
1360
1430
|
}));
|
|
1361
1431
|
}
|
|
@@ -1370,7 +1440,7 @@ export function createResumeLegs(ctx) {
|
|
|
1370
1440
|
const PLAN_REVIEW_GATE_KIND = "plan_review";
|
|
1371
1441
|
const planReviewBinding = { gateKinds: [PLAN_REVIEW_GATE_KIND] };
|
|
1372
1442
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, planReviewBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
1373
|
-
const noPending = { status: 404, body: {
|
|
1443
|
+
const noPending = { status: 404, body: refusalBody({ errorCode: "not_found.plan_review", message: "no pending plan_review for this session (already decided or expired)" }) };
|
|
1374
1444
|
if (!token) {
|
|
1375
1445
|
log404("no-pending-checkpoint");
|
|
1376
1446
|
return refuse(noPending);
|
|
@@ -1395,13 +1465,13 @@ export function createResumeLegs(ctx) {
|
|
|
1395
1465
|
deps.logger?.warn?.("plan_review_unreadable_row_withheld", { sessionId, note: "row unreadable and the caller's ownership of THAT token is unproven — folded into the uniform 404" });
|
|
1396
1466
|
return refuse(noPending);
|
|
1397
1467
|
}
|
|
1398
|
-
return refuse({ status: 409, body: {
|
|
1468
|
+
return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) });
|
|
1399
1469
|
}
|
|
1400
1470
|
if (!cp) {
|
|
1401
1471
|
log404("checkpoint-missing");
|
|
1402
1472
|
if (!(await mayDiscloseRowError(cs, token, decider)))
|
|
1403
1473
|
return refuse(noPending);
|
|
1404
|
-
return refuse({ status: 404, body: {
|
|
1474
|
+
return refuse({ status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) });
|
|
1405
1475
|
}
|
|
1406
1476
|
if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
|
|
1407
1477
|
deps.logger?.warn?.("plan_review_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
|
|
@@ -1411,17 +1481,19 @@ export function createResumeLegs(ctx) {
|
|
|
1411
1481
|
if (gateKind !== PLAN_REVIEW_GATE_KIND) {
|
|
1412
1482
|
return refuse({
|
|
1413
1483
|
status: 409,
|
|
1414
|
-
body: {
|
|
1415
|
-
error: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a plan_review — a tool approval must be decided via POST /v1/approvals/:id/decide`,
|
|
1484
|
+
body: refusalBody({
|
|
1416
1485
|
errorCode: "gate_not_plan_review",
|
|
1417
|
-
|
|
1486
|
+
message: `task is suspended on a '${gateKind ?? "unknown"}' gate, not a plan_review — a tool approval must be decided via POST /v1/approvals/:id/decide`,
|
|
1487
|
+
}),
|
|
1418
1488
|
});
|
|
1419
1489
|
}
|
|
1420
|
-
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1490
|
+
const ctxRead = await readResumeCtx(cs.getCtx(sessionId), ctxOpening(sessionId));
|
|
1421
1491
|
if (ctxRead.kind === "corrupt")
|
|
1422
1492
|
return refuse(resumeCtxCorruptAnswer(ctxRead.where));
|
|
1493
|
+
if (ctxRead.kind === "unsealable")
|
|
1494
|
+
return refuse(resumeCtxUnsealableAnswer(ctxRead));
|
|
1423
1495
|
if (ctxRead.kind === "absent")
|
|
1424
|
-
return refuse({ status: 409, body: {
|
|
1496
|
+
return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) });
|
|
1425
1497
|
const replay = storedBodyReplayRefusal(ctxRead.ctx, deps.config);
|
|
1426
1498
|
if (replay !== undefined)
|
|
1427
1499
|
return refuse(refuseStoredBodyReplay(sessionId, replay));
|
|
@@ -1457,7 +1529,7 @@ export function createResumeLegs(ctx) {
|
|
|
1457
1529
|
const routeCtxBase = {
|
|
1458
1530
|
deps,
|
|
1459
1531
|
registry: { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters },
|
|
1460
|
-
helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode,
|
|
1532
|
+
helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk },
|
|
1461
1533
|
local: { images: createImagesLocal(deps), sessions: createSessionsLocal(), sessionSync: createSessionSyncLocal(), capabilities: createCapabilitiesLocal() },
|
|
1462
1534
|
legs: { prepareSpec, finalizeTaskResult, resumeCheckpoint, driveResumeIntoRunLog, resumePreempted, resumeWake, preparePlanReview, drivePlanReview },
|
|
1463
1535
|
};
|