@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
|
@@ -54,7 +54,11 @@ export interface FleetTaskRow {
|
|
|
54
54
|
* 🔴 与 `name` 分域:`name` 是**显示名**(源 `description ?? name`,模型自由文本);本键是**身份键**,
|
|
55
55
|
* 消费端按串相等把「@某个名字」绑到这一行(同类型两个子代在 `agentType` 上不可分,在本键上可分)。
|
|
56
56
|
* ADDITIVE / tolerate-absent:**缺席 = 一次匿名委派**(不是「不知道它叫什么」)—— 7.25.0 之前本键在本仓
|
|
57
|
-
*
|
|
57
|
+
* 没有任何源,恒缺席。**VERBATIM,不过 `redactSecrets`**(S-546 B6①:本注此前写「与 `name` 同一道边界,emit 前
|
|
58
|
+
* redact」,而三处铸点从本键到货起就是原样投 —— 见本文件 spawn / tick / terminal 三臂的行注与
|
|
59
|
+
* `trace/core-keyset-guard.ts` 的 `spawnName` 记账):它是**身份键**,消费端按串相等绑行,脱敏会把两个合法队友名
|
|
60
|
+
* 改写成同一串;出生点校验过的字符集里也没有可脱敏的形。残余(已披露)= 有人把真凭据当队友名写进
|
|
61
|
+
* `Agent({name})`,那一串原样上 fleet 面。展示名 `name` 照旧脱敏。 */
|
|
58
62
|
agentName?: string;
|
|
59
63
|
/** Owning principal (= the run's scope). The `/v1/fleet/stream` route filters to the caller's scope (owner-gated)
|
|
60
64
|
* unless the caller is fleet-wide (operator / trace-token). NOT rendered — a tenant-isolation field. */
|
package/dist/fleet-client.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fleet worker-side wiring(announce/heartbeat + usage 批报;契约 = registry-core 0.1.48+
|
|
3
3
|
* `/fleet`,center 端点 = sema-registry `/api/fleet/*`)。设计要点:
|
|
4
|
-
* - **可选分层(D3)
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* - **可选分层(D3),两条环各自有门(S-866 ④)**:客户端在中心车道配齐时启动(`centerLaneConfigured` —— baseUrl + 声明了
|
|
5
|
+
* 凭证 + worker,与租约管理器、用量累加器**同一只**谓词);**用量环**只要这三件;**公告环**(announce / 心跳 / 注销)另要
|
|
6
|
+
* `FLEET_ADVERTISE_ADDRESS` 显式声明(可达性是运维的话,不自猜:k8s pod IP / docker alias / 127.0.0.1:port 只有部署环境自己
|
|
7
|
+
* 知道)。不配中心的部署零行为变化;配了中心而不公告的引擎只报用量、不注册(7.105.0 及以前它连用量都不报)。
|
|
7
8
|
* - **announce=注册=心跳同形状幂等 upsert**:boot 后(listen 之后)POST 一次 + 每 heartbeatMs 续。
|
|
8
9
|
* `ready` / `draining` 两字段由**同一只**就绪谓词的同一次读铸(S-740:`http/readiness.ts` 的 `readinessOf`,
|
|
9
10
|
* 与 `/health.ready`、`GET /readyz`、计费提交门同源):`ready = verdict.ready`(= roster 已落 ∧ ¬draining),
|
|
@@ -74,8 +75,9 @@ export interface FleetClientOpts {
|
|
|
74
75
|
worker: string;
|
|
75
76
|
};
|
|
76
77
|
instanceId: string;
|
|
77
|
-
/** Reachable base URL of THIS instance — the operator's explicit declaration (`FLEET_ADVERTISE_ADDRESS`).
|
|
78
|
-
|
|
78
|
+
/** Reachable base URL of THIS instance — the operator's explicit declaration (`FLEET_ADVERTISE_ADDRESS`).
|
|
79
|
+
* S-866 ④:**缺席 = 公告环不装**(不注册、不心跳、不注销;`announceNow` 是空操作),用量环照装。 */
|
|
80
|
+
advertiseAddress?: string | undefined;
|
|
79
81
|
version: string;
|
|
80
82
|
/** S-740:就绪判(装配层接 `readinessOf(deployment)` —— HTTP 面读的同一个 deps 组)。每条公告现读**一次**,
|
|
81
83
|
* `ready` 与 `draining` 两字段都由这一次读铸(不会撕裂成 `ready:true` + `draining:true`)。 */
|
|
@@ -104,22 +106,25 @@ export interface FleetClient {
|
|
|
104
106
|
stop: () => Promise<void>;
|
|
105
107
|
}
|
|
106
108
|
/**
|
|
107
|
-
* Start the worker-side fleet loops. Caller has already gated on
|
|
108
|
-
*
|
|
109
|
+
* Start the worker-side fleet loops. Caller has already gated on the center lane (startFleetClientFromEnv below).
|
|
110
|
+
* The announce loop runs iff `advertiseAddress` is present; the usage loop iff `usage` is present (S-866 ④: the two
|
|
111
|
+
* loops no longer share one gate).
|
|
109
112
|
*/
|
|
110
113
|
export declare function startFleetClient(opts: FleetClientOpts): FleetClient;
|
|
111
114
|
/**
|
|
112
|
-
* Env/config gate (D3
|
|
113
|
-
* + worker — S-651: declared, not "currently readable": a token
|
|
114
|
-
* client, every request re-reads it)
|
|
115
|
-
*
|
|
116
|
-
*
|
|
115
|
+
* Env/config gate (D3 可选分层;S-866 ④ 两条环各自有门): start when the center lane is configured —
|
|
116
|
+
* {@link centerLaneConfigured}(baseUrl + a DECLARED credential + worker — S-651: declared, not "currently readable": a token
|
|
117
|
+
* file that is unreadable right now still starts the client, every request re-reads it), the SAME predicate the lease
|
|
118
|
+
* manager and the usage accumulator use. The usage loop needs nothing more; the announce loop additionally needs the
|
|
119
|
+
* operator's explicit reachable address (`FLEET_ADVERTISE_ADDRESS`; a malformed one refuses boot). Lane not configured →
|
|
120
|
+
* undefined (zero behavior change for non-fleet deployments); an address set while only the worker is missing is warned
|
|
121
|
+
* (half-configured announce).
|
|
117
122
|
*/
|
|
118
123
|
export declare function startFleetClientFromEnv(cfg: {
|
|
119
124
|
configCenter?: (CenterEndpoint & {
|
|
120
125
|
worker?: string;
|
|
121
126
|
}) | undefined;
|
|
122
127
|
}, opts: Omit<FleetClientOpts, "center" | "advertiseAddress"> & {
|
|
123
|
-
advertiseAddress?: string;
|
|
128
|
+
advertiseAddress?: string | undefined;
|
|
124
129
|
}): FleetClient | undefined;
|
|
125
130
|
//# sourceMappingURL=fleet-client.d.ts.map
|
package/dist/fleet-client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InstanceAnnouncement, UsageReport, DEFAULT_HEARTBEAT_MS } from "@sema-agent/settings-schema/fleet";
|
|
2
2
|
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
3
|
-
import { centerFetch } from "./config-center/center-request.js";
|
|
3
|
+
import { centerFetch, centerLaneConfigured } from "./config-center/center-request.js";
|
|
4
4
|
export const FLEET_USAGE_KEY_CAP = 1024;
|
|
5
5
|
export const FLEET_CLIENT_FETCH_TIMEOUT_MS = 5000;
|
|
6
6
|
export class FleetUsageAccumulator {
|
|
@@ -81,8 +81,9 @@ export function startFleetClient(opts) {
|
|
|
81
81
|
const send = (route, method, json) => centerFetch(opts.center, { kind: route }, { method, json, timeoutMs: FLEET_CLIENT_FETCH_TIMEOUT_MS, fetchImpl: f });
|
|
82
82
|
let stopped = false;
|
|
83
83
|
let failStreak = 0;
|
|
84
|
+
const address = opts.advertiseAddress;
|
|
84
85
|
const announceOnce = async () => {
|
|
85
|
-
if (stopped)
|
|
86
|
+
if (stopped || address === undefined)
|
|
86
87
|
return;
|
|
87
88
|
try {
|
|
88
89
|
const verdict = opts.readiness();
|
|
@@ -91,7 +92,7 @@ export function startFleetClient(opts) {
|
|
|
91
92
|
const body = {
|
|
92
93
|
instanceId: opts.instanceId,
|
|
93
94
|
worker: opts.center.worker,
|
|
94
|
-
address
|
|
95
|
+
address,
|
|
95
96
|
version: opts.version,
|
|
96
97
|
ready: verdict.ready,
|
|
97
98
|
draining: !verdict.ready && verdict.reasons.includes("draining"),
|
|
@@ -153,9 +154,11 @@ export function startFleetClient(opts) {
|
|
|
153
154
|
opts.logger?.warn("fleet_usage_report_failed", { error: err instanceof Error ? err.message : String(err), entries: entries.length });
|
|
154
155
|
}
|
|
155
156
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
const announcing = address !== undefined;
|
|
158
|
+
if (announcing)
|
|
159
|
+
void announceNow();
|
|
160
|
+
const hb = announcing ? setInterval(() => void announceNow(), heartbeatMs) : undefined;
|
|
161
|
+
hb?.unref?.();
|
|
159
162
|
const uw = opts.usage ? setInterval(() => void flushUsage(), usageWindowMs) : undefined;
|
|
160
163
|
uw?.unref?.();
|
|
161
164
|
let stopping = false;
|
|
@@ -163,13 +166,16 @@ export function startFleetClient(opts) {
|
|
|
163
166
|
if (stopping)
|
|
164
167
|
return;
|
|
165
168
|
stopping = true;
|
|
166
|
-
|
|
169
|
+
if (hb)
|
|
170
|
+
clearInterval(hb);
|
|
167
171
|
if (uw)
|
|
168
172
|
clearInterval(uw);
|
|
169
173
|
while (inFlight !== undefined)
|
|
170
174
|
await inFlight;
|
|
171
175
|
stopped = true;
|
|
172
176
|
await flushUsage();
|
|
177
|
+
if (!announcing)
|
|
178
|
+
return;
|
|
173
179
|
try {
|
|
174
180
|
await send("fleet-instances", "DELETE", { instanceId: opts.instanceId, worker: opts.center.worker });
|
|
175
181
|
}
|
|
@@ -180,23 +186,27 @@ export function startFleetClient(opts) {
|
|
|
180
186
|
}
|
|
181
187
|
export function startFleetClientFromEnv(cfg, opts) {
|
|
182
188
|
const cc = cfg.configCenter;
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
const raw = opts.advertiseAddress ?? process.env.FLEET_ADVERTISE_ADDRESS;
|
|
190
|
+
const address = raw === undefined || raw === "" ? undefined : raw;
|
|
191
|
+
if (address !== undefined && cc?.baseUrl && cc.credential.declared) {
|
|
192
|
+
let parsed;
|
|
193
|
+
try {
|
|
194
|
+
parsed = new URL(address);
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
parsed = undefined;
|
|
198
|
+
}
|
|
199
|
+
if (parsed === undefined || (parsed.protocol !== "http:" && parsed.protocol !== "https:")) {
|
|
200
|
+
throw new Error(`FLEET_ADVERTISE_ADDRESS must be a parseable http(s) base URL (e.g. "http://10.0.0.5:8090"); got "${buildDisplaySafeUrl(address)}" — the fleet center rejects any other shape on every announce`);
|
|
201
|
+
}
|
|
195
202
|
}
|
|
196
|
-
if (!
|
|
197
|
-
|
|
203
|
+
if (!centerLaneConfigured(cfg)) {
|
|
204
|
+
if (address !== undefined && cc?.baseUrl && cc.credential.declared) {
|
|
205
|
+
opts.logger?.warn("fleet_client_no_worker_scope", { note: "FLEET_ADVERTISE_ADDRESS set but SEMA_REGISTRY_WORKER missing — announce needs the worker (dispatch key); fleet client not started" });
|
|
206
|
+
}
|
|
198
207
|
return undefined;
|
|
199
208
|
}
|
|
200
|
-
|
|
209
|
+
const lane = cfg.configCenter;
|
|
210
|
+
return startFleetClient({ ...opts, center: { baseUrl: lane.baseUrl, credential: lane.credential, worker: lane.worker }, advertiseAddress: address });
|
|
201
211
|
}
|
|
202
212
|
//# sourceMappingURL=fleet-client.js.map
|
package/dist/fleet-lease.d.ts
CHANGED
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fleet worker-side QUOTA-LEASE consumption(调度框架 D4=AP 模式的 worker 半场;
|
|
3
|
-
* 契约 = registry-core `/fleet` QuotaLease + center `POST /api/fleet/leases`,center 侧已实装并经
|
|
4
|
-
* chaos 测试验证)。
|
|
5
|
-
*
|
|
6
|
-
* ## 模型(roaming/CDN)
|
|
7
|
-
* center 把某 principal 全局预算的一片(默认 $5 slice / 1h TTL)预授权给 ONE worker;worker 本地扣减
|
|
8
|
-
* (`recordSpend`,零 per-call 往返),`shouldRenewLease`(任一维度 80% 或寿命 80%)提前后台续租;center 用
|
|
9
|
-
* usage 批报事后单调对账(`/api/fleet/usage` 入库顺手 reconcile —— worker 不需要单独上报 consumed)。
|
|
10
|
-
*
|
|
11
|
-
* ## 协商式发现(谁走 lease?)
|
|
12
|
-
* worker 不在本地解析 EntitlementBudget —— 首次 admit 向 center POST,center 用 resolveEntitlement 判:
|
|
13
|
-
* - 200 {lease} → 该 principal 是 lease-enforced,进入本地扣减周期;
|
|
14
|
-
* - 409 "principal is not lease-enforced" → 负缓存 {@link NOT_LEASE_TTL_MS},走既有本地 CostQuota 路径;
|
|
15
|
-
* - 409 `code:"quota_exhausted"` → 提交拒绝(429,镜像 quotaExceeded 姿势),短缓存防打爆 center;
|
|
16
|
-
* - 网络/5xx → **fail-open 放行**(AP 哲学:center 短暂不可达绝不停集群;对账兜底,
|
|
17
|
-
* 过花敞口 ≤ slice 尺寸 —— 正是 D4 承诺的 trade)。
|
|
18
|
-
* 单飞(single-flight):同 principal 并发首触/续租只发一个 RTT。
|
|
19
|
-
*
|
|
20
|
-
* ## 边界(documented,非疏漏)
|
|
21
|
-
* - lease 状态 **in-memory**:worker 重启丢本地 consumed → center 续租时用 reports+monotone guard 补账,
|
|
22
|
-
* 敞口同样 ≤ slice(AP 设计内)。
|
|
23
|
-
* - 409 判别靠 center 返回的结构化 code(两端常量);center 侧变更时此处同步。
|
|
24
|
-
* - 全部 swallow-guarded:lease 面绝不 fault 任务提交路径。
|
|
25
|
-
*/
|
|
26
|
-
import { QuotaLease } from "@sema-agent/settings-schema/fleet";
|
|
27
1
|
import { type CenterEndpoint } from "./config-center/center-request.js";
|
|
28
2
|
/** 负缓存 TTL:非 lease-enforced 的 principal 多久后再问一次 center(entitlement 升级为 lease 的收敛上界)。 */
|
|
29
3
|
export declare const NOT_LEASE_TTL_MS: number;
|
|
@@ -33,8 +7,18 @@ export declare const EXHAUSTED_RETRY_MS: number;
|
|
|
33
7
|
* 重新协商,一次多余 RTT,无正确性影响。 */
|
|
34
8
|
export declare const LEASE_PRINCIPAL_CAP = 4096;
|
|
35
9
|
/** 复审 O1:lease 协商 RTT 的硬上限——leaseDenied 在提交热路径同步 await 它,一个 accept-but-hang 的
|
|
36
|
-
* center
|
|
10
|
+
* center 必须迅速塌成连接级失败臂而不是拖垮提交。取 2.5s(远小于任何续租余量,略紧于 registry 的 8s)。 */
|
|
37
11
|
export declare const LEASE_FETCH_TIMEOUT_MS = 2500;
|
|
12
|
+
/** S-866 ③:`closed` 姿态下连接级失败给调用方的重试窗(秒)。**固定短窗** —— 不猜中心何时恢复;503 可重试,下一次提交就是
|
|
13
|
+
* 下一次谈判(失败臂不缓存,中心一恢复即恢复)。 */
|
|
14
|
+
export declare const LEASE_UNAVAILABLE_RETRY_SEC = 5;
|
|
15
|
+
/** S-866 ③:租约连接级失败的两种姿态(= caps `centerWiring.failMode` 的词表;生效值由 `createFleetLeaseFromEnv` 从
|
|
16
|
+
* `FLEET_LEASE_FAIL_CLOSED` 定)。加一种 = 下面按姿态穷举的 `Record` 当场编译红。 */
|
|
17
|
+
export declare const LEASE_FAIL_MODES: readonly ["open", "closed"];
|
|
18
|
+
export type LeaseFailMode = (typeof LEASE_FAIL_MODES)[number];
|
|
19
|
+
/** S-866 ③:`lease_unavailable` 的两个成因(503 体 `reason` 的词表,与 caps `leaseHealth` 的两个失败词同名)。 */
|
|
20
|
+
export declare const LEASE_UNAVAILABLE_CAUSES: readonly ["auth-rejected", "unreachable"];
|
|
21
|
+
export type LeaseUnavailableCause = (typeof LEASE_UNAVAILABLE_CAUSES)[number];
|
|
38
22
|
/** E4 定稿的 409 结构化字段(center verbatim → 我方 429 body verbatim 透传;人话归壳/web)。 */
|
|
39
23
|
export type QuotaDenyDetail = {
|
|
40
24
|
windowType?: "fiveHour" | "weekly" | "budget";
|
|
@@ -43,17 +27,6 @@ export type QuotaDenyDetail = {
|
|
|
43
27
|
isEstimate?: boolean;
|
|
44
28
|
pool?: string;
|
|
45
29
|
};
|
|
46
|
-
type PrincipalState = {
|
|
47
|
-
kind: "lease";
|
|
48
|
-
lease: QuotaLease;
|
|
49
|
-
} | {
|
|
50
|
-
kind: "not-lease";
|
|
51
|
-
until: number;
|
|
52
|
-
} | {
|
|
53
|
-
kind: "exhausted";
|
|
54
|
-
until: number;
|
|
55
|
-
detail?: QuotaDenyDetail;
|
|
56
|
-
};
|
|
57
30
|
export type LeaseAdmission = {
|
|
58
31
|
ok: true;
|
|
59
32
|
} | {
|
|
@@ -61,12 +34,47 @@ export type LeaseAdmission = {
|
|
|
61
34
|
reason: "budget_exhausted";
|
|
62
35
|
retryAfterSec: number;
|
|
63
36
|
detail?: QuotaDenyDetail;
|
|
37
|
+
} | {
|
|
38
|
+
ok: false;
|
|
39
|
+
reason: "lease_unavailable";
|
|
40
|
+
cause: LeaseUnavailableCause;
|
|
41
|
+
retryAfterSec: number;
|
|
64
42
|
};
|
|
43
|
+
/** 一次被拒的准入(HTTP 映射 `leaseRefusalOf` 的入参闭集)。 */
|
|
44
|
+
export type LeaseRefusalAdmission = Extract<LeaseAdmission, {
|
|
45
|
+
ok: false;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* S-866 ② —— **最近一次谈判怎么收尾的**(`issueOrRenew` 收尾写;新状态,本管理器只此一枚)。闭集:
|
|
49
|
+
* · `none` —— 本进程还没谈判过;
|
|
50
|
+
* · `ok` —— 中心给了合法答复(200 租约 / 409 not-lease / 409 耗尽 —— 耗尽也是「中心在判」);
|
|
51
|
+
* · `auth-error` —— 中心 401 / 403 / 400,或这一次读凭证失败(请求根本没发);
|
|
52
|
+
* · `unavailable` —— 不可达 / 5xx / 超时 / 坏体 / 其它码。
|
|
53
|
+
* caps `centerWiring.leaseHealth` 由它投影({@link FleetLeaseManager.snapshot})。
|
|
54
|
+
*/
|
|
55
|
+
export declare const LEASE_ADMIT_OUTCOMES: readonly ["none", "ok", "auth-error", "unavailable"];
|
|
56
|
+
export type LeaseAdmitOutcome = (typeof LEASE_ADMIT_OUTCOMES)[number];
|
|
57
|
+
/** S-866 ② caps `centerWiring.leaseHealth` 的词表。 */
|
|
58
|
+
export declare const LEASE_HEALTH_WORDS: readonly ["ok", "auth-rejected", "unreachable", "unobserved"];
|
|
59
|
+
export type LeaseHealth = (typeof LEASE_HEALTH_WORDS)[number];
|
|
60
|
+
/** S-866 ② caps `centerWiring.caller.lease` 的词表(F-1 裁定:过期 / 耗尽而未续成 ⇒ `unobserved`)。 */
|
|
61
|
+
export declare const CALLER_LEASE_WORDS: readonly ["enforced", "not-enforced", "exhausted", "unobserved"];
|
|
62
|
+
export type CallerLease = (typeof CALLER_LEASE_WORDS)[number];
|
|
63
|
+
/** `snapshot()` 的产物(纯数据):调用方这一刻的租约读数 + 管理器最近一次谈判的健康位。`retryAfterSec` 只随 `exhausted`。 */
|
|
64
|
+
export interface LeaseSnapshot {
|
|
65
|
+
readonly leaseHealth: LeaseHealth;
|
|
66
|
+
readonly caller: {
|
|
67
|
+
readonly lease: CallerLease;
|
|
68
|
+
readonly retryAfterSec?: number;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
65
71
|
export interface FleetLeaseOpts {
|
|
66
72
|
/** S-651:凭证是**来源**(每次协商取一次值),不是构造期烧进 header 的字符串。 */
|
|
67
73
|
center: CenterEndpoint & {
|
|
68
74
|
worker: string;
|
|
69
75
|
};
|
|
76
|
+
/** S-866 ③:连接级失败臂的姿态。**必填** —— 安全轴上没有「不知道该放还是该拒」这一形(缺省在配置层按部署姿态派生,不在这里猜)。 */
|
|
77
|
+
failMode: LeaseFailMode;
|
|
70
78
|
fetchImpl?: typeof fetch;
|
|
71
79
|
logger?: {
|
|
72
80
|
info?(msg: string, meta?: Record<string, unknown>): void;
|
|
@@ -84,6 +92,10 @@ export declare class FleetLeaseManager {
|
|
|
84
92
|
private readonly f;
|
|
85
93
|
private readonly now;
|
|
86
94
|
private failStreak;
|
|
95
|
+
/** S-866 ②:最近一次谈判的收尾({@link LeaseAdmitOutcome})。只在 `issueOrRenewInner` 的各收尾点写。 */
|
|
96
|
+
private lastAdmit;
|
|
97
|
+
/** S-866 ③ 生效姿态(caps `centerWiring.failMode` 读的就是它 —— 与执行的那一只同源)。 */
|
|
98
|
+
readonly failMode: LeaseFailMode;
|
|
87
99
|
constructor(opts: FleetLeaseOpts);
|
|
88
100
|
/**
|
|
89
101
|
* Admission check for one billed submission. Fast path is synchronous-shaped (live lease / fresh negative
|
|
@@ -104,25 +116,38 @@ export declare class FleetLeaseManager {
|
|
|
104
116
|
weightedTokens?: number;
|
|
105
117
|
costMicroUsd?: number;
|
|
106
118
|
}): void;
|
|
107
|
-
/**
|
|
108
|
-
|
|
119
|
+
/**
|
|
120
|
+
* S-866 ② —— 调用方这一刻的租约读数(caps `centerWiring.caller` / `leaseHealth` 的唯一来源;7.105.0 的 `peek()` 退役:它只回 kind、
|
|
121
|
+
* 不看 `until`,过期条目照回原 kind)。**纯读**:不打中心、不写状态、不动 LRU 序(`Map.get` 不改插入序)—— caps 路由自己不 admit。
|
|
122
|
+
* 规则表(设计稿 v1.1 §2②,逐行钉在 `test/fleet-lease.test.ts`):
|
|
123
|
+
* · principal 缺席 / 无条目(含被 LRU 逐出)⇒ `unobserved`;
|
|
124
|
+
* · `not-lease` 且 `now < until` ⇒ `not-enforced`;到期 ⇒ `unobserved`;
|
|
125
|
+
* · `exhausted` 且 `now < until` ⇒ `exhausted` + `retryAfterSec = ceil((until-now)/1000)`(只此一处来源);到期 ⇒ `unobserved`;
|
|
126
|
+
* · `lease` 未到期 ∧ 各轴有余(= `!leaseExhausted`,与 `admit` 同一判据)⇒ `enforced`;到期或耗尽而尚未续成 ⇒ `unobserved`
|
|
127
|
+
* (F-1 裁定:此刻额度没人在判 —— 过期条目每次 admit 都去重谈,重谈失败靠连接级臂的姿态决定放不放)。
|
|
128
|
+
* `leaseHealth` 按 `lastAdmit`({@link LEASE_HEALTH_BY_OUTCOME})。
|
|
129
|
+
*/
|
|
130
|
+
snapshot(principal: string | undefined, now: number): LeaseSnapshot;
|
|
109
131
|
/** 🔴 复审 O2(Opus)的鉴权失败臂,S-651 起两个入口共用(中心 401/403/400;凭证文件不可读):运维配置事故,
|
|
110
|
-
*
|
|
111
|
-
|
|
132
|
+
* 不是可用性事件。**每次**都 warn + 专用计数,且**不缓存**(配置一修好下一次谈判即恢复)。放不放行归姿态
|
|
133
|
+
* ({@link connectionFailure},S-866 ③):open = 照旧放行(凭证抖一下不许把单用户锁在门外);closed = 拒。 */
|
|
134
|
+
private authFailure;
|
|
135
|
+
/** S-866 ③:两条连接级失败臂的**唯一**收尾 —— 按姿态穷举(加一种姿态 = 编译红)。 */
|
|
136
|
+
private connectionFailure;
|
|
112
137
|
private set;
|
|
113
138
|
private issueOrRenew;
|
|
114
139
|
private issueOrRenewInner;
|
|
115
140
|
}
|
|
116
141
|
/**
|
|
117
|
-
* Env/config gate(
|
|
118
|
-
* 它是出站消费面,不是入站可达性声明)
|
|
119
|
-
*
|
|
142
|
+
* Env/config gate:中心车道配齐({@link centerLaneConfigured} —— 与 fleet 用量环 / 用量累加器**同一只**谓词,S-866 ④)即启;
|
|
143
|
+
* 不需要 FLEET_ADVERTISE_ADDRESS(它是出站消费面,不是入站可达性声明)。缺任一件 → undefined,非 fleet 部署零行为变化。
|
|
144
|
+
* S-651:按**声明**装、不按内容装 —— `SEMA_REGISTRY_TOKEN_FILE`(`CENTER_CREDENTIAL_ENV.tokenFile`)在场而
|
|
120
145
|
* 文件此刻不可读,管理器照样装上(每次协商取值,失败走响亮的鉴权失败臂;文件恢复即恢复)。
|
|
121
146
|
*/
|
|
122
147
|
export declare function createFleetLeaseFromEnv(cfg: {
|
|
123
148
|
configCenter?: (CenterEndpoint & {
|
|
124
149
|
worker?: string;
|
|
125
150
|
}) | undefined;
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
fleetLeaseFailClosed: boolean;
|
|
152
|
+
}, opts: Omit<FleetLeaseOpts, "center" | "failMode">): FleetLeaseManager | undefined;
|
|
128
153
|
//# sourceMappingURL=fleet-lease.d.ts.map
|
package/dist/fleet-lease.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import { QuotaLease, leaseExhausted, shouldRenewLease } from "@sema-agent/settings-schema/fleet";
|
|
2
2
|
import { CenterCredentialUnavailableError } from "./center-credential.js";
|
|
3
|
-
import { centerFetch } from "./config-center/center-request.js";
|
|
3
|
+
import { centerFetch, centerLaneConfigured } from "./config-center/center-request.js";
|
|
4
4
|
export const NOT_LEASE_TTL_MS = 10 * 60 * 1000;
|
|
5
5
|
export const EXHAUSTED_RETRY_MS = 30 * 1000;
|
|
6
6
|
export const LEASE_PRINCIPAL_CAP = 4096;
|
|
7
7
|
export const LEASE_FETCH_TIMEOUT_MS = 2500;
|
|
8
|
+
export const LEASE_UNAVAILABLE_RETRY_SEC = 5;
|
|
9
|
+
export const LEASE_FAIL_MODES = ["open", "closed"];
|
|
10
|
+
export const LEASE_UNAVAILABLE_CAUSES = ["auth-rejected", "unreachable"];
|
|
11
|
+
const LOUD_BY_FAIL_MODE = {
|
|
12
|
+
open: { authWarn: "fleet_lease_auth_error_fail_open", unreachableWarn: "fleet_lease_unavailable_fail_open", unreachableMetric: "fleet_lease_fail_open_total", consequence: "budget NOT enforced until fixed" },
|
|
13
|
+
closed: { authWarn: "fleet_lease_auth_error_fail_closed", unreachableWarn: "fleet_lease_unavailable_fail_closed", unreachableMetric: "fleet_lease_fail_closed_total", consequence: "billed submissions refused (503 fleet.lease_unavailable) until fixed" },
|
|
14
|
+
};
|
|
15
|
+
export const LEASE_ADMIT_OUTCOMES = ["none", "ok", "auth-error", "unavailable"];
|
|
16
|
+
export const LEASE_HEALTH_WORDS = ["ok", "auth-rejected", "unreachable", "unobserved"];
|
|
17
|
+
export const CALLER_LEASE_WORDS = ["enforced", "not-enforced", "exhausted", "unobserved"];
|
|
18
|
+
const LEASE_HEALTH_BY_OUTCOME = {
|
|
19
|
+
none: "unobserved",
|
|
20
|
+
ok: "ok",
|
|
21
|
+
"auth-error": "auth-rejected",
|
|
22
|
+
unavailable: "unreachable",
|
|
23
|
+
};
|
|
8
24
|
export class FleetLeaseManager {
|
|
9
25
|
opts;
|
|
10
26
|
state = new Map();
|
|
@@ -12,10 +28,13 @@ export class FleetLeaseManager {
|
|
|
12
28
|
f;
|
|
13
29
|
now;
|
|
14
30
|
failStreak = 0;
|
|
31
|
+
lastAdmit = "none";
|
|
32
|
+
failMode;
|
|
15
33
|
constructor(opts) {
|
|
16
34
|
this.opts = opts;
|
|
17
35
|
this.f = opts.fetchImpl ?? fetch;
|
|
18
36
|
this.now = opts.now ?? Date.now;
|
|
37
|
+
this.failMode = opts.failMode;
|
|
19
38
|
}
|
|
20
39
|
async admit(principal) {
|
|
21
40
|
const now = this.now();
|
|
@@ -41,13 +60,42 @@ export class FleetLeaseManager {
|
|
|
41
60
|
st.lease.consumed.weightedTokens += d.weightedTokens ?? 0;
|
|
42
61
|
st.lease.consumed.costMicroUsd += d.costMicroUsd ?? 0;
|
|
43
62
|
}
|
|
44
|
-
|
|
45
|
-
|
|
63
|
+
snapshot(principal, now) {
|
|
64
|
+
const leaseHealth = LEASE_HEALTH_BY_OUTCOME[this.lastAdmit];
|
|
65
|
+
const st = principal === undefined ? undefined : this.state.get(principal);
|
|
66
|
+
if (st === undefined)
|
|
67
|
+
return { leaseHealth, caller: { lease: "unobserved" } };
|
|
68
|
+
switch (st.kind) {
|
|
69
|
+
case "not-lease":
|
|
70
|
+
return { leaseHealth, caller: { lease: now < st.until ? "not-enforced" : "unobserved" } };
|
|
71
|
+
case "exhausted":
|
|
72
|
+
return { leaseHealth, caller: now < st.until ? { lease: "exhausted", retryAfterSec: Math.ceil((st.until - now) / 1000) } : { lease: "unobserved" } };
|
|
73
|
+
case "lease":
|
|
74
|
+
return { leaseHealth, caller: { lease: leaseExhausted(st.lease, now) ? "unobserved" : "enforced" } };
|
|
75
|
+
default: {
|
|
76
|
+
const unhandled = st;
|
|
77
|
+
throw new Error(`fleet lease: unknown principal state ${JSON.stringify(unhandled)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
46
80
|
}
|
|
47
|
-
|
|
48
|
-
|
|
81
|
+
authFailure(principal, status, note) {
|
|
82
|
+
const loud = LOUD_BY_FAIL_MODE[this.failMode];
|
|
83
|
+
this.lastAdmit = "auth-error";
|
|
84
|
+
this.opts.logger?.warn?.(loud.authWarn, { status, principal, note: `${note} (${loud.consequence})` });
|
|
49
85
|
this.opts.metrics?.inc("fleet_lease_auth_error_total", { status: String(status) });
|
|
50
|
-
return
|
|
86
|
+
return this.connectionFailure("auth-rejected");
|
|
87
|
+
}
|
|
88
|
+
connectionFailure(cause) {
|
|
89
|
+
switch (this.failMode) {
|
|
90
|
+
case "open":
|
|
91
|
+
return { ok: true };
|
|
92
|
+
case "closed":
|
|
93
|
+
return { ok: false, reason: "lease_unavailable", cause, retryAfterSec: LEASE_UNAVAILABLE_RETRY_SEC };
|
|
94
|
+
default: {
|
|
95
|
+
const unhandled = this.failMode;
|
|
96
|
+
throw new Error(`fleet lease: unknown fail mode ${String(unhandled)}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
51
99
|
}
|
|
52
100
|
set(principal, st) {
|
|
53
101
|
if (!this.state.has(principal) && this.state.size >= LEASE_PRINCIPAL_CAP) {
|
|
@@ -80,10 +128,12 @@ export class FleetLeaseManager {
|
|
|
80
128
|
if (this.failStreak > 0)
|
|
81
129
|
this.opts.logger?.info?.("fleet_lease_recovered", { after: this.failStreak });
|
|
82
130
|
this.failStreak = 0;
|
|
131
|
+
this.lastAdmit = "ok";
|
|
83
132
|
this.opts.metrics?.inc("fleet_lease_issued_total");
|
|
84
133
|
return { ok: true };
|
|
85
134
|
}
|
|
86
135
|
if (res.status === 409) {
|
|
136
|
+
this.lastAdmit = "ok";
|
|
87
137
|
const body = (await res.json().catch(() => ({})));
|
|
88
138
|
if (body.code === "quota_exhausted") {
|
|
89
139
|
const until = this.now() + EXHAUSTED_RETRY_MS;
|
|
@@ -103,26 +153,28 @@ export class FleetLeaseManager {
|
|
|
103
153
|
return { ok: true };
|
|
104
154
|
}
|
|
105
155
|
if (res.status === 401 || res.status === 403 || res.status === 400) {
|
|
106
|
-
return this.
|
|
156
|
+
return this.authFailure(principal, res.status, "center rejected the lease request — check the service token / worker id / center URL");
|
|
107
157
|
}
|
|
108
158
|
throw new Error(`lease ${res.status}`);
|
|
109
159
|
}
|
|
110
160
|
catch (err) {
|
|
111
161
|
if (err instanceof CenterCredentialUnavailableError) {
|
|
112
|
-
return this.
|
|
162
|
+
return this.authFailure(principal, "credential-unreadable", `${err.reason} — no lease request sent`);
|
|
113
163
|
}
|
|
164
|
+
const loud = LOUD_BY_FAIL_MODE[this.failMode];
|
|
165
|
+
this.lastAdmit = "unavailable";
|
|
114
166
|
this.failStreak += 1;
|
|
115
167
|
if (this.failStreak === 1)
|
|
116
|
-
this.opts.logger?.warn?.(
|
|
117
|
-
this.opts.metrics?.inc(
|
|
118
|
-
return
|
|
168
|
+
this.opts.logger?.warn?.(loud.unreachableWarn, { error: err instanceof Error ? err.message : String(err), consequence: loud.consequence });
|
|
169
|
+
this.opts.metrics?.inc(loud.unreachableMetric);
|
|
170
|
+
return this.connectionFailure("unreachable");
|
|
119
171
|
}
|
|
120
172
|
}
|
|
121
173
|
}
|
|
122
174
|
export function createFleetLeaseFromEnv(cfg, opts) {
|
|
123
|
-
|
|
124
|
-
if (!cc?.baseUrl || !cc.credential.declared || !cc.worker)
|
|
175
|
+
if (!centerLaneConfigured(cfg))
|
|
125
176
|
return undefined;
|
|
126
|
-
|
|
177
|
+
const cc = cfg.configCenter;
|
|
178
|
+
return new FleetLeaseManager({ ...opts, center: { baseUrl: cc.baseUrl, credential: cc.credential, worker: cc.worker }, failMode: cfg.fleetLeaseFailClosed ? "closed" : "open" });
|
|
127
179
|
}
|
|
128
180
|
//# sourceMappingURL=fleet-lease.js.map
|
|
@@ -18,7 +18,6 @@ export declare const GOVERNANCE_HTTP_STATUS: {
|
|
|
18
18
|
readonly "memory.admission_denied": 403;
|
|
19
19
|
readonly "memory.admission_required": 503;
|
|
20
20
|
readonly "memory.capture_optout_denied": 403;
|
|
21
|
-
readonly "config.tool_mount_denied": 400;
|
|
22
21
|
readonly "config.locked_key": 400;
|
|
23
22
|
readonly "usage.window_exhausted": null;
|
|
24
23
|
readonly "config.compliance_required": null;
|
|
@@ -2,7 +2,6 @@ export const GOVERNANCE_HTTP_STATUS = {
|
|
|
2
2
|
"memory.admission_denied": 403,
|
|
3
3
|
"memory.admission_required": 503,
|
|
4
4
|
"memory.capture_optout_denied": 403,
|
|
5
|
-
"config.tool_mount_denied": 400,
|
|
6
5
|
"config.locked_key": 400,
|
|
7
6
|
"usage.window_exhausted": null,
|
|
8
7
|
"config.compliance_required": null,
|
package/dist/hosted-posture.d.ts
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
* 一台多用户 / 团队机器只要运维**忘设** `REQUIRE_PRINCIPAL`,本判据就**看不见**它 —— 无论它有没有
|
|
60
60
|
* 配签发方、受不受 reconciler 管。那台机器上:`principal` 缺席走单用户 `"default"` 赦免 ⇒ 全服无主会话
|
|
61
61
|
* 被列给每一个调用方;上面四条能力授予面(hooks / `settings.env` / `cwd` / `additionalDirectories`)对每个
|
|
62
|
-
*
|
|
62
|
+
* 调用方打开;写容纳面天花板那条腿(下文)也按单用户放行。
|
|
63
63
|
* `settings.env` 的射程要说准:适配器的 `env` 是**并**到 `process.env` 上的逐键覆盖(`plugins/remote-env-host.ts`
|
|
64
64
|
* 的 `mergeEnv`),而 `task-settings.ts` 的 `cleanEnvMap` 没有任何键名拒集 ⇒ `PATH` / `LD_PRELOAD` /
|
|
65
65
|
* `NODE_OPTIONS` 一视同仁。
|
|
@@ -101,22 +101,6 @@ export type TaskWriteFaceInput = TaskHostPostureScope & Readonly<Pick<ServiceCon
|
|
|
101
101
|
export declare const HOST_POSTURE_REFUSED_CODE = "request.host_posture_refused";
|
|
102
102
|
/** 射程判据读的键集(托管证据 + 车道)。 */
|
|
103
103
|
export type TaskHostPostureScope = HostedPostureInput & Readonly<Pick<ServiceConfigFlat, "remoteExec">>;
|
|
104
|
-
/**
|
|
105
|
-
* **腿③(clay 裁定① 2026-09-22)** —— `approverPosture` 在这台部署上兑现吗?`false` ⇒ 调用点
|
|
106
|
-
* **忽略 + 留声**(与 `task_hooks_ignored` / `task_cwd_ignored` / `task_shell_env_ignored` 同姿势)。
|
|
107
|
-
*
|
|
108
|
-
* 🔴 **为什么这一条不拒**(初版给了 422,当批撤回):这个键**零授权** —— 引擎只把它渲给模型,没有任何
|
|
109
|
-
* 裁决腿读它(`http/wire-types.ts` 的字段注逐字,来源是 core 的 `ApproverPosture` 头注)。而续跑腿
|
|
110
|
-
* **逐字重放盘上的体**,调用方**删不掉**它 ⇒ 一条 park 会被永久砖死;plan_review 驱动段(`drivePlanReview`)上更贵:
|
|
111
|
-
* nonce 先烧、拒在其后才抛,运维重签再撞同一个拒 = 无限循环。本仓对这一类的成文结论逐字是
|
|
112
|
-
* 「**砖死 resume 比降级糟**」(`boot/resolve-spec.ts` 顶部那段列了三处先例)。
|
|
113
|
-
* ⇒ 把本批最硬的处置给族里**危害最小**的成员,方向是错的。
|
|
114
|
-
*
|
|
115
|
-
* ⚠️ 它**不是**没有危害:引擎据此把 bypass 指令渲给模型,而那段文字指示模型**改用 Bash 改文件**
|
|
116
|
-
* 而不是用文件工具 ⇒ 写动作从**文件带**(core 写门 / `sensitiveWritePatterns` / `writeProtectedPaths`)
|
|
117
|
-
* 挪到**shell 带**(`shellGate`)。所以它要被**忽略**,而不是原样过境;留声让运维看得见。
|
|
118
|
-
*/
|
|
119
|
-
export declare function taskApproverPostureHonored(c: TaskHostPostureScope): boolean;
|
|
120
104
|
/**
|
|
121
105
|
* **腿②** —— 这条请求要掀开写围栏,这台部署给不给?返回拒因;`undefined` = 放行。
|
|
122
106
|
*
|
|
@@ -129,4 +113,9 @@ export declare function taskApproverPostureHonored(c: TaskHostPostureScope): boo
|
|
|
129
113
|
* 两条腿同射程、不同处置,射程只有一处答案。
|
|
130
114
|
*/
|
|
131
115
|
export declare function taskWriteFaceRefusal(c: TaskWriteFaceInput): string | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* **腿④** —— 这条请求要让 host shell 继承整份用户环境(`inheritEnv:"all"`),这台部署给不给?返回拒因;`undefined` = 放行。
|
|
118
|
+
* 调用方只在值为 `"all"` 时问(`"scrub"` 就是缺省,任何部署都受理)。
|
|
119
|
+
*/
|
|
120
|
+
export declare function taskInheritEnvRefusal(c: TaskHostPostureScope): string | undefined;
|
|
132
121
|
//# sourceMappingURL=hosted-posture.d.ts.map
|
package/dist/hosted-posture.js
CHANGED
|
@@ -22,22 +22,19 @@ function hostPostureDeclarationsHonored(c) {
|
|
|
22
22
|
return true;
|
|
23
23
|
return !buildHostedPosture(c).hosted;
|
|
24
24
|
}
|
|
25
|
-
export function taskApproverPostureHonored(c) {
|
|
26
|
-
return hostPostureDeclarationsHonored(c);
|
|
27
|
-
}
|
|
28
25
|
export function taskWriteFaceRefusal(c) {
|
|
29
26
|
switch (c.taskWriteFaceEscalation) {
|
|
30
27
|
case "allow":
|
|
31
28
|
return undefined;
|
|
32
29
|
case "refuse":
|
|
33
|
-
return (`permissionMode:"bypassPermissions" opens the WRITE containment face (
|
|
30
|
+
return (`permissionMode:"bypassPermissions" opens the WRITE containment face (the engine's bypassPermissions mode writes outside the write roots) — the run could write outside the task root. ` +
|
|
34
31
|
`Refused because this deployment pins TASK_WRITE_FACE_ESCALATION=refuse (the operator's own declaration — it wins over any request, unconditionally). ` +
|
|
35
32
|
`Omit permissionMode:"bypassPermissions", or ask the operator for TASK_WRITE_FACE_ESCALATION=allow.`);
|
|
36
33
|
case undefined:
|
|
37
34
|
break;
|
|
38
35
|
default: {
|
|
39
36
|
const unhandled = c.taskWriteFaceEscalation;
|
|
40
|
-
return (`permissionMode:"bypassPermissions" opens the WRITE containment face (
|
|
37
|
+
return (`permissionMode:"bypassPermissions" opens the WRITE containment face (the engine's bypassPermissions mode writes outside the write roots) — the run could write outside the task root. ` +
|
|
41
38
|
`Refused because this deployment carries an UNRECOGNISED TASK_WRITE_FACE_ESCALATION word (${String(unhandled)}) — a knob this build cannot interpret is never read as permission. ` +
|
|
42
39
|
`Set TASK_WRITE_FACE_ESCALATION to one of ${TASK_WRITE_FACE_ESCALATIONS.join(" | ")}, or unset it to let the deployment posture decide.`);
|
|
43
40
|
}
|
|
@@ -45,8 +42,16 @@ export function taskWriteFaceRefusal(c) {
|
|
|
45
42
|
if (hostPostureDeclarationsHonored(c))
|
|
46
43
|
return undefined;
|
|
47
44
|
const facts = buildHostedPosture(c);
|
|
48
|
-
return (`permissionMode:"bypassPermissions" opens the WRITE containment face (
|
|
45
|
+
return (`permissionMode:"bypassPermissions" opens the WRITE containment face (the engine's bypassPermissions mode writes outside the write roots) — the run could write outside the task root. ` +
|
|
49
46
|
`Refused because this deployment is HOSTED/multi-tenant (${hostedBecause(facts)}), so the task layer may only TIGHTEN the write face, never open it. ` +
|
|
50
47
|
`Set TASK_WRITE_FACE_ESCALATION=allow if the DEPLOYMENT permits the task layer to open it, or omit permissionMode:"bypassPermissions".`);
|
|
51
48
|
}
|
|
49
|
+
export function taskInheritEnvRefusal(c) {
|
|
50
|
+
if (hostPostureDeclarationsHonored(c))
|
|
51
|
+
return undefined;
|
|
52
|
+
const facts = buildHostedPosture(c);
|
|
53
|
+
return (`inheritEnv:"all" hands the model-driven host shell this machine's WHOLE environment (credential-shaped variables included — the host lane scrubs them by default). ` +
|
|
54
|
+
`Refused because this deployment is HOSTED/multi-tenant (${hostedBecause(facts)}), so the task layer may not widen what the shell inherits. ` +
|
|
55
|
+
`Omit inheritEnv (or send "scrub"); "all" is honored only on a single-user deployment.`);
|
|
56
|
+
}
|
|
52
57
|
//# sourceMappingURL=hosted-posture.js.map
|
package/dist/http/admission.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export declare const MAX_OUTPUT_SCHEMA_CHARS = 32768;
|
|
|
54
54
|
export declare function validateUserSkills(skills: unknown): string | null;
|
|
55
55
|
export declare function createAdmission(ctx: {
|
|
56
56
|
deps: FlatServiceDeps;
|
|
57
|
-
runnerFor:
|
|
57
|
+
runnerFor: FlatServiceDeps["runnerFor"];
|
|
58
58
|
} & Pick<RouteHelpers, "readJson">): {
|
|
59
59
|
prepareSpec: (req: IncomingMessage, res: ServerResponse, bodyIn?: TaskRequestBody, onTypedFailure?: (err: HttpError) => void, legFacts?: {
|
|
60
60
|
liveStream?: boolean;
|