@sema-agent/settings-schema 14.6.0 → 14.8.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 +475 -0
- package/README.md +89 -5
- package/conformance/VERSION +1 -1
- package/conformance/cases/c84-sandbox-git-block-carried-verbatim/expected.json +153 -0
- package/conformance/cases/c84-sandbox-git-block-carried-verbatim/input.json +23 -0
- package/conformance/cases/c84-sandbox-git-block-carried-verbatim/meta.json +5 -0
- package/conformance/cases/c85-sandbox-git-refused-block-still-carried/expected.json +153 -0
- package/conformance/cases/c85-sandbox-git-refused-block-still-carried/input.json +23 -0
- package/conformance/cases/c85-sandbox-git-refused-block-still-carried/meta.json +5 -0
- package/dist/api/auth-bridge.d.ts +16 -2
- package/dist/api/auth-bridge.js +59 -24
- package/dist/api/credentials.d.ts +281 -0
- package/dist/api/credentials.js +149 -0
- package/dist/api/scopes.js +5 -1
- package/dist/atomic-write.d.ts +15 -0
- package/dist/atomic-write.js +109 -5
- package/dist/catalog-manages.d.ts +15 -0
- package/dist/catalog-manages.js +60 -0
- package/dist/center-refs.d.ts +68 -0
- package/dist/center-refs.js +70 -0
- package/dist/config-d-file.js +33 -15
- package/dist/config-dir-migration.js +1 -0
- package/dist/config-fns.js +147 -81
- package/dist/config-root-documents.d.ts +106 -0
- package/dist/config-root-documents.js +93 -0
- package/dist/config-root-node.d.ts +117 -0
- package/dist/config-root-node.js +260 -0
- package/dist/conformance/canonical.js +4 -4
- package/dist/conformance/contract-adapter.js +26 -1
- package/dist/conformance/corpus.js +4 -4
- package/dist/conformance/derive.js +2 -2
- package/dist/conformance/run.d.ts +3 -0
- package/dist/conformance/run.js +53 -12
- package/dist/conformance/temp-roots.d.ts +31 -0
- package/dist/conformance/temp-roots.js +184 -0
- package/dist/cross-session.d.ts +4 -4
- package/dist/declared-capture.d.ts +41 -0
- package/dist/declared-capture.js +344 -0
- package/dist/default-model.d.ts +1 -1
- package/dist/default-model.js +5 -4
- package/dist/env-model-table.d.ts +163 -0
- package/dist/env-model-table.js +257 -0
- package/dist/file-edit.js +11 -15
- package/dist/fleet.d.ts +36 -7
- package/dist/fleet.js +83 -32
- package/dist/hash.d.ts +0 -9
- package/dist/hash.js +64 -7
- package/dist/hooks-config.js +18 -3
- package/dist/http-hook-allowlists.d.ts +22 -9
- package/dist/http-hook-allowlists.js +169 -91
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/mcp-request-entry.d.ts +67 -13
- package/dist/mcp-request-entry.js +132 -93
- package/dist/mcp-server-name.d.ts +17 -0
- package/dist/mcp-server-name.js +19 -0
- package/dist/migration-receipt.d.ts +10 -6
- package/dist/migration-receipt.js +5 -3
- package/dist/node.d.ts +1 -0
- package/dist/node.js +2 -0
- package/dist/one-time-migrations.d.ts +4 -2
- package/dist/one-time-migrations.js +236 -96
- package/dist/own-key-read.d.ts +247 -0
- package/dist/own-key-read.js +449 -0
- package/dist/plain-json-object.d.ts +11 -1
- package/dist/plain-json-object.js +37 -1
- package/dist/remote-exec.js +9 -1
- package/dist/retired-form.d.ts +19 -0
- package/dist/retired-form.js +24 -1
- package/dist/safety-merge-spec.d.ts +6 -6
- package/dist/safety-merge-spec.js +50 -37
- package/dist/sandbox-git.d.ts +64 -0
- package/dist/sandbox-git.js +89 -0
- package/dist/scheduler-store.js +19 -11
- package/dist/settings-units.js +14 -12
- package/dist/tier-words.d.ts +33 -0
- package/dist/tier-words.js +43 -0
- package/dist/types.d.ts +12 -2
- package/dist/types.js +35 -6
- package/dist/url-shape.d.ts +16 -0
- package/dist/url-shape.js +18 -0
- package/dist/web-admin-credential.js +5 -2
- package/dist/web-admin-session-url.js +2 -2
- package/dist/worker-row-verdict.d.ts +11 -0
- package/dist/worker-row-verdict.js +58 -2
- package/dist/workers-rows.js +38 -10
- package/dist/zod-unwrap.d.ts +6 -0
- package/dist/zod-unwrap.js +14 -7
- package/docs/API-SURFACE.md +100 -5
- package/docs/CONSUMER-INTEGRATION.md +250 -2
- package/llms-full.txt +26 -4
- package/llms.txt +11 -4
- package/package.json +2 -2
- package/dist/definition-copy.d.ts +0 -23
- package/dist/definition-copy.js +0 -52
|
@@ -92,8 +92,8 @@ export declare const MIN_TRUST_WORDS: readonly ["local", "session", "project", "
|
|
|
92
92
|
export declare const MinTrust: z.ZodEnum<{
|
|
93
93
|
local: "local";
|
|
94
94
|
session: "session";
|
|
95
|
-
project: "project";
|
|
96
95
|
global: "global";
|
|
96
|
+
project: "project";
|
|
97
97
|
managed: "managed";
|
|
98
98
|
task: "task";
|
|
99
99
|
}>;
|
|
@@ -151,8 +151,8 @@ export declare const MemberAdmission: z.ZodObject<{
|
|
|
151
151
|
minTrust: z.ZodOptional<z.ZodEnum<{
|
|
152
152
|
local: "local";
|
|
153
153
|
session: "session";
|
|
154
|
-
project: "project";
|
|
155
154
|
global: "global";
|
|
155
|
+
project: "project";
|
|
156
156
|
managed: "managed";
|
|
157
157
|
task: "task";
|
|
158
158
|
}>>;
|
|
@@ -161,8 +161,8 @@ export declare const MemberAdmission: z.ZodObject<{
|
|
|
161
161
|
minTrust: z.ZodEnum<{
|
|
162
162
|
local: "local";
|
|
163
163
|
session: "session";
|
|
164
|
-
project: "project";
|
|
165
164
|
global: "global";
|
|
165
|
+
project: "project";
|
|
166
166
|
managed: "managed";
|
|
167
167
|
task: "task";
|
|
168
168
|
}>;
|
|
@@ -264,8 +264,8 @@ export declare const MergeCategorySpec: z.ZodObject<{
|
|
|
264
264
|
minTrust: z.ZodEnum<{
|
|
265
265
|
local: "local";
|
|
266
266
|
session: "session";
|
|
267
|
-
project: "project";
|
|
268
267
|
global: "global";
|
|
268
|
+
project: "project";
|
|
269
269
|
managed: "managed";
|
|
270
270
|
task: "task";
|
|
271
271
|
}>;
|
|
@@ -314,8 +314,8 @@ export declare const MergeCategorySpec: z.ZodObject<{
|
|
|
314
314
|
minTrust: z.ZodOptional<z.ZodEnum<{
|
|
315
315
|
local: "local";
|
|
316
316
|
session: "session";
|
|
317
|
-
project: "project";
|
|
318
317
|
global: "global";
|
|
318
|
+
project: "project";
|
|
319
319
|
managed: "managed";
|
|
320
320
|
task: "task";
|
|
321
321
|
}>>;
|
|
@@ -324,8 +324,8 @@ export declare const MergeCategorySpec: z.ZodObject<{
|
|
|
324
324
|
minTrust: z.ZodEnum<{
|
|
325
325
|
local: "local";
|
|
326
326
|
session: "session";
|
|
327
|
-
project: "project";
|
|
328
327
|
global: "global";
|
|
328
|
+
project: "project";
|
|
329
329
|
managed: "managed";
|
|
330
330
|
task: "task";
|
|
331
331
|
}>;
|
|
@@ -66,8 +66,8 @@
|
|
|
66
66
|
*/
|
|
67
67
|
import { z } from "zod";
|
|
68
68
|
import { MAX_HASH_DEPTH, stableStringify } from "./hash.js";
|
|
69
|
-
import { captureStringArray, dataDescriptor } from "./
|
|
70
|
-
import {
|
|
69
|
+
import { captureStringArray, dataDescriptor, isAccessorDescriptor, isolatedObject } from "./own-key-read.js";
|
|
70
|
+
import { jsonShapeOf } from "./plain-json-object.js";
|
|
71
71
|
/** How values from different layers/principals combine for a category. */
|
|
72
72
|
export const MergeShape = z.enum(["denyFirst", "replace", "runAll", "overrideByName", "concat"]);
|
|
73
73
|
/** The trust-tier VOCABULARY as a RUNTIME VALUE — the ONE source of the ladder's words, in the same form as
|
|
@@ -183,10 +183,11 @@ function orderVerdict(diff) {
|
|
|
183
183
|
return diff > 0 ? "stricter" : diff < 0 ? "looser" : "equal";
|
|
184
184
|
}
|
|
185
185
|
/** A string list as a set (order and duplicates do not count); `undefined` when the value is not an array of strings
|
|
186
|
-
* — a sparse array, an accessor
|
|
187
|
-
*
|
|
186
|
+
* — a sparse array, an accessor or non-enumerable element (the verdict is about the caller's own value: an accessor is not a stored value, it may answer
|
|
187
|
+
* differently on the next read — the `none` rule's domain says the same; no getter is called), a proxy that cannot be read (revoked, a trap that throws) is not one:
|
|
188
|
+
* all through the package's one string-array reading, strict ({@link captureStringArray}), so the comparator never throws on them and never splits a string. */
|
|
188
189
|
function stringSetOf(v) {
|
|
189
|
-
const list = captureStringArray(v);
|
|
190
|
+
const list = captureStringArray(v, "strict");
|
|
190
191
|
return list === undefined ? undefined : new Set(list);
|
|
191
192
|
}
|
|
192
193
|
/** `superset ⇒ stricter` for setUnion, the mirror for setNarrower. */
|
|
@@ -213,45 +214,57 @@ const NOT_STRICT_JSON = { ok: false };
|
|
|
213
214
|
function strictJsonCopy(v, depth = 0) {
|
|
214
215
|
if (depth > MAX_HASH_DEPTH)
|
|
215
216
|
return NOT_STRICT_JSON;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const member = strictJsonStored(Object.getOwnPropertyDescriptor(v, i), depth);
|
|
229
|
-
if (!member.ok)
|
|
217
|
+
const shape = jsonShapeOf(v); // the shape is asked ONCE, of the seat (total): no `typeof` split, no second prototype read
|
|
218
|
+
switch (shape) {
|
|
219
|
+
case "null":
|
|
220
|
+
return { ok: true, value: v };
|
|
221
|
+
case "primitive":
|
|
222
|
+
if (typeof v === "string" || typeof v === "boolean")
|
|
223
|
+
return { ok: true, value: v };
|
|
224
|
+
return typeof v === "number" && Number.isFinite(v) ? { ok: true, value: v } : NOT_STRICT_JSON; // undefined / symbol / bigint / NaN / ±Infinity
|
|
225
|
+
case "array": {
|
|
226
|
+
const array = v;
|
|
227
|
+
const length = array.length;
|
|
228
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0)
|
|
230
229
|
return NOT_STRICT_JSON;
|
|
231
|
-
|
|
230
|
+
const out = [];
|
|
231
|
+
for (let i = 0; i < length; i++) {
|
|
232
|
+
const member = strictJsonStored(Object.getOwnPropertyDescriptor(array, i), depth);
|
|
233
|
+
if (!member.ok)
|
|
234
|
+
return NOT_STRICT_JSON;
|
|
235
|
+
Object.defineProperty(out, i, dataDescriptor(member.value));
|
|
236
|
+
}
|
|
237
|
+
return { ok: true, value: out };
|
|
232
238
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
case "plain": {
|
|
240
|
+
const record = v;
|
|
241
|
+
const out = isolatedObject();
|
|
242
|
+
for (const key of Reflect.ownKeys(record)) {
|
|
243
|
+
if (typeof key === "symbol")
|
|
244
|
+
continue; // never serialized
|
|
245
|
+
const desc = Object.getOwnPropertyDescriptor(record, key);
|
|
246
|
+
if (desc !== undefined && desc.enumerable !== true)
|
|
247
|
+
continue; // never serialized (what `Object.keys` leaves out)
|
|
248
|
+
const member = strictJsonStored(desc, depth);
|
|
249
|
+
if (!member.ok)
|
|
250
|
+
return NOT_STRICT_JSON;
|
|
251
|
+
Object.defineProperty(out, key, dataDescriptor(member.value));
|
|
252
|
+
}
|
|
253
|
+
return { ok: true, value: out };
|
|
254
|
+
}
|
|
255
|
+
case "non_plain_object": // function / Date / Map / class instance
|
|
256
|
+
case "uninspectable": // a proxy that cannot be inspected: outside the domain
|
|
246
257
|
return NOT_STRICT_JSON;
|
|
247
|
-
|
|
258
|
+
default: {
|
|
259
|
+
const unhandled = shape;
|
|
260
|
+
throw new Error(`strictJsonCopy: unhandled shape ${String(unhandled)}`);
|
|
261
|
+
}
|
|
248
262
|
}
|
|
249
|
-
return { ok: true, value: out };
|
|
250
263
|
}
|
|
251
264
|
/** One member, judged by the descriptor read ONCE: a stored value (a hole has no descriptor; an accessor has no `value`) that is itself strict JSON. */
|
|
252
265
|
function strictJsonStored(desc, depth) {
|
|
253
|
-
if (desc === undefined ||
|
|
254
|
-
return NOT_STRICT_JSON; //
|
|
266
|
+
if (desc === undefined || isAccessorDescriptor(desc))
|
|
267
|
+
return NOT_STRICT_JSON; // 访问器不是存着的值(描述符的字段只认自有的,见内核)
|
|
255
268
|
return strictJsonCopy(desc.value, depth + 1);
|
|
256
269
|
}
|
|
257
270
|
/** `none`: compares the DESCRIPTOR VIEW, read once ({@link strictJsonCopy}), and serializes only the copies — member values never go through `[[Get]]`. The ONE guard around the
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 沙箱 git 平面的 **origin 形** 与 **有效配置块 `sandboxGit`**(14.7.0)。纯、浏览器安全。
|
|
3
|
+
*
|
|
4
|
+
* ## 律(一条)
|
|
5
|
+
* **一个 origin 就是它自己的 WHATWG origin**:串等于 `new URL(串).origin`,且 scheme ∈ {http, https}。规范形 ——
|
|
6
|
+
* 小写 host、去缺省端口(https 的 443、http 的 80)、无尾斜杠、无 userinfo / 路径 / 查询 / 片段 —— 不是一张手写规则表,而是这一个等式的推论:
|
|
7
|
+
* 凡 WHATWG 会改写的串(大写、缺省端口、IPv4 简写、百分号编码的 host、串内制表符、前后空白、反斜杠……)都不等于自己的 origin,所以都不是规范形。
|
|
8
|
+
* 判官只有 {@link isSandboxGitOrigin} 一只;请求体的 `origins`、应答行的 `origin`、有效配置块的 `origin` 全读它(经 {@link SandboxGitOrigin})。
|
|
9
|
+
* 「这个串是不是 http(s) URL」归包内唯一的 URL 形判官 `httpUrlFlaws`(`./url-shape`),本模块只加「等于自己的 origin」这一格。
|
|
10
|
+
* origin 不表达子路径部署(`https://host/gitea` 不是 origin)—— 规范化器不存在:把带路径的串静默削成 origin 会把凭据送到运维没写的地方。
|
|
11
|
+
*
|
|
12
|
+
* ## 有效配置块 `sandboxGit`
|
|
13
|
+
* `/effective` 载荷上的**非域顶层键**(web-admin 按部署登记册投影,不是存储域):可选、无默认 —— 缺席 = 这个部署没有投影这一块。
|
|
14
|
+
* `readEffectiveWire` 对它走 open-world 逐字承运(从不校验、从不丢,`EffectiveWire.sandboxGit` 是 `unknown`);**判它的座位只有 {@link SandboxGitConfig}**,由消费方在用之前判。
|
|
15
|
+
*
|
|
16
|
+
* · `origins[i].origin` —— 本部署代理哪些 origin(规范形,见上);≤ {@link SANDBOX_GIT_MAX_ORIGINS} 项、两两不同。
|
|
17
|
+
* · `origins[i].connectUrl` —— 控制台「连接 Gitea(git)」页的绝对 https URL(非秘密:{@link SandboxGitConnectUrl})。
|
|
18
|
+
*
|
|
19
|
+
* 🔴 **块里 http origin 可读**:它为什么送不出凭据(传输不加密)是逐 origin 应答里的一个词(`insecure_transport`,`./api/credentials` 的判定序表第 1 行)
|
|
20
|
+
* 与能力读数里的一个词,不是块的校验拒。块若因一个 http origin 整块不可读,同块里的 https origin 一并失效,「逐 origin 说拒因」就无从谈起;
|
|
21
|
+
* http origin 只配路由、永不取值,是消费方的事。
|
|
22
|
+
*
|
|
23
|
+
* 解析剥未知键(与 `SessionMirrorPolicy` 同律:加字段恒为加性)。
|
|
24
|
+
*/
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
/** 一次最多代理的 origin 数(请求体、应答、有效配置块同一个上界)。 */
|
|
27
|
+
export declare const SANDBOX_GIT_MAX_ORIGINS = 16;
|
|
28
|
+
/**
|
|
29
|
+
* 判:`raw` 是不是规范形的 http(s) origin。合格 ⇔ 它是层级式 http(s) URL 且**逐字等于**自己的 WHATWG origin。
|
|
30
|
+
* 纯函数、不抛;非串由 {@link SandboxGitOrigin} 的 zod 形先拒。
|
|
31
|
+
*/
|
|
32
|
+
export declare function isSandboxGitOrigin(raw: string): boolean;
|
|
33
|
+
/** 一个规范形的 origin(`scheme://host[:port]`,scheme ∈ {http, https})。判官 {@link isSandboxGitOrigin};拒句是定句,不回显值。 */
|
|
34
|
+
export declare const SandboxGitOrigin: z.ZodString;
|
|
35
|
+
export type SandboxGitOrigin = z.infer<typeof SandboxGitOrigin>;
|
|
36
|
+
/**
|
|
37
|
+
* 一张 origin 表:每项是规范形、两两不同、至多 {@link SANDBOX_GIT_MAX_ORIGINS} 项。**空表合法**(有效配置块可以没有 origin);要求至少一项的座位(申领请求)自己 `.min(1)`。
|
|
38
|
+
* 重复的那一项被点名(坐标 = 第二次出现的下标)。
|
|
39
|
+
*/
|
|
40
|
+
export declare const SandboxGitOriginList: z.ZodArray<z.ZodString>;
|
|
41
|
+
export type SandboxGitOriginList = z.infer<typeof SandboxGitOriginList>;
|
|
42
|
+
/**
|
|
43
|
+
* 控制台「连接 Gitea(git)」页的绝对 URL:**https**,且是 {@link NON_SECRET_URL_NO_MIGRATION} 那一类(层级式 URL、无 userinfo、无片段、路径与查询里没有秘密形的值)。
|
|
44
|
+
* 它是 wire 输入,不是存量文档里的值,所以走「无迁移指路」那一只。
|
|
45
|
+
*/
|
|
46
|
+
export declare const SandboxGitConnectUrl: z.ZodString;
|
|
47
|
+
export type SandboxGitConnectUrl = z.infer<typeof SandboxGitConnectUrl>;
|
|
48
|
+
/** 有效配置块里的一个 origin 条目。 */
|
|
49
|
+
export declare const SandboxGitOriginEntry: z.ZodObject<{
|
|
50
|
+
origin: z.ZodString;
|
|
51
|
+
connectUrl: z.ZodString;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
export type SandboxGitOriginEntry = z.infer<typeof SandboxGitOriginEntry>;
|
|
54
|
+
/**
|
|
55
|
+
* 有效配置块 `sandboxGit`(`/effective` 载荷的非域顶层键,可选、无默认)。判它的**唯一座位**:消费方在用之前对 `wire.sandboxGit` 跑 `safeParse`。
|
|
56
|
+
* `origins` ≤ {@link SANDBOX_GIT_MAX_ORIGINS} 项,按 `origin` 两两不同(重复的那一项的 `origin` 字段被点名);空表合法。
|
|
57
|
+
*/
|
|
58
|
+
export declare const SandboxGitConfig: z.ZodObject<{
|
|
59
|
+
origins: z.ZodArray<z.ZodObject<{
|
|
60
|
+
origin: z.ZodString;
|
|
61
|
+
connectUrl: z.ZodString;
|
|
62
|
+
}, z.core.$strip>>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
export type SandboxGitConfig = z.infer<typeof SandboxGitConfig>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 沙箱 git 平面的 **origin 形** 与 **有效配置块 `sandboxGit`**(14.7.0)。纯、浏览器安全。
|
|
3
|
+
*
|
|
4
|
+
* ## 律(一条)
|
|
5
|
+
* **一个 origin 就是它自己的 WHATWG origin**:串等于 `new URL(串).origin`,且 scheme ∈ {http, https}。规范形 ——
|
|
6
|
+
* 小写 host、去缺省端口(https 的 443、http 的 80)、无尾斜杠、无 userinfo / 路径 / 查询 / 片段 —— 不是一张手写规则表,而是这一个等式的推论:
|
|
7
|
+
* 凡 WHATWG 会改写的串(大写、缺省端口、IPv4 简写、百分号编码的 host、串内制表符、前后空白、反斜杠……)都不等于自己的 origin,所以都不是规范形。
|
|
8
|
+
* 判官只有 {@link isSandboxGitOrigin} 一只;请求体的 `origins`、应答行的 `origin`、有效配置块的 `origin` 全读它(经 {@link SandboxGitOrigin})。
|
|
9
|
+
* 「这个串是不是 http(s) URL」归包内唯一的 URL 形判官 `httpUrlFlaws`(`./url-shape`),本模块只加「等于自己的 origin」这一格。
|
|
10
|
+
* origin 不表达子路径部署(`https://host/gitea` 不是 origin)—— 规范化器不存在:把带路径的串静默削成 origin 会把凭据送到运维没写的地方。
|
|
11
|
+
*
|
|
12
|
+
* ## 有效配置块 `sandboxGit`
|
|
13
|
+
* `/effective` 载荷上的**非域顶层键**(web-admin 按部署登记册投影,不是存储域):可选、无默认 —— 缺席 = 这个部署没有投影这一块。
|
|
14
|
+
* `readEffectiveWire` 对它走 open-world 逐字承运(从不校验、从不丢,`EffectiveWire.sandboxGit` 是 `unknown`);**判它的座位只有 {@link SandboxGitConfig}**,由消费方在用之前判。
|
|
15
|
+
*
|
|
16
|
+
* · `origins[i].origin` —— 本部署代理哪些 origin(规范形,见上);≤ {@link SANDBOX_GIT_MAX_ORIGINS} 项、两两不同。
|
|
17
|
+
* · `origins[i].connectUrl` —— 控制台「连接 Gitea(git)」页的绝对 https URL(非秘密:{@link SandboxGitConnectUrl})。
|
|
18
|
+
*
|
|
19
|
+
* 🔴 **块里 http origin 可读**:它为什么送不出凭据(传输不加密)是逐 origin 应答里的一个词(`insecure_transport`,`./api/credentials` 的判定序表第 1 行)
|
|
20
|
+
* 与能力读数里的一个词,不是块的校验拒。块若因一个 http origin 整块不可读,同块里的 https origin 一并失效,「逐 origin 说拒因」就无从谈起;
|
|
21
|
+
* http origin 只配路由、永不取值,是消费方的事。
|
|
22
|
+
*
|
|
23
|
+
* 解析剥未知键(与 `SessionMirrorPolicy` 同律:加字段恒为加性)。
|
|
24
|
+
*/
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
import { buildQuotedIssue } from "./quoted-issue.js";
|
|
27
|
+
import { NON_SECRET_URL_NO_MIGRATION } from "./types.js";
|
|
28
|
+
import { httpUrlFlaws } from "./url-shape.js";
|
|
29
|
+
/** 一次最多代理的 origin 数(请求体、应答、有效配置块同一个上界)。 */
|
|
30
|
+
export const SANDBOX_GIT_MAX_ORIGINS = 16;
|
|
31
|
+
/**
|
|
32
|
+
* 判:`raw` 是不是规范形的 http(s) origin。合格 ⇔ 它是层级式 http(s) URL 且**逐字等于**自己的 WHATWG origin。
|
|
33
|
+
* 纯函数、不抛;非串由 {@link SandboxGitOrigin} 的 zod 形先拒。
|
|
34
|
+
*/
|
|
35
|
+
export function isSandboxGitOrigin(raw) {
|
|
36
|
+
if (httpUrlFlaws(raw, []).length > 0)
|
|
37
|
+
return false;
|
|
38
|
+
return new URL(raw).origin === raw;
|
|
39
|
+
}
|
|
40
|
+
/** 一个规范形的 origin(`scheme://host[:port]`,scheme ∈ {http, https})。判官 {@link isSandboxGitOrigin};拒句是定句,不回显值。 */
|
|
41
|
+
export const SandboxGitOrigin = z
|
|
42
|
+
.string()
|
|
43
|
+
.refine(isSandboxGitOrigin, buildQuotedIssue `must be a canonical http(s) origin — scheme://host[:port] equal to its own WHATWG origin: lowercase host, no default port, no userinfo, path, query, fragment or trailing slash`);
|
|
44
|
+
/** 给定下标的 origin 在它之前已经出现过 ⇒ 下标。重复判在**规范形**之上(两个都是规范形的串,相等即同一个 origin)。 */
|
|
45
|
+
function repeatedAt(origins) {
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const repeats = [];
|
|
48
|
+
origins.forEach((o, i) => {
|
|
49
|
+
if (seen.has(o))
|
|
50
|
+
repeats.push(i);
|
|
51
|
+
seen.add(o);
|
|
52
|
+
});
|
|
53
|
+
return repeats;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 一张 origin 表:每项是规范形、两两不同、至多 {@link SANDBOX_GIT_MAX_ORIGINS} 项。**空表合法**(有效配置块可以没有 origin);要求至少一项的座位(申领请求)自己 `.min(1)`。
|
|
57
|
+
* 重复的那一项被点名(坐标 = 第二次出现的下标)。
|
|
58
|
+
*/
|
|
59
|
+
export const SandboxGitOriginList = z
|
|
60
|
+
.array(SandboxGitOrigin)
|
|
61
|
+
.max(SANDBOX_GIT_MAX_ORIGINS)
|
|
62
|
+
.superRefine((origins, ctx) => {
|
|
63
|
+
for (const i of repeatedAt(origins))
|
|
64
|
+
ctx.addIssue({ code: "custom", ...buildQuotedIssue `duplicate origin`, path: [i] });
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* 控制台「连接 Gitea(git)」页的绝对 URL:**https**,且是 {@link NON_SECRET_URL_NO_MIGRATION} 那一类(层级式 URL、无 userinfo、无片段、路径与查询里没有秘密形的值)。
|
|
68
|
+
* 它是 wire 输入,不是存量文档里的值,所以走「无迁移指路」那一只。
|
|
69
|
+
*/
|
|
70
|
+
export const SandboxGitConnectUrl = NON_SECRET_URL_NO_MIGRATION.refine((u) => URL.canParse(u) && new URL(u).protocol === "https:", buildQuotedIssue `must be an https URL — the connect page is opened by a person and carries their session`);
|
|
71
|
+
/** 有效配置块里的一个 origin 条目。 */
|
|
72
|
+
export const SandboxGitOriginEntry = z.object({
|
|
73
|
+
/** 本部署代理的 origin(规范形)。http 在这里可读(见模块头注)。 */
|
|
74
|
+
origin: SandboxGitOrigin,
|
|
75
|
+
/** 该 origin 的「连接」页,绝对 https URL。 */
|
|
76
|
+
connectUrl: SandboxGitConnectUrl,
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* 有效配置块 `sandboxGit`(`/effective` 载荷的非域顶层键,可选、无默认)。判它的**唯一座位**:消费方在用之前对 `wire.sandboxGit` 跑 `safeParse`。
|
|
80
|
+
* `origins` ≤ {@link SANDBOX_GIT_MAX_ORIGINS} 项,按 `origin` 两两不同(重复的那一项的 `origin` 字段被点名);空表合法。
|
|
81
|
+
*/
|
|
82
|
+
export const SandboxGitConfig = z
|
|
83
|
+
.object({
|
|
84
|
+
origins: z.array(SandboxGitOriginEntry).max(SANDBOX_GIT_MAX_ORIGINS),
|
|
85
|
+
})
|
|
86
|
+
.superRefine((block, ctx) => {
|
|
87
|
+
for (const i of repeatedAt(block.origins.map((e) => e.origin)))
|
|
88
|
+
ctx.addIssue({ code: "custom", ...buildQuotedIssue `duplicate origin`, path: ["origins", i, "origin"] });
|
|
89
|
+
});
|
package/dist/scheduler-store.js
CHANGED
|
@@ -1,22 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ownDeclaredValueOf } from './own-key-read.js';
|
|
2
|
+
import { isJsonArray, isPlainJsonObject } from './plain-json-object.js';
|
|
2
3
|
import { isSessionLifetime } from './scheduler-lifetime.js';
|
|
3
4
|
import { migrateSchedulerRecords } from './scheduler-store-migration.js';
|
|
4
5
|
/** Structural validation of one record (required fields + a well-formed `when`). Unknown fields are allowed. */
|
|
5
6
|
export function isSchedulerRecord(v) {
|
|
6
7
|
if (!isPlainJsonObject(v))
|
|
7
8
|
return false;
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
// a type guard, so every field is read STRICTLY from the record's own keys (own, enumerable, data — the kernel's `ownDeclaredValueOf`): an inherited field is absent
|
|
10
|
+
// (a polluted `Object.prototype.prompt` is not a prompt the record carries); an accessor / hidden field, or one that cannot be read ⇒ not a record — no getter is called,
|
|
11
|
+
// so what the caller reads from the record next is what was judged
|
|
12
|
+
const field = (o, key) => {
|
|
13
|
+
const read = ownDeclaredValueOf(o, key);
|
|
14
|
+
return read.ok ? read.value : undefined;
|
|
15
|
+
};
|
|
16
|
+
if (typeof field(v, 'id') !== 'string' || typeof field(v, 'scope') !== 'string' || typeof field(v, 'prompt') !== 'string' || typeof field(v, 'createdMs') !== 'number')
|
|
10
17
|
return false;
|
|
11
|
-
const w =
|
|
18
|
+
const w = field(v, 'when');
|
|
12
19
|
if (!isPlainJsonObject(w))
|
|
13
20
|
return false;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
const kind = field(w, 'kind');
|
|
22
|
+
if (kind === 'cron')
|
|
23
|
+
return typeof field(w, 'expr') === 'string';
|
|
24
|
+
if (kind === 'at')
|
|
25
|
+
return typeof field(w, 'atMs') === 'number';
|
|
26
|
+
if (kind === 'delay')
|
|
27
|
+
return typeof field(w, 'delaySec') === 'number';
|
|
20
28
|
return false;
|
|
21
29
|
}
|
|
22
30
|
/** 一条会话链上的记录过期多久算「永驻孤儿」:`lifetime: "session"` ∧ `sessionId` 在场 ∧ `nextRunMs` 已过去这么久 ⇒ 读口丢弃并告警(7 天)。 */
|
|
@@ -38,7 +46,7 @@ export function parseSchedulerStore(text, opts) {
|
|
|
38
46
|
catch {
|
|
39
47
|
return { records: [], warnings: [{ kind: 'unreadable-file', why: 'not-json' }] };
|
|
40
48
|
}
|
|
41
|
-
if (!
|
|
49
|
+
if (!isJsonArray(parsed))
|
|
42
50
|
return { records: [], warnings: [{ kind: 'unreadable-file', why: 'not-an-array' }] };
|
|
43
51
|
const warnings = [];
|
|
44
52
|
const kept = [];
|
package/dist/settings-units.js
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* the engine's env catalog, whose one implementation is the server's.
|
|
43
43
|
*/
|
|
44
44
|
import { classifyHooksConfig } from "./hooks-config.js";
|
|
45
|
+
import { captureJson, captureStringArray, ownEntriesOf } from "./own-key-read.js";
|
|
45
46
|
import { isPlainJsonObject } from "./plain-json-object.js";
|
|
46
47
|
import { SAFETY_MERGE_SPEC, resolveMemberTighten, tightenVerdict } from "./safety-merge-spec.js";
|
|
47
48
|
/** The settings-document units (see the file header). `path` is the unit's place in a settings document. */
|
|
@@ -98,20 +99,17 @@ const UNREADABLE = { ok: false, reason: "unreadable" };
|
|
|
98
99
|
const LAUNCH_ONLY = { ok: false, reason: "launch_only" };
|
|
99
100
|
/** `stringList`: an array whose every element is a non-empty string (a hole reads as `undefined`, so a sparse array is not one). */
|
|
100
101
|
function isNonEmptyStringList(v) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for (let i = 0; i < v.length; i++) {
|
|
104
|
-
const x = v[i];
|
|
105
|
-
if (typeof x !== "string" || x.length === 0)
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
return true;
|
|
102
|
+
const list = captureStringArray(v, "strict"); // the package's one string-array reading, strict: the value is handed back as is, so an accessor / hidden index ⇒ not a list (no getter is called)
|
|
103
|
+
return list !== undefined && list.every((x) => x.length > 0);
|
|
109
104
|
}
|
|
110
|
-
/** `envMap`: a plain object (prototype `Object.prototype` or `null`), non-empty keys, string values.
|
|
105
|
+
/** `envMap`: a plain object (prototype `Object.prototype` or `null`), non-empty keys, string values. The value is handed back AS IS, so its members are read strictly —
|
|
106
|
+
* the package's one whole-container read, strict (`ownEntriesOf`: one `ownKeys`, each own string key's descriptor asked once — symbol keys never; never `[[Get]]`, never a
|
|
107
|
+
* proxy's `get` trap): an accessor member or a non-enumerable member ⇒ not an env map (no getter is called — what the caller reads next is what was judged); unreadable ⇒ not one. */
|
|
111
108
|
function isEnvMap(v) {
|
|
112
109
|
if (!isPlainJsonObject(v))
|
|
113
110
|
return false;
|
|
114
|
-
|
|
111
|
+
const members = ownEntriesOf(v);
|
|
112
|
+
return members.ok && members.value.every(([k, x]) => k.length > 0 && typeof x === "string");
|
|
115
113
|
}
|
|
116
114
|
/** What a declared member's value class adds on top of its comparator's domain — read through the comparator
|
|
117
115
|
* {@link resolveMemberTighten} derived from the row, so the seat is interpreted in one place: `list` ⇒ setUnion / setNarrower,
|
|
@@ -144,8 +142,12 @@ function shapedReading(unit, row, rule, raw) {
|
|
|
144
142
|
case "killSwitch":
|
|
145
143
|
// CC spells ON "disable"; the boolean itself is the member comparator's domain
|
|
146
144
|
return { ok: true, value: raw === "disable" ? true : raw };
|
|
147
|
-
case "hooks":
|
|
148
|
-
|
|
145
|
+
case "hooks": {
|
|
146
|
+
// the value is handed back AS IS, so it is read strictly (as every unit is): the whole tree is snapshotted by the kernel's strict whole-container read first —
|
|
147
|
+
// an accessor anywhere or a non-enumerable member ⇒ not readable (no getter is called) — and the classifier judges the snapshot, not the caller's object
|
|
148
|
+
const snapshot = captureJson(raw);
|
|
149
|
+
return snapshot.ok && classifyHooksConfig({ hooks: snapshot.value }).outcome === "loaded" ? { ok: true, value: raw } : UNREADABLE;
|
|
150
|
+
}
|
|
149
151
|
case "envMap":
|
|
150
152
|
return isEnvMap(raw) ? { ok: true, value: raw } : UNREADABLE;
|
|
151
153
|
case "nonEmptyString":
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档位词(纯、零 I/O、浏览器安全;**包内叶子模块,不出包**):引擎的档序与 CC 档位别名、从某档起向低档的梯,与「一个目录词在档位展开之后落到哪条条目」。
|
|
3
|
+
* 本包里档位词的唯一一处:default 角色的档梯(`./default-model`)与写者计划助手的落点(`./env-model-table`)都从这里取。
|
|
4
|
+
* 镜像 core(`DEFAULT_TIER_ORDER` / `CC_MODEL_TIER_ALIASES` / `expandTiers`):`test/tier-words-parity-14.8.0` 对 devDependency 里精确钉住的那一版 core 对拍。
|
|
5
|
+
*/
|
|
6
|
+
/** 引擎的档序(高 → 低)。 */
|
|
7
|
+
export declare const TIER_ORDER: readonly ["ultra", "max", "pro", "flash", "lite"];
|
|
8
|
+
/** 一个档位词。 */
|
|
9
|
+
export type TierWord = (typeof TIER_ORDER)[number];
|
|
10
|
+
/** CC 档位词 → 引擎档位。 */
|
|
11
|
+
export declare const CC_TIER_ALIASES: {
|
|
12
|
+
readonly fable: "max";
|
|
13
|
+
readonly mythos: "max";
|
|
14
|
+
readonly opus: "pro";
|
|
15
|
+
readonly sonnet: "flash";
|
|
16
|
+
readonly haiku: "lite";
|
|
17
|
+
};
|
|
18
|
+
/** 档序里从 `S` 起(含)向低档的那一段,字面元组型。 */
|
|
19
|
+
type LadderFrom<S extends TierWord, T extends readonly TierWord[] = typeof TIER_ORDER> = T extends readonly [infer H, ...infer R extends readonly TierWord[]] ? H extends S ? Readonly<T> : LadderFrom<S, R> : readonly [];
|
|
20
|
+
/** 一档的梯:档序里从它起(含)向低档的那一段(没绑的档沿它往下落)。 */
|
|
21
|
+
export declare function tierLadderFrom<S extends TierWord>(tier: S): LadderFrom<S>;
|
|
22
|
+
/**
|
|
23
|
+
* 一个目录词(条目名 / 档位词 / CC 档位别名 / `best`)在**档位展开之后**落到哪条目录条目名 —— 与引擎装配档位表的两步同答:
|
|
24
|
+
* 先摘掉活跃组里绑定目标不可服务的档(引擎的生效装配在交给 core 之前做这一步,并 warn 点名),再按 core `expandTiers` 展开
|
|
25
|
+
* (对拍 devDependency 那一版 core 的「摘后表」)。所以这里只有一条绑定规则:**一档的绑定只在它点名一条可服务条目时算数**,否则当作没绑。展开规则:
|
|
26
|
+
* · 目录自己的键先赢(`has(word)`;一条就叫 `flash` 的条目遮住档位词);
|
|
27
|
+
* · 档位词 ⇒ 它的梯({@link tierLadderFrom})上第一个有绑定的档;摘完没有任何绑定 ⇒ 只认目录键;
|
|
28
|
+
* · CC 档位别名 ⇒ 经它对应的档位;`best` ⇒ 档序里第一个有绑定的档;别的词 ⇒ 落不到。
|
|
29
|
+
* `has` = 调用方的「这个名是目录里一条可服务(启用)的条目」;`tiers` = `resolveActiveTiers` 的读数(原表,未摘)—— 它的键只有五个档位词
|
|
30
|
+
* (档位组的绑定表在 schema 里只收这五个键,别的键被剥掉),所以只按档位词读表。
|
|
31
|
+
*/
|
|
32
|
+
export declare function landCatalogWord(word: string, has: (name: string) => boolean, tiers: Readonly<Partial<Record<TierWord, string>>> | undefined): string | undefined;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 档位词(纯、零 I/O、浏览器安全;**包内叶子模块,不出包**):引擎的档序与 CC 档位别名、从某档起向低档的梯,与「一个目录词在档位展开之后落到哪条条目」。
|
|
3
|
+
* 本包里档位词的唯一一处:default 角色的档梯(`./default-model`)与写者计划助手的落点(`./env-model-table`)都从这里取。
|
|
4
|
+
* 镜像 core(`DEFAULT_TIER_ORDER` / `CC_MODEL_TIER_ALIASES` / `expandTiers`):`test/tier-words-parity-14.8.0` 对 devDependency 里精确钉住的那一版 core 对拍。
|
|
5
|
+
*/
|
|
6
|
+
/** 引擎的档序(高 → 低)。 */
|
|
7
|
+
export const TIER_ORDER = ["ultra", "max", "pro", "flash", "lite"];
|
|
8
|
+
/** CC 档位词 → 引擎档位。 */
|
|
9
|
+
export const CC_TIER_ALIASES = { fable: "max", mythos: "max", opus: "pro", sonnet: "flash", haiku: "lite" };
|
|
10
|
+
/** 一档的梯:档序里从它起(含)向低档的那一段(没绑的档沿它往下落)。 */
|
|
11
|
+
export function tierLadderFrom(tier) {
|
|
12
|
+
return TIER_ORDER.slice(TIER_ORDER.indexOf(tier));
|
|
13
|
+
}
|
|
14
|
+
const isTierWord = (word) => TIER_ORDER.includes(word);
|
|
15
|
+
/**
|
|
16
|
+
* 一个目录词(条目名 / 档位词 / CC 档位别名 / `best`)在**档位展开之后**落到哪条目录条目名 —— 与引擎装配档位表的两步同答:
|
|
17
|
+
* 先摘掉活跃组里绑定目标不可服务的档(引擎的生效装配在交给 core 之前做这一步,并 warn 点名),再按 core `expandTiers` 展开
|
|
18
|
+
* (对拍 devDependency 那一版 core 的「摘后表」)。所以这里只有一条绑定规则:**一档的绑定只在它点名一条可服务条目时算数**,否则当作没绑。展开规则:
|
|
19
|
+
* · 目录自己的键先赢(`has(word)`;一条就叫 `flash` 的条目遮住档位词);
|
|
20
|
+
* · 档位词 ⇒ 它的梯({@link tierLadderFrom})上第一个有绑定的档;摘完没有任何绑定 ⇒ 只认目录键;
|
|
21
|
+
* · CC 档位别名 ⇒ 经它对应的档位;`best` ⇒ 档序里第一个有绑定的档;别的词 ⇒ 落不到。
|
|
22
|
+
* `has` = 调用方的「这个名是目录里一条可服务(启用)的条目」;`tiers` = `resolveActiveTiers` 的读数(原表,未摘)—— 它的键只有五个档位词
|
|
23
|
+
* (档位组的绑定表在 schema 里只收这五个键,别的键被剥掉),所以只按档位词读表。
|
|
24
|
+
*/
|
|
25
|
+
export function landCatalogWord(word, has, tiers) {
|
|
26
|
+
if (has(word))
|
|
27
|
+
return word;
|
|
28
|
+
if (tiers === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
/** 一档的绑定:只在它点名一条可服务条目时算数(否则引擎已摘掉它)。 */
|
|
31
|
+
const bound = (tier) => {
|
|
32
|
+
const target = Object.hasOwn(tiers, tier) ? tiers[tier] : undefined;
|
|
33
|
+
return target !== undefined && has(target) ? target : undefined;
|
|
34
|
+
};
|
|
35
|
+
const firstBound = (ladder) => ladder.map(bound).find((landed) => landed !== undefined);
|
|
36
|
+
if (isTierWord(word))
|
|
37
|
+
return firstBound(tierLadderFrom(word));
|
|
38
|
+
if (Object.hasOwn(CC_TIER_ALIASES, word))
|
|
39
|
+
return firstBound(tierLadderFrom(CC_TIER_ALIASES[word]));
|
|
40
|
+
if (word === "best")
|
|
41
|
+
return firstBound(TIER_ORDER);
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1830,11 +1830,11 @@ export interface SkillsManifest {
|
|
|
1830
1830
|
* legal hash scheme. */
|
|
1831
1831
|
export declare const SkillsManifestSchema: z.ZodObject<{
|
|
1832
1832
|
skills: z.ZodPrefault<z.ZodArray<z.ZodObject<{
|
|
1833
|
+
description: z.ZodDefault<z.ZodString>;
|
|
1833
1834
|
name: z.ZodString;
|
|
1834
1835
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1835
1836
|
notes: z.ZodOptional<z.ZodString>;
|
|
1836
1837
|
rosters: z.ZodPrefault<z.ZodArray<z.ZodString>>;
|
|
1837
|
-
description: z.ZodDefault<z.ZodString>;
|
|
1838
1838
|
scenarios: z.ZodPrefault<z.ZodArray<z.ZodString>>;
|
|
1839
1839
|
allowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1840
1840
|
disallowedTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -1885,6 +1885,8 @@ export declare const McpServerSpec: z.ZodObject<{
|
|
|
1885
1885
|
local: "local";
|
|
1886
1886
|
execution: "execution";
|
|
1887
1887
|
}>>;
|
|
1888
|
+
sessionMetaKey: z.ZodOptional<z.ZodUnknown>;
|
|
1889
|
+
startupTimeoutMs: z.ZodOptional<z.ZodUnknown>;
|
|
1888
1890
|
source: z.ZodOptional<z.ZodUnknown>;
|
|
1889
1891
|
toolFaces: z.ZodOptional<z.ZodUnknown>;
|
|
1890
1892
|
toolAxes: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -1918,6 +1920,8 @@ export declare const McpConfig: z.ZodObject<{
|
|
|
1918
1920
|
local: "local";
|
|
1919
1921
|
execution: "execution";
|
|
1920
1922
|
}>>;
|
|
1923
|
+
sessionMetaKey: z.ZodOptional<z.ZodUnknown>;
|
|
1924
|
+
startupTimeoutMs: z.ZodOptional<z.ZodUnknown>;
|
|
1921
1925
|
source: z.ZodOptional<z.ZodUnknown>;
|
|
1922
1926
|
toolFaces: z.ZodOptional<z.ZodUnknown>;
|
|
1923
1927
|
toolAxes: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -2520,6 +2524,8 @@ export declare const DOMAIN_SCHEMAS: {
|
|
|
2520
2524
|
local: "local";
|
|
2521
2525
|
execution: "execution";
|
|
2522
2526
|
}>>;
|
|
2527
|
+
sessionMetaKey: z.ZodOptional<z.ZodUnknown>;
|
|
2528
|
+
startupTimeoutMs: z.ZodOptional<z.ZodUnknown>;
|
|
2523
2529
|
source: z.ZodOptional<z.ZodUnknown>;
|
|
2524
2530
|
toolFaces: z.ZodOptional<z.ZodUnknown>;
|
|
2525
2531
|
toolAxes: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -3219,10 +3225,14 @@ export type EffectiveKey = z.infer<typeof EffectiveKey>;
|
|
|
3219
3225
|
* `prompts` is the second wire divergence: on the wire it is {@link EffectivePromptsWire} (`{schemaVersion: 2, artifact}` — the one
|
|
3220
3226
|
* artifact {@link promptArtifactFor} selected for the pulling worker, stamped with its publish provenance), carried verbatim by the
|
|
3221
3227
|
* read leg and judged by the consumer (that shape, then the engine's verify) — optional (absent = nothing published, or the publish
|
|
3222
|
-
* gate is closed), never the storage shape.
|
|
3228
|
+
* gate is closed), never the storage shape.
|
|
3229
|
+
* `sandboxGit` (14.7.0) is the third wire-only key and the third carried verbatim: the sandbox git plane's origin block — web-admin projects it from the
|
|
3230
|
+
* deployment's registry, it is NOT a stored domain, so `buildEffective` never produces it and `readEffectiveWire` neither validates nor drops it. Optional with
|
|
3231
|
+
* no default (absent = the deployment projects no such block); the ONE seat that judges it is `SandboxGitConfig` (`./sandbox-git`), run by the consumer before use. */
|
|
3223
3232
|
export type EffectiveWire = Omit<EffectiveConfig, "skills" | "prompts"> & {
|
|
3224
3233
|
skills: SkillsManifest;
|
|
3225
3234
|
prompts?: unknown;
|
|
3235
|
+
sandboxGit?: unknown;
|
|
3226
3236
|
};
|
|
3227
3237
|
/** Reserved audit "domain" for RBAC/user-management events (not a config domain → no snapshot, not
|
|
3228
3238
|
* rollback-able). Lets user changes share the one audit stream the Audit page already renders. */
|