@sema-agent/server 7.117.0 → 7.119.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 (159) hide show
  1. package/MIGRATION.md +182 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +58 -28
  5. package/deploy/sema-up/README.md +2 -0
  6. package/deploy/sema-up/chart/templates/minio.yaml +2 -1
  7. package/deploy/sema-up/chart/templates/server-v4pro.yaml +1 -0
  8. package/deploy/sema-up/chart/templates/server.yaml +1 -0
  9. package/deploy/sema-up/chart/values.yaml +7 -1
  10. package/deploy/sema-up/cluster-up.sh +20 -8
  11. package/deploy/sema-up/kube-up.sh +29 -16
  12. package/deploy/sema-up/lib/buckets.sh +21 -0
  13. package/deploy/sema-up/lib/health.sh +33 -0
  14. package/deploy/sema-up/sema-up.sh +14 -9
  15. package/deploy/sema-up/smoke.sh +16 -6
  16. package/deploy/sema-up/stack/compose.yaml +1 -1
  17. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -1
  18. package/dist/approval-ask-audit-store.js +2 -7
  19. package/dist/approval-card.d.ts +40 -10
  20. package/dist/approval-card.js +11 -1
  21. package/dist/approval.d.ts +55 -17
  22. package/dist/approval.js +74 -33
  23. package/dist/audit.d.ts +12 -1
  24. package/dist/audit.js +62 -2
  25. package/dist/auto-mode-face.d.ts +2 -3
  26. package/dist/bake-runner/main.js +1 -6
  27. package/dist/bake-runner/protocol.d.ts +18 -0
  28. package/dist/bake-runner/protocol.js +22 -2
  29. package/dist/bake-runner/runner.d.ts +12 -8
  30. package/dist/bake-runner/runner.js +5 -9
  31. package/dist/boot/config-center.js +1 -1
  32. package/dist/boot/coordinators.js +4 -1
  33. package/dist/boot/device-lane.d.ts +3 -3
  34. package/dist/boot/device-lane.js +1 -2
  35. package/dist/boot/leader.d.ts +4 -4
  36. package/dist/boot/leader.js +1 -2
  37. package/dist/boot/parked-revive-gate.d.ts +4 -6
  38. package/dist/boot/parked-revive-gate.js +2 -6
  39. package/dist/boot/resolve-spec.js +9 -11
  40. package/dist/boot/runner-deps.js +2 -2
  41. package/dist/boot/stage-07-capability-layer.js +7 -3
  42. package/dist/boot/stage-10-http-server.js +3 -2
  43. package/dist/boot/stores.js +12 -7
  44. package/dist/capabilities/center-plugins.d.ts +2 -0
  45. package/dist/capabilities/center-plugins.js +24 -6
  46. package/dist/capabilities/code-review-council.js +8 -11
  47. package/dist/capabilities/hands-lane.d.ts +25 -0
  48. package/dist/capabilities/hands-lane.js +1 -1
  49. package/dist/capabilities/mcp-probe.d.ts +2 -2
  50. package/dist/capabilities/scenarios.d.ts +34 -7
  51. package/dist/capabilities/scenarios.js +33 -6
  52. package/dist/config-catalog.js +3 -3
  53. package/dist/config.d.ts +14 -18
  54. package/dist/config.js +15 -26
  55. package/dist/deployment-governance.d.ts +5 -7
  56. package/dist/deployment-governance.js +4 -5
  57. package/dist/host-decision.d.ts +114 -31
  58. package/dist/host-decision.js +39 -2
  59. package/dist/http/admission.js +12 -6
  60. package/dist/http/approval-sweeps.js +1 -1
  61. package/dist/http/mcp-dial-deadline.d.ts +35 -0
  62. package/dist/http/mcp-dial-deadline.js +21 -0
  63. package/dist/http/memory-compliance-failure-reply.d.ts +11 -5
  64. package/dist/http/memory-compliance-failure-reply.js +17 -8
  65. package/dist/http/memory-store-refusal-reply.d.ts +14 -11
  66. package/dist/http/memory-store-refusal-reply.js +16 -5
  67. package/dist/http/resume-legs.js +8 -3
  68. package/dist/http/routes/approvals-assistant.d.ts +25 -3
  69. package/dist/http/routes/approvals-assistant.js +27 -26
  70. package/dist/http/routes/capabilities.js +6 -3
  71. package/dist/http/routes/mcp-reconnect.d.ts +17 -0
  72. package/dist/http/routes/mcp-reconnect.js +6 -1
  73. package/dist/http/routes/memory-bundle.js +1 -1
  74. package/dist/http/routes/memory-compliance.js +1 -1
  75. package/dist/http/routes/memory-origin.d.ts +1 -1
  76. package/dist/http/routes/memory-origin.js +1 -1
  77. package/dist/http/routes/runs.js +47 -33
  78. package/dist/http/routes/sessions.d.ts +1 -1
  79. package/dist/http/routes/sessions.js +23 -22
  80. package/dist/http/routes/workflows.js +2 -0
  81. package/dist/http/wire-types.d.ts +17 -13
  82. package/dist/images/manifest.d.ts +7 -1
  83. package/dist/images/manifest.js +18 -16
  84. package/dist/memory-store-refusal.d.ts +38 -18
  85. package/dist/memory-store-refusal.js +25 -7
  86. package/dist/model-unreachable-notice.d.ts +1 -1
  87. package/dist/namespaced-rule-name.d.ts +46 -41
  88. package/dist/namespaced-rule-name.js +36 -22
  89. package/dist/observability/corrupt-read-seat.d.ts +4 -0
  90. package/dist/observability/corrupt-read-seat.js +1 -0
  91. package/dist/observability/fail-open.d.ts +19 -3
  92. package/dist/observability/fail-open.js +19 -3
  93. package/dist/operator-ask.d.ts +6 -2
  94. package/dist/orchestration/workflow-agent-session-index.d.ts +82 -59
  95. package/dist/orchestration/workflow-agent-session-index.js +80 -14
  96. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -0
  97. package/dist/orchestration/workflow-completion-inbox.js +39 -17
  98. package/dist/orchestration/workflow-notify-journal.d.ts +1 -1
  99. package/dist/orchestration/workflow-notify-journal.js +7 -2
  100. package/dist/outbound-dispatcher.d.ts +29 -20
  101. package/dist/outbound-dispatcher.js +105 -16
  102. package/dist/parked-decide.d.ts +43 -48
  103. package/dist/parked-decide.js +54 -16
  104. package/dist/permission-rule-vocab.d.ts +4 -3
  105. package/dist/permission-rule-vocab.js +1 -1
  106. package/dist/plugins/approval-ask-store-sql.d.ts +17 -20
  107. package/dist/plugins/approval-ask-store-sql.js +0 -3
  108. package/dist/plugins/approval-exemption-store.d.ts +4 -0
  109. package/dist/plugins/approval-exemption-store.js +1 -0
  110. package/dist/plugins/blob-backend.d.ts +13 -0
  111. package/dist/plugins/blob-backend.js +26 -0
  112. package/dist/plugins/local-session-store.d.ts +78 -47
  113. package/dist/plugins/local-session-store.js +152 -122
  114. package/dist/plugins/one-time-migrations.d.ts +11 -0
  115. package/dist/plugins/one-time-migrations.js +128 -4
  116. package/dist/plugins/pg-pool.js +1 -1
  117. package/dist/plugins/snapshot-bucket-presence.d.ts +36 -0
  118. package/dist/plugins/snapshot-bucket-presence.js +40 -0
  119. package/dist/plugins/store-backend.d.ts +17 -6
  120. package/dist/plugins/store-backend.js +46 -16
  121. package/dist/plugins/store-open-wait.d.ts +33 -0
  122. package/dist/plugins/store-open-wait.js +73 -0
  123. package/dist/plugins/tidb-pool.js +1 -1
  124. package/dist/plugins/web-search.d.ts +20 -1
  125. package/dist/plugins/web-search.js +39 -10
  126. package/dist/plugins/workflow-run-store-sql.d.ts +7 -6
  127. package/dist/plugins/workflow-run-store-sql.js +5 -7
  128. package/dist/request-key-closure.js +1 -1
  129. package/dist/request-value-closure.d.ts +1 -1
  130. package/dist/rule-entry-list.d.ts +35 -46
  131. package/dist/rule-entry-list.js +18 -26
  132. package/dist/rules-consent.js +3 -0
  133. package/dist/run-local.js +5 -3
  134. package/dist/runs.d.ts +1 -1
  135. package/dist/security.d.ts +5 -3
  136. package/dist/security.js +4 -0
  137. package/dist/session-policy-wire.d.ts +13 -12
  138. package/dist/session-policy-wire.js +11 -11
  139. package/dist/spec-fields.d.ts +13 -11
  140. package/dist/spec-fields.js +4 -7
  141. package/dist/standing-answer-grant.d.ts +14 -2
  142. package/dist/standing-answer-grant.js +8 -1
  143. package/dist/store-posture.d.ts +14 -6
  144. package/dist/store-posture.js +7 -4
  145. package/dist/task-settings.d.ts +27 -55
  146. package/dist/task-settings.js +13 -21
  147. package/dist/tool-approval.d.ts +133 -138
  148. package/dist/tool-approval.js +175 -66
  149. package/dist/trace/core-keyset-guard.d.ts +5 -5
  150. package/dist/trace/redact.d.ts +6 -0
  151. package/dist/trace/redact.js +9 -9
  152. package/dist/trace/self-minted-wire.d.ts +16 -2
  153. package/dist/trace/self-minted-wire.js +7 -0
  154. package/dist/user-input-ledger.d.ts +30 -14
  155. package/dist/user-input-ledger.js +25 -14
  156. package/package.json +2 -2
  157. package/scripts/drain-check-local.mjs +74 -3
  158. package/scripts/drain-check.mysql.sql +43 -17
  159. package/scripts/drain-check.pg.sql +46 -20
