@fro.bot/systematic 2.11.0 → 2.12.1

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/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
1
  import type { Plugin } from '@opencode-ai/plugin';
2
+ export declare const applyBootstrapContent: (output: {
3
+ system: string[];
4
+ }, content: string) => void;
2
5
  declare const SystematicPlugin: Plugin;
3
6
  export default SystematicPlugin;
package/dist/index.js CHANGED
@@ -1,18 +1,19 @@
1
1
  // @bun
2
2
  import {
3
+ AgentOverlaySchema,
4
+ CategoryOverlaySchema,
5
+ assertSourceCategoryModelDefaults,
3
6
  convertFileWithCache,
4
7
  extractAgentFrontmatter,
5
8
  extractCommandFrontmatter,
6
9
  findAgentsInDir,
7
10
  findCommandsInDir,
8
11
  findSkillsInDir,
9
- isAgentMode,
10
- isPermissionSetting,
11
12
  isRecord,
12
13
  loadConfig,
13
14
  loadConfigWithSources,
14
15
  parseFrontmatter
15
- } from "./index-mfy9dbdx.js";
16
+ } from "./index-b4ht76qd.js";
16
17
 
17
18
  // src/index.ts
18
19
  import fs4 from "fs";
@@ -88,19 +89,6 @@ var SOURCE_CATEGORY_MODEL_DEFAULTS = {
88
89
  review: ["anthropic/claude-opus-4.7", "openai/gpt-5.5"],
89
90
  workflow: ["openai/gpt-5.4-mini", "anthropic/claude-haiku-4-5"]
90
91
  };
91
- var ALLOWED_OVERLAY_FIELDS = new Set([
92
- "model",
93
- "variant",
94
- "temperature",
95
- "top_p",
96
- "permission",
97
- "mode",
98
- "color",
99
- "steps",
100
- "hidden",
101
- "disable",
102
- "skills"
103
- ]);
104
92
  function buildBundledAgentInventory(agentsDir, disabledAgents) {
105
93
  const categories = readCategoryDirs(agentsDir);
106
94
  const agentsByQualifiedId = {};
@@ -221,17 +209,7 @@ function assertSourceCategoryModelCoverage(categories) {
221
209
  }
222
210
  }
223
211
  function validateSourceCategoryModelDefaults(defaults = SOURCE_CATEGORY_MODEL_DEFAULTS) {
224
- for (const [category, value] of Object.entries(defaults)) {
225
- if (!Array.isArray(value)) {
226
- throw new Error(`Source category model defaults: ${category} must be a non-empty array of provider/model strings`);
227
- }
228
- if (value.length === 0) {
229
- throw new Error(`Source category model defaults: ${category} must be a non-empty array of provider/model strings`);
230
- }
231
- for (const [index, model] of value.entries()) {
232
- validateModel("source category model defaults", `source category model defaults.${category}[${index}]`, model);
233
- }
234
- }
212
+ assertSourceCategoryModelDefaults(defaults);
235
213
  }
236
214
  function validateExactAgentOverlays(inventory, overlays, nativeAgents, enabledSkills) {
237
215
  const result = [];
@@ -281,150 +259,35 @@ function validateCategoryOverlays(inventory, overlays, enabledSkills) {
281
259
  }
282
260
  return result;
283
261
  }
284
- function validateOverlayFields(overlay, targetType, enabledSkills) {
285
- if (Object.hasOwn(overlay.value, "skills") && hasPermissionSkill(overlay.value.permission)) {
286
- throwConfigError(overlay.sourcePath, overlay.keyPath, "cannot set both skills and permission.skill in the same overlay object");
287
- }
288
- for (const [field, value] of Object.entries(overlay.value)) {
289
- const keyPath = `${overlay.keyPath}.${field}`;
290
- if (!ALLOWED_OVERLAY_FIELDS.has(field)) {
291
- throwConfigError(overlay.sourcePath, keyPath, `unsupported agent overlay field "${field}"`);
292
- }
293
- if (targetType === "category" && field === "disable") {
294
- throwConfigError(overlay.sourcePath, keyPath, "disable is only valid for exact agent overlays");
295
- }
296
- validateOverlayFieldValue(overlay.sourcePath, keyPath, field, value, enabledSkills);
297
- }
298
- }
299
262
  function hasPermissionSkill(permission) {
300
263
  return isRecord(permission) && Object.hasOwn(permission, "skill");
301
264
  }
