@aiwg/cli 2026.9.24 → 2026.10.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/agentic/code/providers/capability-matrix.yaml +2 -1
- package/dist/src/artifacts/discovery-shadow-evaluator.js +177 -0
- package/dist/src/artifacts/discovery-shadow.js +195 -0
- package/dist/src/artifacts/index-builder.js +47 -9
- package/dist/src/artifacts/query-engine.js +94 -1
- package/dist/src/artifacts/types.js +1 -0
- package/dist/src/cli/handlers/decision.js +177 -0
- package/dist/src/cli/handlers/gates.js +154 -0
- package/dist/src/cli/handlers/index.js +5 -1
- package/dist/src/cli/handlers/sessions.js +10 -7
- package/dist/src/cli/handlers/utilities.js +20 -0
- package/dist/src/config/aiwg-config.js +15 -2
- package/dist/src/decision/acceptance.js +25 -0
- package/dist/src/decision/adapters/jev.js +29 -3
- package/dist/src/decision/ask.js +352 -0
- package/dist/src/decision/calibration/registry.js +9 -0
- package/dist/src/decision/compose.js +74 -15
- package/dist/src/decision/context-live-promotion.js +344 -0
- package/dist/src/decision/context-live-qualification.js +591 -0
- package/dist/src/decision/context-pruning.js +847 -0
- package/dist/src/decision/driver.js +432 -0
- package/dist/src/decision/egress-live-qualification.js +874 -0
- package/dist/src/decision/ensemble/contract.js +64 -1
- package/dist/src/decision/ensemble/index.js +1 -0
- package/dist/src/decision/ensemble/report.js +8 -42
- package/dist/src/decision/ensemble/runtime.js +571 -0
- package/dist/src/decision/ensemble-study/artifacts.js +85 -0
- package/dist/src/decision/ensemble-study/calibration.js +443 -0
- package/dist/src/decision/ensemble-study/corpus.js +229 -0
- package/dist/src/decision/ensemble-study/multifact-artifacts.js +23 -0
- package/dist/src/decision/ensemble-study/multifact-study.js +596 -0
- package/dist/src/decision/ensemble-study/multifact.js +241 -0
- package/dist/src/decision/ensemble-study/protocol.js +77 -0
- package/dist/src/decision/ensemble-study/score.js +462 -0
- package/dist/src/decision/ensemble-study/staged.js +87 -0
- package/dist/src/decision/ensemble-study/statistics.js +84 -0
- package/dist/src/decision/evaluate.js +227 -14
- package/dist/src/decision/export.js +4 -0
- package/dist/src/decision/feature-export.js +1074 -0
- package/dist/src/decision/graph-index.js +16 -0
- package/dist/src/decision/graph-live-qualification.js +1041 -0
- package/dist/src/decision/graph-live-workload.js +245 -0
- package/dist/src/decision/graph-skill-bridge.js +21 -3
- package/dist/src/decision/heldout/calibration.js +140 -0
- package/dist/src/decision/heldout/collector.js +430 -0
- package/dist/src/decision/heldout/contract.js +309 -0
- package/dist/src/decision/heldout/d29-generator-ids.js +12 -0
- package/dist/src/decision/heldout/d29-passage-baseline-v2.js +93 -0
- package/dist/src/decision/heldout/d29-passage-baseline-v3.js +100 -0
- package/dist/src/decision/heldout/d29-passage-baseline.js +66 -0
- package/dist/src/decision/heldout/d29-pools-v8.js +575 -0
- package/dist/src/decision/heldout/d29-pools.js +288 -0
- package/dist/src/decision/heldout/d29-v6.js +169 -0
- package/dist/src/decision/heldout/d29-v7.js +202 -0
- package/dist/src/decision/heldout/d29-v8.js +586 -0
- package/dist/src/decision/heldout/generator-registry.js +67 -0
- package/dist/src/decision/heldout/generators.js +696 -0
- package/dist/src/decision/heldout/index.js +8 -0
- package/dist/src/decision/heldout/journal.js +346 -0
- package/dist/src/decision/heldout/study-generators.js +64 -0
- package/dist/src/decision/heldout/types.js +2 -0
- package/dist/src/decision/host-policy.js +61 -0
- package/dist/src/decision/index.js +13 -0
- package/dist/src/decision/interop-profile.v1.json +112 -0
- package/dist/src/decision/interop.js +653 -0
- package/dist/src/decision/issue-triage/index.js +3 -0
- package/dist/src/decision/issue-triage/runtime.js +1069 -0
- package/dist/src/decision/issue-triage/types.js +2 -0
- package/dist/src/decision/jev-credentials.js +121 -0
- package/dist/src/decision/patterns/catalog.js +28 -8
- package/dist/src/decision/patterns/playground.js +28 -11
- package/dist/src/decision/patterns/specs.js +19 -10
- package/dist/src/decision/preprocessed-evidence.js +724 -0
- package/dist/src/decision/qualification/capture.js +8 -2
- package/dist/src/decision/qualification/gate-evidence.js +19 -4
- package/dist/src/decision/qualification/quality.js +57 -49
- package/dist/src/decision/qualification/release.js +76 -3
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/routing/contract.js +175 -0
- package/dist/src/decision/routing/index.js +5 -0
- package/dist/src/decision/routing/report.js +216 -0
- package/dist/src/decision/routing/runtime.js +685 -0
- package/dist/src/decision/routing/types.js +2 -0
- package/dist/src/decision/sdlc-screening.js +1157 -0
- package/dist/src/decision/sensitivity/comparative.js +154 -0
- package/dist/src/decision/sensitivity/contract.js +218 -0
- package/dist/src/decision/sensitivity/index.js +5 -0
- package/dist/src/decision/sensitivity/runtime.js +912 -0
- package/dist/src/decision/sensitivity/types.js +2 -0
- package/dist/src/decision/validate.js +26 -0
- package/dist/src/extensions/commands/definitions.js +38 -0
- package/dist/src/extensions/manifest.js +44 -0
- package/dist/src/gates/discovery.js +315 -0
- package/dist/src/gates/driver.js +388 -0
- package/dist/src/gates/evaluate.js +719 -0
- package/dist/src/gates/floors.js +255 -0
- package/dist/src/gates/index.js +17 -0
- package/dist/src/gates/providers/evidence.js +38 -0
- package/dist/src/gates/providers/index.js +21 -0
- package/dist/src/gates/providers/isolated-child-source.js +446 -0
- package/dist/src/gates/providers/loader.js +1069 -0
- package/dist/src/gates/providers/paired.js +52 -0
- package/dist/src/gates/providers/proportion.js +60 -0
- package/dist/src/gates/providers/registry.js +40 -0
- package/dist/src/gates/providers/scalar.js +41 -0
- package/dist/src/gates/providers/screening.js +53 -0
- package/dist/src/gates/providers/types.js +25 -0
- package/dist/src/gates/registry.js +1003 -0
- package/dist/src/gates/report.js +102 -0
- package/dist/src/gates/schema.js +56 -0
- package/dist/src/gates/stats/binomial.js +26 -0
- package/dist/src/gates/stats/bootstrap.js +62 -0
- package/dist/src/gates/stats/clopper-pearson.js +95 -0
- package/dist/src/gates/stats/digest.js +26 -0
- package/dist/src/gates/stats/error.js +5 -0
- package/dist/src/gates/stats/index.js +9 -0
- package/dist/src/gates/stats/normal.js +35 -0
- package/dist/src/gates/stats/paired.js +120 -0
- package/dist/src/gates/stats/splits.js +30 -0
- package/dist/src/gates/types.js +8 -0
- package/dist/src/governance/redaction.js +14 -4
- package/dist/src/mcp/muse-native-config.mjs +123 -0
- package/dist/src/mcp/tools/decision.mjs +228 -0
- package/dist/src/mcp/tools/subsystems.mjs +2 -0
- package/dist/src/providers/capability-matrix.yaml +2 -1
- package/dist/src/providers/provider-definitions.js +2 -1
- package/dist/src/sessions/adapters/muse.js +336 -39
- package/dist/src/sessions/batch-import.js +7 -0
- package/dist/src/sessions/workspace-discovery.js +13 -2
- package/package.json +1 -1
- package/schemas/decision/ConformalFrozenOpenData.v2.schema.json +371 -0
- package/schemas/decision/ConformalOpenDataReport.v2.schema.json +421 -0
- package/schemas/decision/ConformalPreregistration.v2.schema.json +529 -0
- package/schemas/decision/ContextPruningCandidate.v1.schema.json +47 -0
- package/schemas/decision/ContextPruningEvaluationReport.v1.schema.json +758 -0
- package/schemas/decision/ContextPruningPreregistration.v1.schema.json +175 -0
- package/schemas/decision/ContextPruningReceipt.v1.schema.json +72 -0
- package/schemas/decision/D17Multifact.v1.schema.json +1851 -0
- package/schemas/decision/D17StudyAnalysis.v1.schema.json +42 -0
- package/schemas/decision/D17StudyAnalysis.v2.schema.json +51 -0
- package/schemas/decision/D17StudyCalibration.v1.schema.json +819 -0
- package/schemas/decision/D17StudyDryRun.v1.schema.json +169 -0
- package/schemas/decision/D17StudyDryRun.v2.schema.json +239 -0
- package/schemas/decision/D17StudyGold.v1.schema.json +108 -0
- package/schemas/decision/D17StudyGuide.v1.schema.json +63 -0
- package/schemas/decision/D17StudyNativeTemplates.v1.schema.json +619 -0
- package/schemas/decision/D17StudyProtocol.v1.schema.json +241 -0
- package/schemas/decision/D17StudyReport.v1.schema.json +902 -0
- package/schemas/decision/D17StudyReport.v2.schema.json +1319 -0
- package/schemas/decision/D17StudyReview.v1.schema.json +175 -0
- package/schemas/decision/D17StudySplits.v1.schema.json +217 -0
- package/schemas/decision/D29Study.v1.schema.json +1903 -0
- package/schemas/decision/D29Study.v2.schema.json +1344 -0
- package/schemas/decision/D29Study.v3.schema.json +609 -0
- package/schemas/decision/D29Study.v4.schema.json +1133 -0
- package/schemas/decision/D29Study.v6.schema.json +1205 -0
- package/schemas/decision/D29Study.v7.schema.json +1471 -0
- package/schemas/decision/D29Study.v8.schema.json +671 -0
- package/schemas/decision/D29Study.v9.schema.json +778 -0
- package/schemas/decision/DecisionComparativeReplayAuditSample.v1.schema.json +137 -0
- package/schemas/decision/DecisionComparativeReplayCorpus.v1.schema.json +258 -0
- package/schemas/decision/DecisionComparativeReplayFreeze.v1.schema.json +74 -0
- package/schemas/decision/DecisionComparativeReplayGold.v1.schema.json +50 -0
- package/schemas/decision/DecisionComparativeReplayGoldAsset.v1.schema.json +37 -0
- package/schemas/decision/DecisionComparativeReplayIntegrity.v1.schema.json +81 -0
- package/schemas/decision/DecisionComparativeReplayMembers.v1.schema.json +52 -0
- package/schemas/decision/DecisionComparativeReplayPreregistration.v1.schema.json +113 -0
- package/schemas/decision/DecisionComparativeReplayReport.v1.schema.json +407 -0
- package/schemas/decision/DecisionComparativeReplayReviewPackets.v1.schema.json +260 -0
- package/schemas/decision/DecisionComparativeReplaySplit.v1.schema.json +84 -0
- package/schemas/decision/DecisionFeatureObservation.v1.schema.json +564 -0
- package/schemas/decision/DecisionFeatureSet.v1.schema.json +155 -0
- package/schemas/decision/DecisionInteropMapping.v1.schema.json +85 -0
- package/schemas/decision/DecisionIssueTriageEvaluationManifest.v1.schema.json +174 -0
- package/schemas/decision/DecisionIssueTriageEvaluationReport.v1.schema.json +280 -0
- package/schemas/decision/DecisionIssueTriagePilotPack.v1.schema.json +114 -0
- package/schemas/decision/DecisionRouteReceipt.v1.schema.json +155 -0
- package/schemas/decision/DecisionRoutingPolicy.v1.schema.json +132 -0
- package/schemas/decision/DecisionRoutingShadowReport.v1.schema.json +170 -0
- package/schemas/decision/DecisionRoutingTask.v1.schema.json +49 -0
- package/schemas/decision/DecisionRuleset.schema.json +8 -2
- package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +8 -2
- package/schemas/decision/DecisionSensitivityPlan.v1.schema.json +191 -0
- package/schemas/decision/DecisionSensitivityReport.v1.schema.json +163 -0
- package/schemas/decision/HeldoutApproval.v1.schema.json +312 -0
- package/schemas/decision/HeldoutAttempt.v1.schema.json +211 -0
- package/schemas/decision/HeldoutBaseline.v1.schema.json +90 -0
- package/schemas/decision/HeldoutCalibrationPhase.v1.schema.json +87 -0
- package/schemas/decision/HeldoutCorpus.v1.schema.json +167 -0
- package/schemas/decision/HeldoutEvent.v1.schema.json +41 -0
- package/schemas/decision/HeldoutFrozen.v1.schema.json +142 -0
- package/schemas/decision/HeldoutPreregistration.v1.schema.json +222 -0
- package/schemas/decision/HeldoutSpendEvent.v1.schema.json +122 -0
- package/schemas/decision/HeldoutSpendHead.v1.schema.json +25 -0
- package/schemas/decision/HeldoutSpendHead.v2.schema.json +46 -0
- package/schemas/decision/HeldoutSummary.v1.schema.json +101 -0
- package/schemas/decision/OpaDecisionLogExport.v1.schema.json +65 -0
- package/schemas/decision/PreprocessedEvidence.v1.schema.json +306 -0
- package/schemas/decision/RulesetResult.v1alpha2.schema.json +175 -0
- package/schemas/decision/SdlcEvidenceScreening.v1.schema.json +499 -0
- package/schemas/decision/SdlcScreeningPreregistration.v1.schema.json +132 -0
- package/schemas/decision/SdlcScreeningPreregistration.v2.schema.json +138 -0
- package/schemas/decision/SdlcScreeningRelease.v1.schema.json +654 -0
- package/schemas/decision/SdlcScreeningRelease.v2.schema.json +654 -0
- package/tools/agents/deploy-agents.mjs +1 -1
- package/tools/agents/providers/base.mjs +40 -18
- package/tools/agents/providers/factory.mjs +10 -2
- package/tools/agents/providers/muse-hooks.mjs +1 -29
- package/tools/agents/providers/muse.mjs +6 -3
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
3
3
|
import { dirname, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
6
|
+
import addFormats from 'ajv-formats';
|
|
6
7
|
import { canonicalJson } from '../security/artifact-trust.js';
|
|
7
8
|
import { admitEntry, EntryAdmissionError } from './entry.js';
|
|
8
9
|
import { DECISION_API_VERSION, DECISION_API_VERSION_STRUCTURED } from './types.js';
|
|
@@ -29,6 +30,8 @@ export class DecisionValidationError extends Error {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
let validators = null;
|
|
33
|
+
let decisionAjv = null;
|
|
34
|
+
let preprocessedEvidenceValidator = null;
|
|
32
35
|
function schemaRoot() {
|
|
33
36
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
34
37
|
const candidates = [resolve(here, '../../schemas/decision'), resolve(here, '../../../schemas/decision')];
|
|
@@ -47,6 +50,7 @@ function getValidators() {
|
|
|
47
50
|
ajv.addSchema(admissionSchema);
|
|
48
51
|
ajv.addSchema(contextEvidenceSchema);
|
|
49
52
|
ajv.addSchema(providerPrefixSchema);
|
|
53
|
+
decisionAjv = ajv;
|
|
50
54
|
validators = new Map();
|
|
51
55
|
for (const [kind, filename] of Object.entries(schemaFiles)) {
|
|
52
56
|
const schema = JSON.parse(readFileSync(resolve(schemaRoot(), filename), 'utf8'));
|
|
@@ -58,6 +62,26 @@ function getValidators() {
|
|
|
58
62
|
}
|
|
59
63
|
return validators;
|
|
60
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* D24: closed-schema errors for a PreprocessedEvidence manifest, with formats enforced, or
|
|
67
|
+
* null when valid. The runtime resolver runs this so it can never accept what the schema rejects.
|
|
68
|
+
*/
|
|
69
|
+
export function preprocessedEvidenceSchemaErrors(value) {
|
|
70
|
+
if (!preprocessedEvidenceValidator) {
|
|
71
|
+
const ajv = new Ajv2020({ strict: true, allErrors: true });
|
|
72
|
+
addFormats(ajv);
|
|
73
|
+
preprocessedEvidenceValidator = ajv.compile(JSON.parse(readFileSync(resolve(schemaRoot(), 'PreprocessedEvidence.v1.schema.json'), 'utf8')));
|
|
74
|
+
}
|
|
75
|
+
return preprocessedEvidenceValidator(value) ? null : ajvMessage(preprocessedEvidenceValidator.errors);
|
|
76
|
+
}
|
|
77
|
+
/** D24: RulesetResult v1alpha2 `preprocessingLineage` shape errors, or null when valid. */
|
|
78
|
+
export function preprocessingLineageSchemaErrors(value) {
|
|
79
|
+
getValidators();
|
|
80
|
+
const validate = decisionAjv.getSchema('https://aiwg.io/schemas/decision/RulesetResult.v1alpha2.schema.json#/$defs/preprocessingLineage');
|
|
81
|
+
if (!validate)
|
|
82
|
+
throw new DecisionValidationError('preprocessing lineage schema is unavailable');
|
|
83
|
+
return validate(value) ? null : ajvMessage(validate.errors);
|
|
84
|
+
}
|
|
61
85
|
export function artifactDigest(value) {
|
|
62
86
|
const started = performance.now();
|
|
63
87
|
admitEntry(value);
|
|
@@ -116,6 +140,7 @@ export function assertDecisionWriterVersion(value, semantic) {
|
|
|
116
140
|
*/
|
|
117
141
|
export const DECISION_RESULT_V1ALPHA2_FIELDS = [
|
|
118
142
|
{ field: 'batchResult', scope: 'decision', owner: 'D07', semantic: 'batch-receipt' },
|
|
143
|
+
{ field: 'batchRequests', scope: 'ruleset', owner: 'D04', semantic: 'batch-request-usage' },
|
|
119
144
|
{ field: 'batch', scope: 'attempt', owner: 'D04', semantic: 'batch-provenance' },
|
|
120
145
|
{ field: 'admission', scope: 'attempt', owner: 'D05', semantic: 'admission-evidence' },
|
|
121
146
|
{ field: 'context', scope: 'decision', owner: 'D06', semantic: 'context-evidence' },
|
|
@@ -126,6 +151,7 @@ export const DECISION_RESULT_V1ALPHA2_FIELDS = [
|
|
|
126
151
|
{ field: 'calibrationCompatibility', scope: 'decision', owner: 'D09', semantic: 'calibration-pin' },
|
|
127
152
|
{ field: 'cache', scope: 'ruleset', owner: 'D15', semantic: 'result-cache-receipt' },
|
|
128
153
|
{ field: 'projection', scope: 'ruleset', owner: 'D10', semantic: 'projection-opt-out' },
|
|
154
|
+
{ field: 'preprocessingLineage', scope: 'ruleset', owner: 'D24', semantic: 'trust-projection' },
|
|
129
155
|
];
|
|
130
156
|
/** JSON paths of every v1alpha2-only field present in a DecisionResult or RulesetResult. */
|
|
131
157
|
export function decisionResultV1Alpha2Fields(value) {
|
|
@@ -182,6 +182,42 @@ export const installationCommand = {
|
|
|
182
182
|
allowedTools: ['Read', 'Write'],
|
|
183
183
|
},
|
|
184
184
|
};
|
|
185
|
+
export const decisionCommand = {
|
|
186
|
+
id: 'decision',
|
|
187
|
+
type: 'command',
|
|
188
|
+
name: 'Decision Driver',
|
|
189
|
+
description: 'Inspect, validate and run governed decision classification workflows',
|
|
190
|
+
version: '1.0.0',
|
|
191
|
+
capabilities: ['cli', 'decision', 'classification', 'mcp', 'validation'],
|
|
192
|
+
keywords: ['decision', 'classification', 'patterns', 'mcp', 'evaluate', 'validate'],
|
|
193
|
+
category: 'utility',
|
|
194
|
+
platforms: { claude: 'full', generic: 'full' },
|
|
195
|
+
deployment: { pathTemplate: '.{platform}/commands/{id}.md', core: true },
|
|
196
|
+
metadata: {
|
|
197
|
+
type: 'command',
|
|
198
|
+
template: 'utility',
|
|
199
|
+
argumentHint: '<capabilities|patterns|validate|evaluate|setup> [options]',
|
|
200
|
+
allowedTools: ['Read', 'Write', 'Bash'],
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
export const gatesCommand = {
|
|
204
|
+
id: 'gates',
|
|
205
|
+
type: 'command',
|
|
206
|
+
name: 'Gates',
|
|
207
|
+
description: 'Validate, evaluate and inspect declarative gate packs offline',
|
|
208
|
+
version: '1.0.0',
|
|
209
|
+
capabilities: ['cli', 'gates', 'validation', 'evaluation'],
|
|
210
|
+
keywords: ['gates', 'gate pack', 'validate', 'evaluate', 'promotion', 'hold', 'rollback'],
|
|
211
|
+
category: 'utility',
|
|
212
|
+
platforms: { claude: 'full', generic: 'full' },
|
|
213
|
+
deployment: { pathTemplate: '.{platform}/commands/{id}.md', core: true },
|
|
214
|
+
metadata: {
|
|
215
|
+
type: 'command',
|
|
216
|
+
template: 'utility',
|
|
217
|
+
argumentHint: '<validate|evaluate|show|list> [options]',
|
|
218
|
+
allowedTools: ['Read'],
|
|
219
|
+
},
|
|
220
|
+
};
|
|
185
221
|
// Renamed from `refreshCommand` as part of #694 (avoid collision with git sync
|
|
186
222
|
// semantics) and re-linked to `refreshHandler` in #919. Users who type
|
|
187
223
|
// `aiwg sync` still reach this handler via its 'sync' alias and see a
|
|
@@ -3715,6 +3751,8 @@ export const commandDefinitions = [
|
|
|
3715
3751
|
contextFirewallCommand,
|
|
3716
3752
|
updateCommand,
|
|
3717
3753
|
installationCommand,
|
|
3754
|
+
decisionCommand,
|
|
3755
|
+
gatesCommand,
|
|
3718
3756
|
refreshCommand,
|
|
3719
3757
|
regenerateCommand,
|
|
3720
3758
|
workspaceContextCommand,
|
|
@@ -40,6 +40,7 @@ export const ProjectLocalTypeSchema = z.enum([
|
|
|
40
40
|
*/
|
|
41
41
|
const safeRelativePath = z.string().regex(/^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*\/?$/, 'must be a relative path (alphanumeric + _-, no leading slash, no ..)');
|
|
42
42
|
const safeModuleFile = z.string().regex(/^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*\.mjs$/, 'must be a relative .mjs path (alphanumeric + _-, no leading slash, no ..)');
|
|
43
|
+
const safeJsonFile = z.string().regex(/^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*\.json$/, 'must be a relative .json path (alphanumeric + _-, no leading slash, no ..)');
|
|
43
44
|
// Single-char ids are allowed; multi-char ids must end with alphanumeric
|
|
44
45
|
// (no trailing hyphen). This pattern: `[a-z0-9]([a-z0-9-]*[a-z0-9])?`
|
|
45
46
|
const bundleNamePattern = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;
|
|
@@ -55,6 +56,7 @@ const ArtifactListsSchema = z.object({
|
|
|
55
56
|
hooks: z.array(z.string()).max(50).optional(),
|
|
56
57
|
commands: z.array(z.string()).max(200).optional(),
|
|
57
58
|
behaviors: z.array(z.string()).max(50).optional(),
|
|
59
|
+
gatePacks: z.array(z.string().min(1).max(128)).max(50).optional(),
|
|
58
60
|
});
|
|
59
61
|
const EntryPathsSchema = z.object({
|
|
60
62
|
agents: safeRelativePath.optional(),
|
|
@@ -65,6 +67,7 @@ const EntryPathsSchema = z.object({
|
|
|
65
67
|
hooks: safeRelativePath.optional(),
|
|
66
68
|
commands: safeRelativePath.optional(),
|
|
67
69
|
behaviors: safeRelativePath.optional(),
|
|
70
|
+
gatePacks: safeRelativePath.optional(),
|
|
68
71
|
}).strict();
|
|
69
72
|
export const AddonConfigSchema = z.object({
|
|
70
73
|
entry: EntryPathsSchema.optional(),
|
|
@@ -76,6 +79,10 @@ export const FrameworkConfigSchema = z.object({
|
|
|
76
79
|
path: safeRelativePath.optional(),
|
|
77
80
|
files: z.array(safeRelativePath).max(100).optional(),
|
|
78
81
|
ignore: z.array(safeRelativePath).max(100).optional(),
|
|
82
|
+
gatePacks: z.array(z.string().min(1).max(128)).max(50).optional(),
|
|
83
|
+
entry: z.object({
|
|
84
|
+
gatePacks: safeRelativePath.optional(),
|
|
85
|
+
}).strict().optional(),
|
|
79
86
|
contextContributions: z.object({
|
|
80
87
|
hookFragment: safeRelativePath.optional(),
|
|
81
88
|
sectionsDir: safeRelativePath.optional(),
|
|
@@ -136,6 +143,37 @@ export const ProviderConfigSchema = z.object({
|
|
|
136
143
|
aliases: z.array(z.string().min(1).max(64)).max(20).optional(),
|
|
137
144
|
capabilities: ProviderCapabilityOverridesSchema.optional(),
|
|
138
145
|
}).strict();
|
|
146
|
+
/**
|
|
147
|
+
* Review attestation for one gate metric provider (#2831).
|
|
148
|
+
*
|
|
149
|
+
* A reviewer records who reviewed which exact provider bytes and when. The
|
|
150
|
+
* `codeDigest` must equal the loader-computed code digest over the provider
|
|
151
|
+
* module plus its resolved local imports (and lockfile-pinned externals);
|
|
152
|
+
* registration refuses absent attestations and digest mismatches. Null or
|
|
153
|
+
* unknown digests never verify: they fail closed.
|
|
154
|
+
*/
|
|
155
|
+
export const GateProviderReviewSchema = z.object({
|
|
156
|
+
reviewer: z.string().min(1).max(128),
|
|
157
|
+
reviewedAt: z.string().datetime(),
|
|
158
|
+
codeDigest: z.string().regex(/^sha256:[0-9a-f]{64}$/, 'must be a sha256 digest'),
|
|
159
|
+
}).strict();
|
|
160
|
+
/**
|
|
161
|
+
* One addon/extension gate metric provider declared by a bundle manifest
|
|
162
|
+
* (#2831). The module is loaded ONLY through the gates registry from this
|
|
163
|
+
* declaration, inside the bundle root (no traversal, no escaping symlinks,
|
|
164
|
+
* no absolute paths, size-limited), never by arbitrary import. Core
|
|
165
|
+
* providers remain the default; this field is optional and disabled unless
|
|
166
|
+
* the caller explicitly enables bundle providers.
|
|
167
|
+
*/
|
|
168
|
+
export const GateProviderEntrySchema = z.object({
|
|
169
|
+
id: z.string().min(1).max(128).regex(/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/, 'must be a namespaced provider id <scope>/<name> (alphanumeric plus . _ -)'),
|
|
170
|
+
version: z.string().regex(/^\d+\.\d+\.\d+/, 'CalVer or SemVer (X.Y.Z[...])'),
|
|
171
|
+
module: safeModuleFile,
|
|
172
|
+
description: z.string().min(1).max(512),
|
|
173
|
+
review: GateProviderReviewSchema.optional(),
|
|
174
|
+
reviewFile: safeJsonFile.optional(),
|
|
175
|
+
}).strict().refine((entry) => (entry.review !== undefined) !== (entry.reviewFile !== undefined), { message: 'exactly one of review or reviewFile is required' });
|
|
176
|
+
export const GateProvidersSchema = z.array(GateProviderEntrySchema).max(20).refine((entries) => new Set(entries.map((entry) => entry.id)).size === entries.length, { message: 'gate provider ids must be unique within a bundle' }).refine((entries) => new Set(entries.map((entry) => entry.module)).size === entries.length, { message: 'gate provider modules must be unique within a bundle' });
|
|
139
177
|
export const CliCommandsSchema = z.object({
|
|
140
178
|
namespace: z.string()
|
|
141
179
|
.min(1)
|
|
@@ -197,6 +235,12 @@ export const BundleManifestSchema = z.object({
|
|
|
197
235
|
// Expandable CLI namespace contributed by an addon-shaped bundle.
|
|
198
236
|
// The same block is accepted by bundled addon manifests.
|
|
199
237
|
cli_commands: CliCommandsSchema.optional(),
|
|
238
|
+
// Addon/extension gate metric providers (#2831). Optional and disabled by
|
|
239
|
+
// default: the gates loader resolves each `module` inside the bundle root
|
|
240
|
+
// only, verifies the review attestation digest against the computed code
|
|
241
|
+
// digest, and registers the provider through the gates registry. Core
|
|
242
|
+
// providers remain the default when this field is absent.
|
|
243
|
+
gateProviders: GateProvidersSchema.optional(),
|
|
200
244
|
})
|
|
201
245
|
.strict()
|
|
202
246
|
.refine((m) => {
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { lstatSync, readdirSync, readFileSync, existsSync, realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { basename, join, resolve, sep } from 'node:path';
|
|
3
|
+
import { parseDecisionJson, parseDecisionYaml } from '../decision/entry.js';
|
|
4
|
+
import { MANIFEST_MAX_BYTES } from '../extensions/manifest.js';
|
|
5
|
+
import { validateGateDocument } from './schema.js';
|
|
6
|
+
export const GATE_PACKS_DIR = 'gate-packs';
|
|
7
|
+
/** Admission cap for one gate-pack authoring file, checked before reading (same as the artifact index). */
|
|
8
|
+
export const GATE_PACK_MAX_BYTES = 256 * 1024;
|
|
9
|
+
/** Manifest-declared bundle subdirs stay inside the bundle: segments only, no `..`, no leading `/`. */
|
|
10
|
+
const SAFE_RELATIVE_DIR = /^[a-zA-Z0-9_-]+(?:\/[a-zA-Z0-9_-]+)*\/?$/;
|
|
11
|
+
const realpathIfExists = (candidate) => {
|
|
12
|
+
try {
|
|
13
|
+
return realpathSync(candidate);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a manifest-declared gate-packs dir inside its bundle. A raw
|
|
21
|
+
* `entry.gatePacks` value is validated as a safe relative path and contained
|
|
22
|
+
* by realpath: `../../../outside` and symlink escapes are load errors, never
|
|
23
|
+
* followed. A missing dir resolves (callers treat it as empty); a hostile
|
|
24
|
+
* declaration throws even when the dir is absent.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveGatePacksDir(bundleDir, declared) {
|
|
27
|
+
const subdir = declared ?? GATE_PACKS_DIR;
|
|
28
|
+
if (!SAFE_RELATIVE_DIR.test(subdir)) {
|
|
29
|
+
throw new Error(`gatePacks dir '${subdir}' must be a relative path inside the bundle`
|
|
30
|
+
+ ` (alphanumeric + _-, no leading slash, no ..): ${bundleDir}`);
|
|
31
|
+
}
|
|
32
|
+
const absolute = resolve(bundleDir, subdir);
|
|
33
|
+
const realBundle = realpathIfExists(bundleDir);
|
|
34
|
+
const realTarget = realpathIfExists(absolute);
|
|
35
|
+
if (realBundle !== null && realTarget !== null
|
|
36
|
+
&& realTarget !== realBundle && !realTarget.startsWith(realBundle + sep)) {
|
|
37
|
+
throw new Error(`gatePacks dir '${subdir}' escapes its bundle: ${bundleDir}`);
|
|
38
|
+
}
|
|
39
|
+
return absolute;
|
|
40
|
+
}
|
|
41
|
+
const GATE_PACK_SUFFIXES = ['.gatepack.yaml', '.gatepack.yml', '.gatepack.json'];
|
|
42
|
+
/** True when the filename is a gate-pack authoring file (never HITL `gates/`). */
|
|
43
|
+
export function isGatePackFile(filename) {
|
|
44
|
+
const lower = filename.toLowerCase();
|
|
45
|
+
return GATE_PACK_SUFFIXES.some(suffix => lower.endsWith(suffix));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sorted gate-pack authoring files directly under the bundle's gate-packs
|
|
49
|
+
* dir. Every match is lstat-checked before it is returned: symlinks are
|
|
50
|
+
* rejected (never followed), only regular files qualify, the 256 KiB cap is
|
|
51
|
+
* enforced before any read, and the realpath must stay inside the dir.
|
|
52
|
+
*/
|
|
53
|
+
export function listGatePackFiles(bundleDir, gatePacksDir = GATE_PACKS_DIR) {
|
|
54
|
+
const dir = resolveGatePacksDir(bundleDir, gatePacksDir);
|
|
55
|
+
const realDir = realpathIfExists(dir);
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = readdirSync(dir);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const files = [];
|
|
64
|
+
for (const entry of entries.filter(isGatePackFile).sort()) {
|
|
65
|
+
const full = join(dir, entry);
|
|
66
|
+
let status;
|
|
67
|
+
try {
|
|
68
|
+
status = lstatSync(full);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new Error(`gate pack unreadable: ${full}: ${error instanceof Error ? error.message : String(error)}`);
|
|
72
|
+
}
|
|
73
|
+
if (status.isSymbolicLink())
|
|
74
|
+
throw new Error(`gate pack must not be a symlink: ${full}`);
|
|
75
|
+
if (!status.isFile())
|
|
76
|
+
throw new Error(`gate pack is not a regular file: ${full}`);
|
|
77
|
+
if (status.size > GATE_PACK_MAX_BYTES) {
|
|
78
|
+
throw new Error(`gate pack exceeds 256 KiB before reading: ${full}`);
|
|
79
|
+
}
|
|
80
|
+
const real = realpathIfExists(full);
|
|
81
|
+
if (realDir !== null && real !== null && real !== realDir && !real.startsWith(realDir + sep)) {
|
|
82
|
+
throw new Error(`gate pack escapes its bundle: ${full}`);
|
|
83
|
+
}
|
|
84
|
+
files.push(full);
|
|
85
|
+
}
|
|
86
|
+
return files;
|
|
87
|
+
}
|
|
88
|
+
/** Read the bundle manifest's gate-pack declaration when present (both legacy and Zod shapes). */
|
|
89
|
+
export function readBundleGatePacksDeclaration(bundleDir) {
|
|
90
|
+
const manifestPath = join(resolve(bundleDir), 'manifest.json');
|
|
91
|
+
if (!existsSync(manifestPath))
|
|
92
|
+
return {};
|
|
93
|
+
let manifest;
|
|
94
|
+
try {
|
|
95
|
+
const status = statSync(manifestPath);
|
|
96
|
+
if (!status.isFile() || status.size > MANIFEST_MAX_BYTES)
|
|
97
|
+
return {};
|
|
98
|
+
manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest))
|
|
104
|
+
return {};
|
|
105
|
+
const root = manifest;
|
|
106
|
+
const entry = root.entry;
|
|
107
|
+
const dir = typeof entry?.gatePacks === 'string' ? entry.gatePacks
|
|
108
|
+
: typeof entry?.['gate-packs'] === 'string' ? String(entry['gate-packs'])
|
|
109
|
+
: undefined;
|
|
110
|
+
const topNames = Array.isArray(root.gatePacks)
|
|
111
|
+
? root.gatePacks.filter((name) => typeof name === 'string')
|
|
112
|
+
: undefined;
|
|
113
|
+
for (const configKey of ['addonConfig', 'extensionConfig', 'frameworkConfig']) {
|
|
114
|
+
const config = root[configKey];
|
|
115
|
+
if (!config || typeof config !== 'object')
|
|
116
|
+
continue;
|
|
117
|
+
if (topNames === undefined && Array.isArray(config.gatePacks)) {
|
|
118
|
+
const names = config.gatePacks.filter((name) => typeof name === 'string');
|
|
119
|
+
return { ...(dir ? { dir } : {}), names };
|
|
120
|
+
}
|
|
121
|
+
const configEntry = config.entry;
|
|
122
|
+
if (dir === undefined && typeof configEntry?.gatePacks === 'string') {
|
|
123
|
+
return { dir: configEntry.gatePacks, ...(topNames ? { names: topNames } : {}) };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (root.frameworkConfig && typeof root.frameworkConfig === 'object') {
|
|
127
|
+
const framework = root.frameworkConfig;
|
|
128
|
+
if (Array.isArray(framework.gatePacks)) {
|
|
129
|
+
const names = framework.gatePacks.filter((name) => typeof name === 'string');
|
|
130
|
+
const frameworkEntry = framework.entry;
|
|
131
|
+
const frameworkDir = typeof frameworkEntry?.gatePacks === 'string' ? frameworkEntry.gatePacks : dir;
|
|
132
|
+
return { ...(frameworkDir ? { dir: frameworkDir } : {}), names };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { ...(dir ? { dir } : {}), ...(topNames ? { names: topNames } : {}) };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Parse one authoring file with admission limits and closed-schema validation.
|
|
139
|
+
* Never returns an invalid pack. The file is lstat-checked before any read:
|
|
140
|
+
* symlinks are rejected, only regular files qualify, and the 256 KiB cap is
|
|
141
|
+
* enforced up front so an unbounded read can never happen here.
|
|
142
|
+
*/
|
|
143
|
+
export function loadGatePackFile(filePath) {
|
|
144
|
+
let status;
|
|
145
|
+
try {
|
|
146
|
+
status = lstatSync(filePath);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
throw new Error(`gate pack unreadable: ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
150
|
+
}
|
|
151
|
+
if (status.isSymbolicLink())
|
|
152
|
+
throw new Error(`gate pack must not be a symlink: ${filePath}`);
|
|
153
|
+
if (!status.isFile())
|
|
154
|
+
throw new Error(`gate pack is not a regular file: ${filePath}`);
|
|
155
|
+
if (status.size > GATE_PACK_MAX_BYTES)
|
|
156
|
+
throw new Error(`gate pack exceeds 256 KiB before reading: ${filePath}`);
|
|
157
|
+
let content;
|
|
158
|
+
try {
|
|
159
|
+
content = readFileSync(filePath, 'utf8');
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
throw new Error(`gate pack unreadable: ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
163
|
+
}
|
|
164
|
+
let document;
|
|
165
|
+
try {
|
|
166
|
+
document = filePath.toLowerCase().endsWith('.json') ? parseDecisionJson(content) : parseDecisionYaml(content);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
throw new Error(`gate pack unparseable: ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const pack = validateGateDocument(document);
|
|
173
|
+
if (pack.kind !== 'GatePack')
|
|
174
|
+
throw new Error(`expected kind GatePack, got '${String(document.kind)}'`);
|
|
175
|
+
return pack;
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
throw new Error(`gate pack invalid: ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Register every pack in one bundle directory. Invalid packs are rejected
|
|
183
|
+
* with a file-pathed diagnostic; nothing invalid enters the registry.
|
|
184
|
+
*/
|
|
185
|
+
export function registerBundleGatePacks(registry, bundleDir, origin) {
|
|
186
|
+
const declaration = readBundleGatePacksDeclaration(bundleDir);
|
|
187
|
+
// The declared gate-packs dir itself must be a real directory (checked
|
|
188
|
+
// before realpath resolution): a symlinked dir would load packs from
|
|
189
|
+
// outside the bundle under the bundle's origin.
|
|
190
|
+
try {
|
|
191
|
+
if (lstatSync(join(resolve(bundleDir), declaration.dir ?? GATE_PACKS_DIR)).isSymbolicLink()) {
|
|
192
|
+
throw new Error(`bundle gate-packs dir must not be a symlink: ${bundleDir}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error instanceof Error && /symlink/.test(error.message))
|
|
197
|
+
throw error;
|
|
198
|
+
/* absent dir: listGatePackFiles reports nothing */
|
|
199
|
+
}
|
|
200
|
+
// Containment validation (throws on traversal escapes).
|
|
201
|
+
const gatePacksDir = resolveGatePacksDir(bundleDir, declaration.dir);
|
|
202
|
+
const files = listGatePackFiles(bundleDir, declaration.dir ?? GATE_PACKS_DIR);
|
|
203
|
+
const wanted = declaration.names ? new Set(declaration.names) : null;
|
|
204
|
+
const loaded = [];
|
|
205
|
+
for (const file of files) {
|
|
206
|
+
const short = basename(file).replace(/\.gatepack\.(yaml|yml|json)$/i, '');
|
|
207
|
+
if (wanted && !wanted.has(short))
|
|
208
|
+
continue;
|
|
209
|
+
const pack = loadGatePackFile(file);
|
|
210
|
+
try {
|
|
211
|
+
registry.registerPack(pack, origin.bundle === undefined ? { namespace: origin.namespace } : origin);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
throw new Error(`gate pack rejected: ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
215
|
+
}
|
|
216
|
+
loaded.push(pack.metadata.id);
|
|
217
|
+
}
|
|
218
|
+
if (wanted) {
|
|
219
|
+
for (const name of wanted) {
|
|
220
|
+
if (!files.some(file => basename(file).replace(/\.gatepack\.(yaml|yml|json)$/i, '') === name)) {
|
|
221
|
+
throw new Error(`gate pack missing: ${gatePacksDir}/${name}.gatepack.yaml (declared by manifest)`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return loaded;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Derive a `--pack-dir` bundle's contribution origin from its manifest, never
|
|
229
|
+
* from its path. Unverified `--pack-dir` bundles are ALWAYS project-scoped:
|
|
230
|
+
* only the installed tree (via `discoverShippedGatePacks`) contributes
|
|
231
|
+
* `aiwg:` packs. A manifest whose `{id, type}` claims a shipped bundle
|
|
232
|
+
* (under `<aiwgRoot>/agentic/code/...`) or an installed project bundle
|
|
233
|
+
* (under `<cwd>/.aiwg/...`) that is not its own directory refuses:
|
|
234
|
+
* impersonating a trusted bundle id through an unverified path is a load
|
|
235
|
+
* error. Path substrings such as `/addons/` are never consulted.
|
|
236
|
+
*/
|
|
237
|
+
export function originForPackDir(bundleDir, options) {
|
|
238
|
+
const manifestPath = join(resolve(bundleDir), 'manifest.json');
|
|
239
|
+
let claimed = null;
|
|
240
|
+
try {
|
|
241
|
+
const status = statSync(manifestPath);
|
|
242
|
+
if (!status.isFile() || status.size > MANIFEST_MAX_BYTES)
|
|
243
|
+
return { namespace: 'project' };
|
|
244
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
245
|
+
if (typeof manifest?.id === 'string' && manifest.id.trim() !== ''
|
|
246
|
+
&& (manifest.type === 'framework' || manifest.type === 'addon' || manifest.type === 'extension')) {
|
|
247
|
+
claimed = { id: manifest.id, type: manifest.type };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return { namespace: 'project' };
|
|
252
|
+
}
|
|
253
|
+
if (claimed !== null) {
|
|
254
|
+
const plural = claimed.type === 'framework' ? 'frameworks' : claimed.type === 'addon' ? 'addons' : 'extensions';
|
|
255
|
+
const candidates = [];
|
|
256
|
+
if (options?.aiwgRoot)
|
|
257
|
+
candidates.push({ root: join(resolve(options.aiwgRoot), 'agentic/code', plural, claimed.id), label: 'shipped' });
|
|
258
|
+
if (options?.cwd)
|
|
259
|
+
candidates.push({ root: join(resolve(options.cwd), '.aiwg', plural, claimed.id), label: 'installed' });
|
|
260
|
+
const own = realpathIfExists(resolve(bundleDir));
|
|
261
|
+
for (const candidate of candidates) {
|
|
262
|
+
const trusted = realpathIfExists(candidate.root);
|
|
263
|
+
if (trusted === null)
|
|
264
|
+
continue;
|
|
265
|
+
if (own === null || own !== trusted) {
|
|
266
|
+
throw new Error(`pack-dir bundle claims a ${candidate.label} bundle id '${claimed.id}'`
|
|
267
|
+
+ ` that is not its own directory: ${bundleDir}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return { namespace: 'project' };
|
|
272
|
+
}
|
|
273
|
+
const BUNDLE_GROUPS = [
|
|
274
|
+
{ kind: 'framework', sub: 'frameworks' },
|
|
275
|
+
{ kind: 'addon', sub: 'addons' },
|
|
276
|
+
{ kind: 'extension', sub: 'extensions' },
|
|
277
|
+
];
|
|
278
|
+
/**
|
|
279
|
+
* Register shipped packs under `agentic/code/<group>/<bundle>/gate-packs/`.
|
|
280
|
+
* Shipped packs use the `aiwg:` namespace (`aiwg:<bundle>/<name>`); the
|
|
281
|
+
* bundle kind is only an install hint. Project-local bundles register under
|
|
282
|
+
* their own `framework:`/`addon:`/`extension:` namespace via
|
|
283
|
+
* `registerBundleGatePacks` directly.
|
|
284
|
+
*/
|
|
285
|
+
export function discoverShippedGatePacks(aiwgRoot, registry) {
|
|
286
|
+
const loaded = [];
|
|
287
|
+
for (const group of BUNDLE_GROUPS) {
|
|
288
|
+
const groupDir = resolve(aiwgRoot, 'agentic/code', group.sub);
|
|
289
|
+
let bundles;
|
|
290
|
+
try {
|
|
291
|
+
bundles = readdirSync(groupDir).filter(name => {
|
|
292
|
+
try {
|
|
293
|
+
return statSync(join(groupDir, name)).isDirectory();
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}).sort();
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
for (const bundle of bundles) {
|
|
304
|
+
const bundleDir = join(groupDir, bundle);
|
|
305
|
+
const files = listGatePackFiles(bundleDir);
|
|
306
|
+
if (!files.length && !existsSync(join(bundleDir, 'manifest.json')))
|
|
307
|
+
continue;
|
|
308
|
+
if (!files.length)
|
|
309
|
+
continue;
|
|
310
|
+
loaded.push(...registerBundleGatePacks(registry, bundleDir, { namespace: 'aiwg', bundle }));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return loaded;
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=discovery.js.map
|