Ask approval for Admiral expanded scope

This commit is contained in:
Codex committed 2026-07-12 09:50:43 -04:00
1 parent f2ec1e9437
commit d500a29186
3 files changed
+35 -4

No files matched your search

+13
View File
@@ -25169,6 +25169,19 @@ Be specific and actionable. Format as structured JSON.`;
throw new Error('Approval required for ' + tgt + ' — choose Autonomous mode, or approve in the receipts panel.');
}
}
if (res.status === 403 && Array.isArray(d.approvals) && d.approvals.length) {
const approvalIds = d.approvals.map(function(a){ return a && a.id; }).filter(Boolean);
const targets = (d.outOfScopeTargets || []).join(', ');
if (isLab || autonomous) {
await Promise.all(approvalIds.map(function(id){
return fetch('/api/approvals/' + id + '/approve', { method:'POST', headers:{'Content-Type':'application/json'}, body:'{}' });
}));
res = await fetch('/api/admiral/launch', launchOpts({ approvalIds: approvalIds }));
d = await res.json();
} else {
throw new Error('Expanded target approval required for ' + (targets || 'planner-added targets') + ' — approve the receipts or choose Autonomous mode.');
}
}
if (res.status === 409 && d.review && d.review.status === 'hold') throw new Error('Op Admiral plan gate is HOLD — ' + ((d.review && d.review.reason) || 'manual review required before execution'));
if (!res.ok || d.error) throw new Error(d.error || ('launch failed (' + res.status + ')'));
const nOps = (d.operators || []).length;
@@ -33,9 +33,10 @@ describe('local API authorization hardening invariants', () => {
it('Admiral live launch re-checks every General-produced execution target before mission bring-up', () => {
const route = routeBlock("app.post('/api/admiral/launch'", '// =============================================================================\n// BOUNTY PLATFORM INTEGRATIONS');
expect(route).toMatch(/ensureExecTargetsWithinApprovedTarget\(execConfig\.targets, brief\.target\)/);
expect(route).toMatch(/ensureExecTargetsAuthorized\(execConfig\.targets,\s*brief\.target,\s*req\.body/);
expect(route).toMatch(/outOfScopeTargets\.length/);
expect(route.indexOf('ensureExecTargetsWithinApprovedTarget(execConfig.targets, brief.target)'))
expect(route.indexOf('ensureExecTargetsAuthorized(execConfig.targets, brief.target'))
.toBeLessThan(route.indexOf('bringUpMissionFromPlan(execConfig, generalConfig)'));
expect(route).toMatch(/approvals/);
});
});
+19 -2
View File
@@ -1104,6 +1104,15 @@ function ensureExecTargetsWithinApprovedTarget(targets: string[], approvedTarget
.filter(target => hostFromTarget(target) !== approvedHost);
}
function ensureExecTargetsAuthorized(
targets: string[],
approvedTarget: string,
body: Record<string, unknown>,
): string[] {
return ensureExecTargetsWithinApprovedTarget(targets, approvedTarget)
.filter(target => !findApproval(body, 'autonomous_execution', target));
}
function approvalMatchesGateScope(approval: ApprovalRequest, action: GuardAction, operationId: string, target: string): boolean {
if (!approvalMatches(approval, action, target)) return false;
if (approval.operationId && operationId && approval.operationId !== operationId) return false;
@@ -7326,12 +7335,20 @@ app.post('/api/admiral/launch', async (req: Request, res: Response): Promise<voi
res.status(409).json({ error: 'General plan gate is HOLD', mode: 'live', plan, review: execConfig.review });
return;
}
const outOfScopeTargets = ensureExecTargetsWithinApprovedTarget(execConfig.targets, brief.target);
const outOfScopeTargets = ensureExecTargetsAuthorized(execConfig.targets, brief.target, req.body as Record<string, unknown>);
if (outOfScopeTargets.length) {
const approvals = outOfScopeTargets.map(target => createApprovalRequest(
'autonomous_execution',
target,
`Admiral plan expanded execution scope from ${brief.target} to ${target}`,
req.body as Record<string, unknown>
));
res.status(403).json({
error: 'Admiral LIVE plan contains targets outside the approved brief target',
error: 'Admiral LIVE plan needs approval for expanded target scope',
approvedTarget: brief.target,
outOfScopeTargets,
approvals,
next: 'Approve the expanded target receipts, then retry /api/admiral/launch with approvalIds.',
});
return;
}