@sema-agent/server 7.43.0 → 7.44.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/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EffectiveConfig application — mutate the boot `ServiceConfig` OVER with a pulled `EffectiveConfig`
|
|
3
|
-
* (models/roles/tiers/projects `applyEffective`, the 6 runtime gates `applyRuntimeGates`, the per-request-hot
|
|
4
|
-
* governance baton `applyRuntimeHot`) plus their shared helpers (`toModel`, `mutateInPlace`,
|
|
5
|
-
* `resolveDefaultModelName`, `logEffectiveDiff`, `runtimeHasActiveGate`). Split out of `facade.ts`
|
|
6
|
-
* (design/158 A13, internal-lossless — the facade re-exports every symbol below unchanged).
|
|
7
|
-
*/
|
|
8
1
|
import { isThinkingLevel } from "@sema-agent/core";
|
|
9
2
|
import { resolveActiveTiers, SEALED_BOX_ALG } from "@sema-agent/registry-core";
|
|
10
3
|
import { sealedKeyPoison } from "../sealed-key.js";
|
|
@@ -15,11 +8,8 @@ import { registerTeams } from "../capabilities/team.js";
|
|
|
15
8
|
import { stageLimits } from "./stage-limits.js";
|
|
16
9
|
import { BATCH1_LIMIT_KEYS } from "./hot-keys-registry.js";
|
|
17
10
|
const asModelThinking = (v) => (v && isThinkingLevel(v) && v !== "off" ? v : undefined);
|
|
18
|
-
/** design/123: accept only a positive finite roster charsPerToken (fail-safe to env default / core 4). */
|
|
19
11
|
const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
20
12
|
function toModel(m, envDefaults = {}) {
|
|
21
|
-
// design/111 L2②: a center model MAY declare defaultThinking/reasoningEffortLevels; absent → inherit the env-derived
|
|
22
|
-
// model's values (BL-8 — same class as reasoning/contextWindow/maxTokens, else a roster silently drops the env default).
|
|
23
13
|
const thinkingDefault = asModelThinking(m.defaultThinking) ?? envDefaults.defaultThinking;
|
|
24
14
|
const effortLevels = (m.reasoningEffortLevels ? m.reasoningEffortLevels.map(asModelThinking).filter((x) => x !== undefined) : undefined) ?? envDefaults.reasoningEffortLevels ?? [];
|
|
25
15
|
const model = {
|
|
@@ -27,102 +17,35 @@ function toModel(m, envDefaults = {}) {
|
|
|
27
17
|
name: m.name,
|
|
28
18
|
provider: m.provider,
|
|
29
19
|
api: m.api,
|
|
30
|
-
// 实机 /model 切换 404 定谳: a catalog model MAY carry its own gateway URL — the
|
|
31
|
-
// hardcoded "" made core's brain fall back to the boot-env endpoint, so cross-endpoint `body.model`
|
|
32
|
-
// selection hit the WRONG gateway (a model id routed to a gateway that does not serve it → 404).
|
|
33
|
-
// Absent still = "" (brain env default;
|
|
34
|
-
// byte-compat for single-gateway rosters). core brain honors model.baseUrl; keys stay per-model via apiKeyEnv.
|
|
35
20
|
baseUrl: m.baseUrl ?? "",
|
|
36
|
-
// BL-8 extended (systematic-audit): fall back to the ENV-derived model's reasoning, not Boolean(undefined)=false —
|
|
37
|
-
// else a center roster that omits `reasoning` SILENTLY disables it even though the env lane defaults it ON
|
|
38
|
-
// (config.ts MODEL_REASONING="true"). A center model MAY set false to override. Same class as contextWindow/maxTokens.
|
|
39
21
|
reasoning: m.reasoning !== undefined ? Boolean(m.reasoning) : (envDefaults.reasoning ?? true),
|
|
40
22
|
input: m.vision ? ["text", "image"] : ["text"],
|
|
41
|
-
// BL-8: fall back to the ENV-derived model's value (MODEL_CONTEXT_WINDOW / MODEL_MAX_TOKENS), not a
|
|
42
|
-
// hardcoded literal — else a center roster that replaces the env model SILENTLY clamps maxTokens to 4096
|
|
43
|
-
// (the prod review-gw runs deepseek at 32000), shrinking every response. A center model MAY still set its
|
|
44
|
-
// own value to override. (ROBUSTNESS-BACKLOG:28 / BL-8.)
|
|
45
23
|
contextWindow: m.contextWindow ?? envDefaults.contextWindow ?? 262144,
|
|
46
24
|
maxTokens: m.maxTokens ?? envDefaults.maxTokens ?? 4096,
|
|
47
|
-
// design/123 D2 (BL-8 class): center override ?? env default (MODEL_CHARS_PER_TOKEN) ?? omit = core 4.
|
|
48
|
-
// Positive-finite guard mirrors the env lane — a bad roster value must not zero structural estimates.
|
|
49
25
|
...(charsPerTokenOf(m.charsPerToken) !== undefined || envDefaults.charsPerToken !== undefined
|
|
50
26
|
? { charsPerToken: charsPerTokenOf(m.charsPerToken) ?? envDefaults.charsPerToken }
|
|
51
27
|
: {}),
|
|
52
28
|
cost: m.cost
|
|
53
29
|
? { input: m.cost.input, output: m.cost.output, cacheRead: m.cost.cacheRead ?? 0, cacheWrite: m.cost.cacheWrite ?? 0 }
|
|
54
30
|
: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
55
|
-
// Explicit cache family by provider (core 1.23) — same as the env path in config.ts. Anthropic brain
|
|
56
|
-
// reports input EXCLUDING cached; the openai gateway INCLUDES it (design/18).
|
|
57
31
|
params: { ...(m.tier ? { tier: m.tier } : {}), promptCacheFamily: m.provider === "anthropic" ? "input-excludes-cached" : "input-includes-cached" },
|
|
58
|
-
// design/111 L2②: per-model default thinking + endpoint-accepted tiers (center override ?? env default, BL-8).
|
|
59
32
|
...(thinkingDefault ? { defaultThinking: thinkingDefault } : {}),
|
|
60
33
|
...(effortLevels.length > 0 && m.provider !== "anthropic" ? { compat: { reasoningEffortLevels: effortLevels } } : {}),
|
|
61
|
-
// Generation tuning (1.60) carried verbatim — without this a center roster that replaces the env model
|
|
62
|
-
// would silently drop the degenerate-looping prevention (freq/pres penalties). Absent → unchanged.
|
|
63
34
|
...(m.extraBody ? { extraBody: m.extraBody } : {}),
|
|
64
|
-
// Per-model behavior-slot sentences ([998]⑤c consumer half) — verbatim, same posture as extraBody.
|
|
65
35
|
...(Array.isArray(m.promptGuidance) && m.promptGuidance.length > 0 ? { promptGuidance: m.promptGuidance.map(String) } : {}),
|
|
66
36
|
};
|
|
67
|
-
// [818]①/A-065 P2-6 1M dual-window: an explicit roster `autoCompactTokens` (structural, like tier
|
|
68
|
-
// passthroughs) wins; else the model-id table (sonnet-5 family ∧ >=1M ⇒ 967000 — CC ships that value
|
|
69
|
-
// per-model, not per window width; see applyAutoCompactWindow header); else absent. A non-sonnet 1M roster
|
|
70
|
-
// model gets NO implicit value — roster culture is explicit declaration. Same helper as the env lane.
|
|
71
37
|
const explicitAct = m.autoCompactTokens;
|
|
72
38
|
applyAutoCompactWindow(model, typeof explicitAct === "number" ? explicitAct : undefined);
|
|
73
39
|
return model;
|
|
74
40
|
}
|
|
75
|
-
/** Apply center config OVER the env-derived config (mutates `config`). Empty domains keep env defaults. */
|
|
76
|
-
/** Replace a record's contents IN PLACE (clear keys, copy source) so holders of the SAME reference see the
|
|
77
|
-
* change. This is what makes models/roles/pricing HOT-apply: core's Runner reads
|
|
78
|
-
* `this.deps.models/roles/pricing` LIVE per-task and `/v1/models` reads `config.models` — both share the
|
|
79
|
-
* reference captured at boot, so mutating it (vs reassigning) updates both with no split, no Runner rebuild. */
|
|
80
41
|
export function mutateInPlace(target, source) {
|
|
81
42
|
for (const k of Object.keys(target))
|
|
82
43
|
delete target[k];
|
|
83
44
|
Object.assign(target, source);
|
|
84
45
|
}
|
|
85
|
-
/** 每个 config 对象上一次 COMMIT 的世代号([2283]③ CAS 的 live 端)。WeakMap——config 回收即回收;
|
|
86
|
-
* version 0(未发布/本地一次性 lane)不参与登记。 */
|
|
87
46
|
const appliedGeneration = new WeakMap();
|
|
88
|
-
/** [2311]/[2314] 裁①:env 派生 roles 基线(首次进入模型面 apply 时快照,冻结拷贝)。roles 的 file/center
|
|
89
|
-
* lane 语义=**逐键 override,env 基线为底,非粘**——上游(registry-core resolveEffectiveForWorker)本就是
|
|
90
|
-
* 逐键 merge 链;整表替换会让低信任写者(center 编辑面)静默抹掉 operator 的 env 部署契约(MODEL_VERIFIER
|
|
91
|
-
* 反自评等),且旧守卫 `length>0` 粘住的不是 env 底而是**上一个 center 世代**(applyRuntimeHot 头注
|
|
92
|
-
* NON-STICKY 论证同族,roles 曾漏在纪律外——cli [2311] 实测「用户撤销被静默吞掉」即此)。 */
|
|
93
47
|
const envRolesBaseline = new WeakMap();
|
|
94
|
-
/**
|
|
95
|
-
* #233 / A-002.8 —— 无法解读的名单条目的**代表元**。
|
|
96
|
-
*
|
|
97
|
-
* 为什么需要它(codex 复审 R1 真 finding):把坏条目**丢掉**会让 `[7, null]` 这种「在场但一条都读不
|
|
98
|
-
* 懂」的名单塌成**空数组**,而空数组在消费端的语义恰恰是「治理关闭 = 全放行」—— 一个表达"我要限制"
|
|
99
|
-
* 的配置,因为写错了类型,反而把门整个打开。方向必须反过来:名单在场 = operator 意图限制;读不懂的
|
|
100
|
-
* 条目 ⇒ 换成一个**永不可能是 catalog name** 的代表元(前导 NUL,registry-core 的 name 词法不可能产
|
|
101
|
-
* 出),于是它在归一时解不到任何目录条目 ⇒ 谁都准不了。
|
|
102
|
-
*
|
|
103
|
-
* 后果是**有界的**:被拒的只有「用户逐 turn 显式点模型」这一件事;`default` / roles / 档位表照常,
|
|
104
|
-
* 任务照跑。写错配置的代价是"点不了模型"而不是"服务不可用",且 warn 当场点名成因。
|
|
105
|
-
* 🔴 只活在内存判定面:不进 wire(`/v1/models` 只发布尔)、不进日志(warn 打的是原始坏值的类型标记)。
|
|
106
|
-
*/
|
|
107
48
|
export const UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY = "\u0000<unparsable-at-model-allowlist-entry>";
|
|
108
|
-
/**
|
|
109
|
-
* #233 / A-002.8 —— `models.atModelAllowlist` 的 STAGE 侧归一(纯函数,可抛的事都不做)。
|
|
110
|
-
*
|
|
111
|
-
* 语义(registry-core `ModelsDomain.atModelAllowlist` 的 JSDoc 同源):**缺席/空 = 全放行**。返回值恒是
|
|
112
|
-
* 一个新数组,调用点每次 apply 整表重算 ⇒ **非粘**。
|
|
113
|
-
*
|
|
114
|
-
* ⚠️ 入参刻意收 `unknown`:`EffectiveConfig` 把它声明成 `string[]`,但 **local lane 的 eff 来自
|
|
115
|
-
* `FileConfigStore` 的原始 JSON**(不是 zod 输出),类型在那条腿上不是运行时保证。
|
|
116
|
-
*
|
|
117
|
-
* 坏形处置(不静默,三条各有一句;共同的方向 = **在场即意图限制,读不懂就更严不更松**):
|
|
118
|
-
* - 字段在场但**不是数组** ⇒ 响亮 warn + 落**一条代表元**(见上)= 用户逐 turn 选择全拒。刻意不按
|
|
119
|
-
* "缺席"处置:那会把一个写坏了的**限制**声明翻译成"不限制",正是治理轴上最不该有的失败方向。
|
|
120
|
-
* 也刻意不整代拒绝:那会把整个模型面(models/roles/tiers/密钥)一起拖下水,爆炸半径不成比例。
|
|
121
|
-
* - 数组里的**非串/空串**成员 ⇒ **逐条换成代表元**(不是丢弃)+ warn:名单只会更窄,不会更宽。
|
|
122
|
-
* - 成员名**不在本世代启用目录里**(悬空 ref)⇒ warn(照 `sema_registry_default_dangling` 先例)。
|
|
123
|
-
* 保留原串:悬空成员在消费侧本就归一不到任何条目;留声必须有——名单整条悬空时"谁都点不了"是
|
|
124
|
-
* fail-closed 的真后果,运维要能一眼看见成因。
|
|
125
|
-
*/
|
|
126
49
|
function stageAtModelAllowlist(raw, models, logger) {
|
|
127
50
|
if (raw === undefined || raw === null)
|
|
128
51
|
return [];
|
|
@@ -160,24 +83,12 @@ function stageAtModelAllowlist(raw, models, logger) {
|
|
|
160
83
|
}
|
|
161
84
|
export function applyEffective(config, eff, logger, opts = {}) {
|
|
162
85
|
const staged = stageEffective(config, eff, logger, opts);
|
|
163
|
-
// #325(test [4809]):CAS 的序轴=**收货序数**(opts.generation,config-center 收货处铸的进程内
|
|
164
|
-
// 单调 ordinal),不再是 source version——local 腿的 version 是内容哈希(registry-core file-store
|
|
165
|
-
// getVersion(),上游只承诺「同 files⇒同 number」,无序承诺),按它比序会把哈希更小的合法新代
|
|
166
|
-
// 永久静默拒掉。generation 缺席(单元测试/一次性 lane)退回 version:既有单调 version 语义不变。
|
|
167
86
|
const gen = opts.generation ?? staged.version;
|
|
168
87
|
const live = appliedGeneration.get(config);
|
|
169
88
|
if (staged.version > 0 && live !== undefined && gen < live) {
|
|
170
|
-
// [2283]③:两次拉取的 staging 并发/乱序完成时,慢的旧世代不得整体覆盖快的新世代——每次都是
|
|
171
|
-
// 「整世代」,但方向反了。拒绝必须留痕(C6);caller 的 etag 纪律本就只在 apply 成功后推进,
|
|
172
|
-
// 下一拍拉到的自然是更新的世代。等版本重放(etag 未推进的幂等重放,同 ordinal)照常放行。
|
|
173
89
|
logger?.warn("sema_registry_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" });
|
|
174
90
|
return false;
|
|
175
91
|
}
|
|
176
|
-
// core 5.49 `Runner.swapModels` 换装腿([4775]①,defer 臂退役):caller 传入 swapPlane 时,staged
|
|
177
|
-
// 模型面在 COMMIT 之前先喂给全部 boot Runner 原子换代——swap 自己 expandTiers 验证候选,抛=原代
|
|
178
|
-
// byte-identical 不动 ⇒ 这里按候选整拒处理(与 CAS 拒同一 false 臂:活配置零触碰、etag 不进、
|
|
179
|
-
// 下一拍原样重判,中心修好即自愈)。先 swap 后 commit 的次序是硬约束:反过来 commit 先落,swap 再
|
|
180
|
-
// 抛,admission(config.models)已在新代而执行(Runner 私有目录)还在旧代——正是 R10 封掉的分裂窗。
|
|
181
92
|
if (staged.modelPlane !== undefined && opts.swapPlane !== undefined) {
|
|
182
93
|
try {
|
|
183
94
|
opts.swapPlane({ models: staged.modelPlane.models, tiers: staged.modelPlane.activeTiers });
|
|
@@ -193,9 +104,7 @@ export function applyEffective(config, eff, logger, opts = {}) {
|
|
|
193
104
|
}
|
|
194
105
|
commitStaged(config, staged);
|
|
195
106
|
if (staged.version > 0)
|
|
196
|
-
appliedGeneration.set(config, gen);
|
|
197
|
-
// ── post-commit notifications(全部世代描述性通知在最后一笔赋值之后,[2283]②)──
|
|
198
|
-
// #322 世代账:apply 报告也是「世代描述性通知」⇒ 同样在 commit 之后发(commit 段零回调纪律不破)。
|
|
107
|
+
appliedGeneration.set(config, gen);
|
|
199
108
|
opts.report?.({
|
|
200
109
|
version: staged.version,
|
|
201
110
|
...(opts.generation !== undefined ? { ordinal: opts.generation } : {}),
|
|
@@ -224,34 +133,22 @@ export function applyEffective(config, eff, logger, opts = {}) {
|
|
|
224
133
|
logger?.info("sema_registry_projects", { projects: Object.keys(staged.projects) });
|
|
225
134
|
return true;
|
|
226
135
|
}
|
|
227
|
-
/** COMMIT 段:纯赋值,零 await/零回调/零发射(源码钉守着——[2283]①②)。mutateInPlace/整对象重赋值/
|
|
228
|
-
* registry 表整体换装(registerTeams/registerCollabWorkflows 皆为过滤+swap 的赋值形,无回调面)。 */
|
|
229
136
|
function commitStaged(config, staged) {
|
|
230
137
|
const mp = staged.modelPlane;
|
|
231
138
|
if (mp) {
|
|
232
|
-
config.modelApiKeyEnv = mp.modelApiKeyEnv;
|
|
233
|
-
config.modelApiKeys = mp.modelApiKeys;
|
|
139
|
+
config.modelApiKeyEnv = mp.modelApiKeyEnv;
|
|
140
|
+
config.modelApiKeys = mp.modelApiKeys;
|
|
234
141
|
mutateInPlace(config.modelQuotaWeights, mp.quotaWeights);
|
|
235
|
-
mutateInPlace(config.models, mp.models);
|
|
142
|
+
mutateInPlace(config.models, mp.models);
|
|
236
143
|
config.model = config.models.default;
|
|
237
|
-
mutateInPlace(config.roles, mp.roles);
|
|
144
|
+
mutateInPlace(config.roles, mp.roles);
|
|
238
145
|
mutateInPlace(config.tiers, mp.activeTiers);
|
|
239
|
-
// #233:整表重赋值(不是 mutateInPlace)—— 本键的全部消费点(resolveSpec / side-query / GET /v1/models /
|
|
240
|
-
// 未知模型 400 的 available)都是**逐请求**从 `config` 上读,没有任何一处在 boot 期捕获这个数组本身
|
|
241
|
-
// (与 `config.modelApiKeyEnv` 的 "reassign ok" 同判据)。非粘由 stage 侧整表重算保证。
|
|
242
|
-
// 🔴 刻意与整个模型面**同生共死**(在 `if (mp)` 里,不在外面):名单说的是"这本目录里哪些可点",
|
|
243
|
-
// 所以它必须与它描述的那本目录同世代。plane 被跳过时(deferModelPlane 的 tier-frozen 窗口 /
|
|
244
|
-
// 本世代零启用模型 ⇒ 目录仍是上一世代或 env 底),名单跟着一起停在同一世代——把新名单配到旧目录上
|
|
245
|
-
// 才是真正的错配(按新名字判旧目录 = 全员悬空 = 谁都点不了)。
|
|
246
146
|
config.atModelAllowlist = mp.atModelAllowlist;
|
|
247
147
|
}
|
|
248
148
|
if (staged.gateAssignments) {
|
|
249
149
|
for (const [k, v] of staged.gateAssignments)
|
|
250
150
|
config[k] = v;
|
|
251
151
|
}
|
|
252
|
-
// #322 批1:limitsHttp 组的整批赋值(stage 已保证「要么全在、要么全空」)。缺席基线的键**删**而不是
|
|
253
|
-
// 写 undefined —— 本仓的 presence-as-semantics 判定(`"k" in config`)只打平铺面,写 undefined 会让
|
|
254
|
-
// 「center 撤键回默认」看起来像「显式设成 undefined」。
|
|
255
152
|
for (const [k, v] of staged.limits.assignments)
|
|
256
153
|
config[k] = v;
|
|
257
154
|
for (const k of staged.limits.deletions)
|
|
@@ -265,47 +162,23 @@ function commitStaged(config, staged) {
|
|
|
265
162
|
registerTeams(tp.teams);
|
|
266
163
|
registerCollabWorkflows(tp.workflows);
|
|
267
164
|
}
|
|
268
|
-
mutateInPlace(config.projects, staged.projects);
|
|
269
|
-
// 世代登记在 applyEffective 内做(#325:登记的是收货序数 gen,不是 source version)。
|
|
165
|
+
mutateInPlace(config.projects, staged.projects);
|
|
270
166
|
}
|
|
271
|
-
/** STAGE 段:一切计算/校验/解封/投影(可抛;抛=候选整体拒绝,活配置零触碰)。内容判定性 warn/error
|
|
272
|
-
* (描述 eff 真伪,与是否 commit 无关)在此发;世代描述性 info 只装载荷,post-commit 发。 */
|
|
273
167
|
function stageEffective(config, eff, logger, opts) {
|
|
274
|
-
// version 0 / empty effective = the config-center has nothing for us yet — typically CONFIG_PUBLISH_MODE
|
|
275
|
-
// is ON but nothing has been published. We do NOT wipe: models/roles fall back to env (the enabled>0
|
|
276
|
-
// guard below), teams to BUILTIN_TEAMS (registerTeams resets to built-ins). Warn once at boot so the
|
|
277
|
-
// operator knows their config isn't live (publish a version, or disable the publish gate). See the
|
|
278
|
-
// config-center's ORCHESTRATOR-REVIEW §④ publish-gate contract.
|
|
279
168
|
if (!opts.teamsOnly && !eff.version) {
|
|
280
169
|
logger?.warn("sema_registry_unpublished", {
|
|
281
170
|
version: eff.version,
|
|
282
171
|
note: "empty/unpublished effective config — using env + built-in fallback; publish a config or unset CONFIG_PUBLISH_MODE",
|
|
283
172
|
});
|
|
284
173
|
}
|
|
285
|
-
// models/roles HOT-apply on BOTH boot and refresh (TOC "save = effective"). We MUTATE
|
|
286
|
-
// config.models/roles IN PLACE (mutateInPlace) — NOT reassign — so the live Runner (reads `this.deps.models/
|
|
287
|
-
// roles` per-task by reference) and `/v1/models` (reads config.models) share the one updated object with NO
|
|
288
|
-
// split, no Runner rebuild, no core change. Caller (main.ts refresh) additionally refreshes `pricing` (same
|
|
289
|
-
// ref) + rebuilds `keyResolver`. (Was startup-only when reassignment split the Runner's ref from config.)
|
|
290
174
|
const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
|
|
291
175
|
let modelPlane;
|
|
292
|
-
// codex R10: `deferModelPlane` skips the WHOLE model plane (models/roles/tiers + per-model keys) — main.ts
|
|
293
|
-
// sets it when the Runner is tier-frozen (private expanded copy) and the plane changed: hot-applying would
|
|
294
|
-
// split admission (/v1/models, catalog gates) from the Runner's frozen generation, letting a same-key retarget
|
|
295
|
-
// silently route/bill the stale Model object through the restart window. Deferred = live plane stays on the
|
|
296
|
-
// Runner's generation; the models-tiers restart slice signals the orchestrator; the restarted process applies
|
|
297
|
-
// the new plane at boot BEFORE Runner construction. Caller logs the deferral (never silent).
|
|
298
176
|
if (enabled.length > 0 && opts.deferModelPlane !== true) {
|
|
299
177
|
const models = {};
|
|
300
178
|
const modelApiKeyEnv = {};
|
|
301
|
-
const modelApiKeys = {};
|
|
302
|
-
// Capture the env-derived model's generation bounds BEFORE the loop overwrites config.model (line below):
|
|
303
|
-
// a center model that omits maxTokens/contextWindow inherits the operator's env values, not a literal (BL-8).
|
|
179
|
+
const modelApiKeys = {};
|
|
304
180
|
const envDefaults = { maxTokens: config.model.maxTokens, contextWindow: config.model.contextWindow, reasoning: config.model.reasoning, charsPerToken: config.model.charsPerToken, defaultThinking: config.model.defaultThinking, reasoningEffortLevels: config.model.compat?.reasoningEffortLevels };
|
|
305
181
|
for (const m of enabled) {
|
|
306
|
-
// Guard (since 1.128.0): a registry baseUrl now DIRECTS model traffic (incl. that model's
|
|
307
|
-
// Authorization key) — a registry editor is a lower-trust writer than the env, so a non-http(s)/unparsable
|
|
308
|
-
// value is REJECTED back to "" (boot-env gateway) and every honored override is logged (observable routing).
|
|
309
182
|
const rawBaseUrl = m.baseUrl;
|
|
310
183
|
let safeUrl = rawBaseUrl;
|
|
311
184
|
if (rawBaseUrl) {
|
|
@@ -325,22 +198,6 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
325
198
|
}
|
|
326
199
|
}
|
|
327
200
|
models[m.name] = toModel(safeUrl === rawBaseUrl ? m : { ...m, baseUrl: safeUrl }, envDefaults);
|
|
328
|
-
// Sealed-box custody (registry-core 0.10.5): a model MAY carry `sealedApiKey` — the
|
|
329
|
-
// api key pasted in the web UI and sealed BROWSER-SIDE against this host's X25519 public key; the
|
|
330
|
-
// center stores only the ciphertext. We unseal with the private key registered under the EXACT
|
|
331
|
-
// `publicKeyId` (no try-every-key scanning — the contract selects precisely) and inject the
|
|
332
|
-
// plaintext into the in-memory per-model key pipeline (config.modelApiKeys → getApiKeyAndHeaders).
|
|
333
|
-
// Mutual exclusion (contract): sealed OUTRANKS apiKeyEnv when both are set — and STRICTLY so: a
|
|
334
|
-
// sealed key that fails to unseal (missing private key / bad base64 / MAC failure) does NOT fall
|
|
335
|
-
// back to the env-NAME reference. Falling back would turn "the key I pasted isn't live" into a
|
|
336
|
-
// silent misroute onto some stale env key; we fail LOUD (warn). And since a merely-ABSENT map
|
|
337
|
-
// entry is indistinguishable from "no per-model key configured" — which with a gateway
|
|
338
|
-
// MODEL_API_KEY deployed meant the model's tasks silently burned the shared account — a failed
|
|
339
|
-
// unseal plants a POISON PILL in the map instead of injecting nothing: the key resolver turns it
|
|
340
|
-
// into a task-level SealedKeyPoisonedError on every brain call (the gateway key is NEVER a silent
|
|
341
|
-
// fallback for a broken sealed key) until the operator fixes custody and a later apply delivers a
|
|
342
|
-
// decryptable ciphertext (the rebuild below makes recovery automatic). 🔴 Neither the plaintext
|
|
343
|
-
// NOR the ciphertext ever reaches a log line or the poison object (write-only discipline).
|
|
344
201
|
if (m.sealedApiKey) {
|
|
345
202
|
const s = m.sealedApiKey;
|
|
346
203
|
if (s.alg !== SEALED_BOX_ALG) {
|
|
@@ -365,25 +222,16 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
365
222
|
}
|
|
366
223
|
}
|
|
367
224
|
}
|
|
368
|
-
// Per-model key: center declares the env var NAME (never the secret) holding this model's upstream
|
|
369
|
-
// key. Drives the spec's getApiKeyAndHeaders so each model authenticates with its own account; the
|
|
370
|
-
// value is resolved from process.env in THIS service (secrets stay in env, design/15). A model
|
|
371
|
-
// without apiKeyEnv (or whose env var is unset) falls back to the gateway key — purely additive.
|
|
372
|
-
// (`else if` on sealedApiKey ABSENCE: a sealed model never consults the env reference — see above.)
|
|
373
225
|
else if (m.apiKeyEnv) {
|
|
374
226
|
if (process.env[m.apiKeyEnv])
|
|
375
227
|
modelApiKeyEnv[m.name] = m.apiKeyEnv;
|
|
376
228
|
else
|
|
377
229
|
logger?.warn("sema_registry_model_key_env_missing", { model: m.name, apiKeyEnv: m.apiKeyEnv, hint: "env var not set in this service — model falls back to the gateway key" });
|
|
378
230
|
}
|
|
379
|
-
// A center model whose provider has no matching brain in THIS service's env will mis-route:
|
|
380
|
-
// createRoutingBrain falls back to the gateway brain. The anthropic route needs ANTHROPIC_API_KEY.
|
|
381
231
|
if (m.provider === "anthropic" && !config.anthropic) {
|
|
382
232
|
logger?.warn("sema_registry_model_no_brain", { model: m.name, provider: m.provider, hint: "set ANTHROPIC_API_KEY in this service's env, else it mis-routes to the gateway brain" });
|
|
383
233
|
}
|
|
384
234
|
}
|
|
385
|
-
// weight-at-burn 源:quotaWeight 按 name+id 双键索引(tracer 的 brain.call e.model=模型 id,
|
|
386
|
-
// 目录键=name——双键免猜);非法值(≤0/NaN)按缺省 1 丢弃。IN PLACE 与 models 同批 hot-apply。
|
|
387
235
|
const quotaWeights = {};
|
|
388
236
|
for (const m of enabled) {
|
|
389
237
|
const qw = typeof m.quotaWeight === "number" && Number.isFinite(m.quotaWeight) && m.quotaWeight > 0 ? m.quotaWeight : undefined;
|
|
@@ -393,8 +241,6 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
393
241
|
quotaWeights[m.id] = qw;
|
|
394
242
|
}
|
|
395
243
|
}
|
|
396
|
-
// [2311]/[2314] 裁①:首次进入模型面 apply 时快照 env 派生基线(此刻 config.roles 尚未被任何 center
|
|
397
|
-
// 世代写过——boot 序=loadConfig→applyEffective;deferModelPlane 跳过的 apply 不进本分支,不污染)。
|
|
398
244
|
if (!envRolesBaseline.has(config))
|
|
399
245
|
envRolesBaseline.set(config, { ...config.roles });
|
|
400
246
|
const centerRoles = {};
|
|
@@ -404,25 +250,10 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
404
250
|
else if ("select" in tgt)
|
|
405
251
|
centerRoles[role] = { select: tgt.select };
|
|
406
252
|
}
|
|
407
|
-
// 逐键 override(env 底)+ 非粘:每次 apply 从基线整表重算——center 撤键/发空表 = 该键/整表回 env 底,
|
|
408
|
-
// 上一世代绝不驻留;center 同名键盖 env(operator 两边都配时 center 编辑面可见可审)。
|
|
409
253
|
const roles = { ...envRolesBaseline.get(config), ...centerRoles };
|
|
410
|
-
// registry-core 0.9.0: tier groups → the engine's single active tier table. `resolveActiveTiers`
|
|
411
|
-
// is registry-core's ONE resolution point (active group → its bound tiers; no active / dangling / all-unbound
|
|
412
|
-
// → undefined = INERT). Values are catalog NAME strings (open, not enum'd); dangling bindings were already
|
|
413
|
-
// culled center-side. mutateInPlace keeps the reference RunnerDeps.tiers holds — the expansion itself happens
|
|
414
|
-
// at Runner CONSTRUCTION (core 1.265 expandTiers), so a refresh-time change is restart-to-apply like models.
|
|
415
|
-
// [2373]B-6:EffectiveConfig.models 已声明 tierGroups/activeTierGroup(types.ts)——旧「CenterModel
|
|
416
|
-
// doesn't type them」注随宽铸一起退役;resolveActiveTiers 收 registry-core 形,窄化一步即可。
|
|
417
254
|
const activeTiers = resolveActiveTiers(eff.models) ?? {};
|
|
418
|
-
// [865]①(生产实锤:面板上选了 A 模型,实际跑的是目录里的另一个)显式默认解析——enabled[0] 会被任何改变 pool 顺序的写
|
|
419
|
-
// 静默翻转,default 必须消费 wire 里已有的显式意图。解析单源 = resolveDefaultModelName(dry-run 的
|
|
420
|
-
// wouldDefaultModel 共用同一只,报数与真 apply 永不撕裂——codex M2)。
|
|
421
255
|
const picked = resolveDefaultModelName(eff, (n) => models[n] !== undefined, enabled[0].name, (source, name) => logger?.warn("sema_registry_default_dangling", { source, name, hint: "explicit default names a model that is not in the enabled catalog — falling to the next source" }));
|
|
422
256
|
models.default = models[picked.name];
|
|
423
|
-
// #233 / A-002.8 `@`-mention 允许名单:与 roles 同族的**非粘整表重算**——每次 modelPlane apply 都从
|
|
424
|
-
// 本世代的 eff 重算,center 撤名单/发空表 ⇒ 本表清空 ⇒ 回「全放行」。粘写法(`length>0` 才写)会让
|
|
425
|
-
// 上一个 center 世代的名单常驻 = **收不回的治理锁**(roles 那条 [2314] 裁①同族病,不重犯)。
|
|
426
257
|
const atModelAllowlist = stageAtModelAllowlist(eff.models?.atModelAllowlist, models, logger);
|
|
427
258
|
modelPlane = {
|
|
428
259
|
models,
|
|
@@ -435,47 +266,21 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
435
266
|
infoLine: { count: enabled.length, default: models.default.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atModelAllowlist: atModelAllowlist.length, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
|
|
436
267
|
};
|
|
437
268
|
}
|
|
438
|
-
// Runtime governance/limit gates (phase-2): STARTUP only (restart-to-apply) — the live RateLimiter / CostQuota
|
|
439
|
-
// / approval gate are built from `config` AFTER this in main.ts, so mutating it here before they're constructed
|
|
440
|
-
// is what takes effect; a refresh (teamsOnly) must NOT touch them (the running gates hold their boot values).
|
|
441
|
-
// governance 切新位:治理三件优先读 governance 域(真值),runtime 旧位(双写镜像)fallback——
|
|
442
|
-
// 双写期两处逐键相等语义不变;center 撤双写后 governance 即唯一来源。限额残余(rateLimit/cost 五件)仍在 runtime。
|
|
443
269
|
const gatesView = eff.governance ? { ...eff.runtime, ...eff.governance } : eff.runtime;
|
|
444
270
|
const gates = opts.teamsOnly ? undefined : stageRuntimeGates(gatesView, logger);
|
|
445
|
-
// #322 批1:limitsHttp 组是**热**的 ⇒ 与 hot 二件同姿势,boot 与 refresh 双腿都算(不在 teamsOnly 门内)。
|
|
446
|
-
// 五个既有闸键因此从 `stageRuntimeGates` 的 boot-only 车道**移出**(RUNTIME_GATE_KEYS 里剩 approvalRequire),
|
|
447
|
-
// 单一属主:一个键只能有一个写者,否则 boot 一次 refresh 一次的两条腿迟早给出不同答案。
|
|
448
|
-
// 🔴 `limits` 按**键存在性**透传,不按真值(codex R1-F1 同族):`null`/`0`/`""` 这些畸形值必须原样送到
|
|
449
|
-
// stage 的判形门上去,truthy 过滤会把它们折成「域缺席」= 静默回 env 底,正是那条 finding 要堵的形。
|
|
450
271
|
const limits = stageLimits(config, { version: typeof eff.version === "number" ? eff.version : 0, ...("limits" in eff ? { limits: eff.limits } : {}), legacyGates: legacyGateValues(eff.runtime) }, logger);
|
|
451
|
-
// Runtime governance "second baton" (center §10): autonomy + commandPolicy are per-request HOT (read live in
|
|
452
|
-
// resolveSpec), NOT baked into boot middleware → apply on BOTH boot and refresh (outside the teamsOnly guard) so
|
|
453
|
-
// they hot-reload. No restart-to-apply signal (they take effect on the next task without a restart).
|
|
454
272
|
const hot = stageRuntimeHot(config, gatesView, logger);
|
|
455
|
-
// Teams → registry (hot-reloadable; center overrides/extends the built-ins).
|
|
456
|
-
// codex R11: teams + collab workflows carry MODEL REFERENCES (member.model / workflow model args) — when the
|
|
457
|
-
// model plane is deferred (tier-frozen Runner, see the deferModelPlane guard above) these faces must defer
|
|
458
|
-
// WITH it, or a candidate that atomically adds/retargets a model AND updates a team/workflow to reference it
|
|
459
|
-
// publishes a mixed generation: the new template goes live while config.models/the Runner stay old (a new ref
|
|
460
|
-
// fails as unknown; a same-name retarget silently executes stale). One catalog generation = one visibility.
|
|
461
273
|
let teamsPlane;
|
|
462
274
|
if (opts.deferModelPlane !== true) {
|
|
463
275
|
const teams = {};
|
|
464
276
|
for (const t of (eff.teams?.teams ?? []).filter((t) => t.enabled !== false)) {
|
|
465
277
|
teams[t.name] = {
|
|
466
278
|
name: t.name,
|
|
467
|
-
// F8: honor member.model (core TeamMember.model supports it) — was silently dropped, leaving the
|
|
468
|
-
// member to fall back to the "team" role instead of the operator's chosen model.
|
|
469
279
|
members: t.members.map((mm) => ({ role: mm.role, ...(mm.model ? { model: mm.model } : {}), modelRole: mm.modelRole, systemPrompt: mm.systemPrompt })),
|
|
470
280
|
rounds: t.rounds,
|
|
471
281
|
synthesizer: t.synthesizer ? { role: t.synthesizer.role, modelRole: t.synthesizer.modelRole, systemPrompt: t.synthesizer.systemPrompt } : undefined,
|
|
472
282
|
};
|
|
473
283
|
}
|
|
474
|
-
// collab → named-workflow projection(切片 1.5,design/140 统一解;切片① 的
|
|
475
|
-
// TeamTemplate 投影已整体替换——纪律「别留双路径降级」)。可投影子集翻译成命名 workflow 注册条目
|
|
476
|
-
// (执行体=core 内置 team-discussion 脚本,center 模板=defaultArgs 合并链第二级;键=collab id,shell
|
|
477
|
-
// `/team`/LLM 经 Workflow({name}) 调用);子集外整条跳过+结构化上报,绝不静默降级。HOT:boot+refresh
|
|
478
|
-
// 双腿整表替换(非粘——center 停发即空表,内置 workflow 不受影响)。register* 在 commit 段成对换装。
|
|
479
284
|
const projected = projectCollabToWorkflows(eff.collab?.templates);
|
|
480
285
|
const notices = [];
|
|
481
286
|
if (Object.keys(teams).length > 0)
|
|
@@ -488,12 +293,6 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
488
293
|
notices.push({ level: "info", msg: "sema_registry_collab_notes", fields: { notes: projected.notes } });
|
|
489
294
|
teamsPlane = { teams, workflows: projected.workflows, notices };
|
|
490
295
|
}
|
|
491
|
-
// 142-S4 projects 域(registry-core 0.10.0):center 项目登记簿 → config.projects(键=
|
|
492
|
-
// projectId)。消费是 per-request 查表(memoryScope 派生 + defaultScopes 种子,security.ts/main.ts),
|
|
493
|
-
// 所以 boot+refresh 双腿都 mutateInPlace 热应用(整表替换,非粘——center 停发即清空);env lane 恒空。
|
|
494
|
-
// [2373]B-8:裸 Record 臂删——registry-core 0.13.0 ProjectsConfig 只有包裹形 `{projects:{...}}`
|
|
495
|
-
// (types.d.ts:3599,且 :12231 钉进 EffectiveConfig),裸形无 producer(与 Q15 declared 双形容忍同族)。
|
|
496
|
-
// 防御保留:非 object 条目跳过,字段逐个验型(wire verbatim,不信任形状)。
|
|
497
296
|
const projDomain = eff.projects;
|
|
498
297
|
const rawProjects = (projDomain && typeof projDomain === "object" && !Array.isArray(projDomain) &&
|
|
499
298
|
"projects" in projDomain && typeof projDomain.projects === "object"
|
|
@@ -502,7 +301,7 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
502
301
|
const projects = {};
|
|
503
302
|
for (const [pid, reg] of Object.entries(rawProjects)) {
|
|
504
303
|
if (!reg || typeof reg !== "object" || Array.isArray(reg))
|
|
505
|
-
continue;
|
|
304
|
+
continue;
|
|
506
305
|
const r = reg;
|
|
507
306
|
projects[pid] = {
|
|
508
307
|
...(typeof r.displayName === "string" ? { displayName: r.displayName } : {}),
|
|
@@ -520,38 +319,8 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
520
319
|
projects,
|
|
521
320
|
};
|
|
522
321
|
}
|
|
523
|
-
/**
|
|
524
|
-
* Apply the center `runtime` governance/limit gates OVER the env-derived config (mutates `config`).
|
|
525
|
-
*
|
|
526
|
-
* UNIFORM presence semantics across all 6 gates (center `f95438a` adopted this from our sentinel flag —
|
|
527
|
-
* SERVICE-INTEGRATION「Runtime 治理/限额闸」): all six fields are center `.optional()`, so
|
|
528
|
-
* `undefined` = center isn't managing it → KEEP env;
|
|
529
|
-
* present (any value, INCLUDING an explicit 0 / [] = "publish the gate OFF") → OVERRIDE env.
|
|
530
|
-
* (Earlier center had `rateLimitPerMin`/`approvalRequire` as `.default(0/[])`, which would have let a default
|
|
531
|
-
* silently disable an env-set gate — we flagged it, center made all six optional, so a plain presence rule is
|
|
532
|
-
* now both correct and uniform. No sentinel special-casing.)
|
|
533
|
-
*
|
|
534
|
-
* Only guard: `costQuotaWindowSec` must be `> 0` (a 0 window is nonsensical — core divides by it). Center's
|
|
535
|
-
* schema is `.positive()` so it can't send 0; this is belt-and-suspenders.
|
|
536
|
-
*
|
|
537
|
-
* Per-worker overrides ride the per-worker `/effective?worker=` response (center merges WorkerSpec.runtime over
|
|
538
|
-
* the fleet default server-side), so this single "effective beats env" rule already honors per-worker > fleet.
|
|
539
|
-
*/
|
|
540
|
-
/** The 6 runtime governance/limit gates, in ONE place (council DRY): the consume loop, the restart-to-apply
|
|
541
|
-
* signal, and the dry-run diff all derive from this list, so adding a 7th gate is a single edit and can't
|
|
542
|
-
* silently miss `runtimeHasActiveGate` (which would break the restart-to-apply warning). All six share keys
|
|
543
|
-
* across `EffectiveConfig["runtime"]` and `ServiceConfig`. Exported (not in the original single-file form) so
|
|
544
|
-
* the sibling restart-signal module can share the identical key list + presence rule. */
|
|
545
322
|
export const RUNTIME_GATE_KEYS = ["rateLimitPerMin", "approvalRequire", "maxTaskCostUsd", "maxTaskTokens", "maxPrincipalCostUsd", "costQuotaWindowSec"];
|
|
546
|
-
/**
|
|
547
|
-
* #322 批1:六闸里**已转热**的五个(限额族)——它们的属主是 `stage-limits.ts`(boot+refresh 双腿),
|
|
548
|
-
* 所以 boot-only 的闸车道不再写它们,`runtime-gates` 重启片也不再为它们铸重启理由(键是热的却还要求重启
|
|
549
|
-
* = 白白滚动重启整片 fleet)。剩下的 `approvalRequire` 仍是 restart-to-apply,归批2。
|
|
550
|
-
*/
|
|
551
323
|
export const HOT_RUNTIME_GATE_KEYS = new Set(BATCH1_LIMIT_KEYS);
|
|
552
|
-
/** Uniform presence rule (center f95438a): a gate is "managed" when the field is present — any number (incl. 0),
|
|
553
|
-
* or an array for approvalRequire. Sole guard: costQuotaWindowSec must be > 0 (a 0 window is nonsensical — core
|
|
554
|
-
* divides by it; center's `.positive()` already enforces, belt-and-suspenders here). */
|
|
555
324
|
export function runtimeGatePresent(rt, key) {
|
|
556
325
|
const v = rt[key];
|
|
557
326
|
if (key === "approvalRequire")
|
|
@@ -560,42 +329,17 @@ export function runtimeGatePresent(rt, key) {
|
|
|
560
329
|
return typeof v === "number" && v > 0;
|
|
561
330
|
return typeof v === "number";
|
|
562
331
|
}
|
|
563
|
-
/**
|
|
564
|
-
* A1:中心下发的 `approvalRequire` 名单**按 env 腿同款判据**验过再落配。此前只验 `Array.isArray` 就逐字
|
|
565
|
-
* 落配——含退役工具名(`bash`/`Task`/`KillShell`)或 pre-prefix/不完整 MCP 名(`figma__x`/`mcp__figma`)时
|
|
566
|
-
* 该名单项永不匹配任何现役工具,审批门静默失守,而同一拼法从 env 腿(`APPROVAL_REQUIRE`)进来是 boot 拒启
|
|
567
|
-
* ——两腿判据不对称,写配置的人无从知道自己踩的是哪一条。判据单点=config.ts `findUnmatchableToolNames`。
|
|
568
|
-
*
|
|
569
|
-
* 处置形照本文件既有的域级拒绝先例(`commandPolicy` 非法 ⇒ fail-loud + 保留前值):常驻进程不能因为一次
|
|
570
|
-
* 坏发布 crash,所以**只拒这一个域**(其余五闸照常应用)、保留前值、留一条响亮 warn。「半应用一个被静默
|
|
571
|
-
* 削弱的门」才是要堵的那种失败。
|
|
572
|
-
*
|
|
573
|
-
* 顺带 C1-2:数组项 trim + 去空串,与 env csv 归一(`csv()` 的 `.map(trim).filter(Boolean)`)一致——
|
|
574
|
-
* 否则 `" Bash "` 从中心腿进来会是个永不匹配的名字,正是本条要消灭的那类静默失效。
|
|
575
|
-
*
|
|
576
|
-
* ⚠️ codex R1(#186 同批复审):**形错必须拒域,不能折成 `[]`**。旧实现先按 `typeof === "string"` 过滤再验
|
|
577
|
-
* 判据,于是 `[123]`(非串项)与 `"Bash"`(整个不是数组)双双归约成空名单、判据自然干净 ⇒ 被当作「运维
|
|
578
|
-
* 显式把闸关掉」落配,把前值换成空门且**零告警**。方向与 A1 相反(A1 是门空转,这条是门蒸发),但同属本域
|
|
579
|
-
* 要堵的失败,且在安全轴上是静默 fail-open。判据分家:**空串/纯空白仍丢弃**(C1-2 的 env csv 归一,env 腿
|
|
580
|
-
* 产不出非串项),**非串项 / 非数组=形错 ⇒ 拒整域保前值**;显式 `[]` 依旧是合法的「闸关掉」。
|
|
581
|
-
*/
|
|
582
332
|
function stageApprovalRequire(raw, logger) {
|
|
583
|
-
// `kept` 逐臂说真话:内容臂的后果是「门空转」,形错臂的后果是「门蒸发」——两句不能混用。
|
|
584
333
|
const refuse = (detail, names, kept) => {
|
|
585
334
|
logger?.warn("sema_registry_approval_require_invalid", { detail, names: [...names], kept });
|
|
586
335
|
return {};
|
|
587
336
|
};
|
|
588
337
|
const KEPT_WOULD_MATCH_NOTHING = "the prior approvalRequire is unchanged — the published list would have silently matched nothing";
|
|
589
338
|
const KEPT_WOULD_REMOVE_GATE = "the prior approvalRequire is unchanged — coercing this malformed value would have silently REMOVED the approval gate";
|
|
590
|
-
// 形错门(codex R1),先于内容判据:此处**不能**静默归一,归一的结果恰好是「空门」。
|
|
591
|
-
// 非数组一支是**纵深防御**:唯一调用点先过 `runtimeGatePresent`(判 `Array.isArray`),非数组在那里就按
|
|
592
|
-
// 「缺席」跳过、保前值——已是 fail-safe。本函数签名收 `unknown`,所以自己也把这一形收口,免得 present
|
|
593
|
-
// 判据日后放宽时非数组悄悄掉进下面的归一腿(测试两支都钉了)。
|
|
594
339
|
if (!Array.isArray(raw)) {
|
|
595
340
|
return refuse(`published runtime.approvalRequire must be an ARRAY of whole tool names — got ${raw === null ? "null" : typeof raw}. ` +
|
|
596
341
|
`Refusing the domain instead of coercing it: an empty list means "no approval gate", so coercing a malformed value would SILENTLY remove the gate.`, [], KEPT_WOULD_REMOVE_GATE);
|
|
597
342
|
}
|
|
598
|
-
// 逐项报**下标 + 实际类型**(运维要能在发布件里定位到那一项;只报个数无法行动)。
|
|
599
343
|
const nonString = raw
|
|
600
344
|
.map((n, i) => (typeof n === "string" ? undefined : { i, t: n === null ? "null" : typeof n }))
|
|
601
345
|
.filter((e) => e !== undefined);
|
|
@@ -604,7 +348,6 @@ function stageApprovalRequire(raw, logger) {
|
|
|
604
348
|
`(${nonString.map((e) => `[${e.i}]:${e.t}`).join(", ")}) — every entry must be a whole tool name string. ` +
|
|
605
349
|
`Refusing the domain instead of dropping them: dropping every entry would SILENTLY remove the approval gate.`, nonString.map((e) => `[${e.i}]`), KEPT_WOULD_REMOVE_GATE);
|
|
606
350
|
}
|
|
607
|
-
// wire verbatim ⇒ 空串/纯空白按 env csv 归一丢弃(C1-2);元素形状已在上面的形错门收口。
|
|
608
351
|
const names = raw.filter((n) => typeof n === "string").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
609
352
|
const bad = findUnmatchableToolNames(names);
|
|
610
353
|
if (bad.length > 0) {
|
|
@@ -612,8 +355,6 @@ function stageApprovalRequire(raw, logger) {
|
|
|
612
355
|
}
|
|
613
356
|
return { value: names };
|
|
614
357
|
}
|
|
615
|
-
/** [2283] stage 半场:六闸的赋值清单(纯计算,零变异)。commit 段照单赋值,通知载荷 post-commit 发。
|
|
616
|
-
* A1 的名单校验是**内容判定**(描述 eff 的真伪,与是否 commit 无关)⇒ 与 commandPolicy 同,stage 期发 warn。 */
|
|
617
358
|
function stageRuntimeGates(rt, logger) {
|
|
618
359
|
if (!rt)
|
|
619
360
|
return { assignments: [] };
|
|
@@ -621,24 +362,22 @@ function stageRuntimeGates(rt, logger) {
|
|
|
621
362
|
const applied = {};
|
|
622
363
|
for (const key of RUNTIME_GATE_KEYS) {
|
|
623
364
|
if (HOT_RUNTIME_GATE_KEYS.has(key))
|
|
624
|
-
continue;
|
|
365
|
+
continue;
|
|
625
366
|
if (!runtimeGatePresent(rt, key))
|
|
626
|
-
continue;
|
|
367
|
+
continue;
|
|
627
368
|
if (key === "approvalRequire") {
|
|
628
369
|
const staged = stageApprovalRequire(rt[key], logger);
|
|
629
370
|
if (staged.value === undefined)
|
|
630
|
-
continue;
|
|
371
|
+
continue;
|
|
631
372
|
assignments.push([key, staged.value]);
|
|
632
373
|
applied[key] = staged.value;
|
|
633
374
|
continue;
|
|
634
375
|
}
|
|
635
|
-
assignments.push([key, rt[key]]);
|
|
376
|
+
assignments.push([key, rt[key]]);
|
|
636
377
|
applied[key] = rt[key];
|
|
637
378
|
}
|
|
638
379
|
return { assignments, ...(Object.keys(applied).length > 0 ? { info: applied } : {}) };
|
|
639
380
|
}
|
|
640
|
-
/** #322:旧位(`runtime`)里**已过 presence 判据**的限额闸值。判据属主恒是 `runtimeGatePresent`
|
|
641
|
-
* —— stage-limits 不复刻它,只消费它的产物(两位判据不同的理由见 stage-limits 的 `LimitsSource` 注)。 */
|
|
642
381
|
function legacyGateValues(rt) {
|
|
643
382
|
const out = {};
|
|
644
383
|
if (!rt)
|
|
@@ -649,10 +388,6 @@ function legacyGateValues(rt) {
|
|
|
649
388
|
return out;
|
|
650
389
|
}
|
|
651
390
|
export function applyRuntimeGates(config, rt, logger) {
|
|
652
|
-
// 独立调用面的兼容壳(测试/外部):stage → 就地赋值 → 通知。applyEffective 不走这里——它把
|
|
653
|
-
// assignments 并进自己的 commit 段以保住整世代原子性([2283]②)。
|
|
654
|
-
// #322:限额五闸的属主已移交热腿,所以本壳**同样**要过 stage-limits —— 否则一个仍叫 applyRuntimeGates
|
|
655
|
-
// 的函数会静默漏掉六个键里的五个(比改名更坏的那种沉默)。
|
|
656
391
|
const s = stageRuntimeGates(rt, logger);
|
|
657
392
|
for (const [k, v] of s.assignments)
|
|
658
393
|
config[k] = v;
|
|
@@ -665,37 +400,13 @@ export function applyRuntimeGates(config, rt, logger) {
|
|
|
665
400
|
if (Object.keys(info).length > 0)
|
|
666
401
|
logger?.info("sema_registry_runtime", info);
|
|
667
402
|
}
|
|
668
|
-
/**
|
|
669
|
-
* Apply the runtime governance "second baton" (center §10): `autonomy` + `commandPolicy`. UNLIKE the 6 gates in
|
|
670
|
-
* {@link applyRuntimeGates}, these are per-request HOT (read live in main.ts `resolveSpec` via
|
|
671
|
-
* `applyRuntimeGovernance`), so they apply on BOTH boot and refresh (NOT via RUNTIME_GATE_KEYS / restart-to-apply).
|
|
672
|
-
*
|
|
673
|
-
* 🔴 NON-STICKY (differs from the restart-to-apply gates on purpose — adversarial-review HIGH): a hot field that
|
|
674
|
-
* center STOPS publishing must REVERT to the env baseline, not keep the last center value. The restart-to-apply
|
|
675
|
-
* gates can be sticky because a refresh never touches them (the running middleware holds boot values until a
|
|
676
|
-
* restart re-reads env+center); a HOT field has no such reset, so "absent ⇒ keep" would silently freeze a stale
|
|
677
|
-
* center override (e.g. center published `auto`, then un-published it — the gate would stay OFF forever). So we
|
|
678
|
-
* recompute every call as `present ? center : envBaseline`. The env baseline = `AUTONOMY` (re-derived, env is
|
|
679
|
-
* immutable at runtime) for autonomy; `undefined` (no env scalar source) for commandPolicy.
|
|
680
|
-
*
|
|
681
|
-
* 🔴 commandPolicy is VALIDATED here (adversarial-review HIGH): an invalid command (glob/path/operator — which
|
|
682
|
-
* core's EXACT-name matcher would silently never match → a hole) is rejected FAIL-LOUD and the prior good policy
|
|
683
|
-
* is KEPT (a broken publish never half-applies a silently-weakened gate).
|
|
684
|
-
*/
|
|
685
|
-
/** [2283] stage 半场:hot 二件的赋值决定(计算+校验;`sema_registry_commandpolicy_invalid` 是内容判定,
|
|
686
|
-
* stage 期发——它描述 eff 的真伪,与是否 commit 无关)。对比基准=stage 时刻的 config 现值:stage 与
|
|
687
|
-
* commit 同一同步 tick,期间无人能改 config(单线程),对比不失效。 */
|
|
688
403
|
function stageRuntimeHot(config, rt, logger) {
|
|
689
404
|
const infoLine = {};
|
|
690
|
-
// autonomy: present ⇒ center value; absent ⇒ revert to the env baseline (never a stale center override).
|
|
691
405
|
const envAutonomy = parseAutonomy(process.env.AUTONOMY);
|
|
692
406
|
const autonomyNext = rt?.autonomy !== undefined ? rt.autonomy : envAutonomy;
|
|
693
407
|
const setAutonomy = config.autonomy !== autonomyNext;
|
|
694
408
|
if (setAutonomy)
|
|
695
409
|
infoLine.autonomy = autonomyNext ?? "(env-baseline)";
|
|
696
|
-
// commandPolicy: present+valid ⇒ apply; present+invalid ⇒ fail-loud + keep prior; absent ⇒ revert to baseline
|
|
697
|
-
// (undefined — no env scalar source for structured command rules). Only re-set + log on an ACTUAL change
|
|
698
|
-
// (deep-equal compare) — refresh runs every ~60s and the policy is usually unchanged; logging every tick = noise.
|
|
699
410
|
let setCommandPolicy = false;
|
|
700
411
|
let commandPolicyNext;
|
|
701
412
|
if (rt?.commandPolicy !== undefined) {
|
|
@@ -706,18 +417,17 @@ function stageRuntimeHot(config, rt, logger) {
|
|
|
706
417
|
else if (JSON.stringify(config.commandPolicy) !== JSON.stringify(rt.commandPolicy)) {
|
|
707
418
|
setCommandPolicy = true;
|
|
708
419
|
commandPolicyNext = rt.commandPolicy;
|
|
709
|
-
infoLine.commandPolicy = rt.commandPolicy.length;
|
|
420
|
+
infoLine.commandPolicy = rt.commandPolicy.length;
|
|
710
421
|
}
|
|
711
422
|
}
|
|
712
423
|
else if (config.commandPolicy !== undefined) {
|
|
713
424
|
setCommandPolicy = true;
|
|
714
|
-
commandPolicyNext = undefined;
|
|
425
|
+
commandPolicyNext = undefined;
|
|
715
426
|
infoLine.commandPolicy = "(env-baseline)";
|
|
716
427
|
}
|
|
717
428
|
return { setAutonomy, autonomyNext, setCommandPolicy, commandPolicyNext, infoLine };
|
|
718
429
|
}
|
|
719
430
|
export function applyRuntimeHot(config, rt, logger) {
|
|
720
|
-
// 独立调用面的兼容壳(测试/外部)——applyEffective 不走这里(同 applyRuntimeGates 的注)。
|
|
721
431
|
const s = stageRuntimeHot(config, rt, logger);
|
|
722
432
|
if (s.setAutonomy)
|
|
723
433
|
config.autonomy = s.autonomyNext;
|
|
@@ -726,22 +436,7 @@ export function applyRuntimeHot(config, rt, logger) {
|
|
|
726
436
|
if (Object.keys(s.infoLine).length > 0)
|
|
727
437
|
logger?.info("sema_registry_runtime_hot", s.infoLine);
|
|
728
438
|
}
|
|
729
|
-
/** [865]①/H3:activeTierGroup 档绑定当默认时的档梯,与 core 单一语义源逐字对齐(core roles.js:
|
|
730
|
-
* `ROLE_TIER_DEFAULTS.default = "pro"` + `resolveTier` 从本档位置**只向低档**扫 DEFAULT_TIER_ORDER
|
|
731
|
-
* [ultra,max,pro,flash,lite] ⇒ pro→flash→lite,绝不向上抓 max/ultra——引擎运行时对 "default" 角色就是
|
|
732
|
-
* 这么解析的,applyEffective 的 default 若用别的梯子会与真路由再度撕裂,codex H3)。 */
|
|
733
439
|
const DEFAULT_ROLE_TIER_LADDER = ["pro", "flash", "lite"];
|
|
734
|
-
/**
|
|
735
|
-
* [865]① 显式默认解析——applyEffective 与 dry-run(logEffectiveDiff.wouldDefaultModel)共用的单源。优先级
|
|
736
|
-
* (显式源指向不在目录/未启用的名 ⇒ onDangling 回调后落下一级,绝不静默指错模型):
|
|
737
|
-
* 1) eff.models.default 结构键 —— ✅ 既得路径([874] registry-core 0.10.11 落 `default?: string` schema
|
|
738
|
-
* 键+per-worker /effective 直通;本地 lane 由 config-provider mapToServiceEffective 直通)。写面悬空 ref
|
|
739
|
-
* 由 registry-core superRefine fail-loud;存量/未经 schema 路径仍走本函数的 warn 降级(双层姿势与
|
|
740
|
-
* activeTierGroup 同构)。(1.200-1.202 期间此级恒空=当时如实的前行 seam,codex H2 注记已兑现。)
|
|
741
|
-
* 2) roles.default 的显式 {model} 绑定(现有 wire 真源)。
|
|
742
|
-
* 3) activeTierGroup 档绑定,按 {@link DEFAULT_ROLE_TIER_LADDER}(= core 对 "default" 角色的档语义)。
|
|
743
|
-
* 4) fallback(enabled[0]——原兜底,唯一不触发 onDangling 的落点)。
|
|
744
|
-
*/
|
|
745
440
|
export function resolveDefaultModelName(eff, has, fallback, onDangling) {
|
|
746
441
|
const activeTiers = resolveActiveTiers(eff.models) ?? {};
|
|
747
442
|
const structural = eff.models?.default;
|
|
@@ -760,11 +455,6 @@ export function resolveDefaultModelName(eff, has, fallback, onDangling) {
|
|
|
760
455
|
}
|
|
761
456
|
return { name: fallback, source: "enabled[0]" };
|
|
762
457
|
}
|
|
763
|
-
/**
|
|
764
|
-
* Read-only comparison (SEMA_REGISTRY_DRY_RUN): log what the center config WOULD change vs the current
|
|
765
|
-
* (env-derived) config, WITHOUT applying it. The safe-rollout step the config-center recommends — verify
|
|
766
|
-
* the center's models/roles/teams match (or intentionally differ from) the env baseline before going live.
|
|
767
|
-
*/
|
|
768
458
|
export function logEffectiveDiff(config, eff, logger) {
|
|
769
459
|
const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
|
|
770
460
|
const centerModels = enabled.map((m) => ({ name: m.name, id: m.id, provider: m.provider, apiKeyEnv: m.apiKeyEnv ?? null, tier: m.tier ?? null }));
|
|
@@ -774,9 +464,7 @@ export function logEffectiveDiff(config, eff, logger) {
|
|
|
774
464
|
logger?.warn("sema_registry_dry_run", {
|
|
775
465
|
note: "DRY RUN — registry config NOT applied; unset SEMA_REGISTRY_DRY_RUN to go live",
|
|
776
466
|
version: eff.version,
|
|
777
|
-
wouldOverrideModels: enabled.length > 0,
|
|
778
|
-
// [865]①/codex M2:与 applyEffective 同一只解析器——dry-run 的上线前核验必须报真 apply 会选的默认,
|
|
779
|
-
// 报 enabled[0] 会让核验对显式默认(roles.default/tier 档)配置给出错误结论。
|
|
467
|
+
wouldOverrideModels: enabled.length > 0,
|
|
780
468
|
wouldDefaultModel: enabled.length > 0 ? resolveDefaultModelName(eff, (n) => enabled.some((m) => m.name === n), enabled[0].name).name : null,
|
|
781
469
|
currentDefaultModel: config.model.id,
|
|
782
470
|
models: { center: centerModels, envDerived: envModels },
|
|
@@ -785,14 +473,11 @@ export function logEffectiveDiff(config, eff, logger) {
|
|
|
785
473
|
skills: { centerWouldLoad: (eff.skills?.skills ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
|
|
786
474
|
mcp: { centerWouldRegister: (eff.mcp?.servers ?? []).filter((s) => s.enabled !== false).map((s) => s.name) },
|
|
787
475
|
runtime: {
|
|
788
|
-
// What the gates would become if applied (present center value overrides; absent → kept env shown as current).
|
|
789
476
|
center: eff.runtime ?? null,
|
|
790
477
|
current: Object.fromEntries(RUNTIME_GATE_KEYS.map((k) => [k, config[k]])),
|
|
791
478
|
},
|
|
792
479
|
});
|
|
793
480
|
}
|
|
794
|
-
/** True if the center `runtime` carries any managed gate (a PRESENT field) — i.e. a refresh that changed it
|
|
795
|
-
* needs a restart to take effect (restart-to-apply). Same key list + presence rule as applyRuntimeGates. */
|
|
796
481
|
export function runtimeHasActiveGate(rt) {
|
|
797
482
|
if (!rt)
|
|
798
483
|
return false;
|