@sema-agent/server 7.123.0 → 7.124.1
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 +138 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +53 -51
- package/dist/approval-ask-audit-store.d.ts +27 -32
- package/dist/approval-ask-audit-store.js +10 -9
- package/dist/approval-crash-convergence.d.ts +2 -2
- package/dist/approval-crash-convergence.js +5 -5
- package/dist/approval-deny-reasons.d.ts +163 -51
- package/dist/approval-deny-reasons.js +79 -8
- package/dist/approval-policy-settlement.d.ts +4 -1
- package/dist/approval-policy-settlement.js +3 -2
- package/dist/approval-reconciler.d.ts +3 -3
- package/dist/approval-reconciler.js +10 -10
- package/dist/approval.d.ts +1 -1
- package/dist/approval.js +1 -1
- package/dist/audit.js +1 -1
- package/dist/auth-bridge.d.ts +8 -8
- package/dist/auth-bridge.js +6 -6
- package/dist/auth-keys.d.ts +2 -2
- package/dist/auto-mode-face.d.ts +1 -1
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/execution-env.js +1 -0
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +4 -4
- package/dist/boot/org-memory.d.ts +3 -3
- package/dist/boot/org-memory.js +5 -5
- package/dist/boot/resolve-spec.js +7 -7
- package/dist/boot/runner-deps.d.ts +1 -1
- package/dist/boot/runtime-caps.d.ts +1 -1
- package/dist/boot/runtime-caps.js +10 -10
- package/dist/boot/shutdown.d.ts +2 -2
- package/dist/boot/shutdown.js +2 -2
- package/dist/boot/side-query-lane.d.ts +3 -3
- package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
- package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
- package/dist/boot/stage-03-store-backend.d.ts +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +6 -6
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +6 -5
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +3 -3
- package/dist/boot/stage-09-leader.js +7 -7
- package/dist/boot/stage-10-http-server.d.ts +3 -3
- package/dist/boot/stage-10-http-server.js +19 -19
- package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
- package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
- package/dist/boot/{config-center.js → web-admin.js} +38 -22
- package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
- package/dist/brain.d.ts +1 -1
- package/dist/brain.js +1 -1
- package/dist/budget.d.ts +3 -3
- package/dist/canonical-json.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/center-wiring.d.ts +12 -5
- package/dist/capabilities/center-wiring.js +11 -2
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/team.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/center-credential-renewer.d.ts +1 -1
- package/dist/center-credential-renewer.js +7 -7
- package/dist/center-credential.d.ts +9 -9
- package/dist/center-credential.js +6 -6
- package/dist/config-catalog.d.ts +7 -10
- package/dist/config-catalog.js +15 -15
- package/dist/config-d-migration.d.ts +32 -0
- package/dist/config-d-migration.js +59 -0
- package/dist/config-invariants.d.ts +14 -4
- package/dist/config-invariants.js +31 -2
- package/dist/config-lkg.d.ts +26 -6
- package/dist/config-lkg.js +43 -14
- package/dist/config-provider.d.ts +23 -19
- package/dist/config-provider.js +24 -26
- package/dist/config-types.d.ts +52 -51
- package/dist/config.d.ts +9 -46
- package/dist/config.js +33 -92
- package/dist/execution-lane-caps.d.ts +1 -1
- package/dist/fleet/drain-entry.d.ts +1 -1
- package/dist/fleet/engine-signal.d.ts +8 -8
- package/dist/fleet/fleet-bus.d.ts +12 -3
- package/dist/fleet/fleet-bus.js +13 -9
- package/dist/fleet/serving-lease.d.ts +3 -3
- package/dist/fleet/serving-lease.js +1 -1
- package/dist/fleet-client.d.ts +7 -7
- package/dist/fleet-client.js +5 -4
- package/dist/fleet-lease.d.ts +3 -3
- package/dist/fleet-lease.js +2 -2
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/hosted-posture.d.ts +4 -4
- package/dist/http/admission.js +4 -4
- package/dist/http/checkpoint-ctx.d.ts +20 -3
- package/dist/http/checkpoint-ctx.js +13 -5
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/health-self-report.d.ts +4 -4
- package/dist/http/health-self-report.js +3 -3
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +4 -3
- package/dist/http/resume-legs.js +31 -18
- package/dist/http/route-ctx.d.ts +8 -0
- package/dist/http/routes/admin-config-refresh.d.ts +1 -1
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.d.ts +1 -1
- package/dist/http/routes/admin-drain.js +5 -4
- package/dist/http/routes/approvals-assistant.js +4 -4
- package/dist/http/routes/capabilities.js +5 -5
- package/dist/http/routes/config-catalog.js +2 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/runs.js +8 -4
- package/dist/http/routes/sessions-list.js +2 -1
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.js +3 -3
- package/dist/http/routes/tasks.js +3 -2
- package/dist/http/run-meta.d.ts +1 -1
- package/dist/http/run-meta.js +2 -2
- package/dist/http/server.d.ts +7 -7
- package/dist/http/server.js +1 -1
- package/dist/http/stored-body-replay.js +3 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/key-resolver.d.ts +1 -1
- package/dist/leader/wire.d.ts +2 -2
- package/dist/leader/wire.js +2 -1
- package/dist/main.js +1 -1
- package/dist/mcp-tool-faces.d.ts +2 -2
- package/dist/model-entry-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.js +4 -4
- package/dist/model-provider.d.ts +2 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-url.d.ts +1 -1
- package/dist/model-route-url.js +2 -1
- package/dist/model-select.d.ts +24 -21
- package/dist/model-select.js +22 -17
- package/dist/observability/metrics.d.ts +1 -1
- package/dist/org-memory-admission.d.ts +1 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -2
- package/dist/plugins/approval-ask-store-file.js +3 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
- package/dist/plugins/approval-ask-store-memory.js +10 -4
- package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
- package/dist/plugins/approval-ask-store-sql.js +20 -13
- package/dist/plugins/caching-session-store.d.ts +0 -10
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/file-run-store.js +9 -3
- package/dist/plugins/leader-run-store-sql.d.ts +3 -2
- package/dist/plugins/leader-run-store-sql.js +3 -2
- package/dist/plugins/local-session-store.d.ts +20 -27
- package/dist/plugins/local-session-store.js +19 -17
- package/dist/plugins/memory-run-store.js +9 -3
- package/dist/plugins/one-time-migrations.js +26 -2
- package/dist/plugins/scheduler-support.d.ts +26 -7
- package/dist/plugins/scheduler-support.js +96 -53
- package/dist/plugins/store-backend.js +1 -1
- package/dist/plugins/store-contracts.d.ts +20 -4
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -1
- package/dist/posture-source.d.ts +1 -1
- package/dist/read-face-posture.d.ts +3 -3
- package/dist/read-face-posture.js +1 -1
- package/dist/run-local.js +6 -3
- package/dist/runtime-caps-resolver.d.ts +4 -4
- package/dist/runtime-caps-resolver.js +2 -2
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sealed-key.d.ts +4 -4
- package/dist/sealed-key.js +3 -2
- package/dist/security.d.ts +3 -3
- package/dist/service-version.d.ts +1 -1
- package/dist/service-version.js +5 -0
- package/dist/session-titler.d.ts +0 -11
- package/dist/session-titler.js +3 -5
- package/dist/shared-memory-scope-authorizer.d.ts +4 -4
- package/dist/task-a2a.d.ts +1 -1
- package/dist/tool-approval.d.ts +43 -4
- package/dist/tool-approval.js +147 -98
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.d.ts +3 -3
- package/dist/trace/redact.d.ts +2 -23
- package/dist/trace/redact.js +17 -15
- package/dist/trace/self-minted-wire.d.ts +134 -3
- package/dist/trace/self-minted-wire.js +46 -9
- package/dist/url-refusal.d.ts +35 -0
- package/dist/url-refusal.js +15 -0
- package/dist/user-authored-text.d.ts +31 -0
- package/dist/user-authored-text.js +18 -0
- package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
- package/dist/{config-center → web-admin-client}/apply-effective.js +100 -119
- package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
- package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
- package/dist/{config-center → web-admin-client}/center-request.js +5 -4
- package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
- package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
- package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
- package/dist/{config-center → web-admin-client}/facade.js +2 -2
- package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
- package/dist/{config-center → web-admin-client}/http-client.js +12 -12
- package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/read-face.js +4 -4
- package/dist/{config-center → web-admin-client}/read-warnings.d.ts +23 -16
- package/dist/{config-center → web-admin-client}/read-warnings.js +33 -23
- package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
- package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
- package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
- package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
- package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
- package/dist/{config-center → web-admin-client}/types.d.ts +12 -39
- package/package.json +2 -2
- package/skills/find-skills.md +2 -2
- package/dist/url-shape.d.ts +0 -31
- package/dist/url-shape.js +0 -41
- /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
- /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
- /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/types.js +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { expandTiers, resolveTaskModel as coreResolveTaskModel } from "@sema-agent/core";
|
|
2
|
+
import { WEB_ADMIN_ENV, narrowAtMention, resolveActiveTiers, resolveCatalogDefaultModel } from "@sema-agent/settings-schema";
|
|
2
3
|
import { modelKeyPoison } from "../key-resolver.js";
|
|
4
|
+
import { readAtMention } from "../model-select.js";
|
|
3
5
|
import { applyAutoCompactWindow, buildModelCostKey, parseAutonomy, unmatchableToolNamesRefusal } from "../config.js";
|
|
4
6
|
import { buildModelCompat, readModelCompat } from "../model-compat.js";
|
|
5
7
|
import { buildModelHeadersKey } from "../model-entry-headers.js";
|
|
@@ -64,41 +66,27 @@ export function mutateInPlace(target, source) {
|
|
|
64
66
|
}
|
|
65
67
|
const appliedGeneration = new WeakMap();
|
|
66
68
|
const envRolesBaseline = new WeakMap();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
logger?.warn("sema_registry_at_model_allowlist_invalid", {
|
|
73
|
-
type: typeof raw,
|
|
74
|
-
hint: "models.atModelAllowlist must be an array of catalog model names — an unreadable allowlist is treated as ALLOWING NOTHING (fail-closed: a broken restriction must not read as 'no restriction'); tasks still run on the default/role models",
|
|
69
|
+
function stageAtMention(raw, usableNames, logger) {
|
|
70
|
+
const read = readAtMention(raw);
|
|
71
|
+
if (read === undefined) {
|
|
72
|
+
logger?.warn("web_admin_at_mention_invalid", {
|
|
73
|
+
hint: "models.atMention does not read as {mode:\"all\"} | {mode:\"none\"} | {mode:\"only\", models:[…]} — treated as {mode:\"none\"} (fail-closed: a broken restriction must not read as 'no restriction'); tasks still run on the default/role models",
|
|
75
74
|
});
|
|
76
|
-
return
|
|
75
|
+
return { mode: "none" };
|
|
77
76
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
const narrowed = narrowAtMention(read, usableNames);
|
|
78
|
+
if (read.mode === "only") {
|
|
79
|
+
const kept = new Set(narrowed.mode === "only" ? narrowed.models : []);
|
|
80
|
+
const dangling = read.models.filter((n) => !kept.has(n));
|
|
81
|
+
if (dangling.length > 0) {
|
|
82
|
+
logger?.warn("web_admin_at_mention_dangling", {
|
|
83
|
+
dangling,
|
|
84
|
+
narrowedTo: narrowed.mode,
|
|
85
|
+
hint: "these models.atMention names are not in this generation's usable catalog (not enabled, or unavailable on this host) — they allow nothing; when none is left the reading is {mode:\"none\"} and every user model pick is refused",
|
|
86
|
+
});
|
|
86
87
|
}
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
logger?.warn("sema_registry_at_model_allowlist_bad_entries", {
|
|
90
|
-
bad,
|
|
91
|
-
hint: "non-string / empty entries in models.atModelAllowlist allow NOTHING (they are kept as unresolvable placeholders, never dropped — dropping them would silently WIDEN the allowlist, and an all-bad list would collapse to the empty = allow-everything form)",
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
const dangling = names.filter((n) => n !== UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY && models[n] === undefined);
|
|
95
|
-
if (dangling.length > 0) {
|
|
96
|
-
logger?.warn("sema_registry_at_model_allowlist_dangling", {
|
|
97
|
-
dangling,
|
|
98
|
-
hint: "these @-mention allowlist entries name a model that is not in the enabled catalog — they allow nothing; an allowlist that is ENTIRELY dangling refuses every user model pick",
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
return names;
|
|
89
|
+
return narrowed;
|
|
102
90
|
}
|
|
103
91
|
export function applyEffective(config, eff, logger, opts) {
|
|
104
92
|
const staged = stageEffective(config, eff, logger, opts);
|
|
@@ -106,7 +94,7 @@ export function applyEffective(config, eff, logger, opts) {
|
|
|
106
94
|
const live = appliedGeneration.get(config);
|
|
107
95
|
const ordered = opts.generation !== undefined || staged.version > 0;
|
|
108
96
|
if (ordered && live !== undefined && gen < live) {
|
|
109
|
-
logger?.warn("
|
|
97
|
+
logger?.warn("web_admin_stale_generation_refused", { staged: staged.version, stagedOrdinal: gen, live, note: "an older staged generation must not overwrite a newer committed one — refused whole; next poll replays" });
|
|
110
98
|
return false;
|
|
111
99
|
}
|
|
112
100
|
const mp = staged.modelPlaneGroup.stages.models;
|
|
@@ -115,7 +103,7 @@ export function applyEffective(config, eff, logger, opts) {
|
|
|
115
103
|
opts.swapPlane({ models: mp.models, tiers: mp.activeTiers });
|
|
116
104
|
}
|
|
117
105
|
catch (err) {
|
|
118
|
-
logger?.warn("
|
|
106
|
+
logger?.warn("web_admin_model_plane_swap_refused", {
|
|
119
107
|
version: staged.version,
|
|
120
108
|
error: err instanceof Error ? err.message : String(err),
|
|
121
109
|
note: "the Runner refused the candidate model plane (swapDeps screens the whole candidate atomically — the current generation stays in force untouched); candidate refused whole, re-judged next poll (/health shows target≠applied while it persists)",
|
|
@@ -143,13 +131,13 @@ export function applyEffective(config, eff, logger, opts) {
|
|
|
143
131
|
opts.report({ ...report, fullyApplied: refusedGroups(report).length === 0, modelRefusals: modelRefusalVerdictOf(staged.modelPlaneGroup) });
|
|
144
132
|
}
|
|
145
133
|
if (staged.limits.changed.length > 0)
|
|
146
|
-
logger?.info("
|
|
134
|
+
logger?.info("web_admin_limits", { version: staged.version, keys: staged.limits.changed });
|
|
147
135
|
if (mp)
|
|
148
|
-
logger?.info("
|
|
136
|
+
logger?.info("web_admin_models", mp.infoLine);
|
|
149
137
|
if (staged.approvalGate.next !== undefined)
|
|
150
|
-
logger?.info("
|
|
138
|
+
logger?.info("web_admin_runtime", { version: staged.version, approvalRequire: staged.approvalGate.next });
|
|
151
139
|
if (Object.keys(staged.hot.infoLine).length > 0)
|
|
152
|
-
logger?.info("
|
|
140
|
+
logger?.info("web_admin_runtime_hot", staged.hot.infoLine);
|
|
153
141
|
for (const n of staged.modelPlaneGroup.stages.collab?.notices ?? []) {
|
|
154
142
|
if (n.level === "warn")
|
|
155
143
|
logger?.warn(n.msg, n.fields);
|
|
@@ -157,7 +145,7 @@ export function applyEffective(config, eff, logger, opts) {
|
|
|
157
145
|
logger?.info(n.msg, n.fields);
|
|
158
146
|
}
|
|
159
147
|
if (Object.keys(staged.projects).length > 0)
|
|
160
|
-
logger?.info("
|
|
148
|
+
logger?.info("web_admin_projects", { projects: Object.keys(staged.projects) });
|
|
161
149
|
return true;
|
|
162
150
|
}
|
|
163
151
|
function commitStaged(config, staged) {
|
|
@@ -172,7 +160,7 @@ function commitStaged(config, staged) {
|
|
|
172
160
|
config.model = config.models.default;
|
|
173
161
|
mutateInPlace(config.roles, mp.roles);
|
|
174
162
|
mutateInPlace(config.tiers, mp.activeTiers);
|
|
175
|
-
config.
|
|
163
|
+
config.atMention = mp.atMention;
|
|
176
164
|
}
|
|
177
165
|
if (staged.approvalGate.next !== undefined)
|
|
178
166
|
config.approvalRequire = staged.approvalGate.next;
|
|
@@ -191,7 +179,7 @@ function commitStaged(config, staged) {
|
|
|
191
179
|
}
|
|
192
180
|
export const MODEL_PLANE_DOMAINS = ["models", "collab"];
|
|
193
181
|
const MODEL_PLANE_SURFACES = {
|
|
194
|
-
models: ["models", "model", "modelRefusals", "droppedCatalogEntries", "modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "roles", "tiers", "
|
|
182
|
+
models: ["models", "model", "modelRefusals", "droppedCatalogEntries", "modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "roles", "tiers", "atMention"],
|
|
195
183
|
collab: ["collabWorkflows"],
|
|
196
184
|
};
|
|
197
185
|
const MODEL_PLANE_GROUP_KEYS = MODEL_PLANE_DOMAINS.flatMap((d) => MODEL_PLANE_SURFACES[d]);
|
|
@@ -239,7 +227,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
239
227
|
logger?.warn(refusalEventOf(r.reason), refusalWarnFields(name, r));
|
|
240
228
|
const refuse = (code, reason, detail = {}) => {
|
|
241
229
|
const invariant = PLANE_REFUSAL_INVARIANT_OF[code];
|
|
242
|
-
logger?.warn("
|
|
230
|
+
logger?.warn("web_admin_model_plane_refused", {
|
|
243
231
|
version: typeof eff.version === "number" ? eff.version : 0,
|
|
244
232
|
invariant,
|
|
245
233
|
reason,
|
|
@@ -258,16 +246,16 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
258
246
|
const envDefaults = { maxTokens: envModel.maxTokens, contextWindow: envModel.contextWindow, reasoning: envModel.reasoning, charsPerToken: envModel.charsPerToken, defaultThinking: envModel.defaultThinking, compat: envModel.compat };
|
|
259
247
|
for (const m of usable) {
|
|
260
248
|
if (m.baseUrl)
|
|
261
|
-
logger?.warn("
|
|
249
|
+
logger?.warn("web_admin_model_base_url", { model: m.name, baseUrl: buildDisplaySafeUrl(m.baseUrl), hint: "registry-directed gateway override — this model's key authenticates AGAINST THIS URL" });
|
|
262
250
|
models[m.name] = toModel(m, envDefaults, logger);
|
|
263
251
|
if (m.sealedApiKey) {
|
|
264
252
|
const s = m.sealedApiKey;
|
|
265
253
|
if (!sealedKeys) {
|
|
266
|
-
logger?.warn("
|
|
254
|
+
logger?.warn("web_admin_model_sealed_key_no_store", { model: m.name, publicKeyId: s.publicKeyId, hint: "no sealed-key store on this host (boot custody init failed or lane unwired) — key POISONED: tasks on this model fail loud (no apiKeyEnv fallback, no gateway fallback)" });
|
|
267
255
|
modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "no_key_store", publicKeyId: s.publicKeyId });
|
|
268
256
|
}
|
|
269
257
|
else if (!sealedKeys.has(s.publicKeyId)) {
|
|
270
|
-
logger?.warn("
|
|
258
|
+
logger?.warn("web_admin_model_sealed_key_no_private_key", { model: m.name, publicKeyId: s.publicKeyId, held: sealedKeys.ids(), hint: "ciphertext targets a private key this host doesn't hold (rotated away / other host) — key POISONED: tasks on this model fail loud until the operator re-pastes the key in the web UI (it seals against the newest registered public key)" });
|
|
271
259
|
modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "stale_ciphertext", publicKeyId: s.publicKeyId });
|
|
272
260
|
}
|
|
273
261
|
else {
|
|
@@ -275,7 +263,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
275
263
|
modelApiKeys[m.name] = sealedKeys.open(s.ciphertext, s.publicKeyId);
|
|
276
264
|
}
|
|
277
265
|
catch (err) {
|
|
278
|
-
logger?.warn("
|
|
266
|
+
logger?.warn("web_admin_model_sealed_key_open_failed", { model: m.name, publicKeyId: s.publicKeyId, err: String(err), hint: "corrupt/foreign ciphertext — key POISONED: tasks on this model fail loud (no apiKeyEnv fallback, no gateway fallback)" });
|
|
279
267
|
modelApiKeys[m.name] = modelKeyPoison({ source: "sealed", reason: "sealed_decrypt_failed", publicKeyId: s.publicKeyId, errKind: err instanceof Error ? err.name : "unknown" });
|
|
280
268
|
}
|
|
281
269
|
}
|
|
@@ -283,7 +271,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
283
271
|
else if (m.apiKeyEnv) {
|
|
284
272
|
modelApiKeyEnv[m.name] = m.apiKeyEnv;
|
|
285
273
|
if (!process.env[m.apiKeyEnv])
|
|
286
|
-
logger?.warn("
|
|
274
|
+
logger?.warn("web_admin_model_key_env_missing", { model: m.name, apiKeyEnv: m.apiKeyEnv, hint: `env var ${m.apiKeyEnv} not set (or empty) in this service — key POISONED: tasks on this model fail loud naming ${m.apiKeyEnv} (no gateway fallback) until it is set; or drop apiKeyEnv from the entry to use the gateway key on purpose` });
|
|
287
275
|
}
|
|
288
276
|
}
|
|
289
277
|
const quotaWeights = {};
|
|
@@ -305,16 +293,6 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
305
293
|
centerRoles[role] = { select: tgt.select };
|
|
306
294
|
}
|
|
307
295
|
const roles = { ...envRolesBaseline.get(config), ...centerRoles };
|
|
308
|
-
let picked;
|
|
309
|
-
if (usable.length > 0) {
|
|
310
|
-
picked = resolveDefaultModelName(eff, (n) => models[n] !== undefined, usable[0].name, (source, name) => logger?.warn("sema_registry_default_dangling", {
|
|
311
|
-
source,
|
|
312
|
-
...(Object.hasOwn(verdict.refused, name) ? refusedNameLogField("name", name) : { name }),
|
|
313
|
-
...(Object.hasOwn(verdict.refused, name) ? { unavailable: verdict.refused[name].code } : {}),
|
|
314
|
-
hint: "explicit default names a model that is not in the usable catalog (not enabled, or unavailable on this host — see `unavailable`) — falling to the next source",
|
|
315
|
-
}));
|
|
316
|
-
models.default = models[picked.name];
|
|
317
|
-
}
|
|
318
296
|
const activeTiers = {};
|
|
319
297
|
for (const [tier, target] of Object.entries(resolveActiveTiers(eff.models) ?? {})) {
|
|
320
298
|
if (models[target] !== undefined) {
|
|
@@ -322,7 +300,39 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
322
300
|
continue;
|
|
323
301
|
}
|
|
324
302
|
const refusal = Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
|
|
325
|
-
logger?.warn("
|
|
303
|
+
logger?.warn("web_admin_tier_binding_unavailable", { tier, ...(refusal !== undefined ? refusedNameLogField("model", target) : { model: target }), ...(refusal !== undefined ? { code: refusal.code } : {}), hint: "this tier binding names a model that is not in this generation's usable catalog — the binding is dropped (the tier word falls to the next lower bound tier, core's ladder); fix the entry or rebind the tier" });
|
|
304
|
+
}
|
|
305
|
+
let picked;
|
|
306
|
+
if (usable.length > 0) {
|
|
307
|
+
const decided = resolveCatalogDefaultModel(eff.models, (n) => models[n] !== undefined, usable[0].name);
|
|
308
|
+
switch (decided.kind) {
|
|
309
|
+
case "named":
|
|
310
|
+
for (const d of decided.dangling) {
|
|
311
|
+
logger?.warn("web_admin_default_dangling", {
|
|
312
|
+
source: d.source,
|
|
313
|
+
...(Object.hasOwn(verdict.refused, d.name) ? refusedNameLogField("name", d.name) : { name: d.name }),
|
|
314
|
+
...(Object.hasOwn(verdict.refused, d.name) ? { unavailable: verdict.refused[d.name].code } : {}),
|
|
315
|
+
hint: "explicit default names a model that is not in the usable catalog (not enabled, or unavailable on this host — see `unavailable`) — falling to the next source",
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
picked = { name: decided.name, source: decided.source };
|
|
319
|
+
break;
|
|
320
|
+
case "select": {
|
|
321
|
+
const engine = engineDefaultOfSelect(decided.select, models, activeTiers);
|
|
322
|
+
if ("unresolved" in engine) {
|
|
323
|
+
return refuse(DEFAULT_MODEL_MISSING_CODE, `this catalog candidate has no default model [${DEFAULT_MODEL_MISSING_CODE}] — roles.default is a {select} that the engine resolves to no usable catalog entry ` +
|
|
324
|
+
"(no enabled entry this host can serve matches its criteria, and no tier binding catches the default role); widen the criteria, enable a matching entry, or bind roles.default to a model by name", { engine: engine.unresolved });
|
|
325
|
+
}
|
|
326
|
+
picked = { name: engine.name, source: "roles.default.select" };
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
default: {
|
|
330
|
+
const unhandled = decided;
|
|
331
|
+
throw new Error(`catalog default: unknown kind ${JSON.stringify(unhandled)}`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (picked !== undefined)
|
|
335
|
+
models.default = models[picked.name];
|
|
326
336
|
}
|
|
327
337
|
const nextView = { ...config, models, tiers: activeTiers, modelRefusals: verdict.refused };
|
|
328
338
|
const noDefault = crossInvariant("default-model-resolvable").check({ live: config, next: nextView });
|
|
@@ -338,10 +348,10 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
338
348
|
const target = typeof spec === "string" ? spec : spec !== null && typeof spec === "object" && "model" in spec && typeof spec.model === "string" ? spec.model : undefined;
|
|
339
349
|
const refusal = target !== undefined && Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
|
|
340
350
|
if (refusal !== undefined) {
|
|
341
|
-
logger?.warn("
|
|
351
|
+
logger?.warn("web_admin_role_binding_unavailable", { role, ...refusedNameLogField("model", target), code: refusal.code, hint: ROLE_BINDING_UNAVAILABLE_HINT });
|
|
342
352
|
}
|
|
343
353
|
}
|
|
344
|
-
const
|
|
354
|
+
const atMention = stageAtMention(eff.models?.atMention, new Set(usable.map((m) => m.name)), logger);
|
|
345
355
|
const defaultModel = models.default;
|
|
346
356
|
const plane = {
|
|
347
357
|
models,
|
|
@@ -352,8 +362,8 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
|
|
|
352
362
|
quotaWeights,
|
|
353
363
|
roles,
|
|
354
364
|
activeTiers,
|
|
355
|
-
|
|
356
|
-
infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? refusedNamesLogFields("unavailable", Object.keys(verdict.refused)) : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers),
|
|
365
|
+
atMention,
|
|
366
|
+
infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? refusedNamesLogFields("unavailable", Object.keys(verdict.refused)) : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atMention: atMention.mode, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
|
|
357
367
|
};
|
|
358
368
|
return { stages: { models: plane, collab: stageCollabDomain(eff) }, defaultName: picked.name, refused: verdict.refused };
|
|
359
369
|
}
|
|
@@ -362,23 +372,23 @@ function stageCollabDomain(eff) {
|
|
|
362
372
|
const projected = projectCollabToWorkflows(eff.collab?.templates);
|
|
363
373
|
const notices = [];
|
|
364
374
|
if (Object.keys(projected.workflows).length > 0)
|
|
365
|
-
notices.push({ level: "info", msg: "
|
|
375
|
+
notices.push({ level: "info", msg: "web_admin_collab_workflows", fields: { workflows: Object.keys(projected.workflows) } });
|
|
366
376
|
if (projected.skipped.length > 0)
|
|
367
|
-
notices.push({ level: "warn", msg: "
|
|
377
|
+
notices.push({ level: "warn", msg: "web_admin_collab_skipped", fields: { skipped: projected.skipped } });
|
|
368
378
|
if (projected.notes.length > 0)
|
|
369
|
-
notices.push({ level: "info", msg: "
|
|
379
|
+
notices.push({ level: "info", msg: "web_admin_collab_notes", fields: { notes: projected.notes } });
|
|
370
380
|
return { workflows: projected.workflows, notices };
|
|
371
381
|
}
|
|
372
382
|
function stageEffective(config, eff, logger, opts) {
|
|
373
383
|
if (opts.leg === "boot" && !eff.version) {
|
|
374
|
-
logger?.warn("
|
|
384
|
+
logger?.warn("web_admin_unpublished", {
|
|
375
385
|
version: eff.version,
|
|
376
386
|
note: "empty/unpublished effective config — using env + built-in fallback; publish a config or unset CONFIG_PUBLISH_MODE",
|
|
377
387
|
});
|
|
378
388
|
}
|
|
379
389
|
const modelPlaneGroup = stageModelPlane(config, eff, logger, opts.sealedKeys);
|
|
380
390
|
const approvalGate = stageApprovalGate(config, eff.governance, opts, logger);
|
|
381
|
-
const limits = stageLimits(config, { version: typeof eff.version === "number" ? eff.version : 0, ...("limits" in eff ? { limits: eff.limits } : {})
|
|
391
|
+
const limits = stageLimits(config, { version: typeof eff.version === "number" ? eff.version : 0, ...("limits" in eff ? { limits: eff.limits } : {}) }, logger);
|
|
382
392
|
const hot = stageRuntimeHot(config, eff.governance, logger);
|
|
383
393
|
const projDomain = eff.projects;
|
|
384
394
|
const rawProjects = (projDomain && typeof projDomain === "object" && !Array.isArray(projDomain) &&
|
|
@@ -405,7 +415,6 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
405
415
|
projects,
|
|
406
416
|
};
|
|
407
417
|
}
|
|
408
|
-
export const RUNTIME_GATE_KEYS = ["rateLimitPerMin", "maxTaskCostUsd", "maxTaskTokens", "maxPrincipalCostUsd", "costQuotaWindowSec"];
|
|
409
418
|
export function centerManagedConfigKeys(eff) {
|
|
410
419
|
const out = new Set();
|
|
411
420
|
if (!eff)
|
|
@@ -416,12 +425,6 @@ export function centerManagedConfigKeys(eff) {
|
|
|
416
425
|
if (Object.prototype.hasOwnProperty.call(limits, k))
|
|
417
426
|
out.add(k);
|
|
418
427
|
}
|
|
419
|
-
const rt = eff.runtime;
|
|
420
|
-
if (rt) {
|
|
421
|
-
for (const k of RUNTIME_GATE_KEYS)
|
|
422
|
-
if (runtimeGatePresent(rt, k))
|
|
423
|
-
out.add(k);
|
|
424
|
-
}
|
|
425
428
|
if (eff.governance) {
|
|
426
429
|
if (eff.governance.autonomy !== undefined)
|
|
427
430
|
out.add("autonomy");
|
|
@@ -443,16 +446,10 @@ export function centerManagedConfigKeys(eff) {
|
|
|
443
446
|
}
|
|
444
447
|
if ((eff.models?.models ?? []).some((m) => m.enabled !== false))
|
|
445
448
|
out.add("models");
|
|
446
|
-
if (eff.models?.
|
|
447
|
-
out.add("
|
|
449
|
+
if (eff.models?.atMention !== undefined)
|
|
450
|
+
out.add("atMention");
|
|
448
451
|
return out;
|
|
449
452
|
}
|
|
450
|
-
export function runtimeGatePresent(rt, key) {
|
|
451
|
-
const v = rt[key];
|
|
452
|
-
if (key === "costQuotaWindowSec")
|
|
453
|
-
return typeof v === "number" && v > 0;
|
|
454
|
-
return typeof v === "number";
|
|
455
|
-
}
|
|
456
453
|
const KEPT_WOULD_MATCH_NOTHING = "the prior approvalRequire is unchanged — the published list would have silently matched nothing";
|
|
457
454
|
const KEPT_WOULD_REMOVE_GATE = "the prior approvalRequire is unchanged — coercing this malformed value would have silently REMOVED the approval gate";
|
|
458
455
|
const KEPT_WOULD_NEVER_PARK = "the prior approvalRequire is unchanged — this deployment has nowhere to park a gated call, so applying the list would leave it silently unenforced";
|
|
@@ -477,7 +474,7 @@ function stageApprovalRequire(raw) {
|
|
|
477
474
|
return {
|
|
478
475
|
ok: false,
|
|
479
476
|
detail: unmatchable.message,
|
|
480
|
-
reason: `published governance.approvalRequire has ${unmatchable.names.length} entr${unmatchable.names.length === 1 ? "y" : "ies"} that match no live tool (retired name / incomplete protocol name / server segment the engine never mints / CC-form rule) — the offending entries are named in the
|
|
477
|
+
reason: `published governance.approvalRequire has ${unmatchable.names.length} entr${unmatchable.names.length === 1 ? "y" : "ies"} that match no live tool (retired name / incomplete protocol name / server segment the engine never mints / CC-form rule) — the offending entries are named in the web_admin_approval_require_invalid log line`,
|
|
481
478
|
names: unmatchable.names,
|
|
482
479
|
kept: KEPT_WOULD_MATCH_NOTHING,
|
|
483
480
|
};
|
|
@@ -504,7 +501,7 @@ function stageApprovalGate(config, gov, leg, logger) {
|
|
|
504
501
|
if (!approvalEnvBaseline.has(config))
|
|
505
502
|
approvalEnvBaseline.set(config, Object.freeze([...config.approvalRequire]));
|
|
506
503
|
const refuse = (r) => {
|
|
507
|
-
logger?.warn("
|
|
504
|
+
logger?.warn("web_admin_approval_require_invalid", { keys: [...APPROVAL_GROUP_KEYS], detail: r.detail, names: [...r.names], kept: r.kept });
|
|
508
505
|
return { rejected: { keys: [...APPROVAL_GROUP_KEYS], reason: r.reason } };
|
|
509
506
|
};
|
|
510
507
|
const raw = gov?.approvalRequire;
|
|
@@ -524,27 +521,18 @@ function stageApprovalGate(config, gov, leg, logger) {
|
|
|
524
521
|
const same = next.length === config.approvalRequire.length && next.every((n, i) => n === config.approvalRequire[i]);
|
|
525
522
|
return same ? {} : { next };
|
|
526
523
|
}
|
|
527
|
-
function legacyGateValues(rt) {
|
|
528
|
-
const out = {};
|
|
529
|
-
if (!rt)
|
|
530
|
-
return out;
|
|
531
|
-
for (const k of RUNTIME_GATE_KEYS)
|
|
532
|
-
if (runtimeGatePresent(rt, k))
|
|
533
|
-
out[k] = rt[k];
|
|
534
|
-
return out;
|
|
535
|
-
}
|
|
536
524
|
export function applyRuntimeGates(config, eff, leg, logger) {
|
|
537
525
|
const approval = stageApprovalGate(config, eff?.governance, leg, logger);
|
|
538
526
|
if (approval.next !== undefined)
|
|
539
527
|
config.approvalRequire = approval.next;
|
|
540
|
-
const limits = stageLimits(config, { version: 0,
|
|
528
|
+
const limits = stageLimits(config, { version: 0, ...(eff !== undefined && "limits" in eff ? { limits: eff.limits } : {}) }, logger);
|
|
541
529
|
for (const [k, v] of limits.assignments)
|
|
542
530
|
config[k] = v;
|
|
543
531
|
for (const k of limits.deletions)
|
|
544
532
|
delete config[k];
|
|
545
533
|
const info = { ...(approval.next !== undefined ? { approvalRequire: approval.next } : {}), ...Object.fromEntries(limits.assignments) };
|
|
546
534
|
if (Object.keys(info).length > 0)
|
|
547
|
-
logger?.info("
|
|
535
|
+
logger?.info("web_admin_runtime", info);
|
|
548
536
|
}
|
|
549
537
|
function stageRuntimeHot(config, rt, logger) {
|
|
550
538
|
const infoLine = {};
|
|
@@ -558,7 +546,7 @@ function stageRuntimeHot(config, rt, logger) {
|
|
|
558
546
|
if (rt?.commandPolicy !== undefined) {
|
|
559
547
|
const errors = validateCommandRules(rt.commandPolicy);
|
|
560
548
|
if (errors.length > 0) {
|
|
561
|
-
logger?.error("
|
|
549
|
+
logger?.error("web_admin_commandpolicy_invalid", { errors, kept: config.commandPolicy?.length ?? 0 });
|
|
562
550
|
}
|
|
563
551
|
else if (JSON.stringify(config.commandPolicy) !== JSON.stringify(rt.commandPolicy)) {
|
|
564
552
|
setCommandPolicy = true;
|
|
@@ -580,26 +568,19 @@ export function applyRuntimeHot(config, gov, logger) {
|
|
|
580
568
|
if (s.setCommandPolicy)
|
|
581
569
|
config.commandPolicy = s.commandPolicyNext;
|
|
582
570
|
if (Object.keys(s.infoLine).length > 0)
|
|
583
|
-
logger?.info("
|
|
571
|
+
logger?.info("web_admin_runtime_hot", s.infoLine);
|
|
584
572
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
{
|
|
593
|
-
...DEFAULT_ROLE_TIER_LADDER.map((t) => ({ source: `tierGroup.${t}`, name: activeTiers[t] })),
|
|
594
|
-
];
|
|
595
|
-
for (const c of candidates) {
|
|
596
|
-
if (!c.name)
|
|
597
|
-
continue;
|
|
598
|
-
if (has(c.name))
|
|
599
|
-
return { name: c.name, source: c.source };
|
|
600
|
-
onDangling?.(c.source, c.name);
|
|
573
|
+
function engineDefaultOfSelect(select, models, activeTiers) {
|
|
574
|
+
const catalog = expandTiers(models, activeTiers) ?? models;
|
|
575
|
+
let picked;
|
|
576
|
+
try {
|
|
577
|
+
picked = coreResolveTaskModel({ modelRole: "default" }, { models: catalog, roles: { default: { select } } }).model;
|
|
578
|
+
}
|
|
579
|
+
catch (err) {
|
|
580
|
+
return { unresolved: err instanceof Error ? err.message : String(err) };
|
|
601
581
|
}
|
|
602
|
-
|
|
582
|
+
const name = Object.keys(models).find((n) => models[n] === picked);
|
|
583
|
+
return name !== undefined ? { name } : { unresolved: "the engine picked a model object that is not an entry of the usable catalog" };
|
|
603
584
|
}
|
|
604
585
|
export function logEffectiveDiff(config, eff, logger) {
|
|
605
586
|
const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
|
|
@@ -608,8 +589,8 @@ export function logEffectiveDiff(config, eff, logger) {
|
|
|
608
589
|
const centerModels = enabled.map((m) => ({ name: m.name, id: m.id, provider: m.provider, apiKeyEnv: m.apiKeyEnv ?? null, tier: m.tier ?? null }));
|
|
609
590
|
const envModels = Object.entries(config.models).map(([name, m]) => ({ name, id: m.id, provider: m.provider }));
|
|
610
591
|
const centerRoles = Object.fromEntries(Object.entries(eff.models?.roles ?? {}).map(([r, t]) => [r, "model" in t ? t.model : { select: t.select }]));
|
|
611
|
-
logger?.warn("
|
|
612
|
-
note:
|
|
592
|
+
logger?.warn("web_admin_dry_run", {
|
|
593
|
+
note: `DRY RUN — web-admin config NOT applied; unset ${WEB_ADMIN_ENV.dryRun} to go live`,
|
|
613
594
|
version: eff.version,
|
|
614
595
|
wouldOverrideModels: enabled.length > 0,
|
|
615
596
|
wouldDefaultModel: plane.defaultName ?? null,
|
|
@@ -621,9 +602,9 @@ export function logEffectiveDiff(config, eff, logger) {
|
|
|
621
602
|
collab: { centerWouldRegister: Object.keys(projectCollabToWorkflows(eff.collab?.templates).workflows) },
|
|
622
603
|
skills: { centerWouldLoad: (eff.skills?.skills ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
|
|
623
604
|
mcp: { centerWouldRegister: (eff.mcp?.servers ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
|
|
624
|
-
|
|
625
|
-
center: eff.
|
|
626
|
-
current: Object.fromEntries(
|
|
605
|
+
limits: {
|
|
606
|
+
center: eff.limits ?? null,
|
|
607
|
+
current: Object.fromEntries(BATCH1_LIMIT_KEYS.map((k) => [k, config[k]])),
|
|
627
608
|
},
|
|
628
609
|
governance: {
|
|
629
610
|
center: eff.governance ?? null,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* `CONFIG_PROVIDER=local` 下是 registry-core file-store 的内容哈希(u53,上游契约只承诺
|
|
10
10
|
* 「同 files ⇒ 同 number」,从未承诺序)——把它当序数消费,哈希更小的合法新代会被永久
|
|
11
11
|
* 静默忽略且本账双盲(target/applied 都不动)。修后:**推进判断吃收货处铸的进程内单调
|
|
12
|
-
* ordinal**(
|
|
12
|
+
* ordinal**(web-admin `generationOf`;等版本重放沿用同 ordinal=幂等语义保留),
|
|
13
13
|
* `version` 只做对账身份标——configTargetVersion 语义从「max(见过)」变为「**最后**见过的
|
|
14
14
|
* 源代」(remote 单调腿二者等价;local 腿这才是真相)。
|
|
15
15
|
*
|
|
@@ -36,7 +36,7 @@ export type ConfigApplyGroup = (typeof CONFIG_APPLY_GROUPS)[number];
|
|
|
36
36
|
/**
|
|
37
37
|
* S-810(车QU codex r1 [high] F1,验真后修)—— 一组在**热腿**被整组拒之后,新进程在 **boot 腿**上对同一候选重判会怎样。每组一格的闭集
|
|
38
38
|
* (`satisfies Record<ConfigApplyGroup, …>`:加一组 ⇒ 这里编译红,不许静默落进某一格)。部分应用的拍据此决定能不能发重启信号
|
|
39
|
-
* (`boot/
|
|
39
|
+
* (`boot/web-admin.ts` 的 `signalRestart`):
|
|
40
40
|
* · `"refuses-group"` —— boot 腿同一判据、同样整组拒,其余组照落 ⇒ 重启收敛(新进程的 boot 快照就是这份候选,同一差异不再签)。
|
|
41
41
|
* `limitsHttp`:`stageLimits` 不分腿。
|
|
42
42
|
* · `"may-refuse-boot"` —— boot 腿的结局取决于那一刻的部署事实,可能整个进程起不来 ⇒ 照信号重启不保证收敛(可能把一台还在服务的
|
|
@@ -5,10 +5,10 @@ export declare const CENTER_PULL_TIMEOUT_MS = 8000;
|
|
|
5
5
|
/** 已装 settings-schema 的 major(机器派生)。版本号不成形 ⇒ 模块装载即抛(装坏了的包不该带着一个猜出来的
|
|
6
6
|
* major 去跟中心协商 wire 形)。 */
|
|
7
7
|
export declare const SETTINGS_SCHEMA_MAJOR: string;
|
|
8
|
-
/** 一个中心端点 = 基址 + 凭证来源(`ServiceConfig.
|
|
8
|
+
/** 一个中心端点 = 基址 + 凭证来源(`ServiceConfig.webAdmin` 结构上满足)。 */
|
|
9
9
|
export interface CenterEndpoint {
|
|
10
10
|
readonly baseUrl: string;
|
|
11
|
-
/** 活性组的基址(env `
|
|
11
|
+
/** 活性组的基址(env `SEMA_WEB_ADMIN_LIVE_URL` 的回声;缺席 ⇒ 活性组也打 {@link baseUrl})。「缺席 = 主基址」这条规则只在 {@link centerFetch} 读。 */
|
|
12
12
|
readonly liveBaseUrl?: string | undefined;
|
|
13
13
|
readonly credential: CenterCredentialSource;
|
|
14
14
|
}
|
|
@@ -21,11 +21,11 @@ export interface CenterEndpoint {
|
|
|
21
21
|
* (7.105.0 及以前累加器与用量环都挂在公告地址上,不公告的引擎于是从不报用量,租约的已耗量只剩中心自己的估计)。
|
|
22
22
|
*/
|
|
23
23
|
export declare function centerLaneConfigured<C extends {
|
|
24
|
-
readonly
|
|
24
|
+
readonly webAdmin?: (CenterEndpoint & {
|
|
25
25
|
readonly worker?: string | undefined;
|
|
26
26
|
}) | undefined;
|
|
27
27
|
}>(cfg: C): cfg is C & {
|
|
28
|
-
readonly
|
|
28
|
+
readonly webAdmin: NonNullable<C["webAdmin"]> & {
|
|
29
29
|
readonly worker: string;
|
|
30
30
|
};
|
|
31
31
|
};
|
|
@@ -73,8 +73,8 @@ export interface CenterRequestInit {
|
|
|
73
73
|
readonly fetchImpl?: typeof fetch;
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
76
|
-
* 「后面要拼路径的基址」的拒句 —— 中心基址三席(`
|
|
77
|
-
* 公告地址(`FLEET_ADVERTISE_ADDRESS`,中心往它后面拼 `/v1/...`)同读。判官是
|
|
76
|
+
* 「后面要拼路径的基址」的拒句 —— 中心基址三席(`SEMA_WEB_ADMIN_URL` / `SEMA_WEB_ADMIN_LIVE_URL` / `SEMA_WEB_ADMIN_WEB_URL`)与本实例的
|
|
77
|
+
* 公告地址(`FLEET_ADVERTISE_ADDRESS`,中心往它后面拼 `/v1/...`)同读。判官是 settings-schema 的 `httpUrlFlaws`(层级式 http(s) URL;Node 的 fetch
|
|
78
78
|
* 认 `file://` / `data:`,配错的地址不许变成读本地文件),本函数只声明这一席禁哪几件({@link PATH_BASE_FORBIDS})。起服时 `config.ts` 判
|
|
79
79
|
* (拒启),请求期 {@link centerFetch} 对同一份基址再判一次(自拼 `ServiceConfig` 的嵌入方也吃同一条规则)。合格 ⇒ `undefined`;否则交回
|
|
80
80
|
* `<subject> <规则词>; got "<展示面安全形>"` —— 拒句里没有从值里解析出来的任何记号(scheme / userinfo / 主机名),捕获点照原样落日志即可。
|
|
@@ -93,7 +93,7 @@ export declare function centerTokenRefresh(issuer: string, refreshToken: string,
|
|
|
93
93
|
}): Promise<Response>;
|
|
94
94
|
/**
|
|
95
95
|
* 对中心发一条请求。返回响应与**这次请求所用凭证的身份**(调用方据此给拿回来的候选打分箱标记,
|
|
96
|
-
* 见 `boot/
|
|
96
|
+
* 见 `boot/web-admin.ts` 的 LKG / prompt 状态分箱)。
|
|
97
97
|
*/
|
|
98
98
|
export declare function centerFetch(center: CenterEndpoint, route: CenterRoute, init: CenterRequestInit): Promise<{
|
|
99
99
|
res: Response;
|
|
@@ -2,7 +2,8 @@ import settingsSchemaPackage from "@sema-agent/settings-schema/package.json" wit
|
|
|
2
2
|
import { AUTH_V1_PATHS } from "@sema-agent/settings-schema/api/auth";
|
|
3
3
|
import { FLEET_LIVE_PATHS, FLEET_PATHS } from "@sema-agent/settings-schema/fleet";
|
|
4
4
|
import { CenterCredentialUnavailableError } from "../center-credential.js";
|
|
5
|
-
import {
|
|
5
|
+
import { WEB_ADMIN_ENV, httpUrlFlaws } from "@sema-agent/settings-schema";
|
|
6
|
+
import { buildHttpUrlRefusal } from "../url-refusal.js";
|
|
6
7
|
export const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
|
|
7
8
|
export const CENTER_PULL_TIMEOUT_MS = 8000;
|
|
8
9
|
export const SETTINGS_SCHEMA_MAJOR = (() => {
|
|
@@ -13,7 +14,7 @@ export const SETTINGS_SCHEMA_MAJOR = (() => {
|
|
|
13
14
|
return m[1];
|
|
14
15
|
})();
|
|
15
16
|
export function centerLaneConfigured(cfg) {
|
|
16
|
-
const cc = cfg.
|
|
17
|
+
const cc = cfg.webAdmin;
|
|
17
18
|
return cc !== undefined && cc.baseUrl !== "" && cc.credential.declared && cc.worker !== undefined && cc.worker !== "";
|
|
18
19
|
}
|
|
19
20
|
export function pullViewQuery(view) {
|
|
@@ -83,7 +84,7 @@ export async function centerTokenRefresh(issuer, refreshToken, init) {
|
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
86
|
export async function centerFetch(center, route, init) {
|
|
86
|
-
assertPathBaseUrl(center.baseUrl,
|
|
87
|
+
assertPathBaseUrl(center.baseUrl, WEB_ADMIN_ENV.url);
|
|
87
88
|
const cred = center.credential.read();
|
|
88
89
|
if (!cred.ok)
|
|
89
90
|
throw new CenterCredentialUnavailableError(cred.reason);
|
|
@@ -97,7 +98,7 @@ export async function centerFetch(center, route, init) {
|
|
|
97
98
|
const path = routePath(route, cred.kind);
|
|
98
99
|
const liveBase = FLEET_LIVE_PATHS.includes(path) ? center.liveBaseUrl : undefined;
|
|
99
100
|
if (liveBase !== undefined)
|
|
100
|
-
assertPathBaseUrl(liveBase,
|
|
101
|
+
assertPathBaseUrl(liveBase, WEB_ADMIN_ENV.liveUrl);
|
|
101
102
|
const url = `${(liveBase ?? center.baseUrl).replace(/\/+$/, "")}${path}`;
|
|
102
103
|
const res = await (init.fetchImpl ?? fetch)(url, {
|
|
103
104
|
method: init.method ?? "GET",
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 🔴 三条纪律:
|
|
5
5
|
* 1. `EFFECTIVE_CONFIG_KEYS` 与 {@link EffectiveConfig} **类型级**绑定(`satisfies Record<keyof …, true>`):接口加键 /
|
|
6
6
|
* 删键而不动这里 = **编译错**——运行时键表永不与类型面漂移(这不是第二份表,是同一份接口的运行时投影;
|
|
7
|
-
*
|
|
7
|
+
* governance 域的键表同法,用于与 schema zod shape 的集合等式墓碑;settings-schema 11 退役了 `runtime` 域,它的键表随之删)。
|
|
8
8
|
* 2. 三张登记表(非域白名单 / 不消费表 / 远端专属表)每行**必须带理由**;行的成立性由对账门**双向**钉——登记了
|
|
9
9
|
* 却不成立(死账)与成立了却没登记(半接线)都红,理由失效之日格子自己翻红(墓碑),禁裸豁免。
|
|
10
10
|
* 3. schema 侧的数据(`DOMAINS` / `PORTABLE_DOMAINS` / zod shape)一律由对账门**读 dist**,绝不抄进这里
|
|
@@ -15,8 +15,6 @@ import type { EffectiveConfig } from "./types.js";
|
|
|
15
15
|
export declare const EFFECTIVE_CONFIG_KEYS: ReadonlyArray<keyof EffectiveConfig>;
|
|
16
16
|
/** 信封键(不是域)。 */
|
|
17
17
|
export declare const EFFECTIVE_ENVELOPE_KEYS: readonly ["version", "updatedAt"];
|
|
18
|
-
/** `runtime` 域(限额残余)的键集——对账门与 schema `RuntimeConfig.shape` 钉集合等式([ref] 件3 退役键墓碑)。 */
|
|
19
|
-
export declare const RUNTIME_DOMAIN_KEYS: readonly string[];
|
|
20
18
|
/** `governance` 域(治理三件)的键集——对账门与 schema `GovernanceConfig.shape` 钉集合等式。 */
|
|
21
19
|
export declare const GOVERNANCE_DOMAIN_KEYS: readonly string[];
|
|
22
20
|
/** EffectiveConfig 上**不是** settings-schema 域的顶层键(键 → 成文理由)。⚠️ 墓碑语义:任一键将来出现在 schema
|
|
@@ -7,7 +7,6 @@ const EFFECTIVE_CONFIG_KEY_TABLE = {
|
|
|
7
7
|
mcp: true,
|
|
8
8
|
a2a: true,
|
|
9
9
|
scenarios: true,
|
|
10
|
-
runtime: true,
|
|
11
10
|
limits: true,
|
|
12
11
|
projects: true,
|
|
13
12
|
governance: true,
|
|
@@ -17,14 +16,6 @@ const EFFECTIVE_CONFIG_KEY_TABLE = {
|
|
|
17
16
|
};
|
|
18
17
|
export const EFFECTIVE_CONFIG_KEYS = Object.keys(EFFECTIVE_CONFIG_KEY_TABLE);
|
|
19
18
|
export const EFFECTIVE_ENVELOPE_KEYS = ["version", "updatedAt"];
|
|
20
|
-
const RUNTIME_DOMAIN_KEY_TABLE = {
|
|
21
|
-
rateLimitPerMin: true,
|
|
22
|
-
maxTaskCostUsd: true,
|
|
23
|
-
maxTaskTokens: true,
|
|
24
|
-
maxPrincipalCostUsd: true,
|
|
25
|
-
costQuotaWindowSec: true,
|
|
26
|
-
};
|
|
27
|
-
export const RUNTIME_DOMAIN_KEYS = Object.keys(RUNTIME_DOMAIN_KEY_TABLE);
|
|
28
19
|
const GOVERNANCE_DOMAIN_KEY_TABLE = {
|
|
29
20
|
autonomy: true,
|
|
30
21
|
commandPolicy: true,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Web-admin adapter — pull the effective config from the web-admin control plane and
|
|
3
3
|
* apply it OVER the env-derived defaults (env = fallback, center = override). Follows the
|
|
4
4
|
* "universal internal schema → translate at the boundary" pattern, with our security boundary kept:
|
|
5
5
|
*
|
|
6
6
|
* (name history: this module tracks the control plane's product name, which has changed over time
|
|
7
7
|
* — registry → sema-web → sema-web-admin (2026-08-26 repo/product rename; an earlier revision of
|
|
8
8
|
* this note listed "planned sema-admin" as the next step). The module itself keeps a neutral, product-name-free
|
|
9
|
-
* identifier ("
|
|
9
|
+
* identifier ("web-admin") and does not chase the rename.)
|
|
10
10
|
*
|
|
11
11
|
* - the CENTER owns the LOGICAL config: the model roster (names/capabilities/tier), the role map,
|
|
12
12
|
* and collab (multi-agent discussion) templates;
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
* (`governance.approvalRequire` left this list in S-668 波 2 — its own staged group, hot on both legs.)
|
|
42
42
|
*
|
|
43
43
|
* ([ref] A13, internal-lossless) This module is now a FACADE: the implementation lives
|
|
44
|
-
* alongside it in `src/
|
|
44
|
+
* alongside it in `src/web-admin-client/` split by responsibility group (HTTP client / EffectiveConfig
|
|
45
45
|
* application / restart-signal detection / skills+MCP consumption). Every symbol below is
|
|
46
|
-
* re-exported UNCHANGED — existing `from ".../
|
|
46
|
+
* re-exported UNCHANGED — existing `from ".../web-admin-client/facade.js"` imports need zero changes
|
|
47
47
|
* across a rename of this file's *contents*; a path rename (this move, 2026-07-29) does require the
|
|
48
48
|
* importers to update, which was done in the same commit.
|
|
49
49
|
*/
|
|
50
|
-
export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory,
|
|
51
|
-
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot,
|
|
50
|
+
export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, WebAdminHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
|
|
51
|
+
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
|
|
52
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";
|