@sema-agent/server 7.150.0 → 7.151.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 +185 -0
- package/USAGE.md +53 -25
- package/deploy/sema-up/README.md +7 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
- package/deploy/sema-up/chart/templates/server.yaml +5 -3
- package/deploy/sema-up/chart/values-full-single.yaml +3 -1
- package/deploy/sema-up/chart/values.yaml +8 -1
- package/deploy/sema-up/cluster-up.sh +1 -0
- package/deploy/sema-up/kube-up.sh +1 -1
- package/deploy/sema-up/lib/flagshape.sh +1 -1
- package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
- package/deploy/sema-up/lib/helm-values-kube.py +2 -0
- package/dist/adoption/import.js +3 -3
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +2 -2
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/auth-bridge.d.ts +20 -0
- package/dist/auth-bridge.js +9 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/bake-runner/main.d.ts +4 -2
- package/dist/bind-host.d.ts +2 -3
- package/dist/bind-host.js +2 -4
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/engine-lease.d.ts +5 -0
- package/dist/boot/engine-lease.js +1 -0
- package/dist/boot/execution-env.d.ts +8 -14
- package/dist/boot/execution-env.js +54 -17
- package/dist/boot/execution-lane.js +32 -5
- package/dist/boot/late-binds.d.ts +8 -4
- package/dist/boot/late-binds.js +7 -1
- package/dist/boot/memory-consolidation.d.ts +3 -2
- package/dist/boot/memory-consolidation.js +6 -6
- package/dist/boot/reapers.d.ts +5 -0
- package/dist/boot/reapers.js +12 -1
- package/dist/boot/resolve-spec.d.ts +3 -3
- package/dist/boot/resolve-spec.js +31 -22
- package/dist/boot/runner-deps.d.ts +9 -2
- package/dist/boot/runner-deps.js +3 -3
- package/dist/boot/session-age-out.d.ts +36 -0
- package/dist/boot/session-age-out.js +101 -0
- package/dist/boot/session-faces.d.ts +5 -14
- package/dist/boot/session-faces.js +9 -5
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-05-execution-env.d.ts +5 -3
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +7 -4
- package/dist/boot/stage-06-runners.js +15 -6
- package/dist/boot/stage-07-capability-layer.d.ts +7 -4
- package/dist/boot/stage-07-capability-layer.js +2 -1
- package/dist/boot/stage-08-reapers.d.ts +7 -4
- package/dist/boot/stage-08-reapers.js +4 -2
- package/dist/boot/stage-09-leader.d.ts +11 -5
- package/dist/boot/stage-09-leader.js +12 -3
- package/dist/boot/stage-10-http-server.d.ts +11 -5
- package/dist/boot/stage-10-http-server.js +3 -6
- package/dist/boot/web-admin.js +3 -1
- package/dist/boot/workflow-orchestration.d.ts +7 -2
- package/dist/boot/workflow-orchestration.js +24 -7
- package/dist/bounded-rotation.d.ts +29 -0
- package/dist/bounded-rotation.js +24 -0
- package/dist/capability-facts.d.ts +22 -13
- package/dist/capability-facts.js +28 -18
- package/dist/center-credential.d.ts +25 -6
- package/dist/center-credential.js +25 -5
- package/dist/config-catalog.d.ts +26 -7
- package/dist/config-catalog.js +14 -11
- package/dist/config-invariants.d.ts +2 -1
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +10 -4
- package/dist/config.d.ts +51 -2
- package/dist/config.js +35 -24
- package/dist/deployment-shape.d.ts +9 -1
- package/dist/deployment-shape.js +2 -1
- package/dist/device-protocol.d.ts +5 -4
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.js +3 -2
- package/dist/device-ws-protocol.d.ts +63 -10
- package/dist/device-ws-protocol.js +56 -25
- package/dist/engine-identity.d.ts +19 -7
- package/dist/engine-identity.js +10 -2
- package/dist/entry-mode-clamp-notice.d.ts +2 -1
- package/dist/execution-env-refusal.d.ts +23 -0
- package/dist/execution-env-refusal.js +5 -0
- package/dist/execution-lane-caps.d.ts +3 -1
- package/dist/field-conflict.d.ts +1 -1
- package/dist/file-history-capture.d.ts +11 -4
- package/dist/file-history-capture.js +9 -6
- package/dist/file-history-disclosure.d.ts +7 -5
- package/dist/file-history-disclosure.js +7 -5
- package/dist/fleet/drain-entry.d.ts +4 -0
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/host-lsp-manager.d.ts +2 -2
- package/dist/http/admission.js +17 -2
- package/dist/http/approval-sweeps.js +5 -5
- package/dist/http/checkpoint-ctx.d.ts +2 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/dispatch.d.ts +2 -1
- package/dist/http/dispatch.js +34 -27
- package/dist/http/durable-run-leg.js +16 -16
- package/dist/http/health-self-report.d.ts +9 -1
- package/dist/http/health-self-report.js +2 -0
- package/dist/http/owner-access.d.ts +31 -11
- package/dist/http/owner-access.js +37 -6
- package/dist/http/resume-ctx-read.js +25 -3
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +259 -79
- package/dist/http/route-ctx.d.ts +18 -8
- package/dist/http/route-table.d.ts +1 -1
- package/dist/http/routes/admin-memory-optout.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +4 -3
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/capabilities.js +8 -5
- package/dist/http/routes/fleet.js +10 -8
- package/dist/http/routes/projects.d.ts +3 -2
- package/dist/http/routes/projects.js +4 -5
- package/dist/http/routes/runs.js +102 -36
- package/dist/http/routes/session-sync.d.ts +4 -3
- package/dist/http/routes/session-sync.js +172 -77
- package/dist/http/routes/sessions-list.js +13 -7
- package/dist/http/routes/sessions.js +10 -6
- package/dist/http/routes/tasks.js +30 -16
- package/dist/http/routes/trace-usage.js +2 -2
- package/dist/http/server.d.ts +21 -27
- package/dist/http/server.js +2 -2
- package/dist/http/submit-ctx.js +5 -4
- package/dist/http/wire-types.d.ts +14 -4
- package/dist/identity-source.d.ts +20 -3
- package/dist/identity-source.js +14 -0
- package/dist/index.d.ts +4 -0
- package/dist/lane-home-probe.d.ts +3 -7
- package/dist/lane-home-probe.js +0 -1
- package/dist/leader/leader.d.ts +2 -1
- package/dist/leg-facts.d.ts +13 -5
- package/dist/leg-facts.js +2 -1
- package/dist/memory-scope.d.ts +5 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-sync-client.d.ts +30 -2
- package/dist/memory-sync-client.js +16 -9
- package/dist/observability/err-text.d.ts +7 -0
- package/dist/observability/err-text.js +5 -0
- package/dist/observability/fail-open.d.ts +7 -3
- package/dist/observability/fail-open.js +7 -3
- package/dist/observability/metrics.js +1 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
- package/dist/orchestration/workflow-notify-journal.js +12 -1
- package/dist/orchestration/workflow-park-decision.d.ts +70 -18
- package/dist/orchestration/workflow-park-decision.js +49 -10
- package/dist/orchestration/workflow-park-holder.d.ts +65 -0
- package/dist/orchestration/workflow-park-holder.js +98 -0
- package/dist/orchestration/workflow-record-fence.d.ts +36 -0
- package/dist/orchestration/workflow-record-fence.js +42 -0
- package/dist/owner-faces.d.ts +1 -1
- package/dist/parked-decide.d.ts +104 -53
- package/dist/parked-decide.js +153 -117
- package/dist/peer-directory.d.ts +2 -2
- package/dist/peer-inbound-face.d.ts +2 -6
- package/dist/peer-inbound-face.js +0 -3
- package/dist/permission-mode-admission.d.ts +41 -5
- package/dist/permission-mode-admission.js +29 -3
- package/dist/plugins/adoption-log-sql.d.ts +3 -1
- package/dist/plugins/adoption-log-sql.js +7 -1
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +8 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
- package/dist/plugins/approval-ask-store-memory.js +28 -9
- package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
- package/dist/plugins/approval-ask-store-sql.js +34 -5
- package/dist/plugins/approval-exemption-store.js +3 -0
- package/dist/plugins/approval-nonce-store.js +2 -0
- package/dist/plugins/breaker-state-sql.js +2 -0
- package/dist/plugins/caching-session-store.d.ts +9 -15
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/device-store-sql.js +6 -0
- package/dist/plugins/error-origin.d.ts +18 -0
- package/dist/plugins/error-origin.js +10 -0
- package/dist/plugins/execution-placement-sql.js +6 -6
- package/dist/plugins/image-bake-store-sql.js +6 -0
- package/dist/plugins/image-index-sql.js +4 -0
- package/dist/plugins/landed-attendants.d.ts +212 -0
- package/dist/plugins/landed-attendants.js +161 -0
- package/dist/plugins/leader-run-store-sql.js +4 -0
- package/dist/plugins/local-session-live-reference.d.ts +40 -0
- package/dist/plugins/local-session-live-reference.js +39 -0
- package/dist/plugins/local-session-store.d.ts +71 -33
- package/dist/plugins/local-session-store.js +129 -27
- package/dist/plugins/memory-engine-pg.js +4 -0
- package/dist/plugins/memory-engine-sql-apply.js +5 -2
- package/dist/plugins/memory-engine-tidb.js +4 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
- package/dist/plugins/memory-sync-store-pg.js +5 -0
- package/dist/plugins/memory-sync-store-tidb.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +57 -63
- package/dist/plugins/one-time-migrations.js +162 -90
- package/dist/plugins/org-delivery-quota-store.js +4 -1
- package/dist/plugins/outcome-ledger-sql.js +2 -0
- package/dist/plugins/permission-rule-store-sql.js +11 -4
- package/dist/plugins/pg-cost-quota.js +4 -0
- package/dist/plugins/pg-pool.js +24 -1
- package/dist/plugins/pg-rate-limiter.js +2 -0
- package/dist/plugins/posix-shell-fs.d.ts +6 -1
- package/dist/plugins/posix-shell-fs.js +4 -1
- package/dist/plugins/remote-env-adb.d.ts +19 -15
- package/dist/plugins/remote-env-adb.js +41 -15
- package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
- package/dist/plugins/remote-env-connect-seat.js +72 -0
- package/dist/plugins/remote-env-device.d.ts +22 -5
- package/dist/plugins/remote-env-device.js +106 -8
- package/dist/plugins/remote-env-e2b.d.ts +15 -6
- package/dist/plugins/remote-env-e2b.js +42 -32
- package/dist/plugins/remote-env-file-error.js +6 -2
- package/dist/plugins/remote-env-host.d.ts +17 -0
- package/dist/plugins/remote-env-host.js +30 -38
- package/dist/plugins/remote-env-k8s.d.ts +41 -39
- package/dist/plugins/remote-env-k8s.js +53 -30
- package/dist/plugins/remote-env-local-docker.d.ts +13 -8
- package/dist/plugins/remote-env-local-docker.js +34 -41
- package/dist/plugins/remote-env-ssh.d.ts +18 -12
- package/dist/plugins/remote-env-ssh.js +60 -59
- package/dist/plugins/remote-shell.d.ts +98 -1
- package/dist/plugins/remote-shell.js +218 -0
- package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
- package/dist/plugins/resume-anchor-store-sql.js +12 -5
- package/dist/plugins/retention-lane-store-sql.js +4 -0
- package/dist/plugins/retention-sql.d.ts +20 -2
- package/dist/plugins/retention-sql.js +11 -0
- package/dist/plugins/run-store-sql.d.ts +21 -2
- package/dist/plugins/run-store-sql.js +31 -7
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
- package/dist/plugins/schema-consistency.d.ts +4 -2
- package/dist/plugins/schema-consistency.js +19 -2
- package/dist/plugins/send-file-ledger.js +3 -0
- package/dist/plugins/serving-requirement.d.ts +26 -7
- package/dist/plugins/serving-requirement.js +11 -4
- package/dist/plugins/session-birth.d.ts +6 -2
- package/dist/plugins/session-capture-record-store-sql.js +2 -0
- package/dist/plugins/session-ext-table.d.ts +14 -1
- package/dist/plugins/session-ext-table.js +5 -1
- package/dist/plugins/session-row-sql.d.ts +49 -2
- package/dist/plugins/session-row-sql.js +56 -13
- package/dist/plugins/session-store-sql.d.ts +34 -18
- package/dist/plugins/session-store-sql.js +98 -17
- package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
- package/dist/plugins/shared-memory-store-sql.js +6 -2
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/sql-driver.js +18 -3
- package/dist/plugins/sql-errors.d.ts +11 -6
- package/dist/plugins/sql-errors.js +5 -6
- package/dist/plugins/stop-then-start-sentence.d.ts +10 -4
- package/dist/plugins/stop-then-start-sentence.js +14 -10
- package/dist/plugins/stop-then-start.d.ts +109 -0
- package/dist/plugins/stop-then-start.js +104 -0
- package/dist/plugins/store-backend.d.ts +40 -13
- package/dist/plugins/store-backend.js +169 -27
- package/dist/plugins/store-boundary.d.ts +57 -0
- package/dist/plugins/store-boundary.js +139 -0
- package/dist/plugins/store-contracts.d.ts +17 -0
- package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
- package/dist/plugins/store-fault-sentence-m53.js +25 -0
- package/dist/plugins/store-fault.d.ts +26 -7
- package/dist/plugins/store-fault.js +18 -2
- package/dist/plugins/task-attachment-store.js +9 -0
- package/dist/plugins/task-list-store-sql.d.ts +2 -2
- package/dist/plugins/task-list-store-sql.js +10 -8
- package/dist/plugins/tidb-cost-quota.js +4 -0
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-rate-limiter.js +4 -0
- package/dist/plugins/user-input-ledger-sql.js +4 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
- package/dist/plugins/workflow-run-store-sql.js +64 -4
- package/dist/plugins/workspace-fill.d.ts +4 -7
- package/dist/plugins/workspace-fill.js +2 -1
- package/dist/plugins/write-behind-counter.js +5 -0
- package/dist/project-context.d.ts +0 -2
- package/dist/project-context.js +4 -8
- package/dist/project-registry.d.ts +46 -0
- package/dist/project-registry.js +44 -0
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +4 -0
- package/dist/request-value-closure.js +10 -0
- package/dist/run-acceptance.d.ts +3 -2
- package/dist/run-acceptance.js +4 -6
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +37 -8
- package/dist/runs.js +53 -12
- package/dist/runtime-governance.d.ts +5 -4
- package/dist/security.d.ts +30 -32
- package/dist/security.js +41 -7
- package/dist/server-secret-env.d.ts +12 -10
- package/dist/server-secret-env.js +24 -15
- package/dist/session-bindings.d.ts +82 -0
- package/dist/session-bindings.js +18 -0
- package/dist/session-children-retire.d.ts +2 -2
- package/dist/session-children-retire.js +2 -6
- package/dist/session-deletion.d.ts +16 -1
- package/dist/session-error-wire.d.ts +3 -3
- package/dist/session-error-wire.js +3 -3
- package/dist/session-incarnation.d.ts +10 -0
- package/dist/session-incarnation.js +10 -1
- package/dist/session-kind.d.ts +57 -0
- package/dist/session-kind.js +22 -0
- package/dist/session-row.d.ts +21 -1
- package/dist/session-row.js +7 -1
- package/dist/session-sync-kernel.d.ts +24 -14
- package/dist/session-sync-kernel.js +3 -6
- package/dist/session-sync.d.ts +4 -98
- package/dist/session-sync.js +1 -71
- package/dist/session-titler.d.ts +10 -3
- package/dist/session-titler.js +19 -6
- package/dist/ssh-host-key.d.ts +5 -0
- package/dist/ssh-host-key.js +3 -0
- package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
- package/dist/stores-sql/file-history-store-sql.js +108 -70
- package/dist/stores-sql/pg-session-storage.d.ts +34 -7
- package/dist/stores-sql/pg-session-storage.js +37 -13
- package/dist/stores-sql/retention-store-sql.d.ts +1 -9
- package/dist/stores-sql/retention-store-sql.js +2 -10
- package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
- package/dist/stores-sql/session-policy-store-sql.js +61 -44
- package/dist/stores-sql/session-write-hooks.d.ts +11 -0
- package/dist/stores-sql/tidb-session-store.d.ts +58 -7
- package/dist/stores-sql/tidb-session-store.js +37 -13
- package/dist/submission-exit.d.ts +3 -2
- package/dist/submission-exit.js +9 -1
- package/dist/task-cwd.d.ts +2 -2
- package/dist/task-settings.d.ts +6 -5
- package/dist/terminal.d.ts +33 -2
- package/dist/terminal.js +139 -9
- package/dist/tool-approval.d.ts +140 -6
- package/dist/tool-approval.js +165 -72
- package/dist/trace/self-minted-wire.d.ts +290 -27
- package/dist/trace/self-minted-wire.js +101 -13
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +78 -27
- package/package.json +2 -2
- package/wire-changes/server@7.151.0.json +762 -0
- package/dist/http/session-inherit-env.d.ts +0 -36
- package/dist/http/session-inherit-env.js +0 -6
package/dist/http/resume-legs.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead } from "../observability/run-terminal.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { uuidv7, resumeWithVerification, resumeCascade, CheckpointError, judgeCheckpointRow, defaultTaskRegistry, taskNotificationDedupKey, validatePendingSteer, GLOBAL_USAGE_KEY } from "@sema-agent/core";
|
|
4
|
-
import { decideParkedAgent,
|
|
4
|
+
import { answerOfHolderFence, answerOfHolderRedeem, decideParkedAgent, deliverableEntryOf, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, holderFenceFaultAnswer, planWorkflowParkedDecide, redeemHere, runChannelEnded, workflowParkGuidance } from "../parked-decide.js";
|
|
5
5
|
import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
|
|
6
|
-
import {
|
|
6
|
+
import { heldParksOf, readRunParks } from "../orchestration/workflow-agent-session-index.js";
|
|
7
|
+
import { isAdoptionQuiescing, trackMaintenanceLeg } from "../adoption/quiesce.js";
|
|
8
|
+
import { runFactsOf, sealRouteDeps, sessionFactsReadOf, sessionOccupantReadOf } from "./owner-access.js";
|
|
7
9
|
import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../session-workspace.js";
|
|
8
10
|
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";
|
|
9
11
|
import { HttpError, checkpointPartsOf, decodeCheckpointScope, scopeCellOf, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
10
12
|
import { keyAdmits } from "../owner-faces.js";
|
|
11
13
|
import { personOf } from "../owner-brands.js";
|
|
12
14
|
import { recoveredSubject } from "../owner-faces.js";
|
|
13
|
-
import { beatRunHeartbeat, graftParkToolCallId, heldParkOf, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort, voidHeldParkCard } from "../runs.js";
|
|
15
|
+
import { beatRunHeartbeat, createRunAnchorWriter, graftParkToolCallId, heldParkOf, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort, voidHeldParkCard } from "../runs.js";
|
|
14
16
|
import { commitParkRow } from "../park-row-commit.js";
|
|
15
17
|
import { atHostBoundary, failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane, outwardThrownMessage } from "../terminal.js";
|
|
16
18
|
import { voidVerifierPauseCard } from "../verifier-pause-card.js";
|
|
@@ -60,7 +62,7 @@ import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflic
|
|
|
60
62
|
import { refusalAfterLostClaim } from "./claim-heartbeat.js";
|
|
61
63
|
import { CONTINUATION_CLAIM_FACE, ClaimFaultAnswer, commitExecutionContext, faultedAnswerOf, isRootSessionGoneError, resolveBeforeClaim, rootSessionAbsentAnswer, stampSpecRoot, withdrawClaim } from "./execution-context.js";
|
|
62
64
|
import { timeClaim } from "./claim-latency.js";
|
|
63
|
-
import {
|
|
65
|
+
import { commitLegBindings } from "../session-bindings.js";
|
|
64
66
|
import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind, isEngineQuestionRow } from "../approval-content-kind.js";
|
|
65
67
|
import { deploymentListAsks } from "../approval.js";
|
|
66
68
|
import { isParkLedgerEventType } from "../trace/ledger-events.js";
|
|
@@ -70,11 +72,18 @@ import { buildCheckpointVersionKeys, resumeAssemblyRefusalAnswer, resumeRefusalW
|
|
|
70
72
|
import { refusalBody, resumeRefusalFacts } from "./refusal-body.js";
|
|
71
73
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
72
74
|
import { restateResumeCtx } from "./resume-restatement.js";
|
|
75
|
+
import { stampReplayEntryMode } from "../permission-mode-admission.js";
|
|
73
76
|
import { httpErrorRefusal } from "./send.js";
|
|
74
77
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
75
78
|
import { createSubmissionSeat } from "../submission-exit.js";
|
|
76
79
|
import { fieldConflictFacts } from "../field-conflict.js";
|
|
77
80
|
import { createSessionNotifyDoor } from "../session-notify-door.js";
|
|
81
|
+
const HANDOFF_RETRY_CODES = new Set(["conflict.session_active_run", "not_found.parked_checkpoint", "conflict.claim_lost"]);
|
|
82
|
+
function handoffResultOf(refusal, refusedWhy) {
|
|
83
|
+
const errorCode = "errorCode" in refusal.body ? refusal.body.errorCode : undefined;
|
|
84
|
+
const code = typeof errorCode === "string" ? errorCode : `status_${refusal.status}`;
|
|
85
|
+
return refusal.status >= 500 || HANDOFF_RETRY_CODES.has(code) ? { kind: "retry", why: code } : { kind: "refused", why: refusedWhy ?? code };
|
|
86
|
+
}
|
|
78
87
|
function compositionUnlinkedAnswer() {
|
|
79
88
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context does not match this card — the run is a cascade but the card records no ladder rung, so the ladder cannot be continued" }) };
|
|
80
89
|
}
|
|
@@ -302,6 +311,13 @@ export function decidedActionOf(binding) {
|
|
|
302
311
|
export function createResumeLegs(ctx) {
|
|
303
312
|
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, admitRun, safeDecode, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
|
|
304
313
|
const ctxOpening = (sessionId) => ({ sessionId, opener: deps.checkpointCtxSealing });
|
|
314
|
+
const replayCtxOf = (stored, restatement) => {
|
|
315
|
+
const restated = restateResumeCtx(stored, restatement);
|
|
316
|
+
if (restated.unattendedLeg === true)
|
|
317
|
+
return restated;
|
|
318
|
+
const body = stampReplayEntryMode(restated.body, deps.config);
|
|
319
|
+
return body === restated.body ? restated : { ...restated, body };
|
|
320
|
+
};
|
|
305
321
|
const resumeFleetScope = (req, auth) => req ? (gatedSubject(req, deps.config) ?? "default") : (auth.subject ?? "default");
|
|
306
322
|
function createRememberIntent(sessionId, remember) {
|
|
307
323
|
let pending;
|
|
@@ -569,73 +585,56 @@ export function createResumeLegs(ctx) {
|
|
|
569
585
|
if (!plan.ok)
|
|
570
586
|
return plan.deny;
|
|
571
587
|
const fingerprint = parkDecisionFingerprint(plan.outcome, restatement);
|
|
572
|
-
const live = await deliverToHoldingRun(origin, { token, outcome: plan.outcome, decision: parkedReq.decision, fingerprint, restated: restatement !== undefined });
|
|
573
|
-
if (live.kind === "answered")
|
|
574
|
-
return live.answer;
|
|
575
588
|
const decisions = deps.workflowParkDecisions;
|
|
576
|
-
|
|
577
|
-
if (decisions === undefined || runStore === undefined) {
|
|
589
|
+
if (decisions === undefined || deps.runStore === undefined) {
|
|
578
590
|
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
579
591
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
return undefined;
|
|
583
|
-
const run = await runFactsOf(runStore, held.deliveredTaskId);
|
|
584
|
-
return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId, NO_SETTLEMENT_EVIDENCE) : undefined;
|
|
585
|
-
};
|
|
586
|
-
const replay = await liveReceiptOf(await decisions.get(token));
|
|
587
|
-
if (replay !== undefined)
|
|
588
|
-
return replay;
|
|
589
|
-
let recordFailure;
|
|
590
|
-
const answerOfRecordFailure = async (failed) => {
|
|
591
|
-
if (failed.kind === "faulted")
|
|
592
|
-
return faultedAnswerOf(failed.err, CONTINUATION_CLAIM_FACE.faulted);
|
|
592
|
+
if (origin.run.status === "running") {
|
|
593
|
+
let verdict;
|
|
593
594
|
try {
|
|
594
|
-
|
|
595
|
+
verdict = await decisions.recordForRunningHolder({ token, runId: plan.runId, hostSessionId: plan.hostSessionId, outcome: plan.outcome, fingerprint, deliveredTaskId: plan.runId, acceptedAtMs: Date.now() }, { restated: restatement !== undefined });
|
|
595
596
|
}
|
|
596
|
-
catch (
|
|
597
|
-
return faultedAnswerOf(
|
|
597
|
+
catch (err) {
|
|
598
|
+
return faultedAnswerOf(err, () => holderFenceFaultAnswer(plan.runId));
|
|
598
599
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
if (!recorded) {
|
|
612
|
-
recordFailure = { kind: "not_recorded", deliveredTaskId };
|
|
613
|
-
throw new Error(DELIVERY_NOT_RECORDED);
|
|
600
|
+
switch (verdict) {
|
|
601
|
+
case "not_running":
|
|
602
|
+
break;
|
|
603
|
+
case "not_held":
|
|
604
|
+
case "restated":
|
|
605
|
+
case "other_decision":
|
|
606
|
+
return answerOfHolderFence(verdict, plan.runId);
|
|
607
|
+
case "stands":
|
|
608
|
+
return answerOfHolderRedeem(await redeemHere(plan.runId, origin.run.scope, { token, outcome: plan.outcome }), plan.runId, parkedReq.decision);
|
|
609
|
+
default: {
|
|
610
|
+
const unhandled = verdict;
|
|
611
|
+
throw new Error(`unhandled workflow holder fence verdict: ${String(unhandled)}`);
|
|
614
612
|
}
|
|
615
|
-
}
|
|
616
|
-
};
|
|
617
|
-
const hostToken = await cs
|
|
618
|
-
.findPendingTokenBySession(plan.hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" })
|
|
619
|
-
.catch(() => undefined);
|
|
620
|
-
const admittedPayer = decodeCheckpointScope(cp.scope);
|
|
621
|
-
let out;
|
|
622
|
-
try {
|
|
623
|
-
out = hostToken
|
|
624
|
-
? await resumeWake(plan.hostSessionId, plan.message, decider?.subject, req, acceptEarly, delivery, admittedPayer, restatement)
|
|
625
|
-
: await startFreshHostTurn(plan.hostSessionId, plan.message.text, admittedPayer, req, delivery, plan.runId, restatement);
|
|
613
|
+
}
|
|
626
614
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
615
|
+
const laned = await deliverViaResumeLane({
|
|
616
|
+
hostSessionId: plan.hostSessionId,
|
|
617
|
+
runId: plan.runId,
|
|
618
|
+
runScope: origin.run.scope,
|
|
619
|
+
token,
|
|
620
|
+
outcome: plan.outcome,
|
|
621
|
+
fingerprint,
|
|
622
|
+
message: plan.message,
|
|
623
|
+
...(restatement !== undefined ? { restatement } : {}),
|
|
624
|
+
initiator: { kind: "decide", req, caller: decider?.subject, ...(acceptEarly === true ? { acceptEarly: true } : {}), admittedPayer: decodeCheckpointScope(cp.scope) },
|
|
625
|
+
});
|
|
626
|
+
switch (laned.kind) {
|
|
627
|
+
case "launched":
|
|
628
|
+
case "replayed":
|
|
629
|
+
case "refused":
|
|
630
|
+
return laned.out;
|
|
631
|
+
case "record_failed":
|
|
632
|
+
return await answerOfRecordFailure(laned.failure, plan.hostSessionId);
|
|
633
|
+
default: {
|
|
634
|
+
const unhandled = laned;
|
|
635
|
+
throw new Error(`unhandled resume lane delivery: ${JSON.stringify(unhandled)}`);
|
|
636
|
+
}
|
|
632
637
|
}
|
|
633
|
-
const failed = recordFailure;
|
|
634
|
-
if (failed !== undefined)
|
|
635
|
-
return await answerOfRecordFailure(failed);
|
|
636
|
-
if (acceptedTaskIdOf(out) === undefined)
|
|
637
|
-
return (await liveReceiptOf(await decisions.get(token))) ?? out;
|
|
638
|
-
return out;
|
|
639
638
|
}
|
|
640
639
|
}
|
|
641
640
|
const runCtx = await ctxPromise;
|
|
@@ -644,7 +643,7 @@ export function createResumeLegs(ctx) {
|
|
|
644
643
|
if (runCtx === undefined || runCtx.ctx === null)
|
|
645
644
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
646
645
|
const { runSessionId, ctx: storedCtx } = runCtx;
|
|
647
|
-
const ctx =
|
|
646
|
+
const ctx = replayCtxOf(storedCtx, restatement);
|
|
648
647
|
const auth = { sessionId: runSessionId, subject: checkpointPartsOf(cp.scope)?.subject, memoryScope: ctx.memoryScope };
|
|
649
648
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
650
649
|
if (!resolved.ok)
|
|
@@ -691,6 +690,7 @@ export function createResumeLegs(ctx) {
|
|
|
691
690
|
onNotice: (n) => engineNotices.push(n),
|
|
692
691
|
...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
|
|
693
692
|
...(ctx.projectContext !== undefined ? { projectContext: ctx.projectContext } : {}),
|
|
693
|
+
...(ctx.memorySeed !== undefined ? { memorySeed: ctx.memorySeed } : {}),
|
|
694
694
|
lease,
|
|
695
695
|
execution,
|
|
696
696
|
});
|
|
@@ -979,7 +979,7 @@ export function createResumeLegs(ctx) {
|
|
|
979
979
|
...heldDecisions,
|
|
980
980
|
...(workflowDecision !== undefined && taskId !== undefined ? { afterCheckpointResolve: () => workflowDecision.commitBeforeLaunch(taskId) } : {}),
|
|
981
981
|
};
|
|
982
|
-
|
|
982
|
+
commitLegBindings(deps, undefined, sessionId, args.legFacts?.sessionBindings);
|
|
983
983
|
if (args.onResumeCommitted)
|
|
984
984
|
await args.onResumeCommitted();
|
|
985
985
|
let unregisterEngineNotice;
|
|
@@ -1036,7 +1036,7 @@ export function createResumeLegs(ctx) {
|
|
|
1036
1036
|
const verifyTailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
1037
1037
|
const verifyInternals = {
|
|
1038
1038
|
...launchInternals,
|
|
1039
|
-
...compositionLegRoot(args.legFacts?.
|
|
1039
|
+
...compositionLegRoot(args.legFacts?.sessionBindings.cwd),
|
|
1040
1040
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
1041
1041
|
onForwardEvent: (e) => {
|
|
1042
1042
|
fleetPub?.onForwardEvent(e);
|
|
@@ -1148,15 +1148,18 @@ export function createResumeLegs(ctx) {
|
|
|
1148
1148
|
const append = (type, data) => (rs && taskId ? serialAppend(type, data) : Promise.resolve());
|
|
1149
1149
|
cancelLedgerAppend = append;
|
|
1150
1150
|
await openEngineNoticePort(append);
|
|
1151
|
-
const anchorOwner = subject ?? null;
|
|
1152
1151
|
const sessionFactsOf = sessionFactsReadOf(deps.sessionStorage);
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
:
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
:
|
|
1152
|
+
const anchors = createRunAnchorWriter({
|
|
1153
|
+
seat: deps.backend?.landedAttendants(),
|
|
1154
|
+
sessionId,
|
|
1155
|
+
owner: subject ?? null,
|
|
1156
|
+
promptKey: taskId && freshRunClaimed ? taskId : undefined,
|
|
1157
|
+
witness: args.legFacts?.sessionWitness,
|
|
1158
|
+
occupantOf: sessionOccupantReadOf(deps.sessionStorage),
|
|
1159
|
+
leafOf: sessionFactsOf === undefined ? undefined : async (id) => (await sessionFactsOf(id))?.leafId,
|
|
1160
|
+
});
|
|
1161
|
+
const captureTurnAnchor = anchors.turn;
|
|
1162
|
+
const captureUserMessageAnchor = anchors.prompt;
|
|
1160
1163
|
const anchor = new TurnAnchorCapture(captureTurnAnchor, "resume", captureUserMessageAnchor);
|
|
1161
1164
|
const flush = async () => {
|
|
1162
1165
|
if (reasoning) {
|
|
@@ -1623,7 +1626,7 @@ export function createResumeLegs(ctx) {
|
|
|
1623
1626
|
return resumeCtxUnreadableAnswer();
|
|
1624
1627
|
if (ctxRead.kind === "absent")
|
|
1625
1628
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1626
|
-
const ctx =
|
|
1629
|
+
const ctx = replayCtxOf(ctxRead.ctx, restatement);
|
|
1627
1630
|
const auth = { sessionId, subject: checkpointPartsOf(cp.scope)?.subject, memoryScope: ctx.memoryScope };
|
|
1628
1631
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1629
1632
|
if (!resolved.ok)
|
|
@@ -1668,7 +1671,7 @@ export function createResumeLegs(ctx) {
|
|
|
1668
1671
|
}),
|
|
1669
1672
|
};
|
|
1670
1673
|
}
|
|
1671
|
-
const ctx =
|
|
1674
|
+
const ctx = replayCtxOf(storedCtx, restatement);
|
|
1672
1675
|
const windowDenial = await usageWindowDenialFor(subject === undefined ? undefined : personOf(recoveredSubject(subject)));
|
|
1673
1676
|
if (windowDenial)
|
|
1674
1677
|
return { status: windowDenial.status, body: windowDenial.body };
|
|
@@ -1757,7 +1760,7 @@ export function createResumeLegs(ctx) {
|
|
|
1757
1760
|
return resumeCtxUnreadableAnswer();
|
|
1758
1761
|
if (ctxRead.kind === "absent")
|
|
1759
1762
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1760
|
-
const ctx =
|
|
1763
|
+
const ctx = replayCtxOf(ctxRead.ctx, restatement);
|
|
1761
1764
|
const auth = { sessionId: cp.sessionId, subject: checkpointPartsOf(cp.scope)?.subject, memoryScope: ctx.memoryScope };
|
|
1762
1765
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1763
1766
|
if (!resolved.ok)
|
|
@@ -1784,7 +1787,7 @@ export function createResumeLegs(ctx) {
|
|
|
1784
1787
|
...(workflowDecision !== undefined ? { workflowDecision } : {}),
|
|
1785
1788
|
...(engineNotices.length > 0 ? { engineNotices } : {}),
|
|
1786
1789
|
...(acceptEarly === true ? { acceptEarly: true } : {}),
|
|
1787
|
-
...(req !== undefined ? { callerInitiated: true } : {}),
|
|
1790
|
+
...(req !== undefined || workflowDecision !== undefined ? { callerInitiated: true } : {}),
|
|
1788
1791
|
...(ownerLeaseAdmittedFor !== undefined && auth.subject === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
|
|
1789
1792
|
});
|
|
1790
1793
|
}
|
|
@@ -1792,6 +1795,183 @@ export function createResumeLegs(ctx) {
|
|
|
1792
1795
|
resolved.peerLease.release();
|
|
1793
1796
|
}
|
|
1794
1797
|
}
|
|
1798
|
+
async function answerOfRecordFailure(failed, hostSessionId) {
|
|
1799
|
+
if (failed.kind === "faulted")
|
|
1800
|
+
return faultedAnswerOf(failed.err, CONTINUATION_CLAIM_FACE.faulted);
|
|
1801
|
+
const runStore = deps.runStore;
|
|
1802
|
+
if (runStore === undefined)
|
|
1803
|
+
throw new Error("workflow park decide: a decision row failed to land without a run store — the claim fence it was written under is the run store");
|
|
1804
|
+
try {
|
|
1805
|
+
return await refusalAfterLostClaim(deps, runStore, hostSessionId, failed.deliveredTaskId);
|
|
1806
|
+
}
|
|
1807
|
+
catch (e) {
|
|
1808
|
+
return faultedAnswerOf(e, CONTINUATION_CLAIM_FACE.faulted);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
async function deliverViaResumeLane(d) {
|
|
1812
|
+
const decisions = deps.workflowParkDecisions;
|
|
1813
|
+
const runStore = deps.runStore;
|
|
1814
|
+
const cs = deps.checkpointStore;
|
|
1815
|
+
if (decisions === undefined || runStore === undefined || cs === undefined) {
|
|
1816
|
+
return { kind: "refused", out: { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) } };
|
|
1817
|
+
}
|
|
1818
|
+
const liveReceiptOf = async (held) => {
|
|
1819
|
+
if (held === null || held.fingerprint !== d.fingerprint)
|
|
1820
|
+
return undefined;
|
|
1821
|
+
const run = await runFactsOf(runStore, held.deliveredTaskId);
|
|
1822
|
+
return run != null && !isTerminalRunStatus(run.status) ? acceptedResumingReceipt(held.deliveredTaskId, held.hostSessionId, NO_SETTLEMENT_EVIDENCE) : undefined;
|
|
1823
|
+
};
|
|
1824
|
+
const replay = await liveReceiptOf(await decisions.get(d.token));
|
|
1825
|
+
if (replay !== undefined)
|
|
1826
|
+
return { kind: "replayed", out: replay };
|
|
1827
|
+
await runChannelEnded(d.runId, d.runScope);
|
|
1828
|
+
const condition = d.initiator.kind === "run_ended" ? { expect: d.fingerprint } : undefined;
|
|
1829
|
+
let recordFailure;
|
|
1830
|
+
const delivery = {
|
|
1831
|
+
carried: [{ runId: d.runId, token: d.token, outcome: d.outcome }],
|
|
1832
|
+
commitBeforeLaunch: async (deliveredTaskId) => {
|
|
1833
|
+
let recorded;
|
|
1834
|
+
try {
|
|
1835
|
+
recorded = await decisions.record({ token: d.token, runId: d.runId, hostSessionId: d.hostSessionId, outcome: d.outcome, fingerprint: d.fingerprint, deliveredTaskId, acceptedAtMs: Date.now() }, condition);
|
|
1836
|
+
}
|
|
1837
|
+
catch (err) {
|
|
1838
|
+
recordFailure = { kind: "faulted", err };
|
|
1839
|
+
throw err;
|
|
1840
|
+
}
|
|
1841
|
+
if (!recorded) {
|
|
1842
|
+
recordFailure = { kind: "not_recorded", deliveredTaskId };
|
|
1843
|
+
throw new Error(DELIVERY_NOT_RECORDED);
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
};
|
|
1847
|
+
const hostToken = await cs.findPendingTokenBySession(d.hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" }).catch(() => undefined);
|
|
1848
|
+
const req = d.initiator.kind === "decide" ? d.initiator.req : undefined;
|
|
1849
|
+
const acceptEarly = d.initiator.kind === "decide" ? d.initiator.acceptEarly : undefined;
|
|
1850
|
+
let out;
|
|
1851
|
+
try {
|
|
1852
|
+
out = hostToken
|
|
1853
|
+
? await resumeWake(d.hostSessionId, d.message, d.initiator.caller, req, acceptEarly, delivery, d.initiator.admittedPayer, d.restatement)
|
|
1854
|
+
: await startFreshHostTurn(d.hostSessionId, d.message.text, d.initiator.admittedPayer, req, delivery, d.runId, d.restatement);
|
|
1855
|
+
}
|
|
1856
|
+
catch (err) {
|
|
1857
|
+
const failed = recordFailure;
|
|
1858
|
+
if (failed === undefined)
|
|
1859
|
+
throw err;
|
|
1860
|
+
return { kind: "record_failed", failure: failed };
|
|
1861
|
+
}
|
|
1862
|
+
const failed = recordFailure;
|
|
1863
|
+
if (failed !== undefined)
|
|
1864
|
+
return { kind: "record_failed", failure: failed };
|
|
1865
|
+
if (acceptedTaskIdOf(out) === undefined) {
|
|
1866
|
+
const raced = await liveReceiptOf(await decisions.get(d.token));
|
|
1867
|
+
return raced !== undefined ? { kind: "replayed", out: raced } : { kind: "refused", out };
|
|
1868
|
+
}
|
|
1869
|
+
return { kind: "launched", out };
|
|
1870
|
+
}
|
|
1871
|
+
async function handOverRunEndedDecisions(runId) {
|
|
1872
|
+
if (isAdoptionQuiescing())
|
|
1873
|
+
throw new Error(`workflow run ${runId}: an adoption arc is running in this process — the decision handoff yields and is retried on the next recovery sweep`);
|
|
1874
|
+
const handing = handOverHeldRows(runId);
|
|
1875
|
+
trackMaintenanceLeg(handing);
|
|
1876
|
+
await handing;
|
|
1877
|
+
}
|
|
1878
|
+
async function handOverHeldRows(runId) {
|
|
1879
|
+
const decisions = deps.workflowParkDecisions;
|
|
1880
|
+
const records = deps.workflowRunStore;
|
|
1881
|
+
const cs = deps.checkpointStore;
|
|
1882
|
+
const index = deps.workflowAgentSessionIndex;
|
|
1883
|
+
if (decisions === undefined || records === undefined || cs === undefined || index === undefined)
|
|
1884
|
+
return;
|
|
1885
|
+
const run = await records.get(runId);
|
|
1886
|
+
if (run === null)
|
|
1887
|
+
return;
|
|
1888
|
+
const parks = readRunParks(run);
|
|
1889
|
+
if (parks === undefined) {
|
|
1890
|
+
deps.logger?.warn?.("workflow_park_handoff_refused", { runId, why: "parks_unreadable" });
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
const held = heldParksOf(runId, parks);
|
|
1894
|
+
if (run.status === "running") {
|
|
1895
|
+
for (const park of held) {
|
|
1896
|
+
if ((await decisions.get(park.token)) !== null) {
|
|
1897
|
+
throw new Error(`workflow run ${runId}: its completion arrived while its record still reads running (the terminal record write did not land) and it holds an accepted decision — leaving the completion pending until recovery ends the record`);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return;
|
|
1901
|
+
}
|
|
1902
|
+
for (const park of held) {
|
|
1903
|
+
const row = await decisions.get(park.token);
|
|
1904
|
+
if (row === null)
|
|
1905
|
+
continue;
|
|
1906
|
+
const deliverable = await deliverableEntryOf({ checkpointStore: cs, workflowAgentSessionIndex: index, workflowRunStore: records }, row);
|
|
1907
|
+
if (deliverable.kind === "unreadable")
|
|
1908
|
+
throw new Error(`workflow run ${runId}: the card or the holder of a recorded decision could not be read — leaving the completion pending for the next recovery sweep`);
|
|
1909
|
+
if (deliverable.kind === "not_deliverable" || deliverable.entry.runId !== runId)
|
|
1910
|
+
continue;
|
|
1911
|
+
const result = await handOverOne(runId, run.scope, deliverable, row);
|
|
1912
|
+
deps.metrics?.inc("workflow_park_handoff_total", { result: result.kind });
|
|
1913
|
+
switch (result.kind) {
|
|
1914
|
+
case "delivered":
|
|
1915
|
+
continue;
|
|
1916
|
+
case "retry":
|
|
1917
|
+
throw new Error(`workflow run ${runId}: the decision handoff must be retried (${result.why}) — leaving the completion pending for the next recovery sweep`);
|
|
1918
|
+
case "refused":
|
|
1919
|
+
deps.logger?.warn?.("workflow_park_handoff_refused", { runId, why: result.why });
|
|
1920
|
+
continue;
|
|
1921
|
+
default: {
|
|
1922
|
+
const unhandled = result;
|
|
1923
|
+
throw new Error(`unhandled workflow park handoff result: ${JSON.stringify(unhandled)}`);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
async function handOverOne(runId, runScope, parkEntry, row) {
|
|
1929
|
+
const cs = deps.checkpointStore;
|
|
1930
|
+
const hostSessionId = parkEntry.origin.run.originatingSessionId;
|
|
1931
|
+
if (hostSessionId === undefined || hostSessionId === "")
|
|
1932
|
+
return { kind: "refused", why: "host_unknown" };
|
|
1933
|
+
const ownerScope = parkEntry.card.scope;
|
|
1934
|
+
const hostToken = await cs.findPendingTokenBySession(hostSessionId, undefined, { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" });
|
|
1935
|
+
if (hostToken) {
|
|
1936
|
+
const hostCard = await cs.get(hostToken);
|
|
1937
|
+
if (hostCard !== null && hostCard.scope !== ownerScope)
|
|
1938
|
+
return { kind: "refused", why: "owner_mismatch" };
|
|
1939
|
+
}
|
|
1940
|
+
const parts = checkpointPartsOf(ownerScope);
|
|
1941
|
+
const quota = ownerCostQuotaDenied(parts?.person);
|
|
1942
|
+
if (quota !== undefined)
|
|
1943
|
+
return handoffResultOf(quota, "payer_cost_quota");
|
|
1944
|
+
const lease = await ownerFleetLeaseDenied(parts?.person);
|
|
1945
|
+
if (lease !== undefined)
|
|
1946
|
+
return handoffResultOf(lease, "payer_fleet_lease");
|
|
1947
|
+
const laned = await deliverViaResumeLane({
|
|
1948
|
+
hostSessionId,
|
|
1949
|
+
runId,
|
|
1950
|
+
runScope,
|
|
1951
|
+
token: row.token,
|
|
1952
|
+
outcome: row.outcome,
|
|
1953
|
+
fingerprint: row.fingerprint,
|
|
1954
|
+
message: workflowParkGuidance(runId, parkEntry.origin.park.callKey, row.outcome.decision === "allow" ? "approve" : "deny"),
|
|
1955
|
+
initiator: { kind: "run_ended", caller: parts?.subject, admittedPayer: decodeCheckpointScope(ownerScope) },
|
|
1956
|
+
});
|
|
1957
|
+
switch (laned.kind) {
|
|
1958
|
+
case "launched":
|
|
1959
|
+
case "replayed":
|
|
1960
|
+
return { kind: "delivered" };
|
|
1961
|
+
case "refused":
|
|
1962
|
+
return handoffResultOf(laned.out);
|
|
1963
|
+
case "record_failed": {
|
|
1964
|
+
if (laned.failure.kind === "faulted")
|
|
1965
|
+
return { kind: "retry", why: "store_fault" };
|
|
1966
|
+
const now = await deps.workflowParkDecisions.get(row.token);
|
|
1967
|
+
return now?.fingerprint === row.fingerprint ? { kind: "retry", why: "claim_lost" } : { kind: "refused", why: "superseded" };
|
|
1968
|
+
}
|
|
1969
|
+
default: {
|
|
1970
|
+
const unhandled = laned;
|
|
1971
|
+
throw new Error(`unhandled resume lane delivery: ${JSON.stringify(unhandled)}`);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1795
1975
|
async function preparePlanReview(sessionId, log, decider, sighting, restatement) {
|
|
1796
1976
|
const cs = deps.checkpointStore;
|
|
1797
1977
|
const refuse = (answer) => ({ ok: false, answer });
|
|
@@ -1865,7 +2045,7 @@ export function createResumeLegs(ctx) {
|
|
|
1865
2045
|
return refuse(resumeCtxUnreadableAnswer());
|
|
1866
2046
|
if (ctxRead.kind === "absent")
|
|
1867
2047
|
return refuse({ status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) });
|
|
1868
|
-
const ctx =
|
|
2048
|
+
const ctx = replayCtxOf(ctxRead.ctx, restatement);
|
|
1869
2049
|
const replay = storedBodyReplayRefusal(ctx, deps.config);
|
|
1870
2050
|
if (replay !== undefined)
|
|
1871
2051
|
return refuse(refuseStoredBodyReplay(sessionId, replay));
|
|
@@ -1920,6 +2100,6 @@ export function createResumeLegs(ctx) {
|
|
|
1920
2100
|
local: { images: createImagesLocal(deps), sessions: createSessionsLocal(), sessionSync: createSessionSyncLocal(), capabilities: createCapabilitiesLocal() },
|
|
1921
2101
|
legs: { prepareSpec, finalizeTaskResult, resumeCheckpoint, driveResumeIntoRunLog, resumePreempted, resumeWake, preparePlanReview, drivePlanReview, sessionNotify: sessionNotifyDoor },
|
|
1922
2102
|
};
|
|
1923
|
-
return { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, decisionRowAdmits, resumePreempted, startFreshHostTurn, resumeWake, preparePlanReview, drivePlanReview, sessionNotifyDoor };
|
|
2103
|
+
return { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, decisionRowAdmits, resumePreempted, startFreshHostTurn, resumeWake, preparePlanReview, drivePlanReview, sessionNotifyDoor, handOverRunEndedDecisions };
|
|
1924
2104
|
}
|
|
1925
2105
|
//# sourceMappingURL=resume-legs.js.map
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -227,6 +227,16 @@ export interface CheckpointCtx {
|
|
|
227
227
|
* 缺席 = 没继承(含 7.150.0 之前写下的行:不回填,续跑不继承 = 那时的行为)。
|
|
228
228
|
*/
|
|
229
229
|
projectContext?: import("../project-context.js").ProjectContext;
|
|
230
|
+
/**
|
|
231
|
+
* 这条 run 的**登记簿默认读层种子**(`project-registry.ts` 的 {@link import("../project-registry.js").MemorySeed}:种子项目 + 判出它的那一位观者)—— 新提交腿判一次:
|
|
232
|
+
* 这次提交带的项目对提交者可见(与 `GET /v1/projects/:id` 同一只判,观者 = 读面同一只推导)⇒ 种子;带了而判不出 ⇒ `null`(判过,无);没带项目 ⇒ 缺席。**服务端写、不来自请求体**(与 {@link memoryScope} 同为
|
|
233
|
+
* `body` 的兄弟键:提交腿推导、落盘,续跑原样读回)。冻结的是**观者**不是词:续跑以这一位观者经同一只可见性判重读此刻的登记(停驻期间改了词 ⇒ 新词;删了 / 换了主人 /
|
|
234
|
+
* 不再对他可见 ⇒ 没有种子)。写点:`buildCheckpointCtx`(提交腿那一笔,经 `persistSubmissionCtx` 读这条腿的部署事实);leader worker 的写者不带(它的体不带项目)。
|
|
235
|
+
* 续跑腿的写回传原 ctx / 其展开 ⇒ 随行。读者只有一只:`resume-legs.ts` `resolveResumeSpec` 把它原样递给装配器(`opts.memorySeed`)—— 续跑腿不推观者、不按 `body.projectId` 现查。
|
|
236
|
+
* 缺席而存体带了项目 = 本格出现之前写下的行(不回填:那时的提交腿没有判过可见性)⇒ 续跑不取种子,装配器记运维一行 `resume_memory_seed_unjudged`。
|
|
237
|
+
* 形只有一种读法(`frozenMemorySeedOf`,读口 `resume-ctx-read.ts`)。
|
|
238
|
+
*/
|
|
239
|
+
memorySeed?: import("../project-registry.js").MemorySeed | null;
|
|
230
240
|
}
|
|
231
241
|
/**
|
|
232
242
|
* `/decide` 第三条车道交给宿主投递腿(wake:`DriveResumeArgs` / fresh:`DurableRunLegArgs`)的**一件**:这一跑在内存里带着的决定,
|
|
@@ -378,7 +388,8 @@ export interface DriveResumeArgs {
|
|
|
378
388
|
/**
|
|
379
389
|
* 🔴 **[ref](codex 交叉复审 r2-[medium] 验真后修):这条腿是**调用方发起**的吗?**
|
|
380
390
|
*
|
|
381
|
-
* `true` = 有人在 HTTP 上点了按钮(resume / plan_review / wake / decide / 赎回席)
|
|
391
|
+
* `true` = 有人在 HTTP 上点了按钮(resume / plan_review / wake / decide / 赎回席),或这一跑交付的是一条**已受理的人的决定**
|
|
392
|
+
* (workflow 停驻卡的 run 收场送达把它交给宿主会话的下一跑 —— 没有请求,但花的是卡属主的钱)⇒ 吃按**付费方**
|
|
382
393
|
* (卡属主)判的成本配额准入(`ownerCostQuotaDenied`)。缺席/`false` = **部署自己的结算腿**
|
|
383
394
|
* (内部 D-D SLA deny-sweep)⇒ **不吃**那道门。
|
|
384
395
|
*
|
|
@@ -392,9 +403,8 @@ export interface DriveResumeArgs {
|
|
|
392
403
|
* 并且整段时间攥着会话 claim。**方向也自洽**:deny 是**停下**这条 run 的动作,为了省钱而拒绝执行「停」
|
|
393
404
|
* 与 preempt 路由早就成文的那句同一条(「否则超顶租户连自己的花费都停不下来」)。
|
|
394
405
|
*
|
|
395
|
-
*
|
|
396
|
-
* (
|
|
397
|
-
* 本位只是把同一条判据显式化成一个有名字的位,而不是让下游去猜。
|
|
406
|
+
* 判别子**不再**只是 `req` 的在场与否:`req` 缺席的腿有两种 —— SLA 拒扫(结算,不吃门)与 workflow 停驻卡的 run 收场送达(交付一条人的决定,
|
|
407
|
+
* 吃门,fail-closed)。所以本位是一个**有名字的事实**,由起腿的那一处写明(wake 腿:`req` 在场 ∨ 这一跑带着一件决定投递),下游只读这一位、不去猜 `req`。
|
|
398
408
|
* ⚠️ **射程覆盖两道门**:紧邻的 fleet-lease 准入(`ownerFleetLeaseDenied`)读**同一位** —— [ref] 件⑤②
|
|
399
409
|
* (codex r3-[high])把它一并改吃了 `callerInitiated`,理由与三问逐字同 cost 半场。
|
|
400
410
|
* 🔴 本句是 [ref] R4 改写的(2026-09-03,7.57.0 发车前合并重扫 wf_1abaaa9a):此处原文写着「紧邻的
|
|
@@ -417,7 +427,7 @@ export interface DriveResumeArgs {
|
|
|
417
427
|
*
|
|
418
428
|
* 所以由上游把「已受理」这个事实传下来,`driveResumeLeg` 据此跳过重复协商。**安全性**:两处的键是
|
|
419
429
|
* **同一个表达式**(`decodeCheckpointScope(cp.scope)`,上游门与下游 `auth.principal` 同源),射程条件也
|
|
420
|
-
* 同一条(
|
|
430
|
+
* 同一条(上游判过两门 ⇔ 下游 `callerInitiated`:`/decide` 与 workflow 停驻卡的 run 收场送达都在交下来之前按卡属主判过)⇒ 跳过的是一次**逐字相同**的判定,不是一道门。
|
|
421
431
|
* 缺席/`false` ⇒ 照常自己判(其余三条驱动腿与 sweep 一字不变)。
|
|
422
432
|
*/
|
|
423
433
|
ownerLeaseAdmitted?: boolean;
|
|
@@ -675,15 +685,15 @@ export interface RouteDeclFacts {
|
|
|
675
685
|
* 编排器片 2 件 2:值 = 这一种计费请求的**种类**(`submit` 新工作 / `hitl-answer` 对等人的卡作答 / `resume` 续跑;闭集属主
|
|
676
686
|
* `http/readiness.ts` 的 `BILLABLE_KINDS`)—— 排空与租约过期只拒 `submit`(放行闭集表 `ADMISSION_TABLE`),无凭证门不看种类。 */
|
|
677
687
|
readonly billable?: import("./readiness.js").BillableKind;
|
|
678
|
-
/** 「本条 url
|
|
688
|
+
/** 「本条 url 上这几个方法在**请求没带凭据门认的凭据**(service 凭据 / 鉴权桥验过的登录令牌;`identity-source.ts` 头注律 5)时必须 fail-closed」。
|
|
679
689
|
* 判据(两个合取项)= 「授权的唯一输入是 principal 头」∧「持久改写,或爆炸半径跨租户的治理读」;
|
|
680
690
|
* 逐口论证在各 `routes/*.ts` 的头注里,声明行只记结论。 */
|
|
681
691
|
readonly credentialGated?: readonly RouteMethod[];
|
|
682
692
|
/** 「这条 url **是**计费提交面,但它到底提不提交由**请求体里的方法**决定」—— 单 URL 多方法的
|
|
683
693
|
* JSON-RPC 端点。两道**可用性** 503(drain / roster-pending)按 url 一刀切会把同端点上的纯读一起
|
|
684
694
|
* 关掉,所以分派器对本行**跳过**那两道,改由域模块在写分支里施加同样的两道(文案逐字同源)。
|
|
685
|
-
* 🔴 只豁免那两道,**不豁免安全轴**:`billable` 本身仍为 true
|
|
686
|
-
* 503 照旧罩着(
|
|
695
|
+
* 🔴 只豁免那两道,**不豁免安全轴**:`billable` 本身仍为 true,写门那道 fail-closed
|
|
696
|
+
* 503 照旧罩着(它判的是「这次请求带没带过门的凭据」,与方法无关,且必须在解析请求体之前)。 */
|
|
687
697
|
readonly methodDispatched?: true;
|
|
688
698
|
}
|
|
689
699
|
/** **可分派行**多出来的列([ref] 第三刀):身份 + 它真认的方法(+ [ref] 的订阅类)。纯门行一列都没有(见下)。 */
|
|
@@ -136,7 +136,7 @@ export declare function routeLabel(_method: string, url: string): string;
|
|
|
136
136
|
export declare function isBillableSubmitPath(url: string): boolean;
|
|
137
137
|
/** 编排器片 2 件 2:这条 url 是哪一**种**计费请求(`undefined` = 不在计费面)。声明序第一条命中的行定种类(今天每条 url 至多命中一行)。 */
|
|
138
138
|
export declare function billableKindOf(url: string): BillableKind | undefined;
|
|
139
|
-
/** [ref]([ref] 件5)——
|
|
139
|
+
/** [ref]([ref] 件5)—— **改写门**:不烧模型、但在请求没带凭据门认的凭据时(`identity-source.ts` 头注律 5)必须
|
|
140
140
|
* 与 billable / bake 两族一样 fail-closed 的写门。与 `isBillableSubmitPath` **刻意分开**:这几扇门
|
|
141
141
|
* `billable=false`(零模型工作),塞进那张名单会让它们连带吃 drain 与 roster-pending 两道 503 ——
|
|
142
142
|
* 而收编与规则读写在排空期/roster 未落时做完全无害。 */
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* 的调用方不该从「这台有没有授权表」上读出部署形态。
|
|
27
27
|
* 🔴 四口都在 `isCredentialGatedRewrite`(server.ts)门内(codex R1-[high] 验真后补):授权的**唯一**输入是
|
|
28
28
|
* principal 头 ∧ 一次调用的爆炸半径跨租户(改缺省行 = 改整台部署每个 principal 的 verdict;读面 = 全部授权行 +
|
|
29
|
-
* operator 手写的 note)——
|
|
29
|
+
* operator 手写的 note)—— 请求没带凭据门认的凭据(且部署未开 `ALLOW_UNAUTHED_WRITES`)时那个头**可自造**,与
|
|
30
30
|
* `/v1/memory/erase` / `POST /v1/memory/export` 同一条论证。
|
|
31
31
|
*
|
|
32
32
|
* ── 为什么缺席形是 **501 `capability.*`** 而不是 404(与 consolidation 阀门刻意分家)──────────────────
|
|
@@ -2,8 +2,7 @@ import { chosenAgentName } from "@sema-agent/core";
|
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedSubject, sendUnauthorized } from "../principal-gate.js";
|
|
4
4
|
import { PRINCIPAL_MAX_BYTES } from "../../principal-shape.js";
|
|
5
|
-
import {
|
|
6
|
-
import { taskSpecIdentityOf } from "../../engine-identity.js";
|
|
5
|
+
import { engineScopeOf } from "../../engine-identity.js";
|
|
7
6
|
import { routeListFilter } from "../owner-access.js";
|
|
8
7
|
import { assertNever } from "../route-ctx.js";
|
|
9
8
|
import * as facts from "../../capability-facts.js";
|
|
@@ -99,7 +98,7 @@ export async function handleAgentRoster(req, res, match, ctx) {
|
|
|
99
98
|
}
|
|
100
99
|
const status = statusRaw === null ? undefined : statusRaw;
|
|
101
100
|
const subject = gatedSubject(req, deps.config);
|
|
102
|
-
const ownScope =
|
|
101
|
+
const ownScope = engineScopeOf(subject);
|
|
103
102
|
let scope;
|
|
104
103
|
if (gateOwner !== null) {
|
|
105
104
|
scope = ownScope;
|
|
@@ -38,7 +38,8 @@ import { fieldConflictFacts } from "../../field-conflict.js";
|
|
|
38
38
|
import { buildSettlementEvidence } from "../../gate-settlement.js";
|
|
39
39
|
import { seatText } from "../../trace/exit-seat.js";
|
|
40
40
|
import { approvalQueueKindOfDoorId } from "../../approval-queue-kind.js";
|
|
41
|
-
import { defaultPeerInboundFace, parsePeerInboundId
|
|
41
|
+
import { defaultPeerInboundFace, parsePeerInboundId } from "../../peer-inbound-face.js";
|
|
42
|
+
import { engineScopeOfOwner } from "../../engine-identity.js";
|
|
42
43
|
import { RESUME_RESTATABLE_KEYS, readResumeRestatement } from "../resume-restatement.js";
|
|
43
44
|
const DECIDE_BODY_DECLARED_KEYS = [
|
|
44
45
|
"answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decidedVia", "decision", "feedback", "reason", "remember", "updatedInput",
|
|
@@ -949,7 +950,7 @@ async function decidePeerInbound(req, res, id, env) {
|
|
|
949
950
|
decider = proof.principal;
|
|
950
951
|
}
|
|
951
952
|
const coord = parsePeerInboundId(id);
|
|
952
|
-
const outcome = coord === undefined ? { kind: "not_found" } : await env.face.decide(
|
|
953
|
+
const outcome = coord === undefined ? { kind: "not_found" } : await env.face.decide(engineScopeOfOwner(decider), coord, decision);
|
|
953
954
|
switch (outcome.kind) {
|
|
954
955
|
case "not_found":
|
|
955
956
|
sendError(res, 404, "not_found.approval", "approval not found");
|
|
@@ -1016,7 +1017,7 @@ function redactPendingDisclosures(row) {
|
|
|
1016
1017
|
function peerInboundRowsFor(face, principal, operatorOwnerParam) {
|
|
1017
1018
|
if (operatorOwnerParam !== undefined && operatorOwnerParam !== principal)
|
|
1018
1019
|
return () => [];
|
|
1019
|
-
const scope =
|
|
1020
|
+
const scope = engineScopeOfOwner(principal);
|
|
1020
1021
|
return () => face.list(scope);
|
|
1021
1022
|
}
|
|
1022
1023
|
function projectPendingForWire(row, deployment, agent) {
|