diff --git a/core/index.d.ts b/core/index.d.ts index 90039f02537..67c55ba1d58 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -497,7 +497,7 @@ export interface ChatHistoryItem { toolCallStates?: ToolCallState[]; isGatheringContext?: boolean; reasoning?: Reasoning; - appliedRules?: RuleWithSource[]; + appliedRules?: Omit[]; conversationSummary?: string; } 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/components/mainInput/ContinueInputBox.tsx b/gui/src/components/mainInput/ContinueInputBox.tsx index c731b761b86..b42057fab55 100644 --- a/gui/src/components/mainInput/ContinueInputBox.tsx +++ b/gui/src/components/mainInput/ContinueInputBox.tsx @@ -21,7 +21,7 @@ interface ContinueInputBoxProps { ) => void; editorState?: JSONContent; contextItems?: ContextItemWithId[]; - appliedRules?: RuleWithSource[]; + 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 2dd29c13e0f..de981a2f9bc 100644 --- a/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx +++ b/gui/src/components/mainInput/belowMainInput/RulesPeek.tsx @@ -1,20 +1,23 @@ import { DocumentTextIcon, GlobeAltIcon } from "@heroicons/react/24/outline"; -import { RuleWithSource } from "core"; import { getLastNPathParts } from "core/util/uri"; -import { ComponentType, 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?: RuleWithSource[]; + appliedRules?: Omit[]; icon?: ComponentType>; } interface RulesPeekItemProps { - rule: RuleWithSource; + rule: Omit; } // Convert technical source to user-friendly text -const getSourceLabel = (rule: RuleWithSource): string => { +const getSourceLabel = (rule: Omit): string => { switch (rule.source) { case "default-chat": return "Default Chat"; @@ -45,29 +48,11 @@ 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); - } - }; - return (
openRules(rule)} >
{isGlobal ? ( @@ -88,19 +73,6 @@ export function RulesPeekItem({ rule }: RulesPeekItemProps) {
-
- {displayedRule} - {isRuleLong && ( - - {expanded ? "(collapse)" : "(expand)"} - - )} -
Source: {getSourceLabel(rule)}
diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 3ec2fe27b87..b5e9d0c4751 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: RuleWithSource[]; + appliedRules: Omit[]; }>, ) => { if (state.history[payload.index]) { diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index 218883cb919..81ea66e85e2 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -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