Merge pull request #57267 from NousResearch/bb/desktop-journey-memory-graph
feat(desktop): /journey opens the memory graph overlay instead of printing text
This commit is contained in:
commit
eb506e656a
7 changed files with 62 additions and 2 deletions
|
|
@ -191,6 +191,7 @@ export function DesktopController() {
|
|||
currentView,
|
||||
openAgents,
|
||||
openCommandCenterSection,
|
||||
openStarmap,
|
||||
profilesOpen,
|
||||
settingsOpen,
|
||||
starmapOpen,
|
||||
|
|
@ -739,6 +740,7 @@ export function DesktopController() {
|
|||
busyRef,
|
||||
createBackendSessionForSend,
|
||||
handleSkinCommand,
|
||||
openMemoryGraph: openStarmap,
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession: resumeSession,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ function Harness({
|
|||
busyRef,
|
||||
onReady,
|
||||
onSeedState,
|
||||
openMemoryGraph,
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession,
|
||||
|
|
@ -63,6 +64,7 @@ function Harness({
|
|||
busyRef?: MutableRefObject<boolean>
|
||||
onReady: (handle: HarnessHandle) => void
|
||||
onSeedState?: (state: Record<string, unknown>) => void
|
||||
openMemoryGraph?: () => void
|
||||
refreshSessions: () => Promise<void>
|
||||
requestGateway: <T>(method: string, params?: Record<string, unknown>) => Promise<T>
|
||||
resumeStoredSession?: (storedSessionId: string) => Promise<void> | void
|
||||
|
|
@ -91,6 +93,7 @@ function Harness({
|
|||
busyRef: localBusyRef,
|
||||
createBackendSessionForSend: async () => RUNTIME_SESSION_ID,
|
||||
handleSkinCommand: () => '',
|
||||
openMemoryGraph: openMemoryGraph ?? (() => undefined),
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession: resumeStoredSession ?? (() => undefined),
|
||||
|
|
@ -369,6 +372,29 @@ describe('usePromptActions desktop slash pickers', () => {
|
|||
expect(requestGateway).not.toHaveBeenCalledWith('slash.exec', expect.anything())
|
||||
})
|
||||
|
||||
it('opens the memory graph overlay for /journey and its aliases instead of hitting the backend', async () => {
|
||||
const openMemoryGraph = vi.fn()
|
||||
const requestGateway = vi.fn(async () => ({}) as never)
|
||||
|
||||
let handle: HarnessHandle | null = null
|
||||
render(
|
||||
<Harness
|
||||
onReady={h => (handle = h)}
|
||||
openMemoryGraph={openMemoryGraph}
|
||||
refreshSessions={async () => undefined}
|
||||
requestGateway={requestGateway}
|
||||
/>
|
||||
)
|
||||
|
||||
await handle!.submitText('/journey')
|
||||
await handle!.submitText('/memory-graph')
|
||||
await handle!.submitText('/learning')
|
||||
|
||||
expect(openMemoryGraph).toHaveBeenCalledTimes(3)
|
||||
expect(requestGateway).not.toHaveBeenCalledWith('slash.exec', expect.anything())
|
||||
expect(requestGateway).not.toHaveBeenCalledWith('command.dispatch', expect.anything())
|
||||
})
|
||||
|
||||
it('marks a timed-out handoff as failed so the next attempt can retry', async () => {
|
||||
vi.useFakeTimers()
|
||||
const calls: { method: string; params?: Record<string, unknown> }[] = []
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { AppendMessage, ThreadMessage } from '@assistant-ui/react'
|
|||
import { useStore } from '@nanostores/react'
|
||||
import { type MutableRefObject, useCallback, useEffect, useRef } from 'react'
|
||||
|
||||
import { transcribeAudio, PROMPT_SUBMIT_REQUEST_TIMEOUT_MS } from '@/hermes'
|
||||
import { PROMPT_SUBMIT_REQUEST_TIMEOUT_MS, transcribeAudio } from '@/hermes'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { stripAnsi } from '@/lib/ansi'
|
||||
import { branchGroupForUser, type ChatMessage, chatMessageText, textPart } from '@/lib/chat-messages'
|
||||
|
|
@ -158,6 +158,7 @@ interface PromptActionsOptions {
|
|||
branchCurrentSession: () => Promise<boolean>
|
||||
createBackendSessionForSend: (preview?: string | null) => Promise<string | null>
|
||||
handleSkinCommand: (arg: string) => string
|
||||
openMemoryGraph: () => void
|
||||
refreshSessions: () => Promise<void>
|
||||
requestGateway: <T>(method: string, params?: Record<string, unknown>, timeoutMs?: number) => Promise<T>
|
||||
resumeStoredSession: (storedSessionId: string) => Promise<void> | void
|
||||
|
|
@ -185,6 +186,7 @@ export function usePromptActions({
|
|||
branchCurrentSession,
|
||||
createBackendSessionForSend,
|
||||
handleSkinCommand,
|
||||
openMemoryGraph,
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession,
|
||||
|
|
@ -447,6 +449,7 @@ export function usePromptActions({
|
|||
createBackendSessionForSend,
|
||||
handleSkinCommand,
|
||||
handoffSession,
|
||||
openMemoryGraph,
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ interface SlashCommandDeps {
|
|||
platform: string,
|
||||
options?: { onProgress?: (state: string) => void; sessionId?: string }
|
||||
) => Promise<{ ok: boolean; error?: string }>
|
||||
openMemoryGraph: () => void
|
||||
refreshSessions: () => Promise<void>
|
||||
requestGateway: GatewayRequest
|
||||
resumeStoredSession: (storedSessionId: string) => Promise<void> | void
|
||||
|
|
@ -75,6 +76,7 @@ export function useSlashCommand(deps: SlashCommandDeps) {
|
|||
createBackendSessionForSend,
|
||||
handleSkinCommand,
|
||||
handoffSession,
|
||||
openMemoryGraph,
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession,
|
||||
|
|
@ -388,6 +390,13 @@ export function useSlashCommand(deps: SlashCommandDeps) {
|
|||
renderSlashOutput(`error: ${err instanceof Error ? err.message : String(err)}`)
|
||||
}
|
||||
},
|
||||
// /journey (aliases /learning, /memory-graph) opens the memory graph
|
||||
// overlay — the desktop's visual counterpart of the TUI journey
|
||||
// timeline — instead of printing a text rendering into the transcript.
|
||||
// Args are ignored, matching the TUI overlay behavior.
|
||||
journey: async () => {
|
||||
openMemoryGraph()
|
||||
},
|
||||
// /hatch opens the pet generator overlay (the desktop's rich, multi-step
|
||||
// generate→pick→hatch→adopt flow). A typed description seeds the prompt
|
||||
// so `/hatch a cyber fox` lands on the composer step prefilled.
|
||||
|
|
@ -612,6 +621,7 @@ export function useSlashCommand(deps: SlashCommandDeps) {
|
|||
createBackendSessionForSend,
|
||||
handleSkinCommand,
|
||||
handoffSession,
|
||||
openMemoryGraph,
|
||||
refreshSessions,
|
||||
requestGateway,
|
||||
resumeStoredSession,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { type MutableRefObject, useCallback } from 'react'
|
||||
|
||||
import type { Translations } from '@/i18n'
|
||||
import { PROMPT_SUBMIT_REQUEST_TIMEOUT_MS } from '@/hermes'
|
||||
import type { Translations } from '@/i18n'
|
||||
import { type ChatMessage, textPart } from '@/lib/chat-messages'
|
||||
import { optimisticAttachmentRef } from '@/lib/chat-runtime'
|
||||
import { setMutableRef } from '@/lib/mutable-ref'
|
||||
|
|
|
|||
|
|
@ -73,6 +73,18 @@ describe('desktop slash command curation', () => {
|
|||
expect(resolveDesktopCommand('/browser')?.args).toBe(true)
|
||||
})
|
||||
|
||||
it('routes /journey (and aliases) to the memory graph overlay action', () => {
|
||||
expect(resolveDesktopCommand('/journey')?.surface).toEqual({ kind: 'action', action: 'journey' })
|
||||
expect(resolveDesktopCommand('/memory-graph')?.surface).toEqual({ kind: 'action', action: 'journey' })
|
||||
expect(resolveDesktopCommand('/learning')?.surface).toEqual({ kind: 'action', action: 'journey' })
|
||||
expect(isDesktopSlashCommand('/journey')).toBe(true)
|
||||
expect(isDesktopSlashCommand('/memory-graph')).toBe(true)
|
||||
expect(isDesktopSlashSuggestion('/journey')).toBe(true)
|
||||
// Aliases execute but stay out of the popover.
|
||||
expect(isDesktopSlashSuggestion('/memory-graph')).toBe(false)
|
||||
expect(desktopSlashUnavailableMessage('/journey')).toBeNull()
|
||||
})
|
||||
|
||||
it('allows aliases to execute without cluttering the popover', () => {
|
||||
expect(isDesktopSlashSuggestion('/reset')).toBe(false)
|
||||
expect(isDesktopSlashCommand('/reset')).toBe(true)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export type DesktopActionId =
|
|||
| 'handoff'
|
||||
| 'hatch'
|
||||
| 'help'
|
||||
| 'journey'
|
||||
| 'new'
|
||||
| 'pet'
|
||||
| 'profile'
|
||||
|
|
@ -122,6 +123,12 @@ const DESKTOP_COMMAND_SPECS: readonly DesktopCommandSpec[] = [
|
|||
surface: action('browser'),
|
||||
args: true
|
||||
},
|
||||
{
|
||||
name: '/journey',
|
||||
description: 'Open the memory graph — skills + memories over time',
|
||||
aliases: ['/learning', '/memory-graph'],
|
||||
surface: action('journey')
|
||||
},
|
||||
|
||||
// Overlay pickers
|
||||
{ name: '/model', description: 'Switch the model for this session', surface: picker('model'), hidden: true },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue