import assert from 'node:assert/strict' import { execFileSync, spawnSync } from 'node:child_process' import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { test } from 'node:test' const root = dirname(dirname(fileURLToPath(import.meta.url))) test('packed contracts compile in a consumer without the Core checkout', () => { const consumer = mkdtempSync(join(tmpdir(), 'agent-contracts-consumer-')) try { const pack = spawnSync('npm', ['pack', '--json', '--ignore-scripts', '--pack-destination', consumer], { cwd: root, encoding: 'utf8', }) assert.equal(pack.status, 0, `Core must expose an independently packable contracts package:\n${pack.stderr}`) const [artifact] = JSON.parse(pack.stdout) assert.equal(artifact.name, '@agent-system/contracts') assert.ok(artifact.files.some(file => file.path === 'package/index.d.ts'), 'declarations must be packed') assert.ok(artifact.files.some(file => file.path === 'package/index.js'), 'ESM entry must be packed') assert.ok(!artifact.files.some(file => file.path.endsWith('.go')), 'Go implementation is outside the npm package') writeFileSync(join(consumer, 'package.json'), JSON.stringify({ private: true, type: 'module' })) writeFileSync(join(consumer, 'consumer.ts'), ` import type { ActivityCard, Message, SurfaceSnapshot } from '@agent-system/contracts' const card: ActivityCard = { id: 'card', projectId: 'project', conversationId: 'conversation', kind: 'progress', status: 'active', title: 'Working', createdAt: '2026-10-13T00:00:00Z', updatedAt: '2026-10-13T00:00:00Z' } const message: Message = { id: 'message', conversationId: 'conversation', role: 'assistant', parts: [{ type: 'text', text: card.title }], createdAt: card.createdAt } type Surface = SurfaceSnapshot // @ts-expect-error unknown message roles are not part of the wire contract const invalidRole: Message['role'] = 'operator' export { message, invalidRole }; export type { Surface } `) const tsc = [join(root, 'node_modules/typescript/bin/tsc'), '--noEmit', '--strict', '--skipLibCheck', '--module', 'NodeNext', '--moduleResolution', 'NodeNext', 'consumer.ts'] const missing = spawnSync(process.execPath, tsc, { cwd: consumer, encoding: 'utf8' }) assert.notEqual(missing.status, 0, 'the consumer must not resolve Core types before package installation') assert.match(missing.stdout, /TS2307.*@agent-system\/contracts/) execFileSync('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', '--offline', '--package-lock=false', join(consumer, artifact.filename)], { cwd: consumer, stdio: 'pipe' }) execFileSync(process.execPath, tsc, { cwd: consumer, stdio: 'inherit' }) execFileSync(process.execPath, ['--input-type=module', '-e', "import * as contracts from '@agent-system/contracts'; if (Object.keys(contracts).length) throw Error('contracts must remain type-only')"], { cwd: consumer, stdio: 'pipe' }) const declaration = readFileSync(join(consumer, 'node_modules/@agent-system/contracts/package/index.d.ts'), 'utf8') assert.match(declaration, /export interface SurfaceSnapshot/) } finally { rmSync(consumer, { recursive: true, force: true }) } })