@neurcode/action 0.2.1 → 0.2.2

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/dist/index.js CHANGED
@@ -35282,7 +35282,13 @@ function wrappy (fn, cb) {
35282
35282
  "use strict";
35283
35283
 
35284
35284
  Object.defineProperty(exports, "__esModule", ({ value: true }));
35285
- exports.CLI_JSON_CONTRACT_VERSION = void 0;
35285
+ exports.RUNTIME_COMPATIBILITY_CONTRACT_VERSION = exports.RUNTIME_COMPATIBILITY_CONTRACT_ID = exports.CLI_JSON_CONTRACT_VERSION = void 0;
35286
+ exports.compareSemver = compareSemver;
35287
+ exports.isSemverAtLeast = isSemverAtLeast;
35288
+ exports.getMinimumCompatiblePeerVersion = getMinimumCompatiblePeerVersion;
35289
+ exports.getRuntimeMinimumPeerVersionMatrix = getRuntimeMinimumPeerVersionMatrix;
35290
+ exports.buildRuntimeCompatibilityDescriptor = buildRuntimeCompatibilityDescriptor;
35291
+ exports.evaluateRuntimePeerCompatibility = evaluateRuntimePeerCompatibility;
35286
35292
  exports.parseCliPlanJsonPayload = parseCliPlanJsonPayload;
35287
35293
  exports.parseCliApplyJsonPayload = parseCliApplyJsonPayload;
35288
35294
  exports.parseCliVerifyJsonPayload = parseCliVerifyJsonPayload;
@@ -35291,7 +35297,24 @@ exports.parseCliShipJsonPayload = parseCliShipJsonPayload;
35291
35297
  exports.parseCliShipRunsJsonPayload = parseCliShipRunsJsonPayload;
35292
35298
  exports.parseCliShipResumeJsonPayload = parseCliShipResumeJsonPayload;
35293
35299
  exports.parseCliShipAttestationVerifyJsonPayload = parseCliShipAttestationVerifyJsonPayload;
35300
+ exports.parseCliCompatJsonPayload = parseCliCompatJsonPayload;
35294
35301
  exports.CLI_JSON_CONTRACT_VERSION = '2026-04-04';
35302
+ exports.RUNTIME_COMPATIBILITY_CONTRACT_ID = 'neurcode-runtime-compatibility';
35303
+ exports.RUNTIME_COMPATIBILITY_CONTRACT_VERSION = '2026-04-04';
35304
+ const RUNTIME_MINIMUM_PEER_VERSIONS = {
35305
+ cli: {
35306
+ action: '0.2.1',
35307
+ api: '0.2.0',
35308
+ },
35309
+ action: {
35310
+ cli: '0.9.35',
35311
+ api: '0.2.0',
35312
+ },
35313
+ api: {
35314
+ cli: '0.9.35',
35315
+ action: '0.2.1',
35316
+ },
35317
+ };
35295
35318
  function asRecord(value, label) {
35296
35319
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
35297
35320
  throw new Error(`${label}: expected object`);
@@ -35347,6 +35370,125 @@ function asContractVersion(record) {
35347
35370
  }
35348
35371
  return value;
35349
35372
  }
35373
+ function asRuntimeComponent(record, key, label) {
35374
+ const value = asString(record, key, label);
35375
+ if (value === 'cli' || value === 'action' || value === 'api') {
35376
+ return value;
35377
+ }
35378
+ throw new Error(`${label}: expected ${key}:("cli"|"action"|"api")`);
35379
+ }
35380
+ function parseRuntimeMinimumPeerVersions(value, label) {
35381
+ if (value === undefined || value === null)
35382
+ return {};
35383
+ const record = asRecord(value, `${label}.minimumPeerVersions`);
35384
+ const next = {};
35385
+ for (const component of ['cli', 'action', 'api']) {
35386
+ const componentValue = record[component];
35387
+ if (componentValue === undefined)
35388
+ continue;
35389
+ if (typeof componentValue !== 'string' || !componentValue.trim()) {
35390
+ throw new Error(`${label}.minimumPeerVersions: expected ${component}:string`);
35391
+ }
35392
+ next[component] = componentValue.trim();
35393
+ }
35394
+ return next;
35395
+ }
35396
+ function parseRuntimeCompatibilityDescriptor(value, label) {
35397
+ const record = asRecord(value, `${label}.compatibility`);
35398
+ return {
35399
+ contractId: asString(record, 'contractId', `${label}.compatibility`),
35400
+ runtimeContractVersion: asString(record, 'runtimeContractVersion', `${label}.compatibility`),
35401
+ cliJsonContractVersion: asString(record, 'cliJsonContractVersion', `${label}.compatibility`),
35402
+ component: asRuntimeComponent(record, 'component', `${label}.compatibility`),
35403
+ componentVersion: asString(record, 'componentVersion', `${label}.compatibility`),
35404
+ minimumPeerVersions: parseRuntimeMinimumPeerVersions(record.minimumPeerVersions, `${label}.compatibility`),
35405
+ };
35406
+ }
35407
+ function parseSemver(value) {
35408
+ const normalized = value.trim().replace(/^v/i, '').split('+')[0].split('-')[0];
35409
+ const match = normalized.match(/^(\d+)\.(\d+)\.(\d+)$/);
35410
+ if (!match)
35411
+ return null;
35412
+ const major = Number(match[1]);
35413
+ const minor = Number(match[2]);
35414
+ const patch = Number(match[3]);
35415
+ if (!Number.isFinite(major) || !Number.isFinite(minor) || !Number.isFinite(patch)) {
35416
+ return null;
35417
+ }
35418
+ return [major, minor, patch];
35419
+ }
35420
+ function compareSemver(left, right) {
35421
+ const l = parseSemver(left);
35422
+ const r = parseSemver(right);
35423
+ if (!l || !r)
35424
+ return null;
35425
+ for (let idx = 0; idx < 3; idx += 1) {
35426
+ if (l[idx] > r[idx])
35427
+ return 1;
35428
+ if (l[idx] < r[idx])
35429
+ return -1;
35430
+ }
35431
+ return 0;
35432
+ }
35433
+ function isSemverAtLeast(actual, minimum) {
35434
+ const compare = compareSemver(actual, minimum);
35435
+ if (compare === null)
35436
+ return null;
35437
+ return compare >= 0;
35438
+ }
35439
+ function getMinimumCompatiblePeerVersion(component, peer) {
35440
+ return RUNTIME_MINIMUM_PEER_VERSIONS[component][peer];
35441
+ }
35442
+ function getRuntimeMinimumPeerVersionMatrix() {
35443
+ return {
35444
+ cli: { ...RUNTIME_MINIMUM_PEER_VERSIONS.cli },
35445
+ action: { ...RUNTIME_MINIMUM_PEER_VERSIONS.action },
35446
+ api: { ...RUNTIME_MINIMUM_PEER_VERSIONS.api },
35447
+ };
35448
+ }
35449
+ function buildRuntimeCompatibilityDescriptor(component, componentVersion) {
35450
+ return {
35451
+ contractId: exports.RUNTIME_COMPATIBILITY_CONTRACT_ID,
35452
+ runtimeContractVersion: exports.RUNTIME_COMPATIBILITY_CONTRACT_VERSION,
35453
+ cliJsonContractVersion: exports.CLI_JSON_CONTRACT_VERSION,
35454
+ component,
35455
+ componentVersion,
35456
+ minimumPeerVersions: { ...RUNTIME_MINIMUM_PEER_VERSIONS[component] },
35457
+ };
35458
+ }
35459
+ function evaluateRuntimePeerCompatibility(versions) {
35460
+ const issues = [];
35461
+ for (const component of ['cli', 'action', 'api']) {
35462
+ for (const peer of ['cli', 'action', 'api']) {
35463
+ if (peer === component)
35464
+ continue;
35465
+ const required = getMinimumCompatiblePeerVersion(component, peer);
35466
+ if (!required)
35467
+ continue;
35468
+ const actual = versions[peer];
35469
+ const compatible = isSemverAtLeast(actual, required);
35470
+ if (compatible === null) {
35471
+ issues.push({
35472
+ component,
35473
+ peer,
35474
+ required,
35475
+ actual,
35476
+ code: 'UNPARSABLE_VERSION',
35477
+ });
35478
+ }
35479
+ else if (!compatible) {
35480
+ issues.push({
35481
+ component,
35482
+ peer,
35483
+ required,
35484
+ actual,
35485
+ code: 'VERSION_BELOW_MINIMUM',
35486
+ });
35487
+ }
35488
+ }
35489
+ }
35490
+ return issues;
35491
+ }
35350
35492
  function parseCliPlanJsonPayload(value, label = 'plan') {
35351
35493
  const record = asRecord(value, label);
35352
35494
  return {
@@ -35446,6 +35588,22 @@ function parseCliShipAttestationVerifyJsonPayload(value, label = 'ship-attestati
35446
35588
  digest: asOptionalRecord(record, 'digest', label),
35447
35589
  };
35448
35590
  }
35591
+ function parseCliCompatJsonPayload(value, label = 'compat') {
35592
+ const record = asRecord(value, label);
35593
+ const component = asString(record, 'component', label);
35594
+ if (component !== 'cli') {
35595
+ throw new Error(`${label}: expected component:"cli"`);
35596
+ }
35597
+ return {
35598
+ ...record,
35599
+ contractVersion: asContractVersion(record),
35600
+ success: asBoolean(record, 'success', label),
35601
+ timestamp: asString(record, 'timestamp', label),
35602
+ component: 'cli',
35603
+ componentVersion: asString(record, 'componentVersion', label),
35604
+ compatibility: parseRuntimeCompatibilityDescriptor(record.compatibility, label),
35605
+ };
35606
+ }
35449
35607
  //# sourceMappingURL=index.js.map
35450
35608
 
35451
35609
  /***/ }),
