From b28102ec15415ec37eb5499759e4780069b61039 Mon Sep 17 00:00:00 2001 From: agrabauskas Date: Fri, 22 Aug 2025 19:48:38 +0100 Subject: [PATCH 1/5] Fix: Remove applied rules from chat history --- core/index.d.ts | 13 +++- .../components/mainInput/ContinueInputBox.tsx | 4 +- .../mainInput/belowMainInput/RulesPeek.tsx | 65 +++++++++---------- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/core/index.d.ts b/core/index.d.ts index 90039f02537..f8fd4e2eda6 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -497,10 +497,21 @@ export interface ChatHistoryItem { toolCallStates?: ToolCallState[]; isGatheringContext?: boolean; reasoning?: Reasoning; - appliedRules?: RuleWithSource[]; + appliedRules?: AppliedRule[]; conversationSummary?: string; } +export interface AppliedRule { + name?: string; + slug?: string; + source: RuleSource; + globs?: string | string[]; + regex?: string | string[]; + description?: string; + ruleFile?: string; + alwaysApply?: boolean; +} + export interface LLMFullCompletionOptions extends BaseCompletionOptions { log?: boolean; model?: string; diff --git a/gui/src/components/mainInput/ContinueInputBox.tsx b/gui/src/components/mainInput/ContinueInputBox.tsx index c731b761b86..dee89509dc8 100644 --- a/gui/src/components/mainInput/ContinueInputBox.tsx +++ b/gui/src/components/mainInput/ContinueInputBox.tsx @@ -1,5 +1,5 @@ import { Editor, JSONContent } from "@tiptap/react"; -import { ContextItemWithId, InputModifiers, RuleWithSource } from "core"; +import { AppliedRule, ContextItemWithId, InputModifiers } from "core"; import { useMemo } from "react"; import { defaultBorderRadius, vscBackground } from ".."; import { useAppSelector } from "../../redux/hooks"; @@ -21,7 +21,7 @@ interface ContinueInputBoxProps { ) => void; editorState?: JSONContent; contextItems?: ContextItemWithId[]; - appliedRules?: RuleWithSource[]; + appliedRules?: AppliedRule[]; hidden?: boolean; inputId: string; // used to keep track of things per input in redux } diff --git a/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx b/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx index 2dd29c13e0f..18bab366309 100644 --- a/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx +++ b/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx @@ -1,20 +1,22 @@ import { DocumentTextIcon, GlobeAltIcon } from "@heroicons/react/24/outline"; -import { RuleWithSource } from "core"; +import { AppliedRule } from "core"; import { getLastNPathParts } from "core/util/uri"; -import { ComponentType, useMemo, useState } from "react"; +import { ComponentType, useContext, useMemo, useState } from "react"; import ToggleDiv from "../../ToggleDiv"; +import { IdeMessengerContext } from "../../../context/IdeMessenger"; +import { DEFAULT_SYSTEM_MESSAGES_URL } from "core/llm/defaultSystemMessages"; interface RulesPeekProps { - appliedRules?: RuleWithSource[]; + appliedRules?: AppliedRule[]; icon?: ComponentType>; } interface RulesPeekItemProps { - rule: RuleWithSource; + rule: AppliedRule; } // Convert technical source to user-friendly text -const getSourceLabel = (rule: RuleWithSource): string => { +const getSourceLabel = (rule: AppliedRule): string => { switch (rule.source) { case "default-chat": return "Default Chat"; @@ -45,29 +47,37 @@ const getSourceLabel = (rule: RuleWithSource): string => { export function RulesPeekItem({ rule }: RulesPeekItemProps) { const isGlobal = rule.alwaysApply ?? !rule.globs; - const [expanded, setExpanded] = useState(false); - // Define maximum length for rule text display - const maxRuleLength = 100; - const isRuleLong = rule.rule.length > maxRuleLength; - - // Get the displayed rule text based on expanded state - const displayedRule = - isRuleLong && !expanded - ? `${rule.rule.slice(0, maxRuleLength)}...` - : rule.rule; - - const toggleExpand = () => { - if (isRuleLong) { - setExpanded(!expanded); + const ideMessenger = useContext(IdeMessengerContext); + const handleOpen = async () => { + if (rule.slug) { + void ideMessenger.request("controlPlane/openUrl", { + path: `${rule.slug}/new-version`, + orgSlug: undefined, + }); + } else if (rule.ruleFile) { + ideMessenger.post("openFile", { + path: rule.ruleFile, + }); + } else if ( + rule.source === "default-chat" || + rule.source === "default-plan" || + rule.source === "default-agent" + ) { + ideMessenger.post("openUrl", DEFAULT_SYSTEM_MESSAGES_URL); + } else { + ideMessenger.post("config/openProfile", { + profileId: undefined, + element: { sourceFile: (rule as any).sourceFile }, + }); } }; return (
{isGlobal ? ( @@ -88,19 +98,6 @@ export function RulesPeekItem({ rule }: RulesPeekItemProps) {
-
- {displayedRule} - {isRuleLong && ( - - {expanded ? "(collapse)" : "(expand)"} - - )} -
Source: {getSourceLabel(rule)}
From bed4c819f42a8eb700b41244eaf8cce91d3a4359 Mon Sep 17 00:00:00 2001 From: agrabauskas Date: Fri, 22 Aug 2025 19:54:04 +0100 Subject: [PATCH 2/5] nit --- gui/src/redux/slices/sessionSlice.ts | 4 ++-- gui/src/redux/thunks/streamNormalInput.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 3ec2fe27b87..fdf58c7ba47 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -7,6 +7,7 @@ import { } from "@reduxjs/toolkit"; import { JSONContent } from "@tiptap/react"; import { + AppliedRule, ApplyState, AssistantChatMessage, ChatHistoryItem, @@ -16,7 +17,6 @@ import { FileSymbolMap, MessageModes, PromptLog, - RuleWithSource, Session, SessionMetadata, ThinkingChatMessage, @@ -503,7 +503,7 @@ export const sessionSlice = createSlice({ payload, }: PayloadAction<{ index: number; - appliedRules: RuleWithSource[]; + appliedRules: AppliedRule[]; }>, ) => { if (state.history[payload.index]) { diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index 218883cb919..0379166c586 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -207,7 +207,7 @@ export const streamNormalInput = createAsyncThunk< dispatch( setAppliedRulesAtIndex({ index: appliedRuleIndex, - appliedRules: appliedRules, + appliedRules: { ...appliedRules }, }), ); From fb2c6834374bfdbbdf6f78b15c56aa2df308b04f Mon Sep 17 00:00:00 2001 From: agrabauskas Date: Thu, 28 Aug 2025 11:24:22 +0100 Subject: [PATCH 3/5] Omit type, share code --- core/index.d.ts | 13 +---- .../components/mainInput/ContinueInputBox.tsx | 4 +- .../mainInput/Lump/sections/RulesSection.tsx | 56 +++++++++---------- .../mainInput/belowMainInput/RulesPeek.tsx | 39 +++---------- gui/src/redux/slices/sessionSlice.ts | 2 +- 5 files changed, 39 insertions(+), 75 deletions(-) diff --git a/core/index.d.ts b/core/index.d.ts index f8fd4e2eda6..67c55ba1d58 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -497,21 +497,10 @@ export interface ChatHistoryItem { toolCallStates?: ToolCallState[]; isGatheringContext?: boolean; reasoning?: Reasoning; - appliedRules?: AppliedRule[]; + appliedRules?: Omit[]; conversationSummary?: string; } -export interface AppliedRule { - name?: string; - slug?: string; - source: RuleSource; - globs?: string | string[]; - regex?: string | string[]; - description?: string; - ruleFile?: string; - alwaysApply?: boolean; -} - export interface LLMFullCompletionOptions extends BaseCompletionOptions { log?: boolean; model?: string; diff --git a/gui/src/components/mainInput/ContinueInputBox.tsx b/gui/src/components/mainInput/ContinueInputBox.tsx index dee89509dc8..b42057fab55 100644 --- a/gui/src/components/mainInput/ContinueInputBox.tsx +++ b/gui/src/components/mainInput/ContinueInputBox.tsx @@ -1,5 +1,5 @@ import { Editor, JSONContent } from "@tiptap/react"; -import { AppliedRule, ContextItemWithId, InputModifiers } from "core"; +import { ContextItemWithId, InputModifiers, RuleWithSource } from "core"; import { useMemo } from "react"; import { defaultBorderRadius, vscBackground } from ".."; import { useAppSelector } from "../../redux/hooks"; @@ -21,7 +21,7 @@ interface ContinueInputBoxProps { ) => void; editorState?: JSONContent; contextItems?: ContextItemWithId[]; - appliedRules?: AppliedRule[]; + appliedRules?: Omit[]; hidden?: boolean; inputId: string; // used to keep track of things per input in redux } diff --git a/gui/src/components/mainInput/Lump/sections/RulesSection.tsx b/gui/src/components/mainInput/Lump/sections/RulesSection.tsx index 009fcb59bfb..32ffb022e4c 100644 --- a/gui/src/components/mainInput/Lump/sections/RulesSection.tsx +++ b/gui/src/components/mainInput/Lump/sections/RulesSection.tsx @@ -29,10 +29,33 @@ interface RuleCardProps { rule: RuleWithSource; } +export const openRules = async (rule: Omit) => { + const ideMessenger = useContext(IdeMessengerContext); + if (rule.slug) { + void ideMessenger.request("controlPlane/openUrl", { + path: `${rule.slug}/new-version`, + orgSlug: undefined, + }); + } else if (rule.ruleFile) { + ideMessenger.post("openFile", { + path: rule.ruleFile, + }); + } else if ( + rule.source === "default-chat" || + rule.source === "default-plan" || + rule.source === "default-agent" + ) { + ideMessenger.post("openUrl", DEFAULT_SYSTEM_MESSAGES_URL); + } else { + ideMessenger.post("config/openProfile", { + profileId: undefined, + element: { sourceFile: (rule as any).sourceFile }, + }); + } +}; + const RuleCard: React.FC = ({ rule }) => { const dispatch = useAppDispatch(); - const ideMessenger = useContext(IdeMessengerContext); - const mode = useAppSelector((store) => store.session.mode); const policy = useAppSelector((state) => rule.name ? state.ui.ruleSettings[rule.name] || DEFAULT_RULE_SETTING @@ -41,29 +64,6 @@ const RuleCard: React.FC = ({ rule }) => { const isDisabled = policy === "off"; - const handleOpen = async () => { - if (rule.slug) { - void ideMessenger.request("controlPlane/openUrl", { - path: `${rule.slug}/new-version`, - orgSlug: undefined, - }); - } else if (rule.ruleFile) { - ideMessenger.post("openFile", { - path: rule.ruleFile, - }); - } else if ( - rule.source === "default-chat" || - rule.source === "default-plan" || - rule.source === "default-agent" - ) { - ideMessenger.post("openUrl", DEFAULT_SYSTEM_MESSAGES_URL); - } else { - ideMessenger.post("config/openProfile", { - profileId: undefined, - element: { sourceFile: (rule as any).sourceFile }, - }); - } - }; const handleTogglePolicy = () => { if (rule.name) { @@ -139,12 +139,12 @@ const RuleCard: React.FC = ({ rule }) => { {" "} {rule.source === "default-chat" || - rule.source === "default-agent" ? ( - + rule.source === "default-agent" ? ( + openRules(rule)} text="View"> ) : ( - + openRules(rule)} text="Edit"> )} diff --git a/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx b/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx index 18bab366309..de981a2f9bc 100644 --- a/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx +++ b/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx @@ -1,22 +1,23 @@ import { DocumentTextIcon, GlobeAltIcon } from "@heroicons/react/24/outline"; -import { AppliedRule } from "core"; import { getLastNPathParts } from "core/util/uri"; -import { ComponentType, useContext, useMemo, useState } from "react"; +import { ComponentType, useContext, useMemo } from "react"; import ToggleDiv from "../../ToggleDiv"; import { IdeMessengerContext } from "../../../context/IdeMessenger"; import { DEFAULT_SYSTEM_MESSAGES_URL } from "core/llm/defaultSystemMessages"; +import { RuleWithSource } from "core"; +import { openRules } from "../Lump/sections/RulesSection"; interface RulesPeekProps { - appliedRules?: AppliedRule[]; + appliedRules?: Omit[]; icon?: ComponentType>; } interface RulesPeekItemProps { - rule: AppliedRule; + rule: Omit; } // Convert technical source to user-friendly text -const getSourceLabel = (rule: AppliedRule): string => { +const getSourceLabel = (rule: Omit): string => { switch (rule.source) { case "default-chat": return "Default Chat"; @@ -47,37 +48,11 @@ const getSourceLabel = (rule: AppliedRule): string => { export function RulesPeekItem({ rule }: RulesPeekItemProps) { const isGlobal = rule.alwaysApply ?? !rule.globs; - - const ideMessenger = useContext(IdeMessengerContext); - const handleOpen = async () => { - if (rule.slug) { - void ideMessenger.request("controlPlane/openUrl", { - path: `${rule.slug}/new-version`, - orgSlug: undefined, - }); - } else if (rule.ruleFile) { - ideMessenger.post("openFile", { - path: rule.ruleFile, - }); - } else if ( - rule.source === "default-chat" || - rule.source === "default-plan" || - rule.source === "default-agent" - ) { - ideMessenger.post("openUrl", DEFAULT_SYSTEM_MESSAGES_URL); - } else { - ideMessenger.post("config/openProfile", { - profileId: undefined, - element: { sourceFile: (rule as any).sourceFile }, - }); - } - }; - return (
openRules(rule)} >
{isGlobal ? ( diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index fdf58c7ba47..c0aaee5e39f 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -503,7 +503,7 @@ export const sessionSlice = createSlice({ payload, }: PayloadAction<{ index: number; - appliedRules: AppliedRule[]; + appliedRules: ChatHistoryItem["appliedRules"]; }>, ) => { if (state.history[payload.index]) { From 331c81fce55ff63b2ed22dd9f7321ffab0d419ff Mon Sep 17 00:00:00 2001 From: agrabauskas Date: Thu, 28 Aug 2025 11:57:49 +0100 Subject: [PATCH 4/5] actually remove the rule contents --- core/llm/rules/getSystemMessageWithRules.ts | 13 ++++++++----- gui/src/redux/slices/sessionSlice.ts | 3 ++- gui/src/redux/thunks/streamNormalInput.ts | 4 ++-- gui/src/redux/util/constructMessages.ts | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/llm/rules/getSystemMessageWithRules.ts b/core/llm/rules/getSystemMessageWithRules.ts index dc03ad43f1a..bf2b19d7019 100644 --- a/core/llm/rules/getSystemMessageWithRules.ts +++ b/core/llm/rules/getSystemMessageWithRules.ts @@ -324,7 +324,7 @@ export const getApplicableRules = ( return applicableRules; }; -export function getRuleId(rule: RuleWithSource): string { +export function getRuleId(rule: Omit): string { return rule.slug ?? rule.ruleFile ?? rule.name ?? rule.source; } @@ -342,9 +342,9 @@ export const getSystemMessageWithRules = ({ rulePolicies?: RulePolicies; }): { systemMessage: string; - appliedRules: RuleWithSource[]; + appliedRules: Omit[]; } => { - const appliedRules = getApplicableRules( + const rules = getApplicableRules( userMessage, availableRules, contextItems, @@ -352,7 +352,7 @@ export const getSystemMessageWithRules = ({ ); let systemMessage = baseSystemMessage ?? ""; - for (const rule of appliedRules) { + for (const rule of rules) { if (systemMessage) { systemMessage += "\n\n"; } @@ -361,6 +361,9 @@ export const getSystemMessageWithRules = ({ return { systemMessage, - appliedRules, + appliedRules: rules.map(fullRule => { + const { rule, ...rest } = fullRule // destruct rule key + return rest; + }), }; }; diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index c0aaee5e39f..34529d8df21 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -17,6 +17,7 @@ import { FileSymbolMap, MessageModes, PromptLog, + RuleWithSource, Session, SessionMetadata, ThinkingChatMessage, @@ -503,7 +504,7 @@ export const sessionSlice = createSlice({ payload, }: PayloadAction<{ index: number; - appliedRules: ChatHistoryItem["appliedRules"]; + appliedRules: Omit[]; }>, ) => { if (state.history[payload.index]) { diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index 0379166c586..81ea66e85e2 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -207,7 +207,7 @@ export const streamNormalInput = createAsyncThunk< dispatch( setAppliedRulesAtIndex({ index: appliedRuleIndex, - appliedRules: { ...appliedRules }, + appliedRules: appliedRules, }), ); @@ -282,7 +282,7 @@ export const streamNormalInput = createAsyncThunk< ...(appliedRules.length > 0 && { rules: appliedRules.map((rule) => ({ id: getRuleId(rule), - rule: rule.rule, + rule: "", // TODO: remove rule key from type. The contents should be present in the prompt slug: rule.slug, })), }), diff --git a/gui/src/redux/util/constructMessages.ts b/gui/src/redux/util/constructMessages.ts index f75eb8b4997..90a5877443e 100644 --- a/gui/src/redux/util/constructMessages.ts +++ b/gui/src/redux/util/constructMessages.ts @@ -34,7 +34,7 @@ export function constructMessages( useSystemToolsFramework?: SystemMessageToolsFramework, ): { messages: ChatMessage[]; - appliedRules: RuleWithSource[]; + appliedRules: Omit[]; appliedRuleIndex: number; } { // Find the most recent conversation summary and filter history accordingly From 936723a5e3135e2c8f155f55d9cb09f8f7394abf Mon Sep 17 00:00:00 2001 From: agrabauskas Date: Thu, 28 Aug 2025 12:11:48 +0100 Subject: [PATCH 5/5] nit --- gui/src/redux/slices/sessionSlice.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 34529d8df21..b5e9d0c4751 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -7,7 +7,6 @@ import { } from "@reduxjs/toolkit"; import { JSONContent } from "@tiptap/react"; import { - AppliedRule, ApplyState, AssistantChatMessage, ChatHistoryItem,