Skip to content

chore: release 1.0.2b7 - #1951

Merged
jubaoliang merged 104 commits into
mainfrom
release/1.0.2b7
Oct 12, 2026
Merged

jubaoliang merged 104 commits into
mainfrom
release/1.0.2b7

Conversation

@jubaoliang

Copy link
Copy Markdown
Collaborator

[1.0.2b7] - 2026-10-12

新增

  • Obsidian CLI 连接器;内置连接器目录改为中英文 JSON
  • arXiv 论文检索;Telegram 可限制允许的用户 ID
  • 腾讯云实时语音;记忆提炼预算与超时可配置
  • 云端桥接命令行;会话列表显示进行中与待处理状态
  • 权限选择与自定义侧栏对齐,对话始终可用

修复

  • 回复操作始终可见;无 PostgreSQL 驱动时仍用 SQLite 启动
  • 团队审批期间保留成员回复;删除会话或用户时清理 checkpoint 与工作区
  • 桌面端保留监听配置,支持拖入文件夹,端口占用时不另起实例
  • Docker 首次启动、云端协同连接,以及不兼容的流式用量参数
  • 暗色主题编辑器、英文界面残留中文、便携版升级与输入栏「+」菜单
  • 飞牛原生启动退出、元宝图标,以及对端专家的模型与知识库保存

变更

  • 头像、技能卡片、个人设置抽屉与登录页外观
  • 升级 octop-harness 与 octop-memory;README 加入 Octop Pet

Release checklist

  • CI green
  • Merge this PR into main
  • After merge, GitHub Action auto-pushes v1.0.2b7 tag on main tip

Made with Cursor

Yourdaylight and others added 30 commits September 22, 2026 20:49
Dictate with live partial results: the composer mic streams 16 kHz PCM over a
WebSocket to Octop, which signs and proxies the audio to Tencent's realtime ASR
V2 endpoint. Credentials never leave the server; the browser only learns a
`stt_realtime` boolean from `/api/voice/active`.

- settings: add a "realtime speech recognition" switch plus an AppID field to
  the Tencent Cloud voice drawer. Both live in `voice_providers.extra_json`, so
  no schema migration is needed.
- interaction: with realtime on, the mic becomes press-and-hold (start on
  pointerdown, finish on release/leave/cancel) and stays available while a reply
  is streaming, so dictation can be queued. With the switch off the previous
  click-to-record one-shot upload path is unchanged.
- protocol: realtime ASR V2 answers with `result` (`slice_type`, `index`,
  `voice_text_str`) while the docs describe `sentences`; both are normalized in
  `infra/voice/realtime.py`.
- errors: new `VOICE_REALTIME_UNAVAILABLE` code plus localized copy for the
  Tencent realtime error codes (4002-4009, 5000-5002).

Verified end-to-end against Tencent with real speech (edge-tts -> 16 kHz PCM
over the live WebSocket) and by `make all`.
….0.2b3

