@sema-agent/server 4.3.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/USAGE.md +16 -16
  4. package/dist/approval-hmac.js +1 -1
  5. package/dist/approval.d.ts +17 -37
  6. package/dist/approval.js +26 -112
  7. package/dist/auth-bridge.d.ts +0 -1
  8. package/dist/auth-bridge.js +0 -1
  9. package/dist/auth-keys.d.ts +1 -1
  10. package/dist/auth-keys.js +1 -1
  11. package/dist/bench/s1/run-firm.js +1 -1
  12. package/dist/boot/config-center.d.ts +1 -1
  13. package/dist/boot/config-center.js +4 -3
  14. package/dist/boot/reapers.d.ts +1 -2
  15. package/dist/boot/reapers.js +4 -5
  16. package/dist/boot/resolve-spec.d.ts +1 -3
  17. package/dist/boot/resolve-spec.js +21 -35
  18. package/dist/boot/stores.js +1 -1
  19. package/dist/brain.js +3 -1
  20. package/dist/capabilities/center-prompts.d.ts +2 -3
  21. package/dist/capabilities/center-prompts.js +2 -3
  22. package/dist/capabilities/sandbox-file-send.d.ts +0 -2
  23. package/dist/capabilities/sandbox-file-send.js +2 -5
  24. package/dist/capabilities/scenarios.d.ts +2 -13
  25. package/dist/capabilities/scenarios.js +3 -22
  26. package/dist/capabilities/send-user-file-tool.js +3 -3
  27. package/dist/config-center/apply-effective.js +51 -13
  28. package/dist/config-center/types.d.ts +10 -3
  29. package/dist/config-provider.d.ts +1 -1
  30. package/dist/config-provider.js +6 -11
  31. package/dist/config-types.d.ts +15 -29
  32. package/dist/config.d.ts +28 -0
  33. package/dist/config.js +122 -68
  34. package/dist/fleet/fleet-bus.d.ts +1 -1
  35. package/dist/fleet/fleet-bus.js +25 -33
  36. package/dist/fleet-client.js +1 -1
  37. package/dist/http/active-run-conflict.d.ts +2 -0
  38. package/dist/http/active-run-conflict.js +1 -0
  39. package/dist/http/routes/approvals-assistant.d.ts +2 -1
  40. package/dist/http/routes/approvals-assistant.js +10 -96
  41. package/dist/http/routes/capabilities.js +6 -5
  42. package/dist/http/routes/fleet.js +7 -2
  43. package/dist/http/routes/images.js +10 -4
  44. package/dist/http/routes/runs.js +42 -65
  45. package/dist/http/routes/session-sync.d.ts +1 -1
  46. package/dist/http/routes/session-sync.js +2 -1
  47. package/dist/http/routes/tasks.js +7 -7
  48. package/dist/http/routes/trace-usage.js +1 -1
  49. package/dist/http/routes/workflows.js +3 -1
  50. package/dist/http/server.d.ts +1 -6
  51. package/dist/http/server.js +15 -10
  52. package/dist/http/verify-rounds.d.ts +2 -2
  53. package/dist/http/wire-types.d.ts +1 -1
  54. package/dist/images/bake-validate.d.ts +0 -22
  55. package/dist/images/bake-validate.js +0 -4
  56. package/dist/index.d.ts +4 -4
  57. package/dist/index.js +2 -5
  58. package/dist/main.js +9 -10
  59. package/dist/memory-scope.d.ts +1 -2
  60. package/dist/memory-scope.js +8 -4
  61. package/dist/orchestration/workflow-completion-inbox.d.ts +6 -4
  62. package/dist/orchestration/workflow-completion-inbox.js +6 -4
  63. package/dist/plugins/breaker-state-sql.d.ts +2 -3
  64. package/dist/plugins/file-run-store.d.ts +1 -1
  65. package/dist/plugins/file-run-store.js +1 -1
  66. package/dist/plugins/image-index-sql.d.ts +0 -1
  67. package/dist/plugins/image-index-sql.js +0 -1
  68. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  69. package/dist/plugins/local-checkpoint-store.js +1 -1
  70. package/dist/plugins/memory-engine-pg.js +2 -1
  71. package/dist/plugins/memory-engine-tidb.js +1 -1
  72. package/dist/plugins/memory-engine-vector-util.d.ts +2 -3
  73. package/dist/plugins/memory-engine-vector-util.js +2 -3
  74. package/dist/plugins/memory-run-store.d.ts +2 -2
  75. package/dist/plugins/memory-run-store.js +1 -1
  76. package/dist/plugins/memory-session-policy-store.d.ts +1 -1
  77. package/dist/plugins/memory-session-policy-store.js +1 -1
  78. package/dist/plugins/memory-sync-store-pg.js +1 -1
  79. package/dist/plugins/pg-pool.js +8 -10
  80. package/dist/plugins/pg-rate-limiter.d.ts +1 -4
  81. package/dist/plugins/pg-rate-limiter.js +0 -1
  82. package/dist/plugins/roster-store-sql.d.ts +1 -3
  83. package/dist/plugins/roster-store-sql.js +3 -5
  84. package/dist/plugins/store-backend.d.ts +7 -17
  85. package/dist/plugins/store-backend.js +14 -30
  86. package/dist/plugins/tidb-pool.js +6 -30
  87. package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
  88. package/dist/plugins/workflow-journal-store-sql.js +1 -1
  89. package/dist/principal-jwt.js +2 -2
  90. package/dist/prompts-domain-validate.d.ts +4 -3
  91. package/dist/prompts-domain-validate.js +7 -8
  92. package/dist/router/route-orchestration.js +3 -3
  93. package/dist/run-local.js +13 -10
  94. package/dist/runs.js +8 -6
  95. package/dist/runtime-caps-resolver.d.ts +3 -2
  96. package/dist/runtime-caps-resolver.js +4 -4
  97. package/dist/runtime-governance.d.ts +4 -7
  98. package/dist/runtime-governance.js +12 -17
  99. package/dist/sealed-key.d.ts +1 -1
  100. package/dist/sealed-key.js +1 -1
  101. package/dist/security.d.ts +0 -5
  102. package/dist/security.js +8 -12
  103. package/dist/session-sync.d.ts +0 -2
  104. package/dist/session-sync.js +10 -17
  105. package/dist/spec-fields.js +6 -6
  106. package/dist/task-settings.d.ts +0 -27
  107. package/dist/task-settings.js +42 -11
  108. package/dist/tool-approval.d.ts +3 -3
  109. package/dist/tool-approval.js +10 -10
  110. package/dist/trace/artifacts.d.ts +11 -0
  111. package/dist/trace/artifacts.js +8 -19
  112. package/dist/trace/ledger-sink.js +7 -1
  113. package/dist/trace/project.d.ts +17 -0
  114. package/dist/trace/project.js +58 -5
  115. package/package.json +3 -3
  116. package/dist/capabilities/oa-tools.d.ts +0 -15
  117. package/dist/capabilities/oa-tools.js +0 -54
  118. package/dist/plugins/approval-store-sql.d.ts +0 -116
  119. package/dist/plugins/approval-store-sql.js +0 -150
  120. package/dist/plugins/file-workflow-journal-store.d.ts +0 -12
  121. package/dist/plugins/file-workflow-journal-store.js +0 -12
  122. package/dist/plugins/pg-approval-store.d.ts +0 -9
  123. package/dist/plugins/pg-approval-store.js +0 -9
  124. package/dist/plugins/pg-breaker-state.d.ts +0 -8
  125. package/dist/plugins/pg-breaker-state.js +0 -8
  126. package/dist/plugins/pg-checkpoint-store.d.ts +0 -10
  127. package/dist/plugins/pg-checkpoint-store.js +0 -10
  128. package/dist/plugins/pg-file-snapshot-store.d.ts +0 -8
  129. package/dist/plugins/pg-file-snapshot-store.js +0 -8
  130. package/dist/plugins/pg-image-bake.d.ts +0 -12
  131. package/dist/plugins/pg-image-bake.js +0 -11
  132. package/dist/plugins/pg-image-index.d.ts +0 -12
  133. package/dist/plugins/pg-image-index.js +0 -11
  134. package/dist/plugins/pg-outcome-ledger.d.ts +0 -12
  135. package/dist/plugins/pg-outcome-ledger.js +0 -11
  136. package/dist/plugins/pg-resume-anchor-store.d.ts +0 -7
  137. package/dist/plugins/pg-resume-anchor-store.js +0 -7
  138. package/dist/plugins/pg-run-store.d.ts +0 -9
  139. package/dist/plugins/pg-run-store.js +0 -9
  140. package/dist/plugins/pg-session-policy-store.d.ts +0 -7
  141. package/dist/plugins/pg-session-policy-store.js +0 -7
  142. package/dist/plugins/pg-session-store.d.ts +0 -12
  143. package/dist/plugins/pg-session-store.js +0 -12
  144. package/dist/plugins/pg-tool-result-store.d.ts +0 -9
  145. package/dist/plugins/pg-tool-result-store.js +0 -9
  146. package/dist/plugins/pg-workflow-journal-store.d.ts +0 -9
  147. package/dist/plugins/pg-workflow-journal-store.js +0 -9
  148. package/dist/plugins/pg-workflow-run-store.d.ts +0 -9
  149. package/dist/plugins/pg-workflow-run-store.js +0 -9
  150. package/dist/plugins/tidb-approval-store.d.ts +0 -8
  151. package/dist/plugins/tidb-approval-store.js +0 -8
  152. package/dist/plugins/tidb-breaker-state.d.ts +0 -7
  153. package/dist/plugins/tidb-breaker-state.js +0 -7
  154. package/dist/plugins/tidb-checkpoint-store.d.ts +0 -9
  155. package/dist/plugins/tidb-checkpoint-store.js +0 -9
  156. package/dist/plugins/tidb-file-snapshot-store.d.ts +0 -8
  157. package/dist/plugins/tidb-file-snapshot-store.js +0 -8
  158. package/dist/plugins/tidb-image-bake.d.ts +0 -12
  159. package/dist/plugins/tidb-image-bake.js +0 -11
  160. package/dist/plugins/tidb-image-index.d.ts +0 -12
  161. package/dist/plugins/tidb-image-index.js +0 -11
  162. package/dist/plugins/tidb-outcome-ledger.d.ts +0 -12
  163. package/dist/plugins/tidb-outcome-ledger.js +0 -12
  164. package/dist/plugins/tidb-resume-anchor-store.d.ts +0 -7
  165. package/dist/plugins/tidb-resume-anchor-store.js +0 -7
  166. package/dist/plugins/tidb-run-store.d.ts +0 -10
  167. package/dist/plugins/tidb-run-store.js +0 -9
  168. package/dist/plugins/tidb-session-policy-store.d.ts +0 -7
  169. package/dist/plugins/tidb-session-policy-store.js +0 -7
  170. package/dist/plugins/tidb-tool-result-store.d.ts +0 -8
  171. package/dist/plugins/tidb-tool-result-store.js +0 -10
  172. package/dist/plugins/tidb-workflow-journal-store.d.ts +0 -9
  173. package/dist/plugins/tidb-workflow-journal-store.js +0 -9
  174. package/dist/plugins/tidb-workflow-run-store.d.ts +0 -10
  175. package/dist/plugins/tidb-workflow-run-store.js +0 -10
  176. package/dist/plugins/workflow-journal-limits.d.ts +0 -12
  177. package/dist/plugins/workflow-journal-limits.js +0 -12
