- {/* Main conversation column */}
-
+ {/* Main conversation column. `relative` hosts the floating
+ question navigator overlay. */}
+
{/* Header in normal flow, fixed height. */}
+ {/*
+ ChatGPT-style question index. Anchored to the chat column (this
+ relative wrapper), not the browser edge; insets keep it below the
+ overlay header (h-12) and above the docked input box, and it is
+ pointer-events-none outside its own cards so it never blocks the
+ header buttons or input.
+ */}
+ {!isLandingState && (
+
+ )}
diff --git a/frontend-web/src/pages/NewAgentPage.tsx b/frontend-web/src/pages/NewAgentPage.tsx
index 822738e6..61295a5b 100644
--- a/frontend-web/src/pages/NewAgentPage.tsx
+++ b/frontend-web/src/pages/NewAgentPage.tsx
@@ -47,9 +47,10 @@ import {
createAgent,
updateAgent,
} from "@/core/agents/api";
+import { getStoredAuth } from "@/core/auth";
import { useI18n } from "@/core/i18n/hooks";
import { useModels } from "@/core/models/hooks";
-import type { Skill } from "@/core/skills";
+import { isAgentSkillCandidate, type Skill } from "@/core/skills";
import { useSkills } from "@/core/skills/hooks";
import { useThreadStream } from "@/core/threads/hooks";
import { uuid } from "@/core/utils/uuid";
@@ -94,9 +95,19 @@ export default function NewAgentPage() {
const { skills, isLoading: isSkillsLoading } = useSkills();
const { models, isLoading: isModelsLoading } = useModels();
+ // 可配置的技能限定为「内置 + 自己的 + 已发布到广场的」,
+ // 其他用户未发布的自定义技能不可选。
+ const storedAuth = useMemo(() => getStoredAuth(), []);
const enabledSkills = useMemo(
- () => skills.filter((skill) => skill.enabled),
- [skills],
+ () =>
+ skills.filter((skill) =>
+ isAgentSkillCandidate(
+ skill,
+ storedAuth?.user_id ?? null,
+ storedAuth?.system_role === "admin",
+ ),
+ ),
+ [skills, storedAuth],
);
const filteredSkills = useMemo(() => {
diff --git a/offline-backend-20260512/backend/CLAUDE.md b/offline-backend-20260512/backend/CLAUDE.md
index b2b5b9c0..50df7255 100644
--- a/offline-backend-20260512/backend/CLAUDE.md
+++ b/offline-backend-20260512/backend/CLAUDE.md
@@ -281,7 +281,7 @@ Two on-demand/automatic refresh paths layer on top:
**Per-user UI preferences** (`deerflow/persistence/user_preferences/`, wired as `app.state.user_preferences_store`; migration `20260603_03`, also auto-created by `create_all`): one JSON-blob row per user so new toggles need no schema change. Router `/api/user-preferences` (`GET`/`PUT`, account-scoped). Current key: `hide_admin_prompt_prefix` — when true the chat input hides the admin-configured prompt-prefix switch and never applies the prefix (the user uses only their own prompts; enforced in `ChatPage` for both `shouldApplyPrefix` and `promptPrefixProp`). Toggled from the "管理常用提示词" dialog. Tests: `tests/test_user_preferences.py`.
-**HTML skills (tools the render step uses).** Two offline skills under `skills/public/` shape the generated HTML and are toggled in the skills settings page (enabled by default): `html-page-builder` (a self-contained inline-CSS design system — colors/cards/tables/KPI/responsive) and `html-charts` (inline-SVG charts that render in the no-`allow-scripts` preview iframe; optional local ECharts documented in its `assets/README.md`). A skill opts into HTML injection via `metadata.kind: html_page` in its SKILL.md frontmatter; `_collect_html_skill_instructions()` reads the bodies of **enabled** such skills and `build_generation_prompt(skill_instructions=…)` injects them, so the (raw-model) render step honors the user's skill selection. The data-gathering step and the render/repair/style passes all use the task's selected `model_name` (no silent fallback to the default model — and there is **no hardcoded GLM-5**; an unset selection falls back to config `models[0]`). To stop auxiliary LLM calls from leaking onto the default model, **`TitleMiddleware` is skipped when `is_scheduled_run` is set** (`_build_middlewares` in `lead_agent/agent.py`) — scheduled/HTML runs use throwaway threads whose titles are never shown, and `config.title.model_name` is usually unset (→ would call `models[0]`, not the chosen model). The completeness gate (`validate_html`) also requires a non-empty `
` and forbids external stylesheets, on top of the doctype/html/head/body + non-empty-body + no-script checks.
+**HTML skills (tools the render step uses).** Two offline skills under `skills/public/` shape the generated HTML and are toggled in the skills settings page (enabled by default): `html-page-builder` (a self-contained inline-CSS design system — colors/cards/tables/KPI/responsive), `html-charts` (inline-SVG charts that render in the no-`allow-scripts` preview iframe; optional local ECharts documented in its `assets/README.md`), and `html-multipage` (multi-"page" navigation **inside the single generated HTML file**: each page is a `` toggled by anchor links + CSS `:target` — no JS, so it survives `sanitize_html`, and `constrain_same_page_anchor_links` keeps `#page-…` hrefs untouched; the frontend's `html-preview-frame.tsx` renders via a `blob:` URL precisely so in-page anchors work; cross-file `*.html` links remain forbidden/stripped). A skill opts into HTML injection via `metadata.kind: html_page` in its SKILL.md frontmatter; `_collect_html_skill_instructions()` reads the bodies of **enabled** such skills and `build_generation_prompt(skill_instructions=…)` injects them, so the (raw-model) render step honors the user's skill selection. The data-gathering step and the render/repair/style passes all use the task's selected `model_name` (no silent fallback to the default model — and there is **no hardcoded GLM-5**; an unset selection falls back to config `models[0]`). To stop auxiliary LLM calls from leaking onto the default model, **`TitleMiddleware` is skipped when `is_scheduled_run` is set** (`_build_middlewares` in `lead_agent/agent.py`) — scheduled/HTML runs use throwaway threads whose titles are never shown, and `config.title.model_name` is usually unset (→ would call `models[0]`, not the chosen model). The completeness gate (`validate_html`) also requires a non-empty `` and forbids external stylesheets, on top of the doctype/html/head/body + non-empty-body + no-script checks.
### Knowledge Base (`packages/harness/deerflow/knowledge/`)
diff --git a/offline-backend-20260512/backend/extensions_config.json b/offline-backend-20260512/backend/extensions_config.json
index 576832f5..522e4597 100644
--- a/offline-backend-20260512/backend/extensions_config.json
+++ b/offline-backend-20260512/backend/extensions_config.json
@@ -8,6 +8,7 @@
"workspace-search": { "enabled": true },
"policy-knowledge": { "enabled": false },
"html-page-builder": { "enabled": true },
- "html-charts": { "enabled": true }
+ "html-charts": { "enabled": true },
+ "html-multipage": { "enabled": true }
}
}