@@ -31,6 +31,10 @@ unset _self _hops _dir
31
31
  . "$HERE/lib/shell-access.sh"
32
32
  # 取值旗的形(S-621):判据与词表同 sema-up.sh 一处定义。
33
33
  . "$HERE/lib/flagshape.sh"
34
+ # 引擎 /health 体里「落在哪个后端」的读法与期望值(smoke.sh / kube-up.sh 同一只)。
35
+ . "$HERE/lib/health.sh"
36
+ # 对象存储桶的唯一表(sema-up.sh / kube-up.sh / smoke.sh 同读这一张)。
37
+ . "$HERE/lib/buckets.sh"
34
38
  log() { printf '[cluster-up] %s\n' "$*" >&2; }
35
39
  die() { log "⛔ $*"; exit 1; }
36
40
 
@@ -419,8 +423,9 @@ PY
419
423
  # 凭据清单经 stdin 直接 apply,不落远端盘(落 /tmp 失败时会残留)
420
424
  rrun 0 k3s kubectl apply -f - < "$RENDERED" >/dev/null
421
425
  fi
422
- rsudo 0 <<'EOF'
426
+ rsudo 0 $(buckets_prefixed local/ $(buckets_k8s)) <<'EOF'
423
427
  set -e
428
+ BUCKETS=("$@") # 要建的桶(local/<桶>):清单只在 lib/buckets.sh 一处,经位置参数递给远端
424
429
  # runner token 跨 ns 复制要赶在 server 起 pod 前(晚了=CreateContainerConfigError 干等——真踩);
425
430
  # SA token secret 由控制器异步填充,先等它有值
426
431
  TOK=""
@@ -431,6 +436,12 @@ done
431
436
  [ -n "$TOK" ] || { echo "sema-runner-token 未被控制器填充" >&2; exit 1; }
432
437
  k3s kubectl create secret generic sema-runner-token-copy -n sema --from-literal=token="$(printf '%s' "$TOK" | base64 -d)" \
433
438
  --dry-run=client -o yaml | k3s kubectl apply -f - >/dev/null
439
+ # MinIO 建桶(备份桶+引擎桶)—— 排在等 PG healthy 与等 server rollout **之前**:引擎起服会核快照桶在不在,桶缺时 server 不就绪、rollout 永远等不到,而建桶又排在它后面 ⇒ 互等;
440
+ # MinIO 不依赖 PG,建桶也就不必陪着等 PG(冷启拉镜像 + init 可要几分钟,会把引擎的等桶窗耗掉)
441
+ k3s kubectl rollout status deployment/sema-minio -n sema --timeout=300s >/dev/null
442
+ PW=$(k3s kubectl get secret sema-minio -n sema -o jsonpath='{.data.rootPassword}' | base64 -d)
443
+ k3s kubectl exec deploy/sema-minio -n sema -- sh -c \
444
+ "mc alias set local http://localhost:9000 sema '$PW' >/dev/null && mc mb --ignore-existing ${BUCKETS[*]}" >/dev/null
434
445
  # PG healthy(冷启拉镜像+init 可要几分钟)
