@voce-engine/cli 0.1.0-rc.2 → 0.1.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +132 -23
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The `voce` command is the offline-first, explicit-path CLI for VOCE v0.1. It does not load lifecycle scripts, discover global packages, inspect credentials, or call a Provider unless the command explicitly selects the built-in `mock` provider.
|
|
4
4
|
|
|
5
|
-
> `0.1.0-rc.
|
|
5
|
+
> `0.1.0-rc.4` is a release candidate, not production-ready. CLI behavior may change before `0.1.0`.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install --global @voce-engine/cli@0.1.0-rc.
|
|
8
|
+
npm install --global @voce-engine/cli@0.1.0-rc.4
|
|
9
9
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.0-rc.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.0-rc.4";
|
|
2
2
|
export declare function runCli(argv?: string[]): Promise<number>;
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { lstat, mkdir, readdir, readFile, realpath, rename, writeFile } from 'node:fs/promises';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import * as process from 'node:process';
|
|
5
|
-
import { MOCK_IMAGE_PROFILE, MOCK_JPEG_PROFILE, MOCK_LIMITED_REFERENCE_PROFILE, compileEvaluationReport, computeExecutionRunHash, computeCompilationContextHash, computeOntologyInstanceHash, computeSemanticReviewRequestHash, createConstraintWaiver, createHumanAcceptanceDecision, compileConstraints, createScenarioPackRegistry, executeOffline, executeSemanticReview, FixtureReferenceInterpreter, FixtureSemanticReviewer, guardPromptCandidate, renderStaticTraceReport, sha256, traceModelFromExecution, validateStructuralImage, canonicalize, } from '@voce-engine/core';
|
|
5
|
+
import { MOCK_IMAGE_PROFILE, MOCK_JPEG_PROFILE, MOCK_LIMITED_REFERENCE_PROFILE, compileEvaluationReport, computeExecutionRunHash, computeCompilationContextHash, computeOntologyInstanceHash, computeSemanticReviewRequestHash, createConstraintWaiver, createHumanAcceptanceDecision, compileConstraints, compilePromptIR, createReferenceCandidate, planPipeline, planReferences, createScenarioPackRegistry, executeOffline, executeSemanticReview, FixtureReferenceInterpreter, FixtureSemanticReviewer, guardPromptCandidate, renderStaticTraceReport, sha256, traceModelFromExecution, validateStructuralImage, canonicalize, VISUAL_COMPOSITION_CATALOG, computeVisualCompositionCatalogHash, expandVisualCompositionPreset, } from '@voce-engine/core';
|
|
6
6
|
import { FIXTURE_M6_OPAQUE_PNG, fixtureChangeIntent, fixtureM4ConstraintInput, fixtureM5Candidate, fixtureM5ExecutionInput, fixtureM5GuardInput, fixtureM5PromptIR, fixtureM6Artifact, fixtureM6Authorization, fixtureScopePlan, } from '@voce-engine/testkit';
|
|
7
7
|
import { BUNDLE_MANIFEST_SCHEMA_VERSION } from '@voce-engine/contracts';
|
|
8
|
-
export const CLI_VERSION = '0.1.0-rc.
|
|
8
|
+
export const CLI_VERSION = '0.1.0-rc.4';
|
|
9
9
|
const TOOL_ID = '@voce-engine/cli';
|
|
10
|
-
const CONTRACTS_VERSION = '0.1.0-rc.
|
|
11
|
-
const CORE_VERSION = '0.1.0-rc.
|
|
10
|
+
const CONTRACTS_VERSION = '0.1.0-rc.4';
|
|
11
|
+
const CORE_VERSION = '0.1.0-rc.4';
|
|
12
12
|
const SOURCE_SCHEMA = 'voce.scenario-pack-source/v1alpha1';
|
|
13
13
|
const PACK_SCHEMA = 'voce.scenario-pack/v1alpha1';
|
|
14
14
|
const HASH = /^sha256:[0-9a-f]{64}$/;
|
|
@@ -337,17 +337,107 @@ function probeInput(probe, resolution, caseSpec) {
|
|
|
337
337
|
const requestedScopePlan = fixtureScopePlan(['person.identity'], 'ref-01', caseSpec.id, caseSpec.revision);
|
|
338
338
|
const ontologyBase = { ...base.ontologyInstance, id: `ontology-${probe.id}`, caseId: caseSpec.id, caseRevision: caseSpec.revision, contextHash: context.contextHash, requestedScopePlanHash: requestedScopePlan.planHash, unknownPaths: probe.unknownPaths ?? [], unspecifiedPaths: [], facts: [], conflicts: [], unresolvedItems: [], decisionTrace: [] };
|
|
339
339
|
const ontologyInstance = { ...ontologyBase, instanceHash: computeOntologyInstanceHash({ ...ontologyBase, instanceHash: '' }) };
|
|
340
|
-
const intents = (probe.intents ?? []).map((intent) => ({ ...fixtureChangeIntent(intent.id, intent.operation, intent.targetPath, intent.requestedValue), importance: intent.importance ?? 'required' }));
|
|
340
|
+
const intents = (probe.intents ?? []).map((intent) => ({ ...fixtureChangeIntent(intent.id, intent.operation, intent.targetPath, intent.requestedValue), importance: intent.importance ?? 'required', ...(intent.sourceHintIds === undefined ? {} : { sourceHintIds: [...intent.sourceHintIds].sort(compare) }) }));
|
|
341
341
|
const waivers = probe.waiverTarget ? [createConstraintWaiver({ schemaVersion: 'voce.constraint-waiver/v1alpha1', id: `waiver-${probe.id}`, caseId: caseSpec.id, caseRevision: caseSpec.revision, contextHash: context.contextHash, targetId: probe.waiverTarget, authority: 'user', decidedBy: 'fixture-reviewer', reasonCode: 'FIXTURE_EXPLICIT_WAIVER', decidedAt: '2026-01-01T00:00:00.000Z' })] : [];
|
|
342
342
|
return fixtureM4ConstraintInput({ caseId: caseSpec.id, caseRevision: caseSpec.revision, context, contextHash: context.contextHash, requestedScopePlanHash: requestedScopePlan.planHash, ontologyInstance, changeIntents: intents, effectiveScenario: resolution.effectiveScenario, waivers });
|
|
343
343
|
}
|
|
344
|
-
function
|
|
345
|
-
|
|
346
|
-
return {
|
|
344
|
+
function pathRelated(left, right) { return left === right || left.startsWith(`${right}.`) || right.startsWith(`${left}.`); }
|
|
345
|
+
function referenceProbeCandidates(probe, ir) {
|
|
346
|
+
return (probe.references ?? []).map((reference) => {
|
|
347
|
+
const constraintIds = ir.constraints.filter((constraint) => reference.ontologyScopes.some((scope) => constraint.targetPaths.some((target) => pathRelated(scope, target)))).map((constraint) => constraint.id);
|
|
348
|
+
const goalIds = ir.goals.filter((goal) => typeof goal.targetPath === 'string' && reference.ontologyScopes.some((scope) => pathRelated(scope, goal.targetPath))).map((goal) => goal.id);
|
|
349
|
+
const artifact = { id: reference.assetId ?? reference.id, storeId: 'fixture-store', contentHash: sha256({ fixtureReference: reference.id }), mediaType: 'image/png', byteLength: 64, role: 'reference', resolverId: 'fixture-resolver', availability: 'available', retentionClass: 'fixture', redactionPolicy: 'hash-only' };
|
|
350
|
+
return createReferenceCandidate({ schemaVersion: 'voce.reference-candidate/v1alpha1', id: reference.id, assetId: artifact.id, artifact, contentHash: artifact.contentHash, mediaType: artifact.mediaType, byteLength: artifact.byteLength, role: reference.role, ontologyScopes: reference.ontologyScopes, importance: reference.importance, constraintIds, sourceBindingIds: reference.sourceBindingIds ?? [], goalIds, orderKey: reference.orderKey ?? reference.id });
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function coreProbeEvidence(probe, resolution, caseSpec, profile) {
|
|
354
|
+
const baseInput = probeInput(probe, resolution, caseSpec);
|
|
355
|
+
const ir = compileConstraints(baseInput);
|
|
356
|
+
const presetInput = { ...baseInput, changeIntents: [...baseInput.changeIntents, ...expandVisualCompositionPreset('rule-of-thirds')] };
|
|
357
|
+
const presetIr = compileConstraints(presetInput);
|
|
358
|
+
const evidence = {
|
|
347
359
|
status: ir.status, constraintHash: ir.deterministicSignature, blockingConflictCount: ir.conflicts.filter((item) => item.blocking).length,
|
|
348
360
|
conflictCodes: ir.conflicts.map((item) => item.code).sort(compare), reviewRequirementCount: ir.reviewRequirements.length,
|
|
349
361
|
reviewReasons: ir.reviewRequirements.map((item) => item.reasonCode).sort(compare), waivedWarnings: ir.warnings.filter((item) => item === 'REQUIRED_CONFLICT_WAIVED' || item === 'HARD_CONFLICT_CANNOT_WAIVE').sort(compare),
|
|
350
362
|
degradationCount: ir.degradedPreferences.length, personIntentCount: (probe.intents ?? []).filter((item) => item.targetPath.startsWith('person.')).length,
|
|
363
|
+
constraintTargetPaths: ir.constraints.flatMap((item) => item.targetPaths).sort(compare),
|
|
364
|
+
constraintSourceIds: [...new Set(ir.constraints.flatMap((item) => item.sourceIds))].sort(compare),
|
|
365
|
+
presetId: 'rule-of-thirds', presetConstraintStatus: presetIr.status, presetConstraintHashBefore: ir.deterministicSignature, presetConstraintHashAfter: presetIr.deterministicSignature,
|
|
366
|
+
};
|
|
367
|
+
const candidates = referenceProbeCandidates(probe, ir);
|
|
368
|
+
const presetCandidates = referenceProbeCandidates(probe, presetIr);
|
|
369
|
+
const maximumReferenceCount = probe.maximumReferenceCount ?? profile.maximumReferenceCount;
|
|
370
|
+
const plan = planReferences({ schemaVersion: 'voce.reference-planning-input/v1alpha1', caseId: caseSpec.id, caseRevision: caseSpec.revision, contextHash: ir.contextHash, constraintIR: ir, candidates, dependencies: [], profile, budget: { maximumReferenceCount, usedReferenceCount: 0, byteLengthKnown: true, unknownByteLengthAssetIds: [] } });
|
|
371
|
+
const presetPlan = planReferences({ schemaVersion: 'voce.reference-planning-input/v1alpha1', caseId: caseSpec.id, caseRevision: caseSpec.revision, contextHash: presetIr.contextHash, constraintIR: presetIr, candidates: presetCandidates, dependencies: [], profile, budget: { maximumReferenceCount, usedReferenceCount: 0, byteLengthKnown: true, unknownByteLengthAssetIds: [] } });
|
|
372
|
+
evidence.referenceStatus = plan.status;
|
|
373
|
+
evidence.referencePlanHash = plan.planHash;
|
|
374
|
+
evidence.selectedRoles = plan.ordered.map((item) => item.role).sort(compare);
|
|
375
|
+
evidence.omittedRoles = plan.omitted.map((item) => candidates.find((candidate) => candidate.id === item.candidateId)?.role ?? item.candidateId).sort(compare);
|
|
376
|
+
evidence.blockedRoles = plan.blockedReferences.map((item) => candidates.find((candidate) => candidate.id === item.candidateId)?.role ?? item.candidateId).sort(compare);
|
|
377
|
+
evidence.selectedSourceBindingIds = [...new Set(plan.ordered.flatMap((item) => item.sourceBindingIds))].sort(compare);
|
|
378
|
+
evidence.presetReferenceCountBefore = plan.ordered.length;
|
|
379
|
+
evidence.presetReferenceCountAfter = presetPlan.ordered.length;
|
|
380
|
+
evidence.presetReferencePlanHashBefore = plan.planHash;
|
|
381
|
+
evidence.presetReferencePlanHashAfter = presetPlan.planHash;
|
|
382
|
+
if (!probe.references?.length)
|
|
383
|
+
return evidence;
|
|
384
|
+
if (ir.status !== 'ok' || plan.status !== 'ok') {
|
|
385
|
+
evidence.promptStatus = 'blocked';
|
|
386
|
+
return evidence;
|
|
387
|
+
}
|
|
388
|
+
const pipelineResult = planPipeline({ schemaVersion: 'voce.pipeline-planning-input/v1alpha1', caseId: caseSpec.id, caseRevision: caseSpec.revision, contextHash: ir.contextHash, outputContract: probeInput(probe, resolution, caseSpec).outputContract, constraintIR: ir, referencePlan: plan, profile });
|
|
389
|
+
if (!pipelineResult.pipelinePlan) {
|
|
390
|
+
evidence.promptStatus = 'blocked';
|
|
391
|
+
return evidence;
|
|
392
|
+
}
|
|
393
|
+
const outputContract = probeInput(probe, resolution, caseSpec).outputContract;
|
|
394
|
+
const prompt = compilePromptIR({ schemaVersion: 'voce.prompt-compilation-input/v1alpha2', caseId: caseSpec.id, caseRevision: caseSpec.revision, context: probeInput(probe, resolution, caseSpec).context, contextHash: ir.contextHash, constraintIR: ir, referencePlan: plan, pipelinePlan: pipelineResult.pipelinePlan, outputContract, targetAdapter: { id: profile.adapterId, version: profile.version, digest: profile.adapterDigest }, targetCapabilityProfile: { id: profile.id, version: profile.version, digest: profile.profileHash }, effectiveScenario: resolution.effectiveScenario });
|
|
395
|
+
evidence.promptStatus = 'ok';
|
|
396
|
+
evidence.promptHash = prompt.deterministicSignature;
|
|
397
|
+
evidence.promptTargetPaths = ir.constraints.filter((constraint) => prompt.constraintCoverage.some((coverage) => coverage.constraintId === constraint.id && (coverage.sectionIds.length > 0 || coverage.referenceMappingIds.length > 0 || coverage.parameterIds.length > 0))).flatMap((constraint) => constraint.targetPaths).sort(compare);
|
|
398
|
+
evidence.promptReferenceRoles = prompt.referenceMappings.map((mapping) => mapping.role).sort(compare);
|
|
399
|
+
return evidence;
|
|
400
|
+
}
|
|
401
|
+
function scenarioContributions(scenario, category) { return scenario[category].filter(isObject); }
|
|
402
|
+
function contributionArray(contributions, field) { return contributions.flatMap((contribution) => Array.isArray(contribution[field]) ? contribution[field].filter(isObject) : []); }
|
|
403
|
+
function semanticCriteriaForScenario(scenario) {
|
|
404
|
+
const criteria = contributionArray(scenarioContributions(scenario, 'reviewTemplates'), 'criteria').map((criterion) => {
|
|
405
|
+
const id = stringField(criterion, 'id', 'REVIEW_TEMPLATE_INVALID');
|
|
406
|
+
const kind = stringField(criterion, 'kind', 'REVIEW_TEMPLATE_INVALID');
|
|
407
|
+
const importance = criterion.importance;
|
|
408
|
+
if (importance !== 'hard' && importance !== 'required' && importance !== 'preferred')
|
|
409
|
+
throw new CliError('REVIEW_TEMPLATE_INVALID', 'Review criterion importance is invalid.', EXIT.contract);
|
|
410
|
+
if (criterion.targetPath !== undefined && typeof criterion.targetPath !== 'string')
|
|
411
|
+
throw new CliError('REVIEW_TEMPLATE_INVALID', 'Review criterion targetPath is invalid.', EXIT.contract);
|
|
412
|
+
if (criterion.prompt !== undefined && typeof criterion.prompt !== 'string')
|
|
413
|
+
throw new CliError('REVIEW_TEMPLATE_INVALID', 'Review criterion prompt is invalid.', EXIT.contract);
|
|
414
|
+
return { id, kind, importance, ...(criterion.targetPath === undefined ? {} : { targetPath: criterion.targetPath }), ...(criterion.prompt === undefined ? {} : { prompt: criterion.prompt }) };
|
|
415
|
+
});
|
|
416
|
+
const byId = new Map();
|
|
417
|
+
for (const criterion of criteria.sort((left, right) => compare(left.id, right.id))) {
|
|
418
|
+
const prior = byId.get(criterion.id);
|
|
419
|
+
if (prior && canonicalize(prior) !== canonicalize(criterion))
|
|
420
|
+
throw new CliError('REVIEW_TEMPLATE_ID_COLLISION', 'Review criteria with one ID disagree.', EXIT.contract);
|
|
421
|
+
byId.set(criterion.id, criterion);
|
|
422
|
+
}
|
|
423
|
+
return [...byId.values()];
|
|
424
|
+
}
|
|
425
|
+
function scenarioEvidence(scenario) {
|
|
426
|
+
const ontology = contributionArray(scenarioContributions(scenario, 'ontologyVocabulary'), 'paths');
|
|
427
|
+
const scopes = scenarioContributions(scenario, 'interpretationScopes');
|
|
428
|
+
const bindings = contributionArray(scopes, 'bindings');
|
|
429
|
+
const promptDefinitions = contributionArray(scenarioContributions(scenario, 'promptSections'), 'sections');
|
|
430
|
+
const promptPaths = promptDefinitions.flatMap((definition) => Array.isArray(definition.requiredPaths) ? definition.requiredPaths.filter((item) => typeof item === 'string') : []);
|
|
431
|
+
const promptSectionOrder = promptDefinitions.map((definition) => ({ id: String(definition.id ?? ''), order: typeof definition.order === 'number' ? definition.order : 0 })).filter((item) => item.id).sort((left, right) => left.order - right.order || compare(left.id, right.id)).map((item) => item.id);
|
|
432
|
+
const criteria = semanticCriteriaForScenario(scenario);
|
|
433
|
+
return {
|
|
434
|
+
ontologyPaths: ontology.map((item) => String(item.path ?? '')).filter(Boolean).sort(compare),
|
|
435
|
+
bindingPolicies: bindings.map((item) => `${String(item.assetRole ?? '')}|${String(item.relation ?? '')}|${String(item.targetPath ?? '')}|${String(item.priority ?? '')}`).sort(compare),
|
|
436
|
+
promptTargetPaths: [...new Set(promptPaths)].sort(compare),
|
|
437
|
+
promptSectionOrder,
|
|
438
|
+
visualComposition: { catalogId: VISUAL_COMPOSITION_CATALOG.id, catalogHash: computeVisualCompositionCatalogHash(), pathCount: VISUAL_COMPOSITION_CATALOG.paths.length, presetCount: VISUAL_COMPOSITION_CATALOG.presets.length, fullShotChanges: expandVisualCompositionPreset('full-shot').map((item) => ({ targetPath: item.targetPath, ...(item.requestedValue === undefined ? {} : { requestedValue: item.requestedValue }) })) },
|
|
439
|
+
reviewCriterionIds: criteria.map((item) => item.id).sort(compare),
|
|
440
|
+
reviewTargetPaths: criteria.map((item) => item.targetPath).filter((item) => typeof item === 'string').sort(compare),
|
|
351
441
|
};
|
|
352
442
|
}
|
|
353
443
|
function referenceEvidence(fixture, resolution, caseSpec) {
|
|
@@ -406,13 +496,13 @@ function profileFor(id) {
|
|
|
406
496
|
async function loadProfile(filePath) { const object = record(await readJson(path.resolve(filePath), 'PROFILE_FILE_UNSAFE'), 'PROFILE_JSON_INVALID'); return profileFor(stringField(object, 'id', 'PROFILE_INVALID')); }
|
|
407
497
|
function profilePin(profile) { return { id: profile.id, version: profile.version, digest: profile.profileHash }; }
|
|
408
498
|
function toolPin(id, version) { return { id, version, digest: sha256({ id, version }) }; }
|
|
409
|
-
function defaultCase(doc, packId) {
|
|
499
|
+
function defaultCase(doc, packId, packVersion) {
|
|
410
500
|
assertKnown(doc, ['schemaVersion', 'id', 'revision', 'fixtureId', 'mode', 'scenario', 'userIntent', 'assets', 'trustedMetadata', 'policies', 'requestedOutput'], 'CASE_UNKNOWN_FIELD');
|
|
411
501
|
const id = typeof doc.id === 'string' ? doc.id : `${packId}-case`;
|
|
412
502
|
const revision = typeof doc.revision === 'number' ? doc.revision : 1;
|
|
413
503
|
const output = isObject(doc.requestedOutput) ? doc.requestedOutput : { artifactKind: 'image', dataType: 'image', mediaTypes: ['image/png'], cardinality: { min: 1, max: 1 }, background: 'opaque', allowAlpha: false };
|
|
414
504
|
const asset = { id: 'ref-01', storeId: 'fixture-store', contentHash: sha256({ fixture: `${id}:ref-01` }), mediaType: 'image/png', byteLength: 64, role: 'reference', resolverId: 'fixture-resolver', availability: 'available', retentionClass: 'fixture', redactionPolicy: 'hash-only' };
|
|
415
|
-
return { schemaVersion: 'voce.case-spec/v1alpha1', id, revision, mode: 'manual', scenario: { root: { packId, versionRange:
|
|
505
|
+
return { schemaVersion: 'voce.case-spec/v1alpha1', id, revision, mode: 'manual', scenario: { root: { packId, versionRange: packVersion }, extensions: [] }, userIntent: typeof doc.userIntent === 'string' ? doc.userIntent : 'Offline fixture case.', assets: [asset], trustedMetadata: [], policies: { schemaVersion: 'voce.case-policies/v1alpha1', observationConfirmation: 'explicit', bindingConfirmation: 'explicit', allowDeclaredDefaults: true }, requestedOutput: output };
|
|
416
506
|
}
|
|
417
507
|
async function compileInputs(casePath, scenarioPath, profilePath) {
|
|
418
508
|
const caseDoc = record(await readJson(path.resolve(casePath), 'CASE_FILE_UNSAFE'), 'CASE_JSON_INVALID');
|
|
@@ -422,14 +512,14 @@ async function compileInputs(casePath, scenarioPath, profilePath) {
|
|
|
422
512
|
const resolution = registry.resolve({ root: { packId: loaded.descriptor.manifest.packId, versionRange: loaded.descriptor.manifest.version }, extensions: [] });
|
|
423
513
|
if (resolution.status !== 'resolved')
|
|
424
514
|
throw new CliError('PACK_RESOLUTION_BLOCKED', 'ScenarioPack resolution is blocked.', EXIT.contract);
|
|
425
|
-
const caseSpec = defaultCase(caseDoc, loaded.descriptor.manifest.packId);
|
|
515
|
+
const caseSpec = defaultCase(caseDoc, loaded.descriptor.manifest.packId, loaded.descriptor.manifest.version);
|
|
426
516
|
const fixtureId = typeof caseDoc.fixtureId === 'string' ? caseDoc.fixtureId : caseSpec.id;
|
|
427
517
|
const fixture = loaded.fixtures.find((candidate) => candidate.id === fixtureId) ?? loaded.fixtures[0] ?? { id: fixtureId, profileId: MOCK_IMAGE_PROFILE.id };
|
|
428
518
|
const profile = await loadProfile(profilePath);
|
|
429
519
|
const prompt = fixtureM5Candidate(fixtureM5PromptIR(profile));
|
|
430
520
|
const input = fixtureM5ExecutionInput(profile);
|
|
431
|
-
const coreProbes = Object.fromEntries((fixture.coreProbes ?? []).sort((left, right) => compare(left.id, right.id)).map((probe) => [probe.id, coreProbeEvidence(probe, resolution, caseSpec)]));
|
|
432
|
-
const acceptance = { resolution: { status: resolution.status, selectedCount: resolution.report.selected.length, effectiveScenarioHash: resolution.effectiveScenario.effectiveScenarioHash }, reference: referenceEvidence(fixture, resolution, caseSpec), coreProbes, compile: { constraintHash: input.constraintIR.deterministicSignature, referencePlanHash: input.referencePlan.planHash, pipelinePlanHash: input.pipelinePlan.planHash } };
|
|
521
|
+
const coreProbes = Object.fromEntries((fixture.coreProbes ?? []).sort((left, right) => compare(left.id, right.id)).map((probe) => [probe.id, coreProbeEvidence(probe, resolution, caseSpec, profile)]));
|
|
522
|
+
const acceptance = { resolution: { status: resolution.status, selectedCount: resolution.report.selected.length, effectiveScenarioHash: resolution.effectiveScenario.effectiveScenarioHash }, scenario: scenarioEvidence(resolution.effectiveScenario), reference: referenceEvidence(fixture, resolution, caseSpec), coreProbes, compile: { constraintHash: input.constraintIR.deterministicSignature, referencePlanHash: input.referencePlan.planHash, pipelinePlanHash: input.pipelinePlan.planHash } };
|
|
433
523
|
return { loaded, resolution, caseSpec, profile, fixture, input, prompt, acceptance };
|
|
434
524
|
}
|
|
435
525
|
function bundlePins(profile, scenario) { return { tool: toolPin(TOOL_ID, CLI_VERSION), core: toolPin('@voce-engine/core', CORE_VERSION), contracts: toolPin('@voce-engine/contracts', CONTRACTS_VERSION), scenario: { id: scenario.descriptor.manifest.packId, version: scenario.descriptor.manifest.version, digest: scenario.descriptor.packageDigest }, profile: profilePin(profile) }; }
|
|
@@ -546,12 +636,12 @@ async function compileCommand(args) {
|
|
|
546
636
|
const written = await writeBundle(outputPath, 'compiled', { id: result.caseSpec.id, revision: result.caseSpec.revision }, bundlePins(result.profile, result.loaded), payload);
|
|
547
637
|
return outputSummary(written.manifest, { promptGuard: guard.status, scenario: result.loaded.descriptor.manifest.packId });
|
|
548
638
|
}
|
|
549
|
-
async function semanticForRun(run, artifacts, profile) {
|
|
639
|
+
async function semanticForRun(run, artifacts, profile, criteria) {
|
|
550
640
|
const reviewer = new FixtureSemanticReviewer();
|
|
551
641
|
const inputHash = sha256({ runId: run.id, artifacts: artifacts.map((item) => item.contentHash).sort() });
|
|
552
642
|
const model = reviewer.version;
|
|
553
643
|
const adapter = { id: 'voce.fixture-semantic-reviewer-adapter', version: '1.0.0', digest: sha256({ id: 'voce.fixture-semantic-reviewer-adapter', version: '1.0.0' }) };
|
|
554
|
-
const requestBase = { schemaVersion: 'voce.semantic-review-request/v1alpha1', id: `semantic-${run.id}`, caseId: run.caseId, caseRevision: run.caseRevision, contextHash: run.contextHash, inputHash, outputArtifacts: artifacts, criteria: [{ id: 'fixture.semantic', kind: 'semantic_fidelity', importance: 'required', prompt: 'Offline fixture semantic proposal.' }], model, adapter, profile: { id: profile.id, version: profile.version, digest: profile.profileHash }, authorizationId: `semantic-auth-${run.id}`, destination: 'local', allowedEvidenceRegionIds: [], dataCategories: ['image'], budget: { schemaVersion: 'voce.budget/v1alpha1', id: `semantic-budget-${run.id}`, maximumCalls: 1, maximumRetries: 0, timeoutMs: 60_000 } };
|
|
644
|
+
const requestBase = { schemaVersion: 'voce.semantic-review-request/v1alpha1', id: `semantic-${run.id}`, caseId: run.caseId, caseRevision: run.caseRevision, contextHash: run.contextHash, inputHash, outputArtifacts: artifacts, criteria: criteria.length ? criteria : [{ id: 'fixture.semantic', kind: 'semantic_fidelity', importance: 'required', prompt: 'Offline fixture semantic proposal.' }], model, adapter, profile: { id: profile.id, version: profile.version, digest: profile.profileHash }, authorizationId: `semantic-auth-${run.id}`, destination: 'local', allowedEvidenceRegionIds: [], dataCategories: ['image'], budget: { schemaVersion: 'voce.budget/v1alpha1', id: `semantic-budget-${run.id}`, maximumCalls: 1, maximumRetries: 0, timeoutMs: 60_000 } };
|
|
555
645
|
const request = { ...requestBase, requestHash: computeSemanticReviewRequestHash(requestBase) };
|
|
556
646
|
const authorization = fixtureM6Authorization({ id: request.authorizationId, caseId: request.caseId, caseRevision: request.caseRevision, contextHash: request.contextHash, stepId: request.id, purpose: 'semantic_review', inputHash, artifactHashes: artifacts.map((item) => item.contentHash), adapter, profileDigest: request.profile.digest, destination: 'local', dataCategories: ['image'], budget: request.budget, modelId: model.id, modelVersion: model.version });
|
|
557
647
|
const execution = await executeSemanticReview(reviewer, request, authorization);
|
|
@@ -578,11 +668,13 @@ async function runCommand(args) {
|
|
|
578
668
|
const outputContract = { artifactKind: 'image', dataType: 'image', mediaTypes: ['image/png'], cardinality: { min: 1, max: 1 }, background: 'opaque', allowAlpha: false };
|
|
579
669
|
const structural = validateStructuralImage({ schemaVersion: 'voce.structural-validation-input/v1alpha1', id: `structural-${run.id}`, artifacts: [{ artifact: structuralArtifact, bytes: FIXTURE_M6_OPAQUE_PNG }], outputContract, expectedCardinality: { min: 1, max: 1 } });
|
|
580
670
|
const fixture = bundle.payload.fixture;
|
|
671
|
+
const scenario = bundle.payload.scenario?.effectiveScenario;
|
|
672
|
+
const semanticCriteria = scenario ? semanticCriteriaForScenario(scenario) : [];
|
|
581
673
|
let semanticProposal;
|
|
582
674
|
const semanticReceipts = [];
|
|
583
675
|
const reconciliation = [];
|
|
584
676
|
if (fixture?.semanticReview) {
|
|
585
|
-
const semantic = await semanticForRun(run, [structuralArtifact, ...artifacts], profileFor(String(bundle.payload.profile?.id ?? MOCK_IMAGE_PROFILE.id)));
|
|
677
|
+
const semantic = await semanticForRun(run, [structuralArtifact, ...artifacts], profileFor(String(bundle.payload.profile?.id ?? MOCK_IMAGE_PROFILE.id)), semanticCriteria);
|
|
586
678
|
semanticProposal = semantic.report;
|
|
587
679
|
semanticReceipts.push(semantic.receipt);
|
|
588
680
|
reconciliation.push(semantic.remote);
|
|
@@ -591,7 +683,7 @@ async function runCommand(args) {
|
|
|
591
683
|
const evaluation = compileEvaluationReport({ run: { id: run.id, technicalOutcome: run.technicalOutcome, state: run.state, contextHash: run.contextHash, pipelinePlanHash: run.pipelinePlanHash, promptArtifactHash: run.promptArtifactHash }, structural, semanticProposal, humanAcceptance: human, cleanup: result.cleanupReceipts, replay: { mode: 'artifact', status: 'available', code: 'REPLAY_AVAILABLE', artifactIds: artifacts.map((item) => item.id) }, artifacts: [structuralArtifact, ...artifacts], sourceHashes: { compiledBundle: bundle.manifest.semanticHash, executionRun: computeExecutionRunHash(run) } });
|
|
592
684
|
const traceModel = traceModelFromExecution({ run, receipts: [...result.receipts, ...semanticReceipts], cleanup: result.cleanupReceipts, reconciliation: [...result.remoteCallRuns, ...reconciliation], artifacts: [structuralArtifact, ...artifacts], structural, semanticProposal, humanAcceptance: human, budgets: input.pipelinePlan.steps.map((step) => step.budget), destinations: input.pipelinePlan.dataTransfers.map((item) => item.destination), constraintHash: input.constraintIR.deterministicSignature, referencePlanHash: input.referencePlan.planHash, promptHash: input.executionAuthorization.promptArtifactHash, warnings: result.reasons });
|
|
593
685
|
const acceptance = isObject(bundle.payload.acceptance) ? clone(bundle.payload.acceptance) : {};
|
|
594
|
-
const runEvidence = { executionStatus: result.status, executionCode: result.code, receiptCount: result.receipts.length + semanticReceipts.length, receiptAdapters: [...result.receipts, ...semanticReceipts].map((item) => item.adapterId).sort(compare), artifactMediaTypes: artifacts.map((item) => item.mediaType).sort(compare), artifactRoles: artifacts.map((item) => item.role).sort(compare), structuralStatus: structural.status, semanticStatus: semanticProposal?.status ?? 'absent', humanStatus: human?.status ?? 'absent', evaluationStatus: evaluation.status, traceModelPresent: true, traceModelHash: traceModel.modelHash };
|
|
686
|
+
const runEvidence = { executionStatus: result.status, executionCode: result.code, receiptCount: result.receipts.length + semanticReceipts.length, receiptAdapters: [...result.receipts, ...semanticReceipts].map((item) => item.adapterId).sort(compare), artifactMediaTypes: artifacts.map((item) => item.mediaType).sort(compare), artifactRoles: artifacts.map((item) => item.role).sort(compare), structuralStatus: structural.status, semanticStatus: semanticProposal?.status ?? 'absent', semanticCriterionIds: semanticProposal?.findings.map((item) => item.criterionId).sort(compare) ?? [], semanticFindingStatuses: semanticProposal?.findings.map((item) => item.status).sort(compare) ?? [], humanStatus: human?.status ?? 'absent', evaluationStatus: evaluation.status, traceModelPresent: true, traceModelHash: traceModel.modelHash };
|
|
595
687
|
acceptance.run = runEvidence;
|
|
596
688
|
const runAssertions = fixture ? assertFixtureAssertions(fixture, acceptance, 'run') : [];
|
|
597
689
|
acceptance.assertions = [...(Array.isArray(acceptance.assertions) ? acceptance.assertions : []), ...runAssertions].sort((left, right) => compare(String(left.id), String(right.id)));
|
|
@@ -615,15 +707,16 @@ async function packTestCommand(source) {
|
|
|
615
707
|
const resolution = registry.resolve({ root: { packId: loaded.descriptor.manifest.packId, versionRange: loaded.descriptor.manifest.version }, extensions: [] });
|
|
616
708
|
if (resolution.status !== 'resolved')
|
|
617
709
|
throw new CliError('PACK_RESOLUTION_BLOCKED', 'ScenarioPack resolution is blocked.', EXIT.contract);
|
|
710
|
+
const semanticCriteria = semanticCriteriaForScenario(resolution.effectiveScenario);
|
|
618
711
|
const results = [];
|
|
619
712
|
for (const fixture of loaded.fixtures) {
|
|
620
713
|
const profile = profileFor(fixture.profileId ?? MOCK_IMAGE_PROFILE.id);
|
|
621
|
-
const caseSpec = defaultCase({ id: fixture.id }, loaded.descriptor.manifest.packId);
|
|
714
|
+
const caseSpec = defaultCase({ id: fixture.id }, loaded.descriptor.manifest.packId, loaded.descriptor.manifest.version);
|
|
622
715
|
const input = fixtureM5ExecutionInput(profile);
|
|
623
716
|
const prompt = fixtureM5Candidate(fixtureM5PromptIR(profile));
|
|
624
717
|
const guard = guardPromptCandidate(fixtureM5GuardInput(fixtureM5PromptIR(profile), prompt));
|
|
625
|
-
const coreProbes = Object.fromEntries((fixture.coreProbes ?? []).sort((left, right) => compare(left.id, right.id)).map((probe) => [probe.id, coreProbeEvidence(probe, resolution, caseSpec)]));
|
|
626
|
-
const acceptance = { resolution: { status: resolution.status, selectedCount: resolution.report.selected.length, effectiveScenarioHash: resolution.effectiveScenario.effectiveScenarioHash }, reference: referenceEvidence(fixture, resolution, caseSpec), coreProbes, compile: { constraintHash: input.constraintIR.deterministicSignature, referencePlanHash: input.referencePlan.planHash, pipelinePlanHash: input.pipelinePlan.planHash, promptGuard: guard.status } };
|
|
718
|
+
const coreProbes = Object.fromEntries((fixture.coreProbes ?? []).sort((left, right) => compare(left.id, right.id)).map((probe) => [probe.id, coreProbeEvidence(probe, resolution, caseSpec, profile)]));
|
|
719
|
+
const acceptance = { resolution: { status: resolution.status, selectedCount: resolution.report.selected.length, effectiveScenarioHash: resolution.effectiveScenario.effectiveScenarioHash }, scenario: scenarioEvidence(resolution.effectiveScenario), reference: referenceEvidence(fixture, resolution, caseSpec), coreProbes, compile: { constraintHash: input.constraintIR.deterministicSignature, referencePlanHash: input.referencePlan.planHash, pipelinePlanHash: input.pipelinePlan.planHash, promptGuard: guard.status } };
|
|
627
720
|
const result = executeOffline(input);
|
|
628
721
|
const run = result.run ?? result.executionRun;
|
|
629
722
|
if (!run)
|
|
@@ -636,7 +729,7 @@ async function packTestCommand(source) {
|
|
|
636
729
|
const semanticReceipts = [];
|
|
637
730
|
const reconciliation = [];
|
|
638
731
|
if (fixture.semanticReview) {
|
|
639
|
-
const semantic = await semanticForRun(run, [structuralArtifact, ...artifacts], profile);
|
|
732
|
+
const semantic = await semanticForRun(run, [structuralArtifact, ...artifacts], profile, semanticCriteria);
|
|
640
733
|
semanticProposal = semantic.report;
|
|
641
734
|
semanticReceipts.push(semantic.receipt);
|
|
642
735
|
reconciliation.push(semantic.remote);
|
|
@@ -645,7 +738,7 @@ async function packTestCommand(source) {
|
|
|
645
738
|
const compiledHash = sha256({ packId: loaded.descriptor.manifest.packId, fixtureId: fixture.id });
|
|
646
739
|
const evaluation = compileEvaluationReport({ run: { id: run.id, technicalOutcome: run.technicalOutcome, state: run.state, contextHash: run.contextHash, pipelinePlanHash: run.pipelinePlanHash, promptArtifactHash: run.promptArtifactHash }, structural, semanticProposal, humanAcceptance: human, cleanup: result.cleanupReceipts, replay: { mode: 'artifact', status: 'available', code: 'REPLAY_AVAILABLE', artifactIds: artifacts.map((item) => item.id) }, artifacts: [structuralArtifact, ...artifacts], sourceHashes: { compiledBundle: compiledHash, executionRun: computeExecutionRunHash(run) } });
|
|
647
740
|
const traceModel = traceModelFromExecution({ run, receipts: [...result.receipts, ...semanticReceipts], cleanup: result.cleanupReceipts, reconciliation: [...result.remoteCallRuns, ...reconciliation], artifacts: [structuralArtifact, ...artifacts], structural, semanticProposal, humanAcceptance: human, budgets: input.pipelinePlan.steps.map((step) => step.budget), destinations: input.pipelinePlan.dataTransfers.map((item) => item.destination), constraintHash: input.constraintIR.deterministicSignature, referencePlanHash: input.referencePlan.planHash, promptHash: input.executionAuthorization.promptArtifactHash, warnings: result.reasons });
|
|
648
|
-
acceptance.run = { executionStatus: result.status, executionCode: result.code, receiptCount: result.receipts.length + semanticReceipts.length, receiptAdapters: [...result.receipts, ...semanticReceipts].map((item) => item.adapterId).sort(compare), artifactMediaTypes: artifacts.map((item) => item.mediaType).sort(compare), artifactRoles: artifacts.map((item) => item.role).sort(compare), structuralStatus: structural.status, semanticStatus: semanticProposal?.status ?? 'absent', humanStatus: human?.status ?? 'absent', evaluationStatus: evaluation.status, traceModelPresent: true, traceModelHash: traceModel.modelHash };
|
|
741
|
+
acceptance.run = { executionStatus: result.status, executionCode: result.code, receiptCount: result.receipts.length + semanticReceipts.length, receiptAdapters: [...result.receipts, ...semanticReceipts].map((item) => item.adapterId).sort(compare), artifactMediaTypes: artifacts.map((item) => item.mediaType).sort(compare), artifactRoles: artifacts.map((item) => item.role).sort(compare), structuralStatus: structural.status, semanticStatus: semanticProposal?.status ?? 'absent', semanticCriterionIds: semanticProposal?.findings.map((item) => item.criterionId).sort(compare) ?? [], semanticFindingStatuses: semanticProposal?.findings.map((item) => item.status).sort(compare) ?? [], humanStatus: human?.status ?? 'absent', evaluationStatus: evaluation.status, traceModelPresent: true, traceModelHash: traceModel.modelHash };
|
|
649
742
|
const assertions = assertFixtureAssertions(fixture, acceptance, 'all');
|
|
650
743
|
const expected = fixture.expectedStatus ?? 'completed';
|
|
651
744
|
const accepted = result.status === expected || (expected === 'needs_review' && result.status === 'completed');
|
|
@@ -654,7 +747,23 @@ async function packTestCommand(source) {
|
|
|
654
747
|
const failed = results.some((item) => item.status === 'failed');
|
|
655
748
|
return { status: failed ? 'failed' : 'passed', packId: loaded.descriptor.manifest.packId, resolutionHash: resolution.report.reportHash, lockHash: resolution.lock.lockHash, effectiveScenarioHash: resolution.effectiveScenario.effectiveScenarioHash, fixtureCount: results.length, results };
|
|
656
749
|
}
|
|
657
|
-
async function doctorCommand() {
|
|
750
|
+
async function doctorCommand() {
|
|
751
|
+
const major = Number(process.versions.node.split('.')[0]);
|
|
752
|
+
return {
|
|
753
|
+
status: major >= 20 ? 'ok' : 'blocked',
|
|
754
|
+
node: { version: process.versions.node, supported: major >= 20 },
|
|
755
|
+
contracts: { schemaVersion: BUNDLE_MANIFEST_SCHEMA_VERSION, packageVersion: CONTRACTS_VERSION },
|
|
756
|
+
paths: { explicitOnly: true },
|
|
757
|
+
providers: { default: 'disabled', mock: 'offline', networkProbe: false },
|
|
758
|
+
authProbe: { inspected: false },
|
|
759
|
+
visualComposition: {
|
|
760
|
+
catalogId: VISUAL_COMPOSITION_CATALOG.id,
|
|
761
|
+
catalogHash: computeVisualCompositionCatalogHash(),
|
|
762
|
+
pathCount: VISUAL_COMPOSITION_CATALOG.paths.length,
|
|
763
|
+
presetCount: VISUAL_COMPOSITION_CATALOG.presets.length,
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
}
|
|
658
767
|
function required(args, key) { if (!args[key])
|
|
659
768
|
throw new CliError('ARGUMENT_MISSING', `Missing --${key}.`, EXIT.usage); return args[key]; }
|
|
660
769
|
function help() { return `voce ${CLI_VERSION}\n\nOffline-first explicit-path CLI.\n\nCommands:\n voce pack inspect --source <path> [--json]\n voce pack validate --source <path> [--json]\n voce pack test --source <path> [--json]\n voce case compile --case <file> --scenario <path> --profile <file> --out <dir> [--json]\n voce case run --bundle <dir> --provider mock --out <dir> [--json]\n voce trace render --bundle <dir> --out <html> [--json]\n voce compare --before <dir> --after <dir> [--out <file>] [--json]\n voce doctor [--json]\n\nExit codes: 0 success, 2 usage, 3 input, 4 contract/hash, 5 offline/provider, 6 output, 7 internal.`; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voce-engine/cli",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.4",
|
|
4
4
|
"description": "Offline-first command line tools for the VOCE v0.1 release candidate.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"tag": "next"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@voce-engine/contracts": "0.1.0-rc.
|
|
40
|
-
"@voce-engine/core": "0.1.0-rc.
|
|
41
|
-
"@voce-engine/testkit": "0.1.0-rc.
|
|
39
|
+
"@voce-engine/contracts": "0.1.0-rc.4",
|
|
40
|
+
"@voce-engine/core": "0.1.0-rc.4",
|
|
41
|
+
"@voce-engine/testkit": "0.1.0-rc.4"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc -b"
|