chore: sync main into develop after 1.0.2b3
Align EN/CN README with shipped features and current install/channel
docs; fix GitHub heading anchors and drop obsolete extras examples.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Dark-theme surfaces that use var(--fn-bg-container, #fff) — most
visibly the knowledge-base Markdown preview body, toolbar and outline
panel — resolved to a white background while text stayed light, leaving
preview content invisible. Define the variable as #ffffff (light) and
#141414 (dark), and point the Admin/Users badge tints that borrowed the
name for a translucent fill at --fn-bg-tertiary so their look is
unchanged.

Fixes #1215
Store group order, item placement, and hidden entries on the user so navigation can be rearranged without showing items the account cannot access.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…tter (#1222)

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…#1220)

Collapse the dual user_role_id / role model so users.role and invites.role
hold the template public id, block deleting roles still in use, and keep
the chat composer visible under mobile browser visualViewport.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Link labels now match the product names while the repository URLs stay unchanged.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Marketplace plugins written before the octop_harness rename fail to install
and enable with ModuleNotFoundError. Alias that import onto the installed package.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
#1255)

POST /workspace/archive writes archive entries straight into the
workspace; _safe_zip_name only blocks traversal, so a zip could plant
files under _builtin_skills/ and .octop/_builtin_skills/. That prefix is
owned by Octop: DELETE and POST /workspace/move refuse it via
_assert_workspace_mutable and sync_octop_builtin_skills only prunes its
own retired names, so planted skills both load as kind=builtin and stay
irremovable through the API. This restores the #1105 guard (filter plus
warnings reporting) together with its regression test, which a later
"restore prior behaviors" commit had removed along with the filter.

Fixes #1254

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
About 30 --fn-* custom properties were referenced across dashboard
styles but never defined in theme-vars.css. References without a
fallback resolved to invalid-at-computed-value-time (transparent
surfaces, e.g. the Connectors page sunken wells), and fallbacks were
often light-only literals that broke the dark theme. Define the
missing tokens for both themes, aligned with their nearest existing
design-system values, and fix the misspelled --text-secondary /
--border-color references that could never resolve.

Fixes #1239
* feat(ollama): allow configuring local model download directory

Users who move Ollama models off the default path could list custom models
but Octop treated them as not downloaded. Persist an OLLAMA_MODELS directory,
scan manifests for downloaded names, and pass the path when starting serve.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ollama): keep service running when saving models dir

Saving the download directory no longer sends enabled=false, so a running
Ollama daemon is not stopped. Disk-scanned models report size 0 so the UI
does not show the manifest file size as the model size.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…1311)

Give the bundled assistant a product overview plus links to octop.cloud and the docs, and keep CLI setup for changes on the running instance.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(ollama): list local models even when Octop service is off

Windows users often run Ollama themselves. Octop only queried the daemon
when its managed-service toggle was on, so already-pulled models showed
as not downloaded. List from a reachable daemon without starting serve,
and mark IDs from fetch-models as downloaded.

* docs: note Ollama downloaded-model detection when the service toggle is off
`_stream_frames` (the mobile JPEG fallback) retried forever: on `TimeoutError`
it logged and `continue`d, and on `captured is None` it just `continue`d. A
phone unplugged mid-stream, or a dead adb bridge, makes every capture fail at
once, so the loop spun at the frame interval indefinitely — a warning per
iteration, a log flood, and a canvas frozen on the last frame with nothing ever
sent to explain it.

The desktop sibling `desktop/stream.py::_stream_loop` already counts consecutive
misses, warns once, and after 30 sends an error frame and breaks. This mirrors
that: timeouts and `None` both count as misses, the first one is logged once,
and at `_CAPTURE_MISS_LIMIT` (30) the stream sends the
`{"type": "error", "message": ...}` frame the rest of this router already uses
and returns. A successful frame resets the streak, so an occasional dropped
frame still does not tear the stream down.

