libre-webui 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -15
- package/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +9 -0
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +2 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.d.ts +8 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.js +98 -0
- package/backend/dist/persistence/postgresPersonalAutomationsMigration.js.map +1 -0
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.js +274 -0
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/resourceTypes.d.ts +83 -2
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
- package/backend/dist/persistence/resourceTypes.js.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.d.ts +1 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +181 -0
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.js +233 -0
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.js +41 -0
- package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.d.ts +3 -0
- package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.js +3 -0
- package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js +101 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
- package/backend/dist/routes/automations.d.ts +3 -0
- package/backend/dist/routes/automations.d.ts.map +1 -0
- package/backend/dist/routes/automations.js +232 -0
- package/backend/dist/routes/automations.js.map +1 -0
- package/backend/dist/routes/calendar.d.ts +3 -0
- package/backend/dist/routes/calendar.d.ts.map +1 -0
- package/backend/dist/routes/calendar.js +206 -0
- package/backend/dist/routes/calendar.js.map +1 -0
- package/backend/dist/routes/chat.d.ts.map +1 -1
- package/backend/dist/routes/chat.js +99 -7
- package/backend/dist/routes/chat.js.map +1 -1
- package/backend/dist/routes/ollama.d.ts.map +1 -1
- package/backend/dist/routes/ollama.js +4 -1
- package/backend/dist/routes/ollama.js.map +1 -1
- package/backend/dist/routes/search.d.ts.map +1 -1
- package/backend/dist/routes/search.js +5 -3
- package/backend/dist/routes/search.js.map +1 -1
- package/backend/dist/services/automationSchedulerService.d.ts +28 -0
- package/backend/dist/services/automationSchedulerService.d.ts.map +1 -0
- package/backend/dist/services/automationSchedulerService.js +191 -0
- package/backend/dist/services/automationSchedulerService.js.map +1 -0
- package/backend/dist/services/automationService.d.ts +53 -0
- package/backend/dist/services/automationService.d.ts.map +1 -0
- package/backend/dist/services/automationService.js +224 -0
- package/backend/dist/services/automationService.js.map +1 -0
- package/backend/dist/services/chatGenerationService.d.ts.map +1 -1
- package/backend/dist/services/chatGenerationService.js +7 -1
- package/backend/dist/services/chatGenerationService.js.map +1 -1
- package/backend/dist/services/chatService.d.ts +9 -1
- package/backend/dist/services/chatService.d.ts.map +1 -1
- package/backend/dist/services/chatService.js +91 -13
- package/backend/dist/services/chatService.js.map +1 -1
- package/backend/dist/services/contextCompactionService.d.ts +11 -6
- package/backend/dist/services/contextCompactionService.d.ts.map +1 -1
- package/backend/dist/services/contextCompactionService.js +127 -27
- package/backend/dist/services/contextCompactionService.js.map +1 -1
- package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
- package/backend/dist/services/durableChatGenerationService.js +15 -3
- package/backend/dist/services/durableChatGenerationService.js.map +1 -1
- package/backend/dist/services/ollamaService.d.ts.map +1 -1
- package/backend/dist/services/ollamaService.js +9 -2
- package/backend/dist/services/ollamaService.js.map +1 -1
- package/backend/dist/services/pluginService.d.ts +10 -0
- package/backend/dist/services/pluginService.d.ts.map +1 -1
- package/backend/dist/services/pluginService.js +42 -7
- package/backend/dist/services/pluginService.js.map +1 -1
- package/backend/dist/services/preferencesService.d.ts.map +1 -1
- package/backend/dist/services/preferencesService.js +3 -0
- package/backend/dist/services/preferencesService.js.map +1 -1
- package/backend/dist/services/titleGenerationService.d.ts.map +1 -1
- package/backend/dist/services/titleGenerationService.js +5 -1
- package/backend/dist/services/titleGenerationService.js.map +1 -1
- package/backend/dist/services/webSearchPlanService.d.ts +18 -0
- package/backend/dist/services/webSearchPlanService.d.ts.map +1 -0
- package/backend/dist/services/webSearchPlanService.js +160 -0
- package/backend/dist/services/webSearchPlanService.js.map +1 -0
- package/backend/dist/services/webSearchService.d.ts +9 -3
- package/backend/dist/services/webSearchService.d.ts.map +1 -1
- package/backend/dist/services/webSearchService.js +8 -2
- package/backend/dist/services/webSearchService.js.map +1 -1
- package/backend/dist/storage.d.ts +7 -1
- package/backend/dist/storage.d.ts.map +1 -1
- package/backend/dist/storage.js +65 -1
- package/backend/dist/storage.js.map +1 -1
- package/backend/dist/types/index.d.ts +79 -0
- package/backend/dist/types/index.d.ts.map +1 -1
- package/backend/dist/types/index.js.map +1 -1
- package/backend/dist/utils/automationSchedule.d.ts +26 -0
- package/backend/dist/utils/automationSchedule.d.ts.map +1 -0
- package/backend/dist/utils/automationSchedule.js +231 -0
- package/backend/dist/utils/automationSchedule.js.map +1 -0
- package/backend/dist/utils/chatContext.d.ts +7 -0
- package/backend/dist/utils/chatContext.d.ts.map +1 -1
- package/backend/dist/utils/chatContext.js +13 -2
- package/backend/dist/utils/chatContext.js.map +1 -1
- package/backend/dist/utils/openAIResponsesAdapter.d.ts.map +1 -1
- package/backend/dist/utils/openAIResponsesAdapter.js +3 -1
- package/backend/dist/utils/openAIResponsesAdapter.js.map +1 -1
- package/backend/dist/utils/pluginChatAdapter.d.ts +1 -0
- package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
- package/backend/dist/utils/pluginChatAdapter.js +82 -19
- package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
- package/backend/dist/utils/pluginModelCatalog.d.ts +20 -0
- package/backend/dist/utils/pluginModelCatalog.d.ts.map +1 -1
- package/backend/dist/utils/pluginModelCatalog.js +101 -9
- package/backend/dist/utils/pluginModelCatalog.js.map +1 -1
- package/backend/dist/utils/resourceLimits.d.ts +7 -0
- package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
- package/backend/dist/utils/resourceLimits.js +7 -0
- package/backend/dist/utils/resourceLimits.js.map +1 -1
- package/backend/dist/utils/thinkingOptions.d.ts +23 -3
- package/backend/dist/utils/thinkingOptions.d.ts.map +1 -1
- package/backend/dist/utils/thinkingOptions.js +35 -9
- package/backend/dist/utils/thinkingOptions.js.map +1 -1
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +9 -3
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/package.json +1 -1
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/assets/index-6o-Hc2wS.js +41 -0
- package/frontend/dist/assets/index-D-pM957b.css +1 -0
- package/frontend/dist/index.html +12 -12
- package/frontend/dist/js/ArtifactContainer-CpUi6ekF.js +1 -0
- package/frontend/dist/js/{ArtifactDemoPage-TONeQvof.js → ArtifactDemoPage-BeNs3gHp.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-CV2XXu_t.js → ArtifactSandboxFrame-qsj_Y3LV.js} +2 -2
- package/frontend/dist/js/ArtifactSlideOutPanel-CSFRluWA.js +1 -0
- package/frontend/dist/js/AutomationsPage-KUeWoZNv.js +1 -0
- package/frontend/dist/js/CalendarPage-D9I972w8.js +1 -0
- package/frontend/dist/js/ChatPage-CmBx4oAL.js +16 -0
- package/frontend/dist/js/{FirstTimeSetup-smhj0X6o.js → FirstTimeSetup-Df0YJni3.js} +1 -1
- package/frontend/dist/js/GalleryPage-bLCrdJN_.js +1 -0
- package/frontend/dist/js/HomePage-Uc6PEg-U.js +1 -0
- package/frontend/dist/js/{LibreClawPage-CRKei1MM.js → LibreClawPage-CG63DHg1.js} +1 -1
- package/frontend/dist/js/{ModelManagerModals-D0amaOnl.js → ModelManagerModals-DcLGYN3l.js} +1 -1
- package/frontend/dist/js/ModelSelector-yxJC64rM.js +1 -0
- package/frontend/dist/js/ModelsPage-1R_TxgmJ.js +2 -0
- package/frontend/dist/js/NotesPage-L65j5HoW.js +1 -0
- package/frontend/dist/js/PersonasPage-CKeBpHIH.js +13 -0
- package/frontend/dist/js/PluginUsagePage-BX1SKMbW.js +1 -0
- package/frontend/dist/js/{RichMessageContent-Bxi_u0gC.js → RichMessageContent-DiySVYGB.js} +1 -1
- package/frontend/dist/js/SettingsModal-68SG1uZ2.js +1 -0
- package/frontend/dist/js/SystemPage-CytpZiQV.js +1 -0
- package/frontend/dist/js/UserManagementPage-BzJBvZiS.js +1 -0
- package/frontend/dist/js/WorkPage-C8gr0QJe.js +6 -0
- package/frontend/dist/js/api-BDtqBtqw.js +14 -0
- package/frontend/dist/js/{appStore-CJtomipz.js → appStore-0JnCUlEq.js} +2 -2
- package/frontend/dist/js/ar-CSpi37Gr.js +2 -0
- package/frontend/dist/js/{authStore-BckFDL70.js → authStore-DbMdvNgJ.js} +3 -3
- package/frontend/dist/js/{babel-D24oDI9y.js → babel-BsYoxD-7.js} +1 -1
- package/frontend/dist/js/bn-CyH1BeDD.js +2 -0
- package/frontend/dist/js/calendarDates-Co3o_05Y.js +1 -0
- package/frontend/dist/js/chatStore-CHQ2uINS.js +3 -0
- package/frontend/dist/js/{config-CNVNpf_q.js → config-DSOYQ9a3.js} +1 -1
- package/frontend/dist/js/cs-Cv2NfRK5.js +2 -0
- package/frontend/dist/js/da-D6D6IvPK.js +2 -0
- package/frontend/dist/js/de-BNoJJTrT.js +2 -0
- package/frontend/dist/js/es-kzo9C7q_.js +2 -0
- package/frontend/dist/js/{estree-D1TYSBcf.js → estree-BcnBcOAx.js} +7 -7
- package/frontend/dist/js/fr-CjHYvJRp.js +2 -0
- package/frontend/dist/js/hi-BU98AdCQ.js +2 -0
- package/frontend/dist/js/{html-Dmddj0zf.js → html-DkY55Jys.js} +10 -10
- package/frontend/dist/js/id-CpdedejX.js +2 -0
- package/frontend/dist/js/is-Cp5dDWBH.js +2 -0
- package/frontend/dist/js/it-CuH_cK4U.js +2 -0
- package/frontend/dist/js/ja-BWXXXiHn.js +2 -0
- package/frontend/dist/js/ko-DQgv2M1t.js +2 -0
- package/frontend/dist/js/{markdown-Bl_LsHEk.js → markdown-IZSI9DXw.js} +6 -6
- package/frontend/dist/js/{messageCodeStyles-CgKxn4tN.js → messageCodeStyles-BFNR9Wv6.js} +2 -2
- package/frontend/dist/js/ms-DCcRoIcX.js +2 -0
- package/frontend/dist/js/nl-CbKR1_EU.js +2 -0
- package/frontend/dist/js/pl-CRyJZccn.js +2 -0
- package/frontend/dist/js/{postcss-Bx9CTcIc.js → postcss-BJ0okz7n.js} +1 -1
- package/frontend/dist/js/pt-BXYmHFGH.js +2 -0
- package/frontend/dist/js/{router-vendor-CbPDks39.js → router-vendor-DGFGM3EP.js} +1 -1
- package/frontend/dist/js/ru-M9SyZG4b.js +2 -0
- package/frontend/dist/js/{standalone-BWau8hEE.js → standalone-Csw3jfJ_.js} +5 -5
- package/frontend/dist/js/sv-DJ486LTU.js +2 -0
- package/frontend/dist/js/th-Ch7gKtZv.js +2 -0
- package/frontend/dist/js/tr-ClehWhSC.js +2 -0
- package/frontend/dist/js/{typescript-DxGn3Yn-.js → typescript-CnF4UVx7.js} +2 -2
- package/frontend/dist/js/{ui-jlx9WA5A.js → ui-CC-K19wO.js} +2 -2
- package/frontend/dist/js/ui-vendor-B1UgO3vd.js +177 -0
- package/frontend/dist/js/uk-BqAd5zHF.js +2 -0
- package/frontend/dist/js/{vi-BgRaIrQC.js → vi-D9gjlDVB.js} +2 -2
- package/frontend/dist/js/{workApi-DWRpZ_cq.js → workApi-C3uy9EDT.js} +3 -3
- package/frontend/dist/js/{yaml-DioTxVLA.js → yaml-DzT5hBSH.js} +5 -5
- package/frontend/dist/js/zh-DKTyHkiR.js +2 -0
- package/package.json +2 -2
- package/frontend/dist/assets/index-BlTbxYPP.js +0 -33
- package/frontend/dist/assets/index-CbiWSGOj.css +0 -1
- package/frontend/dist/js/ArtifactContainer-C-n1vPsA.js +0 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-2exw7YqL.js +0 -1
- package/frontend/dist/js/ChatPage-wnyG_TyC.js +0 -23
- package/frontend/dist/js/GalleryPage-BZfeWlBg.js +0 -1
- package/frontend/dist/js/HomePage-gf4GgI6z.js +0 -1
- package/frontend/dist/js/ModelSelector-C5Tn6y0p.js +0 -1
- package/frontend/dist/js/ModelsPage-Rbags4Xq.js +0 -2
- package/frontend/dist/js/NotesPage-9NR9HZZ8.js +0 -1
- package/frontend/dist/js/PersonasPage-DYP67Qiy.js +0 -13
- package/frontend/dist/js/PluginUsagePage-QtCgymRu.js +0 -1
- package/frontend/dist/js/SettingsModal-B4RelkLP.js +0 -1
- package/frontend/dist/js/SystemPage-xFSTYdBS.js +0 -1
- package/frontend/dist/js/UserManagementPage-Cp430WCP.js +0 -1
- package/frontend/dist/js/WorkPage-DFES41Qm.js +0 -6
- package/frontend/dist/js/api-DUUxxx_B.js +0 -14
- package/frontend/dist/js/ar-DMH3qbt6.js +0 -2
- package/frontend/dist/js/bn-CGDX8khx.js +0 -2
- package/frontend/dist/js/chatStore-DK-_wd-j.js +0 -3
- package/frontend/dist/js/cs-CJLgUk3m.js +0 -2
- package/frontend/dist/js/da-DnZMnCVM.js +0 -2
- package/frontend/dist/js/de-CWpguVQ5.js +0 -2
- package/frontend/dist/js/es-DVU1vjm0.js +0 -2
- package/frontend/dist/js/fr-pWnSnypD.js +0 -2
- package/frontend/dist/js/hi-B6xDfxA5.js +0 -2
- package/frontend/dist/js/id-BThSTUAG.js +0 -2
- package/frontend/dist/js/is-BOohDFie.js +0 -2
- package/frontend/dist/js/it-BdRArcCC.js +0 -2
- package/frontend/dist/js/ja-5Cq35sMr.js +0 -2
- package/frontend/dist/js/ko-BlzNRtpQ.js +0 -2
- package/frontend/dist/js/ms-CtGeurkZ.js +0 -2
- package/frontend/dist/js/nl-MKPHGjVF.js +0 -2
- package/frontend/dist/js/pl-BwBrqCey.js +0 -2
- package/frontend/dist/js/pt-CKzoHWBB.js +0 -2
- package/frontend/dist/js/ru-BQC4jh0Y.js +0 -2
- package/frontend/dist/js/sv-Cm0qXxzM.js +0 -2
- package/frontend/dist/js/th-DUPbiRI2.js +0 -2
- package/frontend/dist/js/tr-AsFu_Br5.js +0 -2
- package/frontend/dist/js/ui-vendor-54SgntkX.js +0 -177
- package/frontend/dist/js/uk-DrMMOZ17.js +0 -2
- package/frontend/dist/js/zh-B4rcgcAy.js +0 -2
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Libre WebUI
|
|
3
|
+
* Copyright (C) 2025 Kroonen AI, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import express from 'express';
|
|
18
|
+
import automationService from '../services/automationService.js';
|
|
19
|
+
import automationSchedulerService from '../services/automationSchedulerService.js';
|
|
20
|
+
import { authenticate } from '../middleware/auth.js';
|
|
21
|
+
import { ResourcePolicyError } from '../utils/resourceLimits.js';
|
|
22
|
+
import { InvalidTriggerError, occurrencesBetween, } from '../utils/automationSchedule.js';
|
|
23
|
+
const router = express.Router();
|
|
24
|
+
router.use(authenticate);
|
|
25
|
+
const userIdOf = (req) => req.user?.userId || 'default';
|
|
26
|
+
/** Occurrence projections span at most 62 days per request. */
|
|
27
|
+
const MAX_OCCURRENCE_RANGE_MS = 62 * 24 * 60 * 60 * 1000;
|
|
28
|
+
function sendAutomationError(res, error, fallback) {
|
|
29
|
+
if (error instanceof ResourcePolicyError ||
|
|
30
|
+
error instanceof InvalidTriggerError) {
|
|
31
|
+
const statusCode = error instanceof ResourcePolicyError ? error.statusCode : 400;
|
|
32
|
+
res.status(statusCode).json({ success: false, error: error.message });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
res.status(500).json({
|
|
36
|
+
success: false,
|
|
37
|
+
error: error instanceof Error ? error.message : fallback,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function readEpoch(value, field) {
|
|
41
|
+
const parsed = typeof value === 'string' ? Number(value) : value;
|
|
42
|
+
if (typeof parsed !== 'number' ||
|
|
43
|
+
!Number.isSafeInteger(parsed) ||
|
|
44
|
+
parsed < 0) {
|
|
45
|
+
throw new ResourcePolicyError(`${field} must be an epoch-ms integer`, 400);
|
|
46
|
+
}
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
function readAutomationBody(body) {
|
|
50
|
+
const provider = typeof body.provider === 'string' && body.provider
|
|
51
|
+
? body.provider
|
|
52
|
+
: undefined;
|
|
53
|
+
const model = typeof body.model === 'string' && body.model ? body.model : undefined;
|
|
54
|
+
return {
|
|
55
|
+
name: typeof body.name === 'string' ? body.name : '',
|
|
56
|
+
instructions: typeof body.instructions === 'string' ? body.instructions : '',
|
|
57
|
+
triggers: body.triggers,
|
|
58
|
+
...(provider ? { provider } : {}),
|
|
59
|
+
...(model ? { model } : {}),
|
|
60
|
+
notify: body.notify === 'off' ? 'off' : 'app',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
router.get('/', async (req, res) => {
|
|
64
|
+
try {
|
|
65
|
+
res.json({
|
|
66
|
+
success: true,
|
|
67
|
+
data: await automationService.getAutomations(userIdOf(req)),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
sendAutomationError(res, error, 'Failed to load automations');
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
router.post('/', async (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const automation = await automationService.createAutomation(readAutomationBody((req.body ?? {})), userIdOf(req));
|
|
77
|
+
res.json({ success: true, data: automation });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
sendAutomationError(res, error, 'Failed to create automation');
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
/** Upcoming computed occurrences across the caller's active automations. */
|
|
84
|
+
router.get('/occurrences', async (req, res) => {
|
|
85
|
+
try {
|
|
86
|
+
const from = readEpoch(req.query.from, 'from');
|
|
87
|
+
const to = readEpoch(req.query.to, 'to');
|
|
88
|
+
if (to <= from || to - from > MAX_OCCURRENCE_RANGE_MS) {
|
|
89
|
+
throw new ResourcePolicyError('The requested range must be positive and span at most 62 days', 400);
|
|
90
|
+
}
|
|
91
|
+
const automations = await automationService.getAutomations(userIdOf(req));
|
|
92
|
+
const occurrences = automations
|
|
93
|
+
.filter(automation => automation.status === 'active')
|
|
94
|
+
.flatMap(automation => occurrencesBetween(automation.triggers, from, to, 200).map(at => ({
|
|
95
|
+
automationId: automation.id,
|
|
96
|
+
name: automation.name,
|
|
97
|
+
at,
|
|
98
|
+
})))
|
|
99
|
+
.sort((left, right) => left.at - right.at);
|
|
100
|
+
res.json({ success: true, data: occurrences });
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
sendAutomationError(res, error, 'Failed to project occurrences');
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
router.get('/runs', async (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const options = {};
|
|
109
|
+
if (typeof req.query.automationId === 'string') {
|
|
110
|
+
options.automationId = req.query.automationId;
|
|
111
|
+
}
|
|
112
|
+
if (req.query.from !== undefined) {
|
|
113
|
+
options.from = readEpoch(req.query.from, 'from');
|
|
114
|
+
}
|
|
115
|
+
if (req.query.to !== undefined) {
|
|
116
|
+
options.to = readEpoch(req.query.to, 'to');
|
|
117
|
+
}
|
|
118
|
+
res.json({
|
|
119
|
+
success: true,
|
|
120
|
+
data: await automationService.listRuns(userIdOf(req), options),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
sendAutomationError(res, error, 'Failed to load automation runs');
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
router.get('/runs/summary', async (req, res) => {
|
|
128
|
+
try {
|
|
129
|
+
res.json({
|
|
130
|
+
success: true,
|
|
131
|
+
data: await automationService.runsSummary(userIdOf(req)),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
sendAutomationError(res, error, 'Failed to load the run summary');
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
router.post('/runs/seen', async (req, res) => {
|
|
139
|
+
try {
|
|
140
|
+
const marked = await automationService.markRunsSeen(userIdOf(req));
|
|
141
|
+
res.json({ success: true, data: { marked } });
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
sendAutomationError(res, error, 'Failed to mark runs as seen');
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
router.get('/:automationId', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const automation = await automationService.getAutomation(req.params.automationId, userIdOf(req));
|
|
150
|
+
if (!automation) {
|
|
151
|
+
res.status(404).json({
|
|
152
|
+
success: false,
|
|
153
|
+
error: 'Automation not found',
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
res.json({ success: true, data: automation });
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
sendAutomationError(res, error, 'Failed to load the automation');
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
router.put('/:automationId', async (req, res) => {
|
|
164
|
+
try {
|
|
165
|
+
const automation = await automationService.updateAutomation(req.params.automationId, readAutomationBody((req.body ?? {})), userIdOf(req));
|
|
166
|
+
if (!automation) {
|
|
167
|
+
res.status(404).json({
|
|
168
|
+
success: false,
|
|
169
|
+
error: 'Automation not found',
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
res.json({ success: true, data: automation });
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
sendAutomationError(res, error, 'Failed to update the automation');
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
router.delete('/:automationId', async (req, res) => {
|
|
180
|
+
try {
|
|
181
|
+
const deleted = await automationService.deleteAutomation(req.params.automationId, userIdOf(req));
|
|
182
|
+
if (!deleted) {
|
|
183
|
+
res.status(404).json({
|
|
184
|
+
success: false,
|
|
185
|
+
error: 'Automation not found',
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
res.json({ success: true, message: 'Automation deleted' });
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
sendAutomationError(res, error, 'Failed to delete the automation');
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
const setStatus = async (req, res, status) => {
|
|
196
|
+
try {
|
|
197
|
+
const automation = await automationService.setAutomationStatus(req.params.automationId, userIdOf(req), status);
|
|
198
|
+
if (!automation) {
|
|
199
|
+
res.status(404).json({
|
|
200
|
+
success: false,
|
|
201
|
+
error: 'Automation not found',
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
res.json({ success: true, data: automation });
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
sendAutomationError(res, error, 'Failed to change the automation status');
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
router.post('/:automationId/pause', (req, res) => setStatus(req, res, 'paused'));
|
|
212
|
+
router.post('/:automationId/resume', (req, res) => setStatus(req, res, 'active'));
|
|
213
|
+
router.post('/:automationId/run', async (req, res) => {
|
|
214
|
+
try {
|
|
215
|
+
const userId = userIdOf(req);
|
|
216
|
+
const automation = await automationService.getAutomation(req.params.automationId, userId);
|
|
217
|
+
if (!automation) {
|
|
218
|
+
res.status(404).json({
|
|
219
|
+
success: false,
|
|
220
|
+
error: 'Automation not found',
|
|
221
|
+
});
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const runId = await automationSchedulerService.runNow(automation.id, userId);
|
|
225
|
+
res.status(202).json({ success: true, data: { runId } });
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
sendAutomationError(res, error, 'Failed to start the automation run');
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
export default router;
|
|
232
|
+
//# sourceMappingURL=automations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automations.js","sourceRoot":"","sources":["../../src/routes/automations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../../src/routes/calendar.ts"],"names":[],"mappings":"AAiCA,QAAA,MAAM,MAAM,4CAAmB,CAAC;eAqPjB,MAAM"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Libre WebUI
|
|
3
|
+
* Copyright (C) 2025 Kroonen AI, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import express from 'express';
|
|
18
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
19
|
+
import storageService from '../storage.js';
|
|
20
|
+
import { authenticate } from '../middleware/auth.js';
|
|
21
|
+
import { MAX_CALENDAR_EVENT_NOTES_LENGTH, MAX_CALENDAR_EVENT_TITLE_LENGTH, ResourcePolicyError, } from '../utils/resourceLimits.js';
|
|
22
|
+
import { InvalidTriggerError, occurrencesBetween, validateTriggers, } from '../utils/automationSchedule.js';
|
|
23
|
+
const router = express.Router();
|
|
24
|
+
router.use(authenticate);
|
|
25
|
+
const userIdOf = (req) => req.user?.userId || 'default';
|
|
26
|
+
/** A calendar range query may span at most 13 months. */
|
|
27
|
+
const MAX_RANGE_MS = 13 * 32 * 24 * 60 * 60 * 1000;
|
|
28
|
+
function sendCalendarError(res, error, fallback) {
|
|
29
|
+
if (error instanceof ResourcePolicyError ||
|
|
30
|
+
error instanceof InvalidTriggerError) {
|
|
31
|
+
const statusCode = error instanceof ResourcePolicyError ? error.statusCode : 400;
|
|
32
|
+
res.status(statusCode).json({ success: false, error: error.message });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
res.status(500).json({
|
|
36
|
+
success: false,
|
|
37
|
+
error: error instanceof Error ? error.message : fallback,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function readTextField(value, field, maximum, required) {
|
|
41
|
+
if (value === undefined || value === null || value === '') {
|
|
42
|
+
if (required) {
|
|
43
|
+
throw new ResourcePolicyError(`${field} is required`, 400);
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
if (typeof value !== 'string') {
|
|
48
|
+
throw new ResourcePolicyError(`${field} must be a string`, 400);
|
|
49
|
+
}
|
|
50
|
+
if (value.length > maximum) {
|
|
51
|
+
throw new ResourcePolicyError(`${field} exceeds the maximum length of ${maximum} characters`, 400);
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
function readEpoch(value, field) {
|
|
56
|
+
const parsed = typeof value === 'string' ? Number(value) : value;
|
|
57
|
+
if (typeof parsed !== 'number' ||
|
|
58
|
+
!Number.isSafeInteger(parsed) ||
|
|
59
|
+
parsed < 0) {
|
|
60
|
+
throw new ResourcePolicyError(`${field} must be an epoch-ms integer`, 400);
|
|
61
|
+
}
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
function readRange(req) {
|
|
65
|
+
const from = readEpoch(req.query.from, 'from');
|
|
66
|
+
const to = readEpoch(req.query.to, 'to');
|
|
67
|
+
if (to <= from || to - from > MAX_RANGE_MS) {
|
|
68
|
+
throw new ResourcePolicyError('The requested range must be positive and span at most 13 months', 400);
|
|
69
|
+
}
|
|
70
|
+
return { from, to };
|
|
71
|
+
}
|
|
72
|
+
function readEventBody(body, existing) {
|
|
73
|
+
const title = readTextField(body.title, 'title', MAX_CALENDAR_EVENT_TITLE_LENGTH, existing === undefined) ??
|
|
74
|
+
existing?.title ??
|
|
75
|
+
'';
|
|
76
|
+
const notes = body.notes !== undefined
|
|
77
|
+
? readTextField(body.notes, 'notes', MAX_CALENDAR_EVENT_NOTES_LENGTH, false)
|
|
78
|
+
: existing?.notes;
|
|
79
|
+
const startAt = body.startAt !== undefined
|
|
80
|
+
? readEpoch(body.startAt, 'startAt')
|
|
81
|
+
: existing?.startAt;
|
|
82
|
+
if (startAt === undefined) {
|
|
83
|
+
throw new ResourcePolicyError('startAt is required', 400);
|
|
84
|
+
}
|
|
85
|
+
const endAt = body.endAt !== undefined && body.endAt !== null
|
|
86
|
+
? readEpoch(body.endAt, 'endAt')
|
|
87
|
+
: body.endAt === null
|
|
88
|
+
? undefined
|
|
89
|
+
: existing?.endAt;
|
|
90
|
+
if (endAt !== undefined && endAt < startAt) {
|
|
91
|
+
throw new ResourcePolicyError('endAt must not precede startAt', 400);
|
|
92
|
+
}
|
|
93
|
+
const allDay = body.allDay !== undefined
|
|
94
|
+
? body.allDay === true
|
|
95
|
+
: (existing?.allDay ?? false);
|
|
96
|
+
const recurrence = body.recurrence !== undefined
|
|
97
|
+
? body.recurrence === null
|
|
98
|
+
? undefined
|
|
99
|
+
: validateTriggers([body.recurrence], 1)[0]
|
|
100
|
+
: existing?.recurrence;
|
|
101
|
+
return {
|
|
102
|
+
title,
|
|
103
|
+
...(notes !== undefined ? { notes } : {}),
|
|
104
|
+
startAt,
|
|
105
|
+
...(endAt !== undefined ? { endAt } : {}),
|
|
106
|
+
allDay,
|
|
107
|
+
...(recurrence !== undefined ? { recurrence } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Project a recurring event's occurrences into [from, to). */
|
|
111
|
+
function expandRecurring(event, from, to) {
|
|
112
|
+
if (!event.recurrence)
|
|
113
|
+
return [];
|
|
114
|
+
const duration = event.endAt !== undefined ? event.endAt - event.startAt : 0;
|
|
115
|
+
const effectiveFrom = Math.max(from, event.startAt);
|
|
116
|
+
if (effectiveFrom >= to)
|
|
117
|
+
return [];
|
|
118
|
+
return occurrencesBetween([event.recurrence], effectiveFrom, to)
|
|
119
|
+
.filter(occurrence => occurrence !== event.startAt)
|
|
120
|
+
.map(occurrence => ({
|
|
121
|
+
...event,
|
|
122
|
+
id: `${event.id}:${occurrence}`,
|
|
123
|
+
baseEventId: event.id,
|
|
124
|
+
startAt: occurrence,
|
|
125
|
+
...(event.endAt !== undefined ? { endAt: occurrence + duration } : {}),
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
router.get('/events', async (req, res) => {
|
|
129
|
+
try {
|
|
130
|
+
const userId = userIdOf(req);
|
|
131
|
+
const { from, to } = readRange(req);
|
|
132
|
+
const [inRange, recurring] = await Promise.all([
|
|
133
|
+
storageService.getCalendarEventsBetween(from, to, userId),
|
|
134
|
+
storageService.getRecurringCalendarEvents(userId),
|
|
135
|
+
]);
|
|
136
|
+
const seen = new Set(inRange.map(event => event.id));
|
|
137
|
+
const expanded = recurring.flatMap(event => expandRecurring(event, from, to).filter(occurrence => !seen.has(occurrence.id)));
|
|
138
|
+
const events = [...inRange, ...expanded].sort((left, right) => left.startAt - right.startAt);
|
|
139
|
+
res.json({ success: true, data: events });
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
sendCalendarError(res, error, 'Failed to load calendar events');
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
router.post('/events', async (req, res) => {
|
|
146
|
+
try {
|
|
147
|
+
const now = Date.now();
|
|
148
|
+
const event = {
|
|
149
|
+
id: uuidv4(),
|
|
150
|
+
...readEventBody((req.body ?? {})),
|
|
151
|
+
createdAt: now,
|
|
152
|
+
updatedAt: now,
|
|
153
|
+
};
|
|
154
|
+
await storageService.saveCalendarEvent(event, userIdOf(req));
|
|
155
|
+
res.json({ success: true, data: event });
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
sendCalendarError(res, error, 'Failed to create calendar event');
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
router.put('/events/:eventId', async (req, res) => {
|
|
162
|
+
try {
|
|
163
|
+
const userId = userIdOf(req);
|
|
164
|
+
const existing = await storageService.getCalendarEvent(req.params.eventId, userId);
|
|
165
|
+
if (!existing) {
|
|
166
|
+
res.status(404).json({
|
|
167
|
+
success: false,
|
|
168
|
+
error: 'Calendar event not found',
|
|
169
|
+
});
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const event = {
|
|
173
|
+
...existing,
|
|
174
|
+
...readEventBody((req.body ?? {}), existing),
|
|
175
|
+
id: existing.id,
|
|
176
|
+
createdAt: existing.createdAt,
|
|
177
|
+
updatedAt: Date.now(),
|
|
178
|
+
};
|
|
179
|
+
await storageService.saveCalendarEvent(event, userId);
|
|
180
|
+
res.json({ success: true, data: event });
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
sendCalendarError(res, error, 'Failed to update calendar event');
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
router.delete('/events/:eventId', async (req, res) => {
|
|
187
|
+
try {
|
|
188
|
+
const deleted = await storageService.deleteCalendarEvent(req.params.eventId, userIdOf(req));
|
|
189
|
+
if (!deleted) {
|
|
190
|
+
res.status(404).json({
|
|
191
|
+
success: false,
|
|
192
|
+
error: 'Calendar event not found',
|
|
193
|
+
});
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
res.json({
|
|
197
|
+
success: true,
|
|
198
|
+
message: 'Calendar event deleted',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
sendCalendarError(res, error, 'Failed to delete calendar event');
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
export default router;
|
|
206
|
+
//# sourceMappingURL=calendar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar.js","sourceRoot":"","sources":["../../src/routes/calendar.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/routes/chat.ts"],"names":[],"mappings":"AA6IA,QAAA,MAAM,MAAM,4CAAmB,CAAC;eA+zDjB,MAAM"}
|
|
@@ -26,7 +26,8 @@ import { authenticate, requireAdmin, } from '../middleware/auth.js';
|
|
|
26
26
|
import { getCompactionConfig, setCompactionConfig, } from '../services/contextCompactionService.js';
|
|
27
27
|
import { extractStatistics } from '../utils/generationUtils.js';
|
|
28
28
|
import agentCliService from '../services/agentCliService.js';
|
|
29
|
-
import { buildWebSearchEnhancedContent, isWebSearchAvailable, userCanUseWebSearch,
|
|
29
|
+
import { buildWebSearchEnhancedContent, isWebSearchAvailable, userCanUseWebSearch, } from '../services/webSearchService.js';
|
|
30
|
+
import { runPlannedWebSearch } from '../services/webSearchPlanService.js';
|
|
30
31
|
import { userModel } from '../models/userModel.js';
|
|
31
32
|
import chatGenerationService from '../services/chatGenerationService.js';
|
|
32
33
|
import preferencesService from '../services/preferencesService.js';
|
|
@@ -268,6 +269,34 @@ router.put('/sessions/:sessionId/messages/:messageId', async (req, res) => {
|
|
|
268
269
|
});
|
|
269
270
|
}
|
|
270
271
|
});
|
|
272
|
+
// Truncate a session at a message: drop it and everything after it
|
|
273
|
+
// (edit-and-resend). The metadata PUT deliberately ignores `messages`,
|
|
274
|
+
// so this is the only way a client can shorten the server-side history.
|
|
275
|
+
router.post('/sessions/:sessionId/messages/:messageId/truncate', async (req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
const sessionId = req.params.sessionId;
|
|
278
|
+
const messageId = req.params.messageId;
|
|
279
|
+
const userId = req.user?.userId || 'default';
|
|
280
|
+
const updatedSession = await chatService.truncateMessagesFrom(sessionId, messageId, userId);
|
|
281
|
+
if (!updatedSession) {
|
|
282
|
+
res.status(404).json({
|
|
283
|
+
success: false,
|
|
284
|
+
error: 'Session or message not found',
|
|
285
|
+
});
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
res.json({
|
|
289
|
+
success: true,
|
|
290
|
+
data: updatedSession,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
res.status(500).json({
|
|
295
|
+
success: false,
|
|
296
|
+
error: getErrorMessage(error, 'Failed to truncate session'),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
271
300
|
// Delete a chat session
|
|
272
301
|
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
273
302
|
try {
|
|
@@ -410,7 +439,12 @@ router.post('/sessions/:sessionId/generate', async (req, res) => {
|
|
|
410
439
|
(await isWebSearchAvailable()) &&
|
|
411
440
|
(await userCanUseWebSearch(await userModel.getUserById(userId)))) {
|
|
412
441
|
try {
|
|
413
|
-
const results = await
|
|
442
|
+
const { results } = await runPlannedWebSearch({
|
|
443
|
+
message,
|
|
444
|
+
session,
|
|
445
|
+
userId,
|
|
446
|
+
signal,
|
|
447
|
+
});
|
|
414
448
|
if (results.length > 0) {
|
|
415
449
|
enhancedContent = buildWebSearchEnhancedContent(enhancedContent, results, message);
|
|
416
450
|
hasRelevantContext = true;
|
|
@@ -429,7 +463,7 @@ router.post('/sessions/:sessionId/generate', async (req, res) => {
|
|
|
429
463
|
session,
|
|
430
464
|
userId,
|
|
431
465
|
options,
|
|
432
|
-
persistedMessages: await chatService.getMessagesForContext(sessionId, userId),
|
|
466
|
+
persistedMessages: await chatService.getMessagesForContext(sessionId, userId, undefined, signal),
|
|
433
467
|
content: message,
|
|
434
468
|
hasRelevantContext,
|
|
435
469
|
enhancedContent,
|
|
@@ -857,7 +891,12 @@ router.post('/sessions/:sessionId/generate/stream', async (req, res) => {
|
|
|
857
891
|
(await userCanUseWebSearch(await userModel.getUserById(userId)))) {
|
|
858
892
|
await emitDurable({ type: 'search', status: 'searching' });
|
|
859
893
|
try {
|
|
860
|
-
const results = await
|
|
894
|
+
const { results } = await runPlannedWebSearch({
|
|
895
|
+
message,
|
|
896
|
+
session,
|
|
897
|
+
userId,
|
|
898
|
+
signal,
|
|
899
|
+
});
|
|
861
900
|
if (results.length > 0) {
|
|
862
901
|
searchEnhancedContent = buildWebSearchEnhancedContent(documentContext.enhancedContent, results, message);
|
|
863
902
|
webSearchSources = results.map(({ title, url }) => ({
|
|
@@ -897,7 +936,7 @@ router.post('/sessions/:sessionId/generate/stream', async (req, res) => {
|
|
|
897
936
|
session,
|
|
898
937
|
userId,
|
|
899
938
|
options,
|
|
900
|
-
persistedMessages: await chatService.getMessagesForContext(sessionId, userId),
|
|
939
|
+
persistedMessages: await chatService.getMessagesForContext(sessionId, userId, undefined, signal),
|
|
901
940
|
content: message,
|
|
902
941
|
enhancedContent: searchEnhancedContent ?? documentContext.enhancedContent,
|
|
903
942
|
hasRelevantContext: Boolean(searchEnhancedContent ?? documentContext.hasRelevantContext),
|
|
@@ -1423,8 +1462,61 @@ router.post('/sessions/:sessionId/messages/:messageId/branches', authenticate, c
|
|
|
1423
1462
|
});
|
|
1424
1463
|
}
|
|
1425
1464
|
});
|
|
1426
|
-
/**
|
|
1427
|
-
|
|
1465
|
+
/**
|
|
1466
|
+
* Undo one compaction: the summary knows exactly which messages it replaced,
|
|
1467
|
+
* so restoring is reactivating them and removing the summary.
|
|
1468
|
+
*/
|
|
1469
|
+
router.post('/sessions/:sessionId/compaction/:messageId/restore', authenticate, chatRateLimiter, async (req, res) => {
|
|
1470
|
+
try {
|
|
1471
|
+
const sessionId = req.params.sessionId;
|
|
1472
|
+
const messageId = req.params.messageId;
|
|
1473
|
+
const userId = req.user?.userId || 'default';
|
|
1474
|
+
const session = await chatService.restoreCompaction(sessionId, userId, messageId);
|
|
1475
|
+
if (!session) {
|
|
1476
|
+
res.status(404).json({
|
|
1477
|
+
success: false,
|
|
1478
|
+
error: 'No restorable summary with that id in this session',
|
|
1479
|
+
});
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
res.json({ success: true, data: session });
|
|
1483
|
+
}
|
|
1484
|
+
catch (error) {
|
|
1485
|
+
logger.error('Restore compaction error:', error);
|
|
1486
|
+
res.status(500).json({
|
|
1487
|
+
success: false,
|
|
1488
|
+
error: getErrorMessage(error, 'Failed to restore compacted messages'),
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
});
|
|
1492
|
+
/**
|
|
1493
|
+
* The rolling window every conversation runs with, for any signed-in user:
|
|
1494
|
+
* the context meter needs the real message count to mirror what is sent.
|
|
1495
|
+
* Deliberately narrow — none of the admin compaction configuration (prompt,
|
|
1496
|
+
* model, thresholds) leaves this endpoint.
|
|
1497
|
+
*/
|
|
1498
|
+
router.get('/context-policy', async (_req, res) => {
|
|
1499
|
+
try {
|
|
1500
|
+
const compaction = await getCompactionConfig();
|
|
1501
|
+
res.json({
|
|
1502
|
+
success: true,
|
|
1503
|
+
data: {
|
|
1504
|
+
windowMessages: compaction.enabled
|
|
1505
|
+
? Math.max(10, compaction.keepRecentMessages)
|
|
1506
|
+
: 10,
|
|
1507
|
+
},
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
catch (error) {
|
|
1511
|
+
res.status(500).json({
|
|
1512
|
+
success: false,
|
|
1513
|
+
error: getErrorMessage(error, 'Failed to load context policy'),
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
});
|
|
1517
|
+
/** Context compaction settings: admin-only in both directions — the custom
|
|
1518
|
+
* summarizer prompt is administrator configuration, not user data. */
|
|
1519
|
+
router.get('/compaction-config', requireAdmin, async (_req, res) => {
|
|
1428
1520
|
try {
|
|
1429
1521
|
res.json({ success: true, data: await getCompactionConfig() });
|
|
1430
1522
|
}
|