From 0d9c473a06a9055ec38ecc6af5b1d3534a4909c1 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Wed, 11 Mar 2026 03:03:53 +0100 Subject: [PATCH] feat(session-anchors): configurable budget scale + research-backed truncation fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove 500-char assistant text truncation in anchor serializer — research consensus (JetBrains NeurIPS 2025, SWE-agent, OpenDev ACC) is that agent reasoning must never be truncated; only tool outputs get observation-masked. Add AnchorBudgetScale type with 4 presets (Small=2K, Medium=6K, Large=12K, Full=20K) and per-project range slider in SettingsTab. Remove Ollama-specific warning toast — budget slider handles context limits generically. --- v2/src/lib/agent-dispatcher.ts | 4 +- v2/src/lib/components/Agent/AgentPane.svelte | 8 +-- .../components/Workspace/ContextTab.svelte | 9 +-- .../components/Workspace/ProjectBox.svelte | 2 +- .../components/Workspace/SettingsTab.svelte | 56 +++++++++++++++++++ v2/src/lib/stores/anchors.svelte.ts | 14 +++-- v2/src/lib/types/anchors.ts | 22 ++++++++ v2/src/lib/types/groups.ts | 3 + v2/src/lib/utils/anchor-serializer.ts | 9 ++- 9 files changed, 104 insertions(+), 23 deletions(-) diff --git a/v2/src/lib/agent-dispatcher.ts b/v2/src/lib/agent-dispatcher.ts index c15b67d..2908b97 100644 --- a/v2/src/lib/agent-dispatcher.ts +++ b/v2/src/lib/agent-dispatcher.ts @@ -31,6 +31,7 @@ import { extractWritePaths, extractWorktreePath } from './utils/tool-files'; import { hasAutoAnchored, markAutoAnchored, addAnchors, getAnchorSettings } from './stores/anchors.svelte'; import { selectAutoAnchors, serializeAnchorsForInjection } from './utils/anchor-serializer'; import type { SessionAnchor } from './types/anchors'; +import { getEnabledProjects } from './stores/workspace.svelte'; let unlistenMsg: (() => void) | null = null; let unlistenExit: (() => void) | null = null; @@ -418,7 +419,8 @@ function triggerAutoAnchor( ): void { markAutoAnchored(projectId); - const settings = getAnchorSettings(projectId); + const project = getEnabledProjects().find(p => p.id === projectId); + const settings = getAnchorSettings(project?.anchorBudgetScale); const { turns, totalTokens } = selectAutoAnchors( messages, sessionPrompt, diff --git a/v2/src/lib/components/Agent/AgentPane.svelte b/v2/src/lib/components/Agent/AgentPane.svelte index 86eb61c..d949c55 100644 --- a/v2/src/lib/components/Agent/AgentPane.svelte +++ b/v2/src/lib/components/Agent/AgentPane.svelte @@ -14,7 +14,7 @@ import { getInjectableAnchors, getProjectAnchors, addAnchors, removeAnchor } from '../../stores/anchors.svelte'; import { estimateTokens } from '../../utils/anchor-serializer'; import type { SessionAnchor } from '../../types/anchors'; - import { notify } from '../../stores/notifications.svelte'; + import AgentTree from './AgentTree.svelte'; import { getHighlighter, highlightCode, escapeHtml } from '../../utils/highlight'; import type { @@ -171,12 +171,6 @@ if (anchors.length > 0) { // Anchors store pre-serialized content — join them directly systemPrompt = anchors.map(a => a.content).join('\n'); - - // Warn if Ollama provider — default context windows (2K-4K) may be too small - if (providerId === 'ollama') { - const anchorTokens = anchors.reduce((sum, a) => sum + a.estimatedTokens, 0); - notify('warning', `Ollama: injecting ~${anchorTokens} anchor tokens into system prompt. Ensure num_ctx >= 8192 to avoid truncation.`); - } } } diff --git a/v2/src/lib/components/Workspace/ContextTab.svelte b/v2/src/lib/components/Workspace/ContextTab.svelte index 1535465..3f48de6 100644 --- a/v2/src/lib/components/Workspace/ContextTab.svelte +++ b/v2/src/lib/components/Workspace/ContextTab.svelte @@ -9,15 +9,16 @@ removeAnchor, changeAnchorType, } from '../../stores/anchors.svelte'; - import { DEFAULT_ANCHOR_SETTINGS, MAX_ANCHOR_TOKEN_BUDGET } from '../../types/anchors'; - import type { SessionAnchor, AnchorType } from '../../types/anchors'; + import { ANCHOR_BUDGET_SCALE_MAP, MAX_ANCHOR_TOKEN_BUDGET } from '../../types/anchors'; + import type { SessionAnchor, AnchorType, AnchorBudgetScale } from '../../types/anchors'; interface Props { sessionId: string | null; projectId?: string; + anchorBudgetScale?: AnchorBudgetScale; } - let { sessionId, projectId }: Props = $props(); + let { sessionId, projectId, anchorBudgetScale }: Props = $props(); // Reactive session data let session = $derived(sessionId ? getAgentSession(sessionId) : undefined); @@ -193,7 +194,7 @@ let anchors = $derived(projectId ? getProjectAnchors(projectId) : []); let injectableAnchors = $derived(projectId ? getInjectableAnchors(projectId) : []); let anchorTokens = $derived(projectId ? getInjectableTokenCount(projectId) : 0); - let anchorBudget = $derived(DEFAULT_ANCHOR_SETTINGS.anchorTokenBudget); + let anchorBudget = $derived(ANCHOR_BUDGET_SCALE_MAP[anchorBudgetScale ?? 'medium']); let anchorBudgetPct = $derived(anchorBudget > 0 ? Math.min((anchorTokens / anchorBudget) * 100, 100) : 0); function anchorTypeLabel(t: AnchorType): string { diff --git a/v2/src/lib/components/Workspace/ProjectBox.svelte b/v2/src/lib/components/Workspace/ProjectBox.svelte index 07ec3ec..03f4dd7 100644 --- a/v2/src/lib/components/Workspace/ProjectBox.svelte +++ b/v2/src/lib/components/Workspace/ProjectBox.svelte @@ -157,7 +157,7 @@
- +
diff --git a/v2/src/lib/components/Workspace/SettingsTab.svelte b/v2/src/lib/components/Workspace/SettingsTab.svelte index edb3b68..3737b49 100644 --- a/v2/src/lib/components/Workspace/SettingsTab.svelte +++ b/v2/src/lib/components/Workspace/SettingsTab.svelte @@ -20,6 +20,7 @@ import { invoke } from '@tauri-apps/api/core'; import { getProviders } from '../../providers/registry.svelte'; import type { ProviderId, ProviderSettings } from '../../providers/types'; + import { ANCHOR_BUDGET_SCALES, ANCHOR_BUDGET_SCALE_LABELS, type AnchorBudgetScale } from '../../types/anchors'; const PROJECT_ICONS = [ '📁', '🚀', '🤖', '🌐', '🔧', '🎮', '📱', '💻', @@ -771,6 +772,27 @@ {/if} +
+ + + Anchor Budget + +
+ { + const idx = parseInt((e.target as HTMLInputElement).value); + updateProject(activeGroupId, project.id, { anchorBudgetScale: ANCHOR_BUDGET_SCALES[idx] }); + }} + /> + {ANCHOR_BUDGET_SCALE_LABELS[project.anchorBudgetScale ?? 'medium']} +
+
+