- Added a new function to resolve local model snapshots for ONNX models, allowing complete offline loading.
- Updated the embedding model builder to utilize local snapshots when available, enhancing performance and reducing external dependencies.
- Added a unit test to verify the correct usage of local snapshots in the embedding process.
Co-authored-by: jubaoliang <jubaoliang@gmail.com>
* feat(experts): add Chinese-first business process expert
* docs(experts): consolidate business process attribution
* fix(experts): use supported business process icons
* fix(experts): complete business operations template integration
---------
Co-authored-by: jubaoliang <jubaoliang@gmail.com>
* feat: add a decision model for skills, tools, and teams
Let a small model shortlist what the chat model sees, including a local Laya install, and show the result on the turn.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: stop Laya through the POSIX-safe kill helper
Windows mypy does not see os.killpg, so process-group shutdown goes through the shared helper.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: compare the Laya install command without a POSIX path
Windows quotes the interpreter, so the displayed command is checked against the same argv list.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: show Laya readiness and wait for local decisions
Pin the local-CPU timeout fix and distinguish a ready Laya service from the service currently enabled for decisions.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: save each decision service and pick the default on the card
Clicking a card selects the default. Endpoint and key stay in the drawer
so Jev, Laya, and custom can be kept at once.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Importing a SkillHub skillset failed the whole package when one skill zip returned 404. Missing skills are skipped, and the import still fails when every skill is missing.
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.
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>
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>
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>
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>
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>
LangGraph evaluates interrupt `when` without the HITL thread ContextVar,
so persisted allow_all/allow_tools never skipped execute. Resolve thread
id from the runnable config and auto-resume leftover dashboard cards.
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: keep hung remote backends from blocking boot
S3/Postgres still use the old harness protocol and a per-call event loop, so one stuck List/Get can prevent HTTP from coming up. Run that I/O on a dedicated worker with timeouts, fail the one expert, and remap leftover unwritable workspace paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: avoid MagicMock workspace_dir in boot timeout test
Windows rejects the mock's stringified path when the fake harness mkdirs workspace_dir. Register the agent with an empty config instead.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Turn exhausted model-call retries into a recovery prompt with the real
cause, unwrap that wrapper in stream/UI classification, and keep
background inbox jobs marked failed when the installed harness can see
the [model_call_failed] mark.
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Session-end memory extraction calls the aux model non-streaming with a
fixed 120s light-tier read timeout. Nothing bounded what the model may
generate, so a reasoning-heavy local model chains past the timeout and
extraction degrades every time (TencentCloud/Octop#1360).
Expose the aux call options end to end:
- PUT/GET .../memory/extract-config gains extract_max_tokens,
extract_light_timeout_s, extract_heavy_timeout_s and
extract_extra_body (thinking controls for OpenAI-compatible
endpoints). Numeric 0 and {} reset to automatic; values are clamped
(timeouts >= 30s, tokens <= 65536).
- AgentManager._memory_extract_settings forwards them as
HarnessAgentConfig fields, gated on the installed harness knowing
them, so hot reload keeps working against older releases.
- Memory settings page gains a "提炼调用参数" card (token budget +
light/heavy wait limits) with copy distinguishing the wait limit from
the idle trigger; locales updated.
Requires the companion octop-harness (config/runtime/client plumbing)
and octop-memory (default extraction budget) changes; safe to merge
independently since values stay dormant until the harness supports
them.
`_fetch_ranking_json` was the only fetch in this module that hand-rolled
`urllib.request.urlopen` instead of calling `_http_request`.
`response.read()` therefore buffered the whole body with no size limit, where
`_http_request` checks `Content-Length` and then reads in `HTTP_READ_CHUNK`
slices up to `MAX_HTTP_BYTES` — the search path already passes a cap.
Its `except` ladder also stopped at `HTTPError`/`URLError`/`TimeoutError`, so a
mid-stream `ConnectionResetError` or an `http.client.HTTPException` escaped as a
raw exception. The endpoint that serves rankings (`routers/skills.py::
_hub_rankings`, used by `GET /skills/hub/rankings` and
`GET /agents/{id}/skills/hub/rankings`) only maps `SkillHubMarketError` to 502
and `SkillHubMarketTimeout` to 504, so an upstream hiccup became an opaque 500.
Rankings now go through `_http_request` like `_fetch_search_json` and
`_download_skillhub_package` already do. The 4 MiB JSON cap is named
`_MAX_JSON_BYTES` and used by both JSON fetches instead of repeating the literal.
`_Response` in `tests/unit/agents/test_skillhub_market.py` is the stub that
`test_fetch_ranking_json_uses_showcase_endpoint` hands back from `urlopen`, so it
has to accept the `size` argument `_http_request` passes. It now wraps the
payload in `io.BytesIO` and reads exactly like `_BytesResponse` next door, which
also makes the read loop stop on the first empty chunk instead of spinning.
The cache file is shared by every official account but the record only held
the token and its expiry, so the reuse check looked at time alone. After
switching WECHAT_APP_ID the publisher kept sending the previous account's
token for up to two hours: draft/add succeeded against the wrong account, the
command exited 0, and it still printed a successful publish. Tag each record
with the AppID it was issued for and refuse to reuse a token that was not
fetched for the account currently configured.
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>
* 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>
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>
`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>
* 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
* harness agent upgrade for cache polish
* format
* feat(memory): add coordinated memory slim commands and progress UI
* build(deps): require harness-memory 0.9.11
* fix(deps): restore harness-memory 0.9.11 floor after merge
* fix(agents): await async shutdown to drain SQLite workers
Use HarnessAgentManager.aclose() during shutdown so SQLite cleanup
finishes before the event loop closes, preventing background threads
from raising "Event loop is closed".
Add a real SQLite regression test and preserve the existing ordering
that waits for memory slimming to finish before closing agents.
Validation: make all — 3582 passed, 17 skipped.
Allow editing AGENTS.md, picking marketplace or other-expert skills, and
adding subagents in the create/edit drawers. Default is a blank AGENTS.md
template; marketplace or copy failures warn instead of aborting create.
Co-authored-by: Cursor <cursoragent@cursor.com>