Ruling 1: model records keep a bounded previous list (64 ids, oldest dropped)
of ids that left by rotation, grace expiry or re-issue; a fire removes the
record. A tokens.keys leaf matches and counts every id in the lineage that
holds it. Lineage ids are unique host-wide. An orphaned leaf limits nothing,
logs one value-free line when it becomes orphaned, and the Console usage
view lists it.
Ruling 2: an agent with no model key under a key quota refuses
key-quota-no-model-key, passed through the broker, launcher and stop reason.
Ruling 3: covered by the lineage; no mayLaunch change.
Ruling 4: the usage module receives a frozen { readUsageQueue } only.
Ruling 5: every telemetry fixture wait races a 5 s real-timer deadline.
363 lines
23 KiB
JavaScript
363 lines
23 KiB
JavaScript
// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): core's side of the limit checks (limits.mjs). Fixture
|
|
// checks load from a scratch plug-in tree through its own loader; the shipped token-limit loads
|
|
// through the real one. Records are usage_records rows in a real store.
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { Store } from '../src/store.mjs';
|
|
import { createLimits, limitLeaves } from '../src/limits.mjs';
|
|
import { tree, LIMIT_V1 } from '../../plugins/tests/helpers.mjs';
|
|
|
|
const SINCE = '2026-10-18T00:00:00.000Z';
|
|
const CODER = ['tokens', 'instances', 'coder'];
|
|
const KEY = ['tokens', 'keys', 'k1'];
|
|
const LIMITS = { agents: 4, tokens: { instances: { coder: { limit: 100, since: SINCE } }, keys: { k1: { limit: 5, since: SINCE } } } };
|
|
const SUBJECT = { instance: 'coder', provider: 'anthropic', model: 'opus' };
|
|
|
|
// One fixture check per behaviour. hit(a) names the coder limit with the limit the file holds.
|
|
const FIXTURE = (mayLaunch, mustStop, report) => `export const interfaceVersion = 2;
|
|
const hit = (a, over = {}) => ({ path: ['tokens', 'instances', 'coder'], limit: a.limits.tokens.instances.coder.limit, count: 7, unknownTurns: 1, ...over });
|
|
const entry = (a, path, rest) => { let v = a.limits; for (const k of path) v = v[k]; return { path, limit: v.limit, since: v.since, ...rest }; };
|
|
export function mayLaunch(a) { ${mayLaunch} }
|
|
export function mustStop(a) { ${mustStop} }
|
|
export function report(a) { ${report} }
|
|
`;
|
|
const FIXTURES = {
|
|
allow: FIXTURE('return { allow: true };', 'return { stop: false };', 'return [];'),
|
|
refuse: FIXTURE('return { allow: false, hit: hit(a) };', 'return { stop: true, hit: hit(a) };',
|
|
"return [entry(a, ['tokens', 'instances', 'coder'], { notEnforced: 'fixture leaves it' })];"),
|
|
counts: FIXTURE('return { allow: true };', 'return { stop: false };',
|
|
"return [entry(a, ['tokens', 'instances', 'coder'], { count: '12', kinds: { input: 1, output: 2, cacheWrite: 3, cacheRead: '6' }, unknownTurns: 2 })];"),
|
|
echo: FIXTURE('globalThis.__limitArgs = a; a.limits.tokens = null; a.records.length = 0; return { allow: true };',
|
|
'globalThis.__limitArgs = a; return { stop: false };', 'globalThis.__limitArgs = a; return [];'),
|
|
throws: FIXTURE("throw new Error('boom');", "throw new Error('boom');", "throw new Error('boom');"),
|
|
coded: FIXTURE("throw Object.assign(new Error('x'), { code: 'quota-gone' });",
|
|
"throw Object.assign(new Error('x'), { code: 'quota-gone' });", "throw Object.assign(new Error('x'), { code: 'quota-gone' });"),
|
|
badcode: FIXTURE("throw { code: 'Not A Code' };", "throw 'text';", "throw { code: 7 };"),
|
|
malformed: FIXTURE("return { allow: 'yes' };", "return 'no';", 'return {};'),
|
|
extra: FIXTURE('return { allow: true, hit: hit(a) };', 'return { stop: false, why: 1 };',
|
|
"return [entry(a, ['tokens', 'instances', 'coder'], { notEnforced: '' })];"),
|
|
thenable: FIXTURE('return Promise.resolve({ allow: true });', 'return { then() {} };', 'return Promise.resolve([]);'),
|
|
// An async check that fails: core must handle the rejection, or the process sees it unhandled.
|
|
rejects: FIXTURE("return Promise.reject(new Error('late'));", "return Promise.reject(new Error('late'));",
|
|
"return Promise.reject(new Error('late'));"),
|
|
notleaf: FIXTURE("return { allow: false, hit: hit(a, { path: ['tokens', 'instances'] }) };",
|
|
'return { stop: true, hit: hit(a, { limit: 999 }) };',
|
|
"const e = entry(a, ['tokens', 'instances', 'coder'], { notEnforced: 'twice' }); return [e, e];"),
|
|
badcount: FIXTURE('return { allow: false, hit: hit(a, { count: -1 }) };', 'return { stop: true, hit: { ...hit(a), extra: 1 } };',
|
|
"return [entry(a, ['tokens', 'instances', 'coder'], { notEnforced: 'x', count: 1 })];"),
|
|
stale: FIXTURE('return { allow: true };', 'return { stop: false };',
|
|
"return [{ ...entry(a, ['tokens', 'instances', 'coder'], { notEnforced: 'x' }), since: '2020-01-01T00:00:00.000Z' }];"),
|
|
};
|
|
|
|
async function setup(t, { real = false } = {}) {
|
|
const root = mkdtempSync(join(tmpdir(), 'limit-checks-'));
|
|
const store = new Store(root);
|
|
t.after(() => { store.close(); rmSync(root, { recursive: true, force: true }); });
|
|
const logged = [];
|
|
const org = { demo: { id: 'demo', plugins: { 'limit-check': [] }, launch: { limits: structuredClone(LIMITS) } } };
|
|
let unreadable = false;
|
|
const read = id => {
|
|
if (unreadable || !Object.hasOwn(org, id)) throw new Error(`can't read ${id}`);
|
|
return structuredClone(org[id]);
|
|
};
|
|
let makeLoader;
|
|
if (!real) {
|
|
const plugins = tree(t);
|
|
for (const [name, body] of Object.entries(FIXTURES)) plugins.write('limit-check', name, body);
|
|
plugins.write('limit-check', 'old', LIMIT_V1('old'));
|
|
({ createLoader: makeLoader } = await import(plugins.loaderUrl));
|
|
}
|
|
const limits = createLimits({ store, read, log: e => logged.push(e), ...(makeLoader ? { makeLoader } : {}) });
|
|
const use = (...names) => { org.demo.plugins['limit-check'] = names; };
|
|
let seq = 0;
|
|
const record = ({ run = 'r1', turn = `t${++seq}`, source = 'pi-session', instance = 'coder', provider = 'anthropic', model = 'opus',
|
|
status = 'known', counts = [10, 20, 30, 40], phase = 'end', at = '2026-10-18T06:00:00.000Z', business = 'demo' } = {}) => {
|
|
// A known begin row holds zero counts; an unknown row holds none and a code.
|
|
const isKnown = status === 'known';
|
|
const values = isKnown ? (phase === 'end' ? counts : [0, 0, 0, 0]) : [null, null, null, null];
|
|
store.run(`INSERT INTO usage_records(at,business,run,instance,provider,model,account,turn,source,phase,status,code,cursor,input,output,cache_write,cache_read)
|
|
VALUES (?,?,?,?,?,?,NULL,?,?,?,?,?,?,?,?,?,?)`, at, business, run, instance, provider, model, turn, source, phase,
|
|
status, isKnown ? null : 'no-source', null, ...values);
|
|
return turn;
|
|
};
|
|
return { store, limits, logged, org, use, record, unreadable: v => { unreadable = v; } };
|
|
}
|
|
|
|
const failed = (check, code) => [CODER, KEY].map((path, i) => ({ path, limit: i ? 5 : 100, since: SINCE, status: 'check-failed', check, code }));
|
|
const notEnforced = (reasons = [null, null]) => [CODER, KEY].map((path, i) => ({ path, limit: i ? 5 : 100, since: SINCE, status: 'not-enforced', reason: reasons[i] }));
|
|
|
|
test('REQ-LIMIT-3 limitLeaves finds every limit object and skips the agent numbers', () => {
|
|
assert.deepEqual(limitLeaves(LIMITS), [
|
|
{ path: CODER, limit: 100, since: SINCE },
|
|
{ path: KEY, limit: 5, since: SINCE },
|
|
]);
|
|
assert.deepEqual(limitLeaves(null), []);
|
|
assert.deepEqual(limitLeaves({ tokens: { models: { anthropic: { 'claude/opus.5': { limit: 1, since: SINCE } } } } }),
|
|
[{ path: ['tokens', 'models', 'anthropic', 'claude/opus.5'], limit: 1, since: SINCE }]);
|
|
});
|
|
|
|
test('REQ-LIMIT-3 limitLeaves takes token limits at schema depth only, whatever their names', () => {
|
|
const leaf = limit => ({ limit, since: SINCE });
|
|
assert.deepEqual(limitLeaves({
|
|
providers: { limit: 3 }, models: { limit: { limit: 2 }, anthropic: { limit: 2 } },
|
|
tokens: { providers: { limit: leaf(5) }, models: { limit: { limit: leaf(6) } }, keys: { limit: leaf(7) } },
|
|
}), [
|
|
{ path: ['tokens', 'providers', 'limit'], limit: 5, since: SINCE },
|
|
{ path: ['tokens', 'models', 'limit', 'limit'], limit: 6, since: SINCE },
|
|
{ path: ['tokens', 'keys', 'limit'], limit: 7, since: SINCE },
|
|
]);
|
|
assert.deepEqual(limitLeaves({ providers: { limit: 3 }, models: { limit: { m: 2 } } }), []);
|
|
});
|
|
|
|
test('REQ-LIMIT-3 with no check switched on a launch is allowed, a turn never stops, and every limit is not enforced', async t => {
|
|
const c = await setup(t);
|
|
assert.deepEqual(c.limits.report('demo'), notEnforced()); // before any refresh, too
|
|
await c.limits.refresh('demo');
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: true });
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: false });
|
|
assert.deepEqual(c.limits.report('demo'), notEnforced());
|
|
assert.deepEqual(c.logged, []);
|
|
});
|
|
|
|
test('REQ-LIMIT-3 a fixture check that refuses refuses the launch and stops the turn, logged with the limit and the count', async t => {
|
|
const c = await setup(t);
|
|
c.use('allow', 'refuse', 'throws');
|
|
const hit = { path: CODER, limit: 100, count: 7, unknownTurns: 1 };
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: 'refuse', hit });
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: true, check: 'refuse', hit });
|
|
// The first refusal wins: the check after it isn't asked, so nothing logs a failure.
|
|
assert.deepEqual(c.logged, ['mayLaunch', 'mustStop'].map(name => ({ event: 'limit-reached', business: 'demo', check: 'refuse', export: name,
|
|
subject: { ...SUBJECT, account: null }, ...hit })));
|
|
// A limit change reaches the check on the next ask.
|
|
c.org.demo.launch.limits.tokens.instances.coder.limit = 300;
|
|
assert.equal((await c.limits.mayLaunch('demo', SUBJECT)).hit.limit, 300);
|
|
});
|
|
|
|
test('REQ-LIMIT-3 a report that omits a limit shows it not enforced; a counted entry and a reason come through', async t => {
|
|
const c = await setup(t);
|
|
c.use('allow');
|
|
await c.limits.refresh('demo');
|
|
assert.deepEqual(c.limits.report('demo'), notEnforced());
|
|
c.use('allow', 'refuse');
|
|
await c.limits.refresh('demo');
|
|
assert.deepEqual(c.limits.report('demo'), notEnforced(['fixture leaves it', null]));
|
|
c.use('refuse', 'counts');
|
|
await c.limits.refresh('demo');
|
|
// A count from any check wins over another check's reason.
|
|
assert.deepEqual(c.limits.report('demo'), [
|
|
{ path: CODER, limit: 100, since: SINCE, status: 'counted', check: 'counts', count: '12',
|
|
kinds: { input: 1, output: 2, cacheWrite: 3, cacheRead: '6' }, unknownTurns: 2 },
|
|
notEnforced()[1],
|
|
]);
|
|
});
|
|
|
|
test('REQ-LIMIT-4 each check gets the business, a copy of the limits, the subject, the records and the time', async t => {
|
|
const c = await setup(t);
|
|
c.record({ turn: 'a' });
|
|
c.record({ turn: 'b', business: 'other' });
|
|
c.use('echo');
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: true });
|
|
const a = globalThis.__limitArgs;
|
|
assert.deepEqual(Object.keys(a).sort(), ['business', 'limits', 'now', 'records', 'subject']);
|
|
assert.equal(a.business, 'demo');
|
|
assert.deepEqual(a.subject, { ...SUBJECT, account: null });
|
|
assert.ok(!Number.isNaN(Date.parse(a.now)));
|
|
// The check mutated its copy; core's view and the next ask are untouched.
|
|
await c.limits.mustStop('demo', SUBJECT);
|
|
const b = globalThis.__limitArgs;
|
|
assert.deepEqual(b.limits, LIMITS);
|
|
assert.deepEqual(b.records.map(r => [r.turn, r.status, r.input]), [['a', 'known', 10]]);
|
|
await c.limits.refresh('demo');
|
|
c.limits.report('demo');
|
|
assert.deepEqual(Object.keys(globalThis.__limitArgs).sort(), ['business', 'limits', 'now', 'records']);
|
|
// Within one ask each check gets its own copy: echo nulls its tokens, and refuse after it still reads limit 100.
|
|
c.use('echo', 'refuse');
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: 'refuse', hit: { path: CODER, limit: 100, count: 7, unknownTurns: 1 } });
|
|
delete globalThis.__limitArgs;
|
|
});
|
|
|
|
test('REQ-LIMIT-4 fail closed: a throw refuses, stops and fails the report, naming the check and the code', async t => {
|
|
const c = await setup(t);
|
|
for (const [name, codes] of [['throws', ['check-threw', 'check-threw', 'check-threw']], ['coded', ['quota-gone', 'quota-gone', 'quota-gone']],
|
|
['badcode', ['check-threw', 'check-threw', 'check-threw']]]) {
|
|
c.use(name);
|
|
c.logged.length = 0;
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: name, code: codes[0] }, name);
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: true, check: name, code: codes[1] }, name);
|
|
assert.deepEqual(c.limits.report('demo'), failed(name, codes[2]), name);
|
|
assert.deepEqual(c.logged, ['mayLaunch', 'mustStop', 'report'].map((exp, i) =>
|
|
({ event: 'limit-check-failed', business: 'demo', check: name, export: exp, code: codes[i] })), name);
|
|
}
|
|
});
|
|
|
|
test('REQ-LIMIT-4 fail closed: a malformed answer, a promise, a rejected promise, a non-boolean or a bad hit is malformed-answer', async t => {
|
|
const c = await setup(t);
|
|
for (const name of ['malformed', 'extra', 'thenable', 'rejects', 'notleaf', 'badcount', 'stale']) {
|
|
c.use(name);
|
|
const may = await c.limits.mayLaunch('demo', SUBJECT);
|
|
const stop = await c.limits.mustStop('demo', SUBJECT);
|
|
const malformed = { check: name, code: 'malformed-answer' };
|
|
// stale answers mayLaunch and mustStop well; only its report is wrong (a since the file doesn't hold).
|
|
if (name === 'stale') {
|
|
assert.deepEqual(may, { allow: true });
|
|
assert.deepEqual(stop, { stop: false });
|
|
} else {
|
|
assert.deepEqual(may, { allow: false, ...malformed }, name);
|
|
assert.deepEqual(stop, { stop: true, ...malformed }, name);
|
|
}
|
|
assert.deepEqual(c.limits.report('demo'), failed(name, 'malformed-answer'), name);
|
|
}
|
|
});
|
|
|
|
test('REQ-LIMIT-4 fail closed: a check switched on but not loaded refuses, stops and fails every limit as not-loaded', async t => {
|
|
const c = await setup(t);
|
|
// Refused on its first load: nothing is switched on, though the file names it.
|
|
c.use('old');
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: 'old', code: 'not-loaded' });
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: true, check: 'old', code: 'not-loaded' });
|
|
assert.deepEqual(c.limits.report('demo'), failed('old', 'not-loaded'));
|
|
// A version 1 module lacks report, and the loader checks exports before the version.
|
|
assert.ok(c.logged.some(e => e.event === 'plugin-refused' && e.name === 'old' && e.code === 'missing-export'));
|
|
// A refused load keeps the last good modules, but the file now names one more: still not loaded.
|
|
c.use('allow');
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: true });
|
|
c.use('allow', 'missing-check');
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: 'missing-check', code: 'not-loaded' });
|
|
assert.deepEqual(c.limits.report('demo'), failed('missing-check', 'not-loaded'));
|
|
});
|
|
|
|
// The state() fix: the loader's modules(kind) throws unknown-kind for a kind it doesn't hold, so
|
|
// state() reads loaded() first. Without the guard both cases fail check null, code unknown-kind.
|
|
test('REQ-LIMIT-4 state(), never-loaded case: before any load the report shows not enforced, or not-loaded for a switched-on check', async t => {
|
|
const c = await setup(t);
|
|
assert.deepEqual(c.limits.report('demo'), notEnforced());
|
|
c.use('refuse');
|
|
assert.deepEqual(c.limits.report('demo'), failed('refuse', 'not-loaded'));
|
|
// report never loads: nothing was loaded or refused.
|
|
assert.deepEqual(c.logged, []);
|
|
});
|
|
|
|
test('REQ-LIMIT-4 state(), first-load-refused case: a check refused on its first load is not-loaded in mayLaunch, mustStop and report', async t => {
|
|
const c = await setup(t);
|
|
c.use('old');
|
|
await c.limits.refresh('demo');
|
|
assert.ok(c.logged.some(e => e.event === 'plugin-refused' && e.name === 'old'));
|
|
assert.deepEqual(c.limits.report('demo'), failed('old', 'not-loaded'));
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: 'old', code: 'not-loaded' });
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: true, check: 'old', code: 'not-loaded' });
|
|
});
|
|
|
|
test('REQ-LIMIT-4 fail closed: an unreadable organization file refuses and stops with check null; the report throws', async t => {
|
|
const c = await setup(t);
|
|
c.use('allow');
|
|
await c.limits.refresh('demo');
|
|
c.unreadable(true);
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: null, code: 'organization-unreadable' });
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: true, check: null, code: 'organization-unreadable' });
|
|
assert.throws(() => c.limits.report('demo'), /can't read demo/);
|
|
assert.deepEqual(c.logged.at(-1), { event: 'limit-check-failed', business: 'demo', check: null, export: 'mustStop', code: 'organization-unreadable' });
|
|
});
|
|
|
|
test('REQ-LIMIT-4 fail closed: unreadable records refuse and stop with check null and fail the report', async () => {
|
|
const store = { all() { throw new Error('disk gone'); } };
|
|
const limits = createLimits({ store, read: () => ({ plugins: {}, launch: { limits: LIMITS } }), log: () => {} });
|
|
assert.deepEqual(await limits.mayLaunch('demo', SUBJECT), { allow: false, check: null, code: 'records-unreadable' });
|
|
assert.deepEqual(await limits.mustStop('demo', SUBJECT), { stop: true, check: null, code: 'records-unreadable' });
|
|
assert.deepEqual(limits.report('demo'), failed(null, 'records-unreadable'));
|
|
});
|
|
|
|
test('REQ-LIMIT-3 the shipped token-limit, through the real loader, counts known records only since the limit was set', async t => {
|
|
const c = await setup(t, { real: true });
|
|
c.use('token-limit');
|
|
// A known turn: its begin row, then its end with counts (100 tokens).
|
|
const t1 = c.record({ phase: 'begin' });
|
|
c.record({ turn: t1 });
|
|
// A turn with no usage source switched on: the '@none' unknown record, begin and end.
|
|
const t2 = c.record({ source: '@none', status: 'unknown', phase: 'begin' });
|
|
c.record({ turn: t2, source: '@none', status: 'unknown' });
|
|
// A turn begun and never ended counts as unknown.
|
|
c.record({ phase: 'begin' });
|
|
// Before the limit's since, and another instance's: neither counts.
|
|
c.record({ at: '2026-10-17T23:59:59.999Z' });
|
|
c.record({ instance: 'tester' });
|
|
await c.limits.refresh('demo');
|
|
assert.deepEqual(c.limits.report('demo'), [
|
|
{ path: CODER, limit: 100, since: SINCE, status: 'counted', check: 'token-limit', count: 100,
|
|
kinds: { input: 10, output: 20, cacheWrite: 30, cacheRead: 40 }, unknownTurns: 2 },
|
|
{ path: KEY, limit: 5, since: SINCE, status: 'not-enforced', reason: 'no records name a key' },
|
|
]);
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: false, check: 'token-limit',
|
|
hit: { path: CODER, limit: 100, count: 100, unknownTurns: 2 } });
|
|
assert.deepEqual(await c.limits.mustStop('demo', { ...SUBJECT, instance: 'tester' }), { stop: false });
|
|
// Unknown turns alone never reach a limit: with every turn unknown the count is 0.
|
|
c.org.demo.launch.limits.tokens.instances.coder.since = '2026-10-18T07:00:00.000Z';
|
|
c.record({ source: '@none', status: 'unknown', at: '2026-10-18T08:00:00.000Z' });
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', SUBJECT), { allow: true });
|
|
await c.limits.refresh('demo');
|
|
assert.equal(c.limits.report('demo')[0].count, 0);
|
|
});
|
|
|
|
test('REQ-LIMIT-3 a token limit for a provider named limit is counted and refuses, beside an agent limit of the same name', async t => {
|
|
const c = await setup(t, { real: true });
|
|
c.use('token-limit');
|
|
c.org.demo.launch.limits = { providers: { limit: 1 }, tokens: { providers: { limit: { limit: 50, since: SINCE } } } };
|
|
const t1 = c.record({ provider: 'limit', phase: 'begin' });
|
|
c.record({ turn: t1, provider: 'limit' });
|
|
const path = ['tokens', 'providers', 'limit'];
|
|
assert.deepEqual(await c.limits.mayLaunch('demo', { ...SUBJECT, provider: 'limit' }), { allow: false, check: 'token-limit',
|
|
hit: { path, limit: 50, count: 100, unknownTurns: 0 } });
|
|
assert.deepEqual(await c.limits.mustStop('demo', SUBJECT), { stop: false });
|
|
assert.deepEqual(c.limits.report('demo'), [{ path, limit: 50, since: SINCE, status: 'counted', check: 'token-limit', count: 100,
|
|
kinds: { input: 10, output: 20, cacheWrite: 30, cacheRead: 40 }, unknownTurns: 0 }]);
|
|
assert.deepEqual(c.logged.filter(e => e.event === 'limit-check-failed'), []);
|
|
});
|
|
|
|
test('REQ-LIMIT-3 core reaches limit checks only through the plug-in loader', () => {
|
|
const src = readFileSync(new URL('../src/limits.mjs', import.meta.url), 'utf8');
|
|
const imports = [...src.matchAll(/^import .* from '([^']+)';$/gm)].map(m => m[1]);
|
|
assert.deepEqual(imports, ['../../plugins/src/loader.mjs', './usage.mjs']);
|
|
assert.doesNotMatch(src, /token-limit|import\(/);
|
|
for (const file of ['../src/runtime.mjs', '../src/broker.mjs', '../src/process.mjs']) {
|
|
assert.doesNotMatch(readFileSync(new URL(file, import.meta.url), 'utf8'), /limit-check\/|token-limit/, file);
|
|
}
|
|
});
|
|
|
|
test('P5 missing, ambiguous or invalid model key lookup fails closed without leaking a lookup error', async t => {
|
|
const root=mkdtempSync(join(tmpdir(),'p5-limit-')),store=new Store(root),log=[];t.after(()=>{store.close();rmSync(root,{recursive:true,force:true});});
|
|
const key='012345abcdef',doc={plugins:{'limit-check':['daily-quota']},launch:{limits:{tokens:{keys:{[key]:{limit:5,since:SINCE,reset:'00:00',timeZone:'UTC'}}}}}};
|
|
for(const lookup of [()=>null,()=>['012345abcdef','abcdef012345'],()=> 'msk-private-lookup',()=>{throw Error('PRIVATE-LOOKUP-CANARY');}]) {
|
|
const l=createLimits({store,read:()=>doc,log:e=>log.push(e),keyForSubject:lookup});
|
|
assert.deepEqual(await l.mayLaunch('demo',SUBJECT),{allow:false,check:null,code:'proxy-key-refused'});
|
|
assert.deepEqual(await l.mustStop('demo',SUBJECT,{run:'old-run'}),{stop:true,check:null,code:'proxy-key-refused'});
|
|
}
|
|
assert.equal(JSON.stringify(log).includes('PRIVATE-LOOKUP-CANARY'),false);assert.equal(JSON.stringify(log).includes('msk-private-lookup'),false);
|
|
});
|
|
// Row 333 R3 (#1831): the run's own key is looked up, and its lineage decides the leaf. A leaf on
|
|
// the retiring key now limits the agent's current key too, so a launch on the current key refuses.
|
|
test('P5 trusted lookup uses the retiring run key, and the lineage carries its leaf to the current key',async t=>{
|
|
const root=mkdtempSync(join(tmpdir(),'p5-old-key-')),store=new Store(root);t.after(()=>{store.close();rmSync(root,{recursive:true,force:true});});
|
|
const old='012345abcdef',current='abcdef012345',asked=[],doc={plugins:{'limit-check':['daily-quota']},launch:{limits:{tokens:{keys:{[old]:{limit:0,since:SINCE,reset:'00:00',timeZone:'UTC'}}}}}};
|
|
const l=createLimits({store,read:()=>doc,log:()=>{},keyForSubject:(_,s)=>{asked.push(s.run??null);return s.run==='old-run'?old:current;},keyLineages:()=>[[current,old]]});
|
|
const hit={path:['tokens','keys',old],limit:0,count:0,unknownTurns:0};
|
|
assert.deepEqual(await l.mayLaunch('demo',SUBJECT),{allow:false,check:'daily-quota',hit});
|
|
assert.deepEqual(await l.mustStop('demo',SUBJECT,{run:'old-run'}),{stop:true,check:'daily-quota',hit});
|
|
assert.deepEqual(asked,[null,'old-run']);
|
|
const unrelated=createLimits({store,read:()=>doc,log:()=>{},keyForSubject:()=>current,keyLineages:()=>[[current],[old]]});
|
|
assert.deepEqual(await unrelated.mayLaunch('demo',SUBJECT),{allow:true});
|
|
});
|
|
|
|
test('P5 actual model metadata lookup rejects missing/ambiguous owners and never cross-attributes a retiring run',async()=>{
|
|
const { modelUsageKey }=await import('../src/limits.mjs');
|
|
const old='012345abcdef',current='abcdef012345';
|
|
const rows=[{service:'model',instance:'demo/coder',keyId:current,retiring:[{keyId:old}]},{service:'model',instance:'other/coder',keyId:'123456abcdef',retiring:[]}];
|
|
const runs=new Map([['demo:old-run',{instance:'demo/coder',keyId:old}],['demo:foreign-run',{instance:'other/coder',keyId:'123456abcdef'}]]);
|
|
assert.equal(modelUsageKey(rows,runs,'demo',{instance:'coder'}),current);
|
|
assert.equal(modelUsageKey(rows,runs,'demo',{instance:'coder',run:'old-run'}),old);
|
|
for(const [rs,s] of [[rows,{instance:'coder',run:'absent'}],[rows,{instance:'coder',run:'foreign-run'}],[[...rows,{...rows[0]}],{instance:'coder'}],[[{...rows[0],retiring:[]}],{instance:'coder',run:'old-run'}]]) {
|
|
assert.throws(()=>modelUsageKey(rs,runs,'demo',s),{code:'proxy-key-refused'});
|
|
}
|
|
});
|