@openchambery/web 2.0.0-beta.4 → 2.0.0-beta.6
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/dist/assets/{AgentAvatar-Q1SmFRx2.js → AgentAvatar-BBmQXYDq.js} +1 -1
- package/dist/assets/AssistantView-BpvbpZBC.js +3 -0
- package/dist/assets/{DiagramView-BW92schO.js → DiagramView-UwKDr8HS.js} +1 -1
- package/dist/assets/{FadeInOnReveal-BZmwl-zE.js → FadeInOnReveal-w_BQ7K7K.js} +1 -1
- package/dist/assets/Icon-B9kZgYZn.js +1 -0
- package/dist/assets/{MarkdownRenderer-Bv05iDZZ.js → MarkdownRenderer-C8tDsO5F.js} +2 -2
- package/dist/assets/{MarkdownRendererImpl-C81xqYib.js → MarkdownRendererImpl-DRZ1iTbg.js} +1 -1
- package/dist/assets/MarkstreamRendererImpl-fU6uhDTr.js +3 -0
- package/dist/assets/{MobileDetailNavigation-BMgIu7zc.js → MobileDetailNavigation-MXUDe0nG.js} +1 -1
- package/dist/assets/{MobileShareBridge-BOXGNHmC.js → MobileShareBridge-C7x4jOGb.js} +2 -2
- package/dist/assets/MobileSurface-BQSmXYJ_.js +2 -0
- package/dist/assets/{MultiRunWindow-B5At2Z95.js → MultiRunWindow-ClfxxgP5.js} +1 -1
- package/dist/assets/{OnboardingScreen-bsPLgz4r.js → OnboardingScreen-BxItVnnO.js} +1 -1
- package/dist/assets/{OverlayScrollbar-4wp4migh.js → OverlayScrollbar-B4e0QD05.js} +1 -1
- package/dist/assets/SettingsView-DtWgey4y.js +25 -0
- package/dist/assets/{SettingsWindow-CMtX2bMl.js → SettingsWindow-Du8y1EI8.js} +1 -1
- package/dist/assets/TerminalView-DL9qkQDj.js +8 -0
- package/dist/assets/{ToolOutputDialog-uH1ZZDSS.js → ToolOutputDialog-BXawDmmf.js} +1 -1
- package/dist/assets/{appThemeRegistry-DK7q2juD.js → appThemeRegistry-DsSahEsq.js} +1 -1
- package/dist/assets/{button-CcNJUxUd.js → button-CwXyux0a.js} +1 -1
- package/dist/assets/collapsible-BZ6_cYJd.js +1 -0
- package/dist/assets/context-menu-DHrRifPE.js +1 -0
- package/dist/assets/{desktopHosts-C6HrP0jq.js → desktopHosts-JGFqhlaM.js} +1 -1
- package/dist/assets/{desktopRelayRestore-DdVBp45U.js → desktopRelayRestore-CHEwrI93.js} +1 -1
- package/dist/assets/{diagram-CxTs9hCQ.js → diagram-BwnPNKWt.js} +1 -1
- package/dist/assets/{dialog-KMVL9QKF.js → dialog-Dp0lQv5K.js} +1 -1
- package/dist/assets/es-DicJ8lKD.js +15 -0
- package/dist/assets/fileMentionTouchSelection-PZQrywzZ.js +3 -0
- package/dist/assets/{fileQueries-CjdBRy8y.js → fileQueries-DN6gbLhM.js} +1 -1
- package/dist/assets/fr-lhgVcBW_.js +13 -0
- package/dist/assets/{gitApi-yuWg9BN4.js → gitApi-C1oGENfw.js} +4 -4
- package/dist/assets/{imageSource-9vQBq3zp.js → imageSource-BMztRu0c.js} +1 -1
- package/dist/assets/{input-CrIvk3HZ.js → input-Biw8aNzR.js} +1 -1
- package/dist/assets/insertionBoundaries-BmQh-tke.js +1 -0
- package/dist/assets/ja-B9EZwOcw.js +15 -0
- package/dist/assets/ko-Dxk-uJRb.js +15 -0
- package/dist/assets/main-BnyE5H4i.js +169 -0
- package/dist/assets/main-D2BzpnUt.css +1 -0
- package/dist/assets/main-DOgXHO44.js +2 -0
- package/dist/assets/{markdownHeightCache-BmXVY_gc.js → markdownHeightCache-BOOrGCuG.js} +1 -1
- package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
- package/dist/assets/{mcpQueries-C7jknHV8.js → mcpQueries-D9Dyzcve.js} +1 -1
- package/dist/assets/miniChat-C7FecHIX.js +2 -0
- package/dist/assets/mobile-_zrfbam4.js +2 -0
- package/dist/assets/multirun-BLFbv4I9.js +4 -0
- package/dist/assets/persistence-D0y7226-.js +3 -0
- package/dist/assets/pl-CFhwWLJj.js +15 -0
- package/dist/assets/platform-D_c-T5nO.js +6 -0
- package/dist/assets/projectMeta-BCdCRuYS.js +3 -0
- package/dist/assets/pt-BR-Ba6GfZ6J.js +15 -0
- package/dist/assets/renderElectronMiniChatApp-QQq7dj-0.js +2 -0
- package/dist/assets/renderMobileApp-BVIkeTKi.js +28 -0
- package/dist/assets/{runtimeConfig-BEANkvOd.js → runtimeConfig-S9D4fVI5.js} +1 -1
- package/dist/assets/runtimeEndpointReset-B6eE14f9.js +149 -0
- package/dist/assets/sessionChangeSummary-DLo3Iv_g.js +2 -0
- package/dist/assets/{sessionLookup-34XFmP9C.js → sessionLookup-BXXTw54z.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-DyEV0Heg.js → sortable-tabs-strip-CTY_wG46.js} +1 -1
- package/dist/assets/src-lS1keDVV.css +2 -0
- package/dist/assets/store-DmowK-GO.js +15 -0
- package/dist/assets/{terminalApi-CXaBEoa_.js → terminalApi-Dhqqp4xi.js} +1 -1
- package/dist/assets/{textarea-BBOlRI9O.js → textarea-QxsmcBGj.js} +1 -1
- package/dist/assets/{tooltip-CPI4fr1a.js → tooltip-CpMLl5_k.js} +1 -1
- package/dist/assets/uk-ChchBzy6.js +15 -0
- package/dist/assets/{url-DRDQXjyX.js → url-CGbJbiJq.js} +1 -1
- package/dist/assets/{useAppFontEffects-CiDe42I5.js → useAppFontEffects-BDs0O2qK.js} +56 -51
- package/dist/assets/{useAssistantUIStore-asNG6K7n.js → useAssistantUIStore-Bz4tRflF.js} +1 -1
- package/dist/assets/useDesktopWindowControlsLayout-ojoBEglc.js +1 -0
- package/dist/assets/{useEffectiveDirectory-C_CouL5v.js → useEffectiveDirectory-B_nHyDMJ.js} +1 -1
- package/dist/assets/{useFontPreferences-B0oUZAiB.js → useFontPreferences-Xw1vSkmC.js} +1 -1
- package/dist/assets/useMobileNavigationStore-D_96D661.js +414 -0
- package/dist/assets/{useRuntimeAPIs-DwLtO0c_.js → useRuntimeAPIs-DSLXJybW.js} +1 -1
- package/dist/assets/useSessionAutoCleanup-Dw34f-EW.js +2 -0
- package/dist/assets/{useThemeSystem-BlNUWH47.js → useThemeSystem-DMzWVJaW.js} +1 -1
- package/dist/assets/useUIStore-syVmbuDA.js +1 -0
- package/dist/assets/{useWorkerHighlightedLines-CPNYz6Wf.js → useWorkerHighlightedLines-CNlfP7nA.js} +1 -1
- package/dist/assets/{vendor-base-ui-CDjyofeD.js → vendor-base-ui-CnHfUmDN.js} +1 -1
- package/dist/assets/{vendor-cmdk-BFH0UpXX.js → vendor-cmdk-CeAGfrLo.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-accessibility-BrR3AKvM.js → vendor-dnd-kit-accessibility-BY5LhhUB.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-core-CNfdvk6H.js → vendor-dnd-kit-core-CvWxUPdb.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-sortable-DQUaTeGj.js → vendor-dnd-kit-sortable-hHdP9lLb.js} +1 -1
- package/dist/assets/{vendor-framer-motion-4dogsUk3.js → vendor-framer-motion-DFy_BSVd.js} +1 -1
- package/dist/assets/{vendor-legendapp-list-BU7MdPZR.js → vendor-legendapp-list-WE32lwNk.js} +1 -1
- package/dist/assets/{vendor-markstream-react-qHC2FykU.js → vendor-markstream-react-DX55XLGO.js} +3 -3
- package/dist/assets/vendor-opencode-client-C12gfjag.js +12 -0
- package/dist/assets/{vendor-pierre-diffs-CUfqC003.js → vendor-pierre-diffs-DX7XTPG3.js} +1 -1
- package/dist/assets/{vendor-react-C94A0AHl.js → vendor-react-DLPElpoQ.js} +1 -1
- package/dist/assets/{vendor-react-drawio-FVlWqKVE.js → vendor-react-drawio-ECtEwvbc.js} +1 -1
- package/dist/assets/{vendor-reactuses-core-C2A3EjUi.js → vendor-reactuses-core-Dq5ocTF8.js} +1 -1
- package/dist/assets/{vendor-sonner-DNqHmUQs.js → vendor-sonner-DVzioF3d.js} +1 -1
- package/dist/assets/{vendor-stream-diffs-CwbKsmlv.js → vendor-stream-diffs-DWy3xSmp.js} +2 -2
- package/dist/assets/{vendor-tanstack-react-query-D0iFK57C.js → vendor-tanstack-react-query-D7pys_c9.js} +1 -1
- package/dist/assets/{vendor-tanstack-react-virtual-B2kV6cnT.js → vendor-tanstack-react-virtual-tlDgH6PG.js} +1 -1
- package/dist/assets/{vendor-zustand-BqLKf6ZY.js → vendor-zustand-B4dlGEM2.js} +1 -1
- package/dist/assets/zh-CN-Z_bkUQk5.js +15 -0
- package/dist/assets/zh-TW-CZeTm7kd.js +15 -0
- package/dist/index.html +13 -13
- package/dist/mini-chat.html +13 -13
- package/dist/mobile.html +13 -13
- package/package.json +3 -2
- package/server/index.js +38 -7
- package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
- package/server/lib/browser-provider/contract.js +312 -0
- package/server/lib/browser-provider/host.js +421 -0
- package/server/lib/browser-provider/host.test.js +304 -0
- package/server/lib/browser-provider/routes.js +122 -0
- package/server/lib/browser-provider/routes.test.js +120 -0
- package/server/lib/browser-provider/selection-store.js +43 -0
- package/server/lib/browser-provider/selection-store.test.js +35 -0
- package/server/lib/notifications/DOCUMENTATION.md +3 -1
- package/server/lib/notifications/runtime.js +110 -26
- package/server/lib/notifications/runtime.test.js +92 -0
- package/server/lib/notifications/template-runtime.js +130 -38
- package/server/lib/notifications/template-runtime.test.js +96 -0
- package/server/lib/opencode/DOCUMENTATION.md +4 -0
- package/server/lib/opencode/ensure-cli.d.ts +8 -0
- package/server/lib/opencode/feature-routes-runtime.js +10 -0
- package/server/lib/opencode/feature-routes-runtime.test.js +7 -0
- package/server/lib/opencode/proxy.js +3 -1
- package/server/lib/opencode/proxy.test.js +3 -3
- package/server/lib/opencode/routes.js +30 -0
- package/server/lib/opencode/upgrade-screen.d.ts +30 -0
- package/server/lib/opencode/upgrade-screen.js +299 -0
- package/server/lib/opencode/upgrade-screen.test.js +196 -0
- package/server/lib/session-metadata/DOCUMENTATION.md +27 -17
- package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
- package/server/lib/session-metadata/opencode-session-record.js +316 -0
- package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
- package/server/lib/session-metadata/routes.js +5 -1
- package/server/lib/session-metadata/session-archive.js +1 -1
- package/server/lib/session-metadata/session-metadata-store.d.ts +2 -0
- package/server/lib/session-metadata/session-metadata-store.js +63 -16
- package/server/lib/session-metadata/session-projection.js +26 -2
- package/server/lib/session-title/runtime.js +2 -2
- package/dist/assets/AssistantView-CwXkiCAd.js +0 -3
- package/dist/assets/Icon-AQPvxMiU.js +0 -1
- package/dist/assets/MarkstreamRendererImpl-CXUCQuKJ.js +0 -3
- package/dist/assets/MobileSurface-xuc_z0_G.js +0 -2
- package/dist/assets/SettingsView-CJ6tcH5_.js +0 -25
- package/dist/assets/TerminalView-Dvr-ysTS.js +0 -8
- package/dist/assets/collapsible-ZmZ6rLWs.js +0 -3
- package/dist/assets/context-menu-B2LXJgXL.js +0 -1
- package/dist/assets/es-CN3E_zXj.js +0 -15
- package/dist/assets/fileMentionTouchSelection-7RIwlpkB.js +0 -4
- package/dist/assets/fr-9zZvQZD2.js +0 -13
- package/dist/assets/insertionBoundaries-BiUCSkx-.js +0 -1
- package/dist/assets/ja-Dnjk_82N.js +0 -15
- package/dist/assets/ko-BxCEE99C.js +0 -15
- package/dist/assets/main-C9mIBo77.js +0 -2
- package/dist/assets/main-DiL6w6KF.js +0 -169
- package/dist/assets/main-qSeeJRW1.css +0 -1
- package/dist/assets/markdownTheme-CKac3wKC.js +0 -1
- package/dist/assets/miniChat-CwtUaElU.js +0 -2
- package/dist/assets/mobile-DB-6--ws.js +0 -2
- package/dist/assets/multirun-TAsxFI8e.js +0 -4
- package/dist/assets/persistence-56kq8Z4V.js +0 -3
- package/dist/assets/pl-CNNsWnPM.js +0 -15
- package/dist/assets/platform-C13fMVEs.js +0 -6
- package/dist/assets/pt-BR-BZB2DJMp.js +0 -15
- package/dist/assets/renderElectronMiniChatApp-CGj0MNRU.js +0 -2
- package/dist/assets/renderMobileApp-CGMMqbsO.js +0 -28
- package/dist/assets/runtimeEndpointReset-A_ZBUJFS.js +0 -147
- package/dist/assets/sessionChangeSummary-qOLrP9fw.js +0 -2
- package/dist/assets/src-ubCWXZhR.css +0 -2
- package/dist/assets/store-DLCiZaA6.js +0 -15
- package/dist/assets/uk-rwQrxeYM.js +0 -15
- package/dist/assets/useDesktopWindowControlsLayout-BlizAlOt.js +0 -1
- package/dist/assets/useMobileNavigationStore-DfPClaW-.js +0 -414
- package/dist/assets/useSessionAutoCleanup-DtrfbxAp.js +0 -2
- package/dist/assets/useUIStore-CQYKJnrg.js +0 -1
- package/dist/assets/vendor-opencode-client-BOe2qpQb.js +0 -12
- package/dist/assets/zh-CN-BvmBDnkD.js +0 -15
- package/dist/assets/zh-TW-9wcHTX0y.js +0 -15
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
import { readNotificationSessionRecord } from './template-runtime.js';
|
|
2
|
+
|
|
3
|
+
const trimmedText = (value) => (typeof value === 'string' && value.trim() ? value.trim() : '');
|
|
4
|
+
|
|
5
|
+
const readNotificationEventFields = (payload) => {
|
|
6
|
+
const properties = payload?.properties && typeof payload.properties === 'object' && !Array.isArray(payload.properties)
|
|
7
|
+
? payload.properties
|
|
8
|
+
: {};
|
|
9
|
+
const data = payload?.data && typeof payload.data === 'object' && !Array.isArray(payload.data)
|
|
10
|
+
? payload.data
|
|
11
|
+
: {};
|
|
12
|
+
const form = (properties.form && typeof properties.form === 'object' ? properties.form : null)
|
|
13
|
+
|| (data.form && typeof data.form === 'object' ? data.form : null);
|
|
14
|
+
const questions = Array.isArray(properties.questions)
|
|
15
|
+
? properties.questions
|
|
16
|
+
: (Array.isArray(data.questions) ? data.questions : []);
|
|
17
|
+
const firstQuestion = questions[0] && typeof questions[0] === 'object' ? questions[0] : null;
|
|
18
|
+
const field = Array.isArray(form?.fields)
|
|
19
|
+
? form.fields.find((item) => item && typeof item === 'object' && item.hidden !== true)
|
|
20
|
+
: null;
|
|
21
|
+
return {
|
|
22
|
+
requestId: trimmedText(properties.id)
|
|
23
|
+
|| trimmedText(properties.requestID)
|
|
24
|
+
|| trimmedText(properties.requestId)
|
|
25
|
+
|| trimmedText(data.id)
|
|
26
|
+
|| trimmedText(data.requestID)
|
|
27
|
+
|| trimmedText(data.requestId)
|
|
28
|
+
|| trimmedText(form?.id),
|
|
29
|
+
permission: trimmedText(properties.permission) || trimmedText(data.permission) || trimmedText(data.action),
|
|
30
|
+
message: trimmedText(data.message) || trimmedText(properties.message),
|
|
31
|
+
header: trimmedText(firstQuestion?.header) || trimmedText(field?.title) || trimmedText(form?.title),
|
|
32
|
+
questionText: trimmedText(firstQuestion?.question)
|
|
33
|
+
|| trimmedText(field?.description)
|
|
34
|
+
|| trimmedText(field?.title)
|
|
35
|
+
|| trimmedText(form?.title),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
1
39
|
export const createNotificationTriggerRuntime = (deps) => {
|
|
2
40
|
const {
|
|
3
41
|
readSettingsFromDisk,
|
|
@@ -255,14 +293,14 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
255
293
|
if (!response.ok) {
|
|
256
294
|
return cached;
|
|
257
295
|
}
|
|
258
|
-
const session = await response.json().catch(() => null);
|
|
259
|
-
if (!session
|
|
296
|
+
const session = readNotificationSessionRecord(await response.json().catch(() => null));
|
|
297
|
+
if (!session) {
|
|
260
298
|
return cached;
|
|
261
299
|
}
|
|
262
300
|
|
|
263
301
|
const meta = {
|
|
264
302
|
parentID: normalizeParentID(session.parentID),
|
|
265
|
-
title:
|
|
303
|
+
title: trimmedText(session.title) || null,
|
|
266
304
|
complete: true,
|
|
267
305
|
...metaFromOpenchamber(session.metadata?.openchamber),
|
|
268
306
|
};
|
|
@@ -350,11 +388,20 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
350
388
|
? payload.properties
|
|
351
389
|
: (payload.data && typeof payload.data === 'object' ? payload.data : null);
|
|
352
390
|
const info = props?.info;
|
|
391
|
+
const data = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)
|
|
392
|
+
? payload.data
|
|
393
|
+
: null;
|
|
353
394
|
const sessionId =
|
|
354
395
|
info?.sessionID ??
|
|
355
396
|
info?.sessionId ??
|
|
356
397
|
props?.sessionID ??
|
|
357
398
|
props?.sessionId ??
|
|
399
|
+
props?.form?.sessionID ??
|
|
400
|
+
props?.form?.sessionId ??
|
|
401
|
+
data?.sessionID ??
|
|
402
|
+
data?.sessionId ??
|
|
403
|
+
data?.form?.sessionID ??
|
|
404
|
+
data?.form?.sessionId ??
|
|
358
405
|
props?.session ??
|
|
359
406
|
null;
|
|
360
407
|
return typeof sessionId === 'string' && sessionId.length > 0 ? sessionId : null;
|
|
@@ -362,11 +409,32 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
362
409
|
|
|
363
410
|
const extractDirectoryFromPayload = (payload) => {
|
|
364
411
|
if (!payload || typeof payload !== 'object') return undefined;
|
|
365
|
-
const props = payload.properties;
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
const
|
|
369
|
-
|
|
412
|
+
const props = payload.properties && typeof payload.properties === 'object' ? payload.properties : {};
|
|
413
|
+
const data = payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : {};
|
|
414
|
+
const info = props.info && typeof props.info === 'object' ? props.info : {};
|
|
415
|
+
const dataInfo = data.info && typeof data.info === 'object' ? data.info : {};
|
|
416
|
+
const candidates = [
|
|
417
|
+
props.directory,
|
|
418
|
+
info.directory,
|
|
419
|
+
info.location?.directory,
|
|
420
|
+
payload.location?.directory,
|
|
421
|
+
data.directory,
|
|
422
|
+
data.location?.directory,
|
|
423
|
+
dataInfo.directory,
|
|
424
|
+
dataInfo.location?.directory,
|
|
425
|
+
];
|
|
426
|
+
for (const value of candidates) {
|
|
427
|
+
const directory = trimmedText(value);
|
|
428
|
+
if (directory) return directory;
|
|
429
|
+
}
|
|
430
|
+
return undefined;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const resolvePushSessionName = async (variables, sessionId, directory) => {
|
|
434
|
+
const fromTemplate = trimmedText(variables?.session_name);
|
|
435
|
+
if (fromTemplate) return fromTemplate;
|
|
436
|
+
const meta = await fetchSessionMeta(sessionId, directory);
|
|
437
|
+
return trimmedText(meta?.title);
|
|
370
438
|
};
|
|
371
439
|
|
|
372
440
|
// A session with an ACTIVE goal suppresses per-turn ready notifications;
|
|
@@ -383,7 +451,7 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
383
451
|
signal: AbortSignal.timeout(2000),
|
|
384
452
|
});
|
|
385
453
|
if (!response.ok) return false;
|
|
386
|
-
const session = await response.json().catch(() => null);
|
|
454
|
+
const session = readNotificationSessionRecord(await response.json().catch(() => null));
|
|
387
455
|
const goal = session?.metadata?.openchamber?.goal;
|
|
388
456
|
return Boolean(goal && typeof goal === 'object' && goal.status === 'active');
|
|
389
457
|
} catch {
|
|
@@ -505,12 +573,12 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
505
573
|
const completionTemplate = templates.completion || { title: 'Task completed', message: '{session_name}' };
|
|
506
574
|
|
|
507
575
|
const variables = await buildTemplateVariables(payload, sessionId);
|
|
508
|
-
sessionName =
|
|
576
|
+
sessionName = await resolvePushSessionName(variables, sessionId, notificationDirectory);
|
|
509
577
|
|
|
510
578
|
const messageId = info?.id;
|
|
511
579
|
let lastMessage = extractLastMessageText(payload);
|
|
512
580
|
if (!lastMessage) {
|
|
513
|
-
lastMessage = await fetchLastAssistantMessageText(sessionId, messageId);
|
|
581
|
+
lastMessage = await fetchLastAssistantMessageText(sessionId, messageId, undefined, notificationDirectory);
|
|
514
582
|
}
|
|
515
583
|
|
|
516
584
|
variables.last_message = await prepareNotificationLastMessage({
|
|
@@ -561,7 +629,21 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
561
629
|
return;
|
|
562
630
|
}
|
|
563
631
|
|
|
564
|
-
if (
|
|
632
|
+
if ((
|
|
633
|
+
payload.type === 'form.replied'
|
|
634
|
+
|| payload.type === 'form.cancelled'
|
|
635
|
+
|| payload.type === 'question.replied'
|
|
636
|
+
|| payload.type === 'question.rejected'
|
|
637
|
+
) && sessionId) {
|
|
638
|
+
const existingTimer = pushQuestionDebounceTimers.get(sessionId);
|
|
639
|
+
if (existingTimer) {
|
|
640
|
+
clearTimeout(existingTimer);
|
|
641
|
+
pushQuestionDebounceTimers.delete(sessionId);
|
|
642
|
+
}
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if ((payload.type === 'question.asked' || payload.type === 'form.created') && sessionId) {
|
|
565
647
|
if (await shouldSkipSystemSessionNotification(sessionId, notificationDirectory)) {
|
|
566
648
|
return;
|
|
567
649
|
}
|
|
@@ -582,9 +664,9 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
582
664
|
return;
|
|
583
665
|
}
|
|
584
666
|
|
|
585
|
-
const
|
|
586
|
-
const header =
|
|
587
|
-
const questionText =
|
|
667
|
+
const prompt = readNotificationEventFields(payload);
|
|
668
|
+
const header = prompt.header;
|
|
669
|
+
const questionText = prompt.questionText;
|
|
588
670
|
|
|
589
671
|
let title = 'Needs your answer';
|
|
590
672
|
let body = '';
|
|
@@ -592,7 +674,7 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
592
674
|
|
|
593
675
|
try {
|
|
594
676
|
const variables = await buildTemplateVariables(payload, sessionId);
|
|
595
|
-
sessionName =
|
|
677
|
+
sessionName = await resolvePushSessionName(variables, sessionId, notificationDirectory);
|
|
596
678
|
variables.last_message = questionText || header || '';
|
|
597
679
|
|
|
598
680
|
const templates = settings.notificationTemplates || {};
|
|
@@ -643,8 +725,8 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
643
725
|
}
|
|
644
726
|
|
|
645
727
|
if (payload.type === 'permission.replied' && sessionId) {
|
|
646
|
-
const requestId = payload.
|
|
647
|
-
const requestKey =
|
|
728
|
+
const requestId = readNotificationEventFields(payload).requestId;
|
|
729
|
+
const requestKey = requestId ? `${sessionId}:${requestId}` : null;
|
|
648
730
|
const pendingNotification = pushPermissionDebounceTimers.get(sessionId);
|
|
649
731
|
if (!pendingNotification) {
|
|
650
732
|
return;
|
|
@@ -664,9 +746,10 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
664
746
|
if (await shouldSkipSystemSessionNotification(sessionId, notificationDirectory)) {
|
|
665
747
|
return;
|
|
666
748
|
}
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
const
|
|
749
|
+
const permissionFields = readNotificationEventFields(payload);
|
|
750
|
+
const requestId = permissionFields.requestId;
|
|
751
|
+
const permission = permissionFields.permission;
|
|
752
|
+
const requestKey = requestId ? `${sessionId}:${requestId}` : null;
|
|
670
753
|
if (requestKey && notifiedPermissionRequests.has(requestKey)) {
|
|
671
754
|
return;
|
|
672
755
|
}
|
|
@@ -704,11 +787,12 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
704
787
|
return;
|
|
705
788
|
}
|
|
706
789
|
|
|
707
|
-
const sessionTitle = payload.properties?.sessionTitle;
|
|
790
|
+
const sessionTitle = payload.properties?.sessionTitle ?? payload.data?.sessionTitle;
|
|
708
791
|
const permissionText = typeof permission === 'string' && permission.length > 0 ? permission : '';
|
|
792
|
+
const detail = permissionFields.message;
|
|
709
793
|
const fallbackMessage = typeof sessionTitle === 'string' && sessionTitle.trim().length > 0
|
|
710
794
|
? sessionTitle.trim()
|
|
711
|
-
: permissionText || 'Agent is waiting for your approval';
|
|
795
|
+
: detail || permissionText || 'Agent is waiting for your approval';
|
|
712
796
|
|
|
713
797
|
let title = 'Needs permission';
|
|
714
798
|
let body = '';
|
|
@@ -716,7 +800,7 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
716
800
|
|
|
717
801
|
try {
|
|
718
802
|
const variables = await buildTemplateVariables(payload, sessionId);
|
|
719
|
-
sessionName =
|
|
803
|
+
sessionName = await resolvePushSessionName(variables, sessionId, notificationDirectory);
|
|
720
804
|
variables.last_message = fallbackMessage;
|
|
721
805
|
|
|
722
806
|
const templates = settings.notificationTemplates || {};
|
|
@@ -784,8 +868,8 @@ export const createNotificationTriggerRuntime = (deps) => {
|
|
|
784
868
|
signal: AbortSignal.timeout(2000),
|
|
785
869
|
});
|
|
786
870
|
if (response.ok) {
|
|
787
|
-
const session = await response.json().catch(() => null);
|
|
788
|
-
|
|
871
|
+
const session = readNotificationSessionRecord(await response.json().catch(() => null));
|
|
872
|
+
sessionName = trimmedText(session?.title);
|
|
789
873
|
}
|
|
790
874
|
} catch {
|
|
791
875
|
// Session name is presentation sugar for the mobile push — never block on it.
|
|
@@ -626,6 +626,97 @@ describe('notification event toggles', () => {
|
|
|
626
626
|
expect(sendPushToAllUiSessions).not.toHaveBeenCalled();
|
|
627
627
|
});
|
|
628
628
|
|
|
629
|
+
it('notifies on an OpenCode 2 question form and a permission request', async () => {
|
|
630
|
+
vi.useFakeTimers();
|
|
631
|
+
vi.stubGlobal('fetch', vi.fn(async () => jsonResponse({
|
|
632
|
+
data: { id: 'ses_ask', parentID: null, title: '发布说明', metadata: {} },
|
|
633
|
+
})));
|
|
634
|
+
const resolveNotificationTemplate = vi.fn((template, variables) => (
|
|
635
|
+
typeof template === 'string'
|
|
636
|
+
? template.replace(/\{(\w+)\}/g, (_match, key) => variables?.[key] ?? '')
|
|
637
|
+
: template
|
|
638
|
+
));
|
|
639
|
+
const { runtime, emitDesktopNotification, sendApnsToAllUiSessions } = createRuntime({
|
|
640
|
+
buildTemplateVariables: vi.fn(async () => ({ session_name: '' })),
|
|
641
|
+
resolveNotificationTemplate,
|
|
642
|
+
readSettingsFromDisk: vi.fn(async () => ({
|
|
643
|
+
...defaultSettings,
|
|
644
|
+
notificationTemplates: {
|
|
645
|
+
question: { title: 'Needs your answer', message: '{last_message}' },
|
|
646
|
+
},
|
|
647
|
+
})),
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
await runtime.maybeSendPushForTrigger({
|
|
651
|
+
type: 'form.created',
|
|
652
|
+
data: {
|
|
653
|
+
form: {
|
|
654
|
+
id: 'frm_1',
|
|
655
|
+
sessionID: 'ses_ask',
|
|
656
|
+
title: 'Questions',
|
|
657
|
+
metadata: { kind: 'question' },
|
|
658
|
+
fields: [{ key: 'q0', title: '确认入口', description: '从哪个入口触发?' }],
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
});
|
|
662
|
+
await vi.runAllTimersAsync();
|
|
663
|
+
|
|
664
|
+
expect(emitDesktopNotification).toHaveBeenCalledWith(expect.objectContaining({
|
|
665
|
+
kind: 'question',
|
|
666
|
+
body: '从哪个入口触发?',
|
|
667
|
+
}));
|
|
668
|
+
expect(sendApnsToAllUiSessions).toHaveBeenCalledWith(
|
|
669
|
+
expect.objectContaining({ type: 'question', sessionName: '发布说明' }),
|
|
670
|
+
{ requireNoSse: true },
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
emitDesktopNotification.mockClear();
|
|
674
|
+
sendApnsToAllUiSessions.mockClear();
|
|
675
|
+
await runtime.maybeSendPushForTrigger({
|
|
676
|
+
type: 'permission.asked',
|
|
677
|
+
data: {
|
|
678
|
+
id: 'per_1',
|
|
679
|
+
sessionID: 'ses_ask',
|
|
680
|
+
action: 'edit',
|
|
681
|
+
message: '修改 README',
|
|
682
|
+
resources: ['README.md'],
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
await vi.runAllTimersAsync();
|
|
686
|
+
|
|
687
|
+
expect(emitDesktopNotification).toHaveBeenCalledWith(expect.objectContaining({
|
|
688
|
+
kind: 'permission',
|
|
689
|
+
body: '修改 README',
|
|
690
|
+
}));
|
|
691
|
+
expect(sendApnsToAllUiSessions).toHaveBeenCalledWith(
|
|
692
|
+
expect.objectContaining({ type: 'permission', sessionName: '发布说明' }),
|
|
693
|
+
{ requireNoSse: true },
|
|
694
|
+
);
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('uses the wrapped OpenCode 2 session title when the template has none', async () => {
|
|
698
|
+
vi.stubGlobal('fetch', vi.fn(async () => jsonResponse({
|
|
699
|
+
data: {
|
|
700
|
+
id: 'ses_done',
|
|
701
|
+
parentID: null,
|
|
702
|
+
title: '发布说明',
|
|
703
|
+
metadata: {},
|
|
704
|
+
},
|
|
705
|
+
})));
|
|
706
|
+
const { runtime, sendApnsToAllUiSessions } = createRuntime({
|
|
707
|
+
buildTemplateVariables: vi.fn(async () => ({ session_name: '' })),
|
|
708
|
+
});
|
|
709
|
+
await runtime.maybeSendPushForTrigger({
|
|
710
|
+
type: 'session.execution.succeeded',
|
|
711
|
+
location: { directory: '/repo' },
|
|
712
|
+
data: { sessionID: 'ses_done' },
|
|
713
|
+
});
|
|
714
|
+
expect(sendApnsToAllUiSessions).toHaveBeenCalledWith(
|
|
715
|
+
expect.objectContaining({ type: 'ready', sessionName: '发布说明' }),
|
|
716
|
+
{ requireNoSse: true },
|
|
717
|
+
);
|
|
718
|
+
});
|
|
719
|
+
|
|
629
720
|
it('still sends goal settle push by default', async () => {
|
|
630
721
|
vi.stubGlobal('fetch', vi.fn(async () => jsonResponse({ id: 'ses_goal', title: 'Goal run' })));
|
|
631
722
|
const { runtime, sendPushToAllUiSessions } = createRuntime();
|
|
@@ -637,6 +728,7 @@ describe('notification event toggles', () => {
|
|
|
637
728
|
});
|
|
638
729
|
expect(sendPushToAllUiSessions).toHaveBeenCalledTimes(1);
|
|
639
730
|
expect(sendPushToAllUiSessions.mock.calls[0][0]).toMatchObject({ tag: 'goal-ses_goal' });
|
|
731
|
+
expect(sendPushToAllUiSessions.mock.calls[0][0].data).toMatchObject({ sessionName: 'Goal run' });
|
|
640
732
|
});
|
|
641
733
|
});
|
|
642
734
|
|
|
@@ -1,5 +1,75 @@
|
|
|
1
|
+
import { projectSessionMessageRecords } from '../session-turn-pages/session-message-projection.js';
|
|
1
2
|
import { summarizeText as summarizeSharedText } from '../text/summarization.js';
|
|
2
3
|
|
|
4
|
+
const trimmedText = (value) => (typeof value === 'string' && value.trim() ? value.trim() : '');
|
|
5
|
+
|
|
6
|
+
const isSessionRecord = (value) => {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
8
|
+
return typeof value.id === 'string'
|
|
9
|
+
|| typeof value.sessionID === 'string'
|
|
10
|
+
|| typeof value.title === 'string'
|
|
11
|
+
|| typeof value.parentID === 'string'
|
|
12
|
+
|| (value.metadata && typeof value.metadata === 'object');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* OpenCode 2 `GET /api/session/:id` returns `{ data: Session }`.
|
|
17
|
+
* Legacy responses and tests still return the session object itself.
|
|
18
|
+
* @param {unknown} payload
|
|
19
|
+
* @returns {object | null}
|
|
20
|
+
*/
|
|
21
|
+
export const readNotificationSessionRecord = (payload) => {
|
|
22
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return null;
|
|
23
|
+
if (isSessionRecord(payload.data)) return payload.data;
|
|
24
|
+
return isSessionRecord(payload) ? payload : null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const TITLE_EVENT_TYPES = new Set(['session.created', 'session.updated', 'session.renamed']);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Title carried by a session lifecycle event.
|
|
31
|
+
* v2 puts it on `data.title` (`session.created` / `session.renamed`).
|
|
32
|
+
* Legacy events put it on `properties.info.title`.
|
|
33
|
+
* @param {object | null | undefined} payload
|
|
34
|
+
* @returns {{ sessionId: string, title: string } | null}
|
|
35
|
+
*/
|
|
36
|
+
export const readSessionTitleFromEvent = (payload) => {
|
|
37
|
+
if (!payload || typeof payload !== 'object' || !TITLE_EVENT_TYPES.has(payload.type)) return null;
|
|
38
|
+
const bodies = [payload.properties, payload.data].filter((body) => body && typeof body === 'object' && !Array.isArray(body));
|
|
39
|
+
for (const body of bodies) {
|
|
40
|
+
const info = body.info && typeof body.info === 'object' ? body.info : null;
|
|
41
|
+
const title = trimmedText(info?.title) || trimmedText(body.title);
|
|
42
|
+
const sessionId = trimmedText(info?.id)
|
|
43
|
+
|| trimmedText(info?.sessionID)
|
|
44
|
+
|| trimmedText(body.sessionID)
|
|
45
|
+
|| trimmedText(body.id);
|
|
46
|
+
if (title && sessionId) return { sessionId, title };
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const readNotificationDirectory = (payload) => {
|
|
52
|
+
const properties = payload?.properties && typeof payload.properties === 'object' ? payload.properties : {};
|
|
53
|
+
const data = payload?.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : {};
|
|
54
|
+
const info = properties.info && typeof properties.info === 'object' ? properties.info : {};
|
|
55
|
+
const dataInfo = data.info && typeof data.info === 'object' ? data.info : {};
|
|
56
|
+
const candidates = [
|
|
57
|
+
properties.directory,
|
|
58
|
+
info.directory,
|
|
59
|
+
info.location?.directory,
|
|
60
|
+
payload?.location?.directory,
|
|
61
|
+
data.directory,
|
|
62
|
+
data.location?.directory,
|
|
63
|
+
dataInfo.directory,
|
|
64
|
+
dataInfo.location?.directory,
|
|
65
|
+
];
|
|
66
|
+
for (const value of candidates) {
|
|
67
|
+
const directory = trimmedText(value);
|
|
68
|
+
if (directory) return directory;
|
|
69
|
+
}
|
|
70
|
+
return '';
|
|
71
|
+
};
|
|
72
|
+
|
|
3
73
|
export const createNotificationTemplateRuntime = (deps) => {
|
|
4
74
|
const {
|
|
5
75
|
readSettingsFromDisk,
|
|
@@ -133,37 +203,45 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
133
203
|
return '';
|
|
134
204
|
};
|
|
135
205
|
|
|
136
|
-
const fetchLastAssistantMessageText = async (sessionId, messageId, maxLength = NOTIFICATION_BODY_MAX_CHARS) => {
|
|
206
|
+
const fetchLastAssistantMessageText = async (sessionId, messageId, maxLength = NOTIFICATION_BODY_MAX_CHARS, directory) => {
|
|
137
207
|
if (!sessionId) return '';
|
|
138
208
|
|
|
139
209
|
try {
|
|
140
|
-
const
|
|
141
|
-
const
|
|
210
|
+
const base = buildOpenCodeUrl(`/session/${encodeURIComponent(sessionId)}/message`, '');
|
|
211
|
+
const params = new URLSearchParams({ limit: '5', order: 'desc' });
|
|
212
|
+
const scopedDirectory = trimmedText(directory);
|
|
213
|
+
if (scopedDirectory) params.set('directory', scopedDirectory);
|
|
214
|
+
const response = await fetch(`${base}?${params.toString()}`, {
|
|
142
215
|
method: 'GET',
|
|
143
216
|
headers: {
|
|
144
217
|
Accept: 'application/json',
|
|
145
|
-
...getOpenCodeAuthHeaders(),
|
|
218
|
+
...(typeof getOpenCodeAuthHeaders === 'function' ? getOpenCodeAuthHeaders() : {}),
|
|
146
219
|
},
|
|
147
220
|
signal: AbortSignal.timeout(3000),
|
|
148
221
|
});
|
|
149
222
|
|
|
150
223
|
if (!response.ok) return '';
|
|
151
224
|
|
|
152
|
-
const
|
|
153
|
-
|
|
225
|
+
const payload = await response.json().catch(() => null);
|
|
226
|
+
// Legacy lists are a chronological array. OpenCode 2 returns `{ data }`
|
|
227
|
+
// and `order=desc` puts the newest turn first.
|
|
228
|
+
const newestFirst = !Array.isArray(payload);
|
|
229
|
+
const records = Array.isArray(payload)
|
|
230
|
+
? payload
|
|
231
|
+
: (Array.isArray(payload?.data) ? payload.data : null);
|
|
232
|
+
if (!records) return '';
|
|
233
|
+
const projected = projectSessionMessageRecords(records);
|
|
234
|
+
const messages = newestFirst ? projected : [...projected].reverse();
|
|
154
235
|
|
|
155
236
|
let target = null;
|
|
156
237
|
if (messageId) {
|
|
157
238
|
target = messages.find((message) => message?.info?.id === messageId && message?.info?.role === 'assistant');
|
|
158
239
|
}
|
|
159
240
|
if (!target) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
241
|
+
target = messages.find((message) => (
|
|
242
|
+
message?.info?.role === 'assistant'
|
|
243
|
+
&& extractTextFromParts(message.parts, maxLength).length > 0
|
|
244
|
+
));
|
|
167
245
|
}
|
|
168
246
|
|
|
169
247
|
if (!target || !Array.isArray(target.parts)) return '';
|
|
@@ -175,9 +253,9 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
175
253
|
};
|
|
176
254
|
|
|
177
255
|
const cacheSessionTitle = (sessionId, title) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
256
|
+
const id = trimmedText(sessionId);
|
|
257
|
+
const name = trimmedText(title);
|
|
258
|
+
if (id && name) sessionTitleCache.set(id, name);
|
|
181
259
|
};
|
|
182
260
|
|
|
183
261
|
const getCachedSessionTitle = (sessionId) => {
|
|
@@ -185,15 +263,12 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
185
263
|
};
|
|
186
264
|
|
|
187
265
|
const maybeCacheSessionInfoFromEvent = (payload) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const info = payload.properties?.info;
|
|
192
|
-
if (!info || typeof info !== 'object') return;
|
|
193
|
-
cacheSessionTitle(info.id, info.title);
|
|
266
|
+
const titled = readSessionTitleFromEvent(payload);
|
|
267
|
+
if (!titled) return;
|
|
268
|
+
cacheSessionTitle(titled.sessionId, titled.title);
|
|
194
269
|
};
|
|
195
270
|
|
|
196
|
-
const fetchSessionInfo = async (sessionId) => {
|
|
271
|
+
const fetchSessionInfo = async (sessionId, directory) => {
|
|
197
272
|
if (!sessionId) return null;
|
|
198
273
|
|
|
199
274
|
const cached = sessionInfoCache.get(sessionId);
|
|
@@ -202,20 +277,24 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
202
277
|
}
|
|
203
278
|
|
|
204
279
|
try {
|
|
205
|
-
const
|
|
280
|
+
const base = buildOpenCodeUrl(`/session/${encodeURIComponent(sessionId)}`, '');
|
|
281
|
+
const url = directory ? `${base}?directory=${encodeURIComponent(directory)}` : base;
|
|
206
282
|
const response = await fetch(url, {
|
|
207
283
|
method: 'GET',
|
|
208
|
-
headers: {
|
|
284
|
+
headers: {
|
|
285
|
+
Accept: 'application/json',
|
|
286
|
+
...(typeof getOpenCodeAuthHeaders === 'function' ? getOpenCodeAuthHeaders() : {}),
|
|
287
|
+
},
|
|
209
288
|
signal: AbortSignal.timeout(2000),
|
|
210
289
|
});
|
|
211
290
|
if (!response.ok) {
|
|
212
291
|
console.warn(`[Notification] fetchSessionInfo: ${response.status} for session ${sessionId}`);
|
|
213
292
|
return null;
|
|
214
293
|
}
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
217
|
-
sessionInfoCache.set(sessionId, { data, at: Date.now() });
|
|
218
|
-
return
|
|
294
|
+
const session = readNotificationSessionRecord(await response.json().catch(() => null));
|
|
295
|
+
if (session) {
|
|
296
|
+
sessionInfoCache.set(sessionId, { data: session, at: Date.now() });
|
|
297
|
+
return session;
|
|
219
298
|
}
|
|
220
299
|
return null;
|
|
221
300
|
} catch (error) {
|
|
@@ -227,7 +306,11 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
227
306
|
const buildTemplateVariables = async (payload, sessionId) => {
|
|
228
307
|
const info = payload?.properties?.info || {};
|
|
229
308
|
|
|
230
|
-
|
|
309
|
+
const fromEvent = readSessionTitleFromEvent(payload);
|
|
310
|
+
let sessionTitle = trimmedText(payload?.properties?.sessionTitle)
|
|
311
|
+
|| trimmedText(payload?.properties?.session?.title)
|
|
312
|
+
|| trimmedText(info.sessionTitle)
|
|
313
|
+
|| (fromEvent && (!sessionId || fromEvent.sessionId === sessionId) ? fromEvent.title : '');
|
|
231
314
|
|
|
232
315
|
if (!sessionTitle && sessionId) {
|
|
233
316
|
const cached = getCachedSessionTitle(sessionId);
|
|
@@ -238,25 +321,34 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
238
321
|
|
|
239
322
|
let sessionInfo = null;
|
|
240
323
|
if (!sessionTitle && sessionId) {
|
|
241
|
-
sessionInfo = await fetchSessionInfo(sessionId);
|
|
242
|
-
|
|
243
|
-
|
|
324
|
+
sessionInfo = await fetchSessionInfo(sessionId, readNotificationDirectory(payload));
|
|
325
|
+
const fetchedTitle = trimmedText(sessionInfo?.title);
|
|
326
|
+
if (fetchedTitle) {
|
|
327
|
+
sessionTitle = fetchedTitle;
|
|
244
328
|
cacheSessionTitle(sessionId, sessionTitle);
|
|
245
329
|
}
|
|
246
330
|
}
|
|
247
331
|
|
|
332
|
+
const eventAgent = trimmedText(info.agent) || trimmedText(info.mode);
|
|
333
|
+
const eventModel = trimmedText(info.modelID)
|
|
334
|
+
|| trimmedText(info.model?.modelID)
|
|
335
|
+
|| trimmedText(info.model?.id);
|
|
336
|
+
if ((!eventAgent || !eventModel) && sessionId && !sessionInfo) {
|
|
337
|
+
sessionInfo = await fetchSessionInfo(sessionId, readNotificationDirectory(payload));
|
|
338
|
+
}
|
|
339
|
+
|
|
248
340
|
const agentName = (() => {
|
|
249
|
-
const mode =
|
|
250
|
-
? info.agent.trim()
|
|
251
|
-
: (typeof info.mode === 'string' ? info.mode.trim() : '');
|
|
341
|
+
const mode = eventAgent || trimmedText(sessionInfo?.agent) || trimmedText(sessionInfo?.mode);
|
|
252
342
|
if (!mode) return 'Agent';
|
|
253
343
|
return mode.split(/[-_\s]+/).filter(Boolean)
|
|
254
344
|
.map((token) => token.charAt(0).toUpperCase() + token.slice(1)).join(' ');
|
|
255
345
|
})();
|
|
256
346
|
|
|
257
347
|
const modelName = (() => {
|
|
258
|
-
const raw =
|
|
259
|
-
|
|
348
|
+
const raw = eventModel
|
|
349
|
+
|| trimmedText(sessionInfo?.model?.modelID)
|
|
350
|
+
|| trimmedText(sessionInfo?.model?.id)
|
|
351
|
+
|| trimmedText(sessionInfo?.modelID);
|
|
260
352
|
if (!raw) return 'Assistant';
|
|
261
353
|
return raw.split(/[-_]+/).filter(Boolean)
|
|
262
354
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|