@@ -34,7 +34,7 @@ import {
3434import { Share2 , SquarePen } from 'lucide-react' ;
3535import { useObjectTranslation } from '@object-ui/i18n' ;
3636import { toast } from 'sonner' ;
37- import { useNavigate } from 'react-router-dom' ;
37+ import { useNavigate , useLocation } from 'react-router-dom' ;
3838import {
3939 sanitizeChatMessagesForCache ,
4040 useChatConversation ,
@@ -250,6 +250,52 @@ function buildEditorSuggestions(
250250 : [ `Add fields to ${ subject } ` , `Suggest validations for ${ subject } ` , 'Add a status picklist field' ] ;
251251}
252252
253+ /** Segments after `:appName` that are route prefixes, not object names. */
254+ const NON_OBJECT_ROUTE_SEGMENTS = new Set ( [
255+ 'view' , 'record' , 'page' , 'dashboard' , 'design' , 'report' , 'metadata' ,
256+ ] ) ;
257+
258+ /** Decode a URL segment, falling back to the raw value on malformed input. */
259+ function safeDecodeSegment ( value : string ) : string {
260+ try {
261+ return decodeURIComponent ( value ) ;
262+ } catch {
263+ return value ;
264+ }
265+ }
266+
267+ /**
268+ * Derive the object (and record) the user is currently viewing from the
269+ * console URL, so the agent can act on "this object" without the user
270+ * restating it. Mirrors the URL layout parsed by `useTrackRouteAsRecent`:
271+ *
272+ * /apps/:appName/:objectName
273+ * /apps/:appName/:objectName/:recordId
274+ * /apps/:appName/:objectName/new
275+ *
276+ * Tolerates an optional shell prefix (e.g. `/_console`) by locating the
277+ * `apps` segment dynamically. Returns an empty object when the path isn't an
278+ * object route (dashboard/page/report/metadata) or the segment doesn't match
279+ * a known object in the current app.
280+ */
281+ function resolveCurrentRouteObject (
282+ pathname : string ,
283+ objects : ConsoleObject [ ] ,
284+ ) : { objectName ?: string ; recordId ?: string } {
285+ const parts = pathname . split ( '/' ) . filter ( Boolean ) ;
286+ const appsIdx = parts . indexOf ( 'apps' ) ;
287+ // Need at least [apps, appName, objectName].
288+ if ( appsIdx === - 1 || parts . length < appsIdx + 3 ) return { } ;
289+
290+ const objectSeg = safeDecodeSegment ( parts [ appsIdx + 2 ] ) ;
291+ if ( NON_OBJECT_ROUTE_SEGMENTS . has ( objectSeg ) ) return { } ;
292+ if ( ! objects . some ( ( o ) => o . name === objectSeg ) ) return { } ;
293+
294+ const recordSeg = parts [ appsIdx + 3 ] ? safeDecodeSegment ( parts [ appsIdx + 3 ] ) : undefined ;
295+ const recordId = recordSeg && recordSeg !== 'new' ? recordSeg : undefined ;
296+ return { objectName : objectSeg , ...( recordId ? { recordId } : { } ) } ;
297+ }
298+
253299interface ChatbotInnerProps {
254300 appLabel : string ;
255301 appName ?: string ;
@@ -301,6 +347,15 @@ function ChatbotInner({
301347} : ChatbotInnerProps ) {
302348 const { language } = useObjectTranslation ( ) ;
303349 const navigate = useNavigate ( ) ;
350+ const location = useLocation ( ) ;
351+
352+ // The object/record the user is currently viewing in the runtime console,
353+ // derived from the route. Lets the agent answer "analyse this object" and
354+ // scope data queries to the open page without the user naming it.
355+ const currentRouteObject = React . useMemo (
356+ ( ) => resolveCurrentRouteObject ( location . pathname , objects ) ,
357+ [ location . pathname , objects ] ,
358+ ) ;
304359
305360 // What the user is currently editing in a designer (if any). Merged into
306361 // the agent context so "add a priority field" acts on the open object,
@@ -350,6 +405,11 @@ function ChatbotInner({
350405 // Publish posture, so the agent's narration matches reality (an
351406 // auto-published build is live, not "to publish").
352407 autoPublishAiBuilds : getRuntimeConfig ( ) . features . autoPublishAiBuilds ,
408+ // The object/record currently open in the runtime console — the
409+ // backend injects its schema and scopes data queries to it, so
410+ // "analyse this object" works without the user naming it.
411+ ...( currentRouteObject . objectName ? { objectName : currentRouteObject . objectName } : { } ) ,
412+ ...( currentRouteObject . recordId ? { recordId : currentRouteObject . recordId } : { } ) ,
353413 // The metadata item currently open in a designer, so the agent
354414 // can act on "this object/view/…" without the user restating it.
355415 ...( editor ? { editing : editor } : { } ) ,
0 commit comments