@sema-agent/server 4.3.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/USAGE.md +5 -3
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.d.ts +5 -37
- package/dist/approval.js +12 -112
- package/dist/auth-bridge.d.ts +0 -1
- package/dist/auth-bridge.js +0 -1
- package/dist/auth-keys.d.ts +1 -1
- package/dist/auth-keys.js +1 -1
- package/dist/bench/s1/run-firm.js +1 -1
- package/dist/boot/config-center.d.ts +1 -1
- package/dist/boot/config-center.js +3 -2
- package/dist/boot/reapers.d.ts +1 -2
- package/dist/boot/reapers.js +4 -5
- package/dist/boot/resolve-spec.d.ts +1 -3
- package/dist/boot/resolve-spec.js +21 -35
- package/dist/boot/stores.js +1 -1
- package/dist/brain.js +3 -1
- package/dist/capabilities/center-prompts.d.ts +2 -3
- package/dist/capabilities/center-prompts.js +2 -3
- package/dist/capabilities/sandbox-file-send.d.ts +0 -2
- package/dist/capabilities/sandbox-file-send.js +2 -5
- package/dist/capabilities/scenarios.d.ts +0 -9
- package/dist/capabilities/scenarios.js +1 -20
- package/dist/capabilities/send-user-file-tool.js +3 -3
- package/dist/config-center/apply-effective.js +8 -8
- package/dist/config-center/types.d.ts +10 -3
- package/dist/config-provider.js +6 -11
- package/dist/config-types.d.ts +12 -26
- package/dist/config.js +95 -68
- package/dist/fleet/fleet-bus.js +25 -33
- package/dist/fleet-client.js +1 -1
- package/dist/http/active-run-conflict.d.ts +2 -0
- package/dist/http/active-run-conflict.js +1 -0
- package/dist/http/routes/approvals-assistant.d.ts +2 -1
- package/dist/http/routes/approvals-assistant.js +10 -96
- package/dist/http/routes/capabilities.js +6 -5
- package/dist/http/routes/fleet.js +4 -1
- package/dist/http/routes/images.js +10 -4
- package/dist/http/routes/runs.js +34 -49
- package/dist/http/routes/session-sync.d.ts +1 -1
- package/dist/http/routes/session-sync.js +2 -1
- package/dist/http/routes/tasks.js +7 -7
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/server.d.ts +1 -6
- package/dist/http/server.js +15 -10
- package/dist/http/verify-rounds.d.ts +2 -2
- package/dist/http/wire-types.d.ts +1 -1
- package/dist/images/bake-validate.d.ts +0 -22
- package/dist/images/bake-validate.js +0 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -5
- package/dist/main.js +5 -9
- package/dist/memory-scope.js +2 -2
- package/dist/orchestration/workflow-completion-inbox.d.ts +6 -4
- package/dist/orchestration/workflow-completion-inbox.js +6 -4
- package/dist/plugins/breaker-state-sql.d.ts +2 -3
- package/dist/plugins/image-index-sql.d.ts +0 -1
- package/dist/plugins/image-index-sql.js +0 -1
- package/dist/plugins/local-checkpoint-store.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.js +1 -1
- package/dist/plugins/memory-engine-pg.js +2 -1
- package/dist/plugins/memory-engine-tidb.js +1 -1
- package/dist/plugins/memory-engine-vector-util.d.ts +2 -3
- package/dist/plugins/memory-engine-vector-util.js +2 -3
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-session-policy-store.d.ts +1 -1
- package/dist/plugins/memory-session-policy-store.js +1 -1
- package/dist/plugins/memory-sync-store-pg.js +1 -1
- package/dist/plugins/pg-pool.js +8 -10
- package/dist/plugins/pg-rate-limiter.d.ts +1 -4
- package/dist/plugins/pg-rate-limiter.js +0 -1
- package/dist/plugins/roster-store-sql.d.ts +1 -3
- package/dist/plugins/roster-store-sql.js +3 -5
- package/dist/plugins/store-backend.d.ts +7 -17
- package/dist/plugins/store-backend.js +14 -30
- package/dist/plugins/tidb-pool.js +4 -28
- package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
- package/dist/plugins/workflow-journal-store-sql.js +1 -1
- package/dist/principal-jwt.js +2 -2
- package/dist/prompts-domain-validate.d.ts +4 -3
- package/dist/prompts-domain-validate.js +7 -8
- package/dist/router/route-orchestration.js +3 -3
- package/dist/run-local.js +5 -4
- package/dist/runs.js +8 -6
- package/dist/runtime-caps-resolver.d.ts +3 -2
- package/dist/runtime-caps-resolver.js +4 -4
- package/dist/runtime-governance.d.ts +4 -7
- package/dist/runtime-governance.js +12 -17
- package/dist/sealed-key.d.ts +1 -1
- package/dist/sealed-key.js +1 -1
- package/dist/security.d.ts +0 -5
- package/dist/security.js +8 -12
- package/dist/session-sync.d.ts +0 -2
- package/dist/session-sync.js +10 -17
- package/dist/spec-fields.js +6 -6
- package/dist/task-settings.js +7 -7
- package/dist/tool-approval.d.ts +3 -3
- package/dist/tool-approval.js +10 -10
- package/dist/trace/artifacts.d.ts +11 -0
- package/dist/trace/artifacts.js +8 -19
- package/dist/trace/ledger-sink.js +7 -1
- package/dist/trace/project.d.ts +17 -0
- package/dist/trace/project.js +55 -5
- package/package.json +2 -2
- package/dist/capabilities/oa-tools.d.ts +0 -15
- package/dist/capabilities/oa-tools.js +0 -54
- package/dist/plugins/approval-store-sql.d.ts +0 -116
- package/dist/plugins/approval-store-sql.js +0 -150
- package/dist/plugins/file-workflow-journal-store.d.ts +0 -12
- package/dist/plugins/file-workflow-journal-store.js +0 -12
- package/dist/plugins/pg-approval-store.d.ts +0 -9
- package/dist/plugins/pg-approval-store.js +0 -9
- package/dist/plugins/pg-breaker-state.d.ts +0 -8
- package/dist/plugins/pg-breaker-state.js +0 -8
- package/dist/plugins/pg-checkpoint-store.d.ts +0 -10
- package/dist/plugins/pg-checkpoint-store.js +0 -10
- package/dist/plugins/pg-file-snapshot-store.d.ts +0 -8
- package/dist/plugins/pg-file-snapshot-store.js +0 -8
- package/dist/plugins/pg-image-bake.d.ts +0 -12
- package/dist/plugins/pg-image-bake.js +0 -11
- package/dist/plugins/pg-image-index.d.ts +0 -12
- package/dist/plugins/pg-image-index.js +0 -11
- package/dist/plugins/pg-outcome-ledger.d.ts +0 -12
- package/dist/plugins/pg-outcome-ledger.js +0 -11
- package/dist/plugins/pg-resume-anchor-store.d.ts +0 -7
- package/dist/plugins/pg-resume-anchor-store.js +0 -7
- package/dist/plugins/pg-run-store.d.ts +0 -9
- package/dist/plugins/pg-run-store.js +0 -9
- package/dist/plugins/pg-session-policy-store.d.ts +0 -7
- package/dist/plugins/pg-session-policy-store.js +0 -7
- package/dist/plugins/pg-session-store.d.ts +0 -12
- package/dist/plugins/pg-session-store.js +0 -12
- package/dist/plugins/pg-tool-result-store.d.ts +0 -9
- package/dist/plugins/pg-tool-result-store.js +0 -9
- package/dist/plugins/pg-workflow-journal-store.d.ts +0 -9
- package/dist/plugins/pg-workflow-journal-store.js +0 -9
- package/dist/plugins/pg-workflow-run-store.d.ts +0 -9
- package/dist/plugins/pg-workflow-run-store.js +0 -9
- package/dist/plugins/tidb-approval-store.d.ts +0 -8
- package/dist/plugins/tidb-approval-store.js +0 -8
- package/dist/plugins/tidb-breaker-state.d.ts +0 -7
- package/dist/plugins/tidb-breaker-state.js +0 -7
- package/dist/plugins/tidb-checkpoint-store.d.ts +0 -9
- package/dist/plugins/tidb-checkpoint-store.js +0 -9
- package/dist/plugins/tidb-file-snapshot-store.d.ts +0 -8
- package/dist/plugins/tidb-file-snapshot-store.js +0 -8
- package/dist/plugins/tidb-image-bake.d.ts +0 -12
- package/dist/plugins/tidb-image-bake.js +0 -11
- package/dist/plugins/tidb-image-index.d.ts +0 -12
- package/dist/plugins/tidb-image-index.js +0 -11
- package/dist/plugins/tidb-outcome-ledger.d.ts +0 -12
- package/dist/plugins/tidb-outcome-ledger.js +0 -12
- package/dist/plugins/tidb-resume-anchor-store.d.ts +0 -7
- package/dist/plugins/tidb-resume-anchor-store.js +0 -7
- package/dist/plugins/tidb-run-store.d.ts +0 -10
- package/dist/plugins/tidb-run-store.js +0 -9
- package/dist/plugins/tidb-session-policy-store.d.ts +0 -7
- package/dist/plugins/tidb-session-policy-store.js +0 -7
- package/dist/plugins/tidb-tool-result-store.d.ts +0 -8
- package/dist/plugins/tidb-tool-result-store.js +0 -10
- package/dist/plugins/tidb-workflow-journal-store.d.ts +0 -9
- package/dist/plugins/tidb-workflow-journal-store.js +0 -9
- package/dist/plugins/tidb-workflow-run-store.d.ts +0 -10
- package/dist/plugins/tidb-workflow-run-store.js +0 -10
- package/dist/plugins/workflow-journal-limits.d.ts +0 -12
- package/dist/plugins/workflow-journal-limits.js +0 -12
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { CODE_AGENT_PROMPT, CODE_SYSTEM_PROMPT, DEFAULT_SYSTEM_PROMPT, assembleCodeTools,
|
|
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
|
|
@@ -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
|
|
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 =
|
|
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) =>
|
|
166
|
+
const gated = [...(gate.deny ?? []), ...(gate.require ?? []), ...(gate.neverAuto ?? [])].some((t) => t === canonical);
|
|
167
167
|
return gated ? [] : [deps.spec];
|
|
168
168
|
};
|
|
169
169
|
}
|
|
@@ -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
|
-
//
|
|
296
|
-
//
|
|
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 的
|
|
@@ -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
|
-
//
|
|
364
|
-
//
|
|
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
|
-
|
|
368
|
-
|
|
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)) {
|
|
@@ -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
|
-
* 同款域包裹)
|
|
184
|
-
*
|
|
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
|
-
}
|
|
195
|
+
};
|
|
189
196
|
/** registry-core 0.6:治理三件(autonomy/commandPolicy/approvalRequire)拆新 governance 域=真值;
|
|
190
197
|
* 过渡期 effective 在 runtime 位双写镜像。读取优先 governance 新位、runtime 旧位 fallback(顺序契约:
|
|
191
198
|
* 消费端先切换、确认后 center 才撤双写,治理闸零 fail-open 窗口)。 */
|
package/dist/config-provider.js
CHANGED
|
@@ -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
|
|
214
|
-
//
|
|
215
|
-
|
|
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
|
},
|
package/dist/config-types.d.ts
CHANGED
|
@@ -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
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
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
|
|
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?: ("
|
|
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,19 +846,6 @@ 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.
|
|
@@ -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" | "
|
|
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" | "
|
|
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 {
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { CODE_AGENT_PROMPT, formatUserScope, isThinkingLevel, RECOMMENDED_SENSITIVE_PATTERNS } from "@sema-agent/core";
|
|
4
|
+
import { CODE_AGENT_PROMPT, formatUserScope, isThinkingLevel, protocolOf, RECOMMENDED_SENSITIVE_PATTERNS, RETIRED_TOOL_NAMES } from "@sema-agent/core";
|
|
5
5
|
import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES } from "@sema-agent/registry-core";
|
|
6
6
|
import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js"; // design/158 A4: the parser leaf — NOT security.js (base config layer must not value-import the 55KiB auth module)
|
|
7
7
|
import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
|
|
@@ -60,16 +60,32 @@ function defaultSkillsDir() {
|
|
|
60
60
|
return "skills"; // import.meta 不可用的兜底(不应发生在 ESM 构建里)
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
64
|
-
*
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
/** [2354] 兼容面全清:退役 env 名墓碑——老名**在场即拒启**带新名指引(boolEnvWithLegacyNegated 3.0.0
|
|
64
|
+
* 负名墓碑同形)。静默忽略=部署带着失效配置无感跑;双读=兼容层吸收下游冒烟。都不要。 */
|
|
65
|
+
const RETIRED_ENVS = [
|
|
66
|
+
["TIDB_HOST", "MYSQL_HOST"], ["TIDB_PORT", "MYSQL_PORT"], ["TIDB_USER", "MYSQL_USER"],
|
|
67
|
+
["TIDB_PASSWORD", "MYSQL_PASSWORD"], ["TIDB_DATABASE", "MYSQL_DATABASE"], ["TIDB_POOL_SIZE", "MYSQL_POOL_SIZE"],
|
|
68
|
+
["CONFIG_CENTER_URL", "SEMA_REGISTRY_URL"], ["CONFIG_CENTER_TOKEN", "SEMA_REGISTRY_TOKEN"],
|
|
69
|
+
["CONFIG_CENTER_DRY_RUN", "SEMA_REGISTRY_DRY_RUN"], ["CONFIG_CENTER_WORKER", "SEMA_REGISTRY_WORKER"],
|
|
70
|
+
["ANTHROPIC_BASEURL", "ANTHROPIC_BASE_URL"],
|
|
71
|
+
["REGISTRY_ADMIN_TOKEN", "SEMA_REGISTRY_ADMIN_TOKEN"], // [2373]B-1:墓碑族唯一漏网的双读(config-center.ts 发射点)
|
|
72
|
+
// [2354] OA 场景整体退役(clay 令 2026-08-01):配置本身作废,无替代名。
|
|
73
|
+
["OA_API_BASEURL", "(retired — OA scenario removed in 5.0.0)"],
|
|
74
|
+
["OA_SERVICE_TOKEN", "(retired — OA scenario removed in 5.0.0)"],
|
|
75
|
+
["OA_ISSUE_TOKEN", "(retired — OA scenario removed in 5.0.0)"],
|
|
76
|
+
["OA_ISSUE_BASEURL", "(retired — OA scenario removed in 5.0.0)"],
|
|
77
|
+
["OA_ISSUE_OWNER", "(retired — OA scenario removed in 5.0.0)"],
|
|
78
|
+
["OA_ISSUE_REPO", "(retired — OA scenario removed in 5.0.0)"],
|
|
79
|
+
["OA_ISSUE_LABELS", "(retired — OA scenario removed in 5.0.0)"],
|
|
80
|
+
["APPROVAL_POLL_MS", "(retired — OA poll-approval lane removed in 5.0.0; durable checkpoint lane is the only gate)"],
|
|
81
|
+
];
|
|
82
|
+
function rejectRetiredEnvs() {
|
|
83
|
+
const hit = RETIRED_ENVS.filter(([o]) => process.env[o] !== undefined && process.env[o] !== "");
|
|
84
|
+
if (hit.length > 0) {
|
|
85
|
+
throw new Error(`retired env name(s) set ([2354] 兼容面全清,5.0.0 起拒启): ` +
|
|
86
|
+
hit.map(([o, n]) => `${o} → use ${n}`).join("; ") +
|
|
87
|
+
` — rename the env key(s); the value semantics are unchanged.`);
|
|
71
88
|
}
|
|
72
|
-
return v;
|
|
73
89
|
}
|
|
74
90
|
/** Numeric env with validation: a non-numeric value FAILS at startup instead of silently becoming `NaN`
|
|
75
91
|
* (which e.g. slips past the `runStaleSec` liveness guard and silently disables the reaper). */
|
|
@@ -387,11 +403,11 @@ function parseStoreDomain(ctx) {
|
|
|
387
403
|
// 在场时,dbBackend 的旧默认 "mysql" 是**引擎选择器**(auto 探测/coords 构建都 key 它),默认切
|
|
388
404
|
// local 会把这些既有形错切到文件店(回归钉在 config-coverage)。
|
|
389
405
|
const sqlSignal = (!!process.env.SESSION_BACKEND && process.env.SESSION_BACKEND !== "memory") ||
|
|
390
|
-
!!(process.env.
|
|
406
|
+
!!(process.env.MYSQL_HOST || process.env.PG_HOST);
|
|
391
407
|
const bareBoot = !dbBackendSet && !sqlSignal;
|
|
392
408
|
const multiTenantBareBoot = bareBoot && requirePrincipal;
|
|
393
|
-
|
|
394
|
-
const dbBackend =
|
|
409
|
+
// [2354]:公名枚举不再收 "tidb"(enumEnv 对越集值自带拒启);内部逻辑不变。
|
|
410
|
+
const dbBackend = enumEnv("DB_BACKEND", bareBoot ? (multiTenantBareBoot ? "memory" : "local") : "mysql", ["mysql", "pg", "local", "memory"]);
|
|
395
411
|
if (multiTenantBareBoot) {
|
|
396
412
|
CONFIG_NOTICES.push({
|
|
397
413
|
event: "db_backend_default_memory_multi_tenant",
|
|
@@ -407,7 +423,7 @@ function parseStoreDomain(ctx) {
|
|
|
407
423
|
// backend); an UNSET DB_BACKEND keeps the memory default (no surprise DB requirement on a bare boot).
|
|
408
424
|
// `mysql` is accepted as an alias of the internal durable label `tidb` (see ServiceConfig.sessionBackend).
|
|
409
425
|
const sqlEngineExplicit = !!process.env.DB_BACKEND && dbBackend !== "local" && dbBackend !== "memory";
|
|
410
|
-
const sessionBackendRaw = enumEnv("SESSION_BACKEND", sqlEngineExplicit ? "mysql" : "memory", ["memory", "mysql", "
|
|
426
|
+
const sessionBackendRaw = enumEnv("SESSION_BACKEND", sqlEngineExplicit ? "mysql" : "memory", ["memory", "mysql", "auto"]); // [2354]:公名 "tidb" 退役
|
|
411
427
|
const sessionBackend = sessionBackendRaw === "mysql" ? "tidb" : sessionBackendRaw;
|
|
412
428
|
// P0.5 variant-2: the file-backed `local` backend's data root. Same tail as main.ts's `localRoot` and
|
|
413
429
|
// run-local.ts's `root` (CONFIG_LOCAL_DIR → AGENT_DATA_DIR → ~/.ai-agent), so the HTTP service + a run-local
|
|
@@ -421,7 +437,7 @@ function parseStoreDomain(ctx) {
|
|
|
421
437
|
// `auto` builds the DB coords only when a host is actually configured — otherwise it stays memory with no
|
|
422
438
|
// probe. (Explicit mysql always builds them so a missing host fails loudly.) `needsDb` is engine-agnostic;
|
|
423
439
|
// the connection coords are built for whichever engine `dbBackend` selects (MYSQL_* preferred, TIDB_* alias).
|
|
424
|
-
const dbHostSet = dbBackend === "pg" ? !!process.env.PG_HOST : !!
|
|
440
|
+
const dbHostSet = dbBackend === "pg" ? !!process.env.PG_HOST : !!process.env.MYSQL_HOST;
|
|
425
441
|
const needsTidb = sessionBackend === "tidb" || (sessionBackend === "auto" && dbHostSet);
|
|
426
442
|
const needsDb = needsTidb;
|
|
427
443
|
return {
|
|
@@ -433,14 +449,13 @@ function parseStoreDomain(ctx) {
|
|
|
433
449
|
localDataRoot,
|
|
434
450
|
tidb: needsDb && dbBackend === "mysql"
|
|
435
451
|
? {
|
|
436
|
-
host:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
? Number(process.env.MYSQL_POOL_SIZE || process.env.TIDB_POOL_SIZE)
|
|
452
|
+
host: env("MYSQL_HOST"),
|
|
453
|
+
port: Number(env("MYSQL_PORT", "3306")),
|
|
454
|
+
user: env("MYSQL_USER"),
|
|
455
|
+
password: env("MYSQL_PASSWORD", ""),
|
|
456
|
+
database: env("MYSQL_DATABASE"),
|
|
457
|
+
connectionLimit: process.env.MYSQL_POOL_SIZE
|
|
458
|
+
? Number(process.env.MYSQL_POOL_SIZE)
|
|
444
459
|
: undefined,
|
|
445
460
|
}
|
|
446
461
|
: undefined,
|
|
@@ -528,7 +543,7 @@ function parseModelDomain() {
|
|
|
528
543
|
// credential alone does NOT infer (a bare ANTHROPIC_API_KEY historically only assembles the cloud route for
|
|
529
544
|
// registry-declared anthropic models); neither present ⇒ "gateway", byte-identical to before.
|
|
530
545
|
const explicitProvider = process.env.MODEL_PROVIDER; // env() semantics: "" = unset
|
|
531
|
-
const inferredAnthropicBaseUrl = firstSetEnv("
|
|
546
|
+
const inferredAnthropicBaseUrl = firstSetEnv("ANTHROPIC_BASE_URL"); // [2354]:无下划线非标名退役(墓碑)
|
|
532
547
|
const inferredAnthropicCred = firstSetEnv("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN");
|
|
533
548
|
const provider = (() => {
|
|
534
549
|
if (explicitProvider !== undefined && explicitProvider !== "")
|
|
@@ -719,7 +734,7 @@ function parseModelDomain() {
|
|
|
719
734
|
? {
|
|
720
735
|
apiKey: firstSetEnv("ANTHROPIC_API_KEY"),
|
|
721
736
|
authToken: firstSetEnv("ANTHROPIC_AUTH_TOKEN"),
|
|
722
|
-
baseUrl: firstSetEnv("
|
|
737
|
+
baseUrl: firstSetEnv("ANTHROPIC_BASE_URL"),
|
|
723
738
|
version: process.env.ANTHROPIC_VERSION,
|
|
724
739
|
cacheBreakpoints: (process.env.ANTHROPIC_CACHE_BREAKPOINTS ?? "true") === "true",
|
|
725
740
|
...(process.env.ANTHROPIC_MAX_RETRIES ? { maxRetries: Number(env("ANTHROPIC_MAX_RETRIES", "2")) } : {}),
|
|
@@ -755,9 +770,9 @@ function parseModelDomain() {
|
|
|
755
770
|
to: env("MODEL_DEGRADE_TO"),
|
|
756
771
|
atCostFraction: Number(env("MODEL_DEGRADE_AT_COST_FRACTION", "0.7")),
|
|
757
772
|
reactive: boolEnv("MODEL_DEGRADE_REACTIVE", false),
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
773
|
+
// core 5.1.0([2390]②):词表=DegradeReason 全集;缺席时 core 默认含 server_error(默认门变宽),
|
|
774
|
+
// 本 env 是运维收窄/放宽的唯一旋钮。拼错词 boot 响亮拒——盲铸静默折无效成员是 TASK_TIMEOUT_SEC 同族病。
|
|
775
|
+
downgradeOn: parseDegradeOn(csv("MODEL_DEGRADE_ON")),
|
|
761
776
|
// vision precheck (adversarial-review finding): the degrade TARGET is an external gateway
|
|
762
777
|
// model NOT in config.models, so resolveSpec's precheck (which only sees the picked model) can't see its
|
|
763
778
|
// vision capability — a vision-capable main + text-only degrade target would let images reach the text-only
|
|
@@ -771,6 +786,18 @@ function parseModelDomain() {
|
|
|
771
786
|
}
|
|
772
787
|
/** 域:approval(审批/HITL 门)—— 审批名单与预算、durable 面、直连门开关与钥、敏感写门、elicitation。
|
|
773
788
|
* 注:直连门的**激活**(directDoorActive)与 D-G §3 半开门不变量是跨域合取,留在装配层。 */
|
|
789
|
+
/** MODEL_DEGRADE_ON 词表=core `DegradeReason` 全集(集合用该类型铸=锁步:core 删词此处 tsc 红,加词这里
|
|
790
|
+
* 补一个词即可)。非法词 boot 响亮拒——盲铸静默折无效成员与 TASK_TIMEOUT_SEC 非法值折「无超时」同族。 */
|
|
791
|
+
const DEGRADE_ON_WORDS = new Set(["breaker_open", "rate_limit", "budget", "server_error", "last_resort"]);
|
|
792
|
+
function parseDegradeOn(words) {
|
|
793
|
+
if (words.length === 0)
|
|
794
|
+
return undefined;
|
|
795
|
+
const bad = words.filter((w) => !DEGRADE_ON_WORDS.has(w));
|
|
796
|
+
if (bad.length > 0) {
|
|
797
|
+
throw new Error(`MODEL_DEGRADE_ON has unknown trigger word(s): ${bad.join(", ")} (valid: ${[...DEGRADE_ON_WORDS].join(", ")})`);
|
|
798
|
+
}
|
|
799
|
+
return words;
|
|
800
|
+
}
|
|
774
801
|
function parseApprovalDomain(ctx) {
|
|
775
802
|
const { postureOn } = ctx; // 跨域入参②:posture 三态(single-user turnkey ⇒ HITL 面默认 ON)
|
|
776
803
|
// design/80 D-G: the direct-connect approval door anchors (all must be present to ACTIVATE; fail-closed).
|
|
@@ -778,15 +805,34 @@ function parseApprovalDomain(ctx) {
|
|
|
778
805
|
const dgHmacKeys = parseApprovalHmacKeys(process.env.APPROVAL_HMAC_KEYS);
|
|
779
806
|
// DURABLE_APPROVAL 既是本域字段,也是直连门的硬前置 —— 那条前置的理由与断言在装配层的跨域不变量段。
|
|
780
807
|
const durableApproval = boolEnv("DURABLE_APPROVAL", false);
|
|
808
|
+
// [2380] core 5.0.0 RB-476:approval 名单里的退役工具名**装配期拒启**(带现役名指引)。core 自己的
|
|
809
|
+
// prepare 门(config.legacy_tool_name)在提交期才响——boot 期先拒,部署带失效名单不启动而不是首任务炸。
|
|
810
|
+
// 名单 RAW 直传(canonicalToolName 折叠面已随 core 退役,见 approval.ts)。
|
|
811
|
+
const rejectRetiredToolNames = (envName, names) => {
|
|
812
|
+
const hit = names.filter((n) => RETIRED_TOOL_NAMES.has(n));
|
|
813
|
+
if (hit.length > 0) {
|
|
814
|
+
throw new Error(`${envName} names retired tool(s) (core 5.0.0 RB-476 — the alias fold is gone, these never match): ` +
|
|
815
|
+
hit.map((n) => `${n} → ${RETIRED_TOOL_NAMES.get(n)}`).join("; "));
|
|
816
|
+
}
|
|
817
|
+
// [2382]① core 5.1.0 判别式:pre-prefix 短名(figma__x 形)静态表拦不住,而这些名单是 server 侧
|
|
818
|
+
// ToolPolicy 原样比对——core 的 prepare 硬臂(G-3)只管 core 侧 rules,管不到这里;放过=deny 静默
|
|
819
|
+
// 永不匹配(失守方向)。名含 `__` 且协议前缀不认识 ⇒ boot 拒。部署自有工具真叫 `figma__x` 的极端形
|
|
820
|
+
// 会被误拒(boot 无 roster 可豁免)——与上面退役表墓碑同一个已接受权衡,失败响亮且指引明确。
|
|
821
|
+
const prePrefix = names.filter((n) => n.includes("__") && protocolOf(n) === undefined);
|
|
822
|
+
if (prePrefix.length > 0) {
|
|
823
|
+
throw new Error(`${envName} names look like pre-prefix MCP short forms (the v4 auto-prefix fold is gone — these never match a live tool): ` +
|
|
824
|
+
prePrefix.map((n) => `${n} → mcp__${n}`).join("; "));
|
|
825
|
+
}
|
|
826
|
+
return names;
|
|
827
|
+
};
|
|
781
828
|
return {
|
|
782
|
-
approvalRequire: csv("APPROVAL_REQUIRE"),
|
|
783
|
-
approvalDeny: csv("APPROVAL_DENY"),
|
|
784
|
-
approvalPollMs: Number(env("APPROVAL_POLL_MS", "1500")),
|
|
829
|
+
approvalRequire: rejectRetiredToolNames("APPROVAL_REQUIRE", csv("APPROVAL_REQUIRE")),
|
|
830
|
+
approvalDeny: rejectRetiredToolNames("APPROVAL_DENY", csv("APPROVAL_DENY")),
|
|
785
831
|
durableApproval,
|
|
786
832
|
resourceSuspend: boolEnv("RESOURCE_SUSPEND", false),
|
|
787
833
|
resourceSuspendTtlSec: Math.max(0, numEnv("RESOURCE_SUSPEND_TTL_SEC", "0")), // 0 ⇒ core default (30d)
|
|
788
834
|
approvalAutoBudget: Math.min(10000, Math.max(0, numEnv("APPROVAL_AUTO_BUDGET", "0"))), // clamp [0,10000]; 0 = off
|
|
789
|
-
approvalNeverAuto: csv("APPROVAL_NEVER_AUTO"),
|
|
835
|
+
approvalNeverAuto: rejectRetiredToolNames("APPROVAL_NEVER_AUTO", csv("APPROVAL_NEVER_AUTO")),
|
|
790
836
|
approvalHmacKeys: dgHmacKeys, // D-G: WIRED (see the field's JSDoc); empty ⇒ inactive, not unimplemented
|
|
791
837
|
directApprovalDoor,
|
|
792
838
|
// ③ sensitive-path write deny set: unset = core's recommended set; explicit value = full replacement
|
|
@@ -910,11 +956,10 @@ function parseAuthDomain(ctx) {
|
|
|
910
956
|
.map((s) => [s.slice(0, s.indexOf("=")).trim(), s.slice(s.indexOf("=") + 1).trim()])
|
|
911
957
|
.filter(([tok, sys]) => tok.length > 0 && sys.length > 0));
|
|
912
958
|
return {
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
bindHostSource: process.env.BIND_HOST ? "BIND_HOST" : process.env.HOST ? "HOST" : undefined,
|
|
959
|
+
// [2354]:HOST 读取臂已删(zsh 恒设 HOST=机器名,不可墓碑——静默不读;cli/桌面 [2053] 起已传
|
|
960
|
+
// BIND_HOST)。绑址唯一口=BIND_HOST;空串=未设。
|
|
961
|
+
bindHost: process.env.BIND_HOST || undefined,
|
|
962
|
+
bindHostSource: process.env.BIND_HOST ? "BIND_HOST" : undefined,
|
|
918
963
|
authToken: process.env.SERVICE_AUTH_TOKEN,
|
|
919
964
|
authTokens,
|
|
920
965
|
allowUnauthedWrites: boolEnv("ALLOW_UNAUTHED_WRITES", false),
|
|
@@ -1210,35 +1255,16 @@ function parseIntegrationsDomain() {
|
|
|
1210
1255
|
// 编码产品出厂态对标 CC 出厂态,通用域中性 persona 用 DEFAULT_SCENARIO=default 显式选回。
|
|
1211
1256
|
defaultScenario: env("DEFAULT_SCENARIO", "code"),
|
|
1212
1257
|
skillsDir: env("SKILLS_DIR", defaultSkillsDir()),
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
// 开 issue 工具:默认开到 OA 系统仓(AI-Only/oa-asset-system),写权限 token 走独立 env。
|
|
1216
|
-
oaIssue: process.env.OA_ISSUE_TOKEN
|
|
1217
|
-
? {
|
|
1218
|
-
// npm 卫生纪律:不再烤内网 git 主机缺省 — 开了 OA_ISSUE_TOKEN 就必须给 host(fail-loud)。
|
|
1219
|
-
baseUrl: (() => {
|
|
1220
|
-
const u = process.env.OA_ISSUE_BASEURL ?? process.env.GIT_API_BASEURL;
|
|
1221
|
-
if (!u)
|
|
1222
|
-
throw new Error("OA_ISSUE_TOKEN is set but no OA_ISSUE_BASEURL / GIT_API_BASEURL — the issue tool needs a git host");
|
|
1223
|
-
return u;
|
|
1224
|
-
})(),
|
|
1225
|
-
owner: env("OA_ISSUE_OWNER", "AI-Only"),
|
|
1226
|
-
repo: env("OA_ISSUE_REPO", "oa-asset-system"),
|
|
1227
|
-
token: env("OA_ISSUE_TOKEN"),
|
|
1228
|
-
defaultLabels: csv("OA_ISSUE_LABELS").map(Number).filter((n) => Number.isInteger(n)),
|
|
1229
|
-
}
|
|
1230
|
-
: undefined,
|
|
1258
|
+
// [2354] clay 令:OA 场景整体退役(oa-tools/oa 场景/OA 轮询审批车道随 5.0.0 全清)——
|
|
1259
|
+
// OA_* env 全族进 RETIRED_ENVS 墓碑(在场即拒启),此处零读取。
|
|
1231
1260
|
// sema-registry (control plane) URL/token/worker. The orchestrator renamed the worker-injected env from
|
|
1232
|
-
// CONFIG_CENTER_*
|
|
1233
|
-
|
|
1234
|
-
// fallback — and keep working after the orchestrator drops the legacy CONFIG_CENTER_* names. Pure config
|
|
1235
|
-
// plumbing: no API/auth/task-logic change.
|
|
1236
|
-
configCenter: (process.env.SEMA_REGISTRY_URL || process.env.CONFIG_CENTER_URL)
|
|
1261
|
+
// [2354]:CONFIG_CENTER_* 双读已删(墓碑在 rejectRetiredEnvs);SEMA_REGISTRY_* 唯一。
|
|
1262
|
+
configCenter: process.env.SEMA_REGISTRY_URL
|
|
1237
1263
|
? {
|
|
1238
|
-
baseUrl:
|
|
1239
|
-
token: process.env.SEMA_REGISTRY_TOKEN ||
|
|
1240
|
-
dryRun:
|
|
1241
|
-
worker: process.env.SEMA_REGISTRY_WORKER ||
|
|
1264
|
+
baseUrl: env("SEMA_REGISTRY_URL"),
|
|
1265
|
+
token: process.env.SEMA_REGISTRY_TOKEN || "",
|
|
1266
|
+
dryRun: process.env.SEMA_REGISTRY_DRY_RUN === "true",
|
|
1267
|
+
worker: process.env.SEMA_REGISTRY_WORKER || undefined,
|
|
1242
1268
|
}
|
|
1243
1269
|
: undefined,
|
|
1244
1270
|
configProvider: process.env.CONFIG_PROVIDER || undefined,
|
|
@@ -1254,7 +1280,7 @@ const MODEL_PLANE_GROUP_KEYS = [
|
|
|
1254
1280
|
"modelApiKeyEnv", "modelApiKeys", "modelQuotaWeights", "tiers", "projects", "roles", "cascadeLadder", "degrade",
|
|
1255
1281
|
];
|
|
1256
1282
|
const APPROVAL_GROUP_KEYS = [
|
|
1257
|
-
"approvalRequire", "approvalDeny", "
|
|
1283
|
+
"approvalRequire", "approvalDeny", "approvalTimeoutSec", "approvalAutoBudget", "approvalNeverAuto",
|
|
1258
1284
|
"approvalHmacKeys", "durableApproval", "directApprovalDoor", "directDoorActive", "resourceSuspend",
|
|
1259
1285
|
"resourceSuspendTtlSec", "askQuestionEnabled", "toolApprovalEnabled", "mcpElicitation", "sensitiveWritePatterns",
|
|
1260
1286
|
"manualModeShellGate",
|
|
@@ -1286,7 +1312,7 @@ const LIMITS_HTTP_GROUP_KEYS = [
|
|
|
1286
1312
|
const OBSERVABILITY_GROUP_KEYS = ["metricsToken", "traceToken", "toolTrace", "traceThinking", "logLevel", "otel"];
|
|
1287
1313
|
const INTEGRATIONS_GROUP_KEYS = [
|
|
1288
1314
|
"pluginsAllowHosts", "mcpServers", "configBootFetchBudgetMs", "gitApiBaseUrl", "gitApiToken", "defaultScenario",
|
|
1289
|
-
"skillsDir", "
|
|
1315
|
+
"skillsDir", "configCenter", "configProvider", "configLocalDir",
|
|
1290
1316
|
];
|
|
1291
1317
|
/** 组名 → 该组取景的平铺键(introspection 面:测试用它钉「每个平铺键恰好被一组取景」)。 */
|
|
1292
1318
|
export const CONFIG_GROUP_KEYS = {
|
|
@@ -1339,6 +1365,7 @@ export function attachConfigGroups(flat) {
|
|
|
1339
1365
|
return flat;
|
|
1340
1366
|
}
|
|
1341
1367
|
export function loadConfig() {
|
|
1368
|
+
rejectRetiredEnvs(); // [2354] 退役 env 墓碑:在场即拒启带新名指引
|
|
1342
1369
|
CONFIG_WARNINGS.length = 0; // repeated loadConfig() calls (test setup) must not accumulate stale warnings
|
|
1343
1370
|
CONFIG_NOTICES.length = 0;
|
|
1344
1371
|
CONFIG_KNOBS.clear(); // the polarity table describes THIS load, never a previous one's env
|
|
@@ -1379,8 +1406,8 @@ export function loadConfig() {
|
|
|
1379
1406
|
const { principalJwtPubkeys, principalJwtIss, principalJwtAud, operatorPrincipals: dgOperators, authTokens } = auth;
|
|
1380
1407
|
const { enabled: imageBakesEnabled, runnerPrincipal: bakeRunnerPrincipal } = orchestration.imageBakes;
|
|
1381
1408
|
// 🔴 the direct-door crypto gate lives ONLY in the durable (checkpointStore) /decide branch — so DURABLE_APPROVAL
|
|
1382
|
-
// is a HARD precondition. Without it, checkpointStore is undefined
|
|
1383
|
-
//
|
|
1409
|
+
// is a HARD precondition. Without it, checkpointStore is undefined and there is NO decide path at all
|
|
1410
|
+
// ([2354] A-5:legacy trusted-header /decide 车道已在 5.0.0 退役——本不变量从「防旁路」变成「防死门」,保留)。
|
|
1384
1411
|
const directDoorActive = directApprovalDoor && dgDurable && dgHmacKeys.length > 0 && principalJwtPubkeys.length > 0 && !!principalJwtIss && !!principalJwtAud;
|
|
1385
1412
|
// 🔴 boot invariant (D-G §3): a half-configured direct door is the WORST state (looks on, verifies nothing) — and
|
|
1386
1413
|
// an empty OPERATOR_PRINCIPALS makes isOperator() true-for-all (= cross-tenant escalation). Refuse to start.
|