// Row 125 (#1619, REQ-LIMIT-3, REQ-LIMIT-4): the launcher asks the bus's limit checks before // every launch, after the agent limits. A stub host answers the mayLaunch op; no sandbox, Docker // or session process. Any answer that isn't an allow refuses (fail closed, lead decision 150). import test from 'node:test'; import assert from 'node:assert/strict'; import {readFileSync} from 'node:fs'; import {fileURLToPath} from 'node:url'; import {createLauncher} from '../src/launcher.mjs'; import {initializeControl} from '../../processing-control/src/state.mjs'; import {fixture,tmp,withoutHostCredentials} from './helpers.mjs'; import {launchLogFile} from '../../seat/src/session.mjs'; const HIT={path:['tokens','instances','coder'],limit:100,count:120,unknownTurns:1}; // answer is what the mayLaunch op returns, or an Error it throws. function setup(t,answer,{initialize=true}={}){ const root=tmp(t),f=fixture(root,'acme',doc=>{doc.roles.coder.vars.model='claude-sonnet-5-5';return doc;}),ops=[]; withoutHostCredentials(f.env,{piKey:true}); if(initialize)initializeControl(f.dataRoot); const host={business:'acme',beforeClose:()=>{},op:async x=>{ ops.push(x); if(x.op==='identity')return {business:'acme',role:'pm'}; if(x.op==='mayLaunch'){if(answer instanceof Error)throw answer;return answer;} if(x.op==='authorizeLaunch')throw Object.assign(Error('fixture authorization stop'),{code:'fixture-policy'}); return {}; }}; const launcher=createLauncher({host,system:JSON.parse(readFileSync(f.config,'utf8')),env:f.env,bwrap:'/usr/bin/true',log:()=>{}}); const last=()=>JSON.parse(readFileSync(launchLogFile(f.dataRoot,'acme'),'utf8').trim().split('\n').at(-1)); return {launcher,ops,last}; } test('REQ-LIMIT-4 the launcher asks mayLaunch with the instance, provider, model and account (row 126), and goes on to authorization on an allow',async t=>{ const {launcher,ops}=setup(t,{allow:true}); assert.equal((await launcher.fromSession('fixture-cap','coder')).error,'fixture-policy'); assert.deepEqual(ops.find(x=>x.op==='mayLaunch'),{op:'mayLaunch',business:'acme',instance:'coder',provider:'zai',model:'claude-sonnet-5-5',account:null}); assert.ok(ops.findIndex(x=>x.op==='mayLaunch')x.op==='authorizeLaunch')); }); test('REQ-LIMIT-3 REQ-LIMIT-4 a launch whose subject is at its limit is refused limit-reached and logged with the check, limit and count',async t=>{ const {launcher,ops,last}=setup(t,{allow:false,check:'token-limit',hit:HIT}); assert.deepEqual(await launcher.fromSession('fixture-cap','coder'),{ok:false,error:'limit-reached'}); const event=last(); assert.equal(event.event,'refused'); assert.equal(event.code,'limit-reached'); assert.deepEqual(event.limit,{check:'token-limit',...HIT}); assert.deepEqual(JSON.parse(event.why),HIT); assert.ok(ops.find(x=>x.op==='refuse'&&x.code==='limit-reached')); assert.equal(ops.find(x=>x.op==='authorizeLaunch'),undefined); assert.deepEqual(launcher.registry.sessions,[]); }); test('REQ-LIMIT-4 fail closed: a refused op, a check failure or a malformed answer refuses limit-check-failed, naming the check and the code',async t=>{ for(const [answer,why] of [ [Object.assign(Error('op refused'),{code:'quota-gone'}),'bus: quota-gone'], [Error('no code'),'bus: launch-op-refused'], [{allow:false,check:'token-limit',code:'not-loaded'},'token-limit: not-loaded'], [{allow:false,check:'token-limit',code:'check-threw'},'token-limit: check-threw'], [{allow:false,check:null,code:'organization-unreadable'},'bus: organization-unreadable'], // Row 333 R3 (#1831) ruling 2: an agent with no model key under a key quota, by its own code. [{allow:false,check:null,code:'key-quota-no-model-key'},'bus: key-quota-no-model-key'], [{allow:'yes'},'bus: malformed-answer'], [{},'bus: malformed-answer'], [null,'bus: malformed-answer'], [undefined,'bus: malformed-answer'], ]){ const {launcher,ops,last}=setup(t,answer); assert.deepEqual(await launcher.fromSession('fixture-cap','coder'),{ok:false,error:'limit-check-failed'},why); const event=last(); assert.equal(event.code,'limit-check-failed',why); assert.equal(event.why,why); assert.equal(event.limit,undefined,why); assert.equal(ops.find(x=>x.op==='authorizeLaunch'),undefined,why); } }); test('REQ-LIMIT-4 the launcher reaches token-limit only through the bus: launcher.mjs imports no limit check',()=>{ const src=readFileSync(fileURLToPath(new URL('../src/launcher.mjs',import.meta.url)),'utf8'); assert.doesNotMatch(src,/token-limit/); assert.doesNotMatch(src,/limit-check\//); // The launcher loads model providers and harnesses, but never a limit check. assert.deepEqual([...src.matchAll(/\.load\(([^)]*)\)/g)].map(m=>m[1]),["{ 'model-provider': providerNames }",'{ harness: [m.harness] }']); assert.doesNotMatch(src,/modules\(\s*["'](?!harness["'])/); assert.doesNotMatch(src,/\bimport\s*\(/); }); // Row 191: only trusted bus startup initializes control; an early seat launch refuses. test('a launcher before trusted control initialization refuses processing-uninitialized',async t=>{ const {launcher,ops}=setup(t,{allow:true},{initialize:false}); assert.deepEqual(await launcher.fromSession('fixture-cap','coder'),{ok:false,error:'processing-uninitialized'}); assert.equal(ops.some(x=>x.op==='mayLaunch'||x.op==='authorizeLaunch'),false); assert.deepEqual(launcher.registry.sessions,[]); });