v1.2.0: advisor is per session and off by default (new and existing sessions); old global setting ignored

This commit is contained in:
Agent committed 2026-10-11 05:16:05 +00:00
1 parent f85f445ef6
commit 0f423e3699
6 files changed
+152 -55

No files matched your search

+26 -13
View File
@@ -85,14 +85,15 @@ window.__ModuleLoader__.load({
const fill = (s, v) => String(s).replace(/\{(\w+)\}/g, (_, k) => (v && v[k] !== undefined ? v[k] : ''));
const effortName = (e) => (e?.id === 'xhigh' ? 'Extra' : e?.name ?? e?.id);
// ---- host settings store (shared by every session's control) ----
const settings = (() => {
// ---- host settings, one store per session (the advisor is chosen per session) ----
function makeStore(sessionId) {
let value = null;
let inflight = null;
const subs = new Set();
const emit = () => subs.forEach((f) => f());
const url = `omp-advisor/settings?session=${encodeURIComponent(sessionId)}`;
async function call(init) {
const res = await fetch('omp-advisor/settings', { ...init, headers: { accept: 'application/json', ...(init?.body ? { 'content-type': 'application/json' } : {}) } });
const res = await fetch(url, { ...init, headers: { accept: 'application/json', ...(init?.body ? { 'content-type': 'application/json' } : {}) } });
let json = {};
try { json = await res.json(); } catch { /* empty */ }
if (!res.ok) throw new Error(json.message || res.statusText || String(res.status));
@@ -112,11 +113,17 @@ window.__ModuleLoader__.load({
try { value = await call({ method: 'POST', body: JSON.stringify(patch) }); emit(); } catch (e) { value = prev; emit(); throw e; }
},
};
})();
}
const stores = new Map();
const storeFor = (sessionId) => {
let s = stores.get(sessionId);
if (!s) { s = makeStore(sessionId); stores.set(sessionId, s); }
return s;
};
function useSettings() {
const v = React.useSyncExternalStore(settings.subscribe, settings.get);
React.useEffect(() => { if (v === null) settings.load(); }, [v]);
function useSettings(store) {
const v = React.useSyncExternalStore(store.subscribe, store.get);
React.useEffect(() => { if (v === null) store.load(); }, [store, v]);
return v;
}
@@ -136,9 +143,15 @@ window.__ModuleLoader__.load({
return { left: Math.max(8, left), top: Math.max(8, Math.min(top, vh - m.height - 8)) };
}
function AdvisorPicker({ directory, load, t }) {
React.useEffect(installCss, []);
const st = useSettings();
function AdvisorPicker(props) {
// Session-scoped slot; without an id (draft composer) there is nothing to configure yet.
if (typeof props.sessionId !== 'string' || !props.sessionId) return null;
return h(AdvisorPickerFor, props);
}
function AdvisorPickerFor({ sessionId, directory, load, t }) {
const store = storeFor(sessionId);
const st = useSettings(store);
const dir = React.useSyncExternalStore((f) => directory.subscribe(f), () => directory.getSnapshot());
const [open, setOpen] = React.useState(false);
const [hover, setHover] = React.useState(null); // { key, el, model, group }
@@ -151,7 +164,7 @@ window.__ModuleLoader__.load({
const subRef = React.useRef(null);
const hoverTimer = React.useRef(null);
const enabled = st?.enabled !== false;
const enabled = st?.enabled === true; // off until this session turns it on (also while loading)
const own = !!(st?.provider && st?.model);
const groups = dir?.groups ?? [];
const current = own ? groups.find((g) => g.id === st.provider)?.models.find((m) => m.id === st.model) : null;
@@ -187,7 +200,7 @@ window.__ModuleLoader__.load({
const choose = async (patch) => {
setErr(null);
setOpen(false);
try { await settings.save(patch); } catch (e) { setErr(String(e.message || e)); setOpen(true); }
try { await store.save(patch); } catch (e) { setErr(String(e.message || e)); setOpen(true); }
};
const pickModel = (group, model, effort) => choose({
enabled: true, provider: group.id, model: model.id,
@@ -292,7 +305,7 @@ window.__ModuleLoader__.load({
const directory = ctx.modelDirectories.directoryFor(sessionId);
// Read-only use of the directory: the advisor never calls directory.select (that would
// switch the agent's own model).
return { directory: directory.store, load: () => { directory.load().catch(() => {}); } };
return { sessionId, directory: directory.store, load: () => { directory.load().catch(() => {}); } };
},
}, AdvisorPicker));
},