@@ -20,7 +20,7 @@
20
20
  import { realpathSync } from "node:fs";
21
21
  import { join } from "node:path";
22
22
  import { NodeExecutionEnv, QUESTION_AWAITS_RESUME, combinePolicies, createAllowDenyPolicy, createDurableQuestionPolicy, expandTiers, tightenTaskSpec } from "@sema-agent/core";
23
- import { createOaApprovalPolicy, createDurableAskPolicy } from "../approval.js";
23
+ import { createDurableAskPolicy } from "../approval.js";
24
24
  import { cappedCeiling } from "../budget.js";
25
25
  import { centerPromptProvider, centerIdentityAssembled } from "../capabilities/center-prompts.js";
26
26
  import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
@@ -39,7 +39,8 @@ import { resourceSuspendOptIn } from "../resource-suspend.js";
39
39
  import { isIsolatedExecEnv, routeServiceTask, supPostureOverrides } from "../router/route-orchestration.js";
40
40
  import { gateExecutionLane } from "../runtime-caps-resolver.js";
41
41
  import { applyRuntimeGovernance, stripDelegationTools } from "../runtime-governance.js";
42
- import { HttpError, memorySpecForRequest, encodeCheckpointScope } from "../security.js";
42
+ import { HttpError, encodeCheckpointScope } from "../security.js";
43
+ import { memorySpecForRequest } from "../memory-scope.js";
43
44
  import { mcpForScenario } from "../config-center/facade.js";
44
45
  import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptProfileFromBody, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolNameListFromBody } from "../spec-fields.js";
45
46
  import { cwdHonored, effectiveHostWorkspace, inProcessSingleUserLane, isValidCwd, parseAdditionalDirectories, satisfiedByProcessCwd, shellEnvMismatchCount } from "../task-cwd.js";
@@ -48,7 +49,7 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSet
48
49
  import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
49
50
  import { redactSecrets } from "../trace/redact.js";
50
51
  export function createResolveSpec(ctx) {
51
- const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, approvalEnabled, approvalStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, } = ctx;
52
+ const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, } = ctx;
52
53
  // Deployment-owned mapping. Tools/prompt/skills come from the selected scenario (assembled once,
53
54
  // bound per request); identity/session/policy stay server-side. Never taken from the request body.
54
55
  // design/158 A8:从 createHttpServer 实参里提出来(原地占该字面量 1,093 行中的 967 行)——
@@ -692,8 +693,9 @@ export function createResolveSpec(ctx) {
692
693
  // provider 才不会各看一版(TOCTOU:门判安全、pick 到已组装包=core 侧静默丢 rider 复活)。
693
694
  promptProvider: centerDecls ? centerPromptProvider(centerDecls, scenarioName) : cap.promptProvider,
694
695
  // F4: high-risk write approval gate. DURABLE (design/45, opt-in): the gated `ask` SUSPENDS the task
695
- // (durable checkpoint, resumable on any replica). Else the legacy POLL gate: the call waits on a
696
- // durable pending row any instance can decide via /v1/approvals, releasing at the task deadline.
696
+ // (durable checkpoint, resumable on any replica). [2354]/[2356] A-5(5.0.0): the legacy POLL gate
697
+ // (createOaApprovalPolicy over ApprovalStore pending rows) is retired durable is the ONLY gate; an
698
+ // operator with approval intent but no checkpointStore falls to `undefined` (core's UNGATED warning).
697
699
  // Durable ask (TC-5.4, core 1.95): in durable mode an AskUserQuestion call suspends like an F4 gate —
698
700
  // the question policy adjudicates it `ask`, the operator answers out-of-band, and the resume carries
699
701
  // the QuestionAnswer (server.ts `body.answer` → onQuestion closure). Suspend side mounts the tool with
@@ -711,36 +713,20 @@ export function createResolveSpec(ctx) {
711
713
  }
712
714
  : {}),
