diff --git a/packages/app/src/components/toast-host.tsx b/packages/app/src/components/toast-host.tsx index 082a03787..e275f0bb0 100644 --- a/packages/app/src/components/toast-host.tsx +++ b/packages/app/src/components/toast-host.tsx @@ -6,7 +6,7 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { useTranslation } from "react-i18next"; import { useIsCompactFormFactor } from "@/constants/layout"; import { isWeb } from "@/constants/platform"; -import { AlertTriangle, CheckCircle2 } from "lucide-react-native"; +import { AlertTriangle, CheckCircle2, Info } from "lucide-react-native"; import { getOverlayRoot, OVERLAY_Z } from "@/lib/overlay-root"; import { HEADER_INNER_HEIGHT, @@ -14,7 +14,7 @@ import { HEADER_TOP_PADDING_MOBILE, } from "@/constants/layout"; -export type ToastVariant = "default" | "success" | "error"; +export type ToastVariant = "default" | "info" | "success" | "warning" | "error"; export interface ToastShowOptions { icon?: ReactNode; @@ -230,7 +230,9 @@ export function ToastViewport({ const toastAnimatedStyle = useMemo( () => [ styles.toast, + toastVariant === "info" ? styles.toastInfo : null, toastVariant === "success" ? styles.toastSuccess : null, + toastVariant === "warning" ? styles.toastWarning : null, toastVariant === "error" ? styles.toastError : null, { marginTop: topOffset, @@ -250,8 +252,12 @@ export function ToastViewport({ } let defaultIcon: ReactNode = null; - if (toast.variant === "success") { + if (toast.variant === "info") { + defaultIcon = ; + } else if (toast.variant === "success") { defaultIcon = ; + } else if (toast.variant === "warning") { + defaultIcon = ; } else if (toast.variant === "error") { defaultIcon = ; } @@ -313,6 +319,12 @@ const styles = StyleSheet.create((theme) => ({ toastSuccess: { borderColor: theme.colors.border, }, + toastInfo: { + borderColor: theme.colors.palette.blue[300], + }, + toastWarning: { + borderColor: theme.colors.palette.amber[500], + }, toastError: { borderColor: theme.colors.destructive, }, diff --git a/packages/app/src/utils/provider-notice-toast.ts b/packages/app/src/utils/provider-notice-toast.ts index 478f929f8..7c3ace8b5 100644 --- a/packages/app/src/utils/provider-notice-toast.ts +++ b/packages/app/src/utils/provider-notice-toast.ts @@ -13,7 +13,7 @@ export function showProviderNoticeToast( return; } toast.show(notice.message, { - variant: "default", - durationMs: notice.type === "warning" ? 3200 : undefined, + variant: notice.type, + durationMs: notice.type === "warning" ? 5000 : undefined, }); } diff --git a/packages/server/src/server/agent/provider-notices.ts b/packages/server/src/server/agent/provider-notices.ts index 921f70a50..d2a9c3af5 100644 --- a/packages/server/src/server/agent/provider-notices.ts +++ b/packages/server/src/server/agent/provider-notices.ts @@ -1,6 +1,11 @@ import type { AgentProviderNotice } from "./agent-sdk-types.js"; -export const SETTING_APPLIES_NEXT_TURN_NOTICE: AgentProviderNotice = { - type: "info", - message: "This change applies next turn.", +export const MODE_APPLIES_NEXT_TURN_NOTICE: AgentProviderNotice = { + type: "warning", + message: "Permission mode applies next turn", +}; + +export const THINKING_APPLIES_NEXT_TURN_NOTICE: AgentProviderNotice = { + type: "warning", + message: "Thinking level applies next turn", }; diff --git a/packages/server/src/server/agent/providers/claude/agent.test.ts b/packages/server/src/server/agent/providers/claude/agent.test.ts index 20f8f5235..bd535ac13 100644 --- a/packages/server/src/server/agent/providers/claude/agent.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.test.ts @@ -704,8 +704,8 @@ describe("ClaudeAgentSession features", () => { }); await expect(session.setThinkingOption?.("ultracode")).resolves.toEqual({ - type: "info", - message: "This change applies next turn.", + type: "warning", + message: "Thinking level applies next turn", }); await session.close(); diff --git a/packages/server/src/server/agent/providers/claude/agent.ts b/packages/server/src/server/agent/providers/claude/agent.ts index 99a6acfe2..a0403f2e6 100644 --- a/packages/server/src/server/agent/providers/claude/agent.ts +++ b/packages/server/src/server/agent/providers/claude/agent.ts @@ -50,7 +50,7 @@ import { claudeQuery, type ClaudeOptions, type ClaudeQueryFactory } from "./quer import { realClaudeRewindSdk, revertClaudeConversation, revertClaudeFiles } from "./rewind.js"; import { normalizeProviderReplayTimestamp } from "../../provider-history-timestamps.js"; import { claudeProjectDirSync } from "./project-dir.js"; -import { SETTING_APPLIES_NEXT_TURN_NOTICE } from "../../provider-notices.js"; +import { THINKING_APPLIES_NEXT_TURN_NOTICE } from "../../provider-notices.js"; import { isProviderImageMarkdown, materializeProviderImage, @@ -2209,7 +2209,7 @@ class ClaudeAgentSession implements AgentSession { } this.queryRestartNeeded = true; if (this.activeForegroundTurnId || this.autonomousTurn) { - return SETTING_APPLIES_NEXT_TURN_NOTICE; + return THINKING_APPLIES_NEXT_TURN_NOTICE; } } diff --git a/packages/server/src/server/agent/providers/codex-app-server-agent.test.ts b/packages/server/src/server/agent/providers/codex-app-server-agent.test.ts index 569370cae..7a585f0ce 100644 --- a/packages/server/src/server/agent/providers/codex-app-server-agent.test.ts +++ b/packages/server/src/server/agent/providers/codex-app-server-agent.test.ts @@ -347,12 +347,12 @@ describe("Codex app-server provider", () => { const session = createSession({ modeId: "auto", thinkingOptionId: "medium" }); await expect(session.setMode("full-access")).resolves.toEqual({ - type: "info", - message: "This change applies next turn.", + type: "warning", + message: "Permission mode applies next turn", }); await expect(session.setThinkingOption?.("high")).resolves.toEqual({ - type: "info", - message: "This change applies next turn.", + type: "warning", + message: "Thinking level applies next turn", }); session.activeForegroundTurnId = null; diff --git a/packages/server/src/server/agent/providers/codex-app-server-agent.ts b/packages/server/src/server/agent/providers/codex-app-server-agent.ts index fd6b7b68e..f816e33b0 100644 --- a/packages/server/src/server/agent/providers/codex-app-server-agent.ts +++ b/packages/server/src/server/agent/providers/codex-app-server-agent.ts @@ -92,7 +92,10 @@ import { resolveBinaryVersion, } from "./diagnostic-utils.js"; import { appendOrReplaceGrowingAssistantMessage, runProviderTurn } from "./provider-runner.js"; -import { SETTING_APPLIES_NEXT_TURN_NOTICE } from "../provider-notices.js"; +import { + MODE_APPLIES_NEXT_TURN_NOTICE, + THINKING_APPLIES_NEXT_TURN_NOTICE, +} from "../provider-notices.js"; import type { WorkspaceGitService } from "../../workspace-git-service.js"; function assertChildWithPipes( @@ -3943,7 +3946,7 @@ export class CodexAppServerAgentSession implements AgentSession { this.currentMode = modeId; this.cachedRuntimeInfo = null; if (this.activeForegroundTurnId) { - return SETTING_APPLIES_NEXT_TURN_NOTICE; + return MODE_APPLIES_NEXT_TURN_NOTICE; } } @@ -3961,7 +3964,7 @@ export class CodexAppServerAgentSession implements AgentSession { this.refreshResolvedCollaborationMode(); this.cachedRuntimeInfo = null; if (this.activeForegroundTurnId) { - return SETTING_APPLIES_NEXT_TURN_NOTICE; + return THINKING_APPLIES_NEXT_TURN_NOTICE; } } diff --git a/packages/server/src/server/agent/providers/omp/agent.test.ts b/packages/server/src/server/agent/providers/omp/agent.test.ts index be6666248..032315696 100644 --- a/packages/server/src/server/agent/providers/omp/agent.test.ts +++ b/packages/server/src/server/agent/providers/omp/agent.test.ts @@ -232,7 +232,10 @@ describe("OMP agent client and session", () => { expect.objectContaining({ name: "review", kind: "skill" }), ]), ); - await expect(omp.setMode("ask")).resolves.toEqual(expect.objectContaining({ type: "info" })); + await expect(omp.setMode("ask")).resolves.toEqual({ + type: "warning", + message: "Start a new OMP session to change approval mode", + }); }); test("rewinds natively, interrupts, and shuts down", async () => { diff --git a/packages/server/src/server/agent/providers/omp/agent.ts b/packages/server/src/server/agent/providers/omp/agent.ts index a16187096..1e002c9f4 100644 --- a/packages/server/src/server/agent/providers/omp/agent.ts +++ b/packages/server/src/server/agent/providers/omp/agent.ts @@ -1084,9 +1084,8 @@ export class OmpAgentSession implements AgentSession { throw new Error(`Invalid OMP mode '${modeId}'`); } return { - type: "info", - message: - "OMP approval mode is set when the agent launches. Start a new OMP session to use a different mode.", + type: "warning", + message: "Start a new OMP session to change approval mode", }; }