@sema-agent/server 7.149.0 → 7.150.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 +122 -9
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +39 -15
- package/dist/approval-card.d.ts +1 -1
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +61 -25
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-10-http-server.d.ts +33 -1
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capability-facts.d.ts +65 -56
- package/dist/capability-facts.js +42 -35
- package/dist/config-catalog.js +1 -1
- package/dist/config-types.d.ts +12 -11
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +2 -2
- package/dist/http/approval-sweeps.js +2 -1
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +4 -4
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +4 -4
- package/dist/http/owner-access.d.ts +243 -19
- package/dist/http/owner-access.js +184 -5
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.js +16 -1
- package/dist/http/resume-legs.js +18 -14
- package/dist/http/route-ctx.d.ts +23 -11
- package/dist/http/routes/a2a-serve.js +15 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +8 -9
- package/dist/http/routes/capabilities.js +4 -3
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +23 -30
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.js +3 -7
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +9 -8
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +8 -4
- package/dist/http/server.js +5 -1
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +1 -2
- package/dist/http/submit-ctx.js +3 -2
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +8 -2
- package/dist/leg-facts.js +1 -0
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +5 -5
- package/dist/observability/fail-open.js +5 -5
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/owner-faces.d.ts +27 -3
- package/dist/owner-faces.js +2 -0
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/local-checkpoint-store.d.ts +4 -1
- package/dist/plugins/local-checkpoint-store.js +3 -0
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +74 -49
- package/dist/plugins/one-time-migrations.js +134 -69
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +3 -1
- package/dist/plugins/remote-env-e2b.js +11 -8
- package/dist/plugins/remote-env-host.d.ts +1 -1
- package/dist/plugins/remote-env-host.js +20 -11
- package/dist/plugins/remote-env-k8s.js +3 -2
- package/dist/plugins/remote-env-local-docker.js +3 -2
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
- package/dist/plugins/schema-consistency.d.ts +7 -5
- package/dist/plugins/schema-consistency.js +29 -16
- package/dist/plugins/schema-drift-sentence.js +1 -1
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +42 -3
- package/dist/plugins/stop-then-start-sentence.js +44 -0
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-contracts.d.ts +12 -2
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/tidb-pool.js +4 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/run-cancel-card.d.ts +1 -1
- package/dist/run-cancel-card.js +2 -1
- package/dist/security.d.ts +1 -1
- package/dist/session-agent.d.ts +1 -10
- package/dist/session-agent.js +0 -6
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
- package/dist/stores-sql/checkpoint-store-sql.js +106 -18
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/task-cwd.d.ts +4 -3
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +13 -7
- package/dist/tool-approval.d.ts +7 -6
- package/dist/tool-approval.js +41 -37
- package/dist/trace/engine-notice-wire.d.ts +14 -0
- package/dist/trace/engine-notice-wire.js +18 -1
- package/dist/trace/project.d.ts +4 -3
- package/dist/trace/project.js +3 -2
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/self-minted-wire.d.ts +150 -17
- package/dist/trace/self-minted-wire.js +54 -16
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/package.json +2 -2
- package/wire-changes/server@7.150.0.json +335 -0
|
@@ -8,7 +8,7 @@ import { approvalPayloadFingerprint } from "../../plugins/approval-payload-finge
|
|
|
8
8
|
import { carriesDeploymentListQuestion, standingAnswerDurablyRecordable } from "../../tool-approval.js";
|
|
9
9
|
import { APPROVAL_GATE_KINDS } from "../../approval-content-kind.js";
|
|
10
10
|
import { isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
11
|
-
import { principalFrom,
|
|
11
|
+
import { principalFrom, scopeCellOf, CHECKPOINT_PUBLIC_SCOPE, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../../security.js";
|
|
12
12
|
import { keyAdmits } from "../../owner-faces.js";
|
|
13
13
|
import { personOf } from "../../owner-brands.js";
|
|
14
14
|
import { editedPlanBinding, verifyDirectDoorProof } from "../../principal-jwt.js";
|
|
@@ -23,7 +23,7 @@ import { sleep } from "../sse-log.js";
|
|
|
23
23
|
import { bindSseLifecycle } from "../sse-lifecycle.js";
|
|
24
24
|
import { sendJson, sendError, sseHeaders, logRequestError, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "../send.js";
|
|
25
25
|
import { gatedSubject, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
26
|
-
import { admitRunById, admitSessionById, callerEntry, callerOf, callerOfDecision, deciderSubjectOf, enter,
|
|
26
|
+
import { admitRunById, admitSessionById, callerEntry, callerOf, callerOfDecision, deciderSubjectOf, enter, latestRunIdOf, listRunsOfScope, protocolEntry, readSessionAgent, readVisibleRun, refuse, runFactsOf, withCaller } from "../owner-access.js";
|
|
27
27
|
import { governanceOriginOf } from "../../runtime-governance.js";
|
|
28
28
|
import { deploymentListAsks } from "../../approval.js";
|
|
29
29
|
import { grantBindingOf } from "../../standing-answer-grant.js";
|
|
@@ -40,7 +40,6 @@ import { seatText } from "../../trace/exit-seat.js";
|
|
|
40
40
|
import { approvalQueueKindOfDoorId } from "../../approval-queue-kind.js";
|
|
41
41
|
import { defaultPeerInboundFace, parsePeerInboundId, peerBoxScope } from "../../peer-inbound-face.js";
|
|
42
42
|
import { RESUME_RESTATABLE_KEYS, readResumeRestatement } from "../resume-restatement.js";
|
|
43
|
-
import { readSessionAgent } from "../../session-agent.js";
|
|
44
43
|
const DECIDE_BODY_DECLARED_KEYS = [
|
|
45
44
|
"answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decidedVia", "decision", "feedback", "reason", "remember", "updatedInput",
|
|
46
45
|
...RESUME_RESTATABLE_KEYS,
|
|
@@ -280,8 +279,8 @@ async function decideIdempotentReplay(cs, args) {
|
|
|
280
279
|
};
|
|
281
280
|
}
|
|
282
281
|
async function gateObjectivePreview(reader, runStore, agentStore, gate, runSessionId) {
|
|
283
|
-
const previewOf = (taskId) => readVisibleRun(reader, runStore, taskId);
|
|
284
|
-
const own = typeof runSessionId === "string" ? ((await runStore.getActiveTaskId(runSessionId)) ?? (await runStore
|
|
282
|
+
const previewOf = async (taskId) => (await readVisibleRun(reader, runStore, taskId))?.row;
|
|
283
|
+
const own = typeof runSessionId === "string" ? ((await runStore.getActiveTaskId(runSessionId)) ?? (await latestRunIdOf(runStore, runSessionId))) : undefined;
|
|
285
284
|
if (own !== undefined)
|
|
286
285
|
return (await previewOf(own))?.objectivePreview ?? null;
|
|
287
286
|
if (agentStore === undefined)
|
|
@@ -462,7 +461,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
462
461
|
if (typeof host === "string")
|
|
463
462
|
byGate.set(host, s);
|
|
464
463
|
});
|
|
465
|
-
const runs = await runStore
|
|
464
|
+
const runs = await listRunsOfScope(runStore, scope, 100);
|
|
466
465
|
const tasks = runs
|
|
467
466
|
.filter((r) => r.status === "running" || isParkedRunStatus(r.status))
|
|
468
467
|
.map((r) => {
|
|
@@ -511,7 +510,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
511
510
|
let flagged = await runStore.requestPreempt(taskId, run.owner);
|
|
512
511
|
const PREEMPT_RECLASSIFY_ATTEMPTS = 3;
|
|
513
512
|
for (let attempt = 0; !flagged; attempt++) {
|
|
514
|
-
const now = await runStore
|
|
513
|
+
const now = (await readVisibleRun(preemptEntry, runStore, taskId))?.row;
|
|
515
514
|
if (now !== undefined && isParkedRunStatus(now.status)) {
|
|
516
515
|
sendError(res, 409, "conflict.already_suspended", "task is already suspended", { taskId, status: now.status });
|
|
517
516
|
return;
|
|
@@ -661,7 +660,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
661
660
|
}
|
|
662
661
|
const sighting = typeof body.checkpointId === "string" ? { face: "checkpointId", value: body.checkpointId } : undefined;
|
|
663
662
|
const decisionEntry = callerEntry(req, deps, match.decl.id);
|
|
664
|
-
const run = await runStore
|
|
663
|
+
const run = await runFactsOf(runStore, taskId);
|
|
665
664
|
if (!run) {
|
|
666
665
|
if (!deps.config.directDoorActive)
|
|
667
666
|
deps.logger?.info?.("decide_404", { reason: "no-row", taskId, principalPresent: principal !== undefined });
|
|
@@ -694,7 +693,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
694
693
|
singleUse = proof.singleUse === undefined ? undefined : { proof: proof.singleUse, store };
|
|
695
694
|
}
|
|
696
695
|
const deciderEntry = withCaller(decisionEntry, callerOfDecision(decisionEntry.caller, deciderPrincipal, deps.config));
|
|
697
|
-
const decisionAdmitted =
|
|
696
|
+
const decisionAdmitted = await readVisibleRun(deciderEntry, runStore, taskId);
|
|
698
697
|
if (decisionAdmitted === undefined) {
|
|
699
698
|
deps.logger?.info?.("decide_404", { reason: "owner-mismatch", taskId, principalPresent: principal !== undefined });
|
|
700
699
|
refuse(deciderEntry, res, "not_visible");
|
|
@@ -37,7 +37,7 @@ import { judgeAutoModeArming } from "../../auto-mode-face.js";
|
|
|
37
37
|
import { coercePermissionModeStrict, PERMISSION_MODE_WORDS, taskHooksHonored } from "../../task-settings.js";
|
|
38
38
|
import { gatedPerson, sendPrincipalRefusal } from "../principal-gate.js";
|
|
39
39
|
import { readAdoptionImportCapability } from "../../adoption/import.js";
|
|
40
|
-
import {
|
|
40
|
+
import { headReadPresent } from "../../session-incarnation.js";
|
|
41
41
|
import { callerTrust } from "../caller-trust.js";
|
|
42
42
|
import { sessionRulesNamespaceFormsHonored } from "../../session-policy-wire.js";
|
|
43
43
|
function routePairingOf(deps, name, m) {
|
|
@@ -207,6 +207,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
207
207
|
sideQuery: true,
|
|
208
208
|
agentRoster: facts.agentRoster(deps).available,
|
|
209
209
|
projects: facts.projectRegistry(deps).available,
|
|
210
|
+
projectInheritance: facts.projectRegistry(deps).available,
|
|
210
211
|
retention: retentionAdvertisement(deps.config),
|
|
211
212
|
peerLane: Boolean(deps.peerDirectory),
|
|
212
213
|
scenarioSubject: true,
|
|
@@ -214,12 +215,12 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
214
215
|
sessionSearch: facts.sessionList(deps).available,
|
|
215
216
|
sessionFork: facts.sessionFork(deps).available,
|
|
216
217
|
sessionDelete: facts.sessionDelete(deps).available,
|
|
217
|
-
resumeAt: Boolean(deps.resumeAnchorStore &&
|
|
218
|
+
resumeAt: Boolean(deps.resumeAnchorStore && headReadPresent(deps.sessionStorage)),
|
|
218
219
|
promptSuggestions: true,
|
|
219
220
|
sessionPolicy: facts.sessionPolicy(deps).available,
|
|
220
221
|
sessionRulesNamespaceForms: sessionRulesNamespaceFormsHonored(),
|
|
221
222
|
restoreFiles: Boolean(deps.fileHistoryStore),
|
|
222
|
-
rewindFilesTo: Boolean(deps.fileHistoryStore && deps.resumeAnchorStore &&
|
|
223
|
+
rewindFilesTo: Boolean(deps.fileHistoryStore && deps.resumeAnchorStore && headReadPresent(deps.sessionStorage)),
|
|
223
224
|
sessionRewindFiles: facts.sessionRewindFiles(deps).available,
|
|
224
225
|
sessionRewindFilesPreview: facts.sessionRewindFilesPreview(deps).available,
|
|
225
226
|
fileHistoryCapture: fileHistoryCaptureOf(deps.fileHistoryStore, deps.config.fileHistoryCapture),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* [ref] §5 S2([ref])—— `GET /v1/config/catalog`:配置目录自描述端点(operator lane)。
|
|
3
3
|
*
|
|
4
4
|
* TOB 运维在 web 上要一个**不撒谎**的配置目录:哪个键、什么腿(env/center)、改了何时生效
|
|
5
5
|
* (hot/restart-片名/deploy/inert)、本机实际值 —— 今天这些事实散在 config.ts 数千行与三份文档里,
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { isParkedRunStatus, isTerminalRunStatus, runProvenElsewhere } from "../../plugins/store-contracts.js";
|
|
3
3
|
import { DeadlineExceeded, MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
|
-
import { admitSessionById, enter } from "../owner-access.js";
|
|
5
|
+
import { admitSessionById, enter, runFactsOf } from "../owner-access.js";
|
|
6
6
|
import { assertNever } from "../route-ctx.js";
|
|
7
7
|
import * as facts from "../../capability-facts.js";
|
|
8
8
|
import { seatText } from "../../trace/exit-seat.js";
|
|
@@ -118,7 +118,7 @@ export async function handleMcpReconnect(req, res, match, ctx) {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
const run = activeTaskId === undefined ? undefined : await runStore
|
|
121
|
+
const run = activeTaskId === undefined ? undefined : await runFactsOf(runStore, activeTaskId);
|
|
122
122
|
if (run && isParkedRunStatus(run.status)) {
|
|
123
123
|
sendError(res, 409, "steering.not_running", "run is waiting on a pending decision — the MCP re-dial verb needs a live turn; a resumed continuation dials every declared server afresh (the re-dial applies to the current turn only and is not persisted), so resume this run first — a re-dial cannot be queued behind the decision");
|
|
124
124
|
return;
|
|
@@ -116,7 +116,7 @@ export declare function buildClearanceRowWire(r: OriginClearanceRow): {
|
|
|
116
116
|
baseRev: string;
|
|
117
117
|
origin: {
|
|
118
118
|
at: number;
|
|
119
|
-
cause?: "static" | "
|
|
119
|
+
cause?: "static" | "observed" | "derived" | "unattributed" | undefined;
|
|
120
120
|
taint: "external";
|
|
121
121
|
};
|
|
122
122
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
2
|
import { gatedSubject, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
|
-
import {
|
|
3
|
+
import { admitSessionById, enter, refuse } from "../owner-access.js";
|
|
4
4
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
5
5
|
import { assertNever } from "../route-ctx.js";
|
|
6
6
|
import * as facts from "../../capability-facts.js";
|
|
@@ -54,8 +54,7 @@ export async function handleNotifyWake(req, res, match, ctx) {
|
|
|
54
54
|
sendError(res, 501, "capability.session_ownership_required", "external notify requires the session-ownership face (sessionStorage.headOf)");
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
|
-
const
|
|
58
|
-
const notifyAdmitted = await admitSessionRow(notifyEntry, res, owned.sessionStorage, notifySession, notifyHead);
|
|
57
|
+
const notifyAdmitted = await admitSessionById(notifyEntry, res, owned.sessionStorage, notifySession);
|
|
59
58
|
if (notifyAdmitted === undefined)
|
|
60
59
|
return;
|
|
61
60
|
const payload = {
|
|
@@ -48,6 +48,7 @@ export declare const PROJECT_ROW_DISPOSITIONS: {
|
|
|
48
48
|
readonly registeredBy: "forward";
|
|
49
49
|
readonly registeredAt: "forward";
|
|
50
50
|
readonly archivedAt: "forward";
|
|
51
|
+
readonly contentAttestedAt: "forward";
|
|
51
52
|
};
|
|
52
53
|
/** 项目行的键全集(处置表的键 = wire 上可能出现的每一个键;自铸登记表 `trace/self-minted-wire.ts` 的 `projectRowKey` 按它 `satisfies`)。 */
|
|
53
54
|
export type ProjectRowKey = keyof typeof PROJECT_ROW_DISPOSITIONS;
|
|
@@ -4,6 +4,7 @@ import { sendUnauthorized } from "../principal-gate.js";
|
|
|
4
4
|
import { routeListFilter } from "../owner-access.js";
|
|
5
5
|
import { assertNever } from "../route-ctx.js";
|
|
6
6
|
import * as facts from "../../capability-facts.js";
|
|
7
|
+
import { projectVisible } from "../../project-context.js";
|
|
7
8
|
import { projectByTable } from "../../trace/projection-table.js";
|
|
8
9
|
export const PROJECT_NOT_FOUND_MESSAGE = "no such project";
|
|
9
10
|
export const PROJECT_ROW_DISPOSITIONS = {
|
|
@@ -19,6 +20,7 @@ export const PROJECT_ROW_DISPOSITIONS = {
|
|
|
19
20
|
registeredBy: "forward",
|
|
20
21
|
registeredAt: "forward",
|
|
21
22
|
archivedAt: "forward",
|
|
23
|
+
contentAttestedAt: "forward",
|
|
22
24
|
};
|
|
23
25
|
export function projectRowOf(id, registration) {
|
|
24
26
|
return projectByTable("project", PROJECT_ROW_DISPOSITIONS, registration, { id });
|
|
@@ -37,8 +39,8 @@ function readArchivedQuery(raw) {
|
|
|
37
39
|
export async function handleProjects(req, res, match, ctx) {
|
|
38
40
|
const { deps } = ctx;
|
|
39
41
|
const { safeDecode } = ctx.helpers;
|
|
40
|
-
const
|
|
41
|
-
if (!fleetWide && gateOwner === null) {
|
|
42
|
+
const viewer = routeListFilter(req, deps, match.decl.id);
|
|
43
|
+
if (!viewer.fleetWide && viewer.gateOwner === null) {
|
|
42
44
|
sendUnauthorized(res, "identity");
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
@@ -47,7 +49,7 @@ export async function handleProjects(req, res, match, ctx) {
|
|
|
47
49
|
sendError(res, 501, "capability.unavailable", "the project registry is not available on this deployment: projects are published only by the configuration center, and no center configuration is applied here");
|
|
48
50
|
return;
|
|
49
51
|
}
|
|
50
|
-
const visible = (registration) =>
|
|
52
|
+
const visible = (registration) => projectVisible(registration, viewer);
|
|
51
53
|
switch (match.decl.id) {
|
|
52
54
|
case "projects-list": {
|
|
53
55
|
const archived = readArchivedQuery(new URLSearchParams(match.search).get("archived"));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* [ref] 车2 —— **托管留存的 operator 治理面**(设计稿 `docs/
|
|
2
|
+
* [ref] 车2 —— **托管留存的 operator 治理面**(设计稿 `docs/[ref]-retention-lane.md` §5/§6)。
|
|
3
3
|
*
|
|
4
4
|
* · `PUT /v1/ops/retention/holds/:domain` —— 放置 legal hold;
|
|
5
5
|
* · `DELETE /v1/ops/retention/holds/:domain` —— 解除;
|
|
@@ -43,7 +43,7 @@ export type PermissionModePersistMiss = (typeof PERMISSION_MODE_PERSIST_MISSES)[
|
|
|
43
43
|
* · run 行仍在本副本(`runProvenElsewhere` 的反面 —— 别的副本续跑了它,续跑的 CAS 赢点把行上的副本改成它自己);
|
|
44
44
|
* · 本副本这条 run 此刻登记的活流仍是接下切档的那一只(本副本上续跑的新腿登记的是另一只流对象;腿收场时按对象身份摘掉自己的)。
|
|
45
45
|
*/
|
|
46
|
-
export declare function switchLegHolds<S extends object>(fenced: FencedRun, thisInstanceId: string | undefined, liveNow: S | undefined, switched: S): boolean;
|
|
46
|
+
export declare function switchLegHolds<S extends object>(fenced: Pick<FencedRun, "instanceId">, thisInstanceId: string | undefined, liveNow: S | undefined, switched: S): boolean;
|
|
47
47
|
/** 本判官改了词时回执旁的那一格(`admission`)。 */
|
|
48
48
|
interface PermissionModeAdmissionNote {
|
|
49
49
|
readonly requested: PermissionMode;
|
|
@@ -7,7 +7,7 @@ import * as facts from "../../capability-facts.js";
|
|
|
7
7
|
import { PERMISSION_MODE_WORDS, coercePermissionModeStrict, parseTaskSettings } from "../../task-settings.js";
|
|
8
8
|
import { admitPermissionModeWord } from "../../permission-mode-admission.js";
|
|
9
9
|
import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnreadableAnswer, resumeCtxUnsealableAnswer } from "../resume-ctx-read.js";
|
|
10
|
-
import { sealCheckpointCtx
|
|
10
|
+
import { sealCheckpointCtx } from "../checkpoint-ctx.js";
|
|
11
11
|
import { serviceVersion } from "../../service-version.js";
|
|
12
12
|
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
13
13
|
export const PERMISSION_MODE_PERSIST_MISSES = ["claim_released", "context_absent", "store_failed"];
|
|
@@ -114,7 +114,7 @@ export async function handlePermissionMode(req, res, match, ctx) {
|
|
|
114
114
|
}
|
|
115
115
|
let persistMiss;
|
|
116
116
|
try {
|
|
117
|
-
const next = sealCheckpointCtx({ ...
|
|
117
|
+
const next = sealCheckpointCtx({ ...stored, body: { ...stored.body, permissionMode: admitted }, writerVersion: serviceVersion() }, deps.checkpointCtxSealing, sessionId);
|
|
118
118
|
const rewrite = await checkpointStore.rewriteCtxWhileClaimed(sessionId, next, { fence: runStore, taskId, legHolds: (fenced) => switchLegHolds(fenced, deps.instanceId, ctx.registry.steerableRuns.get(taskId), live) });
|
|
119
119
|
persistMiss = rewrite === "written" ? undefined : rewrite;
|
|
120
120
|
}
|
|
@@ -13,7 +13,7 @@ export declare function handleRuns(req: IncomingMessage, res: ServerResponse, ma
|
|
|
13
13
|
/** 同上纪律(子代理 / 任务句柄 / HITL 应答段)。 */
|
|
14
14
|
export declare function handleRunVerbs(req: IncomingMessage, res: ServerResponse, match: RouteMatch<RunVerbsRouteId>, ctx: RouteCtx): Promise<void>;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* [ref] 车2 件2 —— `POST /v1/runs` 的**建行 + 驱动**段,从路由体里原样搬出来的具名腿。
|
|
17
17
|
*
|
|
18
18
|
* 🔴 为什么提取:A2A 的 `message/send`(routes/a2a-serve.ts)必须走**同一条**提交链 —— 第二条提交
|
|
19
19
|
* 路径就是第二个写者,而这条链上挂着 durable taskId 幂等重放、lease/治理窗 pre-admission、
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -30,16 +30,15 @@ import { ActorAssertionWire } from "@sema-agent/settings-schema";
|
|
|
30
30
|
import * as facts from "../../capability-facts.js";
|
|
31
31
|
import { assertNever } from "../route-ctx.js";
|
|
32
32
|
import { readRunFace } from "../run-read-face.js";
|
|
33
|
-
import { replayOfExistingRun } from "../submit-replay.js";
|
|
33
|
+
import { replayOfExistingRun, replayOfKey } from "../submit-replay.js";
|
|
34
34
|
import { mintAndDriveDurableRun } from "../durable-run-leg.js";
|
|
35
35
|
import { SUBMISSION_CLAIM_FACE } from "../execution-context.js";
|
|
36
|
-
import { admitRunById,
|
|
36
|
+
import { admitRunById, admitRunByIdem, enter, readVisibleRun, sessionFactsReadOf } from "../owner-access.js";
|
|
37
37
|
import { keyAdmission, ownerCellOf, recoveredSubject } from "../../owner-faces.js";
|
|
38
38
|
import { personOf } from "../../owner-brands.js";
|
|
39
39
|
import { withSubject } from "../../observability/principal-context.js";
|
|
40
40
|
import { readResumeCtx, resumeCtxUnreadableAnswer } from "../resume-ctx-read.js";
|
|
41
41
|
import { storedBodyReplayAnswer, storedBodyReplayLogFields } from "../checkpoint-ctx.js";
|
|
42
|
-
import { headReadOf } from "../../session-incarnation.js";
|
|
43
42
|
import { serviceVersion } from "../../service-version.js";
|
|
44
43
|
import { storedBodyReplayRefusal } from "../stored-body-replay.js";
|
|
45
44
|
import { haltBodyIssue, steerBodyKeyIssue } from "../../request-key-closure.js";
|
|
@@ -106,7 +105,7 @@ function sendHandleMiss(res, run, thisInstanceId) {
|
|
|
106
105
|
}
|
|
107
106
|
return false;
|
|
108
107
|
}
|
|
109
|
-
async function streamRunEvents(req, res, deps, runStore, taskId) {
|
|
108
|
+
async function streamRunEvents(req, res, deps, runStore, entry, taskId) {
|
|
110
109
|
await streamSseLog(req, res, {
|
|
111
110
|
preamble: async (signal) => {
|
|
112
111
|
const gate = resolveStreamApprovalGate({
|
|
@@ -135,10 +134,7 @@ async function streamRunEvents(req, res, deps, runStore, taskId) {
|
|
|
135
134
|
return buildApprovalPreambleSseFrames(frames);
|
|
136
135
|
},
|
|
137
136
|
onPreambleFailure: (reason, err) => deps.logger?.warn?.("approval_preamble_failed", { taskId, reason, face: "run.events", error: err instanceof Error ? err.message : String(err ?? "") }),
|
|
138
|
-
statusOf: async (id) =>
|
|
139
|
-
const run = await runStore.getRun(id);
|
|
140
|
-
return run ? runRowAt("run-events", runStore, run) : undefined;
|
|
141
|
-
},
|
|
137
|
+
statusOf: async (id) => (await readVisibleRun(entry, runStore, id))?.row,
|
|
142
138
|
getEvents: (id, after) => runStore.getEvents(id, after),
|
|
143
139
|
formatEvent: (ev) => {
|
|
144
140
|
const type = normalizeRunEventType(ev.type);
|
|
@@ -208,9 +204,9 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
208
204
|
return;
|
|
209
205
|
}
|
|
210
206
|
const inflight = idemCache.peek(inflightKeyOf(idem, "durable", clientTaskId));
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
207
|
+
const replayed = inflight ? undefined : await replayOfKey({ idemKey: idem.key }, { subject: submitter, idem, ...(clientTaskId !== undefined ? { clientTaskId } : {}) }, "durable", deps, runStore);
|
|
208
|
+
const resp = inflight ? await inflight : replayed;
|
|
209
|
+
if (resp !== undefined) {
|
|
214
210
|
sendJson(res, resp.status, resp.body);
|
|
215
211
|
return;
|
|
216
212
|
}
|
|
@@ -246,26 +242,23 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
246
242
|
const entry = enter(req, res, deps, match.decl.id);
|
|
247
243
|
if (entry === undefined)
|
|
248
244
|
return;
|
|
249
|
-
let
|
|
250
|
-
let pathId;
|
|
245
|
+
let admitted;
|
|
251
246
|
if (match.decl.id === "runs-by-idem") {
|
|
252
247
|
const rawKey = new URLSearchParams(match.search).get("idempotencyKey");
|
|
253
248
|
if (!rawKey) {
|
|
254
249
|
sendError(res, 400, "request.query_invalid", "idempotencyKey is required: pass the raw Idempotency-Key the submission was sent with");
|
|
255
250
|
return;
|
|
256
251
|
}
|
|
257
|
-
|
|
252
|
+
admitted = await admitRunByIdem(entry, res, runStore, idemKeyOf(rawKey, idemCallerOf(req, ctx), "run"));
|
|
258
253
|
}
|
|
259
254
|
else {
|
|
260
|
-
|
|
261
|
-
run = await runStore.getRun(pathId);
|
|
255
|
+
admitted = await admitRunById(entry, res, runStore, match.params.taskId);
|
|
262
256
|
}
|
|
263
|
-
const admitted = await admitRunRow(entry, res, runStore, run, pathId);
|
|
264
257
|
if (admitted === undefined)
|
|
265
258
|
return;
|
|
266
259
|
const taskId = admitted.row.taskId;
|
|
267
260
|
if (match.decl.id === "run-events") {
|
|
268
|
-
await streamRunEvents(req, res, deps, runStore, taskId);
|
|
261
|
+
await streamRunEvents(req, res, deps, runStore, entry, taskId);
|
|
269
262
|
}
|
|
270
263
|
else {
|
|
271
264
|
sendJson(res, 200, await readRunFace(deps, runStore, admitted.row));
|
|
@@ -296,7 +289,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
296
289
|
if (token) {
|
|
297
290
|
const won = (await expireCardUnderStoredScope(cs, token)) === CARD_EXPIRE_OUTCOME.expired;
|
|
298
291
|
if (!won) {
|
|
299
|
-
const now = await rs
|
|
292
|
+
const now = (await readVisibleRun(entry, rs, taskId))?.row;
|
|
300
293
|
const st = now?.status;
|
|
301
294
|
if (st !== undefined && (isParkedRunStatus(st) || st === "running")) {
|
|
302
295
|
sendError(res, 409, "conflict.approval_settled", "pending approval was settled concurrently (decided or expired) — re-check the run and retry cancel if it is still active", { taskId, status: st });
|
|
@@ -310,7 +303,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
310
303
|
else {
|
|
311
304
|
const claimed = await rs.markResuming(taskId, deps.instanceId ?? "default");
|
|
312
305
|
if (!claimed) {
|
|
313
|
-
const now = await rs
|
|
306
|
+
const now = (await readVisibleRun(entry, rs, taskId))?.row;
|
|
314
307
|
const st = now?.status;
|
|
315
308
|
if (st !== undefined && (st === "running" || isParkedRunStatus(st))) {
|
|
316
309
|
sendError(res, 409, "conflict.approval_settled", "pending approval was settled concurrently (decided or expired) — re-check the run and retry cancel if it is still active", { taskId, status: st });
|
|
@@ -350,7 +343,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
350
343
|
sendError(res, 500, "internal.cancel_not_terminalized", `cancel progressed (${note}) but could not terminalize the run row (retried) — retry cancel; a stuck row is reaped after the stale window (${e instanceof Error ? e.message : String(e)})`, { taskId });
|
|
351
344
|
return;
|
|
352
345
|
}
|
|
353
|
-
const finalRow = await rs
|
|
346
|
+
const finalRow = (await readVisibleRun(entry, rs, taskId).catch(() => undefined))?.row;
|
|
354
347
|
if (finalRow && finalRow.errorCode !== "cancelled" && isTerminalRunStatus(finalRow.status)) {
|
|
355
348
|
sendJson(res, 202, { taskId, status: finalRow.status, errorCode: finalRow.errorCode ?? null, cancelRequested: finalRow.cancelRequested, note: `session unlocked; the run was terminalized concurrently (${finalRow.errorCode ?? finalRow.status}) before this cancel's write — reporting the actual ledger state` });
|
|
356
349
|
return;
|
|
@@ -361,7 +354,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
361
354
|
let flagged = await runStore.requestCancel(taskId, run.owner);
|
|
362
355
|
const CANCEL_RECLASSIFY_ATTEMPTS = 3;
|
|
363
356
|
for (let attempt = 0; !flagged; attempt++) {
|
|
364
|
-
const now = await runStore
|
|
357
|
+
const now = (await readVisibleRun(entry, runStore, taskId))?.row;
|
|
365
358
|
if (now !== undefined && isParkedRunStatus(now.status)) {
|
|
366
359
|
await cancelSuspended(now.result);
|
|
367
360
|
return;
|
|
@@ -505,7 +498,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
505
498
|
}
|
|
506
499
|
if (code !== "steering.not_running")
|
|
507
500
|
throw e;
|
|
508
|
-
const freshBeforePark = await runStore
|
|
501
|
+
const freshBeforePark = (await readVisibleRun(entry, runStore, taskId).catch(() => undefined))?.row;
|
|
509
502
|
const raced = await tryPark();
|
|
510
503
|
if (raced === "parked") {
|
|
511
504
|
sendParked(freshBeforePark?.status ?? run.status);
|
|
@@ -554,8 +547,8 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
554
547
|
}
|
|
555
548
|
return;
|
|
556
549
|
}
|
|
557
|
-
const
|
|
558
|
-
if (deps.checkpointStore &&
|
|
550
|
+
const sessionFactsOf = sessionFactsReadOf(deps.sessionStorage);
|
|
551
|
+
if (deps.checkpointStore && sessionFactsOf) {
|
|
559
552
|
const activeTaskId = await runStore.getActiveTaskId?.(run.sessionId).catch(() => undefined);
|
|
560
553
|
if (activeTaskId !== undefined && activeTaskId !== null && activeTaskId !== taskId) {
|
|
561
554
|
sendNotRunning(`run is ${run.status} and its session has an ACTIVE run (${activeTaskId}) — steer that run instead`);
|
|
@@ -605,7 +598,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
605
598
|
mintResult.duplicateKey = true;
|
|
606
599
|
return;
|
|
607
600
|
}
|
|
608
|
-
const leafId = await
|
|
601
|
+
const leafId = await sessionFactsOf(run.sessionId).then((h) => h?.leafId).catch(() => undefined);
|
|
609
602
|
if (leafId === undefined || leafId === null)
|
|
610
603
|
return;
|
|
611
604
|
const resumeRead = await readResumeCtx(cs.getCtx(run.sessionId), { sessionId: run.sessionId, opener: deps.checkpointCtxSealing });
|
|
@@ -779,7 +772,7 @@ export async function handleRuns(req, res, match, ctx) {
|
|
|
779
772
|
catch (e) {
|
|
780
773
|
if (e.code !== "steering.not_running")
|
|
781
774
|
throw e;
|
|
782
|
-
const freshRun = await runStore
|
|
775
|
+
const freshRun = (await readVisibleRun(entry, runStore, taskId).catch(() => undefined))?.row;
|
|
783
776
|
sendNoLiveTurn(freshRun?.status ?? run.status, true);
|
|
784
777
|
return;
|
|
785
778
|
}
|
|
@@ -1509,9 +1502,9 @@ async function createDurableRunHeld(req, res, ctx, prepared, idem) {
|
|
|
1509
1502
|
const submitter = gatedSubject(req, deps.config);
|
|
1510
1503
|
const caller = { subject: submitter, idem, ...(clientTaskId !== undefined ? { clientTaskId } : {}) };
|
|
1511
1504
|
if (clientTaskId) {
|
|
1512
|
-
const
|
|
1513
|
-
if (
|
|
1514
|
-
return
|
|
1505
|
+
const replayed = await replayOfKey({ taskId: clientTaskId }, caller, "durable", deps, runStore);
|
|
1506
|
+
if (replayed !== undefined)
|
|
1507
|
+
return replayed;
|
|
1515
1508
|
}
|
|
1516
1509
|
if ((await leaseDenied(req, res, seat)) || (await usageWindowDenied(req, res, seat)))
|
|
1517
1510
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { memorySyncReportOf } from "@sema-agent/core";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
-
import { admitSessionById, enter } from "../owner-access.js";
|
|
3
|
+
import { admitSessionById, enter, latestRunIdOf, readVisibleSession } from "../owner-access.js";
|
|
4
4
|
import { assertNever } from "../route-ctx.js";
|
|
5
5
|
import * as facts from "../../capability-facts.js";
|
|
6
6
|
import { leafOfAdmittedHead } from "../../session-incarnation.js";
|
|
@@ -94,7 +94,6 @@ export async function handleSessionMemorySync(req, res, match, ctx) {
|
|
|
94
94
|
sendError(res, 400, "request.path_malformed", "session id segment is not valid percent-encoding");
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
|
-
const readHead = () => sessionStorage.headOf(sessionId);
|
|
98
97
|
const admitted = await admitSessionById(entry, res, sessionStorage, sessionId);
|
|
99
98
|
if (admitted === undefined)
|
|
100
99
|
return;
|
|
@@ -122,10 +121,10 @@ export async function handleSessionMemorySync(req, res, match, ctx) {
|
|
|
122
121
|
const quiet = async () => {
|
|
123
122
|
if ((await runStore.getActiveTaskId(sessionId)) !== undefined)
|
|
124
123
|
return false;
|
|
125
|
-
if ((await runStore
|
|
124
|
+
if ((await latestRunIdOf(runStore, sessionId)) !== observedTaskId)
|
|
126
125
|
return false;
|
|
127
|
-
const now = await
|
|
128
|
-
return now !== undefined && leafOfAdmittedHead(now) === sourceRevision;
|
|
126
|
+
const now = await readVisibleSession(entry, sessionStorage, sessionId);
|
|
127
|
+
return now !== undefined && leafOfAdmittedHead(now.row) === sourceRevision;
|
|
129
128
|
};
|
|
130
129
|
const fenced = () => reportOf({ target, sourceRevision, scopes, refused: { code: "session_active", message: SESSION_ACTIVE_SYNC_MESSAGE } });
|
|
131
130
|
let report;
|
|
@@ -7,10 +7,9 @@ import { classifySyncRelationshipByIds, createStagedLogFeed, MalformedImportEntr
|
|
|
7
7
|
import { isUuidShape } from "../../security.js";
|
|
8
8
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, logRequestError, msg } from "../send.js";
|
|
9
9
|
import { StampedSessionArchiveError } from "../../session-error-wire.js";
|
|
10
|
-
import { readSessionAgent } from "../../session-agent.js";
|
|
11
10
|
import { buildDisplaySafeUrl } from "../../trace/redact.js";
|
|
12
11
|
import { storeFaultClassOf } from "../../plugins/store-fault.js";
|
|
13
|
-
import { enter,
|
|
12
|
+
import { enter, readSessionAgent, readSyncGate, refuse } from "../owner-access.js";
|
|
14
13
|
import { listFilterOf, ownerKeyOf, sameKey } from "../../owner-faces.js";
|
|
15
14
|
const SYNC_BLOB_MAX_BYTES = 64 * 1024 * 1024;
|
|
16
15
|
const SYNC_NDJSON_LINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
@@ -18,7 +17,6 @@ import { SHA256_HEX_RE } from "../../digest-form.js";
|
|
|
18
17
|
import { assertNever } from "../route-ctx.js";
|
|
19
18
|
import { readSessionRulesRecords, describeSessionRulesRecordProblem } from "../../session-policy-wire.js";
|
|
20
19
|
import { readSessionPinned, readSessionTitle } from "../../session-row.js";
|
|
21
|
-
import { deletionHeadReadOf, sessionFaceOfHead } from "../../session-incarnation.js";
|
|
22
20
|
import { defaultTaskRegistry } from "@sema-agent/core";
|
|
23
21
|
import { SessionActiveRunError, SessionForkInProgressError, SessionWriterPresentError } from "../../execution-placement.js";
|
|
24
22
|
import { probeSessionChildren, sessionTenantScope } from "../../session-children-retire.js";
|
|
@@ -94,10 +92,8 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
94
92
|
return;
|
|
95
93
|
}
|
|
96
94
|
const judgeSessionGate = async () => {
|
|
97
|
-
const face =
|
|
98
|
-
|
|
99
|
-
const mayTouch = held !== undefined && (await judgeSession(syncEntry, sessionStorage, sessionId, held)) !== undefined;
|
|
100
|
-
return { face, occupant: held, mayPull: face !== undefined && mayTouch, mayPush: held === undefined || mayTouch };
|
|
95
|
+
const { face, occupant, mayTouch } = await readSyncGate(syncEntry, sessionStorage, sessionId);
|
|
96
|
+
return { face, occupant, mayPull: face !== undefined && mayTouch, mayPush: occupant === undefined || mayTouch };
|
|
101
97
|
};
|
|
102
98
|
let gate;
|
|
103
99
|
try {
|
|
@@ -26,7 +26,7 @@ export interface SessionsLocal {
|
|
|
26
26
|
degraded?: boolean;
|
|
27
27
|
}>;
|
|
28
28
|
}>;
|
|
29
|
-
/**
|
|
29
|
+
/** [ref] 件4:`/a2a` 面板的短 TTL single-flight 缓存。与 `mcpStatusCache` **分开一张表**而不是
|
|
30
30
|
* 共用带前缀的键:两张面板的体不同形(`peers` vs `servers`),塞进一张表就得靠 unknown 兜住两形,
|
|
31
31
|
* 于是「取到的是哪一形」变成运行期约定 —— 两张表让它回到类型上。 */
|
|
32
32
|
a2aStatusCache: Map<string, {
|