kimi-code / packages /agent-core-v2 /scripts /check-import-boundaries.mjs
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
4e23b01 verified
Raw History Blame Contribute Delete
7.74 kB
#!/usr/bin/env node
import { readFileSync, readdirSync, statSync } from 'node:fs';
import { dirname, join, relative, resolve } from 'node:path';
const PKG_ROOT = resolve(import.meta.dirname, '..');
export const SRC_ROOT = join(PKG_ROOT, 'src');
const TEST_ROOT = join(PKG_ROOT, 'test');
const HUMAN_ROOT = join(SRC_ROOT, 'human');
const ADAPTER_ROOT = join(SRC_ROOT, 'llm-adapter');
const LOOP_MACHINE_ADAPTER_ROOT = join(SRC_ROOT, 'agent/loop/machine');
const SESSION_LIFECYCLE_ADAPTER_ROOT = join(SRC_ROOT, 'session/agentLifecycle');
const SELF_PACKAGE_PREFIX = '@moonshot-ai/agent-core-v2/';
const KOSONG_PATH_RE = /(?:^|\/)kosong(?:\/|$)/;
const TRAIT_FILE_RE = /\/trait\.ts$/;
const FORMAT_LOWER_FILE_RE = /\/bases\/[^/]+\/(?:format|lower)\.ts$/;
const FORMAT_LOWER_MODULE_RE = /\/bases\/[^/]+\/(?:format|lower)$/;
const TRAIT_MODULE_RE = /\/trait$/;
const BASES_DIR_RE = /\/llm\/requester\/bases(?:\/|$)/;
const BASES_INTERNAL_MODULE_RE =
/\/llm\/requester\/bases\/[^/]+\/(?:format|lower|patterns|reasoning-key)$/;
const TEST_DIR_RE = /\/test(?:\/|$)/;
function traitBoundaryViolation(absFile, targetAbs, specifier) {
const message = `format and trait never import each other ('${specifier}') — both sides speak only the neutral wire/chunk types in the protocol's contract.ts`;
if (TRAIT_FILE_RE.test(absFile) && FORMAT_LOWER_MODULE_RE.test(targetAbs)) {
return message;
}
if (FORMAT_LOWER_FILE_RE.test(absFile) && TRAIT_MODULE_RE.test(targetAbs)) {
return message;
}
return undefined;
}
function basesInternalViolation(absFile, targetAbs, specifier) {
if (!BASES_INTERNAL_MODULE_RE.test(targetAbs)) return undefined;
if (TRAIT_FILE_RE.test(absFile) && FORMAT_LOWER_MODULE_RE.test(targetAbs)) return undefined;
if (BASES_DIR_RE.test(absFile) || TEST_DIR_RE.test(absFile)) return undefined;
return `protocol format modules are internal to the requester pipeline ('${specifier}') — only llm/requester/bases code and tests may import format/lower/patterns; everyone else speaks contract/trait/requester`;
}
const HUMAN_VOCABULARY = new Set([
'agent/origin',
'llm/message',
'llm/usage',
'llm/capability',
'llm/thinking',
'llm/finish-reason',
'llm/response-format',
'llm/media/upload',
'llm/media/image-formats',
'llm/requester/requester',
'llm/toolCallIdNormalizer',
'llm-kimi/trait',
'interaction/interaction',
'interaction/machine',
'interaction/facade',
'utils/watch',
'xstate2',
]);
const V2_ONLY_FIRST_SEGMENTS = new Set([
'llm-adapter',
'app',
'workspace',
'features',
'state',
'wire',
'persistence',
'os',
'mcpCore',
'errors',
'debug',
'program',
'runtime',
'_base',
]);
function isInside(root, absPath) {
const rel = relative(root, absPath);
return rel !== '' && !rel.startsWith('..');
}
function humanSubpathOf(specifier) {
if (specifier.startsWith('#human/')) return specifier.slice('#human/'.length);
if (specifier.startsWith(`${SELF_PACKAGE_PREFIX}human/`)) {
return specifier.slice(`${SELF_PACKAGE_PREFIX}human/`.length);
}
return undefined;
}
function stripTs(path) {
return path.endsWith('.ts') ? path.slice(0, -'.ts'.length) : path;
}
function resolveIntraV2(specifier, fromFile) {
if (specifier.startsWith('#human/')) {
return join(HUMAN_ROOT, specifier.slice('#human/'.length));
}
if (specifier.startsWith('#/')) {
if (isInside(HUMAN_ROOT, fromFile)) {
return join(HUMAN_ROOT, specifier.slice(2));
}
return join(SRC_ROOT, specifier.slice(2));
}
if (specifier.startsWith(SELF_PACKAGE_PREFIX)) {
return join(SRC_ROOT, specifier.slice(SELF_PACKAGE_PREFIX.length));
}
if (specifier.startsWith('.')) {
return resolve(dirname(fromFile), specifier);
}
return undefined;
}
const IMPORT_RE =
/(?:import|export)\s+(?:type\s+)?(?:[^'";]*?\s+from\s+)?['"]([^'"]+)['"]|(?:import|require)\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
export function checkSource(source, absFile) {
const violations = [];
const inSrc = !relative(SRC_ROOT, absFile).startsWith('..');
const inHuman = isInside(HUMAN_ROOT, absFile);
const inAdapter =
isInside(ADAPTER_ROOT, absFile) ||
isInside(LOOP_MACHINE_ADAPTER_ROOT, absFile) ||
isInside(SESSION_LIFECYCLE_ADAPTER_ROOT, absFile);
let match;
IMPORT_RE.lastIndex = 0;
while ((match = IMPORT_RE.exec(source)) !== null) {
const specifier = match[1] ?? match[2];
if (!specifier) continue;
const line = source.slice(0, match.index).split('\n').length;
if (KOSONG_PATH_RE.test(specifier)) {
violations.push({
file: absFile,
line,
message: `the kosong kernel is deleted (${specifier}) — request/provider code lives in #human/llm, the v2 compatibility boundary is #/llm-adapter`,
});
continue;
}
if (!inSrc) continue;
const targetAbs = resolveIntraV2(specifier, absFile);
if (targetAbs !== undefined) {
const basesInternal = basesInternalViolation(absFile, stripTs(targetAbs), specifier);
if (basesInternal !== undefined) {
violations.push({ file: absFile, line, message: basesInternal });
}
}
if (inHuman) {
if (specifier.startsWith('#/')) {
const first = specifier.slice(2).split('/')[0];
if (first !== undefined && V2_ONLY_FIRST_SEGMENTS.has(first)) {
violations.push({
file: absFile,
line,
message: `human must not import outside its kernel ('${specifier}') — human is the pure LLM/agent kernel: it never imports llm-adapter or v2 domains`,
});
continue;
}
}
if (targetAbs !== undefined && !isInside(HUMAN_ROOT, targetAbs)) {
violations.push({
file: absFile,
line,
message: `human must not import outside its kernel ('${specifier}') — human is the pure LLM/agent kernel: it never imports llm-adapter or v2 domains`,
});
}
if (targetAbs !== undefined) {
const traitBoundary = traitBoundaryViolation(absFile, stripTs(targetAbs), specifier);
if (traitBoundary !== undefined) {
violations.push({ file: absFile, line, message: traitBoundary });
}
}
continue;
}
const humanSub = humanSubpathOf(specifier);
if (humanSub !== undefined && !inAdapter && !HUMAN_VOCABULARY.has(stripTs(humanSub))) {
violations.push({
file: absFile,
line,
message: `only llm-adapter, agent/loop/machine and session/agentLifecycle may import the human implementation ('${specifier}') — v2 code outside those adapter layers is limited to the vocabulary modules (${[...HUMAN_VOCABULARY].join(', ')})`,
});
}
}
return violations;
}
export function checkFile(absFile) {
return checkSource(readFileSync(absFile, 'utf8'), absFile);
}
function walk(dir) {
const out = [];
for (const entry of readdirSync(dir)) {
if (entry === 'node_modules' || entry === 'dist') continue;
const abs = join(dir, entry);
const st = statSync(abs);
if (st.isDirectory()) out.push(...walk(abs));
else if (abs.endsWith('.ts')) out.push(abs);
}
return out;
}
function main() {
const files = [...walk(SRC_ROOT), ...walk(TEST_ROOT)];
const violations = files.flatMap((f) => checkFile(f));
if (violations.length === 0) {
console.log(`check-import-boundaries: OK (${files.length} files)`);
return 0;
}
for (const v of violations) {
console.error(`${relative(PKG_ROOT, v.file)}:${v.line}: ${v.message}`);
}
console.error(`\ncheck-import-boundaries: ${violations.length} violation(s)`);
return 1;
}
const isMain = process.argv[1] && resolve(process.argv[1]) === import.meta.filename;
if (isMain) {
process.exit(main());
}