fix(row 332): proxy first check runs every key-independent refusal; testProxy test option (Vikunja #774)

F1: with system.proxy set, the first launcher check no longer returns
before the account, sandbox, capacity and limit checks. It skips only the
harness preflight and the session proxy key match, which need the key P2
admits in startLocked. A launch that can never start now refuses before
authorizeLaunch, the workspace and checkouts, and modelPreflight. The first
proxy pass accepts only credential "proxy" for a declared account, tries
the fallback model when the primary refuses, and lets mayLaunch
key-quota-no-model-key through so P2 can issue the first key. The final
check is unchanged and still runs before newRun and spawn.

F4: the proxy test option is renamed testProxy and createLauncher throws
test-proxy-refused when it is given with system.proxy.
launch-account.test.mjs is a second caller, so the option stays.

Tests: proxy-launch-order.test.mjs (6), launcher.test.mjs F4 throw test;
the managed launch fixture gains bwrap, admit and trace.ops.
This commit is contained in:
jason.woltje committed 2026-11-02 20:51:51 -06:00
1 parent 1ff45c4ad6
commit 85ecdfbde5
5 files changed
+228 -53

No files matched your search

+80 -44
View File
@@ -219,9 +219,11 @@ export function createLauncher({
host,
system,
env = process.env,
// P2 supplies only {baseUrl,key}; a factory may supply the current agent's
// context per check. P3 never reads a key store or calls the proxy itself.
proxy = null,
// Tests only (#774 F4): a static session proxy context {baseUrl,key}, or a
// factory giving one per check, for a provider "proxy" route while
// system.proxy is unset. It skips P2 admission, so createLauncher refuses it
// together with system.proxy. P3 never reads a key store or calls the proxy.
testProxy = null,
tracker = false,
launches = true,
makeLoader = createLoader,
@@ -236,6 +238,8 @@ export function createLauncher({
stopOptions = {},
log = (l) => process.stderr.write(`mosaic-bus: ${l}\n`),
}) {
if (testProxy !== null && system.proxy)
throw Object.assign(new Error("testProxy skips P2 admission, so it is refused with system.proxy set"), { code: "test-proxy-refused" });
const dataRoot = system.dataRoot;
function admission(generation = null) {
try { return assertProcessing(dataRoot, generation); }
@@ -395,14 +399,81 @@ export function createLauncher({
let harness = await harnessOf(primary);
let route = "primary";
let routeReason = null;
// The first check validates the request before authorization/workspace work.
// Managed credentials are acquired once in startLocked, then supplied to
// the final check. Never reject proxy-only login using upstream host env.
if (system.proxy && !proxyReady)
// A launch is checked twice. Without system.proxy both are full checks: the first before the
// authorization, workspace and checkout work, the final one in startLocked before newRun. With
// system.proxy the first check skips only what needs the key P2 admits in startLocked: the
// harness preflight and the match of the session proxy key against launch.account.name. It
// still runs the account declaration (credential "proxy" only), the sandbox, capacity, the
// limit checks and admission, so a launch that can never start costs no authorization record,
// checkout or key (#774 F1). The final check is always full, on the admitted key. Never reject
// proxy-only login using upstream host env.
const early = Boolean(system.proxy) && !proxyReady;
// The checks that follow the route's preflight. The parameters shadow the outer route on
// purpose: the first check of a proxy launch gates a route it hasn't chosen. sessionProxy is
// null there, and the account must then name credential "proxy".
const gate = async (model, harness, sessionProxy) => {
const proxied = early || Boolean(sessionProxy);
// Row 126 (#1620): only a launch on a harness whose module counts the account spends the named
// account. The bus environment must hold the named set and no other credential variable, so the
// session can't log in some other way. Messages name variables, never values.
const account = call(model.harness, "countsAccount", () => harness.countsAccount()) === true ? b.launch.account?.name ?? null : null;
if (account !== null) {
// The usage records keep this name (bus schema 3i), so it must never be a credential's value.
const holds = [...new Set(SESSION_CREDENTIALS.flat())].filter((k) => env[k] === account);
if (sessionProxy?.key === account) holds.push("session proxy key");
if (holds.length) throw new Refusal("account-is-credential", `launch.account.name is the value of ${holds.join(", ")}; it names the account, never its credential`);
if (proxied || b.launch.account.credential === "proxy") {
if (!proxied || b.launch.account.credential !== "proxy")
throw new Refusal("account-credential-missing", `account ${account} needs the declared proxy credential`);
} else {
const named = b.launch.account.credential.split("+");
if (!SESSION_CREDENTIALS.some((set) => set.join("+") === named.join("+")) || !named.every((k) => Boolean(env[k])))
throw new Refusal("account-credential-missing", `account ${account} names ${named.join(" with ")}, which the bus host doesn't hold as a session login`);
const other = [...new Set(SESSION_CREDENTIALS.flat())].filter((k) => !named.includes(k) && Boolean(env[k]));
if (other.length) throw new Refusal("account-credential-ambiguous", `account ${account} names ${named.join(" with ")}, but the bus host also sets ${other.join(", ")}`);
}
}
const unavailable = sandboxProblem({ bwrap });
if (unavailable) throw new Refusal("sandbox-unavailable", unavailable);
const hit=limitHit(registry.sessions,b.id,b.launch.limits,model);
if(hit){const error=new Refusal("capacity-full",JSON.stringify(hit));error.limit=hit;throw error;}
// Row 125 (#1619, REQ-LIMIT-4): the limit checks. An answer that isn't an allow refuses,
// and so does a bus that can't answer (fail closed, lead decision 150).
let may;
try {
may = await host.op({ op: "mayLaunch", business: b.id, instance, provider: model.provider, model: model.model, account });
} catch (e) {
throw new Refusal("limit-check-failed", `bus: ${e.code ?? "launch-op-refused"}`);
}
// Row 333: under a daily key quota the bus refuses key-quota-no-model-key for an agent P2
// hasn't issued a key yet. Only the first check of a proxy launch lets that one answer pass:
// startLocked's modelPreflight issues the key, and the final check asks again with it.
if (early && may?.allow === false && may.check === null && may.code === "key-quota-no-model-key") return account;
if (may?.allow !== true) {
if (may?.hit) { const error = new Refusal("limit-reached", JSON.stringify(may.hit)); error.limit = { check: may.check, ...may.hit }; throw error; }
throw new Refusal("limit-check-failed", `${may?.check ?? "bus"}: ${may?.code ?? "malformed-answer"}`);
}
return account;
};
if (early) {
// The final check takes the fallback only when the primary's preflight fails, which this check
// can't know yet, so it refuses only when neither route would pass, with the primary's reason.
try {
await gate(primary, harness, null);
} catch (e) {
if (!(e instanceof Refusal) || !fallback) throw e;
try {
await gate(fallback, await harnessOf(fallback), null);
} catch (also) {
throw also instanceof Refusal ? e : also;
}
}
admission(admissionGeneration);
return { resolved, model, account: null, harness, route, routeReason, admissionGeneration };
}
const proxyFor = async (selected) => {
if (!system.proxy && selected.provider !== "proxy") return null;
const context = system.proxy ? proxyContext : typeof proxy === "function" ? await proxy({ business: b.id, instance }) : proxy;
const context = system.proxy ? proxyContext : typeof testProxy === "function" ? await testProxy({ business: b.id, instance }) : testProxy;
if (!context || typeof context.key !== "string" || !context.key
|| typeof context.baseUrl !== "string" || !/^http:\/\/127\.0\.0\.1:[0-9]+$/.test(context.baseUrl)) {
throw new Refusal("no-session-credential", "proxy model needs its own session proxy context");
@@ -425,42 +496,7 @@ export function createLauncher({
if (also) throw new Refusal(also.code, `fallback ${fallback.harness}: ${also.message} (primary ${primary.harness}: ${failed.message})`);
[model, harness, route, routeReason] = [fallback, second, "fallback", failed.code];
}
// Row 126 (#1620): only a launch on a harness whose module counts the account spends the named
// account. The bus environment must hold the named set and no other credential variable, so the
// session can't log in some other way. Messages name variables, never values.
const account = call(model.harness, "countsAccount", () => harness.countsAccount()) === true ? b.launch.account?.name ?? null : null;
if (account !== null) {
// The usage records keep this name (bus schema 3i), so it must never be a credential's value.
const holds = [...new Set(SESSION_CREDENTIALS.flat())].filter((k) => env[k] === account);
if (sessionProxy?.key === account) holds.push("session proxy key");
if (holds.length) throw new Refusal("account-is-credential", `launch.account.name is the value of ${holds.join(", ")}; it names the account, never its credential`);
if (sessionProxy || b.launch.account.credential === "proxy") {
if (!sessionProxy || b.launch.account.credential !== "proxy")
throw new Refusal("account-credential-missing", `account ${account} needs the declared proxy credential`);
} else {
const named = b.launch.account.credential.split("+");
if (!SESSION_CREDENTIALS.some((set) => set.join("+") === named.join("+")) || !named.every((k) => Boolean(env[k])))
throw new Refusal("account-credential-missing", `account ${account} names ${named.join(" with ")}, which the bus host doesn't hold as a session login`);
const other = [...new Set(SESSION_CREDENTIALS.flat())].filter((k) => !named.includes(k) && Boolean(env[k]));
if (other.length) throw new Refusal("account-credential-ambiguous", `account ${account} names ${named.join(" with ")}, but the bus host also sets ${other.join(", ")}`);
}
}
const unavailable = sandboxProblem({ bwrap });
if (unavailable) throw new Refusal("sandbox-unavailable", unavailable);
const hit=limitHit(registry.sessions,b.id,b.launch.limits,model);
if(hit){const error=new Refusal("capacity-full",JSON.stringify(hit));error.limit=hit;throw error;}
// Row 125 (#1619, REQ-LIMIT-4): the limit checks. An answer that isn't an allow refuses,
// and so does a bus that can't answer (fail closed, lead decision 150).
let may;
try {
may = await host.op({ op: "mayLaunch", business: b.id, instance, provider: model.provider, model: model.model, account });
} catch (e) {
throw new Refusal("limit-check-failed", `bus: ${e.code ?? "launch-op-refused"}`);
}
if (may?.allow !== true) {
if (may?.hit) { const error = new Refusal("limit-reached", JSON.stringify(may.hit)); error.limit = { check: may.check, ...may.hit }; throw error; }
throw new Refusal("limit-check-failed", `${may?.check ?? "bus"}: ${may?.code ?? "malformed-answer"}`);
}
const account = await gate(model, harness, sessionProxy);
admission(admissionGeneration);
return { resolved, model, account, harness, route, routeReason, admissionGeneration, sessionProxy };
}
+4 -4
View File
@@ -14,8 +14,8 @@ const VALUE='stand-in-value-7f3a';
const ACCOUNT={name:'jason-max',credential:'CLAUDE_CODE_OAUTH_TOKEN'};
// set is the credential variables the bus environment holds; every other one is removed. A pi
// launch gets one made-up pi key, so its own preflight passes on any host. proxy is the launcher's
// static session proxy context (row 332); edit changes the rest of the file.
// launch gets one made-up pi key, so its own preflight passes on any host. proxy goes to the launcher's
// testProxy option, a static session proxy context (row 332, #774 F4); edit changes the rest of the file.
function setup(t,{harness='claude-code',account=ACCOUNT,set=['CLAUDE_CODE_OAUTH_TOKEN'],proxy=null,edit=doc=>doc}={}){
const root=tmp(t),f=fixture(root,'acme',doc=>{
doc.roles.coder.vars.model='claude-sonnet-5-5';doc.roles.coder.vars.harness=harness;
@@ -32,7 +32,7 @@ function setup(t,{harness='claude-code',account=ACCOUNT,set=['CLAUDE_CODE_OAUTH_
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:()=>{},...(proxy?{proxy}:{})});
const launcher=createLauncher({host,system:JSON.parse(readFileSync(f.config,'utf8')),env:f.env,bwrap:'/usr/bin/true',log:()=>{},...(proxy?{testProxy:proxy}:{})});
const last=()=>JSON.parse(readFileSync(launchLogFile(f.dataRoot,'acme'),'utf8').trim().split('\n').at(-1));
const may=()=>ops.find(x=>x.op==='mayLaunch');
return {launcher,ops,last,may};
@@ -114,7 +114,7 @@ test('REQ-LIMIT-2 with no launch.account a claude-code launch carries null and t
});
// Row 332 R2 (#1830, review F3): the account checks on a launch with a session proxy context. A
// proxy model is routed through the launcher's static proxy option; no proxy runs.
// proxy model is routed through the launcher's testProxy option; no proxy runs.
const PROXY={baseUrl:'http://127.0.0.1:9',key:'fx332-acct-key'};
const proxyModel=doc=>{
doc.launch.models['proxy-sonnet']={provider:'proxy',model:'claude-sonnet-5-5',route:'anthropic'};
+13 -1
View File
@@ -154,7 +154,7 @@ test('P3 proxy key reaches the spawned runner but never session.json or the bund
doc.launch.models['claude-opus-5-5'] = { provider: 'proxy', model: 'claude-fixture', route: 'anthropic' };
return doc;
});
const { launcher, f, close } = await setup(t, doc => doc, prepared, { proxy });
const { launcher, f, close } = await setup(t, doc => doc, prepared, { testProxy: proxy });
const launched = await launcher.launchPm();
const session = readSessions(f.dataRoot).find(s => s.run === launched.run);
assert.ok(session);
@@ -169,6 +169,18 @@ test('P3 proxy key reaches the spawned runner but never session.json or the bund
assert.equal(await close(), 0);
});
// #774 F4: testProxy skips P2 admission, so createLauncher refuses it next to system.proxy, in both
// forms, before it reads anything else.
test('#774 F4 createLauncher refuses testProxy together with system.proxy', async t => {
const root = tmp(t);
const system = { dataRoot: join(root, 'data'), proxy: { port: 8317, binary: '/fixture-unused', firstOutputSeconds: 1 } };
const host = { business: 'acme', op: async () => { throw new Error('no host operation expected'); } };
for (const testProxy of [{ baseUrl: 'http://127.0.0.1:12345', key: 'fx774-static' }, async () => ({ baseUrl: 'http://127.0.0.1:12345', key: 'fx774-factory' })])
assert.throws(() => createLauncher({ host, system, env: { MOSAIC_CONFIG: join(root, 'config', 'config.json'), MOSAIC_ROLES_DIR: join(root, 'roles') }, testProxy, log: () => {} }),
(e) => e.code === 'test-proxy-refused' && /system\.proxy/.test(e.message) && !e.message.includes('fx774'));
assert.equal(existsSync(join(root, 'data')), false, 'refused before any data-root work');
});
test("bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest", { skip, timeout: 30000 }, async (t) => {
// Row 122: uncertain write receipts cannot prevent the window's first chief launch.
const prepared = prepare(t);
@@ -15,9 +15,11 @@ import { readSessions, runsDir, sessionsFile, SESSIONS_VERSION, launchLogFile }
export const waitFor = async (read, message, ms=15000) => { const end=Date.now()+ms;while(Date.now()<end){const value=await read();if(value)return value;await new Promise(r=>setTimeout(r,20));}throw Error(message); };
export const pause = ms => new Promise(r=>setTimeout(r,ms));
const id = value => createHash('sha256').update(value).digest('hex').slice(0,12);
export async function managedLaunchFixture(t,{proxy=true,launches=true,refuseProbe=false,harness='pi'}={}){
// #774: bwrap overrides the launcher's sandbox binary; admit rewrites what P2 admits, so a test can
// reach the final check's key match. trace.ops names every trusted host operation in order.
export async function managedLaunchFixture(t,{proxy=true,launches=true,refuseProbe=false,harness='pi',bwrap=null,admit=null}={}){
const root=fs.mkdtempSync(join(tmpdir(),'ml653-')),closers=[],history=new Set(['fx653-manage']),proxyReceipts=[],logs=[];
let server,bus,launcher,stderr;const trace={preflightAttempts:0,successfulPreflights:0,enableBeforeSpawn:[],bindings:[],stalls:[]};
let server,bus,launcher,stderr;const trace={preflightAttempts:0,successfulPreflights:0,enableBeforeSpawn:[],bindings:[],stalls:[],ops:[]};
const checkHistory=entries=>{const text=JSON.stringify(entries);for(const value of history)if(text.includes(value))throw Error('fixture credential leaked');};
t.after(async()=>{const failures=[];for(const close of [...closers].reverse()){try{await close();}catch{failures.push('owned-close');}}try{server?.closeAllConnections();if(server?.listening)await new Promise((resolve,reject)=>server.close(e=>e?reject(e):resolve()));}catch{failures.push('owned-server-close');}try{checkHistory({logs,proxyReceipts,trace});}catch{failures.push('value-free-receipts');}if(stderr)process.stderr.write=stderr;try{fs.rmSync(root,{recursive:true,force:true});}catch{failures.push('owned-root-close');}if(failures.length)throw new AggregateError(failures.map(code=>Error(code)),'owned managed fixture cleanup failed');});
const f=fixture(root,'acme',doc=>{doc.plugins={...doc.plugins,harness:[harness]};for(const role of Object.values(doc.roles))role.vars={...role.vars,harness};doc.roles.chief.vars.model='claude-opus-5-5';return doc;});
@@ -70,7 +72,8 @@ export async function managedLaunchFixture(t,{proxy=true,launches=true,refusePro
};
const host={business:'acme',path:bus.path,dataRoot:f.dataRoot,beforeClose:fn=>hooks.push(fn),
op:async m=>{
if(m.op==='modelPreflight'){trace.preflightAttempts++;const value=await actualOp(m);if(value){trace.successfulPreflights++;expectedKeyId=id(value.key);}return value;}
trace.ops.push(m.op);
if(m.op==='modelPreflight'){trace.preflightAttempts++;let value=await actualOp(m);if(value&&admit)value=admit(value);if(value){trace.successfulPreflights++;expectedKeyId=id(value.key);}return value;}
if(m.op==='landHolders'&&trace.successfulPreflights){
// Existing trusted host call occurs after enable publication and BEFORE bundle,
// registerRuntime/spawnSession/bind in the production startLocked sequence.
@@ -82,7 +85,7 @@ export async function managedLaunchFixture(t,{proxy=true,launches=true,refusePro
},
bindLaunch:async record=>{const result=bus.bindLaunch(record);trace.bindings.push({run:record.run,pid:record.pid,startTime:record.startTime,modelKeyId:record.modelKeyId});return result;}};
const makeLoader=await fakeHarnesses({after:close=>closers.push(close)},{[harness]:adapter});
launcher=createLauncher({host,system,env,launches,makeLoader,sessionDefaults:{pollInterval:50},harnessVersion:'0.85.1',log:line=>logs.push(line)});
launcher=createLauncher({host,system,env,launches,makeLoader,...(bwrap?{bwrap}:{}),sessionDefaults:{pollInterval:50},harnessVersion:'0.85.1',log:line=>logs.push(line)});
// The existing trusted host hook owns all launcher-created children. No registry
// PID is signalled by fixture teardown; normal bounded launcher close is used.
closers.push(async()=>{for(const close of hooks)await close(0);});await launcher.listen();
@@ -0,0 +1,124 @@
// Vikunja #774 F1 (row 332 review): with system.proxy set, a launch that can never start refuses in
// the first check, before authorizeLaunch, the workspace and checkouts, and the P2 modelPreflight
// that may issue a key. Only the harness preflight and the session proxy key match wait for the
// admitted key, in the final check. Real launcher, bus runtime and runner (managed P2 fixture).
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { join } from 'node:path';
import { sandboxProblem } from '../../seat/src/sandbox.mjs';
import { workspaceDir } from '../../seat/src/session.mjs';
import { managedLaunchFixture } from './model-managed-launch-fixture.mjs';
const skip = sandboxProblem() ?? false;
function editBusiness(f, update) {
const path = join(f.root, 'config', 'businesses', 'acme.json');
const document = JSON.parse(fs.readFileSync(path, 'utf8'));
update(document);
fs.writeFileSync(path, JSON.stringify(document) + '\n', { mode: 0o600 });
}
// A capability the bus issued to a claimed chief, as a launched chief holds.
function chief(f) {
const cap = f.bus.broker.bindLaunch({ business: 'acme', role: 'chief', run: 'chief-774', harness: f.f.doc.roles.chief.vars.harness });
f.bus.broker.request(cap, { verb: 'role.claim' });
return cap;
}
// The refusal came from the first check: nothing after it ran.
function refusedFirst(f, before, preflights, bindings = 0, instance = 'coder') {
assert.equal(f.trace.preflightAttempts, preflights, 'no modelPreflight for a launch the first check refuses');
assert.deepEqual(f.trace.ops.slice(before).filter(op => ['authorizeLaunch', 'credentialStatus', 'modelPreflight'].includes(op)), [],
'no authorizeLaunch, credentialStatus or modelPreflight after the first check');
assert.equal(fs.existsSync(join(workspaceDir(f.f.dataRoot, 'acme', instance), 'stack')), false, 'no checkout clone or fetch');
assert.equal(f.trace.bindings.length, bindings, 'no runner bound for the refused launch');
}
const refusal = (f, code) => f.launchLog().filter(e => e.event === 'refused' && e.code === code);
const claudeCoder = doc => {
doc.launch.models['claude-opus-5-5'].provider = 'anthropic';
doc.roles.coder.vars.model = 'claude-opus-5-5';
};
test('#774 F1 proxy capacity-full refuses in the first check: no modelPreflight, checkout or authorizeLaunch; the ordinary launch before it goes live', { skip, timeout: 60000 }, async t => {
const f = await managedLaunchFixture(t);
const cap = chief(f);
const ordinary = await f.launcher.fromSession(cap, 'reviewer');
assert.equal(ordinary.ok, true, JSON.stringify(ordinary));
assert.ok(f.sessions().find(s => s.run === ordinary.result.run)?.live, 'the ordinary proxy launch is live');
assert.equal(f.trace.preflightAttempts, 1, 'the ordinary proxy launch makes one P2 admission');
editBusiness(f, doc => { doc.launch.limits.total = 1; });
const before = f.trace.ops.length;
assert.deepEqual(await f.launcher.fromSession(cap, 'coder'), { ok: false, error: 'capacity-full' });
refusedFirst(f, before, 1, 1);
assert.deepEqual(refusal(f, 'capacity-full').map(e => e.limit), [{ scope: 'total', limit: 1, running: 1 }]);
f.valueFree();
});
test('#774 F1 proxy limit-reached refuses in the first check: no modelPreflight, checkout or authorizeLaunch', { skip, timeout: 60000 }, async t => {
const f = await managedLaunchFixture(t);
editBusiness(f, doc => {
doc.plugins = { ...doc.plugins, 'limit-check': ['token-limit'] };
doc.launch.limits.tokens = { instances: { coder: { limit: 0, since: '2026-10-18T00:00:00.000Z' } } };
});
const before = f.trace.ops.length;
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'limit-reached' });
refusedFirst(f, before, 0);
assert.equal(refusal(f, 'limit-reached')[0]?.limit?.check, 'token-limit');
assert.equal(f.runDirectories().length, 0);
f.valueFree();
});
test('#774 F1 proxy sandbox-unavailable refuses in the first check: no modelPreflight, checkout or authorizeLaunch', { skip, timeout: 60000 }, async t => {
const f = await managedLaunchFixture(t, { bwrap: '/nonexistent-774/bwrap' });
const before = f.trace.ops.length;
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'sandbox-unavailable' });
refusedFirst(f, before, 0);
assert.equal(f.runDirectories().length, 0);
f.valueFree();
});
test('#774 F1 proxy launch.account declared with an upstream credential refuses in the first check, before modelPreflight', { skip, timeout: 60000 }, async t => {
const f = await managedLaunchFixture(t, { harness: 'claude-code' });
editBusiness(f, doc => {
claudeCoder(doc);
doc.launch.account = { name: 'jason-max', credential: 'CLAUDE_CODE_OAUTH_TOKEN' };
});
const before = f.trace.ops.length;
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'account-credential-missing' });
refusedFirst(f, before, 0);
assert.equal(refusal(f, 'account-credential-missing')[0]?.why, 'account jason-max needs the declared proxy credential');
assert.equal(f.runDirectories().length, 0);
f.valueFree();
});
// The key match needs the admitted key, so it stays in the final check: after modelPreflight,
// before newRun and spawn. The fixture makes P2 admit a key equal to the account name.
test('#774 F1 proxy session key equal to launch.account.name still refuses account-is-credential in the final check', { skip, timeout: 60000 }, async t => {
const f = await managedLaunchFixture(t, { harness: 'claude-code', admit: value => ({ ...value, key: 'jason-max' }) });
editBusiness(f, doc => {
claudeCoder(doc);
doc.launch.account = { name: 'jason-max', credential: 'proxy' };
});
assert.deepEqual(await f.launcher.fromSession(chief(f), 'coder'), { ok: false, error: 'account-is-credential' });
assert.equal(f.trace.preflightAttempts, 1, 'the first check passed and P2 admitted a key');
assert.equal(f.trace.ops.filter(op => op === 'authorizeLaunch').length, 1);
assert.equal(refusal(f, 'account-is-credential')[0]?.why, 'launch.account.name is the value of session proxy key; it names the account, never its credential');
assert.equal(f.trace.bindings.length, 0);
assert.equal(f.sessions().length, 0);
assert.equal(f.runDirectories().length, 0, 'refused before newRun');
f.valueFree();
});
// Row 333: under a daily key quota the bus refuses mayLaunch key-quota-no-model-key for an agent
// with no model key. In the first check that is the one answer that waits for P2 to issue a key.
test('#774 F1 proxy launch of an agent with no key yet passes the first check under a daily key quota, and both checks ask mayLaunch', { skip, timeout: 60000 }, async t => {
const f = await managedLaunchFixture(t);
editBusiness(f, doc => {
doc.plugins = { ...doc.plugins, 'limit-check': ['token-limit'] };
doc.launch.limits.tokens = { keys: { '000000000774': { limit: 1000000, since: '2026-10-18T00:00:00.000Z', reset: '00:00', timeZone: 'UTC' } } };
});
assert.equal(f.bus.credentials.modelRecord('acme/chief'), null, 'chief holds no model key yet');
const answer = await f.launcher.launchPm();
assert.ok(f.sessions().find(s => s.run === answer.run)?.live, 'the launch goes live');
assert.equal(f.trace.successfulPreflights, 1);
assert.equal(f.trace.ops.filter(op => op === 'mayLaunch').length, 2, 'mayLaunch in the first and the final check');
f.valueFree();
});