@raishin/vanguard-frontier-agentic 3.1.0 → 3.2.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +1 -1
- package/catalog/asset-integrity.json +63 -28
- package/catalog/model-assignments.json +19613 -0
- package/catalog/model-policy.json +371 -0
- package/catalog/model-registry.json +134 -0
- package/catalog/skill-manifest.json +18 -18
- package/package.json +9 -4
- package/plugins/vanguard-frontier-agentic/.codex-plugin/plugin.json +1 -1
- package/schemas/model-policy.schema.json +60 -0
- package/schemas/model-registry.schema.json +127 -0
- package/scripts/export-marketplace-agents.mjs +48 -15
- package/scripts/model-policy.mjs +1353 -0
- package/skills/gcp/gcp-firebase-developer/SKILL.md +1 -1
- package/skills/gcp/gcp-gke-platform-operator/SKILL.md +1 -1
- package/skills/salesforce/salesforce-agentforce-stdm-observer-skill/SKILL.md +1 -1
- package/skills/salesforce/salesforce-apex-log-analyzer-skill/SKILL.md +1 -1
- package/skills/salesforce/salesforce-apex-test-runner-skill/SKILL.md +1 -1
- package/skills/salesforce/salesforce-soql-explorer-skill/SKILL.md +1 -1
- package/tests/test-vfa-export-coverage.test.mjs +30 -0
- package/tests/validate-skill-coherence.py +364 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanguard-frontier-agentic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Curated marketplace for cloud and zero-trust AI workflows. 331 agents, 286 skills, and rules across AWS, Azure, OCI, GCP, Alibaba Cloud, Huawei Cloud, Kubernetes, and Terraform.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Raishin",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/Raishin/vanguard-frontier-agentic/schemas/model-policy.schema.json",
|
|
4
|
+
"title": "VFA Model Policy",
|
|
5
|
+
"description": "Per-harness model and reasoning-effort policy for agent harness variants. Canonical intent consumed by scripts/model-policy.mjs, which projects it into executable harness files and generates catalog/model-assignments.json. Precedence: agent > role > provider > all; 'auto' omits the managed field so the harness runtime default applies.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["manifest_version", "rules"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"manifest_version": {
|
|
11
|
+
"const": 1
|
|
12
|
+
},
|
|
13
|
+
"description": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"defaults": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"description": "Informational: the implicit baseline when no rule matches. Both fields are constitutionally 'auto'.",
|
|
19
|
+
"required": ["model", "reasoning_effort"],
|
|
20
|
+
"additionalProperties": false,
|
|
21
|
+
"properties": {
|
|
22
|
+
"model": { "const": "auto" },
|
|
23
|
+
"reasoning_effort": { "const": "auto" }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"rules": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["scope", "harness"],
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"anyOf": [
|
|
33
|
+
{ "required": ["model"] },
|
|
34
|
+
{ "required": ["reasoning_effort"] }
|
|
35
|
+
],
|
|
36
|
+
"properties": {
|
|
37
|
+
"scope": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"pattern": "^(all|(provider|role|agent):[a-z0-9][a-z0-9-]*)$",
|
|
40
|
+
"description": "Targeting scope: 'all', 'provider:<id>', 'role:<id>' (install-roles.json), or 'agent:<id>'."
|
|
41
|
+
},
|
|
42
|
+
"harness": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"enum": ["codex", "copilot", "claude-code", "cursor", "gemini", "kiro"]
|
|
45
|
+
},
|
|
46
|
+
"model": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]*$",
|
|
49
|
+
"description": "Model name for this harness, or 'auto' to clear the field. This pattern is shape only (union of every harness's charset, including codex's ':' for Ollama name:tag and '/' for OpenRouter author/model); registry membership per harness is enforced by scripts/model-policy.mjs check against catalog/model-registry.json."
|
|
50
|
+
},
|
|
51
|
+
"reasoning_effort": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"enum": ["auto", "none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
54
|
+
"description": "Reasoning effort for harnesses that support it (codex, claude-code today). This is the union vocabulary across harnesses; the per-harness subset (codex: none|minimal|low|medium|high|xhigh; claude-code: low|medium|high|xhigh|max; cursor: no reasoning_effort field — unsupported, not even 'none') is enforced by scripts/model-policy.mjs check against catalog/model-registry.json. 'auto' clears the field."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/Raishin/vanguard-frontier-agentic/schemas/model-registry.schema.json",
|
|
4
|
+
"title": "VFA Model Registry",
|
|
5
|
+
"description": "Verified model-name and reasoning-effort capability matrix per harness. Consumed by scripts/model-policy.mjs to fail closed on values that would 404 (model_not_found) or be silently dropped at request time. Namespaces are evaluated in array order; the first whose 'match' pattern accepts the value classifies it. 'closed' membership requires the model to be listed in 'models'; 'open' namespaces (catalogs too large to enumerate, e.g. Ollama/OpenRouter) validate shape only. Refresh via .claude/skills/model-registry-refresh/SKILL.md; human-readable companion: docs/model-policy-matrix.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["manifest_version", "last_refreshed", "harnesses"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"manifest_version": { "const": 1 },
|
|
11
|
+
"description": { "type": "string" },
|
|
12
|
+
"last_refreshed": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
|
|
15
|
+
"description": "Date the registry as a whole was last re-verified against official documentation."
|
|
16
|
+
},
|
|
17
|
+
"harnesses": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"required": ["codex", "claude-code", "cursor"],
|
|
20
|
+
"additionalProperties": false,
|
|
21
|
+
"properties": {
|
|
22
|
+
"codex": { "$ref": "#/definitions/harness" },
|
|
23
|
+
"claude-code": { "$ref": "#/definitions/harness" },
|
|
24
|
+
"cursor": { "$ref": "#/definitions/harness" }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"definitions": {
|
|
29
|
+
"harness": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"required": ["reasoning_key", "reasoning_efforts", "namespaces"],
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"properties": {
|
|
34
|
+
"reasoning_key": {
|
|
35
|
+
"type": ["string", "null"],
|
|
36
|
+
"description": "Config key the harness uses for reasoning effort (codex: model_reasoning_effort in codex.toml; claude-code: effort in frontmatter). null = harness has no projectable reasoning field."
|
|
37
|
+
},
|
|
38
|
+
"reasoning_efforts": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": { "type": "string", "pattern": "^[a-z]+$" },
|
|
41
|
+
"description": "Complete vocabulary of effort values the harness accepts. Empty = no reasoning field."
|
|
42
|
+
},
|
|
43
|
+
"reasoning_note": { "type": "string" },
|
|
44
|
+
"namespaces": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"minItems": 1,
|
|
47
|
+
"items": { "$ref": "#/definitions/namespace" }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"namespace": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["id", "match", "membership"],
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
57
|
+
"description": { "type": "string" },
|
|
58
|
+
"match": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "Anchored (^...$) regular expression classifying a model value into this namespace. Evaluated in array order; first match wins."
|
|
61
|
+
},
|
|
62
|
+
"membership": {
|
|
63
|
+
"enum": ["closed", "open"],
|
|
64
|
+
"description": "closed: value must appear in 'models' (fail closed on unregistered names). open: shape-validated only; catalog too large/volatile to enumerate."
|
|
65
|
+
},
|
|
66
|
+
"model_provider": {
|
|
67
|
+
"type": ["string", "null"],
|
|
68
|
+
"description": "codex only: value projected as model_provider in codex.toml when a model in this namespace is assigned. null = default provider (no line projected)."
|
|
69
|
+
},
|
|
70
|
+
"requires_provider_table": {
|
|
71
|
+
"type": "boolean",
|
|
72
|
+
"description": "codex only: the operator's codex config must define a matching [model_providers.<id>] table for this route to work."
|
|
73
|
+
},
|
|
74
|
+
"reasoning_efforts": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": { "type": "string", "pattern": "^[a-z]+$" },
|
|
77
|
+
"description": "Effort values verified to work for models routed through this namespace. Overrides the harness vocabulary; empty = pinning reasoning onto this namespace fails check (fail closed)."
|
|
78
|
+
},
|
|
79
|
+
"reasoning_note": { "type": "string" },
|
|
80
|
+
"sources": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": { "type": "string" },
|
|
83
|
+
"description": "Official documentation URLs / Context7 library IDs backing this namespace's entries."
|
|
84
|
+
},
|
|
85
|
+
"examples": {
|
|
86
|
+
"type": "array",
|
|
87
|
+
"items": { "type": "string" },
|
|
88
|
+
"description": "Advisory sample values for open namespaces (not an allowlist)."
|
|
89
|
+
},
|
|
90
|
+
"models": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": { "$ref": "#/definitions/model" },
|
|
93
|
+
"description": "Verified allowlist for closed namespaces."
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"model": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"required": ["id", "last_verified"],
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"properties": {
|
|
102
|
+
"id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/-]*$" },
|
|
103
|
+
"reasoning_efforts": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"items": { "type": "string", "pattern": "^[a-z]+$" },
|
|
106
|
+
"description": "Effort values this model supports. Omitted = inherit the namespace (or harness) vocabulary. Empty array = non-reasoning model; pinning any effort fails check."
|
|
107
|
+
},
|
|
108
|
+
"last_verified": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
|
|
109
|
+
"source": { "type": "string" },
|
|
110
|
+
"note": { "type": "string" },
|
|
111
|
+
"status": {
|
|
112
|
+
"enum": ["available", "retiring", "retired"],
|
|
113
|
+
"description": "Provider lifecycle state. Omitted = available. 'retiring' = provider announced a shutdown; policies pinning it get warnings everywhere (check/apply/report, assignments index, TUI). 'retired' = shutdown effective; projection falls back to the documented successor (chain-followed) with a loud warning until the policy is migrated. Behavior is driven ONLY by this committed field, never by the wall clock — flip it via the model-registry-refresh workflow."
|
|
114
|
+
},
|
|
115
|
+
"retirement_date": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
|
|
118
|
+
"description": "Provider-announced shutdown date (informational, surfaced in warnings; does not itself change projection)."
|
|
119
|
+
},
|
|
120
|
+
"successor": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"description": "Documented replacement model id within the same namespace. Required when status is 'retired' (it is the fallback target); strongly recommended for 'retiring'. Must reference an existing model entry; chains must terminate at a non-retired model."
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -273,16 +273,6 @@ function ensurePlatform(platform) {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function assertWithin(parent, child, label) {
|
|
276
|
-
// Lexical containment check — path.resolve() is purely string-based and does
|
|
277
|
-
// NOT follow symlinks. This guards against traversal strings (../../) and
|
|
278
|
-
// metadata.json with absolute paths outside the repo.
|
|
279
|
-
//
|
|
280
|
-
// Residual TOCTOU: if an adversary races to create a symlink at `child`
|
|
281
|
-
// AFTER this check but BEFORE the actual write, the symlink target would be
|
|
282
|
-
// written to. copyFile()/copySkillTree() use lstatSync on source AND
|
|
283
|
-
// destination to detect pre-existing symlinks, which closes the window for
|
|
284
|
-
// the common case. A fully TOCTOU-proof solution requires O_NOFOLLOW at the
|
|
285
|
-
// kernel level, which is not exposed by Node.js fs APIs.
|
|
286
276
|
const resolvedParent = path.resolve(parent);
|
|
287
277
|
const resolvedChild = path.resolve(child);
|
|
288
278
|
const sep = path.sep;
|
|
@@ -295,6 +285,47 @@ function assertWithin(parent, child, label) {
|
|
|
295
285
|
}
|
|
296
286
|
}
|
|
297
287
|
|
|
288
|
+
function assertNoSymlinkAncestor(parent, child, label) {
|
|
289
|
+
assertWithin(parent, child, label);
|
|
290
|
+
|
|
291
|
+
const resolvedParent = path.resolve(parent);
|
|
292
|
+
const resolvedChild = path.resolve(child);
|
|
293
|
+
const relativeParent = path.dirname(path.relative(resolvedParent, resolvedChild));
|
|
294
|
+
if (!relativeParent || relativeParent === ".") return;
|
|
295
|
+
|
|
296
|
+
let current = resolvedParent;
|
|
297
|
+
for (const part of relativeParent.split(path.sep)) {
|
|
298
|
+
current = path.join(current, part);
|
|
299
|
+
let stat;
|
|
300
|
+
try {
|
|
301
|
+
stat = fs.lstatSync(current);
|
|
302
|
+
} catch (err) {
|
|
303
|
+
if (err && err.code === "ENOENT") return;
|
|
304
|
+
throw err;
|
|
305
|
+
}
|
|
306
|
+
if (stat.isSymbolicLink()) {
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Refusing to ${label}: symbolic link ancestor '${current}' would redirect writes outside '${resolvedParent}'. ` +
|
|
309
|
+
`Remove the symlink and retry.`
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
if (!stat.isDirectory()) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`Refusing to ${label}: path ancestor '${current}' is not a directory.`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function assertSafeWriteDestination(parent, destination, label) {
|
|
321
|
+
// Lexical containment blocks traversal strings and absolute-path metadata.
|
|
322
|
+
// The ancestor walk then rejects pre-existing symlinks such as `.codex -> /x`
|
|
323
|
+
// before mkdir/copy can follow them. Node does not expose fully race-proof
|
|
324
|
+
// O_NOFOLLOW directory creation, so this intentionally closes the reachable
|
|
325
|
+
// planted-symlink case without pretending to solve privileged TOCTOU races.
|
|
326
|
+
assertNoSymlinkAncestor(parent, destination, label);
|
|
327
|
+
}
|
|
328
|
+
|
|
298
329
|
function loadSkills() {
|
|
299
330
|
const skillsRoot = path.join(repoRoot, "skills");
|
|
300
331
|
if (!fs.existsSync(skillsRoot)) return new Map();
|
|
@@ -325,11 +356,12 @@ function loadSkills() {
|
|
|
325
356
|
return byName;
|
|
326
357
|
}
|
|
327
358
|
|
|
328
|
-
function copySkillTree(sourceDir, destDir, force) {
|
|
359
|
+
function copySkillTree(sourceDir, destDir, force, targetRoot) {
|
|
329
360
|
assertWithin(repoRoot, sourceDir, "read skill source");
|
|
330
361
|
for (const entry of fs.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
331
362
|
const src = path.join(sourceDir, entry.name);
|
|
332
363
|
const dst = path.join(destDir, entry.name);
|
|
364
|
+
assertSafeWriteDestination(targetRoot, dst, "write skill tree destination");
|
|
333
365
|
if (entry.isSymbolicLink()) {
|
|
334
366
|
throw new Error(`Refusing to copy symbolic link in skill tree: ${src}`);
|
|
335
367
|
}
|
|
@@ -342,7 +374,7 @@ function copySkillTree(sourceDir, destDir, force) {
|
|
|
342
374
|
);
|
|
343
375
|
}
|
|
344
376
|
if (entry.isDirectory()) {
|
|
345
|
-
copySkillTree(src, dst, force);
|
|
377
|
+
copySkillTree(src, dst, force, targetRoot);
|
|
346
378
|
continue;
|
|
347
379
|
}
|
|
348
380
|
if (!entry.isFile()) continue;
|
|
@@ -404,7 +436,8 @@ function resolveCompanionSkills(selectedAgents, skillsByName, role, includeAll,
|
|
|
404
436
|
return { skillNames: [...skillNames].sort(), orphans };
|
|
405
437
|
}
|
|
406
438
|
|
|
407
|
-
function copyFile(source, destination, force) {
|
|
439
|
+
function copyFile(source, destination, force, targetRoot) {
|
|
440
|
+
assertSafeWriteDestination(targetRoot, destination, "write file destination");
|
|
408
441
|
const sourceStat = fs.lstatSync(source);
|
|
409
442
|
if (sourceStat.isSymbolicLink()) {
|
|
410
443
|
throw new Error(`Refusing to copy symbolic link as harness source: ${source}`);
|
|
@@ -662,7 +695,7 @@ function main() {
|
|
|
662
695
|
|
|
663
696
|
for (const operation of operations) {
|
|
664
697
|
assertWithin(args.repo, operation.dest, "write destination");
|
|
665
|
-
copyFile(operation.source, operation.dest, args.force);
|
|
698
|
+
copyFile(operation.source, operation.dest, args.force, args.repo);
|
|
666
699
|
if (platform === "codex") {
|
|
667
700
|
rewriteCodexAgentSkillPaths(operation.dest, args.repo);
|
|
668
701
|
}
|
|
@@ -705,7 +738,7 @@ function main() {
|
|
|
705
738
|
if (!sourceDir) continue;
|
|
706
739
|
const destDir = path.join(args.repo, skillsDestRoot, skillName);
|
|
707
740
|
assertWithin(args.repo, destDir, "write skill destination");
|
|
708
|
-
copySkillTree(sourceDir, destDir, args.force);
|
|
741
|
+
copySkillTree(sourceDir, destDir, args.force, args.repo);
|
|
709
742
|
console.log(`installed\tskill:${skillName}\t${platform}\t${path.relative(args.repo, destDir)}`);
|
|
710
743
|
bundled += 1;
|
|
711
744
|
}
|