435
446
  for t in $(seq 1 120); do
436
447
  s=$(k3s kubectl get cluster sema-pg -n sema -o jsonpath='{.status.phase}' 2>/dev/null || true)
@@ -438,24 +449,25 @@ for t in $(seq 1 120); do
438
449
  done
439
450
  [ "$s" = "Cluster in healthy state" ] || { k3s kubectl get cluster -n sema; echo "PG 未达 healthy" >&2; exit 1; }
440
451
  k3s kubectl rollout status deployment/sema-server -n sema --timeout=300s >/dev/null
441
- # MinIO 建桶(备份桶+对象桶)
442
- PW=$(k3s kubectl get secret sema-minio -n sema -o jsonpath='{.data.rootPassword}' | base64 -d)
443
- k3s kubectl exec deploy/sema-minio -n sema -- sh -c \
444
- "mc alias set local http://localhost:9000 sema '$PW' >/dev/null && mc mb --ignore-existing local/sema-pg-backup local/workspaces local/blobs" >/dev/null
445
452
  echo STACK_OK
446
453
  EOF
447
454
 
448
455
  # ── 8. smoke(节点内经 NodePort;外网入口=LB 在云商侧配,打印指引)────────────────
449
456
  log "8/8 smoke"
450
457
  # NodePort 被 nodeport-addresses 钉在节点网段——loopback 不在其内,必须打 node-ip(真踩 curl exit 7)
451
- SMOKE=$(rsudo 0 <<EOF
452
- H=\$(curl -m5 -s -o /dev/null -w '%{http_code}' "http://$K3S0_IP:$NODEPORT/health" || echo 000)
458
+ # 第一行 = 两个 HTTP 码,其后 = /health 体(后端位在里面:200 + 401 测不出「服务在内存里跑」,验收必须读那一位)
459
+ SMOKE_OUT=$(rsudo 0 <<EOF
460
+ HB=\$(curl -m5 -s -w '\n%{http_code}' "http://$K3S0_IP:$NODEPORT/health" || printf '\n000')
461
+ H="\${HB##*\$'\n'}"
453
462
  A=\$(curl -m5 -s -o /dev/null -w '%{http_code}' "http://$K3S0_IP:$NODEPORT/v1/runs" || echo 000)
454
463
  echo "health=\$H auth=\$A"
464
+ printf '%s' "\${HB%\$'\n'*}"
455
465
  EOF
456
466
  )
467
+ SMOKE="${SMOKE_OUT%%$'\n'*}"
457
468
  case "$SMOKE" in *"health=200 auth=401"*) : ;; *) die "smoke 失败:$SMOKE(期望 health=200 auth=401)" ;; esac
458
- log "smoke ✓ $SMOKE"
469
+ BK_WHY=$(health_backend_check "${SMOKE_OUT#*$'\n'}") || die "smoke 失败:$BK_WHY。下一步:kubectl -n sema logs deploy/sema-server(引擎在库没连上时不会再退内存起服 —— 它会拒启;读到 memory 多半是旧镜像)"
470
+ log "smoke ✓ $SMOKE backend=$SEMA_UP_EXPECT_SESSION_BACKEND"
459
471
  cat >&2 <<EOF
460
472
 
461
473
  ══════════ cluster-up 完成($MACHINE_COUNT 节点)══════════
@@ -9,7 +9,7 @@
9
9
  # 输入(sema-up.sh 注入;也可单独跑):
10
10
  # WORK(状态目录)PROJECT REGION KUBE_MODE [KUBECONFIG_PATH] [DRY_RUN]
11
11
  # USERS REGISTRY_ENABLED GIT_MODE [GIT_URL GIT_TOKEN]
12
- # SEMA_SERVER_IMAGE(必需)[SEMA_REGISTRY_IMAGE][GITEA_IMAGE]
12
+ # SEMA_SERVER_IMAGE(必需)[SEMA_REGISTRY_IMAGE][GITEA_IMAGE][MINIO_IMAGE(整条引用,换国内源用;缺省 = chart 钉死的 tag)]
13
13
  # MODEL_GATEWAY_BASEURL MODEL_ID MODEL_API_KEY
14
14
  # [NAMESPACE=sema][STORAGE_CLASS=集群默认][CNPG_VERSION]
15
15
  set -euo pipefail
@@ -33,6 +33,10 @@ unset _self _hops _dir
33
33
  . "$HERE/lib/shell-access.sh"
34
34
  # 取值旗的形(S-621):判据与词表同 sema-up.sh 一处定义。
35
35
  . "$HERE/lib/flagshape.sh"
36
+ # 引擎 /health 体里「落在哪个后端」的读法与期望值(smoke.sh / cluster-up.sh 同一只)。
37
+ . "$HERE/lib/health.sh"
38
+ # 对象存储桶的唯一表(sema-up.sh / cluster-up.sh / smoke.sh 同读这一张)。
39
+ . "$HERE/lib/buckets.sh"
36
40
  log() { printf '[kube-up] %s\n' "$*" >&2; }
37
41
  die() { log "⛔ $*"; exit 1; }
38
42
 
@@ -214,6 +218,7 @@ helm_values() { # helm_values <giteaBaseUrl> <oauthId> <oauthSecret>
214
218
  V_REG_PW="${REGISTRY_ADMIN_PASSWORD:-}" V_REG_SS="${REGISTRY_SESSION_SECRET:-}" V_REG_PT="${REGISTRY_PULL_TOKEN:-}" \
215
219
  V_GITEA_ENABLED="$([ "$GIT_MODE" = provision ] && echo true || echo false)" \
216
220
  V_GITEA_IMAGE="${GITEA_IMAGE:-gitea/gitea:1.24}" V_NODE_IP="$NODE_IP" \
221
+ V_MINIO_IMAGE="${MINIO_IMAGE:-}" \
217
222
  python3 - <<'PY'
218
223
  import json, os
219
224
  e = os.environ
@@ -244,6 +249,8 @@ v = {
244
249
  "gitea": {"enabled": e["V_GITEA_ENABLED"] == "true", "image": e["V_GITEA_IMAGE"],
245
250
  "storageClass": e["V_SC"], "domain": e["V_NODE_IP"]},
246
251
  }
252
+ # MinIO 镜像:给了才递(默认只在 chart values.yaml 写一处,不在脚本里再抄一份);国内源 = 整条引用
253
+ if e["V_MINIO_IMAGE"]: v["minio"]["image"] = e["V_MINIO_IMAGE"]
247
254
  print(json.dumps(v))
248
255
  PY
249
256
  }
