mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-10 18:22:20 +02:00
fix(agent): harden approval lifecycle
This commit is contained in:
+4
-4
@@ -10,9 +10,9 @@ import modelsModule from './js/models.js?v=20260715startupcalm2';
|
||||
import ragModule from './js/rag.js';
|
||||
import presetsModule from './js/presets.js';
|
||||
import searchModule from './js/search.js';
|
||||
import chatModule from './js/chat.js?v=20260815toolapproval3';
|
||||
import chatModule from './js/chat.js?v=20260815toolapproval4';
|
||||
import compareModule from './js/compare/index.js?v=20260723compareicon2';
|
||||
import documentModule from './js/document.js?v=20260722emailfastindex1';
|
||||
import documentModule from './js/document.js?v=20260815approvalsave1';
|
||||
import searchChatModule from './js/search-chat.js';
|
||||
import { makeWindowDraggable } from './js/windowDrag.js';
|
||||
import {
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
settleSessionHydration
|
||||
} from './js/startupShell.js';
|
||||
import markdownModule from './js/markdown.js';
|
||||
import chatRenderer from './js/chatRenderer.js?v=20260815toolapproval3';
|
||||
import chatRenderer from './js/chatRenderer.js?v=20260815toolapproval4';
|
||||
import sessionModule from './js/sessions.js';
|
||||
import memoryModule from './js/memory.js?v=20260722memoryloading1';
|
||||
import voiceRecorderModule from './js/voiceRecorder.js';
|
||||
@@ -33,7 +33,7 @@ import tasksModule from './js/tasks.js?v=20260723tasksbulkfeedback1';
|
||||
import calendarModule from './js/calendar.js';
|
||||
import notesModule from './js/notes.js';
|
||||
import adminModule from './js/admin.js?v=20260716openrouter3';
|
||||
import settingsModule from './js/settings.js?v=20260722emailfastindex1';
|
||||
import settingsModule from './js/settings.js?v=20260815approvalsave1';
|
||||
// Eagerly bind unified minimize/restore behavior across all tool modals.
|
||||
import './js/modalManager.js?v=20260723compareicon2';
|
||||
// Desktop window tiling — drag a modal near an edge/corner to snap.
|
||||
|
||||
+8
-8
@@ -258,8 +258,8 @@
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/static/fonts/FiraCode-Regular.woff2">
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="/static/fonts/FiraCode-SemiBold.woff2">
|
||||
<link rel="stylesheet" href="/static/style.css?v=20260808startupshell1">
|
||||
<link rel="modulepreload" href="/static/app.js?v=20260815toolapproval3">
|
||||
<link rel="modulepreload" href="/static/js/chat.js?v=20260815toolapproval3">
|
||||
<link rel="modulepreload" href="/static/app.js?v=20260815toolapproval4">
|
||||
<link rel="modulepreload" href="/static/js/chat.js?v=20260815toolapproval4">
|
||||
<link rel="modulepreload" href="/static/js/ui.js">
|
||||
<link rel="modulepreload" href="/static/js/sessions.js">
|
||||
<link rel="modulepreload" href="/static/js/markdown.js">
|
||||
@@ -2532,20 +2532,20 @@
|
||||
<script type="module" src="/static/js/search.js"></script>
|
||||
<script type="module" src="/static/js/spinner.js"></script>
|
||||
<script type="module" src="/static/js/tts-ai.js"></script>
|
||||
<script type="module" src="/static/js/document.js?v=20260722emailfastindex1"></script>
|
||||
<script type="module" src="/static/js/document.js?v=20260815approvalsave1"></script>
|
||||
<script type="module" src="/static/js/gallery.js?v=20260708match1"></script>
|
||||
<script type="module" src="/static/js/chatRenderer.js?v=20260815toolapproval3"></script>
|
||||
<script type="module" src="/static/js/chatRenderer.js?v=20260815toolapproval4"></script>
|
||||
<script type="module" src="/static/js/codeRunner.js"></script>
|
||||
<script type="module" src="/static/js/chatStream.js?v=20260722emailfastindex1"></script>
|
||||
<script type="module" src="/static/js/chat.js?v=20260815toolapproval3"></script>
|
||||
<script type="module" src="/static/js/chatStream.js?v=20260815approvalsave1"></script>
|
||||
<script type="module" src="/static/js/chat.js?v=20260815toolapproval4"></script>
|
||||
<script type="module" src="/static/js/cookbook.js"></script>
|
||||
<script src="/static/js/cookbookSchedule.js"></script>
|
||||
<script type="module" src="/static/js/search-chat.js"></script>
|
||||
<script type="module" src="/static/js/theme.js"></script>
|
||||
<script type="module" src="/static/js/censor.js"></script>
|
||||
<script type="module" src="/static/js/settings.js?v=20260723compareicon1"></script>
|
||||
<script type="module" src="/static/js/settings.js?v=20260815approvalsave1"></script>
|
||||
<script type="module" src="/static/js/assistant.js"></script>
|
||||
<script type="module" src="/static/app.js?v=20260815toolapproval3"></script> <!-- app.js must be LAST -->
|
||||
<script type="module" src="/static/app.js?v=20260815toolapproval4"></script> <!-- app.js must be LAST -->
|
||||
<script type="module" src="/static/js/init.js?v=20260715freshroot3"></script>
|
||||
<script type="module" src="/static/js/a11y.js"></script>
|
||||
<script nonce="{{CSP_NONCE}}">if('serviceWorker' in navigator){navigator.serviceWorker.register('/static/sw.js').catch(()=>{});}</script>
|
||||
|
||||
+86
-34
@@ -8,18 +8,18 @@
|
||||
import Storage from './storage.js';
|
||||
import uiModule from './ui.js';
|
||||
import sessionModule from './sessions.js';
|
||||
import chatRenderer from './chatRenderer.js?v=20260815toolapproval3';
|
||||
import chatStream from './chatStream.js';
|
||||
import chatRenderer from './chatRenderer.js?v=20260815toolapproval4';
|
||||
import chatStream from './chatStream.js?v=20260815approvalsave1';
|
||||
import { addAITTSButton } from './tts-ai.js';
|
||||
import markdownModule from './markdown.js';
|
||||
import spinnerModule from './spinner.js';
|
||||
import presetsModule from './presets.js';
|
||||
import fileHandlerModule from './fileHandler.js';
|
||||
import searchModule from './search.js';
|
||||
import documentModule from './document.js?v=20260722emailfastindex1';
|
||||
import * as emailInbox from './emailInbox.js?v=20260722emailfastindex1';
|
||||
import documentModule from './document.js?v=20260815approvalsave1';
|
||||
import * as emailInbox from './emailInbox.js?v=20260815approvalsave1';
|
||||
import codeRunnerModule from './codeRunner.js';
|
||||
import slashCommands, { initSlashCommands, isCommand, handleSlashCommand, handleSetupInput, handleSetupWizard, typewriterInto } from './slashCommands.js?v=20260722emailfastindex1';
|
||||
import slashCommands, { initSlashCommands, isCommand, handleSlashCommand, handleSetupInput, handleSetupWizard, typewriterInto } from './slashCommands.js?v=20260815approvalsave1';
|
||||
import createResearchSynapse from './researchSynapse.js';
|
||||
import { createStreamRenderer } from './streamingRenderer.js';
|
||||
import { wireArrowUpRecall, getUserMessagesFromChatHistory } from './composerArrowUpRecall.js?v=20260714promptrecall';
|
||||
@@ -72,6 +72,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
}
|
||||
const input = document.getElementById('message');
|
||||
if (input) {
|
||||
_pendingToolApproval.draft = input.value || '';
|
||||
input.value = label;
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
@@ -86,6 +87,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
_pendingToolApproval = {
|
||||
approval_id: String(detail.approval_id),
|
||||
decision,
|
||||
document_id: String(detail.document_id || ''),
|
||||
};
|
||||
_submitToolApprovalWhenIdle(
|
||||
_pendingToolApproval.approval_id,
|
||||
@@ -1267,6 +1269,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
if (_sendInFlight) return;
|
||||
const _sendPerf = _createChatSendPerf();
|
||||
_sendInFlight = true;
|
||||
const approvalForSend = _pendingToolApproval;
|
||||
_setForegroundChatBusy(true);
|
||||
// Instant visual feedback so the user sees their click was accepted
|
||||
// even before the streaming button state kicks in below.
|
||||
@@ -1281,7 +1284,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
};
|
||||
|
||||
// --- Setup mode: intercept next message (but let slash commands through) ---
|
||||
{
|
||||
if (!approvalForSend) {
|
||||
const el = uiModule.el;
|
||||
const rawMsg = (el('message').value || '').trim();
|
||||
const currentSetupMode = slashCommands.getSetupMode();
|
||||
@@ -1311,7 +1314,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
if (!msg.trim() && !fileHandlerModule.getPendingCount() && !(_pendingRegenAttachments && _pendingRegenAttachments.length)) { _releaseSendFlag(); return; }
|
||||
|
||||
// --- Slash commands: execute directly without AI (no session needed) ---
|
||||
if (isCommand(msg.trim())) {
|
||||
if (!approvalForSend && isCommand(msg.trim())) {
|
||||
const handled = await handleSlashCommand(msg.trim());
|
||||
if (handled) {
|
||||
el('message').value = '';
|
||||
@@ -1438,7 +1441,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
}
|
||||
|
||||
// --- API key guard: warn if message looks like an API key ---
|
||||
if (API_KEY_RE.test(msg.trim())) {
|
||||
if (!approvalForSend && API_KEY_RE.test(msg.trim())) {
|
||||
if (!await window.styledConfirm('This looks like an API key. Sending it to the AI could expose it.\n\nDid you mean to use /setup instead?', { confirmText: 'Send anyway', danger: true })) {
|
||||
_releaseSendFlag();
|
||||
return;
|
||||
@@ -1573,7 +1576,9 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
if (sessionModule.clearStreamComplete) sessionModule.clearStreamComplete(sessionModule.getCurrentSessionId());
|
||||
|
||||
// Check for document selection context before consuming display override
|
||||
const docSel = documentModule && documentModule.getSelectionContext();
|
||||
const docSel = !approvalForSend && documentModule
|
||||
? documentModule.getSelectionContext()
|
||||
: null;
|
||||
if (docSel) {
|
||||
const sels = Array.isArray(docSel) ? docSel : [docSel];
|
||||
const lineRefs = sels.map(s =>
|
||||
@@ -1593,7 +1598,9 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
// stuck flag can't silently eat the next turn's recovery budget.
|
||||
if (!skipBubble) { _autoNudges = 0; _autoContinuePending = false; }
|
||||
else if (_autoContinuePending) { _autoContinuePending = false; }
|
||||
const _pendingAttachInfo = fileHandlerModule.getPendingCount() ? fileHandlerModule.getPendingInfo() : null;
|
||||
const _pendingAttachInfo = !approvalForSend && fileHandlerModule.getPendingCount()
|
||||
? fileHandlerModule.getPendingInfo()
|
||||
: null;
|
||||
// Pre-read importable file contents before upload clears pending files
|
||||
const IMPORTABLE_EXT = /\.(txt|py|js|ts|html|htm|css|md|json|csv|yml|yaml|sh|sql|rs|go|java|c|cpp|h|rb|php|xml|jsx|tsx|log|toml|ini|conf|env|vue|svelte|scss|sass|less)$/i;
|
||||
const _importableFiles = [];
|
||||
@@ -1611,7 +1618,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
_userMsgEl = addMessage('user', userDisplay, null, _pendingAttachInfo ? { attachments: _pendingAttachInfo } : null);
|
||||
}
|
||||
_sendPerf.mark('user_bubble_visible');
|
||||
messageInput.value = '';
|
||||
messageInput.value = approvalForSend ? (approvalForSend.draft || '') : '';
|
||||
messageInput.style.height = '';
|
||||
messageInput.dispatchEvent(new Event('input'));
|
||||
// Mobile: dismiss the on-screen keyboard after sending. iOS in
|
||||
@@ -1645,13 +1652,15 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
}
|
||||
|
||||
let ids = [];
|
||||
try {
|
||||
_sendPerf.mark('upload_begin');
|
||||
ids = await fileHandlerModule.uploadPending({ sessionId: sessionModule.getCurrentSessionId() });
|
||||
_sendPerf.mark('upload_done');
|
||||
} catch(e) {
|
||||
console.error('upload failed', e);
|
||||
_sendPerf.mark('upload_failed');
|
||||
if (!approvalForSend) {
|
||||
try {
|
||||
_sendPerf.mark('upload_begin');
|
||||
ids = await fileHandlerModule.uploadPending({ sessionId: sessionModule.getCurrentSessionId() });
|
||||
_sendPerf.mark('upload_done');
|
||||
} catch(e) {
|
||||
console.error('upload failed', e);
|
||||
_sendPerf.mark('upload_failed');
|
||||
}
|
||||
}
|
||||
if (_pendingAttachInfo && !ids.length && !(_pendingRegenAttachments && _pendingRegenAttachments.length)) {
|
||||
if (_userMsgEl && _userMsgEl.parentNode) _userMsgEl.remove();
|
||||
@@ -1668,10 +1677,10 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
// edited OCR text via the server-side .vision cache). Always CONSUME the
|
||||
// slot — even when empty / errored — so the regen ids can't bleed into
|
||||
// an unrelated next message if uploadPending() above had thrown.
|
||||
if (_pendingRegenAttachments && _pendingRegenAttachments.length) {
|
||||
if (!approvalForSend && _pendingRegenAttachments && _pendingRegenAttachments.length) {
|
||||
ids = ids.concat(_pendingRegenAttachments);
|
||||
}
|
||||
_pendingRegenAttachments = null;
|
||||
if (!approvalForSend) _pendingRegenAttachments = null;
|
||||
|
||||
// The optimistic user bubble was rendered before the upload assigned ids,
|
||||
// so image previews couldn't show (the renderer needs att.id). Now that
|
||||
@@ -1752,14 +1761,50 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
if (activeEmailComposerCtx?.docId) {
|
||||
activeDocIdForSend = activeEmailComposerCtx.docId;
|
||||
}
|
||||
if (documentModule && activeDocIdForSend) {
|
||||
const shouldSaveActiveDoc = !approvalForSend || (
|
||||
approvalForSend.document_id
|
||||
&& approvalForSend.document_id === activeDocIdForSend
|
||||
);
|
||||
if (documentModule && activeDocIdForSend && shouldSaveActiveDoc) {
|
||||
try {
|
||||
_sendPerf.mark('doc_save_begin');
|
||||
await documentModule.saveDocument();
|
||||
const documentSaved = await documentModule.saveDocument({
|
||||
silent: !!approvalForSend,
|
||||
});
|
||||
_sendPerf.mark('doc_save_done');
|
||||
if (approvalForSend && documentSaved === false) {
|
||||
if (_userMsgEl && _userMsgEl.parentNode) _userMsgEl.remove();
|
||||
if (
|
||||
_pendingToolApproval
|
||||
&& _pendingToolApproval.approval_id === approvalForSend.approval_id
|
||||
) {
|
||||
_pendingToolApproval = null;
|
||||
}
|
||||
uiModule.showError && uiModule.showError(
|
||||
'Document could not be saved, so the action was not approved. Reload the chat to retry.'
|
||||
);
|
||||
updateSubmitButton('idle', submitBtn);
|
||||
_releaseSendFlag();
|
||||
return;
|
||||
}
|
||||
} catch(e) {
|
||||
console.warn('doc auto-save failed', e);
|
||||
_sendPerf.mark('doc_save_failed');
|
||||
if (approvalForSend) {
|
||||
if (_userMsgEl && _userMsgEl.parentNode) _userMsgEl.remove();
|
||||
if (
|
||||
_pendingToolApproval
|
||||
&& _pendingToolApproval.approval_id === approvalForSend.approval_id
|
||||
) {
|
||||
_pendingToolApproval = null;
|
||||
}
|
||||
uiModule.showError && uiModule.showError(
|
||||
'Document could not be saved, so the action was not approved. Reload the chat to retry.'
|
||||
);
|
||||
updateSubmitButton('idle', submitBtn);
|
||||
_releaseSendFlag();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1789,23 +1834,30 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
const fd = new FormData();
|
||||
fd.append('message', _finalMsgWithInject);
|
||||
fd.append('session', streamSessionId);
|
||||
if (_pendingToolApproval) {
|
||||
fd.append('tool_approval_id', _pendingToolApproval.approval_id);
|
||||
fd.append('tool_approval_decision', _pendingToolApproval.decision);
|
||||
_pendingToolApproval = null;
|
||||
if (approvalForSend) {
|
||||
fd.append('tool_approval_id', approvalForSend.approval_id);
|
||||
fd.append('tool_approval_decision', approvalForSend.decision);
|
||||
if (
|
||||
_pendingToolApproval
|
||||
&& _pendingToolApproval.approval_id === approvalForSend.approval_id
|
||||
) {
|
||||
_pendingToolApproval = null;
|
||||
}
|
||||
}
|
||||
if (selectedRouteForSend.model) fd.append('selected_model', selectedRouteForSend.model);
|
||||
if (selectedRouteForSend.endpoint_url) fd.append('selected_endpoint_url', selectedRouteForSend.endpoint_url);
|
||||
if (selectedRouteForSend.endpoint_id) fd.append('selected_endpoint_id', selectedRouteForSend.endpoint_id);
|
||||
if (ids.length) fd.append('attachments', JSON.stringify(ids));
|
||||
// Auto-save & send active doc ID so the backend sees latest content
|
||||
if (documentModule && activeDocIdForSend) {
|
||||
try {
|
||||
_sendPerf.mark('doc_silent_save_begin');
|
||||
await documentModule.saveDocument({ silent: true });
|
||||
_sendPerf.mark('doc_silent_save_done');
|
||||
} catch (_e) {
|
||||
_sendPerf.mark('doc_silent_save_failed');
|
||||
if (documentModule && activeDocIdForSend && shouldSaveActiveDoc) {
|
||||
if (!approvalForSend) {
|
||||
try {
|
||||
_sendPerf.mark('doc_silent_save_begin');
|
||||
await documentModule.saveDocument({ silent: true });
|
||||
_sendPerf.mark('doc_silent_save_done');
|
||||
} catch (_e) {
|
||||
_sendPerf.mark('doc_silent_save_failed');
|
||||
}
|
||||
}
|
||||
fd.append('active_doc_id', activeDocIdForSend);
|
||||
}
|
||||
@@ -1859,7 +1911,7 @@ import { createTerminalStreamError, isRecoverableStreamError } from './chatStrea
|
||||
if (isAgentMode) {
|
||||
fd.append('allow_web_search', el('web-toggle').checked ? 'true' : 'false');
|
||||
}
|
||||
if (el('research-toggle').checked) {
|
||||
if (!approvalForSend && el('research-toggle').checked) {
|
||||
fd.append('use_research', 'true');
|
||||
// Research always runs in chat mode — override agent if set
|
||||
fd.set('mode', 'chat');
|
||||
|
||||
@@ -1367,7 +1367,7 @@ document.addEventListener('click', function(e) {
|
||||
} catch {}
|
||||
});
|
||||
} else if (kind === 'document') {
|
||||
import('./document.js?v=20260722emailfastindex1').then(mod => {
|
||||
import('./document.js?v=20260815approvalsave1').then(mod => {
|
||||
const open = mod.loadDocument
|
||||
|| mod.openDocument
|
||||
|| (mod.default && (mod.default.loadDocument || mod.default.openDocument));
|
||||
@@ -1389,7 +1389,7 @@ document.addEventListener('click', function(e) {
|
||||
if (open) open(id);
|
||||
}).catch(() => {});
|
||||
} else if (kind === 'email') {
|
||||
import('./emailLibrary.js?v=20260722emailfastindex1').then(mod => {
|
||||
import('./emailLibrary.js?v=20260815approvalsave1').then(mod => {
|
||||
const open = mod.openEmailLibrary || (mod.default && mod.default.openEmailLibrary);
|
||||
if (open) open({ uid: id });
|
||||
}).catch(() => {});
|
||||
@@ -2433,6 +2433,9 @@ export function renderAskUserCard(payload, options) {
|
||||
approval_id: aq.approval_id,
|
||||
decision: String((opt && opt.value) || '').toLowerCase(),
|
||||
label,
|
||||
document_id: aq.action && aq.action.document_id
|
||||
? String(aq.action.document_id)
|
||||
: '',
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,7 @@ import Storage from './storage.js';
|
||||
import themeModule from './theme.js';
|
||||
import markdownModule from './markdown.js';
|
||||
import sessionModule from './sessions.js';
|
||||
import documentModule from './document.js?v=20260722emailfastindex1';
|
||||
import documentModule from './document.js?v=20260815approvalsave1';
|
||||
|
||||
/**
|
||||
* Handle a ui_control SSE event — AI-driven UI manipulation.
|
||||
@@ -156,7 +156,7 @@ export function handleUIControl(uiData) {
|
||||
if (fn) fn();
|
||||
}).catch(function(){});
|
||||
} else if (panel === 'email') {
|
||||
import('./emailLibrary.js?v=20260722emailfastindex1').then(function(mod) {
|
||||
import('./emailLibrary.js?v=20260815approvalsave1').then(function(mod) {
|
||||
var fn = mod.openEmailLibrary || (mod.default && mod.default.openEmailLibrary);
|
||||
if (fn) fn();
|
||||
}).catch(function(){});
|
||||
@@ -205,7 +205,7 @@ export function handleUIControl(uiData) {
|
||||
} catch (e) {
|
||||
console.warn('open_email_reply existing draft update failed:', e);
|
||||
}
|
||||
import('./emailInbox.js?v=20260722emailfastindex1').then(function(mod) {
|
||||
import('./emailInbox.js?v=20260815approvalsave1').then(function(mod) {
|
||||
var fn = mod.openReplyDraft || (mod.default && mod.default.openReplyDraft);
|
||||
if (fn) fn(uiData.uid, uiData.folder || 'INBOX', uiData.mode || 'reply', uiData.body || '');
|
||||
}).catch(function(e) {
|
||||
|
||||
@@ -3934,7 +3934,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||
leadingIcon: 'check',
|
||||
action: 'View Message',
|
||||
onAction: () => {
|
||||
import('./emailLibrary.js?v=20260722emailfastindex1').then(mod => {
|
||||
import('./emailLibrary.js?v=20260815approvalsave1').then(mod => {
|
||||
const open = mod.openEmailLibrary || (mod.default && mod.default.openEmailLibrary);
|
||||
if (open) open({
|
||||
account_id: data.account_id || activeAccountId || null,
|
||||
@@ -9401,9 +9401,9 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||
|
||||
/** Save manual edits */
|
||||
export async function saveDocument({ silent = false, forceVersion = false } = {}) {
|
||||
if (!activeDocId) return;
|
||||
if (!activeDocId) return false;
|
||||
const textarea = document.getElementById('doc-editor-textarea');
|
||||
if (!textarea) return;
|
||||
if (!textarea) return false;
|
||||
const savingDocId = activeDocId;
|
||||
saveCurrentToMap();
|
||||
const localDoc = docs.get(savingDocId);
|
||||
@@ -9422,7 +9422,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||
});
|
||||
if (res.status === 404) {
|
||||
if (silent && localDoc?.language === 'email') {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
// Streaming/empty email drafts can leave a local tab pointing at a temp
|
||||
// or already-deleted document. Do not keep surfacing autosave errors for
|
||||
@@ -9434,7 +9434,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||
}
|
||||
_syncDocIndicator();
|
||||
if (!silent && uiModule) uiModule.showError('Document no longer exists');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!res.ok) throw new Error(`Document save failed: HTTP ${res.status}`);
|
||||
const doc = await res.json();
|
||||
@@ -9447,6 +9447,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||
}
|
||||
_syncDocIndicator();
|
||||
if (!silent && uiModule) uiModule.showToast(forceVersion ? 'New version saved' : 'Document saved');
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Failed to save document:', e);
|
||||
const now = Date.now();
|
||||
@@ -9454,6 +9455,7 @@ import { bindMenuDismiss, dismissOrRemove } from './escMenuStack.js';
|
||||
uiModule.showError(silent ? 'Autosave failed' : 'Failed to save document');
|
||||
_lastAutoSaveErrorAt = now;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import spinnerModule from './spinner.js';
|
||||
import sessionModule from './sessions.js';
|
||||
import { initEmailLibrary, openEmailLibrary, closeEmailLibrary, isOpen as isLibOpen, prewarmEmailLibrary, prewarmUnreadEmails } from './emailLibrary.js?v=20260722emailfastindex1';
|
||||
import { initEmailLibrary, openEmailLibrary, closeEmailLibrary, isOpen as isLibOpen, prewarmEmailLibrary, prewarmUnreadEmails } from './emailLibrary.js?v=20260815approvalsave1';
|
||||
import * as Modals from './modalManager.js';
|
||||
import { applyEdgeDock } from './modalSnap.js';
|
||||
import { buildReplyAllCc, extractEmail } from './emailLibrary/replyRecipients.js';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import spinnerModule from './spinner.js';
|
||||
import { styledConfirm, showToast, emptyStateIcon } from './ui.js';
|
||||
import { folderDisplayName, sortedFolders } from './emailInbox.js?v=20260722emailfastindex1';
|
||||
import { folderDisplayName, sortedFolders } from './emailInbox.js?v=20260815approvalsave1';
|
||||
import settingsModule from './settings.js';
|
||||
import * as Modals from './modalManager.js';
|
||||
import { topPortalZ } from './toolWindowZOrder.js';
|
||||
@@ -6680,7 +6680,7 @@ function _wireAttachmentHandlers(reader, folder) {
|
||||
ownerModal.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
const docMod = await import('./document.js?v=20260722emailfastindex1');
|
||||
const docMod = await import('./document.js?v=20260815approvalsave1');
|
||||
const load = (docMod && docMod.loadDocument) || (docMod && docMod.default && docMod.default.loadDocument);
|
||||
if (typeof load === 'function') {
|
||||
await load(json.doc_id);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import Storage from './storage.js';
|
||||
import uiModule, { autoResize, styledPrompt } from './ui.js';
|
||||
import chatRenderer from './chatRenderer.js?v=20260815toolapproval3';
|
||||
import chatRenderer from './chatRenderer.js?v=20260815toolapproval4';
|
||||
import { providerLogo } from './providers.js';
|
||||
import { initModelPicker, updateModelPicker } from './modelPicker.js?v=20260722ctxheader1';
|
||||
import themeModule from './theme.js';
|
||||
|
||||
@@ -2745,7 +2745,7 @@ async function initEmailAccountsSettings() {
|
||||
|
||||
el('set-email-open-library-settings')?.addEventListener('click', async () => {
|
||||
try {
|
||||
const mod = await import('./emailLibrary.js?v=20260722emailfastindex1');
|
||||
const mod = await import('./emailLibrary.js?v=20260815approvalsave1');
|
||||
if (typeof mod.openEmailLibrarySettings === 'function') {
|
||||
await mod.openEmailLibrarySettings();
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import modelsModule from './models.js';
|
||||
import chatRenderer from './chatRenderer.js';
|
||||
import spinnerModule from './spinner.js';
|
||||
import themeModule from './theme.js';
|
||||
import documentModule from './document.js?v=20260722emailfastindex1';
|
||||
import documentModule from './document.js?v=20260815approvalsave1';
|
||||
import workspaceModule from './workspace.js';
|
||||
import settingsModule from './settings.js';
|
||||
import cookbookModule from './cookbook.js';
|
||||
|
||||
Reference in New Issue
Block a user