@sema-agent/server 7.71.0 → 7.72.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 +2 -1
- package/dist/approval-card.d.ts +16 -17
- package/dist/approval-card.js +0 -9
- package/dist/boot/runner-deps.d.ts +32 -16
- package/dist/brain.js +2 -9
- package/dist/budget.d.ts +12 -1
- package/dist/budget.js +1 -1
- package/dist/config-catalog.js +12 -12
- package/dist/config-center/apply-effective.js +2 -4
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/restart-signal.js +2 -2
- package/dist/config-center/skills-mcp.js +15 -0
- package/dist/config-types.d.ts +68 -16
- package/dist/config-types.js +19 -1
- package/dist/config.d.ts +42 -0
- package/dist/config.js +58 -21
- package/dist/http/durable-run-leg.d.ts +64 -0
- package/dist/http/durable-run-leg.js +63 -0
- package/dist/http/route-ctx.d.ts +14 -0
- package/dist/http/route-ctx.js +3 -0
- package/dist/http/routes/approvals-assistant.js +3 -3
- package/dist/http/routes/capabilities.js +2 -2
- package/dist/http/routes/runs.js +10 -58
- package/dist/http/routes/workflows.js +1 -0
- package/dist/http/server.js +55 -17
- package/dist/main.js +4 -1
- package/dist/observability/fail-open.d.ts +1 -1
- package/dist/observability/fail-open.js +1 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/tool-trace.d.ts +18 -1
- package/dist/observability/tool-trace.js +4 -3
- package/dist/parked-decide.js +3 -2
- package/dist/plugins/e2b-orphan-reclaim.d.ts +4 -4
- package/dist/plugins/e2b-orphan-reclaim.js +18 -20
- package/dist/plugins/store-contracts.d.ts +27 -0
- package/dist/plugins/store-contracts.js +3 -2
- package/dist/run-cancel-context.js +1 -1
- package/dist/run-local.js +4 -1
- package/dist/runs.d.ts +16 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +5 -3
- package/dist/task-a2a.js +3 -2
- package/dist/task-mcp.js +3 -2
- package/dist/tool-approval.d.ts +26 -21
- package/dist/tool-approval.js +2 -2
- package/dist/trace/core-keyset-guard.d.ts +10 -4
- package/dist/trace/project.d.ts +12 -0
- package/dist/trace/project.js +19 -3
- package/dist/usage-analytics.d.ts +9 -1
- package/dist/usage-analytics.js +8 -0
- package/dist/wire-name-bound.d.ts +14 -0
- package/dist/wire-name-bound.js +13 -0
- package/package.json +2 -2
package/USAGE.md
CHANGED
|
@@ -161,7 +161,8 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
161
161
|
**坏形/坏词/未知键 ⇒ 丢掉这条 `compat` 声明、模型本体保留**,并打一条 `model_compat_dropped`
|
|
162
162
|
点名 warn(引擎回落到自己的推断 = 今天的行为);一条写坏的声明**不会**让这只模型或整套目录消失。
|
|
163
163
|
- 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
|
|
164
|
-
- **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens
|
|
164
|
+
- **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens**)。**四键一个都不设 = 未定价**:成本读面**缺席**(`result.stats.costMicroUsd` 整键不出、`costBreakdown` 不产、`/v1/capabilities.pricingConfigured=false`),不是 `$0` —— 消费端应渲成「未知」。设其中任一键 ⇒ 四键在场(未设的按 0)。本地自托管(qwen)真的没有 per-token 外部花费时,请**显式**把四键设成 `0`:那是一次「声明免费」,读面照常发 `costMicroUsd:0` 且 `pricingConfigured=true`。云模型(如 review-gw 的 deepseek-v4-flash)必须设真价,否则 spend 无从计算。字段名是 `costUsd`,**非美元计价的网关要先折算**(如 DeepSeek 官方 CNY ÷ 汇率)。配置控制面管的模型走 `CenterModel.cost`(中心存价、不存 secret;名册行不带 `cost` = 该模型未定价,同上)。
|
|
165
|
+
- **成本天花板要有价才咬得住**:`MAX_TASK_COST_USD` / `MAX_PRINCIPAL_COST_USD` 量的是 `costUsd`,未定价的模型算不出成本 ⇒ 这两道闸**永不触发**。目录里一只带价模型都没有却配了天花板时,boot 会打一条 `cost_ceiling_without_pricing` 告警(不拒启);token 轴的 `MAX_TASK_TOKENS` 不受影响。
|
|
165
166
|
- 成本计量**自动开**:从 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 降级)。
|
|
166
167
|
- **近预算降级 vs 硬闸**:降级(`MODEL_DEGRADE_TO`,到 `atCostFraction` 切便宜模型)是**撑长**预算、任务仍完成(出口质量下降、发 `task.degraded` 事件可告警);硬闸(`maxCostUsd` 全额)仍在,切了便宜模型还超全额 → `limits.max_cost_exceeded` 停。
|
|
167
168
|
- **`METRICS_TOKEN`**(可选,只读):设了它,`GET /metrics`+`/metrics/summary` 接受**它或** `SERVICE_AUTH_TOKEN`。**全 fleet 设同一个值** → 管理端(sema-web,规划名 sema-admin)用**一个** token 拉所有 worker 的指标,**无需持有各 worker 的全权 `SERVICE_AUTH_TOKEN`**(不破坏 secret 边界)。即使外泄也只暴露指标(只读)。
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -328,27 +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
|
-
/**
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
331
|
+
/**
|
|
332
|
+
* ⚰️ **退役成员的形(server 7.69.0–7.71.0 铸过,7.72.0 起永不再铸)** —— core 7.14.0 [ref] 把
|
|
333
|
+
* `classifierUnavailable` 从 ask 侧**整族**删掉(事实改骑 `GateDisposition.denied.cause`),所以本仓
|
|
334
|
+
* 的**铸造**腿(`buildApprovalCard` / 活卡帧 / inbox 行)本批全部摘掉;窄读函数
|
|
335
|
+
* `readClassifierUnavailable` 与对 core 型面的合规钉连同它们的素材一起删(素材没了,钉的两端只剩一端)。
|
|
336
|
+
*
|
|
337
|
+
* 🔴 **这只 schema 本身留着,而且是承重的,不是没删干净**:`ApprovalCardSchema` 是 `.strict()`,
|
|
338
|
+
* 而它同时是 `approval_ask.card_json` 这张**耐久列**的读契约(读侧三个 `safeParse`:重放腿、幂等重入、
|
|
339
|
+
* 对账扫描)。把成员从 strict schema 上删掉 ⇒ 7.69.0–7.71.0 铸下、升级时**仍然 pending** 的那些行
|
|
340
|
+
* 当场判假 ⇒ 「跳过 + 一次 warn」:人手上那张卡在重连后变不回来、幂等重入退成 park。
|
|
341
|
+
* ⇒ 一条耐久投影的 schema **必须读得懂它自己写过的每一版**;写侧只写当前引擎供得出的键,读侧接受
|
|
342
|
+
* 全部历史成员。这不是给退役键开特例,这是 strict + durable 这一组合的内生义务(同样的处置将适用于
|
|
343
|
+
* 未来任何一次键退役)。
|
|
344
|
+
*
|
|
345
|
+
* 形保持原样(单成员 `cause` 必填、`.strict()`):它描述的是**已经写在库里的字节**,不许再动。
|
|
346
|
+
*/
|
|
338
347
|
declare const ClassifierUnavailableSchema: z.ZodObject<{
|
|
339
348
|
cause: z.ZodString;
|
|
340
349
|
}, z.core.$strict>;
|
|
341
350
|
export type ClassifierUnavailable = z.infer<typeof ClassifierUnavailableSchema>;
|
|
342
|
-
/**
|
|
343
|
-
* `AskRequest.classifierUnavailable`(core 7.10.0 [ref])的**边界窄读** —— 活卡帧与 `card_json` 的唯一
|
|
344
|
-
* 铸造点(与 {@link readDenialLimitFallback} 同款分工),两面结构性同值。
|
|
345
|
-
*
|
|
346
|
-
* 🔴 **缺席不是断言**:缺席同时覆盖「分类器答上了」「这只 ask 没资格走分类器」「本部署没接分类器」
|
|
347
|
-
* 三形(core d.ts 逐字:read presence, never absence),消费端禁读成「分类器好着呢」。
|
|
348
|
-
* 🔴 **echo-only**:server 不据它做任何裁决 —— 熔断/超时的处置全在 core 的分类器站,据本键在本仓自铸
|
|
349
|
-
* 第二套回落判据就是同一语义面两个写者(源头修复纪律)。
|
|
350
|
-
*/
|
|
351
|
-
export declare function readClassifierUnavailable(req: unknown): ClassifierUnavailable | undefined;
|
|
352
351
|
/**
|
|
353
352
|
* `AskRequest.denialLimitFallback`(core 7.4.0 [ref])的**边界窄读** —— 活卡帧与 `card_json` 的唯一铸造点
|
|
354
353
|
* (与 {@link readRuleOffersAbsence} / {@link readProbeCause} 同款分工),两面结构性同值。
|
package/dist/approval-card.js
CHANGED
|
@@ -238,13 +238,6 @@ const _denialLimitFallbackConformance = [true, true];
|
|
|
238
238
|
void _denialLimitFallbackConformance;
|
|
239
239
|
const DenialLimitFallbackEnvelopeSchema = z.object({ denialLimitFallback: DenialLimitFallbackSchema.optional() });
|
|
240
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
|
-
}
|
|
248
241
|
export function readDenialLimitFallback(req) {
|
|
249
242
|
const parsed = DenialLimitFallbackEnvelopeSchema.safeParse(req);
|
|
250
243
|
return parsed.success ? parsed.data.denialLimitFallback : undefined;
|
|
@@ -312,7 +305,6 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
312
305
|
const ruleEvidence = readRuleEvidence(req);
|
|
313
306
|
const ruleOffersAbsence = readRuleOffersAbsence(req);
|
|
314
307
|
const denialLimitFallback = readDenialLimitFallback(req);
|
|
315
|
-
const classifierUnavailable = readClassifierUnavailable(req);
|
|
316
308
|
const toolCallId = clip(source.toolCallId, MAX_IDENT);
|
|
317
309
|
const sourceTaskId = clip(source.sourceTaskId, MAX_IDENT);
|
|
318
310
|
const sourceAgentName = clip(source.sourceAgentName, MAX_AGENT_NAME);
|
|
@@ -335,7 +327,6 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
335
327
|
...(source.ruleOffers !== undefined && source.ruleOffers.length > 0 ? { ruleOffers: source.ruleOffers.map(copyRuleOffer) } : {}),
|
|
336
328
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
337
329
|
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
338
|
-
...(classifierUnavailable !== undefined ? { classifierUnavailable } : {}),
|
|
339
330
|
...(source.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
340
331
|
...(sourceTaskId !== undefined ? { sourceTaskId } : {}),
|
|
341
332
|
...(sourceAgentName !== undefined ? { sourceAgentName } : {}),
|
|
@@ -84,26 +84,42 @@ export interface RunnerDepsCtx {
|
|
|
84
84
|
*
|
|
85
85
|
* 本键从「差异键」升进{@link createSharedRunnerDeps 共享基座}。修前它**只**挂在 subRunner 上,主
|
|
86
86
|
* runner 靠 per-task `spec.checkpointStore` 拿店 —— 而 core 的 `run_workflow` 是挂在**主** Runner
|
|
87
|
-
* (`prepare-caps-and-workflow.js:
|
|
88
|
-
* :249 是 7.11.2 的坐标
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* `
|
|
93
|
-
*
|
|
87
|
+
* (`prepare-caps-and-workflow.js:279` 的 `runner: runnerSelf`;**core 7.14.0 亲核行号**,7.12.0 时是
|
|
88
|
+
* :244、B-060 票据引的 :249 是 7.11.2 的坐标 —— 这一格每版都在漂,引用前必须对着装树重读)上的。
|
|
89
|
+
*
|
|
90
|
+
* ⚠️ **B-060 当初写下的病理有一半已被 core 7.14.0 消解,如实订正**([ref] 的 `CheckpointSeat` 重构):
|
|
91
|
+
* · **7.13.0 及以前**:`orchestration/workflow-primitives.js` 只在父侧
|
|
92
|
+
* `parentCheckpointStoreDisabled === true` 时给子代盖 `"disabled"`,**不把父侧真店透传给子代**
|
|
93
|
+
* ⇒ 子代的 `resolveCheckpointStore(spec, deps)` 落到主 runner 这一格 = `undefined`
|
|
94
|
+
* ⇒ `parkLane {capable:false, reasons:["no_checkpoint_store"]}` ⇒ 受门的调用不是 park 而是
|
|
95
|
+
* `delegation.ask_unresolvable{parkLaneExisted:false}` 直接拒(过度拒绝)。
|
|
96
|
+
* · **7.14.0 起**:core 在 prepare 一次铸出 `checkpointSeat = checkpointSeatOf(spec, resolved)`
|
|
97
|
+
* (`prepare-task.js:262`),两条委派车道**逐字复制**它到子代 —— workflow 腿经
|
|
98
|
+
* `parentCheckpointStore: checkpointSeat`(`prepare-caps-and-workflow.js:335`)落到
|
|
99
|
+
* `workflow-primitives.js:80`(现在的条件是「父侧有座 ∧(座是 disabled ∨ 子 spec 没自带店)」⇒
|
|
100
|
+
* **真店也透传**),Task 腿经 `ToolExecuteContext.checkpointStoreForChildren`。
|
|
101
|
+
* ⇒ 「子代拿不到店」这条病路 core 侧已经堵死。**但本席仍然承重**,理由是那只座**本身**就从
|
|
102
|
+
* `resolveCheckpointStore(spec, deps)` 来:主 runner 这一格空着、而 spec 又没带店时,座就是
|
|
103
|
+
* `undefined`,子代照样什么都拿不到。本席不是 core 单源的第二个写者,它就是 core 读的那个 deps 格。
|
|
94
104
|
*
|
|
95
105
|
* 🔴 host 腿的读数判据是**core 侧硬的**,不是本仓的但愿:`resolveCheckpointStore`
|
|
96
|
-
* (`checkpoint-store.js:
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
106
|
+
* (`checkpoint-store.js:370`,7.14.0 亲核)**spec 优先**——`spec.checkpointStore` 在场就用它、
|
|
107
|
+
* `"disabled"` 就是关、只有缺席才落 deps。而本仓 host 腿恒经 `resolveSpec` 的 durableEnabled 分支盖上
|
|
108
|
+
* **同一个实例**,且 `durableEnabled ⇔ checkpointStore !== undefined`(`boot/coordinators.ts:70` 与
|
|
109
|
+
* main.ts 的构造条件同源)⇒ 每一条经 `resolveSpec` 的腿看到的店逐字同一只。
|
|
110
|
+
*
|
|
111
|
+
* 🔴 **core 7.14.0 新加的响亮拒(装配纪律,本仓已合规但必须知道)**:`agents/subagent.js` 在
|
|
112
|
+
* `wantsBackground` 臂上会检查「`ScenarioDeps.checkpointStore`(→ `bg.checkpointStore`)与本 run 的座
|
|
113
|
+
* **不是同一只**」⇒ `configError("config.invalid_checkpoint_store")` **抛**。本仓 main.ts 的
|
|
114
|
+
* `scenarioDeps.checkpointStore` 与本席是同一个 `const`,run-local 两边都不接 ⇒ 两条腿都不触发;
|
|
115
|
+
* 哪天有人给这两处接了不同的店,失败是响亮的而不是静默半场。
|
|
100
116
|
*
|
|
101
117
|
* ⚠️ **如实登记一处不经解析器的直读**(codex 交叉复审提名,亲读 core dist 确认):
|
|
102
|
-
* `core/runner/run-terminal-adoption.js:
|
|
103
|
-
* `
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
118
|
+
* `core/runner/run-terminal-adoption.js:80`(7.14.0 亲核;7.13.0 时是 :74)读的是
|
|
119
|
+
* `runner.deps.checkpointStore` **本身**,不走 `resolveCheckpointStore`,**也不走座** —— 所以 7.14.0
|
|
120
|
+
* 的座重构对它一个字节都没改,本席对它仍是唯一来源。它是「宿主任务 park 时,把没排空的 steer /
|
|
121
|
+
* follow-up 迁移到 park token 上」那一步 —— 修前主 runner 这一格是空的,于是**整段迁移静默不跑**
|
|
122
|
+
* (那些输入随本腿一起丢,只留 `task.user_followup_undrained` 一类通告)。接上之后它开始真的跑。
|
|
107
123
|
*
|
|
108
124
|
* 🔴 这一席是**部署腿级**的答案,不是「哪只 Runner」的属性:一条腿要么有赎回口(HTTP 面的
|
|
109
125
|
* `/v1/approvals/:id/decide` + resume 家族)、于是它的每只 Runner 都该有店;要么没有(`run-local`
|
package/dist/brain.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { adjudicateModelRoute, createAnthropicBrain, createCircuitBreakerBrain, createDegradingBrain, createFailoverBrain, createOpenAIBrain, createRoutingBrain, hasAuthCarrier, resolveRouteCredential, routeRefusalText, } from "@sema-agent/core";
|
|
2
2
|
import { normalizeBaseUrl } from "@sema-agent/core";
|
|
3
|
-
import {
|
|
3
|
+
import { buildDefaultResilience } from "./config-types.js";
|
|
4
4
|
import { resolveModelApiKey } from "./key-resolver.js";
|
|
5
5
|
function isOffDeploymentRoot(baseUrl, config) {
|
|
6
6
|
if (!baseUrl)
|
|
@@ -15,14 +15,7 @@ function degradeDeclaredRoot(fallbackBaseUrl, config) {
|
|
|
15
15
|
return armed ? config.gatewayBaseUrl : (fallbackBaseUrl || config.gatewayBaseUrl);
|
|
16
16
|
}
|
|
17
17
|
export const FAST_FAIL_MAX_RETRIES = 2;
|
|
18
|
-
const DEFAULT_RESILIENCE =
|
|
19
|
-
connectTimeoutMs: 30_000,
|
|
20
|
-
firstTokenTimeoutMs: MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT,
|
|
21
|
-
idleTimeoutMs: 300_000,
|
|
22
|
-
circuitBreaker: false,
|
|
23
|
-
failureThreshold: 5,
|
|
24
|
-
cooldownMs: 30_000,
|
|
25
|
-
};
|
|
18
|
+
const DEFAULT_RESILIENCE = buildDefaultResilience();
|
|
26
19
|
export function createBrain(config, deps = {}) {
|
|
27
20
|
const { fetchImpl, breakerState } = deps;
|
|
28
21
|
const r = config.resilience ?? DEFAULT_RESILIENCE;
|
package/dist/budget.d.ts
CHANGED
|
@@ -11,7 +11,10 @@ import type { QuotaTracker } from "./observability/cost-quota.js";
|
|
|
11
11
|
import type { FleetUsageAccumulator } from "./fleet-client.js";
|
|
12
12
|
import type { FleetLeaseManager } from "./fleet-lease.js";
|
|
13
13
|
import { type PromptManifestTracker } from "./observability/prompt-manifest.js";
|
|
14
|
-
/** ③ Per-model pricing keyed by model id (core falls back to each Model.cost when a model is absent).
|
|
14
|
+
/** ③ Per-model pricing keyed by model id (core falls back to each Model.cost when a model is absent).
|
|
15
|
+
* B-089:`m.cost` 运行期可缺席(未定价的模型不铸这个键 —— `config.ts` 的 `buildModelCostKey`),于是
|
|
16
|
+
* 未定价模型在本表里**没有行**;core 的 `isModelPriced` 两边都读(`pricing[id]` 或 `model.cost`),
|
|
17
|
+
* 两边同时缺席 = 这只模型未定价 ⇒ 成本读面整键缺席,而不是编造的 0。 */
|
|
15
18
|
export declare function buildPricing(models: Record<string, Model>): Record<string, ModelPricing>;
|
|
16
19
|
/**
|
|
17
20
|
* 缝合审 M3:`capabilities.pricingConfigured` 的判据 —— 提成函数放在 pricing 自己的文件里,因为
|
|
@@ -23,6 +26,14 @@ export declare function buildPricing(models: Record<string, Model>): Record<stri
|
|
|
23
26
|
*
|
|
24
27
|
* 同一条论证在同一个字面量里**已经执行过一次**:紧邻的 `restartState: () => configCenter.restartState()`
|
|
25
28
|
* 带着注释「A live getter (not a snapshot) so /health always reads current」—— 兄弟键漏了。本仓第 7 例同形。
|
|
29
|
+
*
|
|
30
|
+
* 🔴 B-089(判据本身,同一条「0 不是缺席」的病):此前判的是「**有没有一项正价**」
|
|
31
|
+
* (`some(v > 0)`),于是一台把四项单价**显式**配成 0 的部署(= 运营者声明这个模型免费)被报成
|
|
32
|
+
* 「没配价目表」—— 正好是本键要消歧的两态被折成同一个 false,消费端把一个可信的 $0 渲成 unknown。
|
|
33
|
+
* 判据改为**表在不在**:与 core [ref] 的 `isModelPriced`(「price table 是否 EXIST」)同向,也与
|
|
34
|
+
* 本键在 wire 契约里的原话(「价目表是否已配」)一致。
|
|
35
|
+
* 三态读回:未定价 ⇒ 空表 ⇒ false(与改前同值);声明免费 ⇒ 有行(全零)⇒ true(**唯一变的一格**);
|
|
36
|
+
* 配了真价 ⇒ true(与改前同值)。
|
|
26
37
|
*/
|
|
27
38
|
export declare function isPricingConfigured(pricing: Record<string, ModelPricing>): boolean;
|
|
28
39
|
/**
|
package/dist/budget.js
CHANGED
|
@@ -10,7 +10,7 @@ export function buildPricing(models) {
|
|
|
10
10
|
return pricing;
|
|
11
11
|
}
|
|
12
12
|
export function isPricingConfigured(pricing) {
|
|
13
|
-
return Object.
|
|
13
|
+
return Object.keys(pricing).length > 0;
|
|
14
14
|
}
|
|
15
15
|
export function cardinalityGuard(cap, onOverflow) {
|
|
16
16
|
const seen = new Set();
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
2
|
import { THINKING_FORMATS } from "./model-compat.js";
|
|
3
|
-
import {
|
|
3
|
+
import { RESILIENCE_DEFAULTS } from "./config-types.js";
|
|
4
4
|
import { webSearchConfigFromEnv } from "./plugins/web-search.js";
|
|
5
5
|
import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
|
|
6
6
|
export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
@@ -246,8 +246,8 @@ export const CONFIG_CATALOG = [
|
|
|
246
246
|
r("MODEL_API_KEY", "modelPlane", "secret", "网关腿共享 API key(per-model 覆写在 center 侧)", { danger: [...SEC, ...BILL] }),
|
|
247
247
|
r("MODEL_AUTO_COMPACT_TOKENS", "modelPlane", "number", "主槽 dual-window 触发阈(1M 窗不再隐式 967000,缺席则用普通触发几何)", { derivedDefaultNote: "缺席 ⇒ 不铸键(core W-33000 几何)" }),
|
|
248
248
|
r("MODEL_CASCADE_LADDER", "modelPlane", "csv", "质量门级联梯(目录名 cheapest→strongest;opt-in per-task cascade:true)", { danger: BILL, configKey: "cascadeLadder" }),
|
|
249
|
-
r("MODEL_CB_COOLDOWN_MS", "modelPlane", "number", "熔断冷却窗(ms)", { staticDefault:
|
|
250
|
-
r("MODEL_CB_FAILURE_THRESHOLD", "modelPlane", "number", "熔断失败阈", { staticDefault:
|
|
249
|
+
r("MODEL_CB_COOLDOWN_MS", "modelPlane", "number", "熔断冷却窗(ms)", { staticDefault: String(RESILIENCE_DEFAULTS.cooldownMs.value) }),
|
|
250
|
+
r("MODEL_CB_FAILURE_THRESHOLD", "modelPlane", "number", "熔断失败阈", { staticDefault: String(RESILIENCE_DEFAULTS.failureThreshold.value) }),
|
|
251
251
|
r("MODEL_CHARS_PER_TOKEN", "modelPlane", "number", "结构估 token 系数(CJK 密集模型建议 2-3;坏值回 core 默认)", { derivedDefaultNote: "缺席 ⇒ core 默认 4" }),
|
|
252
252
|
r("MODEL_CHEAP_AUTO_COMPACT_TOKENS", "modelPlane", "number", "cheap 槽 dual-window 触发阈(主槽值刻意不外溢)", { derivedDefaultNote: "缺席 ⇒ 不铸键" }),
|
|
253
253
|
r("MODEL_CHEAP_CHARS_PER_TOKEN", "modelPlane", "number", "cheap 槽结构估 token 系数", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
|
|
@@ -260,14 +260,14 @@ export const CONFIG_CATALOG = [
|
|
|
260
260
|
r("MODEL_CHEAP_MAX_TOKENS", "modelPlane", "number", "cheap 槽输出上限", { derivedDefaultNote: "缺席 ⇒ 继承主模型", center: cModels("models[].maxTokens") }),
|
|
261
261
|
r("MODEL_CHEAP_REASONING", "modelPlane", "boolean", "cheap 槽 reasoning 声明(三态:缺席继承主模型)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
|
|
262
262
|
r("MODEL_CHEAP_VISION", "modelPlane", "boolean", "cheap 槽 vision 声明(三态:缺席继承主模型;text-only cheap 必设 false 让预检真拦)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
|
|
263
|
-
r("MODEL_CIRCUIT_BREAKER", "modelPlane", "boolean", "模型腿熔断开关", { staticDefault:
|
|
263
|
+
r("MODEL_CIRCUIT_BREAKER", "modelPlane", "boolean", "模型腿熔断开关", { staticDefault: String(RESILIENCE_DEFAULTS.circuitBreaker.value) }),
|
|
264
264
|
r("MODEL_CODE_ROLES", "modelPlane", "csv", "给点名 role 附编码 persona(role 表内名字才生效)"),
|
|
265
|
-
r("MODEL_CONNECT_TIMEOUT_MS", "modelPlane", "number", "连接超时(ms;0=关)", { staticDefault:
|
|
265
|
+
r("MODEL_CONNECT_TIMEOUT_MS", "modelPlane", "number", "连接超时(ms;0=关)", { staticDefault: String(RESILIENCE_DEFAULTS.connectTimeoutMs.value) }),
|
|
266
266
|
r("MODEL_CONTEXT_WINDOW", "modelPlane", "number", "主模型上下文窗声明", { staticDefault: "262144", configKey: "model", resolve: (c) => c.model.contextWindow ?? null, center: cModels("models[].contextWindow") }),
|
|
267
|
-
r("MODEL_COST_CACHE_READ", "modelPlane", "number", "主模型 cache-read 单价(per-1M USD
|
|
268
|
-
r("MODEL_COST_CACHE_WRITE", "modelPlane", "number", "主模型 cache-write 单价", {
|
|
269
|
-
r("MODEL_COST_INPUT", "modelPlane", "number", "主模型 input 单价(NaN 会让成本天花板整条消失 ⇒ 坏值拒启)", {
|
|
270
|
-
r("MODEL_COST_OUTPUT", "modelPlane", "number", "主模型 output 单价", {
|
|
267
|
+
r("MODEL_COST_CACHE_READ", "modelPlane", "number", "主模型 cache-read 单价(per-1M USD;显式 0=声明免费,不是「没配」)", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.cacheRead ?? null, center: cModels("models[].cost.cacheRead") }),
|
|
268
|
+
r("MODEL_COST_CACHE_WRITE", "modelPlane", "number", "主模型 cache-write 单价", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.cacheWrite ?? null, center: cModels("models[].cost.cacheWrite") }),
|
|
269
|
+
r("MODEL_COST_INPUT", "modelPlane", "number", "主模型 input 单价(NaN 会让成本天花板整条消失 ⇒ 坏值拒启)", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.input ?? null, center: cModels("models[].cost.input") }),
|
|
270
|
+
r("MODEL_COST_OUTPUT", "modelPlane", "number", "主模型 output 单价", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.output ?? null, center: cModels("models[].cost.output") }),
|
|
271
271
|
r("MODEL_DEFAULT_THINKING", "modelPlane", "enum", "主模型默认思考档(llm-core ThinkingLevel;off=不设模型级默认;未知词拒启)", { enumValues: ["minimal", "low", "medium", "high", "xhigh", "max", "off"], derivedDefaultNote: "缺席/off ⇒ 不铸 defaultThinking(逐任务档由 spec>role 决定)", resolve: (c) => c.model.defaultThinking ?? null }),
|
|
272
272
|
r("MODEL_DEGRADE_AT_COST_FRACTION", "modelPlane", "number", "预算降级触发比(spent/budget;>1=实际别降级)", { staticDefault: "0.7", danger: BILL }),
|
|
273
273
|
r("MODEL_DEGRADE_ON", "modelPlane", "csv", "反应式降级触发词表(core DegradeReason 全集;拼错拒启)", { center: { domain: "models", path: "degrade", centerKey: "models", precedence: "center-wins", timing: "restart", restartSlice: "degrade-route" } }),
|
|
@@ -275,12 +275,12 @@ export const CONFIG_CATALOG = [
|
|
|
275
275
|
r("MODEL_DEGRADE_TO", "modelPlane", "string", "降级目标模型(设了=开降级;外部网关名不必在目录)", { danger: BILL }),
|
|
276
276
|
r("MODEL_DEGRADE_TO_VISION", "modelPlane", "boolean", "降级目标的 vision 声明(false ⇒ 带图任务丢降级不丢图)", { staticDefault: "false" }),
|
|
277
277
|
r("MODEL_EXTRA_BODY", "modelPlane", "opaque", "生成参数直通 JSON(top_k/logit_bias 等;必须静态 —— 前缀缓存键序)", { danger: SEC }),
|
|
278
|
-
r("MODEL_FIRST_TOKEN_TIMEOUT_MS", "modelPlane", "number", "首 token 超时(ms;0=关 —— 开流不吐字的唯一看门狗;自托管长 prefill 后端按此档留足)", { staticDefault: String(
|
|
278
|
+
r("MODEL_FIRST_TOKEN_TIMEOUT_MS", "modelPlane", "number", "首 token 超时(ms;0=关 —— 开流不吐字的唯一看门狗;自托管长 prefill 后端按此档留足)", { staticDefault: String(RESILIENCE_DEFAULTS.firstTokenTimeoutMs.value) }),
|
|
279
279
|
r("MODEL_FREQUENCY_PENALTY", "modelPlane", "number", "frequency_penalty 直通(压过 EXTRA_BODY 同键)"),
|
|
280
280
|
r("MODEL_GATEWAY_BASEURL", "modelPlane", "url", "openai 兼容网关 base URL(默认 localhost 占位 —— 公开分发不烤内网坐标)", { staticDefault: "http://127.0.0.1:8000/v1", configKey: "gatewayBaseUrl", center: cModels("models[].baseUrl") }),
|
|
281
281
|
r("MODEL_GATEWAY_FALLBACK_URLS", "modelPlane", "url", "网关故障转移地址表(CSV)", { configKey: "gatewayFallbackUrls" }),
|
|
282
282
|
r("MODEL_ID", "modelPlane", "string", "主模型 id(3.0.0 起无出厂缺省,缺 ⇒ 拒启指路)", { configKey: "model", resolve: (c) => c.model.id, center: cModels("models[].id") }),
|
|
283
|
-
r("MODEL_IDLE_TIMEOUT_MS", "modelPlane", "number", "流中静默看门狗(ms;0=关)", { staticDefault:
|
|
283
|
+
r("MODEL_IDLE_TIMEOUT_MS", "modelPlane", "number", "流中静默看门狗(ms;0=关)", { staticDefault: String(RESILIENCE_DEFAULTS.idleTimeoutMs.value) }),
|
|
284
284
|
r("MODEL_MAX_TOKENS", "modelPlane", "number", "主模型输出上限", { derivedDefaultNote: "缺席 ⇒ min(32000, contextWindow)", resolve: (c) => c.model.maxTokens ?? null, center: cModels("models[].maxTokens") }),
|
|
285
285
|
r("MODEL_PRESENCE_PENALTY", "modelPlane", "number", "presence_penalty 直通"),
|
|
286
286
|
r("MODEL_PROVIDER", "modelPlane", "string", "主模型 provider(路由选 brain)", { derivedDefaultNote: "缺席:ANTHROPIC base+cred 同在 ⇒ anthropic,否则 gateway", resolve: (c) => c.model.provider, center: cModels("models[].provider") }),
|
|
@@ -340,7 +340,7 @@ export const CONFIG_CATALOG = [
|
|
|
340
340
|
r("QUESTION_TTL_MS", "approval", "number", "AskUserQuestion 无人应答释放窗(ms)", { derivedDefaultNote: "缺席 ⇒ 出厂窗 300000" }),
|
|
341
341
|
r("RATE_LIMIT_RPM", "limitsHttp", "number", "计费提交每分钟限流(0=关)", { staticDefault: "0", danger: BILL, configKey: "rateLimitPerMin", center: cLimits("rateLimitPerMin") }),
|
|
342
342
|
r("READ_DENY_BUILTIN_EXCLUDE", "orchestration", "csv", "按行名删内建 deny 条目(档位之后生效;绝不削任务显式 deny)", { danger: SEC, configKey: "readDenyBuiltinExclude", center: cRead("denyBuiltinExclude", "readDenyBuiltinExclude") }),
|
|
343
|
-
r("READ_DENY_BUILTIN_TIERS", "orchestration", "csv", "内建 deny 表档位选择(
|
|
343
|
+
r("READ_DENY_BUILTIN_TIERS", "orchestration", "csv", "内建 deny 表档位选择(闭集五档 credentials/shell-history/browser/wallet/agent-config;none=显式空姿态)", { danger: SEC, configKey: "readDenyBuiltinTiers", center: cRead("denyBuiltinTiers", "readDenyBuiltinTiers") }),
|
|
344
344
|
r("READ_DENY_PATTERNS", "orchestration", "csv", "READ 面额外 deny pattern 表", { danger: SEC, configKey: "readDenyPatterns", center: cRead("denyPatterns", "readDenyPatterns") }),
|
|
345
345
|
r("READ_FACE", "orchestration", "enum", "READ 容纳面档位(env 逐键恒赢 center —— 部署主权表达)", { enumValues: ["open", "roots"], danger: SEC, derivedDefaultNote: "缺席 ⇒ 按 posture 派生(S-167):单用户 turnkey(REQUIRE_PRINCIPAL 未设)铸 open;多租户不铸键、core 默认 roots。生效来源见 GET /v1/diagnostics/wiring 的 readFace.source 四词", configKey: "readFace", center: cRead("face", "readFace") }),
|
|
346
346
|
r("REAP_INTERVAL_SEC", "limitsHttp", "number", "reaper 周期(秒)", { staticDefault: "60", configKey: "reapIntervalSec" }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveActiveTiers, SEALED_BOX_ALG } from "@sema-agent/settings-schema";
|
|
2
2
|
import { sealedKeyPoison } from "../sealed-key.js";
|
|
3
|
-
import { applyAutoCompactWindow, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
|
|
3
|
+
import { applyAutoCompactWindow, buildModelCostKey, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
|
|
4
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";
|
|
@@ -37,9 +37,7 @@ function toModel(m, envDefaults = {}, logger) {
|
|
|
37
37
|
...(charsPerTokenOf(m.charsPerToken) !== undefined || envDefaults.charsPerToken !== undefined
|
|
38
38
|
? { charsPerToken: charsPerTokenOf(m.charsPerToken) ?? envDefaults.charsPerToken }
|
|
39
39
|
: {}),
|
|
40
|
-
cost: m.cost
|
|
41
|
-
? { input: m.cost.input, output: m.cost.output, cacheRead: m.cost.cacheRead ?? 0, cacheWrite: m.cost.cacheWrite ?? 0 }
|
|
42
|
-
: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
40
|
+
...buildModelCostKey(m.cost ? { input: m.cost.input, output: m.cost.output, cacheRead: m.cost.cacheRead ?? 0, cacheWrite: m.cost.cacheWrite ?? 0 } : undefined),
|
|
43
41
|
params: { ...(m.tier ? { tier: m.tier } : {}), promptCacheFamily: m.provider === "anthropic" ? "input-excludes-cached" : "input-includes-cached" },
|
|
44
42
|
...(thinkingDefault ? { defaultThinking: thinkingDefault } : {}),
|
|
45
43
|
...(compat ? { compat } : {}),
|
|
@@ -45,5 +45,5 @@ export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, app
|
|
|
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
47
|
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";
|
|
48
|
-
export type { CenterSkillManifest, CenterMcpServer, CenterA2aPeer, CenterReadFace, EffectiveConfig, ExecutionRuling, SessionMirrorRuling, } from "./types.js";
|
|
48
|
+
export type { CenterSkillManifest, CenterMcpServer, CenterA2aPeer, CenterModel, CenterReadFace, EffectiveConfig, ExecutionRuling, SessionMirrorRuling, } from "./types.js";
|
|
49
49
|
//# sourceMappingURL=facade.d.ts.map
|
|
@@ -19,8 +19,8 @@ function enabledOnly(rows) {
|
|
|
19
19
|
return rows.filter((r) => r.enabled !== false);
|
|
20
20
|
}
|
|
21
21
|
function catalogEntryFingerprint(entry) {
|
|
22
|
-
const { cost
|
|
23
|
-
return route;
|
|
22
|
+
const { cost, quotaWeight: _quotaWeight, enabled: _enabled, ...route } = entry;
|
|
23
|
+
return { ...route, costDeclared: cost !== undefined };
|
|
24
24
|
}
|
|
25
25
|
function planeEntriesFingerprint(rows) {
|
|
26
26
|
const enabled = enabledOnly(rows);
|
|
@@ -4,6 +4,7 @@ import { skillContentHash } from "@sema-agent/settings-schema";
|
|
|
4
4
|
import { CONTENT_ORIGIN_WORDS, describeBadContentOrigin, isContentOriginWord } from "../mcp-content-origin.js";
|
|
5
5
|
import { fetchSkillContent } from "./http-client.js";
|
|
6
6
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
7
|
+
import { describeBadWireName, isUsableWireName } from "../wire-name-bound.js";
|
|
7
8
|
export async function applyCenterSkills(baseline, manifest, baseUrl, token, logger, fetchImpl = fetch, diskCacheDir) {
|
|
8
9
|
const { promises: fsp } = await import("node:fs");
|
|
9
10
|
const { join: joinPath } = await import("node:path");
|
|
@@ -84,6 +85,13 @@ export function resolveMcpServers(mcp, logger) {
|
|
|
84
85
|
for (const s of mcp.servers ?? []) {
|
|
85
86
|
if (s.enabled === false)
|
|
86
87
|
continue;
|
|
88
|
+
if (!isUsableWireName(s.name)) {
|
|
89
|
+
logger?.warn("sema_registry_mcp_name_unusable", {
|
|
90
|
+
server: describeBadWireName(s.name),
|
|
91
|
+
note: "server SKIPPED — an MCP server name is its identity (it becomes the tool namespace prefix) and core REFUSES an out-of-bound name at prepare, which would fail every task in this scenario. Shorten the name and republish; sibling servers are unaffected.",
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
87
95
|
const allow = s.allowTools && s.allowTools.length > 0 ? { allowTools: s.allowTools } : {};
|
|
88
96
|
const elicit = s.elicitation === true ? { elicitation: true } : {};
|
|
89
97
|
let contentOrigin = {};
|
|
@@ -144,6 +152,13 @@ export function resolveA2aPeers(a2a, logger) {
|
|
|
144
152
|
for (const p of a2a.peers ?? []) {
|
|
145
153
|
if (p.enabled === false)
|
|
146
154
|
continue;
|
|
155
|
+
if (!isUsableWireName(p.name)) {
|
|
156
|
+
logger?.warn("sema_registry_a2a_name_unusable", {
|
|
157
|
+
peer: describeBadWireName(p.name),
|
|
158
|
+
note: "peer SKIPPED — an A2A peer name is its identity (it lands as the roster's `origin.peer`) and core REFUSES an out-of-bound name at prepare, which would fail every task in this scenario. Shorten the name and republish; sibling peers are unaffected.",
|
|
159
|
+
});
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
147
162
|
const headers = resolveRefs(p.headerRefs, p.name, "header", logger, "sema_registry_a2a_env_missing", "skipping this A2A peer — referenced env var is unset in this service");
|
|
148
163
|
if (headers === null)
|
|
149
164
|
continue;
|
package/dist/config-types.d.ts
CHANGED
|
@@ -56,23 +56,67 @@ export interface RetentionSweepConfig {
|
|
|
56
56
|
*/
|
|
57
57
|
export declare const MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC = 300;
|
|
58
58
|
/**
|
|
59
|
-
* S-
|
|
59
|
+
* S-220 —— **`resilience` 块缺省的唯一属主**(S-202 的单源从一根扩到整族)。
|
|
60
60
|
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
61
|
+
* 这是一张**表**,不是六个常量:缺省长在三条腿上(解析器腿 `config.ts` 的 `numEnv`/`boolEnv` 缺省、
|
|
62
|
+
* 目录腿 `config-catalog.ts` 的 `staticDefault`、`brain.ts` 的 `DEFAULT_RESILIENCE` —— config 对象
|
|
63
|
+
* 没带 resilience 块时的同姿态兜底),而三条腿此前**各写一份字面量**。S-202 只把 `firstTokenTimeoutMs`
|
|
64
|
+
* 一根收成单源,于是剩下五根还在「改一处漏一处 = 目录对外宣告的缺省与真跑的不是同一代」这条病里 ——
|
|
65
|
+
* 按类修的形是把**整族**收进一张表,让三条腿都只许引用它,而不是再加五个同形常量(六个常量 = 六份
|
|
66
|
+
* 要各自记得引用的纪律;一张表 = 一条)。
|
|
65
67
|
*
|
|
66
|
-
*
|
|
67
|
-
* `
|
|
68
|
+
* 每行带自己的 **env 键名** —— 它是这一行的**身份**,给消费门当锚(目录行查名、三腿对账)。
|
|
69
|
+
* ⚠️ 但 `config.ts` / `config-catalog.ts` 的读点仍写**字面** env 名,不写 `RESILIENCE_DEFAULTS.x.env`:
|
|
70
|
+
* D1 双向对账门(`test/config-catalog.test.ts` ⑦)是 AST 扫 `numEnv("NAME", …)` 的字面名来数「src/ 在读
|
|
71
|
+
* 哪些键」,改成间接引用会让这六个键在那道门眼里整体消失(实测:6 个幽灵键 + 动态读棘轮 27→33)——
|
|
72
|
+
* 拿一条真在岗的对账门去换一处写法上的统一是亏的。键名的同源由那道门自己咬:src/ 字面名 ⟺ 目录行名
|
|
73
|
+
* ⟺ 本表的 `env`(S-220 那一格按 `env` 查目录行,查不到当场红),三方互钉,没有静默漂移的空间。
|
|
74
|
+
* `satisfies Readonly<Record<keyof ServiceConfigFlat["resilience"], …>>` 双向咬:块上加字段而表里没行
|
|
75
|
+
* ⇒ tsc 红(加字段的人被迫当场回答「它的出厂缺省是多少、归哪个 env」);表里多一行块上没有的键同样红。
|
|
68
76
|
*
|
|
69
|
-
*
|
|
70
|
-
* (
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
77
|
+
* ⚠️ 语义面**一个数字都没动**:`0 = 关`(三个看门狗)与「熔断默认关」照旧,env 恒赢。
|
|
78
|
+
* 首 token 那一行的 600_000 由来(S-202 clay 裁 A):自托管后端在长 prefill(大上下文 / 冷缓存 /
|
|
79
|
+
* 排队)下首字合理地要等几分钟,120s 会把一次**正常**的慢启动判成 `[network]` 失败去重试,而重试
|
|
80
|
+
* 再交同一份 prompt 给同一个排队中的后端只会更慢更贵;依赖旧姿态的部署显式写回
|
|
81
|
+
* `MODEL_FIRST_TOKEN_TIMEOUT_MS=120000` 即可。
|
|
82
|
+
*
|
|
83
|
+
* 🔴 **常量在此、别处只许引用**:`test/config-catalog.test.ts` 的 S-220 两格咬 —— 一格按表逐行钉
|
|
84
|
+
* 三腿同值(目录 staticDefault / 解析器实算值 / {@link buildDefaultResilience}),一格扫全 `src/`
|
|
85
|
+
* 判「字面量」这一类写法为红(下一份镜像出生即红)。
|
|
86
|
+
*/
|
|
87
|
+
export declare const RESILIENCE_DEFAULTS: {
|
|
88
|
+
readonly connectTimeoutMs: {
|
|
89
|
+
readonly env: "MODEL_CONNECT_TIMEOUT_MS";
|
|
90
|
+
readonly value: 30000;
|
|
91
|
+
};
|
|
92
|
+
readonly firstTokenTimeoutMs: {
|
|
93
|
+
readonly env: "MODEL_FIRST_TOKEN_TIMEOUT_MS";
|
|
94
|
+
readonly value: 600000;
|
|
95
|
+
};
|
|
96
|
+
readonly idleTimeoutMs: {
|
|
97
|
+
readonly env: "MODEL_IDLE_TIMEOUT_MS";
|
|
98
|
+
readonly value: 300000;
|
|
99
|
+
};
|
|
100
|
+
readonly circuitBreaker: {
|
|
101
|
+
readonly env: "MODEL_CIRCUIT_BREAKER";
|
|
102
|
+
readonly value: false;
|
|
103
|
+
};
|
|
104
|
+
readonly failureThreshold: {
|
|
105
|
+
readonly env: "MODEL_CB_FAILURE_THRESHOLD";
|
|
106
|
+
readonly value: 5;
|
|
107
|
+
};
|
|
108
|
+
readonly cooldownMs: {
|
|
109
|
+
readonly env: "MODEL_CB_COOLDOWN_MS";
|
|
110
|
+
readonly value: 30000;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* S-220 —— 「config 对象没带 resilience 块」时的整块兜底,**逐字段派生自 {@link RESILIENCE_DEFAULTS}**。
|
|
115
|
+
*
|
|
116
|
+
* 返回类型把键集钉死(缺键 / 多键都是 tsc 红),所以这里剩下的唯一手写物是键名本身,值一个字面量都没有。
|
|
117
|
+
* 每次调用铸一只新对象(共享一只导出常量会被消费方就地改掉,那是另一条病)。
|
|
74
118
|
*/
|
|
75
|
-
export declare
|
|
119
|
+
export declare function buildDefaultResilience(): ServiceConfigFlat["resilience"];
|
|
76
120
|
/**
|
|
77
121
|
* [ref] D1/D2 —— consolidation driver 阀门的三根旋钮(语义与三问见
|
|
78
122
|
* {@link ServiceConfigFlat.memoryConsolidationDriver})。纯数据。
|
|
@@ -1625,9 +1669,17 @@ export interface ServiceConfigFlat {
|
|
|
1625
1669
|
pattern: string;
|
|
1626
1670
|
caseSensitive: boolean;
|
|
1627
1671
|
}[];
|
|
1628
|
-
/** [ref] 件 G5(core 5.33.0 [ref],clay 裁定件):**内建** deny 表的档位选择(
|
|
1629
|
-
* shell-history / browser / wallet)。`READ_DENY_BUILTIN_TIERS`=逗号分隔;`none
|
|
1630
|
-
* `[]`:一档内建都不要);坏档名 / 空串**启动期响亮拒**(校验用 core 自己的
|
|
1672
|
+
/** [ref] 件 G5(core 5.33.0 [ref],clay 裁定件):**内建** deny 表的档位选择(闭集**五档**:credentials /
|
|
1673
|
+
* shell-history / browser / wallet / **agent-config**)。`READ_DENY_BUILTIN_TIERS`=逗号分隔;`none`=显式
|
|
1674
|
+
* 空姿态(core 的 `[]`:一档内建都不要);坏档名 / 空串**启动期响亮拒**(校验用 core 自己的
|
|
1675
|
+
* `resolveReadDenyBuiltins` —— 词表**单一属主在 core**,本仓零镜像:加档这天校验自动认,这段散文是
|
|
1676
|
+
* 唯一要跟手的东西)。
|
|
1677
|
+
* 🔴 **`agent-config`(core 7.14.0 [ref] C-e 新加的第五档)**:agent **自己的配置文件** —— 它们的 `env` /
|
|
1678
|
+
* server 段里明文放着 API key(壳的 `settings.json` env 块、项目 `.mcp.json` 的 server env、引擎宿主
|
|
1679
|
+
* data-root 下的 `.env`)。此前对这类文件的一次结构化读会把密钥**逐字**落进工具结果、从而落进会话记录。
|
|
1680
|
+
* 行名的是**文件**不是配置**目录**(`.sema`/`.claude` 底下还住着 agents/skills/hooks 与记忆库 —— 模型
|
|
1681
|
+
* 当作日常材料读写的东西);同族工具的 token **存储**(`.credentials.json`/`.codex/auth.json` …)归
|
|
1682
|
+
* `credentials` 档,不归本档 ⇒ 为排查 agent 配置而关掉本档的部署,凭证行照样拦着。
|
|
1631
1683
|
* 缺席=不铸 ⇒ core 自默认(除 shell-history 外全开)。**仅 deployment 席**:TaskSpec 上没有同名键,
|
|
1632
1684
|
* 任务/受治脚本无法据此放宽自己的部署(core 侧的设计,与本仓「部署级旋钮无条件施加」同向)。 */
|
|
1633
1685
|
readDenyBuiltinTiers?: readonly string[];
|
package/dist/config-types.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
export const RETENTION_MODES = ["audit-only", "enforce"];
|
|
2
2
|
export const MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC = 300;
|
|
3
|
-
export const
|
|
3
|
+
export const RESILIENCE_DEFAULTS = {
|
|
4
|
+
connectTimeoutMs: { env: "MODEL_CONNECT_TIMEOUT_MS", value: 30_000 },
|
|
5
|
+
firstTokenTimeoutMs: { env: "MODEL_FIRST_TOKEN_TIMEOUT_MS", value: 600_000 },
|
|
6
|
+
idleTimeoutMs: { env: "MODEL_IDLE_TIMEOUT_MS", value: 300_000 },
|
|
7
|
+
circuitBreaker: { env: "MODEL_CIRCUIT_BREAKER", value: false },
|
|
8
|
+
failureThreshold: { env: "MODEL_CB_FAILURE_THRESHOLD", value: 5 },
|
|
9
|
+
cooldownMs: { env: "MODEL_CB_COOLDOWN_MS", value: 30_000 },
|
|
10
|
+
};
|
|
11
|
+
export function buildDefaultResilience() {
|
|
12
|
+
const d = RESILIENCE_DEFAULTS;
|
|
13
|
+
return {
|
|
14
|
+
connectTimeoutMs: d.connectTimeoutMs.value,
|
|
15
|
+
firstTokenTimeoutMs: d.firstTokenTimeoutMs.value,
|
|
16
|
+
idleTimeoutMs: d.idleTimeoutMs.value,
|
|
17
|
+
circuitBreaker: d.circuitBreaker.value,
|
|
18
|
+
failureThreshold: d.failureThreshold.value,
|
|
19
|
+
cooldownMs: d.cooldownMs.value,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
4
22
|
//# sourceMappingURL=config-types.js.map
|
package/dist/config.d.ts
CHANGED
|
@@ -99,6 +99,48 @@ export declare function logConfigDiagnostics(logger: {
|
|
|
99
99
|
info?: (event: string, fields?: Record<string, unknown>) => void;
|
|
100
100
|
debug?: (event: string, fields?: Record<string, unknown>) => void;
|
|
101
101
|
}): void;
|
|
102
|
+
/** B-089:`Model.cost` 的四项(per-1M USD)。类型从 core 的 `Model` 取,不在本仓重列一份。 */
|
|
103
|
+
type ModelCost = Model["cost"];
|
|
104
|
+
/**
|
|
105
|
+
* 🔴 B-089 —— `Model.cost` 这**一个键**的唯一铸点(可展开片段):有价目表 ⇒ 键在;没配 ⇒ **键不出**。
|
|
106
|
+
* 三个铸点(env 主槽 / env cheap 槽 / config-center 车道)共用它,所以「缺席怎么表达」在本仓只有一处答案。
|
|
107
|
+
*
|
|
108
|
+
* 为什么这里有一处 cast:`Model.cost` 在 core 7.13.0 的**类型面**上仍是必填
|
|
109
|
+
* (`dist/engine/llm/types.d.ts:803`),而它的**运行期**判据读的正是这个键在不在
|
|
110
|
+
* (`dist/core/pricing.js:15`,[ref] 的单一谓词)——「缺席」是 core 明文支持并依赖的运行期状态,
|
|
111
|
+
* 只是类型面还没跟上(core 也未把 `isModelPriced` 从 index 导出,本仓无法直接复用那只谓词)。
|
|
112
|
+
* 把这条唯一的类型让步收在这里,调用点一处不必再 cast。
|
|
113
|
+
*
|
|
114
|
+
* ⚠️ 连带纪律:既然运行期可缺席,**读** `Model.cost` 一律先判在场(`budget.ts` 的
|
|
115
|
+
* `if (m.cost)`、catalog 行的 `c.model.cost?.input`),别写 `model.cost.input` —— 类型面不会拦你。
|
|
116
|
+
* 终态是 core 把该键改成可选,届时本函数的 cast 与本段注一起删。
|
|
117
|
+
*/
|
|
118
|
+
export declare function buildModelCostKey(cost: ModelCost | undefined): Pick<Model, "cost">;
|
|
119
|
+
/**
|
|
120
|
+
* 🔴 B-089 —— 「配了 $ 成本天花板,但目录里一只带价模型都没有」的判据 + 文案(纯函数,零副作用)。
|
|
121
|
+
*
|
|
122
|
+
* 两道天花板(`MAX_TASK_COST_USD` / `MAX_PRINCIPAL_COST_USD`)量的是 `costUsd`,而一只**未定价**的模型
|
|
123
|
+
* 算不出成本([ref] 之后它连 `costMicroUsd` 这个键都不发)—— 于是这两根旋钮在这台机器上**永不可能
|
|
124
|
+
* 触发**:运营方以为配了 $ 护栏,实际一道都没有,且没有任何一处会响。不拒启([ref] 只要求 loud ——
|
|
125
|
+
* 这是一台**本来就在服务**的机器的既有形,而且「先配上限、稍后配单价」是正当的上线顺序)。
|
|
126
|
+
*
|
|
127
|
+
* ⚠️ 位置即判据(codex r1 [medium] 真复现):**必须拿生效后的目录与限额来判,不能在 `loadConfig` 里判**。
|
|
128
|
+
* `loadConfig` 只见 env 车道,而 `main.ts` 在第一次 effective 拉取**之前**就把 boot 诊断排空了 ——
|
|
129
|
+
* 在那里判会同时犯两个方向的错:center 名册已配价、env 未配 ⇒ **假警**;env 已配价、center 名册撤了价
|
|
130
|
+
* (或天花板只由 center 的 limits 下发)⇒ **漏警**。故本函数只给判据,席位在两只入口各自的
|
|
131
|
+
* **采纳之后**(`main.ts` 的 `createConfigCenterRuntime` 之后 / `run-local.ts` 的 `applyEffective` 之后)。
|
|
132
|
+
*
|
|
133
|
+
* 判据 = **目录里没有任何一只模型带 `cost`**,与 `budget.buildPricing` 的填表判据同一个(它只为带 cost
|
|
134
|
+
* 的模型建行),而 core 的 `isModelPriced` 读的正是 `pricing[id] ?? model.cost` —— 两边同时缺席才是未定价。
|
|
135
|
+
* 本模块刻意不 import `budget.ts`(那边挂着 metrics/fleet 一串运行期依赖,config 是纯解析层)。
|
|
136
|
+
*/
|
|
137
|
+
export declare function buildUnpricedCeilingNotice(models: Record<string, Model>, limits: {
|
|
138
|
+
maxTaskCostUsd: number;
|
|
139
|
+
maxPrincipalCostUsd: number;
|
|
140
|
+
}): {
|
|
141
|
+
event: string;
|
|
142
|
+
fields: Record<string, string>;
|
|
143
|
+
} | undefined;
|
|
102
144
|
/** [ref] N8/N9 — the BOOLEAN member of this module's env-parser family (`env`/`env2`/`numEnv`/
|
|
103
145
|
* `numEnvBounded`/`optFinitePositiveEnv`/`enumEnv`/`headerNameEnv`/`firstSetEnv`). Until now booleans were the
|
|
104
146
|
* one type parsed by a hand-written literal comparison at every single site, and that is exactly why the same
|