@@ -317,6 +324,12 @@ done
317
324
  [ -n "$TOK" ] || die "sema-runner-token 未被控制器填充。下一步:kubectl -n sema-sandbox get secret,sa"
318
325
  printf '%s' "$TOK" | base64 -d | $KUBECTL create secret generic sema-runner-token-copy -n "$NAMESPACE" \
319
326
  --from-file=token=/dev/stdin --dry-run=client -o yaml | $KUBECTL apply -f - >/dev/null
327
+ $KUBECTL rollout status deployment/sema-minio -n "$NAMESPACE" --timeout=300s >/dev/null
328
+ # MinIO 建桶(幂等;备份桶+引擎桶,清单只在 lib/buckets.sh 一处)。引擎起服会核快照桶在不在,所以建桶排在等 PG healthy 与等 server rollout 之前
329
+ # (MinIO 不依赖 PG;PG 冷启可要几分钟,不该把引擎的等桶窗耗掉)
330
+ $KUBECTL exec deploy/sema-minio -n "$NAMESPACE" -- sh -c \
331
+ "mc alias set local http://localhost:9000 sema \"\$MINIO_ROOT_PASSWORD\" >/dev/null && mc mb --ignore-existing $(buckets_prefixed local/ $(buckets_k8s))" >/dev/null \
332
+ || die "MinIO 建桶失败。下一步:kubectl -n $NAMESPACE logs deploy/sema-minio"
320
333
  # PG healthy(冷启拉镜像+init 可要几分钟)
321
334
  s=""
322
335
  for t in $(seq 1 120); do
@@ -324,11 +337,6 @@ for t in $(seq 1 120); do
324
337
  [ "$s" = "Cluster in healthy state" ] && break; sleep 5
325
338
  done
326
339
  [ "$s" = "Cluster in healthy state" ] || { $KUBECTL get cluster -n "$NAMESPACE" >&2 || true; die "PG 未达 healthy。下一步:kubectl -n $NAMESPACE describe cluster sema-pg;PVC Pending 时查 StorageClass"; }
327
- $KUBECTL rollout status deployment/sema-minio -n "$NAMESPACE" --timeout=300s >/dev/null
328
- # MinIO 建桶(幂等;备份桶+对象桶)
329
- $KUBECTL exec deploy/sema-minio -n "$NAMESPACE" -- sh -c \
330
- "mc alias set local http://localhost:9000 sema \"\$MINIO_ROOT_PASSWORD\" >/dev/null && mc mb --ignore-existing local/sema-pg-backup local/workspaces local/blobs local/backups" >/dev/null \
331
- || die "MinIO 建桶失败。下一步:kubectl -n $NAMESPACE logs deploy/sema-minio"
332
340
  $KUBECTL rollout status deployment/sema-server -n "$NAMESPACE" --timeout=600s >/dev/null || die "sema-server 未就绪。下一步:kubectl -n $NAMESPACE logs deploy/sema-server(常见:镜像拉取失败→imagePullSecrets/私仓凭据)"
333
341
 
334
342
  # ── Gitea 引导(provision:管理员/token/OAuth app;全部 in-pod 动作,不依赖 NodePort 可达)──
@@ -379,30 +387,35 @@ fi
379
387
 
380
388
  # ── smoke(port-forward 直验——kind/远程 kubeconfig 下 NodePort 对本机不可达,别靠它)──
381
389
  log "5/6 smoke"
