@sema-agent/server 7.76.0 → 7.76.2
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/USAGE.md +47 -21
- package/dist/boot/engine-lease.d.ts +89 -11
- package/dist/boot/engine-lease.js +26 -11
- package/dist/boot/exit-last.d.ts +24 -0
- package/dist/boot/exit-last.js +46 -0
- package/dist/boot/shutdown.js +28 -4
- package/dist/config-catalog.js +3 -2
- package/dist/config-types.d.ts +23 -11
- package/dist/config.js +8 -8
- package/dist/http/principal-gate.d.ts +24 -1
- package/dist/http/principal-gate.js +16 -1
- package/dist/http/routes/a2a-serve.js +2 -2
- package/dist/http/routes/admin-config-refresh.js +2 -2
- package/dist/http/routes/admin-drain.js +2 -2
- package/dist/http/routes/admin-memory-optout.js +2 -2
- package/dist/http/routes/adoption.js +2 -2
- package/dist/http/routes/approvals-assistant.js +10 -6
- package/dist/http/routes/attachments.js +2 -2
- package/dist/http/routes/config-catalog.js +2 -2
- package/dist/http/routes/devices.js +2 -2
- package/dist/http/routes/fleet.js +2 -2
- package/dist/http/routes/leader.js +2 -2
- package/dist/http/routes/memory-bundle.js +2 -2
- package/dist/http/routes/memory-compliance.js +2 -2
- package/dist/http/routes/memory-consolidation.js +2 -2
- package/dist/http/routes/memory-origin.js +2 -2
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/notify-wake.js +3 -3
- package/dist/http/routes/observability.js +2 -2
- package/dist/http/routes/retention-ops.js +2 -2
- package/dist/http/routes/run-memory-capture.js +2 -2
- package/dist/http/routes/runs.js +13 -13
- package/dist/http/routes/sessions.js +7 -7
- package/dist/http/routes/side-query.js +2 -2
- package/dist/http/routes/workflows.js +3 -3
- package/dist/http/server.js +2 -2
- package/dist/parent-watch.d.ts +32 -18
- package/dist/parent-watch.js +12 -10
- package/dist/plugins/tidb-session-storage.d.ts +4 -0
- package/dist/plugins/tidb-session-storage.js +7 -2
- package/dist/principal-jwt.d.ts +23 -3
- package/dist/principal-jwt.js +8 -0
- package/dist/tool-approval.js +2 -2
- package/dist/trace/redact.d.ts +23 -1
- package/dist/trace/redact.js +45 -11
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IncomingMessage } from "node:http";
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
3
|
/** Coerce a possibly-repeated request header to a single string (or undefined). */
|
|
4
4
|
export declare function headerStr(v: string | string[] | undefined): string | undefined;
|
|
@@ -21,6 +21,29 @@ export declare function isLoopbackAddress(addr: string | undefined): boolean;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function verifiedIdentitySource(req: IncomingMessage, config: ServiceConfig): "sso" | "direct_door_jwt" | "service_token" | undefined;
|
|
23
23
|
export declare function gatedPrincipal(req: IncomingMessage, config: ServiceConfig): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* 「**出示了凭证但验不过** ≠ **没出示**」在 **direct-door principal JWT** 面上的形 —— 本仓
|
|
26
|
+
* {@link bearerPresentedButUnverified} 早就是同一条律在 Bearer 面的形,这里只是把它推到另一面。
|
|
27
|
+
*
|
|
28
|
+
* 判据三段,缺一不成立:①这台机器是直连门(BFF/gated 上 `principalHeader` 由上游验过,本函数恒 false ⇒
|
|
29
|
+
* 那条部署形上零行为变化);②`X-Approval-Principal-Token` **在场**(缺席 = 真的没出示,那是
|
|
30
|
+
* `auth.principal_required` 的语义,不许借本码);③它**验不过**({@link verifiedPrincipal} 回 undefined)。
|
|
31
|
+
*/
|
|
32
|
+
export declare function principalTokenPresentedButUnverified(req: IncomingMessage, config: ServiceConfig): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* 🔴 **`requirePrincipal` 部署上「这次请求没有可用身份」的唯一 401 铸点**(S-305,7.76.2)。
|
|
35
|
+
*
|
|
36
|
+
* 此前 45 个路由各自手抄同一行 `sendError(res, 401, "auth.principal_required", …)`,于是直连门上
|
|
37
|
+
* 「JWT 出示了但过期/签不上」与「什么头都没带」在 wire 上**同形**(黑盒实测:plan_review 带过期 JWT
|
|
38
|
+
* 拿到 `auth.principal_required`,而契约与路由层的 `verifyDirectDoorProof` 说的是 `principal_unverified`)。
|
|
39
|
+
* 两件事的处置完全不同 —— 前者「重签一张再来」,后者「你压根没接上身份面」。
|
|
40
|
+
*
|
|
41
|
+
* 一条规则、一处落点:{@link principalTokenPresentedButUnverified} 为真 ⇒ `401 principal_unverified`
|
|
42
|
+
* (既有码,文案与 `principal-jwt.ts` 的验签层同句);否则 `401 auth.principal_required`(原样)。
|
|
43
|
+
* 路由层 `verifyDirectDoorProof` 自己那条 `principal_unverified` 臂保留 —— 它守的是 decide/plan_review
|
|
44
|
+
* 调用链上的**证明三件套**,与本门(身份面)是两道门。
|
|
45
|
+
*/
|
|
46
|
+
export declare function sendPrincipalRefusal(res: ServerResponse, req: IncomingMessage, config: ServiceConfig): void;
|
|
24
47
|
/** The EXPLICIT operator gate (IMAGE-API-DESIGN.md §P2.4a MUST-FIX) — NOT the bare `isOperator` (whose empty-list
|
|
25
48
|
* "true-for-all" is an unrestricted-write exec door the instant OPERATOR_PRINCIPALS is empty). Requires a non-empty
|
|
26
49
|
* operator set AND a present principal IN it. The same form the preempt/resume endpoints already use.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { timingSafeEqual } from "node:crypto";
|
|
2
|
-
import { verifiedPrincipal, ssoVerifiedPrincipal } from "../security.js";
|
|
2
|
+
import { PRINCIPAL_TOKEN_HEADER, verifiedPrincipal, ssoVerifiedPrincipal } from "../security.js";
|
|
3
|
+
import { sendError } from "./send.js";
|
|
3
4
|
export function headerStr(v) {
|
|
4
5
|
return Array.isArray(v) ? v[0] : v;
|
|
5
6
|
}
|
|
@@ -50,6 +51,20 @@ export function gatedPrincipal(req, config) {
|
|
|
50
51
|
return undefined;
|
|
51
52
|
return verifiedPrincipal(req, config);
|
|
52
53
|
}
|
|
54
|
+
export function principalTokenPresentedButUnverified(req, config) {
|
|
55
|
+
if (!config.directDoorActive)
|
|
56
|
+
return false;
|
|
57
|
+
if (headerStr(req.headers[PRINCIPAL_TOKEN_HEADER]) === undefined)
|
|
58
|
+
return false;
|
|
59
|
+
return verifiedPrincipal(req, config) === undefined;
|
|
60
|
+
}
|
|
61
|
+
export function sendPrincipalRefusal(res, req, config) {
|
|
62
|
+
if (principalTokenPresentedButUnverified(req, config)) {
|
|
63
|
+
sendError(res, 401, "principal_unverified", "principal token failed verification");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
sendError(res, 401, "auth.principal_required", `missing principal header '${config.principalHeader}'`);
|
|
67
|
+
}
|
|
53
68
|
export function explicitOperatorOk(principal, operatorPrincipals) {
|
|
54
69
|
return operatorPrincipals.length > 0 && principal !== undefined && operatorPrincipals.includes(principal);
|
|
55
70
|
}
|
|
@@ -6,7 +6,7 @@ import { isParkedRunStatus, isTerminalRunStatus } from "../../plugins/store-cont
|
|
|
6
6
|
import { HttpError } from "../../security.js";
|
|
7
7
|
import { scopedIdempotencyKey } from "../idempotency.js";
|
|
8
8
|
import { sendJson, sendError } from "../send.js";
|
|
9
|
-
import { gatedPrincipal } from "../principal-gate.js";
|
|
9
|
+
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
10
10
|
import { createDurableRun } from "./runs.js";
|
|
11
11
|
export const A2A_PROTOCOL_VERSION = "1.0";
|
|
12
12
|
export const A2A_VERSION_HEADER = "a2a-version";
|
|
@@ -344,7 +344,7 @@ async function handleA2aRpcBody(req, res, url, ctx, miss) {
|
|
|
344
344
|
res.setHeader(A2A_VERSION_HEADER, A2A_PROTOCOL_VERSION);
|
|
345
345
|
if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
|
|
346
346
|
deps.metrics?.inc("a2a_serve_rejected_total", { reason: "principal_required" });
|
|
347
|
-
|
|
347
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
348
348
|
return;
|
|
349
349
|
}
|
|
350
350
|
let payload;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
2
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
3
|
export const ADMIN_CONFIG_REFRESH_PATH = "/v1/admin/config/refresh";
|
|
4
4
|
const OPERATOR_ONLY_MESSAGE = "config refresh is operator-only: the caller principal is not in OPERATOR_PRINCIPALS (an empty list means NO one is an operator — configure it to enable this endpoint)";
|
|
5
5
|
export async function handleAdminConfigRefresh(req, res, url, ctx) {
|
|
@@ -16,7 +16,7 @@ async function handleAdminConfigRefreshBody(req, res, url, ctx, miss) {
|
|
|
16
16
|
}
|
|
17
17
|
const principal = gatedPrincipal(req, deps.config);
|
|
18
18
|
if (deps.config.requirePrincipal && !principal) {
|
|
19
|
-
|
|
19
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
2
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
3
|
export const ADMIN_DRAIN_PATH = "/v1/admin/drain";
|
|
4
4
|
export const MAX_DRAIN_REASON_CHARS = 256;
|
|
5
5
|
const OPERATOR_ONLY_MESSAGE = "drain declaration is operator-only: the caller principal is not in OPERATOR_PRINCIPALS (an empty list means NO one is an operator — configure it to enable this endpoint)";
|
|
@@ -17,7 +17,7 @@ async function handleAdminDrainBody(req, res, url, ctx, miss) {
|
|
|
17
17
|
}
|
|
18
18
|
const principal = gatedPrincipal(req, deps.config);
|
|
19
19
|
if (deps.config.requirePrincipal && !principal) {
|
|
20
|
-
|
|
20
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
3
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { memoryOptOutPrincipalShapeProblem } from "../../plugins/memory-optout-grant-store-sql.js";
|
|
5
5
|
export const ADMIN_MEMORY_OPTOUT_PATH = "/v1/admin/memory-optout";
|
|
6
6
|
export const ADMIN_MEMORY_OPTOUT_OVERRIDE_RE = /^\/v1\/admin\/memory-optout\/([^/]+)$/;
|
|
@@ -38,7 +38,7 @@ async function handleBody(req, res, url, ctx, miss) {
|
|
|
38
38
|
}
|
|
39
39
|
const principal = gatedPrincipal(req, deps.config);
|
|
40
40
|
if (deps.config.requirePrincipal && !principal) {
|
|
41
|
-
|
|
41
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -2,7 +2,7 @@ import { createAdoptionRunner } from "../../adoption/runner.js";
|
|
|
2
2
|
import { AdoptionRequestSchema } from "../../adoption/wire.js";
|
|
3
3
|
import { checkAdoptionPrincipalShape, checkAdoptionWidths } from "../../adoption/plan.js";
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
const ADOPTION_ID_RE = /^\/v1\/adoption\/([^/]+)$/;
|
|
7
7
|
function sendOutcome(res, out) {
|
|
8
8
|
switch (out.kind) {
|
|
@@ -54,7 +54,7 @@ async function handleAdoptionBody(req, res, url, ctx, miss) {
|
|
|
54
54
|
}
|
|
55
55
|
const principal = gatedPrincipal(req, deps.config);
|
|
56
56
|
if (deps.config.requirePrincipal && !principal) {
|
|
57
|
-
|
|
57
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -5,7 +5,7 @@ import { DECIDED_REPLAY_SCAN_CAP, approvalPayloadFingerprint } from "../../plugi
|
|
|
5
5
|
import { APPROVAL_GATE_KINDS } from "../../tool-approval.js";
|
|
6
6
|
import { isParkedRunStatus } from "../../plugins/store-contracts.js";
|
|
7
7
|
import { principalFrom, decodeCheckpointScope, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../../security.js";
|
|
8
|
-
import { verifyDirectDoorProof } from "../../principal-jwt.js";
|
|
8
|
+
import { editedPlanBinding, isWellFormedUnicode, verifyDirectDoorProof } from "../../principal-jwt.js";
|
|
9
9
|
import { MAX_APPROVAL_REASON_CHARS } from "../../approval-hmac.js";
|
|
10
10
|
import { MAX_APPROVAL_NONCE_CHARS } from "../../plugins/approval-nonce-store.js";
|
|
11
11
|
import { redactedPreview, redactSecrets } from "../../trace/redact.js";
|
|
@@ -14,7 +14,7 @@ import { fleetRunLabels } from "../../fleet/fleet-bus.js";
|
|
|
14
14
|
import { sleep } from "../sse-log.js";
|
|
15
15
|
import { bindSseLifecycle } from "../sse-lifecycle.js";
|
|
16
16
|
import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "../send.js";
|
|
17
|
-
import { gatedPrincipal, explicitOperatorOk, isOperator } from "../principal-gate.js";
|
|
17
|
+
import { gatedPrincipal, explicitOperatorOk, isOperator, sendPrincipalRefusal } from "../principal-gate.js";
|
|
18
18
|
import { governanceOriginOf } from "../active-run-conflict.js";
|
|
19
19
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
20
20
|
export const ASSISTANT_PREEMPT_RE = /^\/v1\/assistant\/tasks\/([^/]+)\/preempt$/;
|
|
@@ -246,7 +246,7 @@ async function handleApprovalsAssistantBody(req, res, url, ctx, miss) {
|
|
|
246
246
|
return;
|
|
247
247
|
}
|
|
248
248
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
249
|
-
|
|
249
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
250
250
|
return;
|
|
251
251
|
}
|
|
252
252
|
if (!deps.config.resourceSuspend || !deps.checkpointStore) {
|
|
@@ -310,7 +310,7 @@ async function handleApprovalsAssistantBody(req, res, url, ctx, miss) {
|
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
312
312
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
313
|
-
|
|
313
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
316
|
const taskId = resumeMatch[1];
|
|
@@ -338,7 +338,7 @@ async function handleApprovalsAssistantBody(req, res, url, ctx, miss) {
|
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
340
340
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
341
|
-
|
|
341
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
342
342
|
return;
|
|
343
343
|
}
|
|
344
344
|
const taskId = planReviewMatch[1];
|
|
@@ -352,6 +352,10 @@ async function handleApprovalsAssistantBody(req, res, url, ctx, miss) {
|
|
|
352
352
|
sendError(res, 400, "request.field_conflict", "decision 'edit' requires a non-empty editedPlan (the operator's revised plan)");
|
|
353
353
|
return;
|
|
354
354
|
}
|
|
355
|
+
if (decision === "edit" && !isWellFormedUnicode(body.editedPlan)) {
|
|
356
|
+
sendError(res, 400, "request.field_invalid", "editedPlan must be well-formed Unicode text (a lone surrogate cannot be signed unambiguously)");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
355
359
|
if (decision !== "edit" && body.editedPlan !== undefined) {
|
|
356
360
|
sendError(res, 400, "request.field_conflict", `editedPlan is only valid with decision 'edit' (got '${decision}')`);
|
|
357
361
|
return;
|
|
@@ -399,7 +403,7 @@ async function handleApprovalsAssistantBody(req, res, url, ctx, miss) {
|
|
|
399
403
|
}
|
|
400
404
|
const storeNowMs = await store.nowMs();
|
|
401
405
|
const hdr = (n) => { const h = req.headers[n]; return Array.isArray(h) ? h[0] : h; };
|
|
402
|
-
const proof = verifyDirectDoorProof({ jwt: hdr(PRINCIPAL_TOKEN_HEADER), mac: hdr(APPROVAL_MAC_HEADER), kid: hdr(APPROVAL_MAC_KID_HEADER) }, { sessionId: run.sessionId, boundCallId: planNonce ?? null, decision, reason: typeof body.reason === "string" ? body.reason : null }, deps.config, { actionBinding: false, nowSec: Math.floor(storeNowMs / 1000) });
|
|
406
|
+
const proof = verifyDirectDoorProof({ jwt: hdr(PRINCIPAL_TOKEN_HEADER), mac: hdr(APPROVAL_MAC_HEADER), kid: hdr(APPROVAL_MAC_KID_HEADER) }, { sessionId: run.sessionId, boundCallId: planNonce ?? null, boundInputHash: decision === "edit" ? editedPlanBinding(body.editedPlan) : null, decision, reason: typeof body.reason === "string" ? body.reason : null }, deps.config, { actionBinding: false, nowSec: Math.floor(storeNowMs / 1000) });
|
|
403
407
|
if (!proof.ok) {
|
|
404
408
|
sendError(res, proof.status, proof.errorCode, proof.error);
|
|
405
409
|
return;
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { uuidv7, sanitizePathComponent } from "@sema-agent/core";
|
|
3
3
|
import { HttpError } from "../../security.js";
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
|
-
import { gatedPrincipal } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
export async function handleAttachments(req, res, url, ctx) {
|
|
7
7
|
const miss = { fell: false };
|
|
8
8
|
await handleAttachmentsBody(req, res, url, ctx, miss);
|
|
@@ -23,7 +23,7 @@ async function handleAttachmentsBody(req, res, url, ctx, miss) {
|
|
|
23
23
|
}
|
|
24
24
|
const principal = gatedPrincipal(req, deps.config);
|
|
25
25
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
26
|
-
|
|
26
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
const owner = principal ?? "default";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DOMAINS } from "@sema-agent/settings-schema";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
3
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { configKnobTable } from "../../config.js";
|
|
5
5
|
import { buildConfigCatalog } from "../../config-catalog.js";
|
|
6
6
|
export const CONFIG_CATALOG_PATH = "/v1/config/catalog";
|
|
@@ -23,7 +23,7 @@ function handleConfigCatalogBody(req, res, url, ctx, miss) {
|
|
|
23
23
|
}
|
|
24
24
|
const principal = gatedPrincipal(req, deps.config);
|
|
25
25
|
if (deps.config.requirePrincipal && !principal) {
|
|
26
|
-
|
|
26
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sendError, sendJson } from "../send.js";
|
|
2
|
-
import { explicitOperatorOk, gatedPrincipal } from "../principal-gate.js";
|
|
2
|
+
import { explicitOperatorOk, gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
3
|
import { ssoVerifiedScope } from "../../security.js";
|
|
4
4
|
import { assertDeviceIdShape, assertRootSessionIdShape, sameDeviceOwner, } from "../../device-store.js";
|
|
5
5
|
const DEVICE_NOT_FOUND_MESSAGE = "device not found";
|
|
@@ -60,7 +60,7 @@ async function handleDevicesBody(req, res, url, ctx, miss) {
|
|
|
60
60
|
}
|
|
61
61
|
const principal = gatedPrincipal(req, deps.config);
|
|
62
62
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
63
|
-
|
|
63
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
const store = deps.deviceStore;
|
|
@@ -2,7 +2,7 @@ import { hookNoticeWire } from "../../fleet/fleet-bus.js";
|
|
|
2
2
|
import { FLEET_SNAPSHOT_TERMINAL_MAX_ROWS, isTerminalWorkflowRowStatus, recentTerminalWorkflowRows } from "../../fleet/fleet-terminal-window.js";
|
|
3
3
|
import { taskNotificationFoldKey } from "../../orchestration/workflow-completion-inbox.js";
|
|
4
4
|
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
5
|
-
import { gatedPrincipal } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
export async function handleFleet(req, res, url, ctx) {
|
|
7
7
|
const miss = { fell: false };
|
|
8
8
|
await handleFleetBody(req, res, url, ctx, miss);
|
|
@@ -20,7 +20,7 @@ async function handleFleetBody(req, res, url, ctx, miss) {
|
|
|
20
20
|
const { fleetWide } = sessionOwnerScope(req);
|
|
21
21
|
const principal = gatedPrincipal(req, deps.config);
|
|
22
22
|
if (deps.config.requirePrincipal && !principal && !fleetWide) {
|
|
23
|
-
|
|
23
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const fleetParams = new URL(req.url ?? "", "http://x").searchParams;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
-
import { gatedPrincipal, headerStr } from "../principal-gate.js";
|
|
2
|
+
import { gatedPrincipal, headerStr, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
3
|
export async function handleLeader(req, res, url, ctx) {
|
|
4
4
|
const miss = { fell: false };
|
|
5
5
|
await handleLeaderBody(req, res, url, ctx, miss);
|
|
@@ -13,7 +13,7 @@ async function handleLeaderBody(req, res, url, ctx, miss) {
|
|
|
13
13
|
const isLeaderGet = req.method === "GET" && /^\/v1\/leader\/[^/]+$/.test(url);
|
|
14
14
|
if (isLeaderPost || isLeaderGet) {
|
|
15
15
|
if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
|
|
16
|
-
|
|
16
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
const requester = gatedPrincipal(req, deps.config) ?? null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { buildOriginFencedBundle, readOriginAware } from "../../memory-origin-fence.js";
|
|
4
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
4
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
5
5
|
export const MEMORY_BUNDLE_EXPORT_PATH = "/v1/memory/export";
|
|
6
6
|
export const MEMORY_BUNDLE_IMPORT_PATH = "/v1/memory/import";
|
|
7
7
|
const ExportRequestSchema = z.object({ scopes: z.array(z.string().min(1)).min(1) });
|
|
@@ -72,7 +72,7 @@ async function handleMemoryBundleBody(req, res, url, ctx, miss) {
|
|
|
72
72
|
}
|
|
73
73
|
const principal = gatedPrincipal(req, deps.config);
|
|
74
74
|
if (deps.config.requirePrincipal && !principal) {
|
|
75
|
-
|
|
75
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
3
|
import { erasureInputSmugglesPrototypeKey } from "../../memory-operator-faces.js";
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
export const MEMORY_ERASE_PATH = "/v1/memory/erase";
|
|
7
7
|
export const MEMORY_PROVENANCE_RE = /^\/v1\/memory\/entries\/([^/]+)\/provenance$/;
|
|
8
8
|
const EraseRequestSchema = z.object({
|
|
@@ -61,7 +61,7 @@ async function handleMemoryComplianceBody(req, res, url, ctx, miss) {
|
|
|
61
61
|
}
|
|
62
62
|
const principal = gatedPrincipal(req, deps.config);
|
|
63
63
|
if (deps.config.requirePrincipal && !principal) {
|
|
64
|
-
|
|
64
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
4
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
5
5
|
export const MEMORY_CONSOLIDATION_STATUS_PATH = "/v1/admin/memory/consolidation";
|
|
6
6
|
export const MEMORY_CONSOLIDATION_RUN_PATH = "/v1/admin/memory/consolidation/run";
|
|
7
7
|
const RunRequestSchema = z.object({ scope: z.string().optional() }).strict();
|
|
@@ -86,7 +86,7 @@ async function handleMemoryConsolidationBody(req, res, url, ctx, miss) {
|
|
|
86
86
|
}
|
|
87
87
|
const principal = gatedPrincipal(req, deps.config);
|
|
88
88
|
if (deps.config.requirePrincipal && !principal) {
|
|
89
|
-
|
|
89
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
92
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { ControlPlaneCorruptError } from "@sema-agent/core";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
4
|
import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
|
|
5
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
export const MEMORY_ORIGIN_EXTERNAL_PATH = "/v1/memory/origin/external";
|
|
7
7
|
export const MEMORY_ORIGIN_CLEARANCES_PATH = "/v1/memory/origin/clearances";
|
|
8
8
|
export const MEMORY_ORIGIN_CLEAR_RE = /^\/v1\/memory\/origin\/entries\/([^/]+)\/clear$/;
|
|
@@ -123,7 +123,7 @@ async function handleMemoryOriginBody(req, res, url, ctx, miss) {
|
|
|
123
123
|
}
|
|
124
124
|
const principal = gatedPrincipal(req, deps.config);
|
|
125
125
|
if (deps.config.requirePrincipal && !principal) {
|
|
126
|
-
|
|
126
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -2,7 +2,7 @@ import { formatUserScope } from "@sema-agent/core";
|
|
|
2
2
|
import { parseMemorySyncRequest } from "../../memory-sync.js";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
4
|
import { buildOriginFence, readOriginAware } from "../../memory-origin-fence.js";
|
|
5
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
5
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
const ORG_PREFIX = "org:";
|
|
7
7
|
export const MEMORY_EXPORT_MAX_LIMIT = 500;
|
|
8
8
|
function encodeExportCursor(c) {
|
|
@@ -171,7 +171,7 @@ async function handleMemoryPolicyBody(req, res, url, ctx, miss) {
|
|
|
171
171
|
if (url === "/v1/policy" && req.method === "GET") {
|
|
172
172
|
const principal = gatedPrincipal(req, deps.config);
|
|
173
173
|
if (deps.config.requirePrincipal && !principal) {
|
|
174
|
-
|
|
174
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
177
|
sendJson(res, 200, {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { taskNotificationInboxEntry } from "../../orchestration/workflow-completion-inbox.js";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
3
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
5
5
|
export const SESSION_WAKE_RE = /^\/v1\/sessions\/[^/]+\/wake$/;
|
|
6
6
|
export async function handleNotifyWake(req, res, url, ctx) {
|
|
@@ -18,7 +18,7 @@ async function handleNotifyWakeBody(req, res, url, ctx, miss) {
|
|
|
18
18
|
return;
|
|
19
19
|
const principal = gatedPrincipal(req, deps.config);
|
|
20
20
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
21
|
-
|
|
21
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
const notifySession = decodeURIComponent(url.split("/")[3]);
|
|
@@ -112,7 +112,7 @@ async function handleNotifyWakeBody(req, res, url, ctx, miss) {
|
|
|
112
112
|
return;
|
|
113
113
|
const principal = gatedPrincipal(req, deps.config);
|
|
114
114
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
115
|
-
|
|
115
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
118
118
|
const wakeSession = decodeURIComponent(url.split("/")[3]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
-
import { gatedPrincipal, isOperator, explicitOperatorOk } from "../principal-gate.js";
|
|
2
|
+
import { gatedPrincipal, isOperator, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
3
|
export async function handleObservability(req, res, url, ctx) {
|
|
4
4
|
const miss = { fell: false };
|
|
5
5
|
await handleObservabilityBody(req, res, url, ctx, miss);
|
|
@@ -34,7 +34,7 @@ async function handleObservabilityBody(req, res, url, ctx, miss) {
|
|
|
34
34
|
if (url === "/v1/usage" && req.method === "GET") {
|
|
35
35
|
const principal = gatedPrincipal(req, deps.config);
|
|
36
36
|
if (!principal) {
|
|
37
|
-
|
|
37
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
const windowSec = deps.config.costQuotaWindowSec;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { sendJson, sendError } from "../send.js";
|
|
2
|
-
import { gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
2
|
+
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
3
3
|
import { RETENTION_AUDIT_DEFAULT_LIMIT, RETENTION_AUDIT_MAX_LIMIT } from "../../plugins/retention-lane-store-sql.js";
|
|
4
4
|
const HOLD_RE = /^\/v1\/ops\/retention\/holds\/([^/]*)$/;
|
|
5
5
|
const AUDIT_PATH = "/v1/ops/retention/audit";
|
|
@@ -40,7 +40,7 @@ async function handleRetentionBody(req, res, url, ctx, miss) {
|
|
|
40
40
|
}
|
|
41
41
|
const principal = gatedPrincipal(req, deps.config);
|
|
42
42
|
if (deps.config.requirePrincipal && !principal) {
|
|
43
|
-
|
|
43
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
if (!explicitOperatorOk(principal, deps.config.operatorPrincipals)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { isParkedRunStatus, isTerminalRunStatus } from "../../plugins/store-contracts.js";
|
|
3
3
|
import { sendJson, sendError } from "../send.js";
|
|
4
|
-
import { gatedPrincipal } from "../principal-gate.js";
|
|
4
|
+
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
5
5
|
import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage } from "../route-ctx.js";
|
|
6
6
|
export const RUN_MEMORY_CAPTURE_OPTOUT_RE = /^\/v1\/runs\/([^/]+)\/memory\/capture-optout$/;
|
|
7
7
|
const BodySchema = z.object({ reason: z.string().min(1).max(500).optional() }).strict();
|
|
@@ -25,7 +25,7 @@ async function handleBody(req, res, url, ctx, miss) {
|
|
|
25
25
|
}
|
|
26
26
|
const principal = gatedPrincipal(req, deps.config);
|
|
27
27
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
28
|
-
|
|
28
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
const taskId = m[1];
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -23,7 +23,7 @@ import { sendJson, sendError, httpErrorCode, sseHeaders } from "../send.js";
|
|
|
23
23
|
import { readTurnActivityMs } from "../../turn-activity.js";
|
|
24
24
|
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
25
25
|
import { buildCrossSliceUsage, readResourceWindow } from "../../resource-window.js";
|
|
26
|
-
import { headerStr, gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
26
|
+
import { headerStr, gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
27
27
|
import { ActorAssertionWire } from "@sema-agent/settings-schema";
|
|
28
28
|
import { mintAndDriveDurableRun } from "../durable-run-leg.js";
|
|
29
29
|
import { RUN_NOT_FOUND_MESSAGE, runNotFoundMessage } from "../route-ctx.js";
|
|
@@ -82,7 +82,7 @@ export const RUN_TASK_STOP_RE = /^\/v1\/runs\/([^/]+)\/tasks\/([^/]+)\/stop$/;
|
|
|
82
82
|
async function subagentReadGate(req, res, deps, runId) {
|
|
83
83
|
const principal = gatedPrincipal(req, deps.config);
|
|
84
84
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
85
|
-
|
|
85
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
86
86
|
return null;
|
|
87
87
|
}
|
|
88
88
|
const trusted = explicitOperatorOk(principal, deps.config.operatorPrincipals);
|
|
@@ -197,7 +197,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
197
197
|
}
|
|
198
198
|
const taskId = runMatch[1];
|
|
199
199
|
if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
|
|
200
|
-
|
|
200
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
201
201
|
return;
|
|
202
202
|
}
|
|
203
203
|
const run = await deps.runStore.getRun(taskId);
|
|
@@ -279,7 +279,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
279
279
|
return;
|
|
280
280
|
}
|
|
281
281
|
if (deps.config.requirePrincipal && !gatedPrincipal(req, deps.config)) {
|
|
282
|
-
|
|
282
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
283
283
|
return;
|
|
284
284
|
}
|
|
285
285
|
const taskId = cancelMatch[1];
|
|
@@ -407,7 +407,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
407
407
|
}
|
|
408
408
|
const principal = gatedPrincipal(req, deps.config);
|
|
409
409
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
410
|
-
|
|
410
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
411
411
|
return;
|
|
412
412
|
}
|
|
413
413
|
const taskId = steerMatch[1];
|
|
@@ -686,7 +686,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
686
686
|
}
|
|
687
687
|
const principal = gatedPrincipal(req, deps.config);
|
|
688
688
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
689
|
-
|
|
689
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
690
690
|
return;
|
|
691
691
|
}
|
|
692
692
|
const taskId = interruptMatch[1];
|
|
@@ -832,7 +832,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
832
832
|
}
|
|
833
833
|
const principal = gatedPrincipal(req, deps.config);
|
|
834
834
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
835
|
-
|
|
835
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
836
836
|
return;
|
|
837
837
|
}
|
|
838
838
|
const taskId = compactMatch[1];
|
|
@@ -899,7 +899,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
899
899
|
}
|
|
900
900
|
const principal = gatedPrincipal(req, deps.config);
|
|
901
901
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
902
|
-
|
|
902
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
903
903
|
return;
|
|
904
904
|
}
|
|
905
905
|
const taskId = detachMatch[1];
|
|
@@ -1117,7 +1117,7 @@ async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
|
1117
1117
|
}
|
|
1118
1118
|
const principal = gatedPrincipal(req, deps.config);
|
|
1119
1119
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
1120
|
-
|
|
1120
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
1121
1121
|
return;
|
|
1122
1122
|
}
|
|
1123
1123
|
const runId = safeDecode(subVerbMatch[1]);
|
|
@@ -1214,7 +1214,7 @@ async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
|
1214
1214
|
}
|
|
1215
1215
|
const principal = gatedPrincipal(req, deps.config);
|
|
1216
1216
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
1217
|
-
|
|
1217
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
1218
1218
|
return;
|
|
1219
1219
|
}
|
|
1220
1220
|
const id = elicitMatch[1];
|
|
@@ -1240,7 +1240,7 @@ async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
|
1240
1240
|
}
|
|
1241
1241
|
const principal = gatedPrincipal(req, deps.config);
|
|
1242
1242
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
1243
|
-
|
|
1243
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
1244
1244
|
return;
|
|
1245
1245
|
}
|
|
1246
1246
|
const id = questionMatch[1];
|
|
@@ -1266,7 +1266,7 @@ async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
|
1266
1266
|
}
|
|
1267
1267
|
const principal = gatedPrincipal(req, deps.config);
|
|
1268
1268
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
1269
|
-
|
|
1269
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
1270
1270
|
return;
|
|
1271
1271
|
}
|
|
1272
1272
|
const id = approvalMatch[1];
|
|
@@ -1303,7 +1303,7 @@ async function handleRunVerbsBody(req, res, url, ctx, miss) {
|
|
|
1303
1303
|
}
|
|
1304
1304
|
const principal = gatedPrincipal(req, deps.config);
|
|
1305
1305
|
if (deps.config.requirePrincipal && principal === undefined) {
|
|
1306
|
-
|
|
1306
|
+
sendPrincipalRefusal(res, req, deps.config);
|
|
1307
1307
|
return;
|
|
1308
1308
|
}
|
|
1309
1309
|
let rawBody;
|