From 27649f15acf95cc0458db554c278f280f3b0a2e9 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Jul 2026 19:27:07 -0700 Subject: [PATCH 1/2] feat(ag-ui): context-aware welcome suggestions (App mode leads with itinerary prompts) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tag each welcome prompt as capability vs itinerary and pick the featured prompt + "More prompts" order from the current context via suggestionsForAppMode(appModeOn): - App mode ON (map cockpit on screen): feature "Read my itinerary"; the remaining itinerary prompts lead the dropdown, capability prompts trail. - App mode OFF (plain chat): feature "Search docs and cite sources"; capability prompts lead, itinerary prompts trail. Each mode passes [appModeOn]="shell.appMode() === 'on'" so the surface reorders live when the toggle flips. Copy unchanged — only selection and order — so no e2e assertions move. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/app/modes/embed-mode.component.ts | 2 +- .../src/app/modes/popup-mode.component.ts | 2 +- .../modes/welcome-suggestions.component.ts | 29 ++++---- .../src/app/modes/welcome-suggestions.ts | 66 +++++++++++++++---- 4 files changed, 74 insertions(+), 25 deletions(-) diff --git a/examples/ag-ui/angular/src/app/modes/embed-mode.component.ts b/examples/ag-ui/angular/src/app/modes/embed-mode.component.ts index cf14fa70c..52e484807 100644 --- a/examples/ag-ui/angular/src/app/modes/embed-mode.component.ts +++ b/examples/ag-ui/angular/src/app/modes/embed-mode.component.ts @@ -18,7 +18,7 @@ import { WelcomeSuggestionsComponent } from './welcome-suggestions.component'; [selectedModel]="shell.model()" (selectedModelChange)="shell.onModelChange($event)" > - + `, styles: [` diff --git a/examples/ag-ui/angular/src/app/modes/popup-mode.component.ts b/examples/ag-ui/angular/src/app/modes/popup-mode.component.ts index 956ecf57b..8e1b2d938 100644 --- a/examples/ag-ui/angular/src/app/modes/popup-mode.component.ts +++ b/examples/ag-ui/angular/src/app/modes/popup-mode.component.ts @@ -32,7 +32,7 @@ import { AppModePromoComponent } from './app-mode-promo.component'; [showModelPicker]="false" (selectedModelChange)="shell.onModelChange($event)" > - + `, styles: [` diff --git a/examples/ag-ui/angular/src/app/modes/welcome-suggestions.component.ts b/examples/ag-ui/angular/src/app/modes/welcome-suggestions.component.ts index b6c867e36..089006ae3 100644 --- a/examples/ag-ui/angular/src/app/modes/welcome-suggestions.component.ts +++ b/examples/ag-ui/angular/src/app/modes/welcome-suggestions.component.ts @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -import { ChangeDetectionStrategy, Component, output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core'; import { ChatWelcomeSuggestionComponent, ChatSelectComponent, type ChatSelectOption, } from '@threadplane/chat'; -import { FEATURED_SUGGESTIONS, MORE_SUGGESTIONS } from './welcome-suggestions'; +import { suggestionsForAppMode } from './welcome-suggestions'; /** * Demo-side composition that renders the welcome-state suggestion surface @@ -26,13 +26,13 @@ import { FEATURED_SUGGESTIONS, MORE_SUGGESTIONS } from './welcome-suggestions';
(); - protected readonly featuredOne = FEATURED_SUGGESTIONS[0]; - protected readonly moreOptions: readonly ChatSelectOption[] = [ - ...FEATURED_SUGGESTIONS.slice(1), - ...MORE_SUGGESTIONS, - ].map((s) => ({ value: s.value, label: s.label, description: s.description })); + /** + * When true, lead with the itinerary prompts (App mode / map cockpit on + * screen). When false, lead with the broad capability prompts (plain chat). + * Passed by each mode from `shell.appMode()`. + */ + readonly appModeOn = input(false); + + private readonly suggestions = computed(() => suggestionsForAppMode(this.appModeOn())); + protected readonly featuredOne = computed(() => this.suggestions().featured); + protected readonly moreOptions = computed(() => + this.suggestions().more.map((s) => ({ value: s.value, label: s.label, description: s.description })), + ); } diff --git a/examples/ag-ui/angular/src/app/modes/welcome-suggestions.ts b/examples/ag-ui/angular/src/app/modes/welcome-suggestions.ts index 460b87999..0375ebddb 100644 --- a/examples/ag-ui/angular/src/app/modes/welcome-suggestions.ts +++ b/examples/ag-ui/angular/src/app/modes/welcome-suggestions.ts @@ -9,60 +9,102 @@ * frontend-owned itinerary client tools, and the research subagent * (list introduced in #655; presented here via the canonical two-tier * featured + "More prompts" UI). + * + * Each prompt is tagged `kind` so the surface can lead with the prompts + * that match the current context: in App mode (the map cockpit is on + * screen) the itinerary prompts are featured + ordered first; in plain + * chat the broad capability prompts lead. See `suggestionsForAppMode`. */ export interface WelcomeSuggestion { readonly label: string; readonly value: string; readonly description: string; + /** `itinerary` prompts drive the map cockpit; `capability` prompts show + * the transport's general features. Used to order by App mode. */ + readonly kind: 'capability' | 'itinerary'; } -export const FEATURED_SUGGESTIONS: readonly WelcomeSuggestion[] = [ +/** Broad transport-capability prompts (relevant in any mode). */ +const CAPABILITY_SUGGESTIONS: readonly WelcomeSuggestion[] = [ { + kind: 'capability', label: 'Search docs and cite sources', value: 'What do the docs say about streaming?', description: 'Calls a search tool and weaves inline citations into the reply.', }, { + kind: 'capability', label: 'Generative UI: revenue dashboard', value: 'Build me a revenue dashboard', description: 'Streams a composed UI surface the model builds on the fly.', }, { + kind: 'capability', label: 'Approve before a refund', value: 'Issue me a $50 refund', description: 'Pauses mid-run and asks for your explicit approval before proceeding.', }, + { + kind: 'capability', + label: 'Delegate to a research subagent', + value: 'Research AG-UI and give me the highlights', + description: 'Parent spawns a subagent and streams its summary back to you.', + }, ]; -export const MORE_SUGGESTIONS: readonly WelcomeSuggestion[] = [ +/** Prompts that drive the itinerary panel / map cockpit (App mode). */ +const ITINERARY_SUGGESTIONS: readonly WelcomeSuggestion[] = [ { + kind: 'itinerary', label: 'Read my itinerary', value: "What's on my itinerary?", description: 'Agent calls a browser-side tool to read your trip data.', }, { + kind: 'itinerary', label: 'Agent adds a stop to day 2', value: 'Add the Louvre to day 2 of my trip', description: 'Agent mutates the itinerary panel directly via a client tool.', }, { + kind: 'itinerary', label: 'Clear a day (asks first)', value: 'Clear my day 2 plans', description: 'Client tool requires your confirmation before clearing the day.', }, - { - label: 'Delegate to a research subagent', - value: 'Research AG-UI and give me the highlights', - description: 'Parent spawns a subagent and streams its summary back to you.', - }, ]; /** - * Back-compat: unified array combining featured + more in the original - * order. Kept so existing imports don't break. Prefer FEATURED_SUGGESTIONS - * + MORE_SUGGESTIONS for the two-tier UI. + * Pick the featured prompt + "More prompts" order for the current context. + * + * - App mode ON (map cockpit visible): feature "What's on my itinerary?" and + * list the itinerary prompts first — they match what the user is looking at. + * - App mode OFF (plain chat): feature "Search docs…" and lead with the broad + * capability prompts; itinerary prompts trail (they still work, just less + * prominent without the map). */ +export function suggestionsForAppMode(appModeOn: boolean): { + readonly featured: WelcomeSuggestion; + readonly more: readonly WelcomeSuggestion[]; +} { + if (appModeOn) { + const [featured, ...restItinerary] = ITINERARY_SUGGESTIONS; + return { featured, more: [...restItinerary, ...CAPABILITY_SUGGESTIONS] }; + } + const [featured, ...restCapability] = CAPABILITY_SUGGESTIONS; + return { featured, more: [...restCapability, ...ITINERARY_SUGGESTIONS] }; +} + +/** + * Back-compat: the original two-tier split + unified array, in the plain-chat + * order. Prefer `suggestionsForAppMode` for context-aware ordering. + */ +export const FEATURED_SUGGESTIONS: readonly WelcomeSuggestion[] = CAPABILITY_SUGGESTIONS.slice(0, 3); +export const MORE_SUGGESTIONS: readonly WelcomeSuggestion[] = [ + ...ITINERARY_SUGGESTIONS, + ...CAPABILITY_SUGGESTIONS.slice(3), +]; export const WELCOME_SUGGESTIONS: readonly WelcomeSuggestion[] = [ - ...FEATURED_SUGGESTIONS, - ...MORE_SUGGESTIONS, + ...CAPABILITY_SUGGESTIONS, + ...ITINERARY_SUGGESTIONS, ]; From 72280b20ff6c51cba31c595fdf832da5d9bbff8c Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Jul 2026 21:59:14 -0700 Subject: [PATCH 2/2] feat(ag-ui): wire appModeOn into sidebar-mode welcome suggestions 79dbc211 added the [appModeOn] binding to embed/popup only; main's sidebar also renders , so wire it there too (context-aware ordering must work in sidebar mode, where App mode actually lives). --- examples/ag-ui/angular/src/app/modes/sidebar-mode.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ag-ui/angular/src/app/modes/sidebar-mode.component.ts b/examples/ag-ui/angular/src/app/modes/sidebar-mode.component.ts index 8c01d1390..a093cf759 100644 --- a/examples/ag-ui/angular/src/app/modes/sidebar-mode.component.ts +++ b/examples/ag-ui/angular/src/app/modes/sidebar-mode.component.ts @@ -32,7 +32,7 @@ import { WelcomeSuggestionsComponent } from './welcome-suggestions.component'; /> }
- + `, styles: [`