382
- smoke_pf() { # smoke_pf <svc> <port> <path> → http code
383
- local pf_pid code
390
+ smoke_pf() { # smoke_pf <svc> <port> <path> → 第一行 http code,其后是响应体(调用方按需取)
391
+ local pf_pid out code body
384
392
  $KUBECTL port-forward "svc/$1" -n "$NAMESPACE" "1$2:$2" >/dev/null 2>&1 &
385
393
  pf_pid=$!
386
394
  sleep 2
387
- code=$(curl -m5 -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:1$2$3" || echo 000)
395
+ out=$(curl -m5 -s -w '\n%{http_code}' "http://127.0.0.1:1$2$3" || printf '\n000')
388
396
  kill $pf_pid 2>/dev/null; wait $pf_pid 2>/dev/null || true
389
- printf '%s' "$code"
397
+ code="${out##*$'\n'}"; body="${out%$'\n'*}"
398
+ [ "$out" = "$code" ] && body="" # 没有换行 = 只有码(假 curl / 空体)
399
+ printf '%s\n%s' "$code" "$body"
390
400
  }
391
- H=$(smoke_pf sema-server 8090 /health)
401
+ HR=$(smoke_pf sema-server 8090 /health)
402
+ H="${HR%%$'\n'*}"
392
403
  [ "$H" = "200" ] || die "smoke 失败:server /health=$H(期望 200)。下一步:kubectl -n $NAMESPACE logs deploy/sema-server"
393
- A=$(smoke_pf sema-server 8090 /v1/runs)
404
+ # /health 200 ≠ 数据落了库:后端位必须是期望的持久后端(durable(pg));读到 memory = 服务在内存里跑、重启即丢,不许判绿
405
+ BK_WHY=$(health_backend_check "${HR#*$'\n'}") || die "smoke 失败:$BK_WHY。下一步:kubectl -n $NAMESPACE logs deploy/sema-server(引擎在库没连上时不会再退内存起服 —— 它会拒启;读到 memory 多半是旧镜像)"
406
+ A=$(smoke_pf sema-server 8090 /v1/runs); A="${A%%$'\n'*}"
394
407
  [ "$A" = "401" ] || die "smoke 失败:无 token /v1/runs=$A(期望 401=auth 门在)。SERVICE_AUTH_TOKEN 未生效=裸奔,立刻查"
395
408
  R="n/a"
396
409
  if [ "$REGISTRY_ENABLED" = "true" ]; then
397
- R=$(smoke_pf sema-registry 3000 /api/healthz)
410
+ R=$(smoke_pf sema-registry 3000 /api/healthz); R="${R%%$'\n'*}"
398
411
  [ "$R" = "200" ] || die "smoke 失败:registry healthz=$R。下一步:kubectl -n $NAMESPACE logs deploy/sema-registry"
399
412
  fi
400
413
  G="n/a"
401
414
  if [ "$GIT_MODE" = "provision" ]; then
402
- G=$(smoke_pf sema-gitea 3000 /api/healthz)
415
+ G=$(smoke_pf sema-gitea 3000 /api/healthz); G="${G%%$'\n'*}"
403
416
  [ "$G" = "200" ] || die "smoke 失败:gitea healthz=$G"
404
417
  fi
405
- log "smoke ✓ health=$H auth=$A registry=$R gitea=$G"
418
+ log "smoke ✓ health=$H backend=$(health_session_backend "${HR#*$'\n'}") auth=$A registry=$R gitea=$G"
406
419
 
407
420
  # ── summary(一屏;凭据一次性显示)────────────────────────────────────────────────
408
421
  log "6/6 完成"
@@ -414,7 +427,7 @@ log "6/6 完成"
414
427
  echo " 本机不可达 NodePort 时(kind/远程):kubectl -n $NAMESPACE port-forward svc/sema-server 8090:8090"
415
428
  # 壳对 NodePort 的明文 http 按设计拒连(非回环要 https)⇒ 经 port-forward 带到本机回环再 connect。
416
429
  shell_access_text "$ENVF" "装了 kubectl、能连这个集群的机器上:" "kubectl -n $NAMESPACE port-forward svc/sema-server 8090:8090" "http://127.0.0.1:8090"
417
- echo " 数据 CNPG sema-pg×$PG_INSTANCES($PG_AA)+ MinIO(桶:workspaces/blobs/backups/sema-pg-backup)"
430
+ echo " 数据 CNPG sema-pg×$PG_INSTANCES($PG_AA)+ MinIO(桶:$(buckets_k8s | tr ' ' '/'))"
418
431
  if [ "$REGISTRY_ENABLED" = "true" ]; then
419
432
  echo " 配置中心 NodePort 30300 → http://$NODE_IP:30300"
420
433
  if $NEW_REG_PW; then echo " 首登:${REGISTRY_ADMIN_USER:-zhaoheran} / ${REGISTRY_ADMIN_PASSWORD}(⚠ 只显示这一次,之后看 .env)"
@@ -0,0 +1,21 @@
1
+ # lib/buckets.sh — 部署侧「要预置哪些对象存储桶」的唯一表。sema-up.sh(compose 建桶 / 外接 S3 建桶 / 汇总)、kube-up.sh、cluster-up.sh(k8s 建桶)与
2
+ # smoke.sh(验桶)都读这一张,不各写一份串:各写一份的下场是清单漏一只桶,而漏桶的症状是起服后每一次对象写 / 每一拍 sweep 都 404。
3
+ #
4
+ # server **不建桶**(docs/DEPLOY-PREREQS.md「对象存储桶」),引擎起服只核一次快照桶在不在、不在就拒启 —— 所以这张表必须包含引擎要用的每一只:
5
+ # · `session-snapshots` = 引擎的 `SESSION_SNAPSHOT_BUCKET` 缺省名(snapshot-BLOB offload + SendUserFile 私有 presigned 轨 / 沙箱 staging);
6
+ # 由测试把这个名与引擎的缺省逐字咬合(引擎改缺省而这里没跟 ⇒ 红);
7
+ # · `sema-public`(SendUserFile 永久公链轨)**不在表里**:它要匿名 GetObject-only 策略,是运维另配的桶,脚本不替人开匿名读。
8
+ SEMA_UP_ENGINE_BUCKETS="workspaces blobs backups session-snapshots"
9
+ # CNPG 的 WAL 归档 / 每日基备桶(只有 k8s 两条路径有 PG 备份;名与 chart values 的 `pg.backup.bucket: s3://<名>/` 同名,由测试咬合)。
10
+ SEMA_UP_PG_BACKUP_BUCKET="sema-pg-backup"
11
+
12
+ # buckets_k8s → 打印 k8s 路径(kube-up / cluster-up)要建的全部桶,空格分隔,每只一次。
13
+ buckets_k8s() { printf '%s %s' "$SEMA_UP_PG_BACKUP_BUCKET" "$SEMA_UP_ENGINE_BUCKETS"; }
14
+
15
+ # buckets_prefixed <前缀> <桶…> → 每个桶名前加前缀(mc 的 `<别名>/<桶>` 写法),空格分隔。桶名是 S3 闭字符集,不含空白,按空白拆是安全的。
16
+ buckets_prefixed() {
17
+ local p="$1" b out=""
18
+ shift
19
+ for b in "$@"; do out="$out$p$b "; done
20
+ printf '%s' "${out% }"
21
+ }
@@ -0,0 +1,33 @@
1
+ # lib/health.sh — 「装好的引擎 /health 体说它真落在哪个后端」的唯一读法 + sema-up 部署的期望值。
2
+ # smoke.sh / kube-up.sh / cluster-up.sh 三处验收都调它,不各写一份(各写一份的下场:有一处还只看 HTTP 200,把退到内存的服务判绿)。
3
+ #
4
+ # 事实(出处是引擎,不是推测):
5
+ # · `/health` 体里的 `sessionBackend` 是**装配后**的有效店:`durable(<kind>)`(kind = pg | mysql | local)或 `memory`
6
+ # (引擎侧只有一只读法:src/store-posture.ts 的 `sessionStoreLabelOf`);
7
+ # · sema-up 三条路径(compose / kube / 多机)起的引擎一律 `DB_BACKEND=pg`(stack/compose.yaml、chart/templates/server.yaml、
8
+ # stack/sema-stack-cluster.yaml 逐份写死),所以装好的引擎必须读到 `durable(pg)`。读到 `memory` = 会话 / 停驻 / run 账本在进程内存里、
9
+ # 重启即丢 —— /health 照样 200、401 门照样在,只有这一位说了实话,验收必须看它。
10
+ SEMA_UP_EXPECT_SESSION_BACKEND="durable(pg)"
11
+
12
+ # health_session_backend <health 体> → 打印 `sessionBackend` 的值;体读不成 JSON / 没有这一位 ⇒ 打印空串(调用方按「读不出」处置,不猜)。
13
+ health_session_backend() {
14
+ printf '%s' "$1" | python3 -c 'import json,sys
15
+ try:
16
+ v = json.load(sys.stdin).get("sessionBackend", "")
17
+ print(v if isinstance(v, str) else "")
18
+ except Exception:
19
+ print("")' 2>/dev/null
20
+ }
21
+
22
+ # health_backend_check <health 体> → 0 = 落在期望的后端;1 = 不是(stdout 打一句人话,供调用方拼进失败行)。
23
+ health_backend_check() {
24
+ local got
25
+ got="$(health_session_backend "$1")"
26
+ if [ "$got" = "$SEMA_UP_EXPECT_SESSION_BACKEND" ]; then return 0; fi
27
+ if [ -z "$got" ]; then
28
+ printf '/health 体里读不出 sessionBackend(期望 %s)' "$SEMA_UP_EXPECT_SESSION_BACKEND"
29
+ else
30
+ printf '/health.sessionBackend=%s,期望 %s' "$got" "$SEMA_UP_EXPECT_SESSION_BACKEND"
31
+ fi
32
+ return 1
33
+ }
@@ -34,6 +34,8 @@ unset _self _hops _dir
34
34
  . "$HERE/lib/shell-access.sh"
35
35
  # 取值旗的形(S-621):判据与词表(--profile / --users / --region / --git / --kube)的唯一定义;--help 的词表也从这里渲。
36
36
  . "$HERE/lib/flagshape.sh"
37
+ # 对象存储桶的唯一表(kube-up.sh / cluster-up.sh / smoke.sh 同读这一张)。
38
+ . "$HERE/lib/buckets.sh"
37
39
  # 日志一律打到 stderr(stdout 留给 --interactive=json 的协议行)。定义在最前面:参数一解析完就要判形,那时下面的函数都还没定义。
38
40
  log() { printf '[sema-up] %s\n' "$*" >&2; }
39
41
  # 执行车道表的唯一读者(S-599)。定义在**最前面**:`--help` 的词表也从表渲(手抄的词表会在加一条
@@ -647,7 +649,7 @@ if $DRY_RUN; then
647
649
  [ "$GIT_MODE" = "provision" ] && echo " 2. 先起 Gitea → 引导管理员+access token$([ "$REGISTRY_ENABLED" = "true" ] && printf '+OAuth app(接 registry 登录)')"
648
650
  [ -n "$PG_URL$S3_ENDPOINT" ] && echo " 2b. 外接端点连接检测(PG=真 select 1;S3=兼容协议 ls+建桶;证书不校验)"
649
651
  echo " 3. docker compose up -d --wait(profiles=$COMPOSE_PROFILES)$([ -n "$DATA_ROOT" ] && printf ';数据卷 bind → %s' "$DATA_ROOT")"
650
- case ",$COMPOSE_PROFILES," in *,s3,*) echo " 4. MinIO 建桶 workspaces/blobs/backups(幂等)" ;; esac
652
+ case ",$COMPOSE_PROFILES," in *,s3,*) echo " 4. MinIO 建桶 ${SEMA_UP_ENGINE_BUCKETS// //}(幂等)" ;; esac
651
653
  [ "$GIT_MODE" = "connect" ] && echo " +. 验证 $GIT_URL 连通(Gitea→GitHub→通用 git 探测链,证书不校验;Gitea=自动建 OAuth app,其余=打印待人工项)"
652
654
  echo " 5. 装后即验(PG/S3$([ "$PROFILE" != "data-center" ] && printf '/引擎 health')$([ "$REGISTRY_ENABLED" = "true" ] && printf '/registry healthz')$([ "$GIT_MODE" = "provision" ] && printf '/gitea healthz'))+ 一屏 summary"
653
655
  echo " 端口: $PF_PORTS(bind:数据面 $([ "$PROFILE" = "data-center" ] && echo 0.0.0.0 || echo 127.0.0.1);服务/registry/git 0.0.0.0)"
@@ -874,9 +876,9 @@ fi
874
876
  if [ -n "$S3_ENDPOINT" ]; then
875
877
  log "外接 S3 连接检测($S3_ENDPOINT)"
876
878
  S3OK=$(docker run --rm --entrypoint sh -e MC_HOST_x="$(printf '%s' "$S3_ENDPOINT" | sed "s#://#://$S3_ACCESS_KEY:$S3_SECRET_KEY@#")" \
877
- "${MC_IMAGE:-minio/mc:latest}" -c 'mc --insecure ls x/ >/dev/null 2>&1 && mc --insecure mb --ignore-existing x/workspaces x/blobs x/backups >/dev/null 2>&1 && echo S3_OK' 2>/dev/null | tail -1)
879
+ "${MC_IMAGE:-minio/mc:latest}" -c "mc --insecure ls x/ >/dev/null 2>&1 && mc --insecure mb --ignore-existing $(buckets_prefixed x/ $SEMA_UP_ENGINE_BUCKETS) >/dev/null 2>&1 && echo S3_OK" 2>/dev/null | tail -1)
878
880
  [ "$S3OK" = "S3_OK" ] || { log "⛔ 外接 S3 连不上或建桶失败。下一步:核对 endpoint/密钥;需要 S3 兼容协议(MinIO/OSS/COS/R2 等);证书问题已用 --insecure 忽略,若还不通多半是网络/密钥"; exit 1; }
