@sema-agent/server 7.16.0-rc.1 → 7.16.0-rc.2

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.
Files changed (2) hide show
  1. package/dist/config.js +33 -35
  2. package/package.json +1 -1
package/dist/config.js CHANGED
@@ -395,28 +395,33 @@ export function configKnobTable() {
395
395
  function registerKnob(rec) {
396
396
  CONFIG_KNOBS.set(rec.name, rec);
397
397
  }
398
+ /** #241-rc2([3740]③→[3742] 裁定,双臂不留静默):布尔 env 的**闭集词表**解析。
399
+ * 放宽臂=收 true/false/1/0/yes/no/on/off(大小写不敏感)——`=1` 是运维直觉写法,意图明确按意图执行
400
+ * (operator-knob 宪法方向);拒启臂=未知词**抛错**(boot 拒启带词表指路,照 #186 响亮拒先例)——
401
+ * 行为承重键(DURABLE_APPROVAL 决定 checkpointStore 存在性 ⇒ /v1/approvals 路由存在性)静默回默认的
402
+ * 真实后果是「审批面整条静默缺席」([3740]② cli/test 真机链)。返回 undefined = raw 未设/空串(沿用
403
+ * `env()` 的空串=未设约定),由调用方走默认/legacy 分派。 */
404
+ const BOOL_TRUE_WORDS = new Set(["true", "1", "yes", "on"]);
405
+ const BOOL_FALSE_WORDS = new Set(["false", "0", "no", "off"]);
406
+ function parseBoolWord(name, raw) {
407
+ if (raw === undefined || raw === "")
408
+ return undefined;
409
+ const w = raw.toLowerCase();
410
+ if (BOOL_TRUE_WORDS.has(w))
411
+ return true;
412
+ if (BOOL_FALSE_WORDS.has(w))
413
+ return false;
414
+ throw new Error(`${name}=${JSON.stringify(raw)} is not a boolean — accepted (case-insensitive): true/false/1/0/yes/no/on/off. ` +
415
+ `Refusing to boot: a silently defaulted behavior knob is worse than a loud one ([3742]; e.g. DURABLE_APPROVAL ` +
416
+ `decides whether the checkpoint store — and with it /v1/approvals — exists at all).`);
417
+ }
398
418
  function boolEnv(name, def) {
399
- const raw = process.env[name];
400
- let value = def;
401
- let source = "default";
402
- if (raw !== undefined && raw !== "") {
403
- // "" = unset, the same convention `env()`/`firstSetEnv()` use (a compose `${VAR:-}` template passes empty through).
404
- if (raw === "true") {
405
- value = true;
406
- source = "env";
407
- }
408
- else if (raw === "false") {
409
- value = false;
410
- source = "env";
411
- }
412
- else if (!CONFIG_KNOBS.has(name)) {
413
- // Previously a silent default at every hand-written site. Guarded on first registration: the LAZILY
414
- // re-read knobs (boolEnvWithLegacyNegated's host twins, read per exec) must not grow this array for the
415
- // process lifetime on one typo — one typo, one warning. (复审 2026-07-30 F6:早期理由「IMAGE_BAKES_
416
- // ENABLED/DURABLE_APPROVAL 一次 load 读两次」已随九域拆分的去重失效,懒读族成为本守卫的现行理由。)
417
- CONFIG_WARNINGS.push({ env: name, raw });
418
- }
419
- }
419
+ // #241-rc2:词表解析(见 parseBoolWord 顶注)。未知词在 parseBoolWord 里抛 = boot 拒启——旧形的
420
+ // 「CONFIG_WARNINGS 一条 warn + 回默认」臂就此退役(懒读族的防重复 push 守卫随臂一起退役:未知词
421
+ // 现在到不了任何 push)。
422
+ const parsed = parseBoolWord(name, process.env[name]);
423
+ const value = parsed ?? def;
424
+ const source = parsed !== undefined ? "env" : "default";
420
425
  registerKnob({ name, polarity: def ? "opt-out" : "opt-in", value, source });
421
426
  return value;
422
427
  }
@@ -443,20 +448,11 @@ export function boolEnvWithLegacyNegated(name, legacyNegatedName, def) {
443
448
  const legacy = process.env[legacyNegatedName];
444
449
  let value;
445
450
  let source;
446
- if (raw === "true" || raw === "false") {
447
- value = raw === "true";
451
+ const parsedCanonical = parseBoolWord(name, raw); // #241-rc2:与 boolEnv 同一词表同一拒启臂(单一属主)
452
+ if (parsedCanonical !== undefined) {
453
+ value = parsedCanonical;
448
454
  source = "env";
449
455
  }
450
- else if (raw !== undefined && raw !== "") {
451
- if (!CONFIG_KNOBS.has(name)) {
452
- // Same first-registration guard as boolEnv — the three consumers of this helper are the LAZILY re-read
453
- // ones (remote-env-host reads per exec/probe), so an unguarded push would grow CONFIG_WARNINGS for the
454
- // whole process lifetime on a single operator typo (复审 2026-07-29 #1).
455
- CONFIG_WARNINGS.push({ env: name, raw });
456
- }
457
- value = def;
458
- source = "default";
459
- }
460
456
  else if (legacy !== undefined && legacy !== "") {
461
457
  // server 3.0.0([1989],clay 令「不做兼容」):负名旧 env = fail-loud 墓碑。设了(任何值,含
462
458
  // "false"——设它的人同样以为它还生效)即拒启并指路新名。选拒启不选静默忽略:静默会让升级部署的
@@ -2105,8 +2101,10 @@ export function loadConfig() {
2105
2101
  // self-report would omit the worst offenders. Its tri-state semantics are unchanged: an explicit literal wins,
2106
2102
  // anything else (including a typo — deliberately NOT warned here, unlike boolEnv) falls to the posture default.
2107
2103
  const postureOn = (name, infraReady = true) => {
2108
- const envVal = process.env[name];
2109
- const explicit = envVal === "true" ? true : envVal === "false" ? false : undefined;
2104
+ // #241-rc2:显式臂与 boolEnv 同词表同拒启(parseBoolWord 单一属主)——三态语义不变
2105
+ // (显式 true/显式 false/未设 posture 默认),变的只是「显式」的判读:`=1` 是显式 true,
2106
+ // 未知词拒启而不是静默滑进 posture 默认。
2107
+ const explicit = parseBoolWord(name, process.env[name]);
2110
2108
  const value = explicit ?? (singleUserTurnkey && infraReady);
2111
2109
  registerKnob({ name, polarity: "posture", value, source: explicit === undefined ? "posture" : "env" });
2112
2110
  return value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.16.0-rc.1",
3
+ "version": "7.16.0-rc.2",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",