713
715
  }))
714
- : approvalEnabled && approvalStore
715
- ? createOaApprovalPolicy({
716
- store: approvalStore,
717
- requireApproval: config.approvalRequire,
718
- deny: config.approvalDeny,
719
- pollMs: config.approvalPollMs,
720
- context: () => ({ sessionId: auth?.sessionId ?? null, owner: auth?.principal ?? null }),
721
- neverAuto: config.approvalNeverAuto, // workflow audit 2026-07-13: the ordering lock must hold on the OA leg too
722
- // Same probe on the legacy poll gate (sessionId threaded via ctx here).
723
- ...(approvalExemptionStore
724
- ? {
725
- exempt: (sessionId, toolName) => approvalExemptionStore.has(sessionId, toolName),
726
- onExempted: (sessionId, toolName) => logger.info("approval_exempted", { sessionId, toolName }),
727
- }
728
- : {}),
729
- })
730
- : singleUserAutoAcceptBaseline
731
- // 🔴 (live finding: single-user host-lane Bash ran UNGATED, no adjudication): a
732
- // single-user turnkey worker with NO expressed gate intent previously left `toolPolicy` UNDEFINED →
733
- // core's write-capable hand tools mount UNADJUDICATED (core error-logs it every task; host lane has NO
734
- // sandbox to fall back on). CC's trust model is auto-accept, but the GATE MECHANISM must be PRESENT
735
- // (core 原则: "机制留、默认可更宽 auto-accept"). So wire an adjudicated auto-accept BASELINE
736
- // (`createAllowDenyPolicy({})` = a present, effect-aware policy that allows all): satisfies core's
737
- // `hasEffectAwareGate`, restores observability + a hook/tighten point, and the operator STILL tightens
738
- // irreversible ops via `AUTONOMY` / `commandPolicy` (layered TIGHTEN-ONLY by applyRuntimeGovernance).
739
- // Interactive approval routed to the shell HITL = follow-on. 🔴 `singleUserAutoAcceptBaseline` requires
740
- // ZERO gate intent — a single-user op who SET approval flags but wired no store falls to `undefined`
741
- // (core warns = real misconfig, not masked); multi-tenant likewise stays `undefined` (approval required).
742
- ? createAllowDenyPolicy({})
743
- : undefined,
716
+ : singleUserAutoAcceptBaseline
717
+ // 🔴 (live finding: single-user host-lane Bash ran UNGATED, no adjudication): a
718
+ // single-user turnkey worker with NO expressed gate intent previously left `toolPolicy` UNDEFINED →
719
+ // core's write-capable hand tools mount UNADJUDICATED (core error-logs it every task; host lane has NO
720
+ // sandbox to fall back on). CC's trust model is auto-accept, but the GATE MECHANISM must be PRESENT
721
+ // (core 原则: "机制留、默认可更宽 auto-accept"). So wire an adjudicated auto-accept BASELINE
722
+ // (`createAllowDenyPolicy({})` = a present, effect-aware policy that allows all): satisfies core's
723
+ // `hasEffectAwareGate`, restores observability + a hook/tighten point, and the operator STILL tightens
724
+ // irreversible ops via `AUTONOMY` / `commandPolicy` (layered TIGHTEN-ONLY by applyRuntimeGovernance).
725
+ // Interactive approval routed to the shell HITL = follow-on. 🔴 `singleUserAutoAcceptBaseline` requires
726
+ // ZERO gate intent — a single-user op who SET approval flags but wired no store falls to `undefined`
727
+ // (core warns = real misconfig, not masked); multi-tenant likewise stays `undefined` (approval required).
728
+ ? createAllowDenyPolicy({})
729
+ : undefined,
744
730
  // Durable suspend needs both the store (here, per-task) and the opt-in scope key (multi-tenant =
745
731
  // principal; "_" when auth is off). core suspends on a policy `ask` only when these are present.
746
732
  ...(durableEnabled
@@ -27,7 +27,7 @@ import { LocalTaskAttachmentStore } from "../plugins/local-task-attachment-store
27
27
  import { PgTaskAttachmentStore, TiDBTaskAttachmentStore, ensurePgTaskAttachmentSchema, ensureTiDBTaskAttachmentSchema } from "../plugins/task-attachment-store.js";
28
28
  import { createSessionStore } from "../plugins/session-store.js";
29
29
  import { assertCloudSnapshotBlobPosture, openStoreBackendWithFallback } from "../plugins/store-backend.js";
30
- import { memoryEngineBackendFor, memoryEngineRemoteLanePosture } from "../security.js";
30
+ import { memoryEngineBackendFor, memoryEngineRemoteLanePosture } from "../memory-scope.js";
31
31
  export async function openStores(ctx) {
32
32
  const { config, logger, metrics, localRoot } = ctx;
33
33
  // One shared SQL store backend for L1 + L2 (TiDB/MySQL or PostgreSQL per DB_BACKEND). Owned here.
package/dist/brain.js CHANGED
@@ -183,7 +183,9 @@ export function createBrain(config, deps = {}) {
183
183
  fetchImpl,
184
184
  ...timeouts,
185
185
  });
186
- brain = createDegradingBrain({ primary: brain, fallback, fallbackModel, downgradeOn: config.degrade.downgradeOn });
186
+ // core 5.1.0([2390]②):chain-of-one 链形;downgradeOn 缺席时 core 默认含 server_error(默认门
187
+ // 变宽)——MODEL_DEGRADE_ON 是运维收窄旋钮,词表验门在 config.ts parseDegradeOn。
188
+ brain = createDegradingBrain({ primary: brain, fallbacks: [{ brain: fallback, model: fallbackModel }], downgradeOn: config.degrade.downgradeOn });
187
189
  }
188
190
  }
189
191
  return brain;
@@ -22,9 +22,8 @@
22
22
  import { type PromptProvider, type PublishedPromptArtifactEnvelope } from "@sema-agent/core";
23
23
  import { type EffectiveCenterPrompts, type PromptsDomainFaces } from "../prompts-domain-validate.js";
24
24
  /** 判形半场(validateCenterPrompts/validatePromptsDomain/CORE_ENGINE_VERSION 及两 interface)已下沉到
25
- * 中立叶子 `src/prompts-domain-validate.ts`(design/158 S6,lens2 §G:消 config-lkg → capabilities 的
26
- * 唯一分层反向边)。此处 re-export 保兼容——既有消费者(main/run-local/测试)零改动。 */
27
- export { validateCenterPrompts, validatePromptsDomain, CORE_ENGINE_VERSION, type EffectiveCenterPrompts, type PromptsDomainFaces } from "../prompts-domain-validate.js";
25
+ * 中立叶子 `src/prompts-domain-validate.ts`(design/158 S6);消费者直引真源([2354] 兼容面全清,
26
+ * 此处兼容 re-export 已删) */
28
27
  /** [985]b / [1285] — §9.4 by-digest 补拉组合店:local miss ⇒ fetchRaw(digest) 从 center 拉 envelope ⇒
29
28
  * core verifyPromptArtifact 全管线 + **回验「拿到的=要的」digest 逐位**(center 答非所问=丢弃)⇒
30
29
  * best-effort 回写 inner(下次直命中)。任何失败=undefined(诚实缺席,fail-loud 归 core 的
@@ -23,9 +23,8 @@ import { verifyPromptArtifact } from "@sema-agent/core";
23
23
  import { CORE_ENGINE_VERSION } from "../prompts-domain-validate.js";
24
24
  import { SHA256_HEX_RE } from "../digest-form.js";
25
25
  /** 判形半场(validateCenterPrompts/validatePromptsDomain/CORE_ENGINE_VERSION 及两 interface)已下沉到
26
- * 中立叶子 `src/prompts-domain-validate.ts`(design/158 S6,lens2 §G:消 config-lkg → capabilities 的
27
- * 唯一分层反向边)。此处 re-export 保兼容——既有消费者(main/run-local/测试)零改动。 */
28
- export { validateCenterPrompts, validatePromptsDomain, CORE_ENGINE_VERSION } from "../prompts-domain-validate.js";
26
+ * 中立叶子 `src/prompts-domain-validate.ts`(design/158 S6);消费者直引真源([2354] 兼容面全清,
27
+ * 此处兼容 re-export 已删) */
29
28
  /** [985]b / [1285] — §9.4 by-digest 补拉组合店:local miss ⇒ fetchRaw(digest) 从 center 拉 envelope ⇒
30
29
  * core verifyPromptArtifact 全管线 + **回验「拿到的=要的」digest 逐位**(center 答非所问=丢弃)⇒
31
30
  * best-effort 回写 inner(下次直命中)。任何失败=undefined(诚实缺席,fail-loud 归 core 的
@@ -35,7 +35,6 @@
35
35
  */
