@sema-agent/server 7.98.0 → 7.100.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.
Files changed (153) hide show
  1. package/MIGRATION.md +19 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +86 -60
  5. package/deploy/sema-up/lib/envf.sh +110 -42
  6. package/deploy/sema-up/sema-up.sh +76 -34
  7. package/dist/approval-ask-machine.d.ts +10 -0
  8. package/dist/approval-ask-machine.js +3 -0
  9. package/dist/approval-card.d.ts +74 -1
  10. package/dist/approval-card.js +41 -8
  11. package/dist/approval.d.ts +44 -20
  12. package/dist/approval.js +14 -6
  13. package/dist/boot/config-center.d.ts +16 -8
  14. package/dist/boot/config-center.js +206 -103
  15. package/dist/boot/execution-env.d.ts +1 -2
  16. package/dist/boot/execution-env.js +2 -2
  17. package/dist/boot/leader.js +12 -9
  18. package/dist/boot/memory-consolidation.d.ts +38 -0
  19. package/dist/boot/memory-consolidation.js +35 -0
  20. package/dist/boot/org-memory.d.ts +2 -1
  21. package/dist/boot/org-memory.js +9 -5
  22. package/dist/boot/resolve-spec.d.ts +0 -1
  23. package/dist/boot/resolve-spec.js +4 -3
  24. package/dist/boot/runner-deps.d.ts +2 -2
  25. package/dist/boot/runtime-caps.js +4 -4
  26. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  27. package/dist/boot/stage-06-runners.d.ts +2 -2
  28. package/dist/boot/stage-06-runners.js +3 -3
  29. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  30. package/dist/boot/stage-07-capability-layer.js +6 -6
  31. package/dist/boot/stage-08-reapers.d.ts +3 -4
  32. package/dist/boot/stage-08-reapers.js +2 -2
  33. package/dist/boot/stage-09-leader.d.ts +2 -3
  34. package/dist/boot/stage-10-http-server.d.ts +2 -3
  35. package/dist/boot/stage-10-http-server.js +2 -2
  36. package/dist/capabilities/center-plugins.d.ts +14 -2
  37. package/dist/capabilities/center-plugins.js +13 -5
  38. package/dist/capabilities/center-prompts.d.ts +3 -0
  39. package/dist/capabilities/center-prompts.js +1 -1
  40. package/dist/capabilities/hands-lane.js +2 -1
  41. package/dist/capabilities/scenarios.js +18 -5
  42. package/dist/capabilities/tool-defer.d.ts +2 -2
  43. package/dist/center-credential.d.ts +112 -0
  44. package/dist/center-credential.js +131 -0
  45. package/dist/config-catalog.d.ts +2 -2
  46. package/dist/config-catalog.js +15 -15
  47. package/dist/config-center/apply-effective.d.ts +34 -39
  48. package/dist/config-center/apply-effective.js +100 -64
  49. package/dist/config-center/apply-ledger.d.ts +35 -16
  50. package/dist/config-center/apply-ledger.js +25 -18
  51. package/dist/config-center/center-request.d.ts +60 -0
  52. package/dist/config-center/center-request.js +71 -0
  53. package/dist/config-center/facade.d.ts +11 -5
  54. package/dist/config-center/facade.js +1 -1
  55. package/dist/config-center/hot-keys-registry.d.ts +11 -4
  56. package/dist/config-center/hot-keys-registry.js +11 -7
  57. package/dist/config-center/http-client.d.ts +17 -9
  58. package/dist/config-center/http-client.js +27 -36
  59. package/dist/config-center/restart-signal.d.ts +21 -14
  60. package/dist/config-center/restart-signal.js +16 -22
  61. package/dist/config-center/skills-mcp.d.ts +4 -1
  62. package/dist/config-center/skills-mcp.js +2 -2
  63. package/dist/config-center/types.d.ts +3 -3
  64. package/dist/config-invariants.d.ts +2 -2
  65. package/dist/config-invariants.js +0 -8
  66. package/dist/config-lkg.d.ts +47 -8
  67. package/dist/config-lkg.js +107 -20
  68. package/dist/config-provider.d.ts +8 -7
  69. package/dist/config-provider.js +5 -5
  70. package/dist/config-types.d.ts +30 -11
  71. package/dist/config.js +11 -13
  72. package/dist/cross-session-settings.js +1 -1
  73. package/dist/fleet-client.d.ts +8 -8
  74. package/dist/fleet-client.js +7 -7
  75. package/dist/fleet-lease.d.ts +11 -11
  76. package/dist/fleet-lease.js +17 -13
  77. package/dist/host-lsp-manager.d.ts +29 -0
  78. package/dist/host-lsp-manager.js +14 -0
  79. package/dist/hosted-posture.d.ts +49 -93
  80. package/dist/hosted-posture.js +1 -25
  81. package/dist/http/active-run-conflict.d.ts +2 -12
  82. package/dist/http/active-run-conflict.js +1 -6
  83. package/dist/http/admission.js +8 -0
  84. package/dist/http/resume-legs.d.ts +1 -1
  85. package/dist/http/resume-legs.js +14 -10
  86. package/dist/http/route-ctx.d.ts +2 -2
  87. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  88. package/dist/http/routes/approvals-assistant.js +2 -2
  89. package/dist/http/routes/memory-compliance.js +5 -0
  90. package/dist/http/routes/memory-origin.js +5 -0
  91. package/dist/http/routes/sessions.js +6 -0
  92. package/dist/http/routes/tasks.js +7 -6
  93. package/dist/http/server.d.ts +6 -5
  94. package/dist/http/wire-types.d.ts +6 -3
  95. package/dist/leader/wire.d.ts +20 -12
  96. package/dist/leader/wire.js +7 -6
  97. package/dist/memory-layer-preflight.d.ts +46 -0
  98. package/dist/memory-layer-preflight.js +121 -0
  99. package/dist/memory-operator-faces.js +17 -1
  100. package/dist/observability/corrupt-read-seat.d.ts +21 -4
  101. package/dist/observability/corrupt-read-seat.js +31 -0
  102. package/dist/observability/fail-open.d.ts +8 -4
  103. package/dist/observability/fail-open.js +8 -4
  104. package/dist/observability/run-terminal.js +1 -0
  105. package/dist/observability/secret-env-scrub.d.ts +3 -26
  106. package/dist/observability/secret-env-scrub.js +3 -2
  107. package/dist/orchestration/workflow-completion-inbox.d.ts +31 -23
  108. package/dist/orchestration/workflow-completion-inbox.js +8 -3
  109. package/dist/plugins/approval-ask-store-file.d.ts +20 -2
  110. package/dist/plugins/approval-ask-store-file.js +21 -3
  111. package/dist/plugins/approval-ask-store-memory.d.ts +2 -0
  112. package/dist/plugins/approval-ask-store-memory.js +39 -13
  113. package/dist/plugins/approval-ask-store-sql.d.ts +24 -5
  114. package/dist/plugins/approval-ask-store-sql.js +27 -3
  115. package/dist/plugins/checkpoint-store-sql.d.ts +9 -3
  116. package/dist/plugins/checkpoint-store-sql.js +19 -2
  117. package/dist/plugins/local-checkpoint-store.js +2 -0
  118. package/dist/plugins/mailbox-store-sql.d.ts +7 -5
  119. package/dist/plugins/mailbox-store-sql.js +6 -4
  120. package/dist/plugins/permission-rule-store-file.js +3 -2
  121. package/dist/plugins/remote-env-host.d.ts +3 -1
  122. package/dist/plugins/remote-env-host.js +3 -2
  123. package/dist/plugins/sql-json-column.d.ts +16 -3
  124. package/dist/plugins/sql-json-column.js +15 -4
  125. package/dist/plugins/web-search.d.ts +42 -22
  126. package/dist/plugins/web-search.js +141 -49
  127. package/dist/project-memory.d.ts +28 -1
  128. package/dist/project-memory.js +22 -22
  129. package/dist/run-local.js +6 -7
  130. package/dist/runs.js +6 -5
  131. package/dist/runtime-caps-resolver.d.ts +8 -8
  132. package/dist/runtime-caps-resolver.js +16 -14
  133. package/dist/runtime-governance.d.ts +50 -3
  134. package/dist/runtime-governance.js +5 -0
  135. package/dist/sealed-key.js +3 -7
  136. package/dist/server-secret-env.d.ts +43 -0
  137. package/dist/server-secret-env.js +18 -0
  138. package/dist/task-settings.d.ts +1 -1
  139. package/dist/tool-approval.d.ts +144 -9
  140. package/dist/tool-approval.js +74 -18
  141. package/dist/trace/core-keyset-guard.d.ts +5 -5
  142. package/dist/trace/ledger-sink.js +2 -2
  143. package/dist/trace/project.d.ts +13 -0
  144. package/dist/trace/project.js +22 -5
  145. package/dist/trace/projection-drop.d.ts +2 -0
  146. package/dist/trace/projection-drop.js +6 -0
  147. package/dist/trace/sema-provenance.d.ts +4 -1
  148. package/dist/trace/sema-provenance.js +1 -1
  149. package/dist/trace/task-notification-facets.d.ts +65 -0
  150. package/dist/trace/task-notification-facets.js +31 -0
  151. package/dist/trace/wire-projection-faces.d.ts +3 -3
  152. package/dist/trace/wire-projection-faces.js +3 -3
  153. package/package.json +5 -4
