feat: multimodal image clients, workspaces, Tushare skill, test fixes

- Replace monolithic image_message_client with HTTP/OCR/legacy modules; tighten OpenAI transport + tool schemas for multimodal downgrade to OCR specialist path.
- Add image/stock workspace prompts (META/SOUL/ROLE_SYSTEM); register experts; tweak specialist agent/direct loop/query_image_attachment.
- Add bundled runtime/skills/tushare-finance (references, api_client, SKILL metadata).
- Document OCR-related env vars; admin chat tweaks; README; weixin_install Ensure-OfficialPluginRuntimeDeps helper.
- Tests: multimodal downgrade + OCR coverage, strict tool pairing in attachment replay guard, workspace contract skips _internal/_system dirs, router/trace/prompt guards.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-05-10 04:41:42 +08:00
parent 6307ade480
commit d1bcc4debe
266 changed files with 57024 additions and 839 deletions

View file

@ -275,6 +275,11 @@ def build_ops_agent(
)
# `default_registry` treats empty allow_tags + empty allow_tools as "no filter". Use an impossible
# tool name so the image specialist gets an empty tool surface (vision-only turns).
_IMAGE_SPECIALIST_TOOL_ALLOWLIST: tuple[str, ...] = ("__oclaw_image_specialist_no_tools__",)
def build_gateway_executor(
store: SqliteStore,
*,
@ -321,14 +326,17 @@ def build_gateway_executor(
path_policy_tenant_id=path_policy_tenant_id,
path_policy_user_id=path_policy_user_id,
)
tools = default_registry(
expert=expert_name_for_specialist(prof.name),
specialist=prof.name,
policy_session_id=policy_session_id,
path_policy_tenant_id=path_policy_tenant_id,
path_policy_user_id=path_policy_user_id,
store=store,
)
reg_kw: dict[str, Any] = {
"expert": expert_name_for_specialist(prof.name),
"specialist": prof.name,
"policy_session_id": policy_session_id,
"path_policy_tenant_id": path_policy_tenant_id,
"path_policy_user_id": path_policy_user_id,
"store": store,
}
if prof.name == "image":
reg_kw["allow_tools"] = list(_IMAGE_SPECIALIST_TOOL_ALLOWLIST)
tools = default_registry(**reg_kw)
return Agent(
store=store,
tools=tools,