Folding the timeout branch into the shared counter also removes the
per-iteration warning flood.
…rs (#1307)

`octop.cli.support.state` keeps a private reader/writer for
`~/.octop/cli_state.json` and both halves are looser than the rest of the tree.

`load()` parses with a bare `json.loads(path.read_text(...))`, so an
unparseable file raises a `json.JSONDecodeError` traceback. This file is read by
nearly every CLI command (`config`, `agent`, `db`, `acting`, `ctx`, the REPL),
so one damaged file aborts all of them at once.

`save()` writes with `path.write_text(...)` — truncate, then write. A crash or a
full disk mid-write leaves exactly the truncated file that `load()` then
refuses, with no warning and no `ensure_root()` in between.

`infra/utils/json_file.py` exists for this; its docstring is explicit that a
torn write "creates the same precondition" as a hand-edit slip and that callers
"must not fall back to an empty dict". Four call sites already route through it
(`cli/commands/run.py`, `agents/plugins/manager.py`, `agents/plugins/seed.py`,
`setup/service.py`).

`load()` now reads via `read_json_object` and translates `JsonFileCorruptError`
into `corrupt_config_error(...)` (`CONFIG_FILE_CORRUPT`) — the same policy as
every other `OCTOP_HOME` JSON reader. `save()` writes via `write_json_atomic`
(mkstemp + `os.replace`) so a reader never observes a partial file. A non-object
payload such as `[1, 2]` also raises the typed error now, instead of a stray
`dict.update` `TypeError`.
OCTOP_LANGFUSE_ENABLED is set by .env.example and forwarded by docker-compose.yml, but nothing reads it: Octop keeps the switch in the settings table (observability_langfuse_enabled) and the langfuse client honours LANGFUSE_TRACING_ENABLED. Add a test that fails while a shipped Langfuse variable has no reader.
media/preview streams user-controlled bytes (chat attachments, tool
outputs) inline on the dashboard origin. image/svg+xml is previewable,
so a navigated preview URL executed as a live document on the app
origin and could run attacker script — the endpoint also accepts the
access_token query parameter for media loads. Serve previews with
Content-Security-Policy: sandbox and X-Content-Type-Options: nosniff;
image/video previews keep working.

Fixes #1242

Co-authored-by: jubaoliang <jubaoliang@gmail.com>
* feat: add Octop↔Octop Bridge for remote agent access

Enable admin-managed peer connections with WS tunnel, path allowlist,
hello_ack handshake, SSRF guards, and Chat shadow-agent hydrate so
operators can probe and use remote agents from the local dashboard.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: serialize bridge turn chunks and flag remote experts

LangChain HumanMessage objects in peer turn.chunk frames crashed
json.dumps; add a bridge JSON default. Show a 远端/Remote badge on
shadow experts in the picker and sidebar lists.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: bridge avatars, named remote badges, and auto-reconnect

Keep bundled / CDN icon URLs for shadow experts and only proxy uploaded
avatars. Show the bridge display name on remote badges. Add a default-on
auto-reconnect switch that backs off on disconnect and disables itself
after repeated failures, recording the reason in last_error.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add card/table views for bridge connections

Match the storage page card layout and add a Segmented toggle so
admins can switch between a card grid and a table of remote links.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: render bridge expert icons correctly on the chat page

Encode bridge agent ids in avatar URLs the same way as chat WS paths,
fall back to Lucide when the image fails, size portraits to fill the
sidebar avatar, and refresh AgentContext when icon_url changes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: tunnel peer models and knowledge bases for bridge chat

Allow read-only GET of peer providers/resolved, active-model, and
knowledge-bases (plus capability) through the Bridge tunnel, expose
local bridge connection routes, and point the chat composer at those
sources when talking to a shadow expert. Hide local connectors for
remote sessions. Also allow agent subagents paths on the tunnel.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: scope bridge chat expert picker to peer agents

When the active expert is a Bridge shadow, the composer expert picker
and @-mentions only list peers on that connection (not local experts).
Encode bridge agent ids for skills and subagents list/install paths.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: hint remote chat pickers to manage settings on the peer

In Bridge sessions, replace local “manage …” footers for models,
skills, knowledge bases, experts, and subagents with a muted
“edit on remote” tip and a toast instead of navigating to local admin.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: polish bridge remote UX and tunnel agent status

Allowlist GET …/status so hubs can read real peer harness state (peers need
this build). Keep history-migration hub-local, skip noisy bridge polls in the
UI, move Bridge under Settings, and mark remote experts with cable + name.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: tunnel remote expert catalogs and polish bridge connection UX

Hub can edit peer subagents, memory, channels, tools, plugins, and MBTI
through the tunnel; surfaces that cannot hop show an in-drawer hint. Peer
must run this build so the new allowlist takes effect.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: group cloud-collab experts and unify header-tunneled composer APIs

Keep the selected remote expert on disconnect, gate peer-only surfaces in the UI, and stop forking composer/task requests through dedicated bridge endpoints.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add search to the Experts toolbar

Filter my experts, teams, and the library from the existing button row so long catalogs stay scannable.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: keep inbound cloud-collab cards peer-owned

Inbound links can only edit name/icon/notes; disconnect, delete, and auto-reconnect stay disabled. Peer offline shows as 离线, peer delete removes the card, and the default inbound name is the short connection id.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: polish inbound cloud-collab UX and unify 对端 copy

Peer-initiated links can only edit display info and cannot redial;
offline cards may be deleted. Local-only pages hint when a peer
expert is selected.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: map peer team speakers onto Bridge shadows

Peer team chats stamped local member ids, so the hub fell back to the host
avatar. Rewrite roster and stream speakers onto bridge:{cid}:{id} shadows,
and keep the local team picker from mixing in peer experts.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: stop rewriting team thread ids in the Bridge tunnel

Member job threads are {room}~{agent}. A blanket JSON replace of the peer
agent id turned those into hub shadows, so history 404ed on the peer.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: rewrite peer media URLs and keep bridge turn composer fields

Live send_file_to_user frames still pointed at peer-local /api/agents/{id}.
Map every teammate id in tunneled JSON, and copy the full dashboard turn
body so knowledge bases and HITL policy are not dropped on the peer.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: rename bridge probe tests to avoid pytest module clash

tests/unit/bridge/test_probe.py collided with tests/unit/mobile/test_probe.py
during collection (same basename).

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: collapse extra experts on Bridge cards and fill avatars

Show at most four experts on a connection card, with the rest behind
expand. Portraits fill the 40px ring without a tinted background.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: lulinzhi37-alt <lulinzhi37@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
….0.2b5

chore: sync main into develop after 1.0.2b5
* fix(update): load prerelease changelog from versioned PyPI JSON

Co-authored-by: Cursor <cursoragent@cursor.com>

* ci(fnos): retry and cache fnpack downloads

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
skill_packages gains copy_policy (snapshot default / lock / deny), set by the
creator or admin via PATCH. deny rejects the workspace copy endpoint with
SKILL_PACKAGE_COPY_DENIED and disables the dashboard button (can_copy). lock
stamps each copied SKILL.md with origin/locked frontmatter; the existing
package-only write guard now also rejects writes to stamped copies for
everyone but the origin package creator (admins keep an escape hatch for
orphaned copies). Copy attempts are audited. Agent-tool-level write
protection for locked copies needs harness support, tracked separately.

Co-authored-by: vicfei <4058491+vicfei@users.noreply.github.com>
httpx 0.28 treats 192.168.0.0/16 as the exact IP 192.168.0.0, so LAN
hosts hit HTTP_PROXY. Also split Windows semicolon lists, parse IPv6
CIDR, and bypass loopback under macOS/Windows system proxies.

Fixes #1347

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: jubaoliang <jubaoliang@gmail.com>
技术报告 TECH-REPORT-qq-mail-header-bug.md 的复现与修复:

- 根因:email.message_from_bytes 默认 compat32 策略下,邮件头含裸
  非 ASCII 字节时 get() 返回 email.header.Header 对象,直接传入
  json.dumps 抛 TypeError: Object of type Header is not JSON
  serializable,真实邮箱场景 100% 必现
- 修复(报告方案 B):新增 _parse_message() 统一以
  email.policy.default 解析,_safe_header() 强制 str();所有面向
  JSON 的头字段(From/Subject/Date)走统一封装
- 附带收益:规范 MIME 编码头(=?utf-8?q?...?=)现在解码为可读
  中文,而非原始编码串
- _extract_body:声明字符集无已装 codec(如 unknown-8bit/伪造名)
  时回退 UTF-8(errors=replace 只挡字节错误,挡不住 LookupError)
- 新增 13 项回归测试:裸 UTF-8 头、MIME 编码头、破损编码字、
  unknown charset 回退、multipart、纯 ASCII 不回归;已在未修复
  代码上验证测试会失败(TypeError)

Co-authored-by: jubaoliang <jubaoliang@gmail.com>
extra stays empty on darwin-arm64, linux-* and windows-amd64 because
write_green_overrides only emits an override file for windows-arm64 and
darwin-amd64. "${extra[@]}" under set -u aborts on the macOS default
/bin/bash before uv ever runs, so `make -f desktop/portable/Makefile
wheels` fails on the host platform. Use the guarded expansion form the
repo already applies in scripts/install.sh.
jubaoliang and others added 27 commits October 9, 2026 09:01
Telegram allowed_user_ids only filters messages once this gateway release is installed.

Co-authored-by: Cursor <cursoragent@cursor.com>
feat(dashboard): Telegram allowed user IDs field
Keep the thread when checkpoint cleanup fails, sweep orphan checkpoints from the CLI, and remove an owned agent's workspace when its user is deleted. Skip FnOS process-tree tests where /proc is absent.

Co-authored-by: Cursor <cursoragent@cursor.com>
pytest tmp dirs on Windows are drive paths, which workspace_plan_path leaves unchanged, so the container-root assertions never matched.

Co-authored-by: Cursor <cursoragent@cursor.com>
…1874)

