@aiwg/cli 2026.9.20 → 2026.9.21
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 +58 -6
- package/agentic/code/providers/model-capabilities.v1.json +34 -0
- package/agentic/code/providers/model-catalog.v1.json +55 -13
- package/bin/aiwg.mjs +2 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.js +1 -0
- package/dist/src/artifacts/enrichment/cli.js +1 -1
- package/dist/src/artifacts/views/cli.js +1 -1
- package/dist/src/audit/operator-decision.js +10 -0
- package/dist/src/auth/credential-store.js +127 -27
- package/dist/src/catalog/builtin-models.json +272 -40
- package/dist/src/catalog/cli.js +1 -1
- package/dist/src/catalog/cli.mjs +1 -1
- package/dist/src/cli/handlers/effect.js +872 -0
- package/dist/src/cli/handlers/help.js +2 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/models.js +41 -10
- package/dist/src/cli/handlers/ralph.js +1 -1
- package/dist/src/cli/handlers/sessions.js +113 -37
- package/dist/src/cli/handlers/subcommands.js +31 -0
- package/dist/src/cli/handlers/use.js +46 -9
- package/dist/src/cli/handlers/utilities.js +10 -0
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/scope-resolver.js +112 -9
- package/dist/src/cli/services/deployment-verification.js +5 -0
- package/dist/src/config/aiwg-config.js +3 -0
- package/dist/src/decision/acceptance-rollout.js +93 -0
- package/dist/src/decision/acceptance.js +175 -0
- package/dist/src/decision/adapters/jev.js +234 -13
- package/dist/src/decision/adapters/llm-subagent.js +60 -12
- package/dist/src/decision/admission.js +550 -0
- package/dist/src/decision/batch-benchmark.js +47 -0
- package/dist/src/decision/batch-receipts/accounting.js +75 -0
- package/dist/src/decision/batch-receipts/export.js +40 -0
- package/dist/src/decision/batch-receipts/index.js +9 -0
- package/dist/src/decision/batch-receipts/protection.js +40 -0
- package/dist/src/decision/batch-receipts/receipt.js +44 -0
- package/dist/src/decision/batch-receipts/result-store.js +341 -0
- package/dist/src/decision/batch-receipts/store.js +362 -0
- package/dist/src/decision/batch-receipts/types.js +2 -0
- package/dist/src/decision/batch-receipts/validate.js +127 -0
- package/dist/src/decision/batch.js +68 -0
- package/dist/src/decision/calibration/index.js +4 -0
- package/dist/src/decision/calibration/receipts.js +130 -0
- package/dist/src/decision/calibration/registry.js +258 -0
- package/dist/src/decision/calibration/types.js +2 -0
- package/dist/src/decision/compile-cache/benchmark.js +62 -0
- package/dist/src/decision/compile-cache/compatibility.js +65 -0
- package/dist/src/decision/compile-cache/file-store.js +280 -0
- package/dist/src/decision/compile-cache/identity.js +26 -0
- package/dist/src/decision/compile-cache/index.js +9 -0
- package/dist/src/decision/compile-cache/prefix.js +21 -0
- package/dist/src/decision/compile-cache/records.js +86 -0
- package/dist/src/decision/compile-cache/runtime.js +68 -0
- package/dist/src/decision/compile-cache/store.js +108 -0
- package/dist/src/decision/compile-cache/types.js +2 -0
- package/dist/src/decision/context-plan.js +234 -0
- package/dist/src/decision/context-qualification.js +60 -0
- package/dist/src/decision/ensemble/aggregate.js +215 -0
- package/dist/src/decision/ensemble/contract.js +316 -0
- package/dist/src/decision/ensemble/drift.js +51 -0
- package/dist/src/decision/ensemble/index.js +6 -0
- package/dist/src/decision/ensemble/report.js +71 -0
- package/dist/src/decision/ensemble/types.js +2 -0
- package/dist/src/decision/evaluate.js +1024 -78
- package/dist/src/decision/file-lifecycle-store.js +168 -0
- package/dist/src/decision/graph-beam.js +40 -0
- package/dist/src/decision/graph-budget.js +110 -0
- package/dist/src/decision/graph-decision-bridge.js +93 -0
- package/dist/src/decision/graph-evidence.js +123 -0
- package/dist/src/decision/graph-flow-adapter.js +56 -0
- package/dist/src/decision/graph-flow.js +105 -0
- package/dist/src/decision/graph-parallel.js +79 -0
- package/dist/src/decision/graph-patterns.js +20 -0
- package/dist/src/decision/graph-run-store.js +66 -0
- package/dist/src/decision/graph-run.js +166 -0
- package/dist/src/decision/graph-skill-bridge.js +91 -0
- package/dist/src/decision/graph-templates.js +56 -0
- package/dist/src/decision/graph.js +115 -0
- package/dist/src/decision/index.js +35 -0
- package/dist/src/decision/job-accounting.js +93 -0
- package/dist/src/decision/job-contract.js +186 -0
- package/dist/src/decision/job-effects.js +95 -0
- package/dist/src/decision/job-evaluate.js +80 -0
- package/dist/src/decision/job-gateway.js +121 -0
- package/dist/src/decision/job-http.js +194 -0
- package/dist/src/decision/job-payload-store.js +258 -0
- package/dist/src/decision/job-poll-limiter.js +92 -0
- package/dist/src/decision/job-quota.js +158 -0
- package/dist/src/decision/job-runtime.js +240 -0
- package/dist/src/decision/job-scheduler.js +26 -0
- package/dist/src/decision/job-service.js +117 -0
- package/dist/src/decision/job-store.js +264 -0
- package/dist/src/decision/job-worker.js +141 -0
- package/dist/src/decision/lifecycle.js +148 -0
- package/dist/src/decision/patterns/artifacts.js +173 -0
- package/dist/src/decision/patterns/catalog.js +108 -0
- package/dist/src/decision/patterns/durable-review.js +237 -0
- package/dist/src/decision/patterns/index.js +8 -0
- package/dist/src/decision/patterns/playground.js +401 -0
- package/dist/src/decision/patterns/recorded.js +43 -0
- package/dist/src/decision/patterns/specs.js +174 -0
- package/dist/src/decision/patterns/types.js +2 -0
- package/dist/src/decision/portable-secrets.js +53 -0
- package/dist/src/decision/projection.js +210 -0
- package/dist/src/decision/qualification/capture.js +67 -0
- package/dist/src/decision/qualification/drift.js +68 -0
- package/dist/src/decision/qualification/gate-evidence.js +172 -0
- package/dist/src/decision/qualification/gates.js +83 -0
- package/dist/src/decision/qualification/index.js +12 -0
- package/dist/src/decision/qualification/load.js +275 -0
- package/dist/src/decision/qualification/manifest.js +37 -0
- package/dist/src/decision/qualification/privacy.js +41 -0
- package/dist/src/decision/qualification/quality.js +213 -0
- package/dist/src/decision/qualification/release.js +116 -0
- package/dist/src/decision/qualification/runner.js +408 -0
- package/dist/src/decision/qualification/types.js +2 -0
- package/dist/src/decision/receipts.js +30 -7
- package/dist/src/decision/result-cache/index.js +6 -0
- package/dist/src/decision/result-cache/integrity.js +28 -0
- package/dist/src/decision/result-cache/key.js +28 -0
- package/dist/src/decision/result-cache/service.js +92 -0
- package/dist/src/decision/result-cache/store.js +327 -0
- package/dist/src/decision/result-cache/types.js +3 -0
- package/dist/src/decision/review/authorization.js +110 -0
- package/dist/src/decision/review/effect-ledger-adapter.js +321 -0
- package/dist/src/decision/review/index.js +12 -0
- package/dist/src/decision/review/migrate.js +34 -0
- package/dist/src/decision/review/operator-audit.js +59 -0
- package/dist/src/decision/review/recovery-adapters.js +252 -0
- package/dist/src/decision/review/recovery.js +33 -0
- package/dist/src/decision/review/service.js +498 -0
- package/dist/src/decision/review/store.js +219 -0
- package/dist/src/decision/review/types.js +2 -0
- package/dist/src/decision/review/validate.js +205 -0
- package/dist/src/decision/scheduler.js +115 -0
- package/dist/src/decision/telemetry/context.js +42 -0
- package/dist/src/decision/telemetry/debug-sidecar.js +152 -0
- package/dist/src/decision/telemetry/exporter.js +77 -0
- package/dist/src/decision/telemetry/file-debug-backend.js +112 -0
- package/dist/src/decision/telemetry/index.js +13 -0
- package/dist/src/decision/telemetry/mapping.js +146 -0
- package/dist/src/decision/telemetry/metrics.js +164 -0
- package/dist/src/decision/telemetry/otlp-http.js +183 -0
- package/dist/src/decision/telemetry/redaction.js +93 -0
- package/dist/src/decision/telemetry/retention.js +109 -0
- package/dist/src/decision/telemetry/runtime.js +310 -0
- package/dist/src/decision/telemetry/trace.js +158 -0
- package/dist/src/decision/telemetry/types.js +3 -0
- package/dist/src/decision/validate.js +135 -5
- package/dist/src/effects/checkpoint-sinks.js +94 -0
- package/dist/src/effects/checkpoint.js +76 -0
- package/dist/src/effects/decision-links.js +54 -0
- package/dist/src/effects/errors.js +48 -0
- package/dist/src/effects/identity.js +178 -0
- package/dist/src/effects/index.js +25 -0
- package/dist/src/effects/keyring.js +160 -0
- package/dist/src/effects/keys.js +130 -0
- package/dist/src/effects/ledger.js +435 -0
- package/dist/src/effects/lifecycle.js +110 -0
- package/dist/src/effects/lock-recovery.js +269 -0
- package/dist/src/effects/reader.js +103 -0
- package/dist/src/effects/records.js +114 -0
- package/dist/src/effects/redaction.js +51 -0
- package/dist/src/effects/schema.js +73 -0
- package/dist/src/effects/store.js +239 -0
- package/dist/src/effects/types.js +49 -0
- package/dist/src/effects/verifiers/decision.js +168 -0
- package/dist/src/effects/verifiers/file.js +106 -0
- package/dist/src/effects/verifiers/git.js +206 -0
- package/dist/src/effects/verifiers/index.js +90 -0
- package/dist/src/effects/verifiers/registry.js +148 -0
- package/dist/src/effects/verifiers/review.js +123 -0
- package/dist/src/effects/verifiers/tracker.js +299 -0
- package/dist/src/effects/verifiers/types.js +21 -0
- package/dist/src/effects/verify.js +295 -0
- package/dist/src/evidence/bundle.js +2 -0
- package/dist/src/extensions/commands/definitions.js +28 -0
- package/dist/src/extensions/manifest.js +1 -0
- package/dist/src/marketplace/provenance.js +25 -27
- package/dist/src/mcp/cli.mjs +11 -1
- package/dist/src/mcp/grok-build-config.mjs +189 -0
- package/dist/src/mcp/registry.js +21 -1
- package/dist/src/mcp/registry.mjs +20 -1
- package/dist/src/models/grok-build-models.js +356 -0
- package/dist/src/models/index.js +1 -0
- package/dist/src/models/model-capabilities.v1.json +34 -0
- package/dist/src/models/model-catalog.v1.json +55 -13
- package/dist/src/models/model-discovery.js +41 -5
- package/dist/src/models/provider-policy.js +3 -2
- package/dist/src/plugin/skill-command-translator.js +1 -0
- package/dist/src/providers/capability-matrix.yaml +58 -6
- package/dist/src/providers/grok-build-acp.js +192 -0
- package/dist/src/providers/grok-build-dispatch.js +251 -0
- package/dist/src/providers/grok-build-headless.js +170 -0
- package/dist/src/providers/muse-paths.js +105 -0
- package/dist/src/providers/provider-definitions.js +93 -7
- package/dist/src/providers/provider-definitions.mjs +21 -1
- package/dist/src/providers/provider-inventory.js +3 -0
- package/dist/src/security/artifact-attestation.js +4 -8
- package/dist/src/security/signing.js +119 -0
- package/dist/src/sessions/adapters/grok-build.js +187 -0
- package/dist/src/sessions/adapters/muse.js +412 -0
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/grok-build-cli.js +196 -0
- package/dist/src/sessions/index.js +3 -0
- package/dist/src/sessions/workspace-discovery.js +9 -5
- package/dist/src/skills/deployer.js +33 -5
- package/dist/src/smiths/context-pipeline/claude-hook.js +2 -0
- package/dist/src/smiths/context-pipeline/context-file-safety.js +18 -0
- package/dist/src/smiths/context-pipeline/finalization.js +1 -32
- package/dist/src/smiths/context-pipeline/generator.js +12 -0
- package/dist/src/smiths/context-pipeline/index.js +1 -1
- package/dist/src/smiths/context-pipeline/managed-hook.js +2 -0
- package/dist/src/smiths/context-pipeline/muse-bridge.js +50 -0
- package/dist/src/smiths/context-pipeline/provider-policy.js +5 -0
- package/dist/src/smiths/context-pipeline/workspace-context.js +22 -16
- package/dist/src/smiths/hook-bridge/grok-build-translator.js +64 -0
- package/dist/src/smiths/hook-bridge/index.js +3 -0
- package/dist/src/smiths/hook-bridge/shim.js +2 -0
- package/dist/src/smiths/hook-bridge/types.js +5 -0
- package/dist/src/smiths/skillsmith/collision-detector.js +1 -0
- package/dist/src/storage/protected-files.js +102 -0
- package/dist/src/tracker/forge-http.js +371 -0
- package/dist/src/tracker/project-inputs.js +41 -0
- package/package.json +2 -1
- package/schemas/decision/BatchReceipt.v1.schema.json +50 -0
- package/schemas/decision/CalibrationArtifact.v1.schema.json +76 -0
- package/schemas/decision/CalibrationCompatibility.v1.schema.json +18 -0
- package/schemas/decision/CalibrationGovernanceReceipt.v1.schema.json +44 -0
- package/schemas/decision/DecisionAdmissionEvidence.v1.schema.json +43 -0
- package/schemas/decision/DecisionBinding.schema.json +309 -0
- package/schemas/decision/DecisionBinding.v1alpha2.schema.json +369 -0
- package/schemas/decision/DecisionCacheTelemetry.v1.schema.json +20 -0
- package/schemas/decision/DecisionChampionChallenger.v1.schema.json +100 -0
- package/schemas/decision/DecisionCompileCacheEntry.v1.schema.json +35 -0
- package/schemas/decision/DecisionCompileCacheIdentity.v1.schema.json +26 -0
- package/schemas/decision/DecisionCompileCacheTombstone.v1.schema.json +31 -0
- package/schemas/decision/DecisionContextEvidence.v1.schema.json +136 -0
- package/schemas/decision/DecisionDefinition.schema.json +159 -0
- package/schemas/decision/DecisionDefinition.v1alpha2.schema.json +203 -0
- package/schemas/decision/DecisionDriftResponse.v1.schema.json +55 -0
- package/schemas/decision/DecisionEnsembleAggregate.v1.schema.json +86 -0
- package/schemas/decision/DecisionEnsembleIntegrityReport.v1.schema.json +64 -0
- package/schemas/decision/DecisionEnsemblePolicy.v1.schema.json +130 -0
- package/schemas/decision/DecisionGraph.v1.schema.json +77 -0
- package/schemas/decision/DecisionJob.v1.schema.json +71 -0
- package/schemas/decision/DecisionLifecyclePolicy.v1.schema.json +52 -0
- package/schemas/decision/DecisionProjectionPolicy.v1.schema.json +65 -0
- package/schemas/decision/DecisionProviderPrefixCompatibility.v1.schema.json +45 -0
- package/schemas/decision/DecisionProviderPrefixEvidence.v1.schema.json +17 -0
- package/schemas/decision/DecisionProviderPrefixIdentity.v1.schema.json +36 -0
- package/schemas/decision/DecisionResult.schema.json +524 -0
- package/schemas/decision/DecisionResult.v1alpha2.schema.json +619 -0
- package/schemas/decision/DecisionResultCacheEntry.v1.schema.json +53 -0
- package/schemas/decision/DecisionReview.schema.json +29 -0
- package/schemas/decision/DecisionRuleset.schema.json +313 -0
- package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +313 -0
- package/schemas/decision/DecisionTelemetryTrace.v1.schema.json +95 -0
- package/schemas/decision/RulesetResult.schema.json +721 -0
- package/schemas/decision/RulesetResult.v1alpha2.schema.json +891 -0
- package/schemas/security/pr-evidence-receipt.v1.schema.json +77 -0
- package/schemas/security/threat-assessment-report.v1.schema.json +71 -1
- package/tools/agents/deploy-agents.mjs +21 -6
- package/tools/agents/providers/base.mjs +7 -4
- package/tools/agents/providers/codex.mjs +1 -1
- package/tools/agents/providers/grok-build.d.mts +7 -0
- package/tools/agents/providers/grok-build.mjs +249 -9
- package/tools/agents/providers/model-role.mjs +6 -3
- package/tools/agents/providers/muse-hooks.mjs +785 -0
- package/tools/agents/providers/muse-paths.mjs +112 -0
- package/tools/agents/providers/muse.mjs +336 -0
|
@@ -15,6 +15,7 @@ import * as path from 'node:path';
|
|
|
15
15
|
import { resolveHermesHome, resolveHermesHomePath } from '../providers/hermes-home.js';
|
|
16
16
|
import { resolveGrokbotSkillsDir } from '../providers/grokbot-paths.js';
|
|
17
17
|
import { resolveGrokHome } from '../providers/grok-build-paths.js';
|
|
18
|
+
import { resolveMuseXdgSkillsDir } from '../providers/muse-paths.js';
|
|
18
19
|
export const hermesHome = resolveHermesHome;
|
|
19
20
|
function piAgentDir() {
|
|
20
21
|
const configured = process.env.PI_CODING_AGENT_DIR;
|
|
@@ -231,17 +232,31 @@ export const USER_SCOPE_PATHS = {
|
|
|
231
232
|
};
|
|
232
233
|
},
|
|
233
234
|
get 'grok-build'() {
|
|
234
|
-
//
|
|
235
|
-
// leave those user paths empty so mirroring does not invent empty trees.
|
|
235
|
+
// Grok Build discovers user agents and skills under the resolved home.
|
|
236
236
|
const home = resolveGrokHome();
|
|
237
237
|
return {
|
|
238
|
-
agents: '',
|
|
238
|
+
agents: home ? path.join(home, 'agents') : '',
|
|
239
239
|
skills: home ? path.join(home, 'skills') : '',
|
|
240
240
|
commands: '',
|
|
241
241
|
rules: '',
|
|
242
242
|
behaviors: '',
|
|
243
243
|
};
|
|
244
244
|
},
|
|
245
|
+
get muse() {
|
|
246
|
+
// #226 — Muse XDG user skills root resolves at deploy time (fail-closed).
|
|
247
|
+
// Bad XDG metadata yields '' so the --scope user mirror skips the skills
|
|
248
|
+
// lane rather than inventing ~/.muse or writing ~/.agents/skills
|
|
249
|
+
// (the cross-provider canonical is a READ surface for Muse, never an
|
|
250
|
+
// AIWG write target — see adr-muse-provider-target.md).
|
|
251
|
+
const skills = resolveMuseXdgSkillsDir() || '';
|
|
252
|
+
return {
|
|
253
|
+
agents: '',
|
|
254
|
+
skills,
|
|
255
|
+
commands: '',
|
|
256
|
+
rules: '',
|
|
257
|
+
behaviors: '',
|
|
258
|
+
};
|
|
259
|
+
},
|
|
245
260
|
factory: {
|
|
246
261
|
// #1164 — Verified against Factory docs (docs.factory.ai/cli/configuration/skills).
|
|
247
262
|
// Skills primary user-scope path is ~/.factory/skills/, NOT the
|
|
@@ -345,6 +360,8 @@ export async function mirrorToUserScope(provider, projectPaths) {
|
|
|
345
360
|
if (!userPaths) {
|
|
346
361
|
return { agents: empty, skills: empty, commands: empty, rules: empty, behaviors: empty };
|
|
347
362
|
}
|
|
363
|
+
if (provider === 'grok-build')
|
|
364
|
+
await assertGrokUserMirrorPaths(projectPaths, userPaths);
|
|
348
365
|
const [agents, skills, commands, rules, behaviors] = await Promise.all([
|
|
349
366
|
userPaths.agents ? mirrorArtifactDir(projectPaths.agents, userPaths.agents) : Promise.resolve(empty),
|
|
350
367
|
userPaths.skills
|
|
@@ -356,16 +373,68 @@ export async function mirrorToUserScope(provider, projectPaths) {
|
|
|
356
373
|
]);
|
|
357
374
|
return { agents, skills, commands, rules, behaviors };
|
|
358
375
|
}
|
|
376
|
+
async function assertGrokUserMirrorPaths(projectPaths, userPaths) {
|
|
377
|
+
const fs = await import('node:fs/promises');
|
|
378
|
+
const statIfPresent = async (target) => {
|
|
379
|
+
try {
|
|
380
|
+
return await fs.lstat(target);
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
if (error.code === 'ENOENT')
|
|
384
|
+
return null;
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
const checkTarget = async (target) => {
|
|
389
|
+
const stat = await statIfPresent(target);
|
|
390
|
+
if (!stat)
|
|
391
|
+
return;
|
|
392
|
+
if (stat.isSymbolicLink() || (!stat.isDirectory() && !stat.isFile())) {
|
|
393
|
+
throw new Error(`Refusing unsafe Grok Build user mirror target: ${target}`);
|
|
394
|
+
}
|
|
395
|
+
if (stat.isDirectory()) {
|
|
396
|
+
for (const name of await fs.readdir(target))
|
|
397
|
+
await checkTarget(path.join(target, name));
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
for (const [destination, sources] of [
|
|
401
|
+
[userPaths.agents, [projectPaths.agents]],
|
|
402
|
+
[userPaths.skills, [projectPaths.skills, projectPaths.kernelSkills ?? '']],
|
|
403
|
+
]) {
|
|
404
|
+
if (!destination)
|
|
405
|
+
continue;
|
|
406
|
+
const rootStat = await statIfPresent(destination);
|
|
407
|
+
if (rootStat && (rootStat.isSymbolicLink() || !rootStat.isDirectory())) {
|
|
408
|
+
throw new Error(`Refusing unsafe Grok Build user mirror root: ${destination}`);
|
|
409
|
+
}
|
|
410
|
+
for (const source of sources) {
|
|
411
|
+
if (!source)
|
|
412
|
+
continue;
|
|
413
|
+
let names;
|
|
414
|
+
try {
|
|
415
|
+
names = await fs.readdir(source);
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
if (error.code === 'ENOENT')
|
|
419
|
+
continue;
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
for (const name of names)
|
|
423
|
+
await checkTarget(path.join(destination, name));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
359
427
|
/**
|
|
360
428
|
* Merge standard and kernel skill directories into one user-discoverable
|
|
361
429
|
* target. Provider deployments intentionally keep those source surfaces
|
|
362
430
|
* separate, while user scope exposes one native skill root. Duplicate skill
|
|
363
|
-
* names are overwritten by the later source
|
|
431
|
+
* names created by the earlier source are overwritten by the later source
|
|
432
|
+
* and counted once; pre-existing operator-owned names are preserved.
|
|
364
433
|
*/
|
|
365
434
|
export async function mirrorSkillDirsToUserScope(sourceDirs, targetDir) {
|
|
366
435
|
const entries = new Set();
|
|
367
436
|
for (const sourceDir of sourceDirs) {
|
|
368
|
-
const result = await mirrorArtifactDir(sourceDir, targetDir);
|
|
437
|
+
const result = await mirrorArtifactDir(sourceDir, targetDir, entries);
|
|
369
438
|
for (const entry of result.entries)
|
|
370
439
|
entries.add(entry);
|
|
371
440
|
}
|
|
@@ -387,7 +456,7 @@ export async function mirrorSkillDirsToUserScope(sourceDirs, targetDir) {
|
|
|
387
456
|
* providers). Failures on individual entries are swallowed so a single bad
|
|
388
457
|
* entry doesn't fail the whole mirror.
|
|
389
458
|
*/
|
|
390
|
-
async function mirrorArtifactDir(src, dst) {
|
|
459
|
+
async function mirrorArtifactDir(src, dst, ownedThisCall = new Set()) {
|
|
391
460
|
if (!src || !dst)
|
|
392
461
|
return { count: 0, targetDir: dst, entries: [] };
|
|
393
462
|
const fs = await import('node:fs/promises');
|
|
@@ -402,9 +471,12 @@ async function mirrorArtifactDir(src, dst) {
|
|
|
402
471
|
// path. Inventory that source for registry accounting instead of trying to
|
|
403
472
|
// recursively copy each entry onto itself.
|
|
404
473
|
if (path.resolve(src) === path.resolve(dst)) {
|
|
405
|
-
const entries =
|
|
406
|
-
|
|
407
|
-
|
|
474
|
+
const entries = [];
|
|
475
|
+
for (const entry of dirents) {
|
|
476
|
+
if ((entry.isDirectory() || entry.isFile()) && await isAiwgManagedEntry(path.join(dst, entry.name), entry.isDirectory())) {
|
|
477
|
+
entries.push(entry.name);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
408
480
|
return { count: entries.length, targetDir: dst, entries };
|
|
409
481
|
}
|
|
410
482
|
await fs.mkdir(dst, { recursive: true });
|
|
@@ -414,6 +486,18 @@ async function mirrorArtifactDir(src, dst) {
|
|
|
414
486
|
const s = path.join(src, entry.name);
|
|
415
487
|
const d = path.join(dst, entry.name);
|
|
416
488
|
try {
|
|
489
|
+
// User-scope directories are shared with operator-created artifacts.
|
|
490
|
+
// A same-named unmanaged entry belongs to the operator, even when a
|
|
491
|
+
// project has an AIWG-managed source with that name.
|
|
492
|
+
try {
|
|
493
|
+
const existing = await fs.lstat(d);
|
|
494
|
+
if (!ownedThisCall.has(entry.name) && !await isAiwgManagedEntry(d, existing.isDirectory()))
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
if (error.code !== 'ENOENT')
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
417
501
|
if (entry.isDirectory()) {
|
|
418
502
|
await fs.cp(s, d, { recursive: true, force: true });
|
|
419
503
|
}
|
|
@@ -432,6 +516,25 @@ async function mirrorArtifactDir(src, dst) {
|
|
|
432
516
|
}
|
|
433
517
|
return { count, targetDir: dst, entries };
|
|
434
518
|
}
|
|
519
|
+
async function isAiwgManagedEntry(target, directory) {
|
|
520
|
+
const fs = await import('node:fs/promises');
|
|
521
|
+
if (directory) {
|
|
522
|
+
try {
|
|
523
|
+
const marker = await fs.readFile(path.join(target, '.aiwg-managed'), 'utf8');
|
|
524
|
+
if (marker.trim() === 'aiwg')
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
catch { /* legacy deployed skill may use an inline marker */ }
|
|
528
|
+
}
|
|
529
|
+
const markerFile = directory ? path.join(target, 'SKILL.md') : target;
|
|
530
|
+
try {
|
|
531
|
+
const text = await fs.readFile(markerFile, 'utf8');
|
|
532
|
+
return /(?:#|<!--) aiwg:managed v[^\s]+ [^\r\n]+/.test(text.slice(0, 2048));
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
435
538
|
/**
|
|
436
539
|
* #1156 Phase 1 — some app providers are exclusively user-scope. `--scope
|
|
437
540
|
* project` against them is meaningless because their paths are home-rooted;
|
|
@@ -44,6 +44,11 @@ const RESTART_NOTICES = {
|
|
|
44
44
|
action: 'Restart the Grok Build session so it reloads .grok skills and AGENTS.md.',
|
|
45
45
|
reason: 'Grok Build reload semantics are not yet verified for live refresh; treat deploys as restart-required.',
|
|
46
46
|
},
|
|
47
|
+
muse: {
|
|
48
|
+
policy: 'restart-required',
|
|
49
|
+
action: 'Trust this workspace when prompted, then start a new Muse session (or re-read skills) so Muse loads the deployed AGENTS.md bridge and skills.',
|
|
50
|
+
reason: 'Muse Code loads project AGENTS.md only after the workspace is explicitly trusted, and reads its skill registry when a session starts. Reloading an IDE window does not apply here.',
|
|
51
|
+
},
|
|
47
52
|
factory: {
|
|
48
53
|
policy: 'restart-required',
|
|
49
54
|
action: 'Restart the Factory droid runtime so it reloads deployed droids.',
|
|
@@ -174,6 +174,7 @@ export function resolveDelivery(delivery) {
|
|
|
174
174
|
* should tune. Conservative 10 default.
|
|
175
175
|
* - grokbot: desktop multi-agent; conservative 4 until native evidence.
|
|
176
176
|
* - grok-build: experimental; conservative 4 until Wave 2/3 evidence.
|
|
177
|
+
* - muse: experimental; conservative 4 until Wave 2/3 evidence.
|
|
177
178
|
* - unknown: conservative 4 default.
|
|
178
179
|
*/
|
|
179
180
|
export const PROVIDER_PARALLELISM_DEFAULTS = {
|
|
@@ -191,6 +192,8 @@ export const PROVIDER_PARALLELISM_DEFAULTS = {
|
|
|
191
192
|
// Desktop multi-agent; conservative until native concurrency evidence exists.
|
|
192
193
|
grokbot: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
193
194
|
'grok-build': { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
195
|
+
// Muse Code is experimental; conservative until Wave 2/3 native concurrency evidence (#235).
|
|
196
|
+
muse: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
194
197
|
// Conservative defaults for remaining stable/experimental harnesses (#249).
|
|
195
198
|
openhuman: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
196
199
|
omp: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { applyPrimitiveAcceptance } from './acceptance.js';
|
|
3
|
+
function canonical(value) {
|
|
4
|
+
if (Array.isArray(value))
|
|
5
|
+
return `[${value.map(canonical).join(',')}]`;
|
|
6
|
+
if (value && typeof value === 'object') {
|
|
7
|
+
return `{${Object.entries(value)
|
|
8
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
9
|
+
.map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(',')}}`;
|
|
10
|
+
}
|
|
11
|
+
return JSON.stringify(value);
|
|
12
|
+
}
|
|
13
|
+
function digest(value) {
|
|
14
|
+
return `sha256:${createHash('sha256').update(canonical(value)).digest('hex')}`;
|
|
15
|
+
}
|
|
16
|
+
function required(value, label) {
|
|
17
|
+
if (!value.trim())
|
|
18
|
+
throw new Error(`${label} must not be empty`);
|
|
19
|
+
}
|
|
20
|
+
function timestamp(value, label) {
|
|
21
|
+
if (!Number.isFinite(Date.parse(value)))
|
|
22
|
+
throw new Error(`${label} must be a valid date-time`);
|
|
23
|
+
}
|
|
24
|
+
export function acceptancePolicyIdentity(id, policy) {
|
|
25
|
+
required(id, 'policy id');
|
|
26
|
+
return { id, version: policy.version, digest: digest(policy) };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Replays two immutable policy versions over already-stored observations. This
|
|
30
|
+
* function has no adapter, credential, executor, or action callback by design.
|
|
31
|
+
*/
|
|
32
|
+
export function replayAcceptancePolicyShadow(input) {
|
|
33
|
+
required(input.rolloutId, 'rollout id');
|
|
34
|
+
required(input.useCaseId, 'use case id');
|
|
35
|
+
timestamp(input.recordedAt, 'recordedAt');
|
|
36
|
+
if (!input.evidence.length)
|
|
37
|
+
throw new Error('shadow rollout requires stored evidence');
|
|
38
|
+
const ids = new Set();
|
|
39
|
+
const evidence = input.evidence.map(item => {
|
|
40
|
+
required(item.id, 'stored evidence id');
|
|
41
|
+
if (ids.has(item.id))
|
|
42
|
+
throw new Error(`duplicate stored evidence id '${item.id}'`);
|
|
43
|
+
ids.add(item.id);
|
|
44
|
+
const before = structuredClone(item);
|
|
45
|
+
const incumbent = applyPrimitiveAcceptance(item.definition, input.incumbent.policy, item.observation);
|
|
46
|
+
const candidate = applyPrimitiveAcceptance(item.definition, input.candidate.policy, item.observation);
|
|
47
|
+
if (canonical(item) !== canonical(before))
|
|
48
|
+
throw new Error(`shadow replay mutated stored evidence '${item.id}'`);
|
|
49
|
+
if (!incumbent.acceptance || !candidate.acceptance)
|
|
50
|
+
throw new Error(`shadow replay produced no acceptance evidence for '${item.id}'`);
|
|
51
|
+
return {
|
|
52
|
+
id: item.id,
|
|
53
|
+
storedObservationDigest: digest({ definition: item.definition, observation: item.observation }),
|
|
54
|
+
incumbentAcceptance: incumbent.acceptance,
|
|
55
|
+
candidateAcceptance: candidate.acceptance,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
const withoutDigest = {
|
|
59
|
+
schemaVersion: 'decision-acceptance-shadow/v1', rolloutId: input.rolloutId, useCaseId: input.useCaseId,
|
|
60
|
+
incumbent: acceptancePolicyIdentity(input.incumbent.id, input.incumbent.policy),
|
|
61
|
+
candidate: acceptancePolicyIdentity(input.candidate.id, input.candidate.policy),
|
|
62
|
+
evidence,
|
|
63
|
+
summary: {
|
|
64
|
+
total: evidence.length,
|
|
65
|
+
changed: evidence.filter(item => item.incumbentAcceptance.disposition !== item.candidateAcceptance.disposition).length,
|
|
66
|
+
candidateActionCount: evidence.filter(item => item.candidateAcceptance.disposition === 'act').length,
|
|
67
|
+
},
|
|
68
|
+
actionAuthorization: 'not-authorized', recordedAt: input.recordedAt,
|
|
69
|
+
};
|
|
70
|
+
return structuredClone({ ...withoutDigest, digest: digest(withoutDigest) });
|
|
71
|
+
}
|
|
72
|
+
/** Records an explicitly approved, use-case-scoped pin change with an exact rollback target. */
|
|
73
|
+
export function createAcceptancePromotionRecord(input) {
|
|
74
|
+
required(input.promotionId, 'promotion id');
|
|
75
|
+
required(input.useCaseId, 'use case id');
|
|
76
|
+
required(input.approvalReference, 'approval reference');
|
|
77
|
+
required(input.qualificationManifest.path, 'qualification manifest path');
|
|
78
|
+
timestamp(input.promotedAt, 'promotedAt');
|
|
79
|
+
if (input.shadow.useCaseId !== input.useCaseId)
|
|
80
|
+
throw new Error('shadow evidence belongs to a different use case');
|
|
81
|
+
const { digest: claimedShadowDigest, ...shadowPayload } = input.shadow;
|
|
82
|
+
if (digest(shadowPayload) !== claimedShadowDigest)
|
|
83
|
+
throw new Error('shadow record digest mismatch');
|
|
84
|
+
const withoutDigest = {
|
|
85
|
+
schemaVersion: 'decision-acceptance-promotion/v1', promotionId: input.promotionId,
|
|
86
|
+
useCaseId: input.useCaseId, from: input.shadow.incumbent, to: input.shadow.candidate,
|
|
87
|
+
rollback: input.shadow.incumbent, shadowRecordDigest: input.shadow.digest,
|
|
88
|
+
qualificationManifest: structuredClone(input.qualificationManifest),
|
|
89
|
+
approvalReference: input.approvalReference, promotedAt: input.promotedAt,
|
|
90
|
+
};
|
|
91
|
+
return structuredClone({ ...withoutDigest, digest: digest(withoutDigest) });
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=acceptance-rollout.js.map
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { DecisionValidationError } from './validate.js';
|
|
2
|
+
const METRICS = {
|
|
3
|
+
'truth-probability': new Set(['yes-probability', 'calibrated-risk']),
|
|
4
|
+
choice: new Set(['selected-probability', 'native-confidence', 'top-two-margin', 'entropy', 'concentration', 'calibrated-risk']),
|
|
5
|
+
'ordinal-score': new Set(['expected-score', 'native-confidence', 'dispersion', 'concentration', 'calibrated-risk']),
|
|
6
|
+
};
|
|
7
|
+
export function validatePrimitiveAcceptancePolicy(policy, definition) {
|
|
8
|
+
if (!/^\d+\.\d+\.\d+$/.test(policy.version))
|
|
9
|
+
throw new DecisionValidationError('acceptance policy version must be semver');
|
|
10
|
+
if (!policy.compatibleUncertaintyProfiles.length
|
|
11
|
+
|| new Set(policy.compatibleUncertaintyProfiles).size !== policy.compatibleUncertaintyProfiles.length
|
|
12
|
+
|| policy.compatibleUncertaintyProfiles.some(profile => !profile.trim())) {
|
|
13
|
+
throw new DecisionValidationError('primitive acceptance requires unique non-empty compatible uncertainty profiles');
|
|
14
|
+
}
|
|
15
|
+
if (policy.precedence !== 'first-match')
|
|
16
|
+
throw new DecisionValidationError('acceptance policy requires explicit first-match precedence');
|
|
17
|
+
const ids = new Set();
|
|
18
|
+
for (const rule of policy.rules) {
|
|
19
|
+
if (!rule.id || ids.has(rule.id))
|
|
20
|
+
throw new DecisionValidationError('acceptance rule IDs must be non-empty and unique');
|
|
21
|
+
ids.add(rule.id);
|
|
22
|
+
if (!rule.all.length)
|
|
23
|
+
throw new DecisionValidationError(`acceptance rule '${rule.id}' must contain a condition`);
|
|
24
|
+
for (const condition of rule.all)
|
|
25
|
+
if (!METRICS[rule.primitive].has(condition.metric)) {
|
|
26
|
+
throw new DecisionValidationError(`acceptance metric '${condition.metric}' is incompatible with ${rule.primitive}`);
|
|
27
|
+
}
|
|
28
|
+
for (const condition of rule.all)
|
|
29
|
+
validateCondition(condition);
|
|
30
|
+
validateRoute(rule.route);
|
|
31
|
+
}
|
|
32
|
+
[policy.defaultRoute, policy.missingEvidenceRoute, policy.invalidEvidenceRoute, policy.tieRoute].forEach(validateRoute);
|
|
33
|
+
if (policy.requiredOptions) {
|
|
34
|
+
if (!policy.requiredOptions.length || new Set(policy.requiredOptions).size !== policy.requiredOptions.length
|
|
35
|
+
|| policy.requiredOptions.some(option => !option)) {
|
|
36
|
+
throw new DecisionValidationError('required Choice options must be unique non-empty IDs');
|
|
37
|
+
}
|
|
38
|
+
if (definition?.spec.answer.kind !== 'choice')
|
|
39
|
+
throw new DecisionValidationError('requiredOptions is valid only for Choice policies');
|
|
40
|
+
const options = new Set(definition.spec.answer.options.map(option => option.id));
|
|
41
|
+
if (policy.requiredOptions.some(option => !options.has(option))) {
|
|
42
|
+
throw new DecisionValidationError('Choice definition is missing an option required by its acceptance policy');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function applyPrimitiveAcceptance(definition, policy, observation) {
|
|
47
|
+
if (observation.status !== 'success')
|
|
48
|
+
return observation;
|
|
49
|
+
validatePrimitiveAcceptancePolicy(policy, definition);
|
|
50
|
+
const uncertaintyProfile = observation.uncertainty?.profile;
|
|
51
|
+
if (!uncertaintyProfile) {
|
|
52
|
+
return routed(observation, policy, policy.missingEvidenceRoute, 'missing-evidence', {});
|
|
53
|
+
}
|
|
54
|
+
if (!policy.compatibleUncertaintyProfiles.includes(uncertaintyProfile)) {
|
|
55
|
+
return routed(observation, policy, policy.invalidEvidenceRoute, 'invalid-evidence', {});
|
|
56
|
+
}
|
|
57
|
+
const evidence = deriveEvidence(definition, observation);
|
|
58
|
+
if (!evidence)
|
|
59
|
+
return routed(observation, policy, policy.invalidEvidenceRoute, 'invalid-evidence', {});
|
|
60
|
+
if (policy.calibration === 'required' && evidence['calibrated-risk'] === undefined) {
|
|
61
|
+
return routed(observation, policy, policy.missingEvidenceRoute, 'calibration-required', evidence);
|
|
62
|
+
}
|
|
63
|
+
if (definition.spec.answer.kind === 'choice' && evidence['top-two-margin']?.normalizedBps === 0) {
|
|
64
|
+
return routed(observation, policy, policy.tieRoute, 'tie', evidence);
|
|
65
|
+
}
|
|
66
|
+
for (const rule of policy.rules) {
|
|
67
|
+
if (rule.primitive !== definition.spec.answer.kind)
|
|
68
|
+
continue;
|
|
69
|
+
const conditionValues = rule.all.map(condition => evidence[condition.metric]);
|
|
70
|
+
if (conditionValues.some(value => value === undefined)) {
|
|
71
|
+
return routed(observation, policy, policy.missingEvidenceRoute, 'missing-evidence', evidence);
|
|
72
|
+
}
|
|
73
|
+
if (rule.all.every((condition, index) => matches(conditionValues[index].normalizedBps, condition))) {
|
|
74
|
+
return routed(observation, policy, rule.route, 'matched', evidence, rule.id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return routed(observation, policy, policy.defaultRoute, 'default', evidence);
|
|
78
|
+
}
|
|
79
|
+
function deriveEvidence(definition, observation) {
|
|
80
|
+
const values = {};
|
|
81
|
+
const add = (metric, value, normalized, provenance, calibrationRef = null) => {
|
|
82
|
+
if (!Number.isFinite(value) || !Number.isFinite(normalized) || normalized < 0 || normalized > 1)
|
|
83
|
+
return false;
|
|
84
|
+
// Preserve the raw statistic and remove only IEEE-754 representation noise
|
|
85
|
+
// from the separate basis-point comparison value (eight decimal places).
|
|
86
|
+
const normalizedBps = Math.round(normalized * 1_000_000_000_000) / 100_000_000;
|
|
87
|
+
values[metric] = { value, normalizedBps, provenance, calibrationRef };
|
|
88
|
+
return true;
|
|
89
|
+
};
|
|
90
|
+
const uncertainty = observation.uncertainty;
|
|
91
|
+
if (uncertainty?.calibratedRisk
|
|
92
|
+
&& !add('calibrated-risk', uncertainty.calibratedRisk.value, uncertainty.calibratedRisk.value, 'calibrated', uncertainty.calibratedRisk.calibrationRef))
|
|
93
|
+
return null;
|
|
94
|
+
const kind = definition.spec.answer.kind;
|
|
95
|
+
if (kind === 'truth-probability') {
|
|
96
|
+
return typeof observation.value === 'number' && add('yes-probability', observation.value, observation.value, 'provider-value') ? values : null;
|
|
97
|
+
}
|
|
98
|
+
const distribution = uncertainty?.distribution;
|
|
99
|
+
if (!distribution)
|
|
100
|
+
return values;
|
|
101
|
+
const probabilities = Object.values(distribution);
|
|
102
|
+
if (!probabilities.length)
|
|
103
|
+
return null;
|
|
104
|
+
const concentration = probabilities.reduce((sum, probability) => sum + probability ** 2, 0);
|
|
105
|
+
if (!add('concentration', concentration, concentration, 'derived'))
|
|
106
|
+
return null;
|
|
107
|
+
if (uncertainty.confidence !== null && !add('native-confidence', uncertainty.confidence, uncertainty.confidence, 'provider-confidence'))
|
|
108
|
+
return null;
|
|
109
|
+
if (kind === 'choice') {
|
|
110
|
+
if (typeof observation.value !== 'string' || distribution[observation.value] === undefined)
|
|
111
|
+
return null;
|
|
112
|
+
const sorted = [...probabilities].sort((a, b) => b - a);
|
|
113
|
+
const selected = distribution[observation.value];
|
|
114
|
+
const margin = selected - (sorted[1] ?? 0);
|
|
115
|
+
const entropy = probabilities.length <= 1 ? 0
|
|
116
|
+
: -probabilities.reduce((sum, probability) => sum + (probability === 0 ? 0 : probability * Math.log(probability)), 0) / Math.log(probabilities.length);
|
|
117
|
+
if (!add('selected-probability', selected, selected, 'provider-distribution')
|
|
118
|
+
|| !add('top-two-margin', margin, margin, 'derived')
|
|
119
|
+
|| !add('entropy', entropy, entropy, 'derived'))
|
|
120
|
+
return null;
|
|
121
|
+
return values;
|
|
122
|
+
}
|
|
123
|
+
const range = definition.spec.answer.levels.length - 1;
|
|
124
|
+
if (range <= 0)
|
|
125
|
+
return null;
|
|
126
|
+
const expected = Object.entries(distribution).reduce((sum, [level, probability]) => sum + Number(level) * probability, 0);
|
|
127
|
+
const variance = Object.entries(distribution).reduce((sum, [level, probability]) => sum + (Number(level) - expected) ** 2 * probability, 0);
|
|
128
|
+
if (!add('expected-score', expected, expected / range, 'derived')
|
|
129
|
+
|| !add('dispersion', variance, variance / (range ** 2), 'derived'))
|
|
130
|
+
return null;
|
|
131
|
+
return values;
|
|
132
|
+
}
|
|
133
|
+
function routed(observation, policy, route, reason, values, matchedRule = null) {
|
|
134
|
+
const acceptance = {
|
|
135
|
+
policyVersion: policy.version, uncertaintyProfile: observation.uncertainty?.profile ?? null,
|
|
136
|
+
disposition: route.disposition, matchedRule, reason, values,
|
|
137
|
+
...(route.fallbackTarget ? { fallbackTarget: route.fallbackTarget } : {}),
|
|
138
|
+
};
|
|
139
|
+
if (route.disposition === 'act')
|
|
140
|
+
return { ...observation, acceptance };
|
|
141
|
+
return { ...observation, status: 'abstained', reason: 'low-confidence', acceptance };
|
|
142
|
+
}
|
|
143
|
+
function matches(value, condition) {
|
|
144
|
+
switch (condition.op) {
|
|
145
|
+
case 'lt': return value < condition.thresholdBps;
|
|
146
|
+
case 'lte': return value <= condition.thresholdBps;
|
|
147
|
+
case 'gt': return value > condition.thresholdBps;
|
|
148
|
+
case 'gte': return value >= condition.thresholdBps;
|
|
149
|
+
case 'between': return value >= condition.minimumBps && value <= condition.maximumBps;
|
|
150
|
+
case 'outside': return value < condition.minimumBps || value > condition.maximumBps;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function validateCondition(condition) {
|
|
154
|
+
const scalar = condition.op === 'lt' || condition.op === 'lte' || condition.op === 'gt' || condition.op === 'gte';
|
|
155
|
+
if (scalar) {
|
|
156
|
+
if (!Number.isInteger(condition.thresholdBps) || condition.thresholdBps < 0 || condition.thresholdBps > 10_000
|
|
157
|
+
|| condition.minimumBps !== undefined || condition.maximumBps !== undefined) {
|
|
158
|
+
throw new DecisionValidationError('scalar acceptance conditions require one thresholdBps in [0,10000]');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (!Number.isInteger(condition.minimumBps) || !Number.isInteger(condition.maximumBps)
|
|
162
|
+
|| condition.minimumBps < 0 || condition.maximumBps > 10_000 || condition.minimumBps > condition.maximumBps
|
|
163
|
+
|| condition.thresholdBps !== undefined) {
|
|
164
|
+
throw new DecisionValidationError('range acceptance conditions require ordered minimumBps/maximumBps in [0,10000]');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function validateRoute(route) {
|
|
168
|
+
if (route.disposition === 'fallback') {
|
|
169
|
+
if (!route.fallbackTarget)
|
|
170
|
+
throw new DecisionValidationError('fallback route requires fallbackTarget');
|
|
171
|
+
}
|
|
172
|
+
else if (route.fallbackTarget !== undefined)
|
|
173
|
+
throw new DecisionValidationError('fallbackTarget is valid only for fallback routes');
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=acceptance.js.map
|