@sema-agent/server 7.142.0 → 7.143.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 +442 -0
- package/USAGE.md +41 -24
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +7 -5
- package/dist/approval-ask-audit-store.js +12 -6
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +124 -25
- package/dist/approval-card.js +103 -23
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +8 -8
- package/dist/approval-reconciler.js +3 -3
- package/dist/audit.js +4 -2
- package/dist/boot/budget-tracing.d.ts +1 -1
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +3 -3
- package/dist/boot/reapers.js +5 -4
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +8 -8
- package/dist/boot/runner-deps.d.ts +10 -7
- package/dist/boot/runner-deps.js +5 -17
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +16 -2
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +3 -5
- package/dist/boot/stage-05-execution-env.d.ts +5 -2
- package/dist/boot/stage-06-runners.d.ts +5 -2
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +5 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +9 -1
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +7 -7
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +31 -19
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +11 -1
- package/dist/capabilities/scenarios.js +1 -0
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +8 -8
- package/dist/capability-facts.js +4 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +1 -1
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +5 -5
- package/dist/config.d.ts +4 -3
- package/dist/config.js +1 -1
- package/dist/delegated-session-owner.d.ts +5 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/fleet/fleet-bus.d.ts +28 -52
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/hook-runner.js +28 -7
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +71 -49
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +142 -31
- package/dist/http/routes/capabilities.js +11 -2
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +1 -1
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +9 -8
- package/dist/http/routes/session-memory-sync.d.ts +34 -6
- package/dist/http/routes/session-memory-sync.js +27 -10
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +27 -49
- package/dist/http/server.d.ts +13 -7
- package/dist/http/server.js +2 -2
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.js +14 -3
- package/dist/memory-sync.d.ts +20 -4
- package/dist/memory-sync.js +23 -4
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/fail-open.d.ts +20 -4
- package/dist/observability/fail-open.js +17 -4
- package/dist/observability/run-terminal.js +4 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +127 -0
- package/dist/peer-inbound-face.js +158 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.js +18 -6
- package/dist/plugins/approval-ask-store-memory.js +8 -6
- package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
- package/dist/plugins/approval-ask-store-sql.js +30 -11
- package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
- package/dist/plugins/checkpoint-store-sql.js +9 -7
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +29 -0
- package/dist/plugins/file-run-store.js +97 -8
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.js +2 -2
- package/dist/plugins/local-session-store.d.ts +6 -2
- package/dist/plugins/local-session-store.js +8 -2
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +32 -2
- package/dist/plugins/one-time-migrations.js +106 -3
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-k8s.d.ts +37 -24
- package/dist/plugins/remote-env-k8s.js +50 -17
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/store-backend.d.ts +23 -28
- package/dist/plugins/store-backend.js +9 -1
- package/dist/plugins/store-contracts.d.ts +29 -5
- package/dist/plugins/store-contracts.js +4 -1
- package/dist/plugins/task-attachment-store.d.ts +2 -2
- package/dist/plugins/task-attachment-store.js +37 -52
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +1 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +9 -5
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +6 -7
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +12 -0
- package/dist/session-error-wire.js +6 -0
- package/dist/session-incarnation.d.ts +1 -1
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +93 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +11 -8
- package/dist/task-settings.js +31 -11
- package/dist/tool-approval.d.ts +52 -34
- package/dist/tool-approval.js +66 -57
- package/dist/trace/core-keyset-guard.d.ts +11 -8
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +3 -1
- package/dist/trace/exit-seat.d.ts +7 -1
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +2 -2
- package/dist/trace/model-segment.d.ts +55 -25
- package/dist/trace/model-segment.js +101 -33
- package/dist/trace/project.d.ts +7 -4
- package/dist/trace/project.js +6 -0
- package/dist/trace/redact.d.ts +61 -9
- package/dist/trace/redact.js +1490 -468
- package/dist/trace/self-minted-wire.d.ts +510 -38
- package/dist/trace/self-minted-wire.js +165 -17
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +90 -10
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.143.0.json +1167 -0
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { IncomingMessage } from "node:http";
|
|
15
15
|
import { CheckpointError, type QuestionAnswer, type CheckpointToken } from "@sema-agent/core";
|
|
16
|
-
import type { DecideBinding } from "./wire-types.js";
|
|
16
|
+
import type { DecideBinding, DecidedAction } from "./wire-types.js";
|
|
17
17
|
import type { CheckpointStoreFull } from "../plugins/store-backend.js";
|
|
18
18
|
import type { HostDecision } from "../host-decision.js";
|
|
19
19
|
import { type GateSighting } from "../approval-gate-currency.js";
|
|
@@ -23,6 +23,7 @@ import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
|
|
|
23
23
|
import type { createRequestGuards } from "./request-guards.js";
|
|
24
24
|
import type { FlatServiceDeps } from "./server.js";
|
|
25
25
|
import { type ResumeRefusalAnswer } from "./checkpoint-ctx.js";
|
|
26
|
+
import { type ResumeRestatement } from "./resume-restatement.js";
|
|
26
27
|
/**
|
|
27
28
|
* 🔴 **一次 `CheckpointError` 拒的处置 —— 唯一读口**:{@link CHECKPOINT_ERROR_DISPOSITION} 的读法(doctrine 在那张表头)。resume 腿的 catch 臂只调它一次,
|
|
28
29
|
* 行的落点与 wire 的 `retriable` 都读这一个结果。纯函数,钉直接调。运行期遇到表外的码(装树 core 比本二进制多一个码的版本偏斜 —— 型面上拼不出来)
|
|
@@ -35,18 +36,30 @@ export declare function checkpointRefusalAnswer(e: CheckpointError, at: {
|
|
|
35
36
|
readonly taskId?: string;
|
|
36
37
|
readonly versionKeys: Readonly<Record<string, string>>;
|
|
37
38
|
}): ResumeRefusalAnswer;
|
|
39
|
+
/**
|
|
40
|
+
* 一个答案只属于它被展示的那一个动作 —— 决断绑定的**唯一判官**:决断方点名它看到的那一对(`boundCallId` + `boundInputHash`,
|
|
41
|
+
* 队列行上展示的原值),两位都是非空串才成一只 {@link DecidedAction};缺哪位就点名哪位(序 = 体键序)。
|
|
42
|
+
* 不从当前停驻行补:补上的是此刻停在门上的动作,不是决断方看到的那一个(7.142.x 及以前的旧形 —— 同一调用 id 换了入参的新停驻会接住旧决定)。
|
|
43
|
+
*/
|
|
44
|
+
export declare function decidedActionOf(binding: Pick<DecideBinding, "boundCallId" | "boundInputHash">): {
|
|
45
|
+
readonly action: DecidedAction;
|
|
46
|
+
readonly missingKeys?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
readonly action?: undefined;
|
|
49
|
+
readonly missingKeys: readonly ("boundCallId" | "boundInputHash")[];
|
|
50
|
+
};
|
|
38
51
|
export declare function createResumeLegs(ctx: {
|
|
39
52
|
deps: FlatServiceDeps;
|
|
40
53
|
runnerFor: FlatServiceDeps["runnerFor"];
|
|
41
54
|
} & RunRegistry & RouteHelpers & Pick<RouteLegs, "prepareSpec" | "finalizeTaskResult"> & Pick<ReturnType<typeof createRequestGuards>, "ownerCostQuotaDenied" | "ownerFleetLeaseDenied" | "usageWindowDenialFor">): {
|
|
42
55
|
routeCtxBase: RouteCtxBase;
|
|
43
|
-
resumeCheckpoint: (sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req
|
|
56
|
+
resumeCheckpoint: (sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req: IncomingMessage | undefined, answer: QuestionAnswer | undefined, binding: DecideBinding, onResumeCommitted?: (grant: {
|
|
44
57
|
overrideSessionId?: string;
|
|
45
58
|
rememberableToolName: string | null;
|
|
46
59
|
}) => Promise<void>, acceptEarly?: boolean, decider?: {
|
|
47
60
|
principal?: string;
|
|
48
61
|
explicitOperator: boolean;
|
|
49
|
-
}) => Promise<ResumeLegOutcome>;
|
|
62
|
+
}, restatement?: ResumeRestatement) => Promise<ResumeLegOutcome>;
|
|
50
63
|
driveResumeIntoRunLog: (args: DriveResumeArgs) => Promise<ResumeLegOutcome>;
|
|
51
64
|
driveResumeLeg: (args: DriveResumeArgs, onAccepted?: (receipt: {
|
|
52
65
|
status: number;
|
|
@@ -59,9 +72,9 @@ export declare function createResumeLegs(ctx: {
|
|
|
59
72
|
resumePreempted: (sessionId: string, req: IncomingMessage | undefined, acceptEarly?: boolean, decider?: {
|
|
60
73
|
principal?: string;
|
|
61
74
|
explicitOperator: boolean;
|
|
62
|
-
}) => Promise<ResumeLegOutcome>;
|
|
63
|
-
startFreshHostTurn: (sessionId: string, objective: string, principal: string | undefined, req: IncomingMessage, workflowDecision: WorkflowDecisionDelivery, runId: string) => Promise<ResumeLegOutcome>;
|
|
64
|
-
resumeWake: (sessionId: string, message: string | undefined, caller: string | undefined, req: IncomingMessage | undefined, acceptEarly?: boolean, workflowDecision?: WorkflowDecisionDelivery, ownerLeaseAdmittedFor?: string) => Promise<ResumeLegOutcome>;
|
|
75
|
+
}, restatement?: ResumeRestatement) => Promise<ResumeLegOutcome>;
|
|
76
|
+
startFreshHostTurn: (sessionId: string, objective: string, principal: string | undefined, req: IncomingMessage, workflowDecision: WorkflowDecisionDelivery, runId: string, restatement?: ResumeRestatement) => Promise<ResumeLegOutcome>;
|
|
77
|
+
resumeWake: (sessionId: string, message: string | undefined, caller: string | undefined, req: IncomingMessage | undefined, acceptEarly?: boolean, workflowDecision?: WorkflowDecisionDelivery, ownerLeaseAdmittedFor?: string, restatement?: ResumeRestatement) => Promise<ResumeLegOutcome>;
|
|
65
78
|
preparePlanReview: (sessionId: string, log?: {
|
|
66
79
|
taskId?: string;
|
|
67
80
|
principalPresent?: boolean;
|
|
@@ -78,6 +91,7 @@ export declare function createResumeLegs(ctx: {
|
|
|
78
91
|
readonly ok: true;
|
|
79
92
|
readonly prepared: PreparedPlanReview;
|
|
80
93
|
}>;
|
|
81
|
-
drivePlanReview: (prepared: PreparedPlanReview, decision: "approve" | "edit" | "reject", editedPlan: string | undefined, reason: string | undefined, hostDecision: HostDecision, req: IncomingMessage | undefined, acceptEarly?: boolean, permissionModeAfter?: PlanApprovePermissionModeAfter) => Promise<ResumeLegOutcome>;
|
|
94
|
+
drivePlanReview: (prepared: PreparedPlanReview, decision: "approve" | "edit" | "reject", editedPlan: string | undefined, reason: string | undefined, hostDecision: HostDecision, req: IncomingMessage | undefined, acceptEarly?: boolean, permissionModeAfter?: PlanApprovePermissionModeAfter, restatement?: ResumeRestatement) => Promise<ResumeLegOutcome>;
|
|
95
|
+
sessionNotifyDoor: import("../session-notify-door.js").SessionNotifyDoor;
|
|
82
96
|
};
|
|
83
97
|
//# sourceMappingURL=resume-legs.d.ts.map
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -4,7 +4,7 @@ import { uuidv7, resumeWithVerification, CheckpointError, defaultTaskRegistry, t
|
|
|
4
4
|
import { decideParkedAgent, denyWorkflowParkForked, denyWorkflowParksUnreadable, findParkOrigin, heldWorkflowParkDecisions, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
5
5
|
import { parkDecisionFingerprint } from "../orchestration/workflow-park-decision.js";
|
|
6
6
|
import { headReadOf } from "../session-incarnation.js";
|
|
7
|
-
import { createSessionWorkspaceRecorder,
|
|
7
|
+
import { createSessionWorkspaceRecorder, sessionWorkspaceStoreOf } from "../session-workspace.js";
|
|
8
8
|
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
9
|
import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
10
10
|
import { graftParkToolCallId, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort, voidHeldParkCard } from "../runs.js";
|
|
@@ -15,7 +15,7 @@ import { isTerminalRunStatus } from "../plugins/store-contracts.js";
|
|
|
15
15
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
16
16
|
import { recordResumeResourceWindow } from "../resource-window.js";
|
|
17
17
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
18
|
-
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals
|
|
18
|
+
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals } from "../fleet/fleet-bus.js";
|
|
19
19
|
import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
|
|
20
20
|
import { currentApprovalGateToken, currentGateFacesOf, sightsCurrentGate } from "../approval-gate-currency.js";
|
|
21
21
|
import { approverFacingArgs, createApprovalCardEmitter, declaredApprovalFrameFamilies, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
|
|
@@ -25,7 +25,7 @@ import { serviceVersion } from "../service-version.js";
|
|
|
25
25
|
import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
26
26
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
27
27
|
import { withModelReachScope } from "../model-reach-scope.js";
|
|
28
|
-
import {
|
|
28
|
+
import { legFactsOf } from "../leg-facts.js";
|
|
29
29
|
import { createInstructionSelectionLeg } from "../instruction-selection.js";
|
|
30
30
|
import { modelTextEventData, appendParkRow, 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";
|
|
31
31
|
import { redactSecrets } from "../trace/redact.js";
|
|
@@ -57,9 +57,11 @@ import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnsealableAnswer } from
|
|
|
57
57
|
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
58
58
|
import { refusalBody, resumeRefusalFacts } from "./refusal-body.js";
|
|
59
59
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
60
|
+
import { restateResumeCtx } from "./resume-restatement.js";
|
|
60
61
|
import { httpErrorRefusal } from "./send.js";
|
|
61
62
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
62
63
|
import { fieldConflictFacts } from "../field-conflict.js";
|
|
64
|
+
import { createSessionNotifyDoor } from "../session-notify-door.js";
|
|
63
65
|
function legMayHaveReopenedCheckpoint(r) {
|
|
64
66
|
return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
|
|
65
67
|
}
|
|
@@ -107,6 +109,8 @@ const CHECKPOINT_ERROR_DISPOSITION = {
|
|
|
107
109
|
"checkpoint.reopened_concurrently": "retriable",
|
|
108
110
|
"resume.parent_constraint_missing": "retriable",
|
|
109
111
|
"resume.parent_constraint_mismatch": "retriable",
|
|
112
|
+
"resume.composition_mismatch": "retriable",
|
|
113
|
+
"checkpoint.void_unpin_failed": "terminal",
|
|
110
114
|
"resume.constraint_rejected": "retriable",
|
|
111
115
|
"resume.constraint_unprojectable": "retriable",
|
|
112
116
|
"resume.usage_window_exhausted": "retriable",
|
|
@@ -230,14 +234,19 @@ export function checkpointRefusalAnswer(e, at) {
|
|
|
230
234
|
}),
|
|
231
235
|
}, at.retriable);
|
|
232
236
|
}
|
|
237
|
+
export function decidedActionOf(binding) {
|
|
238
|
+
const { boundCallId, boundInputHash } = binding;
|
|
239
|
+
const missingKeys = [...(typeof boundCallId === "string" && boundCallId.length > 0 ? [] : ["boundCallId"]), ...(typeof boundInputHash === "string" && boundInputHash.length > 0 ? [] : ["boundInputHash"])];
|
|
240
|
+
return missingKeys.length === 0 ? { action: { boundCallId: boundCallId, boundInputHash: boundInputHash } } : { missingKeys };
|
|
241
|
+
}
|
|
233
242
|
export function createResumeLegs(ctx) {
|
|
234
243
|
const { deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor } = ctx;
|
|
235
244
|
const ctxOpening = (sessionId) => ({ sessionId, opener: deps.checkpointCtxSealing });
|
|
236
245
|
const resumeFleetScope = (req, auth) => req ? (gatedPrincipal(req, deps.config) ?? "default") : (auth.principal ?? "default");
|
|
237
|
-
async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider) {
|
|
246
|
+
async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider, restatement) {
|
|
238
247
|
const cs = deps.checkpointStore;
|
|
239
248
|
const token = await currentApprovalGateToken(cs, sessionId);
|
|
240
|
-
if (binding
|
|
249
|
+
if (binding.checkpointToken && !sightsCurrentGate({ face: "checkpointToken", value: binding.checkpointToken }, token ? { checkpointToken: token } : null)) {
|
|
241
250
|
let seenUnreadable = false;
|
|
242
251
|
const seen = await cs.get(binding.checkpointToken).catch((e) => {
|
|
243
252
|
seenUnreadable = true;
|
|
@@ -326,20 +335,23 @@ export function createResumeLegs(ctx) {
|
|
|
326
335
|
}),
|
|
327
336
|
};
|
|
328
337
|
}
|
|
329
|
-
|
|
338
|
+
const decided = decidedActionOf(binding);
|
|
339
|
+
if (decided.action === undefined) {
|
|
330
340
|
return {
|
|
331
341
|
status: 400,
|
|
332
342
|
body: refusalBody({
|
|
333
343
|
errorCode: "decide_requires_binding",
|
|
334
|
-
message: "decide requires the decision-action binding — echo
|
|
344
|
+
message: "decide requires the decision-action binding — echo boundCallId and boundInputHash exactly as the pending approval you are deciding shows them (GET /v1/approvals), so a decision on a view that has since been replaced is refused instead of landing on the gate that replaced it",
|
|
335
345
|
facts: {
|
|
336
346
|
bindingEnforced: true,
|
|
347
|
+
missingKeys: decided.missingKeys,
|
|
337
348
|
},
|
|
338
349
|
}),
|
|
339
350
|
};
|
|
340
351
|
}
|
|
352
|
+
const action = decided.action;
|
|
341
353
|
const s57RowCallId = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction.toolCallId : undefined;
|
|
342
|
-
if (
|
|
354
|
+
if (s57RowCallId && action.boundCallId !== s57RowCallId) {
|
|
343
355
|
let s57CurrentPending;
|
|
344
356
|
if (decider !== undefined && cp.status === "pending" && cp.sessionId === sessionId && cp.pendingAction.kind === "tool_approval") {
|
|
345
357
|
s57CurrentPending = {
|
|
@@ -400,8 +412,9 @@ export function createResumeLegs(ctx) {
|
|
|
400
412
|
const parkedReq = {
|
|
401
413
|
token, scope: cp.scope, sessionId: cp.sessionId, pendingAction: cp.pendingAction,
|
|
402
414
|
decision, hostDecision, ...(reason !== undefined ? { reason } : {}),
|
|
403
|
-
|
|
415
|
+
binding: { ...(({ checkpointToken: _staleGuardOnly, ...rest }) => rest)(binding), ...action },
|
|
404
416
|
...(answer !== undefined ? { answer } : {}),
|
|
417
|
+
...(restatement !== undefined ? { restatement } : {}),
|
|
405
418
|
...(boundGrant ? { grantRemember: (rootSessionId) => boundGrant(rootSessionId) } : {}),
|
|
406
419
|
};
|
|
407
420
|
if (origin?.kind === "background" && parkedAgentStore !== undefined && parkedSurface !== undefined) {
|
|
@@ -458,7 +471,7 @@ export function createResumeLegs(ctx) {
|
|
|
458
471
|
if (decisions === undefined || runStore === undefined) {
|
|
459
472
|
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
460
473
|
}
|
|
461
|
-
const fingerprint = parkDecisionFingerprint(plan.outcome);
|
|
474
|
+
const fingerprint = parkDecisionFingerprint(plan.outcome, restatement);
|
|
462
475
|
const liveReceiptOf = async (held) => {
|
|
463
476
|
if (held === null || held.fingerprint !== fingerprint)
|
|
464
477
|
return undefined;
|
|
@@ -488,8 +501,8 @@ export function createResumeLegs(ctx) {
|
|
|
488
501
|
.catch(() => undefined);
|
|
489
502
|
const admittedPayer = decodeCheckpointScope(cp.scope);
|
|
490
503
|
const out = hostToken
|
|
491
|
-
? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, delivery, admittedPayer)
|
|
492
|
-
: await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req, delivery, plan.runId);
|
|
504
|
+
? await resumeWake(plan.hostSessionId, plan.message, decider?.principal, req, acceptEarly, delivery, admittedPayer, restatement)
|
|
505
|
+
: await startFreshHostTurn(plan.hostSessionId, plan.message, admittedPayer, req, delivery, plan.runId, restatement);
|
|
493
506
|
if (recordFailure !== undefined)
|
|
494
507
|
throw recordFailure.err;
|
|
495
508
|
if (acceptedTaskIdOf(out) === undefined)
|
|
@@ -502,9 +515,10 @@ export function createResumeLegs(ctx) {
|
|
|
502
515
|
return resumeCtxCorruptAnswer(ctxRead0.where);
|
|
503
516
|
if (ctxRead0.kind === "unsealable")
|
|
504
517
|
return resumeCtxUnsealableAnswer(ctxRead0);
|
|
505
|
-
const
|
|
506
|
-
if (!
|
|
518
|
+
const storedCtx = ctxRead0.kind === "ok" ? ctxRead0.ctx : null;
|
|
519
|
+
if (!storedCtx)
|
|
507
520
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
521
|
+
const ctx = restateResumeCtx(storedCtx, restatement);
|
|
508
522
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
509
523
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
510
524
|
if (!resolved.ok)
|
|
@@ -520,21 +534,19 @@ export function createResumeLegs(ctx) {
|
|
|
520
534
|
}
|
|
521
535
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
522
536
|
const pa = cp.pendingAction;
|
|
523
|
-
const persistedCallId = pa.kind === "tool_approval" ? pa.toolCallId : "";
|
|
524
|
-
const persistedHash = pa.kind === "tool_approval" ? (pa.boundInputHash ?? "") : "";
|
|
525
537
|
const outcome = {
|
|
526
538
|
gate: "policy_ask",
|
|
527
|
-
boundCallId:
|
|
528
|
-
boundInputHash:
|
|
539
|
+
boundCallId: action.boundCallId,
|
|
540
|
+
boundInputHash: action.boundInputHash,
|
|
529
541
|
decision: decision === "approve" ? "allow" : "deny",
|
|
530
|
-
...(decision === "approve" && !isEngineQuestionRow(pa) && binding
|
|
542
|
+
...(decision === "approve" && !isEngineQuestionRow(pa) && binding.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
|
|
531
543
|
...(reason ? { reason } : {}),
|
|
532
|
-
...(binding
|
|
544
|
+
...(binding.feedback !== undefined ? { feedback: binding.feedback } : {}),
|
|
533
545
|
...(answer !== undefined ? { answer } : {}),
|
|
534
546
|
hostDecision,
|
|
535
547
|
};
|
|
536
548
|
const verifyRounds = verifyRoundsFromBody(ctx.body);
|
|
537
|
-
const driven = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner:
|
|
549
|
+
const driven = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner: resolved.runner, legFacts: resolved.legFacts, token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, commitment: { kind: "gate_resolved", ...captureSelectedGate() }, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(boundGrant ? { onResumeCommitted: boundGrant } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}), ...(ownerLeaseAdmittedHere ? { ownerLeaseAdmitted: true } : {}) });
|
|
538
550
|
noteLateSettleIfAccepted(driven);
|
|
539
551
|
return driven;
|
|
540
552
|
}
|
|
@@ -547,14 +559,17 @@ export function createResumeLegs(ctx) {
|
|
|
547
559
|
const resolveResumeSpec = async (ctx, req, auth, lease) => {
|
|
548
560
|
const engineNotices = [];
|
|
549
561
|
const execution = await resolveExecutionContext(deps, { kind: "continuation", root: auth.sessionId, principal: auth.principal });
|
|
550
|
-
const
|
|
562
|
+
const resolved = await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_SUBMIT_STRIP }, req, auth, {
|
|
551
563
|
leg: "resume",
|
|
552
564
|
onNotice: (n) => engineNotices.push(n),
|
|
553
565
|
...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
|
|
554
566
|
lease,
|
|
555
567
|
execution,
|
|
556
|
-
})
|
|
557
|
-
|
|
568
|
+
});
|
|
569
|
+
const legFacts = legFactsOf(resolved);
|
|
570
|
+
const runner = runnerFor(resolved);
|
|
571
|
+
const spec = stampSpecRoot(resolved, execution);
|
|
572
|
+
return { spec, runner, legFacts, engineNotices, execution };
|
|
558
573
|
};
|
|
559
574
|
const refuseStoredBodyReplay = (sessionId, refusal) => {
|
|
560
575
|
deps.logger?.warn?.("resume_refused_stored_body", { sessionId, ...storedBodyReplayLogFields(refusal) });
|
|
@@ -566,8 +581,8 @@ export function createResumeLegs(ctx) {
|
|
|
566
581
|
return { ok: false, answer: refuseStoredBodyReplay(auth.sessionId, replay) };
|
|
567
582
|
const lease = deps.peerDesk.lease("resume");
|
|
568
583
|
try {
|
|
569
|
-
const { spec, engineNotices, execution } = await resolveResumeSpec(ctx, req, auth, lease);
|
|
570
|
-
return { ok: true, spec, engineNotices, execution, peerLease: lease };
|
|
584
|
+
const { spec, runner, legFacts, engineNotices, execution } = await resolveResumeSpec(ctx, req, auth, lease);
|
|
585
|
+
return { ok: true, spec, runner, legFacts, engineNotices, execution, peerLease: lease };
|
|
571
586
|
}
|
|
572
587
|
catch (e) {
|
|
573
588
|
lease.release();
|
|
@@ -603,7 +618,7 @@ export function createResumeLegs(ctx) {
|
|
|
603
618
|
const reparkStatus = outcome.gate === "plan_review" ? "needs_review" : "suspended";
|
|
604
619
|
const commitResumeCtx = () => Promise.resolve().then(() => deps.checkpointStore.putCtx(resumeCtx.sessionId, sealCheckpointCtx({ ...resumeCtx.ctx, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, resumeCtx.sessionId))).catch((e) => {
|
|
605
620
|
deps.metrics?.inc("resume_ctx_not_persisted_total");
|
|
606
|
-
deps.logger?.warn?.("resume_ctx_not_persisted", { sessionId: resumeCtx.sessionId, gate: outcome.gate, error: errorText(e), note: "this continuation runs with the body it was given; the durable rebuild input still holds the PREVIOUS body, so the NEXT continuation of this session rebuilds from that one
|
|
621
|
+
deps.logger?.warn?.("resume_ctx_not_persisted", { sessionId: resumeCtx.sessionId, gate: outcome.gate, error: errorText(e), note: "this continuation runs with the body it was given; the durable rebuild input still holds the PREVIOUS body, so the NEXT continuation of this session rebuilds from that one — its permission mode is never wider than this leg's, but if this leg restated its MCP / A2A peer tables (mcpServers / a2aPeers on the resume request), a later continuation that does not restate them runs with the previous tables again" });
|
|
607
622
|
});
|
|
608
623
|
const legKey = createHash("sha256").update(String(token)).digest("hex");
|
|
609
624
|
const streamApprovalOn = resolveStreamApprovalGate({
|
|
@@ -777,7 +792,7 @@ export function createResumeLegs(ctx) {
|
|
|
777
792
|
let unregisterEngineNotice;
|
|
778
793
|
try {
|
|
779
794
|
const instructionLeg = createInstructionSelectionLeg();
|
|
780
|
-
const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills:
|
|
795
|
+
const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: args.legFacts?.skills });
|
|
781
796
|
result = await instructionLeg.run(sessionId, () => withModelReachScope({ sessionId, taskId }, () => withPrincipal(principal, async () => {
|
|
782
797
|
const resumeNotifiedKeys = new NotifiedKeys();
|
|
783
798
|
if (deps.runStore && taskId) {
|
|
@@ -832,9 +847,8 @@ export function createResumeLegs(ctx) {
|
|
|
832
847
|
if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
|
|
833
848
|
defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
|
|
834
849
|
}
|
|
835
|
-
const hadRow = isFleetAgentTerminalNotification(n) ? (fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false) : false;
|
|
836
850
|
const parked = !resumeLegLive && Boolean(deps.workflowCompletionInbox && sessionId);
|
|
837
|
-
deps.logger?.info?.("task_notification_observed", { route: "resume-verify", taskId: n.task_id, taskType: n.task_type, status: n.status,
|
|
851
|
+
deps.logger?.info?.("task_notification_observed", { route: "resume-verify", taskId: n.task_id, taskType: n.task_type, status: n.status, legLive: resumeLegLive, parkedDurable: parked });
|
|
838
852
|
if (parked) {
|
|
839
853
|
void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(sessionId, principal ?? null, n, Date.now(), taskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "resume-verify", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
|
|
840
854
|
}
|
|
@@ -856,7 +870,7 @@ export function createResumeLegs(ctx) {
|
|
|
856
870
|
resumeLegLive = true;
|
|
857
871
|
const vrEngine = await resumeWithVerification(legRunner, token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, resumeObjective, verifyRounds, verifyInternals);
|
|
858
872
|
const vrCause = cancelCtrl.cause();
|
|
859
|
-
await voidVerifierPauseCard(vrEngine,
|
|
873
|
+
await voidVerifierPauseCard(vrEngine, legRunner);
|
|
860
874
|
const vrJudged = atHostBoundary(vrEngine, sessionId);
|
|
861
875
|
if (vrJudged.terminal.kind === "paused")
|
|
862
876
|
heldPark = vrJudged;
|
|
@@ -939,7 +953,7 @@ export function createResumeLegs(ctx) {
|
|
|
939
953
|
const driveResume = async () => {
|
|
940
954
|
resumeLegLive = true;
|
|
941
955
|
const tailContentMode = resumeTaskConfig.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
942
|
-
const resumeWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage),
|
|
956
|
+
const resumeWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), args.legFacts?.sessionWitness);
|
|
943
957
|
const stream = await legRunner.resumeStream(token, outcome, { ...resumeTaskConfig, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, {
|
|
944
958
|
...launchInternals,
|
|
945
959
|
onForwardEvent: (e) => {
|
|
@@ -976,9 +990,8 @@ export function createResumeLegs(ctx) {
|
|
|
976
990
|
if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
|
|
977
991
|
defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
|
|
978
992
|
}
|
|
979
|
-
const hadRow = isFleetAgentTerminalNotification(n) ? (fleetPub?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false) : false;
|
|
980
993
|
const parked = !resumeLegLive && Boolean(deps.workflowCompletionInbox && sessionId);
|
|
981
|
-
deps.logger?.info?.("task_notification_observed", { route: "resume", taskId: n.task_id, taskType: n.task_type, status: n.status,
|
|
994
|
+
deps.logger?.info?.("task_notification_observed", { route: "resume", taskId: n.task_id, taskType: n.task_type, status: n.status, legLive: resumeLegLive, parkedDurable: parked });
|
|
982
995
|
if (parked) {
|
|
983
996
|
void deps.workflowCompletionInbox.enqueue(taskNotificationInboxEntry(sessionId, principal ?? null, n, Date.now(), taskId, opts?.priority)).catch((err) => deps.logger?.warn?.("park_enqueue_failed", { route: "resume", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
|
|
984
997
|
}
|
|
@@ -1307,7 +1320,7 @@ export function createResumeLegs(ctx) {
|
|
|
1307
1320
|
const owner = decodeCheckpointScope(scope ?? CHECKPOINT_PUBLIC_SCOPE);
|
|
1308
1321
|
return owner === undefined || owner === decider.principal;
|
|
1309
1322
|
}
|
|
1310
|
-
async function resumePreempted(sessionId, req, acceptEarly, decider) {
|
|
1323
|
+
async function resumePreempted(sessionId, req, acceptEarly, decider, restatement) {
|
|
1311
1324
|
const cs = deps.checkpointStore;
|
|
1312
1325
|
const RESUMABLE_GATE_KIND = "resource_limit";
|
|
1313
1326
|
const preemptBinding = { gateKinds: [RESUMABLE_GATE_KIND] };
|
|
@@ -1357,7 +1370,7 @@ export function createResumeLegs(ctx) {
|
|
|
1357
1370
|
return resumeCtxUnsealableAnswer(ctxRead);
|
|
1358
1371
|
if (ctxRead.kind === "absent")
|
|
1359
1372
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1360
|
-
const ctx = ctxRead.ctx;
|
|
1373
|
+
const ctx = restateResumeCtx(ctxRead.ctx, restatement);
|
|
1361
1374
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1362
1375
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1363
1376
|
if (!resolved.ok)
|
|
@@ -1367,13 +1380,13 @@ export function createResumeLegs(ctx) {
|
|
|
1367
1380
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
1368
1381
|
const verifyRounds = verifyRoundsFromBody(ctx.body);
|
|
1369
1382
|
const selectedGate = durableRowIdentity("decision-receipt", cp.checkpointId, token);
|
|
1370
|
-
return await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner:
|
|
1383
|
+
return await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner: resolved.runner, legFacts: resolved.legFacts, token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome: { gate: "resource_limit", decision: "continue" }, commitment: { kind: "gate_resolved", ...selectedGate }, verifyRounds, resumeCtx: { sessionId, ctx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
|
|
1371
1384
|
}
|
|
1372
1385
|
finally {
|
|
1373
1386
|
resolved.peerLease.release();
|
|
1374
1387
|
}
|
|
1375
1388
|
}
|
|
1376
|
-
async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId) {
|
|
1389
|
+
async function startFreshHostTurn(sessionId, objective, principal, req, workflowDecision, runId, restatement) {
|
|
1377
1390
|
const cs = deps.checkpointStore;
|
|
1378
1391
|
if (!deps.runStore) {
|
|
1379
1392
|
return { status: 501, body: refusalBody({ errorCode: "capability.run_store_required", message: "delivering this decision requires a durable run store (DB_BACKEND=mysql|pg|local)" }) };
|
|
@@ -1383,8 +1396,8 @@ export function createResumeLegs(ctx) {
|
|
|
1383
1396
|
return resumeCtxCorruptAnswer(ctxRead.where);
|
|
1384
1397
|
if (ctxRead.kind === "unsealable")
|
|
1385
1398
|
return resumeCtxUnsealableAnswer(ctxRead);
|
|
1386
|
-
const
|
|
1387
|
-
if (!
|
|
1399
|
+
const storedCtx = ctxRead.kind === "ok" ? ctxRead.ctx : null;
|
|
1400
|
+
if (!storedCtx) {
|
|
1388
1401
|
return {
|
|
1389
1402
|
status: 409,
|
|
1390
1403
|
body: refusalBody({
|
|
@@ -1397,6 +1410,7 @@ export function createResumeLegs(ctx) {
|
|
|
1397
1410
|
}),
|
|
1398
1411
|
};
|
|
1399
1412
|
}
|
|
1413
|
+
const ctx = restateResumeCtx(storedCtx, restatement);
|
|
1400
1414
|
const windowDenial = await usageWindowDenialFor(principal || GLOBAL_USAGE_KEY);
|
|
1401
1415
|
if (windowDenial)
|
|
1402
1416
|
return { status: windowDenial.status, body: windowDenial.body };
|
|
@@ -1408,7 +1422,7 @@ export function createResumeLegs(ctx) {
|
|
|
1408
1422
|
const { spec, engineNotices } = resolved;
|
|
1409
1423
|
const hostSpec = { ...spec, objective };
|
|
1410
1424
|
const out = await mintAndDriveDurableRun(routeCtxBase, deps.runStore, {
|
|
1411
|
-
prepared: { spec: hostSpec, runner:
|
|
1425
|
+
prepared: { spec: hostSpec, runner: resolved.runner, legFacts: resolved.legFacts, auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), peerLease: resolved.peerLease, execution: resolved.execution, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
|
|
1412
1426
|
fleetScope: resumeFleetScope(req, auth),
|
|
1413
1427
|
elicitOwner: principal ?? null,
|
|
1414
1428
|
source: "workflow_parked_decide",
|
|
@@ -1427,7 +1441,7 @@ export function createResumeLegs(ctx) {
|
|
|
1427
1441
|
resolved.peerLease.release();
|
|
1428
1442
|
}
|
|
1429
1443
|
}
|
|
1430
|
-
async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor) {
|
|
1444
|
+
async function resumeWake(sessionId, message, caller, req, acceptEarly, workflowDecision, ownerLeaseAdmittedFor, restatement) {
|
|
1431
1445
|
const cs = deps.checkpointStore;
|
|
1432
1446
|
const wakeBinding = { gateKinds: TERMINAL_PARK_GATE_KINDS, pick: "oldest" };
|
|
1433
1447
|
const token = (await cs.findPendingTokenBySession(sessionId, undefined, wakeBinding)) ?? (await cs.findPendingTokenBySession(sessionId));
|
|
@@ -1484,7 +1498,7 @@ export function createResumeLegs(ctx) {
|
|
|
1484
1498
|
return resumeCtxUnsealableAnswer(ctxRead);
|
|
1485
1499
|
if (ctxRead.kind === "absent")
|
|
1486
1500
|
return { status: 409, body: refusalBody({ errorCode: "conflict.resume_context_unavailable", message: "resume context missing — cannot rebuild task config" }) };
|
|
1487
|
-
const ctx = ctxRead.ctx;
|
|
1501
|
+
const ctx = restateResumeCtx(ctxRead.ctx, restatement);
|
|
1488
1502
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1489
1503
|
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1490
1504
|
if (!resolved.ok)
|
|
@@ -1495,7 +1509,8 @@ export function createResumeLegs(ctx) {
|
|
|
1495
1509
|
return await driveResumeIntoRunLog({
|
|
1496
1510
|
peerLease: resolved.peerLease,
|
|
1497
1511
|
execution: resolved.execution,
|
|
1498
|
-
runner:
|
|
1512
|
+
runner: resolved.runner,
|
|
1513
|
+
legFacts: resolved.legFacts,
|
|
1499
1514
|
token,
|
|
1500
1515
|
sessionId: cp.sessionId,
|
|
1501
1516
|
principal: auth.principal,
|
|
@@ -1595,8 +1610,9 @@ export function createResumeLegs(ctx) {
|
|
|
1595
1610
|
return refuse(refuseStoredBodyReplay(sessionId, replay));
|
|
1596
1611
|
return { ok: true, prepared: { sessionId, token, cp, ctx: ctxRead.ctx } };
|
|
1597
1612
|
}
|
|
1598
|
-
async function drivePlanReview(prepared, decision, editedPlan, reason, hostDecision, req, acceptEarly, permissionModeAfter) {
|
|
1599
|
-
const { sessionId, token, cp
|
|
1613
|
+
async function drivePlanReview(prepared, decision, editedPlan, reason, hostDecision, req, acceptEarly, permissionModeAfter, restatement) {
|
|
1614
|
+
const { sessionId, token, cp } = prepared;
|
|
1615
|
+
const ctx = restateResumeCtx(prepared.ctx, restatement);
|
|
1600
1616
|
if ((await deps.checkpointStore.findPendingTokenBySession(sessionId, undefined, { gateKinds: ["plan_review"] })) !== token)
|
|
1601
1617
|
return notResumableAnswer();
|
|
1602
1618
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
@@ -1621,20 +1637,26 @@ export function createResumeLegs(ctx) {
|
|
|
1621
1637
|
hostDecision,
|
|
1622
1638
|
};
|
|
1623
1639
|
const selectedGate = durableRowIdentity("decision-receipt", cp.checkpointId, token);
|
|
1624
|
-
const out = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner:
|
|
1640
|
+
const out = await driveResumeIntoRunLog({ peerLease: resolved.peerLease, execution: resolved.execution, runner: resolved.runner, legFacts: resolved.legFacts, token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, commitment: { kind: "gate_resolved", ...selectedGate }, verifyRounds, resumeCtx: { sessionId, ctx: rebuildCtx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
|
|
1625
1641
|
return out;
|
|
1626
1642
|
}
|
|
1627
1643
|
finally {
|
|
1628
1644
|
resolved.peerLease.release();
|
|
1629
1645
|
}
|
|
1630
1646
|
}
|
|
1647
|
+
const sessionNotifyDoor = createSessionNotifyDoor({
|
|
1648
|
+
activeTaskIdOf: async (sessionId) => deps.runStore?.getActiveTaskId?.(sessionId),
|
|
1649
|
+
liveRuns: steerableRuns,
|
|
1650
|
+
inbox: deps.workflowCompletionInbox,
|
|
1651
|
+
headOf: headReadOf(deps.sessionStorage),
|
|
1652
|
+
});
|
|
1631
1653
|
const routeCtxBase = {
|
|
1632
1654
|
deps,
|
|
1633
1655
|
registry: { idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters },
|
|
1634
1656
|
helpers: { readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk },
|
|
1635
1657
|
local: { images: createImagesLocal(deps), sessions: createSessionsLocal(), sessionSync: createSessionSyncLocal(), capabilities: createCapabilitiesLocal() },
|
|
1636
|
-
legs: { prepareSpec, finalizeTaskResult, resumeCheckpoint, driveResumeIntoRunLog, resumePreempted, resumeWake, preparePlanReview, drivePlanReview },
|
|
1658
|
+
legs: { prepareSpec, finalizeTaskResult, resumeCheckpoint, driveResumeIntoRunLog, resumePreempted, resumeWake, preparePlanReview, drivePlanReview, sessionNotify: sessionNotifyDoor },
|
|
1637
1659
|
};
|
|
1638
|
-
return { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, mayDiscloseRowError, resumePreempted, startFreshHostTurn, resumeWake, preparePlanReview, drivePlanReview };
|
|
1660
|
+
return { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, mayDiscloseRowError, resumePreempted, startFreshHostTurn, resumeWake, preparePlanReview, drivePlanReview, sessionNotifyDoor };
|
|
1639
1661
|
}
|
|
1640
1662
|
//# sourceMappingURL=resume-legs.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 续跑请求的**重述键** —— 「我此刻的对端表」随续跑请求一起说出来(唯一属主)。
|
|
3
|
+
*
|
|
4
|
+
* ## 律(一条)
|
|
5
|
+
* 续跑请求体可带 {@link RESUME_RESTATABLE_KEYS} 里的键(今天两只:`mcpServers` / `a2aPeers`),键名与值形 = 提交体(`POST /v1/tasks`)的同一键:
|
|
6
|
+
* · **在场**(值不是 `undefined` / `null`)⇒ 先过提交腿**同一只**判官(`assertRequestMcpEntries` / `assertRequestA2aEntries`;坏形 = 与提交腿同码同句的
|
|
7
|
+
* 400,判在续跑腿任何状态改动之前),再**取代**续跑重建输入(`checkpoint_ctx` 体)里的那一键:本次重建读它,续跑赢下 CAS 之后随重建输入一起写回
|
|
8
|
+
* (`DriveResumeArgs.resumeCtx` 那一只写点)⇒ 之后的续跑在没有新重述时重放的也是它;
|
|
9
|
+
* · **缺席**(或 `null`,与提交体「缺席或 `null` = 没声明」同一读法)⇒ 存量体那一键原样重放(冻结读法)。
|
|
10
|
+
* 重建之后的路与存量体逐字同一条(`resolveSpec` 的续跑腿:部署锁、闸、部署保留名、合并、对端座登记、用户面通告),所以一份重述在本部署上能挂上什么,
|
|
11
|
+
* 与它作为一次新提交能挂上什么是同一个答案;引擎从 `ResumeTaskConfig.mcp` / `.a2a`(与 `TaskSpec` 同键)收到的就是这张表。
|
|
12
|
+
*
|
|
13
|
+
* ## 不在本仓判的
|
|
14
|
+
* 「停驻快照里那一只调用的目标台还在不在当前表里」是引擎续跑准入的判(引擎 7.61.0 起三码各答各形,以 core 为准:工具不在了 ⇒ `resume.tool_unavailable`、同名而已不是批准时那一只 ⇒ `resume.tool_contract_mismatch`(两者重开检查点)、还是那一只而面或声明变了 ⇒ `resume.target_moved`(否决);本服务逐字过境)—— 本服务只负责把当前表送到,不自判、不预拒。
|
|
15
|
+
*
|
|
16
|
+
* ## 为什么是一张闭表
|
|
17
|
+
* 与提交腿的一次性动作那张闭表(`resume-legs.ts` 的 `ONE_SHOT_SUBMIT_KEYS`:重建时剥掉)对称 —— 那张说「哪些提交键不随重建走」,这张说「哪些提交键由续跑请求重述」。
|
|
18
|
+
* 加一键 = 改这条律本身:判官表 {@link RESTATEMENT_JUDGES} 缺行即编译红,接收它的续跑门各自在键闭集里声明它。
|
|
19
|
+
*/
|
|
20
|
+
import type { TaskRequestBody } from "./wire-types.js";
|
|
21
|
+
import type { CheckpointCtx } from "./route-ctx.js";
|
|
22
|
+
/** 续跑请求可重述的提交键(闭集;`satisfies` 钉在提交体的具名键上 —— 写一个提交体上没有的词 = 编译红)。 */
|
|
23
|
+
export declare const RESUME_RESTATABLE_KEYS: readonly ["a2aPeers", "mcpServers"];
|
|
24
|
+
export type ResumeRestatableKey = (typeof RESUME_RESTATABLE_KEYS)[number];
|
|
25
|
+
/** 一次续跑请求读出的重述(只含在场的键;读者 {@link readResumeRestatement},用者 {@link restateResumeCtx})。 */
|
|
26
|
+
export type ResumeRestatement = {
|
|
27
|
+
readonly [K in ResumeRestatableKey]?: TaskRequestBody[K];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 从一份续跑请求体里读出重述:在场的键逐一过判官,全部过 ⇒ 交回只含在场键的重述;一键都不在 ⇒ `undefined`(冻结读法)。
|
|
31
|
+
* 判官的拒原样抛出(调用方是路由,`HttpError` 由分发器按提交腿同一只渲染器落 wire)。体不是对象 ⇒ `undefined`(体形由各门自己的根形门答)。
|
|
32
|
+
*/
|
|
33
|
+
export declare function readResumeRestatement(body: unknown): ResumeRestatement | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* 把重述盖进续跑重建输入:在场的键取代体里的同名键,其余位(记忆域 / 无人值守标 / 读法记号 / 写者版本)原样随行。`undefined` ⇒ 原 ctx 原样(同一个对象)。
|
|
36
|
+
* 各续跑腿在读出 ctx 的那一拍调一次,之后重建(`resolveResumeSpecOrBlocked`)与写回(`resumeCtx`)读的都是这一份。
|
|
37
|
+
*/
|
|
38
|
+
export declare function restateResumeCtx(ctx: CheckpointCtx, restatement: ResumeRestatement | undefined): CheckpointCtx;
|
|
39
|
+
//# sourceMappingURL=resume-restatement.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { assertRequestMcpEntries } from "../task-mcp.js";
|
|
2
|
+
import { assertRequestA2aEntries } from "../task-a2a.js";
|
|
3
|
+
export const RESUME_RESTATABLE_KEYS = ["a2aPeers", "mcpServers"];
|
|
4
|
+
const RESTATEMENT_JUDGES = {
|
|
5
|
+
a2aPeers: assertRequestA2aEntries,
|
|
6
|
+
mcpServers: assertRequestMcpEntries,
|
|
7
|
+
};
|
|
8
|
+
export function readResumeRestatement(body) {
|
|
9
|
+
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
10
|
+
return undefined;
|
|
11
|
+
const raw = body;
|
|
12
|
+
let out;
|
|
13
|
+
for (const key of RESUME_RESTATABLE_KEYS) {
|
|
14
|
+
const value = Object.hasOwn(raw, key) ? raw[key] : undefined;
|
|
15
|
+
if (value === undefined || value === null)
|
|
16
|
+
continue;
|
|
17
|
+
RESTATEMENT_JUDGES[key](value);
|
|
18
|
+
(out ??= {})[key] = value;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
export function restateResumeCtx(ctx, restatement) {
|
|
23
|
+
return restatement === undefined ? ctx : { ...ctx, body: { ...ctx.body, ...restatement } };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=resume-restatement.js.map
|