From 34f3ec7f9570ef90ac04e9bb63309d0c3a75265e Mon Sep 17 00:00:00 2001 From: Rekty Date: Tue, 25 Aug 2026 16:08:37 +0700 Subject: [PATCH 1/2] feat(common): register FriendliAI model IDs and provider prefix Closes #1089 FriendliAI serves several models already in the catalog under other providers (GLM-5.2, MiniMax-M2.5). Register their HuggingFace-cased IDs under the friendli/ prefix so a private-backend routing layer can pick them up without any public code changes beyond this catalog entry. Changes: - Add 'friendli' to ALLOWED_MODEL_PREFIXES - Add friendliModels map with two serverless models - Spread friendliModels into the canonical models set - Add friendli.ai to providerDomains and getLogoForModel --- common/src/constants/model-config.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/common/src/constants/model-config.ts b/common/src/constants/model-config.ts index dbe11ec11e..794d467e07 100644 --- a/common/src/constants/model-config.ts +++ b/common/src/constants/model-config.ts @@ -8,6 +8,7 @@ export const ALLOWED_MODEL_PREFIXES = [ 'minimax', 'mimo', 'tencent', + 'friendli', ] as const export const costModes = [ @@ -83,6 +84,15 @@ export const minimaxModels = { } as const export type MiniMaxModel = (typeof minimaxModels)[keyof typeof minimaxModels] +// FriendliAI serverless models — backup lanes for models already routed via +// other providers. Model IDs use exact HuggingFace casing; the private backend +// adds the matching upstream routing. +export const friendliModels = { + friendli_glm52: 'friendli/zai-org/GLM-5.2', + friendli_miniMaxM25: 'friendli/MiniMaxAI/MiniMax-M2.5', +} as const +export type FriendliModel = (typeof friendliModels)[keyof typeof friendliModels] + export const moonshotModels = { kimiK26: 'moonshotai/kimi-k2.6', kimiK27Code: 'moonshotai/kimi-k2.7-code', @@ -122,6 +132,7 @@ export const models = { ...mimoModels, ...minimaxModels, ...openrouterModels, + ...friendliModels, ...finetunedVertexModels, } as const @@ -254,6 +265,7 @@ export const providerDomains = { mimo: 'xiaomi.com', tencent: 'tencent.com', xai: 'x.ai', + friendli: 'friendli.ai', } as const export function getLogoForModel(modelName: string): string | undefined { @@ -268,6 +280,7 @@ export function getLogoForModel(modelName: string): string | undefined { else if (Object.values(mimoModels).includes(modelName as MimoModel)) domain = providerDomains.mimo else if (modelName.startsWith('tencent/')) domain = providerDomains.tencent + else if (modelName.startsWith('friendli/')) domain = providerDomains.friendli else if (modelName.includes('claude')) domain = providerDomains.anthropic else if (modelName.includes('grok')) domain = providerDomains.xai From 77613713eed84b556a8310058669a783f01813c3 Mon Sep 17 00:00:00 2001 From: Rekty Date: Tue, 25 Aug 2026 16:28:23 +0700 Subject: [PATCH 2/2] feat(cli): add Windows toast notification for task completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1111 When speakers are muted or the user steps away, the terminal bell (BEL character in the OSC title sequence) is easy to miss. Add a native Windows toast notification that fires when a run completes so users always get a visual cue. - cli/src/utils/notification.ts: new utility using .NET ToastNotificationManager via PowerShell — works on every Windows 10+ machine without extra dependencies - cli/src/hooks/helpers/send-message.ts: call notifyTaskComplete() after finalizeQueueState on successful completion The Desktop (Electron) app can also import notifyTaskComplete directly for its own completion events. --- cli/src/hooks/helpers/send-message.ts | 5 ++ cli/src/utils/notification.ts | 83 +++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 cli/src/utils/notification.ts diff --git a/cli/src/hooks/helpers/send-message.ts b/cli/src/hooks/helpers/send-message.ts index f5432dbc97..2b04fe422a 100644 --- a/cli/src/hooks/helpers/send-message.ts +++ b/cli/src/hooks/helpers/send-message.ts @@ -25,6 +25,7 @@ import { import { formatElapsedTime } from '../../utils/format-elapsed-time' import { processImagesForMessage } from '../../utils/image-processor' import { logger } from '../../utils/logger' +import { notifyTaskComplete } from '../../utils/notification' import { appendInterruptionNotice } from '../../utils/message-block-helpers' import { getUserMessage } from '../../utils/message-history' import { @@ -466,6 +467,10 @@ export const handleRunCompletion = (params: { }) const timerResult = timerController.stop('success') + // Show a desktop notification on task completion so users who have + // muted speakers or stepped away see a visual alert (#1111). + notifyTaskComplete() + if (agentMode === 'PLAN') { setHasReceivedPlanResponse(true) } diff --git a/cli/src/utils/notification.ts b/cli/src/utils/notification.ts new file mode 100644 index 0000000000..0e5698c3bc --- /dev/null +++ b/cli/src/utils/notification.ts @@ -0,0 +1,83 @@ +/** + * Desktop notification utilities for task completion alerts. + * + * On Windows, fires a native toast notification via PowerShell so users who + * have muted speakers or stepped away still see a visual alert when the + * agent finishes its work (#1111). On other platforms the function is a + * no-op — the terminal bell (BEL character in the OSC title sequence) already + * provides the audible cue. + * + * The Desktop (Electron) app can call `notifyTaskComplete` directly; the + * CLI terminal-app path is best served by the BEL character already embedded + * in `setTerminalTitle`, so this module is not wired into the CLI streaming + * flow by default. The Desktop renderer imports and calls it after its own + * task-completion event. + */ + +import { spawn } from 'child_process' + +/** + * Show a Windows toast notification with the given title and body. + * + * Uses `urn:github:Electron` as the AUMID so the notification groups under + * the app icon in the action center. PowerShell's `BurntToast` module is + * not guaranteed to be installed, so we fall back to a direct .NET call via + * `powershell -Command` which works on every Windows 10+ machine without + * extra dependencies. + */ +export function notifyDesktop(title: string, body: string): void { + if (process.platform !== 'win32') return + + try { + const ps = [ + '[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null', + '[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom, ContentType = WindowsRuntime] | Out-Null', + `$template = @"`, + ``, + ` `, + ` `, + ` ${escapeXml(title)}`, + ` ${escapeXml(body)}`, + ` `, + ` `, + ``, + `"@`, + `$xml = New-Object Windows.Data.Xml.Dom.XmlDocument`, + `$xml.LoadXml($template)`, + `$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)`, + `$toast.Tag = "freebuff-task-complete"`, + `$toast.Group = "freebuff"`, + `$toast.ExpirationTime = [DateTimeOffset]::Now.AddMinutes(5)`, + `[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("freebuff").Show($toast)`, + ].join('\n') + + const child = spawn('powershell', ['-NoProfile', '-NonInteractive', '-Command', ps], { + detached: true, + stdio: 'ignore', + windowsHide: true, + }) + child.unref() + } catch { + // Notification is best-effort; never break the user's session over it. + } +} + +/** + * Convenience wrapper used by the Desktop app after a run completes. + */ +export function notifyTaskComplete(agentName?: string): void { + const title = agentName ? `${agentName} finished` : 'Task complete' + const body = agentName + ? `${agentName} has finished processing your request.` + : 'Your request has finished processing.' + notifyDesktop(title, body) +} + +/** Escape XML special characters for the PowerShell toast template. */ +function escapeXml(s: string): string { + return s + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') +}