Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e5fe011
Merge pull request #385 from TencentCloud/chore/sync-develop-after-ma…
github-actions[bot] Aug 23, 2026
22bf41b
feat(plugins): per-agent tool toggles, plugin reload, UI assets, and …
jubaoliang Aug 23, 2026
caabc16
feat(experts): add clinical source failover policy
Aug 23, 2026
932bc47
feat(experts): add Karpathy knowledge base expert
Aug 23, 2026
0683c74
fix(dashboard): stop Firefox infinite reload on modulepreload errors
huangcheng Aug 24, 2026
f225717
feat(acp): add Kimi Code, Cursor CLI, and Pi built-in runners
huangcheng Aug 24, 2026
ee7934d
fix(dashboard): 按钮组件文案全球化
chujieHong Aug 24, 2026
76fb449
feat(dashboard): integrate copy-to-clipboard utility for improved tex…
liukewia Aug 24, 2026
ac52733
fix(auth): verify SSO ID tokens against discovery issuer (#399)
liukewia Aug 25, 2026
00ba48d
fix(chat): use popovers for compact desktop pickers
XiaoFeiCode Aug 24, 2026
1396e4f
fix(dashboard): wait for service worker activation before reload
XiaoFeiCode Aug 24, 2026
e954974
fix(browser): make runtime dir and writability probe Windows-safe
sjwauto123 Aug 24, 2026
72be41a
feat(experts): add 4 general-purpose expert templates to the library
veenyi Aug 25, 2026
4d9935a
feat(subagents): add 49 zh (Chinese) subagents across hr/legal/supply…
veenyi Aug 25, 2026
f80e8b1
feat(mobile): add ADB shell panel with rotation and split desktop layout
huangcheng Aug 25, 2026
988db77
fix(connectors): fall back to user-level npm prefix when global dir i…
veenyi Aug 25, 2026
d9cb476
feat(fnos): add FnOS NAS app packaging (Docker + native .fpk) (#407)
veenyi Aug 25, 2026
2f33954
fix(chat): accept OS MIME aliases and expand inbound attachment types…
liukewia Aug 25, 2026
5293f0c
feat: add Codex-style task execution experience (#382)
Pandakingxbc Aug 25, 2026
8711b7d
feat(dashboard,backend): native clipboard fallback and chat UI polish…
jubaoliang Aug 25, 2026
bd1fbdd
refactor(fnos): split fnos packaging into docker/ and native/ variant…
jubaoliang Aug 25, 2026
d4ee7ae
Revert "feat: add Codex-style task execution experience (#382)" (#422)
Pandakingxbc Aug 25, 2026
4166bc1
feat(experts): refine clinical source verification (#419)
Pandakingxbc Aug 25, 2026
7910944
fix(tests): align subagent catalog division count with bundled librar…
jubaoliang Aug 25, 2026
02d7d85
feat(onnx): race Hugging Face and hf-mirror for model downloads (#415)
liukewia Aug 26, 2026
acf48d7
feat(connectors): catalog-driven MCP OAuth for Ardot and Dida365 (#414)
huangcheng Aug 26, 2026
76db0e1
feat(knowledge): 支持知识库文件夹重命名 (#417)
chujieHong Aug 26, 2026
1d92570
feat(media): add Volcengine Ark image and video generation (#428)
Pandakingxbc Aug 26, 2026
8b2f1ec
feat: 内置插件随包分发、可配置上传上限,并补齐 Dashboard 推送与聊天音视频
jubaoliang-tencent Aug 26, 2026
d07ed81
docs: 记录内置插件、上传上限与 Dashboard 推送的 Unreleased 变更
jubaoliang-tencent Aug 26, 2026
4d47968
chore: release 0.9.27
jubaoliang-tencent Aug 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
367 changes: 367 additions & 0 deletions .github/workflows/fnos-build-fpk.yml

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@

## [Unreleased]

## [0.9.27] - 2026-08-26

### 新增

- 内置插件随包装分发(默认关闭,卸载后升级不重建)
- 可配置上传上限(`max_upload_mb` / `OCTOP_MAX_UPLOAD_MB`,默认 100MB)
- Dashboard 推送通知(定时任务与主动关怀 toast)
- 聊天音视频附件预览播放,并扩展 inbound 附件 MIME
- 火山方舟 Seedream / Seedance 生成模型配置、测试与结果展示
- 连接器:Ardot、滴答清单;远程 MCP OAuth 改为 catalog 驱动
- 单工具开关热更新与插件工具目录
- ACP 内置 Runner:Kimi Code、Cursor CLI、Pi
- 知识库文件夹重命名
- ONNX 模型下载竞速 Hugging Face 与 hf-mirror
- 远程手机 ADB shell(旋转与分屏布局)
- FnOS NAS 应用打包(Docker / native `.fpk`)
- 专家模板扩充(通用、Karpathy、临床来源策略)
- 中文子智能体约 49 个(HR / 法务 / 供应链)
- Dashboard 剪贴板回退与聊天 UI 打磨

### 修复

- 知识库文件夹操作按钮误开文件夹
- 工具预期失败不再误报为 `stream_error`
- 连接器 OAuth 公网回调 / HTTPS / 自动保存;npm 不可写时回退用户级 prefix
- SSO ID token issuer 校验
- Dashboard:SW 激活后再 reload、Firefox 无限刷新、选择器 popover、文案全球化
- 浏览器 runtime 目录在 Windows 上可写探测

### 变更

- FnOS 打包拆分为 `docker/` 与 `native/`

## [0.9.26] - 2026-08-23

### 新增
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<p align="center">
<a href="https://www.python.org/downloads/"><img alt="Python 3.12+" src="https://img.shields.io/badge/python-3.12%2B-blue?logo=python&logoColor=white" /></a>
<a href="https://github.com/TencentCloud/Octop/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.26-orange" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.27-orange" /></a>
<a href="https://pypi.org/project/octop/"><img src="https://img.shields.io/pypi/v/octop" alt="PyPI" /></a>
<a href="https://github.com/astral-sh/ruff"><img alt="Code Style: Ruff" src="https://img.shields.io/badge/code%20style-ruff-000000?logo=ruff&logoColor=white" /></a>
<a href="https://github.com/TencentCloud/Octop"><img alt="GitHub stars" src="https://img.shields.io/github/stars/TencentCloud/Octop?style=social" /></a>
Expand Down Expand Up @@ -135,6 +135,12 @@ Here are our mid-to-long term plans:

This roadmap may shift as the community grows; treat it as indicative only.


## 🖥️ FnOS (飞牛 NAS) Installation

Octop can be installed on FnOS (飞牛 NAS) devices via the App Center as a `.fpk` package, either as a Docker-backed app or as a native (non-Docker) app. See [`fnos/README.md`](fnos/README.md) for the full packaging guide, the FPK build helper (`scripts/build-fpk.sh`), and the CI pipeline template (`.github/workflows/fnos-build-fpk.yml`).

Initial admin credentials: `admin` / `Octop123` (change after first login). For non-root FnOS installs, the WeCom/Feishu connector CLIs require the fix in [PR #406](https://github.com/TencentCloud/Octop/pull/406) (user-level npm fallback).
## 🚀 Quick Start

### Prerequisites
Expand Down
14 changes: 13 additions & 1 deletion dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,28 @@
<script>
(function () {
var KEY = "octop:chunk-reload";
// Firefox fires error on <link rel="modulepreload"> for /assets/*.js
// even when the file is fine; treating those as stale chunks caused
// infinite location.reload() loops (and aborted real dynamic imports).
var recovering = false;
function isAsset(url) {
return typeof url === "string" && url.indexOf("/assets/") !== -1;
}
function isStylesheetLink(el) {
if (!el || el.tagName !== "LINK") return false;
var rel = (el.rel || "").toLowerCase();
return rel.indexOf("stylesheet") !== -1;
}
function recover() {
if (recovering) return;
try {
if (sessionStorage.getItem(KEY) === "1") {
sessionStorage.removeItem(KEY);
return;
}
sessionStorage.setItem(KEY, "1");
} catch (e) {}
recovering = true;
var reload = function () {
location.reload();
};
Expand Down Expand Up @@ -185,7 +196,8 @@
var t = e.target;
if (!t || !t.tagName) return;
if (t.tagName === "SCRIPT" && isAsset(t.src)) recover();
if (t.tagName === "LINK" && isAsset(t.href)) recover();
// Only stylesheets — never modulepreload / preload (Firefox noise).
if (isStylesheetLink(t) && isAsset(t.href)) recover();
},
true,
);
Expand Down
20 changes: 10 additions & 10 deletions dashboard/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions dashboard/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { createGlobalStyle } from "antd-style";
import { ConfigProvider, theme as antdTheme } from "antd";
import zhCN from "antd/locale/zh_CN";
import enUS from "antd/locale/en_US";
import { useEffect } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useTranslation } from "react-i18next";
Expand Down Expand Up @@ -31,9 +33,14 @@ const GlobalStyle = createGlobalStyle`

function ThemedApp() {
const { isDark, palette, customColor } = useTheme();
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const isMobile = useIsMobile();
const brandTokens = brandTokensFor(palette, isDark, customColor);
// Make antd built-ins (Popconfirm OK/Cancel, Modal default footer, Empty,
// Pagination, DatePicker, Table… ) follow the current UI language.
const antdLocale = i18n.language?.toLowerCase().startsWith("zh")
? zhCN
: enUS;

useUnauthorizedRedirect();

Expand Down Expand Up @@ -98,7 +105,7 @@ function ThemedApp() {
};

return (
<ConfigProvider theme={themeConfig} prefixCls="octop">
<ConfigProvider theme={themeConfig} prefixCls="octop" locale={antdLocale}>
<AntdAppProvider>
<Routes>
<Route path="/login" element={<LoginPage />} />
Expand Down
80 changes: 80 additions & 0 deletions dashboard/src/api/modules/agentTools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { request } from "../request";

export type ToolSettingsSource = "builtin" | "plugin";

export interface ToolSettingsItem {
name: string;
source: ToolSettingsSource;
category: string;
label: string;
description?: string | null;
enabled: boolean;
disableable: boolean;
available?: boolean;
plugin_id?: string | null;
}

export interface ToolSettingsResponse {
tools: ToolSettingsItem[];
}

export type AgentPluginsConfig = Record<
string,
{
tools?: Record<
string,
{
enabled?: boolean;
config?: Record<string, unknown>;
}
>;
}
>;

export interface ToolSettingsPutBody {
disabled_builtin: string[];
plugins?: AgentPluginsConfig;
}

export interface ToolSettingPatchBody {
enabled: boolean;
source: ToolSettingsSource;
plugin_id?: string | null;
}

export const agentToolsApi = {
get(agentId: string): Promise<ToolSettingsResponse> {
return request<ToolSettingsResponse>(
`/agents/${encodeURIComponent(agentId)}/tool-settings`,
);
},

put(
agentId: string,
body: ToolSettingsPutBody,
): Promise<ToolSettingsResponse> {
return request<ToolSettingsResponse>(
`/agents/${encodeURIComponent(agentId)}/tool-settings`,
{
method: "PUT",
body: JSON.stringify(body),
},
);
},

patch(
agentId: string,
toolName: string,
body: ToolSettingPatchBody,
): Promise<ToolSettingsResponse> {
return request<ToolSettingsResponse>(
`/agents/${encodeURIComponent(
agentId,
)}/tool-settings/${encodeURIComponent(toolName)}`,
{
method: "PATCH",
body: JSON.stringify(body),
},
);
},
};
11 changes: 10 additions & 1 deletion dashboard/src/api/modules/knowledgeBases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export interface KnowledgeOnnxDownloadState {
export const DEFAULT_KNOWLEDGE_LIMITS: KnowledgeLimits = {
max_bases_per_owner: 20,
max_docs_per_kb: 100,
max_document_bytes: 20 * 1024 * 1024,
max_document_bytes: 100 * 1024 * 1024,
};

export const knowledgeBasesApi = {
Expand Down Expand Up @@ -252,4 +252,13 @@ export const knowledgeBasesApi = {
request<{ enqueued: number }>(`/knowledge-bases/${id}/reindex`, {
method: "POST",
}),

renameDocument: (id: string, documentId: string, newName: string) =>
request<KnowledgeDocument>(
`/knowledge-bases/${id}/documents/${documentId}/rename`,
{
method: "POST",
body: JSON.stringify({ new_name: newName }),
},
),
};
48 changes: 48 additions & 0 deletions dashboard/src/api/modules/mediaGeneration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { request } from "../request";

export interface MediaGenerationSettings {
enabled: boolean;
provider: "volcengine";
base_url: string;
image_enabled: boolean;
video_enabled: boolean;
image_model: string;
video_model: string;
api_key_set: boolean;
configured: boolean;
}

export interface MediaGenerationSettingsInput {
enabled: boolean;
image_enabled: boolean;
video_enabled: boolean;
image_model: string;
video_model: string;
api_key?: string | null;
}

export interface MediaGenerationTestInput {
kind: "credentials" | "image" | "video";
api_key?: string | null;
image_model?: string;
video_model?: string;
}

export const mediaGenerationApi = {
get: () => request<MediaGenerationSettings>("/admin/media-generation"),

save: (body: MediaGenerationSettingsInput) =>
request<MediaGenerationSettings>("/admin/media-generation", {
method: "PUT",
body: JSON.stringify(body),
}),

test: (body: MediaGenerationTestInput) =>
request<{ ok: boolean; error?: string | null }>(
"/admin/media-generation/test",
{
method: "POST",
body: JSON.stringify(body),
},
),
};
20 changes: 20 additions & 0 deletions dashboard/src/api/modules/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ export interface InstalledPlugin {
name?: string;
kind?: string;
description?: string;
/** Emoji text or absolute image URL from plugin.yaml. */
icon?: string | null;
requires?: string[];
path?: string;
loaded?: boolean;
/** Global enable switch from config.json (default true). */
enabled?: boolean;
error?: string;
ui?: { entry: string; manifest: string } | null;
tools?: {
name: string;
description?: string;
Expand Down Expand Up @@ -77,6 +83,20 @@ export const pluginsApi = {
});
},

setEnabled(pluginId: string, enabled: boolean): Promise<InstalledPlugin> {
return request(`/plugins/${encodeURIComponent(pluginId)}`, {
method: "PATCH",
body: JSON.stringify({ enabled }),
});
},

reload(): Promise<{
status: string;
loaded: { id: string; version: string; kind: string }[];
}> {
return request("/plugins/reload", { method: "POST" });
},

listAgentTools(agentId: string): Promise<{ tools: AgentPluginTool[] }> {
return request(`/plugins/agents/${encodeURIComponent(agentId)}/tools`);
},
Expand Down
6 changes: 6 additions & 0 deletions dashboard/src/api/modules/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ export interface OctopTimezoneSettings {
timezone: string;
}

export interface OctopUploadSettings {
max_upload_mb: number;
max_upload_bytes: number;
}

export interface OctopCapabilitiesSettings {
mobile: { enabled: boolean; backend: string };
}

export const octopSettingsApi = {
timezone: () => request<OctopTimezoneSettings>("/settings/timezone"),
upload: () => request<OctopUploadSettings>("/settings/upload"),
capabilities: () =>
request<OctopCapabilitiesSettings>("/settings/capabilities", {
cache: "no-store",
Expand Down
10 changes: 10 additions & 0 deletions dashboard/src/api/modules/wsNotifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getAuthToken } from "../request";
import { getWsUrl } from "../config";

export function buildDashboardNotifyWsUrl(): string {
const token = getAuthToken();
const base = getWsUrl("/notifications/ws");
if (!token) return base;
const params = new URLSearchParams({ token });
return `${base}?${params.toString()}`;
}
Loading
Loading