From d695803f1f9c34c73d999eb9ee304a6a5075f781 Mon Sep 17 00:00:00 2001 From: Jack Wotherspoon Date: Thu, 26 Mar 2026 22:13:53 -0400 Subject: [PATCH] chore: update channels --- packages/cli/src/gemini.test.tsx | 1 + packages/cli/src/gemini.tsx | 7 ------ packages/cli/src/ui/AppContainer.tsx | 24 ++++++------------- .../src/ui/components/HistoryItemDisplay.tsx | 1 + .../ui/components/messages/InfoMessage.tsx | 8 ++++++- .../src/ui/components/views/ChannelsList.tsx | 16 ++++++++----- packages/cli/src/ui/types.ts | 1 + packages/core/src/config/config.ts | 21 ++++++++++++++++ packages/core/src/utils/events.ts | 14 ++++++++++- 9 files changed, 61 insertions(+), 32 deletions(-) diff --git a/packages/cli/src/gemini.test.tsx b/packages/cli/src/gemini.test.tsx index 5271c0305e..be33809c39 100644 --- a/packages/cli/src/gemini.test.tsx +++ b/packages/cli/src/gemini.test.tsx @@ -575,6 +575,7 @@ describe('gemini.tsx main function kitty protocol', () => { rawOutput: undefined, acceptRawOutputRisk: undefined, isCommand: undefined, + channels: undefined, }); await act(async () => { diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index 62f5b7425f..4b43d7d81b 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -285,13 +285,6 @@ export async function main() { }); } - if (argv.channels && argv.channels.length > 0) { - coreEvents.emitFeedback( - 'info', - `Channels listening for messages: ${argv.channels.join(', ')}`, - ); - } - // Check for invalid input combinations early to prevent crashes if (argv.promptInteractive && !process.stdin.isTTY) { writeToStderr( diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index d49c02547d..3b1e297e0c 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -37,6 +37,7 @@ import { } from './types.js'; import { checkPermissions } from './hooks/atCommandProcessor.js'; import { MessageType, StreamingState } from './types.js'; +import { theme } from './semantic-colors.js'; import { ToolActionsProvider } from './contexts/ToolActionsContext.js'; import { type StartupWarning, @@ -66,7 +67,6 @@ import { flattenMemory, type MemoryChangedPayload, type ChannelMessagePayload, - activeChannels, writeToStdout, disableMouseEvents, enterAlternateScreen, @@ -1233,22 +1233,6 @@ Logging in with Google... Restarting Gemini CLI to continue. }; }, [channelsEnabled, addMessage]); - // Warn about requested channels whose MCP servers did not declare capability. - const channelWarningFired = useRef(false); - useEffect(() => { - if (!isMcpReady || !channelsEnabled || channelWarningFired.current) return; - channelWarningFired.current = true; - const requested = config.getChannels(); - for (const name of requested) { - if (!activeChannels.has(name)) { - coreEvents.emitFeedback( - 'warning', - `Channel "${name}" was requested but the MCP server did not declare channel capability.`, - ); - } - } - }, [isMcpReady, channelsEnabled, config]); - cancelHandlerRef.current = useCallback( (shouldRestorePrompt: boolean = true) => { if (isToolAwaitingConfirmation(pendingHistoryItems)) { @@ -2011,10 +1995,16 @@ Logging in with Google... Restarting Gemini CLI to continue. ); } + const isChannel = payload.style === 'channel'; historyManager.addItem( { type, text: payload.message, + ...(isChannel && { + icon: '» ', + color: theme.text.secondary, + marginTop: 0, + }), }, Date.now(), ); diff --git a/packages/cli/src/ui/components/HistoryItemDisplay.tsx b/packages/cli/src/ui/components/HistoryItemDisplay.tsx index 3ed711a377..76147e097f 100644 --- a/packages/cli/src/ui/components/HistoryItemDisplay.tsx +++ b/packages/cli/src/ui/components/HistoryItemDisplay.tsx @@ -119,6 +119,7 @@ export const HistoryItemDisplay: React.FC = ({ secondaryText={itemForDisplay.secondaryText} icon={itemForDisplay.icon} color={itemForDisplay.color} + marginTop={itemForDisplay.marginTop} marginBottom={itemForDisplay.marginBottom} /> )} diff --git a/packages/cli/src/ui/components/messages/InfoMessage.tsx b/packages/cli/src/ui/components/messages/InfoMessage.tsx index bea86e3834..17f28c9af2 100644 --- a/packages/cli/src/ui/components/messages/InfoMessage.tsx +++ b/packages/cli/src/ui/components/messages/InfoMessage.tsx @@ -14,6 +14,7 @@ interface InfoMessageProps { secondaryText?: string; icon?: string; color?: string; + marginTop?: number; marginBottom?: number; } @@ -22,6 +23,7 @@ export const InfoMessage: React.FC = ({ secondaryText, icon, color, + marginTop, marginBottom, }) => { color ??= theme.status.warning; @@ -29,7 +31,11 @@ export const InfoMessage: React.FC = ({ const prefixWidth = prefix.length; return ( - + {prefix} diff --git a/packages/cli/src/ui/components/views/ChannelsList.tsx b/packages/cli/src/ui/components/views/ChannelsList.tsx index ccd87fb866..ab651b9fca 100644 --- a/packages/cli/src/ui/components/views/ChannelsList.tsx +++ b/packages/cli/src/ui/components/views/ChannelsList.tsx @@ -7,6 +7,7 @@ import type React from 'react'; import { Box, Text } from 'ink'; import { theme } from '../../semantic-colors.js'; +import { RenderInline } from '../../utils/InlineMarkdownRenderer.js'; import type { ChannelInfo } from '../../types.js'; interface ChannelsListProps { @@ -18,10 +19,11 @@ export const ChannelsList: React.FC = ({ channels }) => { return ( No active channels. - - { - "MCP servers must declare `experimental['gemini/channel']` in their capabilities to act as channels." - } + + ); @@ -41,9 +43,11 @@ export const ChannelsList: React.FC = ({ channels }) => { {channel.displayName || channel.name} ({channel.name}) - + Direction:{' '} - + {channel.supportsReply ? 'two-way' : 'one-way'} diff --git a/packages/cli/src/ui/types.ts b/packages/cli/src/ui/types.ts index 47233b5e33..1710765723 100644 --- a/packages/cli/src/ui/types.ts +++ b/packages/cli/src/ui/types.ts @@ -173,6 +173,7 @@ export type HistoryItemInfo = HistoryItemBase & { secondaryText?: string; icon?: string; color?: string; + marginTop?: number; marginBottom?: number; }; diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index a7ada98368..6f59de68cb 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -58,6 +58,7 @@ import { type TelemetryTarget, } from '../telemetry/index.js'; import { coreEvents, CoreEvent } from '../utils/events.js'; +import { activeChannels } from '../channels/types.js'; import { tokenLimit } from '../core/tokenLimits.js'; import { DEFAULT_GEMINI_EMBEDDING_MODEL, @@ -1380,6 +1381,26 @@ export class Config implements McpContext, AgentLoopContext { debugLogger.error('Error initializing MCP clients:', result.reason); } } + // Report channel status after all MCP servers have initialized. + if (this.channels.length > 0) { + const active = this.channels.filter((name) => activeChannels.has(name)); + if (active.length > 0) { + coreEvents.emitFeedback( + 'info', + `Channels listening for messages: ${active.join(', ')}`, + undefined, + { style: 'channel' }, + ); + } + for (const name of this.channels) { + if (!activeChannels.has(name)) { + coreEvents.emitFeedback( + 'warning', + `Channel "${name}" was requested but the MCP server did not declare channel capability.`, + ); + } + } + } }); if (!this.interactive || this.acpMode) { diff --git a/packages/core/src/utils/events.ts b/packages/core/src/utils/events.ts index 2f7539ff02..e282be0e6f 100644 --- a/packages/core/src/utils/events.ts +++ b/packages/core/src/utils/events.ts @@ -41,6 +41,12 @@ export interface UserFeedbackPayload { * or verbose output, while keeping the 'message' field clean for end users. */ error?: unknown; + /** + * Optional semantic style hint for the UI. + * 'channel' renders with secondary text color and a » icon, + * suitable for channel status messages. + */ + style?: 'channel'; } /** @@ -285,8 +291,14 @@ export class CoreEventEmitter extends EventEmitter { severity: FeedbackSeverity, message: string, error?: unknown, + options?: { style?: 'channel' }, ): void { - const payload: UserFeedbackPayload = { severity, message, error }; + const payload: UserFeedbackPayload = { + severity, + message, + error, + ...options, + }; this._emitOrQueue(CoreEvent.UserFeedback, payload); }