Adds token limits under launch.limits.tokens (instances, providers, keys, models.P.M). Core (packages/bus/src/limits.mjs) reads the organization file and the per-turn usage records row 124 writes, then asks every switched-on limit-check plug-in; the first refusal wins. A limit no switched-on check reports reads "not enforced". The limit-check kind is interface 2 (mayLaunch, mustStop, report); a v1 check is refused at load. The shipped token-limit check is pure and counts known end records only; unknown turns add nothing and are shown as their own count. The launcher refuses limit-reached through the host's mayLaunch op, or limit-check-failed naming the check. The broker asks mustStop after a usage end and stops the run at its next receive or state. Settings stamps since on a new or changed limit and runs reset; launch.mjs and business.mjs validate the shape. Readers: mosaic limits and mosaic limits reset, the bus limits view, the Console /api/bus/limits route and the Settings token table with Reset. Key limits stay "not enforced" until row 126 names accounts on records. Owner Dewey, design under lead decision 150. Reviewer Filbert: r1 changes (comment 28965), r2 approve (28981), r3 approve (28996) on candidate manifest 8b71c405...fe56 after the rebase onto row 164 (37 of 38 paths with identical +/- lines, TOOLS.md:621 bracket). Decision 123 go on the bus.js hunk from Mos (mos-0814). Sage gate onb6827d19e: 25 steps, all rc 0 (15 packages, 10 script suites), 2295 node tests and 422 script selftests, 0 failed, 20:03:25Z to 20:08:08Z, network sage-r125_default removed. Live stays as pinned. Not part of the8e7ea89a1window. Co-Authored-By: Claude Opus 5.5 <[email protected]>
45 lines
1.9 KiB
JavaScript
45 lines
1.9 KiB
JavaScript
// A scratch tree per test: a copy of this package's src/ under
|
|
// <root>/packages/plugins, plus fixture modules. The loader finds modules
|
|
// from its own location, so each tree's copy loads only that tree's modules.
|
|
import { cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
|
export const SRC = resolve(dirname(fileURLToPath(import.meta.url)), "..", "src");
|
|
|
|
export const USAGE = name => `export const interfaceVersion = 1;
|
|
export function readTurn() { return ${JSON.stringify(name)}; }
|
|
`;
|
|
export const LIMIT = name => `export const interfaceVersion = 2;
|
|
export function mayLaunch() { return ${JSON.stringify(name)}; }
|
|
export function mustStop() { return false; }
|
|
export function report() { return []; }
|
|
`;
|
|
// A version 1 limit check (before row 125): the loader refuses it.
|
|
export const LIMIT_V1 = name => `export const interfaceVersion = 1;
|
|
export function mayLaunch() { return ${JSON.stringify(name)}; }
|
|
export function mustStop() { return false; }
|
|
`;
|
|
|
|
export function tree(t) {
|
|
const root = mkdtempSync(join(tmpdir(), "mosaic-plugins-"));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
const pkg = join(root, "packages", "plugins");
|
|
cpSync(SRC, join(pkg, "src"), { recursive: true });
|
|
const write = (kind, name, body) => {
|
|
const dir = join(pkg, kind, name);
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(join(dir, "index.mjs"), body);
|
|
return dir;
|
|
};
|
|
const loaderUrl = pathToFileURL(join(pkg, "src", "loader.mjs")).href;
|
|
async function loader(options = {}) {
|
|
const { createLoader } = await import(loaderUrl);
|
|
const logged = [];
|
|
const instance = createLoader({ log: entry => logged.push(entry), ...options });
|
|
return { ...instance, logged };
|
|
}
|
|
return { root, pkg, write, loader, loaderUrl };
|
|
}
|