@sema-agent/server 7.101.1 → 7.103.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 +84 -0
- package/README.md +4 -4
- package/README.zh-CN.md +4 -4
- package/USAGE.md +156 -9
- package/dist/approval-card.d.ts +35 -2
- package/dist/approval-card.js +19 -1
- package/dist/background-session-reap.d.ts +58 -0
- package/dist/background-session-reap.js +90 -0
- package/dist/bake-runner/main.d.ts +21 -3
- package/dist/bake-runner/main.js +18 -11
- package/dist/boot/config-center.d.ts +21 -4
- package/dist/boot/config-center.js +62 -19
- package/dist/boot/exit-boundary.js +17 -0
- package/dist/boot/runner-deps.js +1 -0
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +3 -4
- package/dist/boot/shutdown.d.ts +2 -1
- package/dist/boot/shutdown.js +33 -12
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-07-capability-layer.d.ts +1 -1
- package/dist/boot/stage-07-capability-layer.js +6 -4
- package/dist/boot/stage-08-reapers.d.ts +1 -1
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +13 -4
- package/dist/brain.d.ts +28 -8
- package/dist/brain.js +20 -4
- package/dist/capabilities/builtin-tools.d.ts +14 -1
- package/dist/capabilities/builtin-tools.js +3 -0
- package/dist/capabilities/center-plugins.d.ts +9 -3
- package/dist/capabilities/center-plugins.js +5 -10
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capabilities/scenarios.js +3 -3
- package/dist/center-credential-renewer.d.ts +76 -0
- package/dist/center-credential-renewer.js +359 -0
- package/dist/center-credential.d.ts +76 -22
- package/dist/center-credential.js +119 -43
- package/dist/config-catalog.js +17 -8
- package/dist/config-center/apply-effective.d.ts +16 -0
- package/dist/config-center/apply-effective.js +26 -21
- package/dist/config-center/center-request.d.ts +13 -0
- package/dist/config-center/center-request.js +23 -3
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/http-client.js +2 -5
- package/dist/config-center/mcp-revocation.d.ts +2 -2
- package/dist/config-center/mcp-revocation.js +1 -1
- package/dist/config-center/read-warnings.d.ts +19 -48
- package/dist/config-center/read-warnings.js +7 -49
- package/dist/config-center/restart-signal.d.ts +8 -21
- package/dist/config-center/restart-signal.js +2 -12
- package/dist/config-center/types.d.ts +4 -2
- package/dist/config-provider.js +1 -48
- package/dist/config-types.d.ts +107 -17
- package/dist/config-types.js +7 -2
- package/dist/config.d.ts +7 -0
- package/dist/config.js +91 -29
- package/dist/device-store.d.ts +1 -1
- package/dist/env-name-allowlist-knobs.d.ts +21 -7
- package/dist/env-name-allowlist-knobs.js +7 -2
- package/dist/fleet-client.d.ts +27 -7
- package/dist/fleet-client.js +43 -12
- package/dist/fleet-lease.d.ts +1 -1
- package/dist/host-lsp-manager.d.ts +4 -3
- package/dist/host-lsp-manager.js +2 -6
- package/dist/http/approval-sweeps.js +5 -20
- package/dist/http/dispatch.js +24 -14
- package/dist/http/readiness.d.ts +87 -0
- package/dist/http/readiness.js +54 -0
- package/dist/http/resume-legs.js +5 -4
- package/dist/http/route-table.d.ts +7 -0
- package/dist/http/route-table.js +6 -0
- package/dist/http/routes/a2a-serve.js +6 -14
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/background.d.ts +80 -0
- package/dist/http/routes/background.js +94 -0
- package/dist/http/routes/capabilities.js +2 -0
- package/dist/http/server.d.ts +25 -8
- package/dist/http/server.js +2 -2
- package/dist/leader/wire.d.ts +8 -1
- package/dist/leader/wire.js +4 -3
- package/dist/model-entry-refusal.d.ts +42 -19
- package/dist/model-entry-refusal.js +34 -19
- package/dist/model-route-endpoint.d.ts +10 -0
- package/dist/model-route-endpoint.js +6 -2
- package/dist/observability/fail-open.d.ts +2 -2
- package/dist/observability/fail-open.js +2 -2
- package/dist/observability/secret-env-scrub.d.ts +12 -0
- package/dist/observability/secret-env-scrub.js +12 -0
- package/dist/outbound-dispatcher.d.ts +125 -0
- package/dist/outbound-dispatcher.js +211 -0
- package/dist/parked-decide.d.ts +11 -5
- package/dist/parked-decide.js +4 -1
- package/dist/parked-revive-surface.d.ts +47 -0
- package/dist/parked-revive-surface.js +20 -0
- package/dist/plugins/background-shell-support.d.ts +20 -1
- package/dist/plugins/background-shell-support.js +26 -0
- package/dist/plugins/host-platform.d.ts +16 -1
- package/dist/plugins/host-platform.js +11 -0
- package/dist/plugins/remote-env-host.d.ts +14 -1
- package/dist/plugins/remote-env-host.js +15 -10
- package/dist/project-memory.js +3 -6
- package/dist/run-local.js +2 -0
- package/dist/server-secret-env.d.ts +97 -24
- package/dist/server-secret-env.js +37 -1
- package/dist/tool-approval.d.ts +30 -1
- package/dist/tool-approval.js +31 -8
- package/dist/trace/core-keyset-guard.d.ts +9 -4
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/project.js +13 -0
- package/dist/trace/redact.d.ts +11 -0
- package/dist/trace/redact.js +1 -0
- package/package.json +4 -3
|
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { resolve as resolvePath } from "node:path";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
|
+
import { CENTER_CREDENTIAL_ENV, judgeCenterCredentialDeclaration, parseCenterCredentialFile } from "@sema-agent/settings-schema";
|
|
5
6
|
export class CenterCredentialUnavailableError extends Error {
|
|
6
7
|
reason;
|
|
7
8
|
constructor(reason) {
|
|
@@ -10,20 +11,9 @@ export class CenterCredentialUnavailableError extends Error {
|
|
|
10
11
|
this.name = "CenterCredentialUnavailableError";
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
|
-
export const CENTER_TOKEN_ENV = "SEMA_REGISTRY_TOKEN";
|
|
14
|
-
export const CENTER_TOKEN_FILE_ENV = "SEMA_REGISTRY_TOKEN_FILE";
|
|
15
14
|
export function readCenterCredentialDeclaration(env = process.env) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
if (token !== "" && file !== "") {
|
|
19
|
-
throw new Error(`${CENTER_TOKEN_ENV} and ${CENTER_TOKEN_FILE_ENV} are both set — pick exactly one: ${CENTER_TOKEN_ENV} carries the credential value, ` +
|
|
20
|
-
`${CENTER_TOKEN_FILE_ENV} names a file re-read on every request (rotation without restart). Two writers on one credential is a guess, not a config.`);
|
|
21
|
-
}
|
|
22
|
-
if (file !== "")
|
|
23
|
-
return { from: "file", path: resolvePath(file) };
|
|
24
|
-
if (token !== "")
|
|
25
|
-
return { from: "env", token };
|
|
26
|
-
return { from: "absent" };
|
|
15
|
+
const declared = judgeCenterCredentialDeclaration(env);
|
|
16
|
+
return declared.from === "file" ? { from: "file", path: resolvePath(declared.path) } : declared;
|
|
27
17
|
}
|
|
28
18
|
const digest16 = (s) => createHash("sha256").update(s, "utf8").digest("hex").slice(0, 16);
|
|
29
19
|
function jwtSubject(bearer) {
|
|
@@ -38,56 +28,142 @@ function jwtSubject(bearer) {
|
|
|
38
28
|
return { missing: "no sub claim" };
|
|
39
29
|
return typeof parsed.sub === "string" && parsed.sub !== "" ? { sub: parsed.sub } : { missing: "sub is not a non-empty string" };
|
|
40
30
|
}
|
|
31
|
+
function userTokenIdentity(bearer) {
|
|
32
|
+
const s = jwtSubject(bearer);
|
|
33
|
+
return "sub" in s ? `user-jwt:${digest16(s.sub)}` : `user-jwt-opaque:${digest16(bearer)}`;
|
|
34
|
+
}
|
|
41
35
|
function classify(bearer, worker) {
|
|
42
|
-
if (looksLikeJwt(bearer))
|
|
43
|
-
|
|
44
|
-
return { ok: true, kind: "user-jwt", bearer, identity: "sub" in s ? `user-jwt:${digest16(s.sub)}` : `user-jwt-opaque:${digest16(bearer)}` };
|
|
45
|
-
}
|
|
36
|
+
if (looksLikeJwt(bearer))
|
|
37
|
+
return { ok: true, kind: "user-jwt", bearer, identity: userTokenIdentity(bearer) };
|
|
46
38
|
return { ok: true, kind: "pull-token", bearer, identity: pullTokenIdentity(worker), workerScoped: bearer.startsWith("wpt_") };
|
|
47
39
|
}
|
|
48
40
|
export function pullTokenIdentity(worker) {
|
|
49
41
|
return `pull-token:${worker ?? "global"}`;
|
|
50
42
|
}
|
|
51
43
|
const NONE = { ok: true, kind: "none", identity: "none" };
|
|
52
|
-
|
|
53
|
-
|
|
44
|
+
const PARSER_EMPTY_REASON = "empty";
|
|
45
|
+
function readCredentialText(text, spec, worker, now) {
|
|
46
|
+
const where = `${CENTER_CREDENTIAL_ENV.tokenFile} ${spec.path}`;
|
|
47
|
+
const file = parseCenterCredentialFile(text);
|
|
48
|
+
switch (file.kind) {
|
|
49
|
+
case "bare":
|
|
50
|
+
return classify(file.bearer, worker);
|
|
51
|
+
case "unreadable":
|
|
52
|
+
return file.reason === PARSER_EMPTY_REASON
|
|
53
|
+
? { ok: false, failure: "missing", reason: `${where} is empty` }
|
|
54
|
+
: { ok: false, failure: "malformed", reason: `${where} does not hold a center credential (${file.reason})` };
|
|
55
|
+
case "envelope":
|
|
56
|
+
return readSession(file.envelope, where, spec.multiTenant, now);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function readSession(envelope, where, multiTenant, now) {
|
|
60
|
+
if (multiTenant) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
failure: "multi_tenant_session",
|
|
64
|
+
reason: `${where} holds a personal sign-in session (a "user-session" envelope) and this deployment is multi-tenant (REQUIRE_PRINCIPAL=true) — a multi-tenant service never acts as one person's session; give it the worker pull token (one bare line) instead`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const expiresAt = Date.parse(envelope.expiresAt);
|
|
68
|
+
if (!Number.isFinite(expiresAt)) {
|
|
69
|
+
return { ok: false, failure: "malformed", reason: `${where} holds an envelope whose expiresAt is not a readable instant` };
|
|
70
|
+
}
|
|
71
|
+
const identity = userTokenIdentity(envelope.accessToken);
|
|
72
|
+
if (expiresAt <= now) {
|
|
73
|
+
return { ok: false, failure: "expired", reason: `${where} holds a sign-in session whose access token expired at ${envelope.expiresAt} — not sent (the renewer re-mints it; sign in again if renewal keeps failing)`, identity, expiresAt };
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, kind: "user-session", bearer: envelope.accessToken, identity, expiresAt };
|
|
76
|
+
}
|
|
77
|
+
export function createCenterCredentialSource(spec, worker, deps = {}) {
|
|
78
|
+
const listeners = new Set();
|
|
79
|
+
const onRead = (listener) => {
|
|
80
|
+
listeners.add(listener);
|
|
81
|
+
return () => {
|
|
82
|
+
listeners.delete(listener);
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const observed = (r) => {
|
|
86
|
+
for (const listener of listeners)
|
|
87
|
+
listener(r);
|
|
88
|
+
return r;
|
|
89
|
+
};
|
|
90
|
+
switch (spec.from) {
|
|
54
91
|
case "absent":
|
|
55
|
-
return { origin:
|
|
92
|
+
return { origin: spec, declared: false, read: () => observed(NONE), health: () => ({ state: "ok" }), onRead };
|
|
56
93
|
case "env": {
|
|
57
|
-
const fixed = classify(
|
|
58
|
-
return { origin: { from: "env" }, declared: true, read: () => fixed, health: () => ({ state: "ok" }) };
|
|
94
|
+
const fixed = classify(spec.token, worker);
|
|
95
|
+
return { origin: { from: "env" }, declared: true, read: () => observed(fixed), health: () => ({ state: "ok" }), onRead };
|
|
59
96
|
}
|
|
60
97
|
case "file": {
|
|
61
98
|
const readFile = deps.readFile ?? ((p) => readFileSync(p, "utf8"));
|
|
62
99
|
const now = deps.now ?? Date.now;
|
|
63
100
|
let health = { state: "ok" };
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
101
|
+
const readOnce = () => {
|
|
102
|
+
let text;
|
|
103
|
+
try {
|
|
104
|
+
text = readFile(spec.path);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : "read error";
|
|
108
|
+
return { ok: false, failure: "missing", reason: `${CENTER_CREDENTIAL_ENV.tokenFile} ${spec.path} is unreadable (${code})` };
|
|
109
|
+
}
|
|
110
|
+
return readCredentialText(text, spec, worker, now());
|
|
67
111
|
};
|
|
68
112
|
return {
|
|
69
|
-
origin:
|
|
113
|
+
origin: { from: "file", path: spec.path },
|
|
70
114
|
declared: true,
|
|
71
115
|
read() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const bearer = text.trim();
|
|
81
|
-
if (bearer === "")
|
|
82
|
-
return fail(`${CENTER_TOKEN_FILE_ENV} ${declaration.path} is empty`);
|
|
83
|
-
health = { state: "ok" };
|
|
84
|
-
return classify(bearer, worker);
|
|
116
|
+
const r = readOnce();
|
|
117
|
+
if (r.ok)
|
|
118
|
+
health = { state: "ok" };
|
|
119
|
+
else if (health.state === "unreadable")
|
|
120
|
+
health = { ...health, failure: r.failure, reason: r.reason, failures: health.failures + 1 };
|
|
121
|
+
else
|
|
122
|
+
health = { state: "unreadable", failure: r.failure, reason: r.reason, since: now(), failures: 1 };
|
|
123
|
+
return observed(r);
|
|
85
124
|
},
|
|
86
125
|
health: () => health,
|
|
126
|
+
onRead,
|
|
87
127
|
};
|
|
88
128
|
}
|
|
89
129
|
}
|
|
90
130
|
}
|
|
131
|
+
export const CENTER_CREDENTIAL_REFUSAL_CODES = {
|
|
132
|
+
malformed: "config.center_credential_malformed",
|
|
133
|
+
multi_tenant_session: "config.center_credential_user_session_multi_tenant",
|
|
134
|
+
};
|
|
135
|
+
export function judgeCenterCredentialAtBoot(read) {
|
|
136
|
+
if (read.ok)
|
|
137
|
+
return { verdict: "serve" };
|
|
138
|
+
switch (read.failure) {
|
|
139
|
+
case "missing":
|
|
140
|
+
return {
|
|
141
|
+
verdict: "loud",
|
|
142
|
+
failure: read.failure,
|
|
143
|
+
reason: read.reason,
|
|
144
|
+
effect: "every center request fails its own failure arm until the file is readable (config pull keeps env/LKG-less boot, caps fail-closed, lease takes the loud auth-error arm); no on-disk LKG / prompt state is read back (credential identity unknown)",
|
|
145
|
+
};
|
|
146
|
+
case "expired":
|
|
147
|
+
return {
|
|
148
|
+
verdict: "loud",
|
|
149
|
+
failure: read.failure,
|
|
150
|
+
reason: read.reason,
|
|
151
|
+
effect: "no center request carries the expired token; the renewer re-mints it now when renewal is on (CENTER_CREDENTIAL_RENEW_LEAD_MS > 0), otherwise requests fail their own failure arms until the shell rewrites the file; the on-disk LKG / prompt state of this identity is read back as usual",
|
|
152
|
+
};
|
|
153
|
+
case "malformed": {
|
|
154
|
+
const code = CENTER_CREDENTIAL_REFUSAL_CODES.malformed;
|
|
155
|
+
return {
|
|
156
|
+
verdict: "refuse",
|
|
157
|
+
code,
|
|
158
|
+
sentence: `center credential refused at boot [${code}]: ${read.reason}. The file must hold one bare token line, or the sign-in envelope the shell writes (a CenterCredentialEnvelope JSON object); sign in again so the shell rewrites it, or fix the file — its content is never echoed.`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
case "multi_tenant_session": {
|
|
162
|
+
const code = CENTER_CREDENTIAL_REFUSAL_CODES.multi_tenant_session;
|
|
163
|
+
return { verdict: "refuse", code, sentence: `center credential refused at boot [${code}]: ${read.reason}.` };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
91
167
|
export function scopedTokenNeedsWorker(read, worker) {
|
|
92
168
|
return read.ok && read.kind === "pull-token" && read.workerScoped && !worker;
|
|
93
169
|
}
|
|
@@ -95,8 +171,7 @@ export function centerStateBin(worker, identity) {
|
|
|
95
171
|
return `${worker === undefined ? "global" : `worker-${digest16(worker)}`}.id-${digest16(identity)}`;
|
|
96
172
|
}
|
|
97
173
|
export function credentialIdentityNow(source) {
|
|
98
|
-
|
|
99
|
-
return r.ok ? r.identity : undefined;
|
|
174
|
+
return source.read().identity;
|
|
100
175
|
}
|
|
101
176
|
export function answerStillCurrent(source, usedIdentity) {
|
|
102
177
|
return usedIdentity !== undefined && credentialIdentityNow(source) === usedIdentity;
|
|
@@ -121,11 +196,12 @@ function pinCredential(source, identity) {
|
|
|
121
196
|
if (!r.ok)
|
|
122
197
|
return r;
|
|
123
198
|
if (identity === undefined) {
|
|
124
|
-
return { ok: false, reason: "this state bin was opened while the center credential was unreadable (identity unknown) — it never talks to the center; the next call runs in the current identity's bin" };
|
|
199
|
+
return { ok: false, failure: "missing", reason: "this state bin was opened while the center credential was unreadable (identity unknown) — it never talks to the center; the next call runs in the current identity's bin" };
|
|
125
200
|
}
|
|
126
|
-
return r.identity === identity ? r : { ok: false, reason: "center credential identity changed — this request belongs to the previous identity's state bin and is not sent" };
|
|
201
|
+
return r.identity === identity ? r : { ok: false, failure: "missing", reason: "center credential identity changed — this request belongs to the previous identity's state bin and is not sent" };
|
|
127
202
|
},
|
|
128
203
|
health: () => source.health(),
|
|
204
|
+
onRead: (listener) => source.onRead(listener),
|
|
129
205
|
};
|
|
130
206
|
}
|
|
131
207
|
//# sourceMappingURL=center-credential.js.map
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
|
+
import { CENTER_CREDENTIAL_ENV } from "@sema-agent/settings-schema";
|
|
3
|
+
import { CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS, CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS, CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS } from "./center-credential-renewer.js";
|
|
2
4
|
import { THINKING_FORMATS } from "./model-compat.js";
|
|
3
|
-
import { RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
|
|
5
|
+
import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
|
|
4
6
|
import { webSearchProviderFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
|
|
5
7
|
import { AUTONOMY_MODES } from "./runtime-governance.js";
|
|
6
8
|
import { REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
|
|
@@ -8,6 +10,7 @@ import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
|
|
|
8
10
|
import { MODEL_PROVIDER_WORDS } from "./model-provider.js";
|
|
9
11
|
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
10
12
|
import { describeAllowlistPostures } from "./env-name-allowlist-knobs.js";
|
|
13
|
+
import { buildOutboundProxyPosture } from "./outbound-dispatcher.js";
|
|
11
14
|
export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
12
15
|
export const CATALOG_TIMINGS = ["hot", "restart", "deploy", "inert"];
|
|
13
16
|
export const CATALOG_DANGER_AXES = ["security", "billing", "data"];
|
|
@@ -78,6 +81,7 @@ export const CONFIG_CATALOG = [
|
|
|
78
81
|
r("AUTH_BRIDGE_JWKS_URL", "auth", "url", "registry SSO-JWT 的 JWKS 地址(缺省从 issuer 派生)", { danger: SEC }),
|
|
79
82
|
r("AUTONOMY", "auth", "enum", "运维 autonomy 模式,tighten-only 叠到每个 TaskSpec(read-only/plan ⇒ 只读手,ask ⇒ 恒 shellGate);center 腿热且 non-sticky", { danger: SEC, enumValues: AUTONOMY_MODES, configKey: "autonomy", center: cGov("autonomy") }),
|
|
80
83
|
r("AUTO_MODE_CLASSIFIER_FALLBACK", "approval", "enum", "auto 分类器席答不出裁决时的梯子(main=主模型代判 / none=只留分类器那一级,整条拒)", { enumValues: ["main", "none"], danger: SEC, derivedDefaultNote: "未设/空串 ⇒ 不铸键,跟随引擎缺省 main(core 7.18.0 起;空串按未设,与其余词表旋钮同判);none = 7.77.0 及以前的行为逐字;词表外的值启动期拒启。权限轴放宽,升级须知见 DEPLOY-PREREQS", configKey: "autoModeClassifierFallback" }),
|
|
84
|
+
r("AUTO_MODE_CLASSIFIER_TIMEOUT_MS", "approval", "number", `auto 分类器单级判决上限(ms;缺席 = core 缺省;自托管慢网关按排队 + prefill 留足;一级到点没裁 ⇒ 走下一级,走完仍没裁 ⇒ 这次门控调用整条拒;须为 [${AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS.minMs}, ${AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS.maxMs}] 内十进制整数,0 / 小数 / 越界拒启)`, { derivedDefaultNote: "未设/空串 ⇒ 不铸键,跟随 core 缺省(本仓不复制上游数字;core 改缺省本旋钮照映不改)", configKey: "autoModeClassifierTimeoutMs" }),
|
|
81
85
|
r("BAKE_CACHE_BASE", "orchestration", "url", "烤镜像 fetch-once 缓存源 base(注入 --cache-base;绝不受调用方控制)"),
|
|
82
86
|
r("BAKE_IMAGE_REGISTRY", "orchestration", "string", "烤镜像自动登记的 registry host 前缀(repo=registry/profile)"),
|
|
83
87
|
r("BAKE_LEASE_MS", "orchestration", "number", "bake 租约窗(ms;runner 心跳推进,过期被 reaper 抢)", { staticDefault: "90000" }),
|
|
@@ -89,6 +93,7 @@ export const CONFIG_CATALOG = [
|
|
|
89
93
|
r("BG_AGENT_RETENTION_MS", "orchestration", "number", "TERMINAL 后台 agent 行保留期(ms,reaper 按 scope 清)", { staticDefault: String(7 * 24 * 60 * 60 * 1000), danger: DATA, configKey: "backgroundAgentRetentionMs" }),
|
|
90
94
|
r("BG_AGENT_STALE_RUNNING_MS", "orchestration", "number", "running 后台 agent 行写者心跳断更超此值翻 failed(ms;下限 10min 硬钳)", { staticDefault: String(15 * 60_000), configKey: "backgroundAgentStaleRunningMs" }),
|
|
91
95
|
r("BIND_HOST", "auth", "string", "监听绑址(显式值恒生效)", { danger: SEC, derivedDefaultNote: "缺席:无鉴权写面(allowUnauthedWrites ∧ 零凭证)自动收窄 127.0.0.1,其余全接口", resolve: (c) => c.bindHost ?? null }),
|
|
96
|
+
r("CENTER_CREDENTIAL_RENEW_LEAD_MS", "integrations", "number", `center 凭证续签提前量(ms;${CENTER_CREDENTIAL_ENV.tokenFile} 指向人的登录会话信封时,访问令牌离到期 ≤ 本值即由引擎续签并原子改写同一文件;0 = 关续签只读信封;其余须为 [${CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS}, ${CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS}] 内整数,坏值拒启;裸行 / 值形凭证不续签)`, { staticDefault: String(CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS), danger: SEC, configKey: "centerCredentialRenewLeadMs" }),
|
|
92
97
|
r("COMPLIANCE_ADDITIONAL_DENIES", "auth", "csv", "合规档位之上额外拒绝的能力名(只能收紧;词表=core 闭集,拼错拒启)", { danger: SEC }),
|
|
93
98
|
r("COMPLIANCE_PROFILE", "auth", "enum", "本部署合规档位(core compliancePostureResolver 供数;拼错拒启)", { danger: SEC, enumValues: ["hipaa", "zdr"], resolve: (c) => c.compliancePosture?.profile ?? null }),
|
|
94
99
|
r("CONFIG_BOOT_FETCH_BUDGET_MS", "integrations", "number", "boot 首拉配置的抢答窗(ms;过窗以 env/兜底起服,拉取转后台热应用)", { staticDefault: "1500", configKey: "configBootFetchBudgetMs" }),
|
|
@@ -128,6 +133,7 @@ export const CONFIG_CATALOG = [
|
|
|
128
133
|
r("DOCKER_PIDS_LIMIT", "orchestration", "number", "local-docker fork-bomb 背栓(--pids-limit;非法值回 adapter 默认 512+warn)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ adapter 默认 512" }),
|
|
129
134
|
r("DOCKER_SANDBOX_ENV", "orchestration", "secret", "local-docker 车道每条 docker exec 以 -e 注入的本进程 env 点名表(NAME,NAME;点到的键从 worker env 原样取,值不进 spec/center/日志;与 LSP_ENV_ALLOW / E2B_SANDBOX_ENV 同一只解析器与「点名放行」谓词:形错 / 点名名单型旋钮拒启且不回显值;本进程环境里没有的名按序号 warn、不转发;只在本车道被选中时求值;原文按凭据类对待:只回在场位、不进任何子进程)", { danger: SEC }),
|
|
130
135
|
r("DRAIN_GRACE_MS", "limitsHttp", "number", "SIGTERM 优雅排空窗(ms;期间新计费提交 503+Retry-After)。出厂默认是**服务器部署**的取值;壳自 spawn 的引擎会被壳注入更短的窗 ⇒ 排壳形关机预算时以本行的 effectiveValue(= 本进程实际生效值)为准,不照抄 staticDefault", { staticDefault: String(10 * 60 * 1000), configKey: "drainGraceMs", center: cLimits("drainGraceMs") }),
|
|
136
|
+
r("DRAIN_SECOND_SIGNAL_MIN_GAP_MS", "limitsHttp", "number", `排空期「第二发信号 = 硬停」的最小间隔(ms;锚 = 排空起点。SIGTERM / SIGINT 与起点相隔不足 ⇒ 同一拍的重复投递,一行 drain_duplicate_signal_ignored 后忽略、排空继续;0 = 关,恒按第二发硬停;须为 [${DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS.minMs}, ${DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS.maxMs}] 内十进制整数(上界 = DRAIN_GRACE_MS 地板),小数 / 负数 / 越界拒启)`, { staticDefault: String(DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS), configKey: "drainSecondSignalMinGapMs" }),
|
|
131
137
|
r("DURABLE_APPROVAL", "approval", "boolean", "durable suspend/resume HITL(需 SQL 店;决定 checkpointStore 与 /v1/approvals 面的存在性)", { danger: SEC, derivedDefaultNote: "posture 三态(S-178):显式词恒赢,缺席 = 单用户(REQUIRE_PRINCIPAL≠true)且有耐久店 ⇒ on / 多租户或无耐久店 ⇒ off;生效来源见 GET /v1/diagnostics/wiring 的 serverGates.durableApproval.source", configKey: "durableApproval" }),
|
|
132
138
|
r("E2B_ALLOW_NET", "orchestration", "boolean", "e2b 沙箱出网许可", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 不铸键(adapter 默认允许)" }),
|
|
133
139
|
r("E2B_API_KEY", "orchestration", "secret", "E2B 车道 API 凭据(REMOTE_EXEC=e2b 必需件)", { danger: [...SEC, ...BILL] }),
|
|
@@ -157,7 +163,7 @@ export const CONFIG_CATALOG = [
|
|
|
157
163
|
r("HOST_BG_DEFAULT_TIMEOUT_SEC", "orchestration", "number", "host 后台 shell 缺省超时(秒)", { staticDefault: "300" }),
|
|
158
164
|
r("HOST_BG_ENABLED", "orchestration", "boolean", "host 车道后台 shell 开关", { staticDefault: "true" }),
|
|
159
165
|
r("HOST_BG_FILE_CAP", "orchestration", "number", "host 后台 shell 磁盘护栏(字节;失控输出被 reap)", { staticDefault: String(64 * 1024 * 1024) }),
|
|
160
|
-
r("HOST_BG_KILL_GRACE_MS", "orchestration", "number", "host 后台 shell SIGTERM→SIGKILL 宽限(ms)", { staticDefault: "1000" }),
|
|
166
|
+
r("HOST_BG_KILL_GRACE_MS", "orchestration", "number", "host 后台 shell SIGTERM→SIGKILL 宽限(ms);会话删除 / 会话停止口在软停与硬收之间等同一只", { staticDefault: "1000" }),
|
|
161
167
|
r("HOST_BG_MAX_CONCURRENT", "orchestration", "number", "host 后台 shell 并发帽", { staticDefault: "8" }),
|
|
162
168
|
r("HOST_BG_MAX_TIMEOUT_SEC", "orchestration", "number", "host 后台 shell 超时上限(秒)", { staticDefault: "1800" }),
|
|
163
169
|
r("HOST_BG_MEM_CAP", "orchestration", "number", "host 后台 shell 内存流护栏(字节)", { staticDefault: String(8 * 1024 * 1024) }),
|
|
@@ -167,6 +173,8 @@ export const CONFIG_CATALOG = [
|
|
|
167
173
|
r("HOST_FS_DRAIN_WAIT_MS", "orchestration", "number", "host 执行环境拆除时等在飞文件写落地的上界(ms;到点 destroy 返回,rm 仍排在那批写之后;下限 1)", { staticDefault: "10000" }),
|
|
168
174
|
r("HOST_SHELL_PATH", "orchestration", "path", "host 车道 shell 覆写口(POSIX 与 win32 同源解析)"),
|
|
169
175
|
r("HOST_WORKSPACE_BASE", "orchestration", "path", "host 车道工作区 base"),
|
|
176
|
+
r("HTTPS_PROXY", "limitsHttp", "url", "https: 目标的出网代理 —— 平台标准变量(小写 `https_proxy` 同认且优先 —— 与 undici 读 env 逐字同序);server 进程**所有走 undici 的出网跳**都认:引擎 → 网关、中心拉取 / 公告 / 凭证续签、WebSearch、对象存储、OTLP …(e2b / k8s SDK、ws、git 子进程不在此列,见 USAGE「出网代理」);**回环 / 未指定地址目标恒直连,不看 NO_PROXY**;userinfo 译成 `Proxy-Authorization`,回显 display-safe;Node 运行时下 undici 用不了的值(无 scheme、带路径 / 查询)拒启,机读码 `config.outbound_proxy_invalid`(Bun 运行时不校验);回显只出 scheme//host[:port],形不干净整只占位;boot 冻结;只设小写拼法时本行 envHeld 读 false(目录按大写名判在场),值照实回显", { danger: SEC, derivedDefaultNote: "缺席 / 空 ⇒ https 目标走 HTTP_PROXY(undici 的回落,不是本仓的发明);两者都缺 ⇒ 直连", resolve: (_c, env) => buildOutboundProxyPosture(env).httpsProxy }),
|
|
177
|
+
r("HTTP_PROXY", "limitsHttp", "url", "http: 目标的出网代理(https 目标在 HTTPS_PROXY 缺席时也走它)—— 平台标准变量(小写 `http_proxy` 同认且优先 —— 与 undici 读 env 逐字同序);server 进程**所有走 undici 的出网跳**都认:引擎 → 网关、中心拉取 / 公告 / 凭证续签、WebSearch、对象存储、OTLP …(e2b / k8s SDK、ws、git 子进程不在此列,见 USAGE「出网代理」);**回环 / 未指定地址目标恒直连,不看 NO_PROXY**;userinfo 译成 `Proxy-Authorization`,回显 display-safe;Node 运行时下 undici 用不了的值(无 scheme、带路径 / 查询)拒启,机读码 `config.outbound_proxy_invalid`(Bun 运行时不校验);回显只出 scheme//host[:port],形不干净整只占位;boot 冻结;只设小写拼法时本行 envHeld 读 false(目录按大写名判在场),值照实回显", { danger: SEC, derivedDefaultNote: "缺席 ⇒ http 目标直连", resolve: (_c, env) => buildOutboundProxyPosture(env).httpProxy }),
|
|
170
178
|
r("IMAGE_BAKES_ENABLED", "orchestration", "boolean", "烤镜像 job API(/v1/images/bakes*;需 SQL 池 + 非空 operator 名单,boot 断言)", { staticDefault: "false", danger: SEC }),
|
|
171
179
|
r("INFRA_COST_EGRESS_GB", "limitsHttp", "number", "出网计费费率(per-run 出网计量未接,恒乘 0 —— 设非零会 boot 点名)", { staticDefault: "0", danger: BILL, envTiming: "inert", center: cLimits("infraCostRates.egressGbMicroUsd", { timing: "inert" }) }),
|
|
172
180
|
r("INFRA_COST_SANDBOX_SEC", "limitsHttp", "number", "沙箱秒基建费率(0=不计价)", { staticDefault: "0", danger: BILL, center: cLimits("infraCostRates.sandboxSecMicroUsd") }),
|
|
@@ -209,7 +217,7 @@ export const CONFIG_CATALOG = [
|
|
|
209
217
|
r("LOCKED_CONFIG_KEYS", "auth", "csv", "管理员锁定层声明(词表=core 闭集注册表,未知键拒启)", { danger: SEC, configKey: "lockedConfigKeys" }),
|
|
210
218
|
r("LOG_LEVEL", "observability", "string", "日志档位", { staticDefault: "info", configKey: "logLevel" }),
|
|
211
219
|
r("LSP_ENABLED", "orchestration", "boolean", "LSP 沙箱腿 opt-in(需烤好的 LSP 模板;host 腿另有 LSP_HOST_ENABLED)", { staticDefault: "false", configKey: "lspEnabled" }),
|
|
212
|
-
r("LSP_ENV_ALLOW", "orchestration", "secret", `host 腿语言服务器子进程 env 的点名放行名单(点到的键从服务 env 原样放回、部署方显式承担;没点到的照剥;与 host shell inheritEnv:[names] 同一只谓词;形错 /
|
|
220
|
+
r("LSP_ENV_ALLOW", "orchestration", "secret", `host 腿语言服务器子进程 env 的点名放行名单(点到的键从服务 env 原样放回、部署方显式承担;没点到的照剥;与 host shell inheritEnv:[names] 同一只谓词;形错 / 点名任一只名单型旋钮(含自己)拒启且不回显值;本进程环境里没有的名按序号 warn、不放行、不回显;多租户姿态(忽略时起服 warn 一次):${describeAllowlistPostures()};原文按凭据类对待:只回在场位、不进任何子进程,生效名单见起服披露行 allowed)`, { danger: SEC, derivedDefaultNote: "缺席 / 空串 ⇒ 空名单 = 全剥(7.100.0 行为)" }),
|
|
213
221
|
r("LSP_HOST_ENABLED", "orchestration", "boolean", "LSP host 腿开关(本机 language server,优雅降级)", { staticDefault: "true", configKey: "lspHostEnabled" }),
|
|
214
222
|
r("MANUAL_MODE_SHELL_GATE", "approval", "enum", "手动模式 shell 门收紧腿(off=no-op 非放松 —— 本键只有收紧半场;拼错拒启)", { danger: SEC, enumValues: ["always", "classify", "off"], echoWords: ["always", "classify"], resolve: (c) => c.manualModeShellGate ?? null, derivedDefaultNote: "off/未设 ⇒ 本键不贡献任何东西(归一成键缺席)⇒ 回显 null;真姿态只有 always/classify 两个" }),
|
|
215
223
|
r("MAX_PRINCIPAL_COST_USD", "limitsHttp", "number", "per-principal 窗内成本天花板(0=关)", { staticDefault: "0", danger: BILL, configKey: "maxPrincipalCostUsd", center: cLimits("maxPrincipalCostUsd") }),
|
|
@@ -272,7 +280,6 @@ export const CONFIG_CATALOG = [
|
|
|
272
280
|
r("MODEL_CHEAP_VISION", "modelPlane", "boolean", "cheap 槽 vision 声明(三态:缺席继承主模型;text-only cheap 必设 false 让预检真拦)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
|
|
273
281
|
r("MODEL_CIRCUIT_BREAKER", "modelPlane", "boolean", "模型腿熔断开关", { staticDefault: String(RESILIENCE_DEFAULTS.circuitBreaker.value) }),
|
|
274
282
|
r("MODEL_CODE_ROLES", "modelPlane", "csv", "给点名 role 附编码 persona(role 表内名字才生效)"),
|
|
275
|
-
r("MODEL_CONNECT_TIMEOUT_MS", "modelPlane", "number", "连接超时(ms;0=关)", { staticDefault: String(RESILIENCE_DEFAULTS.connectTimeoutMs.value) }),
|
|
276
283
|
r("MODEL_CONTEXT_WINDOW", "modelPlane", "number", "主模型上下文窗声明", { staticDefault: "262144", configKey: "model", resolve: (c) => c.model.contextWindow ?? null, center: cModels("models[].contextWindow") }),
|
|
277
284
|
r("MODEL_COST_CACHE_READ", "modelPlane", "number", "主模型 cache-read 单价(per-1M USD;显式 0=声明免费,不是「没配」)", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.cacheRead ?? null, center: cModels("models[].cost.cacheRead") }),
|
|
278
285
|
r("MODEL_COST_CACHE_WRITE", "modelPlane", "number", "主模型 cache-write 单价", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.cacheWrite ?? null, center: cModels("models[].cost.cacheWrite") }),
|
|
@@ -285,12 +292,13 @@ export const CONFIG_CATALOG = [
|
|
|
285
292
|
r("MODEL_DEGRADE_TO", "modelPlane", "string", "降级目标模型(设了=开降级;外部网关名不必在目录)", { danger: BILL }),
|
|
286
293
|
r("MODEL_DEGRADE_TO_VISION", "modelPlane", "boolean", "降级目标的 vision 声明(false ⇒ 带图任务丢降级不丢图)", { staticDefault: "false" }),
|
|
287
294
|
r("MODEL_EXTRA_BODY", "modelPlane", "opaque", "生成参数直通 JSON(top_k/logit_bias 等;必须静态 —— 前缀缓存键序)", { danger: SEC }),
|
|
288
|
-
r("
|
|
295
|
+
r("MODEL_FIRST_BYTE_TIMEOUT_MS", "modelPlane", "number", `请求发出 → 响应头的等待(ms;缺席 = core 缺省 + 上传宽限;到点 core 自有一次加宽重试,配了 MODEL_GATEWAY_FALLBACK_URLS 也一样(之后才切备;server 不铸 firstByteRetries);0 = 该段不设引擎钟、传输层的响应头 / 响应体钟也关(TCP + TLS 建连仍归传输层自己的钟);自托管慢网关按排队 + prefill 留足;failover 拓扑想快切须显式设小;须为 [${FIRST_BYTE_TIMEOUT_BOUNDS.minMs}, ${FIRST_BYTE_TIMEOUT_BOUNDS.maxMs}] 内十进制整数,其余拒启)`, { derivedDefaultNote: "未设/空串 ⇒ 不铸键,跟随 core 缺省(本仓不复制上游数字;core 改缺省本旋钮照映不改)", resolve: (c) => c.resilience.firstByteTimeoutMs ?? null }),
|
|
296
|
+
r("MODEL_FIRST_TOKEN_TIMEOUT_MS", "modelPlane", "number", "首 token 超时(ms;0 = 该段不设引擎钟、传输层的响应体钟也关 —— 开流不吐字的唯一看门狗;自托管长 prefill 后端按此档留足)", { staticDefault: String(RESILIENCE_DEFAULTS.firstTokenTimeoutMs.value) }),
|
|
289
297
|
r("MODEL_FREQUENCY_PENALTY", "modelPlane", "number", "frequency_penalty 直通(压过 EXTRA_BODY 同键)"),
|
|
290
298
|
r("MODEL_GATEWAY_BASEURL", "modelPlane", "url", "openai 兼容网关 base URL(S-623:**无出厂缺省** —— 配置能派发到的每条 openai 兼容路由都必须解析到显式端点(条目自己的 baseUrl,或本键),否则拒启 / 热路径整批拒,机读码 `config.model_route_endpoint_missing`,拒因点名条目并给出本机写法 `MODEL_GATEWAY_BASEURL=<本机网关>/v1`;anthropic 单路由部署不设本键合法;S-475:带 userinfo 拒启,机读码 `config.gateway_url_userinfo` —— WHATWG 禁止用带凭据的 URL 构造 fetch Request,凭据放 MODEL_API_KEY;S-483:解析不动/不透明形拒启,机读码 `config.model_route_url_invalid`;S-514:带 `#fragment` 拒启,机读码 `config.model_route_url_fragment`)", { derivedDefaultNote: "缺席 ⇒ 本部署没有网关(不铸键);有 openai 路由落不到端点 ⇒ 拒启", configKey: "gatewayBaseUrl", center: cModels("models[].baseUrl") }),
|
|
291
299
|
r("MODEL_GATEWAY_FALLBACK_URLS", "modelPlane", "url", "网关故障转移地址表(CSV;S-623:主网关 MODEL_GATEWAY_BASEURL 未设而本键设了 ⇒ 拒启,机读码 `config.gateway_fallback_without_primary` —— 备用是主网关的镜像;S-475:**逐项**判 userinfo,任一项带 ⇒ 拒启,机读码 `config.gateway_url_userinfo`;S-483:逐项判可解析性,机读码 `config.model_route_url_invalid`;S-514:**逐项**判 `#fragment`,机读码 `config.model_route_url_fragment`)", { configKey: "gatewayFallbackUrls" }),
|
|
292
300
|
r("MODEL_ID", "modelPlane", "string", "主模型 id(3.0.0 起无出厂缺省,缺 ⇒ 拒启指路)", { configKey: "model", resolve: (c) => c.model.id, center: cModels("models[].id") }),
|
|
293
|
-
r("MODEL_IDLE_TIMEOUT_MS", "modelPlane", "number", "流中静默看门狗(ms;0
|
|
301
|
+
r("MODEL_IDLE_TIMEOUT_MS", "modelPlane", "number", "流中静默看门狗(ms;0 = 该段不设引擎钟、传输层的响应体钟也关)", { staticDefault: String(RESILIENCE_DEFAULTS.idleTimeoutMs.value) }),
|
|
294
302
|
r("MODEL_MAX_TOKENS", "modelPlane", "number", "主模型输出上限", { derivedDefaultNote: "缺席 ⇒ min(32000, contextWindow)", resolve: (c) => c.model.maxTokens ?? null, center: cModels("models[].maxTokens") }),
|
|
295
303
|
r("MODEL_PRESENCE_PENALTY", "modelPlane", "number", "presence_penalty 直通"),
|
|
296
304
|
r("MODEL_PROVIDER", "modelPlane", "enum", "主模型 provider(路由选 brain;S-461 起是闭集,词表外的值启动期拒启并点名机读码 `config.model_provider_invalid`)", { enumValues: MODEL_PROVIDER_WORDS, derivedDefaultNote: "缺席:ANTHROPIC base+cred 同在 ⇒ anthropic,否则 gateway", resolve: (c) => c.model.provider, center: cModels("models[].provider") }),
|
|
@@ -323,6 +331,7 @@ export const CONFIG_CATALOG = [
|
|
|
323
331
|
r("MYSQL_POOL_SIZE", "store", "number", "MySQL 连接池上限(坏值回 driver 默认+warn)", { derivedDefaultNote: "缺席 ⇒ driver 默认池" }),
|
|
324
332
|
r("MYSQL_PORT", "store", "number", "MySQL 协议端口", { staticDefault: "3306" }),
|
|
325
333
|
r("MYSQL_USER", "store", "string", "MySQL 协议用户", { danger: DATA }),
|
|
334
|
+
r("NO_PROXY", "limitsHttp", "csv", "不走代理的目标(小写 `no_proxy` 同认且优先);语法 = undici 的:逗号或空白分隔,`host` 精确且含其子域、`.host` / `*.host` 后缀、`host:port` 限端口、`*` 全直连;**回环 / 未指定地址目标无论写没写都直连**(本仓的无条件规则,不是 NO_PROXY 的一条);boot 冻结", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 除回环外一律按 HTTP(S)_PROXY 走", resolve: (_c, env) => buildOutboundProxyPosture(env).noProxy }),
|
|
326
335
|
r("OPERATOR_PRINCIPALS", "auth", "csv", "operator 名单(operator lane 全族门;审批队列上空名单=旧行为 token 即 operator,explicitOperatorOk 族空名单=谁都不是)", { danger: SEC, configKey: "operatorPrincipals" }),
|
|
327
336
|
r("OTEL_EXPORTER_OTLP_ENDPOINT", "observability", "url", "OTLP 出口(设了=开 otel)"),
|
|
328
337
|
r("OTEL_EXPORTER_OTLP_HEADERS", "observability", "secret", "OTLP 出口头(k=v CSV;常载鉴权头 ⇒ 按凭据处置)", { danger: SEC }),
|
|
@@ -395,8 +404,8 @@ export const CONFIG_CATALOG = [
|
|
|
395
404
|
r("SEMA_PIP_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 pip index 地址"),
|
|
396
405
|
r("SEMA_REGISTRY_ADMIN_TOKEN", "integrations", "secret", "写 center 的管理凭证(仅 boot 上报执行公钥用;刻意不入 ServiceConfig —— 不进 configHash/日志面)", { danger: SEC, scope: "bootstrap-only" }),
|
|
397
406
|
r("SEMA_REGISTRY_DRY_RUN", "integrations", "boolean", "center 腿 dry-run(拉而不应用)", { staticDefault: "false" }),
|
|
398
|
-
r(
|
|
399
|
-
r(
|
|
407
|
+
r(CENTER_CREDENTIAL_ENV.token, "integrations", "secret", `center 拉取凭据(值形;与 ${CENTER_CREDENTIAL_ENV.tokenFile} 二选一,都设=拒启)`, { danger: SEC }),
|
|
408
|
+
r(CENTER_CREDENTIAL_ENV.tokenFile, "integrations", "path", `center 拉取凭据文件(每条中心请求都重读 ⇒ 轮换 / 换账号不重启;与 ${CENTER_CREDENTIAL_ENV.token} 二选一,都设=拒启;内容两种类:裸行 = 静态 bearer / JSON 信封 user-session = 人的登录会话(可续签,S-698);文件不可读 / 为空 = 响亮 + 健康态 unreadable,不当作无凭证;内容读不成 / 多租户读到信封 = 拒启)`, { danger: SEC }),
|
|
400
409
|
r("SEMA_REGISTRY_URL", "integrations", "url", "sema-registry 地址(设了=remote 配置源开关)"),
|
|
401
410
|
r("SEMA_REGISTRY_WORKER", "integrations", "string", "本 worker 在 center 的名字(per-worker effective)"),
|
|
402
411
|
r("SEMA_UV_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 uv index 地址"),
|
|
@@ -10,6 +10,7 @@ import type { ServiceConfig } from "../config-types.js";
|
|
|
10
10
|
import { type SealedKeyOpener } from "../sealed-key.js";
|
|
11
11
|
import type { Logger } from "../observability/logger.js";
|
|
12
12
|
import type { EffectiveConfig } from "./types.js";
|
|
13
|
+
import { type ModelRefusalVerdict } from "../model-entry-refusal.js";
|
|
13
14
|
import { type ConfigApplyReport } from "./apply-ledger.js";
|
|
14
15
|
/** Apply center config OVER the env-derived config (mutates `config`). Empty domains keep env defaults. */
|
|
15
16
|
/** Replace a record's contents IN PLACE (clear keys, copy source) so holders of the SAME reference see the
|
|
@@ -41,6 +42,10 @@ export interface ApplyReport extends ConfigApplyReport {
|
|
|
41
42
|
/** 🔴 codex R2-F1:本候选是否**完整**应用 —— 有任何一组被拒 ⇒ false。调用方据此决定要不要把它发布成
|
|
42
43
|
* LKG / 推进 etag(一个「其余面提交了但某组被拒」的候选覆盖掉好 LKG,重启后就再也回不去了)。 */
|
|
43
44
|
readonly fullyApplied: boolean;
|
|
45
|
+
/** S-768:本候选目录的模型拒因判决 —— 逐条(`entries`,stage 判出的那份候选的,键已过名筛)+ 组拒码(`plane`,模型面组被拒时在场,
|
|
46
|
+
* 判决那刻当数据带出)。落地与组拒同一只来源:两者都描述**这一份**候选(组拒时引擎自己的拒因表不换代,这里仍报被拒的那份)。
|
|
47
|
+
* 候选不管模型面(零启用条目、零墓碑)⇒ `entries` 空、无 `plane`。`/health.modelRefusals` 与心跳的唯一数据源(config-center 投影)。 */
|
|
48
|
+
readonly modelRefusals: ModelRefusalVerdict;
|
|
44
49
|
}
|
|
45
50
|
/**
|
|
46
51
|
* S-668 波 2 —— 一次 apply 在**哪条腿**上跑,以及这条腿知道的**部署事实**。必填(没有缺省腿):安全轴上「忘了说是哪条
|
|
@@ -92,6 +97,17 @@ export type ModelPlaneOf = Pick<EffectiveConfig, ModelPlaneDomain>;
|
|
|
92
97
|
export declare function withModelPlaneOf<E extends EffectiveConfig>(target: E, source: ModelPlaneOf): E;
|
|
93
98
|
/** 「没有中心模型面」—— 组拒且无 LKG 时进程真正服务的那一代(env 表:没有目录、没有中心 collab 模板)。纯数据,每次新铸。 */
|
|
94
99
|
export declare function buildEnvOnlyModelPlane(): ModelPlaneOf;
|
|
100
|
+
/**
|
|
101
|
+
* S-768:组拒码 → 铸它的那条模型面不变量 —— **以码为键**(组拒码闭集的属主是 settings-schema `MODEL_PLANE_REFUSAL_CODES`;那边加一个码
|
|
102
|
+
* ⇒ 本表缺键 ⇒ 本仓 tsc 红)。模型面 stage 的组拒按**码**铸(码是判决那刻的数据),不变量 id 由本表派生 —— 一个 id 两个码
|
|
103
|
+
* (`model-route-endpoint`)时码不可能从 id 反推,反过来查才是全函数。
|
|
104
|
+
*/
|
|
105
|
+
export declare const PLANE_REFUSAL_INVARIANT_OF: {
|
|
106
|
+
readonly "config.default_model_missing": "default-model-resolvable";
|
|
107
|
+
readonly "config.model_refused_name_minted": "refused-names-not-minted";
|
|
108
|
+
readonly "config.model_route_endpoint_missing": "model-route-endpoint";
|
|
109
|
+
readonly "config.gateway_fallback_without_primary": "model-route-endpoint";
|
|
110
|
+
};
|
|
95
111
|
/**
|
|
96
112
|
* UNIFORM presence semantics across the center gates (center `f95438a` adopted this from our sentinel flag —
|
|
97
113
|
* SERVICE-INTEGRATION「Runtime 治理/限额闸」): every gate field is center `.optional()`, so
|
|
@@ -8,7 +8,8 @@ import { stageLimits } from "./stage-limits.js";
|
|
|
8
8
|
import { APPROVAL_GROUP_KEYS, BATCH1_LIMIT_KEYS } from "./hot-keys-registry.js";
|
|
9
9
|
import { buildDisplaySafeUrl } from "../trace/redact.js";
|
|
10
10
|
import { crossInvariant } from "../config-invariants.js";
|
|
11
|
-
import { judgeCatalogEntries, mintedRefusedNames, refusalEventOf, refusalWarnFields,
|
|
11
|
+
import { DEFAULT_MODEL_MISSING_CODE, REFUSED_NAME_MINTED_CODE, judgeCatalogEntries, mintedRefusedNames, refusalEventOf, refusalWarnFields, malformedDroppedCarrierRefusal } from "../model-entry-refusal.js";
|
|
12
|
+
import { judgeModelRouteEndpoint } from "../model-route-endpoint.js";
|
|
12
13
|
import { gateIntentUnserviceableReason } from "../approval.js";
|
|
13
14
|
import { refusedGroups } from "./apply-ledger.js";
|
|
14
15
|
const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
@@ -136,7 +137,7 @@ export function applyEffective(config, eff, logger, opts) {
|
|
|
136
137
|
modelPlane: modelPlaneGroupReport(staged.modelPlaneGroup),
|
|
137
138
|
},
|
|
138
139
|
};
|
|
139
|
-
opts.report({ ...report, fullyApplied: refusedGroups(report).length === 0 });
|
|
140
|
+
opts.report({ ...report, fullyApplied: refusedGroups(report).length === 0, modelRefusals: modelRefusalVerdictOf(staged.modelPlaneGroup) });
|
|
140
141
|
}
|
|
141
142
|
if (staged.limits.changed.length > 0)
|
|
142
143
|
logger?.info("sema_registry_limits", { version: staged.version, keys: staged.limits.changed });
|
|
@@ -206,6 +207,16 @@ function takeModelPlaneDomain(out, source, d) {
|
|
|
206
207
|
export function buildEnvOnlyModelPlane() {
|
|
207
208
|
return { models: { models: [], roles: {} } };
|
|
208
209
|
}
|
|
210
|
+
function modelRefusalVerdictOf(group) {
|
|
211
|
+
const entries = group.refused ?? {};
|
|
212
|
+
return group.rejected !== undefined ? { entries, plane: group.rejected.code } : { entries };
|
|
213
|
+
}
|
|
214
|
+
export const PLANE_REFUSAL_INVARIANT_OF = {
|
|
215
|
+
"config.default_model_missing": "default-model-resolvable",
|
|
216
|
+
"config.model_refused_name_minted": "refused-names-not-minted",
|
|
217
|
+
"config.model_route_endpoint_missing": "model-route-endpoint",
|
|
218
|
+
"config.gateway_fallback_without_primary": "model-route-endpoint",
|
|
219
|
+
};
|
|
209
220
|
function modelPlaneGroupReport(group) {
|
|
210
221
|
return {
|
|
211
222
|
applied: MODEL_PLANE_DOMAINS.flatMap((d) => (group.stages[d] !== undefined ? MODEL_PLANE_SURFACES[d] : [])),
|
|
@@ -215,14 +226,14 @@ function modelPlaneGroupReport(group) {
|
|
|
215
226
|
const ROLE_BINDING_UNAVAILABLE_HINT = "tasks resolving this role fail: alias/tier expansion cannot mint this name; the engine refuses it loud — bind the role to a usable model, or make the entry usable (see the entry's own warn)";
|
|
216
227
|
function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
217
228
|
const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
|
|
218
|
-
const
|
|
219
|
-
if (enabled.length === 0 &&
|
|
229
|
+
const verdict = judgeCatalogEntries(enabled, { anthropicRouteMounted: config.anthropic !== undefined }, eff.models?.droppedIdentities);
|
|
230
|
+
if (enabled.length === 0 && Object.keys(verdict.refused).length === 0 && verdict.malformed.length === 0)
|
|
220
231
|
return { stages: { collab: stageCollabDomain(eff) } };
|
|
221
232
|
{
|
|
222
|
-
const verdict = judgeCatalogEntries(enabled, { anthropicRouteMounted: config.anthropic !== undefined }, eff.models.droppedIdentities);
|
|
223
233
|
for (const [name, r] of Object.entries(verdict.refused))
|
|
224
234
|
logger?.warn(refusalEventOf(r.reason), refusalWarnFields(name, r));
|
|
225
|
-
const refuse = (
|
|
235
|
+
const refuse = (code, reason, detail = {}) => {
|
|
236
|
+
const invariant = PLANE_REFUSAL_INVARIANT_OF[code];
|
|
226
237
|
logger?.warn("sema_registry_model_plane_refused", {
|
|
227
238
|
version: typeof eff.version === "number" ? eff.version : 0,
|
|
228
239
|
invariant,
|
|
@@ -230,16 +241,10 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
230
241
|
...detail,
|
|
231
242
|
kept: "the model plane of this candidate is refused as a group — the current model plane (catalog / roles / tiers / refusals / allowlist, and the collab workflows that reference it) keeps serving and the other groups of this candidate still apply; the candidate is not fully applied (LKG / ETag stay on the last fully applied generation) and is re-judged next pull",
|
|
232
243
|
});
|
|
233
|
-
return { stages: {}, rejected: { keys: MODEL_PLANE_GROUP_KEYS, reason, invariant }, refused: verdict.refused };
|
|
244
|
+
return { stages: {}, rejected: { keys: MODEL_PLANE_GROUP_KEYS, reason, invariant, code }, refused: verdict.refused };
|
|
234
245
|
};
|
|
235
|
-
if (verdict.
|
|
236
|
-
|
|
237
|
-
entries: [...verdict.unreadable],
|
|
238
|
-
code: REFUSED_NAME_MINTED_CODE,
|
|
239
|
-
hint: "these catalog entries were dropped by the settings-schema read and the raw document could not be matched to the snapshot that was parsed (e.g. the local file changed between two reads) — their names cannot be proven, so no tombstone can be minted and the candidate's model plane is refused (a name nobody can prove cannot be shown not to be re-minted by the tier/alias expansion); the next read re-judges",
|
|
240
|
-
});
|
|
241
|
-
return refuse("refused-names-not-minted", unreadableDroppedRefusal(verdict.unreadable));
|
|
242
|
-
}
|
|
246
|
+
if (verdict.malformed.length > 0)
|
|
247
|
+
return refuse(REFUSED_NAME_MINTED_CODE, malformedDroppedCarrierRefusal(verdict.malformed), { entries: [...verdict.malformed] });
|
|
243
248
|
const usable = verdict.usable;
|
|
244
249
|
const models = {};
|
|
245
250
|
const modelApiKeyEnv = {};
|
|
@@ -318,13 +323,13 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
318
323
|
const nextView = { ...config, models, tiers: activeTiers, modelRefusals: verdict.refused };
|
|
319
324
|
const noDefault = crossInvariant("default-model-resolvable").check({ live: config, next: nextView });
|
|
320
325
|
if (noDefault !== undefined)
|
|
321
|
-
return refuse(
|
|
322
|
-
const
|
|
323
|
-
if (
|
|
324
|
-
return refuse(
|
|
326
|
+
return refuse(DEFAULT_MODEL_MISSING_CODE, noDefault);
|
|
327
|
+
const endpoint = judgeModelRouteEndpoint(nextView);
|
|
328
|
+
if (endpoint !== undefined)
|
|
329
|
+
return refuse(endpoint.code, endpoint.reason);
|
|
325
330
|
const minted = crossInvariant("refused-names-not-minted").check({ live: config, next: nextView });
|
|
326
331
|
if (minted !== undefined)
|
|
327
|
-
return refuse(
|
|
332
|
+
return refuse(REFUSED_NAME_MINTED_CODE, minted, { minted: mintedRefusedNames(nextView) });
|
|
328
333
|
for (const [role, spec] of Object.entries(roles)) {
|
|
329
334
|
const target = typeof spec === "string" ? spec : spec !== null && typeof spec === "object" && "model" in spec && typeof spec.model === "string" ? spec.model : undefined;
|
|
330
335
|
const refusal = target !== undefined && Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
|
|
@@ -604,7 +609,7 @@ export function logEffectiveDiff(config, eff, logger) {
|
|
|
604
609
|
wouldOverrideModels: enabled.length > 0,
|
|
605
610
|
wouldDefaultModel: plane.defaultName ?? null,
|
|
606
611
|
...(Object.keys(refused).length > 0 ? { wouldRefuse: Object.fromEntries(Object.entries(refused).map(([n, r]) => [n, r.code])) } : {}),
|
|
607
|
-
...(plane.rejected !== undefined ? { wouldRefuseModelPlane: { invariant: plane.rejected.invariant, reason: plane.rejected.reason } } : {}),
|
|
612
|
+
...(plane.rejected !== undefined ? { wouldRefuseModelPlane: { invariant: plane.rejected.invariant, code: plane.rejected.code, reason: plane.rejected.reason } } : {}),
|
|
608
613
|
currentDefaultModel: config.model.id,
|
|
609
614
|
models: { center: centerModels, envDerived: envModels },
|
|
610
615
|
roles: { center: centerRoles, current: config.roles },
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type CenterCredentialSource } from "../center-credential.js";
|
|
2
2
|
export declare const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
|
|
3
|
+
/** 8s:对中心请求 / 响应式调用的传输上界(拉配置 / 两条 principal 视图 / 内容拉取 / 凭证续签同一值)。 */
|
|
4
|
+
export declare const CENTER_PULL_TIMEOUT_MS = 8000;
|
|
3
5
|
/** 已装 settings-schema 的 major(机器派生)。版本号不成形 ⇒ 模块装载即抛(装坏了的包不该带着一个猜出来的
|
|
4
6
|
* major 去跟中心协商 wire 形)。 */
|
|
5
7
|
export declare const SETTINGS_SCHEMA_MAJOR: string;
|
|
@@ -49,6 +51,17 @@ export interface CenterRequestInit {
|
|
|
49
51
|
readonly timeoutMs: number;
|
|
50
52
|
readonly fetchImpl?: typeof fetch;
|
|
51
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* S-698 —— 凭证续签:`POST {issuer}/api/v1/auth/token/refresh`,form `refresh_token=…`(settings-schema `AUTH_V1_PATHS.tokenRefresh`)。
|
|
56
|
+
* · 以 `issuer` 为根(可带路径前缀;同源与否由续签器先判,本函数只装配);query / fragment 不带过去。
|
|
57
|
+
* · **不带 Authorization**:这条端点凭 refresh token 认人,手里的 access token 可能已经过期。
|
|
58
|
+
* · `redirect: "manual"`:3xx 不跟 —— POST 307/308 会把体(= refresh token)原样带去 `Location`,同源判据在重定向面前失效。
|
|
59
|
+
* 3xx 原样交回,续签器按「被拒」处置。
|
|
60
|
+
*/
|
|
61
|
+
export declare function centerTokenRefresh(issuer: string, refreshToken: string, init: {
|
|
62
|
+
readonly timeoutMs: number;
|
|
63
|
+
readonly fetchImpl?: typeof fetch;
|
|
64
|
+
}): Promise<Response>;
|
|
52
65
|
/**
|
|
53
66
|
* 对中心发一条请求。返回响应与**这次请求所用凭证的身份**(调用方据此给拿回来的候选打分箱标记,
|
|
54
67
|
* 见 `boot/config-center.ts` 的 LKG / prompt 状态分箱)。
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import settingsSchemaPackage from "@sema-agent/settings-schema/package.json" with { type: "json" };
|
|
2
|
+
import { AUTH_V1_PATHS } from "@sema-agent/settings-schema/api/auth";
|
|
2
3
|
import { CenterCredentialUnavailableError } from "../center-credential.js";
|
|
3
4
|
export const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
|
|
5
|
+
export const CENTER_PULL_TIMEOUT_MS = 8000;
|
|
4
6
|
export const SETTINGS_SCHEMA_MAJOR = (() => {
|
|
5
7
|
const version = settingsSchemaPackage.version;
|
|
6
8
|
const m = /^(\d+)\.\d+\.\d+/.exec(version);
|
|
@@ -19,6 +21,7 @@ export function pullViewQuery(view) {
|
|
|
19
21
|
function contentRoot(kind) {
|
|
20
22
|
switch (kind) {
|
|
21
23
|
case "user-jwt":
|
|
24
|
+
case "user-session":
|
|
22
25
|
return "/api/v1/me";
|
|
23
26
|
case "pull-token":
|
|
24
27
|
case "none":
|
|
@@ -45,10 +48,27 @@ function routePath(route, kind) {
|
|
|
45
48
|
return "/api/config/execution-keys";
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
|
-
|
|
49
|
-
const scheme =
|
|
51
|
+
function assertHttpScheme(url, what) {
|
|
52
|
+
const scheme = url.protocol;
|
|
50
53
|
if (scheme !== "http:" && scheme !== "https:")
|
|
51
|
-
throw new Error(
|
|
54
|
+
throw new Error(`${what} must be http(s), got "${scheme}"`);
|
|
55
|
+
}
|
|
56
|
+
export async function centerTokenRefresh(issuer, refreshToken, init) {
|
|
57
|
+
const url = new URL(issuer);
|
|
58
|
+
assertHttpScheme(url, "center credential issuer");
|
|
59
|
+
url.pathname = `${url.pathname.replace(/\/+$/, "")}${AUTH_V1_PATHS.tokenRefresh}`;
|
|
60
|
+
url.search = "";
|
|
61
|
+
url.hash = "";
|
|
62
|
+
return (init.fetchImpl ?? fetch)(url, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: { [SCHEMA_MAJOR_HEADER]: SETTINGS_SCHEMA_MAJOR, "content-type": "application/x-www-form-urlencoded" },
|
|
65
|
+
body: new URLSearchParams({ refresh_token: refreshToken }).toString(),
|
|
66
|
+
redirect: "manual",
|
|
67
|
+
signal: AbortSignal.timeout(init.timeoutMs),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export async function centerFetch(center, route, init) {
|
|
71
|
+
assertHttpScheme(new URL(center.baseUrl), "SEMA_REGISTRY_URL");
|
|
52
72
|
const cred = center.credential.read();
|
|
53
73
|
if (!cred.ok)
|
|
54
74
|
throw new CenterCredentialUnavailableError(cred.reason);
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
*/
|
|
50
50
|
export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
|
|
51
51
|
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, buildEnvOnlyModelPlane, } from "./apply-effective.js";
|
|
52
|
-
export { restartReasons, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type
|
|
52
|
+
export { restartReasons, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type EngineRestartSignal, } from "./restart-signal.js";
|
|
53
53
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
54
54
|
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";
|
|
55
55
|
export type { CenterSkillManifest, CenterMcpServer, CenterA2aPeer, CenterModel, CenterReadFace, EffectiveConfig, ExecutionRuling, SessionMirrorRuling, } from "./types.js";
|
|
@@ -2,9 +2,8 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readEffectiveWire, PrincipalCapsWire } from "@sema-agent/settings-schema";
|
|
3
3
|
import { centerPromptsFromEffective } from "../prompts-domain-validate.js";
|
|
4
4
|
import { splitFoldedReadWarnings, withDroppedIdentities } from "./read-warnings.js";
|
|
5
|
-
import { centerFetch } from "./center-request.js";
|
|
5
|
+
import { CENTER_PULL_TIMEOUT_MS, centerFetch } from "./center-request.js";
|
|
6
6
|
import { answerStillCurrent } from "../center-credential.js";
|
|
7
|
-
const CENTER_PULL_TIMEOUT_MS = 8000;
|
|
8
7
|
export async function fetchEffective(center, etag, fetchImpl = fetch, worker) {
|
|
9
8
|
const { res, identity } = await centerFetch(center, { kind: "effective", worker }, { etag, timeoutMs: CENTER_PULL_TIMEOUT_MS, fetchImpl });
|
|
10
9
|
if (res.status === 304)
|
|
@@ -19,9 +18,7 @@ export async function fetchEffective(center, etag, fetchImpl = fetch, worker) {
|
|
|
19
18
|
delete wire.prompts;
|
|
20
19
|
else
|
|
21
20
|
wire.prompts = centerPrompts;
|
|
22
|
-
const
|
|
23
|
-
const parsedModels = wire.models?.models;
|
|
24
|
-
const { domainErrors, droppedEntries, droppedIdentities } = splitFoldedReadWarnings(warnings, { raw: rawModels, parsed: Array.isArray(parsedModels) ? parsedModels : undefined });
|
|
21
|
+
const { domainErrors, droppedEntries, droppedIdentities } = splitFoldedReadWarnings(warnings);
|
|
25
22
|
const effective = withDroppedIdentities(wire, droppedIdentities);
|
|
26
23
|
return {
|
|
27
24
|
effective,
|