mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-02 03:02:28 +01:00
* feat: add optional tool approval mode Admin-configurable tool_approval_mode setting (ask/auto) for the post-external-context tool approval gate. - ask (default): existing approval flow, gate stays on - auto: bypasses the approval gate for all runs - same effect as ODYSSEUS_TOOL_APPROVAL_GATE=0. Documented honestly in the settings row copy and in THREAT_MODEL.md, per review on #6304. - value validated server-side ({ask, auto}, anything else -> 400) - no new env var: the setting wires to the existing gate control - tests: setting validation + _tool_approval_gate_bypassed behavior * feat: surface deployment gate state on Tool approval row Follow-up to StressTestor's re-review: when ODYSSEUS_TOOL_APPROVAL_GATE is set to a falsy value, the Tool approval row still read 'Always ask' even though the gate was off, misleading admins the other way. GET /api/auth/settings now returns tool_approval_gate_env_disabled (derived from TOOL_APPROVAL_GATE_ENABLED) for admins, and the settings row renders a red notice naming the env var and explaining that the gate is off for every run regardless of the stored setting. Non-admin/scrubbed responses do not expose the deployment state. Test covers both env values and the scrub path. * refactor: rename tool approval mode auto to off The gate-off value is now "off" ("Off (YOLO)" in the UI) so the auto-approve name stays available for the future judged mode. --------- Co-authored-by: Matej <matej@local>
80 lines
3.6 KiB
JavaScript
80 lines
3.6 KiB
JavaScript
// Agent settings panel.
|
|
// Extracted verbatim from static/js/settings.js. Behaviour is unchanged: the
|
|
// functions moved, their bodies did not. settings.js imports them and calls
|
|
// them from the same places, so load order and init sequence are untouched.
|
|
|
|
import { byId as el } from './dom.js';
|
|
import { postSettings as _postSettings } from './api.js';
|
|
|
|
export async function initAgentSettings() {
|
|
var toolsInput = el('set-agentMaxTools');
|
|
var roundsInput = el('set-agentMaxRounds');
|
|
var supInput = el('set-agentSupervisorLadder');
|
|
var approvalModeInput = el('set-toolApprovalMode');
|
|
var msg = el('set-agentMsg');
|
|
if (!toolsInput) return;
|
|
|
|
try {
|
|
var res = await fetch('/api/auth/settings', { credentials: 'same-origin' });
|
|
var settings = await res.json();
|
|
if (settings.agent_max_tool_calls) toolsInput.value = settings.agent_max_tool_calls;
|
|
if (roundsInput && settings.agent_max_rounds) roundsInput.value = settings.agent_max_rounds;
|
|
if (supInput) supInput.checked = !!settings.agent_supervisor_ladder;
|
|
if (approvalModeInput) {
|
|
approvalModeInput.value =
|
|
settings.tool_approval_mode === 'off' ? 'off' : 'ask';
|
|
}
|
|
var approvalEnvNote = el('set-toolApprovalEnvNote');
|
|
if (approvalEnvNote) {
|
|
if (settings.tool_approval_gate_env_disabled) {
|
|
approvalEnvNote.textContent =
|
|
'Forced by deployment: ODYSSEUS_TOOL_APPROVAL_GATE is set to a falsy value on this server, so the approval gate is off for every run regardless of this setting.';
|
|
approvalEnvNote.style.display = 'block';
|
|
} else {
|
|
approvalEnvNote.textContent = '';
|
|
approvalEnvNote.style.display = 'none';
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
|
|
// Clamp + coerce a raw input to an int in [lo, hi]; falls back to `dflt`
|
|
// when blank/non-numeric. Mirrors the server-side validation.
|
|
function clampInt(raw, lo, hi, dflt) {
|
|
var n = parseInt(raw, 10);
|
|
if (isNaN(n)) return dflt;
|
|
return Math.max(lo, Math.min(n, hi));
|
|
}
|
|
|
|
async function save() {
|
|
var tools = clampInt(toolsInput.value, 0, 1000, 0);
|
|
var rounds = roundsInput ? clampInt(roundsInput.value, 1, 200, 20) : null;
|
|
toolsInput.value = tools; // reflect the clamped value
|
|
if (roundsInput) roundsInput.value = rounds;
|
|
var payload = { agent_max_tool_calls: tools };
|
|
if (rounds != null) payload.agent_max_rounds = rounds;
|
|
if (supInput) payload.agent_supervisor_ladder = !!supInput.checked;
|
|
if (approvalModeInput) {
|
|
payload.tool_approval_mode =
|
|
approvalModeInput.value === 'off' ? 'off' : 'ask';
|
|
}
|
|
try {
|
|
await _postSettings(payload);
|
|
msg.textContent = (tools > 0 ? 'Limit: ' + tools + ' tool calls' : 'Unlimited tool calls') +
|
|
(rounds != null ? ' · ' + rounds + ' steps/message' : '') +
|
|
(supInput && supInput.checked ? ' · supervisor on' : '');
|
|
msg.style.color = 'var(--fg)';
|
|
} catch (e) { msg.textContent = e.status === 403 ? 'Admin access is required to change these settings.' : 'Failed to save'; msg.style.color = 'var(--red)'; }
|
|
}
|
|
|
|
toolsInput.addEventListener('change', save);
|
|
if (roundsInput) roundsInput.addEventListener('change', save);
|
|
if (supInput) supInput.addEventListener('change', save);
|
|
if (approvalModeInput) approvalModeInput.addEventListener('change', save);
|
|
var cur = parseInt(toolsInput.value, 10) || 0;
|
|
var curR = roundsInput ? (parseInt(roundsInput.value, 10) || 20) : null;
|
|
msg.textContent = (cur > 0 ? 'Limit: ' + cur + ' tool calls' : 'Unlimited tool calls') +
|
|
(curR != null ? ' · ' + curR + ' steps/message' : '') +
|
|
(supInput && supInput.checked ? ' · supervisor on' : '');
|
|
|
|
}
|