diff --git a/.changeset/runtime-config-docs-links.md b/.changeset/runtime-config-docs-links.md new file mode 100644 index 000000000..1e5d73c4c --- /dev/null +++ b/.changeset/runtime-config-docs-links.md @@ -0,0 +1,5 @@ +--- +'@truefoundry/trueforge-ui': patch +--- + +Link Runtime Config capability toggles to their docs pages. diff --git a/packages/trueforge-ui/src/atoms/draft/AgentRuntimeConfigFields.tsx b/packages/trueforge-ui/src/atoms/draft/AgentRuntimeConfigFields.tsx index eb5566218..a6c6a10a9 100644 --- a/packages/trueforge-ui/src/atoms/draft/AgentRuntimeConfigFields.tsx +++ b/packages/trueforge-ui/src/atoms/draft/AgentRuntimeConfigFields.tsx @@ -41,6 +41,16 @@ const ENVIRONMENT_LOCKED_HINT = "Can't change the environment after a chat has s const NO_SANDBOX_PROVIDER_HINT = 'No sandbox provider yet, add one in Settings → Sandbox'; const DEFAULT_COMPACTION_THRESHOLD_TOKENS = 50_000; +const RUNTIME_CONFIG_DOCS = { + dynamicSubAgents: 'https://trueforge.dev/key-features/subagents', + generativeUi: 'https://trueforge.dev/create-agent/overview#generative-ui', + askUserQuestions: 'https://trueforge.dev/create-agent/overview#ask-clarifying-questions', + sandbox: 'https://trueforge.dev/sandbox', + compaction: 'https://trueforge.dev/key-features/overview#context-compaction', + largeToolResponse: 'https://trueforge.dev/key-features/large-tool-responses', + iterationLimit: 'https://trueforge.dev/create-agent/overview#iteration-limit', +} as const; + const COMPACTION_THRESHOLD_MODE_OPTIONS = [ { value: 'auto', label: 'Auto' }, { value: 'custom', label: 'Custom' }, @@ -51,12 +61,27 @@ type CompactionThresholdMode = (typeof COMPACTION_THRESHOLD_MODE_OPTIONS)[number type RuntimeSwitchField = { label: string; description: string; + docsUrl?: string; checked: boolean; disabled?: boolean; disabledTooltip?: string; update: (enabled: boolean) => AgentRuntimeConfig; }; +function DocsLearnMore({ href }: { href: string }) { + return ( + + Learn more + + + ); +} + export function AgentRuntimeConfigFields({ value, sandboxAvailable, @@ -104,18 +129,21 @@ export function AgentRuntimeConfigFields({ { label: 'Dynamic sub-agents', description: 'Allow the agent to delegate complex work to focused sub-agents.', + docsUrl: RUNTIME_CONFIG_DOCS.dynamicSubAgents, checked: value.dynamicSubAgents?.enabled ?? true, update: enabled => ({ ...value, dynamicSubAgents: { enabled } }), }, { label: 'Generative UI', description: 'Allow rich interactive UI blocks in agent responses.', + docsUrl: RUNTIME_CONFIG_DOCS.generativeUi, checked: value.generativeUi?.enabled ?? true, update: enabled => ({ ...value, generativeUi: { enabled } }), }, { label: 'Ask user questions', description: 'Allow the agent to pause and ask for clarification.', + docsUrl: RUNTIME_CONFIG_DOCS.askUserQuestions, checked: value.askUserQuestions?.enabled ?? true, update: enabled => ({ ...value, askUserQuestions: { enabled } }), }, @@ -154,6 +182,7 @@ export function AgentRuntimeConfigFields({ const sandboxField: RuntimeSwitchField = { label: 'Sandbox', description: 'Provide an isolated environment for code, files, and skills.', + docsUrl: RUNTIME_CONFIG_DOCS.sandbox, checked: sandboxEnabled, disabled: !sandboxAvailable || hasSkills, disabledTooltip: !sandboxAvailable @@ -176,12 +205,14 @@ export function AgentRuntimeConfigFields({ const compactionField: RuntimeSwitchField = { label: 'Context compaction', description: 'Summarize older turns as context fills.', + docsUrl: RUNTIME_CONFIG_DOCS.compaction, checked: compactionEnabled, update: enabled => withCompaction({ ...value.contextManagement?.compaction, enabled }), }; const largeToolResponseField: RuntimeSwitchField = { label: 'Large tool response offloading', description: 'Move large tool output to the sandbox and retain a preview.', + docsUrl: RUNTIME_CONFIG_DOCS.largeToolResponse, checked: value.contextManagement?.largeToolResponse?.enabled ?? true, disabled: !sandboxAvailable, disabledTooltip: !sandboxAvailable ? `Offloading needs a sandbox. ${NO_SANDBOX_PROVIDER_HINT}` : undefined, @@ -230,7 +261,10 @@ export function AgentRuntimeConfigFields({ {field.label} {layout === 'detailed' ? ( - {field.description} + <> + {field.description} + {field.docsUrl ? : null} + > ) : null} {field.disabledTooltip ? ( @@ -264,18 +298,20 @@ export function AgentRuntimeConfigFields({ )} ) : null} - + Iteration limit Maximum agent-loop iterations for one turn. + { @@ -283,7 +319,7 @@ export function AgentRuntimeConfigFields({ if (iterationLimit !== null) onChange({ ...value, iterationLimit }); }} /> - + {webSearchField != null ? switchField({ field: webSearchField, className: cn('py-4', rowClassName) }) : null} {switchField({ field: sandboxField, className: rowClassName })} diff --git a/packages/trueforge-ui/test/atoms/draft/AgentRuntimeConfigFields.test.tsx b/packages/trueforge-ui/test/atoms/draft/AgentRuntimeConfigFields.test.tsx index 9c3021b27..9f9c25ca2 100644 --- a/packages/trueforge-ui/test/atoms/draft/AgentRuntimeConfigFields.test.tsx +++ b/packages/trueforge-ui/test/atoms/draft/AgentRuntimeConfigFields.test.tsx @@ -1,4 +1,4 @@ -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { AgentRuntimeConfigFields } from '@/atoms/draft/AgentRuntimeConfigFields.js'; @@ -83,6 +83,28 @@ function renderRuntimeFields({ } describe('AgentRuntimeConfigFields', () => { + it('links capability and runtime fields to docs in detailed layout', () => { + renderRuntimeFields(); + + const expected = [ + ['Dynamic sub-agents', 'https://trueforge.dev/key-features/subagents'], + ['Generative UI', 'https://trueforge.dev/create-agent/overview#generative-ui'], + ['Ask user questions', 'https://trueforge.dev/create-agent/overview#ask-clarifying-questions'], + ['Iteration limit', 'https://trueforge.dev/create-agent/overview#iteration-limit'], + ['Sandbox', 'https://trueforge.dev/sandbox'], + ['Context compaction', 'https://trueforge.dev/key-features/overview#context-compaction'], + ['Large tool response offloading', 'https://trueforge.dev/key-features/large-tool-responses'], + ] as const; + + for (const [label, href] of expected) { + const fieldRow = screen.getByText(label).closest('.flex'); + expect(fieldRow).not.toBeNull(); + const learnMore = within(fieldRow as HTMLElement).getByRole('link', { name: /Learn more/ }); + expect(learnMore).toHaveAttribute('href', href); + expect(learnMore).toHaveAttribute('target', '_blank'); + } + }); + it('renders environment row in detailed layout with Manage Environments link', async () => { renderRuntimeFields();