@sema-agent/server 7.104.0 → 7.106.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 +114 -0
- package/USAGE.md +193 -52
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -0
- package/deploy/sema-up/cluster-up.sh +15 -2
- package/deploy/sema-up/kube-up.sh +8 -3
- package/deploy/sema-up/lib/flagshape.sh +29 -0
- package/deploy/sema-up/lib/parse-machines.py +4 -1
- package/deploy/sema-up/sema-up.sh +11 -9
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +3 -1
- package/dist/approval-card.d.ts +1 -1
- package/dist/approval.d.ts +6 -1
- package/dist/approval.js +4 -4
- package/dist/auth-bridge.d.ts +14 -0
- package/dist/auth-bridge.js +7 -0
- package/dist/auto-mode-face.d.ts +49 -40
- package/dist/auto-mode-face.js +6 -68
- package/dist/bench/s1/live-deps.d.ts +9 -2
- package/dist/bench/s1/live-deps.js +13 -8
- package/dist/boot/budget-tracing.js +3 -2
- package/dist/boot/config-center.d.ts +4 -0
- package/dist/boot/config-center.js +95 -37
- package/dist/boot/coordinators.d.ts +1 -0
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/device-lane.d.ts +2 -1
- package/dist/boot/execution-env.d.ts +11 -0
- package/dist/boot/execution-env.js +30 -6
- package/dist/boot/execution-lane.js +2 -3
- package/dist/boot/leader.d.ts +2 -1
- package/dist/boot/leader.js +2 -0
- package/dist/boot/parked-revive-gate.d.ts +10 -7
- package/dist/boot/parked-revive-gate.js +4 -11
- package/dist/boot/resolve-spec.d.ts +1 -4
- package/dist/boot/resolve-spec.js +44 -21
- package/dist/boot/runner-deps.d.ts +5 -8
- package/dist/boot/runner-deps.js +5 -10
- package/dist/boot/side-query-lane.d.ts +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -1
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +3 -2
- package/dist/boot/stage-06-runners.js +2 -5
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +1 -2
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -2
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -2
- package/dist/boot/stage-10-http-server.js +11 -7
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +6 -4
- package/dist/brain.d.ts +24 -2
- package/dist/brain.js +29 -10
- package/dist/budget.js +3 -0
- package/dist/capabilities/center-wiring.d.ts +64 -0
- package/dist/capabilities/center-wiring.js +24 -0
- package/dist/capabilities/composite-members.d.ts +89 -0
- package/dist/capabilities/composite-members.js +20 -0
- package/dist/capabilities/execution-lane.d.ts +3 -0
- package/dist/capabilities/execution-lane.js +5 -2
- package/dist/config-catalog.d.ts +30 -5
- package/dist/config-catalog.js +75 -26
- package/dist/config-center/apply-effective.d.ts +1 -1
- package/dist/config-center/apply-effective.js +7 -8
- package/dist/config-center/apply-ledger.d.ts +17 -0
- package/dist/config-center/apply-ledger.js +5 -0
- package/dist/config-center/center-request.d.ts +17 -0
- package/dist/config-center/center-request.js +4 -0
- package/dist/config-center/pull-state.d.ts +29 -0
- package/dist/config-center/pull-state.js +2 -0
- package/dist/config-types.d.ts +41 -14
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +50 -2
- package/dist/config.js +111 -23
- package/dist/cross-session-settings.d.ts +1 -1
- package/dist/deployment-governance.d.ts +12 -1
- package/dist/deployment-governance.js +2 -0
- package/dist/execution-lane-caps.d.ts +30 -2
- package/dist/execution-lane-caps.js +12 -0
- package/dist/file-history-disclosure.d.ts +7 -31
- package/dist/file-history-disclosure.js +12 -0
- package/dist/fleet/fleet-bus.d.ts +5 -1
- package/dist/fleet-client.d.ts +18 -13
- package/dist/fleet-client.js +32 -22
- package/dist/fleet-lease.d.ts +72 -47
- package/dist/fleet-lease.js +66 -14
- package/dist/governance-http-status.d.ts +0 -1
- package/dist/governance-http-status.js +0 -1
- package/dist/hosted-posture.d.ts +6 -17
- package/dist/hosted-posture.js +11 -6
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +14 -9
- package/dist/http/approval-sweeps.js +13 -1
- package/dist/http/dispatch.d.ts +18 -0
- package/dist/http/dispatch.js +18 -5
- package/dist/http/durable-run-leg.js +2 -0
- package/dist/http/request-guards.d.ts +25 -0
- package/dist/http/request-guards.js +25 -5
- package/dist/http/resume-legs.d.ts +1 -1
- package/dist/http/resume-legs.js +19 -11
- package/dist/http/route-ctx.d.ts +9 -0
- package/dist/http/route-table.d.ts +15 -4
- package/dist/http/route-table.js +6 -5
- package/dist/http/routes/approvals-assistant.js +0 -1
- package/dist/http/routes/capabilities.js +12 -11
- package/dist/http/routes/config-catalog.js +2 -1
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/http/routes/fleet.d.ts +3 -2
- package/dist/http/routes/images.js +0 -1
- package/dist/http/routes/runs.js +2 -8
- package/dist/http/routes/session-sync.js +6 -1
- package/dist/http/routes/sessions.js +5 -2
- package/dist/http/routes/side-query.js +8 -1
- package/dist/http/routes/tasks.js +4 -1
- package/dist/http/routes/trace-usage.d.ts +2 -0
- package/dist/http/routes/trace-usage.js +4 -9
- package/dist/http/server.d.ts +29 -8
- package/dist/http/server.js +4 -1
- package/dist/http/session-inherit-env.d.ts +37 -0
- package/dist/http/session-inherit-env.js +6 -0
- package/dist/http/sse-lifecycle.d.ts +1 -1
- package/dist/http/sse-log.d.ts +12 -9
- package/dist/http/sse-log.js +1 -8
- package/dist/http/wire-types.d.ts +19 -28
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/key-resolver.d.ts +74 -19
- package/dist/key-resolver.js +65 -7
- package/dist/leader/leader.d.ts +6 -0
- package/dist/leader/leader.js +2 -1
- package/dist/leader/repair-wire.js +2 -1
- package/dist/leader/wire.d.ts +9 -1
- package/dist/leader/wire.js +10 -6
- package/dist/observability/fail-open.d.ts +9 -1
- package/dist/observability/fail-open.js +9 -1
- package/dist/observability/metrics.js +3 -1
- package/dist/observability/permission-rule-events.d.ts +1 -1
- package/dist/observability/permission-rule-events.js +1 -1
- package/dist/observability/run-terminal.d.ts +82 -9
- package/dist/observability/run-terminal.js +112 -9
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/observability/tool-trace.d.ts +2 -0
- package/dist/operator-ask.d.ts +29 -1
- package/dist/operator-ask.js +6 -0
- package/dist/parked-decide.d.ts +14 -1
- package/dist/parked-decide.js +8 -1
- package/dist/plugins/image-bake-store-sql.d.ts +0 -2
- package/dist/plugins/image-bake-store-sql.js +0 -5
- package/dist/plugins/retention-store-sql.js +1 -0
- package/dist/plugins/run-store-sql.d.ts +3 -2
- package/dist/plugins/schema-indexes.js +1 -0
- package/dist/plugins/session-policy-store-sql.d.ts +38 -7
- package/dist/plugins/session-policy-store-sql.js +73 -18
- package/dist/plugins/sql-driver.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +17 -6
- package/dist/plugins/store-backend.js +16 -5
- package/dist/plugins/tidb-pool.js +5 -4
- package/dist/read-face-posture.d.ts +25 -4
- package/dist/read-face-posture.js +9 -0
- package/dist/request-key-closure.js +2 -2
- package/dist/request-value-closure.d.ts +17 -5
- package/dist/request-value-closure.js +12 -0
- package/dist/router/route-orchestration.d.ts +11 -0
- package/dist/router/route-orchestration.js +7 -6
- package/dist/rules-consent.js +0 -11
- package/dist/run-local.js +4 -2
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +4 -4
- package/dist/runtime-caps-resolver.d.ts +4 -10
- package/dist/runtime-governance.d.ts +8 -11
- package/dist/runtime-governance.js +10 -41
- package/dist/sealed-key.d.ts +0 -40
- package/dist/sealed-key.js +0 -20
- package/dist/server-secret-env.d.ts +11 -12
- package/dist/session-policy-wire.d.ts +100 -12
- package/dist/session-policy-wire.js +71 -8
- package/dist/session-titler.d.ts +11 -0
- package/dist/session-titler.js +6 -2
- package/dist/sql-engine-posture.d.ts +2 -2
- package/dist/store-posture.d.ts +53 -0
- package/dist/store-posture.js +32 -0
- package/dist/task-settings.d.ts +34 -45
- package/dist/task-settings.js +46 -25
- package/dist/tool-approval.d.ts +51 -22
- package/dist/tool-approval.js +44 -8
- package/dist/tool-declaration-http-status.d.ts +12 -0
- package/dist/tool-declaration-http-status.js +12 -0
- package/dist/trace/core-keyset-guard.d.ts +7 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +5 -0
- package/dist/trace/project.d.ts +13 -7
- package/dist/trace/project.js +54 -6
- package/dist/trace/redact.d.ts +7 -0
- package/dist/trace/redact.js +153 -61
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/unattended-permission-mode.d.ts +110 -0
- package/dist/unattended-permission-mode.js +65 -0
- package/package.json +3 -3
- package/dist/boot/session-shell-gate-registry.d.ts +0 -39
- package/dist/boot/session-shell-gate-registry.js +0 -21
- package/dist/plugins/memory-session-policy-store.d.ts +0 -46
- package/dist/plugins/memory-session-policy-store.js +0 -52
- package/dist/roster-bound-http-status.d.ts +0 -6
- package/dist/roster-bound-http-status.js +0 -6
package/dist/http/admission.js
CHANGED
|
@@ -2,9 +2,9 @@ import { isThinkingLevel, expandTiers } from "@sema-agent/core";
|
|
|
2
2
|
import { isModelAllowlisted, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../model-select.js";
|
|
3
3
|
import { modelUnavailableMessage } from "../model-entry-refusal.js";
|
|
4
4
|
import { HttpError } from "../security.js";
|
|
5
|
-
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS,
|
|
5
|
+
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, SETTINGS_PERMISSION_MODE_WORDS, isSettingsPermissionMode, parseTaskSettings, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, taskSettingsKeyIssue } from "../task-settings.js";
|
|
6
6
|
import { taskBodyKeyIssue } from "../request-key-closure.js";
|
|
7
|
-
import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
7
|
+
import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
8
8
|
import { selfOrchestrationDenial } from "../task-workflow.js";
|
|
9
9
|
import { centerEntitlementSourceWired } from "../runtime-caps-resolver.js";
|
|
10
10
|
import { parseHooksConfig } from "../hooks/hook-runner.js";
|
|
@@ -157,13 +157,11 @@ export function createAdmission(ctx) {
|
|
|
157
157
|
sendError(res, 400, "request.field_invalid", "settings.permissions must be an object");
|
|
158
158
|
return null;
|
|
159
159
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
160
|
+
{
|
|
161
|
+
const bucketIssue = settingsPermissionBucketIssue(perms);
|
|
162
|
+
if (bucketIssue) {
|
|
163
|
+
sendError(res, 400, "request.field_invalid", bucketIssue.message, bucketIssue.fields);
|
|
164
|
+
return null;
|
|
167
165
|
}
|
|
168
166
|
}
|
|
169
167
|
const envRaw = st.env;
|
|
@@ -333,6 +331,13 @@ export function createAdmission(ctx) {
|
|
|
333
331
|
return null;
|
|
334
332
|
}
|
|
335
333
|
}
|
|
334
|
+
{
|
|
335
|
+
const inheritEnvIssue = inheritEnvFieldIssue(body);
|
|
336
|
+
if (inheritEnvIssue) {
|
|
337
|
+
sendError(res, 400, "request.field_invalid", inheritEnvIssue);
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
336
341
|
if (fileHistoryRestoreConflict(body)) {
|
|
337
342
|
sendError(res, 400, "request.field_conflict", FILE_HISTORY_DISABLED_NO_RESTORE);
|
|
338
343
|
return null;
|
|
@@ -7,6 +7,18 @@ import { createWallClockJumpGuard } from "../wall-clock-jump-guard.js";
|
|
|
7
7
|
import { hasServiceAuth } from "./dispatch.js";
|
|
8
8
|
import { gatedPrincipal } from "./principal-gate.js";
|
|
9
9
|
import { readinessOf, billableRefusalOf } from "./readiness.js";
|
|
10
|
+
const BLOCKED_SCALAR_KEYS = ["errorCode", "blockedBy", "field", "reason", "count", "cap", "index"];
|
|
11
|
+
function blockedScalars(body) {
|
|
12
|
+
const out = {};
|
|
13
|
+
if (body === null || typeof body !== "object")
|
|
14
|
+
return out;
|
|
15
|
+
for (const k of BLOCKED_SCALAR_KEYS) {
|
|
16
|
+
const v = body[k];
|
|
17
|
+
if (typeof v === "string" || (typeof v === "number" && Number.isFinite(v)))
|
|
18
|
+
out[k] = v;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
10
22
|
export function createApprovalSweeps(ctx) {
|
|
11
23
|
const { deps, resumeCheckpoint, mayDiscloseRowError } = ctx;
|
|
12
24
|
const denySweepClockGuard = createWallClockJumpGuard({
|
|
@@ -59,7 +71,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
59
71
|
continue;
|
|
60
72
|
}
|
|
61
73
|
await resumeCheckpoint(sessionId, "deny", "approval SLA expired — auto-denied", { decidedBy: "sla_timeout" }).then((r) => { if (r.status >= 400)
|
|
62
|
-
deps.logger?.warn?.("deny_sweep_resume_blocked", { sessionId, status: r.status,
|
|
74
|
+
deps.logger?.warn?.("deny_sweep_resume_blocked", { sessionId, status: r.status, ...blockedScalars(r.body) }); }, (e) => deps.logger?.warn?.("deny_sweep_resume_failed", { sessionId, err: redactHead(e instanceof Error ? e.message : String(e), 2000) }));
|
|
63
75
|
}
|
|
64
76
|
if (parkedSkipped > 0)
|
|
65
77
|
deps.logger?.info?.("deny_sweep_parked_skipped", { count: parkedSkipped });
|
package/dist/http/dispatch.d.ts
CHANGED
|
@@ -22,6 +22,24 @@ import type { FlatServiceDeps } from "./server.js";
|
|
|
22
22
|
* 凭证门、[ref] 的 parked 赎回席、以及 [ref] 的 consolidation 阀门启动期告警读同一句(手抄迟早
|
|
23
23
|
* 只改一处,而这条判据决定的是「无凭证部署上写口开不开」)。导出仅为最后一个消费点(main.ts)。 */
|
|
24
24
|
export declare function hasServiceAuth(config: ServiceConfig): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 🔴 S-672(test T-72 [ref];主会话裁 ①,分诊 [ref])—— 全局服务凭据门**之前**那几族只读路
|
|
27
|
+
* (`route-table.ts` 里 `position: "read-credential"` 的行)的**唯一**鉴权谓词:「服务凭据 ∨ 已验 principal」。
|
|
28
|
+
*
|
|
29
|
+
* 病:这几族(trace-usage / workflows / fleet)排在全局门之前,理由只有一条 —— 只读 `TRACE_TOKEN` 单凭据的调用方
|
|
30
|
+
* (registry 用量 / 轨迹中继)在全局门会被 401。可排在门前之后,每一族只剩**块内**自己那道门:fleet / workflows
|
|
31
|
+
* 只在 `REQUIRE_PRINCIPAL` 时要身份,trace 认 `gatedPrincipal`(BFF 形上就是调用方自己写的 principal 头)。于是
|
|
32
|
+
* 配了 service token、没开 REQUIRE_PRINCIPAL 的部署上,`POST /v1/tasks` 吃凭据,这三族读路对端口全开。
|
|
33
|
+
*
|
|
34
|
+
* 规则(一条,三族同读;fail-closed):
|
|
35
|
+
* · 部署**没配**任何 service 凭据(`hasServiceAuth` 假)⇒ 放行 —— 与全局门对 `/v1/tasks` 同判(run-local 形);
|
|
36
|
+
* · 否则要么出示了**服务凭据**(service token 单 / 多,或只读 `TRACE_TOKEN` —— 后者正是这几族在门前的理由),
|
|
37
|
+
* 要么带着**验过的** principal(registry 登录令牌 / direct-door 签名 principal 令牌;判据 = `verifiedIdentitySource`,
|
|
38
|
+
* 与 off-box operator 读面同一只)。调用方自己写的 principal 头不是凭据。
|
|
39
|
+
* 过了本门,各族块内的门(属主 scope / fleet-wide 上限 / `REQUIRE_PRINCIPAL` 的身份门)照旧 —— 本谓词只回答
|
|
40
|
+
* 「这个请求带没带这台部署认的凭据」,不回答「它能看谁的行」。
|
|
41
|
+
*/
|
|
42
|
+
export declare function readCredentialOk(req: IncomingMessage, config: ServiceConfig): boolean;
|
|
25
43
|
export declare function createDispatch(ctx: {
|
|
26
44
|
deps: FlatServiceDeps;
|
|
27
45
|
noteActivity: (url: string) => void;
|
package/dist/http/dispatch.js
CHANGED
|
@@ -3,14 +3,21 @@ import { setSsoPrincipal, ssoVerifiedPrincipal, setSsoScope, isDestructiveSessio
|
|
|
3
3
|
import { publicStoreProbeError } from "../store-live-probe.js";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
5
|
import { ROUTE_METHOD_ORDER } from "./route-ctx.js";
|
|
6
|
-
import {
|
|
6
|
+
import { READ_CREDENTIAL_ROUTES, PUBLIC_ROUTES, POST_CREDENTIAL_ROUTES, routeLabel, isBillableSubmitPath, isCredentialGatedRewrite, isMethodDispatchedSubmitPath, lookupRoute, isProbePath } from "./route-table.js";
|
|
7
7
|
import { sendJson, sendError, isSseResponse } from "./send.js";
|
|
8
8
|
import { readinessOf, healthReadinessKeys, sendReadyz, billableRefusalOf, sendBillableRefusal } from "./readiness.js";
|
|
9
|
-
import { authorized, systemFor, gatedPrincipal, sendPrincipalRefusal, sendUnauthorized } from "./principal-gate.js";
|
|
9
|
+
import { authorized, systemFor, gatedPrincipal, sendPrincipalRefusal, sendUnauthorized, verifiedIdentitySource } from "./principal-gate.js";
|
|
10
10
|
const PROCESS_STARTED_AT_MS = Date.now() - Math.round(process.uptime() * 1000);
|
|
11
11
|
export function hasServiceAuth(config) {
|
|
12
12
|
return Boolean(config.authToken) || Object.keys(config.authTokens ?? {}).length > 0;
|
|
13
13
|
}
|
|
14
|
+
export function readCredentialOk(req, config) {
|
|
15
|
+
if (!hasServiceAuth(config))
|
|
16
|
+
return true;
|
|
17
|
+
if (verifiedIdentitySource(req, config) !== undefined)
|
|
18
|
+
return true;
|
|
19
|
+
return config.traceToken !== undefined && config.traceToken !== "" && authorized(req, config.traceToken);
|
|
20
|
+
}
|
|
14
21
|
function cors(res, req, origins, principalHeader) {
|
|
15
22
|
if (!origins || origins.length === 0)
|
|
16
23
|
return;
|
|
@@ -44,7 +51,7 @@ export function createDispatch(ctx) {
|
|
|
44
51
|
};
|
|
45
52
|
deps.drainState.lastAttachEndedAt = () => lastAttachEndedAt;
|
|
46
53
|
}
|
|
47
|
-
async function dispatchSegment(rows, req, res, url, search, method, allow, ctx) {
|
|
54
|
+
async function dispatchSegment(rows, req, res, url, search, method, allow, ctx, admit) {
|
|
48
55
|
for (const row of rows) {
|
|
49
56
|
if (row.enabled !== undefined && !row.enabled(deps))
|
|
50
57
|
continue;
|
|
@@ -54,6 +61,10 @@ export function createDispatch(ctx) {
|
|
|
54
61
|
allow.push(m);
|
|
55
62
|
if (found.decl === null)
|
|
56
63
|
continue;
|
|
64
|
+
if (admit !== undefined && !admit(req)) {
|
|
65
|
+
sendUnauthorized(res, "credential");
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
57
68
|
await row.handler(req, res, { decl: found.decl, params: found.params, search }, ctx);
|
|
58
69
|
return true;
|
|
59
70
|
}
|
|
@@ -112,7 +123,7 @@ export function createDispatch(ctx) {
|
|
|
112
123
|
...(typeof deps.capabilities?.version === "string" ? { version: deps.capabilities.version } : {}),
|
|
113
124
|
...(deps.drainState?.draining ? { draining: true, ...(deps.drainState.since ? { drainingSince: deps.drainState.since } : {}), ...(deps.drainState.reason ? { drainReason: deps.drainState.reason } : {}) } : {}),
|
|
114
125
|
...(deps.drainState?.inflight ? { inflight: deps.drainState.inflight() } : {}),
|
|
115
|
-
...(deps.
|
|
126
|
+
...(deps.storePosture?.degradedReason !== undefined ? { durable: false, storeDegraded: true } : {}),
|
|
116
127
|
...healthReadinessKeys(readinessOf(deps)),
|
|
117
128
|
model: deps.config.model.id,
|
|
118
129
|
sessionBackend: deps.sessionStoreLabel ?? deps.config.sessionBackend,
|
|
@@ -204,7 +215,9 @@ export function createDispatch(ctx) {
|
|
|
204
215
|
}
|
|
205
216
|
}
|
|
206
217
|
}
|
|
207
|
-
if (await dispatchSegment(
|
|
218
|
+
if (await dispatchSegment(READ_CREDENTIAL_ROUTES, req, res, url, search, method, allow.pre, ctx, (r) => readCredentialOk(r, deps.config)))
|
|
219
|
+
return;
|
|
220
|
+
if (await dispatchSegment(PUBLIC_ROUTES, req, res, url, search, method, allow.pre, ctx))
|
|
208
221
|
return;
|
|
209
222
|
let source = null;
|
|
210
223
|
const anyServiceAuth = hasServiceAuth(deps.config);
|
|
@@ -5,6 +5,7 @@ import { cascadeConfig, runMeta } from "./run-meta.js";
|
|
|
5
5
|
import { resolveStreamApprovalGate } from "../tool-approval.js";
|
|
6
6
|
import { buildActiveRunConflict } from "./active-run-conflict.js";
|
|
7
7
|
import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
|
|
8
|
+
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
8
9
|
import { clearTurnActivity, readTurnActivityMs } from "../turn-activity.js";
|
|
9
10
|
import { recordResourceWindow } from "../resource-window.js";
|
|
10
11
|
export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
@@ -28,6 +29,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
|
|
|
28
29
|
const placementReject = await commitPlacement(deps, runStore, taskId, args.prepared.bindingIntent);
|
|
29
30
|
if (placementReject)
|
|
30
31
|
return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
|
|
32
|
+
commitSessionInheritEnv(deps, args.prepared.spec.sessionId, args.prepared.body);
|
|
31
33
|
if (deps.checkpointStore) {
|
|
32
34
|
await deps.checkpointStore.putCtx(sessionId, { body: args.prepared.body, memoryScope: args.prepared.auth?.memoryScope });
|
|
33
35
|
}
|
|
@@ -5,6 +5,31 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
7
7
|
import type { FlatServiceDeps } from "./server.js";
|
|
8
|
+
import type { LeaseRefusalAdmission } from "../fleet-lease.js";
|
|
9
|
+
/**
|
|
10
|
+
* S-866 ③ —— 租约拒绝的应答形({@link leaseRefusalOf} 的产物;`{status, body}` 交回形 + 镜像头用的 `retryAfterSec`)。
|
|
11
|
+
* 体键序固定 `error, errorCode, …`(冻结门 `FROZEN_RESULT_BODY` 按这个字面形锚)。
|
|
12
|
+
*/
|
|
13
|
+
export interface LeaseRefusal {
|
|
14
|
+
readonly status: 429 | 503;
|
|
15
|
+
readonly body: {
|
|
16
|
+
readonly error: string;
|
|
17
|
+
readonly errorCode: string;
|
|
18
|
+
readonly retryAfterSec: number;
|
|
19
|
+
readonly [extra: string]: unknown;
|
|
20
|
+
};
|
|
21
|
+
readonly retryAfterSec: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* S-866 ③ —— 一次被拒的租约准入 → HTTP 应答的**唯一**映射(三只门 {@link createRequestGuards} 里的 `leaseDenied` /
|
|
25
|
+
* `ownerLeaseDenied` / `ownerFleetLeaseDenied` 同调它;12 个调用点零改)。按拒因穷举(加一臂 = 编译红):
|
|
26
|
+
* · `budget_exhausted` ⇒ **429 `quota_exhausted`**(字节同 7.105.0 的路由发送形:`retryAfterSec` + 中心 E4 结构化字段逐字透传);
|
|
27
|
+
* · `lease_unavailable` ⇒ **503 `fleet.lease_unavailable`**(只在 `FLEET_LEASE_FAIL_CLOSED` 生效时出现):体 `{reason, retryAfterSec}`,
|
|
28
|
+
* `reason` ∈ `auth-rejected | unreachable`(中心拒了本引擎的凭证 / 凭证读不出 ;中心不可达 / 5xx / 超时 / 坏体)。可重试。
|
|
29
|
+
* 7.105.0 及以前三只门各写一份 `!adm.ok ⇒ 429 quota_exhausted`,且 resume 族那份不带 E4 字段(两形分歧,当年如实登记);
|
|
30
|
+
* 一只映射之后两形同体(resume 族的 429 体多出中心的 E4 字段,additive)。
|
|
31
|
+
*/
|
|
32
|
+
export declare function leaseRefusalOf(adm: LeaseRefusalAdmission): LeaseRefusal;
|
|
8
33
|
export declare function createRequestGuards(ctx: {
|
|
9
34
|
deps: FlatServiceDeps;
|
|
10
35
|
}): {
|
|
@@ -4,6 +4,27 @@ import { RUN_NOT_FOUND_MESSAGE } from "./route-ctx.js";
|
|
|
4
4
|
import { sendError } from "./send.js";
|
|
5
5
|
import { authorized, systemFor, gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal } from "./principal-gate.js";
|
|
6
6
|
const MAX_BODY = 8 * 1024 * 1024;
|
|
7
|
+
export function leaseRefusalOf(adm) {
|
|
8
|
+
switch (adm.reason) {
|
|
9
|
+
case "budget_exhausted":
|
|
10
|
+
return { status: 429, body: { error: "budget exhausted (quota lease)", errorCode: "quota_exhausted", retryAfterSec: adm.retryAfterSec, ...(adm.detail ?? {}) }, retryAfterSec: adm.retryAfterSec };
|
|
11
|
+
case "lease_unavailable":
|
|
12
|
+
return {
|
|
13
|
+
status: 503,
|
|
14
|
+
body: { error: "quota lease unavailable — this deployment enforces budgets fail-closed and could not confirm this caller's budget with the quota center, so nothing was run; retry after retryAfterSec seconds", errorCode: "fleet.lease_unavailable", reason: adm.cause, retryAfterSec: adm.retryAfterSec },
|
|
15
|
+
retryAfterSec: adm.retryAfterSec,
|
|
16
|
+
};
|
|
17
|
+
default: {
|
|
18
|
+
const unhandled = adm;
|
|
19
|
+
throw new Error(`lease refusal without an HTTP answer: ${JSON.stringify(unhandled)}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function sendLeaseRefusal(res, refusal) {
|
|
24
|
+
const { error, errorCode, ...extra } = refusal.body;
|
|
25
|
+
res.setHeader("retry-after", String(refusal.retryAfterSec));
|
|
26
|
+
sendError(res, refusal.status, errorCode, error, extra);
|
|
27
|
+
}
|
|
7
28
|
export function createRequestGuards(ctx) {
|
|
8
29
|
const { deps } = ctx;
|
|
9
30
|
function quotaExceeded(req, res) {
|
|
@@ -36,7 +57,8 @@ export function createRequestGuards(ctx) {
|
|
|
36
57
|
if (adm.ok)
|
|
37
58
|
return undefined;
|
|
38
59
|
deps.metrics?.inc("fleet_lease_rejected_total");
|
|
39
|
-
|
|
60
|
+
const refusal = leaseRefusalOf(adm);
|
|
61
|
+
return { status: refusal.status, body: refusal.body };
|
|
40
62
|
}
|
|
41
63
|
async function ownerLeaseDenied(res, owner) {
|
|
42
64
|
if (!deps.fleetLease || !owner)
|
|
@@ -45,8 +67,7 @@ export function createRequestGuards(ctx) {
|
|
|
45
67
|
if (adm.ok)
|
|
46
68
|
return false;
|
|
47
69
|
deps.metrics?.inc("fleet_lease_rejected_total");
|
|
48
|
-
res
|
|
49
|
-
sendError(res, 429, "quota_exhausted", "budget exhausted (quota lease)", { retryAfterSec: adm.retryAfterSec, ...(adm.detail ?? {}) });
|
|
70
|
+
sendLeaseRefusal(res, leaseRefusalOf(adm));
|
|
50
71
|
return true;
|
|
51
72
|
}
|
|
52
73
|
function ownerQuotaDenied(res, owner) {
|
|
@@ -68,8 +89,7 @@ export function createRequestGuards(ctx) {
|
|
|
68
89
|
if (adm.ok)
|
|
69
90
|
return false;
|
|
70
91
|
deps.metrics?.inc("fleet_lease_rejected_total");
|
|
71
|
-
res
|
|
72
|
-
sendError(res, 429, "quota_exhausted", "budget exhausted (quota lease)", { retryAfterSec: adm.retryAfterSec, ...(adm.detail ?? {}) });
|
|
92
|
+
sendLeaseRefusal(res, leaseRefusalOf(adm));
|
|
73
93
|
return true;
|
|
74
94
|
}
|
|
75
95
|
async function usageWindowDenialFor(key) {
|
|
@@ -24,7 +24,7 @@ import type { createRequestGuards } from "./request-guards.js";
|
|
|
24
24
|
import type { FlatServiceDeps } from "./server.js";
|
|
25
25
|
export declare function createResumeLegs(ctx: {
|
|
26
26
|
deps: FlatServiceDeps;
|
|
27
|
-
runnerFor:
|
|
27
|
+
runnerFor: FlatServiceDeps["runnerFor"];
|
|
28
28
|
} & RunRegistry & RouteHelpers & Pick<RouteLegs, "prepareSpec" | "finalizeTaskResult"> & Pick<ReturnType<typeof createRequestGuards>, "ownerCostQuotaDenied" | "ownerFleetLeaseDenied" | "usageWindowDenialFor">): {
|
|
29
29
|
routeCtxBase: RouteCtxBase;
|
|
30
30
|
resumeCheckpoint: (sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req?: IncomingMessage, answer?: QuestionAnswer, binding?: DecideBinding, onResumeCommitted?: (grant: {
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -37,6 +37,7 @@ import { createCapabilitiesLocal } from "./routes/capabilities.js";
|
|
|
37
37
|
import { gatedPrincipal, explicitOperatorOk } from "./principal-gate.js";
|
|
38
38
|
import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
|
|
39
39
|
import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
|
|
40
|
+
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
40
41
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
41
42
|
import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
|
|
42
43
|
import { httpErrorCode } from "./send.js";
|
|
@@ -265,6 +266,7 @@ export function createResumeLegs(ctx) {
|
|
|
265
266
|
sessionFaceOf: deps.sessionStorage?.sessionFaceOf ? deps.sessionStorage.sessionFaceOf.bind(deps.sessionStorage) : sessionFaceUnavailable,
|
|
266
267
|
...(deps.parkedKnownAgentTypes ? { knownAgentTypes: deps.parkedKnownAgentTypes } : {}),
|
|
267
268
|
...(deps.parkedReviveInheritedGate ? { rebuildInheritedGate: deps.parkedReviveInheritedGate } : {}),
|
|
269
|
+
...(deps.toolApproval ? { approverSeatFor: deps.toolApproval.boundUnattendedAsk } : {}),
|
|
268
270
|
...(deps.logger ? { warn: parkWarn } : {}),
|
|
269
271
|
}, parkedReq, { handle: origin.handle, row: origin.row }));
|
|
270
272
|
void ctxPromise.catch(() => { });
|
|
@@ -297,7 +299,7 @@ export function createResumeLegs(ctx) {
|
|
|
297
299
|
if (!ctx)
|
|
298
300
|
return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
|
|
299
301
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
300
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
302
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
301
303
|
if (!resolved.ok)
|
|
302
304
|
return resolved.answer;
|
|
303
305
|
const { spec, engineNotices } = resolved;
|
|
@@ -329,14 +331,18 @@ export function createResumeLegs(ctx) {
|
|
|
329
331
|
}
|
|
330
332
|
const ONE_SHOT_HISTORY_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles"];
|
|
331
333
|
const ONE_SHOT_HISTORY_STRIP = Object.fromEntries(ONE_SHOT_HISTORY_KEYS.map((k) => [k, undefined]));
|
|
332
|
-
const resolveResumeSpec = async (
|
|
334
|
+
const resolveResumeSpec = async (ctx, req, auth) => {
|
|
333
335
|
const engineNotices = [];
|
|
334
|
-
const spec = await deps.resolveSpec({ ...body, ...ONE_SHOT_HISTORY_STRIP }, req, auth, {
|
|
336
|
+
const spec = await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_HISTORY_STRIP }, req, auth, {
|
|
337
|
+
leg: "resume",
|
|
338
|
+
onNotice: (n) => engineNotices.push(n),
|
|
339
|
+
...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
|
|
340
|
+
});
|
|
335
341
|
return { spec, engineNotices };
|
|
336
342
|
};
|
|
337
|
-
const resolveResumeSpecOrBlocked = async (
|
|
343
|
+
const resolveResumeSpecOrBlocked = async (ctx, req, auth) => {
|
|
338
344
|
try {
|
|
339
|
-
const { spec, engineNotices } = await resolveResumeSpec(
|
|
345
|
+
const { spec, engineNotices } = await resolveResumeSpec(ctx, req, auth);
|
|
340
346
|
return { ok: true, spec, engineNotices };
|
|
341
347
|
}
|
|
342
348
|
catch (e) {
|
|
@@ -427,7 +433,7 @@ export function createResumeLegs(ctx) {
|
|
|
427
433
|
if (deps.toolApproval && taskId) {
|
|
428
434
|
const toolApproval = deps.toolApproval;
|
|
429
435
|
resumeTaskConfig.onAsk = approvalLeg.windowZero
|
|
430
|
-
?
|
|
436
|
+
? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId })
|
|
431
437
|
: toolApproval.boundAsk({
|
|
432
438
|
owner: principal ?? null,
|
|
433
439
|
taskId,
|
|
@@ -491,6 +497,7 @@ export function createResumeLegs(ctx) {
|
|
|
491
497
|
deps.modelUsage?.register(taskId);
|
|
492
498
|
resumeManifestLease = deps.promptManifests?.register(taskId) ?? 0;
|
|
493
499
|
}
|
|
500
|
+
commitSessionInheritEnv(deps, sessionId, resumeCtx.ctx.body);
|
|
494
501
|
if (args.onResumeCommitted)
|
|
495
502
|
await args.onResumeCommitted();
|
|
496
503
|
let unregisterEngineNotice;
|
|
@@ -1061,7 +1068,7 @@ export function createResumeLegs(ctx) {
|
|
|
1061
1068
|
return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
|
|
1062
1069
|
const ctx = ctxRead.ctx;
|
|
1063
1070
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1064
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
1071
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1065
1072
|
if (!resolved.ok)
|
|
1066
1073
|
return resolved.answer;
|
|
1067
1074
|
const { spec, engineNotices } = resolved;
|
|
@@ -1093,7 +1100,7 @@ export function createResumeLegs(ctx) {
|
|
|
1093
1100
|
if (windowDenial)
|
|
1094
1101
|
return { status: windowDenial.status, body: windowDenial.body };
|
|
1095
1102
|
const auth = { sessionId, principal, memoryScope: ctx.memoryScope };
|
|
1096
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
1103
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1097
1104
|
if (!resolved.ok)
|
|
1098
1105
|
return resolved.answer;
|
|
1099
1106
|
const { spec, engineNotices } = resolved;
|
|
@@ -1167,7 +1174,7 @@ export function createResumeLegs(ctx) {
|
|
|
1167
1174
|
return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
|
|
1168
1175
|
const ctx = ctxRead.ctx;
|
|
1169
1176
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1170
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
1177
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1171
1178
|
if (!resolved.ok)
|
|
1172
1179
|
return resolved.answer;
|
|
1173
1180
|
const { spec, engineNotices } = resolved;
|
|
@@ -1267,7 +1274,8 @@ export function createResumeLegs(ctx) {
|
|
|
1267
1274
|
}
|
|
1268
1275
|
const approvedMode = decision === "approve" && effMode === "plan" ? (permissionModeAfter ?? "default") : undefined;
|
|
1269
1276
|
const resumeBody = approvedMode === undefined ? ctx.body : { ...ctx.body, permissionMode: approvedMode };
|
|
1270
|
-
const
|
|
1277
|
+
const rebuildCtx = { ...ctx, body: resumeBody };
|
|
1278
|
+
const resolved = await resolveResumeSpecOrBlocked(rebuildCtx, req, auth);
|
|
1271
1279
|
if (!resolved.ok)
|
|
1272
1280
|
return resolved.answer;
|
|
1273
1281
|
const { spec, engineNotices } = resolved;
|
|
@@ -1279,7 +1287,7 @@ export function createResumeLegs(ctx) {
|
|
|
1279
1287
|
...(editedPlan !== undefined ? { editedPlan } : {}),
|
|
1280
1288
|
...(reason ? { reason } : {}),
|
|
1281
1289
|
};
|
|
1282
|
-
const out = await driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx: { sessionId, ctx:
|
|
1290
|
+
const out = await driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx: { sessionId, ctx: rebuildCtx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
|
|
1283
1291
|
return out;
|
|
1284
1292
|
}
|
|
1285
1293
|
const routeCtxBase = {
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -147,6 +147,15 @@ export interface CheckpointCtx {
|
|
|
147
147
|
body: TaskRequestBody;
|
|
148
148
|
/** 提交时这条会话绑定的记忆域(重建 `RequestAuth` 用);缺席 = 没有记忆面。 */
|
|
149
149
|
memoryScope?: string;
|
|
150
|
+
/**
|
|
151
|
+
* 🔴 S-877 候裁 18(codex r3 F1)—— 这条会话的 run 是**无人值守腿**铸的(今天唯一的写者 = leader worker 的 durable 块,
|
|
152
|
+
* `leader/wire.ts`),**服务端写、不来自请求体**(`body` 是提交体;本位是它的兄弟键,客户端够不着)。
|
|
153
|
+
* 续跑家族据此请 `resolveSpec` 带 `unattendedLeg: true`,由它按续跑 Runner 的同名座经 `createUnattendedLeg` / `withUnattendedLeg`
|
|
154
|
+
* 重挂整条腿(模式 = 旋钮、写面 roots、模式释放写保护时的写保护拒层)—— 拒层是活闭包、进不了 checkpoint,而 core 在 spec 不说
|
|
155
|
+
* 模式时取 checkpoint 记下的那一个(`seat ?? recorded`),不重挂 = 续跑落 bypass 而拒层缺席。写回口(`DriveResumeArgs.resumeCtx`)
|
|
156
|
+
* 传原 ctx / 其展开 ⇒ 标随行。缺席 = 不是无人值守腿(HTTP 提交腿;7.106.0 前落盘的行 —— 那时这些腿不铸模式,checkpoint 里也没有)。
|
|
157
|
+
*/
|
|
158
|
+
unattendedLeg?: true;
|
|
150
159
|
}
|
|
151
160
|
/** B4①(命名化):`driveResumeIntoRunLog` 的入参形(resume 家族共用)——原为 9 成员内联匿名对象。 */
|
|
152
161
|
export interface DriveResumeArgs {
|
|
@@ -55,7 +55,17 @@ import type { FlatServiceDeps } from "./server.js";
|
|
|
55
55
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
56
56
|
import type { RouteDecl, RouteDispatchDecl, RouteMatch, RouteCtx, RouteMethod } from "./route-ctx.js";
|
|
57
57
|
/** 一只域 handler 相对**全局服务凭据门**的位置。门前 = 未鉴权可达(每一只都要单独论证),门后 = 先过凭据。 */
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
|
+
* 一行路由在全局服务凭据门的哪一侧、吃哪道门(闭集,三员;S-672 起由两员拆成三员):
|
|
60
|
+
* · `"read-credential"` —— 门**之前**,但过**一只**读路谓词(`dispatch.ts` 的 `readCredentialOk`:部署配了 service
|
|
61
|
+
* 凭据时要「服务凭据(含只读 TRACE_TOKEN)∨ 已验 principal」,没配时与全局门同样放行)。只读路才能坐这一段:
|
|
62
|
+
* 它们在门前只为收只读 TRACE_TOKEN 单凭据的调用方,不是为了对端口全开;
|
|
63
|
+
* · `"public"` —— 门之前、**不吃**任何凭据门:按裁定匿名可达的面(今天只有 A2A agent card,R-11「公网匿名可发现」,
|
|
64
|
+
* 零存量披露);
|
|
65
|
+
* · `"post-credential"` —— 全局服务凭据门(+ 无凭据写门 / 排空 503 / roster-pending 503 / 破坏性会话写门)之后。
|
|
66
|
+
* 修前是 `"pre-credential" | "post-credential"` 两员:门前四族一律不吃任何门,三族只读路因此对端口全开(T-72)。
|
|
67
|
+
*/
|
|
68
|
+
export type RoutePosition = "read-credential" | "public" | "post-credential";
|
|
59
69
|
export interface RouteRow {
|
|
60
70
|
/** 域入口(`routes/*.ts` 的 `handleX`)。签名一致性是编译期钉:入口漂了形 = 编译红,不是运行时静默 404。
|
|
61
71
|
*
|
|
@@ -79,9 +89,10 @@ export interface RouteRow {
|
|
|
79
89
|
readonly enabled?: (deps: FlatServiceDeps) => boolean;
|
|
80
90
|
}
|
|
81
91
|
export declare const ROUTES: readonly RouteRow[];
|
|
82
|
-
/**
|
|
83
|
-
*
|
|
84
|
-
export declare const
|
|
92
|
+
/** 三段(`dispatch.ts` 的分派循环按 read-credential → public → 全局门 → post-credential 各跑一段)。段内序 = 本表行序;
|
|
93
|
+
* 「三段各自连续、按这个次序排」由 `test/route-order-freeze.test.ts` 钉住。 */
|
|
94
|
+
export declare const READ_CREDENTIAL_ROUTES: readonly RouteRow[];
|
|
95
|
+
export declare const PUBLIC_ROUTES: readonly RouteRow[];
|
|
85
96
|
export declare const POST_CREDENTIAL_ROUTES: readonly RouteRow[];
|
|
86
97
|
/**
|
|
87
98
|
* **探针路径**(k8s / 编排器 / 抓取器的常态高频采样):不计 admission inflight、不推活性时钟(`http/server.ts`
|
package/dist/http/route-table.js
CHANGED
|
@@ -38,10 +38,10 @@ function row(spec) {
|
|
|
38
38
|
return spec;
|
|
39
39
|
}
|
|
40
40
|
export const ROUTES = [
|
|
41
|
-
row({ handler: handleTraceUsage, decls: TRACE_USAGE_ROUTES, position: "
|
|
42
|
-
row({ handler: handleWorkflows, decls: WORKFLOWS_ROUTES, position: "
|
|
43
|
-
row({ handler: handleFleet, decls: FLEET_ROUTES, position: "
|
|
44
|
-
row({ handler: handleA2aServe, decls: A2A_SERVE_ROUTES, position: "
|
|
41
|
+
row({ handler: handleTraceUsage, decls: TRACE_USAGE_ROUTES, position: "read-credential", label: "trace-usage" }),
|
|
42
|
+
row({ handler: handleWorkflows, decls: WORKFLOWS_ROUTES, position: "read-credential", label: "workflows" }),
|
|
43
|
+
row({ handler: handleFleet, decls: FLEET_ROUTES, position: "read-credential", label: "fleet" }),
|
|
44
|
+
row({ handler: handleA2aServe, decls: A2A_SERVE_ROUTES, position: "public", label: "a2a-serve" }),
|
|
45
45
|
row({ handler: handleCapabilities, decls: CAPABILITIES_ROUTES, position: "post-credential", label: "capabilities" }),
|
|
46
46
|
row({ handler: handleSideQuery, decls: SIDE_QUERY_ROUTES, position: "post-credential", label: "side-query" }),
|
|
47
47
|
row({ handler: handleTasks, decls: TASKS_ROUTES, position: "post-credential", label: "tasks" }),
|
|
@@ -78,7 +78,8 @@ export const ROUTES = [
|
|
|
78
78
|
row({ handler: handleAttachments, decls: ATTACHMENTS_ROUTES, position: "post-credential", label: "attachments" }),
|
|
79
79
|
row({ handler: handleNotifyWake, decls: NOTIFY_WAKE_ROUTES, position: "post-credential", label: "notify-wake" }),
|
|
80
80
|
];
|
|
81
|
-
export const
|
|
81
|
+
export const READ_CREDENTIAL_ROUTES = ROUTES.filter((r) => r.position === "read-credential");
|
|
82
|
+
export const PUBLIC_ROUTES = ROUTES.filter((r) => r.position === "public");
|
|
82
83
|
export const POST_CREDENTIAL_ROUTES = ROUTES.filter((r) => r.position === "post-credential");
|
|
83
84
|
const PIPELINE_ROUTES = [
|
|
84
85
|
{ id: "health", path: "/health", methods: ["GET"] },
|
|
@@ -347,7 +347,6 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
347
347
|
...(s.mandate !== undefined ? { mandate: s.mandate } : {}),
|
|
348
348
|
...(s.denialLimitFallback !== undefined ? { denialLimitFallback: s.denialLimitFallback } : {}),
|
|
349
349
|
...(s.origin !== undefined ? { origin: s.origin } : {}),
|
|
350
|
-
...(s.ruleStoreUnreadable !== undefined ? { ruleStoreUnreadable: s.ruleStoreUnreadable } : {}),
|
|
351
350
|
objective,
|
|
352
351
|
input,
|
|
353
352
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { expandTiers, reasoningEffortLevelsOf, routePairingStatus } from "@sema-agent/core";
|
|
2
2
|
import { isModelAllowlisted, selectableCatalog } from "../../model-select.js";
|
|
3
|
-
import { resolveModelApiKey } from "../../key-resolver.js";
|
|
4
|
-
import {
|
|
3
|
+
import { modelKeyPoisonOf, resolveModelApiKey } from "../../key-resolver.js";
|
|
4
|
+
import { projectSealedKeyCapability } from "../../sealed-key.js";
|
|
5
5
|
import { cwdHonored, deviceCwdHonored } from "../../task-cwd.js";
|
|
6
|
-
import {
|
|
6
|
+
import { taskWriteFaceRefusal } from "../../hosted-posture.js";
|
|
7
7
|
import { autonomyOverrides } from "../../runtime-governance.js";
|
|
8
8
|
import { DEVICE_PROTOCOL_VERSION, DEVICE_WS_PATH } from "../../device-ws-protocol.js";
|
|
9
9
|
import { a2aInjectionHonored } from "../../task-a2a.js";
|
|
@@ -16,9 +16,10 @@ import { fileHistoryCaptureOf } from "../../file-history-capture.js";
|
|
|
16
16
|
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
17
17
|
import { projectMemoryEngineCapability } from "../../memory-posture.js";
|
|
18
18
|
import { projectWriteProtectionCapability } from "../../write-protection.js";
|
|
19
|
-
import {
|
|
19
|
+
import { projectStoreCapability } from "../../store-posture.js";
|
|
20
20
|
import { projectWebSearchCapability } from "../../plugins/web-search.js";
|
|
21
21
|
import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
|
|
22
|
+
import { gateCompositeMembers } from "../../capabilities/composite-members.js";
|
|
22
23
|
import { projectApprovalsStreamLiveCapability } from "../../capabilities/approvals-push.js";
|
|
23
24
|
import { workflowModelAllowlistFor, selfOrchestrationDenial } from "../../task-workflow.js";
|
|
24
25
|
import { centerEntitlementSourceWired } from "../../runtime-caps-resolver.js";
|
|
@@ -27,7 +28,7 @@ import { coercePermissionModeStrict, SETTINGS_PERMISSION_MODE_WORDS } from "../.
|
|
|
27
28
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
28
29
|
import { sessionBackgroundFacesAvailable } from "./background.js";
|
|
29
30
|
function routePairingOf(deps, name, m) {
|
|
30
|
-
if (
|
|
31
|
+
if (modelKeyPoisonOf(name, deps.config.modelApiKeyEnv ?? {}, process.env, deps.config.modelApiKeys ?? {}) !== undefined)
|
|
31
32
|
return "unknown";
|
|
32
33
|
const key = resolveModelApiKey(name, deps.config.modelApiKeyEnv ?? {}, process.env, deps.config.modelApiKeys ?? {});
|
|
33
34
|
return routePairingStatus(deps.routeJudgeBrain ?? {}, m, key !== undefined ? { apiKey: key } : undefined);
|
|
@@ -65,11 +66,11 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
const entitlementSource = centerEntitlementSourceWired(deps.config);
|
|
68
|
-
const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config),
|
|
69
|
+
const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config), localDeny: deps.config.permissionsDisableAutoMode });
|
|
69
70
|
if (autoModeFault !== undefined)
|
|
70
71
|
deps.logger?.warn("auto_mode_face_resolver_fault", { fault: autoModeFault });
|
|
71
72
|
const rulesLaneReachable = Boolean(deps.ruleConsent) && gatedPrincipal(req, deps.config) !== undefined;
|
|
72
|
-
sendJson(res, 200, {
|
|
73
|
+
sendJson(res, 200, gateCompositeMembers({
|
|
73
74
|
...deps.capabilities,
|
|
74
75
|
asyncRuns: Boolean(deps.runStore),
|
|
75
76
|
artifacts: Boolean(deps.runStore),
|
|
@@ -88,7 +89,8 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
88
89
|
memory: false,
|
|
89
90
|
memoryWrite: false,
|
|
90
91
|
memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
|
|
91
|
-
|
|
92
|
+
store: projectStoreCapability(deps.storePosture ?? { declared: deps.config.dbBackend, backend: "memory" }, deps.sqlEngineFacts?.()),
|
|
93
|
+
centerWiring: deps.centerWiring?.(gatedPrincipal(req, deps.config)),
|
|
92
94
|
webSearch: projectWebSearchCapability(deps.webSearchProvider),
|
|
93
95
|
sessionBackgroundable: Boolean(deps.runStore),
|
|
94
96
|
runMemoryCaptureOptOut: Boolean(deps.runStore),
|
|
@@ -125,7 +127,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
125
127
|
accepted: true,
|
|
126
128
|
classifierSeat: deps.autoModeFace?.seatMounted === true,
|
|
127
129
|
entitlementSource,
|
|
128
|
-
intentArming:
|
|
130
|
+
intentArming: true,
|
|
129
131
|
...autoMode,
|
|
130
132
|
},
|
|
131
133
|
readFace: deps.config.readFace ?? null,
|
|
@@ -141,7 +143,6 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
141
143
|
projectContext: cwdHonored(deps.config),
|
|
142
144
|
callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
|
|
143
145
|
taskWriteFaceOpen: taskWriteFaceRefusal(deps.config) === undefined && autonomyOverrides(deps.config.autonomy).writeFace !== "roots",
|
|
144
|
-
taskApproverPosture: taskApproverPostureHonored(deps.config),
|
|
145
146
|
executionLane: projectExecutionLaneCapability(deps.config),
|
|
146
147
|
mcpInjection: mcpInjectionHonored(deps.config),
|
|
147
148
|
a2a: true,
|
|
@@ -200,7 +201,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
200
201
|
manualCompact: Boolean(deps.runStore),
|
|
201
202
|
configCatalog: deps.config.configCatalogEnabled === true && deps.config.operatorPrincipals.length > 0,
|
|
202
203
|
sealedKey: projectSealedKeyCapability(deps.sealedKeyPublic),
|
|
203
|
-
});
|
|
204
|
+
}));
|
|
204
205
|
return;
|
|
205
206
|
}
|
|
206
207
|
case "capability-mcp": {
|
|
@@ -2,7 +2,7 @@ import { DOMAINS } from "@sema-agent/settings-schema";
|
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { assertNever } from "../route-ctx.js";
|
|
5
|
-
import { configKnobTable } from "../../config.js";
|
|
5
|
+
import { buildRetiredEnvWire, configKnobTable } from "../../config.js";
|
|
6
6
|
import { buildConfigCatalog } from "../../config-catalog.js";
|
|
7
7
|
export const CONFIG_CATALOG_PATH = "/v1/config/catalog";
|
|
8
8
|
const OPERATOR_ONLY_MESSAGE = "the config catalog 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)";
|
|
@@ -27,6 +27,7 @@ export async function handleConfigCatalog(req, res, match, ctx) {
|
|
|
27
27
|
knobs: configKnobTable(),
|
|
28
28
|
centerDomains: DOMAINS,
|
|
29
29
|
centerManagedKeys: deps.configCenterManagedKeys?.(),
|
|
30
|
+
retired: buildRetiredEnvWire(),
|
|
30
31
|
}));
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildPostureKnobReading } from "../../posture-source.js";
|
|
2
2
|
import { buildReadFaceWiring } from "../../read-face-posture.js";
|
|
3
|
+
import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
|
|
3
4
|
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
4
5
|
import { sendJson, sendError } from "../send.js";
|
|
5
6
|
import { gatedPrincipal, explicitOperatorOk, isLoopbackAddress, verifiedIdentitySource } from "../principal-gate.js";
|
|
@@ -104,6 +105,7 @@ export async function handleDiagnostics(req, res, match, ctx) {
|
|
|
104
105
|
sqlEngine: deps.sqlEngineFacts?.() ?? null,
|
|
105
106
|
writeProtection: deps.writeProtection ?? null,
|
|
106
107
|
readFace: buildReadFaceWiring(deps.config),
|
|
108
|
+
executionLane: projectExecutionLaneCapability(deps.config),
|
|
107
109
|
configApply: deps.configApplyState?.() ?? null,
|
|
108
110
|
mcpRevocations: deps.mcpRevocationState?.() ?? null,
|
|
109
111
|
serverGates: buildServerWiringGates({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* [ref] A9 域模块 —— MF-Fleet 实时舰队流。
|
|
3
3
|
*
|
|
4
|
-
* 路由:`GET /v1/fleet/stream`(SSE PUSH:连上先快照,之后每条 per-row delta)
|
|
5
|
-
* (
|
|
4
|
+
* 路由:`GET /v1/fleet/stream`(SSE PUSH:连上先快照,之后每条 per-row delta)。跑在服务凭据门**之前**的
|
|
5
|
+
* 读路谓词段(S-672:先过 `dispatch.ts` 的 `readCredentialOk`「服务凭据 ∨ 已验 principal」,再按 principal 自门,
|
|
6
|
+
* 与 workflow 只读路由同段)。本域零可变状态。
|
|
6
7
|
*/
|
|
7
8
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
8
9
|
import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
|
|
@@ -420,7 +420,6 @@ async function streamBakeEvents(req, res, bakes, bakeId, staleMs) {
|
|
|
420
420
|
return bake ? { status: bake.status, updatedAt: bake.updatedAt } : undefined;
|
|
421
421
|
},
|
|
422
422
|
getEvents: (id, after) => bakes.getEvents(id, after),
|
|
423
|
-
retainedFrom: (id) => bakes.retainedFrom(id),
|
|
424
423
|
formatEvent: (ev) => ({
|
|
425
424
|
id: ev.seq,
|
|
426
425
|
event: ev.kind,
|