@sema-agent/server 7.67.0 → 7.69.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 +19 -2
- package/dist/adoption/plan.js +11 -0
- package/dist/approval-card.d.ts +26 -0
- package/dist/approval-card.js +11 -0
- package/dist/approval.js +5 -4
- package/dist/boot/config-center.d.ts +6 -7
- package/dist/boot/config-center.js +31 -8
- package/dist/boot/leader.js +1 -1
- package/dist/boot/resolve-spec.js +1 -1
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +10 -2
- package/dist/budget.js +1 -1
- package/dist/config-catalog.js +20 -1
- package/dist/config-center/apply-effective.d.ts +8 -1
- package/dist/config-center/apply-effective.js +23 -9
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/read-face.d.ts +30 -1
- package/dist/config-center/read-face.js +33 -15
- package/dist/config-center/restart-signal.js +2 -1
- package/dist/config-center/types.d.ts +20 -5
- package/dist/config-provider.js +1 -0
- package/dist/config-types.d.ts +17 -2
- package/dist/config.d.ts +0 -21
- package/dist/config.js +19 -6
- package/dist/http/route-ctx.d.ts +44 -2
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/runs.js +1 -1
- package/dist/http/routes/tasks.js +1 -0
- package/dist/http/routes/workflows.js +15 -7
- package/dist/http/server.d.ts +15 -2
- package/dist/http/server.js +74 -45
- package/dist/leader/wire.d.ts +9 -2
- package/dist/leader/wire.js +6 -6
- package/dist/main.js +6 -5
- package/dist/mcp-tool-faces.js +11 -2
- package/dist/model-compat.d.ts +81 -0
- package/dist/model-compat.js +52 -0
- package/dist/observability/fail-open.d.ts +4 -0
- package/dist/observability/fail-open.js +4 -0
- package/dist/operator-ask.d.ts +46 -0
- package/dist/operator-ask.js +4 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +96 -0
- package/dist/orchestration/workflow-agent-session-index.js +146 -0
- package/dist/orchestration/workflow-journal-entry.d.ts +78 -0
- package/dist/orchestration/workflow-journal-entry.js +20 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +43 -1
- package/dist/orchestration/workflow-notify-journal.js +43 -3
- package/dist/parked-decide.d.ts +115 -5
- package/dist/parked-decide.js +94 -26
- package/dist/plugins/pg-pool.js +8 -0
- package/dist/plugins/store-backend.d.ts +5 -0
- package/dist/plugins/store-backend.js +3 -1
- package/dist/plugins/tidb-pool.js +8 -0
- package/dist/plugins/workflow-journal-store-sql.js +10 -3
- package/dist/plugins/workflow-run-store-sql.d.ts +30 -0
- package/dist/plugins/workflow-run-store-sql.js +40 -0
- package/dist/router/route-orchestration.js +2 -1
- package/dist/run-local.js +1 -1
- package/dist/runs.d.ts +10 -1
- package/dist/runs.js +4 -1
- package/dist/runtime-governance.js +5 -4
- package/dist/task-mcp.d.ts +50 -6
- package/dist/task-mcp.js +71 -35
- package/dist/tool-approval.d.ts +20 -0
- package/dist/tool-approval.js +2 -1
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/engine-notice-wire.d.ts +17 -1
- package/dist/trace/engine-notice-wire.js +7 -1
- package/package.json +4 -4
package/USAGE.md
CHANGED
|
@@ -124,6 +124,19 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
124
124
|
`med,high` 此前静默窄成只剩 `high` 一档,而这根旋钮的全部用途就是宣告高档可用,静默落空 = 高档
|
|
125
125
|
永远上不去。空串 / 纯分隔(`, `)仍与不设同义(不铸键,行为不变);anthropic 腿刻意不铸 compat
|
|
126
126
|
(那条腿不吃 `reasoning_effort`),`GET /v1/config/catalog` 对它诚实回 `null`。
|
|
127
|
+
- 🔴 **`MODEL_THINKING_FORMAT`(7.69.0 新,同族第三根)**:这台网关按哪种拼法收「开/关思考」——
|
|
128
|
+
`openai|openrouter|deepseek|together|zai|qwen|qwen-chat-template` 七词闭集(上 core
|
|
129
|
+
`OpenAICompletionsCompat.thinkingFormat`),未知词同样 **boot 拒启并点名闭集**。缺席 ⇒ 不铸这一键
|
|
130
|
+
(core 按 baseUrl / 模型 id 自行推断,行为逐字不变)。**真需求**:vLLM / Qwen 类**缺省开思考**的网关,
|
|
131
|
+
只有 `qwen-chat-template`(`chat_template_kwargs.enable_thinking=false`)关得掉,而 `MODEL_EXTRA_BODY`
|
|
132
|
+
穿不过引擎的 OpenAI 保留键表 —— 不设它,这类部署每一轮都白烧一段思考且读数上看不出来。它与
|
|
133
|
+
`MODEL_REASONING_EFFORT_LEVELS` 铸进**同一个** `compat` 对象(两根旋钮同时设两键都在)。
|
|
134
|
+
- 🔴 **逐模型声明(目录腿):`models.models[].compat`**(settings-schema ≥1.10.0;7.69.0 消费)——
|
|
135
|
+
同一张 `OpenAICompletionsCompat` 五键(`thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
|
|
136
|
+
`requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`)可以**按模型**写在
|
|
137
|
+
`config.d/models.json` 或配置控制面的目录条目上,逐键盖过上面两根 env 缺省(BL-8:没写的键仍继承 env)。
|
|
138
|
+
**坏形/坏词/未知键 ⇒ 丢掉这条 `compat` 声明、模型本体保留**,并打一条 `model_compat_dropped`
|
|
139
|
+
点名 warn(引擎回落到自己的推断 = 今天的行为);一条写坏的声明**不会**让这只模型或整套目录消失。
|
|
127
140
|
- 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
|
|
128
141
|
- **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens**,默认 0 = `costUsd` 读 0)。云模型(如 review-gw 的 deepseek-v4-flash)必须设,否则 spend 恒为 $0;本地自托管(qwen)留 0 即对(无 per-token 外部花费)。字段名是 `costUsd`,**非美元计价的网关要先折算**(如 DeepSeek 官方 CNY ÷ 汇率)。配置控制面管的模型走 `CenterModel.cost`(中心存价、不存 secret)。
|
|
129
142
|
- 成本计量**自动开**:从 config 的 `model.cost`(per-1M 绝对 USD)注入 `pricing`,core 算出权威的整数 `costMicroUsd`(避免浮点累计误差)。`/metrics` 新增:`model_cost_micro_usd_total{model}`(覆盖所有 brain 调用=主任务+异步+council 子任务的总花费)、`brain_first_token_ms`(网关 hang 早警)、`brain_call_latency_ms`、`tool_calls_total{name,ok}`、`budget_exceeded_total{code}`、`cost_quota_rejected_total`、`degraded_total{reason}`(1.40 降级)。
|
|
@@ -638,8 +651,12 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
638
651
|
① **本机 env 显式恒赢**(逐键判)——env 是这台机器的部署主权,下发值不会静默盖掉它,被忽略的键留一条
|
|
639
652
|
`sema_registry_read_face_env_wins` warn;② **坏值整域拒**——任何一个成员过不了引擎自己的校验器就整域不落、
|
|
640
653
|
保留 env 前值 + `sema_registry_read_face_invalid` warn(半应用一份 READ 姿态比不应用更危险);
|
|
641
|
-
③
|
|
642
|
-
|
|
654
|
+
③ **一席热、三席重启**(server 7.69.0 起,引擎侧交付了 read-face 的活体座席):`face` 是**热换席** ——
|
|
655
|
+
下发或撤回一个档位,下一拍 refresh(≤60s,或手动 `POST /v1/admin/config/refresh`)当场换进全部 Runner,
|
|
656
|
+
**不重启、也不再签 `read-face` 重启理由**(已在飞的任务保留它准备时的档位,新任务读新档);三个 deny 键
|
|
657
|
+
(`denyPatterns`/`denyBuiltinTiers`/`denyBuiltinExclude`)**仍是 boot-baked**(core 的可换席闭集只收了
|
|
658
|
+
`readFace` 一席),下发改动仍经 `/health` 的 `restart.reasons` 带 `read-face` 通告、由编排器重启兑现。
|
|
659
|
+
当前生效档可从 `GET /v1/capabilities` 的 `readFace` 位读
|
|
643
660
|
(`"open"`/`"roots"`/`null`=本部署未钉,引擎默认当家;deny 表内容属策略内容,**不上**能力面);
|
|
644
661
|
**来源**(env / center / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
|
|
645
662
|
⚠️ 本地 `config.d/` 车道今天**接不上**这一域(registry-core 的可移植域表里没有它),只有
|
package/dist/adoption/plan.js
CHANGED
|
@@ -223,6 +223,17 @@ export const REBIND_LEGS = [
|
|
|
223
223
|
residualKey: undefined,
|
|
224
224
|
why: "resume 重放日志的 scope 守卫列 —— 与 run 行**同事务**(错配会让 load 守卫拒掉整条 resume)",
|
|
225
225
|
},
|
|
226
|
+
{
|
|
227
|
+
leg: "workflow_agent_session#scope",
|
|
228
|
+
table: "workflow_agent_session",
|
|
229
|
+
kind: "bulk-rebind",
|
|
230
|
+
action: "bucket-rebind",
|
|
231
|
+
columns: ["scope"],
|
|
232
|
+
matchColumn: "scope",
|
|
233
|
+
encoding: "verbatim",
|
|
234
|
+
residualKey: undefined,
|
|
235
|
+
why: "S-185 workflow 出身 park 的 join 索引行的租户列(值 = 那条 run 的 `scope`,与 workflow_run/journal 同一个字节)——与它们**同族同事务**迁:不迁 ⇒ 索引行留在旧身份名下,而 run 行已迁,一次收编后人对这条 park 的批准结构上投递不出去",
|
|
236
|
+
},
|
|
226
237
|
{
|
|
227
238
|
leg: "workflow_resume_claim#scope",
|
|
228
239
|
table: "workflow_resume_claim",
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -328,6 +328,26 @@ declare const DenialLimitFallbackSchema: z.ZodObject<{
|
|
|
328
328
|
autoDenyAfterMs: z.ZodNumber;
|
|
329
329
|
}, z.core.$strict>;
|
|
330
330
|
export type DenialLimitFallback = z.infer<typeof DenialLimitFallbackSchema>;
|
|
331
|
+
/** S-185(core 7.10.0 [ref])—— **分类器为什么答不了**。`cause` 是 core 的闭三词
|
|
332
|
+
* (`AUTO_MODE_UNAVAILABLE_CAUSES` = error | timeout | breaker_open),本仓**刻意不枚举**它:词表的单一
|
|
333
|
+
* 属主在 core,抄一份的形会在 core 加词那天把新词静默吞成缺席 —— 而丢的正是「这次不可用是新出现的那
|
|
334
|
+
* 一类」这条信息(与 `AskRequest.origin` 顶注同一条纪律)。判据只到「非空串」这一层形门。
|
|
335
|
+
* `.strict()` + 单成员必填:core 将来给这只对象加成员时本读面当场判假、**整键不铸**,新成员必须由人
|
|
336
|
+
* 处置过才上 wire;而一个**没有 cause** 的空壳等于说「分类器坏了但说不出为什么」,比不说更坏。 */
|
|
337
|
+
declare const ClassifierUnavailableSchema: z.ZodObject<{
|
|
338
|
+
cause: z.ZodString;
|
|
339
|
+
}, z.core.$strict>;
|
|
340
|
+
export type ClassifierUnavailable = z.infer<typeof ClassifierUnavailableSchema>;
|
|
341
|
+
/**
|
|
342
|
+
* `AskRequest.classifierUnavailable`(core 7.10.0 [ref])的**边界窄读** —— 活卡帧与 `card_json` 的唯一
|
|
343
|
+
* 铸造点(与 {@link readDenialLimitFallback} 同款分工),两面结构性同值。
|
|
344
|
+
*
|
|
345
|
+
* 🔴 **缺席不是断言**:缺席同时覆盖「分类器答上了」「这只 ask 没资格走分类器」「本部署没接分类器」
|
|
346
|
+
* 三形(core d.ts 逐字:read presence, never absence),消费端禁读成「分类器好着呢」。
|
|
347
|
+
* 🔴 **echo-only**:server 不据它做任何裁决 —— 熔断/超时的处置全在 core 的分类器站,据本键在本仓自铸
|
|
348
|
+
* 第二套回落判据就是同一语义面两个写者(源头修复纪律)。
|
|
349
|
+
*/
|
|
350
|
+
export declare function readClassifierUnavailable(req: unknown): ClassifierUnavailable | undefined;
|
|
331
351
|
/**
|
|
332
352
|
* `AskRequest.denialLimitFallback`(core 7.4.0 [ref])的**边界窄读** —— 活卡帧与 `card_json` 的唯一铸造点
|
|
333
353
|
* (与 {@link readRuleOffersAbsence} / {@link readProbeCause} 同款分工),两面结构性同值。
|
|
@@ -422,6 +442,9 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
422
442
|
}>;
|
|
423
443
|
autoDenyAfterMs: z.ZodNumber;
|
|
424
444
|
}, z.core.$strict>>;
|
|
445
|
+
classifierUnavailable: z.ZodOptional<z.ZodObject<{
|
|
446
|
+
cause: z.ZodString;
|
|
447
|
+
}, z.core.$strict>>;
|
|
425
448
|
probeCause: z.ZodOptional<z.ZodObject<{
|
|
426
449
|
code: z.ZodString;
|
|
427
450
|
roots: z.ZodObject<{
|
|
@@ -532,6 +555,9 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
532
555
|
}>;
|
|
533
556
|
autoDenyAfterMs: z.ZodNumber;
|
|
534
557
|
}, z.core.$strict>>;
|
|
558
|
+
classifierUnavailable: z.ZodOptional<z.ZodObject<{
|
|
559
|
+
cause: z.ZodString;
|
|
560
|
+
}, z.core.$strict>>;
|
|
535
561
|
probeCause: z.ZodOptional<z.ZodObject<{
|
|
536
562
|
code: z.ZodString;
|
|
537
563
|
roots: z.ZodObject<{
|
package/dist/approval-card.js
CHANGED
|
@@ -237,6 +237,14 @@ const DenialLimitFallbackSchema = z
|
|
|
237
237
|
const _denialLimitFallbackConformance = [true, true];
|
|
238
238
|
void _denialLimitFallbackConformance;
|
|
239
239
|
const DenialLimitFallbackEnvelopeSchema = z.object({ denialLimitFallback: DenialLimitFallbackSchema.optional() });
|
|
240
|
+
const ClassifierUnavailableSchema = z.object({ cause: z.string().min(1).max(MAX_IDENT) }).strict();
|
|
241
|
+
const _classifierUnavailableConformance = [true];
|
|
242
|
+
void _classifierUnavailableConformance;
|
|
243
|
+
const ClassifierUnavailableEnvelopeSchema = z.object({ classifierUnavailable: ClassifierUnavailableSchema.optional() });
|
|
244
|
+
export function readClassifierUnavailable(req) {
|
|
245
|
+
const parsed = ClassifierUnavailableEnvelopeSchema.safeParse(req);
|
|
246
|
+
return parsed.success ? parsed.data.classifierUnavailable : undefined;
|
|
247
|
+
}
|
|
240
248
|
export function readDenialLimitFallback(req) {
|
|
241
249
|
const parsed = DenialLimitFallbackEnvelopeSchema.safeParse(req);
|
|
242
250
|
return parsed.success ? parsed.data.denialLimitFallback : undefined;
|
|
@@ -261,6 +269,7 @@ export const ApprovalCardSchema = z
|
|
|
261
269
|
ruleOffers: z.array(RuleOfferSchema).max(MAX_RULE_OFFERS).optional(),
|
|
262
270
|
ruleOffersAbsence: RuleOffersAbsenceSchema.optional(),
|
|
263
271
|
denialLimitFallback: DenialLimitFallbackSchema.optional(),
|
|
272
|
+
classifierUnavailable: ClassifierUnavailableSchema.optional(),
|
|
264
273
|
probeCause: ProbeCauseSchema.optional(),
|
|
265
274
|
ruleEvidence: RuleEvidenceSchema.optional(),
|
|
266
275
|
fromSubagent: z.literal(true).optional(),
|
|
@@ -303,6 +312,7 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
303
312
|
const ruleEvidence = readRuleEvidence(req);
|
|
304
313
|
const ruleOffersAbsence = readRuleOffersAbsence(req);
|
|
305
314
|
const denialLimitFallback = readDenialLimitFallback(req);
|
|
315
|
+
const classifierUnavailable = readClassifierUnavailable(req);
|
|
306
316
|
const toolCallId = clip(source.toolCallId, MAX_IDENT);
|
|
307
317
|
const sourceTaskId = clip(source.sourceTaskId, MAX_IDENT);
|
|
308
318
|
const sourceAgentName = clip(source.sourceAgentName, MAX_AGENT_NAME);
|
|
@@ -325,6 +335,7 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
325
335
|
...(source.ruleOffers !== undefined && source.ruleOffers.length > 0 ? { ruleOffers: source.ruleOffers.map(copyRuleOffer) } : {}),
|
|
326
336
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
327
337
|
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
338
|
+
...(classifierUnavailable !== undefined ? { classifierUnavailable } : {}),
|
|
328
339
|
...(source.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
329
340
|
...(sourceTaskId !== undefined ? { sourceTaskId } : {}),
|
|
330
341
|
...(sourceAgentName !== undefined ? { sourceAgentName } : {}),
|
package/dist/approval.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "@sema-agent/core";
|
|
2
|
+
import { operatorAsk } from "./operator-ask.js";
|
|
2
3
|
import { isExplicitPosture } from "./posture-source.js";
|
|
3
4
|
export function hasOperatorGateIntent(config) {
|
|
4
5
|
return (config.approvalRequire.length > 0 ||
|
|
@@ -51,11 +52,11 @@ export function createDurableAskPolicy(opts) {
|
|
|
51
52
|
if (deny(toolName))
|
|
52
53
|
return { action: "deny", message: `tool "${req.toolName}" is denied by policy` };
|
|
53
54
|
if (neverAuto(toolName))
|
|
54
|
-
return {
|
|
55
|
+
return operatorAsk(`APPROVAL_NEVER_AUTO(${toolName})`, `human approval required to run "${req.toolName}"`);
|
|
55
56
|
if (require(toolName)) {
|
|
56
57
|
const remEarly = req.budget?.resourceRemainingMicroUsd;
|
|
57
58
|
if (remEarly !== undefined && remEarly <= 0) {
|
|
58
|
-
return
|
|
59
|
+
return operatorAsk("RESOURCE_SUSPEND budget exhausted", `durable resource budget exhausted — human approval required to run "${req.toolName}"`);
|
|
59
60
|
}
|
|
60
61
|
if (opts.exempt && toolName !== "AskUserQuestion") {
|
|
61
62
|
return (async () => {
|
|
@@ -81,13 +82,13 @@ export function createDurableAskPolicy(opts) {
|
|
|
81
82
|
function decideRequired(req, toolName) {
|
|
82
83
|
const rem = req.budget?.resourceRemainingMicroUsd;
|
|
83
84
|
if (rem !== undefined && rem <= 0) {
|
|
84
|
-
return
|
|
85
|
+
return operatorAsk("RESOURCE_SUSPEND budget exhausted", `durable resource budget exhausted — human approval required to run "${req.toolName}"`);
|
|
85
86
|
}
|
|
86
87
|
if (toolName !== "AskUserQuestion" && budget > 0 && autoApproved < budget) {
|
|
87
88
|
autoApproved++;
|
|
88
89
|
return { action: "allow" };
|
|
89
90
|
}
|
|
90
|
-
return {
|
|
91
|
+
return operatorAsk(`APPROVAL_REQUIRE(${toolName})`, `approval required to run "${req.toolName}"`);
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
//# sourceMappingURL=approval.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CenterPromptSource, type
|
|
1
|
+
import { CenterPromptSource, type SwappableDeps } from "@sema-agent/core";
|
|
2
2
|
import { buildPricing } from "../budget.js";
|
|
3
3
|
import { type PromptsDomainFaces } from "../prompts-domain-validate.js";
|
|
4
4
|
import { type Scenario, type ScenarioDeps, type ScenarioDetail } from "../capabilities/scenarios.js";
|
|
@@ -42,16 +42,15 @@ export interface ConfigCenterRuntime {
|
|
|
42
42
|
/** 60s refresh cadence + boot-deferred 到货续接。
|
|
43
43
|
* `limitSync`([ref] 批1)= 恒构造的两只限额座的换代口:commit 之后**零 await 内**推一把,与 pricing /
|
|
44
44
|
* keyResolver 那两件派生重建同族(缺席 = 这条腿本部署没装配,例如 run-local)。
|
|
45
|
-
* `
|
|
46
|
-
*
|
|
45
|
+
* `swapRunnerDeps`(core 7.10.0 `Runner.swapDeps` 一扇门;5.49 起的 `swapModels` 口在 7.10.0 退役、
|
|
46
|
+
* 本参数同语义改名)= **可换席**的换装口:模型面 plane 变更候选在 commit 前喂给全部 boot Runner
|
|
47
|
+
* 原子换代(在场 ⇒ models-tiers defer/restart 臂退役,缺席 ⇒ 旧 defer 行为逐字保留);S-174 起
|
|
48
|
+
* read **face** 席也走同一只函数(见 refresh 拍里的 `applyCenterReadFace` 调用点)。 */
|
|
47
49
|
startRefreshLoop(a: {
|
|
48
50
|
runnerTierFrozen: boolean;
|
|
49
51
|
pricing: ReturnType<typeof buildPricing>;
|
|
50
52
|
limitSync?: LimitSync;
|
|
51
|
-
|
|
52
|
-
models: Record<string, Model>;
|
|
53
|
-
tiers: Record<string, string>;
|
|
54
|
-
}) => void;
|
|
53
|
+
swapRunnerDeps?: (next: SwappableDeps) => void;
|
|
55
54
|
}): void;
|
|
56
55
|
/** [ref]-2:停刷新环(hardShutdown 收尾链;幂等,未起环时 no-op)。 */
|
|
57
56
|
stopRefreshLoop(): void;
|
|
@@ -328,7 +328,8 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
|
-
const
|
|
331
|
+
const readFaceBaseline = { ...(config.readFace !== undefined ? { readFace: config.readFace } : {}), readFaceSource: config.readFaceSource };
|
|
332
|
+
const readFaceEnvHeld = applyCenterReadFace(config, effective ?? { version: 0, updatedAt: "", models: { models: [], roles: {} } }, effective ? logger : undefined).envHeld;
|
|
332
333
|
if (!modelReadyState.ready)
|
|
333
334
|
logger.warn("model_roster_pending", {
|
|
334
335
|
note: "CONFIG_REQUIRE_ROSTER=true and no center roster has landed yet — the env MODEL_ID is treated as a PLACEHOLDER, so billable submissions 503 (and /health reports ready:false) until the first effective pull lands ≥1 enabled model. Unset the knob if this worker's env model is authoritative.",
|
|
@@ -341,6 +342,26 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
341
342
|
...(readFaceEnvHeld.length > 0 ? { readFaceEnvHeld } : {}),
|
|
342
343
|
...(mcpRevocationWired && mcpRevocations.hasBaseline() ? { mcpRevocationLive: true } : {}),
|
|
343
344
|
});
|
|
345
|
+
const applyLiveReadFace = (eff, swap, phase) => {
|
|
346
|
+
const before = config.readFace;
|
|
347
|
+
const beforeSource = config.readFaceSource;
|
|
348
|
+
const { faceChanged } = applyCenterReadFace(config, eff, logger, { phase: "live", baseline: readFaceBaseline });
|
|
349
|
+
if (!faceChanged)
|
|
350
|
+
return;
|
|
351
|
+
if (swap === undefined) {
|
|
352
|
+
logger.warn("read_face_swap_unwired", { face: config.readFace ?? null, phase, note: "the center's read FACE changed but no Runner swap door was wired — the value landed in config while the engine keeps the seat it booted with" });
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
try {
|
|
356
|
+
swap({ readFace: config.readFace });
|
|
357
|
+
logger.info("read_face_swapped", { from: before ?? null, to: config.readFace ?? null, phase, note: "hot seat (core swapDeps) — legs that already prepared keep the face they prepared under" });
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
config.readFace = before;
|
|
361
|
+
config.readFaceSource = beforeSource;
|
|
362
|
+
throw err;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
344
365
|
let ccTimer;
|
|
345
366
|
return {
|
|
346
367
|
providerKind: configProvider?.kind,
|
|
@@ -489,7 +510,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
489
510
|
}
|
|
490
511
|
},
|
|
491
512
|
startRefreshLoop(a) {
|
|
492
|
-
const { runnerTierFrozen, pricing, limitSync,
|
|
513
|
+
const { runnerTierFrozen, pricing, limitSync, swapRunnerDeps } = a;
|
|
493
514
|
if (configProvider) {
|
|
494
515
|
const ccRef = config.configCenter;
|
|
495
516
|
let refreshInFlight = false;
|
|
@@ -555,7 +576,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
555
576
|
}
|
|
556
577
|
else {
|
|
557
578
|
const planeChanged = modelPlaneChanged(appliedPlaneEff, r.effective);
|
|
558
|
-
const planeDeferred =
|
|
579
|
+
const planeDeferred = swapRunnerDeps === undefined && (runnerTierFrozen || planeHasActiveTiers(r.effective)) && planeChanged;
|
|
559
580
|
let applyReport;
|
|
560
581
|
const committed = applyEffective(config, r.effective, logger, {
|
|
561
582
|
teamsOnly: true,
|
|
@@ -566,7 +587,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
566
587
|
applyLedger.recordApply(rep);
|
|
567
588
|
},
|
|
568
589
|
...(planeDeferred ? { deferModelPlane: true } : {}),
|
|
569
|
-
...(
|
|
590
|
+
...(swapRunnerDeps !== undefined && planeChanged ? { swapPlane: swapRunnerDeps } : {}),
|
|
570
591
|
});
|
|
571
592
|
if (!committed)
|
|
572
593
|
return "ok";
|
|
@@ -574,6 +595,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
574
595
|
keyResolver = createKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
575
596
|
limitSync?.sync();
|
|
576
597
|
mcpRevocations.applyEnabledEntries(enabledMcpEntries(r.effective.mcp));
|
|
598
|
+
applyLiveReadFace(r.effective, swapRunnerDeps, "refresh");
|
|
577
599
|
applyLedger.applied(tickGen, r.effective.version);
|
|
578
600
|
if (planeDeferred) {
|
|
579
601
|
logger.warn("models_tiers_plane_deferred", { version: r.effective.version, note: "tier-frozen Runner: the changed model plane (models/roles/tiers/default) is NOT hot-applied — admission stays on the Runner's generation; restart applies the new plane (models-tiers restart signal rides /health)" });
|
|
@@ -596,7 +618,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
596
618
|
}
|
|
597
619
|
const lkgPersisted = await persistLkgDurable(r.effective, r.etag);
|
|
598
620
|
const reasons = restartReasons(effective, r.effective, restartCtx());
|
|
599
|
-
if (
|
|
621
|
+
if (swapRunnerDeps !== undefined) {
|
|
600
622
|
const mi = reasons.indexOf("models-tiers");
|
|
601
623
|
if (mi >= 0)
|
|
602
624
|
reasons.splice(mi, 1);
|
|
@@ -701,7 +723,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
701
723
|
return;
|
|
702
724
|
}
|
|
703
725
|
const planeChangedLate = modelPlaneChanged(appliedPlaneEff, r.effective);
|
|
704
|
-
const planeDeferredLate =
|
|
726
|
+
const planeDeferredLate = swapRunnerDeps === undefined && (runnerTierFrozen || planeHasActiveTiers(r.effective)) && planeChangedLate;
|
|
705
727
|
let applyReportLate;
|
|
706
728
|
const committedLate = applyEffective(config, r.effective, logger, {
|
|
707
729
|
teamsOnly: true,
|
|
@@ -712,13 +734,14 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
712
734
|
applyLedger.recordApply(rep);
|
|
713
735
|
},
|
|
714
736
|
...(planeDeferredLate ? { deferModelPlane: true } : {}),
|
|
715
|
-
...(
|
|
737
|
+
...(swapRunnerDeps !== undefined && planeChangedLate ? { swapPlane: swapRunnerDeps } : {}),
|
|
716
738
|
});
|
|
717
739
|
if (!committedLate)
|
|
718
740
|
return;
|
|
719
741
|
mutateInPlace(pricing, buildPricing(config.models));
|
|
720
742
|
keyResolver = createKeyResolver(config.modelApiKeyEnv, process.env, config.modelApiKeys);
|
|
721
743
|
limitSync?.sync();
|
|
744
|
+
applyLiveReadFace(r.effective, swapRunnerDeps, "boot-deferred");
|
|
722
745
|
applyLedger.applied(lateGen, r.effective.version);
|
|
723
746
|
if (planeDeferredLate)
|
|
724
747
|
logger.warn("models_tiers_plane_deferred", { version: r.effective.version, note: "tier-frozen Runner (env tiers): the late-boot center model plane is NOT hot-applied — restart applies it" });
|
|
@@ -743,7 +766,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
743
766
|
ccEtag = r.etag;
|
|
744
767
|
const lkgPersistedLate = await persistLkgDurable(r.effective, r.etag);
|
|
745
768
|
const reasons = restartReasons(undefined, r.effective, restartCtx());
|
|
746
|
-
if (
|
|
769
|
+
if (swapRunnerDeps !== undefined) {
|
|
747
770
|
const miLate = reasons.indexOf("models-tiers");
|
|
748
771
|
if (miLate >= 0)
|
|
749
772
|
reasons.splice(miLate, 1);
|
package/dist/boot/leader.js
CHANGED
|
@@ -50,7 +50,7 @@ export function createLeaderFace(ctx) {
|
|
|
50
50
|
lockedConfig: governanceSeams.lockedConfig,
|
|
51
51
|
retentionPolicy: governanceSeams.retentionPolicy,
|
|
52
52
|
},
|
|
53
|
-
deploymentPosture: buildDeploymentPostureSeats(config),
|
|
53
|
+
deploymentPosture: () => buildDeploymentPostureSeats(config),
|
|
54
54
|
...(ctx.tracer ? { tracer: ctx.tracer } : {}),
|
|
55
55
|
...(config.usageWindows && ctx.usageWindowStore
|
|
56
56
|
? { usageWindows: config.usageWindows, usageWindowStore: ctx.usageWindowStore }
|
|
@@ -367,7 +367,7 @@ export function createResolveSpec(ctx) {
|
|
|
367
367
|
return merged && config.toolDeferLongtail ? applyLongtailDefer(merged, true) : merged;
|
|
368
368
|
})(cap.tools),
|
|
369
369
|
skills: mergeUserSkills(cap.skills, body.skills, logger),
|
|
370
|
-
mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger),
|
|
370
|
+
mcp: resolveRequestMcp(mcpForScenario(config.mcpServers, scenarioName), body.mcpServers, config, logger, opts?.onNotice !== undefined ? { sessionId: auth?.sessionId, emit: opts.onNotice } : undefined),
|
|
371
371
|
a2a: resolveRequestA2a(a2aForScenario(config.a2aPeers, scenarioName), body.a2aPeers, config, logger),
|
|
372
372
|
promptProvider: centerDecls ? centerPromptProvider(centerDecls, scenarioName) : cap.promptProvider,
|
|
373
373
|
toolPolicy: toolPolicyForHands(hands, durableEnabled
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { type WorkflowRun, type WorkflowRunStore } from "@sema-agent/core";
|
|
15
15
|
import type { ServiceConfig } from "../config.js";
|
|
16
16
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
17
|
+
import { type WorkflowAgentSessionIndex } from "../orchestration/workflow-agent-session-index.js";
|
|
17
18
|
import type { Logger } from "../observability/logger.js";
|
|
18
19
|
import type { Metrics } from "../observability/metrics.js";
|
|
19
20
|
import { type WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js";
|
|
@@ -47,5 +48,6 @@ export declare function createWorkflowOrchestration(ctx: WorkflowOrchestrationCt
|
|
|
47
48
|
finalizeStartedBeforeMs?: number;
|
|
48
49
|
publishTerminalFleetRow?: (id: string, run: WorkflowRun) => void;
|
|
49
50
|
};
|
|
51
|
+
workflowAgentSessionIndex: WorkflowAgentSessionIndex | undefined;
|
|
50
52
|
};
|
|
51
53
|
//# sourceMappingURL=workflow-orchestration.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FileWorkflowRunStore, InMemoryWorkflowRunStore } from "@sema-agent/core";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
4
|
+
import { LocalWorkflowAgentSessionIndex } from "../orchestration/workflow-agent-session-index.js";
|
|
4
5
|
import { FileWorkflowCompletionInbox, InMemoryWorkflowCompletionInbox, resolveCompletionRoute } from "../orchestration/workflow-completion-inbox.js";
|
|
5
6
|
import { FileWorkflowNotifyJournalStore, JournalingWorkflowRunStore, WorkflowNotifyGate } from "../orchestration/workflow-notify-journal.js";
|
|
6
7
|
import { SubagentSteerRegistry } from "../orchestration/subagent-steer.js";
|
|
@@ -54,8 +55,15 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
54
55
|
})
|
|
55
56
|
: undefined;
|
|
56
57
|
const fleetBus = new FleetEventBus();
|
|
58
|
+
const workflowAgentSessionIndex = config.selfOrchestrationEnabled
|
|
59
|
+
? config.workflowRunStoreBackend === "memory"
|
|
60
|
+
? new LocalWorkflowAgentSessionIndex()
|
|
61
|
+
: (sqlWorkflowRunStore
|
|
62
|
+
? backend.workflowAgentSessionIndex()
|
|
63
|
+
: new LocalWorkflowAgentSessionIndex(join(config.localDataRoot ?? localRoot, "workflows"), undefined, (msg, meta) => logger.warn(msg, meta)))
|
|
64
|
+
: undefined;
|
|
57
65
|
const workflowRunStore = baseWorkflowRunStore
|
|
58
|
-
? new JournalingWorkflowRunStore(baseWorkflowRunStore, workflowNotifyGate, fleetBus)
|
|
66
|
+
? new JournalingWorkflowRunStore(baseWorkflowRunStore, workflowNotifyGate, fleetBus, workflowAgentSessionIndex)
|
|
59
67
|
: undefined;
|
|
60
68
|
const replicaLocalNotifyJournal = workflowNotifyJournal instanceof FileWorkflowNotifyJournalStore;
|
|
61
69
|
const workflowRecoverOpts = {
|
|
@@ -70,7 +78,7 @@ export function createWorkflowOrchestration(ctx) {
|
|
|
70
78
|
return {
|
|
71
79
|
sqlWorkflowRunStore, workflowNotifyJournal, workflowCompletionInbox, deliverWorkflowCompletion,
|
|
72
80
|
workflowNotifyGate, fleetBus, workflowRunStore, workflowJournalStore, outcomeSink,
|
|
73
|
-
workflowAgentRegistry, subagentSteerRegistry, workflowRecoverOpts,
|
|
81
|
+
workflowAgentRegistry, subagentSteerRegistry, workflowRecoverOpts, workflowAgentSessionIndex,
|
|
74
82
|
};
|
|
75
83
|
}
|
|
76
84
|
//# sourceMappingURL=workflow-orchestration.js.map
|
package/dist/budget.js
CHANGED
|
@@ -119,7 +119,7 @@ export function createTracer(metrics, costQuota, modelUsage, fleetUsage, fleetLe
|
|
|
119
119
|
else if (e.kind.startsWith("compaction.")) {
|
|
120
120
|
metrics.inc("compaction_events_total", { outcome: e.kind.slice("compaction.".length), trigger: e.trigger ?? "none" });
|
|
121
121
|
}
|
|
122
|
-
else if (e.kind === "permission.persisted_rule_allowed" || e.kind === "permission.rule_store_unreadable") {
|
|
122
|
+
else if (e.kind === "permission.persisted_rule_allowed" || e.kind === "permission.rule_store_unreadable" || e.kind === "permission.read_only_allowed") {
|
|
123
123
|
metrics.inc("permission_rule_events_total", { event: e.kind.slice("permission.".length) });
|
|
124
124
|
}
|
|
125
125
|
};
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
|
+
import { THINKING_FORMATS } from "./model-compat.js";
|
|
3
|
+
import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
|
|
2
4
|
export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
3
5
|
export const CATALOG_TIMINGS = ["hot", "restart", "deploy", "inert"];
|
|
4
6
|
export const CATALOG_DANGER_AXES = ["security", "billing", "data"];
|
|
@@ -24,7 +26,14 @@ const cLimits = (path, over = {}) => ({
|
|
|
24
26
|
});
|
|
25
27
|
const cGov = (path, over = {}) => ({ domain: "governance", path, centerKey: path, precedence: "center-wins", timing: "hot", ...over });
|
|
26
28
|
const cModels = (path) => ({ domain: "models", path, centerKey: "models", precedence: "center-wins", timing: "models-plane" });
|
|
27
|
-
const cRead = (path, centerKey) => ({
|
|
29
|
+
const cRead = (path, centerKey, over = {}) => ({
|
|
30
|
+
domain: "readFace",
|
|
31
|
+
path,
|
|
32
|
+
centerKey,
|
|
33
|
+
precedence: "env-wins",
|
|
34
|
+
...(CENTER_READ_FACE_LIVE_KEYS.has(path) ? { timing: "hot" } : { timing: "restart", restartSlice: "read-face" }),
|
|
35
|
+
...over,
|
|
36
|
+
});
|
|
28
37
|
const SEC = ["security"];
|
|
29
38
|
const BILL = ["billing"];
|
|
30
39
|
const DATA = ["data"];
|
|
@@ -284,6 +293,16 @@ export const CONFIG_CATALOG = [
|
|
|
284
293
|
},
|
|
285
294
|
}),
|
|
286
295
|
r("MODEL_ROUTER_ID", "modelPlane", "string", "leader 路由分类模型覆写", { derivedDefaultNote: "缺席 ⇒ MODEL_CHEAP_ID,再退 default", danger: BILL }),
|
|
296
|
+
r("MODEL_THINKING_FORMAT", "modelPlane", "enum", "本网关收「开/关思考」的拼法(上 core OpenAICompletionsCompat.thinkingFormat;缺省开思考的 vLLM/Qwen 网关只有 qwen-chat-template 关得掉;anthropic 腿不铸此键;未知词拒启)", {
|
|
297
|
+
enumValues: [...THINKING_FORMATS],
|
|
298
|
+
derivedDefaultNote: "缺席 ⇒ 不铸此键,core 按 baseUrl/模型 id 自行推断(默认 openai 的 reasoning_effort)",
|
|
299
|
+
resolve: (c) => {
|
|
300
|
+
const compat = c.model.compat;
|
|
301
|
+
if (compat === undefined || !("thinkingFormat" in compat))
|
|
302
|
+
return null;
|
|
303
|
+
return compat.thinkingFormat ?? null;
|
|
304
|
+
},
|
|
305
|
+
}),
|
|
287
306
|
r("MODEL_VERIFIER", "modelPlane", "string", "verify 门的异构 verifier(须命中目录;typo 回 default 并 boot warn=自评自批)", { derivedDefaultNote: "缺席/不在目录 ⇒ default(=implementer,去相关性失效警告)" }),
|
|
288
307
|
r("MODEL_VISION", "modelPlane", "boolean", "主模型 vision 声明(false 让 model_no_vision 预检真拦)", { staticDefault: "true", resolve: (c) => (Array.isArray(c.model.input) ? c.model.input.includes("image") : true), center: cModels("models[].vision") }),
|
|
289
308
|
r("MYSQL_DATABASE", "store", "string", "MySQL 协议库名(选中该引擎时必填)", { danger: DATA }),
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EffectiveConfig application — mutate the boot `ServiceConfig` OVER with a pulled `EffectiveConfig`
|
|
3
|
+
* (models/roles/tiers/projects `applyEffective`, the boot-only approval gate + runtime limit residue
|
|
4
|
+
* `applyRuntimeGates`, the per-request-hot governance baton `applyRuntimeHot`) plus their shared helpers
|
|
5
|
+
* (`toModel`, `mutateInPlace`, `resolveDefaultModelName`, `logEffectiveDiff`). Split out of `facade.ts`
|
|
6
|
+
* ([ref] A13, internal-lossless — the facade re-exports every symbol below unchanged).
|
|
7
|
+
*/
|
|
1
8
|
import type { Model } from "@sema-agent/core";
|
|
2
9
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
10
|
import { type SealedKeyOpener } from "../sealed-key.js";
|
|
@@ -23,7 +30,7 @@ export declare function mutateInPlace<V>(target: Record<string, V>, source: Reco
|
|
|
23
30
|
* 🔴 只活在内存判定面:不进 wire(`/v1/models` 只发布尔)、不进日志(warn 打的是原始坏值的类型标记)。
|
|
24
31
|
*/
|
|
25
32
|
export declare const UNPARSABLE_AT_MODEL_ALLOWLIST_ENTRY = "\0<unparsable-at-model-allowlist-entry>";
|
|
26
|
-
/** 返回值=是否 COMMIT(false 两臂:[ref]③ 世代序 CAS 拒绝 /
|
|
33
|
+
/** 返回值=是否 COMMIT(false 两臂:[ref]③ 世代序 CAS 拒绝 / 7.10.0 swapPlane 换装拒绝——两臂都在
|
|
27
34
|
* commit 之前,活配置零触碰)。caller 收到 false 必须把**同一世代的旁路消费**(prompts 采用/pricing/
|
|
28
35
|
* keyResolver/etag 推进/LKG 落盘)一并跳过——否则 applyEffective 拒了主面、旁路却半应用同一个被拒
|
|
29
36
|
* 世代,混合世代从侧门回来。首次 apply(live 未登记)恒 true。 */
|
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import { isThinkingLevel } from "@sema-agent/core";
|
|
2
1
|
import { resolveActiveTiers, SEALED_BOX_ALG } from "@sema-agent/settings-schema";
|
|
3
2
|
import { sealedKeyPoison } from "../sealed-key.js";
|
|
4
3
|
import { applyAutoCompactWindow, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
|
|
4
|
+
import { buildModelCompat, readModelCompat } from "../model-compat.js";
|
|
5
5
|
import { validateCommandRules } from "../runtime-governance.js";
|
|
6
6
|
import { projectCollabToWorkflows, registerCollabWorkflows } from "../capabilities/collab-workflows.js";
|
|
7
7
|
import { stageLimits } from "./stage-limits.js";
|
|
8
8
|
import { BATCH1_LIMIT_KEYS } from "./hot-keys-registry.js";
|
|
9
|
-
const asModelThinking = (v) => (v && isThinkingLevel(v) && v !== "off" ? v : undefined);
|
|
10
9
|
const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
11
|
-
function
|
|
12
|
-
const
|
|
13
|
-
|
|
10
|
+
function envDerivedModel(config) {
|
|
11
|
+
const m = config.envModel;
|
|
12
|
+
if (m === undefined) {
|
|
13
|
+
throw new Error("config.envModel is missing — it is the BL-8 inheritance baseline (the boot env-derived model) and is REQUIRED on ServiceConfig. " +
|
|
14
|
+
"A hand-rolled `as ServiceConfig` fixture must set it to the same object as `model`; there is deliberately no fallback to `config.model` (that fallback IS the bug this seat removes).");
|
|
15
|
+
}
|
|
16
|
+
return m;
|
|
17
|
+
}
|
|
18
|
+
function toModel(m, envDefaults = {}, logger) {
|
|
19
|
+
const thinkingDefault = envDefaults.defaultThinking;
|
|
20
|
+
const declared = readModelCompat(m.compat);
|
|
21
|
+
if (!declared.ok)
|
|
22
|
+
logger?.warn("model_compat_dropped", { model: m.name, issue: declared.issue, hint: "the per-model wire-compat declaration was dropped whole — the engine falls back to its own inference (today's behaviour); fix the entry to make it live" });
|
|
23
|
+
else if (declared.compat !== undefined && m.provider === "anthropic") {
|
|
24
|
+
logger?.warn("model_compat_dropped", { model: m.name, issue: "declared on an anthropic model", hint: "`compat` mirrors OpenAICompletionsCompat — the Anthropic brain reads AnthropicMessagesCompat and never sees these keys; the declaration is dropped" });
|
|
25
|
+
}
|
|
26
|
+
const compat = m.provider === "anthropic" ? undefined : buildModelCompat(declared.ok ? declared.compat : undefined, envDefaults.compat);
|
|
14
27
|
const model = {
|
|
15
28
|
id: m.id,
|
|
16
29
|
name: m.name,
|
|
@@ -29,7 +42,7 @@ function toModel(m, envDefaults = {}) {
|
|
|
29
42
|
: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
30
43
|
params: { ...(m.tier ? { tier: m.tier } : {}), promptCacheFamily: m.provider === "anthropic" ? "input-excludes-cached" : "input-includes-cached" },
|
|
31
44
|
...(thinkingDefault ? { defaultThinking: thinkingDefault } : {}),
|
|
32
|
-
...(
|
|
45
|
+
...(compat ? { compat } : {}),
|
|
33
46
|
...(m.extraBody ? { extraBody: m.extraBody } : {}),
|
|
34
47
|
...(Array.isArray(m.promptGuidance) && m.promptGuidance.length > 0 ? { promptGuidance: m.promptGuidance.map(String) } : {}),
|
|
35
48
|
};
|
|
@@ -97,7 +110,7 @@ export function applyEffective(config, eff, logger, opts = {}) {
|
|
|
97
110
|
logger?.warn("sema_registry_model_plane_swap_refused", {
|
|
98
111
|
version: staged.version,
|
|
99
112
|
error: err instanceof Error ? err.message : String(err),
|
|
100
|
-
note: "the Runner refused the candidate model plane (
|
|
113
|
+
note: "the Runner refused the candidate model plane (swapDeps screens the whole candidate atomically — the current generation stays in force untouched); candidate refused whole, re-judged next poll (/health shows target≠applied while it persists)",
|
|
101
114
|
});
|
|
102
115
|
return false;
|
|
103
116
|
}
|
|
@@ -174,7 +187,8 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
174
187
|
const models = {};
|
|
175
188
|
const modelApiKeyEnv = {};
|
|
176
189
|
const modelApiKeys = {};
|
|
177
|
-
const
|
|
190
|
+
const envModel = envDerivedModel(config);
|
|
191
|
+
const envDefaults = { maxTokens: envModel.maxTokens, contextWindow: envModel.contextWindow, reasoning: envModel.reasoning, charsPerToken: envModel.charsPerToken, defaultThinking: envModel.defaultThinking, compat: envModel.compat };
|
|
178
192
|
for (const m of enabled) {
|
|
179
193
|
const rawBaseUrl = m.baseUrl;
|
|
180
194
|
let safeUrl = rawBaseUrl;
|
|
@@ -194,7 +208,7 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
194
208
|
safeUrl = undefined;
|
|
195
209
|
}
|
|
196
210
|
}
|
|
197
|
-
models[m.name] = toModel(safeUrl === rawBaseUrl ? m : { ...m, baseUrl: safeUrl }, envDefaults);
|
|
211
|
+
models[m.name] = toModel(safeUrl === rawBaseUrl ? m : { ...m, baseUrl: safeUrl }, envDefaults, logger);
|
|
198
212
|
if (m.sealedApiKey) {
|
|
199
213
|
const s = m.sealedApiKey;
|
|
200
214
|
if (s.alg !== SEALED_BOX_ALG) {
|
|
@@ -44,6 +44,6 @@ export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCent
|
|
|
44
44
|
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, } from "./apply-effective.js";
|
|
45
45
|
export { restartReasons, planeHasActiveTiers, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type RestartSignal, } from "./restart-signal.js";
|
|
46
46
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
47
|
-
export { applyCenterReadFace } from "./read-face.js";
|
|
47
|
+
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";
|
|
48
48
|
export type { CenterSkillManifest, CenterMcpServer, CenterA2aPeer, CenterReadFace, EffectiveConfig, ExecutionRuling, SessionMirrorRuling, } from "./types.js";
|
|
49
49
|
//# sourceMappingURL=facade.d.ts.map
|
|
@@ -2,5 +2,5 @@ export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCent
|
|
|
2
2
|
export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, } from "./apply-effective.js";
|
|
3
3
|
export { restartReasons, planeHasActiveTiers, modelPlaneChanged, } from "./restart-signal.js";
|
|
4
4
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
5
|
-
export { applyCenterReadFace } from "./read-face.js";
|
|
5
|
+
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
|
|
6
6
|
//# sourceMappingURL=facade.js.map
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import type { ServiceConfig } from "../config-types.js";
|
|
2
|
+
import type { ReadFaceSource } from "../read-face-posture.js";
|
|
2
3
|
import type { Logger } from "../observability/logger.js";
|
|
3
4
|
import type { CenterReadFace, EffectiveConfig } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* S-174 —— 哪些 center 键是**热换席**。判据**不是本仓的意见**:`KEY_MAP` 的 `cfgKey` 落在 core 的闭集
|
|
7
|
+
* `SWAPPABLE_DEP_SEATS` 里(经 `isSwappableDepSeat` 亲问)⇒ 这个键 refresh 期可换;否则它是 boot-baked
|
|
8
|
+
* 座,只有重启能换。core 加/删可换席 ⇒ 本集合与 `read-face` restart 片的指纹**同时**跟着走,不留第二份
|
|
9
|
+
* 手抄表(restart-signal.ts 读的就是这一只)。
|
|
10
|
+
*/
|
|
11
|
+
export declare const CENTER_READ_FACE_LIVE_KEYS: ReadonlySet<keyof CenterReadFace>;
|
|
12
|
+
/** 本进程的**前 center** READ face 姿态(env / posture / 引擎默认)。`live` 相必带 —— 一次发布**撤回**
|
|
13
|
+
* `face` 键时,活席要还原到它,而不是把上一份发布的档位无限期latch 住(那是这条轴上的 fail-OPEN 方向:
|
|
14
|
+
* 组织把 `open` 撤回之后,worker 还在 open)。boot 相不需要:那一刻 `config` 上的值就是基线。 */
|
|
15
|
+
export interface ReadFaceBaseline {
|
|
16
|
+
readonly readFace?: "open" | "roots";
|
|
17
|
+
readonly readFaceSource: ReadFaceSource;
|
|
18
|
+
}
|
|
19
|
+
/** 本次调用落在哪一相(见文件头注「一席热、三席重启」)。判别联合而不是可选字段:`baseline` 只在
|
|
20
|
+
* `live` 相有意义,写成可选就等于允许一次「没有基线的 live 调用」——那正是撤回臂会静默失效的形。 */
|
|
21
|
+
export type CenterReadFaceApply = {
|
|
22
|
+
readonly phase: "boot";
|
|
23
|
+
} | {
|
|
24
|
+
readonly phase: "live";
|
|
25
|
+
readonly baseline: ReadFaceBaseline;
|
|
26
|
+
};
|
|
27
|
+
/** 调用回执。`envHeld` 同旧返回值(喂 `RestartSliceCtx.readFaceEnvHeld`);`faceChanged` 是 live 相的
|
|
28
|
+
* 换席信号 —— 真 ⇒ 调用方必须 `swapDeps({ readFace: config.readFace })` 一次,否则 config 与 Runner 分家。 */
|
|
29
|
+
export interface CenterReadFaceReport {
|
|
30
|
+
readonly envHeld: readonly (keyof CenterReadFace)[];
|
|
31
|
+
readonly faceChanged: boolean;
|
|
32
|
+
}
|
|
4
33
|
/**
|
|
5
34
|
* 应用 center 的 readFace 域(**boot-only**:调用点在 boot 拉取之后、`createRunnerDeps` 之前)。
|
|
6
35
|
* 缺席 ⇒ 零触碰。规则见文件头注 ①②③。
|
|
@@ -10,5 +39,5 @@ import type { CenterReadFace, EffectiveConfig } from "./types.js";
|
|
|
10
39
|
* 否则 center 改一个本机永远用不上的键,整片 fleet 会为一次对自己毫无影响的发布滚动重启一遍
|
|
11
40
|
* (codex 交叉复审 finding 2,验真后采纳的那一半)。
|
|
12
41
|
*/
|
|
13
|
-
export declare function applyCenterReadFace(config: ServiceConfig, eff: EffectiveConfig, logger?: Logger):
|
|
42
|
+
export declare function applyCenterReadFace(config: ServiceConfig, eff: EffectiveConfig, logger?: Logger, apply?: CenterReadFaceApply): CenterReadFaceReport;
|
|
14
43
|
//# sourceMappingURL=read-face.d.ts.map
|