36
36
  import type { ExecutionEnv, ExecutionEnvFactory } from "@sema-agent/core";
37
37
  import type { IssuedFileLink, PreparedDirectUpload } from "../plugins/send-user-file.js";
38
- import { shellQuote as shq } from "../plugins/remote-shell.js";
39
38
  /** Leak backstop (codex review HIGH): core skips destroy on suspend/review paths and a crash may skip it
40
39
  * entirely — the suspendVM wrap covers the common case, this bound covers the rest. Far above any real
41
40
  * per-replica concurrency. 修6: at the cap the registry REFUSES the new registration instead of FIFO-evicting
@@ -92,7 +91,6 @@ export declare class TaskEnvRegistry {
92
91
  * adb/local-docker remain a follow-on).
93
92
  */
94
93
  export declare function sandboxSendLaneEnabled(provider: string | undefined, requirePrincipal: boolean): boolean;
95
- export { shq };
96
94
  /** Strip the capability URL (and any SigV4 signature) from text before it reaches an error/log — including
97
95
  * the percent-encoded forms an adapter may echo (the k8s exec transport carries the command in a request
98
96
  * URI, so an error can quote the URL re-encoded; codex review MED). */
@@ -139,11 +139,8 @@ export function sandboxSendLaneEnabled(provider, requirePrincipal) {
139
139
  return requirePrincipal !== true;
140
140
  return false;
141
141
  }
142
- // design/158 R10: the quoting logic itself now lives solely in remote-shell.ts's shellQuote (this module's
143
- // former standalone copy was byte-identical; its JSDoc note was folded into that owner). `shq` stays a
144
- // re-exported alias — not a plain call-through rename — because two test files import `shq` by name from
145
- // this module.
146
- export { shq };
142
+ // [2354] clay 令兼容面全清:R10 曾留的 `export { shq }` 兼容转口已删——quoting 唯一真源=
143
+ // remote-shell.ts shellQuote(原先仅有的两个消费者是测试文件,已改从真源导入)
147
144
  /** Strip the capability URL (and any SigV4 signature) from text before it reaches an error/log — including
148
145
  * the percent-encoded forms an adapter may echo (the k8s exec transport carries the command in a request
149
146
  * URI, so an error can quote the URL re-encoded; codex review MED). */
