Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ OCTOP_ADMIN_USERNAME=admin
OCTOP_DEFAULT_PASSWORD=octop
OCTOP_ADMIN_DISPLAY_NAME=Admin

# Control-plane database (optional — default is SQLite under ~/.octop)
# For Docker Compose, also list these under `environment:` in docker-compose.yml
# (docker/.env only interpolates; it does not auto-inject into the container).
# Bare-metal / container data volume: same keys may live in ~/.octop/env.
# OCTOP_DATABASE_DRIVER=postgresql
# OCTOP_DATABASE_URL=postgresql://octop:octop@127.0.0.1:5432/octop
# OCTOP_DATABASE_HOST=127.0.0.1
# OCTOP_DATABASE_PORT=5432
# OCTOP_DATABASE_NAME=octop
# OCTOP_DATABASE_USER=octop
# OCTOP_DATABASE_PASSWORD=
# OCTOP_DATABASE_SQLITE_PATH=octop.db

# Common LLM provider credentials (optional — can configure in dashboard)
OPENAI_API_KEY=
DASHSCOPE_API_KEY=
Expand Down
25 changes: 23 additions & 2 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Pre-commit gate: backend ship bar + dashboard production build.
# Pre-commit gate: make all (format BE+FE + lint + typecheck + test) + dashboard build.
# Enable once per clone: make install-hooks
# Bypass (emergency only): SKIP_PRECOMMIT=1 git commit ...
# Or: git commit --no-verify
Expand All @@ -13,9 +13,30 @@ fi
ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT"

echo "[pre-commit] make all (lint + typecheck + test)"
# Remember staged paths so we can re-add them after auto-format rewrites the worktree.
# Portable (bash 3.2+): avoid `mapfile` (bash 4+ only).
STAGED_FILES=()
while IFS= read -r line; do
[[ -n "$line" ]] && STAGED_FILES+=("$line")
done < <(git diff --cached --name-only --diff-filter=ACMR || true)

echo "[pre-commit] make all (format-all + lint + typecheck + test)"
make all