879
- log "外接 S3 就绪(workspaces/blobs/backups 已确保)"
881
+ log "外接 S3 就绪(${SEMA_UP_ENGINE_BUCKETS// //} 已确保)"
880
882
  fi
881
883
 
882
884
  # ── git=provision:先起 Gitea 并引导(OAuth app 的 client id/secret 要在 registry 起来之前
@@ -1005,13 +1007,16 @@ fi
1005
1007
  # D(S-618):up 之前先 `compose config` 渲染对账 —— 容器真正拿到的决策键与本次决策逐值对齐才 up。
1006
1008
  reconcile_render
1007
1009
  log "装配:docker compose profiles=$COMPOSE_PROFILES project=$PROJECT"
1008
- COMPOSE_PROFILES="$COMPOSE_PROFILES" compose up -d --wait --remove-orphans \
1009
- || { log "⛔ compose up 失败。下一步:docker compose -p $PROJECT logs(常见:镜像拉取失败→检查镜像引用/docker login;端口占用→--port 换口)"; exit 1; }
1010
- # 建桶(幂等,一次性动作归 Layer 0;只对本地 MinIO——外接 S3 已在连接检测时确保)
1010
+ # 建桶(幂等,一次性动作归 Layer 0;只对本地 MinIO——外接 S3 已在连接检测时确保)。🔴 排在**全栈 up 之前**:引擎起服会核快照桶在不在
1011
+ # (缺则拒启),而 `up --wait` 要等引擎健康 —— 桶建在 up 之后,新数据卷上就是引擎等桶、compose 等引擎的互等。所以先只起 MinIO(`--wait` 等它健康)、建桶,再起全栈。
1011
1012
  envf_load MINIO_PASSWORD # A:回读生成的 MinIO 口令(建桶用),不 source .env
1012
1013
  case ",$COMPOSE_PROFILES," in *,s3,*)
