Consolidate Odysseus agent harness and tool contracts

This commit is contained in:
pewdiepie-archdaemon
2026-09-17 10:07:40 +00:00
parent 84aa9a91de
commit 218d762427
229 changed files with 28899 additions and 1551 deletions
+150 -21
View File
@@ -6,18 +6,18 @@
// ES6 module — IIFE removed
import Storage from './storage.js';
import uiModule from './ui.js?v=20260908weekhoverfix1';
import uiModule from './ui.js?v=20260916largetoolscroll1';
import sessionModule from './sessions.js';
import chatRenderer, { renderToolIcon } from './chatRenderer.js?v=20260910streamlinks2';
import chatStream from './chatStream.js?v=20260909cardlayout1';
import chatRenderer, { renderToolIcon } from './chatRenderer.js?v=20260914metricssummary1';
import chatStream from './chatStream.js?v=20260913richdiff1';
import { addAITTSButton } from './tts-ai.js';
import markdownModule from './markdown.js';
import spinnerModule from './spinner.js';
import presetsModule from './presets.js?v=20260908personaname1';
import fileHandlerModule from './fileHandler.js?v=20260909mobileattachmentedit1';
import searchModule from './search.js';
import documentModule from './document.js?v=20260911removealignrightshortcut1';
import * as emailInbox from './emailInbox.js?v=20260903emailsend2';
import documentModule from './document.js?v=20260916docctx2';
import * as emailInbox from './emailInbox.js?v=20260914aireply4';
import codeRunnerModule from './codeRunner.js?v=20260831richtexttools91';
import slashCommands, { initSlashCommands, isCommand, handleSlashCommand, handleSetupInput, handleSetupWizard, typewriterInto } from './slashCommands.js?v=20260902tuiharness1';
import createResearchSynapse from './researchSynapse.js?v=20260910roundlabels2';
@@ -277,7 +277,10 @@ import { invalidateSettings } from './appConfig.js';
function _renderContextHeaderRing(pill, pct) {
const value = Math.max(0, Math.min(100, Number(pct || 0)));
pill.style.setProperty('--ctx-color', _contextRingColor(value));
pill.innerHTML = _contextRingMarkup(value);
if (false) {
pill.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>';
}
}
function _clampAutoCompactThreshold(value) {
@@ -300,7 +303,18 @@ import { invalidateSettings } from './appConfig.js';
const hashId = _hashSessionCandidate();
const lastSelectedId = String(window.__odysseusLastSelectedSessionId || '').trim();
const targetId = activeRowId || hashId || lastSelectedId;
if (!targetId) return '';
if (!targetId) {
// New chats are deliberately held in memory until the first prompt.
// Per-chat settings are still actionable before that prompt, so create
// the pending session when a setting needs a real session id.
if (adopt && sm?.hasPendingChat?.() && sm?.materializePendingSession) {
try {
await sm.materializePendingSession();
} catch (_) {}
return (sm.getCurrentSessionId && sm.getCurrentSessionId()) || '';
}
return '';
}
if (!adopt) return targetId;
try {
window.__odysseusComposerUserEdited = true;
@@ -408,8 +422,35 @@ import { invalidateSettings } from './appConfig.js';
await _setChatMemoryExtraction(!memoryToggle.classList.contains('active'), memoryToggle, memoryState);
});
memoryRow.appendChild(memoryCopy);
memoryRow.appendChild(memoryToggle);
popup.appendChild(memoryRow);
memoryRow.appendChild(memoryToggle);
popup.appendChild(memoryRow);
const memoryInjectionOn = d.memory_injection_enabled !== false;
const memoryInjectionRow = document.createElement('div');
memoryInjectionRow.className = 'chat-context-toggle-row';
const memoryInjectionCopy = document.createElement('div');
memoryInjectionCopy.className = 'chat-context-toggle-copy';
const memoryInjectionLabel = document.createElement('span');
memoryInjectionLabel.textContent = 'Memory injection';
const memoryInjectionState = document.createElement('span');
memoryInjectionState.className = 'chat-context-toggle-state';
memoryInjectionState.textContent = memoryInjectionOn ? 'On' : 'Off';
memoryInjectionCopy.appendChild(memoryInjectionLabel);
memoryInjectionCopy.appendChild(memoryInjectionState);
const memoryInjectionToggle = document.createElement('button');
memoryInjectionToggle.type = 'button';
memoryInjectionToggle.className = `chat-context-toggle${memoryInjectionOn ? ' active' : ''}`;
memoryInjectionToggle.setAttribute('role', 'switch');
memoryInjectionToggle.setAttribute('aria-label', 'Memory injection for this chat');
memoryInjectionToggle.setAttribute('aria-checked', memoryInjectionOn ? 'true' : 'false');
memoryInjectionToggle.addEventListener('click', async (e) => {
e.preventDefault();
e.stopPropagation();
await _setChatMemoryInjection(!memoryInjectionToggle.classList.contains('active'), memoryInjectionToggle, memoryInjectionState);
});
memoryInjectionRow.appendChild(memoryInjectionCopy);
memoryInjectionRow.appendChild(memoryInjectionToggle);
popup.appendChild(memoryInjectionRow);
const skillsOn = d.skill_injection_enabled !== false;
const skillsRow = document.createElement('div');
@@ -438,6 +479,7 @@ import { invalidateSettings } from './appConfig.js';
skillsRow.appendChild(skillsToggle);
popup.appendChild(skillsRow);
if (d.thinking_supported) {
const thinkingOn = d.thinking_mode === 'on';
const thinkingRow = document.createElement('div');
thinkingRow.className = 'chat-context-toggle-row';
@@ -458,6 +500,7 @@ import { invalidateSettings } from './appConfig.js';
});
thinkingRow.appendChild(thinkingToggle);
popup.appendChild(thinkingRow);
}
const addGenerationSlider = (label, value, min, max, step, formatter, key) => {
const row = document.createElement('div');
@@ -657,6 +700,41 @@ import { invalidateSettings } from './appConfig.js';
}
}
async function _setChatMemoryInjection(enabled, toggleBtn, stateText) {
const sid = await _resolveCurrentSessionId({ adopt: true });
if (!sid) {
uiModule.showToast('Open a chat first');
return false;
}
const next = !!enabled;
if (toggleBtn) toggleBtn.disabled = true;
try {
const res = await fetch(`/api/session/${encodeURIComponent(sid)}/memory-injection`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin',
body: JSON.stringify({ enabled: next }),
});
if (!res.ok) throw new Error(await res.text());
_contextHeaderData = {
...(_contextHeaderData || {}),
memory_injection_enabled: next,
};
if (toggleBtn) {
toggleBtn.classList.toggle('active', next);
toggleBtn.setAttribute('aria-checked', next ? 'true' : 'false');
}
if (stateText) stateText.textContent = next ? 'On' : 'Off';
uiModule.showToast(next ? 'Memory injection on for this chat' : 'Memory injection off for this chat');
return true;
} catch (err) {
uiModule.showError(`Could not save memory injection: ${err.message || err}`);
return false;
} finally {
if (toggleBtn) toggleBtn.disabled = false;
}
}
async function _saveChatGenerationSettings(change) {
const sid = await _resolveCurrentSessionId({ adopt: true });
if (!sid) return false;
@@ -2189,6 +2267,10 @@ import { invalidateSettings } from './appConfig.js';
const _bubbleMode = (_toggleStateForBubble.mode || 'chat') === 'agent' ? 'agent' : 'chat';
const _bubbleMeta = _pendingAttachInfo ? { attachments: _pendingAttachInfo } : {};
_bubbleMeta.interaction_mode = _bubbleMode;
if (docSel) {
_bubbleMeta.document_id = documentModule?.getCurrentDocId?.() || '';
_bubbleMeta.document_selections = Array.isArray(docSel) ? docSel : [docSel];
}
_userMsgEl = addMessage('user', userDisplay, null, _bubbleMeta);
}
_sendPerf.mark('user_bubble_visible');
@@ -2408,6 +2490,11 @@ import { invalidateSettings } from './appConfig.js';
}
fd.append('active_doc_id', activeDocIdForSend);
}
// A minimized mobile sheet remains linked to chat even though it is not
// visually mounted. An explicit tab close returns no document id.
fd.append('active_doc_state', activeDocIdForSend
? (documentModule?.isPanelOpen?.() ? 'visible' : 'minimized')
: 'none');
// Active email context — when an email reader is open, pass its
// uid/folder/account so "reply", "summarize", "what does this say"
// resolve to the email the user is actually looking at instead of
@@ -3157,6 +3244,7 @@ import { invalidateSettings } from './appConfig.js';
let _liveThinkHeader = null;
let _liveThinkSpinnerSlot = null;
let _liveThinkTimerEl = null;
let _liveThinkSpinner = null;
let _liveThinkTokenCount = 0;
let _liveThinkToggle = null;
let _liveThinkDomId = null;
@@ -3254,6 +3342,22 @@ import { invalidateSettings } from './appConfig.js';
_startLiveThinkTimer();
}
function _removeLiveThinkingSpinner() {
const liveSpinner = _liveThinkSpinner;
_liveThinkSpinner = null;
if (liveSpinner) {
const wrapper = liveSpinner.element;
try { liveSpinner.destroy(); } catch (_) {}
// createWhirlpool returns an outer wrapper around the Spinner's
// inner element; destroy() removes only the inner element.
wrapper?.remove?.();
}
if (_liveThinkSpinnerSlot) {
_liveThinkSpinnerSlot.replaceChildren();
_liveThinkSpinnerSlot = null;
}
}
_flushLiveThinking = ({ text = null, rich = false } = {}) => {
if (text !== null) _queueLiveThinking(text, true);
if (_liveThinkRenderThrottle) _liveThinkRenderThrottle.flush();
@@ -3269,6 +3373,7 @@ import { invalidateSettings } from './appConfig.js';
_liveThinkRenderThrottle = null;
_stopLiveThinkTimer();
_cancelThinkingGrace();
_removeLiveThinkingSpinner();
};
function _finalizeLiveThinking(text, rich = true) {
@@ -3314,7 +3419,7 @@ import { invalidateSettings } from './appConfig.js';
const elapsed = thinkingStartTime ? ((Date.now() - thinkingStartTime) / 1000).toFixed(1) : null;
if (_liveThinkHeader) _liveThinkHeader.textContent = 'View thinking process';
if (_liveThinkTimerEl) _liveThinkTimerEl.textContent = elapsed ? _formatThinkStats(elapsed, _liveThinkTokenCount) : '';
if (_liveThinkSpinnerSlot) _liveThinkSpinnerSlot.remove();
_removeLiveThinkingSpinner();
}
function _cancelThinkingGrace() {
@@ -3356,7 +3461,7 @@ import { invalidateSettings } from './appConfig.js';
roundText = roundText.replace(/<think>/i, '<think time="' + elapsed + '">');
}
if (_liveThinkHeader) _liveThinkHeader.textContent = 'View thinking process';
if (_liveThinkSpinnerSlot) _liveThinkSpinnerSlot.remove();
_removeLiveThinkingSpinner();
if (_liveThinkTimerEl && elapsed) {
_liveThinkTimerEl.textContent = _formatThinkStats(elapsed, _liveThinkTokenCount);
_liveThinkTimerEl.style.marginLeft = 'auto';
@@ -3710,7 +3815,7 @@ import { invalidateSettings } from './appConfig.js';
roundText = roundText.replace(/<think>/i, '<think time="' + _elapsedDone + '">');
}
if (_liveThinkHeader) _liveThinkHeader.textContent = 'View thinking process';
if (_liveThinkSpinnerSlot) _liveThinkSpinnerSlot.remove();
_removeLiveThinkingSpinner();
if (_liveThinkTimerEl && _elapsedDone) {
_liveThinkTimerEl.textContent = _formatThinkStats(_elapsedDone, _liveThinkTokenCount);
_liveThinkTimerEl.style.marginLeft = 'auto';
@@ -4016,12 +4121,12 @@ import { invalidateSettings } from './appConfig.js';
_queueLiveThinking(roundText);
// Whirlpool spinner
if (_liveThinkSpinnerSlot) {
var _wp = spinnerModule.createWhirlpool(12);
_wp.element.style.margin = '0';
_wp.element.style.width = '12px';
_wp.element.style.height = '12px';
_wp.element.style.transform = 'translateY(-1px)'; // align the whirlpool with the header text
_liveThinkSpinnerSlot.appendChild(_wp.element);
_liveThinkSpinner = spinnerModule.createWhirlpool(12);
_liveThinkSpinner.element.style.margin = '0';
_liveThinkSpinner.element.style.width = '12px';
_liveThinkSpinner.element.style.height = '12px';
_liveThinkSpinner.element.style.transform = 'translateY(-1px)'; // align the whirlpool with the header text
_liveThinkSpinnerSlot.appendChild(_liveThinkSpinner.element);
}
if (_thinkingRecheckAt) _scheduleThinkingGrace();
} else if (hasUnclosedThink && isThinking) {
@@ -4460,6 +4565,13 @@ import { invalidateSettings } from './appConfig.js';
if (holder && json.id) holder.dataset.dbId = json.id;
} else if (json.type === 'tool_start') {
// A tool call is the model's first completed output for this
// round, even though it is rendered as a structured card
// rather than prose. Stop the initial TTFT ticker here so
// browser/search execution time is not later mislabeled as
// "waiting for first token" on the continuation bubble. The
// running tool card owns elapsed time until tool_output.
markFirstVisibleOutput();
_closeOpenThinkingMarkup(_isBg);
if (_isBg) continue;
_cancelThinkingTimer();
@@ -6678,10 +6790,14 @@ import { invalidateSettings } from './appConfig.js';
const saveBtn = document.createElement('button');
saveBtn.className = 'edit-save-btn';
saveBtn.textContent = 'Send';
saveBtn.type = 'button';
saveBtn.title = 'Send edited message';
saveBtn.innerHTML = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5"></path><path d="m5 12 7-7 7 7"></path></svg><span>Send</span>';
const cancelBtn = document.createElement('button');
cancelBtn.className = 'edit-cancel-btn';
cancelBtn.textContent = 'Cancel';
cancelBtn.type = 'button';
cancelBtn.title = 'Cancel editing';
cancelBtn.innerHTML = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg><span>Cancel</span>';
btnRow.appendChild(saveBtn);
btnRow.appendChild(cancelBtn);
@@ -7821,7 +7937,7 @@ import { invalidateSettings } from './appConfig.js';
}
} catch (e) {
console.error('open attachment as document failed', e);
import('./ui.js?v=20260908weekhoverfix1').then(m => m.showError && m.showError('Could not open attachment')).catch(() => {});
import('./ui.js?v=20260916largetoolscroll1').then(m => m.showError && m.showError('Could not open attachment')).catch(() => {});
window.open(url, '_blank'); // fallback so the file is still reachable
}
}
@@ -7855,12 +7971,25 @@ import { invalidateSettings } from './appConfig.js';
continueFrom,
_appendViewReportLink,
hasActiveStream,
openContextSettings: () => {
openContextSettings: async () => {
const pill = document.getElementById('chat-context-pill');
if (pill && !pill.hidden) {
pill.click();
return true;
}
const sm = _liveSessionModule();
if (sm?.hasPendingChat?.() && sm?.materializePendingSession) {
try {
const materialized = await sm.materializePendingSession();
if (materialized) {
await refreshChatContextHeader('open-context-settings');
if (pill && !pill.hidden) {
pill.click();
return true;
}
}
} catch (_) {}
}
return false;
},
};