@aviaratech/ai-delivery 0.3.10 → 0.3.11

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 CHANGED
@@ -10,6 +10,15 @@ Install the versioned package from npm or a reviewed package archive. The
10
10
  consuming repository must run its own configuration and runtime-admission
11
11
  procedure before any lifecycle write.
12
12
 
13
+ The selected CLI, MCP server and verification controller use Node 24.21.0 and
14
+ npm 11.19.0. The public library exports also support Node 26.2.0 consumers.
15
+ Package checks run the Node 24 controller against actual npm-packed Node 26
16
+ consumers, including filesystem fixture contention, reader and link rejection,
17
+ success/failure teardown, cancellation, leaked fixtures and positive byte limits.
18
+ For package development, set `AI_DELIVERY_NODE26_EXECUTABLE` to the absolute
19
+ Node 26.2.0 executable before `npm run checks`; CI retains that executable while
20
+ selecting Node 24 for the canonical commands.
21
+
13
22
  ```sh
14
23
  node --version # 24.21.0
15
24
  npm --version # 11.19.0
@@ -4,7 +4,8 @@ import { createHash } from 'node:crypto';
4
4
  import { cpSync, chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, readdirSync, realpathSync, rmSync, symlinkSync, unlinkSync, utimesSync, writeFileSync, } from 'node:fs';
5
5
  import { tmpdir } from 'node:os';
6
6
  import { dirname, join } from 'node:path';
7
- import { test, vi } from 'vitest';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { afterAll, test, vi } from 'vitest';
8
9
  import { syntheticDiscoveryClients, syntheticOverrides } from './fixtures/discovery.js';
9
10
  import { Octokit } from '@octokit/rest';
10
11
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
@@ -4251,13 +4252,71 @@ test('fully cached bounded verification still rejects a newly leaked FIFO', asyn
4251
4252
  rmSync(root, { recursive: true, force: true });
4252
4253
  }
4253
4254
  });
4254
- function filesystemFixtureScript(output, mode) {
4255
+ let node26Package;
4256
+ function packedNode26Consumer() {
4257
+ if (node26Package !== undefined)
4258
+ return node26Package;
4259
+ assert.equal(process.version, 'v24.21.0', 'canonical verification controller must remain Node 24.21.0');
4260
+ const executable = process.env['AI_DELIVERY_NODE26_EXECUTABLE'];
4261
+ assert.ok(executable, 'set AI_DELIVERY_NODE26_EXECUTABLE to the actual Node 26.2.0 executable');
4262
+ assert.equal(execFileSync(executable, ['--version'], { encoding: 'utf8' }).trim(), 'v26.2.0');
4263
+ const temporary = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-node26-')));
4264
+ try {
4265
+ const source = fileURLToPath(new URL('../', import.meta.url));
4266
+ const pack = JSON.parse(execFileSync('npm', ['pack', '--json', '--pack-destination', temporary], {
4267
+ cwd: source,
4268
+ encoding: 'utf8',
4269
+ maxBuffer: 4 * 1024 * 1024,
4270
+ }));
4271
+ execFileSync('tar', ['-xzf', join(temporary, pack[0].filename), '-C', temporary]);
4272
+ const root = join(temporary, 'package');
4273
+ symlinkSync(join(source, 'node_modules'), join(root, 'node_modules'), 'dir');
4274
+ process.stderr.write(`ai-delivery.node26-consumer ${JSON.stringify({
4275
+ controller: process.version,
4276
+ consumer: 'v26.2.0',
4277
+ archiveSha256: createHash('sha256')
4278
+ .update(readFileSync(join(temporary, pack[0].filename)))
4279
+ .digest('hex'),
4280
+ manifestSha256: createHash('sha256')
4281
+ .update(readFileSync(join(root, 'package.json')))
4282
+ .digest('hex'),
4283
+ })}\n`);
4284
+ node26Package = { executable: realpathSync(executable), root, temporary };
4285
+ return node26Package;
4286
+ }
4287
+ catch (error) {
4288
+ rmSync(temporary, { recursive: true, force: true });
4289
+ throw error;
4290
+ }
4291
+ }
4292
+ afterAll(() => {
4293
+ if (node26Package !== undefined)
4294
+ rmSync(node26Package.temporary, { recursive: true, force: true });
4295
+ });
4296
+ function node26ConsumerScript(script) {
4297
+ const consumer = packedNode26Consumer();
4255
4298
  return `
4299
+ require('node:assert/strict').equal(process.version, 'v24.21.0');
4300
+ const child = require('node:child_process').spawnSync(${JSON.stringify(consumer.executable)}, ['-e', ${JSON.stringify(script)}], { cwd: ${JSON.stringify(consumer.root)}, stdio: 'inherit' });
4301
+ if (child.error) throw child.error;
4302
+ process.exit(child.status ?? 1);`;
4303
+ }
4304
+ function filesystemFixtureScript(output, mode, runtime = 'node24') {
4305
+ const script = `
4256
4306
  (async () => {
4257
4307
  const fs = require('node:fs'), cp = require('node:child_process'), path = require('node:path'), assert = require('node:assert/strict');
4258
- const { withVerificationFilesystemFixture } = await import(${JSON.stringify(new URL('./agent.js', import.meta.url).href)});
4259
- const { assertPrivateFile } = await import(${JSON.stringify(new URL('./delivery/common.js', import.meta.url).href)});
4308
+ assert.equal(process.version, ${JSON.stringify(runtime === 'node26' ? 'v26.2.0' : 'v24.21.0')});
4309
+ const { withVerificationFilesystemFixture } = await import(${JSON.stringify(runtime === 'node26' ? '@aviaratech/ai-delivery/agent' : new URL('./agent.js', import.meta.url).href)});
4310
+ const { loadValidRepositoryDeliveryEvidence } = await import(${JSON.stringify(runtime === 'node26' ? '@aviaratech/ai-delivery/delivery' : new URL('./delivery/index.js', import.meta.url).href)});
4260
4311
  const output = ${JSON.stringify(output)}, fifo = path.join(output, 'fifo'), events = path.join(output, 'events');
4312
+ const evidenceId = 'sha256:' + 'a'.repeat(64), currentHead = { sha: 'b'.repeat(40), tree: 'c'.repeat(40) };
4313
+ const evidence = path.join(output, 'ai-delivery', 'receipts', 'delivery@1', currentHead.sha, evidenceId.slice(7) + '.json');
4314
+ fs.mkdirSync(path.dirname(evidence), { recursive: true });
4315
+ const rejectPrivateFile = file => {
4316
+ fs.renameSync(file, evidence);
4317
+ try { assert.throws(() => loadValidRepositoryDeliveryEvidence({ createInput: { gitCommonDir: output, currentHead }, evidenceId }), /private regular file/); }
4318
+ finally { fs.renameSync(evidence, file); }
4319
+ };
4261
4320
  const gate = JSON.parse(process.env.AI_DELIVERY_OUTPUT_OBSERVATION_GATE);
4262
4321
  const trace = value => fs.appendFileSync(events, value + '\\n');
4263
4322
  await withVerificationFilesystemFixture(async () => {
@@ -4270,20 +4329,26 @@ function filesystemFixtureScript(output, mode) {
4270
4329
  if (${JSON.stringify(mode)} === 'block') fs.readFileSync(fifo);
4271
4330
  while (fs.readdirSync(path.join(gate.directory, 'requests')).length === 0) await new Promise(resolve => setTimeout(resolve, 10));
4272
4331
  assert.ok(fs.existsSync(fifo)); trace('scan-queued');
4273
- assert.throws(() => assertPrivateFile(fifo), /private regular file/); trace('reader-rejected');
4332
+ rejectPrivateFile(fifo); trace('reader-rejected');
4274
4333
  const regular = path.join(output, 'regular'), hard = path.join(output, 'hard'), alias = path.join(output, 'alias');
4275
4334
  fs.writeFileSync(regular, 'x'.repeat(128), { mode: 0o600 });
4276
- fs.linkSync(regular, hard); assert.throws(() => assertPrivateFile(hard), /private regular file/); fs.unlinkSync(hard);
4277
- fs.symlinkSync('./regular', alias); assert.throws(() => assertPrivateFile(alias), /private regular file/); fs.unlinkSync(alias);
4335
+ fs.linkSync(regular, hard); rejectPrivateFile(hard); fs.unlinkSync(hard);
4336
+ fs.symlinkSync('./regular', alias); rejectPrivateFile(alias); fs.unlinkSync(alias);
4278
4337
  if (${JSON.stringify(mode)} === 'assertion') throw new Error('synthetic reader assertion failure');
4279
4338
  } finally { fs.unlinkSync(fifo); trace('cleaned'); }
4280
4339
  });
4281
4340
  trace('released');
4282
4341
  })().catch(error => { console.error(error.message); process.exitCode = 1; });`;
4342
+ return runtime === 'node26' ? node26ConsumerScript(script) : script;
4283
4343
  }
4284
- test.each(['pass', 'assertion'])('filesystem fixture gate deterministically queues a scan through reader rejection and %s teardown', async (mode) => {
4344
+ test.each([
4345
+ ['pass', 'node24'],
4346
+ ['assertion', 'node24'],
4347
+ ['pass', 'node26'],
4348
+ ['assertion', 'node26'],
4349
+ ])('filesystem fixture gate deterministically queues a scan through reader rejection and %s teardown on %s', async (mode, runtime) => {
4285
4350
  const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-fixture-gate-')));
4286
- const { root } = await fixture({ firstStageScript: filesystemFixtureScript(output, mode) });
4351
+ const { root } = await fixture({ firstStageScript: filesystemFixtureScript(output, mode, runtime) });
4287
4352
  try {
4288
4353
  const row = await prepareIssueWorktree({
4289
4354
  baseRef: 'main',
@@ -4319,10 +4384,17 @@ test.each(['pass', 'assertion'])('filesystem fixture gate deterministically queu
4319
4384
  rmSync(output, { recursive: true, force: true });
4320
4385
  }
4321
4386
  });
4322
- test.each(['abrupt', 'block', 'cancel'])('filesystem fixture gate preserves leaked FIFO and unrelated files after %s', async (mode) => {
4387
+ test.each([
4388
+ ['abrupt', 'node24'],
4389
+ ['block', 'node24'],
4390
+ ['cancel', 'node24'],
4391
+ ['abrupt', 'node26'],
4392
+ ['block', 'node26'],
4393
+ ['cancel', 'node26'],
4394
+ ])('filesystem fixture gate preserves leaked FIFO and unrelated files after %s on %s', async (mode, runtime) => {
4323
4395
  const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-fixture-failure-')));
4324
4396
  writeFileSync(join(output, 'unrelated'), 'preserved');
4325
- const { root } = await fixture({ firstStageScript: filesystemFixtureScript(output, mode) });
4397
+ const { root } = await fixture({ firstStageScript: filesystemFixtureScript(output, mode, runtime) });
4326
4398
  const controller = new AbortController();
4327
4399
  let running;
4328
4400
  const progress = vi.spyOn(process.stderr, 'write');
@@ -4374,9 +4446,9 @@ test.each(['abrupt', 'block', 'cancel'])('filesystem fixture gate preserves leak
4374
4446
  rmSync(output, { recursive: true, force: true });
4375
4447
  }
4376
4448
  });
4377
- test('filesystem fixture gate still enforces positive byte limits after deterministic contention', async () => {
4449
+ test.each(['node24', 'node26'])('filesystem fixture gate still enforces positive byte limits after deterministic contention on %s', async (runtime) => {
4378
4450
  const output = realpathSync(mkdtempSync(join(tmpdir(), 'ai-delivery-fixture-limit-')));
4379
- const { root } = await fixture({ firstStageScript: filesystemFixtureScript(output, 'pass') });
4451
+ const { root } = await fixture({ firstStageScript: filesystemFixtureScript(output, 'pass', runtime) });
4380
4452
  try {
4381
4453
  const row = await prepareIssueWorktree({
4382
4454
  baseRef: 'main',
@@ -4478,6 +4550,37 @@ test.each(['verification', 'runtime'])('filesystem fixture gate preserves observ
4478
4550
  rmSync(output, { recursive: true, force: true });
4479
4551
  }
4480
4552
  });
4553
+ test('Node 26 packed public exports preserve standalone and malformed-capability behavior', () => {
4554
+ const consumer = packedNode26Consumer();
4555
+ execFileSync(consumer.executable, [
4556
+ '-e',
4557
+ `
4558
+ (async () => {
4559
+ const assert = require('node:assert/strict');
4560
+ assert.equal(process.version, 'v26.2.0');
4561
+ const [root, agent, delivery, mcp] = await Promise.all([
4562
+ import('@aviaratech/ai-delivery'), import('@aviaratech/ai-delivery/agent'),
4563
+ import('@aviaratech/ai-delivery/delivery'), import('@aviaratech/ai-delivery/mcp'),
4564
+ ]);
4565
+ assert.equal(root.withVerificationFilesystemFixture, agent.withVerificationFilesystemFixture);
4566
+ assert.equal(typeof agent.verifyIssue, 'function');
4567
+ assert.equal(typeof delivery.loadValidRepositoryDeliveryEvidence, 'function');
4568
+ assert.equal(typeof mcp.createAiDeliveryMcpServer, 'function');
4569
+ const manifest = JSON.parse(require('node:fs').readFileSync('package.json', 'utf8'));
4570
+ assert.deepEqual(Object.keys(manifest.exports).sort(), ['.', './agent', './delivery', './mcp']);
4571
+ const prior = process.env.AI_DELIVERY_OUTPUT_OBSERVATION_GATE;
4572
+ delete process.env.AI_DELIVERY_OUTPUT_OBSERVATION_GATE;
4573
+ assert.equal(await agent.withVerificationFilesystemFixture(() => 'standalone'), 'standalone');
4574
+ process.env.AI_DELIVERY_OUTPUT_OBSERVATION_GATE = '{}';
4575
+ let executed = false;
4576
+ await assert.rejects(agent.withVerificationFilesystemFixture(() => { executed = true; }), /invalid or stale/);
4577
+ assert.equal(executed, false);
4578
+ if (prior === undefined) delete process.env.AI_DELIVERY_OUTPUT_OBSERVATION_GATE;
4579
+ else process.env.AI_DELIVERY_OUTPUT_OBSERVATION_GATE = prior;
4580
+ })().catch(error => { console.error(error); process.exitCode = 1; });
4581
+ `,
4582
+ ], { cwd: consumer.root, stdio: 'pipe' });
4583
+ });
4481
4584
  test('filesystem fixture helper runs standalone and fails closed on malformed capability', async () => {
4482
4585
  const { withVerificationFilesystemFixture } = await import('./agent.js');
4483
4586
  assert.equal(await withVerificationFilesystemFixture(() => 'standalone'), 'standalone');