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>
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>
CodeQL treats a "pypi.org" substring as an incomplete URL check, and SHA-256 of the OAuth verifier as password hashing. Label only the parsed hostname, and verify S256 with the RFC 7636 vector.
Co-authored-by: Cursor <cursoragent@cursor.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>
* feat(users): add role templates, invite snapshots, and profile avatars
Roles fill defaults for create, edit, and invite without rewriting existing users. Account type stays independent of the selected role.
Co-authored-by: Cursor <cursoragent@cursor.com>
* style: format role and invite repositories for ruff
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: narrow invite role name before lookup
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
GET /api/admin/audit-log forwarded an unvalidated `limit` into `LIMIT ?`,
the same gap #1018 closed for the thread list. SQLite reads a negative LIMIT
as "no limit", so ?limit=-1 returned the whole audit log in one response and
?limit=0 returned an empty page. Bound it with Query(ge=1, le=500), the
largest page the Settings -> Security audit panel offers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Improve team streaming visibility (live speakers, generating footer, IM
dispatch/wrap-up), relax ACP tool enable under sandbox while locking
runners, strip channel think tags per show_thinking, and clarify provider
CLI usage in the assistant skill. Includes self-update/UpdateConfig WIP
from the same working tree.
Co-authored-by: Cursor <cursoragent@cursor.com>
Plugin tools returning large octop_ui payloads (e.g. a bangumi season
with 1200+ episodes, ~228KB JSON) blew up the LLM context because the
same tool-result string served both the model and the UI.
Add OctopUiOffloadMiddleware at the innermost agent middleware layer:
when a ToolMessage content is a >=4000-char string carrying a non-empty
octop_ui renderer and data (and no file:// media refs), the payload is
moved in-place to ToolMessage.artifact and the model-visible content is
replaced by a compact result that keeps title/summary. Tool id,
tool_call_id, name and status are preserved.
Frontend restores rendering from artifact: ToolCallData.artifact,
closeToolCall extraction, history replay path, and resolvePluginUiData
prefers explicit data over data_ref. serialize.py surfaces artifact on
tool_result blocks; history recorder backfills artifact as fallback.
Spec: docs/octop-ui-payload-offload.md
Co-authored-by: jubaoliang <jubaoliang@gmail.com>
* refactor(agents): group thin modules into domain subpackages
Move plugin helpers, memory, settings stores, persona/MBTI, and avatar
bootstrap code under plugins/, memory/, settings/, persona/, and experts/
while keeping top-level import shims so existing call sites stay stable.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(agents): drop compatibility shims after subpackage move
Call sites already import the new package paths; remove the top-level
re-export stubs so the agents tree only shows real modules.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(agents): move thread helpers into threads/
Group artifact shaping, fork, and context breakdown under
agents/threads so top-level only keeps lifecycle/config adapters.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(agents): fold profile/runtime/tool catalog into settings/
Move the remaining config-surface helpers under settings/ and drop the
skill_package id-list aliases in favor of dump/parse_id_list.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(agents): group workspace_dir and execute_env under workspace/
Keep path resolution and harness execute-env injection together as one
workspace package; leave only manager and conversation_mode at top level.
Co-authored-by: Cursor <cursoragent@cursor.com>
* refactor(infra): fix history/SkillHub ownership and drop thin providers/
Move history_projection under history/, share SkillHub host/limits and
route expert skill downloads through skills.skillhub_market, and fold
Codex OAuth helpers into agents.providers to remove the tiny providers/
package.
Co-authored-by: Cursor <cursoragent@cursor.com>
* docs: sync AGENTS.md and guides with infra/agents layout
Reflect subpackages (settings/workspace/threads/persona, history/, skills/)
and drop stale runtime.py / mbti_profiles paths after the agents reorg.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Persist member file refs and tool history on team walls, open docks by producer agent, and inject truncated member answers into host follow-up so wrap-up can follow their advice.
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>
* feat: admin user batch policies, token UX, default FS root, and media push hardening
Add admin user batch enable/disable/delete and resource policies (token quota,
max experts), improve token inputs with K/M presets, default unrestricted
local backends to host filesystem root, and tighten gateway tool-media push
with clearer path-outside-root stream errors.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(workspace): treat Windows drive roots as host-root sentinels
Default FS-root backends use C:/ on Windows; treating that as a scoped
jail put workspaces under the drive root and broke bootstrap/invite tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
`_map_knowledge_error` matched the literal "at most 100" to classify the
document-cap error, but `KnowledgeRepo.create_document` interpolates the
per-base `max_documents`, which has been user-configurable since 90b8ca25.
Any cap other than the default 100 therefore fell through to the base-cap
branch (the repo message also contains "knowledge bases") and the user was
told they had created too many knowledge bases.
Classify both caps by the stable wording of their own messages instead of
a magic number, and drive the mapping from the real repo in a regression
test so the wording coupling cannot silently rot again.
Co-authored-by: sxh <sunxianhong@ncti-gba.cn>
Co-authored-by: sxh313 <sxh313@users.noreply.github.com>
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
files.document_path() keys the stored bytes on {doc_id}{suffix}, so a rename
that drops or swaps the extension moves the row off its own file: the
original-file route 404s, delete_document() unlinks the wrong key with
missing_ok=True and leaks the bytes, and re-saving an md/txt document
writes a second file. rename_document() now restores the document suffix
the way create_text_document() already normalises it; folders and names that
already carry the right extension are untouched.
Closes#1107
Co-authored-by: sxh313 <250161920+sxh313@users.noreply.github.com>
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
GET /api/agents/{agent_id}/threads forwarded an unvalidated `limit` into
`LIMIT ?`. SQLite reads a negative LIMIT as "no limit", so ?limit=-1 returned the
caller's whole thread collection and ?limit=0 returned an empty page; the same
statement reaches psycopg unchanged, where a negative LIMIT is an error. Bound it
with Query(ge=1, le=HISTORY_MAX_LIMIT), the ceiling this module already uses for
message pages.
Co-authored-by: sxh <sunxianhong@ncti-gba.cn>
Co-authored-by: sxh313 <sxh313@users.noreply.github.com>
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
wiki_summary interpolates its `lang` argument straight into the URL host
(`https://{lang}.wikipedia.org/...`), and the tool schema the harness
builds from the signature gives the model an unconstrained `str`. A value
like "evil.com#" makes httpx resolve host=evil.com, and "localhost:8443/"
reaches a loopback port -- the response's `extract` is then echoed back
into the chat, so it is also a read-back channel.
Validate `lang` as a bare subdomain label and return the usual error card
otherwise; real codes (zh, en, zh-classical, simple, nb) are unaffected.
* fix(acp): block outbound runners under directory sandbox
Host-spawned acp_runner would bypass a scoped root_dir jail; gate the
per-agent tool in API/runtime and gray the ACP UI while inbound octop acp stays available.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(acp): allow workspace-scoped local roots for outbound runners
Windows rewrites host `/` to the agent workspace; treating that as a
directory sandbox made ACP enable/round-trip fail on win32 CI.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(update): checkpoint SQLite WAL before desktop process restart
In desktop/portable mode, POST /api/update/restart replaced the process
image with os.execv without flushing the SQLite WAL. Because SqlitePool
uses WAL mode with a large autocheckpoint window, all writes since the
last checkpoint were lost on restart (#800).
Change _restart_desktop_process to accept the OctopServer, and before
execv run PRAGMA wal_checkpoint(TRUNCATE) and close the pool when the
database is SQLite. The restart endpoint now injects the server via
Depends(get_server).
Fixes#800.
* style(tests): format desktop restart regression cases
`fresh_thread` deliveries reset the session to a brand-new thread before the
run, but the history projection only happened after the response passed the
"visible reply" checks. A run that raised — a failing tool call, a HITL
request, an empty reply — therefore left that thread with zero rows, so
opening the conversation after 立即执行 showed a blank chat.
Project in a finally, the same way the interactive processor persists an
incomplete turn, so the prompt and whatever the model streamed before the
failure are recorded. Push and toast stay unchanged: nothing is delivered
until there is a visible reply.
Co-authored-by: sxh313 <sxh313@users.noreply.github.com>
* refactor: update layout styles to support safe area insets
Modified padding and margin properties across various components to utilize environment variables for safe area insets, ensuring better compatibility with devices that have notches or rounded corners. This includes updates to index.html, offline.html, and several layout components to enhance the overall responsiveness and user experience.
* fix: update keyboard offset handling and layout styles
Refactored keyboard offset calculations in the useKeyboardOffset hook to improve handling of soft keyboard visibility. Adjusted layout styles across various components to utilize the largest viewport height (lvh) for better compatibility with devices featuring home indicators. This includes updates to padding, height properties, and background colors to enhance the user experience in chat and main layout components.
* refactor: update chat layout styles for improved responsiveness
Adjusted padding, width, and margin properties across various chat components to utilize environment variables for better responsiveness. This includes updates to the chat input, message list, and welcome components, ensuring consistent spacing and alignment across different screen sizes. Enhanced mobile styles to accommodate varying avatar sizes and maintain layout integrity.
* refactor: enhance chat component styles for consistency and responsiveness
Updated layout styles in chat components to ensure consistent spacing and alignment. Adjusted margin and gap properties to utilize environment variables, improving responsiveness across different screen sizes. Enhanced mobile styles for better alignment of avatars and message bubbles, ensuring a cohesive user experience.
* feat: implement theme management in index.html and update logo assets
Added a script to manage dashboard theme based on user preferences and system settings, ensuring the correct theme is applied before the first paint. Updated CSS to utilize data attributes for theme styling. Replaced existing logo assets with new vertical and horizontal versions for both light and dark themes, and updated references throughout the application. Removed outdated logo files to streamline asset management.
* refactor: update layout styles to improve safe area handling
Enhanced layout components by adjusting padding and margin properties to better utilize environment variables for safe area insets. This includes updates to the PageShell, Sidebar, MainLayout, Login, and Setup components, ensuring improved responsiveness and compatibility with devices featuring notches or rounded corners.
* feat(self_update): implement stashing and restoring of console scripts on Windows during upgrades
Added functionality to rename console scripts before an upgrade to avoid file lock issues on Windows. Introduced methods to stash, restore, and discard stashed scripts, ensuring a smooth upgrade process. Enhanced tests to cover these new behaviors.
* test(self_update): enhance test for stashing console scripts on non-Windows platforms
Updated the test for stashing console scripts to include a monkeypatch for simulating a non-Windows environment. This ensures the test accurately reflects behavior when the system is not Windows, improving test coverage for the self-update functionality.
* feat(provider): add name parameter to provider model interfaces and update session header logic
- Introduced a `name` parameter in `FetchProviderModelsParams` and related interfaces to enhance provider identification.
- Updated `fetchProviderModels` and modal components to utilize the new `name` parameter.
- Enhanced session header management in `opencode_session` to support bundled presets and custom providers.
- Adjusted tests to validate the new functionality and ensure proper session header injection for OpenCode presets.
* feat(deps): add mise.toml for local toolchain configuration and update orcakit-harness-agent to version 1.0.14
- Introduced a new mise.toml file to specify local toolchain versions for Python, Node, NPM, and UV.
- Updated the orcakit-harness-agent dependency version from 1.0.13 to 1.0.14 in pyproject.toml and uv.lock to ensure compatibility with the latest features and fixes.
* refactor(probe): streamline session header construction in probe.py
- Consolidated the session header construction for both `build_probe_chat_model` and `fetch_openai_compatible_models` functions into single-line calls for improved readability and maintainability.
Dashboard turns are queued per thread by the channel debounce lock, but
POST /chat/hitl/resume (and cron delivery) drive the same LangGraph
checkpoint without that lock. A resume racing an in-flight turn on the
same thread runs two concurrent harness executions whose checkpoint
writes interleave.
Acquire a per-(agent_id, thread_id) lock in AgentManager.stream and
AgentManager.resume_hitl — the single chokepoint every driver (dashboard,
IM, cron, team speaker, resume) routes through — so one thread's
checkpoint is only ever executed once at a time.
Co-authored-by: Dang Zitou <dengzitao888@163.com>
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep tool-approval bypass on the conversation (allow this tool / allow all)
without changing global security settings. Also let expert create carry
welcome quick prompts, and replace the login forgot-password tip with a
platform-specific CLI dialog.
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>
`collect_skill_context()` is the only caller that feeds the expert-manifest
generator prompt, and it parsed frontmatter with a private copy instead of
`octop.infra.utils.frontmatter.parse_frontmatter`. The private copy only
accepted a literal `---\n` first line and split each line on `:`.
Two shapes the shared parser handles therefore reached the prompt as a skill
with no description:
* `description: >-` folded blocks collapsed to the literal `'>-'` — the style
the bundled expert skills already use;
* a frontmatter fence behind an HTML comment was not recognised at all, and the
raw `---` fence stayed inside the excerpt.
6 of the 34 SKILL.md files in this tree parse differently between the two
parsers. The value is part of the generator prompt whose output is written back
into the cached expert manifest.json, so it is not cosmetic.
Snapshot avatars only cover custom uploads; persist bundled/remote icon_url in the manifest so published cards no longer fall back to the default Lucide icon.
Co-authored-by: Cursor <cursoragent@cursor.com>