@sema-agent/settings-schema 1.9.0 → 1.10.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/CHANGELOG.md +25 -0
- package/dist/hash.d.ts +7 -3
- package/dist/hash.js +7 -3
- package/dist/types.d.ts +74 -0
- package/dist/types.js +69 -27
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,31 @@
|
|
|
6
6
|
> `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
|
|
7
7
|
> 包名是**历史记录**,如实保留不追改。
|
|
8
8
|
|
|
9
|
+
## 1.10.0 — 2026-09-09 — `models.models[].compat` 引擎兼容声明席(镜像 `OpenAICompletionsCompat`;新键=minor)
|
|
10
|
+
|
|
11
|
+
- 引擎 `OpenAICompletionsCompat`(五键 `thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
|
|
12
|
+
`requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`)早就能按模型发兼容声明,但 `ModelEntry`
|
|
13
|
+
没有这个键 ⇒ 目录条目写了 `compat` 在 parse 期被 strip 剥掉,声明**根本到不了引擎**;`extraBody` 也穿不过(引擎的
|
|
14
|
+
OpenAI 保留键表拦下 `chat_template_kwargs` / `enable_thinking`)。1.10.0 起 `ModelEntry.compat?`
|
|
15
|
+
(`z.unknown().optional()`)。
|
|
16
|
+
- **三问**:①谁需要 —— vLLM / Qwen 类**缺省开思考**的网关(关思考只能靠 `chat_template_kwargs.enable_thinking=false`),
|
|
17
|
+
以及任何要按模型钉 `max_tokens` 拼写 / reasoning 档位的部署;②谁受伤 —— 无:additive 可选键,缺席 = 键不铸 = 今日行为,
|
|
18
|
+
存量配置零迁移;③补偿 —— 不加旋钮(不写就没有这个键)。
|
|
19
|
+
- **解析透明**,与 1.7.2 `source` / 1.9.0 `toolFaces` 同一条规矩:形与词表(`thinkingFormat` 有哪几个词)的校验归
|
|
20
|
+
消费方摄入侧**单点**,本仓刻意不复述那张表 —— 本域整域 all-or-nothing,一次形/词拒会把整套模型目录抹掉(1.7.1 升级
|
|
21
|
+
回归的形)。
|
|
22
|
+
- 唯一保留的解析拒 = 与兄弟字段 `extraBody` **同一只**自由形安全判据(secret 形键/值、访问器、循环、非 JSON、过深):
|
|
23
|
+
1.10.0 把这条流水线从 `extraBody` 里抽成共用的 `vetFreeFormJson(v, field)`(getter-free 描述符走查 → 单次
|
|
24
|
+
`JSON.stringify` → 复扫的 clone 即持久值),两个字段同调一处,不留第二份判据。`extraBody` 的「必须是 plain JSON
|
|
25
|
+
对象」形门是它自己的(它要被摊进请求体),行为逐字不变(消息文本与判定结果均未变)。深度上限仍是 `MAX_EXTRA_BODY_DEPTH`
|
|
26
|
+
(`./hash` 上的已发布导出,不改名 —— 改名是 BREAKING,收益纯属观感),其 JSDoc 已改写成「所有自由形字段共用的一只上限」。
|
|
27
|
+
- 红先:`test/model-compat-1.10.test.ts` 七格(①原样保留 ①b 五键全量 ②缺席不铸 ③坏形不整域默认 ④安全判据拒
|
|
28
|
+
⑤`loadLocalConfig` 全链到 `effective.models.models[].compat` ⑤b `readEffectiveWire` wire 腿同样透传且不降级整域)。
|
|
29
|
+
- 消费方点名(能力显式表态制):**worker 服务**(投影/env/读面,随其 7.69.0)、**CLI**(探针写目录条目)、
|
|
30
|
+
**config-center 产品**(models 域透传)。
|
|
31
|
+
- 顺带:`package-lock.json` 的 `packages[""].version` 自 1.6.0 起停在 `1.5.0`(历次发版只改了顶层 `version`),
|
|
32
|
+
本节一并对齐到 1.10.0。
|
|
33
|
+
|
|
9
34
|
## 1.9.0 — 2026-09-08 — `mcp.servers[].toolFaces` 覆盖层声明面(core 7.8.0 design/388 片 1 F7;新键=minor)
|
|
10
35
|
|
|
11
36
|
- core 7.8.0 起 `McpServerSpec.toolFaces?: Record<remoteToolName, { family?, pathTarget?: {param, aliases?, access, skillScopeEligible?}, renderHints?, ruleFace? }>`
|
package/dist/hash.d.ts
CHANGED
|
@@ -20,9 +20,13 @@ export declare const MAX_HASH_DEPTH = 200;
|
|
|
20
20
|
* getVersion/getEffective (hard-DoS poison pill) — the two caps were each hardened in isolation but never
|
|
21
21
|
* reconciled (R18). The real max wrapper is ~4–6; 32 is a deliberate ≫ margin. */
|
|
22
22
|
export declare const MAX_HASH_WRAPPER_RESERVE = 32;
|
|
23
|
-
/** Depth cap for a standalone
|
|
24
|
-
* ANYTHING the validator accepts is guaranteed hashable in every context it's later hashed in. 168 ≫ any
|
|
25
|
-
*
|
|
23
|
+
/** Depth cap for a standalone FREE-FORM config value, reconciled with the hash cap MINUS the wrapper reserve so
|
|
24
|
+
* that ANYTHING the validator accepts is guaranteed hashable in every context it's later hashed in. 168 ≫ any
|
|
25
|
+
* real free-form value (OpenAI-style request params nest <10 deep).
|
|
26
|
+
* Scope: the ONE cap for EVERY free-form field vetted by `vetFreeFormJson` (`ModelEntry.extraBody` and, since
|
|
27
|
+
* 1.10.0, `ModelEntry.compat`) — they sit at the SAME wrapper depth (`{models:{models:[{…}]}}`), so one cap
|
|
28
|
+
* serves both. The `EXTRA_BODY` spelling is kept because this is a published export on the `./hash` subpath
|
|
29
|
+
* (renaming it would be a BREAKING change for a purely cosmetic gain). */
|
|
26
30
|
export declare const MAX_EXTRA_BODY_DEPTH: number;
|
|
27
31
|
/** Deterministic JSON with sorted object keys (so key order never affects the hash). */
|
|
28
32
|
export declare function stableStringify(v: unknown, depth?: number): string;
|
package/dist/hash.js
CHANGED
|
@@ -20,9 +20,13 @@ export const MAX_HASH_DEPTH = 200;
|
|
|
20
20
|
* getVersion/getEffective (hard-DoS poison pill) — the two caps were each hardened in isolation but never
|
|
21
21
|
* reconciled (R18). The real max wrapper is ~4–6; 32 is a deliberate ≫ margin. */
|
|
22
22
|
export const MAX_HASH_WRAPPER_RESERVE = 32;
|
|
23
|
-
/** Depth cap for a standalone
|
|
24
|
-
* ANYTHING the validator accepts is guaranteed hashable in every context it's later hashed in. 168 ≫ any
|
|
25
|
-
*
|
|
23
|
+
/** Depth cap for a standalone FREE-FORM config value, reconciled with the hash cap MINUS the wrapper reserve so
|
|
24
|
+
* that ANYTHING the validator accepts is guaranteed hashable in every context it's later hashed in. 168 ≫ any
|
|
25
|
+
* real free-form value (OpenAI-style request params nest <10 deep).
|
|
26
|
+
* Scope: the ONE cap for EVERY free-form field vetted by `vetFreeFormJson` (`ModelEntry.extraBody` and, since
|
|
27
|
+
* 1.10.0, `ModelEntry.compat`) — they sit at the SAME wrapper depth (`{models:{models:[{…}]}}`), so one cap
|
|
28
|
+
* serves both. The `EXTRA_BODY` spelling is kept because this is a published export on the `./hash` subpath
|
|
29
|
+
* (renaming it would be a BREAKING change for a purely cosmetic gain). */
|
|
26
30
|
export const MAX_EXTRA_BODY_DEPTH = MAX_HASH_DEPTH - MAX_HASH_WRAPPER_RESERVE;
|
|
27
31
|
/** Deterministic JSON with sorted object keys (so key order never affects the hash). */
|
|
28
32
|
export function stableStringify(v, depth = 0) {
|
package/dist/types.d.ts
CHANGED
|
@@ -184,6 +184,24 @@ export declare const ModelEntry: z.ZodEffects<z.ZodObject<{
|
|
|
184
184
|
* through), e.g. {frequency_penalty:0.5, presence_penalty:0.3}. Non-secret model metadata — same class
|
|
185
185
|
* as cost/tier. Shipped on /effective.models.models[].extraBody; the service merges it into the request. */
|
|
186
186
|
extraBody: z.ZodEffects<z.ZodOptional<z.ZodUnknown>, Record<string, unknown> | undefined, unknown>;
|
|
187
|
+
/** Per-model wire-compat declaration for the OpenAI-completions lane — mirrors the engine's
|
|
188
|
+
* `OpenAICompletionsCompat` (five keys: `thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
|
|
189
|
+
* `requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`). Real need: a vLLM/Qwen-class
|
|
190
|
+
* gateway that thinks by DEFAULT can only be turned off with `chat_template_kwargs.enable_thinking=false`,
|
|
191
|
+
* which `extraBody` cannot carry (the engine reserves those request keys) — and without this key a catalog
|
|
192
|
+
* entry that declares it is stripped at parse, so the declaration never reaches the engine at all.
|
|
193
|
+
*
|
|
194
|
+
* **Parse-transparent** (`z.unknown()`), same rule as `McpServerSpec.source` (1.7.2) and `.toolFaces` (1.9.0):
|
|
195
|
+
* the SHAPE and the VOCABULARY (which `thinkingFormat` words exist) are the consuming side's single point —
|
|
196
|
+
* this package deliberately does NOT restate that table. This domain parses all-or-nothing, so a shape/word
|
|
197
|
+
* rejection HERE would blank the entire model catalog over one bad key (the 1.7.1 upgrade regression). ABSENT
|
|
198
|
+
* = key not minted = today's behaviour, zero migration.
|
|
199
|
+
*
|
|
200
|
+
* The ONE rejection that remains is the free-form SAFETY judgment shared with `extraBody`
|
|
201
|
+
* ({@link vetFreeFormJson}): a secret-shaped key/value, an accessor, a cycle, non-JSON, or too deep to hash
|
|
202
|
+
* later. That is not shape validation — it is the boundary every free-form value must clear to enter the
|
|
203
|
+
* persisted config and `stableHash` (R18). */
|
|
204
|
+
compat: z.ZodEffects<z.ZodOptional<z.ZodUnknown>, unknown, unknown>;
|
|
187
205
|
/** 配额倍率(EXPERT-REDESIGN §4-1):该模型烧 token 双窗额度的倍率,默认 1。折算时点=weight-at-burn
|
|
188
206
|
* (记账时固化到 UsageEntry.quotaWeightAtUse/weightedTokens):改倍率只影响此后记账,不回算窗口内历史。 */
|
|
189
207
|
quotaWeight: z.ZodDefault<z.ZodNumber>;
|
|
@@ -219,6 +237,7 @@ export declare const ModelEntry: z.ZodEffects<z.ZodObject<{
|
|
|
219
237
|
cacheWrite: number;
|
|
220
238
|
} | undefined;
|
|
221
239
|
extraBody?: Record<string, unknown> | undefined;
|
|
240
|
+
compat?: unknown;
|
|
222
241
|
notes?: string | undefined;
|
|
223
242
|
}, {
|
|
224
243
|
id: string;
|
|
@@ -248,6 +267,7 @@ export declare const ModelEntry: z.ZodEffects<z.ZodObject<{
|
|
|
248
267
|
cacheWrite?: number | undefined;
|
|
249
268
|
} | undefined;
|
|
250
269
|
extraBody?: unknown;
|
|
270
|
+
compat?: unknown;
|
|
251
271
|
quotaWeight?: number | undefined;
|
|
252
272
|
enabled?: boolean | undefined;
|
|
253
273
|
notes?: string | undefined;
|
|
@@ -281,6 +301,7 @@ export declare const ModelEntry: z.ZodEffects<z.ZodObject<{
|
|
|
281
301
|
cacheWrite: number;
|
|
282
302
|
} | undefined;
|
|
283
303
|
extraBody?: Record<string, unknown> | undefined;
|
|
304
|
+
compat?: unknown;
|
|
284
305
|
notes?: string | undefined;
|
|
285
306
|
}, {
|
|
286
307
|
id: string;
|
|
@@ -310,6 +331,7 @@ export declare const ModelEntry: z.ZodEffects<z.ZodObject<{
|
|
|
310
331
|
cacheWrite?: number | undefined;
|
|
311
332
|
} | undefined;
|
|
312
333
|
extraBody?: unknown;
|
|
334
|
+
compat?: unknown;
|
|
313
335
|
quotaWeight?: number | undefined;
|
|
314
336
|
enabled?: boolean | undefined;
|
|
315
337
|
notes?: string | undefined;
|
|
@@ -504,6 +526,24 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
504
526
|
* through), e.g. {frequency_penalty:0.5, presence_penalty:0.3}. Non-secret model metadata — same class
|
|
505
527
|
* as cost/tier. Shipped on /effective.models.models[].extraBody; the service merges it into the request. */
|
|
506
528
|
extraBody: z.ZodEffects<z.ZodOptional<z.ZodUnknown>, Record<string, unknown> | undefined, unknown>;
|
|
529
|
+
/** Per-model wire-compat declaration for the OpenAI-completions lane — mirrors the engine's
|
|
530
|
+
* `OpenAICompletionsCompat` (five keys: `thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
|
|
531
|
+
* `requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`). Real need: a vLLM/Qwen-class
|
|
532
|
+
* gateway that thinks by DEFAULT can only be turned off with `chat_template_kwargs.enable_thinking=false`,
|
|
533
|
+
* which `extraBody` cannot carry (the engine reserves those request keys) — and without this key a catalog
|
|
534
|
+
* entry that declares it is stripped at parse, so the declaration never reaches the engine at all.
|
|
535
|
+
*
|
|
536
|
+
* **Parse-transparent** (`z.unknown()`), same rule as `McpServerSpec.source` (1.7.2) and `.toolFaces` (1.9.0):
|
|
537
|
+
* the SHAPE and the VOCABULARY (which `thinkingFormat` words exist) are the consuming side's single point —
|
|
538
|
+
* this package deliberately does NOT restate that table. This domain parses all-or-nothing, so a shape/word
|
|
539
|
+
* rejection HERE would blank the entire model catalog over one bad key (the 1.7.1 upgrade regression). ABSENT
|
|
540
|
+
* = key not minted = today's behaviour, zero migration.
|
|
541
|
+
*
|
|
542
|
+
* The ONE rejection that remains is the free-form SAFETY judgment shared with `extraBody`
|
|
543
|
+
* ({@link vetFreeFormJson}): a secret-shaped key/value, an accessor, a cycle, non-JSON, or too deep to hash
|
|
544
|
+
* later. That is not shape validation — it is the boundary every free-form value must clear to enter the
|
|
545
|
+
* persisted config and `stableHash` (R18). */
|
|
546
|
+
compat: z.ZodEffects<z.ZodOptional<z.ZodUnknown>, unknown, unknown>;
|
|
507
547
|
/** 配额倍率(EXPERT-REDESIGN §4-1):该模型烧 token 双窗额度的倍率,默认 1。折算时点=weight-at-burn
|
|
508
548
|
* (记账时固化到 UsageEntry.quotaWeightAtUse/weightedTokens):改倍率只影响此后记账,不回算窗口内历史。 */
|
|
509
549
|
quotaWeight: z.ZodDefault<z.ZodNumber>;
|
|
@@ -539,6 +579,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
539
579
|
cacheWrite: number;
|
|
540
580
|
} | undefined;
|
|
541
581
|
extraBody?: Record<string, unknown> | undefined;
|
|
582
|
+
compat?: unknown;
|
|
542
583
|
notes?: string | undefined;
|
|
543
584
|
}, {
|
|
544
585
|
id: string;
|
|
@@ -568,6 +609,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
568
609
|
cacheWrite?: number | undefined;
|
|
569
610
|
} | undefined;
|
|
570
611
|
extraBody?: unknown;
|
|
612
|
+
compat?: unknown;
|
|
571
613
|
quotaWeight?: number | undefined;
|
|
572
614
|
enabled?: boolean | undefined;
|
|
573
615
|
notes?: string | undefined;
|
|
@@ -601,6 +643,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
601
643
|
cacheWrite: number;
|
|
602
644
|
} | undefined;
|
|
603
645
|
extraBody?: Record<string, unknown> | undefined;
|
|
646
|
+
compat?: unknown;
|
|
604
647
|
notes?: string | undefined;
|
|
605
648
|
}, {
|
|
606
649
|
id: string;
|
|
@@ -630,6 +673,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
630
673
|
cacheWrite?: number | undefined;
|
|
631
674
|
} | undefined;
|
|
632
675
|
extraBody?: unknown;
|
|
676
|
+
compat?: unknown;
|
|
633
677
|
quotaWeight?: number | undefined;
|
|
634
678
|
enabled?: boolean | undefined;
|
|
635
679
|
notes?: string | undefined;
|
|
@@ -753,6 +797,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
753
797
|
cacheWrite: number;
|
|
754
798
|
} | undefined;
|
|
755
799
|
extraBody?: Record<string, unknown> | undefined;
|
|
800
|
+
compat?: unknown;
|
|
756
801
|
notes?: string | undefined;
|
|
757
802
|
}[];
|
|
758
803
|
roles: Partial<Record<"default" | "summarize" | "subagent" | "team" | "synthesize" | "verifier" | "classifier" | "consolidate", {
|
|
@@ -808,6 +853,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
808
853
|
cacheWrite?: number | undefined;
|
|
809
854
|
} | undefined;
|
|
810
855
|
extraBody?: unknown;
|
|
856
|
+
compat?: unknown;
|
|
811
857
|
quotaWeight?: number | undefined;
|
|
812
858
|
enabled?: boolean | undefined;
|
|
813
859
|
notes?: string | undefined;
|
|
@@ -865,6 +911,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
865
911
|
cacheWrite: number;
|
|
866
912
|
} | undefined;
|
|
867
913
|
extraBody?: Record<string, unknown> | undefined;
|
|
914
|
+
compat?: unknown;
|
|
868
915
|
notes?: string | undefined;
|
|
869
916
|
}[];
|
|
870
917
|
roles: Partial<Record<"default" | "summarize" | "subagent" | "team" | "synthesize" | "verifier" | "classifier" | "consolidate", {
|
|
@@ -920,6 +967,7 @@ export declare const ModelsConfig: z.ZodEffects<z.ZodObject<{
|
|
|
920
967
|
cacheWrite?: number | undefined;
|
|
921
968
|
} | undefined;
|
|
922
969
|
extraBody?: unknown;
|
|
970
|
+
compat?: unknown;
|
|
923
971
|
quotaWeight?: number | undefined;
|
|
924
972
|
enabled?: boolean | undefined;
|
|
925
973
|
notes?: string | undefined;
|
|
@@ -9399,6 +9447,24 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9399
9447
|
* through), e.g. {frequency_penalty:0.5, presence_penalty:0.3}. Non-secret model metadata — same class
|
|
9400
9448
|
* as cost/tier. Shipped on /effective.models.models[].extraBody; the service merges it into the request. */
|
|
9401
9449
|
extraBody: z.ZodEffects<z.ZodOptional<z.ZodUnknown>, Record<string, unknown> | undefined, unknown>;
|
|
9450
|
+
/** Per-model wire-compat declaration for the OpenAI-completions lane — mirrors the engine's
|
|
9451
|
+
* `OpenAICompletionsCompat` (five keys: `thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
|
|
9452
|
+
* `requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`). Real need: a vLLM/Qwen-class
|
|
9453
|
+
* gateway that thinks by DEFAULT can only be turned off with `chat_template_kwargs.enable_thinking=false`,
|
|
9454
|
+
* which `extraBody` cannot carry (the engine reserves those request keys) — and without this key a catalog
|
|
9455
|
+
* entry that declares it is stripped at parse, so the declaration never reaches the engine at all.
|
|
9456
|
+
*
|
|
9457
|
+
* **Parse-transparent** (`z.unknown()`), same rule as `McpServerSpec.source` (1.7.2) and `.toolFaces` (1.9.0):
|
|
9458
|
+
* the SHAPE and the VOCABULARY (which `thinkingFormat` words exist) are the consuming side's single point —
|
|
9459
|
+
* this package deliberately does NOT restate that table. This domain parses all-or-nothing, so a shape/word
|
|
9460
|
+
* rejection HERE would blank the entire model catalog over one bad key (the 1.7.1 upgrade regression). ABSENT
|
|
9461
|
+
* = key not minted = today's behaviour, zero migration.
|
|
9462
|
+
*
|
|
9463
|
+
* The ONE rejection that remains is the free-form SAFETY judgment shared with `extraBody`
|
|
9464
|
+
* ({@link vetFreeFormJson}): a secret-shaped key/value, an accessor, a cycle, non-JSON, or too deep to hash
|
|
9465
|
+
* later. That is not shape validation — it is the boundary every free-form value must clear to enter the
|
|
9466
|
+
* persisted config and `stableHash` (R18). */
|
|
9467
|
+
compat: z.ZodEffects<z.ZodOptional<z.ZodUnknown>, unknown, unknown>;
|
|
9402
9468
|
/** 配额倍率(EXPERT-REDESIGN §4-1):该模型烧 token 双窗额度的倍率,默认 1。折算时点=weight-at-burn
|
|
9403
9469
|
* (记账时固化到 UsageEntry.quotaWeightAtUse/weightedTokens):改倍率只影响此后记账,不回算窗口内历史。 */
|
|
9404
9470
|
quotaWeight: z.ZodDefault<z.ZodNumber>;
|
|
@@ -9434,6 +9500,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9434
9500
|
cacheWrite: number;
|
|
9435
9501
|
} | undefined;
|
|
9436
9502
|
extraBody?: Record<string, unknown> | undefined;
|
|
9503
|
+
compat?: unknown;
|
|
9437
9504
|
notes?: string | undefined;
|
|
9438
9505
|
}, {
|
|
9439
9506
|
id: string;
|
|
@@ -9463,6 +9530,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9463
9530
|
cacheWrite?: number | undefined;
|
|
9464
9531
|
} | undefined;
|
|
9465
9532
|
extraBody?: unknown;
|
|
9533
|
+
compat?: unknown;
|
|
9466
9534
|
quotaWeight?: number | undefined;
|
|
9467
9535
|
enabled?: boolean | undefined;
|
|
9468
9536
|
notes?: string | undefined;
|
|
@@ -9496,6 +9564,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9496
9564
|
cacheWrite: number;
|
|
9497
9565
|
} | undefined;
|
|
9498
9566
|
extraBody?: Record<string, unknown> | undefined;
|
|
9567
|
+
compat?: unknown;
|
|
9499
9568
|
notes?: string | undefined;
|
|
9500
9569
|
}, {
|
|
9501
9570
|
id: string;
|
|
@@ -9525,6 +9594,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9525
9594
|
cacheWrite?: number | undefined;
|
|
9526
9595
|
} | undefined;
|
|
9527
9596
|
extraBody?: unknown;
|
|
9597
|
+
compat?: unknown;
|
|
9528
9598
|
quotaWeight?: number | undefined;
|
|
9529
9599
|
enabled?: boolean | undefined;
|
|
9530
9600
|
notes?: string | undefined;
|
|
@@ -9648,6 +9718,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9648
9718
|
cacheWrite: number;
|
|
9649
9719
|
} | undefined;
|
|
9650
9720
|
extraBody?: Record<string, unknown> | undefined;
|
|
9721
|
+
compat?: unknown;
|
|
9651
9722
|
notes?: string | undefined;
|
|
9652
9723
|
}[];
|
|
9653
9724
|
roles: Partial<Record<"default" | "summarize" | "subagent" | "team" | "synthesize" | "verifier" | "classifier" | "consolidate", {
|
|
@@ -9703,6 +9774,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9703
9774
|
cacheWrite?: number | undefined;
|
|
9704
9775
|
} | undefined;
|
|
9705
9776
|
extraBody?: unknown;
|
|
9777
|
+
compat?: unknown;
|
|
9706
9778
|
quotaWeight?: number | undefined;
|
|
9707
9779
|
enabled?: boolean | undefined;
|
|
9708
9780
|
notes?: string | undefined;
|
|
@@ -9760,6 +9832,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9760
9832
|
cacheWrite: number;
|
|
9761
9833
|
} | undefined;
|
|
9762
9834
|
extraBody?: Record<string, unknown> | undefined;
|
|
9835
|
+
compat?: unknown;
|
|
9763
9836
|
notes?: string | undefined;
|
|
9764
9837
|
}[];
|
|
9765
9838
|
roles: Partial<Record<"default" | "summarize" | "subagent" | "team" | "synthesize" | "verifier" | "classifier" | "consolidate", {
|
|
@@ -9815,6 +9888,7 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
9815
9888
|
cacheWrite?: number | undefined;
|
|
9816
9889
|
} | undefined;
|
|
9817
9890
|
extraBody?: unknown;
|
|
9891
|
+
compat?: unknown;
|
|
9818
9892
|
quotaWeight?: number | undefined;
|
|
9819
9893
|
enabled?: boolean | undefined;
|
|
9820
9894
|
notes?: string | undefined;
|
package/dist/types.js
CHANGED
|
@@ -209,6 +209,40 @@ export function extraBodyUnsafe(v) {
|
|
|
209
209
|
}
|
|
210
210
|
return false;
|
|
211
211
|
}
|
|
212
|
+
/** THE vetting pipeline for every FREE-FORM JSON value that lands in the persisted config — `ModelEntry.extraBody`
|
|
213
|
+
* and `ModelEntry.compat` share this ONE function (1.10.0; before that it was inline in `extraBody` and a second
|
|
214
|
+
* free-form field would have copied it). The judgment is identical for every such field, so it lives in one place:
|
|
215
|
+
*
|
|
216
|
+
* 1. getter-free descriptor walk ({@link extraBodyUnsafe}) over the RAW value — never invokes a getter, and any
|
|
217
|
+
* reflective throw (a hostile Proxy trap could throw a secret-bearing message) collapses into a FIXED message;
|
|
218
|
+
* 2. a SINGLE `JSON.stringify` — its clone is re-scanned, so a Proxy that fakes its descriptors still can't ship
|
|
219
|
+
* a secret, and that same clone IS what we return to persist, so a non-idempotent `toJSON` cannot validate one
|
|
220
|
+
* serialization and persist another (the pre-R10 superRefine+transform stringified TWICE).
|
|
221
|
+
*
|
|
222
|
+
* `field` only NAMES the field in the message — it never changes the judgment. What this does NOT do is validate
|
|
223
|
+
* SHAPE: a field's shape/vocabulary is the consumer's single point (1.7.2 / 1.9.0 rule), and this domain parses
|
|
224
|
+
* all-or-nothing, so a shape rejection here would blank the whole catalog. */
|
|
225
|
+
function vetFreeFormJson(v, field) {
|
|
226
|
+
let rawUnsafe;
|
|
227
|
+
try {
|
|
228
|
+
rawUnsafe = extraBodyUnsafe(v);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return { ok: false, message: `${field} could not be validated (reflective access threw)` }; // R11
|
|
232
|
+
}
|
|
233
|
+
if (rawUnsafe)
|
|
234
|
+
return { ok: false, message: `${field}: a key/value is a secret shape, non-JSON (Infinity/function/Map), an accessor, cyclic, or too deep` };
|
|
235
|
+
let clone;
|
|
236
|
+
try {
|
|
237
|
+
clone = JSON.parse(JSON.stringify(v)); // the ONLY serialization — its result is what we scan AND persist
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return { ok: false, message: `${field} is not JSON-serializable` };
|
|
241
|
+
}
|
|
242
|
+
if (extraBodyUnsafe(clone))
|
|
243
|
+
return { ok: false, message: `${field}: its serialized form carries a secret` };
|
|
244
|
+
return { ok: true, value: clone };
|
|
245
|
+
}
|
|
212
246
|
/** A k8s resource quantity ("250m", "1", "0.5", "512Mi", "1Gi", "128M") — catches garbage at config-submit
|
|
213
247
|
* rather than surfacing as a reconcile ApplyError at the apiserver (consistent with ENV_NAME/DURATION guards). */
|
|
214
248
|
export const K8S_QUANTITY = z.string().regex(/^\d+(\.\d+)?(m|[kKMGTPE]i?)?$/, 'must be a k8s quantity like "250m", "512Mi", "1Gi"');
|
|
@@ -313,10 +347,9 @@ export const ModelEntry = z.object({
|
|
|
313
347
|
extraBody: z
|
|
314
348
|
.unknown()
|
|
315
349
|
.optional()
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
//
|
|
319
|
-
// BEFORE the stringify; the clone is re-scanned so a Proxy that fakes its descriptors still can't ship a secret.
|
|
350
|
+
// The SHAPE gate (plain JSON object) is extraBody's OWN — it is spread into a request body, so a non-object
|
|
351
|
+
// is meaningless here; the SAFETY judgment is the shared vetFreeFormJson pipeline (see its doc: getter-free
|
|
352
|
+
// walk → single stringify → re-scanned clone IS the persisted value).
|
|
320
353
|
.transform((v, ctx) => {
|
|
321
354
|
if (v === undefined)
|
|
322
355
|
return undefined;
|
|
@@ -325,33 +358,42 @@ export const ModelEntry = z.object({
|
|
|
325
358
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "extraBody must be a plain JSON object" });
|
|
326
359
|
return z.NEVER;
|
|
327
360
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
try {
|
|
332
|
-
rawUnsafe = extraBodyUnsafe(v);
|
|
333
|
-
}
|
|
334
|
-
catch {
|
|
335
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "extraBody could not be validated (reflective access threw)" });
|
|
361
|
+
const vetted = vetFreeFormJson(v, "extraBody");
|
|
362
|
+
if (!vetted.ok) {
|
|
363
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: vetted.message });
|
|
336
364
|
return z.NEVER;
|
|
337
365
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
366
|
+
return vetted.value;
|
|
367
|
+
}),
|
|
368
|
+
/** Per-model wire-compat declaration for the OpenAI-completions lane — mirrors the engine's
|
|
369
|
+
* `OpenAICompletionsCompat` (five keys: `thinkingFormat` / `reasoningEffortLevels` / `maxTokensField` /
|
|
370
|
+
* `requiresReasoningContentOnAssistantMessages` / `supportsReasoningEffort`). Real need: a vLLM/Qwen-class
|
|
371
|
+
* gateway that thinks by DEFAULT can only be turned off with `chat_template_kwargs.enable_thinking=false`,
|
|
372
|
+
* which `extraBody` cannot carry (the engine reserves those request keys) — and without this key a catalog
|
|
373
|
+
* entry that declares it is stripped at parse, so the declaration never reaches the engine at all.
|
|
374
|
+
*
|
|
375
|
+
* **Parse-transparent** (`z.unknown()`), same rule as `McpServerSpec.source` (1.7.2) and `.toolFaces` (1.9.0):
|
|
376
|
+
* the SHAPE and the VOCABULARY (which `thinkingFormat` words exist) are the consuming side's single point —
|
|
377
|
+
* this package deliberately does NOT restate that table. This domain parses all-or-nothing, so a shape/word
|
|
378
|
+
* rejection HERE would blank the entire model catalog over one bad key (the 1.7.1 upgrade regression). ABSENT
|
|
379
|
+
* = key not minted = today's behaviour, zero migration.
|
|
380
|
+
*
|
|
381
|
+
* The ONE rejection that remains is the free-form SAFETY judgment shared with `extraBody`
|
|
382
|
+
* ({@link vetFreeFormJson}): a secret-shaped key/value, an accessor, a cycle, non-JSON, or too deep to hash
|
|
383
|
+
* later. That is not shape validation — it is the boundary every free-form value must clear to enter the
|
|
384
|
+
* persisted config and `stableHash` (R18). */
|
|
385
|
+
compat: z
|
|
386
|
+
.unknown()
|
|
387
|
+
.optional()
|
|
388
|
+
.transform((v, ctx) => {
|
|
389
|
+
if (v === undefined)
|
|
390
|
+
return undefined;
|
|
391
|
+
const vetted = vetFreeFormJson(v, "compat");
|
|
392
|
+
if (!vetted.ok) {
|
|
393
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: vetted.message });
|
|
352
394
|
return z.NEVER;
|
|
353
395
|
}
|
|
354
|
-
return
|
|
396
|
+
return vetted.value;
|
|
355
397
|
}),
|
|
356
398
|
/** 配额倍率(EXPERT-REDESIGN §4-1):该模型烧 token 双窗额度的倍率,默认 1。折算时点=weight-at-burn
|
|
357
399
|
* (记账时固化到 UsageEntry.quotaWeightAtUse/weightedTokens):改倍率只影响此后记账,不回算窗口内历史。 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/settings-schema",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Sema settings schema — pure config contract (zod domains + effective-config + roster resolution + ref-integrity + remoteExec) shared by sema-registry, sema-server, and the TOC desktop/CLI. One source of truth. (Renamed at 1.0.0 from @sema-agent/registry-core — name and docs only, no behavior change; note the range moves 0.19.0 -> 1.0.0, so a dependency must become ^1.0.0. The old names @sema-agent/registry-core <=0.19.0 and @sema-ai/registry-core remain published, frozen, for migration.)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|