1013
- docker run --rm --network "${PROJECT}_default" -e MC_HOST_x="http://sema:${MINIO_PASSWORD}@minio:9000" "${MC_IMAGE:-minio/mc:latest}" mb --ignore-existing x/workspaces x/blobs x/backups >/dev/null || { log "⛔ MinIO 建桶失败。下一步:docker compose -p $PROJECT logs minio"; exit 1; } ;;
1014
+ COMPOSE_PROFILES="$COMPOSE_PROFILES" compose up -d --wait minio \
1015
+ || { log "⛔ MinIO 启动失败。下一步:docker compose -p $PROJECT logs minio(常见:镜像拉取失败→检查镜像引用/docker login)"; exit 1; }
1016
+ docker run --rm --network "${PROJECT}_default" -e MC_HOST_x="http://sema:${MINIO_PASSWORD}@minio:9000" "${MC_IMAGE:-minio/mc:latest}" mb --ignore-existing $(buckets_prefixed x/ $SEMA_UP_ENGINE_BUCKETS) >/dev/null || { log "⛔ MinIO 建桶失败。下一步:docker compose -p $PROJECT logs minio"; exit 1; } ;;
1014
1017
  esac
1018
+ COMPOSE_PROFILES="$COMPOSE_PROFILES" compose up -d --wait --remove-orphans \
1019
+ || { log "⛔ compose up 失败。下一步:docker compose -p $PROJECT logs(常见:镜像拉取失败→检查镜像引用/docker login;端口占用→--port 换口)"; exit 1; }
1015
1020
 
1016
1021
  # ── 验证(装后即验,§3d smoke 的子集;全量随时可跑 smoke.sh --project=$PROJECT)──────────────
1017
1022
  # A:回读验证 / 汇总要用的凭据与登记值(不 source .env);缺的键回读成空,由下游 :- 缺省处置。
@@ -1066,9 +1071,9 @@ else
1066
1071
  esac
1067
1072
  [ -n "$PG_URL" ] && echo " PG(外接) $EXT_PG_HOST:$EXT_PG_PORT/$EXT_PG_DB(连接检测已过;凭据在 .env)"
1068
1073
  case ",$COMPOSE_PROFILES," in *,s3,*)
1069
- echo " S3/MinIO http://$HOSTIP:$MINIO_PORT(console :$MINIO_CONSOLE_PORT,桶×$V_BK:workspaces/blobs/backups)" ;;
1074
+ echo " S3/MinIO http://$HOSTIP:$MINIO_PORT(console :$MINIO_CONSOLE_PORT,桶×$V_BK:${SEMA_UP_ENGINE_BUCKETS// //})" ;;
1070
1075
  esac
1071
- [ -n "$S3_ENDPOINT" ] && echo " S3(外接) $S3_ENDPOINT(连接检测+三桶已确保;密钥在 .env)"
1076
+ [ -n "$S3_ENDPOINT" ] && echo " S3(外接) $S3_ENDPOINT(连接检测+引擎桶已确保;密钥在 .env)"
1072
1077
  case ",$COMPOSE_PROFILES," in *,agent,*)
1073
1078
  echo " 引擎 http://$HOSTIP:$SERVER_PORT(/health: $V_HEALTH;API 凭据=SERVICE_AUTH_TOKEN)"
1074
1079
  compose_shell_access_text "$SERVER_PORT" ;;
@@ -22,6 +22,10 @@ unset _self _hops _dir
22
22
  . "$HERE/lib/envf.sh"
23
23
  # 取值旗的形(S-621):判据同 sema-up.sh 一处定义。
24
24
  . "$HERE/lib/flagshape.sh"
25
+ # 引擎 /health 体里「落在哪个后端」的读法与期望值(kube-up / cluster-up 的验收同一只)。
26
+ . "$HERE/lib/health.sh"
27
+ # 对象存储桶的唯一表(sema-up.sh / kube-up.sh / cluster-up.sh 的建桶清单同读这一张)。
28
+ . "$HERE/lib/buckets.sh"
25
29
  log() { printf '[smoke] %s\n' "$*" >&2; }
26
30
 
27
31
  PROJECT="" URL="" TOKEN=""
@@ -68,9 +72,10 @@ if [ -n "$PROJECT" ]; then
68
72
  # 哨兵判定(cat 回读值核对后 echo),不依赖各 mc 子命令的输出行为
69
73
  S3RT=$(docker run --rm --entrypoint sh --network "${PROJECT}_default" -e MC_HOST_x="http://sema:${MINIO_PASSWORD}@minio:9000" "${MC_IMAGE:-minio/mc:latest}" -c 'echo smoke > /tmp/s && mc cp /tmp/s x/blobs/_smoke >/dev/null && [ "$(mc cat x/blobs/_smoke)" = smoke ] && mc rm x/blobs/_smoke >/dev/null && echo S3_ROUNDTRIP_OK' 2>/dev/null | tail -1)
70
74
  [ "$S3RT" = "S3_ROUNDTRIP_OK" ] && ok "S3 put/get/rm 往返" || fail "S3" "对象读写失败" "docker compose -p $PROJECT logs minio"
71
- # ③b 三个约定桶逐个点名(data-center 验收:ls 行数≠桶名对——错桶也会凑数)
72
- BKTS=$(docker run --rm --entrypoint sh --network "${PROJECT}_default" -e MC_HOST_x="http://sema:${MINIO_PASSWORD}@minio:9000" "${MC_IMAGE:-minio/mc:latest}" -c 'for b in workspaces blobs backups; do mc ls "x/$b" >/dev/null 2>&1 && printf "%s " "$b"; done' 2>/dev/null)
73
- [ "$BKTS" = "workspaces blobs backups " ] && ok "桶就绪(workspaces/blobs/backups)" || fail "桶" "缺桶:应有 workspaces/blobs/backups,实有 ${BKTS:-无}" "重跑 sema-up.sh(建桶幂等)"
75
+ # ③b 引擎桶逐个点名(data-center 验收:ls 行数≠桶名对——错桶也会凑数;清单 = lib/buckets.sh 那一张表)
76
+ BKTS=$(docker run --rm --entrypoint sh --network "${PROJECT}_default" -e MC_HOST_x="http://sema:${MINIO_PASSWORD}@minio:9000" "${MC_IMAGE:-minio/mc:latest}" -c "for b in $SEMA_UP_ENGINE_BUCKETS; do mc ls \"x/\$b\" >/dev/null 2>&1 && printf \"%s \" \"\$b\"; done" 2>/dev/null)
77
+ BKTS_WANT="$(printf '%s ' $SEMA_UP_ENGINE_BUCKETS)"
78
+ [ "$BKTS" = "$BKTS_WANT" ] && ok "桶就绪(${SEMA_UP_ENGINE_BUCKETS// //})" || fail "桶" "缺桶:应有 ${SEMA_UP_ENGINE_BUCKETS// //},实有 ${BKTS:-无}" "重跑 sema-up.sh(建桶幂等)"
74
79
  fi