Desktop startup was saving a hardcoded bind address and could launch a second Octop when the preferred port was busy. Chat uploads dropped folders, shared peer experts are no longer offered for delete, and the state backend stays available only for experts that already use it.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
feat(voice): add Tencent Cloud realtime (streaming) speech recognition
Deleting a conversation left the SQLite file the same size. Compact it immediately, or after the expert is idle.

Co-authored-by: Cursor <cursoragent@cursor.com>
pytest tmp dirs on Windows are drive paths, which workspace_plan_path leaves unchanged, so the container-root assertions never matched.

Co-authored-by: Cursor <cursoragent@cursor.com>
A hung docker info raised TimeoutExpired during collection and failed the Windows job instead of skipping the live image tests. Also drop an unused mobile overflow state that failed the dashboard typecheck.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rsation-data

Fix/delete persisted conversation data
feat(memory): configurable distillation call limits (budget, timeouts)
* feat(connectors): add Obsidian CLI as a built-in connector

Let agents search and edit one vault through the official desktop CLI, and show the official install guide in the connector drawer.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(connectors): register Obsidian CLI per OS and satisfy ruff format

Linux must copy the bundled binary into ~/.local/bin, macOS keeps a symlink, and Windows leaves Obsidian.com beside the app so PATH registration still works.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(tests): expect relative plan paths on Windows drive workspaces