@@ -15,7 +15,7 @@
15
15
  set -euo pipefail
16
16
 
17
17
  HERE="$(cd "$(dirname "$0")" && pwd)"
18
- # `.env` 文法(写口 encode / 读口 ENVF_WORD 与规范行判据)的唯一定义 —— 写读两半从这一处取判据。
18
+ # `.env` 文法(写口 envf_representable + envf_encode / 读口 envf_canonical_line)的唯一定义 —— 写读两半从这一处取判据。
19
19
  . "$HERE/lib/envf.sh"
20
20
  # 执行车道表的唯一读者(S-599)。定义在**最前面**:`--help` 的词表也从表渲(手抄的词表会在加一条
21
21
  # 车道时静默分叉),而 `-h` 在参数循环里就被处理,那时后面的函数还没定义。
@@ -169,11 +169,20 @@ log() { printf '[sema-up] %s\n' "$*" >&2; }
169
169
 
170
170
  # compose 入口解析:优先 docker compose(插件),缺了退 docker-compose(独立版)——
171
171
  # homebrew 裸 docker CLI 不带插件是真场景;都没有=报错带下一步
172
- dc() {
173
- if docker compose version >/dev/null 2>&1; then docker compose "$@"
174
- elif command -v docker-compose >/dev/null 2>&1; then docker-compose "$@"
172
+ # dc_argv 设 DC_ARGV(选中的 compose 入口);dc 调它 —— 渲染对账给运维的复现命令也从 DC_ARGV 渲,不另写一份。
173
+ dc_argv() {
174
+ if docker compose version >/dev/null 2>&1; then DC_ARGV=(docker compose)
175
+ elif command -v docker-compose >/dev/null 2>&1; then DC_ARGV=(docker-compose)
175
176
  else log "⛔ 没有可用的 compose(docker compose 插件/docker-compose 都缺)。下一步:装 Docker Desktop/OrbStack,或 brew install docker-compose"; exit 1; fi
176
177
  }
178
+ dc() { dc_argv; "${DC_ARGV[@]}" "$@"; }
179
+ # compose 的全局参数(-p / --env-file / -f)只在这里拼一次(相对 `$HERE/stack`):up / config 走 compose()、teardown 的
180
+ # down、渲染对账给运维的复现命令,都从同一张 COMPOSE_GLOBALS 取(S-646 ④:复现命令曾是手抄的第二份,漏了
181
+ # -f compose.dataroot.yaml 与 profile;teardown 是第三份)。数据根由调用方先定好 DATA_ROOT。
182
+ compose_globals() {
183
+ COMPOSE_GLOBALS=(--project-name "$PROJECT" --env-file "$ENVF" -f compose.yaml)
184
+ if [ -n "$DATA_ROOT" ]; then COMPOSE_GLOBALS+=(-f compose.dataroot.yaml); fi
185
+ }
177
186
 
178
187
  # ── 跨平台探针(Linux 目标机为主;macOS 开发机跑 docker 档也是真场景——npx 分发)────────────
179
188
  ram_mb() { if [ -r /proc/meminfo ]; then awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo
@@ -237,22 +246,23 @@ if $TEARDOWN; then
237
246
  cd "$HERE/stack"
238
247
  if [ -f "$ENVF" ]; then
239
248
  # 读 .env 一律经 lib 的严格读口(S-618 续):裸 grep|cut 会连编码形的单引号一起读出来。
240
- OV=""; envf_has DATA_ROOT_DIR && OV="-f compose.dataroot.yaml"
249
+ # 数据根以 .env 登记的为准(卸载的是首装那一份,不看本次 --data-root);全局参数与 up 同一张表(compose_globals)。
250
+ DATA_ROOT="$(envf_get DATA_ROOT_DIR)" || exit 1
241
251
  # 全 profile down 会对未启用服务也做 config 校验——.env 缺 SEMA_*_IMAGE(该面没开过)时
242
252
  # 整个 down 失败(真踩:假「已卸载」)。补占位符只为过校验;shell env 优先级高于 --env-file
243
253
  envf_has SEMA_SERVER_IMAGE || export SEMA_SERVER_IMAGE="teardown-placeholder"
244
254
  envf_has SEMA_REGISTRY_IMAGE || export SEMA_REGISTRY_IMAGE="teardown-placeholder"
245
- if ! dc --project-name "$PROJECT" --env-file "$ENVF" -f compose.yaml $OV \
255
+ compose_globals
256
+ if ! dc "${COMPOSE_GLOBALS[@]}" \
246
257
  --profile data --profile pg --profile s3 --profile agent --profile registry --profile git \
247
258
  down $($TEARDOWN_VOLUMES && echo -v); then
248
259
  log "⛔ 卸载未完全成功。下一步:docker ps -a --filter label=com.docker.compose.project=$PROJECT 手查残留"
249
260
  exit 1
250
261
  fi
251
262
  log "已卸载 project=$PROJECT $($TEARDOWN_VOLUMES && echo '(含数据卷)' || echo '(数据卷保留:pgdata/miniodata/registrydata/gitdata)')"
252
- if $TEARDOWN_VOLUMES && [ -n "$OV" ]; then
253
- DR="$(envf_get DATA_ROOT_DIR)" || exit 1
254
- # ${DR} 必须带花括号:后面紧跟中文破折号,bash 3.2(macOS 系统壳)会把多字节并进变量名(真踩)
255
- log "⚠ 数据根在大盘 bind 目录 ${DR}——卷对象已删,盘上文件还在;确认不要了再手动 rm -rf"
263
+ if $TEARDOWN_VOLUMES && [ -n "$DATA_ROOT" ]; then
264
+ # ${DATA_ROOT} 必须带花括号:后面紧跟中文破折号,bash 3.2(macOS 系统壳)会把多字节并进变量名(真踩)
265
+ log "⚠ 数据根在大盘 bind 目录 ${DATA_ROOT}——卷对象已删,盘上文件还在;确认不要了再手动 rm -rf"
256
266
  fi
257
267
  else log "没有找到 $ENVF,无可卸载"; fi
258
268
  exit 0
@@ -566,8 +576,8 @@ PG_TUNE_FLAGS="-c max_connections=100 -c shared_buffers=${SB}MB -c effective_cac
566
576
  genpw() { openssl rand -hex 24; }
567
577
  touch "$ENVF"; chmod 600 "$ENVF"
568
578
  # 写口 = lib/envf.sh 的 envf_put(setk / setk_new / resetk 都落到它):值按同一文法序列化 —— 安全裸词原样,
569
- # 含空白 / 特殊字符的走整值单引号编码形(compose 读成字面量、闭引号后不续解析;被 source 也不执行);换行 /
570
- # 回车 / 单引号 ⇒ 写之前响亮拒,点名键、不回显值。setk 判「键在不在」也走严格读口:旧文件里该键那一行不是
579
+ # 含空白 / 特殊字符 / 反斜杠的走整值单引号编码形(compose 读成字面量、闭引号后不续解析;被 source 也不执行);换行 /
580
+ # 回车 / 单引号 / 以奇数个反斜杠结尾 ⇒ 写之前响亮拒,点名键、不回显值,「下一步」按拒因给。setk 判「键在不在」也走严格读口:旧文件里该键那一行不是
571
581
  # 规范形(比如同物理行闭引号带出的二次赋值)⇒ 响亮拒,不再「见行在就跳过」把毒行原样留着(test srv796 RT1)。
572
582
  setk SEMA_PROJECT "$PROJECT"
573
583
  setk PG_PASSWORD "$(genpw)"
@@ -683,7 +693,9 @@ fi
683
693
  envf_load SERVER_PORT PG_PORT MINIO_PORT MINIO_CONSOLE_PORT REGISTRY_PORT GIT_HTTP_PORT GIT_SSH_PORT
684
694
 
685
695
  command -v docker >/dev/null || { log "⛔ 未装 docker。下一步:curl -fsSL https://get.docker.com | sh(cn region 用镜像站,如 https://mirrors.aliyun.com/docker-ce)"; exit 1; }
686
- compose() { ( cd "$HERE/stack" && dc --project-name "$PROJECT" --env-file "$ENVF" -f compose.yaml ${DATA_ROOT:+-f compose.dataroot.yaml} "$@" ); }
696
+ compose() { compose_globals; ( cd "$HERE/stack" && dc "${COMPOSE_GLOBALS[@]}" "$@" ); }
697
+ # shq —— 单引号引用一个词(复现命令要能原样粘贴:路径里可能有空格)。
698
+ shq() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; }
687
699
 
688
700
  # ── D:渲染对账(S-618 的类闭环;生成 .env 之后、compose up 之前)──────────────────────────────────
689
701
  # .env 的写口 / 读口都按物理行判,认不出 compose dotenv 的跨行引号值等边角;唯一能确知「容器真正拿到
@@ -692,22 +704,23 @@ compose() { ( cd "$HERE/stack" && dc --project-name "$PROJECT" --env-file "$ENVF
692
704
  # REQUIRE_EXECUTION_LANE,与本次决策逐值对比,不一致 ⇒ 拒并点名渲染值与决策值。**以 compose 为唯一属主**。
693
705
  # docker 不可达在上面 `command -v docker` 那道已 fail-loud 拦下;config 本身失败(YAML/插值坏)也拒,
694
706
  # 不新铸降级臂:未经对账就不 up。只有 agent 面有这三个决策键,别的档跳过。
695
- reconcile_render() {
696
- case ",$COMPOSE_PROFILES," in *,agent,*) : ;; *) return 0 ;; esac
697
- local exp_rp="$REQUIRE_PRINCIPAL_WANT" exp_re="$LANE_REMOTE_EXEC" exp_rl rendered
698
- exp_rl="$(printf '%s\n' "$LANE_ENV" | sed -n 's/^REQUIRE_EXECUTION_LANE=//p')"
699
- rendered="$(COMPOSE_PROFILES="$COMPOSE_PROFILES" compose config --format json 2>&1)" || {
700
- local rc=$?
701
- # 7.98.0 合并复审 F3:compose 的报文**不回显** —— 它解析 .env 失败时会原样引用那一行(凭据就在行里)。只报退出码 + 出路。
702
- log "⛔ 渲染对账:docker compose config 失败(退出码 $rc),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
703
- log " ↳ 下一步:在本机亲跑 docker compose --project-name $PROJECT --env-file $ENVF -f $HERE/stack/compose.yaml config 看报文(本脚本不转印它:.env 解析失败的报文会原样引用那一行,可能带凭据);常见成因是手改过的 .env 行"
704
- exit 1
705
- }
706
- RECON_JSON="$rendered" python3 - "$exp_rp" "$exp_re" "$exp_rl" <<'PY' || {
707
- import json, os, sys
707
+ #
708
+ # 拒因分两种、各给各的出路(S-656;7.98.0 曾把 stderr 并进 stdout 当 JSON 解析 ⇒ compose 的任何警告都让它 traceback,
709
+ # 拒因还冒充「不一致」):
710
+ # · 渲染结果解析失败 —— compose 退出码非零,或退出 0 但 stdout 不是可解析的 compose JSON:没拿到渲染值,谈不上比对;
711
+ # · 渲染值与决策值不一致 —— 拿到了、逐值比对不上。
712
+ # JSON 只从 stdout 取;stderr 只数行数作诊断,**不转印**(compose 解析 `.env` 失败时报文会原样引用那一行,凭据就在行里;
713
+ # 插值警告里的变量名也可能是凭据的一段)。
714
+ RECON_PY='
715
+ import json, sys
708
716
  want = {"REQUIRE_PRINCIPAL": sys.argv[1], "REMOTE_EXEC": sys.argv[2], "REQUIRE_EXECUTION_LANE": sys.argv[3]}
709
- d = json.loads(os.environ["RECON_JSON"])
710
- env = ((d.get("services") or {}).get("server") or {}).get("environment") or {}
717
+ try:
718
+ d = json.loads(sys.stdin.read())
719
+ env = d["services"]["server"].get("environment") or {}
720
+ if not isinstance(env, dict):
721
+ raise TypeError("environment")
722
+ except Exception:
723
+ raise SystemExit(4) # 渲染结果解析失败:不是 compose JSON / 没有 server 服务
711
724
  bad = []
712
725
  for k, w in want.items():
713
726
  got = env.get(k)
@@ -716,13 +729,42 @@ for k, w in want.items():
716
729
  bad.append("%s:渲染=%r 决策=%r" % (k, got, w))
717
730
  if bad:
718
731
  sys.stderr.write("[sema-up] ↳ 明细:" + " | ".join(bad) + "\n")
719
- raise SystemExit(3)
720
- PY
721
- log "⛔ 渲染对账失败:compose 渲染出的容器决策值与本次决策不一致 —— 以 compose 为唯一属主,拒绝 up"
722
- log " ↳ 下一步:检查 .env 里 REQUIRE_PRINCIPAL / REMOTE_EXEC / REQUIRE_EXECUTION_LANE,以及 shell / -f 里是否**导出**了同名变量(compose 插值 shell 优先于 --env-file,导出为空会盖掉 .env)"
732
+ raise SystemExit(3) # 渲染值与决策值不一致
733
+ '
734
+ reconcile_render() {
735
+ case ",$COMPOSE_PROFILES," in *,agent,*) : ;; *) return 0 ;; esac
736
+ local exp_rp="$REQUIRE_PRINCIPAL_WANT" exp_re="$LANE_REMOTE_EXEC" exp_rl rendered errf nerr rc=0 prc=0 repro a
737
+ exp_rl="$(printf '%s\n' "$LANE_ENV" | sed -n 's/^REQUIRE_EXECUTION_LANE=//p')"
738
+ # 复现命令:与这次 config 同一个 compose 入口、同一张全局参数表、同一组 profile(从源渲,不手抄)。
739
+ dc_argv; compose_globals
740
+ repro="(cd $(shq "$HERE/stack") && COMPOSE_PROFILES=$(shq "$COMPOSE_PROFILES") ${DC_ARGV[*]}"
741
+ for a in "${COMPOSE_GLOBALS[@]}"; do repro="$repro $(shq "$a")"; done
742
+ repro="$repro 'config' '--format' 'json')"
743
+ errf="$(umask 077 && mktemp "$WORK/.recon-stderr.XXXXXX")" || { log "⛔ 渲染对账:在 $WORK 建不了临时文件,无法对账 —— 拒绝在未对账的情况下 up"; exit 1; }
744
+ rendered="$(COMPOSE_PROFILES="$COMPOSE_PROFILES" compose config --format json 2>"$errf")" || rc=$?
745
+ nerr="$(wc -l < "$errf" | tr -d '[:space:]')"; rm -f "$errf"
746
+ if [ "$rc" -ne 0 ]; then
747
+ log "⛔ 渲染对账:渲染结果解析失败 —— docker compose config 失败(退出码 $rc;它在 stderr 输出了 $nerr 行,本脚本不转印),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
748
+ log " ↳ 下一步:在本机亲跑 $repro 看 compose 的报文(本脚本不转印它:.env 解析失败的报文会原样引用那一行,可能带凭据);常见成因是手改过的 .env 行"
723
749
  exit 1
724
- }
725
- log "渲染对账通过:REQUIRE_PRINCIPAL=$exp_rp REMOTE_EXEC=$exp_re REQUIRE_EXECUTION_LANE=$exp_rl(compose 渲染逐值一致)"
750
+ fi
751
+ printf '%s' "$rendered" | python3 -c "$RECON_PY" "$exp_rp" "$exp_re" "$exp_rl" || prc=$?
752
+ case "$prc" in
753
+ 0) : ;;
754
+ 3)
755
+ log "⛔ 渲染对账:渲染值与决策值不一致 —— compose 渲染出的容器决策值与本次决策对不上;以 compose 为唯一属主,拒绝 up"
756
+ log " ↳ 下一步:检查 $ENVF 里 REQUIRE_PRINCIPAL / REMOTE_EXEC / REQUIRE_EXECUTION_LANE,以及 shell / -f 里是否**导出**了同名变量(compose 插值 shell 优先于 --env-file,导出为空会盖掉 .env);亲跑 $repro 可看容器 environment"
757
+ exit 1 ;;
758
+ 4)
759
+ log "⛔ 渲染对账:渲染结果解析失败 —— docker compose config 退出 0,但 stdout 不是可解析的 compose JSON(或其中没有 server 服务;compose 另在 stderr 输出了 $nerr 行,本脚本不转印),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
760
+ log " ↳ 下一步:亲跑 $repro 看 stdout 是不是一份 JSON;不是 ⇒ 这个 compose 入口不支持 --format json 或往 stdout 多打了东西(${DC_ARGV[*]}),换 Docker Compose v2(docker compose 插件)后重跑"
761
+ exit 1 ;;
762
+ *)
763
+ log "⛔ 渲染对账:对账脚本自身失败(python3 退出码 $prc),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
764
+ log " ↳ 下一步:确认本机 python3 可用(python3 -c 'import json'),然后重跑"
765
+ exit 1 ;;
766
+ esac
767
+ log "渲染对账通过:REQUIRE_PRINCIPAL=$exp_rp REMOTE_EXEC=$exp_re REQUIRE_EXECUTION_LANE=$exp_rl(compose 渲染逐值一致)$([ "$nerr" -gt 0 ] && printf ';compose 另在 stderr 输出了 %s 行(多为插值警告;本脚本不转印,up 时 compose 会自己打出来)' "$nerr")"
726
768
  }