# If format rewrote files that were already staged, refresh the index so the
# commit includes the formatted content (worktree vs index drift).
if ((${#STAGED_FILES[@]} > 0)); then
existing=()
for f in "${STAGED_FILES[@]}"; do
if [[ -e "$f" || -L "$f" ]]; then
existing+=("$f")
fi
done
if ((${#existing[@]} > 0)); then
git add -- "${existing[@]}"
fi
fi

echo "[pre-commit] dashboard npm run build"
(
cd "$ROOT/dashboard"
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,20 @@ jobs:

# workflow_run / release:published are unreliable when Release itself was
# started via GITHUB_TOKEN workflow_dispatch (Auto Tag). Explicitly cascade.
#
# This job intentionally has no checkout: pass --repo so `gh` does not need a
# local .git (bare runner → "fatal: not a git repository"). continue-on-error
# keeps a cascade miss from marking PyPI/GitHub release as failed.
sync-develop:
name: Trigger sync main into develop
needs: github-release
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: Dispatch Sync Main Into Develop
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run sync-main-to-develop.yml --ref main
run: |
gh workflow run sync-main-to-develop.yml \
--repo "${{ github.repository }}" \
--ref main
16 changes: 9 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,20 @@ Frontend talks to Octop **only** via `/api` HTTP — never import or assume Pyth

```bash
make install-hooks # once per clone: enable .githooks pre-commit
make all # format-all + lint + typecheck + test (ship bar)
make format-all # backend Ruff + dashboard Prettier write
make lint # ruff check + format check
make typecheck # mypy --strict src/octop
make format # ruff auto-fix + format (backend only)
make format-frontend # prettier write (dashboard only)
uv run pytest -m "not live" # full test suite (no LLM calls)
uv run pytest tests/unit -x -q # unit tests only, stop on first fail
uv run pytest tests/integration -x -q # integration tests only
make lint # ruff check + format check
make typecheck # mypy --strict src/octop
make format # auto-fix lint issues
make all # lint + typecheck + test (ship bar)
cd dashboard && npx tsc --noEmit # frontend typecheck (after UI changes)
make build-frontend # dashboard/ → src/octop/dashboard/
```

**Git hooks (required for local commits):** after cloning, run **`make install-hooks`** once. That sets `core.hooksPath=.githooks` so every `git commit` runs `make all` plus `dashboard` `npm run build` before the commit is created. Bypass only in emergencies: `SKIP_PRECOMMIT=1 git commit …` or `git commit --no-verify`. Do **not** skip hooks to land red tests — fix the suite first (CI runs on Linux **and** Windows).
**Git hooks (required for local commits):** after cloning, run **`make install-hooks`** once. That sets `core.hooksPath=.githooks` so every `git commit` runs **`make all`** (which first runs **`format-all`**: backend Ruff + dashboard Prettier write, then lint / typecheck / test) and dashboard **`npm run build`**. Formatted files that were already staged are re-added so the commit includes the formatted content. Bypass only in emergencies: `SKIP_PRECOMMIT=1 git commit …` or `git commit --no-verify`. Do **not** skip hooks to land red tests — fix the suite first (CI runs on Linux **and** Windows).

## 7. Key patterns

Expand Down Expand Up @@ -336,7 +338,7 @@ Boundary rules are in [§5](#5-module-boundaries). Additionally:
| Internationalization (dashboard) | `dashboard/src/locales/`, `dashboard/src/i18n.ts`, `dashboard/src/utils/apiError.ts` |
| Server timezone (config.json) | `default_timezone` in `config.py`; `GET /api/settings/timezone`; `dashboard/src/hooks/useServerTimezone.ts`; `dashboard/src/utils/formatMessageTime.ts` |
| Test layout & shared helpers | `tests/support/` (`fakes`, `auth`, `http`, `scenarios`, `app`), `tests/integration/conftest.py`, `tests/unit/{db,cron,gateway,agents,api,cli}/` |
| Pre-commit hooks | `make install-hooks` → `.githooks/pre-commit` (`make all` + dashboard build) |
| Pre-commit hooks | `make install-hooks` → `.githooks/pre-commit` (`make all` incl. `format-all` + dashboard build) |
| What is a Thread? | `infra/gateway/threads.py`, `infra/db/repos/threads.py` |
| Workspace backend resolution | `infra/backend/resolver.py`, `infra/backend/adapter.py` |
| Connectors & OAuth | `infra/connectors/`, `api/routers/connectors.py` |
Expand All @@ -350,7 +352,7 @@ Boundary rules are in [§5](#5-module-boundaries). Additionally:
1. **Clarify scope** — read relevant code/docs; confirm assumptions and ambiguities with the user (see [§1](#1-collaboration-principles)).
2. **Hooks** — if this clone has not run `make install-hooks` yet, do it before committing (see [§6](#6-run-commands)). Pre-commit must stay green (`make all` + dashboard build).
3. **Minimal implementation** — change only task-related files; dashboard source is in `dashboard/`, build output in `src/octop/dashboard/` (run `make build-frontend` after UI changes).
4. **Verify** — backend: `make all` (`lint` + `typecheck` + `test`). After `dashboard/` changes, also run `cd dashboard && npx tsc --noEmit` (and `npm run lint` when appropriate). After API route changes, glance at `/api/docs` for readable summaries and schemas. After i18n JSON changes, run `uv run pytest tests/unit/i18n -q`. Treat Windows CI as part of the bar: follow [§7 Cross-platform tests](#7-key-patterns).
4. **Verify** — backend/ship bar: `make all` (`format-all` + `lint` + `typecheck` + `test`). After `dashboard/` changes, also run `cd dashboard && npx tsc --noEmit` (and `npm run lint` when appropriate). After API route changes, glance at `/api/docs` for readable summaries and schemas. After i18n JSON changes, run `uv run pytest tests/unit/i18n -q`. Treat Windows CI as part of the bar: follow [§7 Cross-platform tests](#7-key-patterns).
5. **Wrap up** — remove orphan symbols introduced in this change; do not commit or push unless asked.

### Branching & release
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@

## [Unreleased]

## [0.9.19] - 2026-08-05

### 新增
- 登录页滑动验证控件;侧栏与 Agent 资料抽屉 UI 优化 (#170)
- 聊天历史 API 返回 `turn_active`,重连客户端可 re-subscribe WebSocket 恢复流式输出 (#168, #157)
- Workbench 与聊天 Dock 共用同一 terminal 会话;旧式硬切会话标题迁移为带省略号的裁剪标题 (#157)
- 局部 `root_dir` 下 Linux bubblewrap execute jail(`POST /api/filesystem/ensure-bwrap`、仪表盘 root 目录树 mkdir/rename)(#167)
- 虚拟工作区路径 I/O:host 绝对路径经 `file://` 与 `BackendWorkspace` failback 对齐 (#167)
- 高级设置「更新」页提供按安装方式升级说明与一键检查升级双栏布局;HTTPS 页优化签发状态与预检展示 (#143)

### 修复
- 401 会话过期时通过 React Router 跳转登录,避免整页 reload 导致 lazy chunk 白屏 (#169)

### 变更
- `make all` 先执行前后端 `format-all`(Ruff + Prettier);pre-commit 在 format 后回写已暂存文件并构建 dashboard (#143)
- harness runtime 诊断日志写入 `~/.octop/logs`(与 `octop.log` 并排),不再落到各 agent workspace 的 `logs/`;行内带 `[agent=…]`
- 依赖 `orcakit-harness-agent>=0.9.19`、`harness-gateway>=0.9.1`(scoped root execute jail)
- 企业微信客户群二维码与文档有效期更新至 2026-08-08 (#149)

## [0.9.18] - 2026-08-02

### 新增
Expand Down Expand Up @@ -35,6 +54,12 @@
- 备份/恢复纳入 `skill-packages/` 目录,恢复前清空避免残留 (#108)
- 统一聊天生成中 / 滚动辅助逻辑;antd message 经 App.useApp 绑定,支持主题感知 toast (#119)

### 修复
- Memory 原始事件列表的时间戳按服务器时区展示,与其余 Memory 页保持一致 (#110)

### 修复
- 记忆提取 / 提升等 harness 内部辅助 LLM 默认跟随全局偏好模型(此前切换全局模型后仍回退到首个可用模型)(#110)

## [0.9.16] - 2026-07-29

### 新增
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ git clone https://github.com/TencentCloud/Octop.git octop
cd octop
make install # backend dev dependencies
make install-hooks # once per clone: pre-commit runs make all + dashboard build
make all # backend lint + typecheck + test (CI ship bar)
make all # format-all + backend lint + typecheck + test (ship bar)
```

For frontend work (separate terminal):
Expand All @@ -29,7 +29,7 @@ make check-all # full stack quality gate
|---------|-------------|
| `make install` | Install Python dev dependencies |
| `make install-hooks` | Point git at `.githooks` (pre-commit: `make all` + dashboard build) |
| `make all` | Backend lint + typecheck + test |
| `make all` | `format-all` + backend lint + typecheck + test |
| `make check-all` | Full stack quality gate |
| `make dev` | Start frontend + backend dev servers |
| `make build` | Build dashboard + Python wheel |
Expand Down Expand Up @@ -90,7 +90,7 @@ git clone https://github.com/TencentCloud/Octop.git octop
cd octop
make install
make install-hooks # 每个 clone 执行一次:提交前跑 make all + 前端 build
make all # 后端质量门禁
make all # format-all + 后端 lint / typecheck / test
```

前端开发(另开终端):
Expand Down
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Makefile for Octop
# Usage:
# make - Show this help
# make all - Backend lint + typecheck + test (CI ship bar)
# make all - format (BE+FE) + backend lint + typecheck + test (ship bar)
# make build - Build frontend + Python wheel
# make publish - Build + upload to PyPI
#
Expand Down Expand Up @@ -56,8 +56,8 @@ help:
@echo " test-online pytest against .venv-online (not live)"
@echo " run-online Start octop run from .venv-online"
@echo ""
@echo "Quality targets (backend — CI ship bar):"
@echo " all lint + typecheck + test (backend)"
@echo "Quality targets (ship bar):"
@echo " all format-all + lint + typecheck + test (backend lint/typecheck/test)"
@echo " lint Ruff check + format check (src, tests)"
@echo " format Ruff auto-fix + format (src, tests)"
@echo " typecheck mypy --strict src/octop"
Expand All @@ -71,12 +71,12 @@ help:
@echo ""
@echo "Quality targets (full stack):"
@echo " lint-all lint + lint-frontend"
@echo " format-all format + format-frontend"
@echo " format-all format + format-frontend (also first step of make all)"
@echo " typecheck-all typecheck + typecheck-frontend"
@echo " check-all lint-all + typecheck-all + test"
@echo ""
@echo "Utility targets:"
@echo " install-hooks Point git to .githooks (pre-commit runs make all + npm run build)"
@echo " install-hooks Point git to .githooks (pre-commit: make all + dashboard build)"
@echo " install Install Python dev dependencies (alias: install-dev)"
@echo " install-dev uv sync / pip install -e \".[dev]\""
@echo " install-tools Install build + twine for publishing"
Expand Down Expand Up @@ -196,7 +196,7 @@ run-online:
# ─── Quality (backend) ───────────────────────────────────────────────────────

.PHONY: all
all: lint typecheck test
all: format-all lint typecheck test

.PHONY: lint
lint:
Expand Down Expand Up @@ -266,7 +266,7 @@ install-hooks:
@echo "[install-hooks] Setting core.hooksPath=.githooks"
git config core.hooksPath .githooks
@chmod +x "$(REPO_ROOT)/.githooks/"* 2>/dev/null || true
@echo "[install-hooks] Done. Pre-commit will run: make all && (cd dashboard && npm run build)"
@echo "[install-hooks] Done. Pre-commit will run: make all (incl. format-all), dashboard build"
@echo "[install-hooks] Bypass: SKIP_PRECOMMIT=1 git commit … or git commit --no-verify"

.PHONY: install install-dev
Expand Down
6 changes: 3 additions & 3 deletions 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.18-orange" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.19-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 @@ -415,7 +415,7 @@ tests/ unit/ + integration/
```bash
# Backend
make install # pip install -e ".[dev]"
make all # lint + typecheck + test (ship bar)
make all # format-all + lint + typecheck + test (ship bar)

# Frontend (separate terminal)
make dev-frontend # Vite dev server on :5173 (override with VITE_DEV_PORT)
Expand Down Expand Up @@ -468,7 +468,7 @@ For the customer WeCom support group, scan:
<img src="docs/assets/qrcode.png" alt="WeCom customer group QR code" width="220" />
</p>

> This QR code is valid until **2026-08-03** (UTC+8). Please ask the maintainer for an updated code after it expires.
> This QR code is valid until **2026-08-08** (UTC+8). Please ask the maintainer for an updated code after it expires.

## 📄 License

Expand Down
4 changes: 2 additions & 2 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ tests/ unit/ + integration/
```bash
# 后端
make install # pip install -e ".[dev]"
make all # lint + typecheck + test(发布门槛)
make all # format-all + lint + typecheck + test(发布门槛)

# 前端(另开终端)
make dev-frontend # Vite 开发服务器 :5173
Expand Down Expand Up @@ -478,7 +478,7 @@ cd dashboard && npx tsc --noEmit
<img src="docs/assets/qrcode.png" alt="客户企业微信服务群二维码" width="220" />
</p>

> 二维码有效期至 **2026-08-03**,过期后请联系管理员更新。
> 二维码有效期至 **2026-08-08**,过期后请联系管理员更新。

### 📄 许可证

Expand Down
3 changes: 3 additions & 0 deletions dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ThemeProvider, useTheme } from "./context/ThemeContext";
import { AgentProvider } from "./context/AgentContext";
import { VoiceOutputProvider } from "./context/VoiceOutputContext";
import { useIsMobile } from "./hooks/useIsMobile";
import { useUnauthorizedRedirect } from "./hooks/useUnauthorizedRedirect";
import "./styles/theme-vars.css";
import "./styles/layout.css";
import "./styles/form-override.css";
Expand All @@ -29,6 +30,8 @@ function ThemedApp() {
const { t } = useTranslation();
const isMobile = useIsMobile();

useUnauthorizedRedirect();

// Set document title based on current language
useEffect(() => {
document.title = t("app.pageTitle");
Expand Down
19 changes: 13 additions & 6 deletions dashboard/src/api/modules/connectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,13 @@ export const connectorsApi = {
cli_config_key?: string;
domains?: string[];
}) =>
request<FeishuUserAuthStartResult>("/connectors/feishu-cli/user-auth/start", {
method: "POST",
body: JSON.stringify(body),
}),
request<FeishuUserAuthStartResult>(
"/connectors/feishu-cli/user-auth/start",
{
method: "POST",
body: JSON.stringify(body),
},
),

feishuUserAuthComplete: (body: {
app_id: string;
Expand All @@ -247,7 +250,9 @@ export const connectorsApi = {

feishuUserAuthStartInstance: (instanceId: string) =>
request<FeishuUserAuthStartResult>(
`/connector-instances/${encodeURIComponent(instanceId)}/feishu-user-auth/start`,
`/connector-instances/${encodeURIComponent(
instanceId,
)}/feishu-user-auth/start`,
{ method: "POST" },
),

Expand All @@ -256,7 +261,9 @@ export const connectorsApi = {
body: { device_code: string; cli_config_key?: string },
) =>
request<FeishuUserAuthCompleteResult>(
`/connector-instances/${encodeURIComponent(instanceId)}/feishu-user-auth/complete`,
`/connector-instances/${encodeURIComponent(
instanceId,
)}/feishu-user-auth/complete`,
{
method: "POST",
body: JSON.stringify(body),
Expand Down
2 changes: 2 additions & 0 deletions dashboard/src/api/modules/octopThreads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export interface OctopThreadHistory {
has_more?: boolean;
limit?: number;
offset?: number;
/** True while a turn is still streaming server-side for this thread. */
turn_active?: boolean;
}

export interface OctopThreadPatch {
Expand Down
18 changes: 11 additions & 7 deletions dashboard/src/api/modules/skillPackages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ describe("skillPackagesApi", () => {
method: "PATCH",
body: JSON.stringify({ name: "renamed" }),
});
expect(request).toHaveBeenNthCalledWith(3, "/skill-packages/from-skillhub", {
method: "POST",
body: JSON.stringify({
slug: "starter",
icon_name: "sparkles",
}),
});
expect(request).toHaveBeenNthCalledWith(
3,
"/skill-packages/from-skillhub",
{
method: "POST",
body: JSON.stringify({
slug: "starter",
icon_name: "sparkles",
}),
},
);
expect(request).toHaveBeenNthCalledWith(4, "/skill-packages/pkg-1/skills", {
method: "POST",
body: JSON.stringify({
Expand Down
11 changes: 4 additions & 7 deletions dashboard/src/api/modules/skillPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,10 @@ export const skillPackagesApi = {
packageId: string,
body: { bundle_url: string; version?: string; overwrite?: boolean },
) =>
request<SkillPackageSkill>(
`/skill-packages/${packageId}/skills/import`,
{
method: "POST",
body: JSON.stringify(body),
},
),
request<SkillPackageSkill>(`/skill-packages/${packageId}/skills/import`, {
method: "POST",
body: JSON.stringify(body),
}),

hubSearch: (q: string, limit = 50) =>
request<Record<string, unknown>[]>(
Expand Down
Loading
Loading