write_file rejects drive-letter paths, so Windows CI must keep plans/foo.md relative instead of asserting a rewritten absolute path.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(setup): keep the first cold-install timeout exactly on budget

Subtracting a freshly stored monotonic deadline can yield 900.0000000000001, which failed the portable pip upgrade test on Linux.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(connectors): use the official Obsidian logo

The connector card was showing a placeholder gem. Replace it with the mark from obsidian.md and match the accent to that purple.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Move connector metadata into catalog.json with English and Chinese copy so the dashboard follows Accept-Language, and keep docs/connectors.md in sync with that catalog.
Member speech waits until the host turn is idle, a tool approval pauses that member instead of ending the job, and the live reply stays visible after the user approves. The same change accepts custom role ids and shows long model names in the composer.
Windows ARM64 portable builds omit psycopg-binary, so a top-level psycopg
import crashed startup even when PostgreSQL was not in use.
Copy, read-aloud, and regenerate sit under each reply instead of appearing only on hover.
…1941)

Keep chat always available, and let experts, automation, and token usage be granted in the same groups and order as the operator's sidebar.
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep develop's tree where 1.0.2b6 was squash-merged, and bring over the Discord invite plus the README-only pre-commit skip.

Co-authored-by: Cursor <cursoragent@cursor.com>
binary_path = str(credentials.get("binary_path") or "").strip()
if binary_path:
_reject_obsidian_control_chars("binary_path", binary_path)
if not os.path.isfile(binary_path):
@jubaoliang
jubaoliang merged commit 664c1fb into main Oct 12, 2026
11 of 12 checks passed
@jubaoliang
jubaoliang deleted the release/1.0.2b7 branch October 12, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.