diff --git a/apps/desktop/src/app/chat/composer/rich-editor.ts b/apps/desktop/src/app/chat/composer/rich-editor.ts
index 71491b87496d..21f0286c9f82 100644
--- a/apps/desktop/src/app/chat/composer/rich-editor.ts
+++ b/apps/desktop/src/app/chat/composer/rich-editor.ts
@@ -15,6 +15,7 @@ import {
type SlashChipKind,
slashIconElement
} from '@/components/assistant-ui/directive-text'
+import { sessionRefFallbackLabel } from '@/lib/session-refs'
export const RICH_INPUT_SLOT = 'composer-rich-input'
@@ -59,7 +60,9 @@ export function refChipHtml(kind: string, rawValue: string, displayLabel?: strin
const id = unquoteRef(rawValue)
const text = `@${kind}:${quoteRefValue(id)}`
- return `${directiveIconSvg(kind)}${escapeHtml(displayLabel || refLabel(id))}`
+ const label = displayLabel || (kind === 'session' ? sessionRefFallbackLabel(id) : refLabel(id))
+
+ return `${directiveIconSvg(kind)}${escapeHtml(label)}`
}
export function refChipElement(kind: string, rawValue: string, displayLabel?: string) {
@@ -74,7 +77,7 @@ export function refChipElement(kind: string, rawValue: string, displayLabel?: st
chip.dataset.refKind = kind
chip.className = DIRECTIVE_CHIP_CLASS
label.className = 'truncate'
- label.textContent = displayLabel || refLabel(id)
+ label.textContent = displayLabel || (kind === 'session' ? sessionRefFallbackLabel(id) : refLabel(id))
chip.append(directiveIconElement(kind), label)
return chip
diff --git a/apps/desktop/src/components/assistant-ui/directive-text.test.ts b/apps/desktop/src/components/assistant-ui/directive-text.test.ts
index 60c89f18b1e4..36ab54d743e7 100644
--- a/apps/desktop/src/components/assistant-ui/directive-text.test.ts
+++ b/apps/desktop/src/components/assistant-ui/directive-text.test.ts
@@ -36,4 +36,14 @@ describe('hermesDirectiveFormatter.parse', () => {
{ kind: 'text', text: ' the entry point' }
])
})
+
+ it('parses session links with profile/id values', () => {
+ const segments = hermesDirectiveFormatter.parse('see @session:work/20260101_abc123 next')
+
+ expect(segments).toEqual([
+ { kind: 'text', text: 'see ' },
+ { kind: 'mention', type: 'session', label: '20260101…', id: 'work/20260101_abc123' },
+ { kind: 'text', text: ' next' }
+ ])
+ })
})
diff --git a/apps/desktop/src/components/assistant-ui/directive-text.tsx b/apps/desktop/src/components/assistant-ui/directive-text.tsx
index ffa63a7b8877..6ea2580f16b8 100644
--- a/apps/desktop/src/components/assistant-ui/directive-text.tsx
+++ b/apps/desktop/src/components/assistant-ui/directive-text.tsx
@@ -7,7 +7,11 @@ import { Fragment, useEffect, useMemo, useState } from 'react'
import { ZoomableImage } from '@/components/chat/zoomable-image'
import { extractEmbeddedImages } from '@/lib/embedded-images'
+import { triggerHaptic } from '@/lib/haptics'
import { gatewayMediaDataUrl, isRemoteGateway } from '@/lib/media'
+import { useSessionLinkTitle } from '@/lib/session-link-title'
+import { parseSessionRefValue, sessionRefFallbackLabel } from '@/lib/session-refs'
+import { cn } from '@/lib/utils'
const HERMES_REF_TYPES = ['file', 'folder', 'url', 'image', 'tool', 'line', 'terminal', 'session'] as const
type HermesRefType = (typeof HERMES_REF_TYPES)[number]
@@ -40,11 +44,7 @@ const ICON_PATHS: Record = {
tool: ['M7 10h3v-3l-3.5 -3.5a6 6 0 0 1 8 8l6 6a2 2 0 0 1 -3 3l-6 -6a6 6 0 0 1 -8 -8l3.5 3.5'],
line: ['M5 9l14 0', 'M5 15l14 0', 'M11 4l-4 16', 'M17 4l-4 16'],
terminal: ['M5 7l5 5l-5 5', 'M12 19l7 0'],
- session: [
- 'M8 9h8',
- 'M8 13h6',
- 'M18 4a3 3 0 0 1 3 3v8a3 3 0 0 1 -3 3h-5l-5 3v-3h-2a3 3 0 0 1 -3 -3v-8a3 3 0 0 1 3 -3z'
- ]
+ session: ['M4 4h16v2.172a2 2 0 0 1 -.586 1.414l-4.414 4.414v7l-6 2v-8.5l-4.48 -4.928a2 2 0 0 1 -.52 -1.345v-2.227']
}
const ICON_FALLBACK = ['M8 12a4 4 0 1 0 8 0a4 4 0 1 0 -8 0', 'M16 12v1.5a2.5 2.5 0 0 0 5 0v-1.5a9 9 0 1 0 -5.5 8.28']
@@ -123,9 +123,12 @@ export function slashIconElement(kind: SlashChipKind) {
return iconElementFromPaths(SLASH_ICON_PATHS[kind])
}
-const DirectiveIcon: FC<{ type: string }> = ({ type }) => (
+const DirectiveIcon: FC<{ type: string; className?: string }> = ({
+ type,
+ className = 'size-3 shrink-0 opacity-80'
+}) => (