302
- function validateOverlayFieldValue(sourcePath, keyPath, field, value, enabledSkills) {
303
- switch (field) {
304
- case "model":
305
- if (value === null)
306
- return;
307
- validateModel(sourcePath, keyPath, value);
308
- return;
309
- case "variant":
310
- validateNonEmptyString(sourcePath, keyPath, value);
311
- return;
312
- case "temperature":
313
- validateTemperature(sourcePath, keyPath, value);
314
- return;
315
- case "top_p":
316
- validateTopP(sourcePath, keyPath, value);
317
- return;
318
- case "permission":
319
- validatePermission(sourcePath, keyPath, value);
320
- return;
321
- case "mode":
322
- validateMode(sourcePath, keyPath, value);
323
- return;
324
- case "color":
325
- validateColor(sourcePath, keyPath, value);
326
- return;
327
- case "steps":
328
- validatePositiveInteger(sourcePath, keyPath, value);
329
- return;
330
- case "hidden":
331
- case "disable":
332
- validateBoolean(sourcePath, keyPath, value);
333
- return;
334
- case "skills":
335
- validateSkills(sourcePath, keyPath, value, enabledSkills);
336
- return;
337
- }
338
- }
339
- function validateModel(sourcePath, keyPath, value) {
340
- if (typeof value !== "string") {
341
- throwConfigError(sourcePath, keyPath, "must be a provider/model string");
342
- }
343
- if (value !== value.trim() || /\s/.test(value)) {
344
- throwConfigError(sourcePath, keyPath, "must be a provider/model string");
345
- }
346
- const slashIndex = value.indexOf("/");
347
- if (value === "" || slashIndex <= 0 || slashIndex === value.length - 1) {
348
- throwConfigError(sourcePath, keyPath, "must be a provider/model string");
349
- }
350
- }
351
- function validateNonEmptyString(sourcePath, keyPath, value) {
352
- if (typeof value !== "string" || value === "" || value !== value.trim()) {
353
- throwConfigError(sourcePath, keyPath, "must be a non-empty string");
354
- }
355
- }
356
- function validateTemperature(sourcePath, keyPath, value) {
357
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
358
- throwConfigError(sourcePath, keyPath, "must be a non-negative finite number");
359
- }
360
- }
361
- function validateTopP(sourcePath, keyPath, value) {
362
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
363
- throwConfigError(sourcePath, keyPath, "must be a number from 0 to 1");
364
- }
365
- }
366
- function validatePositiveInteger(sourcePath, keyPath, value) {
367
- if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
368
- throwConfigError(sourcePath, keyPath, "must be a positive integer");
369
- }
370
- }
371
- function validateBoolean(sourcePath, keyPath, value) {
372
- if (typeof value !== "boolean") {
373
- throwConfigError(sourcePath, keyPath, "must be a boolean");
374
- }
375
- }
376
- function validateMode(sourcePath, keyPath, value) {
377
- if (!isAgentMode(value)) {
378
- throwConfigError(sourcePath, keyPath, "must be one of: subagent, primary, all");
265
+ function validateOverlayFields(overlay, targetType, enabledSkills) {
266
+ if (Object.hasOwn(overlay.value, "skills") && hasPermissionSkill(overlay.value.permission)) {
267
+ throwConfigError(overlay.sourcePath, overlay.keyPath, "cannot set both skills and permission.skill in the same overlay object");
379
268
  }
269
+ const result = parseOverlayShape(overlay, targetType);
270
+ validateOverlaySkills(overlay, result.skills, enabledSkills);
380
271
  }
381
- function validateColor(sourcePath, keyPath, value) {
382
- if (typeof value !== "string" || value !== value.trim() || !isOpenCodeColor(value)) {
383
- throwConfigError(sourcePath, keyPath, "must be an OpenCode-compatible color string");
272
+ function parseOverlayShape(overlay, targetType) {
273
+ const schema = targetType === "agent" ? AgentOverlaySchema : CategoryOverlaySchema;
274
+ const result = schema.safeParse(overlay.value);
275
+ if (!result.success) {
276
+ throwOverlaySchemaError(overlay, result.error.issues[0]);
384
277
  }
278
+ return { skills: result.data.skills };
385
279
  }
386
- function isOpenCodeColor(value) {
387
- if (value === "")
388
- return false;
389
- if (/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value))
390
- return true;
391
- return /^[a-zA-Z][a-zA-Z0-9-]*$/.test(value);
280
+ function throwOverlaySchemaError(overlay, issue) {
281
+ const zodPath = issue.code === "unrecognized_keys" ? issue.message.match(/"([^"]+)"/)?.[1] ?? issue.path.join(".") : issue.path.join(".");
282
+ const fullPath = zodPath ? `${overlay.keyPath}.${zodPath}` : overlay.keyPath;
283
+ throwConfigError(overlay.sourcePath, fullPath, issue.message);
392
284
  }
393
- function validateSkills(sourcePath, keyPath, value, enabledSkills) {
394
- if (!Array.isArray(value) || !value.every((skill) => typeof skill === "string" && skill !== "" && skill === skill.trim())) {
395
- throwConfigError(sourcePath, keyPath, "must be an array of non-empty strings");
396
- }
397
- if (!enabledSkills)
285
+ function validateOverlaySkills(overlay, skills, enabledSkills) {
286
+ if (!enabledSkills || !skills)
398
287
  return;
399
- for (const skill of value) {
288
+ for (const skill of skills) {
400
289
  if (!enabledSkills.has(skill)) {
401
- throwConfigError(sourcePath, keyPath, `unknown or disabled skill "${skill}"`);
402
- }
403
- }
404
- }
405
- function validatePermission(sourcePath, keyPath, value) {
406
- if (!isRecord(value)) {
407
- throwConfigError(sourcePath, keyPath, "must be an object");
408
- }
409
- for (const [toolKey, rule] of Object.entries(value)) {
410
- if (toolKey.trim() === "") {
411
- throwConfigError(sourcePath, `${keyPath}.${toolKey}`, "must use a non-empty tool key");
412
- }
413
- validatePermissionRule(sourcePath, `${keyPath}.${toolKey}`, rule);
414
- }
415
- }
416
- function validatePermissionRule(sourcePath, keyPath, value) {
417
- if (isPermissionSetting(value))
418
- return;
419
- if (!isRecord(value)) {
420
- throwConfigError(sourcePath, keyPath, "must be ask, allow, deny, or an object of pattern rules");
421
- }
422
- for (const [pattern, setting] of Object.entries(value)) {
423
- if (pattern.trim() === "") {
424
- throwConfigError(sourcePath, `${keyPath}.${pattern}`, "must use a non-empty permission pattern");
425
- }
426
- if (!isPermissionSetting(setting)) {
427
- throwConfigError(sourcePath, `${keyPath}.${pattern}`, "must be ask, allow, or deny");
290
+ throwConfigError(overlay.sourcePath, `${overlay.keyPath}.skills`, `unknown or disabled skill "${skill}"`);
428
291
  }
429
292
  }
430
293
  }
@@ -823,29 +686,6 @@ function registerSkillsPaths(config, skillsDir) {
823
686
  };
824
687
  }
825
688
 
826
- // src/lib/plugin-singleton.ts
827
- var SINGLETON_KEY = Symbol.for("systematic.singleton.v1");
828
- async function plugInOnce({
829
- doInit,
830
- pid
831
- }) {
832
- const currentPid = pid ?? process.pid;
833
- const g = globalThis;
834
- const existing = g[SINGLETON_KEY];
835
- if (existing && existing.pid === currentPid) {
836
- const hooks2 = await existing.hooksPromise;
837
- return { isFirst: false, hooks: hooks2 };
838
- }
839
- const hooksPromise = doInit();
840
- g[SINGLETON_KEY] = {
841
- pid: currentPid,
842
- loadedAt: Date.now(),
843
- hooksPromise
844
- };
845
- const hooks = await hooksPromise;
846
- return { isFirst: true, hooks };
847
- }
848
-
849
689
  // src/lib/skill-tool.ts
850
690
  import fs3 from "fs";
851
691
  import path4 from "path";
@@ -1008,8 +848,26 @@ var bundledSkillsDir = path5.join(packageRoot, "skills");
1008
848
  var bundledAgentsDir = path5.join(packageRoot, "agents");
1009
849
  var bundledCommandsDir = path5.join(packageRoot, "commands");
1010
850
  var packageJsonPath = path5.join(packageRoot, "package.json");
1011
- var hasLoggedInit = false;
851
+ var BOOTSTRAP_MARKER_OPEN = "<SYSTEMATIC_WORKFLOWS>";
852
+ var BOOTSTRAP_MARKER_CLOSE = "</SYSTEMATIC_WORKFLOWS>";
853
+ var findBootstrapMarkerBlock = (entry) => {
854
+ const start = entry.indexOf(BOOTSTRAP_MARKER_OPEN);
855
+ if (start === -1)
856
+ return null;
857
+ const closeStart = entry.indexOf(BOOTSTRAP_MARKER_CLOSE, start + BOOTSTRAP_MARKER_OPEN.length);
858
+ if (closeStart === -1)
859
+ return null;
860
+ return { start, end: closeStart + BOOTSTRAP_MARKER_CLOSE.length };
861
+ };
1012
862
  var applyBootstrapContent = (output, content) => {
863
+ for (let i = 0;i < output.system.length; i++) {
864
+ const entry = output.system[i];
865
+ const block = findBootstrapMarkerBlock(entry);
866
+ if (block !== null) {
867
+ output.system[i] = entry.slice(0, block.start) + content + entry.slice(block.end);
868
+ return;
869
+ }
870
+ }
1013
871
  if (output.system.length > 0) {
1014
872
  output.system[output.system.length - 1] += `
1015
873
 
@@ -1030,6 +888,7 @@ var getPackageVersion = () => {
1030
888
  }
1031
889
  };
1032
890
  var initializePlugin = async ({ client, directory }) => {
891
+ let hasLoggedInit = false;
1033
892
  const config = loadConfig(directory);
1034
893
  const bootstrapContent = getBootstrapContent(config, { bundledSkillsDir });
1035
894
  const configHandler = createConfigHandler({
@@ -1088,12 +947,10 @@ var initializePlugin = async ({ client, directory }) => {
1088
947
  };
1089
948
  };
1090
949
  var SystematicPlugin = async (input) => {
1091
- const { hooks } = await plugInOnce({
1092
- doInit: () => initializePlugin(input)
1093
- });
1094
- return hooks;
950
+ return initializePlugin(input);
1095
951
  };
1096
952
  var src_default = SystematicPlugin;
1097
953
  export {
1098
- src_default as default
954
+ src_default as default,
955
+ applyBootstrapContent
1099
956
  };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Canonical OpenCode-accepted agent color tokens.
3
+ *
4
+ * These values are validated by OpenCode's `/config` HttpApi schema;
5
+ * any other value is rejected and surfaces to the user as
6
+ * `400: (empty response body)` on TUI launch.
7
+ *
8
+ * See anomalyco/opencode commits 2793502db / 96a534d8c for the
9
+ * server-side schema (PR #346 / v2.9.2 history).
10
+ */
11
+ export declare const OPENCODE_AGENT_COLOR_TOKENS: readonly ["primary", "secondary", "accent", "success", "warning", "error", "info"];
12
+ /**
13
+ * Check whether a value is a valid agent color — either a hex literal
14
+ * (`#RRGGBB`) or a named token from `OPENCODE_AGENT_COLOR_TOKENS`.
15
+ */
16
+ export declare function isValidAgentColor(value: string): boolean;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Zod schema for the user-facing `systematic.json` / `systematic.jsonc` config.
3
+ *
4
+ * ## Zod 4 API notes (verified against zod@4.4.3 during implementation)
5
+ *
6
+ * - `z.toJSONSchema(schema, { target: 'draft-7' })` produces draft-07 JSON Schema.
7
+ * - `.default(value)` DOES round-trip into JSON Schema `default`.
8
+ * - `.meta({ description, examples })` attaches documentation metadata visible in
9
+ * JSON Schema output and via `schema.description`.
10
+ * - `z.object().strict()` rejects unknown keys with `unrecognized_keys` issues that
11
+ * include the offending key names and the path to the containing object.
12
+ * - `z.record(keySchema, valueSchema)` is the canonical 2-arg form (Zod 4 types
13
+ * require both key and value schemas). A single-arg overload works at runtime
14
+ * but is not reflected in the type declarations for this Zod 4 minor.
15
+ */
16
+ import { z } from 'zod';
17
+ export declare const AgentOverlaySchema: z.ZodObject<{
18
+ model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
19
+ variant: z.ZodOptional<z.ZodString>;
20
+ temperature: z.ZodOptional<z.ZodNumber>;
21
+ top_p: z.ZodOptional<z.ZodNumber>;
22
+ mode: z.ZodOptional<z.ZodEnum<{
23
+ subagent: "subagent";
24
+ primary: "primary";
25
+ all: "all";
26
+ }>>;
27
+ color: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
28
+ primary: "primary";
29
+ secondary: "secondary";
30
+ accent: "accent";
31
+ success: "success";
32
+ warning: "warning";
33
+ error: "error";
34
+ info: "info";
35
+ }>, z.ZodString]>>;
36
+ steps: z.ZodOptional<z.ZodNumber>;
37
+ hidden: z.ZodOptional<z.ZodBoolean>;
38
+ disable: z.ZodOptional<z.ZodBoolean>;
39
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
40
+ permission: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodEnum<{
41
+ ask: "ask";
42
+ allow: "allow";
43
+ deny: "deny";
44
+ }>, z.ZodRecord<z.ZodString, z.ZodEnum<{
45
+ ask: "ask";
46
+ allow: "allow";
47
+ deny: "deny";
48
+ }>>]>>>;
49
+ }, z.core.$strict>;
50
+ export declare const CategoryOverlaySchema: z.ZodObject<{
51
+ model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
52
+ variant: z.ZodOptional<z.ZodString>;
53
+ temperature: z.ZodOptional<z.ZodNumber>;
54
+ top_p: z.ZodOptional<z.ZodNumber>;
55
+ mode: z.ZodOptional<z.ZodEnum<{
56
+ subagent: "subagent";
57
+ primary: "primary";
58
+ all: "all";
59
+ }>>;
60
+ color: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
61
+ primary: "primary";
62
+ secondary: "secondary";
63
+ accent: "accent";
64
+ success: "success";
65
+ warning: "warning";
66
+ error: "error";
67
+ info: "info";
68
+ }>, z.ZodString]>>;
69
+ steps: z.ZodOptional<z.ZodNumber>;
70
+ hidden: z.ZodOptional<z.ZodBoolean>;
71
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
72
+ permission: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodEnum<{
73
+ ask: "ask";
74
+ allow: "allow";
75
+ deny: "deny";
76
+ }>, z.ZodRecord<z.ZodString, z.ZodEnum<{
77
+ ask: "ask";
78
+ allow: "allow";
79
+ deny: "deny";
80
+ }>>]>>>;
81
+ }, z.core.$strict>;
82
+ export declare const BootstrapSchema: z.ZodObject<{
83
+ enabled: z.ZodDefault<z.ZodBoolean>;
84
+ file: z.ZodOptional<z.ZodString>;
85
+ }, z.core.$strict>;
86
+ export declare const SystematicConfigSchema: z.ZodObject<{
87
+ $schema: z.ZodOptional<z.ZodString>;
88
+ agents: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
89
+ model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
90
+ variant: z.ZodOptional<z.ZodString>;
91
+ temperature: z.ZodOptional<z.ZodNumber>;
92
+ top_p: z.ZodOptional<z.ZodNumber>;
93
+ mode: z.ZodOptional<z.ZodEnum<{
94
+ subagent: "subagent";
95
+ primary: "primary";
96
+ all: "all";
97
+ }>>;
98
+ color: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
99
+ primary: "primary";
100
+ secondary: "secondary";
101
+ accent: "accent";
102
+ success: "success";
103
+ warning: "warning";
104
+ error: "error";
105
+ info: "info";
106
+ }>, z.ZodString]>>;
107
+ steps: z.ZodOptional<z.ZodNumber>;
108
+ hidden: z.ZodOptional<z.ZodBoolean>;
109
+ disable: z.ZodOptional<z.ZodBoolean>;
110
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
111
+ permission: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodEnum<{
112
+ ask: "ask";
113
+ allow: "allow";
114
+ deny: "deny";
115
+ }>, z.ZodRecord<z.ZodString, z.ZodEnum<{
116
+ ask: "ask";
117
+ allow: "allow";
118
+ deny: "deny";
119
+ }>>]>>>;
120
+ }, z.core.$strict>>>;
121
+ categories: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
122
+ model: z.ZodOptional<z.ZodNullable<z.ZodString>>;
123
+ variant: z.ZodOptional<z.ZodString>;
124
+ temperature: z.ZodOptional<z.ZodNumber>;
125
+ top_p: z.ZodOptional<z.ZodNumber>;
126
+ mode: z.ZodOptional<z.ZodEnum<{
127
+ subagent: "subagent";
128
+ primary: "primary";
129
+ all: "all";
130
+ }>>;
131
+ color: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
132
+ primary: "primary";
133
+ secondary: "secondary";
134
+ accent: "accent";
135
+ success: "success";
136
+ warning: "warning";
137
+ error: "error";
138
+ info: "info";
139
+ }>, z.ZodString]>>;
140
+ steps: z.ZodOptional<z.ZodNumber>;
141
+ hidden: z.ZodOptional<z.ZodBoolean>;
142
+ skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
143
+ permission: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodEnum<{
144
+ ask: "ask";
145
+ allow: "allow";
146
+ deny: "deny";
147
+ }>, z.ZodRecord<z.ZodString, z.ZodEnum<{
148
+ ask: "ask";
149
+ allow: "allow";
150
+ deny: "deny";
151
+ }>>]>>>;
152
+ }, z.core.$strict>>>;
153
+ disabled_skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
154
+ disabled_agents: z.ZodDefault<z.ZodArray<z.ZodString>>;
155
+ disabled_commands: z.ZodDefault<z.ZodArray<z.ZodString>>;
156
+ bootstrap: z.ZodDefault<z.ZodObject<{
157
+ enabled: z.ZodDefault<z.ZodBoolean>;
158
+ file: z.ZodOptional<z.ZodString>;
159
+ }, z.core.$strict>>;
160
+ }, z.core.$strict>;
161
+ export type ValidationResult = {
162
+ success: true;
163
+ data: z.infer<typeof SystematicConfigSchema>;
164
+ } | {
165
+ success: false;
166
+ errors: readonly z.ZodIssue[];
167
+ };
168
+ export declare function validateConfig(input: unknown): ValidationResult;
169
+ export declare function assertSourceCategoryModelDefaults(defaults: Record<string, unknown>): void;
170
+ /**
171
+ * Overlay fields that require a project-or-higher trust source.
172
+ *
173
+ * This list is co-located with the schema definitions above so that any
174
+ * future field additions that need trust protection are added here at the
175
+ * same time. The regression tests in tests/unit/config-schema.test.ts
176
+ * assert that this list agrees with every field tagged `.meta({ trust:
177
+ * 'project-or-higher' })` in AgentOverlaySchema — preventing silent drift.
178
+ *
179
+ * Matches the hand-coded `SECURITY_OVERLAY_FIELDS` set in `src/lib/config.ts`.
180
+ */
181
+ export declare const SECURITY_OVERLAY_FIELDS: readonly string[];