* feat(skills): export selected skills and skill packages as ZIP
Workspace skills can be checked and downloaded together, and a skill package
exports in the same top-level folder layout the ZIP importer already accepts. (#260)
* feat(skills): let skill packages disable ZIP download
Creators and admins can turn off package export. Other users can still view
and mount the package, but cannot download it.
* feat(skills): default package download from the copy policy
* test(db): expect schema v21 after package download migration
---------
Co-authored-by: 薄生 <67680641+Bosheng0422@users.noreply.github.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>
* 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>
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>
Raw 018 SQL alters user_invites even when the table is missing on pre-invite backups. Route v18 through _ensure_user_role_schema and bump watermark assertions to 18.
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Apply the remaining working-tree changes: connectors empty-state layout,
plus the knowledge, HITL, captcha, workspace, user-cache, and SSRF
adjustments with matching tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
aglob("**/*") skips hidden paths, so downloads only packed a few root files.
Walk the local tree (and als/aglob fallbacks) so archives include the full workspace.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(chat): add Ask / Plan / Craft conversation modes
Keep mode sticky per thread, enforce Ask/Plan in harness-agent, and let Plan write a workspace plan before Craft executes it.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Update pyproject.toml
Peek only the first tar member for manifest flags and run list_backup_files
off the event loop so Backup/Restore stays responsive with multi-GB files.
Closes#674
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep partial tokens and stream errors in thread history after a stop or
provider failure, and drive cron empty-state cards from manifest
task_examples. Backup skips stale workspace paths instead of creating them.
Co-authored-by: Cursor <cursoragent@cursor.com>
Downloading a workspace zip (GET /agents/{id}/workspace/archive) could carry a
same-named file from the backend root_dir instead of the workspace's own file.
With virtual_mode and a scoped root_dir that contains the workspace, harness
BackendWorkspace probes {root_dir}/{rel} before {workspace_dir}/{rel}, so a
user's ~/AGENTS.md replaced the workspace copy while the entry name still read
AGENTS.md - invisible to zip checksums.
_read_entry_bytes() now reads host-mounted entries from workspace_dir, the base
present_path reports entries against. Routing through resolve_path() is not
enough: it re-maps system entries (skills/, agents/, sessions/, ...) under
system_files_path, so a workspace still keeping legacy root skills/ fell back to
the shadowed read. Entry names escaping the workspace are skipped rather than
packed, and remote / sandbox backends keep reading through the backend protocol
so stale local files cannot shadow remote objects.
Also stop replace imports from deleting state an archive can never carry: the
export glob does not match hidden paths, so .octop (sessions / skills / auth) was
removed by _clear_local_workspace() and could never be restored.
Tests: shadowing at the backend root, legacy root skills/, and replace import
preserving system state.
Add a `user_policies` table (migration v14) backing per-user `workspace_root_dir`
(restricts local backend roots) and `token_quota` (lifetime token limit). Token
quota is enforced in the agent runtime via TokenQuotaMiddleware. Extract slash
command parsing into infra/gateway/slash/parser.py and expose policies on the
users API. Frontend adds admin policy UI, storage/chat/experts restyle, and a
shared TabLabel / PageLoading component.
Migrate immediately after overlay and repair folded v13 connectors schema so existing backups stay usable. Refuse newer schema versions with BACKUP_SCHEMA_INCOMPATIBLE. Also drop the knowledge-bases Beta nav badge.
Co-authored-by: Cursor <cursoragent@cursor.com>
Default backups skip chat history so archives stay smaller. Restore only
replaces packed config, workspaces, skill packages, plugins, and knowledge
files, and preserves live chats when they were not included.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: inject Admin env into running agents and sum turn token usage
Running shells and Docker sandboxes now pick up ~/.octop/env (and workspace .env) without a full reload; MCP stdio no longer inherits the entire host environment. Usage accounting adds every model call in the turn, and workspace I/O survives the harness rebuild window.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: schema v7 identity, expert profiles, chat artifacts, and KB citations (#346)
* feat: schema v7 identity, expert profiles, chat artifacts, and KB citations
Fold integer PKs plus public string ids, document folders, agent profile columns, and thread artifacts into v7 SQL (not only Python helpers). Experts get workspace avatars and a single welcome_message; chat keeps file changes across sessions, shows knowledge citations, and can drag workspace files.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore: drop cron JobDrawer UX from the schema v7 PR
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: prefer tool-arg paths when recording chat artifacts
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
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>
* feat: implement automatic backup feature with configuration and scheduling
- Added new API endpoints for managing automatic backup settings.
- Introduced a new BackupConfig class to handle automatic backup configurations.
- Implemented functionality to create, run, and manage automatic backups.
- Updated the dashboard to include UI elements for configuring automatic backups.
- Enhanced CLI commands to support automatic backup operations.
- Added localization strings for new backup features in English and Chinese.
- Updated documentation to reflect changes in backup commands and configurations.
- Bumped octop version to 0.9.23 to include these features.
* refactor: clean up backup restore UI and localization strings
- Removed unused localization strings related to backup types in English and Chinese.
- Simplified the BackupRestore component by eliminating the auto backup name check and associated UI elements.
- Updated styles by removing the kindBadge class, streamlining the backup card display.
* refactor: improve error messages and logging in config.py
- Simplified error messages for backup configuration validation.
- Updated logging to avoid exposing sensitive environment variable values.
- Enhanced clarity in warning messages related to integer and boolean coercion.
* refactor: update permission checks for automatic backup endpoints
- Replaced `current_admin` dependency with `require_permission("backup")` for the automatic backup settings endpoints.
- Ensured that only users with the appropriate permissions can access and modify automatic backup configurations.
* fix: Update schema when restoring octop/lightclaw backups
* feat: enhance error logging for OctopError in exception handlers
* feat: enhance backup restore process to include IM channel reloading
* feat: extend backup restore functionality to include cron job reloading
* feat: add JWT secret capture and restore functionality during backup processes
* fix: update Chinese translation for presetProviders term in zh.json
* feat: implement Unix crontab semantics for weekday handling in cron triggers
Added functionality to translate numeric Unix weekdays to names and updated the cron trigger builder to support Unix-style expressions. Enhanced unit tests to validate the new behavior for various weekday formats and ranges.
* feat: enhance backup restore functionality with ownership remapping
- Updated `restore_backup_file` to accept a `user` parameter for identifying the restoring admin.
- Added `owner_user_id` option in CLI for specifying ownership during LightClaw migration archives.
- Implemented user ownership remapping in the database during system backup restoration.
- Enhanced user handling in backup processes to ensure proper ownership assignment and data integrity.
- Updated tests to validate new ownership remapping behavior during migration restores.
* refactor: streamline SQL query formatting in backup test case
- Simplified the SQL query execution in the `_make_migration_backup` function by removing unnecessary line breaks for improved readability.
* feat: add global skill packages with local ZIP import and personalization layout
Introduce instance-level skill packages (DB + disk), mount them on agents, and
support SkillHub/URL/ZIP import. Merge related schema into migration v2 and
cover the zip import path with frontend and API tests.
* fix: hot-sync skill package mounts and dark-mode package cards
Avoid full agent rebuild when mounting packages so Experts UI no longer
hits AGENT_NOT_FOUND; accept workspace-scoped local backends on Windows;
use theme token for package list card background.
* feat: PostgreSQL dual-backend for control plane
Add SqlitePool|PostgresPool behind DatabasePool, parallel PG migrations,
setup wizard defer/bind, pg_dump backups with cross-engine refusal, and
memory defaulting to the control-plane DSN when using PostgreSQL.
* fix(chat): delete checkpoint data when a conversation thread is deleted
Deleting a thread only removed Octop's own metadata row; the actual
message history stayed in the agent's LangGraph checkpointer forever,
making "delete conversation" cosmetic. Also confirm before deleting a
session in the UI, since the action is now genuinely destructive.
Checkpoint deletion runs before the metadata row is removed: if it
fails outright (not just "nothing to delete"), the thread stays
visible/retryable instead of orphaning data with no remaining handle.
* fix(memory): stop suggesting pg_dump for portable export of postgres memory
The 501 hint told users to "use pg_dump on the memory schema instead".
That advice predates the shared-table layout: memory for ALL agents now
lives in one harness_memory schema isolated by a namespace column, so a
schema-level pg_dump would export every agent's memory, not just the
requester's — a cross-agent data exposure if followed on a multi-user
install.
Document the two migration models where the refusal is implemented:
sqlite memory is per-agent files moved via pack/adopt; postgres memory
is shared, and the supported way to use it from another host (e.g.
OpenClaw's harness-memory bridge with --backend postgres) is pointing
that host at the same DSN and namespace — shared, not migrated.
* fix(test): update test_slash_compact to renamed SqlitePool
The /compact test module still imported the pre-rename DBPool, which
broke collection (ImportError) on this branch after develop's compact
feature was merged in — DBPool was renamed to SqlitePool (the concrete
class) with DatabasePool now the Protocol.
DBPool -> SqlitePool at all three sites: the import, the _agent_manager
type hint, and the fixture's DBPool(path) instantiation. SqlitePool is
the byte-for-byte successor of DBPool and matches how every other test
constructs a pool; DatabasePool cannot be used at the instantiation
site (a Protocol is not instantiable).
pytest -m live now collects cleanly (31 selected, 0 errors);
test_slash_compact 3 tests pass.
* fix(test): make memory_backend db_path assertion separator-agnostic
test_explicit_sqlite_overrides_postgresql_control_plane compared the
resolved db_path against the literal "/tmp/ws/memory.sqlite", which the
code never produces on Windows: it builds the path with pathlib, so the
Windows CI job got "\tmp\ws\memory.sqlite" and the string equality failed.
Compare as Path objects so the assertion holds on every OS separator,
matching the .as_posix() discipline already used in test_config.py.
Restore syncs providers and reloads agents in-process; rename cron_timezone to
default_timezone with GET /api/settings/timezone; reload only impacted agents
after provider changes; align dashboard timestamps to the server timezone.
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Normalize backslashes in extract_workspace_rel, allow Windows paths in
channel subprocess args, and catch PermissionError when importing external
files; skip or adjust POSIX-specific tests and hardcoded /tmp paths.
Co-authored-by: Cursor <cursoragent@cursor.com>