@ai-outfitter/outfitter 1.4.0 → 1.6.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/README.md +11 -2
- package/code/pi-extension/src/outfitter-extension.js +9 -1
- package/dist/agents/AgentLaunch.d.ts +2 -2
- package/dist/agents/AgentLaunch.js +4 -3
- package/dist/agents/AgentLaunch.js.map +1 -1
- package/dist/agents/ClaudeStatePersistence.d.ts +18 -0
- package/dist/agents/ClaudeStatePersistence.js +225 -0
- package/dist/agents/ClaudeStatePersistence.js.map +1 -0
- package/dist/cli/OutfitterCli.js +1 -1
- package/dist/cli/OutfitterCli.js.map +1 -1
- package/dist/cli/commands/RunAgentCommand.js +76 -15
- package/dist/cli/commands/RunAgentCommand.js.map +1 -1
- package/dist/cli/commands/SyncCommand.d.ts +7 -3
- package/dist/cli/commands/SyncCommand.js +110 -20
- package/dist/cli/commands/SyncCommand.js.map +1 -1
- package/dist/extensions/PiExtensionCache.d.ts +11 -1
- package/dist/extensions/PiExtensionCache.js +129 -13
- package/dist/extensions/PiExtensionCache.js.map +1 -1
- package/dist/projection/CodexMcp.d.ts +11 -0
- package/dist/projection/CodexMcp.js +143 -0
- package/dist/projection/CodexMcp.js.map +1 -0
- package/dist/projection/Materialize.d.ts +3 -1
- package/dist/projection/Materialize.js +10 -7
- package/dist/projection/Materialize.js.map +1 -1
- package/dist/projection/ProjectHarness.js +75 -32
- package/dist/projection/ProjectHarness.js.map +1 -1
- package/dist/projection/Projection.d.ts +2 -0
- package/dist/projection/Tools.d.ts +91 -0
- package/dist/projection/Tools.js +129 -0
- package/dist/projection/Tools.js.map +1 -0
- package/dist/resolver/AgentDefinition.d.ts +11 -0
- package/dist/resolver/AgentDefinition.js +91 -12
- package/dist/resolver/AgentDefinition.js.map +1 -1
- package/dist/resolver/Layer.d.ts +5 -2
- package/dist/resolver/Layer.js +47 -8
- package/dist/resolver/Layer.js.map +1 -1
- package/dist/resolver/ResolverContext.d.ts +1 -1
- package/dist/resolver/ResolverContext.js +1 -1
- package/dist/resolver/ResolverContext.js.map +1 -1
- package/dist/resolver/ResolverValidation.d.ts +12 -1
- package/dist/resolver/ResolverValidation.js +30 -20
- package/dist/resolver/ResolverValidation.js.map +1 -1
- package/dist/schemas/agent.schema.json +8 -2
- package/dist/schemas/settings.schema.json +1 -1
- package/dist/schemas/system-extension-hook.schema.json +38 -0
- package/dist/settings/Settings.d.ts +1 -1
- package/dist/settings/Settings.js +1 -1
- package/dist/settings/Settings.js.map +1 -1
- package/dist/settings/SettingsLoader.js.map +1 -1
- package/dist/setup/DefaultCatalog.d.ts +18 -0
- package/dist/setup/DefaultCatalog.js +74 -13
- package/dist/setup/DefaultCatalog.js.map +1 -1
- package/dist/sources/SourceCache.d.ts +20 -0
- package/dist/sources/SourceCache.js +31 -1
- package/dist/sources/SourceCache.js.map +1 -1
- package/dist/sources/TransitiveSources.d.ts +43 -0
- package/dist/sources/TransitiveSources.js +177 -0
- package/dist/sources/TransitiveSources.js.map +1 -0
- package/dist/system/SystemExtensionHook.d.ts +34 -0
- package/dist/system/SystemExtensionHook.js +190 -0
- package/dist/system/SystemExtensionHook.js.map +1 -0
- package/dist/validation/SchemaValidator.d.ts +1 -1
- package/dist/validation/SchemaValidator.js +2 -0
- package/dist/validation/SchemaValidator.js.map +1 -1
- package/docs/architecture/state_writeback_strategy.md +15 -3
- package/docs/documentation/README.md +15 -2
- package/docs/documentation/catalogs.md +61 -2
- package/docs/documentation/cli.md +2 -1
- package/docs/documentation/concepts.md +2 -2
- package/docs/documentation/containers.md +63 -14
- package/docs/documentation/hooks.md +33 -0
- package/docs/documentation/in-cluster.md +2 -0
- package/docs/documentation/migration.md +1 -1
- package/docs/documentation/personas.md +1 -1
- package/docs/documentation/settings.md +1 -1
- package/docs/documentation/state.md +27 -0
- package/docs/documentation/support-matrix.md +36 -23
- package/docs/documentation/usecases/org-onboarding-sdlc-report.md +143 -0
- package/docs/documentation/usecases/persona-reviews.md +1 -1
- package/docs/philosophy.md +20 -0
- package/package.json +1 -1
- package/src/schemas/agent.schema.json +8 -2
- package/src/schemas/settings.schema.json +1 -1
- package/src/schemas/system-extension-hook.schema.json +38 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A projectable tool name. Kept in lockstep with `$defs/toolName` in `agent.schema.json`, which
|
|
3
|
+
* rejects the same shapes at the YAML read boundary; a unit test asserts the two stay equal.
|
|
4
|
+
*
|
|
5
|
+
* Three characters are excluded, because projection puts these names into harness argv:
|
|
6
|
+
*
|
|
7
|
+
* - A leading `-` makes the name an independent flag on Claude, where each entry becomes its own
|
|
8
|
+
* argv element. `allow: [Read, --dangerously-skip-permissions]` would not name a tool. It would
|
|
9
|
+
* turn off the permission system this element exists to configure. Profiles compose from remote
|
|
10
|
+
* catalogs and inheritance chains, so a tool name is not always written by the person who runs
|
|
11
|
+
* the agent, which makes this a supply-chain path to arbitrary launch flags.
|
|
12
|
+
* - A comma splits one name into several entries inside pi's `--tools a,b,c` value.
|
|
13
|
+
* - Whitespace is not a separator on either path today, because no shell is involved and pi gets a
|
|
14
|
+
* single argv element. It is rejected as conservatism, and it keeps names portable between the
|
|
15
|
+
* two harnesses. Note that this also excludes Claude's scoped permission rules, such as
|
|
16
|
+
* `Bash(npm run test:*)`. That is deliberate for a harness-neutral field: the same string is
|
|
17
|
+
* meaningless to pi. Relaxing the pattern later is backward compatible.
|
|
18
|
+
*/
|
|
19
|
+
export const TOOL_NAME_PATTERN = /^[^\s,-][^\s,]*$/;
|
|
20
|
+
/**
|
|
21
|
+
* Rejects a tool name that projection cannot carry as a name. This throws instead of filtering,
|
|
22
|
+
* because dropping an entry from a security control without saying so is the failure mode this
|
|
23
|
+
* element must not have. A selection that cannot be projected exactly as written must not launch.
|
|
24
|
+
*/
|
|
25
|
+
const assertProjectableToolNames = (tools) => {
|
|
26
|
+
const offending = invalidToolName(tools);
|
|
27
|
+
if (offending !== undefined) {
|
|
28
|
+
throw new Error(`tool name ${JSON.stringify(offending)} cannot be projected: ${TOOL_NAME_RULE}`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** The rule the pattern encodes, phrased for an error that a profile author reads. */
|
|
32
|
+
export const TOOL_NAME_RULE = "a tool name must not start with '-' or contain a comma or whitespace, because projection would make it a separate harness flag or split it into several names.";
|
|
33
|
+
/**
|
|
34
|
+
* The first tool name in the selection that projection cannot carry, or `undefined` when every name
|
|
35
|
+
* is projectable. Shared with the agent read boundary, so `config.json` overlays, which bypass the
|
|
36
|
+
* JSON Schema, are held to the same invariant.
|
|
37
|
+
*/
|
|
38
|
+
export const invalidToolName = (tools) => [...(tools?.allow ?? []), ...(tools?.deny ?? [])].find((name) => !TOOL_NAME_PATTERN.test(name));
|
|
39
|
+
/**
|
|
40
|
+
* The effective tool allowlist: `allow` minus `deny`, per OFTR-003.10.4 ("denied tools MUST win
|
|
41
|
+
* when projected"). `undefined` and `[]` mean different things and callers must keep them apart:
|
|
42
|
+
* `undefined` is "no allowlist declared", `[]` is "an allowlist that `deny` emptied".
|
|
43
|
+
*/
|
|
44
|
+
export const effectiveToolAllowlist = (tools) => {
|
|
45
|
+
const denied = new Set(tools?.deny ?? []);
|
|
46
|
+
return tools?.allow?.filter((tool) => !denied.has(tool));
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* pi and Claude Code BOTH have an availability control; Claude additionally has a permission layer
|
|
50
|
+
* on top of its own. The pi rows were measured against pi 0.83.0 with a probe extension that
|
|
51
|
+
* registers a uniquely-named tool and prints `pi.getActiveTools()` at `session_start`; the exact
|
|
52
|
+
* runs are recorded in the PR for #255. The Claude rows come from `claude --help` and
|
|
53
|
+
* https://code.claude.com/docs/en/cli-reference — documented behaviour, not locally measured.
|
|
54
|
+
*
|
|
55
|
+
* | | pi | claude |
|
|
56
|
+
* | availability ceiling | `--no-tools --tools a,b,c` (comma-joined) | `--tools a b c` (variadic; built-in set only) |
|
|
57
|
+
* | pre-approval within it | n/a — pi has no permission layer | `--allowedTools a b c` (variadic) |
|
|
58
|
+
* | denylist | `--exclude-tools a,b,c` (comma-joined) | `--disallowedTools a b c` (variadic; a bare name removes the tool from context, per docs) |
|
|
59
|
+
* | no tools at all | `--no-tools` | `--tools ""` (empties the built-in set only) |
|
|
60
|
+
*
|
|
61
|
+
* An allowlist therefore projects to TWO flags on Claude: `--tools` is the ceiling (the tool is not
|
|
62
|
+
* in the session), and `--allowedTools` pre-approves the same names within that ceiling so a
|
|
63
|
+
* headless session is not stopped by a permission prompt for the very tools the profile granted.
|
|
64
|
+
* Either flag alone would be wrong: `--allowedTools` alone leaves every unlisted builtin available
|
|
65
|
+
* (merely unapproved), and `--tools` alone leaves the granted tools prompting for approval.
|
|
66
|
+
*
|
|
67
|
+
* Scope caveat, from the CLI reference: `--tools` governs the BUILT-IN set only. MCP tools
|
|
68
|
+
* (`mcp__server__*`) are untouched by it — they are governed by which servers load (the `mcp`
|
|
69
|
+
* loadout element) and by `--disallowedTools` patterns such as `mcp__*`. So `--tools ""` is not
|
|
70
|
+
* pi's zero-tool session when MCP servers are selected: the builtins are gone, the MCP tools
|
|
71
|
+
* remain. (`mcp__*` does pass TOOL_NAME_PATTERN, but only Claude reads `*` as a wildcard; on pi it
|
|
72
|
+
* would be a literal name, so a harness-neutral profile cannot lean on it.)
|
|
73
|
+
*
|
|
74
|
+
* Four rules follow from the flags themselves:
|
|
75
|
+
*
|
|
76
|
+
* - On pi, `--tools` is a hard allowlist across all three tool categories (built-in, extension, and
|
|
77
|
+
* custom), not an addition to the built-in set. Measured: `--tools read` alone yields exactly
|
|
78
|
+
* `["read"]`, with the probe extension's own tool gone. No base flag is needed to make an
|
|
79
|
+
* allowlist restrict. `--no-tools` is emitted with it anyway, because it costs nothing, states
|
|
80
|
+
* the intent in the argv, and keeps the allowlist a ceiling if a future pi made `--tools`
|
|
81
|
+
* additive.
|
|
82
|
+
* - `--no-tools` is pi's empty-session flag, NOT `--no-builtin-tools`. `--no-builtin-tools` keeps
|
|
83
|
+
* extension and custom tools by design: measured alone it yields `["probe_marker_tool"]`, the
|
|
84
|
+
* extension tool, while `--no-tools` yields `[]`. An allowlist that `deny` empties must therefore
|
|
85
|
+
* project to `--no-tools` on pi and `--tools ""` on Claude (help: 'Use "" to disable all tools'),
|
|
86
|
+
* or the "zero tools" request would fail open and leave the whole builtin set available. The
|
|
87
|
+
* empty value is one empty-string argv element — no shell is involved, so it survives as such.
|
|
88
|
+
* - `deny` is ALWAYS emitted on both harnesses, including when `allow` is also set and has already
|
|
89
|
+
* removed those names. On Claude a bare name in `--disallowedTools` removes the tool from context
|
|
90
|
+
* per the docs, which makes the explicit denial an availability statement in its own right rather
|
|
91
|
+
* than a bet on the approval path. On pi it is belt and braces, since a tool left out of
|
|
92
|
+
* `--tools` does not exist. Either way it satisfies OFTR-003.10.4 ("denied tools MUST win when
|
|
93
|
+
* projected") without depending on the filter alone.
|
|
94
|
+
* - Claude's three flags are all variadic. Each consumes every following token until the next
|
|
95
|
+
* `--flag`, so they are emitted in a fixed order — `--tools`, `--allowedTools`,
|
|
96
|
+
* `--disallowedTools` — where each present flag is terminated by the next one, and the launch
|
|
97
|
+
* plan places the whole group first, ahead of the prompt flags, which terminate the last. They
|
|
98
|
+
* must never sit immediately before pass-through positionals, which would be eaten as tool names.
|
|
99
|
+
* pi's flags take one comma-joined value each, so they are not exposed to this.
|
|
100
|
+
*/
|
|
101
|
+
export const toolArgs = (harness, tools) => {
|
|
102
|
+
// Before filtering, so a poisoned name is rejected even when `deny` would have removed it, and on
|
|
103
|
+
// both harnesses: `projectComposition` is exported and reachable without schema validation, so
|
|
104
|
+
// this cannot assume the read boundary already ran.
|
|
105
|
+
assertProjectableToolNames(tools);
|
|
106
|
+
const allow = effectiveToolAllowlist(tools);
|
|
107
|
+
const deny = tools.deny ?? [];
|
|
108
|
+
if (harness === 'pi') {
|
|
109
|
+
return [
|
|
110
|
+
// No allowlist declared means no ceiling requested; `deny` below still applies.
|
|
111
|
+
...(allow === undefined ? [] : allow.length === 0 ? ['--no-tools'] : ['--no-tools', '--tools', allow.join(',')]),
|
|
112
|
+
...(deny.length === 0 ? [] : ['--exclude-tools', deny.join(',')]),
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
return [
|
|
116
|
+
// The ceiling and the pre-approval within it. An allowlist emptied by `deny` projects to
|
|
117
|
+
// `--tools ""` — one empty-string argv element, Claude's documented "disable all tools" form —
|
|
118
|
+
// with no `--allowedTools`, because there is nothing left to pre-approve. Only the builtin set
|
|
119
|
+
// is emptied: MCP tools from selected servers survive `--tools ""` (see the scope caveat
|
|
120
|
+
// above), so this is close to, but not exactly, pi's zero-tool session.
|
|
121
|
+
...(allow === undefined
|
|
122
|
+
? []
|
|
123
|
+
: allow.length === 0
|
|
124
|
+
? ['--tools', '']
|
|
125
|
+
: ['--tools', ...allow, '--allowedTools', ...allow]),
|
|
126
|
+
...(deny.length === 0 ? [] : ['--disallowedTools', ...deny]),
|
|
127
|
+
];
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=Tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tools.js","sourceRoot":"","sources":["../../src/projection/Tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,17 @@ interface FrontmatterSplit {
|
|
|
32
32
|
}
|
|
33
33
|
/** Splits leading `---` YAML frontmatter from the markdown body. */
|
|
34
34
|
export declare const splitFrontmatter: (content: string) => FrontmatterSplit | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The defect in a RAW `tools` value, before any normalization, or `undefined` when the shape is
|
|
37
|
+
* exactly `{allow?: string[], deny?: string[]}` with non-empty entries. This runs at the read
|
|
38
|
+
* boundary because silent normalization fails open: `asStringArray` turns an unknown key, a string
|
|
39
|
+
* where an array was meant, or a non-string entry into an empty or partial selection, and the
|
|
40
|
+
* agent then launches WITHOUT the restriction the author wrote — under `--strict` included, since
|
|
41
|
+
* nothing is left to warn about. A malformed selection must be a hard error naming the defect.
|
|
42
|
+
* `agent.md` frontmatter is shape-checked by `agent.schema.json`; this is the same invariant for
|
|
43
|
+
* the `config.json` overlay path that bypasses the schema.
|
|
44
|
+
*/
|
|
45
|
+
export declare const toolsShapeDefect: (tools: unknown) => string | undefined;
|
|
35
46
|
/**
|
|
36
47
|
* Parses an agent definition. `configPaths` are highest-precedence first; each is a loadout-only
|
|
37
48
|
* override that JSON-merges by key across layers over the frontmatter loadout.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Parses `agents/<id>/agent.md` frontmatter and merges per-layer config.json loadout overrides.
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { TOOL_NAME_RULE, invalidToolName } from '../projection/Tools.js';
|
|
3
4
|
import { validateSchema } from '../validation/SchemaValidator.js';
|
|
4
5
|
import { parseYamlDocument } from '../validation/YamlDocument.js';
|
|
5
6
|
import { isPromptSourceReference } from '../composer/PromptSource.js';
|
|
@@ -53,20 +54,82 @@ const promptControlsFromRecord = (record) => ({
|
|
|
53
54
|
promptTemplate: isPromptSourceReference(record.prompt_template) ? record.prompt_template : undefined,
|
|
54
55
|
});
|
|
55
56
|
const readMarkdownHeading = (body) => /^#\s+(.+)$/mu.exec(body)?.[1]?.trim();
|
|
56
|
-
const loadoutFromRecord = (record) => {
|
|
57
|
-
|
|
57
|
+
const loadoutFromRecord = (record) => ({
|
|
58
|
+
...emptyLoadout(),
|
|
59
|
+
skills: asStringArray(record.skills),
|
|
60
|
+
subagents: asStringArray(record.subagents),
|
|
61
|
+
mcp: asStringArray(record.mcp),
|
|
62
|
+
extensions: asStringArray(record.extensions),
|
|
63
|
+
plugins: asStringArray(record.plugins),
|
|
64
|
+
model: asString(record.model),
|
|
65
|
+
thinking: asString(record.thinking),
|
|
66
|
+
tools: toolSelectionFromRecord(record.tools),
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* Narrows a raw `tools` value that `toolsShapeDefect` has already accepted. Key presence is
|
|
70
|
+
* preserved: an absent `allow` stays absent, because "no allowlist declared" and "an empty
|
|
71
|
+
* allowlist" project differently and normalizing one into the other would fabricate a ceiling.
|
|
72
|
+
*/
|
|
73
|
+
const toolSelectionFromRecord = (value) => {
|
|
74
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
75
|
+
return undefined;
|
|
76
|
+
const record = value;
|
|
58
77
|
return {
|
|
59
|
-
...
|
|
60
|
-
|
|
61
|
-
subagents: asStringArray(record.subagents),
|
|
62
|
-
mcp: asStringArray(record.mcp),
|
|
63
|
-
extensions: asStringArray(record.extensions),
|
|
64
|
-
plugins: asStringArray(record.plugins),
|
|
65
|
-
model: asString(record.model),
|
|
66
|
-
thinking: asString(record.thinking),
|
|
67
|
-
tools: tools === undefined ? undefined : { allow: asStringArray(tools.allow), deny: asStringArray(tools.deny) },
|
|
78
|
+
...(record.allow === undefined ? {} : { allow: asStringArray(record.allow) }),
|
|
79
|
+
...(record.deny === undefined ? {} : { deny: asStringArray(record.deny) }),
|
|
68
80
|
};
|
|
69
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* The defect in a RAW `tools` value, before any normalization, or `undefined` when the shape is
|
|
84
|
+
* exactly `{allow?: string[], deny?: string[]}` with non-empty entries. This runs at the read
|
|
85
|
+
* boundary because silent normalization fails open: `asStringArray` turns an unknown key, a string
|
|
86
|
+
* where an array was meant, or a non-string entry into an empty or partial selection, and the
|
|
87
|
+
* agent then launches WITHOUT the restriction the author wrote — under `--strict` included, since
|
|
88
|
+
* nothing is left to warn about. A malformed selection must be a hard error naming the defect.
|
|
89
|
+
* `agent.md` frontmatter is shape-checked by `agent.schema.json`; this is the same invariant for
|
|
90
|
+
* the `config.json` overlay path that bypasses the schema.
|
|
91
|
+
*/
|
|
92
|
+
export const toolsShapeDefect = (tools) => {
|
|
93
|
+
if (tools === undefined)
|
|
94
|
+
return undefined;
|
|
95
|
+
if (tools === null || typeof tools !== 'object' || Array.isArray(tools)) {
|
|
96
|
+
return `\`tools\` must be an object of the form {allow?: [...], deny?: [...]}, not ${describeValue(tools)}.`;
|
|
97
|
+
}
|
|
98
|
+
const record = tools;
|
|
99
|
+
const unknownKey = Object.keys(record).find((key) => key !== 'allow' && key !== 'deny');
|
|
100
|
+
if (unknownKey !== undefined) {
|
|
101
|
+
return `\`tools\` has unknown key ${JSON.stringify(unknownKey)}; only "allow" and "deny" are accepted.`;
|
|
102
|
+
}
|
|
103
|
+
return toolListDefect('allow', record.allow) ?? toolListDefect('deny', record.deny);
|
|
104
|
+
};
|
|
105
|
+
/** Names the value's kind for a shape-defect message. */
|
|
106
|
+
const describeValue = (value) => value === null ? 'null' : Array.isArray(value) ? 'an array' : `a ${typeof value}`;
|
|
107
|
+
/** The defect in one raw `tools.allow`/`tools.deny` list, or `undefined` when it is well formed. */
|
|
108
|
+
const toolListDefect = (key, value) => {
|
|
109
|
+
if (value === undefined)
|
|
110
|
+
return undefined;
|
|
111
|
+
if (!Array.isArray(value)) {
|
|
112
|
+
return `\`tools.${key}\` must be an array of tool names, not ${describeValue(value)}.`;
|
|
113
|
+
}
|
|
114
|
+
const badIndex = value.findIndex((entry) => typeof entry !== 'string' || entry === '');
|
|
115
|
+
return badIndex === -1
|
|
116
|
+
? undefined
|
|
117
|
+
: `\`tools.${key}[${badIndex}]\` must be a non-empty string, not ${JSON.stringify(value[badIndex])}.`;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Reports a tool name in the merged loadout that projection cannot carry. The shape is already
|
|
121
|
+
* guaranteed here — frontmatter by `agent.schema.json`, each `config.json` layer by
|
|
122
|
+
* `toolsShapeDefect` in `readConfigLoadout` — but the *names* must be re-checked on the merged
|
|
123
|
+
* record, because a `config.json` overlay can replace `tools` wholesale after the schema check.
|
|
124
|
+
*/
|
|
125
|
+
const toolsIssue = (record, path) => {
|
|
126
|
+
if (record.tools === undefined)
|
|
127
|
+
return undefined;
|
|
128
|
+
const offending = invalidToolName(toolSelectionFromRecord(record.tools));
|
|
129
|
+
return offending === undefined
|
|
130
|
+
? undefined
|
|
131
|
+
: { path, message: `declares an unusable tool name ${JSON.stringify(offending)}: ${TOOL_NAME_RULE}` };
|
|
132
|
+
};
|
|
70
133
|
/** Reads one config.json, restricting it to loadout keys; parse/read/non-object failures are issues. */
|
|
71
134
|
const readConfigLoadout = (configPath) => {
|
|
72
135
|
let parsed;
|
|
@@ -79,7 +142,16 @@ const readConfigLoadout = (configPath) => {
|
|
|
79
142
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
80
143
|
return { path: configPath, message: 'config.json must be a JSON object of loadout overrides.' };
|
|
81
144
|
}
|
|
82
|
-
|
|
145
|
+
const picked = pickLoadoutKeys(parsed);
|
|
146
|
+
// Validate the RAW `tools` shape per layer, before any normalization and before the merge can
|
|
147
|
+
// hide which file is malformed. See `toolsShapeDefect` for why silent normalization fails open.
|
|
148
|
+
if ('tools' in picked && picked.tools !== undefined) {
|
|
149
|
+
const defect = toolsShapeDefect(picked.tools);
|
|
150
|
+
if (defect !== undefined) {
|
|
151
|
+
return { path: configPath, message: `config.json declares a malformed tool selection: ${defect}` };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return picked;
|
|
83
155
|
};
|
|
84
156
|
const parseFrontmatterRecord = (content, agentPath) => {
|
|
85
157
|
const split = splitFrontmatter(content);
|
|
@@ -119,6 +191,13 @@ export const parseAgentDefinition = (content, configPaths, agentPath) => {
|
|
|
119
191
|
}
|
|
120
192
|
merged = { ...merged, ...config };
|
|
121
193
|
}
|
|
194
|
+
// The JSON Schema only sees the frontmatter, and a config.json overlay can replace `tools`
|
|
195
|
+
// wholesale after that check. Validate the merged loadout so an unprojectable tool name is an
|
|
196
|
+
// error from `outfitter validate`, not a surprise at launch.
|
|
197
|
+
const toolIssue = toolsIssue(merged, configPaths[0] ?? agentPath);
|
|
198
|
+
if (toolIssue !== undefined) {
|
|
199
|
+
return toolIssue;
|
|
200
|
+
}
|
|
122
201
|
return {
|
|
123
202
|
name: frontmatter.record.name,
|
|
124
203
|
label: asString(frontmatter.record.label)?.trim() || readMarkdownHeading(frontmatter.body),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentDefinition.js","sourceRoot":"","sources":["../../src/resolver/AgentDefinition.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AgentDefinition.js","sourceRoot":"","sources":["../../src/resolver/AgentDefinition.ts"],"names":[],"mappings":"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"}
|
package/dist/resolver/Layer.d.ts
CHANGED
|
@@ -20,9 +20,12 @@ export interface LayerDiscoveryResult {
|
|
|
20
20
|
* would deadlock every other command behind advice the user cannot act on (OFTR-004.2.15).
|
|
21
21
|
*/
|
|
22
22
|
readonly unsynchronized: readonly string[];
|
|
23
|
+
/** Non-fatal transitive-source skip warnings (unpinned refs, path sources, invalid settings). */
|
|
24
|
+
readonly warnings: readonly string[];
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
|
-
* Orders layers highest precedence first: workspace `.agents`, then global `~/.agents`,
|
|
26
|
-
*
|
|
27
|
+
* Orders layers highest precedence first: workspace `.agents`, then global `~/.agents`, then
|
|
28
|
+
* configured sources in order, then transitive sources those catalogs declare, breadth-first
|
|
29
|
+
* (OFTR-004.6.1, OFTR-004.6.3). Only layers whose root exists on disk are included.
|
|
27
30
|
*/
|
|
28
31
|
export declare const discoverLayers: (input: LayerDiscoveryInput) => LayerDiscoveryResult;
|
package/dist/resolver/Layer.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
// Builds the ordered `.agents` layer stack (workspace over global over remote sources) from settings.
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { createRemoteRepositoryCachePath, formatRemoteSourceDisplay, isRemoteSource,
|
|
4
|
+
import { createRemoteRepositoryCachePath, encodeRemoteSourceSelection, formatRemoteSourceDisplay, isRemoteSource, resolveSourcePayloadRoot, } from '../sources/SourceCache.js';
|
|
5
|
+
import { expandTransitiveSources } from '../sources/TransitiveSources.js';
|
|
5
6
|
const agentsRoot = (directory) => join(directory, '.agents');
|
|
6
7
|
/**
|
|
7
8
|
* Maps an already-materialized repository checkout to the layer `outfitter run` would resolve from
|
|
8
9
|
* it. `outfitter sync` reuses this against its temporary checkout so both agree on the payload root.
|
|
9
10
|
*/
|
|
10
11
|
export const remoteSourceLayer = (repositoryPath, source) => ({
|
|
11
|
-
root:
|
|
12
|
+
root: resolveSourcePayloadRoot(repositoryPath, source),
|
|
12
13
|
origin: 'source',
|
|
13
14
|
label: formatRemoteSourceDisplay(source),
|
|
14
15
|
});
|
|
@@ -16,8 +17,9 @@ const sourceLayer = (input, source) => isRemoteSource(source)
|
|
|
16
17
|
? remoteSourceLayer(createRemoteRepositoryCachePath(input.homeDirectory, source, input.settings.cacheDirectory), source)
|
|
17
18
|
: { root: source.path, origin: 'source', label: source.path };
|
|
18
19
|
/**
|
|
19
|
-
* Orders layers highest precedence first: workspace `.agents`, then global `~/.agents`,
|
|
20
|
-
*
|
|
20
|
+
* Orders layers highest precedence first: workspace `.agents`, then global `~/.agents`, then
|
|
21
|
+
* configured sources in order, then transitive sources those catalogs declare, breadth-first
|
|
22
|
+
* (OFTR-004.6.1, OFTR-004.6.3). Only layers whose root exists on disk are included.
|
|
21
23
|
*/
|
|
22
24
|
export const discoverLayers = (input) => {
|
|
23
25
|
const candidates = [
|
|
@@ -25,14 +27,51 @@ export const discoverLayers = (input) => {
|
|
|
25
27
|
{ root: agentsRoot(input.homeDirectory), origin: 'global', label: 'global' },
|
|
26
28
|
];
|
|
27
29
|
const unsynchronized = [];
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
const invalid = [];
|
|
31
|
+
const appendSourceLayer = (source) => {
|
|
32
|
+
// An absolute or escaping `path:` makes payload-root resolution throw; skip the source with a
|
|
33
|
+
// warning rather than crash every resolve command (list/validate/run/dump).
|
|
34
|
+
let layer;
|
|
35
|
+
try {
|
|
36
|
+
layer = sourceLayer(input, source);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
invalid.push(`Configured source '${isRemoteSource(source) ? formatRemoteSourceDisplay(source) : source.path}' has an invalid path and was skipped.`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
30
42
|
if (isRemoteSource(source) && !existsSync(layer.root)) {
|
|
31
43
|
unsynchronized.push(`Configured remote source '${layer.label}' is not synchronized at '${layer.root}'. Run 'outfitter sync' to fetch it.`);
|
|
32
|
-
|
|
44
|
+
return;
|
|
33
45
|
}
|
|
34
46
|
candidates.push(layer);
|
|
47
|
+
};
|
|
48
|
+
// Deduplicate exact-duplicate configured sources so an identical entry listed twice yields one
|
|
49
|
+
// layer, not a self-shadowing pair (OFTR-004.6.6). Distinct subpaths stay distinct (path-aware).
|
|
50
|
+
const seenSources = new Set();
|
|
51
|
+
const directSources = (input.settings.sources ?? []).filter((source) => {
|
|
52
|
+
const key = isRemoteSource(source) ? encodeRemoteSourceSelection(source) : `path\0${source.path}`;
|
|
53
|
+
if (seenSources.has(key))
|
|
54
|
+
return false;
|
|
55
|
+
seenSources.add(key);
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
for (const source of directSources) {
|
|
59
|
+
appendSourceLayer(source);
|
|
60
|
+
}
|
|
61
|
+
const expansion = expandTransitiveSources({
|
|
62
|
+
directSources,
|
|
63
|
+
resolveCachedCheckoutRoot: (source) => {
|
|
64
|
+
const checkoutRoot = createRemoteRepositoryCachePath(input.homeDirectory, source, input.settings.cacheDirectory);
|
|
65
|
+
return existsSync(checkoutRoot) ? checkoutRoot : undefined;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
for (const transitive of expansion.sources) {
|
|
69
|
+
appendSourceLayer(transitive.source);
|
|
35
70
|
}
|
|
36
|
-
return {
|
|
71
|
+
return {
|
|
72
|
+
layers: candidates.filter((layer) => existsSync(layer.root)),
|
|
73
|
+
unsynchronized,
|
|
74
|
+
warnings: [...invalid, ...expansion.warnings],
|
|
75
|
+
};
|
|
37
76
|
};
|
|
38
77
|
//# sourceMappingURL=Layer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Layer.js","sourceRoot":"","sources":["../../src/resolver/Layer.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,+BAA+B,EAC/B,yBAAyB,EACzB,cAAc,EACd,
|
|
1
|
+
{"version":3,"file":"Layer.js","sourceRoot":"","sources":["../../src/resolver/Layer.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ export interface ResolveResult {
|
|
|
10
10
|
/** The merged settings loaded during resolution, so callers need not reload them. */
|
|
11
11
|
readonly settings: Settings;
|
|
12
12
|
readonly settingsIssues: readonly SettingsLoadIssue[];
|
|
13
|
-
/** Non-fatal
|
|
13
|
+
/** Non-fatal guidance: uncached remote sources plus transitive-source skip warnings. */
|
|
14
14
|
readonly warnings: readonly string[];
|
|
15
15
|
}
|
|
16
16
|
/** The single shared resolution path used by list, validate, run, and dump. */
|
|
@@ -10,7 +10,7 @@ export const resolveEffectiveSet = (input) => {
|
|
|
10
10
|
set: resolveResources(discovered.layers),
|
|
11
11
|
settings: loadedSettings.settings,
|
|
12
12
|
settingsIssues: loadedSettings.issues,
|
|
13
|
-
warnings: discovered.unsynchronized,
|
|
13
|
+
warnings: [...discovered.unsynchronized, ...discovered.warnings],
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
16
|
//# sourceMappingURL=ResolverContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResolverContext.js","sourceRoot":"","sources":["../../src/resolver/ResolverContext.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,OAAO,EAAE,oCAAoC,EAAE,MAAM,+BAA+B,CAAC;AAGrF,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAgBjD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAmB,EAAiB,EAAE;IACxE,MAAM,cAAc,GAAG,oCAAoC,CAAC,KAAK,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEnF,OAAO;QACL,GAAG,EAAE,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC;QACxC,QAAQ,EAAE,cAAc,CAAC,QAAQ;QACjC,cAAc,EAAE,cAAc,CAAC,MAAM;QACrC,QAAQ,EAAE,UAAU,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"ResolverContext.js","sourceRoot":"","sources":["../../src/resolver/ResolverContext.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,OAAO,EAAE,oCAAoC,EAAE,MAAM,+BAA+B,CAAC;AAGrF,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAgBjD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,KAAmB,EAAiB,EAAE;IACxE,MAAM,cAAc,GAAG,oCAAoC,CAAC,KAAK,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEnF,OAAO;QACL,GAAG,EAAE,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC;QACxC,QAAQ,EAAE,cAAc,CAAC,QAAQ;QACjC,cAAc,EAAE,cAAc,CAAC,MAAM;QACrC,QAAQ,EAAE,CAAC,GAAG,UAAU,CAAC,cAAc,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC;KACjE,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -4,8 +4,19 @@ export interface ValidationFinding {
|
|
|
4
4
|
readonly resource: string;
|
|
5
5
|
readonly message: string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Options for {@link validateEffectiveSet}. `deferLoadoutResolution` downgrades an unresolved
|
|
9
|
+
* loadout slug reference from an error to a warning — used when validating a single remote source
|
|
10
|
+
* in isolation during `outfitter sync`, where a catalog may legitimately reference a skill supplied
|
|
11
|
+
* by a catalog it declares as a dependency (OFTR-004.6). Loadout wholeness is then authoritatively
|
|
12
|
+
* enforced against the merged effective set by `outfitter validate`; the run-time composer surfaces
|
|
13
|
+
* an unresolved reference as a non-fatal warning (OFTR-005.3.4), fatal only under `run --strict`.
|
|
14
|
+
*/
|
|
15
|
+
export interface ValidationOptions {
|
|
16
|
+
readonly deferLoadoutResolution?: boolean;
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* Collects validation findings across the effective set. `error` findings always fail validation;
|
|
9
20
|
* `warning` findings (such as shadowed definitions) fail only under `--strict`.
|
|
10
21
|
*/
|
|
11
|
-
export declare const validateEffectiveSet: (set: EffectiveResourceSet, projectDirectory?: string) => readonly ValidationFinding[];
|
|
22
|
+
export declare const validateEffectiveSet: (set: EffectiveResourceSet, projectDirectory?: string, options?: ValidationOptions) => readonly ValidationFinding[];
|
|
@@ -3,8 +3,12 @@ import { compose } from '../composer/Composer.js';
|
|
|
3
3
|
import { isSkillDocumentIssue, readSkillDocument } from '../skills/SkillDocument.js';
|
|
4
4
|
import { isAgentDefinitionIssue, readAgentDefinition } from './AgentDefinition.js';
|
|
5
5
|
import { agentLocalKinds, findResource, listAgentResources, listResources } from './Resource.js';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// Matches the composer's top-level unresolved-loadout warning wording (see `compose` in Composer.ts).
|
|
7
|
+
// It is deliberately coupled to that message; `resolver-validation.test.ts` drives the real composer,
|
|
8
|
+
// so a wording drift fails that test rather than silently disabling deferral.
|
|
9
|
+
const isUnresolvedLoadoutReference = (message) => /^loadout (?:skills|subagents) references unknown (?:skill|agent) '/.test(message);
|
|
10
|
+
const compositionWarningSeverity = (message, options) => isUnresolvedLoadoutReference(message) && !options.deferLoadoutResolution ? 'error' : 'warning';
|
|
11
|
+
const validateAgent = (set, agent, options, projectDirectory) => {
|
|
8
12
|
const definition = readAgentDefinition(agent.winner.path, agent.configPaths);
|
|
9
13
|
if (isAgentDefinitionIssue(definition)) {
|
|
10
14
|
return [{ severity: 'error', resource: `agent:${agent.slug}`, message: definition.message }];
|
|
@@ -22,7 +26,7 @@ const validateAgent = (set, agent, projectDirectory) => {
|
|
|
22
26
|
const compositionFindings = [
|
|
23
27
|
...composed.errors.map((message) => ({ severity: 'error', resource: `agent:${agent.slug}`, message })),
|
|
24
28
|
...composed.warnings.map((message) => ({
|
|
25
|
-
severity: compositionWarningSeverity(message),
|
|
29
|
+
severity: compositionWarningSeverity(message, options),
|
|
26
30
|
resource: `agent:${agent.slug}`,
|
|
27
31
|
message,
|
|
28
32
|
})),
|
|
@@ -76,34 +80,40 @@ const deduplicateFindings = (findings) => {
|
|
|
76
80
|
}
|
|
77
81
|
return [...deduplicated.values()];
|
|
78
82
|
};
|
|
83
|
+
// Validates one agent's local resources: the owning agent must resolve, and each local skill is
|
|
84
|
+
// document-checked; knowledge/commands are opaque file trees today, so only shadowing is surfaced.
|
|
85
|
+
const validateAgentLocalResources = (set, agentSlug) => {
|
|
86
|
+
const findings = [];
|
|
87
|
+
if (findResource(set, 'agent', agentSlug) === undefined) {
|
|
88
|
+
findings.push({
|
|
89
|
+
severity: 'error',
|
|
90
|
+
resource: `agent:${agentSlug}`,
|
|
91
|
+
message: 'agent-local resources require a resolvable owning agent.',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
for (const kind of agentLocalKinds) {
|
|
95
|
+
for (const resource of listAgentResources(set, agentSlug, kind)) {
|
|
96
|
+
findings.push(...shadowFindings(resource));
|
|
97
|
+
if (kind === 'skill')
|
|
98
|
+
findings.push(...validateSkill(resource));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return findings;
|
|
102
|
+
};
|
|
79
103
|
/**
|
|
80
104
|
* Collects validation findings across the effective set. `error` findings always fail validation;
|
|
81
105
|
* `warning` findings (such as shadowed definitions) fail only under `--strict`.
|
|
82
106
|
*/
|
|
83
|
-
export const validateEffectiveSet = (set, projectDirectory) => {
|
|
107
|
+
export const validateEffectiveSet = (set, projectDirectory, options = {}) => {
|
|
84
108
|
const findings = [];
|
|
85
109
|
for (const agent of listResources(set, 'agent')) {
|
|
86
|
-
findings.push(...validateAgent(set, agent, projectDirectory), ...reservedNamespaceFindings(agent));
|
|
110
|
+
findings.push(...validateAgent(set, agent, options, projectDirectory), ...reservedNamespaceFindings(agent));
|
|
87
111
|
}
|
|
88
112
|
for (const skill of listResources(set, 'skill')) {
|
|
89
113
|
findings.push(...validateSkill(skill));
|
|
90
114
|
}
|
|
91
115
|
for (const [agentSlug] of set.agentResources) {
|
|
92
|
-
|
|
93
|
-
findings.push({
|
|
94
|
-
severity: 'error',
|
|
95
|
-
resource: `agent:${agentSlug}`,
|
|
96
|
-
message: 'agent-local resources require a resolvable owning agent.',
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
for (const kind of agentLocalKinds) {
|
|
100
|
-
for (const resource of listAgentResources(set, agentSlug, kind)) {
|
|
101
|
-
findings.push(...shadowFindings(resource));
|
|
102
|
-
// Only skills have a document reader today; knowledge/commands are opaque file trees.
|
|
103
|
-
if (kind === 'skill')
|
|
104
|
-
findings.push(...validateSkill(resource));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
116
|
+
findings.push(...validateAgentLocalResources(set, agentSlug));
|
|
107
117
|
}
|
|
108
118
|
for (const kind of ['agent', 'skill', 'knowledge', 'command']) {
|
|
109
119
|
for (const resource of listResources(set, kind)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResolverValidation.js","sourceRoot":"","sources":["../../src/resolver/ResolverValidation.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEnF,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ResolverValidation.js","sourceRoot":"","sources":["../../src/resolver/ResolverValidation.ts"],"names":[],"mappings":"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"}
|
|
@@ -56,14 +56,20 @@
|
|
|
56
56
|
"tools": {
|
|
57
57
|
"type": "object",
|
|
58
58
|
"properties": {
|
|
59
|
-
"allow": { "type": "array", "items": { "
|
|
60
|
-
"deny": { "type": "array", "items": { "
|
|
59
|
+
"allow": { "type": "array", "items": { "$ref": "#/$defs/toolName" } },
|
|
60
|
+
"deny": { "type": "array", "items": { "$ref": "#/$defs/toolName" } }
|
|
61
61
|
},
|
|
62
62
|
"additionalProperties": false
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"additionalProperties": true,
|
|
66
66
|
"$defs": {
|
|
67
|
+
"toolName": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"minLength": 1,
|
|
70
|
+
"pattern": "^[^\\s,-][^\\s,]*$",
|
|
71
|
+
"description": "A tool name. A leading '-' would become an independent harness flag when projected, and a comma or whitespace would split the name inside a projected flag value, so all three are rejected."
|
|
72
|
+
},
|
|
67
73
|
"slugList": {
|
|
68
74
|
"type": "array",
|
|
69
75
|
"items": { "type": "string", "minLength": 1 }
|