@sema-agent/server 7.131.0 → 7.133.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 +575 -0
- package/README.md +12 -9
- package/README.zh-CN.md +9 -6
- package/USAGE.md +326 -98
- package/deploy/sema-up/README.md +19 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
- package/deploy/sema-up/chart/templates/server.yaml +5 -2
- 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 +31 -13
- 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 +82 -20
- package/dist/approval-card.js +46 -16
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval.d.ts +9 -0
- package/dist/approval.js +24 -3
- package/dist/audit.d.ts +33 -19
- package/dist/audit.js +12 -16
- package/dist/auth-bridge.d.ts +4 -0
- package/dist/auth-bridge.js +10 -11
- package/dist/bake-runner/main.js +3 -2
- package/dist/boot/deferred-sandbox-path-env.js +1 -1
- package/dist/boot/deployment-mods.d.ts +31 -0
- package/dist/boot/deployment-mods.js +26 -0
- package/dist/boot/execution-env.d.ts +1 -1
- package/dist/boot/execution-env.js +27 -12
- package/dist/boot/execution-lane.js +11 -0
- package/dist/boot/leader.js +2 -0
- package/dist/boot/memory-boundary.d.ts +2 -2
- 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.d.ts +17 -0
- package/dist/boot/read-deny-anchor-audit.js +36 -0
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +21 -1
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +74 -123
- package/dist/boot/runner-deps.d.ts +19 -8
- package/dist/boot/runner-deps.js +15 -2
- package/dist/boot/session-faces.d.ts +2 -7
- package/dist/boot/session-faces.js +4 -2
- package/dist/boot/stage-02-web-admin-pull.js +4 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -4
- package/dist/boot/stage-05-execution-env.js +7 -10
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +11 -7
- package/dist/boot/stage-07-capability-layer.d.ts +4 -2
- package/dist/boot/stage-07-capability-layer.js +27 -6
- package/dist/boot/stage-08-reapers.d.ts +4 -2
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +6 -9
- package/dist/boot/stage-10-http-server.d.ts +6 -9
- package/dist/boot/stage-10-http-server.js +7 -9
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +7 -8
- package/dist/boot/web-admin.d.ts +4 -3
- package/dist/boot/web-admin.js +186 -98
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/brain.js +25 -25
- package/dist/breaker-key.d.ts +6 -0
- package/dist/breaker-key.js +29 -0
- package/dist/budget.js +5 -4
- package/dist/caller-text-echo.d.ts +23 -0
- package/dist/caller-text-echo.js +21 -0
- package/dist/capabilities/brand-identity.d.ts +26 -0
- package/dist/capabilities/brand-identity.js +27 -0
- package/dist/capabilities/center-plugins.d.ts +7 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +32 -59
- package/dist/capabilities/center-prompts.js +18 -32
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/engine-capabilities.js +2 -1
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +30 -6
- package/dist/capabilities/hands-lane.js +11 -1
- package/dist/capabilities/mcp-probe.d.ts +48 -3
- package/dist/capabilities/mcp-probe.js +47 -135
- package/dist/capabilities/scenarios.d.ts +95 -45
- package/dist/capabilities/scenarios.js +53 -38
- package/dist/capabilities/skills.d.ts +19 -4
- package/dist/capabilities/skills.js +30 -10
- package/dist/capability-facts.d.ts +34 -4
- package/dist/capability-facts.js +28 -2
- package/dist/center-config-tier.d.ts +69 -0
- package/dist/center-config-tier.js +42 -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 +47 -32
- 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-lkg.d.ts +9 -9
- package/dist/config-lkg.js +20 -4
- package/dist/config-provider.d.ts +9 -2
- package/dist/config-provider.js +18 -6
- package/dist/config-types.d.ts +75 -20
- package/dist/config-types.js +8 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +137 -62
- 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 -22
- package/dist/elicitation.js +3 -2
- package/dist/entry-problems.d.ts +34 -0
- package/dist/entry-problems.js +14 -0
- package/dist/env-name-allowlist-knobs.d.ts +12 -2
- package/dist/env-name-allowlist-knobs.js +5 -4
- package/dist/exec-bounds.d.ts +15 -3
- package/dist/exec-bounds.js +4 -0
- package/dist/execution-lane-caps.d.ts +56 -10
- package/dist/execution-lane-caps.js +50 -0
- package/dist/field-conflict.d.ts +43 -0
- package/dist/field-conflict.js +20 -0
- package/dist/fleet/fleet-bus.d.ts +80 -6
- package/dist/fleet/fleet-bus.js +41 -9
- package/dist/hooks/classic-stations.d.ts +91 -0
- package/dist/hooks/classic-stations.js +27 -0
- package/dist/hooks/command-spawn.d.ts +57 -0
- package/dist/hooks/command-spawn.js +139 -0
- package/dist/hooks/hook-llm.d.ts +8 -5
- package/dist/hooks/hook-llm.js +79 -36
- package/dist/hooks/hook-runner.d.ts +349 -95
- package/dist/hooks/hook-runner.js +855 -568
- 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.d.ts +8 -2
- package/dist/http/admission.js +188 -101
- package/dist/http/approval-sweeps.js +99 -21
- package/dist/http/caller-trust.d.ts +77 -0
- package/dist/http/caller-trust.js +143 -0
- package/dist/http/checkpoint-ctx.d.ts +41 -24
- package/dist/http/checkpoint-ctx.js +23 -16
- package/dist/http/claimed-run.d.ts +14 -0
- package/dist/http/claimed-run.js +15 -0
- package/dist/http/device-placement.d.ts +2 -4
- package/dist/http/device-placement.js +7 -11
- package/dist/http/dispatch.d.ts +0 -4
- package/dist/http/dispatch.js +15 -13
- package/dist/http/durable-run-leg.d.ts +6 -33
- package/dist/http/durable-run-leg.js +36 -10
- package/dist/http/health-self-report.d.ts +7 -5
- package/dist/http/health-self-report.js +4 -18
- package/dist/http/owner-access.d.ts +56 -0
- package/dist/http/owner-access.js +42 -0
- package/dist/http/principal-gate.d.ts +70 -28
- package/dist/http/principal-gate.js +61 -15
- package/dist/http/readiness.d.ts +11 -5
- package/dist/http/readiness.js +10 -3
- package/dist/http/refusal-body.d.ts +21 -0
- package/dist/http/refusal-body.js +4 -0
- package/dist/http/request-guards.d.ts +43 -23
- package/dist/http/request-guards.js +53 -17
- package/dist/http/resume-ctx-read.d.ts +2 -5
- package/dist/http/resume-ctx-read.js +7 -4
- package/dist/http/resume-legs.d.ts +28 -4
- package/dist/http/resume-legs.js +169 -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 -28
- 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/approvals-assistant.js +49 -48
- package/dist/http/routes/background.js +3 -2
- package/dist/http/routes/capabilities.js +21 -5
- 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.js +6 -2
- 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.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 +5 -18
- package/dist/http/routes/runs.d.ts +5 -0
- package/dist/http/routes/runs.js +34 -107
- package/dist/http/routes/session-sync.js +15 -26
- package/dist/http/routes/sessions.js +30 -40
- package/dist/http/routes/shared-memory.js +2 -13
- package/dist/http/routes/side-query.js +8 -7
- package/dist/http/routes/tasks.js +67 -21
- package/dist/http/routes/workflows.d.ts +351 -0
- package/dist/http/routes/workflows.js +153 -119
- package/dist/http/send.d.ts +33 -10
- package/dist/http/send.js +21 -4
- package/dist/http/server.d.ts +27 -18
- package/dist/http/server.js +34 -6
- package/dist/http/stored-body-replay.js +17 -4
- package/dist/http/submit-ctx.d.ts +25 -0
- package/dist/http/submit-ctx.js +40 -0
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +5 -4
- package/dist/http/wire-types.d.ts +11 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -2
- package/dist/lane-home-probe.d.ts +112 -0
- package/dist/lane-home-probe.js +101 -0
- package/dist/leader/endpoint.d.ts +4 -1
- package/dist/leader/endpoint.js +11 -10
- package/dist/limit-refusal.d.ts +59 -0
- package/dist/limit-refusal.js +15 -0
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +32 -19
- package/dist/mcp-assertion-header.js +10 -9
- package/dist/mcp-tool-faces.d.ts +5 -0
- package/dist/mcp-tool-faces.js +6 -4
- package/dist/memory-project-layer.d.ts +37 -0
- package/dist/memory-project-layer.js +46 -0
- package/dist/memory-scope.d.ts +24 -17
- 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-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 +44 -12
- package/dist/model-provider.js +21 -2
- package/dist/model-route-endpoint.d.ts +9 -12
- package/dist/model-route-endpoint.js +8 -11
- 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/cost-taxonomy.d.ts +7 -7
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +18 -4
- package/dist/observability/fail-open.d.ts +76 -14
- package/dist/observability/fail-open.js +76 -14
- package/dist/observability/metrics.js +4 -1
- package/dist/observability/prompt-manifest.d.ts +33 -82
- package/dist/observability/prompt-manifest.js +122 -17
- package/dist/observability/run-terminal.js +14 -6
- package/dist/observability/secret-env-scrub.d.ts +13 -1
- package/dist/observability/secret-env-scrub.js +13 -1
- package/dist/observability/tool-trace.d.ts +16 -21
- package/dist/observability/tool-trace.js +58 -49
- 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 +118 -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 +39 -6
- package/dist/parked-decide.js +98 -51
- package/dist/parked-revive-surface.d.ts +9 -2
- package/dist/parked-revive-surface.js +11 -8
- package/dist/per-task-image.d.ts +7 -1
- package/dist/per-task-image.js +3 -3
- package/dist/permission-rule-list.d.ts +1 -1
- package/dist/permission-rule-list.js +2 -2
- package/dist/plain-string-record.d.ts +25 -0
- package/dist/plain-string-record.js +32 -0
- 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/blob-backend.d.ts +83 -15
- package/dist/plugins/blob-backend.js +68 -15
- 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 +3 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +28 -4
- package/dist/plugins/checkpoint-store-sql.js +59 -28
- 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/k8s-bg-scripts.d.ts +11 -0
- package/dist/plugins/k8s-bg-scripts.js +22 -0
- package/dist/plugins/local-checkpoint-store.d.ts +2 -0
- package/dist/plugins/local-checkpoint-store.js +11 -3
- package/dist/plugins/local-session-store.d.ts +1 -1
- package/dist/plugins/local-session-store.js +1 -1
- package/dist/plugins/local-session-window.d.ts +39 -0
- package/dist/plugins/local-session-window.js +54 -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 +245 -93
- package/dist/plugins/one-time-migrations.js +668 -207
- 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-sql.d.ts +13 -7
- package/dist/plugins/permission-rule-store-sql.js +4 -18
- package/dist/plugins/pg-pool.js +39 -7
- package/dist/plugins/pg-session-storage.d.ts +20 -8
- package/dist/plugins/pg-session-storage.js +34 -21
- package/dist/plugins/remote-env-adb.d.ts +12 -12
- package/dist/plugins/remote-env-adb.js +21 -6
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +1 -1
- package/dist/plugins/remote-env-e2b.js +21 -9
- package/dist/plugins/remote-env-host.d.ts +9 -9
- package/dist/plugins/remote-env-host.js +10 -32
- package/dist/plugins/remote-env-k8s.d.ts +103 -38
- package/dist/plugins/remote-env-k8s.js +288 -95
- package/dist/plugins/remote-env-local-docker.d.ts +27 -13
- package/dist/plugins/remote-env-local-docker.js +82 -11
- package/dist/plugins/remote-env-ssh.d.ts +21 -12
- package/dist/plugins/remote-env-ssh.js +117 -5
- package/dist/plugins/remote-shell.d.ts +0 -1
- package/dist/plugins/remote-shell.js +1 -2
- package/dist/plugins/retention-store-sql.js +6 -2
- package/dist/plugins/run-store-sql.d.ts +16 -14
- package/dist/plugins/run-store-sql.js +22 -2
- package/dist/plugins/schema-consistency.d.ts +54 -0
- package/dist/plugins/schema-consistency.js +52 -0
- 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 +39 -0
- package/dist/plugins/session-epoch-anchor.js +22 -0
- package/dist/plugins/session-store.d.ts +18 -8
- package/dist/plugins/session-store.js +15 -4
- package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
- package/dist/plugins/shared-memory-store-sql.js +5 -16
- package/dist/plugins/sql-driver.d.ts +205 -1
- package/dist/plugins/sql-driver.js +325 -13
- package/dist/plugins/sql-errors.d.ts +64 -0
- package/dist/plugins/sql-errors.js +57 -0
- package/dist/plugins/store-backend.d.ts +36 -1
- package/dist/plugins/store-backend.js +42 -15
- 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 +8 -11
- package/dist/plugins/tidb-session-storage.js +6 -16
- package/dist/plugins/tidb-session-store.d.ts +24 -4
- package/dist/plugins/tidb-session-store.js +29 -5
- 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/prompts-domain-validate.d.ts +17 -61
- package/dist/prompts-domain-validate.js +9 -119
- 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 +9 -2
- package/dist/request-key-closure.js +11 -5
- package/dist/request-value-closure.d.ts +5 -0
- package/dist/request-value-closure.js +11 -3
- package/dist/rule-entry-list.d.ts +38 -11
- package/dist/rule-entry-list.js +106 -30
- 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 +54 -24
- package/dist/runs.d.ts +15 -8
- package/dist/runs.js +35 -1
- 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/security.d.ts +87 -9
- package/dist/security.js +52 -9
- package/dist/server-secret-env.d.ts +44 -18
- package/dist/server-secret-env.js +49 -8
- package/dist/session-error-wire.d.ts +19 -2
- package/dist/session-error-wire.js +5 -1
- package/dist/session-incarnation.d.ts +15 -5
- package/dist/session-incarnation.js +16 -9
- 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 +27 -6
- package/dist/spec-fields.d.ts +28 -16
- package/dist/spec-fields.js +66 -54
- package/dist/sql-engine-posture.d.ts +31 -4
- package/dist/sql-engine-posture.js +36 -1
- package/dist/stored-request-tool-axes-m19.d.ts +19 -4
- package/dist/stored-request-tool-axes-m19.js +30 -2
- package/dist/task-a2a.d.ts +9 -1
- package/dist/task-a2a.js +73 -47
- package/dist/task-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +46 -58
- package/dist/task-mcp.js +82 -107
- package/dist/task-settings.d.ts +95 -108
- package/dist/task-settings.js +77 -98
- package/dist/text-bidi.d.ts +1 -48
- package/dist/text-bidi.js +3 -5
- package/dist/tighten-only-seats.d.ts +150 -0
- package/dist/tighten-only-seats.js +196 -0
- package/dist/tool-approval.d.ts +304 -88
- package/dist/tool-approval.js +623 -239
- package/dist/trace/core-keyset-guard.d.ts +122 -20
- package/dist/trace/engine-notice-wire.d.ts +11 -3
- package/dist/trace/engine-notice-wire.js +8 -1
- package/dist/trace/exit-seat.d.ts +28 -0
- package/dist/trace/exit-seat.js +76 -0
- package/dist/trace/ledger-events.d.ts +6 -3
- package/dist/trace/ledger-sink.js +4 -1
- package/dist/trace/model-segment.js +1 -1
- package/dist/trace/project.d.ts +98 -17
- package/dist/trace/project.js +98 -50
- package/dist/trace/projection-table.d.ts +100 -0
- package/dist/trace/projection-table.js +130 -0
- package/dist/trace/redact.d.ts +54 -0
- package/dist/trace/redact.js +778 -152
- package/dist/trace/self-minted-wire.d.ts +345 -1
- package/dist/trace/self-minted-wire.js +117 -1
- package/dist/trace/wire-projection-faces.d.ts +377 -3
- package/dist/trace/wire-projection-faces.js +164 -1
- 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/candidate-gate.d.ts +6 -6
- package/dist/web-admin-client/candidate-gate.js +3 -3
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/http-client.js +0 -6
- package/dist/web-admin-client/read-face.js +5 -9
- package/dist/web-admin-client/read-warnings.d.ts +13 -0
- package/dist/web-admin-client/read-warnings.js +10 -0
- package/dist/web-admin-client/skills-mcp.d.ts +3 -0
- package/dist/web-admin-client/skills-mcp.js +11 -9
- package/dist/web-admin-client/types.d.ts +2 -3
- package/dist/workspace-cache.d.ts +35 -0
- package/dist/workspace-cache.js +36 -0
- package/dist/workspace-space.d.ts +3 -2
- package/dist/workspace-space.js +2 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +65 -10
- package/package.json +8 -4
- package/scripts/drain-check-local.mjs +34 -1
- package/scripts/drain-check.mysql.sql +30 -2
- package/scripts/drain-check.pg.sql +29 -2
- package/dist/capabilities/memory-notice.d.ts +0 -138
- package/dist/capabilities/memory-notice.js +0 -64
- package/dist/capabilities/prompt.d.ts +0 -17
- package/dist/capabilities/prompt.js +0 -6
- package/dist/mcp-content-origin.d.ts +0 -34
- package/dist/mcp-content-origin.js +0 -10
- package/dist/tool-axis-words.d.ts +0 -27
- package/dist/tool-axis-words.js +0 -9
- package/dist/wire-name-bound.d.ts +0 -14
- package/dist/wire-name-bound.js +0 -13
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,6 +11,7 @@ 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";
|
|
@@ -22,7 +25,7 @@ import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
|
22
25
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
23
26
|
import { withModelReachScope } from "../model-reach-scope.js";
|
|
24
27
|
import { createInstructionSelectionLeg } from "../instruction-selection.js";
|
|
25
|
-
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
28
|
+
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
26
29
|
import { redactSecrets } from "../trace/redact.js";
|
|
27
30
|
import { buildRunObjectivePreview } from "../user-authored-text.js";
|
|
28
31
|
import { createForwardAppendFailedNote, createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
@@ -32,7 +35,7 @@ import { errorText } from "../observability/err-text.js";
|
|
|
32
35
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
33
36
|
import { verifyRoundsFromBody } from "./verify-rounds.js";
|
|
34
37
|
import { planReviewDecisionExitsPlan, storedBodyPermissionMode } from "../task-settings.js";
|
|
35
|
-
import { acceptedResumingReceipt, buildHostLegOutcome } from "./route-ctx.js";
|
|
38
|
+
import { acceptedResumingReceipt, acceptedTaskIdOf, buildHostLegOutcome } from "./route-ctx.js";
|
|
36
39
|
import { mintAndDriveDurableRun } from "./durable-run-leg.js";
|
|
37
40
|
import { createSessionsLocal } from "./routes/sessions.js";
|
|
38
41
|
import { createSessionSyncLocal } from "./routes/session-sync.js";
|
|
@@ -45,10 +48,12 @@ import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
|
45
48
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
46
49
|
import { deploymentListAsks } from "../approval.js";
|
|
47
50
|
import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
|
|
48
|
-
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
51
|
+
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
52
|
+
import { refusalBody } from "./refusal-body.js";
|
|
49
53
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
50
|
-
import {
|
|
54
|
+
import { httpErrorRefusal } from "./send.js";
|
|
51
55
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
56
|
+
import { fieldConflictFacts } from "../field-conflict.js";
|
|
52
57
|
function legMayHaveReopenedCheckpoint(r) {
|
|
53
58
|
return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
|
|
54
59
|
}
|
|
@@ -77,6 +82,15 @@ const WORKER_SWAP_REDEEMABLE = {
|
|
|
77
82
|
installation_record_absent: true,
|
|
78
83
|
installation_record_misaligned: false,
|
|
79
84
|
installation_record_invalid: false,
|
|
85
|
+
unknown_name: true,
|
|
86
|
+
unsupported_pattern: true,
|
|
87
|
+
not_a_list: false,
|
|
88
|
+
not_a_string: false,
|
|
89
|
+
invalid_record: false,
|
|
90
|
+
renamed_mount: true,
|
|
91
|
+
registry_occupied: true,
|
|
92
|
+
unknown_model_ref: true,
|
|
93
|
+
not_a_model_ref: true,
|
|
80
94
|
};
|
|
81
95
|
function checkpointRowRedeemableElsewhere(reason) {
|
|
82
96
|
return reason !== undefined && WORKER_SWAP_REDEEMABLE[reason] === true;
|
|
@@ -136,6 +150,7 @@ const CHECKPOINT_ERROR_FIELD_ROLE = {
|
|
|
136
150
|
hostDecision: "outcome_cell",
|
|
137
151
|
approver: "outcome_cell",
|
|
138
152
|
taskConfig: "config",
|
|
153
|
+
deps: "config",
|
|
139
154
|
inheritedGate: "config",
|
|
140
155
|
};
|
|
141
156
|
const FIELD_ROLE_CAUSE = {
|
|
@@ -159,7 +174,28 @@ function decisionRefusalCauseOf(e, gate) {
|
|
|
159
174
|
return role === undefined ? undefined : FIELD_ROLE_CAUSE[role];
|
|
160
175
|
}
|
|
161
176
|
function notResumableAnswer() {
|
|
162
|
-
return { status: 409, body: {
|
|
177
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.not_resumable", message: "run is not in a resumable (suspended) state (already resumed, decided, or expired)" }) };
|
|
178
|
+
}
|
|
179
|
+
export function checkpointRefusalAnswer(e, at) {
|
|
180
|
+
const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, at.gate) : undefined;
|
|
181
|
+
const detail = e.detail;
|
|
182
|
+
return resumeRefusalWithCard({
|
|
183
|
+
status: e.code === "checkpoint.not_found" ? 404 : 409,
|
|
184
|
+
body: refusalBody({
|
|
185
|
+
message: e.message,
|
|
186
|
+
errorCode: e.code === "checkpoint.invalid_outcome" ? (refusalCause !== undefined ? WIRE_CODE_OF_DECISION_REFUSAL[refusalCause] : UNCLASSIFIED_INVALID_OUTCOME_CODE) : e.code,
|
|
187
|
+
facts: {
|
|
188
|
+
...(at.taskId !== undefined ? { taskId: at.taskId } : {}),
|
|
189
|
+
...(detail?.code !== undefined ? { blockedBy: detail.code } : {}),
|
|
190
|
+
...(detail?.field ? { field: detail.field } : {}),
|
|
191
|
+
...(detail?.reason !== undefined ? { reason: detail.reason } : {}),
|
|
192
|
+
...((e.code === "resume.usage_window_exhausted" || e.code === "resume.preflight_rejected") && typeof detail?.retryAfterMs === "number" && Number.isFinite(detail.retryAfterMs) && detail.retryAfterMs > 0
|
|
193
|
+
? { retryAfterSec: Math.max(1, Math.ceil(detail.retryAfterMs / 1000)) }
|
|
194
|
+
: {}),
|
|
195
|
+
...at.versionKeys,
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
}, at.retriable);
|
|
163
199
|
}
|
|
164
200
|
export function createResumeLegs(ctx) {
|
|
165
201
|
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, isFleetWide, runOwnerOk, sessionOwnerOk, sessionOwnerScope, sessionOwnerScopeForWrite, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
|
|
@@ -180,7 +216,7 @@ export function createResumeLegs(ctx) {
|
|
|
180
216
|
}
|
|
181
217
|
if (!token && !attributable && !seenUnreadable) {
|
|
182
218
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true });
|
|
183
|
-
return { status: 404, body: {
|
|
219
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
184
220
|
}
|
|
185
221
|
let currentPending;
|
|
186
222
|
if (token && decider !== undefined) {
|
|
@@ -193,7 +229,7 @@ export function createResumeLegs(ctx) {
|
|
|
193
229
|
const scopeOk = cur !== null && (decider.explicitOperator || curOwner === undefined || curOwner === decider.principal);
|
|
194
230
|
if (cur && cur.sessionId === sessionId && !scopeOk) {
|
|
195
231
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId, staleTokenEchoed: true, foreignCurrentSuppressed: true });
|
|
196
|
-
return { status: 404, body: {
|
|
232
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
197
233
|
}
|
|
198
234
|
if (cur && cur.status === "pending" && cur.sessionId === sessionId && scopeOk && curPa?.kind === "tool_approval" && curPa.toolCallId) {
|
|
199
235
|
currentPending = {
|
|
@@ -205,22 +241,24 @@ export function createResumeLegs(ctx) {
|
|
|
205
241
|
}
|
|
206
242
|
return {
|
|
207
243
|
status: 409,
|
|
208
|
-
body: {
|
|
209
|
-
error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
244
|
+
body: refusalBody({
|
|
210
245
|
errorCode: "approval_stale",
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
246
|
+
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
247
|
+
facts: {
|
|
248
|
+
...(attributable ? { terminal: "resolved" } : {}),
|
|
249
|
+
...(currentPending ? { currentPending } : {}),
|
|
250
|
+
},
|
|
251
|
+
}),
|
|
214
252
|
};
|
|
215
253
|
}
|
|
216
254
|
if (!token) {
|
|
217
255
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId });
|
|
218
|
-
return { status: 404, body: {
|
|
256
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
219
257
|
}
|
|
220
258
|
const cpPromise = cs.get(token);
|
|
221
259
|
const ctxPromise = cs.getCtx(sessionId);
|
|
222
260
|
void ctxPromise.catch(() => { });
|
|
223
|
-
const noApproval = { status: 404, body: {
|
|
261
|
+
const noApproval = { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
224
262
|
let cp;
|
|
225
263
|
try {
|
|
226
264
|
cp = await cpPromise;
|
|
@@ -231,18 +269,18 @@ export function createResumeLegs(ctx) {
|
|
|
231
269
|
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" });
|
|
232
270
|
return noApproval;
|
|
233
271
|
}
|
|
234
|
-
return { status: 409, body: {
|
|
272
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
235
273
|
}
|
|
236
274
|
if (!cp) {
|
|
237
275
|
if (!(await mayDiscloseRowError(cs, token, decider)))
|
|
238
276
|
return noApproval;
|
|
239
|
-
return { status: 404, body: {
|
|
277
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
|
|
240
278
|
}
|
|
241
279
|
if (decider !== undefined && !decider.explicitOperator) {
|
|
242
280
|
const cpOwner = decodeCheckpointScope(cp.scope);
|
|
243
281
|
if (cpOwner !== undefined && cpOwner !== decider.principal) {
|
|
244
282
|
deps.logger?.warn?.("approval_decide_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
|
|
245
|
-
return { status: 404, body: {
|
|
283
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
246
284
|
}
|
|
247
285
|
}
|
|
248
286
|
const decideGateKind = cp.gate?.kind;
|
|
@@ -255,21 +293,25 @@ export function createResumeLegs(ctx) {
|
|
|
255
293
|
: null;
|
|
256
294
|
return {
|
|
257
295
|
status: 409,
|
|
258
|
-
body: {
|
|
259
|
-
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`,
|
|
296
|
+
body: refusalBody({
|
|
260
297
|
errorCode: "gate_not_tool_approval",
|
|
261
|
-
|
|
262
|
-
|
|
298
|
+
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`,
|
|
299
|
+
facts: {
|
|
300
|
+
...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
|
|
301
|
+
},
|
|
302
|
+
}),
|
|
263
303
|
};
|
|
264
304
|
}
|
|
265
305
|
if (req !== undefined && !binding?.checkpointToken && !binding?.boundCallId) {
|
|
266
306
|
return {
|
|
267
307
|
status: 400,
|
|
268
|
-
body: {
|
|
269
|
-
error: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
|
|
308
|
+
body: refusalBody({
|
|
270
309
|
errorCode: "decide_requires_binding",
|
|
271
|
-
|
|
272
|
-
|
|
310
|
+
message: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
|
|
311
|
+
facts: {
|
|
312
|
+
bindingEnforced: true,
|
|
313
|
+
},
|
|
314
|
+
}),
|
|
273
315
|
};
|
|
274
316
|
}
|
|
275
317
|
const s57RowCallId = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
|
|
@@ -284,11 +326,13 @@ export function createResumeLegs(ctx) {
|
|
|
284
326
|
}
|
|
285
327
|
return {
|
|
286
328
|
status: 409,
|
|
287
|
-
body: {
|
|
288
|
-
error: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
329
|
+
body: refusalBody({
|
|
289
330
|
errorCode: "approval_stale",
|
|
290
|
-
|
|
291
|
-
|
|
331
|
+
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
332
|
+
facts: {
|
|
333
|
+
...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
|
|
334
|
+
},
|
|
335
|
+
}),
|
|
292
336
|
};
|
|
293
337
|
}
|
|
294
338
|
const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
|
|
@@ -375,14 +419,48 @@ export function createResumeLegs(ctx) {
|
|
|
375
419
|
const plan = planWorkflowParkedDecide(origin, parkedReq);
|
|
376
420
|
if (!plan.ok)
|
|
377
421
|
return plan.deny;
|
|
378
|
-
const
|
|
422
|
+
const decisions = deps.workflowParkDecisions;
|
|
423
|
+
const runStore = deps.runStore;
|
|
424
|
+
if (decisions === undefined || runStore === undefined) {
|
|
425
|
+
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
426
|
+
}
|
|
427
|
+
const fingerprint = parkDecisionFingerprint(plan.outcome);
|
|
428
|
+
const liveReceiptOf = async (held) => {
|
|
429
|
+
if (held === null || held.fingerprint !== fingerprint)
|
|
430
|
+
return undefined;
|
|
431
|
+
const run = await runStore.getRun(held.deliveredTaskId);
|
|
432
|
+
return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId) : undefined;
|
|
433
|
+
};
|
|
434
|
+
const replay = await liveReceiptOf(await decisions.get(token));
|
|
435
|
+
if (replay !== undefined)
|
|
436
|
+
return replay;
|
|
437
|
+
let recordFailure;
|
|
438
|
+
const delivery = {
|
|
439
|
+
carried: [{ runId: plan.runId, token, outcome: plan.outcome }],
|
|
440
|
+
commitBeforeLaunch: async (deliveredTaskId) => {
|
|
441
|
+
try {
|
|
442
|
+
const recorded = await decisions.record({ token, runId: plan.runId, hostSessionId: plan.hostSessionId, outcome: plan.outcome, fingerprint, deliveredTaskId, acceptedAtMs: Date.now() });
|
|
443
|
+
if (!recorded)
|
|
444
|
+
throw new Error(DELIVERY_NOT_RECORDED);
|
|
445
|
+
}
|
|
446
|
+
catch (err) {
|
|
447
|
+
recordFailure = { err };
|
|
448
|
+
throw err;
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
};
|
|
379
452
|
const hostToken = await cs
|
|
380
453
|
.findPendingTokenBySession(plan.hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" })
|
|
381
454
|
.catch(() => undefined);
|
|
382
455
|
const admittedPayer = decodeCheckpointScope(cp.scope);
|
|
383
|
-
|
|
384
|
-
? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly,
|
|
385
|
-
: await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req,
|
|
456
|
+
const out = hostToken
|
|
457
|
+
? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, delivery, admittedPayer)
|
|
458
|
+
: await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req, delivery, plan.runId);
|
|
459
|
+
if (recordFailure !== undefined)
|
|
460
|
+
throw recordFailure.err;
|
|
461
|
+
if (acceptedTaskIdOf(out) === undefined)
|
|
462
|
+
return (await liveReceiptOf(await decisions.get(token))) ?? out;
|
|
463
|
+
return out;
|
|
386
464
|
}
|
|
387
465
|
}
|
|
388
466
|
const ctxRead0 = await readResumeCtx(ctxPromise);
|
|
@@ -390,7 +468,7 @@ export function createResumeLegs(ctx) {
|
|
|
390
468
|
return resumeCtxCorruptAnswer(ctxRead0.where);
|
|
391
469
|
const ctx = ctxRead0.kind === "ok" ? ctxRead0.ctx : null;
|
|
392
470
|
if (!ctx)
|
|
393
|
-
return { status: 409, body: {
|
|
471
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
394
472
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
395
473
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
396
474
|
if (!resolved.ok)
|
|
@@ -398,10 +476,10 @@ export function createResumeLegs(ctx) {
|
|
|
398
476
|
const { spec, engineNotices } = resolved;
|
|
399
477
|
const pendingTool = cp.pendingAction.toolName;
|
|
400
478
|
if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
|
|
401
|
-
return { status: 400, body: {
|
|
479
|
+
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"]) } }) };
|
|
402
480
|
}
|
|
403
481
|
if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
|
|
404
|
-
return { status: 400, body: {
|
|
482
|
+
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"]) } }) };
|
|
405
483
|
}
|
|
406
484
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
407
485
|
const pa = cp.pendingAction;
|
|
@@ -422,7 +500,7 @@ export function createResumeLegs(ctx) {
|
|
|
422
500
|
noteLateSettleIfAccepted(driven);
|
|
423
501
|
return driven;
|
|
424
502
|
}
|
|
425
|
-
const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles", "userAttachments"];
|
|
503
|
+
const ONE_SHOT_SUBMIT_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles", "userAttachments", "images"];
|
|
426
504
|
const ONE_SHOT_SUBMIT_STRIP = Object.fromEntries(ONE_SHOT_SUBMIT_KEYS.map((k) => [k, undefined]));
|
|
427
505
|
const resolveResumeSpec = async (ctx, req, auth) => {
|
|
428
506
|
const engineNotices = [];
|
|
@@ -435,7 +513,7 @@ export function createResumeLegs(ctx) {
|
|
|
435
513
|
};
|
|
436
514
|
const refuseStoredBodyReplay = (sessionId, refusal) => {
|
|
437
515
|
deps.logger?.warn?.("resume_refused_stored_body", { sessionId, ...storedBodyReplayLogFields(refusal) });
|
|
438
|
-
return storedBodyReplayAnswer(refusal, serviceVersion());
|
|
516
|
+
return storedBodyReplayAnswer(refusal, serviceVersion(), true);
|
|
439
517
|
};
|
|
440
518
|
const resolveResumeSpecOrBlocked = async (ctx, req, auth) => {
|
|
441
519
|
const replay = storedBodyReplayRefusal(ctx, deps.config);
|
|
@@ -447,7 +525,7 @@ export function createResumeLegs(ctx) {
|
|
|
447
525
|
}
|
|
448
526
|
catch (e) {
|
|
449
527
|
if (e instanceof HttpError) {
|
|
450
|
-
return { ok: false, answer: resumeBlockedByPolicyAnswer(
|
|
528
|
+
return { ok: false, answer: resumeRefusalWithCard(resumeBlockedByPolicyAnswer(httpErrorRefusal(e)), true) };
|
|
451
529
|
}
|
|
452
530
|
throw e;
|
|
453
531
|
}
|
|
@@ -501,6 +579,7 @@ export function createResumeLegs(ctx) {
|
|
|
501
579
|
const ownerLeaseDenial = args.callerInitiated === true && args.ownerLeaseAdmitted !== true ? await ownerFleetLeaseDenied(principal) : undefined;
|
|
502
580
|
if (ownerLeaseDenial)
|
|
503
581
|
return ownerLeaseDenial;
|
|
582
|
+
const heldDecisions = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
|
|
504
583
|
let taskId = await deps.runStore?.getActiveTaskId(sessionId);
|
|
505
584
|
let freshRunClaimed = false;
|
|
506
585
|
if (args.mintFreshRun && taskId && deps.runStore) {
|
|
@@ -622,6 +701,14 @@ export function createResumeLegs(ctx) {
|
|
|
622
701
|
deps.modelUsage?.register(taskId);
|
|
623
702
|
resumeManifestLease = deps.promptManifests?.register(taskId) ?? 0;
|
|
624
703
|
}
|
|
704
|
+
if (args.workflowDecision !== undefined && taskId === undefined) {
|
|
705
|
+
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");
|
|
706
|
+
}
|
|
707
|
+
const workflowDecision = args.workflowDecision;
|
|
708
|
+
const launchInternals = {
|
|
709
|
+
...heldDecisions,
|
|
710
|
+
...(workflowDecision !== undefined && taskId !== undefined ? { afterCheckpointResolve: () => workflowDecision.commitBeforeLaunch(taskId) } : {}),
|
|
711
|
+
};
|
|
625
712
|
commitSessionInheritEnv(deps, sessionId, resumeCtx.ctx.body);
|
|
626
713
|
if (args.onResumeCommitted)
|
|
627
714
|
await args.onResumeCommitted();
|
|
@@ -657,6 +744,7 @@ export function createResumeLegs(ctx) {
|
|
|
657
744
|
cancelLedgerAppend = vAppend;
|
|
658
745
|
const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
659
746
|
const verifyInternals = {
|
|
747
|
+
...launchInternals,
|
|
660
748
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
661
749
|
onForwardEvent: (e) => {
|
|
662
750
|
fleetPub?.onForwardEvent(e);
|
|
@@ -785,7 +873,7 @@ export function createResumeLegs(ctx) {
|
|
|
785
873
|
resumeLegLive = true;
|
|
786
874
|
const tailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
787
875
|
const stream = await legRunner.resumeStream(token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
|
|
788
|
-
...
|
|
876
|
+
...launchInternals,
|
|
789
877
|
onForwardEvent: (e) => {
|
|
790
878
|
fleetPub?.onForwardEvent(e);
|
|
791
879
|
{
|
|
@@ -909,6 +997,10 @@ export function createResumeLegs(ctx) {
|
|
|
909
997
|
await flush();
|
|
910
998
|
await append("compaction_outcome", compactionOutcomeEventData(ev));
|
|
911
999
|
break;
|
|
1000
|
+
case "compaction_started":
|
|
1001
|
+
await flush();
|
|
1002
|
+
await append("compaction_started", compactionStartedEventData(ev));
|
|
1003
|
+
break;
|
|
912
1004
|
case "workspace_changed":
|
|
913
1005
|
await flush();
|
|
914
1006
|
await append("workspace_changed", workspaceChangedEventData(ev));
|
|
@@ -1009,7 +1101,6 @@ export function createResumeLegs(ctx) {
|
|
|
1009
1101
|
? deps.toolApproval.runWithContext({
|
|
1010
1102
|
taskId,
|
|
1011
1103
|
owner: principal ?? null,
|
|
1012
|
-
emit: approvalEmit,
|
|
1013
1104
|
abortSignal: cancelCtrl.signal,
|
|
1014
1105
|
sessionId,
|
|
1015
1106
|
emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
|
|
@@ -1028,7 +1119,6 @@ export function createResumeLegs(ctx) {
|
|
|
1028
1119
|
}
|
|
1029
1120
|
catch (e) {
|
|
1030
1121
|
if (e instanceof CheckpointError) {
|
|
1031
|
-
const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, outcome.gate) : undefined;
|
|
1032
1122
|
const disposition = resumeRefusalDisposition(e);
|
|
1033
1123
|
if (disposition === "cancelled") {
|
|
1034
1124
|
await settleCardForCancel();
|
|
@@ -1061,21 +1151,7 @@ export function createResumeLegs(ctx) {
|
|
|
1061
1151
|
settleFleet("failed");
|
|
1062
1152
|
}
|
|
1063
1153
|
}
|
|
1064
|
-
|
|
1065
|
-
return {
|
|
1066
|
-
status,
|
|
1067
|
-
body: {
|
|
1068
|
-
...(taskId !== undefined ? { taskId } : {}),
|
|
1069
|
-
error: e.message,
|
|
1070
|
-
errorCode: e.code === "checkpoint.invalid_outcome" ? (refusalCause !== undefined ? WIRE_CODE_OF_DECISION_REFUSAL[refusalCause] : UNCLASSIFIED_INVALID_OUTCOME_CODE) : e.code,
|
|
1071
|
-
...(e.detail?.field ? { field: e.detail.field } : {}),
|
|
1072
|
-
...((e.code === "resume.usage_window_exhausted" || e.code === "resume.preflight_rejected") && typeof e.detail?.retryAfterMs === "number" && Number.isFinite(e.detail.retryAfterMs) && e.detail.retryAfterMs > 0
|
|
1073
|
-
? { retryAfterSec: Math.max(1, Math.ceil(e.detail.retryAfterMs / 1000)) }
|
|
1074
|
-
: {}),
|
|
1075
|
-
...(retriable ? { retriable: true } : {}),
|
|
1076
|
-
...(e.code === "checkpoint.unsupported_version" ? buildCheckpointVersionKeys(resumeCtx.ctx.writerVersion, serviceVersion()) : {}),
|
|
1077
|
-
},
|
|
1078
|
-
};
|
|
1154
|
+
return checkpointRefusalAnswer(e, { gate: outcome.gate, retriable, ...(taskId !== undefined ? { taskId } : {}), versionKeys: e.code === "checkpoint.unsupported_version" ? buildCheckpointVersionKeys(resumeCtx.ctx.writerVersion, serviceVersion()) : {} });
|
|
1079
1155
|
}
|
|
1080
1156
|
const thrownCause = cancelCtrl.cause();
|
|
1081
1157
|
if (thrownCause !== undefined) {
|
|
@@ -1160,7 +1236,7 @@ export function createResumeLegs(ctx) {
|
|
|
1160
1236
|
const RESUMABLE_GATE_KIND = "resource_limit";
|
|
1161
1237
|
const preemptBinding = { gateKinds: [RESUMABLE_GATE_KIND] };
|
|
1162
1238
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, preemptBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
1163
|
-
const noSuspension = { status: 404, body: {
|
|
1239
|
+
const noSuspension = { status: 404, body: refusalBody({ errorCode: "not_found.suspension", message: "no resumable suspension for this task (already running, resumed, or expired)" }) };
|
|
1164
1240
|
if (!token)
|
|
1165
1241
|
return noSuspension;
|
|
1166
1242
|
if (decider !== undefined && !decider.explicitOperator) {
|
|
@@ -1183,12 +1259,12 @@ export function createResumeLegs(ctx) {
|
|
|
1183
1259
|
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" });
|
|
1184
1260
|
return noSuspension;
|
|
1185
1261
|
}
|
|
1186
|
-
return { status: 409, body: {
|
|
1262
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
1187
1263
|
}
|
|
1188
1264
|
if (!cp) {
|
|
1189
1265
|
if (!(await mayDiscloseRowError(cs, token, decider)))
|
|
1190
1266
|
return noSuspension;
|
|
1191
|
-
return { status: 404, body: {
|
|
1267
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
|
|
1192
1268
|
}
|
|
1193
1269
|
if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
|
|
1194
1270
|
deps.logger?.warn?.("assistant_resume_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
|
|
@@ -1196,13 +1272,13 @@ export function createResumeLegs(ctx) {
|
|
|
1196
1272
|
}
|
|
1197
1273
|
const gateKind = cp.gate?.kind;
|
|
1198
1274
|
if (gateKind !== RESUMABLE_GATE_KIND) {
|
|
1199
|
-
return { status: 409, body: {
|
|
1275
|
+
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` }) };
|
|
1200
1276
|
}
|
|
1201
1277
|
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1202
1278
|
if (ctxRead.kind === "corrupt")
|
|
1203
1279
|
return resumeCtxCorruptAnswer(ctxRead.where);
|
|
1204
1280
|
if (ctxRead.kind === "absent")
|
|
1205
|
-
return { status: 409, body: {
|
|
1281
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1206
1282
|
const ctx = ctxRead.ctx;
|
|
1207
1283
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1208
1284
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
@@ -1213,10 +1289,10 @@ export function createResumeLegs(ctx) {
|
|
|
1213
1289
|
const verifyRounds = verifyRoundsFromBody(ctx.body);
|
|
1214
1290
|
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 } : {}) });
|
|
1215
1291
|
}
|
|
1216
|
-
async function startFreshHostTurn(sessionId, objective, principal, req,
|
|
1292
|
+
async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId) {
|
|
1217
1293
|
const cs = deps.checkpointStore;
|
|
1218
1294
|
if (!deps.runStore) {
|
|
1219
|
-
return { status: 501, body: {
|
|
1295
|
+
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
1220
1296
|
}
|
|
1221
1297
|
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1222
1298
|
if (ctxRead.kind === "corrupt")
|
|
@@ -1225,12 +1301,14 @@ export function createResumeLegs(ctx) {
|
|
|
1225
1301
|
if (!ctx) {
|
|
1226
1302
|
return {
|
|
1227
1303
|
status: 409,
|
|
1228
|
-
body: {
|
|
1229
|
-
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",
|
|
1304
|
+
body: refusalBody({
|
|
1230
1305
|
errorCode: "conflict.resume_context_unavailable",
|
|
1231
|
-
runId,
|
|
1232
|
-
|
|
1233
|
-
|
|
1306
|
+
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",
|
|
1307
|
+
facts: {
|
|
1308
|
+
runId,
|
|
1309
|
+
staleAfterSec: deps.config.runStaleSec,
|
|
1310
|
+
},
|
|
1311
|
+
}),
|
|
1234
1312
|
};
|
|
1235
1313
|
}
|
|
1236
1314
|
const windowDenial = await usageWindowDenialFor(principal || GLOBAL_USAGE_KEY);
|
|
@@ -1247,7 +1325,7 @@ export function createResumeLegs(ctx) {
|
|
|
1247
1325
|
fleetScope: resumeFleetScope(req, auth),
|
|
1248
1326
|
elicitOwner: principal ?? null,
|
|
1249
1327
|
source: "workflow_parked_decide",
|
|
1250
|
-
|
|
1328
|
+
workflowDecision,
|
|
1251
1329
|
});
|
|
1252
1330
|
if (out.existing) {
|
|
1253
1331
|
throw new Error(`decide host turn: createRun observed an existing row ${out.existing.taskId} for a freshly minted taskId (no idempotency key was sent)`);
|
|
@@ -1257,18 +1335,18 @@ export function createResumeLegs(ctx) {
|
|
|
1257
1335
|
const minted = out.body;
|
|
1258
1336
|
return acceptedResumingReceipt(minted.taskId, minted.sessionId);
|
|
1259
1337
|
}
|
|
1260
|
-
async function resumeWake(sessionId, message, caller, req, acceptEarly,
|
|
1338
|
+
async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor) {
|
|
1261
1339
|
const cs = deps.checkpointStore;
|
|
1262
1340
|
const wakeBinding = { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" };
|
|
1263
1341
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, wakeBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
1264
1342
|
if (!token)
|
|
1265
|
-
return { status: 404, body: {
|
|
1266
|
-
const operator = explicitOperatorOk(caller, deps.config
|
|
1343
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1344
|
+
const operator = explicitOperatorOk(caller, deps.config);
|
|
1267
1345
|
if (!operator) {
|
|
1268
1346
|
const scope = await cs.peekPendingScope(sessionId, wakeBinding).then((s) => (s === undefined ? cs.peekPendingScope(sessionId) : s)).catch(() => undefined);
|
|
1269
1347
|
const decodedScope = scope != null ? decodeCheckpointScope(scope) : undefined;
|
|
1270
1348
|
if (decodedScope !== undefined && decodedScope !== caller) {
|
|
1271
|
-
return { status: 404, body: {
|
|
1349
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1272
1350
|
}
|
|
1273
1351
|
}
|
|
1274
1352
|
let cp;
|
|
@@ -1278,22 +1356,22 @@ export function createResumeLegs(ctx) {
|
|
|
1278
1356
|
catch (e) {
|
|
1279
1357
|
if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
|
|
1280
1358
|
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" });
|
|
1281
|
-
return { status: 404, body: {
|
|
1359
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1282
1360
|
}
|
|
1283
|
-
return { status: 409, body: {
|
|
1361
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
1284
1362
|
}
|
|
1285
1363
|
if (!cp) {
|
|
1286
1364
|
if (!(await mayDiscloseRowError(cs, token, { principal: caller, explicitOperator: operator }))) {
|
|
1287
|
-
return { status: 404, body: {
|
|
1365
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.parked_checkpoint", message: "no parked checkpoint for this session (nothing to wake)" }) };
|
|
1288
1366
|
}
|
|
1289
|
-
return { status: 404, body: {
|
|
1367
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) };
|
|
1290
1368
|
}
|
|
1291
1369
|
if (!operator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== caller) {
|
|
1292
|
-
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)" }) };
|
|
1293
1371
|
}
|
|
1294
1372
|
const gateKind = cp.gate?.kind;
|
|
1295
1373
|
if (gateKind !== "task_done") {
|
|
1296
|
-
return { status: 409, body: {
|
|
1374
|
+
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` }) };
|
|
1297
1375
|
}
|
|
1298
1376
|
const trusted = operator;
|
|
1299
1377
|
if (message !== undefined) {
|
|
@@ -1302,7 +1380,7 @@ export function createResumeLegs(ctx) {
|
|
|
1302
1380
|
}
|
|
1303
1381
|
catch (e) {
|
|
1304
1382
|
if (e instanceof CheckpointError && e.code === "steering.invalid_content") {
|
|
1305
|
-
return { status: 422, body: { errorCode: "steering.invalid_content",
|
|
1383
|
+
return { status: 422, body: refusalBody({ errorCode: "steering.invalid_content", message: e.message }) };
|
|
1306
1384
|
}
|
|
1307
1385
|
throw e;
|
|
1308
1386
|
}
|
|
@@ -1311,7 +1389,7 @@ export function createResumeLegs(ctx) {
|
|
|
1311
1389
|
if (ctxRead.kind === "corrupt")
|
|
1312
1390
|
return resumeCtxCorruptAnswer(ctxRead.where);
|
|
1313
1391
|
if (ctxRead.kind === "absent")
|
|
1314
|
-
return { status: 409, body: {
|
|
1392
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1315
1393
|
const ctx = ctxRead.ctx;
|
|
1316
1394
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1317
1395
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
@@ -1331,10 +1409,10 @@ export function createResumeLegs(ctx) {
|
|
|
1331
1409
|
verifyRounds: undefined,
|
|
1332
1410
|
resumeCtx: { sessionId, ctx },
|
|
1333
1411
|
mintFreshRun: { source: "wake" },
|
|
1412
|
+
...(workflowDecision !== undefined ? { workflowDecision } : {}),
|
|
1334
1413
|
...(engineNotices.length > 0 ? { engineNotices } : {}),
|
|
1335
1414
|
...(acceptEarly === true ? { acceptEarly: true } : {}),
|
|
1336
1415
|
...(req !== undefined ? { callerInitiated: true } : {}),
|
|
1337
|
-
...(workflowParkedResume !== undefined ? { workflowParkedResume } : {}),
|
|
1338
1416
|
...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
|
|
1339
1417
|
}));
|
|
1340
1418
|
}
|
|
@@ -1349,7 +1427,7 @@ export function createResumeLegs(ctx) {
|
|
|
1349
1427
|
const PLAN_REVIEW_GATE_KIND = "plan_review";
|
|
1350
1428
|
const planReviewBinding = { gateKinds: [PLAN_REVIEW_GATE_KIND] };
|
|
1351
1429
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, planReviewBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
1352
|
-
const noPending = { status: 404, body: {
|
|
1430
|
+
const noPending = { status: 404, body: refusalBody({ errorCode: "not_found.plan_review", message: "no pending plan_review for this session (already decided or expired)" }) };
|
|
1353
1431
|
if (!token) {
|
|
1354
1432
|
log404("no-pending-checkpoint");
|
|
1355
1433
|
return refuse(noPending);
|
|
@@ -1374,13 +1452,13 @@ export function createResumeLegs(ctx) {
|
|
|
1374
1452
|
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" });
|
|
1375
1453
|
return refuse(noPending);
|
|
1376
1454
|
}
|
|
1377
|
-
return refuse({ status: 409, body: {
|
|
1455
|
+
return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) });
|
|
1378
1456
|
}
|
|
1379
1457
|
if (!cp) {
|
|
1380
1458
|
log404("checkpoint-missing");
|
|
1381
1459
|
if (!(await mayDiscloseRowError(cs, token, decider)))
|
|
1382
1460
|
return refuse(noPending);
|
|
1383
|
-
return refuse({ status: 404, body: {
|
|
1461
|
+
return refuse({ status: 404, body: refusalBody({ errorCode: "not_found.checkpoint", message: "checkpoint not found" }) });
|
|
1384
1462
|
}
|
|
1385
1463
|
if (decider !== undefined && !decider.explicitOperator && cp.scope !== CHECKPOINT_PUBLIC_SCOPE && cp.scope !== decider.principal) {
|
|
1386
1464
|
deps.logger?.warn?.("plan_review_row_scope_mismatch", { sessionId, note: "loaded pending row is foreign to the verified caller — TOCTOU fold" });
|
|
@@ -1390,17 +1468,17 @@ export function createResumeLegs(ctx) {
|
|
|
1390
1468
|
if (gateKind !== PLAN_REVIEW_GATE_KIND) {
|
|
1391
1469
|
return refuse({
|
|
1392
1470
|
status: 409,
|
|
1393
|
-
body: {
|
|
1394
|
-
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`,
|
|
1471
|
+
body: refusalBody({
|
|
1395
1472
|
errorCode: "gate_not_plan_review",
|
|
1396
|
-
|
|
1473
|
+
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`,
|
|
1474
|
+
}),
|
|
1397
1475
|
});
|
|
1398
1476
|
}
|
|
1399
1477
|
const ctxRead = await readResumeCtx(cs.getCtx(sessionId));
|
|
1400
1478
|
if (ctxRead.kind === "corrupt")
|
|
1401
1479
|
return refuse(resumeCtxCorruptAnswer(ctxRead.where));
|
|
1402
1480
|
if (ctxRead.kind === "absent")
|
|
1403
|
-
return refuse({ status: 409, body: {
|
|
1481
|
+
return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) });
|
|
1404
1482
|
const replay = storedBodyReplayRefusal(ctxRead.ctx, deps.config);
|
|
1405
1483
|
if (replay !== undefined)
|
|
1406
1484
|
return refuse(refuseStoredBodyReplay(sessionId, replay));
|