@sema-agent/server 7.148.0 → 7.149.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 +429 -0
- package/README.md +16 -1
- package/README.zh-CN.md +4 -0
- package/USAGE.md +89 -55
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +32 -32
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/resolve-spec.js +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +2 -1
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +5 -4
- package/dist/capability-facts.js +3 -14
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +36 -32
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +37 -5
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/admission.js +3 -3
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/claim-heartbeat.d.ts +3 -3
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +5 -3
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +30 -11
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +3 -2
- package/dist/http/owner-access.js +2 -3
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +8 -2
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +244 -92
- package/dist/http/route-ctx.d.ts +6 -0
- package/dist/http/routes/a2a-serve.js +2 -0
- package/dist/http/routes/approvals-assistant.js +3 -2
- package/dist/http/routes/capabilities.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/runs.js +10 -7
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +46 -34
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/tasks.js +127 -93
- package/dist/http/server.d.ts +4 -5
- package/dist/http/server.js +30 -5
- package/dist/http/submit-ctx.d.ts +1 -0
- package/dist/http/submit-ctx.js +2 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leg-facts.d.ts +7 -2
- package/dist/leg-facts.js +1 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/fail-open.d.ts +14 -2
- package/dist/observability/fail-open.js +14 -2
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +3 -2
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +50 -22
- package/dist/plugins/local-checkpoint-store.d.ts +4 -0
- package/dist/plugins/local-checkpoint-store.js +2 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +47 -21
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +46 -20
- package/dist/plugins/one-time-migrations.js +102 -79
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +10 -12
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +1 -7
- package/dist/plugins/remote-env-e2b.js +10 -7
- package/dist/plugins/remote-env-host.d.ts +50 -9
- package/dist/plugins/remote-env-host.js +77 -20
- package/dist/plugins/remote-env-k8s.js +4 -2
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +8 -23
- package/dist/plugins/run-store-sql.d.ts +26 -7
- package/dist/plugins/run-store-sql.js +44 -16
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +3 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +4 -3
- package/dist/plugins/schema-consistency.d.ts +89 -5
- package/dist/plugins/schema-consistency.js +176 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +16 -0
- package/dist/plugins/stop-then-start-sentence.js +7 -0
- package/dist/plugins/store-backend.d.ts +22 -8
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +33 -3
- package/dist/plugins/store-contracts.js +16 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +18 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +19 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +3 -2
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
- package/dist/stores-sql/checkpoint-store-sql.js +3 -5
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +23 -7
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.js +1 -0
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +44 -36
- package/dist/tool-approval.js +66 -47
- package/dist/trace/engine-notice-wire.d.ts +24 -21
- package/dist/trace/engine-notice-wire.js +7 -3
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +4 -2
- package/dist/trace/project.js +8 -7
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +131 -52
- package/dist/trace/self-minted-wire.js +71 -39
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +3 -1
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/dist/plain-string-record.js +0 -70
|
@@ -3,13 +3,16 @@ import { HttpError, createCodedHttpError, ssoVerifiedScope } from "../security.j
|
|
|
3
3
|
import { gatedPerson } from "./principal-gate.js";
|
|
4
4
|
import { DEVICE_ERROR_CODES, assertDeviceIdShape } from "../device-store.js";
|
|
5
5
|
import { buildDeviceRejection } from "../device-enrollment.js";
|
|
6
|
-
import { sameOccupant } from "../execution-placement.js";
|
|
6
|
+
import { SessionForkInProgressError, sameOccupant } from "../execution-placement.js";
|
|
7
7
|
import { deployedExecutionLanes } from "../execution-lane-caps.js";
|
|
8
8
|
import { gateExecutionLane } from "../runtime-caps-resolver.js";
|
|
9
|
-
import { releaseClaimedRun } from "./claimed-run.js";
|
|
9
|
+
import { releaseClaimedRun, withdrawClaimedRun } from "./claimed-run.js";
|
|
10
10
|
import { sessionIdRetiredFor } from "../deleted-session-ids.js";
|
|
11
11
|
import { refusalBody } from "./refusal-body.js";
|
|
12
12
|
import { timeClaim } from "./claim-latency.js";
|
|
13
|
+
import { storeFaultClassOf } from "../plugins/store-fault.js";
|
|
14
|
+
import { resumeClaimFaultAnswer } from "./resume-ctx-read.js";
|
|
15
|
+
import { resumeAssemblyRefusalAnswer, resumeRefusalWithCard } from "./checkpoint-ctx.js";
|
|
13
16
|
export async function resolveExecutionContext(deps, entry) {
|
|
14
17
|
const lanes = deployedExecutionLanes(deps.config);
|
|
15
18
|
switch (entry.kind) {
|
|
@@ -70,8 +73,7 @@ function laneOf(root, cell, lanes) {
|
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
function sessionMirrorError(root) {
|
|
73
|
-
|
|
74
|
-
return createCodedHttpError(answer.status, answer.body.errorCode, answer.body.error, { forkPath: forkPathOf(root) });
|
|
76
|
+
return refusalHttpError(sessionMirrorAnswer(root));
|
|
75
77
|
}
|
|
76
78
|
export function forkPathOf(root) {
|
|
77
79
|
return `/v1/sessions/${encodeURIComponent(root)}/fork`;
|
|
@@ -87,8 +89,7 @@ function sessionMirrorAnswer(root) {
|
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
function rootSessionGoneError() {
|
|
90
|
-
|
|
91
|
-
return createCodedHttpError(absent.status, absent.body.errorCode, absent.body.error);
|
|
92
|
+
return refusalHttpError(rootSessionAbsentAnswer());
|
|
92
93
|
}
|
|
93
94
|
export function isRootSessionGoneError(err) {
|
|
94
95
|
return err instanceof HttpError && err.code === rootSessionAbsentAnswer().body.errorCode;
|
|
@@ -142,8 +143,8 @@ async function admitDevicePlacement(deps, entry, lane, seat, reading) {
|
|
|
142
143
|
try {
|
|
143
144
|
admitted = await deps.deviceEnrollment.admitSession({ rootSessionId: entry.root, placement: reading, requester: owner, ...(requestedDeviceId === undefined ? {} : { requestedDeviceId }) });
|
|
144
145
|
}
|
|
145
|
-
catch {
|
|
146
|
-
throw asHttpError(registryUnavailable
|
|
146
|
+
catch (e) {
|
|
147
|
+
throw asHttpError(faultedAnswerOf(e, registryUnavailable));
|
|
147
148
|
}
|
|
148
149
|
if (admitted.ok)
|
|
149
150
|
return { rootSessionId: entry.root, occupant: reading.occupant, owner, admittedDeviceId: admitted.value.deviceId, firstBind: false };
|
|
@@ -153,8 +154,8 @@ async function admitDevicePlacement(deps, entry, lane, seat, reading) {
|
|
|
153
154
|
try {
|
|
154
155
|
view = await deps.deviceStore.readDeviceForAdmission(requestedDeviceId, owner);
|
|
155
156
|
}
|
|
156
|
-
catch {
|
|
157
|
-
throw asHttpError(registryUnavailable
|
|
157
|
+
catch (e) {
|
|
158
|
+
throw asHttpError(faultedAnswerOf(e, registryUnavailable));
|
|
158
159
|
}
|
|
159
160
|
if (!view)
|
|
160
161
|
throw asHttpError(buildDeviceRejection(DEVICE_ERROR_CODES.NOT_FOUND, "device not found"));
|
|
@@ -169,8 +170,20 @@ export function stampSpecRoot(spec, ctx, submission) {
|
|
|
169
170
|
if (named !== undefined && named !== ctx.root) {
|
|
170
171
|
throw createCodedHttpError(400, "request.precondition_unmet", "the deployment's spec resolver named a session other than the one this submission was authorized for — a submission's root session comes only from the authorizer, so resolveSpec must not set sessionId (read opts.execution.root instead)");
|
|
171
172
|
}
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
switch (submission.use) {
|
|
174
|
+
case "session": {
|
|
175
|
+
const mustExist = ctx.occupant !== undefined || submission.requireExistingSession === true;
|
|
176
|
+
return mustExist ? { ...spec, sessionId: ctx.root, requireExistingSession: true } : { ...spec, sessionId: ctx.root };
|
|
177
|
+
}
|
|
178
|
+
case "ladder_host": {
|
|
179
|
+
const { requireExistingSession: _noAcquireOnALadder, ...ladderSpec } = spec;
|
|
180
|
+
return { ...ladderSpec, sessionId: ctx.root };
|
|
181
|
+
}
|
|
182
|
+
default: {
|
|
183
|
+
const unhandled = submission;
|
|
184
|
+
throw new Error(`stampSpecRoot: unhandled root use ${JSON.stringify(unhandled)}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
174
187
|
}
|
|
175
188
|
export function rootSessionAbsentAnswer() {
|
|
176
189
|
return {
|
|
@@ -181,6 +194,62 @@ export function rootSessionAbsentAnswer() {
|
|
|
181
194
|
}),
|
|
182
195
|
};
|
|
183
196
|
}
|
|
197
|
+
export function isRootSessionGoneRefusal(refusal) {
|
|
198
|
+
return refusal.body.errorCode === rootSessionAbsentAnswer().body.errorCode;
|
|
199
|
+
}
|
|
200
|
+
export function faultedAnswerOf(e, answer) {
|
|
201
|
+
const fault = storeFaultClassOf(e);
|
|
202
|
+
switch (fault) {
|
|
203
|
+
case "corrupt":
|
|
204
|
+
case "connection":
|
|
205
|
+
case "deployment":
|
|
206
|
+
case "unclassified":
|
|
207
|
+
return answer();
|
|
208
|
+
case "defect":
|
|
209
|
+
throw e;
|
|
210
|
+
default: {
|
|
211
|
+
const unhandled = fault;
|
|
212
|
+
throw new Error(`faultedAnswerOf: the store-fault judge answered a class this reader does not place: ${String(unhandled)}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export function runStoreUnavailableAnswer() {
|
|
217
|
+
return {
|
|
218
|
+
status: 503,
|
|
219
|
+
body: refusalBody({ errorCode: "state.run_store_unavailable", message: "this task could not be recorded just now (the store did not answer), so nothing was started. Send it again later; if this keeps happening, ask whoever operates this deployment to check its database." }),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export const SUBMISSION_CLAIM_FACE = { refused: (refusal) => refusal, faulted: runStoreUnavailableAnswer };
|
|
223
|
+
export const CONTINUATION_CLAIM_FACE = {
|
|
224
|
+
refused: (refusal) => (isRootSessionGoneRefusal(refusal) ? refusal : cardWaitingRefusalAnswer(refusal)),
|
|
225
|
+
faulted: resumeClaimFaultAnswer,
|
|
226
|
+
};
|
|
227
|
+
export function cardWaitingRefusalAnswer(refusal) {
|
|
228
|
+
const { error, errorCode, ...facts } = refusal.body;
|
|
229
|
+
return resumeRefusalWithCard(resumeAssemblyRefusalAnswer(refusal.status, { errorCode, message: error, facts }), true);
|
|
230
|
+
}
|
|
231
|
+
export function refusalHttpError(refusal) {
|
|
232
|
+
const { error, errorCode, ...facts } = refusal.body;
|
|
233
|
+
return createCodedHttpError(refusal.status, errorCode, error, Object.keys(facts).length > 0 ? facts : undefined);
|
|
234
|
+
}
|
|
235
|
+
export async function resolveBeforeClaim(deps, entry, face) {
|
|
236
|
+
try {
|
|
237
|
+
return await resolveExecutionContext(deps, entry);
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
if (e instanceof HttpError)
|
|
241
|
+
throw e;
|
|
242
|
+
throw new ClaimFaultAnswer(faultedAnswerOf(e, face.faulted));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
export class ClaimFaultAnswer extends HttpError {
|
|
246
|
+
answer;
|
|
247
|
+
constructor(answer) {
|
|
248
|
+
const { error, errorCode: code, ...facts } = answer.body;
|
|
249
|
+
super(answer.status, error, Object.keys(facts).length > 0 ? { code, extra: facts } : { code });
|
|
250
|
+
this.answer = answer;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
184
253
|
export function placementWriteDeadlineMs(runStaleSec) {
|
|
185
254
|
const stale = typeof runStaleSec === "number" && Number.isFinite(runStaleSec) && runStaleSec > 0 ? runStaleSec : RUN_STALE_SEC_DEFAULT;
|
|
186
255
|
return Math.min(15_000, Math.max(1_000, Math.floor((stale * 1000) / 4)));
|
|
@@ -209,7 +278,7 @@ export async function commitExecutionContext(deps, claim, ctx) {
|
|
|
209
278
|
return timeClaim(deps.metrics, "placement_commit", commitPlacement(deps, claim, ctx, seat, occupant));
|
|
210
279
|
}
|
|
211
280
|
async function commitPlacement(deps, claim, ctx, seat, occupant) {
|
|
212
|
-
const release = (why) =>
|
|
281
|
+
const release = (why, ending) => withdrawClaim(deps, claim, why, ending);
|
|
213
282
|
const deadlineMs = placementWriteDeadlineMs(deps.config?.runStaleSec);
|
|
214
283
|
const withDeadline = async (work) => {
|
|
215
284
|
let timer;
|
|
@@ -227,56 +296,64 @@ async function commitPlacement(deps, claim, ctx, seat, occupant) {
|
|
|
227
296
|
clearTimeout(timer);
|
|
228
297
|
}
|
|
229
298
|
};
|
|
230
|
-
const goneAnswer = () => (claim.kind === "parked" ? claim.goneAnswer : rootSessionAbsentAnswer());
|
|
231
299
|
let refusal;
|
|
232
300
|
try {
|
|
233
301
|
refusal =
|
|
234
302
|
ctx.device !== undefined
|
|
235
|
-
? await commitDevice(deps, seat, ctx.device, withDeadline
|
|
303
|
+
? await commitDevice(deps, seat, ctx.device, withDeadline)
|
|
236
304
|
: ctx.placement === "absent"
|
|
237
|
-
? await commitPlace(seat, ctx, occupant, withDeadline
|
|
238
|
-
: await reassertWriter(seat, ctx, occupant, withDeadline
|
|
305
|
+
? await commitPlace(seat, ctx, occupant, withDeadline)
|
|
306
|
+
: await reassertWriter(seat, ctx, occupant, withDeadline);
|
|
239
307
|
}
|
|
240
308
|
catch (err) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (ctx.device !== undefined) {
|
|
244
|
-
refusal = deviceRefusal(registryUnavailable());
|
|
309
|
+
if (err instanceof SessionForkInProgressError) {
|
|
310
|
+
refusal = { status: 409, body: refusalBody({ errorCode: err.code, message: err.message }) };
|
|
245
311
|
deps.metrics?.inc("execution_placement_rejects_total", { errorCode: refusal.body.errorCode });
|
|
312
|
+
await release(`execution placement rejected: [${refusal.body.errorCode}] ${refusal.body.error}`, "refused");
|
|
246
313
|
return refusal;
|
|
247
314
|
}
|
|
315
|
+
deps.logger?.warn("execution_placement_commit_failed", { root: ctx.root, lane: ctx.lane, placement: ctx.placement, deadlineMs, err: err instanceof Error ? err.message : String(err) });
|
|
316
|
+
await release(`execution placement could not be committed: ${err instanceof Error ? err.message : String(err)}`, "faulted");
|
|
248
317
|
throw err;
|
|
249
318
|
}
|
|
250
319
|
if (refusal === null)
|
|
251
320
|
return null;
|
|
252
321
|
deps.metrics?.inc("execution_placement_rejects_total", { errorCode: refusal.body.errorCode });
|
|
253
|
-
await release(`execution placement rejected: [${refusal.body.errorCode}] ${refusal.body.error}
|
|
322
|
+
await release(`execution placement rejected: [${refusal.body.errorCode}] ${refusal.body.error}`, isRootSessionGoneRefusal(refusal) ? "gone" : "refused");
|
|
254
323
|
return refusal;
|
|
255
324
|
}
|
|
256
|
-
async function
|
|
325
|
+
export async function withdrawClaim(deps, claim, why, ending) {
|
|
326
|
+
const loud = (taskId, step, err) => {
|
|
327
|
+
deps.logger?.warn("execution_placement_run_release_failed", { taskId, step, err: err instanceof Error ? err.message : String(err) });
|
|
328
|
+
deps.metrics?.inc("execution_placement_run_release_failed_total");
|
|
329
|
+
};
|
|
257
330
|
switch (claim.kind) {
|
|
258
331
|
case "run":
|
|
259
|
-
await
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
332
|
+
await withdrawClaimedRun(claim.runStore, claim.taskId, claim.legId, (err, step) => loud(claim.taskId, step, err));
|
|
333
|
+
return;
|
|
334
|
+
case "resumed":
|
|
335
|
+
if (ending !== "gone") {
|
|
336
|
+
await claim.restore();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
await releaseClaimedRun(claim.runStore, claim.taskId, why, (err, step) => loud(claim.taskId, step, err));
|
|
263
340
|
return;
|
|
264
341
|
case "parked":
|
|
265
342
|
await claim.rollback(why);
|
|
266
343
|
return;
|
|
267
344
|
default: {
|
|
268
345
|
const unhandled = claim;
|
|
269
|
-
throw new Error(`
|
|
346
|
+
throw new Error(`withdrawClaim: unknown claim ${JSON.stringify(unhandled)}`);
|
|
270
347
|
}
|
|
271
348
|
}
|
|
272
349
|
}
|
|
273
|
-
async function commitPlace(seat, ctx, occupant, withDeadline
|
|
350
|
+
async function commitPlace(seat, ctx, occupant, withDeadline) {
|
|
274
351
|
if (ctx.lane === "device") {
|
|
275
352
|
return deviceRefusal(buildDeviceRejection(DEVICE_ERROR_CODES.NOT_BOUND, "this session has no device binding — nothing was started"));
|
|
276
353
|
}
|
|
277
|
-
return placeOutcomeAnswer(ctx.root, await withDeadline(seat.place(ctx.root, occupant, { role: "writer", lane: ctx.lane }))
|
|
354
|
+
return placeOutcomeAnswer(ctx.root, await withDeadline(seat.place(ctx.root, occupant, { role: "writer", lane: ctx.lane })));
|
|
278
355
|
}
|
|
279
|
-
function placeOutcomeAnswer(root, outcome
|
|
356
|
+
function placeOutcomeAnswer(root, outcome) {
|
|
280
357
|
switch (outcome.outcome) {
|
|
281
358
|
case "placed":
|
|
282
359
|
case "same":
|
|
@@ -286,17 +363,17 @@ function placeOutcomeAnswer(root, outcome, goneAnswer) {
|
|
|
286
363
|
case "mirror":
|
|
287
364
|
return sessionMirrorAnswer(root);
|
|
288
365
|
case "gone":
|
|
289
|
-
return
|
|
366
|
+
return rootSessionAbsentAnswer();
|
|
290
367
|
default: {
|
|
291
368
|
const unhandled = outcome;
|
|
292
369
|
throw new Error(`commitExecutionContext: no answer for ${JSON.stringify(unhandled)}`);
|
|
293
370
|
}
|
|
294
371
|
}
|
|
295
372
|
}
|
|
296
|
-
async function reassertWriter(seat, ctx, occupant, withDeadline
|
|
373
|
+
async function reassertWriter(seat, ctx, occupant, withDeadline) {
|
|
297
374
|
const now = await withDeadline(seat.read(ctx.root));
|
|
298
375
|
if (now === undefined || !sameOccupant(now.occupant, occupant))
|
|
299
|
-
return
|
|
376
|
+
return rootSessionAbsentAnswer();
|
|
300
377
|
const cell = now.cell;
|
|
301
378
|
switch (cell.role) {
|
|
302
379
|
case "writer":
|
|
@@ -312,20 +389,31 @@ async function reassertWriter(seat, ctx, occupant, withDeadline, goneAnswer) {
|
|
|
312
389
|
}
|
|
313
390
|
}
|
|
314
391
|
}
|
|
315
|
-
async function commitDevice(deps, seat, intent, withDeadline
|
|
392
|
+
async function commitDevice(deps, seat, intent, withDeadline) {
|
|
316
393
|
const enrollment = deps.deviceEnrollment;
|
|
317
394
|
if (!enrollment) {
|
|
318
395
|
throw new Error("commitExecutionContext: a device placement intent was minted without a DeviceEnrollment — assembly bug (both are read from the same ServiceDeps)");
|
|
319
396
|
}
|
|
397
|
+
const registry = async (work) => {
|
|
398
|
+
try {
|
|
399
|
+
return { ok: true, value: await withDeadline(work) };
|
|
400
|
+
}
|
|
401
|
+
catch (e) {
|
|
402
|
+
return { ok: false, refusal: deviceRefusal(faultedAnswerOf(e, registryUnavailable)) };
|
|
403
|
+
}
|
|
404
|
+
};
|
|
320
405
|
if (intent.firstBind) {
|
|
321
|
-
const
|
|
406
|
+
const binding = await registry(enrollment.bindPlacement({ rootSessionId: intent.rootSessionId, occupant: intent.occupant, deviceId: intent.admittedDeviceId, requester: intent.owner }));
|
|
407
|
+
if (!binding.ok)
|
|
408
|
+
return binding.refusal;
|
|
409
|
+
const bound = binding.value;
|
|
322
410
|
if (bound.ok)
|
|
323
411
|
return null;
|
|
324
412
|
if ("reject" in bound)
|
|
325
413
|
return deviceRefusal(bound.reject);
|
|
326
414
|
switch (bound.placement) {
|
|
327
415
|
case "gone":
|
|
328
|
-
return
|
|
416
|
+
return rootSessionAbsentAnswer();
|
|
329
417
|
case "mirror":
|
|
330
418
|
return sessionMirrorAnswer(intent.rootSessionId);
|
|
331
419
|
case "tree_bound":
|
|
@@ -338,8 +426,10 @@ async function commitDevice(deps, seat, intent, withDeadline, goneAnswer) {
|
|
|
338
426
|
}
|
|
339
427
|
const placement = await withDeadline(seat.read(intent.rootSessionId));
|
|
340
428
|
if (placement === undefined || !sameOccupant(placement.occupant, intent.occupant))
|
|
341
|
-
return
|
|
342
|
-
const
|
|
343
|
-
|
|
429
|
+
return rootSessionAbsentAnswer();
|
|
430
|
+
const readmission = await registry(enrollment.admitSession({ rootSessionId: intent.rootSessionId, placement, requester: intent.owner, requestedDeviceId: intent.admittedDeviceId }));
|
|
431
|
+
if (!readmission.ok)
|
|
432
|
+
return readmission.refusal;
|
|
433
|
+
return readmission.value.ok ? null : deviceRefusal(readmission.value.reject);
|
|
344
434
|
}
|
|
345
435
|
//# sourceMappingURL=execution-context.js.map
|
|
@@ -43,8 +43,9 @@ export interface OwnerAccessWiring {
|
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* 🔴 **一次请求的调用方**(唯一铸点之一;另一只是决定口的 {@link callerOfDecision}、提交挂接的 `security.ts` `claimCallerOf`)。
|
|
46
|
-
* 主体 = `gatedSubject`(验身份的那一只;带场景头 ⇒ 这个人的那一格分区);运维 = `explicitOperatorOk(personOf(主体))`(唯一读法,读人);fleet =
|
|
47
|
-
*
|
|
46
|
+
* 主体 = `gatedSubject`(验身份的那一只;带场景头 ⇒ 这个人的那一格分区);运维 = `explicitOperatorOk(personOf(主体))`(唯一读法,读人);fleet = `actsForDeployment`
|
|
47
|
+
* (唯一读法:报了主体 ⇒ 不是 fleet;不报主体 ⇒ 部署没有身份边界时 `dev_open`、出示部署让它代自己说话的 service 凭据时 `deployment_token`,两者互斥 —— 前者要求没配凭据);
|
|
48
|
+
* 只读 trace 凭据单列(它只在读档算 fleet,见面表 `fleet` 列)。报了主体的调用方因此在每一口都是那位主体,与提交挂接的 `claimCallerOf`(fleet 恒缺)同读。
|
|
48
49
|
*/
|
|
49
50
|
export declare function callerOf(req: IncomingMessage, w: Pick<OwnerAccessWiring, "config">): Caller;
|
|
50
51
|
/**
|
|
@@ -5,16 +5,15 @@ import { mintOwnedRow, partsOf, personOf, subjectOf } from "../owner-brands.js";
|
|
|
5
5
|
import { sessionCellFor } from "../security.js";
|
|
6
6
|
import { openFaceFaultWire, SessionOwnerUnreadableError } from "../session-error-wire.js";
|
|
7
7
|
import { seatText } from "../trace/exit-seat.js";
|
|
8
|
-
import { actsForDeployment,
|
|
8
|
+
import { actsForDeployment, explicitOperatorOk, gatedSubject, sendPrincipalRefusal, sendUnauthorized, traceTokenPresented } from "./principal-gate.js";
|
|
9
9
|
import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage, SESSION_NOT_FOUND_MESSAGE } from "./route-ctx.js";
|
|
10
10
|
import { sendError, sendJson } from "./send.js";
|
|
11
11
|
export function callerOf(req, w) {
|
|
12
12
|
const subject = gatedSubject(req, w.config);
|
|
13
|
-
const fleet = devOpenDeployment(w.config) ? "dev_open" : actsForDeployment(req, w.config, () => subject) ? "deployment_token" : undefined;
|
|
14
13
|
return {
|
|
15
14
|
subject,
|
|
16
15
|
operator: explicitOperatorOk(subject === undefined ? undefined : personOf(subject), w.config),
|
|
17
|
-
fleet,
|
|
16
|
+
fleet: actsForDeployment(req, w.config, () => subject),
|
|
18
17
|
traceRead: traceTokenPresented(req, w.config),
|
|
19
18
|
principalRequired: DEPLOYMENT_SHAPE_PREDICATES.principalRequired.holds(w.config),
|
|
20
19
|
multiTenant: DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(w.config),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
3
|
import { type Person, type Subject } from "../owner-brands.js";
|
|
4
|
+
import type { Caller } from "../owner-faces.js";
|
|
4
5
|
import { type IdentitySource, type RunIdentity } from "../identity-source.js";
|
|
5
6
|
import type { Logger } from "../observability/logger.js";
|
|
6
7
|
/** Coerce a possibly-repeated request header to a single string (or undefined). */
|
|
@@ -96,22 +97,28 @@ export declare function sendUnauthorized(res: ServerResponse, face: Unauthorized
|
|
|
96
97
|
/**
|
|
97
98
|
* 🔴 **dev-open 的判据** = 部署形表行 `identityBoundary` 不成立(`deployment-shape.ts`;「这台部署有没有身份边界」的唯一答者):
|
|
98
99
|
* 没声明 `REQUIRE_PRINCIPAL`、配置没接任何已验身份来源(没配 service token、没配鉴权桥 issuer —— 含中心回退、没开直连门)、也没声明只读 trace token:
|
|
99
|
-
*
|
|
100
|
+
* 请求里没有任何东西替主体头作保,头是谁都能自报的(不是认证)—— 但报了它的请求仍是「为那位主体」,不是为部署本人({@link actsForDeployment})。只读配置:鉴权桥的验签器由起服第 9 段按同一个配置读数建
|
|
100
101
|
* (配置里有桥 ⇔ 装了验签器;验签器与配置里的桥两者不一致的装配(任一方向)在 `createHttpServer` 拒),所以这里与先于验签器的一次性迁移段读到同一个答案。
|
|
101
|
-
*
|
|
102
|
-
*
|
|
102
|
+
* 这是部署事实,不是调用方事实:「这位调用方代不代部署本人说话」只有 {@link actsForDeployment} 一只读法(本函数是它的一臂);另一个读者 `dispatch.ts` 的 `/metrics` 族
|
|
103
|
+
* 只拿它判「配了只读凭据的这台部署上免凭据臂关着,不必读调用方」(凭据先于主体判),不判调用方是谁。
|
|
103
104
|
*/
|
|
104
105
|
export declare function devOpenDeployment(config: ServiceConfig): boolean;
|
|
106
|
+
/** 代部署本人说话的那一格 = 属主存取器 `Caller.fleet` 的词(闭集只在 `owner-faces.ts` 拼一次):`dev_open` = 没有身份边界的部署上不报主体的调用方;`deployment_token` = 出示部署让它代自己说话的 service 凭据、不报主体的调用方。 */
|
|
107
|
+
export type DeploymentVoice = NonNullable<Caller["fleet"]>;
|
|
105
108
|
/**
|
|
106
|
-
* 🔴 **律(
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
109
|
+
* 🔴 **律(一句)**:**报了主体的调用方恒是那位主体** —— 每一口、每一种部署形(单用户 / 多租、有没有身份边界)都不改这一句;只有**不报主体**的调用方才代部署本人说话(fleet):
|
|
110
|
+
* 部署没有身份边界({@link devOpenDeployment} ⇒ `dev_open`:整台一人,不报主体的就是那一位 —— 「不报」= 没有主体头,也没出示一枚这台部署验不过的凭据
|
|
111
|
+
* ({@link bearerPresentedButUnverified}:出示了验不过 ≠ 没出示 —— 那枚凭据把主体头抹成「没有可信身份」,不许再落回最宽的那一格)),或出示这台部署的 service 凭据且部署没声明
|
|
112
|
+
* `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT=false`(⇒ `deployment_token`;声明了 ⇒ 它是无身份调用方,按各面既有的「过了凭据门、没有主体」那一臂答)。
|
|
113
|
+
* 带主体头的请求是前端(BFF)代那位主体发的、或调用方自报的「这次请求为谁」—— 它读那位主体、动那位主体的,不读全舰队;dev-open 上主体头谁都能自报,
|
|
114
|
+
* 那是部署选了不验身份(要隔离就给部署接一个身份来源),不是把头读成「为部署本人」的理由:提交的挂接(`security.ts` 的认领面)一向按它判属主,读面、列表与存续面同读这一句。
|
|
115
|
+
* 本函数是这条律的**唯一实施点**,两个读者一次继承 —— 调用方的 fleet 一格(属主存取器 `owner-access.ts` 的 `callerOf`)与 `/metrics` 族(`dispatch.ts`:`dev_open` 臂在没配
|
|
116
|
+
* `METRICS_TOKEN` 时免凭据,`deployment_token` 臂即 service 臂);不加拒码、不加闸点。自报位 `/health.auth.serviceTokenActsForDeployment` 投的是声明位的同一只读法
|
|
117
|
+
* ({@link serviceTokenDeclaredActsForDeployment})与同一只武装谓词(`http/health-self-report.ts`)。
|
|
118
|
+
* 主体只在部署那一格成立时求值(调用点那一次 {@link gatedSubject}):有身份边界的部署上没带 service 凭据的请求不因主体头的形而改答;dev-open 上要判「报没报主体」,
|
|
119
|
+
* 主体头形不合的请求与其余各口同答那一只 400。
|
|
113
120
|
*/
|
|
114
|
-
export declare function actsForDeployment(req: IncomingMessage, config: ServiceConfig, subject: () => Subject | undefined):
|
|
121
|
+
export declare function actsForDeployment(req: IncomingMessage, config: ServiceConfig, subject: () => Subject | undefined): DeploymentVoice | undefined;
|
|
115
122
|
/**
|
|
116
123
|
* 声明位 `SERVICE_TOKEN_ACTS_FOR_DEPLOYMENT` 的**唯一读法**:门({@link actsForDeployment})、自报位(`http/health-self-report.ts`)、起服行
|
|
117
124
|
* ({@link buildServiceTokenPostureNotice})三处都经它,不各写一份真值判断。只有字面 `true` 是「代部署本人」:`loadConfig` 恒写这一格(缺省 `true`),
|
|
@@ -152,7 +152,9 @@ export function devOpenDeployment(config) {
|
|
|
152
152
|
return !DEPLOYMENT_SHAPE_PREDICATES.identityBoundary.holds(config);
|
|
153
153
|
}
|
|
154
154
|
export function actsForDeployment(req, config, subject) {
|
|
155
|
-
|
|
155
|
+
if (devOpenDeployment(config))
|
|
156
|
+
return subject() === undefined && !bearerPresentedButUnverified(req, config) ? "dev_open" : undefined;
|
|
157
|
+
return serviceTokenDeclaredActsForDeployment(config) && systemFor(req, config) !== undefined && subject() === undefined ? "deployment_token" : undefined;
|
|
156
158
|
}
|
|
157
159
|
export function serviceTokenDeclaredActsForDeployment(config) {
|
|
158
160
|
return config.serviceTokenActsForDeployment === true;
|
|
@@ -58,10 +58,11 @@ export interface CheckpointCtxOpening {
|
|
|
58
58
|
}
|
|
59
59
|
export declare function readResumeCtx(load: Promise<StoredCheckpointCtx | null>, opening: CheckpointCtxOpening): Promise<ResumeCtxRead>;
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
62
|
-
* `
|
|
61
|
+
* 续跑家族一次店往返失败的落点 —— 判官的五词逐词表态,**只在这一处**。三个读者同读它:本口读续跑上下文那一读、续跑腿读「这张卡归哪条 run」那一读,
|
|
62
|
+
* 与续跑腿的认领(`resume-legs.ts` 的 `resumeClaimFaultAnswerOf`:认领那一笔,或认领之后执行席那一次读 / 写失败 —— 后者的认领已由写口撤回)。
|
|
63
|
+
* `corrupt` ⇒ 坏体(带坐标);`connection` / `deployment` / `unclassified` ⇒ 读不出 / 写不进;`defect` ⇒ 原样上抛。
|
|
63
64
|
*/
|
|
64
|
-
export declare function
|
|
65
|
+
export declare function resumeStoreFailureOf(e: unknown): {
|
|
65
66
|
readonly kind: "corrupt";
|
|
66
67
|
readonly where: string;
|
|
67
68
|
} | {
|
|
@@ -77,6 +78,16 @@ export declare function resumeCtxUnreadableAnswer(): {
|
|
|
77
78
|
status: number;
|
|
78
79
|
body: RefusalBody;
|
|
79
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* 续跑腿**认领**撞店故障的对外答复 —— 认领那一笔(`markResuming` / wake 现建行的 `createRun`)或认领之后那一次提交(执行席的再读 / 落格)。
|
|
83
|
+
* 与 {@link resumeCtxUnreadableAnswer} **同码同状态**(503 `state.resume_context_unreadable`:店这一拍没答出来,这一请求没有执行,稍后原样重发;一直如此 ⇒ 请运维查库),
|
|
84
|
+
* 句子说的是这一形自己的事:读或写这条任务的存储状态失败了,卡仍待决(认领之后那一形的认领已由写口撤回到取来时的样子)。句子不带店层 detail。
|
|
85
|
+
* 🔴 形是给冻结门看的(与 {@link resumeCtxCorruptAnswer} 同一条):`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: "<整句双引号字面>"`,中间只许有空白。
|
|
86
|
+
*/
|
|
87
|
+
export declare function resumeClaimFaultAnswer(): {
|
|
88
|
+
status: number;
|
|
89
|
+
body: RefusalBody;
|
|
90
|
+
};
|
|
80
91
|
/**
|
|
81
92
|
* 开不开的对外答复([ref])—— 409 `conflict.checkpoint_ctx_unsealable`,机读 `field`(哪一个凭据位,提交体路径;键名不是秘密,值从不进来)+ `reason`
|
|
82
93
|
* (闭集 `CHECKPOINT_CTX_UNSEALABLE_REASONS`)。句子说「这条任务的续跑上下文里带的凭据本服务开不开」、五种成因与出路(按 `reason` 修好保管目录后原样重发,
|
|
@@ -14,11 +14,11 @@ async function readStoredCtx(load) {
|
|
|
14
14
|
stored = await load;
|
|
15
15
|
}
|
|
16
16
|
catch (e) {
|
|
17
|
-
return
|
|
17
|
+
return resumeStoreFailureOf(e);
|
|
18
18
|
}
|
|
19
19
|
return stored == null ? { kind: "absent" } : { kind: "ok", ctx: stored };
|
|
20
20
|
}
|
|
21
|
-
export function
|
|
21
|
+
export function resumeStoreFailureOf(e) {
|
|
22
22
|
const fault = storeFaultClassOf(e);
|
|
23
23
|
switch (fault) {
|
|
24
24
|
case "corrupt":
|
|
@@ -41,6 +41,12 @@ export function resumeCtxUnreadableAnswer() {
|
|
|
41
41
|
body: refusalBody({ errorCode: "state.resume_context_unreadable", message: "this session's stored resume context could not be read just now (reading it failed), so this request was not carried out. Send it again later; if this keeps happening, ask whoever operates this deployment to check its database." }),
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
export function resumeClaimFaultAnswer() {
|
|
45
|
+
return {
|
|
46
|
+
status: 503,
|
|
47
|
+
body: refusalBody({ errorCode: "state.resume_context_unreadable", message: "the stored state of this task could not be read or written just now (the store failed), so this request was not carried out and the task is still waiting for it. Send it again later; if this keeps happening, ask whoever operates this deployment to check its database." }),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
44
50
|
export function resumeCtxUnsealableAnswer(read) {
|
|
45
51
|
return {
|
|
46
52
|
status: 409,
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
* (`RunRegistry` / `RouteHelpers` / `RouteLegs` / `ReturnType<typeof createRequestGuards>`),不手抄签名 ⇒ 不会漂。
|
|
13
13
|
*/
|
|
14
14
|
import type { IncomingMessage } from "node:http";
|
|
15
|
-
import { CheckpointError, type QuestionAnswer, type CheckpointToken, type PendingSteerInput } from "@sema-agent/core";
|
|
15
|
+
import { CheckpointError, type QuestionAnswer, type CheckpointToken, type CheckpointRowVerdict, type PendingSteerInput } from "@sema-agent/core";
|
|
16
16
|
import type { DecideBinding, DecidedAction } from "./wire-types.js";
|
|
17
17
|
import { type KeyCaller } from "../owner-faces.js";
|
|
18
18
|
import { type Subject } from "../owner-brands.js";
|
|
19
19
|
import type { CheckpointStoreFull } from "../plugins/store-backend.js";
|
|
20
20
|
import type { HostDecision } from "../host-decision.js";
|
|
21
21
|
import { type GateSighting } from "../approval-gate-currency.js";
|
|
22
|
+
import { type CancelRunReader } from "../run-cancel-card.js";
|
|
22
23
|
import { type PlanApprovePermissionModeAfter } from "../task-settings.js";
|
|
23
24
|
import type { RouteCtxBase, DriveResumeArgs, PreparedPlanReview, RememberSessionRequest, ResumeCheckpointOutcome, ResumeLegOutcome, WorkflowDecisionDelivery } from "./route-ctx.js";
|
|
24
25
|
import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
|
|
@@ -54,6 +55,34 @@ export declare const CHECKPOINT_ERROR_DISPOSITION: Readonly<Record<CheckpointErr
|
|
|
54
55
|
* ⇒ `"terminal"`:与 `by_reason` 认不得的词同一条 fail-closed 方向(不宽容默认成可重试),也与修前手列串的缺省逐字相同。
|
|
55
56
|
*/
|
|
56
57
|
export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled" | "retriable" | "terminal";
|
|
58
|
+
type NotOkRowVerdict = Exclude<CheckpointRowVerdict, {
|
|
59
|
+
readonly kind: "ok";
|
|
60
|
+
}>;
|
|
61
|
+
/**
|
|
62
|
+
* 🔴 **判官判「本引擎续不了」的一张卡,人从哪儿出去 —— 闭集**(这一行在本引擎上不收任何决定,批 / 拒 / 驳 / 改同拒;出路只看判决与这张卡本身,不看门的哪一扇、也不看决定词)。
|
|
63
|
+
* 唯一铸法 {@link rowWayOutOf};读面 = 决断类续跑拒体上的 `wayOut`(`/decide` 的这一行自己的拒 · `/plan_review` 与 `/resume` 上引擎对这张卡的拒 · 读行臂)。
|
|
64
|
+
* · `cancel_run` —— 判决 `damaged`,且取消动词会结算这一张卡:`POST /v1/runs/:id/cancel` 按店 CAS 把卡 expire、再驱 run 终局(不经判官),之后另起任务;
|
|
65
|
+
* · `newer_worker` —— 判决 `lacking`:更新引擎停驻的行,不低于停驻它那一版的 worker 续得了它,同一决定在那里落得下;卡留 pending(cancel 在这里是放弃它)。
|
|
66
|
+
* 缺席 = 本服务说不出一个客户端落得下的出路(判 `damaged` 而取消动词不会结算这张卡 —— 它停在已终局的 run 的纯 park 上、归属说不出的老行,或店此刻答不出这一问)。
|
|
67
|
+
*/
|
|
68
|
+
export type RowWayOut = "cancel_run" | "newer_worker";
|
|
69
|
+
/**
|
|
70
|
+
* 出路铸法要问店的那一问的座:`cs` + run 店 + 这张卡的令牌 + 铸不成时留痕的日志口(铸法不抛 —— 出路是给客户端的指路,店答不出这一问时拒本身一字不变、只是不指路,并响亮留一行)。
|
|
71
|
+
* 读行抛的五个读者(读行臂)与载入行的三扇门各自在自己的位置上拿着 `cs` 与令牌,同交一座。
|
|
72
|
+
*/
|
|
73
|
+
export interface WayOutSeat {
|
|
74
|
+
readonly cs: Pick<CheckpointStoreFull, "hostSessionOfCard" | "findPendingTokenBySession">;
|
|
75
|
+
/** 取消动词那一问也要看 run 行(只对停着的 run 结算卡);没有耐久 run 店 ⇒ 缺席(取消口恒 501)。 */
|
|
76
|
+
readonly runs: CancelRunReader | undefined;
|
|
77
|
+
readonly token: CheckpointToken;
|
|
78
|
+
readonly logger: FlatServiceDeps["logger"];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* {@link RowWayOut} 的唯一铸法:穷尽 core 的非 ok 判决 kind(core 加一种 ⇒ tsc 红,先来这里定它的出路)。
|
|
82
|
+
* `damaged` ⇒ `cancel_run` 当且仅当**取消动词会结算这一张卡**({@link cancelSettlesCard}:问的就是取消动词自己那一问,只读店的列,与行体坏成什么样无关 ——
|
|
83
|
+
* 判坏的卡的行体可以读不成检查点、属主格可以丢,取消照样落得下;投影若另按载入的行体判,就是同一件事的第二个读者)。
|
|
84
|
+
*/
|
|
85
|
+
export declare function rowWayOutOf(seat: WayOutSeat, kind: NotOkRowVerdict["kind"]): Promise<RowWayOut | undefined>;
|
|
57
86
|
/**
|
|
58
87
|
* 🔴 **停驻行读者上「读行抛」的答复 —— 五个读者(决断口含停驻赎回与 SLA 拒扫的 `resumeCheckpoint` · 续跑 `resumePreempted` · 唤醒 `resumeWake` · 计划审阅
|
|
59
88
|
* `preparePlanReview` · 活卡迟到回决 `redeemParkedAsk`)在各自的披露闸(`mayDiscloseRowError` / `decisionRowAdmits`)与各自的 404 句之后,catch 尾同调这一只**([ref])。
|
|
@@ -64,14 +93,16 @@ export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled
|
|
|
64
93
|
*
|
|
65
94
|
* 写者位交空:版本键只长在 `checkpoint.unsupported_version` 上,而一份读不成检查点的字节,判官(core 全函数,读不动的值落 `damaged`)恒判 `damaged`,到不了那一码。
|
|
66
95
|
* 店日后若交出一只 `lacking` 判决,键缺席(不编值);不为这条到不了的臂再开一条读续跑上下文的口(`getCtx` 的结果只交 `readResumeCtx`,读口律;多一个读者就是多一处要记得)。
|
|
67
|
-
*
|
|
96
|
+
* 载入行那一问(行已读出、判官判 `lacking` 可达)自带写者位,见 {@link loadedRowRefusal}。
|
|
97
|
+
* `seat` 缺席 ⇒ 这个读者不带出路词(`/wake`:成文律,它的拒从不带 `wayOut`)。
|
|
68
98
|
*/
|
|
69
|
-
export declare function rowReadRefusalAnswer(e: unknown): ResumeRefusalAnswer
|
|
99
|
+
export declare function rowReadRefusalAnswer(e: unknown, seat: WayOutSeat | undefined): Promise<ResumeRefusalAnswer>;
|
|
70
100
|
export declare function checkpointRefusalAnswer(e: CheckpointError, at: {
|
|
71
101
|
readonly gate: string;
|
|
72
102
|
readonly retriable: boolean;
|
|
73
103
|
readonly taskId?: string;
|
|
74
104
|
readonly versionKeys: Readonly<Record<string, string>>;
|
|
105
|
+
readonly wayOut?: RowWayOut;
|
|
75
106
|
}): ResumeRefusalAnswer;
|
|
76
107
|
/**
|
|
77
108
|
* 一个答案只属于它被展示的那一个动作 —— 决断绑定的**唯一判官**:决断方点名它看到的那一对(`boundCallId` + `boundInputHash`,
|
|
@@ -116,4 +147,5 @@ export declare function createResumeLegs(ctx: {
|
|
|
116
147
|
drivePlanReview: (prepared: PreparedPlanReview, decision: "approve" | "edit" | "reject", editedPlan: string | undefined, reason: string | undefined, hostDecision: HostDecision, req: IncomingMessage | undefined, acceptEarly?: boolean, permissionModeAfter?: PlanApprovePermissionModeAfter) => Promise<ResumeLegOutcome>;
|
|
117
148
|
sessionNotifyDoor: import("../session-notify-door.js").SessionNotifyDoor;
|
|
118
149
|
};
|
|
150
|
+
export {};
|
|
119
151
|
//# sourceMappingURL=resume-legs.d.ts.map
|