727
769
 
728
770
  # ── 外接端点连接检测(装配前 fail-loud;只验兼容可用,不校验证书——内网自签是常态)────────────
@@ -46,4 +46,14 @@ export declare function deriveAskId(sourceTaskId: string, runId: string, toolCal
46
46
  /** batch_id = sha256('batch' ⊕ sourceTaskId ⊕ runId ⊕ legKey).slice(0,64)。同一 (sourceTaskId, runId,
47
47
  * legKey) 恒派生同一批——批 = 同一决策点的兄弟 ask 集合,故不掺 toolCallId。 */
48
48
  export declare function deriveBatchId(sourceTaskId: string, runId: string, legKey: string): string;
49
+ /**
50
+ * 🔴 S-689 codex R1-F1:**单 ask 批** —— batch_id = sha256('ask-batch' ⊕ askId).slice(0,64)。
51
+ *
52
+ * 批是 **park 路由的单位**:窗到期赢家把整批转投递面(OPEN → ROUTING_UNBOUND)、兄弟连坐 VOID,core 随后 park,
53
+ * 这条腿就此结束 —— 所以「整腿一批」成立。不走 park 路由的部署(`UNATTENDED_APPROVAL_POLICY=deny`)上这个前提
54
+ * 不在:core 拿到的是部署政策拒,腿**继续跑**。整腿一批时,首只到期就把批关了,同腿后续每一只卡 `decideAsk`
55
+ * 恒 `batch_closed`(卡看得见、答不了),兄弟还被连坐成 VOID。⇒ 没有 park 路由时,批退化成 ask 自己。
56
+ * 选用处 = 协调器的 `batchIdFor`(唯一消费者);前缀与腿批不同(`ask-batch` 一元组 vs `batch` 三元组),两族 id 不相撞。
57
+ */
58
+ export declare function deriveAskScopedBatchId(askId: string): string;
49
59
  //# sourceMappingURL=approval-ask-machine.d.ts.map
@@ -33,4 +33,7 @@ export function deriveAskId(sourceTaskId, runId, toolCallId, legKey, parentToolC
33
33
  export function deriveBatchId(sourceTaskId, runId, legKey) {
34
34
  return deterministicId("batch", [sourceTaskId, runId, legKey]);
35
35
  }
36
+ export function deriveAskScopedBatchId(askId) {
37
+ return deterministicId("ask-batch", [askId]);
38
+ }
36
39
  //# sourceMappingURL=approval-ask-machine.js.map
@@ -21,7 +21,7 @@
21
21
  * 没有方法、没有捕获的行为。
22
22
  */
23
23
  import { z } from "zod";
24
- import { READ_ROOT_CANDIDATE_DIR_MAX, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
24
+ import { READ_ROOT_CANDIDATE_DIR_MAX, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
25
25
  import type { AskRow } from "./plugins/approval-ask-store-sql.js";
26
26
  /** 模型自由文本(`sourceAgentName` / `delegation.agentName`)的限长(设计稿 §6.2)。设计 §3.1 把
27
27
  * 「限长 + 脱敏」写成 **server 新增责任**(引擎无此层):spawning model 挑的名字是自由文本,
@@ -351,12 +351,85 @@ export declare const RuleOffersAbsenceSchema: z.ZodEnum<{
351
351
  lane_cannot_speak: "lane_cannot_speak";
352
352
  shadowed: "shadowed";
353
353
  }>;
354
+ /**
355
+ * `ruleOffersAbsence` 座的**三态读数**(S-663 codex r1 起;此前是 {@link readRuleOffersAbsence} 函数体内的两步)。
356
+ * 一只 schema、一处解析,两个读者各自处置「**在场却读不出**」(集外词 / 非串值 = 世代差或第三方 producer):
357
+ * · 展示面 {@link readRuleOffersAbsence}(活卡帧 / `card_json`)—— 按缺席丢并计 F 类(卡上少一行解释 ≪ 卡上出现生词);
358
+ * · 耐久 `/decide` 腿的 remember 判据(`tool-approval.ts` 的 `parkRowStandingAnswerFacts` 的 `mandated` 一格,读 park 行上的
359
+ * 孪生座 `PendingAction.tool_approval.ruleOffersAbsence`,core 同一 factory 铸)—— 判不出是不是强制 ⇒ **按强制**(fail-closed)。
360
+ * 同一个词在两处方向相反是刻意的:展示面的最坏后果是少一行字,权限面的最坏后果是一张会话级免询问。
361
+ * 本函数**不计数**(计不计、计哪个 tag 是读者的处置,不是读数)。
362
+ */
363
+ export type RuleOffersAbsenceSeat = {
364
+ readonly state: "absent";
365
+ } | {
366
+ readonly state: "word";
367
+ readonly word: RuleOffersAbsence;
368
+ } | {
369
+ readonly state: "unreadable";
370
+ readonly raw: unknown;
371
+ };
372
+ export declare function readRuleOffersAbsenceSeat(x: unknown): RuleOffersAbsenceSeat;
354
373
  /**
355
374
  * `AskRequest.ruleOffersAbsence`(core [ref] 修②)的**边界窄读** —— 活卡帧与 `card_json` 的唯一铸造点
356
375
  * (与 {@link readProbeCause} 同款分工),两面结构性同值。闭集词 verbatim(非内容族,零 redact);
357
376
  * 形不合/缺席 ⇒ 不铸键(缺席不是断言 —— 它同时覆盖「有 offers」与结构性无车道的门,core 契约逐字)。
377
+ * 解析在 {@link readRuleOffersAbsenceSeat}(与耐久腿判据共用一只 schema)。S-700 起第三个读者:运维队列行经
378
+ * {@link parkRowFacts} 读寄存行上的 park 孪生座(`PendingAction.tool_approval.ruleOffersAbsence`,core 同一个 factory 铸的同一
379
+ * 闭集)—— 同一只 schema、同一个集外 tag,三面结构性同值;集外 detail 经 {@link outOfSetDetail}(只读值,从不调用值)。
358
380
  */
359
381
  export declare function readRuleOffersAbsence(req: unknown): RuleOffersAbsence | undefined;
382
+ /**
383
+ * park 行 `requiresRealApproval` 座的读数(NP-1 起;此前是 {@link parkRowFacts} 函数体内的一行)—— **`=== true` 严判**,
384
+ * `false` 与其它值读作缺席(core d.ts「Absent otherwise (never `false`)」;与 core `summarizeCheckpoint` 同一条判法)。
385
+ * `kind` 由调用方先判(本函数只读这一位)。两个读者共用这一只,解析单源、处置各归读者(与 {@link readRuleOffersAbsenceSeat}
386
+ * 同一条分工):展示投影 {@link parkRowFacts}(队列行顶层展开)与耐久 `/decide` 腿的 remember 判据(`tool-approval.ts`
387
+ * 的 `parkRowStandingAnswerFacts`)。这一位在两处方向相同(在场才算、读不出 = 缺席)⇒ 不需要三态;也因此权限面读它
388
+ * **不经** `parkRowFacts` —— 那只展示投影顺手读 `ruleOffersAbsence` 并对集外词计展示面的 F 类 tag,而权限面对同一格是
389
+ * fail-closed 的另一种处置(S-663 codex r1 钉「权限面不计展示面 tag」)。
390
+ */
391
+ export declare function readRequiresRealApprovalSeat(toolApproval: object): boolean;
392
+ /** core 寄存行的 `tool_approval` 臂(park 行事实全部住在这一臂上;其余臂没有工具字段)。 */
393
+ type ToolApprovalPark = Extract<CorePendingAction, {
394
+ kind: "tool_approval";
395
+ }>;
396
+ /**
397
+ * 🔴 S-700 P1(client-core CC-180 / cli L-652 的根子;板 [ref] 订正 → [ref] 立案)—— **park 行事实**:
398
+ * 运维队列行(`GET /v1/approvals` + `/stream`)顶层展开的那几位。型**从 core 寄存行派生,不手抄**(S-249 同一条
399
+ * 纪律):core 改名 / 退役任一位 ⇒ 这里 tsc 红;这一位与 {@link RuleOffersAbsence}(从 `AskRequest` 派生)两个
400
+ * 联合漂开 ⇒ {@link parkRowFacts} 的返回处 tsc 红(core 说两座是同一个 factory 铸的同一闭集,本仓照此钉)。
401
+ *
402
+ * 两只店的 `PendingCheckpoint` **直接继承本型**(`extends`),于是 core 再给这一臂加一位同类事实时,改的只有
403
+ * 下面这张 Pick 表 + {@link parkRowFacts} 函数体一行,两店与路由零改。
404
+ * `mandated`(core #1079,7.31.0):7.30.1 的 `PendingAction` 上**没有**这一位 ⇒ 本版不铸;到货后表里加
405
+ * `"mandated"`、函数体按 `requiresRealApproval` 同式加一行。
406
+ */
407
+ export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence">;
408
+ /**
409
+ * S-700 —— park 行事实的**唯一投影**(`pendingAction` 是寄存行上 core 铸的那一只,读自持久层 ⇒ `unknown`)。
410
+ *
411
+ * 病:core 在寄存行 `PendingAction.tool_approval` 上铸的「卡读者该看的事实」(core [ref] 原话:「the SAME fact,
412
+ * restated where a card reader looks for it」)中 `requiresRealApproval` 经 core `summarizeCheckpoint` 早就上了 inbox(`ruleOffersAbsence`
413
+ * 不在 core 的 `CheckpointSummary` 上),而运维队列的两只店
414
+ * `listPending` 是逐键手工组行、一位都没取 ⇒ 同一张卡「inbox 上看得到只有真人能清、队列上看不到」,壳的 park
415
+ * 卡因此恒不知道这件事(client-core 0.82.4 `26fca98b` 的透传在真部署上恒不触发)。
416
+ *
417
+ * 判据(每一条都是 core d.ts 的消费纪律,不是本仓自定):
418
+ * · **kind 先判**(「Every consumer that reads the tool fields MUST branch on `kind` first」)—— 非对象 / 缺席 /
419
+ * 非 `tool_approval` 臂 ⇒ `{}`,那些臂上出现的同名键不是这两位;
420
+ * · `requiresRealApproval` —— 经 {@link readRequiresRealApprovalSeat}(`=== true` 严判,真才带;`false` 与其它值读作
421
+ * 缺席,「Absent otherwise (never `false`)」)。与 core `summarizeCheckpoint` 逐字同一条判法 ⇒ inbox 行与队列行对同一张卡
422
+ * 同答(`test/park-row-facts.test.ts` 钉)。不计数:inbox 那一面对同一形同样静默缺席,两面同律;
423
+ * · `ruleOffersAbsence` —— 经 {@link readRuleOffersAbsence}(活卡帧 / `card_json` 同一只 schema、同一个集外 tag):
424
+ * park 孪生座与同步座是 core 同一个 factory 铸的同一闭集,不另起第二份词表;
425
+ * · 缺席 ≠ false:两位都只在「真」时在场,消费端读**在场**。
426
+ *
427
+ * 🔴 **这是展示/分诊投影,不是权限判据**(core 对两位的定性都是 display metadata:「the resume belts keep reading
428
+ * the gate's own bit」)。本投影对「在场却读不出」的方向是**缺席**(卡上少一行 ≪ 卡上出现生词);一条要从
429
+ * `ruleOffersAbsence` 推「强制」的**权限**判据需要的是三态(读不出 ⇒ 按强制,fail-closed),方向相反,所以权限面
430
+ * 不许拿本函数的产物去推强制 —— 两者共用的是 schema,不是折叠。
431
+ */
432
+ export declare function parkRowFacts(pendingAction: unknown): ParkRowFacts;
360
433
  /** S-114(core 7.4.0 [ref])—— 回落卡的计数与窗。`limit` 是**闭二词**(core d.ts 逐字),两个计数是
361
434
  * 非负整数,`autoDenyAfterMs` 是 core 契约里的 `0..2147483647` 非负整数(`0` = 本卡不武装窗:部署把
362
435
  * 旋钮关了,或 TOTAL 档的卡按 [ref]① 恒 0 等人)。四成员**全必填**——core 的 `DenialLimitFallback` 上
@@ -236,15 +236,48 @@ export function readRuleEvidence(req) {
236
236
  const RULE_OFFERS_ABSENCE_VALUES = ["mandated", "lane_cannot_speak", "shadowed"];
237
237
  export const RuleOffersAbsenceSchema = z.enum(RULE_OFFERS_ABSENCE_VALUES);
238
238
  const RuleOffersAbsenceEnvelopeSchema = z.object({ ruleOffersAbsence: RuleOffersAbsenceSchema.optional() });
239
- export function readRuleOffersAbsence(req) {
240
- const parsed = RuleOffersAbsenceEnvelopeSchema.safeParse(req);
239
+ export function readRuleOffersAbsenceSeat(x) {
240
+ const parsed = RuleOffersAbsenceEnvelopeSchema.safeParse(x);
241
241
  if (parsed.success)
242
- return parsed.data.ruleOffersAbsence;
243
- const raw = req !== null && typeof req === "object" ? req.ruleOffersAbsence : undefined;
244
- if (raw === undefined)
245
- return undefined;
246
- recordFailOpen("server.approval-card.closed-word-out-of-set", `ruleOffersAbsence=${redactSecrets(String(raw)).slice(0, 40)}`);
247
- return undefined;
242
+ return parsed.data.ruleOffersAbsence === undefined ? { state: "absent" } : { state: "word", word: parsed.data.ruleOffersAbsence };
243
+ const raw = x !== null && typeof x === "object" ? x.ruleOffersAbsence : undefined;
244
+ return raw === undefined ? { state: "absent" } : { state: "unreadable", raw };
245
+ }
246
+ export function readRuleOffersAbsence(req) {
247
+ const seat = readRuleOffersAbsenceSeat(req);
248
+ switch (seat.state) {
249
+ case "word":
250
+ return seat.word;
251
+ case "absent":
252
+ return undefined;
253
+ case "unreadable":
254
+ recordFailOpen("server.approval-card.closed-word-out-of-set", `ruleOffersAbsence=${outOfSetDetail(seat.raw)}`);
255
+ return undefined;
256
+ }
257
+ }
258
+ function outOfSetDetail(raw) {
259
+ if (typeof raw === "string")
260
+ return redactSecrets(raw).slice(0, 40);
261
+ if (raw === null)
262
+ return "<null>";
263
+ if (Array.isArray(raw))
264
+ return "<array>";
265
+ if (typeof raw === "object" || typeof raw === "function")
266
+ return `<${typeof raw}>`;
267
+ return String(raw).slice(0, 40);
268
+ }
269
+ export function readRequiresRealApprovalSeat(toolApproval) {
270
+ const v = "requiresRealApproval" in toolApproval ? toolApproval.requiresRealApproval : undefined;
271
+ return v === true;
272
+ }
273
+ export function parkRowFacts(pendingAction) {
274
+ if (typeof pendingAction !== "object" || pendingAction === null || !("kind" in pendingAction) || pendingAction.kind !== "tool_approval")
275
+ return {};
276
+ const ruleOffersAbsence = readRuleOffersAbsence(pendingAction);
277
+ return {
278
+ ...(readRequiresRealApprovalSeat(pendingAction) ? { requiresRealApproval: true } : {}),
279
+ ...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
280
+ };
248
281
  }
249
282
  const DenialLimitFallbackSchema = z
250
283
  .object({
@@ -7,7 +7,26 @@ import { type ToolPolicy } from "@sema-agent/core";
7
7
  import { type PostureSource } from "./posture-source.js";
8
8
  import { type ExecutionLane } from "./execution-lane-caps.js";
9
9
  /**
10
- * 运维**是否表达了门意图** —— boot 的单用户 allow-all 基线只在"零门意图"时才允许铺开,
10
+ * 门意图判据读的五个量(`ServiceConfig` 结构满足)。单独成型是为了让**热路径**能拿「候选视图」来判
11
+ * (S-668 波 2:`{ ...config, approvalRequire: 候选名单 }`)—— 判据只有一份,输入可以是活配置也可以是候选。
12
+ */
13
+ export interface GateIntentView {
14
+ readonly approvalRequire: readonly string[];
15
+ readonly approvalDeny: readonly string[];
16
+ readonly approvalNeverAuto: readonly string[];
17
+ readonly durableApproval: boolean;
18
+ readonly durableApprovalSource: PostureSource;
19
+ }
20
+ /**
21
+ * 三张审批名单(require / deny / never-auto)里**有没有一张在场** —— 「这台部署有一份要执行的审批策略」。
22
+ *
23
+ * 🔴 S-668 波 2(codex r1 F1):leader 车道的 durable 审批席曾只按 `approvalRequire` 非空给出。require 名单转热之后,
24
+ * 中心热撤空它就把 leader worker 上**独立**的 deny / never-auto 两张表一起撤掉(主车道照旧 deny / ask)—— 三张表是
25
+ * 三条独立的限制,任何一张在场都要装策略。{@link hasOperatorGateIntent} 的名单三格就是本函数(同一判据,不写两份)。
26
+ */
27
+ export declare function hasApprovalListIntent(config: Pick<GateIntentView, "approvalRequire" | "approvalDeny" | "approvalNeverAuto">): boolean;
28
+ /**
29
+ * 运维**是否表达了门意图** —— 单用户 allow-all 基线只在"零门意图"时才允许铺开,
11
30
  * 所以这个谓词漏一格 = 那一格的意图被 allow-all 静默吞掉。
12
31
  *
13
32
  * 🔴 由来(2026-07-31 缝合审):这判据本来是 `main.ts` 里的一行内联表达式,只枚举了
@@ -20,26 +39,31 @@ import { type ExecutionLane } from "./execution-lane-caps.js";
20
39
  * 判定挪到**与 inv#2 同一个文件**就是为了这个:两者再想漂开,得有人同时改这两段。
21
40
  * 新增任何"门意图"配置项时,这里必须同步加一格(下面的表驱动测试会点名漏的那格)。
22
41
  */
23
- export declare function hasOperatorGateIntent(config: {
24
- approvalRequire: readonly string[];
25
- approvalDeny: readonly string[];
26
- approvalNeverAuto: readonly string[];
27
- durableApproval: boolean;
28
- durableApprovalSource: PostureSource;
42
+ export declare function hasOperatorGateIntent(config: GateIntentView): boolean;
43
+ /**
44
+ * 单用户 turnkey 的 **auto-accept 基线**适用吗 —— 单用户(未开 `REQUIRE_PRINCIPAL`)∧ 运维零门意图。
45
+ *
46
+ * 🔴 S-668 波 2 ③:**每请求现算**,不是 boot 常量。判据的输入 `approvalRequire` 自本批起在进程内会被中心
47
+ * 热换;boot 期算好的一个布尔会让「名单从空变非空」之后 allow-all 基线仍挂在每个新任务上 —— 运维刚表达的
48
+ * 门意图被基线静默吞掉([ref] 方向)。所以消费点(`boot/resolve-spec.ts` 每个任务、stage-07 的 boot 姿态行、
49
+ * run-local 的补偿 warn)一律调本函数,谁也不持有它的快照。
50
+ */
51
+ export declare function singleUserAutoAcceptBaselineApplies(config: GateIntentView & {
52
+ readonly requirePrincipal: boolean;
29
53
  }): boolean;
30
54
  /**
31
- * 门意图不可服务=boot 拒启(全窗复审 D3-F1,HIGH;与 env 墓碑族同谱系)。
32
- * v4.3.0 的轮询门形(APPROVAL_REQUIRE + DB backend + DURABLE_APPROVAL 未设)在 5.0.0 只剩 core 每任务
33
- * warn——**原先拦、现在放**(方向反转)。「留 undefined 让 core warn」对新配置是曝露误配,对升级存量是
34
- * 门静默消失,后者必须响亮。放这文件与 hasOperatorGateIntent/inv#2 同源(意图判据再漂要同时改两段)。
55
+ * 门意图**不可服务**的判据 —— 返回拒因(可服务 ⇒ `undefined`)。**全仓唯一一份**,两处调用、处置不同:
56
+ * · **boot**(`boot/stage-07-capability-layer.ts`):对装配完的整份 config 判,拒因 ⇒ **拒启**(全窗复审 D3-F1,
57
+ * HIGH;与 env 墓碑族同谱系)。v4.3.0 的轮询门形(APPROVAL_REQUIRE + DB backend + DURABLE_APPROVAL 未设)在
58
+ * 5.0.0 只剩 core 每任务 warn——**原先拦、现在放**(方向反转)。「留 undefined 让 core warn」对新配置是
59
+ * 曝露误配,对升级存量是门静默消失,后者必须响亮。
60
+ * · **热路径**(S-668 波 2 ④,`config-center/apply-effective.ts` 的审批组 stage):对**候选视图**
61
+ * (活配置 + 候选名单)判,拒因 ⇒ 审批组**整组拒**、活配置零变化、世代账停旧代。进程在跑,拒启不是选项;
62
+ * 而放行一份「没有 checkpoint 店可停车」的名单 = 单用户 auto-accept 基线仍在、名单静默不生效。
63
+ * `checkpointStorePresent` 是**部署事实**(本进程建没建出 checkpoint 店),不在 `ServiceConfig` 里 ⇒ 调用方显式递。
64
+ * 放这文件与 hasOperatorGateIntent/inv#2 同源(意图判据再漂要同时改两段)。
35
65
  */
36
- export declare function assertGateIntentServiceable(config: {
37
- approvalRequire: readonly string[];
38
- approvalDeny: readonly string[];
39
- approvalNeverAuto: readonly string[];
40
- durableApproval: boolean;
41
- durableApprovalSource: PostureSource;
42
- }, checkpointStorePresent: boolean): void;
66
+ export declare function gateIntentUnserviceableReason(config: GateIntentView, checkpointStorePresent: boolean): string | undefined;
43
67
  /**
44
68
  * S-602 C3 —— 「**无应答面**」两条 boot warn 的判据与文案(纯函数;stage-07 只负责打日志)。
45
69
  *
@@ -85,8 +109,8 @@ export declare function buildNoResponderBootWarns(input: {
85
109
  * 为什么在 **boot** 判而不是 resolve 时判:两个入参都是**部署级**的(entitlement 源在不在、durable 门开
86
110
  * 不开),per-principal 的那一半改变不了结论;放 resolve 里就得自己造去重,而 boot 天然只跑一次。
87
111
  *
88
- * 纯函数(返回一行或 undefined,不自己写日志)= 红先测得动;调用点在 main.ts 的
89
- * {@link assertGateIntentServiceable} 之后(同一段门装配)。
112
+ * 纯函数(返回一行或 undefined,不自己写日志)= 红先测得动;调用点在 stage-07 的
113
+ * {@link gateIntentUnserviceableReason} 拒启判之后(同一段门装配)。
90
114
  */
91
115
  export declare function buildForceDurableGateInertNotice(input: {
92
116
  /** center per-principal entitlement 源在不在(`centerEntitlementSourceWired` 的结果 —— 判据单源在那里)。 */
package/dist/approval.js CHANGED
@@ -3,19 +3,27 @@ import { operatorAsk } from "./operator-ask.js";
3
3
  import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
4
4
  import { isExplicitPosture } from "./posture-source.js";
5
5
  import { handsOnWorkerFilePlane } from "./execution-lane-caps.js";
6
- export function hasOperatorGateIntent(config) {
6
+ export function hasApprovalListIntent(config) {
7
7
  return (config.approvalRequire.length > 0 ||
8
8
  config.approvalDeny.length > 0 ||
9
- config.approvalNeverAuto.length > 0 ||
9
+ config.approvalNeverAuto.length > 0);
10
+ }
11
+ export function hasOperatorGateIntent(config) {
12
+ return (hasApprovalListIntent(config) ||
10
13
  (config.durableApproval && isExplicitPosture(config.durableApprovalSource)));
11
14
  }
12
- export function assertGateIntentServiceable(config, checkpointStorePresent) {
15
+ export function singleUserAutoAcceptBaselineApplies(config) {
16
+ return config.requirePrincipal !== true && !hasOperatorGateIntent(config);
17
+ }
18
+ export function gateIntentUnserviceableReason(config, checkpointStorePresent) {
13
19
  if (!hasOperatorGateIntent(config) || checkpointStorePresent)
14
- return;
20
+ return undefined;
15
21
  if (!config.durableApproval) {
16
- throw new Error("APPROVAL_REQUIRE/APPROVAL_DENY/APPROVAL_NEVER_AUTO are set but the durable gate is off — since 5.0.0 the poll lane is retired and durable is the ONLY gate. Set DURABLE_APPROVAL=true (with a DB/file backend that has a checkpoint face), or remove the APPROVAL_* lists to run ungated on purpose.");
22
+ return ("an approval list is in force (APPROVAL_REQUIRE / APPROVAL_DENY / APPROVAL_NEVER_AUTO, or the center's governance.approvalRequire) but the durable gate is off — " +
23
+ "since 5.0.0 the poll lane is retired and durable is the ONLY gate, so the list would never be enforced. Set DURABLE_APPROVAL=true (with a DB/file backend that has a checkpoint face), " +
24
+ "or remove the approval lists to run ungated on purpose.");
17
25
  }
18
- throw new Error("DURABLE_APPROVAL=true but no checkpoint store is available — the configured DB backend has no checkpoint face (or no backend is configured). Wire a MySQL-protocol/PostgreSQL/file backend, or unset DURABLE_APPROVAL.");
26
+ return "DURABLE_APPROVAL=true but no checkpoint store is available — the configured DB backend has no checkpoint face (or no backend is configured). Wire a MySQL-protocol/PostgreSQL/file backend, or unset DURABLE_APPROVAL.";
19
27
  }
20
28
  export function buildNoResponderBootWarns(input) {
21
29
  if (input.approvalFaceWired)
@@ -8,6 +8,7 @@ import { type ModelKeyRef } from "../key-resolver.js";
8
8
  import type { Logger } from "../observability/logger.js";
9
9
  import type { Metrics } from "../observability/metrics.js";
10
10
  import type { UnpricedCeilingVerdict } from "./unpriced-ceiling-verdict.js";
11
+ import type { ConsolidationSeatWatch } from "./memory-consolidation.js";
11
12
  import { type RestartSignal } from "../config-center/facade.js";
12
13
  import { type ConfigApplyLedgerView } from "../config-center/apply-ledger.js";
13
14
  import type { LimitSync } from "./limit-sync.js";
@@ -43,19 +44,25 @@ export interface ConfigCenterRuntime {
43
44
  /** 60s refresh cadence + boot-deferred 到货续接。
44
45
  * `limitSync`([ref] 批1)= 恒构造的两只限额座的换代口:commit 之后**零 await 内**推一把,与 pricing /
45
46
  * keyResolver 那两件派生重建同族(缺席 = 这条腿本部署没装配,例如 run-local)。
46
- * `swapRunnerDeps`(core 7.10.0 `Runner.swapDeps` 一扇门;5.49 起的 `swapModels` 口在 7.10.0 退役、
47
- * 本参数同语义改名)= **可换席**的换装口:模型面 plane 变更候选在 commit 前喂给全部 boot Runner
48
- * 原子换代(在场 ⇒ models-tiers defer/restart 臂退役,缺席 ⇒ 旧 defer 行为逐字保留);S-174 起
49
- * read **face** 席也走同一只函数(见 refresh 拍里的 `applyCenterReadFace` 调用点)。
47
+ * `swapRunnerDeps`(core 7.10.0 `Runner.swapDeps` 一扇门)= **可换席**的换装口,**必填**:模型面 plane
48
+ * 变更候选在 commit 前喂给全部 boot Runner 原子换代;S-174 起 read **face** 席也走同一只函数(见 refresh
49
+ * 拍里的 `applyLiveReadFace`)。S-668 波 0 起必填 —— 它缺席的那一形(模型面整面 defer 到重启 + `models-tiers`
50
+ * 重启片)在生产上从来到不了(stage-08 唯一调用点恒传),随片一起退役;要刷新环就要有 Runner 可换。
50
51
  * `ceilingVerdict`(S-232)= 「配了 $ 天花板但目录未定价」这条判决的活载体,与 `limitSync` **同族同序**
51
52
  * (commit 之后、零 await 内重判一次;判决不翻转就静默)。缺席 = 这条腿本部署没装配(run-local 一次性
52
- * CLI 之后不再有刷新拍,它继续用纯判据判一次)。 */
53
+ * CLI 之后不再有刷新拍,它继续用纯判据判一次)。
54
+ * `consolidationSeat`(S-668 波 0,过渡)= 整理驱动席的两侧读数口,喂 `memory-consolidation` 重启片;
55
+ * 缺席 = 阀门关着(没有 boot 烤死的席)。
56
+ * `checkpointStorePresent`(S-668 波 2,**必填**)= 部署事实「本进程建没建出 checkpoint 店」(stage-06 的产物,
57
+ * 不在 ServiceConfig 里)。审批名单转热之后,热腿要用它判 ④「门意图不可服务」(与 boot 拒启同一只判据);
58
+ * 必填 = 刷新环不可能在不知道这件事的情况下热应用审批名单。 */
53
59
  startRefreshLoop(a: {
54
- runnerTierFrozen: boolean;
55
60
  pricing: ReturnType<typeof buildPricing>;
56
61
  limitSync?: LimitSync;
57
- swapRunnerDeps?: (next: SwappableDeps) => void;
62
+ swapRunnerDeps: (next: SwappableDeps) => void;
58
63
  ceilingVerdict?: UnpricedCeilingVerdict;
64
+ consolidationSeat?: ConsolidationSeatWatch;
65
+ checkpointStorePresent: boolean;
59
66
  }): void;
60
67
  /** [ref]-2:停刷新环(hardShutdown 收尾链;幂等,未起环时 no-op)。 */
61
68
  stopRefreshLoop(): void;
@@ -78,7 +85,8 @@ export interface ConfigCenterRuntime {
78
85
  modelReady(): boolean;
79
86
  /** /health.restart(orchestrator 自动重启信号)。 */
80
87
  restartState(): RestartSignal | undefined;
81
- /** /health.modelPlaneDeferred(codex R18 卡死的 plane,无 next-boot handoff)。 */
88
+ /** /health.modelPlaneDeferred:被扣着、还没有 next-boot handoff 的候选(codex R18 起的状态机)。S-668 波 0 起
89
+ * 模型面不再 defer,键名保留,今天承载的是 durable LKG 发布债与 skills 债(见 `planeDeferredNoHandoff`)。 */
82
90
  planeDeferredState(): {
83
91
  version: number;
84
92
  since: number;