@sema-agent/server 7.144.0 → 7.146.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 +621 -0
- package/README.md +3 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +233 -54
- package/deploy/facts/CHANGES.json +25 -0
- package/deploy/facts/README.md +57 -0
- package/deploy/facts/lanes.json +54 -0
- package/deploy/facts/lanes.schema.json +57 -0
- package/deploy/facts/sandbox-rbac.json +113 -0
- package/deploy/facts/sandbox-rbac.schema.json +74 -0
- package/deploy/facts/sealed-key.json +9 -0
- package/deploy/facts/sealed-key.schema.json +52 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
- package/deploy/sema-up/chart/templates/server.yaml +4 -0
- package/deploy/sema-up/chart/values.yaml +7 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
- package/dist/adoption/import-limits.d.ts +6 -0
- package/dist/adoption/import-limits.js +7 -0
- package/dist/adoption/import.d.ts +62 -0
- package/dist/adoption/import.js +107 -0
- package/dist/adoption/wire.d.ts +52 -0
- package/dist/adoption/wire.js +9 -0
- package/dist/approval-ask-machine.d.ts +2 -2
- package/dist/approval-card.d.ts +3 -12
- package/dist/approval-card.js +0 -4
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-gate-currency.js +1 -1
- package/dist/approval-reconciler.d.ts +1 -1
- package/dist/audit.d.ts +2 -21
- package/dist/audit.js +0 -9
- package/dist/boot/deployment-mods.d.ts +11 -1
- package/dist/boot/deployment-mods.js +15 -2
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/engine-lease.d.ts +193 -21
- package/dist/boot/engine-lease.js +146 -20
- package/dist/boot/execution-env.d.ts +4 -0
- package/dist/boot/execution-env.js +12 -0
- package/dist/boot/exit-boundary.d.ts +50 -0
- package/dist/boot/exit-boundary.js +32 -0
- package/dist/boot/exit-last.d.ts +20 -4
- package/dist/boot/exit-last.js +21 -4
- package/dist/boot/governance-seams.js +1 -0
- package/dist/boot/memory-consolidation.d.ts +29 -17
- package/dist/boot/memory-consolidation.js +26 -15
- package/dist/boot/reapers.d.ts +12 -4
- package/dist/boot/reapers.js +39 -15
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +38 -15
- package/dist/boot/runner-deps.js +1 -1
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +6 -4
- package/dist/boot/shutdown.js +27 -9
- package/dist/boot/stage-01-config.js +1 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +3 -0
- package/dist/boot/stage-05-execution-env.js +3 -3
- package/dist/boot/stage-06-runners.d.ts +3 -0
- package/dist/boot/stage-06-runners.js +11 -10
- package/dist/boot/stage-07-capability-layer.d.ts +3 -0
- package/dist/boot/stage-07-capability-layer.js +7 -2
- package/dist/boot/stage-08-reapers.d.ts +3 -0
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +4 -1
- package/dist/boot/stage-10-http-server.d.ts +4 -1
- package/dist/boot/stage-10-http-server.js +4 -3
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +35 -19
- package/dist/boot/workflow-orchestration.d.ts +1 -1
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/capabilities/composite-members.d.ts +3 -1
- package/dist/capabilities/composite-members.js +3 -1
- package/dist/capabilities/scenarios.d.ts +2 -2
- package/dist/capability-facts.d.ts +18 -1
- package/dist/capability-facts.js +25 -7
- package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
- package/dist/config-catalog.js +13 -4
- package/dist/config-d-migration.d.ts +3 -2
- package/dist/config-provider.d.ts +2 -2
- package/dist/config-types.d.ts +43 -4
- package/dist/config.d.ts +6 -1
- package/dist/config.js +67 -11
- package/dist/deleted-session-ids.d.ts +19 -0
- package/dist/deleted-session-ids.js +26 -0
- package/dist/deploy-facts.d.ts +155 -0
- package/dist/deploy-facts.js +94 -0
- package/dist/device-ws-hub.d.ts +48 -3
- package/dist/device-ws-hub.js +48 -2
- package/dist/error-code-family.d.ts +35 -0
- package/dist/error-code-family.js +26 -0
- package/dist/fleet/drain-entry.d.ts +39 -3
- package/dist/fleet/drain-entry.js +6 -0
- package/dist/fleet/fleet-bus.d.ts +15 -4
- package/dist/fleet/fleet-bus.js +17 -3
- package/dist/hooks/branch-transcript.d.ts +1 -1
- package/dist/hooks/branch-transcript.js +1 -0
- package/dist/hooks/classic-stations.d.ts +25 -8
- package/dist/hooks/classic-stations.js +29 -3
- package/dist/hooks/hook-runner.d.ts +33 -16
- package/dist/hooks/hook-runner.js +166 -73
- package/dist/hooks/hook-source.d.ts +14 -2
- package/dist/hooks/hook-source.js +8 -0
- package/dist/http/active-run-conflict.d.ts +1 -1
- package/dist/http/active-run-conflict.js +1 -1
- package/dist/http/admission.js +13 -2
- package/dist/http/checkpoint-ctx.d.ts +60 -71
- package/dist/http/checkpoint-ctx.js +35 -40
- package/dist/http/dispatch.js +18 -2
- package/dist/http/durable-run-leg.d.ts +5 -0
- package/dist/http/durable-run-leg.js +8 -6
- package/dist/http/execution-context.d.ts +4 -0
- package/dist/http/execution-context.js +9 -0
- package/dist/http/health-self-report.d.ts +6 -2
- package/dist/http/health-self-report.js +3 -2
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.js +2 -15
- package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
- package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
- package/dist/http/principal-gate.d.ts +35 -12
- package/dist/http/principal-gate.js +16 -12
- package/dist/http/request-guards.d.ts +1 -1
- package/dist/http/request-guards.js +5 -5
- package/dist/http/resume-legs.d.ts +22 -0
- package/dist/http/resume-legs.js +31 -20
- package/dist/http/route-ctx.d.ts +26 -10
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/adoption.d.ts +11 -0
- package/dist/http/routes/adoption.js +80 -6
- package/dist/http/routes/agents-roster.js +1 -0
- package/dist/http/routes/approvals-assistant.d.ts +20 -1
- package/dist/http/routes/approvals-assistant.js +29 -7
- package/dist/http/routes/background.js +1 -0
- package/dist/http/routes/capabilities.js +4 -1
- package/dist/http/routes/diagnostics.d.ts +9 -14
- package/dist/http/routes/diagnostics.js +10 -20
- package/dist/http/routes/engine.d.ts +31 -0
- package/dist/http/routes/engine.js +81 -0
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-consolidation.js +3 -5
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +2 -4
- package/dist/http/routes/memory-policy.js +5 -1
- package/dist/http/routes/observability.js +3 -6
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +5 -2
- package/dist/http/routes/runs.js +7 -3
- package/dist/http/routes/session-sync.js +24 -25
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.js +4 -0
- package/dist/http/routes/tasks.js +35 -14
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/run-concurrency-cap.d.ts +86 -0
- package/dist/http/run-concurrency-cap.js +54 -0
- package/dist/http/run-meta.d.ts +14 -7
- package/dist/http/run-meta.js +5 -2
- package/dist/http/server.d.ts +27 -16
- package/dist/http/server.js +21 -3
- package/dist/http/stored-body-replay.d.ts +9 -6
- package/dist/http/stored-body-replay.js +19 -5
- package/dist/http/submit-ctx.d.ts +12 -0
- package/dist/http/submit-ctx.js +23 -0
- package/dist/http/wire-types.d.ts +8 -3
- package/dist/json-body-limit.d.ts +8 -0
- package/dist/json-body-limit.js +2 -0
- package/dist/leader/diffout.d.ts +1 -1
- package/dist/local-data-root.d.ts +2 -2
- package/dist/memory-operator-faces.d.ts +9 -4
- package/dist/memory-scope-stores.d.ts +9 -7
- package/dist/memory-scope-stores.js +3 -14
- package/dist/memory-scope.d.ts +36 -0
- package/dist/memory-scope.js +32 -1
- package/dist/net-address.d.ts +53 -0
- package/dist/net-address.js +111 -0
- package/dist/observability/fail-open.d.ts +28 -12
- package/dist/observability/fail-open.js +28 -12
- package/dist/orchestration/workflow-notify-journal.js +2 -3
- package/dist/org-delivery-gate.js +1 -1
- package/dist/peer-directory.d.ts +6 -12
- package/dist/peer-directory.js +5 -1
- package/dist/plugins/approval-ask-store-file.d.ts +16 -2
- package/dist/plugins/approval-ask-store-file.js +21 -1
- package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
- package/dist/plugins/approval-ask-store-memory.js +11 -8
- package/dist/plugins/caching-session-store.d.ts +6 -1
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/checkpoint-store-face.d.ts +426 -0
- package/dist/plugins/checkpoint-store-face.js +152 -0
- package/dist/plugins/file-run-store.d.ts +13 -7
- package/dist/plugins/file-run-store.js +12 -13
- package/dist/plugins/fork-routing-session-store.d.ts +6 -2
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
- package/dist/plugins/local-checkpoint-store.d.ts +16 -5
- package/dist/plugins/local-checkpoint-store.js +25 -6
- package/dist/plugins/local-session-store.d.ts +80 -21
- package/dist/plugins/local-session-store.js +79 -22
- package/dist/plugins/memory-engine-pg.d.ts +29 -6
- package/dist/plugins/memory-engine-pg.js +96 -214
- package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
- package/dist/plugins/memory-engine-sql-apply.js +358 -0
- package/dist/plugins/memory-engine-tidb.d.ts +35 -9
- package/dist/plugins/memory-engine-tidb.js +97 -227
- package/dist/plugins/memory-key-guards.d.ts +1 -1
- package/dist/plugins/memory-origin-law.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +2 -0
- package/dist/plugins/memory-run-store.js +6 -0
- package/dist/plugins/one-time-migrations.d.ts +32 -56
- package/dist/plugins/one-time-migrations.js +27 -119
- package/dist/plugins/permission-rule-store-file.d.ts +17 -3
- package/dist/plugins/permission-rule-store-file.js +8 -1
- package/dist/plugins/pg-pool.js +1 -1
- package/dist/plugins/pg-session-storage.d.ts +17 -4
- package/dist/plugins/pg-session-storage.js +8 -4
- package/dist/plugins/run-store-sql.d.ts +4 -0
- package/dist/plugins/run-store-sql.js +5 -2
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/session-fork.d.ts +31 -9
- package/dist/plugins/session-fork.js +3 -2
- package/dist/plugins/session-placement.d.ts +4 -2
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/sql-json-column.d.ts +5 -1
- package/dist/plugins/store-backend.d.ts +11 -8
- package/dist/plugins/store-backend.js +8 -7
- package/dist/plugins/store-contracts.d.ts +1 -11
- package/dist/plugins/store-contracts.js +0 -9
- package/dist/plugins/store-sweep.d.ts +63 -0
- package/dist/plugins/store-sweep.js +48 -0
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/tidb-session-store.d.ts +16 -3
- package/dist/plugins/tidb-session-store.js +8 -4
- package/dist/prompt-screen.d.ts +18 -0
- package/dist/prompt-screen.js +5 -0
- package/dist/rules-consent.d.ts +56 -51
- package/dist/rules-consent.js +86 -14
- package/dist/run-acceptance.d.ts +40 -10
- package/dist/run-acceptance.js +38 -5
- package/dist/run-cancel-context.d.ts +4 -1
- package/dist/run-cancel-context.js +2 -1
- package/dist/runs.js +2 -2
- package/dist/sandbox-egress-policy.js +6 -11
- package/dist/sandbox-rbac-rules.d.ts +88 -0
- package/dist/sandbox-rbac-rules.js +80 -0
- package/dist/sealed-key-layout.d.ts +17 -0
- package/dist/sealed-key-layout.js +4 -0
- package/dist/sealed-key.js +5 -5
- package/dist/security.d.ts +48 -9
- package/dist/security.js +9 -2
- package/dist/session-agent.d.ts +39 -0
- package/dist/session-agent.js +16 -0
- package/dist/session-error-wire.d.ts +4 -4
- package/dist/session-notify-door.d.ts +5 -3
- package/dist/session-resident-background.d.ts +57 -0
- package/dist/session-resident-background.js +20 -0
- package/dist/session-sync-kernel.d.ts +53 -14
- package/dist/session-sync-kernel.js +70 -1
- package/dist/session-sync.d.ts +5 -3
- package/dist/session-workspace.d.ts +1 -0
- package/dist/session-workspace.js +1 -0
- package/dist/sighup-idle.d.ts +9 -7
- package/dist/sighup-idle.js +4 -5
- package/dist/spec-fields.d.ts +16 -0
- package/dist/spec-fields.js +7 -0
- package/dist/stored-request-env-credentials-m38.d.ts +3 -0
- package/dist/stored-request-env-credentials-m38.js +8 -0
- package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +33 -429
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +70 -209
- package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
- package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
- package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
- package/dist/task-settings.d.ts +59 -4
- package/dist/task-settings.js +50 -4
- package/dist/tool-approval.d.ts +2 -2
- package/dist/trace/core-keyset-guard.d.ts +8 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +6 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +3 -2
- package/dist/trace/model-segment.js +16 -8
- package/dist/trace/project.js +9 -0
- package/dist/trace/redact.d.ts +79 -4
- package/dist/trace/redact.js +523 -123
- package/dist/trace/self-minted-wire.d.ts +391 -1
- package/dist/trace/self-minted-wire.js +145 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/usage-analytics.d.ts +4 -0
- package/package.json +12 -4
- package/scripts/drain-check-local.mjs +8 -29
- package/scripts/drain-check.mysql.sql +3 -24
- package/scripts/drain-check.pg.sql +3 -24
- package/skills/update-config.md +1 -1
- package/wire-changes/server@7.145.0.json +146 -0
- package/wire-changes/server@7.146.0.json +3221 -0
- package/dist/approval-batch-census.d.ts +0 -27
- package/dist/approval-batch-census.js +0 -27
package/dist/http/route-table.js
CHANGED
|
@@ -4,6 +4,7 @@ import { handleDiagnostics, DIAGNOSTICS_ROUTES } from "./routes/diagnostics.js";
|
|
|
4
4
|
import { handleAdoption, ADOPTION_ROUTES } from "./routes/adoption.js";
|
|
5
5
|
import { handleRetention, RETENTION_ROUTES } from "./routes/retention-ops.js";
|
|
6
6
|
import { handleAdminDrain, ADMIN_DRAIN_ROUTES } from "./routes/admin-drain.js";
|
|
7
|
+
import { handleEngine, ENGINE_ROUTES } from "./routes/engine.js";
|
|
7
8
|
import { handleDevices, DEVICES_ROUTES } from "./routes/devices.js";
|
|
8
9
|
import { handleAdminConfigRefresh, ADMIN_CONFIG_REFRESH_ROUTES } from "./routes/admin-config-refresh.js";
|
|
9
10
|
import { handleConfigCatalog, CONFIG_CATALOG_ROUTES } from "./routes/config-catalog.js";
|
|
@@ -65,6 +66,7 @@ export const ROUTES = [
|
|
|
65
66
|
row({ handler: handleAdoption, decls: ADOPTION_ROUTES, position: "post-credential", label: "adoption" }),
|
|
66
67
|
row({ handler: handleRetention, decls: RETENTION_ROUTES, position: "post-credential", label: "retention" }),
|
|
67
68
|
row({ handler: handleAdminDrain, decls: ADMIN_DRAIN_ROUTES, position: "post-credential", label: "admin-drain" }),
|
|
69
|
+
row({ handler: handleEngine, decls: ENGINE_ROUTES, position: "post-credential", label: "engine" }),
|
|
68
70
|
row({ handler: handleDevices, decls: DEVICES_ROUTES, position: "post-credential", label: "devices" }),
|
|
69
71
|
row({ handler: handleAdminConfigRefresh, decls: ADMIN_CONFIG_REFRESH_ROUTES, position: "post-credential", label: "admin-config-refresh" }),
|
|
70
72
|
row({ handler: handleConfigCatalog, decls: CONFIG_CATALOG_ROUTES, position: "post-credential", label: "config-catalog" }),
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
* 一次落库,此后原样回放)。并发同参 POST 由 `adoption_log.from_principal` 的 UNIQUE 仲裁:恰一行落库,
|
|
19
19
|
* 两条连接拿到同一个 adoptionId、同一份回执。
|
|
20
20
|
*
|
|
21
|
+
* ── 第二条车道:源主体的批量导入 `POST /v1/adoption/:id/sessions` ─────────────────────────────────────────────
|
|
22
|
+
* 不是 operator 面:调用者是那条收编行的**源主体本人**(逐字;operator 不能代),把本地会话导进目的主体名下。门序 = 身份(401)→ 设施(501)→
|
|
23
|
+
* 行(404 `not_found.adoption`)→ 源主体(403 `auth.adoption_source_only`,不降级成 404:无行与不匹配同形是一种查不出根因的二义)→ 收编态
|
|
24
|
+
* (409 `adoption.not_adopted`)→ 体形(400)→ 条数帽(400)→ 逐元素(200 + 结局数组)。判断全在 `adoption/import.ts`,本模块只下发。
|
|
25
|
+
*
|
|
21
26
|
* 分层:本模块不值 import `server.ts`(那条边闭合运行时装载环),只 `import type`。
|
|
22
27
|
*/
|
|
23
28
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
@@ -33,6 +38,12 @@ export declare const ADOPTION_ROUTES: readonly [{
|
|
|
33
38
|
readonly pattern: RegExp;
|
|
34
39
|
readonly label: "/v1/adoption/:id";
|
|
35
40
|
readonly methods: readonly ["GET"];
|
|
41
|
+
}, {
|
|
42
|
+
readonly id: "adoption-sessions-import";
|
|
43
|
+
readonly pattern: RegExp;
|
|
44
|
+
readonly label: "/v1/adoption/:id/sessions";
|
|
45
|
+
readonly credentialGated: readonly ["POST"];
|
|
46
|
+
readonly methods: readonly ["POST"];
|
|
36
47
|
}];
|
|
37
48
|
/** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
|
|
38
49
|
export type AdoptionRouteId = RouteIdsOf<typeof ADOPTION_ROUTES>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createAdoptionRunner } from "../../adoption/runner.js";
|
|
2
2
|
import { AdoptionRequestSchema } from "../../adoption/wire.js";
|
|
3
3
|
import { checkAdoptionPrincipalShape, checkAdoptionWidths } from "../../adoption/plan.js";
|
|
4
|
+
import { judgeAdoptionImportBinding, readAdoptionImportBody, runAdoptionImport } from "../../adoption/import.js";
|
|
5
|
+
import { limitRefusal } from "../../limit-refusal.js";
|
|
4
6
|
import { sendJson, sendError } from "../send.js";
|
|
5
7
|
import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
8
|
import { assertNever } from "../route-ctx.js";
|
|
@@ -39,7 +41,21 @@ function sendOutcome(res, out) {
|
|
|
39
41
|
function assertNoRejectCodeLeft(unhandled) {
|
|
40
42
|
throw new Error(`adoption: unhandled reject code ${JSON.stringify(unhandled)} — the wire contract's closed set and this switch have drifted apart`);
|
|
41
43
|
}
|
|
44
|
+
function sendAdoptionStoreRequired(res) {
|
|
45
|
+
sendError(res, 501, "capability.adoption_store_required", "adoption requires a SQL store backend (DB_BACKEND=mysql|pg)");
|
|
46
|
+
}
|
|
42
47
|
export async function handleAdoption(req, res, match, ctx) {
|
|
48
|
+
switch (match.decl.id) {
|
|
49
|
+
case "adoption-submit":
|
|
50
|
+
case "adoption-detail":
|
|
51
|
+
return operatorLane(req, res, match.decl.id, match.params, ctx);
|
|
52
|
+
case "adoption-sessions-import":
|
|
53
|
+
return sourceLane(req, res, match.params, ctx);
|
|
54
|
+
default:
|
|
55
|
+
return assertNever(match.decl.id);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function operatorLane(req, res, id, params, ctx) {
|
|
43
59
|
const { deps } = ctx;
|
|
44
60
|
const { readJson } = ctx.helpers;
|
|
45
61
|
const principal = gatedPrincipal(req, deps.config);
|
|
@@ -59,7 +75,7 @@ export async function handleAdoption(req, res, match, ctx) {
|
|
|
59
75
|
if (!fact.available) {
|
|
60
76
|
switch (fact.why) {
|
|
61
77
|
case "adoption_store_absent":
|
|
62
|
-
|
|
78
|
+
sendAdoptionStoreRequired(res);
|
|
63
79
|
return;
|
|
64
80
|
case "operator_principals_empty":
|
|
65
81
|
sendOperatorPrincipalsRequired(res);
|
|
@@ -70,14 +86,14 @@ export async function handleAdoption(req, res, match, ctx) {
|
|
|
70
86
|
}
|
|
71
87
|
const store = fact.backend.adoptionLog();
|
|
72
88
|
const runner = createAdoptionRunner({ store, dialect: deps.backend?.kind === "pg" ? "pg" : "mysql", now: Date.now, logger: deps.logger });
|
|
73
|
-
switch (
|
|
89
|
+
switch (id) {
|
|
74
90
|
case "adoption-detail": {
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
91
|
+
const adoptionId = ctx.helpers.safeDecode(params.id);
|
|
92
|
+
if (adoptionId === null) {
|
|
77
93
|
sendError(res, 400, "request.id_invalid", "invalid adoption id");
|
|
78
94
|
return;
|
|
79
95
|
}
|
|
80
|
-
const out = await runner.get(
|
|
96
|
+
const out = await runner.get(adoptionId);
|
|
81
97
|
if (!out) {
|
|
82
98
|
sendError(res, 404, "not_found.adoption", "adoption not found");
|
|
83
99
|
return;
|
|
@@ -112,11 +128,69 @@ export async function handleAdoption(req, res, match, ctx) {
|
|
|
112
128
|
return;
|
|
113
129
|
}
|
|
114
130
|
default:
|
|
115
|
-
return assertNever(
|
|
131
|
+
return assertNever(id);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function sourceLane(req, res, params, ctx) {
|
|
135
|
+
const { deps } = ctx;
|
|
136
|
+
if (ctx.helpers.rateLimited(req, res))
|
|
137
|
+
return;
|
|
138
|
+
const principal = gatedPrincipal(req, deps.config);
|
|
139
|
+
if (principal === undefined) {
|
|
140
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const face = facts.adoptionSessionImport(deps);
|
|
144
|
+
if (!face.available) {
|
|
145
|
+
switch (face.why) {
|
|
146
|
+
case "adoption_store_absent":
|
|
147
|
+
sendAdoptionStoreRequired(res);
|
|
148
|
+
return;
|
|
149
|
+
case "session_import_staging_absent":
|
|
150
|
+
sendError(res, 501, "capability.session_store_required", "importing sessions requires a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
151
|
+
return;
|
|
152
|
+
default:
|
|
153
|
+
return assertNever(face.why);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const adoptionId = ctx.helpers.safeDecode(params.id);
|
|
157
|
+
if (adoptionId === null) {
|
|
158
|
+
sendError(res, 400, "request.id_invalid", "invalid adoption id");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const binding = judgeAdoptionImportBinding(await face.backend.adoptionLog().getById(adoptionId), principal);
|
|
162
|
+
switch (binding.kind) {
|
|
163
|
+
case "absent":
|
|
164
|
+
sendError(res, 404, "not_found.adoption", "adoption not found");
|
|
165
|
+
return;
|
|
166
|
+
case "not_source":
|
|
167
|
+
sendError(res, 403, "auth.adoption_source_only", "only this adoption's source principal may import sessions into it (verbatim; an operator cannot import on its behalf)");
|
|
168
|
+
return;
|
|
169
|
+
case "not_adopted":
|
|
170
|
+
sendError(res, 409, "adoption.not_adopted", "this adoption has not completed (it is still in flight, or it was rejected); sessions can be imported once the operator's adoption reaches status adopted");
|
|
171
|
+
return;
|
|
172
|
+
case "bound":
|
|
173
|
+
break;
|
|
174
|
+
default:
|
|
175
|
+
return assertNever(binding);
|
|
176
|
+
}
|
|
177
|
+
const sessions = readAdoptionImportBody(await ctx.helpers.readJson(req));
|
|
178
|
+
if (sessions === undefined) {
|
|
179
|
+
sendError(res, 400, "request.body_shape", "invalid import body — expected exactly {sessions: [...]} (the destination is the adoption's; the body cannot name one)");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const cap = deps.config.adoptionImportMaxSessions;
|
|
183
|
+
if (sessions.length > cap) {
|
|
184
|
+
const refusal = limitRefusal("sessions", "entries", sessions.length, cap, { tail: "split the batch (the current cap is capabilities.adoptionImport.maxSessions), or have the operator raise ADOPTION_IMPORT_MAX_SESSIONS" });
|
|
185
|
+
sendError(res, 400, "request.field_invalid", refusal.message, refusal.facts);
|
|
186
|
+
return;
|
|
116
187
|
}
|
|
188
|
+
const results = await runAdoptionImport({ adoptionId: binding.adoptionId, toPrincipal: binding.toPrincipal, maxSessionBytes: deps.config.adoptionImportMaxSessionBytes, sessionStorage: face.sessionStorage }, sessions);
|
|
189
|
+
sendJson(res, 200, { results });
|
|
117
190
|
}
|
|
118
191
|
export const ADOPTION_ROUTES = [
|
|
119
192
|
{ id: "adoption-submit", path: "/v1/adoption", credentialGated: ["POST"], methods: ["POST"] },
|
|
120
193
|
{ id: "adoption-detail", pattern: /^\/v1\/adoption\/(?<id>[^/]+)$/, label: "/v1/adoption/:id", methods: ["GET"] },
|
|
194
|
+
{ id: "adoption-sessions-import", pattern: /^\/v1\/adoption\/(?<id>[^/]+)\/sessions$/, label: "/v1/adoption/:id/sessions", credentialGated: ["POST"], methods: ["POST"] },
|
|
121
195
|
];
|
|
122
196
|
//# sourceMappingURL=adoption.js.map
|
|
@@ -7,6 +7,7 @@ import { type GateIntentView } from "../../approval.js";
|
|
|
7
7
|
import { type RouteCtx, type RouteDecl, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
8
8
|
import { type KeyClosureIssue } from "../../request-key-closure.js";
|
|
9
9
|
import { type PeerInboundPendingRow } from "../../peer-inbound-face.js";
|
|
10
|
+
import { type PlacementReadingSessionStore, type SessionAgent } from "../../session-agent.js";
|
|
10
11
|
/** 族的成员 = 路由表行 id(改名 / 删行 ⇒ 下表的键当场编译红)。 */
|
|
11
12
|
export type DecisionDoorId = Extract<ApprovalsAssistantRouteId, "approval-decide" | "assistant-task-plan-review">;
|
|
12
13
|
/** 一道门的静态描述 —— 只写**这道门自己**的东西;路径、路径段、别门的键一概不在这里写(从来源读)。 */
|
|
@@ -104,6 +105,19 @@ export type QuestionAnswerCheck = {
|
|
|
104
105
|
*/
|
|
105
106
|
export declare function checkQuestionAnswer(v: unknown): QuestionAnswerCheck;
|
|
106
107
|
export declare function handleApprovalsAssistant(req: IncomingMessage, res: ServerResponse, match: RouteMatch<ApprovalsAssistantRouteId>, ctx: RouteCtx): Promise<void>;
|
|
108
|
+
/** `listPending()` 的行形(两条 durable 读面共用;从 store 的返回类型取,不手抄)。 */
|
|
109
|
+
type PendingRow = Awaited<ReturnType<CheckpointStoreFull["listPending"]>>[number];
|
|
110
|
+
/**
|
|
111
|
+
* 两条 durable 读面共用的行形 —— 行 + [ref]② 追加的**出身格**(派生位,不是列)+ [ref] 行族词(`checkpoint`)+ [ref] 行的会话在委派树里的位置 `agent`
|
|
112
|
+
* (会话视图 `GET /v1/sessions/:id` 体顶层的同名键,同一只读口 `readSessionAgent`;在场 ⇔ 这一次列表读到行的会话是一只 placed 委派子代,见 {@link projectDurableQueue})。
|
|
113
|
+
* 导出只为自铸登记表按键绑型(`trace/self-minted-wire.ts` 的 `approvalQueueRowAgent`)。
|
|
114
|
+
*/
|
|
115
|
+
export type PendingRowForWire = PendingRow & {
|
|
116
|
+
kind: "checkpoint";
|
|
117
|
+
governanceForced?: true;
|
|
118
|
+
rememberRefused?: true;
|
|
119
|
+
agent?: SessionAgent;
|
|
120
|
+
};
|
|
107
121
|
/**
|
|
108
122
|
* 🔴 [ref]-2 / [ref]②:两条 durable 读面(`GET /v1/approvals` + `/v1/approvals/stream`)的**唯一**
|
|
109
123
|
* 投影 —— 脱敏([ref] 件4)+ 出身归因([ref] 的另一半)一次做完。
|
|
@@ -145,7 +159,12 @@ live: Pick<ToolApprovalCoordinator, "listLivePending"> | undefined,
|
|
|
145
159
|
* (`pending` / `resolved`):两族同一扇决断口,帧上的 `kind` 判别。`undefined` = 本腿没有这一半场(流字节只差每帧的 `kind`)。
|
|
146
160
|
* 位置在 `scope` 之前、与 `live` 并列:每个生产调用点都必须表态。
|
|
147
161
|
*/
|
|
148
|
-
peerInbound: (() => readonly PeerInboundPendingRow[]) | undefined,
|
|
162
|
+
peerInbound: (() => readonly PeerInboundPendingRow[]) | undefined,
|
|
163
|
+
/**
|
|
164
|
+
* 🔴 [ref]:停驻行 `agent` 的会话店面 —— 与 `GET /v1/approvals` 同一只(`deps.sessionStorage`),经同一只整批投影 {@link projectDurableQueue}。
|
|
165
|
+
* `undefined` / 店不交 `placementOf` = 这台部署不存委派元组 ⇒ 没有一帧带 `agent`(帧字节与 [ref] 之前逐字相同)。位置在 `scope` 之前、与 `live` 并列:每个生产调用点都必须表态。
|
|
166
|
+
*/
|
|
167
|
+
sessions: PlacementReadingSessionStore | undefined, scope: string | undefined,
|
|
149
168
|
/** [ref]②:出身归因的部署侧一半 + 三张审批名单(问的属主)。缺席 ⇒ 归不出治理出身(键缺席,与 best-effort 同方向)、没有一行读作
|
|
150
169
|
* 部署名单的问(记不住,方向是多问)。**位置在 `pollMs` 之前**是刻意的:`pollMs` 只有测试传,而本参数是每个生产调用点都必须给的那一个。 */
|
|
151
170
|
governance?: PendingProjectionDeployment, pollMs?: number): Promise<void>;
|
|
@@ -3,7 +3,8 @@ import { skillCardFacts } from "../../approval-card.js";
|
|
|
3
3
|
import { hostDecisionFacts, readDecider, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF } from "../../host-decision.js";
|
|
4
4
|
import { sameExecutionRecord } from "@sema-agent/core";
|
|
5
5
|
import { findParkedAgentForCheckpoint } from "../../parked-decide.js";
|
|
6
|
-
import { DECIDED_REPLAY_SCAN_CAP
|
|
6
|
+
import { DECIDED_REPLAY_SCAN_CAP } from "../../plugins/checkpoint-store-face.js";
|
|
7
|
+
import { approvalPayloadFingerprint } from "../../plugins/approval-payload-fingerprint.js";
|
|
7
8
|
import { carriesDeploymentListQuestion, standingAnswerDurablyRecordable } from "../../tool-approval.js";
|
|
8
9
|
import { APPROVAL_GATE_KINDS } from "../../approval-content-kind.js";
|
|
9
10
|
import { isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
@@ -36,6 +37,7 @@ import { seatText } from "../../trace/exit-seat.js";
|
|
|
36
37
|
import { approvalQueueKindOfDoorId } from "../../approval-queue-kind.js";
|
|
37
38
|
import { defaultPeerInboundFace, parsePeerInboundId, peerBoxScope } from "../../peer-inbound-face.js";
|
|
38
39
|
import { RESUME_RESTATABLE_KEYS, readResumeRestatement } from "../resume-restatement.js";
|
|
40
|
+
import { readSessionAgent } from "../../session-agent.js";
|
|
39
41
|
const DECIDE_BODY_DECLARED_KEYS = [
|
|
40
42
|
"answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decidedVia", "decision", "feedback", "reason", "remember", "updatedInput",
|
|
41
43
|
...RESUME_RESTATABLE_KEYS,
|
|
@@ -315,7 +317,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
315
317
|
if (match.decl.id === "approvals-stream") {
|
|
316
318
|
const ownerParam = operator ? (new URLSearchParams(match.search).get("owner") ?? undefined) : undefined;
|
|
317
319
|
const scope = operator ? ownerParam : ownScope;
|
|
318
|
-
await streamApprovals(req, res, cs, deps.toolApproval, peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam), scope, deps.config);
|
|
320
|
+
await streamApprovals(req, res, cs, deps.toolApproval, peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam), deps.sessionStorage, scope, deps.config);
|
|
319
321
|
return;
|
|
320
322
|
}
|
|
321
323
|
if (match.decl.id === "approvals-list") {
|
|
@@ -331,7 +333,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
331
333
|
sendApprovalsReadFailure(req, res, deps.logger, err);
|
|
332
334
|
return;
|
|
333
335
|
}
|
|
334
|
-
const pending = [...
|
|
336
|
+
const pending = [...(await projectDurableQueue(durableRows, deps.config, deps.sessionStorage)), ...peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam)()];
|
|
335
337
|
sendJson(res, 200, {
|
|
336
338
|
pending,
|
|
337
339
|
serverNowMs: Date.now(),
|
|
@@ -1036,7 +1038,7 @@ function peerInboundRowsFor(face, principal, operatorOwnerParam) {
|
|
|
1036
1038
|
const scope = peerBoxScope(principal);
|
|
1037
1039
|
return () => face.list(scope);
|
|
1038
1040
|
}
|
|
1039
|
-
function projectPendingForWire(row, deployment) {
|
|
1041
|
+
function projectPendingForWire(row, deployment, agent) {
|
|
1040
1042
|
const governance = deployment;
|
|
1041
1043
|
const redacted = redactPendingDisclosures(row);
|
|
1042
1044
|
const governanceForced = governanceOriginOf({ riskDescriptor: row.riskDescriptor ?? undefined }, governance);
|
|
@@ -1049,7 +1051,27 @@ function projectPendingForWire(row, deployment) {
|
|
|
1049
1051
|
classes: row.classes,
|
|
1050
1052
|
deploymentListQuestion,
|
|
1051
1053
|
});
|
|
1052
|
-
return { kind: "checkpoint", ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}) };
|
|
1054
|
+
return { kind: "checkpoint", ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}), ...(agent !== undefined ? { agent } : {}) };
|
|
1055
|
+
}
|
|
1056
|
+
async function projectDurableQueue(rows, deployment, sessions) {
|
|
1057
|
+
const agents = await sessionAgentsOf(rows.map((r) => r.sessionId), sessions);
|
|
1058
|
+
return rows.map((r) => projectPendingForWire(r, deployment, agents.get(r.sessionId)));
|
|
1059
|
+
}
|
|
1060
|
+
async function sessionAgentsOf(sessionIds, sessions) {
|
|
1061
|
+
const out = new Map();
|
|
1062
|
+
if (sessions?.placementOf === undefined)
|
|
1063
|
+
return out;
|
|
1064
|
+
await Promise.all([...new Set(sessionIds)].map(async (sessionId) => {
|
|
1065
|
+
try {
|
|
1066
|
+
const agent = await readSessionAgent(sessions, sessionId);
|
|
1067
|
+
if (agent !== undefined)
|
|
1068
|
+
out.set(sessionId, agent);
|
|
1069
|
+
}
|
|
1070
|
+
catch (err) {
|
|
1071
|
+
recordFailOpen("server.approvals.queue-row-agent-unreadable", `session ${JSON.stringify(sessionId)}: ${seatText("operator-log-text", err instanceof Error ? err.message : String(err))}`);
|
|
1072
|
+
}
|
|
1073
|
+
}));
|
|
1074
|
+
return out;
|
|
1053
1075
|
}
|
|
1054
1076
|
function createSseDeltaLane(res, spec) {
|
|
1055
1077
|
let prev = new Map();
|
|
@@ -1068,7 +1090,7 @@ function createSseDeltaLane(res, spec) {
|
|
|
1068
1090
|
},
|
|
1069
1091
|
};
|
|
1070
1092
|
}
|
|
1071
|
-
export async function streamApprovals(req, res, cs, live, peerInbound, scope, governance, pollMs = APPROVALS_STREAM_POLL_MS) {
|
|
1093
|
+
export async function streamApprovals(req, res, cs, live, peerInbound, sessions, scope, governance, pollMs = APPROVALS_STREAM_POLL_MS) {
|
|
1072
1094
|
const keyOf = (p) => {
|
|
1073
1095
|
switch (p.kind) {
|
|
1074
1096
|
case "checkpoint":
|
|
@@ -1118,7 +1140,7 @@ export async function streamApprovals(req, res, cs, live, peerInbound, scope, go
|
|
|
1118
1140
|
while (!closed) {
|
|
1119
1141
|
let snapshot;
|
|
1120
1142
|
try {
|
|
1121
|
-
const pending = [...(await cs.listPending(scope)
|
|
1143
|
+
const pending = [...(await projectDurableQueue(await cs.listPending(scope), governance, sessions)), ...(peerInbound?.() ?? [])];
|
|
1122
1144
|
snapshot = { pending, livePending: live ? live.listLivePending(scope) : [] };
|
|
1123
1145
|
}
|
|
1124
1146
|
catch (err) {
|
|
@@ -35,6 +35,7 @@ import * as facts from "../../capability-facts.js";
|
|
|
35
35
|
import { judgeAutoModeArming } from "../../auto-mode-face.js";
|
|
36
36
|
import { coercePermissionModeStrict, PERMISSION_MODE_WORDS, taskHooksHonored } from "../../task-settings.js";
|
|
37
37
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
38
|
+
import { readAdoptionImportCapability } from "../../adoption/import.js";
|
|
38
39
|
import { headReadOf } from "../../session-incarnation.js";
|
|
39
40
|
import { callerTrust } from "../caller-trust.js";
|
|
40
41
|
import { sessionRulesNamespaceFormsHonored } from "../../session-policy-wire.js";
|
|
@@ -121,6 +122,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
121
122
|
sharedMemory: facts.sharedMemoryRead(deps).available,
|
|
122
123
|
principalInboxDelivery: facts.principalInboxDelivery(deps).available,
|
|
123
124
|
adoption: facts.adoption(deps).available,
|
|
125
|
+
adoptionImport: await readAdoptionImportCapability(facts.adoptionSessionImport(deps), gatedPrincipal(req, deps.config), deps.config),
|
|
124
126
|
memoryBundle: facts.memoryBundle(deps).available,
|
|
125
127
|
memoryCompliance: facts.memoryCompliance(deps).available,
|
|
126
128
|
memoryOrigin: facts.memoryOrigin(deps).available,
|
|
@@ -147,7 +149,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
147
149
|
scratchpadDir: true,
|
|
148
150
|
subagentSteer: facts.subagentSteer(deps).available,
|
|
149
151
|
subagentResume: facts.subagentSteer(deps).available,
|
|
150
|
-
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(deps.config), hooks: taskHooksHonored(deps.config), pluginHooks: taskHooksHonored(deps.config), managedHookGroups: taskHooksHonored(deps.config), httpHookAllowlists: taskHooksHonored(deps.config), tightenOnlySeats: true, approvalFrameFamilies: true },
|
|
152
|
+
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(deps.config), hooks: taskHooksHonored(deps.config), pluginHooks: taskHooksHonored(deps.config), managedHookGroups: taskHooksHonored(deps.config), httpHookAllowlists: taskHooksHonored(deps.config), tightenOnlySeats: true, approvalFrameFamilies: true, autoCompactEnabled: true },
|
|
151
153
|
settings: { permissions: { allowGrant: true, allowLayer: true, layers: true, layeredAllow: true, callerTrust: callerTrust(req, deps.config).trust } },
|
|
152
154
|
permissionModeAuto: {
|
|
153
155
|
accepted: true,
|
|
@@ -187,6 +189,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
187
189
|
mcpProbe: true,
|
|
188
190
|
mcpAssertionHeaderSent: MCP_ASSERTION_HEADER_SENT,
|
|
189
191
|
mcpTransportKinds: [...MCP_REQUEST_TRANSPORT_KINDS],
|
|
192
|
+
engine: { handover: facts.engineHandover(deps).available },
|
|
190
193
|
mcpElicitation: facts.elicitation(deps).available,
|
|
191
194
|
askUserQuestion: facts.askUserQuestion(deps).available,
|
|
192
195
|
toolApproval: facts.toolApproval(deps).available,
|
|
@@ -13,6 +13,7 @@ import { type PostureKnobReading, type PostureSource } from "../../posture-sourc
|
|
|
13
13
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
14
14
|
import type { WiringManifest } from "@sema-agent/core";
|
|
15
15
|
import { type StreamApprovalGate, type StreamApprovalGateInput } from "../../tool-approval.js";
|
|
16
|
+
import { verifiedIdentitySource } from "../principal-gate.js";
|
|
16
17
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
17
18
|
/** 流内审批门的读数形:上场即 `"active"`,否则是那**四**个合取项里**第一个**不满足的原因词
|
|
18
19
|
* (与 `resolveStreamApprovalGate` 同一闭集,词表**取型**自它 ⇒ 增删 reason 在此处自动跟随/编译红;
|
|
@@ -82,23 +83,17 @@ export declare function assertStaticWiringConsistent(facts: StaticWiringFacts, l
|
|
|
82
83
|
warn: (msg: string, meta?: Record<string, unknown>) => void;
|
|
83
84
|
} | undefined): StaticWiringAudit;
|
|
84
85
|
/**
|
|
85
|
-
* [ref]③ clay 裁 (b)
|
|
86
|
-
*
|
|
86
|
+
* 诊断面的**远程来源门**(单一属主,端点与测试同吃;[ref]③ clay 裁 (b) 的那只门,7.63.0 起按身份判):远程请求后面没有
|
|
87
|
+
* **验过的身份**(`verifiedIdentitySource`:service 凭据 / 鉴权桥登录令牌 / 直连门签名令牌)⇒ 拒;本机(loopback)照读。
|
|
87
88
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* (壳缺省钉 BIND_HOST=127.0.0.1,local curl 照常),对远程侦察 fail-closed;配了 token 的部署远程读
|
|
92
|
-
* 走认证(全局门),不进本臂。
|
|
89
|
+
* 名单里的名字、单用户声明放进来的「每个调用方」,都只在这次请求带可验身份时才是身份 —— 没有凭据可验的部署上,主体头是谁都能写的
|
|
90
|
+
* 字符串,端口误暴露到公网就是侦察面(装配拓扑 / 治理姿态 / 写保护名表)。配了 service 凭据的部署上,到得了这里的请求都过了全局凭据门、
|
|
91
|
+
* 必然带着验过的身份,本判不咬;不要求静态凭据在场(鉴权桥的登录令牌就是身份)。
|
|
93
92
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* 边界由反代+token 承担)。`remoteAddress` 缺席(socket 已断等边缘)⇒ 判拒(fail-closed,安全轴方向)。
|
|
93
|
+
* 判源 = TCP 对端地址(`req.socket.remoteAddress`),**非任何头**(XFF 类头在无反代形上是自造面;有反代的部署必然配凭据)。
|
|
94
|
+
* `remoteAddress` 缺席(socket 已断等边缘)⇒ 判拒(fail-closed)。
|
|
97
95
|
*/
|
|
98
|
-
export declare function
|
|
99
|
-
authToken?: string;
|
|
100
|
-
authTokens?: Record<string, string>;
|
|
101
|
-
}): boolean;
|
|
96
|
+
export declare function remoteWithoutVerifiedIdentity(req: IncomingMessage, config: Parameters<typeof verifiedIdentitySource>[1]): boolean;
|
|
102
97
|
export declare function handleDiagnostics(req: IncomingMessage, res: ServerResponse, match: RouteMatch<DiagnosticsRouteId>, ctx: RouteCtx): Promise<void>;
|
|
103
98
|
export declare const DIAGNOSTICS_ROUTES: readonly [{
|
|
104
99
|
readonly id: "diagnostics-wiring";
|
|
@@ -95,32 +95,22 @@ export function assertStaticWiringConsistent(facts, logger) {
|
|
|
95
95
|
}
|
|
96
96
|
return audit;
|
|
97
97
|
}
|
|
98
|
-
export function
|
|
99
|
-
|
|
100
|
-
if (anyServiceAuth)
|
|
101
|
-
return false;
|
|
102
|
-
return !isLoopbackAddress(remoteAddress);
|
|
98
|
+
export function remoteWithoutVerifiedIdentity(req, config) {
|
|
99
|
+
return !isLoopbackAddress(req.socket.remoteAddress) && verifiedIdentitySource(req, config) === undefined;
|
|
103
100
|
}
|
|
104
101
|
export async function handleDiagnostics(req, res, match, ctx) {
|
|
105
102
|
const { deps } = ctx;
|
|
106
103
|
switch (match.decl.id) {
|
|
107
104
|
case "diagnostics-wiring": {
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
sendError(res, 403, "auth.operator_only", "wiring diagnostics are operator-only on a multi-tenant worker");
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (!isLoopbackAddress(req.socket.remoteAddress) && verifiedIdentitySource(req, deps.config) === undefined) {
|
|
119
|
-
sendError(res, 403, "auth.loopback_only", "wiring diagnostics on a zero-credential deployment are readable from loopback only — configure SERVICE_AUTH_TOKEN to read remotely");
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
105
|
+
if (!facts.operatorLane(deps).available) {
|
|
106
|
+
sendOperatorPrincipalsRequired(res);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config)) {
|
|
110
|
+
sendError(res, 403, "auth.operator_only", "wiring diagnostics are operator-only");
|
|
111
|
+
return;
|
|
122
112
|
}
|
|
123
|
-
|
|
113
|
+
if (remoteWithoutVerifiedIdentity(req, deps.config)) {
|
|
124
114
|
sendError(res, 403, "auth.loopback_only", "wiring diagnostics on a zero-credential deployment are readable from loopback only — configure SERVICE_AUTH_TOKEN to read remotely");
|
|
125
115
|
return;
|
|
126
116
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `POST /v1/engine/handover` · `POST /v1/engine/attach` —— 附着租约的交接车道([ref] v3,「Move to background and exit」)。
|
|
3
|
+
*
|
|
4
|
+
* 一只门的两扇口:交接口置交接位(壳随后自己退出、不 SIGTERM 引擎;后台 shell 在跑时引擎不退),attach 口清位(下一只壳收回)。车道、窗、
|
|
5
|
+
* 工作谓词全在租约腿(`boot/engine-lease.ts`);本域只读写**交接门**(`drainState.engineHandover`,收尾链装配时进盒子)。
|
|
6
|
+
* · 门序:身份(多租户形的主体门,与运维口同一只铸点)→ 能力事实 `engineHandover`(与 `capabilities.engine.handover` 同读;不可用 ⇒ 501
|
|
7
|
+
* `capability.engine_handover_unavailable`,按 why 两句)→ 验型(体坏 ⇒ 400,零状态变化)→ 写门。billable=false,**不吃排空 503**
|
|
8
|
+
* (排空一开始租约腿在它的下一拍收摊,能力事实随即答 501;那一拍之内的交接 / attach 无害 —— 引擎已在退)。
|
|
9
|
+
* · 交接体只收 `{ reason: "background_exit" }`(闭集);attach 体只收空对象或空体(字面 `null` 与空体同读 —— 共用读体器的全服务律)。
|
|
10
|
+
* 非对象体 ⇒ 400 `request.invalid_json`(`not_object`)。
|
|
11
|
+
* · 能力事实读两次、是同一只函数:读体之前读一次(决定 501 先于 400),验完体、写门之前**无 await** 再读一次 —— 读体途中腿停了(停机开始)
|
|
12
|
+
* 就答 501,不拿读体之前的旧事实去写一只已不在观察的门(codex r1 [medium])。
|
|
13
|
+
* · 两口幂等:再次交接回同一个 `since`;不在交接车道时 attach 照回此刻车道(`handedOverForMs` 缺席)。
|
|
14
|
+
*/
|
|
15
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
16
|
+
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
17
|
+
export declare const ENGINE_HANDOVER_PATH = "/v1/engine/handover";
|
|
18
|
+
export declare const ENGINE_ATTACH_PATH = "/v1/engine/attach";
|
|
19
|
+
export declare function handleEngine(req: IncomingMessage, res: ServerResponse, match: RouteMatch<EngineRouteId>, ctx: RouteCtx): Promise<void>;
|
|
20
|
+
export declare const ENGINE_ROUTES: readonly [{
|
|
21
|
+
readonly id: "engine-handover";
|
|
22
|
+
readonly path: "/v1/engine/handover";
|
|
23
|
+
readonly methods: readonly ["POST"];
|
|
24
|
+
}, {
|
|
25
|
+
readonly id: "engine-attach";
|
|
26
|
+
readonly path: "/v1/engine/attach";
|
|
27
|
+
readonly methods: readonly ["POST"];
|
|
28
|
+
}];
|
|
29
|
+
type EngineRouteId = RouteIdsOf<typeof ENGINE_ROUTES>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { sendJson, sendError, sendHttpError, invalidJsonError } from "../send.js";
|
|
2
|
+
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
|
+
import * as facts from "../../capability-facts.js";
|
|
4
|
+
import { HANDOVER_REASONS } from "../../boot/engine-lease.js";
|
|
5
|
+
import { assertNever } from "../route-ctx.js";
|
|
6
|
+
export const ENGINE_HANDOVER_PATH = "/v1/engine/handover";
|
|
7
|
+
export const ENGINE_ATTACH_PATH = "/v1/engine/attach";
|
|
8
|
+
const HANDOVER_REASONS_SPELLED = HANDOVER_REASONS.map((r) => `"${r}"`).join(", ");
|
|
9
|
+
const ATTACH_BODY_MESSAGE = "the attach request takes no fields (send {} or an empty body) — nothing was changed";
|
|
10
|
+
function isPlainObject(v) {
|
|
11
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
12
|
+
}
|
|
13
|
+
function readHandoverReason(body) {
|
|
14
|
+
const keys = Object.keys(body);
|
|
15
|
+
if (keys.length !== 1 || keys[0] !== "reason")
|
|
16
|
+
return undefined;
|
|
17
|
+
const reason = body.reason;
|
|
18
|
+
return HANDOVER_REASONS.find((r) => r === reason);
|
|
19
|
+
}
|
|
20
|
+
function sendHandoverUnavailable(res, why) {
|
|
21
|
+
switch (why) {
|
|
22
|
+
case "engine_handover_lane_absent":
|
|
23
|
+
sendError(res, 501, "capability.engine_handover_unavailable", "handover is not available on this engine: its attachment lease is not armed (neither ENGINE_AUTO_EXIT nor SEMA_PARENT_PID), or the deployment is multi-tenant (REQUIRE_PRINCIPAL=true) — GET /v1/capabilities reports engine.handover=false; exit and stop tasks instead");
|
|
24
|
+
return;
|
|
25
|
+
case "engine_lease_stopped":
|
|
26
|
+
sendError(res, 501, "capability.engine_handover_unavailable", "this engine is shutting down: its attachment lease has stopped observing, so there is nothing to hand over or attach to");
|
|
27
|
+
return;
|
|
28
|
+
default:
|
|
29
|
+
return assertNever(why);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function handleEngine(req, res, match, ctx) {
|
|
33
|
+
const { deps } = ctx;
|
|
34
|
+
if (deps.config.requirePrincipal && gatedPrincipal(req, deps.config) === undefined) {
|
|
35
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const before = facts.engineHandover(deps);
|
|
39
|
+
if (!before.available) {
|
|
40
|
+
sendHandoverUnavailable(res, before.why);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const body = await ctx.helpers.readJson(req);
|
|
44
|
+
if (!isPlainObject(body)) {
|
|
45
|
+
sendHttpError(res, invalidJsonError("not_object"));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let write;
|
|
49
|
+
switch (match.decl.id) {
|
|
50
|
+
case "engine-handover": {
|
|
51
|
+
const reason = readHandoverReason(body);
|
|
52
|
+
if (reason === undefined) {
|
|
53
|
+
sendError(res, 400, "request.field_invalid", `the handover request must be exactly { reason } with reason one of ${HANDOVER_REASONS_SPELLED} — nothing was changed`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
write = (door) => door.enter(reason);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "engine-attach": {
|
|
60
|
+
if (Object.keys(body).length > 0) {
|
|
61
|
+
sendError(res, 400, "request.field_invalid", ATTACH_BODY_MESSAGE);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
write = (door) => door.attach();
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
default:
|
|
68
|
+
return assertNever(match.decl.id);
|
|
69
|
+
}
|
|
70
|
+
const now = facts.engineHandover(deps);
|
|
71
|
+
if (!now.available) {
|
|
72
|
+
sendHandoverUnavailable(res, now.why);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
sendJson(res, 200, write(now.door));
|
|
76
|
+
}
|
|
77
|
+
export const ENGINE_ROUTES = [
|
|
78
|
+
{ id: "engine-handover", path: ENGINE_HANDOVER_PATH, methods: ["POST"] },
|
|
79
|
+
{ id: "engine-attach", path: ENGINE_ATTACH_PATH, methods: ["POST"] },
|
|
80
|
+
];
|
|
81
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { sentMemoryStoreRefusal } from "../memory-store-refusal-reply.js";
|
|
4
|
+
import { sentControlPlaneCorrupt } from "../memory-control-plane-corrupt-reply.js";
|
|
4
5
|
import { buildOriginFencedBundle, readOriginAware } from "../../memory-origin-fence.js";
|
|
5
6
|
import { gatedPrincipal, explicitOperatorOk, sendOperatorPrincipalsRequired, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
7
|
import { assertNever } from "../route-ctx.js";
|
|
@@ -42,6 +43,8 @@ function sentClassifiedFailure(res, ctx, face, err) {
|
|
|
42
43
|
return true;
|
|
43
44
|
if (sentMemoryStoreRefusal(res, err, ctx.deps.logger))
|
|
44
45
|
return true;
|
|
46
|
+
if (sentControlPlaneCorrupt(res, err, { kind: "operator" }))
|
|
47
|
+
return true;
|
|
45
48
|
ctx.deps.logger?.warn?.(`memory_bundle_${face}_failed`, { err: String(err) });
|
|
46
49
|
return false;
|
|
47
50
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
2
|
import { sendJson, sendError } from "../send.js";
|
|
4
3
|
import { sentMemoryLayerRefusal } from "../memory-layer-reply.js";
|
|
4
|
+
import { sentControlPlaneCorrupt } from "../memory-control-plane-corrupt-reply.js";
|
|
5
5
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal, sendOperatorPrincipalsRequired } from "../principal-gate.js";
|
|
6
6
|
import { assertNever } from "../route-ctx.js";
|
|
7
7
|
import * as facts from "../../capability-facts.js";
|
|
@@ -104,7 +104,7 @@ export async function handleMemoryConsolidation(req, res, match, ctx) {
|
|
|
104
104
|
const scope = "scope" in decided ? decided.scope : undefined;
|
|
105
105
|
let row;
|
|
106
106
|
try {
|
|
107
|
-
row = scope !== undefined ? faces.lastRun(scope) : undefined;
|
|
107
|
+
row = scope !== undefined ? await faces.lastRun(scope) : undefined;
|
|
108
108
|
}
|
|
109
109
|
catch (err) {
|
|
110
110
|
if (sentMemoryLayerRefusal(res, err, { kind: "operator" }))
|
|
@@ -160,10 +160,8 @@ export async function handleMemoryConsolidation(req, res, match, ctx) {
|
|
|
160
160
|
sendError(res, 409, "memory.consolidation_refused", `memory consolidation refused (${coreCode}): ${err instanceof Error ? err.message : String(err)}`);
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
|
-
if (err
|
|
164
|
-
sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
|
|
163
|
+
if (sentControlPlaneCorrupt(res, err, { kind: "operator" }))
|
|
165
164
|
return;
|
|
166
|
-
}
|
|
167
165
|
ctx.deps.logger?.warn?.("memory_consolidation_run_failed", { scope: decided.scope, err: String(err) });
|
|
168
166
|
sendError(res, 500, "internal.error", "memory consolidation run failed");
|
|
169
167
|
}
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
* 分层:本模块不值 import `server.ts`(那条边闭合运行时装载环),只 `import type`。
|
|
79
79
|
*/
|
|
80
80
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
81
|
-
import {
|
|
81
|
+
import type { OriginClearanceRow } from "@sema-agent/core";
|
|
82
82
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
83
83
|
export declare const MEMORY_ORIGIN_EXTERNAL_PATH = "/v1/memory/origin/external";
|
|
84
84
|
export declare const MEMORY_ORIGIN_CLEARANCES_PATH = "/v1/memory/origin/clearances";
|