@@ -45,17 +45,6 @@ export interface ScenarioDeps {
45
45
  webFetchSummarize?: NonNullable<NonNullable<WebFetchConfig["summarize"]>>;
46
46
  metrics?: Metrics;
47
47
  logger?: Logger;
48
- /** OA backend coordinates for the `oa` scenario (read /api/v1, write /api/assets). */
49
- oaApiBaseUrl?: string;
50
- oaServiceToken?: string;
51
- /** 可选:`oa` 场景注入"开 Gitea issue"工具(OA AI 请求#5);repo/token/labels 固定,模型只填 title/body。 */
52
- oaIssue?: {
53
- baseUrl: string;
54
- owner: string;
55
- repo: string;
56
- token: string;
57
- defaultLabels?: number[];
58
- };
59
48
  /**
60
49
  * When true, the `default` scenario carries the Sema product identity (see {@link semaIdentityProvider}).
61
50
  * Set by the local/TOC deployment (CONFIG_PROVIDER=local / run-local) so the local engine answers "who
@@ -159,8 +148,8 @@ export interface ScenarioDetail {
159
148
  promptSummary: string;
160
149
  enabled: boolean;
161
150
  }
162
- /** 内建六场景详情。default/autonomous/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan/oa
163
- * fail-loud 语义(缺 GIT_API/OA 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
151
+ /** 内建五场景详情。default/autonomous/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
152
+ * fail-loud 语义(缺 GIT_API 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
164
153
  * 测试喂 fake deps 真调工厂对账工具名)。enabled 对内建恒 true(约定②)。 */
165
154
  export declare function builtinScenarioDetails(scenarios: Record<string, Scenario>, deps: Pick<ScenarioDeps, "brandIdentity">): Record<string, ScenarioDetail>;
166
155
  /** center 条目详情(有效性判定与 centerScenarios 完全同款:无效条目既不进 overlay 也不进详情——
@@ -1,11 +1,10 @@
1
- import { CODE_AGENT_PROMPT, CODE_SYSTEM_PROMPT, DEFAULT_SYSTEM_PROMPT, assembleCodeTools, createGiteaIssueTool, defaultTaskRegistry } from "@sema-agent/core";
1
+ import { CODE_AGENT_PROMPT, CODE_SYSTEM_PROMPT, DEFAULT_SYSTEM_PROMPT, assembleCodeTools, defaultTaskRegistry } from "@sema-agent/core";
2
2
  import { HttpError } from "../security.js";
3
3
  import { nowTool } from "./builtin-tools.js";
4
4
  import { GiteaClient, parseRepo, repoToolsFor } from "./repo-tools.js";
5
5
  import { createCouncilTool, clampRounds } from "./code-review-council.js";
6
6
  import { stablePrompt } from "./prompt.js";
7
7
  import { createTeamTool, teamCoordinatorPrompt, getTeam, teamNames } from "./team.js";
8
- import { oaTools, oaPrompt } from "./oa-tools.js";
9
8
  import { createWebSearchBackend, webSearchConfigFromSettings } from "../plugins/web-search.js";
10
9
  import { skillsForScenario } from "./skills.js";
11
10
  /**
@@ -205,23 +204,6 @@ export function buildScenarios(deps) {
205
204
  skills: skillsForScenario(deps.skills, "scan"),
206
205
  };
207
206
  },
208
- oa: (req, principal) => {
209
- if (!deps.oaApiBaseUrl || !deps.oaServiceToken)
210
- throw new HttpError(501, "oa scenario needs OA_API_BASEURL + OA_SERVICE_TOKEN configured");
211
- // OA enforces the END USER's RBAC from the forwarded principal — without one, every OA call would
212
- // run unattributed. Refuse rather than silently send a null principal.
213
- if (!principal)
214
- throw new HttpError(401, "oa scenario requires an authenticated principal (set REQUIRE_PRINCIPAL + x-agent-principal)");
215
- // 写权限的"开 issue"工具仅当配置了 OA_ISSUE_* 时注入;token/repo 固定,模型只填 title/body。
216
- const issueTool = deps.oaIssue
217
- ? [createGiteaIssueTool({ ...deps.oaIssue, name: "open_gitea_issue", description: "把用户的 bug/需求开成 Gitea issue 报给老板。只填 title(简洁)/body(现象、影响、复现/建议)。仓库与标签由部署固定。" })]
218
- : [];
219
- return {
220
- tools: [...oaTools({ baseUrl: deps.oaApiBaseUrl, token: deps.oaServiceToken, principal }), ...issueTool],
221
- skills: skillsForScenario(deps.skills, "oa"),
222
- promptProvider: oaPrompt,
223
- };
224
- },
225
207
  // Multi-agent team: pick a template, the consumer (OA) injects business context, we run it.
226
208
  // Templates resolve from the live registry (`getTeam`) = BUILTIN_TEAMS overridden/extended by the
227
209
  // sema registry at runtime (applyEffective → registerTeams), so a team is config-driven; built-ins
@@ -246,8 +228,8 @@ export function buildScenarios(deps) {
246
228
  export function selectScenario(scenarios, name) {
247
229
  return (name && scenarios[name]) || scenarios.default;
248
230
  }
249
- /** 内建六场景详情。default/autonomous/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan/oa
250
- * fail-loud 语义(缺 GIT_API/OA 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
231
+ /** 内建五场景详情。default/autonomous/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
232
+ * fail-loud 语义(缺 GIT_API 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
251
233
  * 测试喂 fake deps 真调工厂对账工具名)。enabled 对内建恒 true(约定②)。 */
252
234
  export function builtinScenarioDetails(scenarios, deps) {
253
235
  const probe = (name, req, principal, fallback) => {
@@ -270,7 +252,6 @@ export function builtinScenarioDetails(scenarios, deps) {
270
252
  : "core 编码完全体提示词(= code 场景;finalVerification 固定开)"),
271
253
  "code-review": mk("code-review", "代码评审(clone-free 只读仓库工具;council=true 升多镜头评审团)", "repo-readonly", probe("code-review", { repo: "probe/probe" }, undefined, REPO_TOOLS), "评审员/评审团协调提示词(镜头分工+产出契约)"),
272
254
  scan: mk("scan", "中性只读仓库扫描(输出契约由 objective+skills 决定)", "repo-readonly", probe("scan", { repo: "probe/probe" }, undefined, REPO_TOOLS), "无场景附加提示词(objective+center skills 拥有输出契约)"),
273
- oa: mk("oa", "OA 资产运营助手(以最终用户 principal 走 OA API,RBAC+审计在 OA 侧)", "oa-api", probe("oa", {}, "probe:meta", ["oa_schema", "oa_alerts", "oa_list", "oa_get", "oa_lookup", "oa_create", "oa_update", "oa_transition"]), "OA 运营助手提示词(先 schema 后操作、写走受控流转)"),
274
255
  team: mk("team", "多智能体团队圆桌(模板成员+synthesizer,模板可由 registry 配置)", "team", probe("team", {}, undefined, ["run_team"]), "团队协调者提示词(圆桌纪律+合成流程)"),
275
256
  };
276
257
  }
@@ -24,7 +24,7 @@
24
24
  */
25
25
  import { AsyncLocalStorage } from "node:async_hooks";
26
26
  import { Type } from "typebox";
27
- import { builtinAgentDefinitions, canonicalToolName } from "@sema-agent/core";
27
+ import { builtinAgentDefinitions } from "@sema-agent/core";
28
28
  /** Per-run ALS carrier (QuestionCoordinator pattern): run legs wrap their body via runWithContext; the tool
29
29
  * recovers the ambient emit at execute time. No pending state, no respond face — emission is one-way. */
30
30
  export class SendUserFileEmitter {
@@ -155,7 +155,7 @@ const READONLY_BUILTIN_AGENT_TYPES = new Set(builtinAgentDefinitions().map((d) =
155
155
  * definition's name (core subagent.js); membership in core's builtin catalog ⇒ inject nothing for that spawn.
156
156
  */
157
157
  export function subagentSendUserFileExtraTools(deps) {
158
- const canonical = canonicalToolName(SEND_USER_FILE_TOOL_NAME);
158
+ const canonical = SEND_USER_FILE_TOOL_NAME; // RAW(5.0.0):本名恒 canonical,折叠面退役
159
159
  return (ctx) => {
160
160
  // F2: read-only builtin child (Explore/Plan) — never inject.
161
161
  if (ctx.agentType !== undefined && READONLY_BUILTIN_AGENT_TYPES.has(ctx.agentType))
@@ -163,7 +163,7 @@ export function subagentSendUserFileExtraTools(deps) {
163
163
  // F1: deployment policy gates SendUserFile (deny/ask) — withhold from ALL children (fail-closed stopgap
164
164
  // until core propagates the parent toolPolicy into child specs). Canonical-space match (S8 discipline).
165
165
  const gate = deps.approvalGate();
166
- const gated = [...(gate.deny ?? []), ...(gate.require ?? []), ...(gate.neverAuto ?? [])].some((t) => canonicalToolName(t) === canonical);
166
+ const gated = [...(gate.deny ?? []), ...(gate.require ?? []), ...(gate.neverAuto ?? [])].some((t) => t === canonical);
167
167
  return gated ? [] : [deps.spec];
168
168
  };
169
169
  }
@@ -8,7 +8,7 @@
8
8
  import { isThinkingLevel } from "@sema-agent/core";
9
9
  import { resolveActiveTiers, SEALED_BOX_ALG } from "@sema-agent/registry-core";
10
10
  import { sealedKeyPoison } from "../sealed-key.js";
11
- import { applyAutoCompactWindow, parseAutonomy } from "../config.js";
11
+ import { applyAutoCompactWindow, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
12
12
  import { validateCommandRules } from "../runtime-governance.js";
13
13
  import { projectCollabToWorkflows, registerCollabWorkflows } from "../capabilities/collab-workflows.js";
14
14
  import { registerTeams } from "../capabilities/team.js";
@@ -292,8 +292,8 @@ function stageEffective(config, eff, logger, opts) {
292
292
  // → undefined = INERT). Values are catalog NAME strings (open, not enum'd); dangling bindings were already
293
293
  // culled center-side. mutateInPlace keeps the reference RunnerDeps.tiers holds — the expansion itself happens
294
294
  // at Runner CONSTRUCTION (core 1.265 expandTiers), so a refresh-time change is restart-to-apply like models.
295
- // NOTE: the wire face (this module's EffectiveConfig) carries the JSON body verbatim, so tierGroups ride
296
- // eff.models even though CenterModel doesn't type themresolveActiveTiers reads them structurally.
295
+ // [2373]B-6:EffectiveConfig.models 已声明 tierGroups/activeTierGroup(types.ts)——旧「CenterModel
296
+ // doesn't type them」注随宽铸一起退役;resolveActiveTiers registry-core 形,窄化一步即可。
297
297
  const activeTiers = resolveActiveTiers(eff.models) ?? {};
298
298
  // [865]①(生产实锤:面板上选了 A 模型,实际跑的是目录里的另一个)显式默认解析——enabled[0] 会被任何改变 pool 顺序的写
299
299
  // 静默翻转,default 必须消费 wire 里已有的显式意图。解析单源 = resolveDefaultModelName(dry-run 的
@@ -316,7 +316,7 @@ function stageEffective(config, eff, logger, opts) {
316
316
  // governance 切新位:治理三件优先读 governance 域(真值),runtime 旧位(双写镜像)fallback——
317
317
  // 双写期两处逐键相等语义不变;center 撤双写后 governance 即唯一来源。限额残余(rateLimit/cost 五件)仍在 runtime。
318
318
  const gatesView = eff.governance ? { ...eff.runtime, ...eff.governance } : eff.runtime;
319
- const gates = opts.teamsOnly ? undefined : stageRuntimeGates(gatesView);
319
+ const gates = opts.teamsOnly ? undefined : stageRuntimeGates(gatesView, logger);
320
320
  // Runtime governance "second baton" (center §10): autonomy + commandPolicy are per-request HOT (read live in
321
321
  // resolveSpec), NOT baked into boot middleware → apply on BOTH boot and refresh (outside the teamsOnly guard) so
322
322
  // they hot-reload. No restart-to-apply signal (they take effect on the next task without a restart).
@@ -360,13 +360,13 @@ function stageEffective(config, eff, logger, opts) {
360
360
  // 142-S4 projects 域(registry-core 0.10.0):center 项目登记簿 → config.projects(键=
361
361
  // projectId)。消费是 per-request 查表(memoryScope 派生 + defaultScopes 种子,security.ts/main.ts),
362
362
  // 所以 boot+refresh 双腿都 mutateInPlace 热应用(整表替换,非粘——center 停发即清空);env lane 恒空。
363
- // 宽读:域包裹 `{projects:{...}}`(registry-core ProjectsConfig 真形状)与裸 Record 都认(projectId=UUID,
364
- // 永不撞 "projects" 字面键);防御:非 object 条目跳过,字段逐个验型(wire verbatim,不信任形状)。
363
+ // [2373]B-8:裸 Record 臂删——registry-core 0.13.0 ProjectsConfig 只有包裹形 `{projects:{...}}`
364
+ // (types.d.ts:3599,且 :12231 钉进 EffectiveConfig),裸形无 producer( Q15 declared 双形容忍同族)。
365
+ // 防御保留:非 object 条目跳过,字段逐个验型(wire verbatim,不信任形状)。
365
366
  const projDomain = eff.projects;
366
- const rawProjects = (projDomain && typeof projDomain === "object" && !Array.isArray(projDomain)
367
- ? "projects" in projDomain && typeof projDomain.projects === "object"
368
- ? (projDomain.projects ?? {})
369
- : projDomain
367
+ const rawProjects = (projDomain && typeof projDomain === "object" && !Array.isArray(projDomain) &&
368
+ "projects" in projDomain && typeof projDomain.projects === "object"
369
+ ? (projDomain.projects ?? {})
370
370
  : {}) ?? {};
371
371
  const projects = {};
372
372
  for (const [pid, reg] of Object.entries(rawProjects)) {
@@ -422,8 +422,38 @@ export function runtimeGatePresent(rt, key) {
422
422
  return typeof v === "number" && v > 0;
423
423
  return typeof v === "number";
424
424
  }
425
- /** [2283] stage 半场:六闸的赋值清单(纯计算,零变异)。commit 段照单赋值,通知载荷 post-commit 发。 */
426
- function stageRuntimeGates(rt) {
425
+ /**
426
+ * A1:中心下发的 `approvalRequire` 名单**按 env 腿同款判据**验过再落配。此前只验 `Array.isArray` 就逐字
427
+ * 落配——含退役工具名(`bash`/`Task`/`KillShell`)或 pre-prefix/不完整 MCP 名(`figma__x`/`mcp__figma`)时
428
+ * 该名单项永不匹配任何现役工具,审批门静默失守,而同一拼法从 env 腿(`APPROVAL_REQUIRE`)进来是 boot 拒启
429
+ * ——两腿判据不对称,写配置的人无从知道自己踩的是哪一条。判据单点=config.ts `findUnmatchableToolNames`。
430
+ *
431
+ * 处置形照本文件既有的域级拒绝先例(`commandPolicy` 非法 ⇒ fail-loud + 保留前值):常驻进程不能因为一次
432
+ * 坏发布 crash,所以**只拒这一个域**(其余五闸照常应用)、保留前值、留一条响亮 warn。「半应用一个被静默
433
+ * 削弱的门」才是要堵的那种失败。
434
+ *
435
+ * 顺带 C1-2:数组项 trim + 去空串,与 env csv 归一(`csv()` 的 `.map(trim).filter(Boolean)`)一致——
436
+ * 否则 `" Bash "` 从中心腿进来会是个永不匹配的名字,正是本条要消灭的那类静默失效。
437
+ */
438
+ function stageApprovalRequire(raw, logger) {
439
+ // wire verbatim ⇒ 不信任元素形状(整表是 center 发的):非串项与空串一并落地板,与 env csv 归一同形。
440
+ const names = (Array.isArray(raw) ? raw : [])
441
+ .map((n) => (typeof n === "string" ? n.trim() : n))
442
+ .filter((n) => typeof n === "string" && n.length > 0);
443
+ const bad = findUnmatchableToolNames(names);
444
+ if (bad.length > 0) {
445
+ logger?.warn("sema_registry_approval_require_invalid", {
446
+ detail: formatUnmatchableToolNames("published runtime.approvalRequire", bad),
447
+ names: bad.map((b) => b.name),
448
+ kept: "the prior approvalRequire is unchanged — the published list would have silently matched nothing",
449
+ });
450
+ return {};
451
+ }
452
+ return { value: names };
453
+ }
454
+ /** [2283] stage 半场:六闸的赋值清单(纯计算,零变异)。commit 段照单赋值,通知载荷 post-commit 发。
455
+ * A1 的名单校验是**内容判定**(描述 eff 的真伪,与是否 commit 无关)⇒ 与 commandPolicy 同,stage 期发 warn。 */
456
+ function stageRuntimeGates(rt, logger) {
427
457
  if (!rt)
428
458
  return { assignments: [] };
429
459
  const assignments = [];
@@ -431,6 +461,14 @@ function stageRuntimeGates(rt) {
431
461
  for (const key of RUNTIME_GATE_KEYS) {
432
462
  if (!runtimeGatePresent(rt, key))
433
463
  continue; // undefined / sentinel → keep env
464
+ if (key === "approvalRequire") {
465
+ const staged = stageApprovalRequire(rt[key], logger);
466
+ if (staged.value === undefined)
467
+ continue; // 判据未过 ⇒ 只拒该域,保留前值(其余闸不受牵连)
468
+ assignments.push([key, staged.value]);
469
+ applied[key] = staged.value;
470
+ continue;
471
+ }
434
472
  assignments.push([key, rt[key]]); // present (incl. explicit 0/[]) → override
435
473
  applied[key] = rt[key];
436
474
  }
@@ -439,7 +477,7 @@ function stageRuntimeGates(rt) {
439
477
  export function applyRuntimeGates(config, rt, logger) {
440
478
  // 独立调用面的兼容壳(测试/外部):stage → 就地赋值 → 通知。applyEffective 不走这里——它把
441
479
  // assignments 并进自己的 commit 段以保住整世代原子性([2283]②)。
442
- const s = stageRuntimeGates(rt);
480
+ const s = stageRuntimeGates(rt, logger);
443
481
  for (const [k, v] of s.assignments)
444
482
  config[k] = v;
445
483
  if (s.info)
@@ -126,10 +126,17 @@ export interface CenterMcpServer {
126
126
  export interface EffectiveConfig {
127
127
  version: number;
128
128
  updatedAt: string;
129
+ /** [2373]B-6:default/tierGroups/activeTierGroup 收进类型面(registry-core 0.13.0 已声明,
130
+ * types.d.ts:641/679/724)——此前 wire 面靠「JSON verbatim + 消费方结构读」逃逸,投影/消费两侧
131
+ * 各自 as 宽铸。tierGroups 上游是 ZodDefault(合规 center 恒发),但本类型描述的是**到货 JSON**
132
+ * (含非合规/旧存量 center),故仍标 optional——消费方 resolveActiveTiers 自身容缺。 */
129
133
  models: {
130
134
  models: CenterModel[];
131
135
  roles: Record<string, CenterRoleTarget>;
132
136
  atModelAllowlist?: string[];
137
+ default?: string;
138
+ tierGroups?: unknown;
139
+ activeTierGroup?: string;
133
140
  };
134
141
  /** OPTIONAL since registry-core 0.7 deleted the teams domain: a 0.7+ center /effective payload
135
142
  * has NO teams key (collab is its successor). Absent/empty ⇒ registerTeams resets to BUILTIN_TEAMS (the
@@ -180,12 +187,12 @@ export interface EffectiveConfig {
180
187
  };
181
188
  /** 142-S4(registry-core 0.10.0 第 15 域):center 项目登记簿。wire 面 verbatim JSON —
182
189
  * registry-core `ProjectsConfig` 的形状是 `{projects: Record<projectId, ProjectRegistration>}`(skills/mcp
183
- * 同款域包裹),但本 interface 宽读两种形态(包裹/裸 Record——projectId UUID,永远不会撞 "projects"
184
- * 字面键,判包裹无歧义)。消费=applyEffective → config.projects(displayName/gitRemotes/defaultScopes
190
+ * 同款域包裹)。[2373]B-8:裸 Record 联合臂随 apply-effective 的宽读一起删(0.13.0 只有包裹形,
191
+ * 裸形无 producer)。消费=applyEffective → config.projects(displayName/gitRemotes/defaultScopes
185
192
  * 三个 runtime 字段;审计字段不进 ServiceConfig)。 */
186
193
  projects?: {
187
194
  projects?: Record<string, unknown>;
188
- } | Record<string, unknown>;
195
+ };
189
196
  /** registry-core 0.6:治理三件(autonomy/commandPolicy/approvalRequire)拆新 governance 域=真值;
190
197
  * 过渡期 effective 在 runtime 位双写镜像。读取优先 governance 新位、runtime 旧位 fallback(顺序契约:
191
198
  * 消费端先切换、确认后 center 才撤双写,治理闸零 fail-open 窗口)。 */
@@ -53,7 +53,7 @@ export interface ConfigProvider {
53
53
  /** The config-provider knobs read off ServiceConfig (a structural subset — avoids importing the whole
54
54
  * ServiceConfig type and keeps this module testable with a plain object). */
55
55
  export interface ConfigProviderInput {
56
- /** Set when SEMA_REGISTRY_URL (legacy CONFIG_CENTER_URL) is configured (config.ts `configCenter`). `dryRun` is carried through so a
56
+ /** Set when SEMA_REGISTRY_URL (legacy CONFIG_CENTER_URL now boot-rejects, no dual-read) is configured (config.ts `configCenter`). `dryRun` is carried through so a
57
57
  * remote↔local swap via this seam does NOT drop the dry-run posture main.ts branches on (config.ts
58
58
  * `configCenter.dryRun`); the provider itself does not consume it (the caller still branches on it). */
59
59
  configCenter?: {
@@ -210,16 +210,9 @@ export function mapToServiceEffective(eff, version) {
210
210
  ...(m.contextWindow !== undefined ? { contextWindow: m.contextWindow } : {}),
211
211
  ...(m.maxTokens !== undefined ? { maxTokens: m.maxTokens } : {}),
212
212
  // codex F5 ([818]① 1M dual-window): an EXPLICIT `autoCompactTokens` must survive this CLOSED
213
- // projection the remote lane ships the roster JSON verbatim and toModel reads the field
214
- // structurally, but the local lane funnels through this mapper, which silently ATE the explicit
215
- // value (it then fell back to the contextWindow derivation). Structural read (registry-core's
216
- // ModelEntry doesn't declare it — same passthrough posture as tierGroups below). NOTE (honest
217
- // gap): registry-core's zod ModelEntry is "strip"-mode, so a value in a config.d/models.json read
218
- // through FileConfigStore is ALSO dropped upstream of this mapper — that half needs a registry-core
219
- // schema addition; this fix removes OUR projection's drop for any caller of this seam.
220
- ...(typeof m.autoCompactTokens === "number"
221
- ? { autoCompactTokens: m.autoCompactTokens }
222
- : {}),
213
+ // projection. [2373]B-6d:registry-core 0.13.0 已把它收进 ModelEntry schema(types.d.ts:157/212)
214
+ // ——旧注的「strip-mode 会在上游丢值」的 honest gap 已随那次 schema addition 关闭,结构读改类型直读。
215
+ ...(typeof m.autoCompactTokens === "number" ? { autoCompactTokens: m.autoCompactTokens } : {}),
223
216
  ...(m.cost !== undefined ? { cost: m.cost } : {}),
224
217
  ...(m.extraBody !== undefined ? { extraBody: m.extraBody } : {}),
225
218
  // registry-core 0.10.13 promptGuidance ([998]⑤c): the CLOSED projection must pass it through or
@@ -232,10 +225,12 @@ export function mapToServiceEffective(eff, version) {
232
225
  // 本 mapper 是 CLOSED 投影——不直通则本地 lane 静默丢键(autoCompactTokens 同类病),applyEffective 的
233
226
  // resolveDefaultModelName 第一优先级在本地 lane 永远点不亮。写面悬空 ref 由 registry-core superRefine
234
227
  // fail-loud;存量/未经 schema 的路径仍由消费方 warn 降级(config-center/facade.ts resolveDefaultModelName)。
235
- ...(eff.models.default !== undefined ? { default: eff.models.default } : {}),
228
+ ...(eff.models.default !== undefined ? { default: eff.models.default } : {}), // [2373]B-6c:0.13.0 类型直读(types.d.ts:641)
236
229
  ...(eff.models.atModelAllowlist !== undefined ? { atModelAllowlist: eff.models.atModelAllowlist } : {}),
237
230
  // registry-core 0.9.0 档位组: verbatim passthrough — applyEffective's resolveActiveTiers reads
238
231
  // these structurally off eff.models (the remote lane ships them JSON-verbatim; the local lane must too).
232
+ // [2373]B-6b:0.13.0 类型直读(EffectiveConfig.models 面已声明四键,宽铸删)。本地 lane 的
233
+ // eff 来自 FileConfigStore 原始 JSON(非 zod 输出),tierGroups 的 ZodDefault 恒在场性不适用——门保留。
239
234
  ...(eff.models.tierGroups !== undefined ? { tierGroups: eff.models.tierGroups } : {}),
240
235
  ...(eff.models.activeTierGroup !== undefined ? { activeTierGroup: eff.models.activeTierGroup } : {}),
241
236
  },
@@ -299,13 +299,13 @@ export interface ServiceConfigFlat {
299
299
  database: string;
300
300
  connectionLimit?: number;
301
301
  };
302
- /** Which SQL engine the durable stores run on (clay 2026-06-21 PG migration; 2026-07-06 wording: canonical
303
- * value is `mysql` = any MySQL-protocol server — MySQL, TiDB, MariaDB — over the same mysql2 pool; the env
304
- * value `tidb` is a back-compat alias normalized to `mysql` at parse). `mysql` reads the `tidb`-named
305
- * connection coords above (MYSQL_* env preferred, TIDB_* accepted); `pg` (PostgreSQL dialect) reads `pg`
306
- * below. A subsystem that selects a DB backend (sessionBackend === "tidb", read as "the SQL DB") then
307
- * runs on the engine `dbBackend` picks. TiDB is fully supported (any MySQL-protocol engine works —
308
- * memory no longer needs a VECTOR column since design/138 moved it onto the file engine). Env: DB_BACKEND. */
302
+ /** Which SQL engine the durable stores run on (clay 2026-06-21 PG migration). Canonical value is `mysql`
303
+ * = any MySQL-protocol server — MySQL, TiDB, MariaDB — over the same mysql2 pool; `pg` (PostgreSQL
304
+ * dialect) reads `pg` below. [2354]/[2373]B-3(5.0.0):the env value `tidb` and the TIDB_* coord names
305
+ * are RETIRED `tidb` is rejected by the enum and any TIDB_* env in place refuses to start with the
306
+ * MYSQL_* replacement named (see RETIRED_ENVS in config.ts). `mysql` reads MYSQL_* coords only.
307
+ * TiDB itself stays fully supported (any MySQL-protocol engine works — memory no longer needs a VECTOR
308
+ * column since design/138 moved it onto the file engine). Env: DB_BACKEND. */
309
309
  dbBackend: "mysql" | "pg" | "local" | "memory";
310
310
  /** clay 拍(2026-07-27):operator 是否显式设了 DB_BACKEND。main.ts 的降级判据——**默认推导**的
311
311
  * local(裸 boot)在 mkdir/ensure 失败时降级 memory + warn + gauge(auto-DB 不可达先例);
@@ -468,11 +468,10 @@ export interface ServiceConfigFlat {
468
468
  operatorPrincipals: string[];
469
469
  /** Tool names that are always denied. */
470
470
  approvalDeny: string[];
471
- /** Poll interval (ms) for an approval decision. */
472
- approvalPollMs: number;
473
471
  /** design/45 durable-checkpoint (core 1.67): when true (+ a TiDB pool), F4 uses durable **suspend/resume**
474
472
  * instead of poll-blocking — a gated tool `ask` suspends the task (status:"suspended" + checkpoint), an
475
- * operator resumes it on any replica. Default OFF unchanged poll-policy. `DURABLE_APPROVAL=true`. */
473
+ * operator resumes it on any replica. Default OFF([2354] A-5:轮询车道已退役,OFF+门意图 core UNGATED
474
+ * 告警,不再有回退门形). `DURABLE_APPROVAL=true`. */
476
475
  durableApproval: boolean;
477
476
  /** design/80 seam #2 (preemption) / design/74 ("失败≠没做完" third state): when true (+ durable mode), the
478
477
  * /v1/runs durable path opts every task into core `resourceSuspend` — a budget/turns/walltime limit OR a
@@ -797,7 +796,7 @@ export interface ServiceConfigFlat {
797
796
  to: string;
798
797
  atCostFraction: number;
799
798
  reactive: boolean;
800
- downgradeOn?: ("rate_limit" | "breaker_open")[];
799
+ downgradeOn?: import("@sema-agent/core").DegradeReason[];
801
800
  toSupportsImages: boolean;
802
801
  };
803
802
  /** Quality-gate cascade ladder (core 1.45 `runCascade`, opt-in via `cascade:true`): catalog model
@@ -847,25 +846,12 @@ export interface ServiceConfigFlat {
847
846
  defaultScenario: string;
848
847
  /** Directory of baked-in skill Markdown files, loaded once at startup. */
849
848
  skillsDir: string;
850
- /** OA backend base URL for the `oa` scenario tools (read /api/v1, write /api/assets). */
851
- oaApiBaseUrl?: string;
852
- /** Shared trust token forwarded to OA (= OA AGENT_SERVICE_TOKEN); OA enforces per-principal RBAC. */
853
- oaServiceToken?: string;
854
- /** `oa` 场景的"开 Gitea issue"工具配置(OA AI 请求#5);仅当 OA_ISSUE_TOKEN 配置时启用。token 来自 env,不入模型/日志。
855
- * 上线建议:OA_ISSUE_TOKEN 用**最小权限**(仅 issue:write 的 scope),别用全权 token。 */
856
- oaIssue?: {
857
- baseUrl: string;
858
- owner: string;
859
- repo: string;
860
- token: string;
861
- defaultLabels?: number[];
862
- };
863
849
  /** Central sema registry (sema-registry). When set, the service pulls the effective config
864
850
  * on boot (and refreshes) and applies it OVER the env defaults — env is the fallback. Logical config
865
851
  * only (model roster/roles/teams); secrets/gateways stay in this service's env.
866
- * `dryRun` (SEMA_REGISTRY_DRY_RUN=true, legacy CONFIG_CENTER_DRY_RUN): pull + LOG what the registry would change vs the env-derived
852
+ * `dryRun` (SEMA_REGISTRY_DRY_RUN=true; legacy CONFIG_CENTER_DRY_RUN now boot-rejects, no dual-read): pull + LOG what the registry would change vs the env-derived
867
853
  * config, but do NOT apply it — a safe-rollout verification step before flipping to live.
868
- * `worker` (SEMA_REGISTRY_WORKER=<name>, legacy CONFIG_CENTER_WORKER; injected per-worker by the reconciler): scopes the pull to
854
+ * `worker` (SEMA_REGISTRY_WORKER=<name>; legacy CONFIG_CENTER_WORKER now boot-rejects, no dual-read; injected per-worker by the reconciler): scopes the pull to
869
855
  * that worker's roster via `/effective?worker=<name>`. Unset → the global roster (backward compatible). */
870
856
  configCenter?: {
871
857
  baseUrl: string;
@@ -874,7 +860,7 @@ export interface ServiceConfigFlat {
874
860
  worker?: string;
875
861
  };
876
862
  /** Dual-mode config SOURCE (DUAL-MODE-DESIGN §4): "remote" (sema-registry HTTP) | "local" (.env + config.d
877
- * via @sema-agent/registry-core FileConfigStore) | unset → remote if SEMA_REGISTRY_URL (legacy CONFIG_CENTER_URL) is set, else pure-env.
863
+ * via @sema-agent/registry-core FileConfigStore) | unset → remote if SEMA_REGISTRY_URL (legacy CONFIG_CENTER_URL now boot-rejects, no dual-read) is set, else pure-env.
878
864
  * Env: CONFIG_PROVIDER. The same resolver/EffectiveConfig either way (can't drift). */
879
865
  configProvider?: string;
880
866
  /** Root dir holding `config.d/<domain>.json` for the local config source. Env: CONFIG_LOCAL_DIR. */
@@ -886,7 +872,7 @@ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "ses
886
872
  export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "cascadeLadder" | "degrade">;
887
873
  /** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
888
874
  * 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
889
- export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalPollMs" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "askQuestionEnabled" | "toolApprovalEnabled" | "mcpElicitation" | "sensitiveWritePatterns" | "manualModeShellGate">;
875
+ export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "askQuestionEnabled" | "toolApprovalEnabled" | "mcpElicitation" | "sensitiveWritePatterns" | "manualModeShellGate">;
890
876
  /** 组:memory(记忆面 + TOC 同步腿)。 */
891
877
  export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memorySync" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
892
878
  /** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
@@ -899,7 +885,7 @@ export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachme
899
885
  /** 组:observability(可观测)。 */
900
886
  export type ServiceObservabilityConfig = Pick<ServiceConfigFlat, "metricsToken" | "traceToken" | "toolTrace" | "traceThinking" | "logLevel" | "otel">;
901
887
  /** 组:integrations(外部集成)。`mcpServers` 由 sema-registry 适配器填(无 env 腿),归本组。 */
902
- export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "defaultScenario" | "skillsDir" | "oaApiBaseUrl" | "oaServiceToken" | "oaIssue" | "configCenter" | "configProvider" | "configLocalDir">;
888
+ export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "defaultScenario" | "skillsDir" | "configCenter" | "configProvider" | "configLocalDir">;
903
889
  /** 九个组槽。每组恒在场(`loadConfig` / `attachConfigGroups` 装好才交出配置),故不可选——新代码写
904
890
  * `config.modelPlane.model` 不需要 `?.`(可选组会把 `Model` 污染成 `Model | undefined`)。 */
905
891
  export interface ServiceConfigGroups {