@sema-agent/server 7.151.0 → 7.152.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 +266 -0
- package/README.md +15 -3
- package/README.zh-CN.md +10 -2
- package/USAGE.md +93 -64
- package/deploy/sema-up/README.md +5 -4
- package/deploy/sema-up/cluster-up.sh +6 -6
- package/deploy/sema-up/kube-up.sh +5 -5
- package/deploy/sema-up/lib/flagshape.sh +36 -8
- package/deploy/sema-up/machines.example.yaml +3 -1
- package/deploy/sema-up/sema-up.sh +22 -16
- package/dist/approval-ask-audit-store.d.ts +1 -1
- package/dist/approval-ask-machine.d.ts +4 -10
- package/dist/approval-ask-retention.d.ts +1 -1
- package/dist/approval-card.d.ts +10 -12
- package/dist/approval-gate-currency.d.ts +8 -8
- package/dist/approval-gate-currency.js +3 -4
- package/dist/approval-hmac.d.ts +17 -1
- package/dist/approval-hmac.js +7 -1
- package/dist/approval-queue-kind.d.ts +1 -1
- package/dist/approval.d.ts +2 -2
- package/dist/approval.js +2 -2
- package/dist/bind-host.d.ts +1 -3
- package/dist/bind-host.js +2 -3
- package/dist/boot/coordinators.d.ts +1 -1
- package/dist/boot/coordinators.js +9 -10
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/execution-env.d.ts +1 -0
- package/dist/boot/execution-env.js +31 -5
- package/dist/boot/leader.d.ts +31 -30
- package/dist/boot/leader.js +38 -28
- package/dist/boot/leg-path-adjudication.d.ts +40 -0
- package/dist/boot/leg-path-adjudication.js +18 -0
- package/dist/boot/parked-revive-gate.js +3 -15
- package/dist/boot/reapers.d.ts +1 -1
- package/dist/boot/reapers.js +1 -1
- package/dist/boot/resolve-spec.js +12 -26
- package/dist/boot/runner-deps.d.ts +39 -35
- package/dist/boot/runner-deps.js +92 -89
- package/dist/boot/shutdown.d.ts +6 -0
- package/dist/boot/shutdown.js +10 -4
- package/dist/boot/stage-02-web-admin-pull.js +4 -5
- package/dist/boot/stage-05-execution-env.js +2 -1
- package/dist/boot/stage-06-runners.d.ts +2 -0
- package/dist/boot/stage-06-runners.js +10 -4
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +8 -0
- package/dist/boot/stage-10-http-server.js +11 -15
- package/dist/boot/stores.js +7 -4
- package/dist/boot/web-admin.js +2 -7
- package/dist/capabilities/approvals-push.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +28 -13
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capability-facts.d.ts +22 -20
- package/dist/capability-facts.js +18 -34
- package/dist/card-rule-offers.d.ts +32 -0
- package/dist/card-rule-offers.js +130 -0
- package/dist/config-catalog.js +12 -11
- package/dist/config-dir-trust.js +4 -2
- package/dist/config-invariants.d.ts +18 -7
- package/dist/config-invariants.js +51 -8
- package/dist/config-lkg.d.ts +5 -19
- package/dist/config-lkg.js +8 -62
- package/dist/config-provider.d.ts +1 -1
- package/dist/config-root-migration.d.ts +2 -2
- package/dist/config-types.d.ts +28 -38
- package/dist/config.d.ts +7 -5
- package/dist/config.js +55 -16
- package/dist/control-socket.d.ts +51 -0
- package/dist/control-socket.js +115 -0
- package/dist/deployment-governance.d.ts +2 -1
- package/dist/device-ws-hub.d.ts +7 -0
- package/dist/device-ws-hub.js +7 -1
- package/dist/elicitation.js +2 -2
- package/dist/entry-problems.d.ts +40 -3
- package/dist/entry-problems.js +3 -1
- package/dist/execution-env-refusal.d.ts +8 -2
- package/dist/execution-env-refusal.js +3 -0
- package/dist/fleet/engine-signal.d.ts +12 -29
- package/dist/fleet/engine-signal.js +1 -7
- package/dist/fleet-client.js +2 -5
- package/dist/gate-settlement.d.ts +1 -8
- package/dist/gate-settlement.js +0 -4
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +2 -3
- package/dist/hitl-delivery.js +0 -2
- package/dist/hooks/hook-llm.js +4 -2
- package/dist/hooks/hook-runner.d.ts +3 -2
- package/dist/hooks/hook-runner.js +16 -1
- package/dist/host-decision.d.ts +11 -11
- package/dist/http/active-run-conflict.d.ts +35 -2
- package/dist/http/active-run-conflict.js +48 -19
- package/dist/http/approval-sweeps.d.ts +1 -1
- package/dist/http/approval-sweeps.js +5 -2
- package/dist/http/checkpoint-ctx.d.ts +2 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/dispatch.d.ts +6 -2
- package/dist/http/dispatch.js +25 -5
- package/dist/http/durable-run-leg.js +0 -1
- package/dist/http/health-reader.d.ts +16 -9
- package/dist/http/health-reader.js +2 -1
- package/dist/http/health-self-report.js +1 -1
- package/dist/http/owner-access.d.ts +2 -3
- package/dist/http/owner-access.js +3 -4
- package/dist/http/principal-gate.d.ts +49 -21
- package/dist/http/principal-gate.js +61 -11
- package/dist/http/request-channel.d.ts +23 -0
- package/dist/http/request-channel.js +9 -0
- package/dist/http/request-guards.js +17 -2
- package/dist/http/resume-ctx-read.js +20 -3
- package/dist/http/resume-legs.d.ts +2 -2
- package/dist/http/resume-legs.js +111 -41
- package/dist/http/route-ctx.d.ts +73 -7
- package/dist/http/route-ctx.js +17 -0
- package/dist/http/route-table.d.ts +4 -0
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/admin-config-refresh.js +3 -3
- package/dist/http/routes/admin-drain.d.ts +3 -0
- package/dist/http/routes/admin-drain.js +7 -7
- package/dist/http/routes/admin-memory-optout.js +3 -3
- package/dist/http/routes/adoption.js +3 -5
- package/dist/http/routes/approvals-assistant.d.ts +2 -1
- package/dist/http/routes/approvals-assistant.js +95 -48
- package/dist/http/routes/capabilities.d.ts +4 -0
- package/dist/http/routes/capabilities.js +30 -5
- package/dist/http/routes/config-catalog.js +2 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +19 -7
- package/dist/http/routes/images.js +10 -10
- package/dist/http/routes/memory-bundle.js +3 -3
- package/dist/http/routes/memory-compliance.js +3 -3
- package/dist/http/routes/memory-consolidation.js +2 -2
- package/dist/http/routes/memory-origin.js +3 -3
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/observability.js +3 -3
- package/dist/http/routes/retention-ops.js +3 -3
- package/dist/http/routes/rules.js +5 -5
- package/dist/http/routes/run-permission-mode.d.ts +2 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +4 -9
- package/dist/http/routes/runs.js +73 -33
- package/dist/http/routes/session-sync.d.ts +18 -6
- package/dist/http/routes/session-sync.js +38 -36
- package/dist/http/routes/sessions.js +20 -9
- package/dist/http/routes/tasks.js +28 -37
- package/dist/http/routes/workflows.js +1 -1
- package/dist/http/server.d.ts +9 -3
- package/dist/http/server.js +15 -3
- package/dist/http/submit-ctx.d.ts +1 -1
- package/dist/http/submit-ctx.js +2 -0
- package/dist/human-facing-args.d.ts +33 -0
- package/dist/human-facing-args.js +33 -0
- package/dist/index.d.ts +1 -1
- package/dist/k8s-api-credentials.d.ts +29 -0
- package/dist/k8s-api-credentials.js +25 -0
- package/dist/lane-home-probe.d.ts +1 -1
- package/dist/lane-home-probe.js +2 -1
- package/dist/leader/diffup.d.ts +19 -0
- package/dist/leader/diffup.js +7 -0
- package/dist/leader/leader.d.ts +114 -10
- package/dist/leader/leader.js +69 -5
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/run-entry.d.ts +2 -1
- package/dist/leader/run-entry.js +1 -1
- package/dist/leader/wire.d.ts +42 -132
- package/dist/leader/wire.js +81 -67
- package/dist/memory-scope.d.ts +20 -9
- package/dist/memory-scope.js +15 -3
- package/dist/model-entry-headers.d.ts +9 -10
- package/dist/model-entry-headers.js +0 -4
- package/dist/model-entry-refusal.d.ts +7 -4
- package/dist/model-entry-refusal.js +11 -5
- package/dist/model-plane-face.d.ts +1 -1
- package/dist/model-plane-face.js +3 -1
- package/dist/model-route-endpoint.d.ts +13 -7
- package/dist/model-route-endpoint.js +14 -11
- package/dist/model-select.d.ts +22 -0
- package/dist/model-select.js +9 -0
- package/dist/model-unreachable-notice.d.ts +11 -0
- package/dist/model-unreachable-notice.js +4 -4
- package/dist/object-store-buckets.d.ts +640 -6
- package/dist/object-store-buckets.js +166 -0
- package/dist/observability/fail-open.d.ts +20 -16
- package/dist/observability/fail-open.js +20 -16
- package/dist/observability/logger.js +3 -1
- package/dist/operator-roster.d.ts +68 -0
- package/dist/operator-roster.js +32 -0
- package/dist/orchestration/workflow-park-decision.d.ts +1 -1
- package/dist/outbound-dispatcher.d.ts +71 -8
- package/dist/outbound-dispatcher.js +48 -7
- package/dist/owner-faces.d.ts +1 -1
- package/dist/parked-decide.d.ts +2 -2
- package/dist/permission-mode-admission.d.ts +59 -26
- package/dist/permission-mode-admission.js +37 -10
- package/dist/plugins/approval-ask-store-file.d.ts +4 -0
- package/dist/plugins/approval-ask-store-file.js +3 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
- package/dist/plugins/approval-ask-store-memory.js +7 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
- package/dist/plugins/approval-ask-store-sql.js +12 -1
- package/dist/plugins/blob-backend.d.ts +83 -7
- package/dist/plugins/blob-backend.js +57 -6
- package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
- package/dist/plugins/checkpoint-index-columns.js +3 -6
- package/dist/plugins/checkpoint-store-face.d.ts +119 -61
- package/dist/plugins/checkpoint-store-face.js +134 -55
- package/dist/plugins/landed-attendants.d.ts +2 -2
- package/dist/plugins/local-checkpoint-store.d.ts +13 -3
- package/dist/plugins/local-checkpoint-store.js +38 -49
- package/dist/plugins/one-time-migrations.d.ts +51 -21
- package/dist/plugins/one-time-migrations.js +67 -45
- package/dist/plugins/pg-pool.js +11 -9
- package/dist/plugins/remote-env-host.d.ts +29 -10
- package/dist/plugins/remote-env-host.js +99 -19
- package/dist/plugins/remote-env-k8s.d.ts +68 -2
- package/dist/plugins/remote-env-k8s.js +107 -24
- package/dist/plugins/replica-registration.d.ts +1 -1
- package/dist/plugins/replica-registration.js +1 -1
- package/dist/plugins/s3-presign.d.ts +23 -0
- package/dist/plugins/s3-presign.js +37 -3
- package/dist/plugins/scheduler-support.d.ts +2 -2
- package/dist/plugins/scheduler-support.js +0 -3
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-file-ledger.d.ts +2 -2
- package/dist/plugins/send-file-ledger.js +2 -2
- package/dist/plugins/send-user-file.d.ts +3 -4
- package/dist/plugins/send-user-file.js +56 -52
- package/dist/plugins/session-blob-claim-token.d.ts +15 -0
- package/dist/plugins/session-blob-claim-token.js +20 -0
- package/dist/plugins/session-blob-namespace.d.ts +46 -0
- package/dist/plugins/session-blob-namespace.js +116 -0
- package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
- package/dist/plugins/snapshot-bucket-presence.js +8 -2
- package/dist/plugins/sql-json-column.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +44 -2
- package/dist/plugins/store-backend.js +75 -53
- package/dist/plugins/task-attachment-store.d.ts +2 -1
- package/dist/plugins/task-attachment-store.js +5 -1
- package/dist/plugins/tidb-pool.js +19 -24
- package/dist/plugins/workspace-custody.d.ts +4 -3
- package/dist/question.d.ts +2 -1
- package/dist/question.js +5 -13
- package/dist/request-form.d.ts +6 -1
- package/dist/request-form.js +4 -1
- package/dist/rule-issue-reading.d.ts +1 -1
- package/dist/rules-consent.d.ts +1 -1
- package/dist/run-local.js +8 -9
- package/dist/runs.d.ts +17 -8
- package/dist/runs.js +15 -2
- package/dist/runtime-governance.d.ts +2 -2
- package/dist/sema-config-dir.d.ts +2 -1
- package/dist/session-sync.d.ts +27 -11
- package/dist/session-sync.js +15 -7
- package/dist/stores-sql/blob-index-sql.d.ts +30 -9
- package/dist/stores-sql/blob-index-sql.js +40 -21
- package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
- package/dist/stores-sql/checkpoint-store-sql.js +70 -146
- package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
- package/dist/stores-sql/file-history-store-sql.js +100 -28
- package/dist/stores-sql/retention-store-sql.js +3 -9
- package/dist/task-settings.d.ts +6 -5
- package/dist/task-settings.js +3 -3
- package/dist/tool-approval.d.ts +342 -518
- package/dist/tool-approval.js +498 -1207
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/ledger-events.d.ts +3 -3
- package/dist/trace/project.d.ts +2 -3
- package/dist/trace/redact.js +3 -2
- package/dist/trace/self-minted-wire.d.ts +287 -157
- package/dist/trace/self-minted-wire.js +93 -48
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/read-warnings.js +1 -1
- package/dist/web-admin-client/types.d.ts +1 -1
- package/dist/workspace-space.d.ts +10 -4
- package/dist/workspace-space.js +4 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
- package/package.json +6 -2
- package/scripts/embed-minimal.mjs +3 -3
- package/wire-changes/server@7.152.0.json +900 -0
- package/dist/config-d-migration.d.ts +0 -37
- package/dist/config-d-migration.js +0 -60
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
11
11
|
import { type SessionRulesRecord, type FileHistoryExport } from "@sema-agent/core";
|
|
12
|
+
import { type VisibleBlobs } from "../../session-sync.js";
|
|
12
13
|
import { type ImportAnchor, type StagingHandle } from "../../session-sync-kernel.js";
|
|
14
|
+
import { type ListFilter } from "../../owner-faces.js";
|
|
13
15
|
import type { DurableOwnerKey } from "../../owner-brands.js";
|
|
16
|
+
import type { SyncLanding } from "../../plugins/store-backend.js";
|
|
14
17
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
15
18
|
import { type SessionRowPatch } from "../../session-row.js";
|
|
16
19
|
import { type SessionFace } from "../../session-incarnation.js";
|
|
@@ -39,15 +42,24 @@ export type PendingStaging = {
|
|
|
39
42
|
expectedOccupant: SessionFace | undefined;
|
|
40
43
|
/** 开这张暂存时占下的导入租约的键({@link importLeaseKey});续期与放掉都按它,不再从会话 id 重算。 */
|
|
41
44
|
leaseKey: string;
|
|
45
|
+
/** 第一阶段那一次可见性读的答案(落点 = 开暂存时门判的那一格):提交臂的文件历史重放只经它的取值口取字节 —— 许可集在第一阶段定,覆盖写的擦除不改它。 */
|
|
46
|
+
visible: VisibleBlobs;
|
|
42
47
|
};
|
|
43
48
|
/**
|
|
44
|
-
*
|
|
45
|
-
* · id 被占着(活行 ∪ 在删行 —— 「被占着」的唯一读法,门读的 `occupant`)⇒ 落点是住客的属主格(`null` = 无主):所有碰得到这一行的调用方(属主、fleet、存量无主互认)
|
|
46
|
-
* · id 空闲 ⇒ 还没有谁的会话,落点是这位调用方自己将成的属主(
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
+
* 这次推送 / 拉取**落在哪一格**(落点)的唯一读法 —— 导入租约的键、blob 授予(`PUT …/sync/blobs/:hash`)、blob 可见性(第一阶段预检 · 重放取值 · `GET …/sync/history/blobs/:hash`)同读它:
|
|
50
|
+
* · id 被占着(活行 ∪ 在删行 —— 「被占着」的唯一读法,门读的 `occupant`)⇒ 落点是住客的属主格(`null` = 无主):所有碰得到这一行的调用方(属主、fleet、存量无主互认)落在同一格;
|
|
51
|
+
* · id 空闲 ⇒ 还没有谁的会话,落点是这位调用方自己将成的属主(fleet ⇒ 无主格;主体 ⇒ 主体):各落各的,先提交者得(提交的属主 CAS 要求那一格仍空着)。
|
|
52
|
+
* `occupied` 随落点走:只有被占着的 id,它的历史图才是 blob 的来历(空闲 id 上残留的图不是任何一位调用方持有过字节的证明)。
|
|
53
|
+
* 落点的主体只来自门读的 `occupant` 与 `listFilterOf` 的派生,不另读调用方;拉取口只在活行上放行(`pullGateOk`),那时 `occupant` 就是活行。
|
|
49
54
|
*/
|
|
50
|
-
export declare function
|
|
55
|
+
export declare function landingOf(gate: {
|
|
56
|
+
readonly occupant: SessionFace | undefined;
|
|
57
|
+
}, scope: ListFilter, sessionId: string): SyncLanding;
|
|
58
|
+
/**
|
|
59
|
+
* 导入租约的键 = 这次导入的落点({@link landingOf}):落点属主格 + 会话 id。同一份会话上第二份在飞导入,谁发起的都 409(同一格);空闲 id 上一个主体的在飞导入既不挡、也不透给
|
|
60
|
+
* 另一个主体 —— 租约键不含落点属主格时,空闲 id 上任一主体都能占位,并从 409 读到「此 id 此刻有人在导」。占租约在 `pushGateOk` 之后。
|
|
61
|
+
*/
|
|
62
|
+
export declare function importLeaseKey(landing: SyncLanding): string;
|
|
51
63
|
/** 一把导入租约:最后活动时刻 + 持有它的暂存 + 它守的会话 id(清扫与运维日志用,不从键里反解)。 */
|
|
52
64
|
export type ImportLease = {
|
|
53
65
|
at: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { once } from "node:events";
|
|
2
2
|
import { sessionLogDigest, sessionLogDigestsComparable, validateFileHistoryExport } from "@sema-agent/core";
|
|
3
3
|
import { StringDecoder } from "node:string_decoder";
|
|
4
|
-
import { FileHistoryUnreadableError,
|
|
4
|
+
import { FileHistoryUnreadableError, NO_VISIBLE_BLOBS, exportSessionManifest } from "../../session-sync.js";
|
|
5
5
|
import { fileHistoryImportFace } from "../../plugins/landed-attendants.js";
|
|
6
6
|
import * as facts from "../../capability-facts.js";
|
|
7
7
|
import { classifySyncRelationshipByIds, createStagedLogFeed, MalformedImportEntryError, newStagingToken, readImportAnchors, SyncConflictError } from "../../session-sync-kernel.js";
|
|
@@ -23,8 +23,13 @@ import { defaultTaskRegistry } from "@sema-agent/core";
|
|
|
23
23
|
import { SessionActiveRunError, SessionForkInProgressError, SessionWriterPresentError } from "../../execution-placement.js";
|
|
24
24
|
import { probeSessionChildren, sessionTenantScope } from "../../session-children-retire.js";
|
|
25
25
|
import { liveChildrenRefusal } from "./background.js";
|
|
26
|
-
export function
|
|
27
|
-
return
|
|
26
|
+
export function landingOf(gate, scope, sessionId) {
|
|
27
|
+
return gate.occupant !== undefined
|
|
28
|
+
? { sessionId, owner: gate.occupant.owner, occupied: true }
|
|
29
|
+
: { sessionId, owner: scope.fleetWide ? null : scope.gateOwner, occupied: false };
|
|
30
|
+
}
|
|
31
|
+
export function importLeaseKey(landing) {
|
|
32
|
+
return JSON.stringify([landing.owner, landing.sessionId]);
|
|
28
33
|
}
|
|
29
34
|
export function createSessionSyncLocal() {
|
|
30
35
|
const pendingStagings = new Map();
|
|
@@ -108,6 +113,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
108
113
|
}
|
|
109
114
|
const gateFace = gate.face;
|
|
110
115
|
const owner = gateFace === undefined ? undefined : gateFace.owner;
|
|
116
|
+
const landing = landingOf(gate, scope, sessionId);
|
|
111
117
|
const sendSessionAbsent = (reason) => refuse(syncEntry, res, reason);
|
|
112
118
|
const pullGateOk = () => {
|
|
113
119
|
if (!gate.mayPull) {
|
|
@@ -199,7 +205,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
199
205
|
if (plan.wipe && !(await attendants.wipe(sessionId, landed)).landed)
|
|
200
206
|
return { kind: "gone", leg: "wipe" };
|
|
201
207
|
if (plan.fileHistory !== null && canReplayHistory) {
|
|
202
|
-
const h = await attendants.replayHistory(sessionId, landed, plan.fileHistory,
|
|
208
|
+
const h = await attendants.replayHistory(sessionId, landed, plan.fileHistory, plan.getBlob, { allowReplace: plan.allowReplace });
|
|
203
209
|
if (!h.landed)
|
|
204
210
|
return { kind: "gone", leg: "file_history" };
|
|
205
211
|
if (!h.value.ok)
|
|
@@ -221,20 +227,20 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
221
227
|
if (match.decl.id === "session-sync-manifest") {
|
|
222
228
|
if (!pullGateOk())
|
|
223
229
|
return;
|
|
224
|
-
let
|
|
230
|
+
let manifest;
|
|
225
231
|
try {
|
|
226
|
-
|
|
232
|
+
manifest = await exportSessionManifest(sessionId, backend, historyStore);
|
|
227
233
|
}
|
|
228
234
|
catch (e) {
|
|
229
235
|
if (sendSessionPolicyError(res, e) || sendPullUnreadable(req, res, deps.logger, e))
|
|
230
236
|
return;
|
|
231
237
|
throw e;
|
|
232
238
|
}
|
|
233
|
-
if (
|
|
239
|
+
if (manifest === null) {
|
|
234
240
|
sendSessionAbsent("missing");
|
|
235
241
|
return;
|
|
236
242
|
}
|
|
237
|
-
await pullAnswer(() => sendJson(res, 200, { manifest
|
|
243
|
+
await pullAnswer(() => sendJson(res, 200, { manifest }));
|
|
238
244
|
return;
|
|
239
245
|
}
|
|
240
246
|
if (match.decl.id === "session-sync-entries") {
|
|
@@ -301,9 +307,9 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
301
307
|
return;
|
|
302
308
|
}
|
|
303
309
|
if (match.decl.id === "session-sync-history-blob") {
|
|
304
|
-
const
|
|
305
|
-
if (!
|
|
306
|
-
sendSessionSyncUnavailable(res,
|
|
310
|
+
const faces = facts.sessionSyncFileHistory(deps);
|
|
311
|
+
if (!faces.available) {
|
|
312
|
+
sendSessionSyncUnavailable(res, faces.why);
|
|
307
313
|
return;
|
|
308
314
|
}
|
|
309
315
|
if (!pullGateOk())
|
|
@@ -317,25 +323,22 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
317
323
|
sendError(res, 400, "request.id_invalid", "blob hash must be 64 lowercase-hex chars");
|
|
318
324
|
return;
|
|
319
325
|
}
|
|
320
|
-
|
|
321
|
-
const exchange = blobs.blobExchange;
|
|
322
|
-
let envelope;
|
|
326
|
+
let visible;
|
|
323
327
|
try {
|
|
324
|
-
|
|
328
|
+
visible = await faces.blobs.visible(landing, [hash]);
|
|
325
329
|
}
|
|
326
330
|
catch (e) {
|
|
327
331
|
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
328
332
|
return;
|
|
329
333
|
throw e;
|
|
330
334
|
}
|
|
331
|
-
|
|
332
|
-
if (!referenced) {
|
|
335
|
+
if (!visible.hashes.has(hash)) {
|
|
333
336
|
await pullAnswer(() => sendError(res, 404, "not_found.blob", "blob not found"));
|
|
334
337
|
return;
|
|
335
338
|
}
|
|
336
339
|
let bytes;
|
|
337
340
|
try {
|
|
338
|
-
bytes = await
|
|
341
|
+
bytes = await visible.get(hash);
|
|
339
342
|
}
|
|
340
343
|
catch (e) {
|
|
341
344
|
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
@@ -356,9 +359,9 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
356
359
|
if (match.decl.id === "session-sync-blob-put") {
|
|
357
360
|
if (rateLimited(req, res))
|
|
358
361
|
return;
|
|
359
|
-
const
|
|
360
|
-
if (!
|
|
361
|
-
sendSessionSyncUnavailable(res,
|
|
362
|
+
const faces = facts.sessionSyncFileHistory(deps);
|
|
363
|
+
if (!faces.available) {
|
|
364
|
+
sendSessionSyncUnavailable(res, faces.why);
|
|
362
365
|
return;
|
|
363
366
|
}
|
|
364
367
|
if (!pushGateOk())
|
|
@@ -369,7 +372,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
369
372
|
return;
|
|
370
373
|
}
|
|
371
374
|
const body = await readRawBody(req, SYNC_BLOB_MAX_BYTES);
|
|
372
|
-
const put = await blobs.
|
|
375
|
+
const put = await faces.blobs.put(landing, hash, new Uint8Array(body));
|
|
373
376
|
if (!put.ok) {
|
|
374
377
|
if (put.error.blobCap !== undefined) {
|
|
375
378
|
sendError(res, 413, "blob_too_large_for_sql", put.error.message, { wallSource: put.error.blobCap.wallSource });
|
|
@@ -471,15 +474,21 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
471
474
|
sendError(res, 409, "conflict", "sync conflict", { relation: relA });
|
|
472
475
|
return;
|
|
473
476
|
}
|
|
477
|
+
let visible = NO_VISIBLE_BLOBS;
|
|
474
478
|
if (fileHistory !== null) {
|
|
475
479
|
const distinct = new Set();
|
|
476
480
|
for (const t of fileHistory.tracked)
|
|
477
481
|
for (const v of t.versions)
|
|
478
482
|
if (v.blobHash !== null)
|
|
479
483
|
distinct.add(v.blobHash);
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
|
|
484
|
+
if (distinct.size > 0) {
|
|
485
|
+
const faces = facts.sessionSyncFileHistory(deps);
|
|
486
|
+
if (faces.available)
|
|
487
|
+
visible = await faces.blobs.visible(landing, [...distinct]);
|
|
488
|
+
if ([...distinct].some((h) => !visible.hashes.has(h))) {
|
|
489
|
+
await pushAnswer(() => sendMissingBlob(res));
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
483
492
|
}
|
|
484
493
|
}
|
|
485
494
|
let payloadVerified = false;
|
|
@@ -554,7 +563,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
554
563
|
}
|
|
555
564
|
let landedA;
|
|
556
565
|
try {
|
|
557
|
-
landedA = await landAttendants(attendantsA, gateFace, { wipe: false, fileHistory, allowReplace: resolution === "overwrite-dst", policy, operator: operatorOk, anchors, owner: importingPrincipal, row });
|
|
566
|
+
landedA = await landAttendants(attendantsA, gateFace, { wipe: false, fileHistory, getBlob: visible.get, allowReplace: resolution === "overwrite-dst", policy, operator: operatorOk, anchors, owner: importingPrincipal, row });
|
|
558
567
|
}
|
|
559
568
|
catch (e) {
|
|
560
569
|
if (sendSessionPolicyError(res, e))
|
|
@@ -582,7 +591,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
582
591
|
}));
|
|
583
592
|
return;
|
|
584
593
|
}
|
|
585
|
-
const leaseKey = importLeaseKey(
|
|
594
|
+
const leaseKey = importLeaseKey(landing);
|
|
586
595
|
const staleSecRaw = deps.config.syncImportLeaseStaleSec;
|
|
587
596
|
const staleSec = typeof staleSecRaw === "number" && Number.isFinite(staleSecRaw) ? staleSecRaw : 600;
|
|
588
597
|
const staleMs = staleSec * 1000;
|
|
@@ -605,7 +614,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
605
614
|
importLeases.set(leaseKey, { at: Date.now(), stagingId, sessionId });
|
|
606
615
|
pendingStagings.set(stagingId, {
|
|
607
616
|
handle, realSessionId: sessionId, owner: importingPrincipal, ...(resolution ? { resolution } : {}), operatorOk,
|
|
608
|
-
fileHistory, policy, anchors, row, declaredEntryIds: entryIds, openedBy: ownerKeyOf(scope.gateOwner), expectedOccupant: gateFace, leaseKey,
|
|
617
|
+
fileHistory, policy, anchors, row, declaredEntryIds: entryIds, openedBy: ownerKeyOf(scope.gateOwner), expectedOccupant: gateFace, leaseKey, visible,
|
|
609
618
|
});
|
|
610
619
|
sendJson(res, 200, { stagingId, relation: relA.relation });
|
|
611
620
|
return;
|
|
@@ -729,6 +738,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
729
738
|
const landedB = await landAttendants(attendantsB, committed.face, {
|
|
730
739
|
wipe: pending.resolution === "overwrite-dst" && (committed.relation === "fork" || committed.relation === "stale"),
|
|
731
740
|
fileHistory: pending.fileHistory,
|
|
741
|
+
getBlob: pending.visible.get,
|
|
732
742
|
allowReplace: pending.resolution === "overwrite-dst",
|
|
733
743
|
policy: pending.policy,
|
|
734
744
|
operator: pending.operatorOk,
|
|
@@ -869,14 +879,6 @@ function sendFileHistoryReplayFailure(res, failure) {
|
|
|
869
879
|
const { code, permanence } = failure.error;
|
|
870
880
|
sendError(res, 422, code, `file history import failed (${code})`, permanence === undefined ? undefined : { permanence });
|
|
871
881
|
}
|
|
872
|
-
async function firstMissingBlob(blobFaces, referenced) {
|
|
873
|
-
if (referenced.length === 0)
|
|
874
|
-
return undefined;
|
|
875
|
-
if (!blobFaces.available)
|
|
876
|
-
return referenced[0];
|
|
877
|
-
const present = await blobFaces.blobExchange.has(referenced);
|
|
878
|
-
return referenced.find((h) => !present.has(h));
|
|
879
|
-
}
|
|
880
882
|
function sendSessionSyncUnavailable(res, why) {
|
|
881
883
|
switch (why) {
|
|
882
884
|
case "store_backend_absent":
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { SessionError, materializeA2aTools, materializeMcpTools, sanitizePathComponent } from "@sema-agent/core";
|
|
2
3
|
import { a2aForScenario, mcpForScenario } from "../../web-admin-client/facade.js";
|
|
3
4
|
import { HttpError, isUuidShape } from "../../security.js";
|
|
@@ -14,6 +15,7 @@ import { readWritableSessionPermissionRules } from "../../session-policy-wire.js
|
|
|
14
15
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
|
|
15
16
|
import { gatedPerson, gatedSubject, sendPrincipalRefusal } from "../principal-gate.js";
|
|
16
17
|
import { personOf } from "../../owner-brands.js";
|
|
18
|
+
import { scenarioRequestedBySubject } from "../../request-form.js";
|
|
17
19
|
import { leafOfAdmittedHead, sessionFaceOfHead } from "../../session-incarnation.js";
|
|
18
20
|
import { admitSessionById, admitSessionForDeletion, admitSessionRow, admitSessionView, admittedAsFleet, callerEntry, callerOf, deliverVerdict, enter, judgeSessionById, protocolEntry, readOwnerCell, readVisibleLatestRun, refuse, sessionOccupantUnchanged } from "../owner-access.js";
|
|
19
21
|
import { assertNever } from "../route-ctx.js";
|
|
@@ -23,6 +25,7 @@ import { projectSessionListRow } from "./sessions-list.js";
|
|
|
23
25
|
import { readSessionRowPatch } from "../../session-row.js";
|
|
24
26
|
import { forkPathOf, resolveExecutionContext } from "../execution-context.js";
|
|
25
27
|
import { executionPlacementView } from "../../execution-placement.js";
|
|
28
|
+
import { readSessionOccupant, refusalOccupantOf } from "../active-run-conflict.js";
|
|
26
29
|
import { anchorReaderOf, resolveHistoryAnchor } from "../../history-anchor.js";
|
|
27
30
|
import { engineRefusalAnswerOf } from "../engine-refusal.js";
|
|
28
31
|
import { buildRewindFilesReply } from "../rewind-files-reply.js";
|
|
@@ -135,7 +138,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
135
138
|
const ledgerHalf = lastLegMcp === undefined ? {} : { lastLegMcp };
|
|
136
139
|
let scenarioName;
|
|
137
140
|
try {
|
|
138
|
-
scenarioName = await deps.scenarioGate(principal, undefined);
|
|
141
|
+
({ scenario: scenarioName } = await deps.scenarioGate(principal, scenarioRequestedBySubject(caller.subject, undefined)));
|
|
139
142
|
}
|
|
140
143
|
catch (e) {
|
|
141
144
|
if (!(e instanceof HttpError))
|
|
@@ -192,7 +195,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
192
195
|
sendPrincipalRefusal(res, req, deps.config);
|
|
193
196
|
return;
|
|
194
197
|
}
|
|
195
|
-
const scenarioName = await deps.scenarioGate(principal, undefined);
|
|
198
|
+
const { scenario: scenarioName } = await deps.scenarioGate(principal, scenarioRequestedBySubject(gatedSubject(req, deps.config), undefined));
|
|
196
199
|
const specs = a2aForScenario(deps.config.a2aPeers, scenarioName);
|
|
197
200
|
if (!specs || specs.length === 0) {
|
|
198
201
|
sendJson(res, 200, { peers: [], asOf: new Date().toISOString() });
|
|
@@ -256,13 +259,21 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
256
259
|
if (admittedHead === undefined)
|
|
257
260
|
return;
|
|
258
261
|
const leafId = leafOfAdmittedHead(admittedHead.row);
|
|
259
|
-
const
|
|
262
|
+
const runs = facts.durableRuns(deps);
|
|
263
|
+
const occupant = runs.available ? await readSessionOccupant(runs.runStore, headSession) : undefined;
|
|
264
|
+
if (occupant !== undefined && occupant.activeTaskId !== null && !(await sessionOccupantUnchanged(headEntry, headFact.sessionStorage, headSession, admittedHead.row))) {
|
|
265
|
+
refuse(headEntry, res, "missing");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const body = { sessionId: headSession, leafId, ...occupant };
|
|
269
|
+
const bytes = JSON.stringify(body);
|
|
270
|
+
const etag = `"${createHash("sha256").update(bytes).digest("base64url")}"`;
|
|
260
271
|
if (req.headers["if-none-match"] === etag) {
|
|
261
272
|
res.writeHead(304, { etag }).end();
|
|
262
273
|
return;
|
|
263
274
|
}
|
|
264
|
-
res.
|
|
265
|
-
|
|
275
|
+
res.writeHead(200, { "content-type": "application/json", etag });
|
|
276
|
+
res.end(bytes);
|
|
266
277
|
return;
|
|
267
278
|
}
|
|
268
279
|
}
|
|
@@ -767,9 +778,9 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
767
778
|
return;
|
|
768
779
|
}
|
|
769
780
|
const entryId = await resolveHistoryAnchor("rewindFilesTo", anchors, anchorReaderOf(req, deps, rewindEntry.caller), sessionId, read.to, sessionOwner, "to");
|
|
770
|
-
const
|
|
771
|
-
if (
|
|
772
|
-
sendError(res, 409, "conflict.session_active_run", `session has an active run (${
|
|
781
|
+
const holder = await deps.runStore?.getActiveTaskId(sessionId);
|
|
782
|
+
if (holder) {
|
|
783
|
+
sendError(res, 409, "conflict.session_active_run", `session has an active run (${holder}); its files cannot be rewound under it — wait for that run to end (or cancel it), then rewind`, await refusalOccupantOf(deps.runStore, holder));
|
|
773
784
|
return;
|
|
774
785
|
}
|
|
775
786
|
let result;
|
|
@@ -883,7 +894,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
883
894
|
const owner = deleteAdmitted.row.owner;
|
|
884
895
|
const result = await purge(sessionId, owner);
|
|
885
896
|
if ("active" in result) {
|
|
886
|
-
sendError(res, 409, "conflict.session_active_run", "session has an active run; cancel it before deleting",
|
|
897
|
+
sendError(res, 409, "conflict.session_active_run", "session has an active run; cancel it before deleting", await refusalOccupantOf(deps.runStore, result.active));
|
|
887
898
|
return;
|
|
888
899
|
}
|
|
889
900
|
if ("inProgress" in result) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal.js";
|
|
2
2
|
import { runWithVerification, runCascade, taskNotificationDedupKey, uuidv7 } from "@sema-agent/core";
|
|
3
3
|
import { heldFor } from "../../peer-seat.js";
|
|
4
|
-
import { beatRunHeartbeat, createRunAnchorWriter, graftParkToolCallId, heldParkOf, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, zeroWorkRefusalFacts, releaseKeyIfZeroWorkRefusal,
|
|
4
|
+
import { beatRunHeartbeat, createRunAnchorWriter, graftParkToolCallId, heldParkOf, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, zeroWorkRefusalFacts, releaseKeyIfZeroWorkRefusal, TurnAnchorCapture, HEARTBEAT_MS } from "../../runs.js";
|
|
5
5
|
import { commitParkRow } from "../../park-row-commit.js";
|
|
6
6
|
import { atHostBoundary, failureCodeOf, sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
7
7
|
import { voidVerifierPauseCard } from "../../verifier-pause-card.js";
|
|
@@ -172,7 +172,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
172
172
|
};
|
|
173
173
|
const assembleApprovalLeg = (streamApprovalOn) => resolveApprovalLeg({ ...approvalLegInput(streamApprovalOn), nowMonotonicMs: performance.now() });
|
|
174
174
|
const principal = prepared.auth?.subject;
|
|
175
|
-
const wiringOperatorFace = explicitOperatorOk(gatedPerson(req, deps.config), deps.config);
|
|
175
|
+
const wiringOperatorFace = explicitOperatorOk(gatedPerson(req, deps.config), deps.config, "operate");
|
|
176
176
|
let wiringReceipt;
|
|
177
177
|
const instructionLeg = createInstructionSelectionLeg();
|
|
178
178
|
const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: prepared.legFacts?.skills });
|
|
@@ -236,7 +236,6 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
236
236
|
onDisconnect();
|
|
237
237
|
const replayGate = resolveStreamApprovalGate({
|
|
238
238
|
toolApprovalEnabled: Boolean(deps.toolApproval),
|
|
239
|
-
streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
|
|
240
239
|
backend: deps.backend,
|
|
241
240
|
checkpointStore: deps.checkpointStore,
|
|
242
241
|
});
|
|
@@ -291,25 +290,6 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
291
290
|
let durableLegId;
|
|
292
291
|
let durableHeartbeat;
|
|
293
292
|
const cancelledResult = (errorMessage, cause) => mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId, startedAtMs: streamLegStartedAt, message: errorMessage, cause });
|
|
294
|
-
let userMsgEntryId;
|
|
295
|
-
const promptCommit = createPromptCommitReader();
|
|
296
|
-
let anchorPut = false;
|
|
297
|
-
const putRewindAnchor = async () => {
|
|
298
|
-
const entryId = userMsgEntryId;
|
|
299
|
-
const write = createRunAnchorWriter({
|
|
300
|
-
seat: deps.backend?.landedAttendants(),
|
|
301
|
-
sessionId: prepared.spec.sessionId,
|
|
302
|
-
owner: principal ?? null,
|
|
303
|
-
promptKey: durableTaskId,
|
|
304
|
-
witness: prepared.legFacts?.sessionWitness,
|
|
305
|
-
occupantOf: sessionOccupantReadOf(deps.sessionStorage),
|
|
306
|
-
leafOf: undefined,
|
|
307
|
-
}).prompt;
|
|
308
|
-
if (anchorPut || !entryId || write === undefined)
|
|
309
|
-
return;
|
|
310
|
-
anchorPut = true;
|
|
311
|
-
await settleResumeAnchor((held) => write(entryId, held), { leg: "stream", kind: "prompt" });
|
|
312
|
-
};
|
|
313
293
|
try {
|
|
314
294
|
if (deps.runStore) {
|
|
315
295
|
const tid = earlyDurableTid ?? uuidv7();
|
|
@@ -526,15 +506,32 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
526
506
|
steerableRuns.set(durableTaskId, liveStream);
|
|
527
507
|
prepared.acceptance.attachStream({ reachable: () => !closed && !res.writableEnded && !res.destroyed, write: (frame) => writeLiveFrame(res, { ...frame }) });
|
|
528
508
|
const liveWorkspace = createSessionWorkspaceRecorder(sessionWorkspaceStoreOf(deps.sessionStorage), prepared.legFacts?.sessionWitness);
|
|
509
|
+
const anchor = new TurnAnchorCapture(undefined, "stream", createRunAnchorWriter({
|
|
510
|
+
seat: deps.backend?.landedAttendants(),
|
|
511
|
+
sessionId: prepared.spec.sessionId,
|
|
512
|
+
owner: principal ?? null,
|
|
513
|
+
promptKey: durableTaskId,
|
|
514
|
+
witness: prepared.legFacts?.sessionWitness,
|
|
515
|
+
occupantOf: sessionOccupantReadOf(deps.sessionStorage),
|
|
516
|
+
leafOf: undefined,
|
|
517
|
+
}).prompt);
|
|
529
518
|
for await (const ev of liveStream) {
|
|
530
519
|
if (closed)
|
|
531
520
|
break;
|
|
532
521
|
for (const f of ev.type === "done" ? [...childText.cut(), ...topText.cut()] : topText.onEvent(ev))
|
|
533
522
|
writeLiveFrame(res, f);
|
|
534
|
-
const promptEntryId = promptCommit(ev);
|
|
535
|
-
if (promptEntryId !== undefined)
|
|
536
|
-
userMsgEntryId = promptEntryId;
|
|
537
523
|
const causeAtDone = ev.type === "done" ? ac.cause() : undefined;
|
|
524
|
+
switch (ev.type) {
|
|
525
|
+
case "human_input":
|
|
526
|
+
anchor.onHumanInput(ev);
|
|
527
|
+
break;
|
|
528
|
+
case "message_committed":
|
|
529
|
+
await anchor.onMessageCommitted(ev.role, ev.entryId);
|
|
530
|
+
break;
|
|
531
|
+
case "turn_end":
|
|
532
|
+
await anchor.onTurnEnd();
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
538
535
|
await deps.userInputLedger?.observeLegEvent(liveStream, ev);
|
|
539
536
|
await prepared.acceptance.observe(ev);
|
|
540
537
|
if (ev.type === "done")
|
|
@@ -552,7 +549,6 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
552
549
|
}
|
|
553
550
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective);
|
|
554
551
|
noteRunOutcome(finalResult);
|
|
555
|
-
await putRewindAnchor();
|
|
556
552
|
if (!(durableTaskId && deps.runStore && deps.checkpointStore && heldCard !== undefined))
|
|
557
553
|
settleFleet(outwardPlane(finalResult.terminal).status, fleetRunResiduals(finalResult, failureCodeOf(finalResult.terminal)));
|
|
558
554
|
if (ledgerSink && finalResult) {
|
|
@@ -694,7 +690,6 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
694
690
|
: withElicit();
|
|
695
691
|
const streamApprovalOn = resolveStreamApprovalGate({
|
|
696
692
|
toolApprovalEnabled: Boolean(deps.toolApproval),
|
|
697
|
-
streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
|
|
698
693
|
backend: deps.backend,
|
|
699
694
|
checkpointStore: deps.checkpointStore,
|
|
700
695
|
}).active;
|
|
@@ -708,14 +703,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
708
703
|
abortSignal: ac.signal,
|
|
709
704
|
sessionId: prepared.spec.sessionId,
|
|
710
705
|
...approvalLegOriginOf(prepared.execution),
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
legKey: "",
|
|
716
|
-
...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}),
|
|
717
|
-
}
|
|
718
|
-
: { family: "tool_approval", transport: hitlTransport }),
|
|
706
|
+
family: "approval_request",
|
|
707
|
+
transport: hitlTransport,
|
|
708
|
+
legKey: "",
|
|
709
|
+
...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}),
|
|
719
710
|
}
|
|
720
711
|
: undefined;
|
|
721
712
|
if (deps.toolApproval) {
|
|
@@ -727,7 +718,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
727
718
|
taskId: askTaskId,
|
|
728
719
|
sessionId: prepared.spec.sessionId,
|
|
729
720
|
...approvalLegOriginOf(prepared.execution),
|
|
730
|
-
|
|
721
|
+
legKey: "",
|
|
722
|
+
...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}),
|
|
731
723
|
});
|
|
732
724
|
}
|
|
733
725
|
const withApproval = () => approvalCtx && deps.toolApproval ? deps.toolApproval.runWithContext(approvalCtx, withQuestion) : withQuestion();
|
|
@@ -755,7 +747,6 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
755
747
|
await releaseKeyIfZeroWorkRefusal(deps.runStore, durableTaskId, finalResult);
|
|
756
748
|
}
|
|
757
749
|
else {
|
|
758
|
-
await putRewindAnchor();
|
|
759
750
|
const pending = await deps.checkpointStore?.findPendingTokenBySession(prepared.spec.sessionId, undefined, { axis: "run" });
|
|
760
751
|
const dcCause = ac.cause();
|
|
761
752
|
if (pending) {
|
|
@@ -192,7 +192,7 @@ export async function handleWorkflowAgentSteer(req, res, match, ctx) {
|
|
|
192
192
|
sendError(res, 413, "steer.content_too_large", `content exceeds ${STEER_IN_MAX_REQUEST_CHARS} characters (got ${body.content.length}); note only the first ${STEER_IN_MAX_CHARS} are delivered anyway`);
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
|
-
const trusted = explicitOperatorOk(principal, deps.config);
|
|
195
|
+
const trusted = explicitOperatorOk(principal, deps.config, "operate");
|
|
196
196
|
const wfVerdict = await judgeWorkflowRun(steerEntry, runs.store, wfRunId);
|
|
197
197
|
if (wfVerdict === "session_mismatch") {
|
|
198
198
|
refuse(steerEntry, res, "session_mismatch");
|
package/dist/http/server.d.ts
CHANGED
|
@@ -169,11 +169,12 @@ export interface ServiceCoreDeps {
|
|
|
169
169
|
* `GET /v1/sessions/:id/mcp` 与 `…/a2a` 两张会话面板 —— 的场景名都由它给,**点名与缺席都问**:点名 ⇒ 判在册 / allowlist,
|
|
170
170
|
* 拒体(400 `scenario_unknown` 附在册词表 / 400 `scenario_not_allowed` 附 allowlist)与任务提交腿逐字同;缺席 ⇒ 指派链(中心 ruling 胜者,
|
|
171
171
|
* 否则部署缺省),与这位主体 run 时同一读法(指派悬空 ⇒ 503 `state.scenario_assignment_unknown`,同提交腿)。中心配了却答不出这位主体的裁定 ⇒
|
|
172
|
-
* 点名与缺席同答 503 `state.scenario_assignment_unavailable`(同提交腿)
|
|
172
|
+
* 点名与缺席同答 503 `state.scenario_assignment_unavailable`(同提交腿)。门的答是 `{ scenario, source }`(`ScenarioChoice`):上面三张面只取名字,
|
|
173
|
+
* `GET /v1/capabilities/scenario` 把整只答原样交给客户端(这次请求会生效哪只场景、它从哪一层来)—— 名字与来层只有门这一处铸。
|
|
173
174
|
* 读面上这只门走**只读腿**(`createScenarioGate(…, "peek")`):问中心裁定时 center 抖动的那一拍,不替这个主体的真提交留下 fail-closed 降级项
|
|
174
175
|
* (提交腿自己是 `"enforce"`)。
|
|
175
176
|
* 🔴 **必填**:读口的场景名只有这一个来源,没有「缺接缝 ⇒ 读部署缺省 / 点名 404」这类第二种读法 —— 漏接是**编译错**(嵌入方自己装配时,工厂与它的型从包根导出:
|
|
176
|
-
* `createScenarioGate` / `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources`)。
|
|
177
|
+
* `createScenarioGate` / `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources` / `ScenarioChoice` / `ScenarioSource`)。
|
|
177
178
|
*/
|
|
178
179
|
scenarioGate: import("../capabilities/scenarios.js").ScenarioGate;
|
|
179
180
|
/**
|
|
@@ -321,7 +322,7 @@ export interface ServiceCoordinatorDeps {
|
|
|
321
322
|
question?: QuestionCoordinator;
|
|
322
323
|
/** [ref]/[ref]②: live tool-approval coordinator (core onAsk → CC three-choice card). Set when
|
|
323
324
|
* TOOL_APPROVAL_ENABLED. Wired onto the runner's onAsk seam AND consumed here by
|
|
324
|
-
* `POST /v1/
|
|
325
|
+
* the ask decision door (`POST /v1/tasks/:taskId/asks/:askId/decision`) + the per-run ALS wrap on the SYNC streaming leg (live-only by design —
|
|
325
326
|
* every other leg keeps core's fail-closed headless auto-deny, [ref]⑤/[ref]④). */
|
|
326
327
|
toolApproval?: ToolApprovalCoordinator;
|
|
327
328
|
/** [ref] 裁 (c) 审计半场:local 车道的 File ask **审计**读面(`GET /v1/approvals` 的 additive 键
|
|
@@ -944,6 +945,11 @@ export declare function createHttpServer(rawDeps: ServiceDeps): http.Server & {
|
|
|
944
945
|
sessionNotifyDoor: SessionNotifyDoor;
|
|
945
946
|
/** run 收场送达的转交(装配层把它接进 workflow 编排面完成送达的晚绑座;律见 `resume-legs.ts` 的 `handOverRunEndedDecisions`)。 */
|
|
946
947
|
handOverRunEndedDecisions: (runId: string) => Promise<void>;
|
|
948
|
+
/**
|
|
949
|
+
* 本机控制 socket 那只 `http.Server`(建而不听):与本 server 同一只请求监听器,进门打通道标 `control-socket`(`request-channel.ts`),不挂设备 WS 的 upgrade 监听器。
|
|
950
|
+
* 开口(判 + 听)只经 `control-socket.ts` 的 `openControlSocket`;起服第 10 段在 TCP listen 之后开、收尾链与本 server 一起关。嵌入方不开 ⇒ 面缺席。
|
|
951
|
+
*/
|
|
952
|
+
controlSocketServer: http.Server;
|
|
947
953
|
};
|
|
948
954
|
/** SSE: replay durable events after Last-Event-ID, then tail until the run is terminal (or stale). */
|
|
949
955
|
/**
|
package/dist/http/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { outwardPlane } from "../terminal.js";
|
|
3
|
+
import { markControlSocketRequest } from "./request-channel.js";
|
|
3
4
|
import { validatePendingSteer } from "@sema-agent/core";
|
|
4
5
|
import {} from "../web-admin-client/facade.js";
|
|
5
6
|
import { describeValueShape } from "@sema-agent/settings-schema";
|
|
@@ -28,7 +29,7 @@ import { parkDecisionStoreRefusal } from "../orchestration/workflow-park-decisio
|
|
|
28
29
|
import { CHECKPOINT_CTX_SEALING_REQUIRED } from "./checkpoint-ctx.js";
|
|
29
30
|
import { SERVING_LEASE_MEMBERSHIP_REQUIRED } from "../fleet/engine-signal.js";
|
|
30
31
|
import { PARKED_REVIVE_EXECUTION_SEAT_REQUIRED } from "../parked-decide.js";
|
|
31
|
-
import { a2aAudienceVerifiedSourceRefusal, operatorDeclarationRefusal } from "../config-invariants.js";
|
|
32
|
+
import { a2aAudienceVerifiedSourceRefusal, operatorDeclarationRefusal, operatorRosterWellFormedRefusal } from "../config-invariants.js";
|
|
32
33
|
import { retiredServiceDepsRefusal } from "./retired-service-deps.js";
|
|
33
34
|
import { createAdmission, MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills } from "./admission.js";
|
|
34
35
|
import { createResumeLegs } from "./resume-legs.js";
|
|
@@ -94,6 +95,12 @@ export function createHttpServer(rawDeps) {
|
|
|
94
95
|
throw new Error(operatorDeclarationFailure);
|
|
95
96
|
}
|
|
96
97
|
}
|
|
98
|
+
{
|
|
99
|
+
const rosterFailure = operatorRosterWellFormedRefusal(deps.config);
|
|
100
|
+
if (rosterFailure !== undefined) {
|
|
101
|
+
throw new Error(rosterFailure);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
97
104
|
{
|
|
98
105
|
const bridgeDeclared = configuredIdentitySources(deps.config).includes("sso");
|
|
99
106
|
const verifierWired = deps.webAdminJwtVerifier !== undefined;
|
|
@@ -229,7 +236,7 @@ export function createHttpServer(rawDeps) {
|
|
|
229
236
|
const { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, decisionRowAdmits, resumePreempted, startFreshHostTurn, resumeWake, sessionNotifyDoor, handOverRunEndedDecisions } = createResumeLegs({ deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, admitRun, safeDecode, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor });
|
|
230
237
|
const { denyExpiredApprovals, redeemParkedAsk } = createApprovalSweeps({ deps, resumeCheckpoint, decisionRowAdmits });
|
|
231
238
|
const { handle } = createDispatch({ deps, noteActivity, routeCtxBase, counters });
|
|
232
|
-
const
|
|
239
|
+
const onRequest = (req, res) => {
|
|
233
240
|
attachResponseLogger(res, deps.logger);
|
|
234
241
|
void handle(req, res).catch((err) => {
|
|
235
242
|
if (err instanceof HttpError) {
|
|
@@ -258,6 +265,11 @@ export function createHttpServer(rawDeps) {
|
|
|
258
265
|
}
|
|
259
266
|
sendOpenFaceFault(res, err);
|
|
260
267
|
});
|
|
268
|
+
};
|
|
269
|
+
const server = http.createServer(onRequest);
|
|
270
|
+
const controlSocketServer = http.createServer((req, res) => {
|
|
271
|
+
markControlSocketRequest(req);
|
|
272
|
+
onRequest(req, res);
|
|
261
273
|
});
|
|
262
274
|
const deviceLane = facts.deviceLane(deps);
|
|
263
275
|
if (deviceLane.available)
|
|
@@ -289,6 +301,6 @@ export function createHttpServer(rawDeps) {
|
|
|
289
301
|
deps.planCacheProbe?.record(principal, objective, result.terminal.kind === "completed");
|
|
290
302
|
}
|
|
291
303
|
const isRunLiveOnThisReplica = (taskId) => liveRunIdsOf(inflightRuns, steerableRuns).has(taskId);
|
|
292
|
-
return Object.assign(server, { denyExpiredApprovals, isRunLiveOnThisReplica, redeemParkedAsk, sessionNotifyDoor, handOverRunEndedDecisions });
|
|
304
|
+
return Object.assign(server, { denyExpiredApprovals, isRunLiveOnThisReplica, redeemParkedAsk, sessionNotifyDoor, handOverRunEndedDecisions, controlSocketServer });
|
|
293
305
|
}
|
|
294
306
|
//# sourceMappingURL=server.js.map
|
|
@@ -40,7 +40,7 @@ export declare function persistSubmitCtx(store: SubmitCtxStore, deps: {
|
|
|
40
40
|
* 提交腿写续跑输入的入口(三个调用点同调这一只):体与标记按受理座提交时交出的那一份定(见文件头「体里的目标按哪一份写」),只写这一笔。
|
|
41
41
|
* 写不进的处置与答复 = {@link persistSubmitCtx}(本函数只定体、不另判)。
|
|
42
42
|
*/
|
|
43
|
-
export declare function persistSubmissionCtx(store: SubmitCtxStore, deps: Parameters<typeof persistSubmitCtx>[1], claim: Parameters<typeof persistSubmitCtx>[2], prepared: Pick<PreparedTaskSubmission, "body" | "auth" | "verify" | "acceptance" | "bodyFromScreenedCtx"> & Partial<Pick<PreparedTaskSubmission, "legFacts">> & {
|
|
43
|
+
export declare function persistSubmissionCtx(store: SubmitCtxStore, deps: Parameters<typeof persistSubmitCtx>[1], claim: Parameters<typeof persistSubmitCtx>[2], prepared: Pick<PreparedTaskSubmission, "body" | "auth" | "verify" | "acceptance" | "bodyFromScreenedCtx" | "bodyPermissionModeWrite"> & Partial<Pick<PreparedTaskSubmission, "legFacts">> & {
|
|
44
44
|
readonly spec: Pick<PreparedTaskSubmission["spec"], "sessionId" | "model">;
|
|
45
45
|
}): Promise<SubmitCtxRefusal | typeof SUBMIT_CTX_CLAIM_LOST | undefined>;
|
|
46
46
|
//# sourceMappingURL=submit-ctx.d.ts.map
|
package/dist/http/submit-ctx.js
CHANGED
|
@@ -7,6 +7,7 @@ import { limitFactsUnmeasured } from "../limit-refusal.js";
|
|
|
7
7
|
import { serviceVersion } from "../service-version.js";
|
|
8
8
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
9
9
|
import { DEFAULT_MODEL_REF } from "../model-select.js";
|
|
10
|
+
import { submissionPermissionModeWrite } from "../permission-mode-admission.js";
|
|
10
11
|
const WITHHELD_OBJECTIVE = "";
|
|
11
12
|
export function objectiveWithheldIn(ctx) {
|
|
12
13
|
return ctx.promptScreened === true;
|
|
@@ -79,6 +80,7 @@ export async function persistSubmissionCtx(store, deps, claim, prepared) {
|
|
|
79
80
|
...(prepared.legFacts?.projectContext !== undefined ? { projectContext: prepared.legFacts.projectContext } : {}),
|
|
80
81
|
...(prepared.legFacts?.memorySeed !== undefined ? { memorySeed: prepared.legFacts.memorySeed } : {}),
|
|
81
82
|
writerVersion: serviceVersion(),
|
|
83
|
+
permissionModeWrite: prepared.bodyPermissionModeWrite !== undefined ? prepared.bodyPermissionModeWrite.stored : submissionPermissionModeWrite(prepared.body),
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
86
|
//# sourceMappingURL=submit-ctx.js.map
|