@@ -35494,8 +35652,11 @@ const exec = __importStar(__nccwpck_require__(7167));
35494
35652
  const github = __importStar(__nccwpck_require__(5251));
35495
35653
  const contracts_1 = __nccwpck_require__(7239);
35496
35654
  const fs_1 = __nccwpck_require__(9896);
35655
+ const http = __importStar(__nccwpck_require__(8611));
35656
+ const https = __importStar(__nccwpck_require__(5692));
35497
35657
  const path_1 = __nccwpck_require__(6928);
35498
35658
  const verify_mode_1 = __nccwpck_require__(4288);
35659
+ const runtime_compat_1 = __nccwpck_require__(5654);
35499
35660
  const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
35500
35661
  const NON_FAST_FORWARD_PATTERNS = [
35501
35662
  'non-fast-forward',
@@ -35543,6 +35704,17 @@ function parseBoolean(input, fallback) {
35543
35704
  return false;
35544
35705
  return fallback;
35545
35706
  }
35707
+ function parseBooleanOrUndefined(input) {
35708
+ if (input === undefined || input === null || input.trim() === '') {
35709
+ return undefined;
35710
+ }
35711
+ const normalized = input.trim().toLowerCase();
35712
+ if (['1', 'true', 'yes', 'on'].includes(normalized))
35713
+ return true;
35714
+ if (['0', 'false', 'no', 'off'].includes(normalized))
35715
+ return false;
35716
+ return undefined;
35717
+ }
35546
35718
  function parsePositiveInt(input, fallback, min, max) {
35547
35719
  const parsed = Number(input);
35548
35720
  if (!Number.isFinite(parsed))
@@ -35664,6 +35836,56 @@ function readJsonFile(path) {
35664
35836
  return null;
35665
35837
  }
35666
35838
  }
35839
+ function assertStrictDeterministicArtifacts(input) {
35840
+ if (!input.strictMode)
35841
+ return;
35842
+ const errors = [];
35843
+ if (!input.supportsCompiledPolicy) {
35844
+ errors.push('Current CLI does not support --compiled-policy, but strict mode requires a compiled policy artifact.');
35845
+ }
35846
+ if (!input.supportsChangeContract) {
35847
+ errors.push('Current CLI does not support --change-contract, but strict mode requires a change contract artifact.');
35848
+ }
35849
+ const compiledPath = input.compiledPolicyPath?.trim();
35850
+ if (!compiledPath) {
35851
+ errors.push('Missing compiled policy artifact path in strict mode (compiled_policy_path).');
35852
+ }
35853
+ else {
35854
+ const absoluteCompiledPath = (0, path_1.resolve)(input.cwd, compiledPath);
35855
+ const compiled = readJsonFile(absoluteCompiledPath);
35856
+ if (!compiled) {
35857
+ errors.push(`Compiled policy artifact not found or invalid JSON: ${compiledPath}`);
35858
+ }
35859
+ else {
35860
+ const hasFingerprint = typeof compiled.fingerprint === 'string' && compiled.fingerprint.trim().length > 0;
35861
+ const hasRules = Array.isArray(compiled.rules) || Array.isArray(compiled.statements);
35862
+ if (!hasFingerprint && !hasRules) {
35863
+ errors.push(`Compiled policy artifact appears invalid (missing fingerprint/rules): ${compiledPath}`);
35864
+ }
35865
+ }
35866
+ }
35867
+ const contractPath = input.changeContractPath?.trim();
35868
+ if (!contractPath) {
35869
+ errors.push('Missing change contract artifact path in strict mode (change_contract_path).');
35870
+ }
35871
+ else {
35872
+ const absoluteContractPath = (0, path_1.resolve)(input.cwd, contractPath);
35873
+ const contract = readJsonFile(absoluteContractPath);
35874
+ if (!contract) {
35875
+ errors.push(`Change contract artifact not found or invalid JSON: ${contractPath}`);
35876
+ }
35877
+ else {
35878
+ const hasContractId = typeof contract.contractId === 'string' && contract.contractId.trim().length > 0;
35879
+ const hasPlanId = typeof contract.planId === 'string' && contract.planId.trim().length > 0;
35880
+ if (!hasContractId || !hasPlanId) {
35881
+ errors.push(`Change contract artifact appears invalid (missing contractId/planId): ${contractPath}`);
35882
+ }
35883
+ }
35884
+ }
35885
+ if (errors.length > 0) {
35886
+ throw new Error(`Strict enterprise mode requires deterministic compiled-policy + change-contract artifacts:\n- ${errors.join('\n- ')}`);
35887
+ }
35888
+ }
35667
35889
  function detectProjectOrgId(cwd) {
35668
35890
  const statePath = (0, path_1.join)(cwd, '.neurcode', 'config.json');
35669
35891
  const state = readJsonFile(statePath);
@@ -35732,6 +35954,154 @@ function extractLastJsonObject(output) {
35732
35954
  }
35733
35955
  return null;
35734
35956
  }
35957
+ function resolveActionPackageVersion() {
35958
+ const fromEnv = process.env.NEURCODE_ACTION_VERSION || process.env.npm_package_version;
35959
+ if (fromEnv && fromEnv.trim()) {
35960
+ return fromEnv.trim();
35961
+ }
35962
+ const candidates = [
35963
+ (0, path_1.resolve)(__dirname, '../package.json'),
35964
+ (0, path_1.resolve)(process.cwd(), 'package.json'),
35965
+ ];
35966
+ for (const candidate of candidates) {
35967
+ try {
35968
+ if (!(0, fs_1.existsSync)(candidate))
35969
+ continue;
35970
+ const parsed = JSON.parse((0, fs_1.readFileSync)(candidate, 'utf-8'));
35971
+ const version = parsed?.version;
35972
+ if (typeof version === 'string' && version.trim()) {
35973
+ return version.trim();
35974
+ }
35975
+ }
35976
+ catch {
35977
+ // Ignore and continue to next candidate.
35978
+ }
35979
+ }
35980
+ return '0.0.0';
35981
+ }
35982
+ function normalizeApiBaseUrl(value) {
35983
+ return value.trim().replace(/\/+$/, '');
35984
+ }
35985
+ function requestJson(url, timeoutMs) {
35986
+ return new Promise((resolvePromise, rejectPromise) => {
35987
+ let target;
35988
+ try {
35989
+ target = new URL(url);
35990
+ }
35991
+ catch (error) {
35992
+ rejectPromise(new Error(`Invalid API URL: ${url} (${error instanceof Error ? error.message : String(error)})`));
35993
+ return;
35994
+ }
35995
+ const transport = target.protocol === 'http:' ? http : https;
35996
+ const request = transport.request({
35997
+ protocol: target.protocol,
35998
+ hostname: target.hostname,
35999
+ port: target.port || undefined,
36000
+ path: `${target.pathname}${target.search}`,
36001
+ method: 'GET',
36002
+ headers: {
36003
+ Accept: 'application/json',
36004
+ 'User-Agent': 'neurcode-action/runtime-compat',
36005
+ },
36006
+ }, (response) => {
36007
+ let body = '';
36008
+ response.setEncoding('utf8');
36009
+ response.on('data', (chunk) => {
36010
+ body += chunk;
36011
+ });
36012
+ response.on('end', () => {
36013
+ let payload = null;
36014
+ try {
36015
+ payload = body ? JSON.parse(body) : null;
36016
+ }
36017
+ catch {
36018
+ payload = null;
36019
+ }
36020
+ resolvePromise({
36021
+ statusCode: response.statusCode || 0,
36022
+ body,
36023
+ payload,
36024
+ url,
36025
+ });
36026
+ });
36027
+ });
36028
+ request.on('error', (error) => {
36029
+ rejectPromise(new Error(`Request failed for ${url}: ${error.message}`));
36030
+ });
36031
+ request.setTimeout(timeoutMs, () => {
36032
+ request.destroy(new Error(`Request timed out after ${timeoutMs}ms`));
36033
+ });
36034
+ request.end();
36035
+ });
36036
+ }
36037
+ async function resolveApiHealthPayload(apiBaseUrl, timeoutMs) {
36038
+ const baseUrl = normalizeApiBaseUrl(apiBaseUrl);
36039
+ const candidates = [`${baseUrl}/api/v1/health`, `${baseUrl}/health`];
36040
+ const failures = [];
36041
+ for (const url of candidates) {
36042
+ try {
36043
+ const response = await requestJson(url, timeoutMs);
36044
+ const payload = response.payload;
36045
+ const statusValue = payload && typeof payload === 'object' && !Array.isArray(payload)
36046
+ ? payload.status
36047
+ : null;
36048
+ if (response.statusCode >= 200 && response.statusCode < 300 && statusValue === 'ok') {
36049
+ return payload;
36050
+ }
36051
+ failures.push(`${url} returned ${response.statusCode}`);
36052
+ }
36053
+ catch (error) {
36054
+ failures.push(error instanceof Error ? error.message : String(error));
36055
+ }
36056
+ }
36057
+ throw new Error(`API compatibility probe failed (${failures.join(' | ')}).`);
36058
+ }
36059
+ async function runCompatibilityHandshake(input) {
36060
+ const actionVersion = resolveActionPackageVersion();
36061
+ const compatCommand = withCliCommandTimeout(input.cli, ['compat', '--json'], input.timeoutMinutes);
36062
+ const compatResult = await runCommand(compatCommand.cmd, compatCommand.args, { cwd: input.cwd });
36063
+ if (compatResult.exitCode !== 0) {
36064
+ throw new Error(`Compatibility handshake failed: unable to run "neurcode compat --json". ` +
36065
+ `Output: ${stripAnsi(compatResult.output).trim() || 'no output'}`);
36066
+ }
36067
+ const cliCompatPayload = extractLastJsonObject(compatResult.output);
36068
+ if (!cliCompatPayload) {
36069
+ throw new Error('Compatibility handshake failed: CLI compat output did not contain JSON.');
36070
+ }
36071
+ const cliCompatibility = (0, runtime_compat_1.parseCliCompatibilityPayload)(cliCompatPayload);
36072
+ let apiCompatibility = null;
36073
+ let apiVersion;
36074
+ if (input.apiUrl) {
36075
+ const apiHealthPayload = await resolveApiHealthPayload(input.apiUrl, 10000);
36076
+ apiCompatibility = (0, runtime_compat_1.parseApiHealthCompatibilityPayload)(apiHealthPayload);
36077
+ if (!apiCompatibility && input.requireApiCompatibility) {
36078
+ throw new Error('Compatibility handshake failed: API health payload does not include compatibility metadata.');
36079
+ }
36080
+ if (apiCompatibility?.componentVersion) {
36081
+ apiVersion = apiCompatibility.componentVersion;
36082
+ }
36083
+ else if (apiHealthPayload
36084
+ && typeof apiHealthPayload === 'object'
36085
+ && !Array.isArray(apiHealthPayload)
36086
+ && typeof apiHealthPayload.version === 'string') {
36087
+ apiVersion = apiHealthPayload.version;
36088
+ }
36089
+ }
36090
+ const errors = (0, runtime_compat_1.validateActionHandshake)({
36091
+ actionVersion,
36092
+ cliCompatibility,
36093
+ apiCompatibility,
36094
+ requireApiCompatibility: input.requireApiCompatibility && Boolean(input.apiUrl),
36095
+ });
36096
+ if (errors.length > 0) {
36097
+ throw new Error(`Compatibility handshake failed:\n- ${errors.join('\n- ')}`);
36098
+ }
36099
+ return {
36100
+ actionVersion,
36101
+ cliVersion: cliCompatibility.componentVersion,
36102
+ apiVersion,
36103
+ };
36104
+ }
35735
36105
  function parseVerifyResult(output) {
35736
36106
  const parsed = extractLastJsonObject(output);
35737
36107
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
@@ -36140,6 +36510,24 @@ async function ensureCliInstalled(input) {
36140
36510
  function withCliCommandTimeout(cli, args, timeoutMinutes) {
36141
36511
  return withCommandTimeout(cli.cmd, [...cli.argsPrefix, ...args], timeoutMinutes);
36142
36512
  }
36513
+ async function resolveVerifyCapabilities(cli, cwd) {
36514
+ const helpCommand = withCliCommandTimeout(cli, ['verify', '--help'], 2);
36515
+ const helpResult = await runCommand(helpCommand.cmd, helpCommand.args, { cwd });
36516
+ if (helpResult.exitCode !== 0) {
36517
+ core.warning('Unable to detect verify flag capabilities; defaulting to full verify flag set.');
36518
+ return {
36519
+ supportsCompiledPolicy: true,
36520
+ supportsChangeContract: true,
36521
+ supportsEnforceChangeContract: true,
36522
+ };
36523
+ }
36524
+ const helpText = stripAnsi(helpResult.output).toLowerCase();
36525
+ return {
36526
+ supportsCompiledPolicy: helpText.includes('--compiled-policy'),
36527
+ supportsChangeContract: helpText.includes('--change-contract'),
36528
+ supportsEnforceChangeContract: helpText.includes('--enforce-change-contract'),
36529
+ };
36530
+ }
36143
36531
  async function resolveCliInvocation(cwd) {
36144
36532
  const direct = await runCommand('neurcode', ['--version'], { cwd });
36145
36533
  if (direct.exitCode === 0) {
@@ -36353,10 +36741,15 @@ async function run() {
36353
36741
  const cliInstallSource = parseCliInstallSource(core.getInput('neurcode_cli_source'));
36354
36742
  const cliWorkspacePath = core.getInput('neurcode_cli_workspace_path') || 'packages/cli';
36355
36743
  const verifyTimeoutMinutes = parsePositiveInt(core.getInput('verify_timeout_minutes'), 8, 1, 120);
36744
+ const enforceCompatibilityHandshake = parseBoolean(core.getInput('enforce_compatibility_handshake'), true);
36745
+ const requireApiCompatibilityHandshake = parseBoolean(core.getInput('require_api_compatibility_handshake'), true);
36746
+ const compatibilityProbeTimeoutMinutes = parsePositiveInt(core.getInput('compatibility_probe_timeout_minutes'), 2, 1, 15);
36747
+ const configuredApiUrl = (process.env.NEURCODE_API_URL || '').trim();
36356
36748
  const verifyPolicyOnly = parseBoolean(core.getInput('verify_policy_only'), false);
36749
+ const enterpriseMode = parseBoolean(core.getInput('enterprise_mode'), true);
36357
36750
  const compiledPolicyPath = (core.getInput('compiled_policy_path') || 'neurcode.policy.compiled.json').trim();
36358
36751
  const changeContractPath = (core.getInput('change_contract_path') || '.neurcode/change-contract.json').trim();
36359
- const enforceChangeContract = parseBoolean(core.getInput('enforce_change_contract'), false);
36752
+ const enforceChangeContractOverride = parseBooleanOrUndefined(core.getInput('enforce_change_contract'));
36360
36753
  const changedFilesOnly = parseBoolean(core.getInput('changed_files_only'), false);
36361
36754
  const autoRemediate = parseBoolean(core.getInput('auto_remediate'), false);
36362
36755
  const remediationGoalInput = core.getInput('remediation_goal') || '';
@@ -36370,7 +36763,15 @@ async function run() {
36370
36763
  const shipNetworkRetryDelaySeconds = parsePositiveInt(core.getInput('ship_network_retry_delay_seconds'), 5, 0, 30);
36371
36764
  const remediationCommit = parseBoolean(core.getInput('remediation_commit'), false);
36372
36765
  const remediationPush = parseBoolean(core.getInput('remediation_push'), false);
36373
- const enforceStrictVerification = parseBoolean(core.getInput('enforce_strict_verification'), false);
36766
+ const enforceStrictVerificationOverride = parseBooleanOrUndefined(core.getInput('enforce_strict_verification'));
36767
+ const enterpriseEnforcement = (0, verify_mode_1.resolveEnterpriseEnforcement)({
36768
+ enterpriseMode,
36769
+ verifyPolicyOnly,
36770
+ enforceChangeContract: enforceChangeContractOverride,
36771
+ enforceStrictVerification: enforceStrictVerificationOverride,
36772
+ });
36773
+ let enforceChangeContract = enterpriseEnforcement.enforceChangeContract;
36774
+ const enforceStrictVerification = enterpriseEnforcement.enforceStrictVerification;
36374
36775
  const verifyAfterRemediation = parseBoolean(core.getInput('verify_after_remediation'), true);
36375
36776
  const verifyAfterRemediationTimeoutMinutes = parsePositiveInt(core.getInput('verify_after_remediation_timeout_minutes'), verifyTimeoutMinutes, 1, 120);
36376
36777
  const requireRemediationPushSuccess = parseBoolean(core.getInput('require_remediation_push_success'), false);
@@ -36398,6 +36799,52 @@ async function run() {
36398
36799
  source: cliInstallSource,
36399
36800
  workspacePath: cliWorkspacePath,
36400
36801
  });
36802
+ if (enforceCompatibilityHandshake) {
36803
+ const handshake = await runCompatibilityHandshake({
36804
+ cli: cliInvocation,
36805
+ cwd,
36806
+ timeoutMinutes: compatibilityProbeTimeoutMinutes,
36807
+ apiUrl: configuredApiUrl || undefined,
36808
+ requireApiCompatibility: requireApiCompatibilityHandshake,
36809
+ });
36810
+ core.info(`Compatibility handshake passed (action=${handshake.actionVersion}, cli=${handshake.cliVersion}${handshake.apiVersion ? `, api=${handshake.apiVersion}` : ''}).`);
36811
+ core.setOutput('compatibility_handshake', 'passed');
36812
+ core.setOutput('compatibility_contract_version', contracts_1.RUNTIME_COMPATIBILITY_CONTRACT_VERSION);
36813
+ core.setOutput('compatibility_action_version', handshake.actionVersion);
36814
+ core.setOutput('compatibility_cli_version', handshake.cliVersion);
36815
+ if (handshake.apiVersion) {
36816
+ core.setOutput('compatibility_api_version', handshake.apiVersion);
36817
+ }
36818
+ }
36819
+ else {
36820
+ core.warning('Compatibility handshake is disabled via enforce_compatibility_handshake=false.');
36821
+ core.setOutput('compatibility_handshake', 'skipped');
36822
+ }
36823
+ const verifyCapabilities = await resolveVerifyCapabilities(cliInvocation, cwd);
36824
+ const effectiveCompiledPolicyPath = verifyCapabilities.supportsCompiledPolicy
36825
+ ? (compiledPolicyPath || undefined)
36826
+ : undefined;
36827
+ const effectiveChangeContractPath = verifyCapabilities.supportsChangeContract
36828
+ ? (changeContractPath || undefined)
36829
+ : undefined;
36830
+ if (!verifyCapabilities.supportsCompiledPolicy && compiledPolicyPath) {
36831
+ core.warning('CLI does not support --compiled-policy; compiled policy artifact input will be ignored.');
36832
+ }
36833
+ if (!verifyCapabilities.supportsChangeContract && changeContractPath) {
36834
+ core.warning('CLI does not support --change-contract; contract path input will be ignored.');
36835
+ }
36836
+ if (enforceChangeContract && !verifyCapabilities.supportsEnforceChangeContract) {
36837
+ core.warning('CLI does not support --enforce-change-contract; contract hard-fail mode will be disabled.');
36838
+ enforceChangeContract = false;
36839
+ }
36840
+ assertStrictDeterministicArtifacts({
36841
+ cwd,
36842
+ strictMode: enforceStrictVerification,
36843
+ compiledPolicyPath: effectiveCompiledPolicyPath,
36844
+ changeContractPath: effectiveChangeContractPath,
36845
+ supportsCompiledPolicy: verifyCapabilities.supportsCompiledPolicy,
36846
+ supportsChangeContract: verifyCapabilities.supportsChangeContract,
36847
+ });
36401
36848
  const pr = github.context.payload.pull_request;
36402
36849
  const defaultBaseRef = pr ? `origin/${pr.base.ref}` : 'HEAD~1';
36403
36850
  const baseRef = baseRefInput.trim() || defaultBaseRef;
@@ -36405,6 +36852,9 @@ async function run() {
36405
36852
  ? `Running verify in PR context against ${baseRef}`
36406
36853
  : `Running verify in push context against ${baseRef}`);
36407
36854
  core.info(`Verification mode: ${verifyPolicyOnly ? 'policy-only' : 'plan-aware'}`);
36855
+ core.info(`Enterprise mode: ${enterpriseMode ? 'enabled' : 'disabled'}`);
36856
+ core.info(`Verify capabilities => compiled_policy=${verifyCapabilities.supportsCompiledPolicy}, change_contract=${verifyCapabilities.supportsChangeContract}, enforce_change_contract=${verifyCapabilities.supportsEnforceChangeContract}`);
36857
+ core.info(`Enterprise enforcement => enforce_change_contract=${enforceChangeContract}, enforce_strict_verification=${enforceStrictVerification}`);
36408
36858
  let changedFiles = new Set();
36409
36859
  if (changedFilesOnly) {
36410
36860
  const changedFilesRun = await runCommand('git', ['diff', '--name-only', `${baseRef}...HEAD`], { cwd });
@@ -36417,6 +36867,7 @@ async function run() {
36417
36867
  }
36418
36868
  }
36419
36869
  let effectiveVerifyPolicyOnly = verifyPolicyOnly;
36870
+ let effectiveEnforceChangeContract = enforceChangeContract;
36420
36871
  const hasExplicitPlanId = Boolean(planId && planId.trim());
36421
36872
  let policyOnlyFallbackUsed = false;
36422
36873
  let fallbackFailureHint = null;
@@ -36426,9 +36877,10 @@ async function run() {
36426
36877
  projectId: projectId || undefined,
36427
36878
  policyOnly: effectiveVerifyPolicyOnly,
36428
36879
  record,
36429
- compiledPolicyPath: compiledPolicyPath || undefined,
36430
- changeContractPath: changeContractPath || undefined,
36431
- enforceChangeContract,
36880
+ compiledPolicyPath: effectiveCompiledPolicyPath,
36881
+ changeContractPath: effectiveChangeContractPath,
36882
+ enforceChangeContract: effectiveEnforceChangeContract,
36883
+ strictArtifacts: enforceStrictVerification,
36432
36884
  });
36433
36885
  let verifyCommand = withCliCommandTimeout(cliInvocation, verifyArgs, verifyTimeoutMinutes);
36434
36886
  let verifyRun = await runCommand(verifyCommand.cmd, verifyCommand.args, {
@@ -36451,15 +36903,22 @@ async function run() {
36451
36903
  core.warning('Plan-aware verification failed due to missing plan context. Retrying with --policy-only fallback.');
36452
36904
  policyOnlyFallbackUsed = true;
36453
36905
  effectiveVerifyPolicyOnly = true;
36906
+ effectiveEnforceChangeContract =
36907
+ (typeof enforceChangeContractOverride === 'boolean' ? enforceChangeContractOverride : false)
36908
+ && verifyCapabilities.supportsEnforceChangeContract;
36909
+ if (typeof enforceChangeContractOverride !== 'boolean' && enforceChangeContract) {
36910
+ core.info('Policy-only fallback detected; auto-disabling change-contract hard-fail for fallback run.');
36911
+ }
36454
36912
  verifyArgs = (0, verify_mode_1.buildVerifyArgs)({
36455
36913
  baseRef,
36456
36914
  projectId: projectId || undefined,
36457
36915
  planId: undefined,
36458
36916
  policyOnly: true,
36459
36917
  record,
36460
- compiledPolicyPath: compiledPolicyPath || undefined,
36461
- changeContractPath: changeContractPath || undefined,
36462
- enforceChangeContract,
36918
+ compiledPolicyPath: effectiveCompiledPolicyPath,
36919
+ changeContractPath: effectiveChangeContractPath,
36920
+ enforceChangeContract: effectiveEnforceChangeContract,
36921
+ strictArtifacts: enforceStrictVerification,
36463
36922
  });
36464
36923
  verifyCommand = withCliCommandTimeout(cliInvocation, verifyArgs, verifyTimeoutMinutes);
36465
36924
  verifyRun = await runCommand(verifyCommand.cmd, verifyCommand.args, {
@@ -36715,6 +37174,9 @@ async function run() {
36715
37174
  : 'plan_aware';
36716
37175
  core.setOutput('verify_mode', verifyModeOutput);
36717
37176
  core.setOutput('policy_only_fallback_used', policyOnlyFallbackUsed ? 'true' : 'false');
37177
+ core.setOutput('enterprise_mode_active', enterpriseMode ? 'true' : 'false');
37178
+ core.setOutput('enterprise_enforced_change_contract', effectiveEnforceChangeContract ? 'true' : 'false');
37179
+ core.setOutput('enterprise_enforced_strict_verification', enforceStrictVerification ? 'true' : 'false');
36718
37180
  if (remediation) {
36719
37181
  core.setOutput('remediation_status', remediation.status);
36720
37182
  core.setOutput('merge_confidence', remediation.mergeConfidence);
@@ -36762,6 +37224,138 @@ async function run() {
36762
37224
  run();
36763
37225
 
36764
37226
 
37227
+ /***/ }),
37228
+
37229
+ /***/ 5654:
37230
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
37231
+
37232
+ "use strict";
37233
+
37234
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
37235
+ exports.parseCliCompatibilityPayload = parseCliCompatibilityPayload;
37236
+ exports.parseApiHealthCompatibilityPayload = parseApiHealthCompatibilityPayload;
37237
+ exports.validateActionHandshake = validateActionHandshake;
37238
+ const contracts_1 = __nccwpck_require__(7239);
37239
+ function asRecord(value, label) {
37240
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
37241
+ throw new Error(`${label}: expected object`);
37242
+ }
37243
+ return value;
37244
+ }
37245
+ function asString(record, key, label) {
37246
+ const value = record[key];
37247
+ if (typeof value !== 'string' || !value.trim()) {
37248
+ throw new Error(`${label}: expected ${key}:string`);
37249
+ }
37250
+ return value.trim();
37251
+ }
37252
+ function asRuntimeComponent(value, label) {
37253
+ if (value === 'cli' || value === 'action' || value === 'api') {
37254
+ return value;
37255
+ }
37256
+ throw new Error(`${label}: expected component "cli" | "action" | "api"`);
37257
+ }
37258
+ function parseMinimumPeerVersions(value, label) {
37259
+ if (value === undefined || value === null)
37260
+ return {};
37261
+ const record = asRecord(value, `${label}.minimumPeerVersions`);
37262
+ const next = {};
37263
+ for (const component of ['cli', 'action', 'api']) {
37264
+ const item = record[component];
37265
+ if (item === undefined)
37266
+ continue;
37267
+ if (typeof item !== 'string' || !item.trim()) {
37268
+ throw new Error(`${label}.minimumPeerVersions: expected ${component}:string`);
37269
+ }
37270
+ next[component] = item.trim();
37271
+ }
37272
+ return next;
37273
+ }
37274
+ function parseDescriptor(value, label) {
37275
+ const record = asRecord(value, label);
37276
+ return {
37277
+ contractId: asString(record, 'contractId', label),
37278
+ runtimeContractVersion: asString(record, 'runtimeContractVersion', label),
37279
+ cliJsonContractVersion: asString(record, 'cliJsonContractVersion', label),
37280
+ component: asRuntimeComponent(asString(record, 'component', label), label),
37281
+ componentVersion: asString(record, 'componentVersion', label),
37282
+ minimumPeerVersions: parseMinimumPeerVersions(record.minimumPeerVersions, label),
37283
+ };
37284
+ }
37285
+ function parseCliCompatibilityPayload(value) {
37286
+ const parsed = (0, contracts_1.parseCliCompatJsonPayload)(value, 'action-cli-compat');
37287
+ if (parsed.success !== true) {
37288
+ throw new Error('action-cli-compat: success=false');
37289
+ }
37290
+ const descriptor = parseDescriptor(parsed.compatibility, 'action-cli-compat.compatibility');
37291
+ return {
37292
+ source: 'cli',
37293
+ ...descriptor,
37294
+ };
37295
+ }
37296
+ function parseApiHealthCompatibilityPayload(value) {
37297
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
37298
+ return null;
37299
+ }
37300
+ const record = value;
37301
+ if (!record.compatibility) {
37302
+ return null;
37303
+ }
37304
+ const descriptor = parseDescriptor(record.compatibility, 'action-api-compat.compatibility');
37305
+ return {
37306
+ source: 'api',
37307
+ ...descriptor,
37308
+ };
37309
+ }
37310
+ function validateVersionMinimum(label, actual, required, errors) {
37311
+ if (!required)
37312
+ return;
37313
+ const isCompatible = (0, contracts_1.isSemverAtLeast)(actual, required);
37314
+ if (isCompatible === null) {
37315
+ errors.push(`${label}: unable to compare versions (actual=${actual}, required=${required}).`);
37316
+ return;
37317
+ }
37318
+ if (!isCompatible) {
37319
+ errors.push(`${label}: actual=${actual} is below required=${required}.`);
37320
+ }
37321
+ }
37322
+ function validateContractEnvelope(descriptor, expectedComponent, errors) {
37323
+ if (descriptor.component !== expectedComponent) {
37324
+ errors.push(`${descriptor.source} compatibility payload expected component=${expectedComponent} but received ${descriptor.component}.`);
37325
+ }
37326
+ if (descriptor.contractId !== contracts_1.RUNTIME_COMPATIBILITY_CONTRACT_ID) {
37327
+ errors.push(`${descriptor.source} compatibility payload has unexpected contractId=${descriptor.contractId} (expected ${contracts_1.RUNTIME_COMPATIBILITY_CONTRACT_ID}).`);
37328
+ }
37329
+ if (descriptor.runtimeContractVersion !== contracts_1.RUNTIME_COMPATIBILITY_CONTRACT_VERSION) {
37330
+ errors.push(`${descriptor.source} compatibility payload has runtimeContractVersion=${descriptor.runtimeContractVersion} (expected ${contracts_1.RUNTIME_COMPATIBILITY_CONTRACT_VERSION}).`);
37331
+ }
37332
+ if (descriptor.cliJsonContractVersion !== contracts_1.CLI_JSON_CONTRACT_VERSION) {
37333
+ errors.push(`${descriptor.source} compatibility payload has cliJsonContractVersion=${descriptor.cliJsonContractVersion} (expected ${contracts_1.CLI_JSON_CONTRACT_VERSION}).`);
37334
+ }
37335
+ }
37336
+ function validateActionHandshake(input) {
37337
+ const errors = [];
37338
+ const { actionVersion, cliCompatibility } = input;
37339
+ const apiCompatibility = input.apiCompatibility || null;
37340
+ const requireApiCompatibility = input.requireApiCompatibility === true;
37341
+ validateContractEnvelope(cliCompatibility, 'cli', errors);
37342
+ validateVersionMinimum('CLI version required by action', cliCompatibility.componentVersion, (0, contracts_1.getMinimumCompatiblePeerVersion)('action', 'cli'), errors);
37343
+ validateVersionMinimum('Action version required by CLI payload', actionVersion, cliCompatibility.minimumPeerVersions.action, errors);
37344
+ if (!apiCompatibility) {
37345
+ if (requireApiCompatibility) {
37346
+ errors.push('API compatibility payload missing while API compatibility handshake is required.');
37347
+ }
37348
+ return errors;
37349
+ }
37350
+ validateContractEnvelope(apiCompatibility, 'api', errors);
37351
+ validateVersionMinimum('API version required by action', apiCompatibility.componentVersion, (0, contracts_1.getMinimumCompatiblePeerVersion)('action', 'api'), errors);
37352
+ validateVersionMinimum('Action version required by API payload', actionVersion, apiCompatibility.minimumPeerVersions.action, errors);
37353
+ validateVersionMinimum('CLI version required by API payload', cliCompatibility.componentVersion, apiCompatibility.minimumPeerVersions.cli, errors);
37354
+ validateVersionMinimum('API version required by CLI payload', apiCompatibility.componentVersion, cliCompatibility.minimumPeerVersions.api, errors);
37355
+ return errors;
37356
+ }
37357
+
37358
+
36765
37359
  /***/ }),
36766
37360
 
36767
37361
  /***/ 4288:
@@ -36772,6 +37366,7 @@ run();
36772
37366
  Object.defineProperty(exports, "__esModule", ({ value: true }));
36773
37367
  exports.isMissingPlanVerificationFailure = isMissingPlanVerificationFailure;
36774
37368
  exports.buildVerifyArgs = buildVerifyArgs;
37369
+ exports.resolveEnterpriseEnforcement = resolveEnterpriseEnforcement;
36775
37370
  exports.getVerifyFallbackDecision = getVerifyFallbackDecision;
36776
37371
  const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
36777
37372
  const MISSING_PLAN_VERIFY_PATTERNS = [
@@ -36807,10 +37402,24 @@ function buildVerifyArgs(input) {
36807
37402
  args.push('--change-contract', input.changeContractPath);
36808
37403
  if (input.enforceChangeContract)
36809
37404
  args.push('--enforce-change-contract');
37405
+ if (input.strictArtifacts)
37406
+ args.push('--strict-artifacts');
36810
37407
  if (input.record)
36811
37408
  args.push('--record');
36812
37409
  return args;
36813
37410
  }
37411
+ function resolveEnterpriseEnforcement(input) {
37412
+ const enforceChangeContract = typeof input.enforceChangeContract === 'boolean'
37413
+ ? input.enforceChangeContract
37414
+ : (input.enterpriseMode && !input.verifyPolicyOnly);
37415
+ const enforceStrictVerification = typeof input.enforceStrictVerification === 'boolean'
37416
+ ? input.enforceStrictVerification
37417
+ : input.enterpriseMode;
37418
+ return {
37419
+ enforceChangeContract,
37420
+ enforceStrictVerification,
37421
+ };
37422
+ }
36814
37423
  function getVerifyFallbackDecision(input) {
36815
37424
  if (input.verifyExitCode === 0) {
36816
37425
  return { shouldRetryPolicyOnly: false, reason: 'verify_succeeded' };