75
80
  # 外接数据面(--pg-url/--s3-endpoint):有 endpoint 就远程直验(证书不校验)
76
81
  if [ -n "${AGENT_PG_HOST:-}" ] && ! $HAS_PG && [ "${DEPLOY_PROFILE:-}" != "local-plus" ]; then
@@ -100,9 +105,14 @@ except Exception: print("")' 2>/dev/null)
100
105
  fi
101
106
 
102
107
  if [ -n "$URL" ]; then
103
- # ④ /health
104
- H=$(curl -m5 -s "$URL/health" | head -c 200)
105
- case "$H" in *'"status":"ok"'*) ok "/health($URL)" ;; *) fail "/health" "无响应或非 ok:${H:-空}" "服务没起/端口不对/防火墙;docker logs 或 systemctl 看进程" ;; esac
108
+ # ④ /health(整体取回:后端位在体的中后段,截断读会丢它)
109
+ H=$(curl -m5 -s "$URL/health")
110
+ case "$H" in *'"status":"ok"'*) ok "/health($URL)" ;; *) fail "/health" "无响应或非 ok:$(printf '%s' "${H:-空}" | head -c 200)" "服务没起/端口不对/防火墙;docker logs 或 systemctl 看进程" ;; esac
111
+ # ④′ 后端位:/health 200 ≠ 数据落了库 —— 引擎的 sessionBackend 必须是期望的持久后端(durable(pg)),`memory` = 内存里跑、重启即丢
112
+ case "$H" in *'"status":"ok"'*)
113
+ if BK_WHY=$(health_backend_check "$H"); then ok "引擎后端(sessionBackend=$SEMA_UP_EXPECT_SESSION_BACKEND)"
114
+ else fail "引擎后端" "$BK_WHY" "库没连上时引擎不会再退内存起服(拒启);读到 memory 多半是旧镜像或手工起的引擎 —— 看 docker logs / kubectl logs 的 store_sql_open_wait 与拒启句"; fi ;;
115
+ esac
106
116
  if [ -n "$TOKEN" ]; then
107
117
  # ⑤ auth 门:无 token 必须 401(部署安全底线)
108
118
  C=$(curl -m5 -s -o /dev/null -w '%{http_code}' -X POST "$URL/v1/tasks" -H "Content-Type: application/json" -d '{}')
@@ -33,7 +33,7 @@ services:
33
33
  retries: 20
34
34
 
35
35
  minio:
36
- image: ${MINIO_IMAGE:-minio/minio:latest}
36
+ image: ${MINIO_IMAGE:-minio/minio:RELEASE.2025-09-07T16-13-09Z}
37
37
  profiles: ["data", "s3"]
38
38
  restart: unless-stopped
39
39
  # 单机=单盘模式(§3c:显式无冗余;集群形态才上 EC)
@@ -77,7 +77,8 @@ spec:
77
77
  spec:
78
78
  containers:
79
79
  - name: minio
80
- image: minio/minio:latest
80
+ image: minio/minio:RELEASE.2025-09-07T16-13-09Z
81
+ imagePullPolicy: IfNotPresent
81
82
  args: ["server", "/data", "--console-address", ":9001"]
82
83
  env:
83
84
  - { name: MINIO_ROOT_USER, valueFrom: { secretKeyRef: { name: sema-minio, key: rootUser } } }
@@ -155,6 +156,8 @@ spec:
155
156
  env:
156
157
  - { name: PORT, value: "8090" }
157
158
  - { name: DB_BACKEND, value: pg }
159
+ # 引擎 boot 期等库的窗(ms):CNPG 与 server Pod 同时起,库初始化常慢于 server 就绪 —— 窗内重试、到窗仍连不上才拒启(chart 的 server.storeWaitMs 同值)
160
+ - { name: STORE_LOCK_WAIT_MS, value: "300000" }
158
161
  - { name: SESSION_BACKEND, value: auto }
159
162
  - { name: PG_HOST, value: sema-pg-rw.sema.svc }
160
163
  - { name: PG_PORT, value: "5432" }
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { join } from "node:path";
3
- import { AppendLog, ensureDir } from "@sema-agent/core";
3
+ import { AppendLog, ensureDir, isHostDecider } from "@sema-agent/core";
4
4
  import { recordFailOpen } from "./observability/fail-open.js";
5
5
  import { replayJsonlSkipping } from "./observability/corrupt-read-seat.js";
6
6
  export const APPROVAL_ASK_AUDIT_DIR = "approval-ask-audit";
@@ -10,11 +10,6 @@ export const AUDIT_SHUTDOWN_CAUSE_WORDS = { shutdown_before_park: true };
10
10
  export function buildCheckpointAnchorHash(token) {
11
11
  return createHash("sha256").update(token, "utf8").digest("hex").slice(0, 16);
12
12
  }
13
- const HOST_DECIDED_BY_WORDS = ["person", "sla_timeout", "policy"];
14
- const HOST_DECIDED_BY = new Set(HOST_DECIDED_BY_WORDS);
15
- function isHostDecidedBy(v) {
16
- return typeof v === "string" && HOST_DECIDED_BY.has(v);
17
- }
18
13
  function isAuditShutdownCause(v) {
19
14
  return typeof v === "string" && Object.hasOwn(AUDIT_SHUTDOWN_CAUSE_WORDS, v);
20
15
  }
@@ -123,7 +118,7 @@ function readLedgerRecord(u, noteOutOfSetSeat) {
123
118
  const decidedByV = o.decidedBy;
124
119
  const legacySettledByV = decidedByV === undefined ? o.settledBy : undefined;
125
120
  const legacySettledBy = legacySettledByV === "human" || legacySettledByV === "timeout" || legacySettledByV === "aborted" ? legacySettledByV : undefined;
126
- const decidedBy = isHostDecidedBy(decidedByV) ? decidedByV : legacySettledBy !== undefined ? "person" : undefined;
121
+ const decidedBy = isHostDecider(decidedByV) ? decidedByV : legacySettledBy !== undefined ? "person" : undefined;
127
122
  const approver = s("approver");
128
123
  const settledAtMs = num("settledAtMs");
129
124
  const sessionId = s("sessionId");