diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f88569d..5e021bf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: - name: Install dependencies run: python -m pip install -r requirements.txt ruff mypy - name: Lint changed architecture modules - run: ruff check oclaw/interfaces oclaw/extensions oclaw/tests/test_oclaw_gateway_optimizations.py + run: ruff check oclaw/interfaces oclaw/runtime/extensions oclaw/tests/test_oclaw_gateway_optimizations.py - name: Type check critical gateway modules run: mypy --ignore-missing-imports oclaw/interfaces/gateway/context_builder.py oclaw/interfaces/ws/server_methods_bridge.py oclaw/interfaces/ws/turn_runner.py - name: Run tests diff --git a/.gitignore b/.gitignore index a904ae2d..a17f8428 100644 --- a/.gitignore +++ b/.gitignore @@ -42,21 +42,22 @@ data/**/*.log data/**/*.err.log platform/data/ scripts/.run/ +runtime/operations/scripts/.run/ desktop/node_modules/ desktop/dist/ desktop/runtime-data/ # 本地密钥(随仓库目录迁移时记得复制此文件;勿提交) -src/_local/google_oauth_client.json -src/_local/mcp_local.env +_local/google_oauth_client.json +_local/mcp_local.env # MCP admin export / 安装后自动备份(可重装 JSON,本机 path 与列表可能不同) -src/_local/mcp_registry_migrated.json -src/platform/data/*.sqlite -src/platform/data/*.sqlite-journal -src/platform/data/*.sqlite-shm -src/platform/data/*.sqlite-wal -src/platform/data/logs/ -src/platform/data/locks/ -src/platform/data/ops_runtime_state.json +_local/mcp_registry_migrated.json +platform/data/*.sqlite +platform/data/*.sqlite-journal +platform/data/*.sqlite-shm +platform/data/*.sqlite-wal +platform/data/logs/ +platform/data/locks/ +platform/data/ops_runtime_state.json # 历史 Vite 前端已移除;若本地仍有残留目录可忽略或手动删除 web/ @@ -70,5 +71,5 @@ oclaw/desktop/node_modules/ oclaw/desktop/dist/ oclaw/desktop/runtime-data/ oclaw/desktop/assets/oclaw.ico -vendor/openclaw-main.zip -vendor/openclaw/ +vendor/oclaw-main.zip +vendor/oclaw/ diff --git a/README.md b/README.md index a2a958eb..c015e336 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ -# OpenClaw Architecture Root +# Oclaw Architecture Root -This directory is the new architecture root for ongoing refactors. +This repository is fully consolidated under `oclaw/`. ## Layers -- `interfaces/`: transport adapters (HTTP/WS/gateway handlers) +- `runtime/`: core execution loop, routing, skill runtime, hook runtime +- `interfaces/`: transport adapters (HTTP/WS) +- `gateway/`: method handlers and protocol bridging - `application/`: use-cases and orchestration services -- `domain/`: business rules and domain primitives -- `infrastructure/`: integrations and runtime adapters -- `shared/`: shared utilities/types +- `infrastructure/`: runtime-facing integrations/adapters +- `platform/`: shared platform capabilities (llm, persistence, config, files) +- `tools/`: tool registry, MCP adapters, public/system tools +- `skills/`: installable skills and runtime manifests -## Migration Rule -New business logic should be implemented in `oclaw/`. -`oclaw/` modules remain as compatibility bridges during migration. +## Naming Rule +- Use `oclaw` consistently in paths, symbols, and docs. +- Avoid introducing legacy aliases or old naming variants. diff --git a/__init__.py b/__init__.py index 72794d96..540ddb6c 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,5 @@ -"""OpenClaw v2 package root. +"""Oclaw v2 package root. -All new architecture-first code should live under `oclaw/`. -Legacy `src/` modules may import from this package as a compatibility bridge. +All production code in this repository lives under this package root. """ diff --git a/data/mcp_local.env.example b/data/mcp_local.env.example index fdbf843d..b3e3f6ef 100644 --- a/data/mcp_local.env.example +++ b/data/mcp_local.env.example @@ -1,9 +1,9 @@ -# 推荐:复制为 src/_local/mcp_local.env(与 OAuth 等本地密钥同目录,见 .gitignore)。 -# 兼容:仍可放在 data/mcp_local.env(与默认 SQLite 同目录);若两处都存在,同键以 src/_local 为准。 +# 推荐:复制为 _local/mcp_local.env(与 OAuth 等本地密钥同目录,见 .gitignore)。 +# 兼容:仍可放在 data/mcp_local.env(与默认 SQLite 同目录);若两处都存在,同键以 _local 为准。 # ${PROJECT_ROOT} 为仓库根目录。 # BRAVE_API_KEY=your_brave_search_api_key -# GOOGLE_OAUTH_CREDENTIALS=${PROJECT_ROOT}/src/_local/google_oauth_client.json +# GOOGLE_OAUTH_CREDENTIALS=${PROJECT_ROOT}/_local/google_oauth_client.json # 或本机固定路径: GOOGLE_OAUTH_CREDENTIALS=%USERPROFILE%/.ops-assistant/google_oauth_client.json # # GitHub MCP (@modelcontextprotocol/server-github) @@ -29,17 +29,17 @@ # # CONTEXT7_API_KEY=ctx7sk-b5806022-903e-490a-b593-67caa624f3bf # -# OpenClaw runtime 默认不回退 legacy manager/runner;若紧急排障需临时回退,可显式打开: -# AIA_OPENCLAW_ALLOW_LEGACY_FALLBACK=1 +# Oclaw runtime 默认不回退 legacy manager/runner;若紧急排障需临时回退,可显式打开: +# AIA_OCLAW_ALLOW_LEGACY_FALLBACK=1 # -# OpenClaw 仍生效的工具循环预算(可在 Admin -> Tool Policy 配置): +# Oclaw 仍生效的工具循环预算(可在 Admin -> Tool Policy 配置): # AIA_TURN_MAX_TOOL_WORKERS=8 # AIA_TURN_MAX_TOOL_ROUNDS=8 # AIA_TURN_MAX_CONTEXT_MESSAGES=80 # Agent Core run 外环的可重试错误白名单(逗号分隔): -# AIA_OPENCLAW_RETRYABLE_ERROR_CODES=provider_timeout,provider_rate_limited,provider_temporary_error,provider_unavailable,context_overflow,tool_execution_failed +# AIA_OCLAW_RETRYABLE_ERROR_CODES=provider_timeout,provider_rate_limited,provider_temporary_error,provider_unavailable,context_overflow,tool_execution_failed # Admin 保存 retry code 的未知值行为:0=过滤并告警,1=拒绝保存(400) -# AIA_OPENCLAW_RETRY_CODES_STRICT_MODE=0 +# AIA_OCLAW_RETRY_CODES_STRICT_MODE=0 # # 内置工作区工具路径(read_file / run_command 等)默认限制在 AIA_WORKSPACE_ROOT(未设则为项目根)下。 # 全局扩展:多个绝对路径用 | 分隔;允许整盘访问(高风险): diff --git a/desktop/README.md b/desktop/README.md index 0da584c5..6c9dab87 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -24,7 +24,7 @@ npm run dev The app will: 1. Pick an available local port (default prefers `8787`). -2. Start backend using `python -m oclaw.ops gateway start --host 127.0.0.1 --port `. +2. Start backend using `python -m oclaw.runtime.operations gateway start --host 127.0.0.1 --port `. 3. Open `http://127.0.0.1:/chat` in the desktop window. Logs are written under: diff --git a/desktop/main.js b/desktop/main.js index d5ed173f..e0d03eb6 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -122,7 +122,7 @@ async function startBackendProcess() { AIA_DESKTOP_MODE: "1", }; - const args = ["-m", "oclaw.ops", "gateway", "start", "--host", host, "--port", String(port)]; + const args = ["-m", "oclaw.runtime.operations", "gateway", "start", "--host", host, "--port", String(port)]; backendProc = spawn(pythonBin, args, { cwd: APP_ROOT, env, @@ -212,7 +212,7 @@ async function startChannelProcess() { try { const cleanupRes = await runPythonInline( pythonBin, - "from oclaw.ops.runtime import cleanup_orphan_service_processes; k=cleanup_orphan_service_processes('channel:wecom'); print('killed=' + ','.join(str(x) for x in k))", + "from oclaw.runtime.operations.runtime import cleanup_orphan_service_processes; k=cleanup_orphan_service_processes('channel:wecom'); print('killed=' + ','.join(str(x) for x in k))", { AIA_DATA_DIR: DATA_ROOT }, ); if (!cleanupRes.ok) { @@ -230,7 +230,7 @@ async function startChannelProcess() { AIA_DATA_DIR: DATA_ROOT, AIA_DESKTOP_MODE: "1", }; - const args = ["-m", "oclaw.ops", "channel", "wecom", "start", "--mode", "ws", "--interval", "3.0", "--deliver-outbound"]; + const args = ["-m", "oclaw.runtime.operations", "channel", "wecom", "start", "--mode", "ws", "--interval", "3.0", "--deliver-outbound"]; channelStopping = false; channelProc = spawn(pythonBin, args, { cwd: APP_ROOT, diff --git a/docs/ARCHITECTURE_OVERVIEW.md b/docs/ARCHITECTURE_OVERVIEW.md new file mode 100644 index 00000000..b65ea399 --- /dev/null +++ b/docs/ARCHITECTURE_OVERVIEW.md @@ -0,0 +1,32 @@ +# 架构总览(当前真相) + +本文件用于描述当前仓库分层与目录职责,避免迁移后“认知滞后”。 + +## 顶层目录职责 + +- `runtime/`:运行时主域(agent、gateway 执行流、skills/hooks/extensions、operations)。 +- `interfaces/`:对外接口层(HTTP、WS、Admin、Gateway method bridge)。 +- `platform/`:通用平台能力(配置、存储、LLM transport、文件层)。 +- `prompts/`:统一提示词体系(含 runtime 相关提示模板)。 +- `tests/`:测试代码(按你的要求保持顶层)。 +- `docs/`:设计文档、运维说明、迁移记录。 + +## runtime 内部建议边界 + +- `runtime/core/`:可复用执行内核(如 agent 执行管线聚合入口)。 +- `runtime/app/`:应用侧入口组织(面向外部流程的 runtime 编排)。 +- `runtime/agents|chat|orchestration|workers`:领域能力模块。 +- `runtime/skills|hooks|extensions`:可扩展能力载体。 +- `runtime/operations/scripts`:运维脚本与生成器。 + +## 路径规范 + +- 运行时资源路径统一通过 `platform/config/runtime_paths.py` 获取。 +- 禁止新增硬编码目录字符串(如直接拼 `oclaw/runtime/...`)。 + +## 依赖方向(原则) + +- `interfaces -> runtime -> platform`(尽量单向)。 +- `runtime` 不反向依赖 `interfaces`(必要时通过协议/回调解耦)。 +- `docs/tests` 可依赖任意层,但不应反向影响运行时代码设计。 + diff --git a/docs/DESKTOP_PACKAGING.md b/docs/DESKTOP_PACKAGING.md index 7ef33eac..21b6ecdb 100644 --- a/docs/DESKTOP_PACKAGING.md +++ b/docs/DESKTOP_PACKAGING.md @@ -29,7 +29,7 @@ npm run pack:win 这个命令会自动做两件事: -1. 执行 `prepare:icon`,从 `oclaw/admin/static/oliver.svg` 生成 `desktop/assets/oclaw.ico` +1. 执行 `prepare:icon`,从 `oclaw/interfaces/admin/static/oliver.svg` 生成 `desktop/assets/oclaw.ico` 2. 调用 `electron-builder` 生成 NSIS 安装包 ## 4) 打包产物位置 diff --git a/docs/ENVIRONMENT_VARIABLES.md b/docs/ENVIRONMENT_VARIABLES.md index bc657d18..076b6e26 100644 --- a/docs/ENVIRONMENT_VARIABLES.md +++ b/docs/ENVIRONMENT_VARIABLES.md @@ -18,83 +18,83 @@ - `AIA_ASSISTANT_MODE` - 默认:空(代码内决定默认模式) - 作用:助手模式选择 - - 生效:`oclaw/platform/llm/chat_models.py`, `oclaw/agents/factory.py` + - 生效:`oclaw/platform/llm/chat_models.py`, `oclaw/runtime/agents/factory.py` - `AIA_MANAGER_DECISION_MODE` - 默认:空 - 作用:**Legacy(已断开)**:旧 manager 决策模式(如 `rule`) - 说明:oclaw runtime 默认不再走 `CompositeOpsAgent` 的 manager 决策;该变量仅保留以便后续接回 legacy - - 生效:`oclaw/agents/manager_agent.py`(仅 legacy 链路) + - 生效:`oclaw/runtime/agents/manager_agent.py`(仅 legacy 链路) - `AIA_TURN_MAX_TOOL_WORKERS` - 默认:`8` - 作用:单轮工具并发上限 - - 生效:`oclaw/openclaw_runtime/gateway.py`, `oclaw/openclaw_runtime/direct_loop.py` + - 生效:`oclaw/oclaw_runtime/gateway.py`, `oclaw/oclaw_runtime/direct_loop.py` - `AIA_TURN_MAX_TOOL_ROUNDS` - 默认:`8` - 作用:工具循环轮次上限 - - 生效:`oclaw/openclaw_runtime/gateway.py`, `oclaw/openclaw_runtime/direct_loop.py` + - 生效:`oclaw/oclaw_runtime/gateway.py`, `oclaw/oclaw_runtime/direct_loop.py` - `AIA_TURN_MAX_CONTEXT_MESSAGES` - 默认:`80` - 作用:上下文消息上限 - - 生效:`oclaw/openclaw_runtime/gateway.py`, `oclaw/openclaw_runtime/direct_loop.py` + - 生效:`oclaw/oclaw_runtime/gateway.py`, `oclaw/oclaw_runtime/direct_loop.py` -- oclaw async queue/worker(`router -> openclaw_task -> worker`) +- oclaw async queue/worker(`router -> oclaw_task -> worker`) - 当前版本无独立环境变量;复用以上 `AIA_TURN_MAX_*` 配置控制 direct loop 执行上限 - - 生效:`oclaw/openclaw_runtime/gateway.py`, `oclaw/openclaw_runtime/worker.py` + - 生效:`oclaw/oclaw_runtime/gateway.py`, `oclaw/oclaw_runtime/worker.py` - `AIA_PROMPT_FRONTMATTER_STRICT` - 默认:`0` - 作用:`1` 时 `SKILL.md` / `oclaw/prompts/*.md` 的 frontmatter 必须为可解析 YAML;解析失败直接报错(不回落旧版行解析) - - 生效:`oclaw/prompts/frontmatter.py`, `oclaw/prompts/loader.py`, `oclaw/openclaw_runtime/skills.py` + - 生效:`oclaw/prompts/frontmatter.py`, `oclaw/prompts/loader.py`, `oclaw/oclaw_runtime/skills.py` - `AIA_SKILLS_PROMPT_IN_SYSTEM` - 默认:`1`(开启;仅当技能运行时启用) - 作用:是否在 system prompt 末尾附加 oclaw 风格的 `` 目录块(与原生 `tools` 并存) - 说明:设为 `0` 可关闭以降低 token;Admin `AIA_SKILL_RUNTIME_ENABLED` 关闭时本块不生成 - - 生效:`oclaw/openclaw_runtime/skills_prompt.py`, `oclaw/openclaw_runtime/direct_loop.py` + - 生效:`oclaw/oclaw_runtime/skills_prompt.py`, `oclaw/oclaw_runtime/direct_loop.py` - `AIA_SKILLS_PROMPT_MAX_CHARS` - 默认:`18000` - 作用:技能目录 XML 块最大字符数(超出则从列表尾部丢弃条目) - - 生效:`oclaw/openclaw_runtime/skills_prompt.py` + - 生效:`oclaw/oclaw_runtime/skills_prompt.py` - `AIA_SKILL_DISABLED_NAMES` - 默认:空数组(`[]`) - 作用:按技能名禁用模型可见/可执行技能(JSON 数组字符串,例如 `["skill_a","skill_b"]`) - 说明:禁用后同时影响 manifest/prompt 渲染与 direct loop 工具暴露 - - 生效:`oclaw/openclaw_runtime/skills.py`, `oclaw/openclaw_runtime/skills_prompt.py`, `oclaw/openclaw_runtime/skill_installer.py` + - 生效:`oclaw/oclaw_runtime/skills.py`, `oclaw/oclaw_runtime/skills_prompt.py`, `oclaw/oclaw_runtime/skill_installer.py` - `AIA_SKILL_AUTO_INSTALL_ENABLED` - 默认:`1` - 作用:是否允许自动安装 skill(admin auto-install / retry-install(auto)) - 说明:关闭后返回 `auto_install_disabled`,并标记为不可重试 - - 生效:`oclaw/openclaw_runtime/skill_installer.py`, `oclaw/admin/skills_api.py` + - 生效:`oclaw/oclaw_runtime/skill_installer.py`, `oclaw/interfaces/admin/skills_api.py` -- `AIA_OPENCLAW_RETRYABLE_ERROR_CODES` +- `AIA_OCLAW_RETRYABLE_ERROR_CODES` - 默认:`provider_timeout,provider_rate_limited,provider_temporary_error,provider_unavailable,context_overflow,tool_execution_failed` - 作用:Agent Core run 外环的错误重试白名单(逗号分隔) - 说明:仅当 attempt 返回 `status=retry` 且 `error_code` 命中该白名单时才进入下一次 attempt;未知 code 默认在 Admin 保存时会被过滤并告警 - 补充:`relay_envelope_invalid`、`relay_envelope_unsupported_version` 属于输入契约错误,运行时固定按 non-retryable 处理(即使被误加入白名单也不会进入重试链) - - 生效:`oclaw/openclaw_runtime/agent_core_run.py` + - 生效:`oclaw/oclaw_runtime/agent_core_run.py` -- `AIA_OPENCLAW_ROUTER_MODE` +- `AIA_OCLAW_ROUTER_MODE` - 默认:`rule` - 取值:`rule`(启发式)或 `llm_json`(由当前 executor 的 `model.chat` 产出 `{mode,reason}` JSON;解析失败则回落 `rule`) - - 说明:亦可通过同名环境变量覆盖;提示词见 `oclaw/prompts_openclaw/router/decide_route.md` - - 生效:`oclaw/openclaw_runtime/router.py`, `oclaw/openclaw_runtime/gateway.py` + - 说明:亦可通过同名环境变量覆盖;提示词见 `oclaw/prompts_runtime/router/decide_route.md` + - 生效:`oclaw/oclaw_runtime/router.py`, `oclaw/oclaw_runtime/gateway.py` -- oclaw trace 字段与 `event_type` ↔ `oc_stage` 对照见 `oclaw/docs/openclaw-trace-taxonomy.md` -- Skill 安装错误码与重试建议、trace 排障路径见 `oclaw/docs/openclaw-skill-troubleshooting.md` -- Relay 文件指针(含 ACP 父子 run)错误码与排障见 `oclaw/docs/openclaw-skill-troubleshooting.md` 的“Relay 文件指针排障” +- oclaw trace 字段与 `event_type` ↔ `oc_stage` 对照见 `oclaw/docs/oclaw-trace-taxonomy.md` +- Skill 安装错误码与重试建议、trace 排障路径见 `oclaw/docs/oclaw-skill-troubleshooting.md` +- Relay 文件指针(含 ACP 父子 run)错误码与排障见 `oclaw/docs/oclaw-skill-troubleshooting.md` 的“Relay 文件指针排障” -- `AIA_OPENCLAW_RETRY_CODES_STRICT_MODE` +- `AIA_OCLAW_RETRY_CODES_STRICT_MODE` - 默认:`0` - - 作用:控制 Admin 保存 `AIA_OPENCLAW_RETRYABLE_ERROR_CODES` 时的未知 code 行为 + - 作用:控制 Admin 保存 `AIA_OCLAW_RETRYABLE_ERROR_CODES` 时的未知 code 行为 - 说明:`0`=过滤并告警;`1`=直接拒绝保存(HTTP 400) - - 生效:`oclaw/admin/routes.py`, `oclaw/admin/static/app.js` + - 生效:`oclaw/interfaces/admin/routes.py`, `oclaw/interfaces/admin/static/app.js` - `AIA_TOOL_ENFORCED_RETRY_MODE` - 默认:`first_round_only` @@ -111,11 +111,11 @@ - 作用:**Legacy(已断开)**:同签名工具调用预算 - 生效:仅 legacy 链路(保留占位,暂不影响 oclaw) -- `AIA_OPENCLAW_ALLOW_LEGACY_FALLBACK` +- `AIA_OCLAW_ALLOW_LEGACY_FALLBACK` - 默认:`0`(关闭) - 作用:oclaw 执行失败时,是否允许回退到 legacy `executor.run_turn(...)` - 说明:默认 fail-closed(不回退),避免无意中触发旧 manager/runner - - 生效:`oclaw/openclaw_runtime/gateway.py`, `oclaw/agents/specialist_agent.py` + - 生效:`oclaw/oclaw_runtime/gateway.py`, `oclaw/runtime/agents/specialist_agent.py` ## LLM 传输与 replay(OpenAI 兼容) @@ -173,7 +173,7 @@ - 默认:`0`(不限制) - 作用:工具结果写回 LLM 的消息长度上限 - 说明:`0` 表示不做限制(不推荐,可能触发部分网关的单条消息上限 400) - - 生效:`oclaw/chat/tool_runtime.py` + - 生效:`oclaw/runtime/chat/tool_runtime.py` - 观测:管理端聊天流 `tool_use_result` 事件会携带 `llm_wire.{truncated_for_llm,max_chars,result_bytes,result_for_llm_bytes,truncate_ms}` - `AIA_TOOL_LOG_MAX_CHARS` @@ -191,7 +191,7 @@ - `AIA_MCP_ENV_ALLOWLIST` - 默认:内置 allowlist(Brave/Google/GitHub/Context7) - 作用:MCP 子进程可透传环境变量白名单 - - 生效:`oclaw/ops/mcp_env.py` + - 生效:`oclaw/runtime/operations/mcp_env.py` - `AIA_MCP_FILESYSTEM_EXTRA_ROOTS` - 默认:空 @@ -270,7 +270,7 @@ - `AIA_IMAGE_MODEL` - 默认:空(走 profile/模型默认) - 作用:图像模型 - - 生效:`oclaw/platform/llm/image_message_client.py`, `oclaw/agents/specialist_agent.py` + - 生效:`oclaw/platform/llm/image_message_client.py`, `oclaw/runtime/agents/specialist_agent.py` - `AIA_IMAGE_BASE_URL` - 默认:`https://api.openai.com/v1` @@ -329,7 +329,7 @@ - `AIA_WORKSPACE_ROOT` - 默认:项目根 - 作用:工作区主根路径 - - 生效:`oclaw/tools/experts/workspace/workspace_base.py`, `oclaw/indexing/workspace_indexer.py` + - 生效:`oclaw/tools/experts/workspace/workspace_base.py`, `oclaw/tools/workspace_indexer.py` - `AIA_WORKSPACE_EXTRA_ROOTS` - 默认:空 @@ -346,46 +346,46 @@ - `AIA_ASSISTANT_GATEWAY_HOST` - 默认:`0.0.0.0` - 作用:网关监听地址 - - 生效:`oclaw/app_server/fastapi_main.py`, `oclaw/ops/main.py` + - 生效:`oclaw/app_server/fastapi_main.py`, `oclaw/runtime/operations/main.py` - `AIA_ASSISTANT_GATEWAY_PORT` - 默认:`8787` - 作用:网关监听端口 - - 生效:`oclaw/app_server/fastapi_main.py`, `oclaw/ops/main.py` + - 生效:`oclaw/app_server/fastapi_main.py`, `oclaw/runtime/operations/main.py` - `AIA_RUNTIME_LOG_DIR` - 默认:空(使用内部默认目录) - 作用:运行日志目录 - - 生效:`oclaw/ops/runtime.py` + - 生效:`oclaw/runtime/operations/runtime.py` - `AIA_SSE_QUEUE_MAXSIZE` - 默认:`2000` - 作用:SSE 事件队列上限 - - 生效:`oclaw/admin/chat_api.py` + - 生效:`oclaw/interfaces/admin/chat_api.py` ## WeCom 长连接 - `AIA_WECOM_LONGCONN_WORKERS` - 默认:`2` - 作用:入站处理 worker 数 - - 生效:`oclaw/channels/wecom/longconn_runner.py` + - 生效:`oclaw/interfaces/channels/wecom/longconn_runner.py` - `AIA_WECOM_LONGCONN_INBOUND_QUEUE_MAXSIZE` - 默认:`200` - 作用:入站队列长度上限 - - 生效:`oclaw/channels/wecom/longconn_runner.py` + - 生效:`oclaw/interfaces/channels/wecom/longconn_runner.py` ## 安全与密钥 - `AIA_ASSISTANT_PASSWORD` - 默认:空(必须配置) - 作用:管理台管理员密码(bootstrap/login) - - 生效:`oclaw/platform/config/passwords.py`, `oclaw/admin/routes.py` + - 生效:`oclaw/platform/config/passwords.py`, `oclaw/interfaces/admin/routes.py` - `AIA_ASSISTANT_MASTER_KEY` - 默认:空 - 作用:密钥加密(Fernet)主密钥 - - 生效:`oclaw/platform/persistence/sqlite_store.py`, `oclaw/admin/routes.py` + - 生效:`oclaw/platform/persistence/sqlite_store.py`, `oclaw/interfaces/admin/routes.py` ## 存储与迁移 diff --git a/docs/ENVIRONMENT_VARIABLES_CHANGELOG.md b/docs/ENVIRONMENT_VARIABLES_CHANGELOG.md index 56a079a9..bbef8b4b 100644 --- a/docs/ENVIRONMENT_VARIABLES_CHANGELOG.md +++ b/docs/ENVIRONMENT_VARIABLES_CHANGELOG.md @@ -66,7 +66,7 @@ - 依赖:`PyYAML`(`requirements.txt`)。 ### Changed -- `oclaw/prompts/loader.py` 与 `oclaw/openclaw_runtime/skills.py` 统一使用 YAML 解析 frontmatter(失败时默认回落旧行解析,除非开启 STRICT)。 +- `oclaw/prompts/loader.py` 与 `oclaw/oclaw_runtime/skills.py` 统一使用 YAML 解析 frontmatter(失败时默认回落旧行解析,除非开启 STRICT)。 --- @@ -80,7 +80,7 @@ - `AIA_REPLAY_REPAIR_TOOL_PAIRING`(默认 `1`) - `AIA_TOOL_CALL_ID_MAX_LEN`(默认 `40`) - `AIA_PROMPT_TOOL_FALLBACK`(默认 `1`) - - `oclaw/platform/llm/OPENCLAW_MIT_LICENSE.txt`(oclaw 启发实现之 MIT 署名) + - `oclaw/platform/llm/OCLAW_MIT_LICENSE.txt`(oclaw 启发实现之 MIT 署名) ### Changed - 变量前缀统一为 `AIA_*`,项目内不再使用 `OPS_*` / `AI_OPS_*`。 @@ -105,15 +105,15 @@ ## 2026-04-20 / Unreleased ### Added -- `AIA_OPENCLAW_ALLOW_LEGACY_FALLBACK` +- `AIA_OCLAW_ALLOW_LEGACY_FALLBACK` - 默认值:`0` - 用途:oclaw runtime 失败时是否允许回退到 legacy `run_turn` - - 影响模块:`oclaw/openclaw_runtime/gateway.py`, `oclaw/agents/specialist_agent.py` + - 影响模块:`oclaw/oclaw_runtime/gateway.py`, `oclaw/runtime/agents/specialist_agent.py` ### Changed - `AIA_TURN_MAX_*`(tool workers/rounds/context) - 变更前:由 legacy turn runner 读取(历史文件名可能为 `agent_core.py`) - - 变更后:由 oclaw runtime 读取并生效(`oclaw/openclaw_runtime/gateway.py`) + - 变更后:由 oclaw runtime 读取并生效(`oclaw/oclaw_runtime/gateway.py`) - 是否需要重启:是(读取自 settings/db/env 的时机取决于运行方式) ### Deprecated @@ -135,7 +135,7 @@ ### Changed - 无新增环境变量;oclaw runtime 在现有变量下补齐了 memory stage、router sync/async 分流、sqlite task queue、worker 执行链路。 - - 影响模块:`oclaw/openclaw_runtime/gateway.py`, `oclaw/openclaw_runtime/direct_loop.py`, `oclaw/openclaw_runtime/router.py`, `oclaw/openclaw_runtime/worker.py`, `oclaw/platform/persistence/sqlite_store.py` + - 影响模块:`oclaw/oclaw_runtime/gateway.py`, `oclaw/oclaw_runtime/direct_loop.py`, `oclaw/oclaw_runtime/router.py`, `oclaw/oclaw_runtime/worker.py`, `oclaw/platform/persistence/sqlite_store.py` - 是否需要重启:是(升级代码后建议重启进程以启动 worker 与新路由逻辑) ### Migration Checklist @@ -150,10 +150,10 @@ ## 2026-04-20 / Unreleased (AgentCore Retry Matrix) ### Added -- `AIA_OPENCLAW_RETRYABLE_ERROR_CODES` +- `AIA_OCLAW_RETRYABLE_ERROR_CODES` - 默认值:`provider_timeout,provider_rate_limited,provider_temporary_error,provider_unavailable,context_overflow,tool_execution_failed` - 用途:控制 Agent Core run 外环可重试错误白名单 - - 影响模块:`oclaw/openclaw_runtime/agent_core_run.py`, `oclaw/admin/routes.py`, `oclaw/admin/static/app.js` + - 影响模块:`oclaw/oclaw_runtime/agent_core_run.py`, `oclaw/interfaces/admin/routes.py`, `oclaw/interfaces/admin/static/app.js` ### Changed - Agent Core 重试策略从“status=retry 即重试”升级为“retry + error_code 命中白名单才重试”。 @@ -167,15 +167,15 @@ - [x] 已执行编译/测试回归 ### Changed -- `AIA_OPENCLAW_RETRYABLE_ERROR_CODES` 已接入 Admin「Tool Policy」页读写链路。 - - 影响模块:`oclaw/admin/routes.py`, `oclaw/admin/static/app.js` -- `AIA_OPENCLAW_RETRYABLE_ERROR_CODES` 保存时增加未知 code 过滤与告警返回(`unknown_retryable_error_codes`)。 - - 影响模块:`oclaw/admin/routes.py`, `oclaw/admin/static/app.js`, `oclaw/openclaw_runtime/agent_core_run.py` -- `AIA_OPENCLAW_RETRYABLE_ERROR_CODES` 新增严格模式:可配置为未知 code 直接拒绝保存(400)。 - - 影响模块:`oclaw/admin/routes.py`, `oclaw/admin/static/app.js` +- `AIA_OCLAW_RETRYABLE_ERROR_CODES` 已接入 Admin「Tool Policy」页读写链路。 + - 影响模块:`oclaw/interfaces/admin/routes.py`, `oclaw/interfaces/admin/static/app.js` +- `AIA_OCLAW_RETRYABLE_ERROR_CODES` 保存时增加未知 code 过滤与告警返回(`unknown_retryable_error_codes`)。 + - 影响模块:`oclaw/interfaces/admin/routes.py`, `oclaw/interfaces/admin/static/app.js`, `oclaw/oclaw_runtime/agent_core_run.py` +- `AIA_OCLAW_RETRYABLE_ERROR_CODES` 新增严格模式:可配置为未知 code 直接拒绝保存(400)。 + - 影响模块:`oclaw/interfaces/admin/routes.py`, `oclaw/interfaces/admin/static/app.js` ### Added -- `AIA_OPENCLAW_RETRY_CODES_STRICT_MODE` +- `AIA_OCLAW_RETRY_CODES_STRICT_MODE` - 默认值:`0` - 用途:控制 Admin 保存 retry code 时对未知值的处理(过滤告警 / 拒绝) - - 影响模块:`oclaw/admin/routes.py`, `oclaw/admin/static/app.js` + - 影响模块:`oclaw/interfaces/admin/routes.py`, `oclaw/interfaces/admin/static/app.js` diff --git a/docs/GATEWAY_IMAGE_GENERATE.md b/docs/GATEWAY_IMAGE_GENERATE.md index e0431d38..77189905 100644 --- a/docs/GATEWAY_IMAGE_GENERATE.md +++ b/docs/GATEWAY_IMAGE_GENERATE.md @@ -5,7 +5,7 @@ This document describes the Python gateway method `image.generate`. ## Method - Name: `image.generate` -- Handler: `oclaw/gateway/server_methods/image.py` +- Handler: `oclaw/interfaces/gateway/server_methods/image.py` ## Input Params diff --git a/docs/GATEWAY_TELEGRAM_NORMALIZATION.md b/docs/GATEWAY_TELEGRAM_NORMALIZATION.md index 4c54665b..df47b24e 100644 --- a/docs/GATEWAY_TELEGRAM_NORMALIZATION.md +++ b/docs/GATEWAY_TELEGRAM_NORMALIZATION.md @@ -10,7 +10,7 @@ Normalization is applied in these handlers: - `chat.send` - `sessions.send` -The shared implementation lives in `oclaw/gateway/server_methods/telegram_send_normalize.py`. +The shared implementation lives in `oclaw/interfaces/gateway/server_methods/telegram_send_normalize.py`. ## Input fields diff --git a/docs/LLM_TRANSPORTS.md b/docs/LLM_TRANSPORTS.md index b042fe1f..8a590bf7 100644 --- a/docs/LLM_TRANSPORTS.md +++ b/docs/LLM_TRANSPORTS.md @@ -1,6 +1,6 @@ # LLM provider/transport capability matrix (oclaw) -This project follows an **OpenClaw-style explicit provider/transport selection**: +This project follows an **Oclaw-style explicit provider/transport selection**: - You select the transport via **LLM profile `mode`** (not by inferring from `base_url`). - `base_url` can be the same unified gateway URL for all providers; the `mode` determines the wire protocol. @@ -12,7 +12,7 @@ This project follows an **OpenClaw-style explicit provider/transport selection** - **`base_url`**: gateway host URL (may be shared across providers) - **`api_key`** (profile secret): primary credential source (reused across modes) -Transport selection happens in `oclaw/agents/factory.py`. +Transport selection happens in `oclaw/runtime/agents/factory.py`. ## Modes and transports @@ -73,7 +73,7 @@ All transports stream assistant output through the same internal callback: For additional providers, follow this pattern: 1. Add a new transport class under `oclaw/platform/llm/transports/` -2. Extend `mode` selection in `oclaw/agents/factory.py` +2. Extend `mode` selection in `oclaw/runtime/agents/factory.py` 3. Add an **offline stream parser test** under `tests/` 4. Add/verify WS contract tests (delta + final + session.tool) diff --git a/docs/MCP_LOCAL_SERVER.md b/docs/MCP_LOCAL_SERVER.md index e70f23d5..fc9a76d7 100644 --- a/docs/MCP_LOCAL_SERVER.md +++ b/docs/MCP_LOCAL_SERVER.md @@ -505,7 +505,7 @@ Keep responses deterministic and JSON-serializable. - **作用**:按库名/版本拉取较新的官方文档片段,减少「API 记错版本」类幻觉。 - **安装**:`python scripts/install_mcp_context7.py`,或管理台 `POST /admin/api/mcp/install` 使用 [`examples/mcp_install_context7.json`](../examples/mcp_install_context7.json) 中的 `payload`。 -- **密钥**:在 **`oclaw/_local/mcp_local.env`**(推荐)或 `data/mcp_local.env`(兼容)设置 `CONTEXT7_API_KEY`(见 [context7.com/dashboard](https://context7.com/dashboard))。两处都存在时**同键以 `oclaw/_local/mcp_local.env` 为准**(覆盖 `data` 中的同键)。未自定义 `OPS_MCP_ENV_ALLOWLIST` 时,网关默认 allowlist 已包含 `CONTEXT7_API_KEY`(见 `oclaw/ops/mcp_env.py`);若你自定义了 allowlist,请手动追加该键。 +- **密钥**:在 **`oclaw/_local/mcp_local.env`**(推荐)或 `data/mcp_local.env`(兼容)设置 `CONTEXT7_API_KEY`(见 [context7.com/dashboard](https://context7.com/dashboard))。两处都存在时**同键以 `oclaw/_local/mcp_local.env` 为准**(覆盖 `data` 中的同键)。未自定义 `OPS_MCP_ENV_ALLOWLIST` 时,网关默认 allowlist 已包含 `CONTEXT7_API_KEY`(见 `oclaw/runtime/operations/mcp_env.py`);若你自定义了 allowlist,请手动追加该键。 - **装完后**:`Health` → `Sync Tools` → 将 `mcp-context7` 加入通识 specialist 的 MCP 绑定(若脚本已成功 Sync,会自动追加)。 ### 通识侧终端能力(`run_command`) diff --git a/docs/OCLAW_COMPAT_REMOVAL_CHECKLIST.md b/docs/OCLAW_COMPAT_REMOVAL_CHECKLIST.md index 08151577..549cf573 100644 --- a/docs/OCLAW_COMPAT_REMOVAL_CHECKLIST.md +++ b/docs/OCLAW_COMPAT_REMOVAL_CHECKLIST.md @@ -17,10 +17,10 @@ Use this checklist to verify readiness and post-delete safety for `oclaw/app_ser - [x] Plugin bootstrap still loads expected extension set. ## Prompt/skill checks -- [x] Runtime role context still loads from `oclaw/agent/*`. +- [x] Runtime role context still loads from `oclaw/runtime/assets/agent_workspaces/*`. - [x] Skill root priority still effective: - [x] `AIA_SKILLS_ROOT` - - [x] `oclaw/skills` + - [x] `oclaw/runtime/skills` - [x] `skills/` fallback ## Extension policy checks diff --git a/docs/OCLAW_MIGRATION_GUIDE.md b/docs/OCLAW_MIGRATION_GUIDE.md index f5bf88f3..259e2beb 100644 --- a/docs/OCLAW_MIGRATION_GUIDE.md +++ b/docs/OCLAW_MIGRATION_GUIDE.md @@ -1,4 +1,4 @@ -# OpenClaw Migration Guide +# Oclaw Migration Guide This repository is migrating from legacy `oclaw/` runtime wiring to the new `oclaw/` architecture root. @@ -17,7 +17,7 @@ This repository is migrating from legacy `oclaw/` runtime wiring to the new `ocl ## Runtime notes - Gateway HTTP method adapter: `POST /gateway/method` - WS dispatch first resolves method handlers from shared dispatcher. -- Inbound payload use-case entrypoint: `oclaw.application.gateway.process_inbound_payload_usecase` +- Inbound payload use-case entrypoint: `oclaw.runtime.application.gateway.process_inbound_payload_usecase` - HTTP app entrypoint moved to: `oclaw.interfaces.http.fastapi_app` - WS entrypoint moved to: `oclaw.interfaces.ws.entrypoint` - WS runtime bridge path: `oclaw.interfaces.ws.runtime` @@ -62,9 +62,9 @@ This repository is migrating from legacy `oclaw/` runtime wiring to the new `ocl - Legacy root `extensions/` has been merged into `oclaw/extensions/` and removed. ## Prompt and skills policy -- Role context is loaded from `oclaw/agent/*`. +- Role context is loaded from `oclaw/runtime/assets/agent_workspaces/*`. - Skills root priority: 1. `AIA_SKILLS_ROOT` - 2. `oclaw/skills` + 2. `oclaw/runtime/skills` 3. `skills/` (legacy fallback) diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index 0557b280..b8491b9b 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -4,14 +4,14 @@ 关联文档: -- trace 字段与阶段对照:`docs/openclaw-trace-taxonomy.md` -- skill 安装/执行排障:`docs/openclaw-skill-troubleshooting.md` +- trace 字段与阶段对照:`docs/oclaw-trace-taxonomy.md` +- skill 安装/执行排障:`docs/oclaw-skill-troubleshooting.md` --- ## 1. 统一入口(只保留最新) -所有运维命令统一通过 `scripts/`,不要再使用 `python -m oclaw.ops ...` 或历史 `.bat` 方式。 +所有运维命令统一通过 `scripts/`,不要再使用 `python -m oclaw.runtime.operations ...` 或历史 `.bat` 方式。 补充:工具脚本也统一放在 `scripts/`(例如 `seed_mcp_registry.py`、`ws_probe.py`)。 @@ -272,7 +272,7 @@ powershell -ExecutionPolicy Bypass -File .\scripts\weixin_install.ps1 安装目录: -- `data/channel_sidecar/openclaw-weixin/` +- `data/channel_sidecar/oclaw-weixin/` ### 11.2 扫码登录(获取 bot token) @@ -282,8 +282,8 @@ powershell -ExecutionPolicy Bypass -File .\scripts\weixin_login.ps1 登录态写入: -- `data/channel_sidecar/openclaw-weixin/state/openclaw-weixin/accounts/*.json` -- `data/channel_sidecar/openclaw-weixin/state/openclaw-weixin/accounts.json` +- `data/channel_sidecar/oclaw-weixin/state/oclaw-weixin/accounts/*.json` +- `data/channel_sidecar/oclaw-weixin/state/oclaw-weixin/accounts.json` ### 11.3 启动微信 sidecar @@ -305,8 +305,8 @@ powershell -ExecutionPolicy Bypass -File .\scripts\weixin_stop.ps1 日志文件: -- `data/channel_sidecar/openclaw-weixin/logs/weixin_sidecar.log` -- `data/channel_sidecar/openclaw-weixin/logs/weixin_sidecar.err.log` +- `data/channel_sidecar/oclaw-weixin/logs/weixin_sidecar.log` +- `data/channel_sidecar/oclaw-weixin/logs/weixin_sidecar.err.log` ### 11.4 当前行为说明 @@ -440,7 +440,7 @@ python .\scripts\wiki_auto_smoke_test.py 该脚本会: -- 投递一条 `wiki_capture` 任务到 `openclaw_task` +- 投递一条 `wiki_capture` 任务到 `oclaw_task` - 轮询任务状态直到 `done/failed/timeout` - 输出当前写入产物状态(`merged-turns.md`、`topic-index.json`、`index.json`、`LINT_REPORT.md`) diff --git a/docs/oclaw-trace-taxonomy.md b/docs/oclaw-trace-taxonomy.md index 742c396c..1c771d30 100644 --- a/docs/oclaw-trace-taxonomy.md +++ b/docs/oclaw-trace-taxonomy.md @@ -11,5 +11,5 @@ 如需查看完整细粒度字段定义,请在仓库中搜索: - `pipeline` -- `openclaw_task_id` -- `openclaw_worker_id` +- `oclaw_task_id` +- `oclaw_worker_id` diff --git a/docs/openclaw-skill-troubleshooting.md b/docs/runtime-skill-troubleshooting.md similarity index 96% rename from docs/openclaw-skill-troubleshooting.md rename to docs/runtime-skill-troubleshooting.md index e9ff9564..009e7afe 100644 --- a/docs/openclaw-skill-troubleshooting.md +++ b/docs/runtime-skill-troubleshooting.md @@ -27,19 +27,19 @@ 推荐按下列顺序查询同一个 `trace_id`: -1. `openclaw_gateway` +1. `oclaw_gateway` - `skill_manifest` - `router_decision` -2. `openclaw_agent_core` +2. `oclaw_agent_core` - `run_started` - `attempt_started` -3. `openclaw_direct_loop` +3. `oclaw_direct_loop` - `tool_wire_filter` - `tool_result_context_guard` -4. `openclaw_skill_executor` +4. `oclaw_skill_executor` - `skill_selected` - `skill_executed` -5. `openclaw_agent_core` +5. `oclaw_agent_core` - `after_turn_memory` - `attempt_finished` - `run_finished` @@ -52,7 +52,7 @@ - `AIA_SKILLS_PROMPT_IN_SYSTEM` 是否开启 - `AIA_SKILL_DISABLED_NAMES` 是否误禁用了目标 skill - `AIA_SKILL_AUTO_INSTALL_ENABLED` 是否关闭 -- 目标 skill 的 `SKILL.md` 是否包含合法 frontmatter(尤其 `metadata.openclaw.install`) +- 目标 skill 的 `SKILL.md` 是否包含合法 frontmatter(尤其 `metadata.oclaw.install`) ## 5) 典型故障定位 diff --git a/docs/openclaw-trace-taxonomy.md b/docs/runtime-trace-taxonomy.md similarity index 79% rename from docs/openclaw-trace-taxonomy.md rename to docs/runtime-trace-taxonomy.md index 756f5936..102977fd 100644 --- a/docs/openclaw-trace-taxonomy.md +++ b/docs/runtime-trace-taxonomy.md @@ -7,15 +7,15 @@ Trace events share a common `payload` shape across components. Prefer joining on | Key | Meaning | | --- | --- | | `trace_id` | Correlates all events for one gateway turn | -| `pipeline` | Which subsystem emitted the event (`openclaw_gateway`, `openclaw_agent_core`, `openclaw_direct_loop`, `openclaw_skill_executor`) | +| `pipeline` | Which subsystem emitted the event (`oclaw_gateway`, `oclaw_agent_core`, `oclaw_direct_loop`, `oclaw_skill_executor`) | | `oc_stage` | Normalized lifecycle stage (see tables below) | | `lang` | Request language when known | | `run_id` | Agent core run UUID (retry container) | | `attempt_no` | 1-based attempt index inside `run_id` | -| `openclaw_task_id` | Async worker task id when present | -| `openclaw_worker_id` | Worker thread id when present | +| `oclaw_task_id` | Async worker task id when present | +| `oclaw_worker_id` | Worker thread id when present | -## Gateway (`pipeline=openclaw_gateway`) +## Gateway (`pipeline=oclaw_gateway`) | `event_type` | `oc_stage` | | --- | --- | @@ -35,7 +35,7 @@ Trace events share a common `payload` shape across components. Prefer joining on - `relay_envelope_present`: whether `metadata.relay_share_envelope` exists - `relay_envelope_pointer_count`: pointer count inside envelope manifest -## Agent core (`pipeline=openclaw_agent_core`) +## Agent core (`pipeline=oclaw_agent_core`) | `event_type` | `oc_stage` | | --- | --- | @@ -46,7 +46,7 @@ Trace events share a common `payload` shape across components. Prefer joining on | `run_compact` | `compact` | | `run_retry` | `retry` | -## Direct loop (`pipeline=openclaw_direct_loop`) +## Direct loop (`pipeline=oclaw_direct_loop`) | `event_type` | `oc_stage` | | --- | --- | @@ -55,7 +55,7 @@ Trace events share a common `payload` shape across components. Prefer joining on Both include `trace_id` and, when provided by the parent attempt, `run_id` and `attempt_no`. -## Skill executor (`pipeline=openclaw_skill_executor`) +## Skill executor (`pipeline=oclaw_skill_executor`) | `event_type` | `oc_stage` | | --- | --- | @@ -64,7 +64,7 @@ Both include `trace_id` and, when provided by the parent attempt, `run_id` and ` The payload includes `run_id` and `attempt_no` when available, so skill execution can be joined back to `run_started/attempt_started`. -## Attempt memory hook (`pipeline=openclaw_agent_core`) +## Attempt memory hook (`pipeline=oclaw_agent_core`) | `event_type` | `oc_stage` | | --- | --- | diff --git a/gateway/__init__.py b/gateway/__init__.py deleted file mode 100644 index bbf66163..00000000 --- a/gateway/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Python gateway runtime rewrite entrypoint. - -This package is the Python rewrite surface for gateway modules. -""" - -from .server_plugins import load_gateway_plugins -from .server_startup_plugins import prepare_gateway_plugin_bootstrap - -__all__ = [ - "load_gateway_plugins", - "prepare_gateway_plugin_bootstrap", -] - diff --git a/infrastructure/__init__.py b/infrastructure/__init__.py deleted file mode 100644 index 8c505779..00000000 --- a/infrastructure/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Infrastructure integrations (LLM, persistence, plugins, etc.).""" - diff --git a/infrastructure/agent_context/__init__.py b/infrastructure/agent_context/__init__.py deleted file mode 100644 index d158b264..00000000 --- a/infrastructure/agent_context/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Agent-context loaders from `oclaw/agent/*`.""" - -from .loader import build_role_system_context - -__all__ = ["build_role_system_context"] - diff --git a/admin/__init__.py b/interfaces/admin/__init__.py similarity index 100% rename from admin/__init__.py rename to interfaces/admin/__init__.py diff --git a/admin/chat_api.py b/interfaces/admin/chat_api.py similarity index 99% rename from admin/chat_api.py rename to interfaces/admin/chat_api.py index e83206bf..160ef393 100644 --- a/admin/chat_api.py +++ b/interfaces/admin/chat_api.py @@ -14,9 +14,9 @@ from pathlib import Path from fastapi import APIRouter, Body, File, Header, HTTPException, Query, UploadFile from fastapi.responses import Response, StreamingResponse -from oclaw.ops.mcp_env import apply_gateway_mcp_env_to_os -from oclaw.agents.factory import build_gateway_executor -from oclaw.chat.agent import GenerationInterrupted +from oclaw.runtime.operations.mcp_env import apply_gateway_mcp_env_to_os +from oclaw.runtime.agents.factory import build_gateway_executor +from oclaw.runtime.chat.agent import GenerationInterrupted from oclaw.platform.config.paths import db_path from oclaw.platform.files.attachment_assets import AttachmentAssetStore from oclaw.platform.files.file_attachments import ( @@ -29,8 +29,8 @@ from oclaw.platform.files.file_attachments import ( ) from oclaw.platform.files.session_export import export_session_json, export_session_markdown from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.openclaw_runtime.gateway import OpenClawGateway -from oclaw.openclaw_runtime.types import StandardMessage, normalize_interaction_mode, normalize_requested_specialist +from oclaw.runtime.gateway import OclawGateway +from oclaw.runtime.types import StandardMessage, normalize_interaction_mode, normalize_requested_specialist def _oclaw_config_path() -> Path: @@ -706,7 +706,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor if not sess: raise HTTPException(status_code=404, detail="session_not_found") after_iso = str(after or "").strip() - rows = store.openclaw_task_list(tenant_id=tenant_id, session_id=str(session_id), limit=max(50, int(limit) * 5)) + rows = store.oclaw_task_list(tenant_id=tenant_id, session_id=str(session_id), limit=max(50, int(limit) * 5)) events: list[dict[str, Any]] = [] for t in rows: if str(t.task_type or "") != "wiki_capture": @@ -894,7 +894,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor labels = _dispatch_reason_labels_with_overrides(store) _require_administrator_chat_viewer(ctx) tenant_id = str(ctx.get("tenant_id") or "") - rows = store.openclaw_task_list(tenant_id=tenant_id, limit=limit) + rows = store.oclaw_task_list(tenant_id=tenant_id, limit=limit) total = len(rows) dynamic_used = 0 fallback_generalist = 0 @@ -1288,7 +1288,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor policy_session_id=str(session_id), ) try: - gw = OpenClawGateway(store=store) + gw = OclawGateway(store=store) msg = StandardMessage( session_id=str(session_id), tenant_id=tenant_id, @@ -1431,7 +1431,7 @@ def include_chat_routes(router: APIRouter, *, resolve_auth: Callable[[SqliteStor kind = "skill_ui" if ev_name.startswith("skill_") else "tool_ui" _emit(kind, {"event": ev_name, "payload": pl}) - gw = OpenClawGateway(store=store) + gw = OclawGateway(store=store) msg = StandardMessage( session_id=str(session_id), tenant_id=tenant_id, diff --git a/admin/mcp_e2e_probe.py b/interfaces/admin/mcp_e2e_probe.py similarity index 97% rename from admin/mcp_e2e_probe.py rename to interfaces/admin/mcp_e2e_probe.py index f2342001..9662f5f0 100644 --- a/admin/mcp_e2e_probe.py +++ b/interfaces/admin/mcp_e2e_probe.py @@ -3,8 +3,8 @@ from __future__ import annotations from typing import Any -from oclaw.tools.base import ToolRegistry, ToolSpec -from oclaw.tools.tool_validation import validate_tool_arguments +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.tools.tool_validation import validate_tool_arguments def parse_mcp_bound_tool_name(full_name: str) -> tuple[str, str] | None: diff --git a/admin/models_api.py b/interfaces/admin/models_api.py similarity index 99% rename from admin/models_api.py rename to interfaces/admin/models_api.py index 8986b23b..c78d6d48 100644 --- a/admin/models_api.py +++ b/interfaces/admin/models_api.py @@ -12,15 +12,15 @@ from typing import Any from fastapi import APIRouter, Body, Header, HTTPException, Query from fastapi.responses import Response -from oclaw.agents.factory import DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_MODEL -from oclaw.agents.specialists import ( +from oclaw.runtime.agents.factory import DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_MODEL +from oclaw.runtime.agents.specialists import ( AGENT_PROFILE_BINDINGS_KEY, AGENT_ROLE_IDS, dump_agent_profile_bindings, parse_agent_profile_bindings, ) from oclaw.platform.config.paths import db_path -from oclaw.orchestration.evaluation import eval_summary +from oclaw.runtime.orchestration.evaluation import eval_summary from oclaw.platform.persistence.sqlite_store import ( LLM_BUILTIN_OLLAMA_PROFILE_ID, SqliteStore, diff --git a/admin/routes.py b/interfaces/admin/routes.py similarity index 97% rename from admin/routes.py rename to interfaces/admin/routes.py index 98336c5d..b4c9daa9 100644 --- a/admin/routes.py +++ b/interfaces/admin/routes.py @@ -12,38 +12,38 @@ from fastapi import APIRouter, Body, Header, Query from fastapi import HTTPException from fastapi.responses import HTMLResponse -from oclaw.ops.mcp_env import apply_gateway_mcp_env_to_os -from oclaw.ops.providers.registry import build_channel_registry -from oclaw.ops.runtime import cleanup_orphan_service_processes, detect_orphan_service_processes, status_services -from oclaw.ops.stack import cmd_stack_down, cmd_stack_status, cmd_stack_up -from oclaw.orchestration.vector_store import read_vector_memory_runtime +from oclaw.runtime.operations.mcp_env import apply_gateway_mcp_env_to_os +from oclaw.runtime.operations.providers.registry import build_channel_registry +from oclaw.runtime.operations.runtime import cleanup_orphan_service_processes, detect_orphan_service_processes, status_services +from oclaw.runtime.operations.stack import cmd_stack_down, cmd_stack_status, cmd_stack_up +from oclaw.runtime.orchestration.vector_store import read_vector_memory_runtime from oclaw.platform.config.paths import PROJECT_ROOT, db_path from oclaw.platform.config.passwords import load_expected_password from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.agents.specialists import SPECIALISTS -from oclaw.tools.mcp.installer import ( +from oclaw.runtime.agents.specialists import SPECIALISTS +from oclaw.runtime.tools.mcp.installer import ( _safe_server_id, detect_local_dependencies, install_mcp_server, preflight_mcp_server, uninstall_mcp_server, ) -from oclaw.tools.mcp.market import search_mcp_market, trending_mcp_market -from oclaw.tools.mcp.manifest import McpServerManifest -from oclaw.ops.mcp_registry_export import ( +from oclaw.runtime.tools.mcp.market import search_mcp_market, trending_mcp_market +from oclaw.runtime.tools.mcp.manifest import McpServerManifest +from oclaw.runtime.operations.mcp_registry_export import ( build_mcp_install_export_document, mcp_migrated_json_path, persist_mcp_migrated_file, ) -from oclaw.tools.mcp.filesystem_argv import build_mcp_process_command -from oclaw.tools.mcp.registry import McpRegistry -from oclaw.tools.mcp.runtime import McpProcessRuntime -from oclaw.admin.mcp_e2e_probe import build_mcp_e2e_probe_plans -from oclaw.tools.catalog import default_registry -from oclaw.chat.tool_runtime import ToolExecutionContext, ToolExecutor +from oclaw.runtime.tools.mcp.filesystem_argv import build_mcp_process_command +from oclaw.runtime.tools.mcp.registry import McpRegistry +from oclaw.runtime.tools.mcp.runtime import McpProcessRuntime +from oclaw.interfaces.admin.mcp_e2e_probe import build_mcp_e2e_probe_plans +from oclaw.runtime.tools.catalog import default_registry +from oclaw.runtime.chat.tool_runtime import ToolExecutionContext, ToolExecutor from oclaw.platform.llm.chat_models import LLMToolCall -from oclaw.openclaw_runtime.agent_core_attempt import ALL_ATTEMPT_ERROR_CODES -from oclaw.openclaw_runtime.agent_core_run import DEFAULT_RETRYABLE_ERROR_CODES, resolve_retryable_error_codes +from oclaw.runtime.agent_core_attempt import ALL_ATTEMPT_ERROR_CODES +from oclaw.runtime.agent_core_run import DEFAULT_RETRYABLE_ERROR_CODES, resolve_retryable_error_codes _WECOM_CLEAR_KEYS = [ "wecom_mode", @@ -251,8 +251,8 @@ def build_admin_router() -> APIRouter: ctx = _resolve_auth(store, authorization) _require_permission(ctx, "admin:tenant:write") - from oclaw.tools.expert_registry import clear_expert_tool_cache - from oclaw.tools.public_registry import clear_public_tool_cache + from oclaw.runtime.tools.expert_registry import clear_expert_tool_cache + from oclaw.runtime.tools.public_registry import clear_public_tool_cache clear_public_tool_cache() clear_expert_tool_cache() @@ -309,7 +309,7 @@ def build_admin_router() -> APIRouter: store = SqliteStore(db_path()) ctx = _resolve_auth(store, authorization) _require_permission(ctx, "admin:tenant:write") - from oclaw.tools.exposure_plan import build_internal_tool_specs + from oclaw.runtime.tools.exposure_plan import build_internal_tool_specs r = str(role or "").strip().lower() available_roles = _ordered_roles() @@ -349,7 +349,7 @@ def build_admin_router() -> APIRouter: _require_permission(ctx, "admin:tenant:write") from oclaw.platform.llm.tool_wire_policy import load_role_mode_for_role, load_tool_policies_dict_for_role - from oclaw.tools.exposure_plan import build_internal_tool_specs, build_llm_tools_plan + from oclaw.runtime.tools.exposure_plan import build_internal_tool_specs, build_llm_tools_plan roles = _ordered_roles() items: list[dict[str, Any]] = [] @@ -412,7 +412,7 @@ def build_admin_router() -> APIRouter: store = SqliteStore(db_path()) ctx = _resolve_auth(store, authorization) _require_permission(ctx, "admin:tenant:write") - from oclaw.tools.exposure_plan import build_llm_tools_plan + from oclaw.runtime.tools.exposure_plan import build_llm_tools_plan r = str(role or "").strip().lower() available_roles = _ordered_roles() @@ -1312,8 +1312,8 @@ def build_admin_router() -> APIRouter: return {"ok": True, "trace": rows} @router.get("/admin/api/oclaw/tasks") - @router.get("/admin/api/openclaw/tasks") - def api_openclaw_tasks( + @router.get("/admin/api/oclaw/tasks") + def api_oclaw_tasks( task_id: str | None = Query(default=None), session_id: str | None = Query(default=None), status: str | None = Query(default=None), @@ -1327,9 +1327,9 @@ def build_admin_router() -> APIRouter: sid = str(session_id or "").strip() or None st = str(status or "").strip() or None if task_id: - task = store.openclaw_task_get(task_id=str(task_id), tenant_id=tenant_id) + task = store.oclaw_task_get(task_id=str(task_id), tenant_id=tenant_id) return {"ok": True, "task": None if not task else task.__dict__} - rows = store.openclaw_task_list( + rows = store.oclaw_task_list( status=st, session_id=sid, tenant_id=tenant_id, @@ -1342,8 +1342,8 @@ def build_admin_router() -> APIRouter: return {"ok": True, "tasks": [r.__dict__ for r in rows], "counts": by_status} @router.get("/admin/api/oclaw/runs") - @router.get("/admin/api/openclaw/runs") - def api_openclaw_runs( + @router.get("/admin/api/oclaw/runs") + def api_oclaw_runs( run_id: str | None = Query(default=None), session_id: str | None = Query(default=None), status: str | None = Query(default=None), @@ -1356,14 +1356,14 @@ def build_admin_router() -> APIRouter: _require_permission(ctx, "admin:read") tenant_id = str(ctx.get("tenant_id") or "") if run_id: - row = store.openclaw_run_get(run_id=str(run_id), tenant_id=tenant_id) + row = store.oclaw_run_get(run_id=str(run_id), tenant_id=tenant_id) if not row: return {"ok": True, "run": None} out = dict(row.__dict__) if int(include_attempts or 0): - out["attempts"] = store.openclaw_attempt_list(run_id=row.run_id, limit=50) + out["attempts"] = store.oclaw_attempt_list(run_id=row.run_id, limit=50) return {"ok": True, "run": out} - rows = store.openclaw_run_list( + rows = store.oclaw_run_list( tenant_id=tenant_id, session_id=str(session_id or "").strip() or None, status=str(status or "").strip() or None, @@ -1376,7 +1376,7 @@ def build_admin_router() -> APIRouter: by_status[s] = int(by_status.get(s, 0)) + 1 item = dict(r.__dict__) if int(include_attempts or 0): - item["attempts"] = store.openclaw_attempt_list(run_id=r.run_id, limit=20) + item["attempts"] = store.oclaw_attempt_list(run_id=r.run_id, limit=20) out_rows.append(item) configured_retryable = sorted(resolve_retryable_error_codes(store=store)) return { @@ -1384,7 +1384,7 @@ def build_admin_router() -> APIRouter: "runs": out_rows, "counts": by_status, "retry_policy": { - "setting_key": "AIA_OPENCLAW_RETRYABLE_ERROR_CODES", + "setting_key": "AIA_OCLAW_RETRYABLE_ERROR_CODES", "effective_retryable_error_codes": configured_retryable, "default_retryable_error_codes": list(DEFAULT_RETRYABLE_ERROR_CODES), }, @@ -1440,7 +1440,7 @@ def build_admin_router() -> APIRouter: store = SqliteStore(db_path()) ctx = _resolve_auth(store, authorization) _require_permission(ctx, "admin:tenant:write") - # OpenClaw takeover: legacy tool-policy switches are disconnected (kept in DB for later). + # Oclaw takeover: legacy tool-policy switches are disconnected (kept in DB for later). disabled = False retry_mode = "first_round_only" loop_state_machine = True @@ -1451,9 +1451,9 @@ def build_admin_router() -> APIRouter: turn_max_tool_workers = max(1, min(int(tmw_raw), 32)) if tmw_raw.isdigit() else 8 turn_max_tool_rounds = max(1, min(int(tmr_raw), 30)) if tmr_raw.isdigit() else 8 turn_max_context_messages = max(10, min(int(tmc_raw), 400)) if tmc_raw.isdigit() else 80 - # OpenClaw takeover: legacy runner switches are disconnected (kept in DB for later). - turn_runner_impl = "openclaw" - # OpenClaw takeover: legacy manager decision mode is disconnected (kept in DB for later). + # Oclaw takeover: legacy runner switches are disconnected (kept in DB for later). + turn_runner_impl = "oclaw" + # Oclaw takeover: legacy manager decision mode is disconnected (kept in DB for later). manager_decision_mode = "" sse_raw = str(store.get_setting("AIA_SSE_QUEUE_MAXSIZE") or "").strip() sse_queue_maxsize = max(200, min(int(sse_raw), 50_000)) if sse_raw.isdigit() else 2000 @@ -1475,10 +1475,10 @@ def build_admin_router() -> APIRouter: tool_llm_message_max_chars = 0 mcp_fs_raw = str(store.get_setting("AIA_MCP_FILESYSTEM_EXTRA_ROOTS") or "").strip() mcp_env_allow_raw = str(store.get_setting("AIA_MCP_ENV_ALLOWLIST") or "").strip() - retry_codes_raw = str(store.get_setting("AIA_OPENCLAW_RETRYABLE_ERROR_CODES") or "").strip() + retry_codes_raw = str(store.get_setting("AIA_OCLAW_RETRYABLE_ERROR_CODES") or "").strip() if not retry_codes_raw: retry_codes_raw = ",".join(DEFAULT_RETRYABLE_ERROR_CODES) - retry_strict_raw = str(store.get_setting("AIA_OPENCLAW_RETRY_CODES_STRICT_MODE") or "").strip().lower() + retry_strict_raw = str(store.get_setting("AIA_OCLAW_RETRY_CODES_STRICT_MODE") or "").strip().lower() retry_codes_strict_mode = retry_strict_raw in ("1", "true", "yes", "on") wc_workers_raw = str(store.get_setting("AIA_WECOM_LONGCONN_WORKERS") or "").strip() if not wc_workers_raw: @@ -1508,8 +1508,8 @@ def build_admin_router() -> APIRouter: "tool_llm_message_max_chars": tool_llm_message_max_chars, "mcp_filesystem_extra_roots": mcp_fs_raw, "mcp_env_allowlist": mcp_env_allow_raw, - "openclaw_retryable_error_codes": retry_codes_raw, - "openclaw_retry_codes_strict_mode": retry_codes_strict_mode, + "oclaw_retryable_error_codes": retry_codes_raw, + "oclaw_retry_codes_strict_mode": retry_codes_strict_mode, "wecom_longconn_workers": wecom_longconn_workers, "wecom_longconn_inbound_queue_maxsize": wecom_longconn_inbound_queue_maxsize, } @@ -1523,7 +1523,7 @@ def build_admin_router() -> APIRouter: store = SqliteStore(db_path()) ctx = _resolve_auth(store, authorization) _require_permission(ctx, "admin:tenant:write") - # OpenClaw takeover: legacy tool-policy switches are disconnected (kept in DB for later). + # Oclaw takeover: legacy tool-policy switches are disconnected (kept in DB for later). disable_confirm = False retry_mode = "first_round_only" sm = True @@ -1531,7 +1531,7 @@ def build_admin_router() -> APIRouter: tmw = payload.get("turn_max_tool_workers", 8) tmr = payload.get("turn_max_tool_rounds", 8) tmc = payload.get("turn_max_context_messages", 80) - tri = "openclaw" + tri = "oclaw" try: tmw_n = max(1, min(int(tmw), 32)) except Exception: @@ -1562,8 +1562,8 @@ def build_admin_router() -> APIRouter: tmsg = payload.get("tool_llm_message_max_chars", 0) mcp_fs_roots = str(payload.get("mcp_filesystem_extra_roots") or "").strip() mcp_env_allow = str(payload.get("mcp_env_allowlist") or "").strip() - retry_codes = str(payload.get("openclaw_retryable_error_codes") or "").strip() - retry_codes_strict_mode = bool(payload.get("openclaw_retry_codes_strict_mode", False)) + retry_codes = str(payload.get("oclaw_retryable_error_codes") or "").strip() + retry_codes_strict_mode = bool(payload.get("oclaw_retry_codes_strict_mode", False)) unknown_retry_codes: list[str] = [] if retry_codes: vals = [x.strip().lower() for x in retry_codes.split(",") if x and x.strip()] @@ -1631,8 +1631,8 @@ def build_admin_router() -> APIRouter: store.set_setting("AIA_TOOL_LLM_MESSAGE_MAX_CHARS", str(tmsg_n)) store.set_setting("AIA_MCP_FILESYSTEM_EXTRA_ROOTS", mcp_fs_roots) store.set_setting("AIA_MCP_ENV_ALLOWLIST", mcp_env_allow) - store.set_setting("AIA_OPENCLAW_RETRYABLE_ERROR_CODES", retry_codes) - store.set_setting("AIA_OPENCLAW_RETRY_CODES_STRICT_MODE", "1" if retry_codes_strict_mode else "0") + store.set_setting("AIA_OCLAW_RETRYABLE_ERROR_CODES", retry_codes) + store.set_setting("AIA_OCLAW_RETRY_CODES_STRICT_MODE", "1" if retry_codes_strict_mode else "0") store.set_setting("AIA_WECOM_LONGCONN_WORKERS", str(wc_workers_n)) store.set_setting("AIA_WECOM_LONGCONN_INBOUND_QUEUE_MAXSIZE", str(wc_in_q_n)) store.set_setting("WECOM_LONGCONN_WORKERS", str(wc_workers_n)) @@ -1642,7 +1642,7 @@ def build_admin_router() -> APIRouter: actor_user_id=ctx["user_id"], action="tool_policy_update", target_type="tool_policy", - target_id="AIA_TURN_MAX_TOOL_WORKERS,AIA_TURN_MAX_TOOL_ROUNDS,AIA_TURN_MAX_CONTEXT_MESSAGES,AIA_SSE_QUEUE_MAXSIZE,AIA_TOOL_LOG_MAX_CHARS,AIA_ENABLE_MCP_TOOLS,AIA_ENABLE_PLUGIN_TOOLS,AIA_SKILL_RUNTIME_ENABLED,AIA_SKILL_AUTO_INSTALL_ENABLED,AIA_TOOL_LLM_MESSAGE_MAX_CHARS,AIA_MCP_FILESYSTEM_EXTRA_ROOTS,AIA_MCP_ENV_ALLOWLIST,AIA_OPENCLAW_RETRYABLE_ERROR_CODES,AIA_OPENCLAW_RETRY_CODES_STRICT_MODE,AIA_WECOM_LONGCONN_WORKERS,AIA_WECOM_LONGCONN_INBOUND_QUEUE_MAXSIZE", + target_id="AIA_TURN_MAX_TOOL_WORKERS,AIA_TURN_MAX_TOOL_ROUNDS,AIA_TURN_MAX_CONTEXT_MESSAGES,AIA_SSE_QUEUE_MAXSIZE,AIA_TOOL_LOG_MAX_CHARS,AIA_ENABLE_MCP_TOOLS,AIA_ENABLE_PLUGIN_TOOLS,AIA_SKILL_RUNTIME_ENABLED,AIA_SKILL_AUTO_INSTALL_ENABLED,AIA_TOOL_LLM_MESSAGE_MAX_CHARS,AIA_MCP_FILESYSTEM_EXTRA_ROOTS,AIA_MCP_ENV_ALLOWLIST,AIA_OCLAW_RETRYABLE_ERROR_CODES,AIA_OCLAW_RETRY_CODES_STRICT_MODE,AIA_WECOM_LONGCONN_WORKERS,AIA_WECOM_LONGCONN_INBOUND_QUEUE_MAXSIZE", status="ok", detail={ "disable_tool_confirm": disable_confirm, @@ -1663,8 +1663,8 @@ def build_admin_router() -> APIRouter: "tool_llm_message_max_chars": tmsg_n, "mcp_filesystem_extra_roots": mcp_fs_roots, "mcp_env_allowlist": mcp_env_allow, - "openclaw_retryable_error_codes": retry_codes, - "openclaw_retry_codes_strict_mode": retry_codes_strict_mode, + "oclaw_retryable_error_codes": retry_codes, + "oclaw_retry_codes_strict_mode": retry_codes_strict_mode, "unknown_retryable_error_codes": unknown_retry_codes, "wecom_longconn_workers": wc_workers_n, "wecom_longconn_inbound_queue_maxsize": wc_in_q_n, @@ -1690,8 +1690,8 @@ def build_admin_router() -> APIRouter: "tool_llm_message_max_chars": tmsg_n, "mcp_filesystem_extra_roots": mcp_fs_roots, "mcp_env_allowlist": mcp_env_allow, - "openclaw_retryable_error_codes": retry_codes, - "openclaw_retry_codes_strict_mode": retry_codes_strict_mode, + "oclaw_retryable_error_codes": retry_codes, + "oclaw_retry_codes_strict_mode": retry_codes_strict_mode, "unknown_retryable_error_codes": unknown_retry_codes, "wecom_longconn_workers": wc_workers_n, "wecom_longconn_inbound_queue_maxsize": wc_in_q_n, @@ -2804,7 +2804,7 @@ def build_admin_router() -> APIRouter: _require_tenant_scope(ctx, tenant_id) user_id = str(payload.get("user_id") or "").strip() or None from oclaw.platform.embeddings.embedding_client import build_default_embedding_client - from oclaw.orchestration.vector_store import build_vector_store, MemoryVectorItem + from oclaw.runtime.orchestration.vector_store import build_vector_store, MemoryVectorItem client = build_default_embedding_client() vector = build_vector_store(store) @@ -3054,9 +3054,9 @@ def build_admin_router() -> APIRouter: rows = [r for r in rows if str(r.get("status") or "") == st] return {"ok": True, "items": rows} - from oclaw.admin.chat_api import include_chat_routes - from oclaw.admin.models_api import include_model_mgmt_routes - from oclaw.admin.skills_api import include_skill_routes + from oclaw.interfaces.admin.chat_api import include_chat_routes + from oclaw.interfaces.admin.models_api import include_model_mgmt_routes + from oclaw.interfaces.admin.skills_api import include_skill_routes include_chat_routes(router, resolve_auth=_resolve_auth) include_model_mgmt_routes(router, resolve_auth=_resolve_auth) diff --git a/admin/skills_api.py b/interfaces/admin/skills_api.py similarity index 96% rename from admin/skills_api.py rename to interfaces/admin/skills_api.py index 807750e0..ee0fe065 100644 --- a/admin/skills_api.py +++ b/interfaces/admin/skills_api.py @@ -6,8 +6,8 @@ from typing import Any from fastapi import APIRouter, Body, Header, HTTPException -from oclaw.agents.factory import build_gateway_executor -from oclaw.openclaw_runtime.skill_installer import ( +from oclaw.runtime.agents.factory import build_gateway_executor +from oclaw.runtime.skill_installer import ( auto_install_skill_from_payload, create_skill_from_template, install_skill_from_local_dir, @@ -15,7 +15,7 @@ from oclaw.openclaw_runtime.skill_installer import ( list_skills_with_status, set_skill_enabled, ) -from oclaw.openclaw_runtime.skill_role_binding import ( +from oclaw.runtime.skill_role_binding import ( SKILL_ROLE_BINDING_ENABLED_SETTING, SKILL_ROLE_BINDING_KEY, load_skill_role_binding_dict, @@ -23,12 +23,12 @@ from oclaw.openclaw_runtime.skill_role_binding import ( ordered_binding_roles, skill_role_binding_enabled, ) -from oclaw.openclaw_runtime.skills_prompt import collect_skill_catalog_entries -from oclaw.openclaw_runtime.skills import _allowed_tool_names_after_wire_policy, discover_workspace_skill_manifests +from oclaw.runtime.skills_prompt import collect_skill_catalog_entries +from oclaw.runtime.skills import _allowed_tool_names_after_wire_policy, discover_workspace_skill_manifests from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.skills.clawhub_client import get_skill_detail as clawhub_get_skill_detail -from oclaw.tools.skills.clawhub_client import search_skills as clawhub_search_skills +from oclaw.runtime.tools.skills.clawhub_client import get_skill_detail as clawhub_get_skill_detail +from oclaw.runtime.tools.skills.clawhub_client import search_skills as clawhub_search_skills def include_skill_routes( @@ -393,7 +393,7 @@ def include_skill_routes( name = str(payload.get("name") or "").strip() desc = str(payload.get("description") or "").strip() body = str(payload.get("body_markdown") or "").strip() - md = payload.get("metadata_openclaw") + md = payload.get("metadata_oclaw") md = dict(md) if isinstance(md, dict) else {} overwrite = bool(payload.get("overwrite")) out = create_skill_from_template( @@ -401,7 +401,7 @@ def include_skill_routes( name=name, description=desc, body_markdown=body, - metadata_openclaw=md, + metadata_oclaw=md, overwrite=overwrite, ) _audit( @@ -527,8 +527,8 @@ def include_skill_routes( "name": str(payload.get("name") or "").strip() or target, "description": str(payload.get("description") or "retry auto install"), "body_markdown": str(payload.get("body_markdown") or ""), - "metadata_openclaw": dict(payload.get("metadata_openclaw") or {}) - if isinstance(payload.get("metadata_openclaw"), dict) + "metadata_oclaw": dict(payload.get("metadata_oclaw") or {}) + if isinstance(payload.get("metadata_oclaw"), dict) else {}, }, ) diff --git a/admin/static/app.js b/interfaces/admin/static/app.js similarity index 99% rename from admin/static/app.js rename to interfaces/admin/static/app.js index 760edd7b..3e7151c7 100644 --- a/admin/static/app.js +++ b/interfaces/admin/static/app.js @@ -3580,7 +3580,7 @@ async function renderPlugins() { turn_max_tool_workers: 8, turn_max_tool_rounds: 8, turn_max_context_messages: 80, - turn_runner_impl: "openclaw", + turn_runner_impl: "oclaw", manager_decision_mode: "", sse_queue_maxsize: 2000, tool_log_max_chars: 200000, @@ -3589,8 +3589,8 @@ async function renderPlugins() { tool_llm_message_max_chars: 0, mcp_filesystem_extra_roots: "", mcp_env_allowlist: "", - openclaw_retryable_error_codes: "", - openclaw_retry_codes_strict_mode: false, + oclaw_retryable_error_codes: "", + oclaw_retry_codes_strict_mode: false, wecom_longconn_workers: 2, wecom_longconn_inbound_queue_maxsize: 200, }; @@ -3818,13 +3818,13 @@ async function renderPlugins() { value: String(toolPolicy.mcp_env_allowlist || ""), placeholder: "BRAVE_API_KEY,GOOGLE_OAUTH_CREDENTIALS,...", }); - const openclawRetryableErrorCodesInput = el("input", { + const oclawRetryableErrorCodesInput = el("input", { class: "input", - value: String(toolPolicy.openclaw_retryable_error_codes || ""), + value: String(toolPolicy.oclaw_retryable_error_codes || ""), placeholder: "provider_timeout,provider_rate_limited,...", }); - const openclawRetryCodesStrictModeCb = el("input", { type: "checkbox" }); - openclawRetryCodesStrictModeCb.checked = !!toolPolicy.openclaw_retry_codes_strict_mode; + const oclawRetryCodesStrictModeCb = el("input", { type: "checkbox" }); + oclawRetryCodesStrictModeCb.checked = !!toolPolicy.oclaw_retry_codes_strict_mode; const wecomLongconnWorkersInput = el("input", { class: "input", type: "number", @@ -3857,8 +3857,8 @@ async function renderPlugins() { tool_llm_message_max_chars: Number(toolLlmMessageMaxCharsInput.value || 0), mcp_filesystem_extra_roots: String(mcpFilesystemExtraRootsInput.value || ""), mcp_env_allowlist: String(mcpEnvAllowlistInput.value || ""), - openclaw_retryable_error_codes: String(openclawRetryableErrorCodesInput.value || ""), - openclaw_retry_codes_strict_mode: !!openclawRetryCodesStrictModeCb.checked, + oclaw_retryable_error_codes: String(oclawRetryableErrorCodesInput.value || ""), + oclaw_retry_codes_strict_mode: !!oclawRetryCodesStrictModeCb.checked, wecom_longconn_workers: Number(wecomLongconnWorkersInput.value || 2), wecom_longconn_inbound_queue_maxsize: Number(wecomLongconnInboundQueueInput.value || 200), }); @@ -4948,9 +4948,9 @@ async function renderPlugins() { ]), el("div", { class: "row" }, [ el("label", { text: "oclaw retryable error codes (comma separated)" }), - openclawRetryableErrorCodesInput, + oclawRetryableErrorCodesInput, ]), - el("label", { class: "kv" }, [openclawRetryCodesStrictModeCb, document.createTextNode(" Strict mode: reject unknown retry codes")]), + el("label", { class: "kv" }, [oclawRetryCodesStrictModeCb, document.createTextNode(" Strict mode: reject unknown retry codes")]), el("div", { class: "row" }, [ el("label", { text: "WeCom longconn workers (1-8)" }), wecomLongconnWorkersInput, diff --git a/admin/static/chat.html b/interfaces/admin/static/chat.html similarity index 100% rename from admin/static/chat.html rename to interfaces/admin/static/chat.html diff --git a/admin/static/chat.js b/interfaces/admin/static/chat.js similarity index 99% rename from admin/static/chat.js rename to interfaces/admin/static/chat.js index b61a1d6d..2c9fe479 100644 --- a/admin/static/chat.js +++ b/interfaces/admin/static/chat.js @@ -1037,7 +1037,7 @@ function parseReasoningSegments(raw) { return parts.length ? parts : [{ type: "text", text: "" }]; } -// OpenClaw-style: strip // blocks from visible text, +// Oclaw-style: strip // blocks from visible text, // while keeping code fences intact. This avoids "reasoning leaking" into正文 and // handles truncated streams (unfinished tags) safely. const _OC_QUICK_TAG_RE = /<\s*\/?\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking|final)\b/i; @@ -1172,7 +1172,7 @@ function normalizeStreamBodyForUi(raw) { .replace(/\n{3,}/g, "\n\n"); } -// OpenClaw-style streaming stitcher: reduce boundary artifacts without destroying layout. +// Oclaw-style streaming stitcher: reduce boundary artifacts without destroying layout. function createStreamStitcher() { let prevTail = ""; return { @@ -2607,7 +2607,7 @@ async function renderChatUi() { if (String(p.status || "") === "started") { continue; } - // OpenClaw-style: response ack does not terminate the stream. + // Oclaw-style: response ack does not terminate the stream. // Wait strictly for chat final/aborted/error or session.message assistant. continue; } diff --git a/admin/static/default-user-avatar.svg b/interfaces/admin/static/default-user-avatar.svg similarity index 100% rename from admin/static/default-user-avatar.svg rename to interfaces/admin/static/default-user-avatar.svg diff --git a/admin/static/index.html b/interfaces/admin/static/index.html similarity index 100% rename from admin/static/index.html rename to interfaces/admin/static/index.html diff --git a/admin/static/oliver.svg b/interfaces/admin/static/oliver.svg similarity index 100% rename from admin/static/oliver.svg rename to interfaces/admin/static/oliver.svg diff --git a/admin/static/styles.css b/interfaces/admin/static/styles.css similarity index 100% rename from admin/static/styles.css rename to interfaces/admin/static/styles.css diff --git a/admin/static/theme-deepseek.css b/interfaces/admin/static/theme-deepseek.css similarity index 100% rename from admin/static/theme-deepseek.css rename to interfaces/admin/static/theme-deepseek.css diff --git a/app_server/__init__.py b/interfaces/channels/__init__.py similarity index 100% rename from app_server/__init__.py rename to interfaces/channels/__init__.py diff --git a/channels/base.py b/interfaces/channels/base.py similarity index 100% rename from channels/base.py rename to interfaces/channels/base.py diff --git a/channels/wecom/__init__.py b/interfaces/channels/wecom/__init__.py similarity index 100% rename from channels/wecom/__init__.py rename to interfaces/channels/wecom/__init__.py diff --git a/channels/wecom/longconn_runner.py b/interfaces/channels/wecom/longconn_runner.py similarity index 99% rename from channels/wecom/longconn_runner.py rename to interfaces/channels/wecom/longconn_runner.py index 5bc10c00..399c48a5 100644 --- a/channels/wecom/longconn_runner.py +++ b/interfaces/channels/wecom/longconn_runner.py @@ -11,8 +11,8 @@ from collections import deque from pathlib import Path from typing import Any -from oclaw.application.gateway import process_inbound_payload_usecase -from oclaw.channels.wecom.normalize import normalize_wecom_event, normalize_wecom_event_batch +from oclaw.runtime.application.gateway import process_inbound_payload_usecase +from oclaw.interfaces.channels.wecom.normalize import normalize_wecom_event, normalize_wecom_event_batch from oclaw.platform.config.paths import db_path from oclaw.platform.integrations.wecom_client import WeComClient from oclaw.platform.persistence.sqlite_store import SqliteStore diff --git a/channels/wecom/normalize.py b/interfaces/channels/wecom/normalize.py similarity index 100% rename from channels/wecom/normalize.py rename to interfaces/channels/wecom/normalize.py diff --git a/channels/wecom/wecom_bridge.py b/interfaces/channels/wecom/wecom_bridge.py similarity index 95% rename from channels/wecom/wecom_bridge.py rename to interfaces/channels/wecom/wecom_bridge.py index 6c9cc063..c8af7d22 100644 --- a/channels/wecom/wecom_bridge.py +++ b/interfaces/channels/wecom/wecom_bridge.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Any -from oclaw.channels.base import ChannelAdapter, InboundMessage, OutboundMessage +from oclaw.interfaces.channels.base import ChannelAdapter, InboundMessage, OutboundMessage class WeComAdapter(ChannelAdapter): diff --git a/gateway/README.md b/interfaces/gateway/README.gateway.md similarity index 88% rename from gateway/README.md rename to interfaces/gateway/README.gateway.md index b9a024d4..7ee7823e 100644 --- a/gateway/README.md +++ b/interfaces/gateway/README.gateway.md @@ -1,6 +1,6 @@ # gateway -`gateway` 是对 `vendor/openclaw/oclaw/gateway` 的 Python 重写目录。 +`gateway` 是对 `vendor/oclaw/oclaw/gateway` 的 Python 重写目录。 ## 当前状态 - 已重写核心挂载链路: diff --git a/interfaces/gateway/context_builder.py b/interfaces/gateway/context_builder.py index e254f3f2..4a9fc1f4 100644 --- a/interfaces/gateway/context_builder.py +++ b/interfaces/gateway/context_builder.py @@ -2,9 +2,9 @@ from __future__ import annotations from typing import Any -from oclaw.openclaw_runtime.skill_installer import install_skill_from_registry_archive -from oclaw.tools.skills.clawhub_client import get_skill_detail as clawhub_get_skill_detail -from oclaw.tools.skills.clawhub_client import search_skills as clawhub_search_skills +from oclaw.runtime.skill_installer import install_skill_from_registry_archive +from oclaw.runtime.tools.skills.clawhub_client import get_skill_detail as clawhub_get_skill_detail +from oclaw.runtime.tools.skills.clawhub_client import search_skills as clawhub_search_skills def build_common_gateway_context(*, store: Any) -> dict[str, Any]: diff --git a/interfaces/gateway/dispatcher.py b/interfaces/gateway/dispatcher.py index 4f5e2c76..c61d183e 100644 --- a/interfaces/gateway/dispatcher.py +++ b/interfaces/gateway/dispatcher.py @@ -1,12 +1,12 @@ from __future__ import annotations -from oclaw.gateway.server_methods.agent import agent_handlers -from oclaw.gateway.server_methods.chat import chat_handlers -from oclaw.gateway.server_methods.commands import commands_handlers -from oclaw.gateway.server_methods.connect import connect_handlers -from oclaw.gateway.server_methods.sessions import sessions_handlers -from oclaw.gateway.server_methods.skills import skills_handlers -from oclaw.gateway.server_methods.shared_types import GatewayRequestHandlers +from oclaw.interfaces.gateway.server_methods.agent import agent_handlers +from oclaw.interfaces.gateway.server_methods.chat import chat_handlers +from oclaw.interfaces.gateway.server_methods.commands import commands_handlers +from oclaw.interfaces.gateway.server_methods.connect import connect_handlers +from oclaw.interfaces.gateway.server_methods.sessions import sessions_handlers +from oclaw.interfaces.gateway.server_methods.skills import skills_handlers +from oclaw.interfaces.gateway.server_methods.shared_types import GatewayRequestHandlers def build_gateway_method_handlers() -> GatewayRequestHandlers: diff --git a/gateway/python_extensions_loader.py b/interfaces/gateway/python_extensions_loader.py similarity index 95% rename from gateway/python_extensions_loader.py rename to interfaces/gateway/python_extensions_loader.py index 8d73ee08..4e3e3e4b 100644 --- a/gateway/python_extensions_loader.py +++ b/interfaces/gateway/python_extensions_loader.py @@ -8,7 +8,8 @@ from types import ModuleType from typing import Any, Iterable from types import SimpleNamespace -from oclaw.extensions.plugin_api import PluginEntry +from oclaw.platform.config.runtime_paths import runtime_extensions_root +from oclaw.runtime.extensions.plugin_api import PluginEntry def _sanitize_module_name(value: str) -> str: @@ -158,7 +159,7 @@ def discover_python_extension_entries( ext_id = ext_dir.name if wanted and ext_id not in wanted: continue - module_name = f"openclaw_pyext_{_sanitize_module_name(ext_id)}" + module_name = f"oclaw_pyext_{_sanitize_module_name(ext_id)}" module = _load_package_from_dir(package_name=module_name, package_dir=ext_dir) entry = getattr(module, "plugin_entry", None) if isinstance(entry, PluginEntry): @@ -193,9 +194,9 @@ def build_python_extensions_registry( api = RuntimePluginApi(plugin_config={}, config=app_config, logger=logger) # Support both workspace roots: - # - repo root (contains `oclaw/extensions`) - # - `oclaw` package root (contains `extensions`) - extension_roots = [ws_dir / "oclaw" / "extensions", ws_dir / "extensions"] + # - repo root (contains `oclaw/runtime/extensions`) + # - `oclaw` package root (contains `runtime/extensions`) + extension_roots = [runtime_extensions_root(), ws_dir / "oclaw" / "runtime" / "extensions", ws_dir / "runtime" / "extensions"] diagnostics: list[dict[str, Any]] = [] loaded_by_id: dict[str, LoadedExtension] = {} for root in extension_roots: diff --git a/gateway/server_methods/README.md b/interfaces/gateway/server_methods/README.md similarity index 95% rename from gateway/server_methods/README.md rename to interfaces/gateway/server_methods/README.md index 7bf3240e..3e5761bc 100644 --- a/gateway/server_methods/README.md +++ b/interfaces/gateway/server_methods/README.md @@ -1,6 +1,6 @@ # server_methods -这是 `vendor/openclaw/oclaw/gateway/server-methods` 的 Python 重写目录。 +这是 `vendor/oclaw/oclaw/gateway/server-methods` 的 Python 重写目录。 ## 当前已迁移 - `connect.ts` -> `connect.py` diff --git a/gateway/server_methods/__init__.py b/interfaces/gateway/server_methods/__init__.py similarity index 100% rename from gateway/server_methods/__init__.py rename to interfaces/gateway/server_methods/__init__.py diff --git a/gateway/server_methods/agent.py b/interfaces/gateway/server_methods/agent.py similarity index 97% rename from gateway/server_methods/agent.py rename to interfaces/gateway/server_methods/agent.py index 7f49395f..9f0a937e 100644 --- a/gateway/server_methods/agent.py +++ b/interfaces/gateway/server_methods/agent.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Any -from oclaw.agents.agent_scope import resolve_agent_id_from_session_key, resolve_session_agent_id +from oclaw.runtime.agents.agent_scope import resolve_agent_id_from_session_key, resolve_session_agent_id from .shared_types import GatewayRequestHandlers from .validation import error_shape @@ -92,7 +92,7 @@ def _agent_identity_get_handler(opts: dict[str, Any]) -> None: respond, { "agentId": resolved_agent_id, - "displayName": "OpenClaw Assistant", + "displayName": "Oclaw Assistant", "avatarUrl": None, }, ) diff --git a/gateway/server_methods/agents.py b/interfaces/gateway/server_methods/agents.py similarity index 100% rename from gateway/server_methods/agents.py rename to interfaces/gateway/server_methods/agents.py diff --git a/gateway/server_methods/attachment_normalize.py b/interfaces/gateway/server_methods/attachment_normalize.py similarity index 100% rename from gateway/server_methods/attachment_normalize.py rename to interfaces/gateway/server_methods/attachment_normalize.py diff --git a/gateway/server_methods/base_hash.py b/interfaces/gateway/server_methods/base_hash.py similarity index 100% rename from gateway/server_methods/base_hash.py rename to interfaces/gateway/server_methods/base_hash.py diff --git a/gateway/server_methods/channels.py b/interfaces/gateway/server_methods/channels.py similarity index 100% rename from gateway/server_methods/channels.py rename to interfaces/gateway/server_methods/channels.py diff --git a/gateway/server_methods/chat.py b/interfaces/gateway/server_methods/chat.py similarity index 100% rename from gateway/server_methods/chat.py rename to interfaces/gateway/server_methods/chat.py diff --git a/gateway/server_methods/commands.py b/interfaces/gateway/server_methods/commands.py similarity index 100% rename from gateway/server_methods/commands.py rename to interfaces/gateway/server_methods/commands.py diff --git a/gateway/server_methods/config.py b/interfaces/gateway/server_methods/config.py similarity index 97% rename from gateway/server_methods/config.py rename to interfaces/gateway/server_methods/config.py index 03b959d8..0a995bcc 100644 --- a/gateway/server_methods/config.py +++ b/interfaces/gateway/server_methods/config.py @@ -20,7 +20,7 @@ class _ConfigSnapshot: def _read_config_file_snapshot() -> _ConfigSnapshot: """Staging adapter. - The upstream TS version reads JSON5 from OpenClaw config root. + The upstream TS version reads JSON5 from Oclaw config root. Here we keep a minimal in-memory placeholder until full config I/O is ported. """ return _ConfigSnapshot(exists=False, valid=True, config={}) @@ -152,7 +152,7 @@ def _config_open_file_handler(opts: dict[str, Any]) -> Any: return None if not assert_valid_params(params, _validate_config_get_params, "config.openFile", respond): return None - config_path = os.getenv("OPENCLAW_CONFIG_PATH") or "oclaw/oclaw.json" + config_path = os.getenv("OCLAW_CONFIG_PATH") or "oclaw/oclaw.json" respond(True, {"ok": True, "path": config_path}, None, None) return None diff --git a/gateway/server_methods/connect.py b/interfaces/gateway/server_methods/connect.py similarity index 100% rename from gateway/server_methods/connect.py rename to interfaces/gateway/server_methods/connect.py diff --git a/gateway/server_methods/cron.py b/interfaces/gateway/server_methods/cron.py similarity index 100% rename from gateway/server_methods/cron.py rename to interfaces/gateway/server_methods/cron.py diff --git a/gateway/server_methods/devices.py b/interfaces/gateway/server_methods/devices.py similarity index 100% rename from gateway/server_methods/devices.py rename to interfaces/gateway/server_methods/devices.py diff --git a/gateway/server_methods/dispatcher.py b/interfaces/gateway/server_methods/dispatcher.py similarity index 100% rename from gateway/server_methods/dispatcher.py rename to interfaces/gateway/server_methods/dispatcher.py diff --git a/gateway/server_methods/doctor.py b/interfaces/gateway/server_methods/doctor.py similarity index 100% rename from gateway/server_methods/doctor.py rename to interfaces/gateway/server_methods/doctor.py diff --git a/gateway/server_methods/exec_approvals.py b/interfaces/gateway/server_methods/exec_approvals.py similarity index 96% rename from gateway/server_methods/exec_approvals.py rename to interfaces/gateway/server_methods/exec_approvals.py index 6ad888a7..4aaff08f 100644 --- a/gateway/server_methods/exec_approvals.py +++ b/interfaces/gateway/server_methods/exec_approvals.py @@ -30,11 +30,11 @@ def _norm_str(v: Any) -> str | None: def _state(context: Any) -> dict[str, Any]: if not isinstance(context, dict): - return {"path": ".openclaw/exec-approvals.json", "exists": False, "hash": None, "file": {}} + return {"path": ".oclaw/exec-approvals.json", "exists": False, "hash": None, "file": {}} st = context.get("_exec_approvals_state") if isinstance(st, dict): return st - created = {"path": ".openclaw/exec-approvals.json", "exists": False, "hash": None, "file": {}} + created = {"path": ".oclaw/exec-approvals.json", "exists": False, "hash": None, "file": {}} context["_exec_approvals_state"] = created return created diff --git a/gateway/server_methods/health.py b/interfaces/gateway/server_methods/health.py similarity index 100% rename from gateway/server_methods/health.py rename to interfaces/gateway/server_methods/health.py diff --git a/gateway/server_methods/image.py b/interfaces/gateway/server_methods/image.py similarity index 96% rename from gateway/server_methods/image.py rename to interfaces/gateway/server_methods/image.py index d4cc47fa..70e676fb 100644 --- a/gateway/server_methods/image.py +++ b/interfaces/gateway/server_methods/image.py @@ -4,6 +4,7 @@ import importlib.util from pathlib import Path from typing import Any, Callable +from oclaw.platform.config.runtime_paths import runtime_extensions_root from .shared_types import GatewayRequestHandlers from .validation import error_shape @@ -35,10 +36,10 @@ def _load_generate_image_fn() -> Callable[..., Any]: global _generate_image_fn if _generate_image_fn is not None: return _generate_image_fn - file_path = (Path(__file__).resolve().parents[2] / "extensions" / "image-generation-core" / "api.py").resolve() + file_path = (runtime_extensions_root() / "image-generation-core" / "api.py").resolve() if not file_path.exists(): # Backward compatibility for legacy layout. - file_path = Path("extensions/image-generation-core/api.py").resolve() + file_path = Path("runtime/extensions/image-generation-core/api.py").resolve() spec = importlib.util.spec_from_file_location("gateway_image_generation_core_api", str(file_path)) if spec is None or spec.loader is None: raise RuntimeError("failed to load image-generation-core api module") diff --git a/gateway/server_methods/logs.py b/interfaces/gateway/server_methods/logs.py similarity index 100% rename from gateway/server_methods/logs.py rename to interfaces/gateway/server_methods/logs.py diff --git a/gateway/server_methods/models.py b/interfaces/gateway/server_methods/models.py similarity index 100% rename from gateway/server_methods/models.py rename to interfaces/gateway/server_methods/models.py diff --git a/gateway/server_methods/models_auth_status.py b/interfaces/gateway/server_methods/models_auth_status.py similarity index 100% rename from gateway/server_methods/models_auth_status.py rename to interfaces/gateway/server_methods/models_auth_status.py diff --git a/gateway/server_methods/nodes.py b/interfaces/gateway/server_methods/nodes.py similarity index 100% rename from gateway/server_methods/nodes.py rename to interfaces/gateway/server_methods/nodes.py diff --git a/gateway/server_methods/nodes_pending.py b/interfaces/gateway/server_methods/nodes_pending.py similarity index 100% rename from gateway/server_methods/nodes_pending.py rename to interfaces/gateway/server_methods/nodes_pending.py diff --git a/gateway/server_methods/push.py b/interfaces/gateway/server_methods/push.py similarity index 97% rename from gateway/server_methods/push.py rename to interfaces/gateway/server_methods/push.py index e7ff2668..f1ec7be8 100644 --- a/gateway/server_methods/push.py +++ b/interfaces/gateway/server_methods/push.py @@ -41,7 +41,7 @@ def _push_test_handler(opts: dict[str, Any]) -> None: _bad(respond, "nodeId required") return - title = _norm_str(params.get("title")) or "OpenClaw" + title = _norm_str(params.get("title")) or "Oclaw" body = _norm_str(params.get("body")) or f"Push test for node {node_id}" environment = _norm_str(params.get("environment")) diff --git a/gateway/server_methods/record_shared.py b/interfaces/gateway/server_methods/record_shared.py similarity index 100% rename from gateway/server_methods/record_shared.py rename to interfaces/gateway/server_methods/record_shared.py diff --git a/gateway/server_methods/restart_request.py b/interfaces/gateway/server_methods/restart_request.py similarity index 100% rename from gateway/server_methods/restart_request.py rename to interfaces/gateway/server_methods/restart_request.py diff --git a/gateway/server_methods/send.py b/interfaces/gateway/server_methods/send.py similarity index 100% rename from gateway/server_methods/send.py rename to interfaces/gateway/server_methods/send.py diff --git a/gateway/server_methods/sessions.py b/interfaces/gateway/server_methods/sessions.py similarity index 99% rename from gateway/server_methods/sessions.py rename to interfaces/gateway/server_methods/sessions.py index 5320e41f..27b523a4 100644 --- a/gateway/server_methods/sessions.py +++ b/interfaces/gateway/server_methods/sessions.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Any -from oclaw.agents.agent_scope import resolve_default_agent_id +from oclaw.runtime.agents.agent_scope import resolve_default_agent_id from .shared_types import GatewayRequestHandlers from .telegram_send_normalize import normalize_transport_target_for_channel diff --git a/gateway/server_methods/shared_types.py b/interfaces/gateway/server_methods/shared_types.py similarity index 100% rename from gateway/server_methods/shared_types.py rename to interfaces/gateway/server_methods/shared_types.py diff --git a/gateway/server_methods/skills.py b/interfaces/gateway/server_methods/skills.py similarity index 96% rename from gateway/server_methods/skills.py rename to interfaces/gateway/server_methods/skills.py index 5086eb16..3905399f 100644 --- a/gateway/server_methods/skills.py +++ b/interfaces/gateway/server_methods/skills.py @@ -3,14 +3,14 @@ from __future__ import annotations import json from typing import Any -from oclaw.openclaw_runtime.skill_installer import ( +from oclaw.runtime.skill_installer import ( create_skill_from_template, install_skill_from_local_dir, install_skill_from_registry_archive, list_skills_with_status, set_skill_enabled, ) -from oclaw.openclaw_runtime.skills import discover_workspace_skill_manifests, load_skill_manifest +from oclaw.runtime.skills import discover_workspace_skill_manifests, load_skill_manifest from .shared_types import GatewayRequestHandlers from .validation import error_shape @@ -58,7 +58,7 @@ def _skills_bins_handler(opts: dict[str, Any]) -> None: respond = opts.get("respond") bins: set[str] = set() for m in discover_workspace_skill_manifests(): - oc = dict(m.metadata_openclaw or {}) + oc = dict(m.metadata_oclaw or {}) req = oc.get("requires") if isinstance(req, dict): for k in ("bins", "anyBins"): @@ -143,7 +143,7 @@ def _skills_detail_handler(opts: dict[str, Any]) -> None: "description": manifest.description, "skillDir": manifest.skill_dir, "skillFile": manifest.skill_file, - "metadata": {"openclaw": dict(manifest.metadata_openclaw)}, + "metadata": {"oclaw": dict(manifest.metadata_oclaw)}, "body": manifest.body, }, ) @@ -191,14 +191,14 @@ def _skills_install_handler(opts: dict[str, Any]) -> None: return description = _normalize_optional_str(params.get("description")) or f"{name} skill" body = _normalize_optional_str(params.get("body")) or "" - md = params.get("metadata_openclaw") + md = params.get("metadata_oclaw") md = dict(md) if isinstance(md, dict) else {} res = create_skill_from_template( store=store, name=name, description=description, body_markdown=body, - metadata_openclaw=md, + metadata_oclaw=md, overwrite=overwrite, ) _ok(respond, {"ok": res.ok, "result": res.__dict__}) diff --git a/gateway/server_methods/system.py b/interfaces/gateway/server_methods/system.py similarity index 100% rename from gateway/server_methods/system.py rename to interfaces/gateway/server_methods/system.py diff --git a/gateway/server_methods/talk.py b/interfaces/gateway/server_methods/talk.py similarity index 100% rename from gateway/server_methods/talk.py rename to interfaces/gateway/server_methods/talk.py diff --git a/gateway/server_methods/telegram_send_normalize.py b/interfaces/gateway/server_methods/telegram_send_normalize.py similarity index 96% rename from gateway/server_methods/telegram_send_normalize.py rename to interfaces/gateway/server_methods/telegram_send_normalize.py index a7fbdb8d..3eab1313 100644 --- a/gateway/server_methods/telegram_send_normalize.py +++ b/interfaces/gateway/server_methods/telegram_send_normalize.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Any -from oclaw.extensions.telegram import ( +from oclaw.runtime.extensions.telegram import ( normalize_telegram_messaging_target, parse_telegram_reply_to_message_id, parse_telegram_target, diff --git a/gateway/server_methods/tools_catalog.py b/interfaces/gateway/server_methods/tools_catalog.py similarity index 100% rename from gateway/server_methods/tools_catalog.py rename to interfaces/gateway/server_methods/tools_catalog.py diff --git a/gateway/server_methods/tools_effective.py b/interfaces/gateway/server_methods/tools_effective.py similarity index 100% rename from gateway/server_methods/tools_effective.py rename to interfaces/gateway/server_methods/tools_effective.py diff --git a/gateway/server_methods/tts.py b/interfaces/gateway/server_methods/tts.py similarity index 100% rename from gateway/server_methods/tts.py rename to interfaces/gateway/server_methods/tts.py diff --git a/gateway/server_methods/types.py b/interfaces/gateway/server_methods/types.py similarity index 100% rename from gateway/server_methods/types.py rename to interfaces/gateway/server_methods/types.py diff --git a/gateway/server_methods/update.py b/interfaces/gateway/server_methods/update.py similarity index 100% rename from gateway/server_methods/update.py rename to interfaces/gateway/server_methods/update.py diff --git a/gateway/server_methods/usage.py b/interfaces/gateway/server_methods/usage.py similarity index 100% rename from gateway/server_methods/usage.py rename to interfaces/gateway/server_methods/usage.py diff --git a/gateway/server_methods/validation.py b/interfaces/gateway/server_methods/validation.py similarity index 100% rename from gateway/server_methods/validation.py rename to interfaces/gateway/server_methods/validation.py diff --git a/gateway/server_methods/voicewake.py b/interfaces/gateway/server_methods/voicewake.py similarity index 100% rename from gateway/server_methods/voicewake.py rename to interfaces/gateway/server_methods/voicewake.py diff --git a/gateway/server_methods/web.py b/interfaces/gateway/server_methods/web.py similarity index 100% rename from gateway/server_methods/web.py rename to interfaces/gateway/server_methods/web.py diff --git a/gateway/server_methods/wizard.py b/interfaces/gateway/server_methods/wizard.py similarity index 100% rename from gateway/server_methods/wizard.py rename to interfaces/gateway/server_methods/wizard.py diff --git a/gateway/server_plugins.py b/interfaces/gateway/server_plugins.py similarity index 93% rename from gateway/server_plugins.py rename to interfaces/gateway/server_plugins.py index f3001647..719497b2 100644 --- a/gateway/server_plugins.py +++ b/interfaces/gateway/server_plugins.py @@ -4,6 +4,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Any, Callable +from oclaw.platform.config.runtime_paths import runtime_extensions_root from .python_extensions_loader import build_python_extensions_registry GatewayHandler = Callable[..., Any] @@ -44,8 +45,9 @@ def _resolve_gateway_startup_plugin_ids( return out # Auto-discover local plugins when explicit list is absent. roots = [ - ws_root / "oclaw" / "extensions", - ws_root / "extensions", + runtime_extensions_root(), + ws_root / "oclaw" / "runtime" / "extensions", + ws_root / "runtime" / "extensions", ] seen: set[str] = set() for root in roots: @@ -66,14 +68,14 @@ def _resolve_gateway_startup_plugin_ids( return out -def _load_openclaw_plugins( +def _load_oclaw_plugins( *, config: dict[str, Any], workspace_dir: str, only_plugin_ids: list[str], core_gateway_handlers: dict[str, GatewayHandler] | None = None, ) -> dict[str, Any]: - """Minimal Python replacement for loadOpenClawPlugins. + """Minimal Python replacement for loadOclawPlugins. This function intentionally returns a stable registry shape that matches the gateway startup contract used by this project. @@ -113,7 +115,7 @@ def load_gateway_plugins( plugin_registry = {"plugins": [], "gateway_handlers": {}, "http_routes": [], "diagnostics": []} return GatewayPluginLoadResult(plugin_registry=plugin_registry, gateway_methods=list(base_methods)) - plugin_registry = _load_openclaw_plugins( + plugin_registry = _load_oclaw_plugins( config=resolved_cfg, workspace_dir=workspace_dir, only_plugin_ids=chosen_plugin_ids, diff --git a/gateway/server_startup_plugins.py b/interfaces/gateway/server_startup_plugins.py similarity index 95% rename from gateway/server_startup_plugins.py rename to interfaces/gateway/server_startup_plugins.py index 484330b3..98b8506e 100644 --- a/gateway/server_startup_plugins.py +++ b/interfaces/gateway/server_startup_plugins.py @@ -3,8 +3,8 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable -from oclaw.agents.agent_scope import resolve_agent_workspace_dir, resolve_default_agent_id -from oclaw.agents.subagent_registry import init_subagent_registry +from oclaw.runtime.agents.agent_scope import resolve_agent_workspace_dir, resolve_default_agent_id +from oclaw.runtime.agents.subagent_registry import init_subagent_registry from .server_plugins import GatewayPluginLoadResult, load_gateway_plugins diff --git a/interfaces/http/fastapi_app.py b/interfaces/http/fastapi_app.py index 83b762bd..adf1c404 100644 --- a/interfaces/http/fastapi_app.py +++ b/interfaces/http/fastapi_app.py @@ -10,18 +10,18 @@ from fastapi import Request from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles -from oclaw.application.gateway import process_inbound_payload_usecase +from oclaw.runtime.application.gateway import process_inbound_payload_usecase from oclaw.interfaces.gateway.http_adapter import dispatch_gateway_http_method from oclaw.interfaces.ws import ws_gateway_loop -from oclaw.admin.routes import admin_static_dir, build_admin_router -from oclaw.agents.agent_scope import list_agent_ids, resolve_agent_workspace_dir, resolve_default_agent_id -from oclaw.gateway.server_startup_plugins import prepare_gateway_plugin_bootstrap -from oclaw.openclaw_runtime.hooks_runtime import ( +from oclaw.interfaces.admin.routes import admin_static_dir, build_admin_router +from oclaw.runtime.agents.agent_scope import list_agent_ids, resolve_agent_workspace_dir, resolve_default_agent_id +from oclaw.interfaces.gateway.server_startup_plugins import prepare_gateway_plugin_bootstrap +from oclaw.runtime.hooks_runtime import ( initialize_hooks_runtime, resolve_runtime_config, trigger_hook_event, ) -from oclaw.openclaw_runtime.skills import skill_runtime_diagnostics +from oclaw.runtime.skills import skill_runtime_diagnostics from oclaw.platform.config.paths import PROJECT_ROOT, db_path from oclaw.platform.persistence.sqlite_store import SqliteStore from oclaw.interfaces.http.weixin_ilink_api import router as weixin_ilink_router @@ -36,7 +36,7 @@ def _resolve_startup_workspace_dir(cfg: dict[str, Any]) -> str: return ws_text except Exception: pass - return str((PROJECT_ROOT / "oclaw" / "workspace-main").resolve()) + return str((PROJECT_ROOT / "oclaw" / "runtime" / "assets" / "agent_workspaces" / "workspace-main").resolve()) def _resolve_startup_workspace_dirs(cfg: dict[str, Any]) -> list[tuple[str, str]]: diff --git a/interfaces/http/weixin_ilink_api.py b/interfaces/http/weixin_ilink_api.py index a66e1db6..d23d2847 100644 --- a/interfaces/http/weixin_ilink_api.py +++ b/interfaces/http/weixin_ilink_api.py @@ -15,7 +15,7 @@ def _require_ilink_auth( authorization_type: str | None, authorization: str | None, ) -> None: - # Minimal contract required by openclaw-weixin: + # Minimal contract required by oclaw-weixin: # - AuthorizationType: ilink_bot_token # - Authorization: Bearer if (authorization_type or "").strip() != "ilink_bot_token": diff --git a/openclaw_protocol_schemas/agent.json b/interfaces/ws/protocol_schemas/agent.json similarity index 100% rename from openclaw_protocol_schemas/agent.json rename to interfaces/ws/protocol_schemas/agent.json diff --git a/openclaw_protocol_schemas/agent_wait.json b/interfaces/ws/protocol_schemas/agent_wait.json similarity index 100% rename from openclaw_protocol_schemas/agent_wait.json rename to interfaces/ws/protocol_schemas/agent_wait.json diff --git a/openclaw_protocol_schemas/chat.json b/interfaces/ws/protocol_schemas/chat.json similarity index 100% rename from openclaw_protocol_schemas/chat.json rename to interfaces/ws/protocol_schemas/chat.json diff --git a/openclaw_protocol_schemas/connect.json b/interfaces/ws/protocol_schemas/connect.json similarity index 100% rename from openclaw_protocol_schemas/connect.json rename to interfaces/ws/protocol_schemas/connect.json diff --git a/openclaw_protocol_schemas/frames.json b/interfaces/ws/protocol_schemas/frames.json similarity index 100% rename from openclaw_protocol_schemas/frames.json rename to interfaces/ws/protocol_schemas/frames.json diff --git a/openclaw_protocol_schemas/hello_ok.json b/interfaces/ws/protocol_schemas/hello_ok.json similarity index 100% rename from openclaw_protocol_schemas/hello_ok.json rename to interfaces/ws/protocol_schemas/hello_ok.json diff --git a/openclaw_protocol_schemas/sessions.json b/interfaces/ws/protocol_schemas/sessions.json similarity index 100% rename from openclaw_protocol_schemas/sessions.json rename to interfaces/ws/protocol_schemas/sessions.json diff --git a/interfaces/ws/runtime_impl.py b/interfaces/ws/runtime_impl.py index 48d0ce28..009efc53 100644 --- a/interfaces/ws/runtime_impl.py +++ b/interfaces/ws/runtime_impl.py @@ -32,7 +32,7 @@ from oclaw.interfaces.ws.runtime_loop import close_ws as close_ws_impl, run_conn from oclaw.interfaces.ws.server_methods_bridge import build_gateway_context, dispatch_via_server_methods from oclaw.interfaces.ws.turn_runner import run_agent_turn_via_bridge from oclaw.interfaces.ws.ws_schema import format_validation_errors, get_ws_schemas, validate_or_errors -from oclaw.openclaw_runtime.relay_pointer import validate_relay_share_envelope +from oclaw.runtime.relay_pointer import validate_relay_share_envelope class OclawWsGatewayConnection: diff --git a/interfaces/ws/schema_impl.py b/interfaces/ws/schema_impl.py index 20cb7af8..b9d82a13 100644 --- a/interfaces/ws/schema_impl.py +++ b/interfaces/ws/schema_impl.py @@ -2,14 +2,13 @@ from __future__ import annotations import json from dataclasses import dataclass -from pathlib import Path from typing import Any from jsonschema import Draft202012Validator -from oclaw.platform.config.paths import PROJECT_ROOT +from oclaw.platform.config.runtime_paths import ws_protocol_schemas_root -_SCHEMA_DIR = Path(PROJECT_ROOT) / "oclaw" / "openclaw_protocol_schemas" +_SCHEMA_DIR = ws_protocol_schemas_root() def _load_schema(name: str) -> dict[str, Any]: diff --git a/interfaces/ws/server_methods_bridge.py b/interfaces/ws/server_methods_bridge.py index ee77816d..e1e951e6 100644 --- a/interfaces/ws/server_methods_bridge.py +++ b/interfaces/ws/server_methods_bridge.py @@ -105,7 +105,7 @@ def build_gateway_context( rid = str(run_id or "").strip() if not rid: return {"status": "error", "runId": rid, "summary": "runId required"} - run = store.openclaw_run_get(run_id=rid) + run = store.oclaw_run_get(run_id=rid) if not run: return {"status": "pending", "runId": rid, "summary": "run not finished", "pollAfterMs": 250} status = str(run.status or "").strip().lower() diff --git a/interfaces/ws/turn_runner.py b/interfaces/ws/turn_runner.py index 94038c35..d79dbfac 100644 --- a/interfaces/ws/turn_runner.py +++ b/interfaces/ws/turn_runner.py @@ -5,9 +5,9 @@ import threading import uuid from typing import Any, Callable -from oclaw.agents.factory import build_gateway_executor -from oclaw.openclaw_runtime.gateway import OpenClawGateway -from oclaw.openclaw_runtime.types import StandardMessage +from oclaw.runtime.agents.factory import build_gateway_executor +from oclaw.runtime.gateway import OclawGateway +from oclaw.runtime.types import StandardMessage from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore @@ -27,7 +27,7 @@ async def run_agent_turn_via_bridge( msg_text = str(p.get("message") or "").strip() attachments = list(p.get("attachments") or []) store = SqliteStore(db_path()) - gw = OpenClawGateway(store=store) + gw = OclawGateway(store=store) ctx = conn.auth_ctx or {} tenant_id = str(ctx.get("tenant_id") or "") diff --git a/platform/config/runtime_paths.py b/platform/config/runtime_paths.py new file mode 100644 index 00000000..a4870b00 --- /dev/null +++ b/platform/config/runtime_paths.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from pathlib import Path + +from oclaw.platform.config.paths import PROJECT_ROOT + + +def oclaw_root() -> Path: + return (PROJECT_ROOT / "oclaw").resolve() + + +def runtime_root() -> Path: + return (oclaw_root() / "runtime").resolve() + + +def runtime_skills_root() -> Path: + return (runtime_root() / "skills").resolve() + + +def runtime_hooks_root() -> Path: + return (runtime_root() / "hooks").resolve() + + +def runtime_hooks_bundled_root() -> Path: + return (runtime_hooks_root() / "bundled").resolve() + + +def runtime_extensions_root() -> Path: + return (runtime_root() / "extensions").resolve() + + +def runtime_operations_scripts_root() -> Path: + return (runtime_root() / "operations" / "scripts").resolve() + + +def ws_protocol_schemas_root() -> Path: + return (oclaw_root() / "interfaces" / "ws" / "protocol_schemas").resolve() + + +__all__ = [ + "oclaw_root", + "runtime_root", + "runtime_skills_root", + "runtime_hooks_root", + "runtime_hooks_bundled_root", + "runtime_extensions_root", + "runtime_operations_scripts_root", + "ws_protocol_schemas_root", +] + diff --git a/platform/llm/OPENCLAW_MIT_LICENSE.txt b/platform/llm/MIT_LICENSE.txt similarity index 91% rename from platform/llm/OPENCLAW_MIT_LICENSE.txt rename to platform/llm/MIT_LICENSE.txt index 54c69f27..2b536854 100644 --- a/platform/llm/OPENCLAW_MIT_LICENSE.txt +++ b/platform/llm/MIT_LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Peter Steinberger (OpenClaw) +Copyright (c) 2025 Peter Steinberger (Oclaw) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,5 +23,5 @@ SOFTWARE. --- The Python modules `tool_call_id.py` and `replay_policy.py` in this directory -implement replay / tool-call-id handling inspired by OpenClaw's +implement replay / tool-call-id handling inspired by Oclaw's `src/agents/tool-call-id.ts` and `src/agents/transcript-policy.ts`. diff --git a/platform/llm/chat_models.py b/platform/llm/chat_models.py index 5b101bbf..6db6643b 100644 --- a/platform/llm/chat_models.py +++ b/platform/llm/chat_models.py @@ -37,7 +37,7 @@ def build_default_model() -> ChatModel: return RuleBasedChatModel() if mode == "ollama": try: - from oclaw.agents.factory import DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_MODEL + from oclaw.runtime.agents.factory import DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_MODEL return OpenAIChatModel(model=DEFAULT_OLLAMA_MODEL, api_key="ollama", base_url=DEFAULT_OLLAMA_BASE_URL) except Exception: diff --git a/platform/llm/replay_policy.py b/platform/llm/replay_policy.py index 65582f49..3760a33f 100644 --- a/platform/llm/replay_policy.py +++ b/platform/llm/replay_policy.py @@ -1,10 +1,10 @@ """Transcript replay policy for OpenAI-compatible Chat Completions. -Inspired by OpenClaw (MIT) `src/agents/transcript-policy.ts` defaults for +Inspired by Oclaw (MIT) `src/agents/transcript-policy.ts` defaults for `openai-completions`: enable strict tool-call-id sanitization so proxies and multi-provider histories do not break on id format / length. -See OPENCLAW_MIT_LICENSE.txt in this package. +See OCLAW_MIT_LICENSE.txt in this package. """ from __future__ import annotations diff --git a/platform/llm/tool_call_id.py b/platform/llm/tool_call_id.py index 270c6850..1e6dcf99 100644 --- a/platform/llm/tool_call_id.py +++ b/platform/llm/tool_call_id.py @@ -1,10 +1,10 @@ """OpenAI-compatible tool_call_id / tool result id rewriting. -Inspired by OpenClaw (MIT) `src/agents/tool-call-id.ts`: sanitize ids to strict +Inspired by Oclaw (MIT) `src/agents/tool-call-id.ts`: sanitize ids to strict alphanumeric form and keep assistant.tool_calls[].id paired with role=tool tool_call_id in encounter order. -See OPENCLAW_MIT_LICENSE.txt in this package. +See OCLAW_MIT_LICENSE.txt in this package. """ from __future__ import annotations diff --git a/platform/llm/tool_wire_policy.py b/platform/llm/tool_wire_policy.py index 87f5d588..6f903896 100644 --- a/platform/llm/tool_wire_policy.py +++ b/platform/llm/tool_wire_policy.py @@ -705,7 +705,7 @@ def penalty_row_status( def build_tool_wire_snapshot(store: Any, *, role: str | None = None) -> dict[str, Any]: """Aggregate MCP install list + usage + policies for Admin GET.""" - from oclaw.tools.mcp.registry import McpRegistry + from oclaw.runtime.tools.mcp.registry import McpRegistry admin = load_merged_admin_config(store) role_norm = str(role or "").strip().lower() or None diff --git a/platform/llm/transports/google_gemini_sse.py b/platform/llm/transports/google_gemini_sse.py index 5c62f4d6..20585aca 100644 --- a/platform/llm/transports/google_gemini_sse.py +++ b/platform/llm/transports/google_gemini_sse.py @@ -10,7 +10,7 @@ from oclaw.platform.llm.transports.base import ChatModel, LLMResponse, LLMToolCa class GoogleGeminiChatModel(ChatModel): - """Native Google Gemini streaming transport (SSE), modeled after OpenClaw's google transport.""" + """Native Google Gemini streaming transport (SSE), modeled after Oclaw's google transport.""" def __init__(self, *, model: str, api_key: str, base_url: str | None = None): self.model = (model or "").strip() or "gemini-2.5-pro" @@ -34,7 +34,7 @@ class GoogleGeminiChatModel(ChatModel): { "name": name, "description": str(fn.get("description") or ""), - # OpenClaw uses `parametersJsonSchema` + # Oclaw uses `parametersJsonSchema` "parametersJsonSchema": fn.get("parameters") if isinstance(fn.get("parameters"), dict) else {"type": "object"}, } ) @@ -122,7 +122,7 @@ class GoogleGeminiChatModel(ChatModel): model_path = self.model if not model_path.startswith("models/") and not model_path.startswith("tunedModels/"): model_path = f"models/{model_path}" - # Prefer header auth (OpenClaw-style); avoid forcing key into query string. + # Prefer header auth (Oclaw-style); avoid forcing key into query string. return f"{base}/{model_path}:streamGenerateContent?alt=sse" @staticmethod diff --git a/platform/llm/transports/openai_chat_completions.py b/platform/llm/transports/openai_chat_completions.py index 29dfa346..f3f85e97 100644 --- a/platform/llm/transports/openai_chat_completions.py +++ b/platform/llm/transports/openai_chat_completions.py @@ -153,7 +153,7 @@ class OpenAIChatModel(ChatModel): try: from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore - from oclaw.tools.exposure_plan import build_llm_tools_plan + from oclaw.runtime.tools.exposure_plan import build_llm_tools_plan plan = build_llm_tools_plan( store=SqliteStore(db_path()), diff --git a/platform/persistence/sqlite_store.py b/platform/persistence/sqlite_store.py index 6f2469ea..792334d1 100644 --- a/platform/persistence/sqlite_store.py +++ b/platform/persistence/sqlite_store.py @@ -125,7 +125,7 @@ class SessionsListMeta: @dataclass(frozen=True) -class OpenClawTask: +class OclawTask: id: str tenant_id: str session_id: str @@ -143,7 +143,7 @@ class OpenClawTask: @dataclass(frozen=True) -class OpenClawRun: +class OclawRun: run_id: str tenant_id: str session_id: str @@ -774,7 +774,7 @@ class SqliteStore: ) conn.execute( """ - CREATE TABLE IF NOT EXISTS openclaw_task ( + CREATE TABLE IF NOT EXISTS oclaw_task ( id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, session_id TEXT NOT NULL, @@ -794,7 +794,7 @@ class SqliteStore: ) conn.execute( """ - CREATE TABLE IF NOT EXISTS openclaw_run ( + CREATE TABLE IF NOT EXISTS oclaw_run ( run_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, session_id TEXT NOT NULL, @@ -807,7 +807,7 @@ class SqliteStore: ) conn.execute( """ - CREATE TABLE IF NOT EXISTS openclaw_attempt ( + CREATE TABLE IF NOT EXISTS oclaw_attempt ( id INTEGER PRIMARY KEY AUTOINCREMENT, run_id TEXT NOT NULL, tenant_id TEXT NOT NULL, @@ -821,16 +821,16 @@ class SqliteStore: """ ) conn.execute( - "CREATE INDEX IF NOT EXISTS idx_openclaw_task_status_updated ON openclaw_task(status, updated_at)" + "CREATE INDEX IF NOT EXISTS idx_oclaw_task_status_updated ON oclaw_task(status, updated_at)" ) conn.execute( - "CREATE INDEX IF NOT EXISTS idx_openclaw_task_tenant_session ON openclaw_task(tenant_id, session_id, created_at DESC)" + "CREATE INDEX IF NOT EXISTS idx_oclaw_task_tenant_session ON oclaw_task(tenant_id, session_id, created_at DESC)" ) conn.execute( - "CREATE INDEX IF NOT EXISTS idx_openclaw_run_tenant_session ON openclaw_run(tenant_id, session_id, updated_at DESC)" + "CREATE INDEX IF NOT EXISTS idx_oclaw_run_tenant_session ON oclaw_run(tenant_id, session_id, updated_at DESC)" ) conn.execute( - "CREATE INDEX IF NOT EXISTS idx_openclaw_attempt_run_no ON openclaw_attempt(run_id, attempt_no)" + "CREATE INDEX IF NOT EXISTS idx_oclaw_attempt_run_no ON oclaw_attempt(run_id, attempt_no)" ) conn.execute( "CREATE INDEX IF NOT EXISTS idx_knowledge_chunk_source_updated ON knowledge_chunk(source, updated_at)" @@ -2685,7 +2685,7 @@ class SqliteStore: uid = str(user_id or "").strip() if not uid: return - from oclaw.agents.specialists import dump_agent_profile_bindings, parse_agent_profile_bindings + from oclaw.runtime.agents.specialists import dump_agent_profile_bindings, parse_agent_profile_bindings seeded_key = f"llm_personal_pool_seeded:{uid}" act_key = active_llm_profile_setting_key(uid, username) @@ -4953,22 +4953,22 @@ class SqliteStore: return int(cur.rowcount or 0) # ---------------------------- - # OpenClaw tasks + # Oclaw tasks # ---------------------------- - def openclaw_task_create( + def oclaw_task_create( self, *, tenant_id: str, session_id: str, task_type: str = "async_turn", payload: dict[str, Any] | None = None, - ) -> OpenClawTask: + ) -> OclawTask: tid = str(uuid.uuid4()) ts = utc_now_iso() with self._connect() as conn: conn.execute( """ - INSERT INTO openclaw_task + INSERT INTO oclaw_task (id, tenant_id, session_id, task_type, status, payload, result, attempt_count, claimed_by, lease_expires_at, last_error, created_at, updated_at, finished_at) VALUES (?, ?, ?, ?, 'queued', ?, '{}', 0, NULL, NULL, '', ?, ?, NULL) """, @@ -4982,18 +4982,18 @@ class SqliteStore: ts, ), ) - got = self.openclaw_task_get(task_id=tid) + got = self.oclaw_task_get(task_id=tid) if not got: - raise RuntimeError("failed to create openclaw task") + raise RuntimeError("failed to create oclaw task") return got - def openclaw_task_claim( + def oclaw_task_claim( self, *, worker_id: str, lease_seconds: int = 90, task_type: str | None = None, - ) -> OpenClawTask | None: + ) -> OclawTask | None: now = datetime.now(timezone.utc) lease_expires = (now + timedelta(seconds=max(15, min(int(lease_seconds or 90), 1800)))).isoformat() now_iso = now.isoformat() @@ -5006,7 +5006,7 @@ class SqliteStore: params.append(tt) row = conn.execute( f""" - SELECT id FROM openclaw_task + SELECT id FROM oclaw_task WHERE {where} ORDER BY created_at ASC LIMIT 1 @@ -5018,7 +5018,7 @@ class SqliteStore: task_id = str(row["id"]) cur = conn.execute( """ - UPDATE openclaw_task + UPDATE oclaw_task SET status = 'claimed', attempt_count = attempt_count + 1, claimed_by = ?, @@ -5031,14 +5031,14 @@ class SqliteStore: ) if not cur.rowcount: return None - return self.openclaw_task_get(task_id=task_id) + return self.oclaw_task_get(task_id=task_id) - def openclaw_task_finish(self, *, task_id: str, result: dict[str, Any] | None = None) -> bool: + def oclaw_task_finish(self, *, task_id: str, result: dict[str, Any] | None = None) -> bool: ts = utc_now_iso() with self._connect() as conn: cur = conn.execute( """ - UPDATE openclaw_task + UPDATE oclaw_task SET status = 'done', result = ?, lease_expires_at = NULL, @@ -5051,12 +5051,12 @@ class SqliteStore: ) return bool(cur.rowcount and cur.rowcount > 0) - def openclaw_task_fail(self, *, task_id: str, error: str, result: dict[str, Any] | None = None) -> bool: + def oclaw_task_fail(self, *, task_id: str, error: str, result: dict[str, Any] | None = None) -> bool: ts = utc_now_iso() with self._connect() as conn: cur = conn.execute( """ - UPDATE openclaw_task + UPDATE oclaw_task SET status = 'failed', result = ?, lease_expires_at = NULL, @@ -5069,7 +5069,7 @@ class SqliteStore: ) return bool(cur.rowcount and cur.rowcount > 0) - def openclaw_task_get(self, *, task_id: str, tenant_id: str | None = None) -> OpenClawTask | None: + def oclaw_task_get(self, *, task_id: str, tenant_id: str | None = None) -> OclawTask | None: where = "id = ?" params: list[Any] = [str(task_id)] if tenant_id: @@ -5079,7 +5079,7 @@ class SqliteStore: row = conn.execute( """ SELECT id, tenant_id, session_id, task_type, status, payload, result, attempt_count, claimed_by, lease_expires_at, last_error, created_at, updated_at, finished_at - FROM openclaw_task + FROM oclaw_task WHERE """ + where + """ LIMIT 1 """, @@ -5087,7 +5087,7 @@ class SqliteStore: ).fetchone() if not row: return None - return OpenClawTask( + return OclawTask( id=str(row["id"]), tenant_id=str(row["tenant_id"]), session_id=str(row["session_id"]), @@ -5104,14 +5104,14 @@ class SqliteStore: finished_at=str(row["finished_at"]) if row["finished_at"] else None, ) - def openclaw_task_list( + def oclaw_task_list( self, *, status: str | None = None, limit: int = 50, tenant_id: str | None = None, session_id: str | None = None, - ) -> list[OpenClawTask]: + ) -> list[OclawTask]: lim = max(1, min(int(limit or 50), 500)) where = "" params: list[Any] = [] @@ -5129,17 +5129,17 @@ class SqliteStore: rows = conn.execute( f""" SELECT id, tenant_id, session_id, task_type, status, payload, result, attempt_count, claimed_by, lease_expires_at, last_error, created_at, updated_at, finished_at - FROM openclaw_task + FROM oclaw_task {where_clause} ORDER BY created_at DESC LIMIT ? """, (*params, lim), ).fetchall() - out: list[OpenClawTask] = [] + out: list[OclawTask] = [] for row in rows: out.append( - OpenClawTask( + OclawTask( id=str(row["id"]), tenant_id=str(row["tenant_id"]), session_id=str(row["session_id"]), @@ -5158,7 +5158,7 @@ class SqliteStore: ) return out - def openclaw_run_upsert( + def oclaw_run_upsert( self, *, run_id: str, @@ -5171,7 +5171,7 @@ class SqliteStore: with self._connect() as conn: conn.execute( """ - INSERT INTO openclaw_run(run_id, tenant_id, session_id, status, payload, created_at, updated_at) + INSERT INTO oclaw_run(run_id, tenant_id, session_id, status, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(run_id) DO UPDATE SET status = excluded.status, @@ -5190,7 +5190,7 @@ class SqliteStore: ) return True - def openclaw_attempt_append( + def oclaw_attempt_append( self, *, run_id: str, @@ -5205,7 +5205,7 @@ class SqliteStore: with self._connect() as conn: cur = conn.execute( """ - INSERT INTO openclaw_attempt(run_id, tenant_id, session_id, attempt_no, status, reason, payload, created_at) + INSERT INTO oclaw_attempt(run_id, tenant_id, session_id, attempt_no, status, reason, payload, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) """, ( @@ -5221,13 +5221,13 @@ class SqliteStore: ) return int(cur.lastrowid or 0) - def openclaw_attempt_list(self, *, run_id: str, limit: int = 30) -> list[dict[str, Any]]: + def oclaw_attempt_list(self, *, run_id: str, limit: int = 30) -> list[dict[str, Any]]: lim = max(1, min(int(limit or 30), 200)) with self._connect() as conn: rows = conn.execute( """ SELECT id, run_id, tenant_id, session_id, attempt_no, status, reason, payload, created_at - FROM openclaw_attempt + FROM oclaw_attempt WHERE run_id = ? ORDER BY attempt_no ASC LIMIT ? @@ -5251,7 +5251,7 @@ class SqliteStore: ) return out - def openclaw_run_get(self, *, run_id: str, tenant_id: str | None = None) -> OpenClawRun | None: + def oclaw_run_get(self, *, run_id: str, tenant_id: str | None = None) -> OclawRun | None: where = "run_id = ?" params: list[Any] = [str(run_id)] if tenant_id: @@ -5261,7 +5261,7 @@ class SqliteStore: row = conn.execute( f""" SELECT run_id, tenant_id, session_id, status, payload, created_at, updated_at - FROM openclaw_run + FROM oclaw_run WHERE {where} LIMIT 1 """, @@ -5269,7 +5269,7 @@ class SqliteStore: ).fetchone() if not row: return None - return OpenClawRun( + return OclawRun( run_id=str(row["run_id"]), tenant_id=str(row["tenant_id"]), session_id=str(row["session_id"]), @@ -5279,14 +5279,14 @@ class SqliteStore: updated_at=str(row["updated_at"]), ) - def openclaw_run_list( + def oclaw_run_list( self, *, tenant_id: str, session_id: str | None = None, status: str | None = None, limit: int = 50, - ) -> list[OpenClawRun]: + ) -> list[OclawRun]: lim = max(1, min(int(limit or 50), 300)) where = "tenant_id = ?" params: list[Any] = [str(tenant_id)] @@ -5300,17 +5300,17 @@ class SqliteStore: rows = conn.execute( f""" SELECT run_id, tenant_id, session_id, status, payload, created_at, updated_at - FROM openclaw_run + FROM oclaw_run WHERE {where} ORDER BY updated_at DESC LIMIT ? """, (*params, lim), ).fetchall() - out: list[OpenClawRun] = [] + out: list[OclawRun] = [] for row in rows: out.append( - OpenClawRun( + OclawRun( run_id=str(row["run_id"]), tenant_id=str(row["tenant_id"]), session_id=str(row["session_id"]), diff --git a/prompts_openclaw/MAPPING.md b/prompts/MAPPING.runtime.md similarity index 82% rename from prompts_openclaw/MAPPING.md rename to prompts/MAPPING.runtime.md index 3e27f4e6..2d9f7bc7 100644 --- a/prompts_openclaw/MAPPING.md +++ b/prompts/MAPPING.runtime.md @@ -1,6 +1,6 @@ -# OpenClaw Prompt Mapping +# Oclaw Prompt Mapping -该目录作为 OpenClaw 风格提示词的唯一运行时来源(中文单份)。 +该目录作为 Oclaw 风格提示词的唯一运行时来源(中文单份)。 ## 设计目标 - 每个角色一个文件夹,便于独立维护和同步。 @@ -20,7 +20,7 @@ - `shared/*`(复用片段) ## 同步规则 -1. 以上游 OpenClaw 规范为准,优先替换同名文件。 +1. 以上游 Oclaw 规范为准,优先替换同名文件。 2. 本地仅允许: - 路径适配(保持目录结构不变) - 变量名与渲染占位符适配(`{{var}}`) diff --git a/prompts/README.md b/prompts/README.md index 63a5da71..31ce61c8 100644 --- a/prompts/README.md +++ b/prompts/README.md @@ -1,22 +1,22 @@ --- -title: prompts_scope_after_openclaw_migration +title: prompts_scope_after_oclaw_migration summary: Scope boundary for legacy and fallback prompts under oclaw/prompts. read_when: - Maintaining prompt files under oclaw/prompts. - - Distinguishing OpenClaw runtime prompts from non-OpenClaw prompts. + - Distinguishing Oclaw runtime prompts from non-Oclaw prompts. --- # `oclaw/prompts` Scope -OpenClaw runtime prompts have been migrated to `oclaw/prompts_openclaw`. +Oclaw runtime prompts have been migrated to `oclaw/prompts_runtime`. `oclaw/prompts` now keeps only: - `fallback/*`: user-facing fallback/error templates. -- Non-OpenClaw shared templates still used by runtime components, such as: +- Non-Oclaw shared templates still used by runtime components, such as: - `runtime/default_system.{zh,en}.md` - `tools/*` - `image/default_edit_prompt.zh.md` -Do not add new OpenClaw manager/router/specialist/runtime chain prompts here. -Use `oclaw/prompts_openclaw/*` instead. +Do not add new Oclaw manager/router/specialist/runtime chain prompts here. +Use `oclaw/prompts_runtime/*` instead. diff --git a/prompts/__init__.py b/prompts/__init__.py index 38b5e7ce..ea6334f8 100644 --- a/prompts/__init__.py +++ b/prompts/__init__.py @@ -1,3 +1,3 @@ -from .loader import render_openclaw_prompt, render_prompt, render_prompt_for_lang +from .loader import render_prompt, render_prompt_for_lang, render_runtime_prompt -__all__ = ["render_prompt", "render_prompt_for_lang", "render_openclaw_prompt"] +__all__ = ["render_prompt", "render_prompt_for_lang", "render_runtime_prompt"] diff --git a/prompts/fallback/openclaw_runtime_error.zh.md b/prompts/fallback/openclaw_runtime_error.zh.md deleted file mode 100644 index d4c4aa52..00000000 --- a/prompts/fallback/openclaw_runtime_error.zh.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: fallback_openclaw_runtime_error_zh -summary: fallback text when openclaw runtime failed -read_when: gateway exception path ---- -oclaw 运行失败:执行器缺少 model/tools - diff --git a/prompts/fallback/openclaw_runtime_error.en.md b/prompts/fallback/runtime_error.en.md similarity index 100% rename from prompts/fallback/openclaw_runtime_error.en.md rename to prompts/fallback/runtime_error.en.md diff --git a/prompts/fallback/runtime_error.zh.md b/prompts/fallback/runtime_error.zh.md new file mode 100644 index 00000000..5a3f23c3 --- /dev/null +++ b/prompts/fallback/runtime_error.zh.md @@ -0,0 +1,7 @@ +--- +title: fallback_oclaw_runtime_error_zh +summary: fallback text when oclaw runtime failed +read_when: gateway exception path +--- +oclaw 运行失败:执行器缺少 model/tools + diff --git a/prompts/loader.py b/prompts/loader.py index 475d4836..95abfa0a 100644 --- a/prompts/loader.py +++ b/prompts/loader.py @@ -22,8 +22,9 @@ def _prompts_root() -> Path: return (PROJECT_ROOT / "oclaw" / "prompts").resolve() -def _openclaw_prompts_root() -> Path: - return (PROJECT_ROOT / "oclaw" / "prompts_openclaw").resolve() +def _runtime_prompts_root() -> Path: + # Runtime prompts are unified into the single prompts tree. + return _prompts_root() @lru_cache(maxsize=512) @@ -37,12 +38,12 @@ def load_prompt_doc(prompt_path: str) -> PromptDoc: @lru_cache(maxsize=512) -def load_openclaw_prompt_doc(prompt_path: str) -> PromptDoc: - p = (_openclaw_prompts_root() / str(prompt_path)).resolve() +def load_runtime_prompt_doc(prompt_path: str) -> PromptDoc: + p = (_runtime_prompts_root() / str(prompt_path)).resolve() if not p.exists(): - raise FileNotFoundError(f"openclaw prompt not found: {prompt_path}") + raise FileNotFoundError(f"runtime prompt not found: {prompt_path}") raw = p.read_text(encoding="utf-8") - fm, body = parse_markdown_document(raw, source=f"openclaw_prompt:{prompt_path}") + fm, body = parse_markdown_document(raw, source=f"runtime_prompt:{prompt_path}") return PromptDoc(frontmatter=fm, body=body) @@ -69,8 +70,8 @@ def render_prompt(prompt_path: str, *, variables: dict[str, Any] | None = None, return out.strip() -def render_openclaw_prompt(prompt_path: str, *, variables: dict[str, Any] | None = None, strict: bool = True) -> str: - doc = load_openclaw_prompt_doc(prompt_path) +def render_runtime_prompt(prompt_path: str, *, variables: dict[str, Any] | None = None, strict: bool = True) -> str: + doc = load_runtime_prompt_doc(prompt_path) vars_in = dict(variables or {}) missing: set[str] = set() @@ -83,15 +84,15 @@ def render_openclaw_prompt(prompt_path: str, *, variables: dict[str, Any] | None out = _VAR_RE.sub(_repl, doc.body) if strict and missing: - raise ValueError(f"missing openclaw prompt variables for {prompt_path}: {', '.join(sorted(missing))}") + raise ValueError(f"missing runtime prompt variables for {prompt_path}: {', '.join(sorted(missing))}") return out.strip() __all__ = [ "PromptDoc", "load_prompt_doc", - "load_openclaw_prompt_doc", + "load_runtime_prompt_doc", "render_prompt", "render_prompt_for_lang", - "render_openclaw_prompt", + "render_runtime_prompt", ] diff --git a/prompts_openclaw/manager/decision.md b/prompts/manager/decision.md similarity index 100% rename from prompts_openclaw/manager/decision.md rename to prompts/manager/decision.md diff --git a/prompts_openclaw/roles/manager/system.md b/prompts/roles/manager/system.md similarity index 93% rename from prompts_openclaw/roles/manager/system.md rename to prompts/roles/manager/system.md index 0a84a557..14de1d00 100644 --- a/prompts_openclaw/roles/manager/system.md +++ b/prompts/roles/manager/system.md @@ -1,7 +1,7 @@ --- title: oclaw_role_manager_system summary: Manager role system prompt (Chinese only). -read_when: openclaw manager role selected +read_when: oclaw manager role selected --- # Identity 你是编排管理角色(manager)。 diff --git a/prompts_openclaw/roles/specialists/generalist/system.md b/prompts/roles/specialists/generalist/system.md similarity index 100% rename from prompts_openclaw/roles/specialists/generalist/system.md rename to prompts/roles/specialists/generalist/system.md diff --git a/prompts_openclaw/roles/specialists/image/system.md b/prompts/roles/specialists/image/system.md similarity index 100% rename from prompts_openclaw/roles/specialists/image/system.md rename to prompts/roles/specialists/image/system.md diff --git a/prompts_openclaw/roles/specialists/memory_curator/system.md b/prompts/roles/specialists/memory_curator/system.md similarity index 100% rename from prompts_openclaw/roles/specialists/memory_curator/system.md rename to prompts/roles/specialists/memory_curator/system.md diff --git a/prompts_openclaw/roles/specialists/ops/system.md b/prompts/roles/specialists/ops/system.md similarity index 100% rename from prompts_openclaw/roles/specialists/ops/system.md rename to prompts/roles/specialists/ops/system.md diff --git a/prompts_openclaw/router/decide_route.md b/prompts/router/decide_route.md similarity index 94% rename from prompts_openclaw/router/decide_route.md rename to prompts/router/decide_route.md index f1973f1f..545ba89d 100644 --- a/prompts_openclaw/router/decide_route.md +++ b/prompts/router/decide_route.md @@ -1,7 +1,7 @@ --- title: oclaw_router_decide_route summary: LLM JSON routing prompt for sync vs async oclaw turns (Chinese only). -read_when: AIA_OPENCLAW_ROUTER_MODE=llm_json +read_when: AIA_OCLAW_ROUTER_MODE=llm_json --- # 路由决策(结构化输出) diff --git a/prompts_openclaw/runtime/memory_context_block.md b/prompts/runtime/memory_context_block.md similarity index 100% rename from prompts_openclaw/runtime/memory_context_block.md rename to prompts/runtime/memory_context_block.md diff --git a/prompts_openclaw/runtime/project_context_block.md b/prompts/runtime/project_context_block.md similarity index 73% rename from prompts_openclaw/runtime/project_context_block.md rename to prompts/runtime/project_context_block.md index 9a52115b..4cfe94f5 100644 --- a/prompts_openclaw/runtime/project_context_block.md +++ b/prompts/runtime/project_context_block.md @@ -1,7 +1,7 @@ --- title: oclaw_runtime_project_context_block summary: Inject workspace bootstrap files into system prompt project context. -read_when: openclaw system assembly with workspace context +read_when: oclaw system assembly with workspace context --- [project_context] {{project_context}} diff --git a/prompts_openclaw/runtime/system_with_memory.md b/prompts/runtime/system_with_memory.md similarity index 82% rename from prompts_openclaw/runtime/system_with_memory.md rename to prompts/runtime/system_with_memory.md index 9cecddb2..f113b7ac 100644 --- a/prompts_openclaw/runtime/system_with_memory.md +++ b/prompts/runtime/system_with_memory.md @@ -1,7 +1,7 @@ --- title: oclaw_runtime_system_with_memory summary: Runtime system prompt wrapper with injected memory context. -read_when: openclaw system assembly +read_when: oclaw system assembly --- {{system_prompt}} diff --git a/prompts_openclaw/runtime/system_with_skills.md b/prompts/runtime/system_with_skills.md similarity index 73% rename from prompts_openclaw/runtime/system_with_skills.md rename to prompts/runtime/system_with_skills.md index 8d6dec85..7368935c 100644 --- a/prompts_openclaw/runtime/system_with_skills.md +++ b/prompts/runtime/system_with_skills.md @@ -1,6 +1,6 @@ --- title: oclaw_runtime_system_with_skills summary: System prompt body plus optional skills catalog XML. -read_when: openclaw direct loop with skill runtime +read_when: oclaw direct loop with skill runtime --- {{system_body}}{{skills_catalog}} diff --git a/prompts_openclaw/shared/README.md b/prompts/shared/README.md similarity index 76% rename from prompts_openclaw/shared/README.md rename to prompts/shared/README.md index 00f7cae8..6a7d363c 100644 --- a/prompts_openclaw/shared/README.md +++ b/prompts/shared/README.md @@ -1,6 +1,6 @@ --- title: oclaw_prompt_shared_readme -summary: Shared prompt fragments for OpenClaw-style prompt tree. +summary: Shared prompt fragments for Oclaw-style prompt tree. read_when: editing prompt shared snippets --- 该目录用于存放可复用提示词片段。 diff --git a/pytest.ini b/pytest.ini index a6e475c5..9e154591 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,5 @@ [pytest] -testpaths = oclaw/tests +testpaths = tests norecursedirs = vendor .venv venv node_modules dist build .git addopts = -ra diff --git a/runtime/agent_context/__init__.py b/runtime/agent_context/__init__.py new file mode 100644 index 00000000..aa1686d6 --- /dev/null +++ b/runtime/agent_context/__init__.py @@ -0,0 +1,10 @@ +"""Role/system context builders. + +These utilities build a role-specific system context from the runtime asset +workspaces under `oclaw/runtime/assets/agent_workspaces/*`. +""" + +from .loader import build_role_system_context + +__all__ = ["build_role_system_context"] + diff --git a/infrastructure/agent_context/loader.py b/runtime/agent_context/loader.py similarity index 64% rename from infrastructure/agent_context/loader.py rename to runtime/agent_context/loader.py index 76c4b367..d15f16ab 100644 --- a/infrastructure/agent_context/loader.py +++ b/runtime/agent_context/loader.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path from oclaw.platform.config.paths import PROJECT_ROOT -from oclaw.prompts.loader import render_openclaw_prompt +from oclaw.prompts.loader import render_runtime_prompt def _read_text(path: Path) -> str: @@ -25,24 +25,23 @@ def _workspace_for_role(role: str) -> str: def build_role_system_context(role: str) -> str: - """Build role context from @agent files with prompt fallback.""" + """Build role context from runtime asset workspaces with prompt fallback.""" workspace = _workspace_for_role(role) - agent_root = (PROJECT_ROOT / "oclaw" / "agent" / workspace).resolve() + agent_root = (PROJECT_ROOT / "oclaw" / "runtime" / "assets" / "agent_workspaces" / workspace).resolve() parts: list[str] = [] for name in ("AGENTS.md", "IDENTITY.md", "SOUL.md", "USER.md"): t = _read_text(agent_root / name) if t: parts.append(f"# {name}\n{t}") - # Keep concise fallback for missing local role files. role_id = str(role or "").strip().lower() if role_id == "ops": - fallback = render_openclaw_prompt("roles/specialists/ops/system.md", strict=True) + fallback = render_runtime_prompt("roles/specialists/ops/system.md", strict=True) elif role_id == "image": - fallback = render_openclaw_prompt("roles/specialists/image/system.md", strict=True) + fallback = render_runtime_prompt("roles/specialists/image/system.md", strict=True) elif role_id == "memory_curator": - fallback = render_openclaw_prompt("roles/specialists/memory_curator/system.md", strict=True) + fallback = render_runtime_prompt("roles/specialists/memory_curator/system.md", strict=True) else: - fallback = render_openclaw_prompt("roles/specialists/generalist/system.md", strict=True) + fallback = render_runtime_prompt("roles/specialists/generalist/system.md", strict=True) parts.append(f"# FALLBACK_ROLE_SYSTEM\n{fallback}") return "\n\n".join([p for p in parts if p.strip()]).strip() diff --git a/openclaw_runtime/agent_core_attempt.py b/runtime/agent_core_attempt.py similarity index 93% rename from openclaw_runtime/agent_core_attempt.py rename to runtime/agent_core_attempt.py index f56a0fad..a9e901b9 100644 --- a/openclaw_runtime/agent_core_attempt.py +++ b/runtime/agent_core_attempt.py @@ -3,11 +3,11 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable, Optional -from oclaw.chat.turn_types import TurnRunOutcome -from oclaw.openclaw_runtime.direct_loop import run_openclaw_direct_loop -from oclaw.openclaw_runtime.memory_stage import after_turn_memory -from oclaw.openclaw_runtime.types import AttemptState, OpenClawMemoryContext, StandardMessage -from oclaw.orchestration.trace import new_span_id +from oclaw.runtime.chat.turn_types import TurnRunOutcome +from oclaw.runtime.direct_loop import run_direct_loop +from oclaw.runtime.memory_stage import after_turn_memory +from oclaw.runtime.types import AttemptState, OclawMemoryContext, StandardMessage +from oclaw.runtime.orchestration.trace import new_span_id def _workspace_owner_session_id_from_msg(msg: StandardMessage) -> str | None: md = msg.metadata if isinstance(msg.metadata, dict) else {} @@ -45,7 +45,7 @@ class AttemptRunnerInput: max_messages: int max_tool_rounds: int max_tool_workers: int - memory_context: OpenClawMemoryContext | None + memory_context: OclawMemoryContext | None persist_user_message: bool = True on_token: Optional[Callable[[str], None]] = None on_progress: Optional[Callable[[str], None]] = None @@ -95,7 +95,7 @@ def _classify_attempt_error(exc: Exception) -> tuple[str, str, bool]: def run_attempt(*, store: Any, data: AttemptRunnerInput) -> AttemptRunnerOutput: try: - outcome = run_openclaw_direct_loop( + outcome = run_direct_loop( store=store, session_id=data.msg.session_id, lang=data.lang, @@ -142,7 +142,7 @@ def run_attempt(*, store: Any, data: AttemptRunnerInput) -> AttemptRunnerOutput: parent_span_id=data.parent_span_id, event_type="after_turn_memory", payload={ - "pipeline": "openclaw_agent_core", + "pipeline": "oclaw_agent_core", "oc_stage": "memory_done", "run_id": str(data.run_id or ""), "attempt_no": int(data.attempt_no), diff --git a/openclaw_runtime/agent_core_run.py b/runtime/agent_core_run.py similarity index 90% rename from openclaw_runtime/agent_core_run.py rename to runtime/agent_core_run.py index 9191301c..2fcf80db 100644 --- a/openclaw_runtime/agent_core_run.py +++ b/runtime/agent_core_run.py @@ -4,12 +4,12 @@ import uuid from dataclasses import dataclass from typing import Any, Callable, Optional -from oclaw.chat.turn_types import TurnRunOutcome -from oclaw.openclaw_runtime.agent_core_attempt import ALL_ATTEMPT_ERROR_CODES -from oclaw.openclaw_runtime.agent_core_attempt import AttemptRunnerInput, run_attempt -from oclaw.openclaw_runtime.memory_stage import compact_memory_context -from oclaw.openclaw_runtime.types import AttemptState, OpenClawMemoryContext, RunState, StandardMessage -from oclaw.orchestration.trace import new_span_id +from oclaw.runtime.chat.turn_types import TurnRunOutcome +from oclaw.runtime.agent_core_attempt import ALL_ATTEMPT_ERROR_CODES +from oclaw.runtime.agent_core_attempt import AttemptRunnerInput, run_attempt +from oclaw.runtime.memory_stage import compact_memory_context +from oclaw.runtime.types import AttemptState, OclawMemoryContext, RunState, StandardMessage +from oclaw.runtime.orchestration.trace import new_span_id _AGENT_CORE_OC_STAGE: dict[str, str] = { "run_started": "run_start", @@ -35,13 +35,16 @@ class AgentCoreRunInput: max_tool_rounds: int = 8 max_tool_workers: int = 8 max_attempts: int = 2 - memory_context: OpenClawMemoryContext | None = None + memory_context: OclawMemoryContext | None = None on_token: Optional[Callable[[str], None]] = None on_progress: Optional[Callable[[str], None]] = None on_tool_ui: Optional[Callable[[str, dict[str, Any]], None]] = None should_stop: Optional[Callable[[], bool]] = None - openclaw_task_id: str | None = None - openclaw_worker_id: str | None = None + task_id: str | None = None + worker_id: str | None = None + # Backward-compatible aliases kept for older callsites/tests. + oclaw_task_id: str | None = None + oclaw_worker_id: str | None = None skill_binding_role: str | None = None wire_policy_role: str | None = None @@ -66,7 +69,7 @@ DEFAULT_RETRYABLE_ERROR_CODES = ( def resolve_retryable_error_codes(*, store: Any) -> set[str]: raw = "" try: - raw = str(store.get_setting("AIA_OPENCLAW_RETRYABLE_ERROR_CODES") or "").strip() + raw = str(store.get_setting("AIA_OCLAW_RETRYABLE_ERROR_CODES") or "").strip() except Exception: raw = "" if not raw: @@ -89,19 +92,21 @@ def _trace( if not data.trace_id: return merged: dict[str, Any] = dict(payload or {}) - merged.setdefault("pipeline", "openclaw_agent_core") + merged.setdefault("pipeline", "oclaw_agent_core") merged.setdefault("trace_id", str(data.trace_id)) merged.setdefault("lang", str(data.lang or "")) merged.setdefault("run_id", run_id) if attempt_no is not None: merged.setdefault("attempt_no", int(attempt_no)) merged["oc_stage"] = _AGENT_CORE_OC_STAGE.get(event_type, event_type) - tid = str(data.openclaw_task_id or "").strip() + tid = str(data.task_id or data.oclaw_task_id or "").strip() if tid: - merged.setdefault("openclaw_task_id", tid) - wid = str(data.openclaw_worker_id or "").strip() + merged.setdefault("task_id", tid) + merged.setdefault("oclaw_task_id", tid) + wid = str(data.worker_id or data.oclaw_worker_id or "").strip() if wid: - merged.setdefault("openclaw_worker_id", wid) + merged.setdefault("worker_id", wid) + merged.setdefault("oclaw_worker_id", wid) try: store.add_trace_event( session_id=data.msg.session_id, @@ -117,7 +122,7 @@ def _trace( def _persist_run(store: Any, *, run_id: str, msg: StandardMessage, status: str, payload: dict[str, Any]) -> None: try: - store.openclaw_run_upsert( + store.oclaw_run_upsert( run_id=run_id, tenant_id=msg.tenant_id, session_id=msg.session_id, @@ -214,7 +219,7 @@ def run_agent_core(*, store: Any, data: AgentCoreRunInput) -> AgentCoreRunOutput attempts.append(out.state) outcome = out.outcome try: - store.openclaw_attempt_append( + store.oclaw_attempt_append( run_id=run_id, tenant_id=data.msg.tenant_id, session_id=data.msg.session_id, diff --git a/agents/__init__.py b/runtime/agents/__init__.py similarity index 100% rename from agents/__init__.py rename to runtime/agents/__init__.py diff --git a/agents/agent_scope.py b/runtime/agents/agent_scope.py similarity index 98% rename from agents/agent_scope.py rename to runtime/agents/agent_scope.py index 55a58905..d3676db3 100644 --- a/agents/agent_scope.py +++ b/runtime/agents/agent_scope.py @@ -132,7 +132,7 @@ def resolve_agent_workspace_dir(cfg: dict[str, Any], agent_id: str) -> str: if fallback_workspace: return str(Path(fallback_workspace) / aid) - state_dir = str(os.getenv("OPENCLAW_STATE_DIR") or ".openclaw").strip() or ".openclaw" + state_dir = str(os.getenv("OCLAW_STATE_DIR") or ".oclaw").strip() or ".oclaw" return str(Path(state_dir) / f"workspace-{aid}") diff --git a/agents/factory.py b/runtime/agents/factory.py similarity index 96% rename from agents/factory.py rename to runtime/agents/factory.py index 4422c413..023383dc 100644 --- a/agents/factory.py +++ b/runtime/agents/factory.py @@ -5,10 +5,10 @@ from __future__ import annotations import os from typing import Any -from oclaw.agents.agent_scope import resolve_default_agent_id -from oclaw.agents.network_ops_agent import NetworkOpsAgent -from oclaw.agents.specialist_agent import SpecialistProfile -from oclaw.agents.specialists import ( +from oclaw.runtime.agents.agent_scope import resolve_default_agent_id +from oclaw.runtime.agents.network_ops_agent import NetworkOpsAgent +from oclaw.runtime.agents.specialist_agent import SpecialistProfile +from oclaw.runtime.agents.specialists import ( AGENT_PROFILE_BINDINGS_KEY, AGENT_ROLE_IDS, MANAGER_AGENT_ID, @@ -19,9 +19,9 @@ from oclaw.agents.specialists import ( expert_name_for_specialist, parse_agent_profile_bindings, ) -from oclaw.chat.agent import Agent -from oclaw.orchestration.inventory import inventory_snapshot -from oclaw.orchestration.memory import upsert_knowledge_chunks +from oclaw.runtime.chat.agent import Agent +from oclaw.runtime.orchestration.inventory import inventory_snapshot +from oclaw.runtime.orchestration.memory import upsert_knowledge_chunks from oclaw.platform.llm.chat_models import GoogleGeminiChatModel, OpenAIChatModel, RuleBasedChatModel, StaticTextChatModel from oclaw.platform.llm.transports.anthropic_messages import AnthropicMessagesModel from oclaw.platform.llm.transports.openai_responses import OpenAIResponsesModel @@ -32,8 +32,8 @@ from oclaw.platform.persistence.sqlite_store import ( is_administrator_model_pool, ) from oclaw.prompts import render_prompt -from oclaw.tools.catalog import default_registry -from oclaw.tools.plugin_loader import sync_plugin_metadata +from oclaw.runtime.tools.catalog import default_registry +from oclaw.runtime.tools.plugin_loader import sync_plugin_metadata def _openai_missing_key_user_message(lang: str) -> str: diff --git a/agents/network_ops_agent.py b/runtime/agents/network_ops_agent.py similarity index 75% rename from agents/network_ops_agent.py rename to runtime/agents/network_ops_agent.py index 0f119437..5bb7e1ca 100644 --- a/agents/network_ops_agent.py +++ b/runtime/agents/network_ops_agent.py @@ -2,12 +2,12 @@ from __future__ import annotations from typing import Any -from oclaw.chat.agent import Agent +from oclaw.runtime.chat.agent import Agent from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.prompts.loader import render_openclaw_prompt -from oclaw.tools import default_registry +from oclaw.prompts.loader import render_runtime_prompt +from oclaw.runtime.tools import default_registry -NETWORK_SYSTEM_PROMPT_ZH = render_openclaw_prompt("roles/specialists/ops/system.md", strict=True) +NETWORK_SYSTEM_PROMPT_ZH = render_runtime_prompt("roles/specialists/ops/system.md", strict=True) class NetworkOpsAgent(Agent): @@ -36,7 +36,7 @@ class NetworkOpsAgent(Agent): store=store, tools=tools, model=model, - system_prompt=(system_prompt or render_openclaw_prompt("roles/specialists/ops/system.md", strict=True)), + system_prompt=(system_prompt or render_runtime_prompt("roles/specialists/ops/system.md", strict=True)), lang=lang, llm_profile_mode=llm_profile_mode, ) diff --git a/agents/specialist_agent.py b/runtime/agents/specialist_agent.py similarity index 97% rename from agents/specialist_agent.py rename to runtime/agents/specialist_agent.py index c3a61535..7aa304f3 100644 --- a/agents/specialist_agent.py +++ b/runtime/agents/specialist_agent.py @@ -10,19 +10,19 @@ from collections.abc import Callable from dataclasses import dataclass, field from typing import Any, Optional -from oclaw.chat.agent import Agent -from oclaw.chat.agent import GenerationInterrupted -from oclaw.agents.network_ops_agent import NetworkOpsAgent +from oclaw.runtime.chat.agent import Agent +from oclaw.runtime.chat.agent import GenerationInterrupted +from oclaw.runtime.agents.network_ops_agent import NetworkOpsAgent from oclaw.platform.persistence.sqlite_store import SqliteStore from oclaw.platform.files.attachment_assets import AttachmentAssetStore, attachment_id_to_data_url from oclaw.platform.llm.image_message_client import send_image_messages -from oclaw.tools import default_registry -from oclaw.agents.specialists import expert_name_for_specialist +from oclaw.runtime.tools import default_registry +from oclaw.runtime.agents.specialists import expert_name_for_specialist -from oclaw.chat.turn_types import TurnRunOutcome -from oclaw.openclaw_runtime.relay_pointer import build_manifest_from_attachment_refs -from oclaw.openclaw_runtime.types import RelayShareEnvelope -from oclaw.orchestration.protocol import ( +from oclaw.runtime.chat.turn_types import TurnRunOutcome +from oclaw.runtime.relay_pointer import build_manifest_from_attachment_refs +from oclaw.runtime.types import RelayShareEnvelope +from oclaw.runtime.orchestration.protocol import ( AgentTask, PlanStep, SpecialistDelivery, @@ -399,10 +399,10 @@ class SpecialistAgentRunner: path_policy_tenant_id=_path_tenant, path_policy_user_id=_path_user, ) - from oclaw.openclaw_runtime.gateway import OpenClawGateway - from oclaw.openclaw_runtime.types import StandardMessage + from oclaw.runtime.gateway import OclawGateway + from oclaw.runtime.types import StandardMessage - gw = OpenClawGateway(store=self.store) + gw = OclawGateway(store=self.store) msg = StandardMessage( session_id=str(session_id), tenant_id=str(_path_tenant or ""), diff --git a/agents/specialists.py b/runtime/agents/specialists.py similarity index 97% rename from agents/specialists.py rename to runtime/agents/specialists.py index cb2a5d06..f50373ef 100644 --- a/agents/specialists.py +++ b/runtime/agents/specialists.py @@ -4,7 +4,7 @@ from dataclasses import dataclass import json from typing import Any -from oclaw.infrastructure.agent_context import build_role_system_context +from oclaw.runtime.agent_context import build_role_system_context SpecialistId = str diff --git a/agents/subagent_registry.py b/runtime/agents/subagent_registry.py similarity index 91% rename from agents/subagent_registry.py rename to runtime/agents/subagent_registry.py index 9a91883f..496462f7 100644 --- a/agents/subagent_registry.py +++ b/runtime/agents/subagent_registry.py @@ -10,7 +10,7 @@ def init_subagent_registry() -> None: """Initialize subagent registry runtime once. Python gateway currently keeps this as a lightweight compatibility seam, - so startup code can mirror the OpenClaw TypeScript bootstrap flow. + so startup code can mirror the Oclaw TypeScript bootstrap flow. """ global _INITIALIZED with _LOCK: diff --git a/runtime/app/__init__.py b/runtime/app/__init__.py new file mode 100644 index 00000000..7f2d7f87 --- /dev/null +++ b/runtime/app/__init__.py @@ -0,0 +1,2 @@ +"""Application-facing runtime entrypoints.""" + diff --git a/application/__init__.py b/runtime/application/__init__.py similarity index 100% rename from application/__init__.py rename to runtime/application/__init__.py diff --git a/application/gateway/__init__.py b/runtime/application/gateway/__init__.py similarity index 100% rename from application/gateway/__init__.py rename to runtime/application/gateway/__init__.py diff --git a/application/gateway/inbound_service.py b/runtime/application/gateway/inbound_service.py similarity index 95% rename from application/gateway/inbound_service.py rename to runtime/application/gateway/inbound_service.py index bf1905dc..507bad7f 100644 --- a/application/gateway/inbound_service.py +++ b/runtime/application/gateway/inbound_service.py @@ -4,8 +4,8 @@ import hashlib import threading from typing import Any -from oclaw.channels.base import InboundMessage, OutboundMessage -from oclaw.channels.wecom.wecom_bridge import WeComAdapter +from oclaw.interfaces.channels.base import InboundMessage, OutboundMessage +from oclaw.interfaces.channels.wecom.wecom_bridge import WeComAdapter _GATEWAY_AGENT_LOCK = threading.Lock() _GATEWAY_AGENT: Any | None = None @@ -15,7 +15,7 @@ def _get_gateway_agent(store: Any) -> Any: global _GATEWAY_AGENT with _GATEWAY_AGENT_LOCK: if _GATEWAY_AGENT is None: - from oclaw.agents.factory import build_gateway_executor + from oclaw.runtime.agents.factory import build_gateway_executor _GATEWAY_AGENT = build_gateway_executor(store) return _GATEWAY_AGENT @@ -84,7 +84,7 @@ def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str) -> content = t[len("加知识 ") :].strip() if not content: return "知识内容不能为空。示例:加知识 办公室WiFi密码是12345678" - from oclaw.tools.experts.productivity.kb_tools import kb_add_tool + from oclaw.runtime.tools.experts.productivity.kb_tools import kb_add_tool res = kb_add_tool().handler({"tenant_id": tenant_id, "user_id": user_id, "text": content}) if not res.get("ok"): @@ -95,7 +95,7 @@ def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str) -> q = t[len("查知识 ") :].strip() if not q: return "请提供关键词。示例:查知识 WiFi 密码" - from oclaw.tools.experts.productivity.kb_tools import kb_search_tool + from oclaw.runtime.tools.experts.productivity.kb_tools import kb_search_tool res = kb_search_tool().handler({"tenant_id": tenant_id, "query": q, "limit": 5}) if not res.get("ok"): @@ -271,7 +271,7 @@ def _parse_generic_inbound(channel_name: str, payload: dict[str, Any]) -> Inboun def process_inbound_payload(payload: dict[str, Any]) -> dict[str, Any]: - from oclaw.ops.mcp_env import apply_gateway_mcp_env_to_os + from oclaw.runtime.operations.mcp_env import apply_gateway_mcp_env_to_os apply_gateway_mcp_env_to_os() channel_name = str(payload.get("channel") or "wecom").strip().lower() @@ -342,8 +342,8 @@ def process_inbound_payload(payload: dict[str, Any]) -> dict[str, Any]: if not ident: reply = "账号初始化失败,请检查 administrator/tenant 配置。" if ident: - from oclaw.orchestration.policy import ActionPolicyContext, PolicyEngine - from oclaw.orchestration.security import has_explicit_confirmation_token + from oclaw.runtime.orchestration.policy import ActionPolicyContext, PolicyEngine + from oclaw.runtime.orchestration.security import has_explicit_confirmation_token tenant_id = str(ident.get("tenant_id") or "") user_id = str(ident.get("user_id") or "") @@ -405,11 +405,11 @@ def process_inbound_payload(payload: dict[str, Any]) -> dict[str, Any]: user_text = (inbound.text or "").strip() if user_text: try: - from oclaw.openclaw_runtime.gateway import OpenClawGateway - from oclaw.openclaw_runtime.types import StandardMessage + from oclaw.runtime.gateway import OclawGateway + from oclaw.runtime.types import StandardMessage agent = _get_gateway_agent(store) - gw = OpenClawGateway(store=store) + gw = OclawGateway(store=store) msg = StandardMessage( session_id=str(session_id), tenant_id=str(tenant_id or ""), diff --git a/application/gateway/inbound_usecase.py b/runtime/application/gateway/inbound_usecase.py similarity index 100% rename from application/gateway/inbound_usecase.py rename to runtime/application/gateway/inbound_usecase.py diff --git a/agent/workspace-coding/AGENTS.md b/runtime/assets/agent_workspaces/workspace-coding/AGENTS.md similarity index 100% rename from agent/workspace-coding/AGENTS.md rename to runtime/assets/agent_workspaces/workspace-coding/AGENTS.md diff --git a/agent/workspace-coding/IDENTITY.md b/runtime/assets/agent_workspaces/workspace-coding/IDENTITY.md similarity index 100% rename from agent/workspace-coding/IDENTITY.md rename to runtime/assets/agent_workspaces/workspace-coding/IDENTITY.md diff --git a/agent/workspace-coding/SOUL.md b/runtime/assets/agent_workspaces/workspace-coding/SOUL.md similarity index 100% rename from agent/workspace-coding/SOUL.md rename to runtime/assets/agent_workspaces/workspace-coding/SOUL.md diff --git a/agent/workspace-coding/USER.md b/runtime/assets/agent_workspaces/workspace-coding/USER.md similarity index 100% rename from agent/workspace-coding/USER.md rename to runtime/assets/agent_workspaces/workspace-coding/USER.md diff --git a/agent/workspace-coding/memory/README.md b/runtime/assets/agent_workspaces/workspace-coding/memory/README.md similarity index 100% rename from agent/workspace-coding/memory/README.md rename to runtime/assets/agent_workspaces/workspace-coding/memory/README.md diff --git a/agent/workspace-main/AGENTS.md b/runtime/assets/agent_workspaces/workspace-main/AGENTS.md similarity index 100% rename from agent/workspace-main/AGENTS.md rename to runtime/assets/agent_workspaces/workspace-main/AGENTS.md diff --git a/agent/workspace-main/IDENTITY.md b/runtime/assets/agent_workspaces/workspace-main/IDENTITY.md similarity index 86% rename from agent/workspace-main/IDENTITY.md rename to runtime/assets/agent_workspaces/workspace-main/IDENTITY.md index 9d6c4f98..9ee893cd 100644 --- a/agent/workspace-main/IDENTITY.md +++ b/runtime/assets/agent_workspaces/workspace-main/IDENTITY.md @@ -8,7 +8,7 @@ Main Orchestrator ## 核心职责 - 将用户需求转成可执行任务,明确验收标准。 -- 选择合适 specialist(coding/social/ops/image/generalist)。 +- 选择合适 specialist(coding/social/runtime/operations/image/generalist)。 - 汇总 specialist 结果,统一为用户可决策输出。 - 对冲突信息做裁决:以证据充分、风险可控为准。 diff --git a/agent/workspace-main/SOUL.md b/runtime/assets/agent_workspaces/workspace-main/SOUL.md similarity index 100% rename from agent/workspace-main/SOUL.md rename to runtime/assets/agent_workspaces/workspace-main/SOUL.md diff --git a/agent/workspace-main/USER.md b/runtime/assets/agent_workspaces/workspace-main/USER.md similarity index 100% rename from agent/workspace-main/USER.md rename to runtime/assets/agent_workspaces/workspace-main/USER.md diff --git a/agent/workspace-main/memory/README.md b/runtime/assets/agent_workspaces/workspace-main/memory/README.md similarity index 100% rename from agent/workspace-main/memory/README.md rename to runtime/assets/agent_workspaces/workspace-main/memory/README.md diff --git a/agent/workspace-social/AGENTS.md b/runtime/assets/agent_workspaces/workspace-social/AGENTS.md similarity index 100% rename from agent/workspace-social/AGENTS.md rename to runtime/assets/agent_workspaces/workspace-social/AGENTS.md diff --git a/agent/workspace-social/IDENTITY.md b/runtime/assets/agent_workspaces/workspace-social/IDENTITY.md similarity index 100% rename from agent/workspace-social/IDENTITY.md rename to runtime/assets/agent_workspaces/workspace-social/IDENTITY.md diff --git a/agent/workspace-social/SOUL.md b/runtime/assets/agent_workspaces/workspace-social/SOUL.md similarity index 100% rename from agent/workspace-social/SOUL.md rename to runtime/assets/agent_workspaces/workspace-social/SOUL.md diff --git a/agent/workspace-social/USER.md b/runtime/assets/agent_workspaces/workspace-social/USER.md similarity index 100% rename from agent/workspace-social/USER.md rename to runtime/assets/agent_workspaces/workspace-social/USER.md diff --git a/agent/workspace-social/memory/README.md b/runtime/assets/agent_workspaces/workspace-social/memory/README.md similarity index 100% rename from agent/workspace-social/memory/README.md rename to runtime/assets/agent_workspaces/workspace-social/memory/README.md diff --git a/chat/__init__.py b/runtime/chat/__init__.py similarity index 100% rename from chat/__init__.py rename to runtime/chat/__init__.py diff --git a/chat/agent.py b/runtime/chat/agent.py similarity index 95% rename from chat/agent.py rename to runtime/chat/agent.py index ab135ed0..b7f9f022 100644 --- a/chat/agent.py +++ b/runtime/chat/agent.py @@ -9,7 +9,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Any, Optional -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.tools.base import ToolRegistry from oclaw.platform.persistence.sqlite_store import SqliteStore from oclaw.platform.llm.chat_models import ( ChatModel, @@ -23,7 +23,7 @@ from oclaw.platform.llm.chat_models import ( gemini_openai_compat_client, ) from oclaw.prompts.loader import render_prompt_for_lang -from oclaw.tools.tool_validation import validate_tool_arguments +from oclaw.runtime.tools.tool_validation import validate_tool_arguments logger = logging.getLogger(__name__) @@ -81,13 +81,13 @@ class Agent: def _format_ollama_failure_banner(self, exc: BaseException) -> str: # Backward compat wrapper; implementation lives in `src.chat.agent_errors`. - from oclaw.chat.agent_errors import format_ollama_failure_banner + from oclaw.runtime.chat.agent_errors import format_ollama_failure_banner return format_ollama_failure_banner(lang=self.lang, exc=exc) def _format_openai_transport_error(self, exc: BaseException) -> str: # Backward compat wrapper; implementation lives in `src.chat.agent_errors`. - from oclaw.chat.agent_errors import format_openai_transport_error + from oclaw.runtime.chat.agent_errors import format_openai_transport_error return format_openai_transport_error(lang=self.lang, exc=exc) @@ -197,8 +197,8 @@ class Agent: interaction_mode: str | None = None, selected_specialist: str | None = None, ) -> str: - from oclaw.openclaw_runtime.gateway import OpenClawGateway - from oclaw.openclaw_runtime.types import StandardMessage + from oclaw.runtime.gateway import OclawGateway + from oclaw.runtime.types import StandardMessage tenant_id = str(path_policy_tenant_id or "").strip() user_id = str(path_policy_user_id or "").strip() @@ -243,7 +243,7 @@ class Agent: attachments=list(attachments or []), metadata=meta, ) - gw = OpenClawGateway(store=self.store) + gw = OclawGateway(store=self.store) try: res = gw.handle_turn( msg=msg, @@ -264,7 +264,7 @@ class Agent: return str(res.reply_text or "") def _build_llm_messages(self, session_id: str) -> list[dict[str, Any]]: - from oclaw.chat.agent_messages import build_llm_messages + from oclaw.runtime.chat.agent_messages import build_llm_messages msgs = self.store.get_messages(session_id=session_id, limit=self.config.max_messages) return build_llm_messages( diff --git a/chat/agent_errors.py b/runtime/chat/agent_errors.py similarity index 100% rename from chat/agent_errors.py rename to runtime/chat/agent_errors.py diff --git a/chat/agent_messages.py b/runtime/chat/agent_messages.py similarity index 99% rename from chat/agent_messages.py rename to runtime/chat/agent_messages.py index 04f1625c..3e4b5267 100644 --- a/chat/agent_messages.py +++ b/runtime/chat/agent_messages.py @@ -14,10 +14,10 @@ import re from typing import Any from oclaw.platform.llm.chat_models import _normalize_image_b64_payload, gemini_openai_compat_client, ChatModel -from oclaw.chat.tool_runtime import tool_llm_message_max_chars, truncate_tool_result_for_llm_messages +from oclaw.runtime.chat.tool_runtime import tool_llm_message_max_chars, truncate_tool_result_for_llm_messages from oclaw.prompts import render_prompt from oclaw.platform.files.attachment_assets import attachment_id_to_data_url -from oclaw.openclaw_runtime.relay_pointer import parse_pointer_uri +from oclaw.runtime.relay_pointer import parse_pointer_uri logger = logging.getLogger(__name__) _THINK_BLOCK_RE = re.compile(r"\s*(.*?)\s*\s*", flags=re.IGNORECASE | re.DOTALL) diff --git a/chat/tool_runtime.py b/runtime/chat/tool_runtime.py similarity index 98% rename from chat/tool_runtime.py rename to runtime/chat/tool_runtime.py index 0563a9d7..50dfdae6 100644 --- a/chat/tool_runtime.py +++ b/runtime/chat/tool_runtime.py @@ -17,10 +17,10 @@ from dataclasses import dataclass from typing import Any, Callable, Optional from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.tools.base import ToolRegistry from oclaw.platform.llm.chat_models import LLMToolCall -from oclaw.tools.tool_validation import validate_tool_arguments -from oclaw.tools.experts.workspace.workspace_base import workspace_path_access_scope +from oclaw.runtime.tools.tool_validation import validate_tool_arguments +from oclaw.runtime.tools.experts.workspace.workspace_base import workspace_path_access_scope logger = logging.getLogger(__name__) @@ -315,7 +315,7 @@ class ToolExecutor: if not ctx.trace_id: return try: - from oclaw.orchestration.trace import new_span_id + from oclaw.runtime.orchestration.trace import new_span_id ctx.store.add_trace_event( session_id=ctx.session_id, diff --git a/chat/turn_types.py b/runtime/chat/turn_types.py similarity index 100% rename from chat/turn_types.py rename to runtime/chat/turn_types.py diff --git a/openclaw_runtime/command_parser.py b/runtime/command_parser.py similarity index 100% rename from openclaw_runtime/command_parser.py rename to runtime/command_parser.py diff --git a/runtime/core/__init__.py b/runtime/core/__init__.py new file mode 100644 index 00000000..b64db0bb --- /dev/null +++ b/runtime/core/__init__.py @@ -0,0 +1,10 @@ +"""Runtime core building blocks. + +This package groups the reusable execution pipeline components used by +application-facing entrypoints such as the gateway and workers. +""" + +from .agent_execution import AgentCoreRunInput, build_memory_context, run_agent_core, run_direct_loop + +__all__ = ["AgentCoreRunInput", "build_memory_context", "run_agent_core", "run_direct_loop"] + diff --git a/runtime/core/agent_execution.py b/runtime/core/agent_execution.py new file mode 100644 index 00000000..b5258c4f --- /dev/null +++ b/runtime/core/agent_execution.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from oclaw.runtime.agent_core_run import AgentCoreRunInput, run_agent_core +from oclaw.runtime.direct_loop import run_direct_loop +from oclaw.runtime.memory_stage import build_memory_context + +__all__ = ["AgentCoreRunInput", "run_agent_core", "run_direct_loop", "build_memory_context"] + diff --git a/openclaw_runtime/direct_loop.py b/runtime/direct_loop.py similarity index 93% rename from openclaw_runtime/direct_loop.py rename to runtime/direct_loop.py index 7c3c7782..a15840cf 100644 --- a/openclaw_runtime/direct_loop.py +++ b/runtime/direct_loop.py @@ -8,18 +8,18 @@ from dataclasses import dataclass from types import SimpleNamespace from typing import Any, Callable, Optional -from oclaw.chat.agent_messages import build_llm_messages -from oclaw.chat.tool_runtime import ToolExecutionConfig -from oclaw.chat.turn_types import TurnRunOutcome -from oclaw.openclaw_runtime.skill_executor import SkillExecutionContext, SkillExecutor -from oclaw.openclaw_runtime.skills import build_skill_manifest +from oclaw.runtime.chat.agent_messages import build_llm_messages +from oclaw.runtime.chat.tool_runtime import ToolExecutionConfig +from oclaw.runtime.chat.turn_types import TurnRunOutcome +from oclaw.runtime.skill_executor import SkillExecutionContext, SkillExecutor +from oclaw.runtime.skills import build_skill_manifest from oclaw.platform.llm.chat_models import ChatModel -from oclaw.openclaw_runtime.system_prompt import build_openclaw_executor_system_prompt -from oclaw.openclaw_runtime.types import OpenClawMemoryContext -from oclaw.orchestration.trace import new_span_id -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.system_prompt import build_oclaw_executor_system_prompt +from oclaw.runtime.types import OclawMemoryContext +from oclaw.runtime.orchestration.trace import new_span_id +from oclaw.runtime.tools.base import ToolRegistry -_OPENCLAW_TOOL_RESULT_HARD_CAP_CHARS = 24_000 +_OCLAW_TOOL_RESULT_HARD_CAP_CHARS = 24_000 _DIRECT_LOOP_OC_STAGE: dict[str, str] = { "tool_wire_filter": "wire_filter", @@ -43,7 +43,7 @@ def _emit_direct_loop_trace( if not trace_id: return merged: dict[str, Any] = dict(payload or {}) - merged.setdefault("pipeline", "openclaw_direct_loop") + merged.setdefault("pipeline", "oclaw_direct_loop") merged.setdefault("trace_id", str(trace_id)) merged.setdefault("lang", str(lang or "")) merged["oc_stage"] = _DIRECT_LOOP_OC_STAGE.get(event_type, event_type) @@ -113,7 +113,7 @@ def _guard_tool_results_for_llm_context( This does NOT rewrite DB history (tool_log / chat_message). It only guards the in-flight LLM context to prevent provider context overflow spirals. """ - cap = max(4096, min(int(hard_cap_chars or _OPENCLAW_TOOL_RESULT_HARD_CAP_CHARS), 500_000)) + cap = max(4096, min(int(hard_cap_chars or _OCLAW_TOOL_RESULT_HARD_CAP_CHARS), 500_000)) out: list[Any] = [] for m in store_messages or []: role = str(getattr(m, "role", "") or "") @@ -196,7 +196,7 @@ def _build_model_context( system_prompt: str, model: ChatModel, lang: str, - memory_context: OpenClawMemoryContext | None, + memory_context: OclawMemoryContext | None, trace_id: str | None, parent_span_id: str | None, tools: ToolRegistry | None = None, @@ -213,12 +213,12 @@ def _build_model_context( store_messages=rows, trace_id=trace_id, parent_span_id=parent_span_id, - hard_cap_chars=_OPENCLAW_TOOL_RESULT_HARD_CAP_CHARS, + hard_cap_chars=_OCLAW_TOOL_RESULT_HARD_CAP_CHARS, run_id=run_id, attempt_no=attempt_no, lang=lang, ) - final_system = build_openclaw_executor_system_prompt( + final_system = build_oclaw_executor_system_prompt( store=store, tools=tools, base_url=str(base_url or ""), @@ -256,7 +256,7 @@ def _prepare_llm_tools( raw_llm_tools = [s.as_openai_tool() for s in skill_specs] else: raw_llm_tools = tools.as_openai_tools() - from oclaw.tools.exposure_plan import build_llm_tools_plan + from oclaw.runtime.tools.exposure_plan import build_llm_tools_plan plan = build_llm_tools_plan( store=store, @@ -287,7 +287,7 @@ def _prepare_llm_tools( parent_span_id=parent_span_id, event_type="tool_wire_filter", payload={ - "runner": "openclaw_direct", + "runner": "oclaw_direct", "base_url": base_url, "wire_policy_role": str(wire_policy_role or ""), "tools_before": len(raw_names), @@ -333,7 +333,7 @@ def _prepare_llm_tools( parent_span_id=parent_span_id, event_type="tool_exposure_plan", payload={ - "runner": "openclaw_direct", + "runner": "oclaw_direct", "base_url": base_url, "wire_policy_role": str(wire_policy_role or ""), "role_mode": str(plan.role_mode or ""), @@ -447,7 +447,7 @@ def _execute_tool_step( session_id=session_id, lang=lang, user_text=user_text, - specialist="openclaw", + specialist="oclaw", trace_id=trace_id, parent_span_id=parent_span_id, workspace_owner_session_id=workspace_owner_session_id, @@ -467,7 +467,7 @@ def _execute_tool_step( return int((time.perf_counter() - t0) * 1000), results_by_id -def run_openclaw_direct_loop( +def run_oclaw_direct_loop( *, store: Any, session_id: str, @@ -492,7 +492,7 @@ def run_openclaw_direct_loop( path_policy_tenant_id: str | None = None, path_policy_user_id: str | None = None, workspace_dir: str | None = None, - memory_context: OpenClawMemoryContext | None = None, + memory_context: OclawMemoryContext | None = None, persist_user_message: bool = True, tool_signature_budget: int = 2, skill_binding_role: str | None = None, @@ -613,5 +613,9 @@ def run_openclaw_direct_loop( ) -__all__ = ["run_openclaw_direct_loop"] +def run_direct_loop(**kwargs: Any) -> TurnRunOutcome: + return run_oclaw_direct_loop(**kwargs) + + +__all__ = ["run_oclaw_direct_loop", "run_direct_loop"] diff --git a/extensions/README.md b/runtime/extensions/README.md similarity index 81% rename from extensions/README.md rename to runtime/extensions/README.md index 9f41f8db..121a2376 100644 --- a/extensions/README.md +++ b/runtime/extensions/README.md @@ -1,6 +1,6 @@ # extensions(本地重写版) -本目录用于承接 `vendor/openclaw/extensions` 的本地可维护说明,采用中文为主。 +本目录用于承接 `vendor/oclaw/extensions` 的本地可维护说明,采用中文为主。 ## 目录 - `api/`:扩展层 API 总览与接入约定。 diff --git a/extensions/__init__.py b/runtime/extensions/__init__.py similarity index 100% rename from extensions/__init__.py rename to runtime/extensions/__init__.py diff --git a/extensions/anthropic/__init__.py b/runtime/extensions/anthropic/__init__.py similarity index 100% rename from extensions/anthropic/__init__.py rename to runtime/extensions/anthropic/__init__.py diff --git a/extensions/anthropic/api.py b/runtime/extensions/anthropic/api.py similarity index 100% rename from extensions/anthropic/api.py rename to runtime/extensions/anthropic/api.py diff --git a/extensions/anthropic/index.py b/runtime/extensions/anthropic/index.py similarity index 89% rename from extensions/anthropic/index.py rename to runtime/extensions/anthropic/index.py index 929385cc..5672521f 100644 --- a/extensions/anthropic/index.py +++ b/runtime/extensions/anthropic/index.py @@ -1,7 +1,7 @@ from __future__ import annotations from .api import build_anthropic_provider -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry PLUGIN_ID = "anthropic" diff --git a/extensions/api/README.md b/runtime/extensions/api/README.md similarity index 81% rename from extensions/api/README.md rename to runtime/extensions/api/README.md index 7283fa60..fd05836b 100644 --- a/extensions/api/README.md +++ b/runtime/extensions/api/README.md @@ -1,9 +1,9 @@ # API 总览(extensions) -本文是 `vendor/openclaw/extensions` 的 API 视角重写,中文优先。 +本文是 `vendor/oclaw/extensions` 的 API 视角重写,中文优先。 ## extension API 是什么 -- 每个 extension 是一个插件单元,通常通过 `openclaw.plugin.json` 声明 `id`、`name`、`configSchema`。 +- 每个 extension 是一个插件单元,通常通过 `oclaw.plugin.json` 声明 `id`、`name`、`configSchema`。 - 代码入口一般通过 `index.ts` 注册插件行为(如路由注册、事件处理、任务绑定)。 - `api.ts` 常用于统一导出插件 SDK 类型或对外公开接口。 diff --git a/extensions/api/__init__.py b/runtime/extensions/api/__init__.py similarity index 100% rename from extensions/api/__init__.py rename to runtime/extensions/api/__init__.py diff --git a/extensions/plugin_api.py b/runtime/extensions/api/plugin_api.py similarity index 89% rename from extensions/plugin_api.py rename to runtime/extensions/api/plugin_api.py index bea37f5b..32667909 100644 --- a/extensions/plugin_api.py +++ b/runtime/extensions/api/plugin_api.py @@ -6,7 +6,9 @@ from typing import Any, Callable, Protocol class PluginLogger(Protocol): def info(self, message: str) -> None: ... + def warning(self, message: str) -> None: ... + def error(self, message: str) -> None: ... @@ -31,3 +33,7 @@ class PluginEntry: def define_plugin_entry(*, id: str, name: str, description: str, register: Callable[[PluginApi], None]) -> PluginEntry: return PluginEntry(id=id, name=name, description=description, register=register) + + +__all__ = ["PluginApi", "PluginEntry", "PluginLogger", "PluginRuntime", "define_plugin_entry"] + diff --git a/extensions/deepseek/__init__.py b/runtime/extensions/deepseek/__init__.py similarity index 100% rename from extensions/deepseek/__init__.py rename to runtime/extensions/deepseek/__init__.py diff --git a/extensions/deepseek/api.py b/runtime/extensions/deepseek/api.py similarity index 100% rename from extensions/deepseek/api.py rename to runtime/extensions/deepseek/api.py diff --git a/extensions/deepseek/index.py b/runtime/extensions/deepseek/index.py similarity index 89% rename from extensions/deepseek/index.py rename to runtime/extensions/deepseek/index.py index 513acb87..025d128b 100644 --- a/extensions/deepseek/index.py +++ b/runtime/extensions/deepseek/index.py @@ -1,7 +1,7 @@ from __future__ import annotations from .api import build_deepseek_provider -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry PROVIDER_ID = "deepseek" diff --git a/extensions/image-generation-core/__init__.py b/runtime/extensions/image-generation-core/__init__.py similarity index 100% rename from extensions/image-generation-core/__init__.py rename to runtime/extensions/image-generation-core/__init__.py diff --git a/extensions/image-generation-core/api.py b/runtime/extensions/image-generation-core/api.py similarity index 100% rename from extensions/image-generation-core/api.py rename to runtime/extensions/image-generation-core/api.py diff --git a/extensions/image-generation-core/index.py b/runtime/extensions/image-generation-core/index.py similarity index 89% rename from extensions/image-generation-core/index.py rename to runtime/extensions/image-generation-core/index.py index cc26a506..4ac98f60 100644 --- a/extensions/image-generation-core/index.py +++ b/runtime/extensions/image-generation-core/index.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry from .api import list_runtime_image_generation_providers diff --git a/extensions/image-generation-core/runtime_api.py b/runtime/extensions/image-generation-core/runtime_api.py similarity index 100% rename from extensions/image-generation-core/runtime_api.py rename to runtime/extensions/image-generation-core/runtime_api.py diff --git a/extensions/kimi-coding/__init__.py b/runtime/extensions/kimi-coding/__init__.py similarity index 100% rename from extensions/kimi-coding/__init__.py rename to runtime/extensions/kimi-coding/__init__.py diff --git a/extensions/kimi-coding/api.py b/runtime/extensions/kimi-coding/api.py similarity index 100% rename from extensions/kimi-coding/api.py rename to runtime/extensions/kimi-coding/api.py diff --git a/extensions/kimi-coding/index.py b/runtime/extensions/kimi-coding/index.py similarity index 86% rename from extensions/kimi-coding/index.py rename to runtime/extensions/kimi-coding/index.py index 45440426..d19f6158 100644 --- a/extensions/kimi-coding/index.py +++ b/runtime/extensions/kimi-coding/index.py @@ -1,7 +1,7 @@ from __future__ import annotations from .api import build_kimi_coding_provider -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry PLUGIN_ID = "kimi" diff --git a/extensions/memory-core/__init__.py b/runtime/extensions/memory-core/__init__.py similarity index 100% rename from extensions/memory-core/__init__.py rename to runtime/extensions/memory-core/__init__.py diff --git a/extensions/memory-core/api.py b/runtime/extensions/memory-core/api.py similarity index 98% rename from extensions/memory-core/api.py rename to runtime/extensions/memory-core/api.py index 0c836f47..14e31813 100644 --- a/extensions/memory-core/api.py +++ b/runtime/extensions/memory-core/api.py @@ -3,9 +3,9 @@ from __future__ import annotations from pathlib import Path import re -DIARY_START_MARKER = "" -DIARY_END_MARKER = "" -BACKFILL_ENTRY_MARKER = "openclaw:dreaming:backfill-entry" +DIARY_START_MARKER = "" +DIARY_END_MARKER = "" +BACKFILL_ENTRY_MARKER = "oclaw:dreaming:backfill-entry" def _resolve_dreams_path(workspace_dir: str) -> Path: base = Path(workspace_dir) @@ -123,7 +123,7 @@ def dedupe_dream_diary_entries(*, workspace_dir: str) -> dict: def preview_grounded_rem_markdown(*, workspace_dir: str, input_paths: list[str]) -> dict: workspace = Path(workspace_dir).resolve() - # ---- Grounded REM heuristics (ported/simplified from vendor/openclaw memory-core) ---- + # ---- Grounded REM heuristics (ported/simplified from vendor/oclaw memory-core) ---- blocked_section_re = re.compile( r"\b(morning reminders|tasks? for today|to-?do|action items?|next steps?|stats|setup tasks?)\b", re.I, diff --git a/extensions/memory-core/index.py b/runtime/extensions/memory-core/index.py similarity index 87% rename from extensions/memory-core/index.py rename to runtime/extensions/memory-core/index.py index 65130397..4ae21f81 100644 --- a/extensions/memory-core/index.py +++ b/runtime/extensions/memory-core/index.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry PLUGIN_ID = "memory-core" PLUGIN_NAME = "Memory (Core)" diff --git a/extensions/memory-lancedb/__init__.py b/runtime/extensions/memory-lancedb/__init__.py similarity index 100% rename from extensions/memory-lancedb/__init__.py rename to runtime/extensions/memory-lancedb/__init__.py diff --git a/extensions/memory-lancedb/api.py b/runtime/extensions/memory-lancedb/api.py similarity index 51% rename from extensions/memory-lancedb/api.py rename to runtime/extensions/memory-lancedb/api.py index 8420fea6..8a187959 100644 --- a/extensions/memory-lancedb/api.py +++ b/runtime/extensions/memory-lancedb/api.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry __all__ = ["PluginEntry", "define_plugin_entry"] diff --git a/extensions/memory-lancedb/index.py b/runtime/extensions/memory-lancedb/index.py similarity index 95% rename from extensions/memory-lancedb/index.py rename to runtime/extensions/memory-lancedb/index.py index 0802131a..bd25ff78 100644 --- a/extensions/memory-lancedb/index.py +++ b/runtime/extensions/memory-lancedb/index.py @@ -3,7 +3,7 @@ from __future__ import annotations import html import re -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry PROMPT_INJECTION_PATTERNS = ( re.compile(r"ignore (all|any|previous|above|prior) instructions", re.I), diff --git a/extensions/memory-wiki/__init__.py b/runtime/extensions/memory-wiki/__init__.py similarity index 100% rename from extensions/memory-wiki/__init__.py rename to runtime/extensions/memory-wiki/__init__.py diff --git a/extensions/memory-wiki/api.py b/runtime/extensions/memory-wiki/api.py similarity index 100% rename from extensions/memory-wiki/api.py rename to runtime/extensions/memory-wiki/api.py diff --git a/extensions/memory-wiki/index.py b/runtime/extensions/memory-wiki/index.py similarity index 87% rename from extensions/memory-wiki/index.py rename to runtime/extensions/memory-wiki/index.py index 39391c0d..e2af3a38 100644 --- a/extensions/memory-wiki/index.py +++ b/runtime/extensions/memory-wiki/index.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry from .api import build_plugin_config_schema, build_wiki_tool_specs @@ -14,7 +14,7 @@ def build_memory_wiki_plugin_entry() -> PluginEntry: entry = define_plugin_entry( id="memory-wiki", name="Memory Wiki", - description="Persistent wiki compiler and Obsidian-friendly knowledge vault for OpenClaw.", + description="Persistent wiki compiler and Obsidian-friendly knowledge vault for Oclaw.", register=register_memory_wiki_plugin, ) # Best-effort compatibility for loaders that read config schema from entry object. diff --git a/extensions/openai/__init__.py b/runtime/extensions/openai/__init__.py similarity index 100% rename from extensions/openai/__init__.py rename to runtime/extensions/openai/__init__.py diff --git a/extensions/openai/api.py b/runtime/extensions/openai/api.py similarity index 100% rename from extensions/openai/api.py rename to runtime/extensions/openai/api.py diff --git a/extensions/openai/index.py b/runtime/extensions/openai/index.py similarity index 90% rename from extensions/openai/index.py rename to runtime/extensions/openai/index.py index a25898a8..2a8cb3c9 100644 --- a/extensions/openai/index.py +++ b/runtime/extensions/openai/index.py @@ -5,7 +5,7 @@ from .api import ( build_openai_image_generation_provider, build_openai_provider, ) -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry def register_openai_plugin(api) -> None: diff --git a/extensions/api/plugin_api.py b/runtime/extensions/plugin_api.py similarity index 51% rename from extensions/api/plugin_api.py rename to runtime/extensions/plugin_api.py index 463a88a3..66c8af78 100644 --- a/extensions/api/plugin_api.py +++ b/runtime/extensions/plugin_api.py @@ -1,6 +1,5 @@ from __future__ import annotations -from oclaw.extensions.plugin_api import PluginApi, PluginEntry, PluginLogger, PluginRuntime, define_plugin_entry +from oclaw.runtime.extensions.api.plugin_api import PluginApi, PluginEntry, PluginLogger, PluginRuntime, define_plugin_entry __all__ = ["PluginApi", "PluginEntry", "PluginLogger", "PluginRuntime", "define_plugin_entry"] - diff --git a/extensions/qwen/__init__.py b/runtime/extensions/qwen/__init__.py similarity index 100% rename from extensions/qwen/__init__.py rename to runtime/extensions/qwen/__init__.py diff --git a/extensions/qwen/api.py b/runtime/extensions/qwen/api.py similarity index 100% rename from extensions/qwen/api.py rename to runtime/extensions/qwen/api.py diff --git a/extensions/qwen/index.py b/runtime/extensions/qwen/index.py similarity index 87% rename from extensions/qwen/index.py rename to runtime/extensions/qwen/index.py index e2b10ac6..703f8cfb 100644 --- a/extensions/qwen/index.py +++ b/runtime/extensions/qwen/index.py @@ -1,7 +1,7 @@ from __future__ import annotations from .api import QWEN_BASE_URL, build_qwen_provider -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry PROVIDER_ID = "qwen" diff --git a/extensions/telegram/__init__.py b/runtime/extensions/telegram/__init__.py similarity index 100% rename from extensions/telegram/__init__.py rename to runtime/extensions/telegram/__init__.py diff --git a/extensions/telegram/api.py b/runtime/extensions/telegram/api.py similarity index 100% rename from extensions/telegram/api.py rename to runtime/extensions/telegram/api.py diff --git a/extensions/telegram/index.py b/runtime/extensions/telegram/index.py similarity index 86% rename from extensions/telegram/index.py rename to runtime/extensions/telegram/index.py index 3444ec4c..3478dfae 100644 --- a/extensions/telegram/index.py +++ b/runtime/extensions/telegram/index.py @@ -1,7 +1,7 @@ from __future__ import annotations from .api import telegram_plugin -from oclaw.extensions.plugin_api import PluginEntry, define_plugin_entry +from oclaw.runtime.extensions.plugin_api import PluginEntry, define_plugin_entry def register_telegram_channel(api) -> None: diff --git a/extensions/webhooks/README.md b/runtime/extensions/webhooks/README.md similarity index 86% rename from extensions/webhooks/README.md rename to runtime/extensions/webhooks/README.md index beb59ebd..5c1ef9c8 100644 --- a/extensions/webhooks/README.md +++ b/runtime/extensions/webhooks/README.md @@ -1,13 +1,13 @@ # Webhooks 扩展(中文重写) -对应上游目录:`vendor/openclaw/extensions/webhooks` +对应上游目录:`vendor/oclaw/extensions/webhooks` ## 能力定位 -- 提供“认证后的入站 webhook”,把外部自动化请求绑定到 OpenClaw TaskFlow。 +- 提供“认证后的入站 webhook”,把外部自动化请求绑定到 Oclaw TaskFlow。 - 支持多 route 配置,每个 route 对应 path/session/secret/controller。 ## 关键入口 -- `openclaw.plugin.json`:声明插件 `id=webhooks` 及配置 schema。 +- `oclaw.plugin.json`:声明插件 `id=webhooks` 及配置 schema。 - `index.ts`:读取 routes 配置并注册 HTTP 路由。 - `api.ts`:导出插件 SDK 类型入口。 - `runtime-api.ts`:运行时相关公共接口。 diff --git a/extensions/webhooks/__init__.py b/runtime/extensions/webhooks/__init__.py similarity index 100% rename from extensions/webhooks/__init__.py rename to runtime/extensions/webhooks/__init__.py diff --git a/extensions/webhooks/api.py b/runtime/extensions/webhooks/api.py similarity index 92% rename from extensions/webhooks/api.py rename to runtime/extensions/webhooks/api.py index fd3c09f8..4e62daa6 100644 --- a/extensions/webhooks/api.py +++ b/runtime/extensions/webhooks/api.py @@ -8,7 +8,7 @@ def build_webhooks_plugin_entry() -> PluginEntry: return define_plugin_entry( id="webhooks", name="Webhooks", - description="Authenticated inbound webhooks that bind external automation to OpenClaw TaskFlows.", + description="Authenticated inbound webhooks that bind external automation to Oclaw TaskFlows.", register=lambda api: register_webhook_routes(api), ) diff --git a/extensions/webhooks/config.py b/runtime/extensions/webhooks/config.py similarity index 100% rename from extensions/webhooks/config.py rename to runtime/extensions/webhooks/config.py diff --git a/extensions/webhooks/http.py b/runtime/extensions/webhooks/http.py similarity index 99% rename from extensions/webhooks/http.py rename to runtime/extensions/webhooks/http.py index 3da60f06..e7ea0639 100644 --- a/extensions/webhooks/http.py +++ b/runtime/extensions/webhooks/http.py @@ -89,7 +89,7 @@ def _extract_secret(headers: dict[str, str]) -> str: auth = str(headers.get("authorization") or "") if auth.lower().startswith("bearer "): return auth[7:].strip() - return str(headers.get("x-openclaw-webhook-secret") or "").strip() + return str(headers.get("x-oclaw-webhook-secret") or "").strip() def _map_mutation_status(result: dict[str, Any]) -> tuple[int, str | None, str | None]: diff --git a/extensions/webhooks/index.py b/runtime/extensions/webhooks/index.py similarity index 100% rename from extensions/webhooks/index.py rename to runtime/extensions/webhooks/index.py diff --git a/extensions/webhooks/runtime_api.py b/runtime/extensions/webhooks/runtime_api.py similarity index 100% rename from extensions/webhooks/runtime_api.py rename to runtime/extensions/webhooks/runtime_api.py diff --git a/extensions/whatsapp/README.md b/runtime/extensions/whatsapp/README.md similarity index 83% rename from extensions/whatsapp/README.md rename to runtime/extensions/whatsapp/README.md index edbc1d59..f62485e9 100644 --- a/extensions/whatsapp/README.md +++ b/runtime/extensions/whatsapp/README.md @@ -1,14 +1,14 @@ # WhatsApp 扩展(中文重写) -对应上游目录:`vendor/openclaw/extensions/whatsapp` +对应上游目录:`vendor/oclaw/extensions/whatsapp` ## 能力定位 -- 作为 OpenClaw 渠道插件接入 WhatsApp。 +- 作为 Oclaw 渠道插件接入 WhatsApp。 - 负责账号接入、消息目标标准化、群/目录策略、命令策略和运行时辅助能力。 ## 关键入口 - `api.ts`:对外导出主要能力与工具函数(channel 插件、策略、target 规范化等)。 -- `openclaw.plugin.json`:插件元信息(`id=whatsapp`,渠道声明、配置 schema)。 +- `oclaw.plugin.json`:插件元信息(`id=whatsapp`,渠道声明、配置 schema)。 - `oclaw/`:具体实现(收发链路、访问控制、配置解析、运行时逻辑)。 ## API 文件清单(根目录) diff --git a/extensions/whatsapp/__init__.py b/runtime/extensions/whatsapp/__init__.py similarity index 100% rename from extensions/whatsapp/__init__.py rename to runtime/extensions/whatsapp/__init__.py diff --git a/extensions/whatsapp/api.py b/runtime/extensions/whatsapp/api.py similarity index 100% rename from extensions/whatsapp/api.py rename to runtime/extensions/whatsapp/api.py diff --git a/openclaw_runtime/gateway.py b/runtime/gateway.py similarity index 94% rename from openclaw_runtime/gateway.py rename to runtime/gateway.py index ee7b0498..eee52346 100644 --- a/openclaw_runtime/gateway.py +++ b/runtime/gateway.py @@ -8,31 +8,30 @@ from dataclasses import dataclass from pathlib import Path from typing import Any, Callable, Optional -from oclaw.agents.factory import build_ephemeral_executor -from oclaw.infrastructure.agent_context import build_role_system_context -from oclaw.openclaw_runtime.hooks_runtime import ( +from oclaw.runtime.agents.factory import build_ephemeral_executor +from oclaw.runtime.agent_context import build_role_system_context +from oclaw.runtime.hooks_runtime import ( get_active_hooks_config, initialize_hooks_runtime, trigger_hook_event, ) -from oclaw.openclaw_runtime.relay_pointer import summarize_relay_ttl -from oclaw.openclaw_runtime.skills import build_skill_manifest -from oclaw.openclaw_runtime.types import ( - OpenClawSessionContext, +from oclaw.runtime.relay_pointer import summarize_relay_ttl +from oclaw.runtime.skills import build_skill_manifest +from oclaw.runtime.types import ( + OclawSessionContext, StandardMessage, normalize_interaction_mode, normalize_requested_specialist, ) from oclaw.platform.config.paths import PROJECT_ROOT from oclaw.prompts import render_prompt -from oclaw.prompts.loader import render_openclaw_prompt +from oclaw.prompts.loader import render_runtime_prompt -from oclaw.openclaw_runtime.agent_core_run import AgentCoreRunInput, run_agent_core -from oclaw.openclaw_runtime.command_parser import parse_internal_command -from oclaw.openclaw_runtime.memory_stage import build_memory_context -from oclaw.openclaw_runtime.router import decide_route -from oclaw.openclaw_runtime.worker import ensure_worker_started -from oclaw.orchestration.trace import new_span_id, new_trace_id +from oclaw.runtime.command_parser import parse_internal_command +from oclaw.runtime.core.agent_execution import AgentCoreRunInput, build_memory_context, run_agent_core +from oclaw.runtime.router import decide_route +from oclaw.runtime.worker import ensure_worker_started +from oclaw.runtime.orchestration.trace import new_span_id, new_trace_id _OC_STAGE_BY_EVENT: dict[str, str] = { "gateway_received": "ingress", @@ -51,7 +50,7 @@ _DEFAULT_TABULAR_ROWS_READ = 5000 @dataclass(frozen=True) -class OpenClawGatewayResult: +class OclawGatewayResult: run_id: str reply_text: str trace_id: str @@ -73,7 +72,7 @@ class OpenClawGatewayResult: relay_ttl_keep_count: int = 0 -class OpenClawGateway: +class OclawGateway: def __init__(self, *, store: Any): self.store = store @@ -106,7 +105,7 @@ class OpenClawGateway: if not isinstance(raw, dict): return None name = str(raw.get("name") or "").strip() - system_prompt = OpenClawGateway._sanitize_dynamic_system_prompt(raw.get("system_prompt")) + system_prompt = OclawGateway._sanitize_dynamic_system_prompt(raw.get("system_prompt")) reason = str(raw.get("reason") or "").strip() tp = raw.get("tool_policy") tool_policy = tp if isinstance(tp, dict) else {} @@ -140,7 +139,7 @@ class OpenClawGateway: allowed_fixed.append("memory_curator") allowed_fixed_csv = ",".join(allowed_fixed) allowed_fixed_quoted = ", ".join([f'"{x}"' for x in allowed_fixed]) - user_block = render_openclaw_prompt( + user_block = render_runtime_prompt( "manager/decision.md", variables={"agent_registry": f"specialists: {allowed_fixed_csv}"}, strict=True, @@ -208,7 +207,7 @@ class OpenClawGateway: @staticmethod def _tabular_query_system_hint(lang: str) -> str: - limits = OpenClawGateway._tabular_limits_from_config() + limits = OclawGateway._tabular_limits_from_config() preview_rows = int(limits.get("large_table_preview_rows") or _DEFAULT_TABULAR_PREVIEW_ROWS) max_rows_read = int(limits.get("max_rows_read") or _DEFAULT_TABULAR_ROWS_READ) if str(lang or "").startswith("en"): @@ -282,7 +281,7 @@ class OpenClawGateway: ws = str(msg.metadata.get("workspaceDir") or msg.metadata.get("workspace_dir") or "").strip() if ws: return ws - return str(os.getenv("OPENCLAW_WORKSPACE") or "").strip() + return str(os.getenv("OCLAW_WORKSPACE") or "").strip() @staticmethod def _resolve_command_source(msg: StandardMessage) -> str: @@ -297,7 +296,7 @@ class OpenClawGateway: md = msg.metadata if isinstance(msg.metadata, dict) else {} cfg = get_active_hooks_config() return { - "commandSource": OpenClawGateway._resolve_command_source(msg), + "commandSource": OclawGateway._resolve_command_source(msg), "senderId": str(msg.user_id or "unknown"), "workspaceDir": str(workspace_dir or ""), "sessionEntry": { @@ -314,13 +313,13 @@ class OpenClawGateway: def _trace( self, *, - ctx: OpenClawSessionContext, + ctx: OclawSessionContext, event_type: str, payload: dict[str, Any], started_at: float | None = None, ) -> None: merged: dict[str, Any] = dict(payload or {}) - merged.setdefault("pipeline", "openclaw_gateway") + merged.setdefault("pipeline", "oclaw_gateway") merged.setdefault("trace_id", ctx.trace_id) merged.setdefault("lang", str(ctx.lang or "")) merged["oc_stage"] = _OC_STAGE_BY_EVENT.get(event_type, event_type) @@ -350,11 +349,11 @@ class OpenClawGateway: on_tool_ui: Optional[Callable[[str, dict[str, Any]], None]] = None, should_stop: Optional[Callable[[], bool]] = None, specialist_executor_factory: Optional[Callable[[str], Any]] = None, - ) -> OpenClawGatewayResult: + ) -> OclawGatewayResult: t0 = time.perf_counter() trace_id = new_trace_id() rid = str(run_id or "").strip() or str(uuid.uuid4()) - ctx = OpenClawSessionContext( + ctx = OclawSessionContext( session_id=msg.session_id, tenant_id=msg.tenant_id, user_id=msg.user_id, @@ -520,7 +519,7 @@ class OpenClawGateway: on_progress("oclaw: running…") if route.mode == "async_task": worker_id = ensure_worker_started(store=self.store) - task = self.store.openclaw_task_create( + task = self.store.oclaw_task_create( tenant_id=msg.tenant_id, session_id=msg.session_id, task_type="async_turn", @@ -573,7 +572,7 @@ class OpenClawGateway: payload={"ok": True, "elapsed_ms": elapsed_ms, "mode": "async_task", "task_id": str(task.id)}, started_at=t0, ) - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=rid, reply_text=reply, trace_id=trace_id, @@ -626,7 +625,7 @@ class OpenClawGateway: max_messages=_get_int_setting("AIA_TURN_MAX_CONTEXT_MESSAGES", 80, 10, 400), max_tool_rounds=_get_int_setting("AIA_TURN_MAX_TOOL_ROUNDS", 8, 1, 30), max_tool_workers=_get_int_setting("AIA_TURN_MAX_TOOL_WORKERS", 8, 1, 32), - max_attempts=_get_int_setting("AIA_OPENCLAW_MAX_ATTEMPTS", 2, 1, 5), + max_attempts=_get_int_setting("AIA_OCLAW_MAX_ATTEMPTS", 2, 1, 5), memory_context=memory_context, on_token=on_token, on_progress=on_progress, @@ -639,7 +638,7 @@ class OpenClawGateway: reply = core_out.outcome.final_text except Exception as exc: base = render_prompt( - "fallback/openclaw_runtime_error.en.md" if str(lang or "").startswith("en") else "fallback/openclaw_runtime_error.zh.md", + "fallback/runtime_error.en.md" if str(lang or "").startswith("en") else "fallback/runtime_error.zh.md", strict=True, ) detail = f"{type(exc).__name__}: {str(exc or '')}".strip().replace("\n", " ")[:400] @@ -652,7 +651,7 @@ class OpenClawGateway: payload={"ok": bool(str(reply or "").strip()), "elapsed_ms": elapsed_ms, "mode": "sync_direct"}, started_at=t0, ) - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=rid, reply_text=str(reply or ""), trace_id=trace_id, @@ -674,4 +673,4 @@ class OpenClawGateway: ) -__all__ = ["OpenClawGateway", "OpenClawGatewayResult"] +__all__ = ["OclawGateway", "OclawGatewayResult"] diff --git a/hooks/README.md b/runtime/hooks/README.md similarity index 87% rename from hooks/README.md rename to runtime/hooks/README.md index b00b0605..da04b561 100644 --- a/hooks/README.md +++ b/runtime/hooks/README.md @@ -14,12 +14,12 @@ Unified Python hooks runtime and hook packages. Put hooks in any of: - **Bundled**: `oclaw/hooks/bundled//` -- **Managed**: `~/.openclaw/hooks//` +- **Managed**: `~/.oclaw/hooks//` - **Workspace**: `/hooks//` (explicit opt-in by default) Each hook directory must contain: -- `HOOK.md` with YAML frontmatter including `metadata.openclaw.events` +- `HOOK.md` with YAML frontmatter including `metadata.oclaw.events` - `handler.py` (or `index.py`) exporting a callable `handle(event)` ### Minimal self-test diff --git a/hooks/__init__.py b/runtime/hooks/__init__.py similarity index 100% rename from hooks/__init__.py rename to runtime/hooks/__init__.py diff --git a/hooks/_selftest.py b/runtime/hooks/_selftest.py similarity index 88% rename from hooks/_selftest.py rename to runtime/hooks/_selftest.py index f05fe55f..98ce9ee6 100644 --- a/hooks/_selftest.py +++ b/runtime/hooks/_selftest.py @@ -5,17 +5,17 @@ from pathlib import Path import sys HOOKS_PY_DIR = Path(__file__).resolve().parent -OPENCLAW_DIR = HOOKS_PY_DIR.parent -if str(OPENCLAW_DIR) not in sys.path: - sys.path.insert(0, str(OPENCLAW_DIR)) +OCLAW_DIR = HOOKS_PY_DIR.parent +if str(OCLAW_DIR) not in sys.path: + sys.path.insert(0, str(OCLAW_DIR)) -from hooks.internal_hooks import create_hook_event, trigger_hook # noqa: E402 -from hooks.loader import load_internal_hooks # noqa: E402 +from oclaw.runtime.hooks.internal_hooks import create_hook_event, trigger_hook # noqa: E402 +from oclaw.runtime.hooks.loader import load_internal_hooks # noqa: E402 def main() -> int: repo_root = Path(__file__).resolve().parents[1] - bundled = repo_root / "hooks" / "bundled" + bundled = repo_root / "runtime" / "hooks" / "bundled" ws_dir = repo_root / "_hooks_selftest_workspace" ws_dir.mkdir(parents=True, exist_ok=True) (ws_dir / "memory").mkdir(parents=True, exist_ok=True) diff --git a/hooks/bundled/boot-md/HOOK.md b/runtime/hooks/bundled/boot-md/HOOK.md similarity index 95% rename from hooks/bundled/boot-md/HOOK.md rename to runtime/hooks/bundled/boot-md/HOOK.md index 0258e96a..30807721 100644 --- a/hooks/bundled/boot-md/HOOK.md +++ b/runtime/hooks/bundled/boot-md/HOOK.md @@ -2,7 +2,7 @@ name: boot-md description: "Run BOOT.md on gateway startup" metadata: - openclaw: + oclaw: emoji: "🚀" events: ["gateway:startup"] --- diff --git a/hooks/bundled/boot-md/handler.py b/runtime/hooks/bundled/boot-md/handler.py similarity index 87% rename from hooks/bundled/boot-md/handler.py rename to runtime/hooks/bundled/boot-md/handler.py index 803e8a3f..d305ce10 100644 --- a/hooks/bundled/boot-md/handler.py +++ b/runtime/hooks/bundled/boot-md/handler.py @@ -7,10 +7,10 @@ from typing import Any, Iterable def _resolve_state_dir() -> Path: - override = os.environ.get("OPENCLAW_STATE_DIR") or os.environ.get("OPENCLAW_HOME") + override = os.environ.get("OCLAW_STATE_DIR") or os.environ.get("OCLAW_HOME") if override and override.strip(): return Path(os.path.expanduser(override.strip())).resolve() - return Path.home() / ".openclaw" + return Path.home() / ".oclaw" def _candidate_roots(event: Any) -> list[Path]: @@ -20,8 +20,8 @@ def _candidate_roots(event: Any) -> list[Path]: ws = ctx.get("workspaceDir") if isinstance(ctx, dict) else None if isinstance(ws, str) and ws.strip(): roots.append(Path(ws).expanduser()) - # 2) OPENCLAW_WORKSPACE env - env_ws = str(os.getenv("OPENCLAW_WORKSPACE") or "").strip() + # 2) OCLAW_WORKSPACE env + env_ws = str(os.getenv("OCLAW_WORKSPACE") or "").strip() if env_ws: roots.append(Path(env_ws).expanduser()) # 3) repo-local conventional roots @@ -29,7 +29,7 @@ def _candidate_roots(event: Any) -> list[Path]: repo = Path(__file__).resolve().parents[4] roots.extend( [ - repo / "oclaw" / "agent" / "workspace-main", + repo / "oclaw" / "runtime" / "assets" / "agent_workspaces" / "workspace-main", repo / "oclaw" / "workspace-main", repo / "oclaw" / "workspace", repo, diff --git a/hooks/bundled/bootstrap-extra-files/HOOK.md b/runtime/hooks/bundled/bootstrap-extra-files/HOOK.md similarity index 98% rename from hooks/bundled/bootstrap-extra-files/HOOK.md rename to runtime/hooks/bundled/bootstrap-extra-files/HOOK.md index fbbac6a0..f5e65f00 100644 --- a/hooks/bundled/bootstrap-extra-files/HOOK.md +++ b/runtime/hooks/bundled/bootstrap-extra-files/HOOK.md @@ -2,7 +2,7 @@ name: bootstrap-extra-files description: "Inject additional workspace bootstrap files via glob/path patterns" metadata: - openclaw: + oclaw: emoji: "📎" events: ["agent:bootstrap"] --- diff --git a/hooks/bundled/bootstrap-extra-files/handler.py b/runtime/hooks/bundled/bootstrap-extra-files/handler.py similarity index 98% rename from hooks/bundled/bootstrap-extra-files/handler.py rename to runtime/hooks/bundled/bootstrap-extra-files/handler.py index b5c29a15..cb0da8e5 100644 --- a/hooks/bundled/bootstrap-extra-files/handler.py +++ b/runtime/hooks/bundled/bootstrap-extra-files/handler.py @@ -124,7 +124,7 @@ def handle(event: Any) -> None: if not matches: return - # Mutate context.bootstrapFiles (OpenClaw-style). + # Mutate context.bootstrapFiles (Oclaw-style). boot = ctx.get("bootstrapFiles") if not isinstance(boot, list): boot = [] diff --git a/hooks/bundled/command-logger/HOOK.md b/runtime/hooks/bundled/command-logger/HOOK.md similarity index 68% rename from hooks/bundled/command-logger/HOOK.md rename to runtime/hooks/bundled/command-logger/HOOK.md index 1fa28f86..5d7d0c33 100644 --- a/hooks/bundled/command-logger/HOOK.md +++ b/runtime/hooks/bundled/command-logger/HOOK.md @@ -2,12 +2,12 @@ name: command-logger description: "Log all command events to a centralized audit file" metadata: - openclaw: + oclaw: emoji: "📝" events: ["command"] --- # Command Logger Hook (Python) -Logs all `command` events to `~/.openclaw/logs/commands.log` (JSONL). +Logs all `command` events to `~/.oclaw/logs/commands.log` (JSONL). diff --git a/hooks/bundled/command-logger/handler.py b/runtime/hooks/bundled/command-logger/handler.py similarity index 84% rename from hooks/bundled/command-logger/handler.py rename to runtime/hooks/bundled/command-logger/handler.py index 57e3ce3f..e04a2913 100644 --- a/hooks/bundled/command-logger/handler.py +++ b/runtime/hooks/bundled/command-logger/handler.py @@ -7,11 +7,11 @@ from typing import Any, Dict def _resolve_state_dir() -> Path: - # Keep this compatible with typical OpenClaw layouts. - override = os.environ.get("OPENCLAW_STATE_DIR") or os.environ.get("OPENCLAW_HOME") + # Keep this compatible with typical Oclaw layouts. + override = os.environ.get("OCLAW_STATE_DIR") or os.environ.get("OCLAW_HOME") if override and override.strip(): return Path(os.path.expanduser(override.strip())).resolve() - return Path.home() / ".openclaw" + return Path.home() / ".oclaw" def handle(event) -> None: diff --git a/hooks/bundled/session-memory/HOOK.md b/runtime/hooks/bundled/session-memory/HOOK.md similarity index 97% rename from hooks/bundled/session-memory/HOOK.md rename to runtime/hooks/bundled/session-memory/HOOK.md index cdfae09e..4d9bc9a1 100644 --- a/hooks/bundled/session-memory/HOOK.md +++ b/runtime/hooks/bundled/session-memory/HOOK.md @@ -2,7 +2,7 @@ name: session-memory description: "Save session context to memory when /new or /reset command is issued" metadata: - openclaw: + oclaw: emoji: "💾" events: ["command:new", "command:reset"] --- diff --git a/hooks/bundled/session-memory/handler.py b/runtime/hooks/bundled/session-memory/handler.py similarity index 98% rename from hooks/bundled/session-memory/handler.py rename to runtime/hooks/bundled/session-memory/handler.py index f76dd15b..e25dd096 100644 --- a/hooks/bundled/session-memory/handler.py +++ b/runtime/hooks/bundled/session-memory/handler.py @@ -52,7 +52,7 @@ def _workspace_dir_from_event(event: Any) -> Optional[Path]: ws = ctx.get("workspaceDir") if isinstance(ws, str) and ws.strip(): return Path(ws).expanduser() - env_ws = str(os.getenv("OPENCLAW_WORKSPACE") or "").strip() + env_ws = str(os.getenv("OCLAW_WORKSPACE") or "").strip() if env_ws: return Path(env_ws).expanduser() return None diff --git a/hooks/bundled/wiki-auto-inject/HOOK.md b/runtime/hooks/bundled/wiki-auto-inject/HOOK.md similarity index 96% rename from hooks/bundled/wiki-auto-inject/HOOK.md rename to runtime/hooks/bundled/wiki-auto-inject/HOOK.md index 14497772..0e193673 100644 --- a/hooks/bundled/wiki-auto-inject/HOOK.md +++ b/runtime/hooks/bundled/wiki-auto-inject/HOOK.md @@ -2,7 +2,7 @@ name: wiki-auto-inject description: "Inject wiki context before model prompt build" metadata: - openclaw: + oclaw: emoji: "📚" events: ["llm:before_prompt_build"] --- diff --git a/hooks/bundled/wiki-auto-inject/handler.py b/runtime/hooks/bundled/wiki-auto-inject/handler.py similarity index 100% rename from hooks/bundled/wiki-auto-inject/handler.py rename to runtime/hooks/bundled/wiki-auto-inject/handler.py diff --git a/hooks/frontmatter.py b/runtime/hooks/frontmatter.py similarity index 87% rename from hooks/frontmatter.py rename to runtime/hooks/frontmatter.py index d81470e5..5d34c1ca 100644 --- a/hooks/frontmatter.py +++ b/runtime/hooks/frontmatter.py @@ -52,20 +52,20 @@ def parse_frontmatter(markdown: str) -> ParsedHookFrontmatter: return ParsedHookFrontmatter(frontmatter=parsed, body=body) -def resolve_openclaw_metadata(frontmatter: Dict[str, Any]) -> Optional[Dict[str, Any]]: +def resolve_oclaw_metadata(frontmatter: Dict[str, Any]) -> Optional[Dict[str, Any]]: """ - OpenClaw embeds metadata as a JSON-like object under key 'metadata' -> 'openclaw' + Oclaw embeds metadata as a JSON-like object under key 'metadata' -> 'oclaw' (or sometimes as already-parsed dict). We normalize to a dict if present. """ meta = frontmatter.get("metadata") if not isinstance(meta, dict): return None - oc = meta.get("openclaw") + oc = meta.get("oclaw") return oc if isinstance(oc, dict) else None def resolve_hook_key(name: str, entry: Dict[str, Any]) -> str: - # OpenClaw's TS resolves a config key with possible explicit hookKey; keep it simple. + # Oclaw's TS resolves a config key with possible explicit hookKey; keep it simple. md = entry.get("metadata") if isinstance(entry, dict) else None if isinstance(md, dict): hook_key = md.get("hookKey") diff --git a/hooks/internal_hooks.py b/runtime/hooks/internal_hooks.py similarity index 96% rename from hooks/internal_hooks.py rename to runtime/hooks/internal_hooks.py index 000ad1f9..8df5c7f5 100644 --- a/hooks/internal_hooks.py +++ b/runtime/hooks/internal_hooks.py @@ -5,7 +5,7 @@ import logging from dataclasses import dataclass, field from typing import Any, Awaitable, Callable, Dict, List, MutableMapping, Optional, Sequence, Set, Tuple, Union -log = logging.getLogger("openclaw.hooks") +log = logging.getLogger("oclaw.hooks") HookEventType = str HookAction = str @@ -17,7 +17,7 @@ class HookEvent: """ A lightweight, transport-agnostic hook event. - This intentionally mirrors the OpenClaw internal hook model: + This intentionally mirrors the Oclaw internal hook model: - handlers may register on "type" or "type:action" - failures are isolated per-handler """ diff --git a/hooks/loader.py b/runtime/hooks/loader.py similarity index 91% rename from hooks/loader.py rename to runtime/hooks/loader.py index 3fb0183d..dc6933b3 100644 --- a/hooks/loader.py +++ b/runtime/hooks/loader.py @@ -17,7 +17,7 @@ def _load_module_from_path(module_path: str, unique_key: str) -> Optional[object p = Path(module_path) if not p.exists() or not p.is_file(): return None - name = f"openclaw_hook_{unique_key}_{p.stem}".replace("-", "_") + name = f"oclaw_hook_{unique_key}_{p.stem}".replace("-", "_") spec = importlib.util.spec_from_file_location(name, str(p)) if not spec or not spec.loader: return None @@ -49,12 +49,12 @@ def load_internal_hooks( Discover python hooks and register them into the in-process registry. Hook file layout: - /HOOK.md (YAML frontmatter with openclaw metadata) + /HOOK.md (YAML frontmatter with oclaw metadata) /handler.py (or index.py) Metadata fields used: - metadata.openclaw.events: ["type", "type:action", ...] - metadata.openclaw.export: handler function name (default: "default" -> we map to "handle") + metadata.oclaw.events: ["type", "type:action", ...] + metadata.oclaw.export: handler function name (default: "default" -> we map to "handle") """ # Hook system is on by default; only skip when explicitly disabled. if (((config.get("hooks") or {}).get("internal") or {}).get("enabled")) is False: diff --git a/hooks/policy.py b/runtime/hooks/policy.py similarity index 84% rename from hooks/policy.py rename to runtime/hooks/policy.py index 2547bd9e..5222da77 100644 --- a/hooks/policy.py +++ b/runtime/hooks/policy.py @@ -5,7 +5,7 @@ from typing import Any, Callable, Dict, List, Literal, Optional, Sequence, Tuple from .frontmatter import resolve_hook_key -HookSource = Literal["openclaw-bundled", "openclaw-plugin", "openclaw-managed", "openclaw-workspace"] +HookSource = Literal["oclaw-bundled", "oclaw-plugin", "oclaw-managed", "oclaw-workspace"] @dataclass(frozen=True, slots=True) @@ -18,33 +18,33 @@ class HookSourcePolicy: HOOK_SOURCE_POLICIES: Dict[HookSource, HookSourcePolicy] = { - "openclaw-bundled": HookSourcePolicy( + "oclaw-bundled": HookSourcePolicy( precedence=10, trustedLocalCode=True, defaultEnableMode="default-on", - canOverride=("openclaw-bundled",), - canBeOverriddenBy=("openclaw-managed", "openclaw-plugin"), + canOverride=("oclaw-bundled",), + canBeOverriddenBy=("oclaw-managed", "oclaw-plugin"), ), - "openclaw-plugin": HookSourcePolicy( + "oclaw-plugin": HookSourcePolicy( precedence=20, trustedLocalCode=True, defaultEnableMode="default-on", - canOverride=("openclaw-bundled", "openclaw-plugin"), - canBeOverriddenBy=("openclaw-managed",), + canOverride=("oclaw-bundled", "oclaw-plugin"), + canBeOverriddenBy=("oclaw-managed",), ), - "openclaw-managed": HookSourcePolicy( + "oclaw-managed": HookSourcePolicy( precedence=30, trustedLocalCode=True, defaultEnableMode="default-on", - canOverride=("openclaw-bundled", "openclaw-managed", "openclaw-plugin"), - canBeOverriddenBy=("openclaw-managed",), + canOverride=("oclaw-bundled", "oclaw-managed", "oclaw-plugin"), + canBeOverriddenBy=("oclaw-managed",), ), - "openclaw-workspace": HookSourcePolicy( + "oclaw-workspace": HookSourcePolicy( precedence=40, trustedLocalCode=True, defaultEnableMode="explicit-opt-in", - canOverride=("openclaw-workspace",), - canBeOverriddenBy=("openclaw-workspace",), + canOverride=("oclaw-workspace",), + canBeOverriddenBy=("oclaw-workspace",), ), } @@ -76,7 +76,7 @@ def resolve_hook_enable_state(entry: Dict[str, Any], config: Optional[Dict[str, hook_key = resolve_hook_key(name, entry) hook_cfg = resolve_hook_config(config, hook_key) - if source == "openclaw-plugin": + if source == "oclaw-plugin": return {"enabled": True} if isinstance(hook_cfg, dict) and hook_cfg.get("enabled") is False: diff --git a/hooks/workspace.py b/runtime/hooks/workspace.py similarity index 90% rename from hooks/workspace.py rename to runtime/hooks/workspace.py index 0b538558..3de29c44 100644 --- a/hooks/workspace.py +++ b/runtime/hooks/workspace.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Any, Dict, Iterable, List, Literal, Optional, Sequence, Tuple -from .frontmatter import parse_frontmatter, resolve_openclaw_metadata +from .frontmatter import parse_frontmatter, resolve_oclaw_metadata from .policy import HookSource, resolve_hook_entries @@ -55,7 +55,7 @@ def _load_hook_from_dir(hook_dir: Path, source: HookSource, plugin_id: Optional[ if handler_path is None: return None - metadata = resolve_openclaw_metadata(fm) or {} + metadata = resolve_oclaw_metadata(fm) or {} return { "hook": { @@ -97,15 +97,15 @@ def discover_workspace_hook_entries( extra_dirs: Optional[Sequence[str]] = None, ) -> List[HookEntry]: """ - Python port of OpenClaw hook discovery strategy. + Python port of Oclaw hook discovery strategy. - bundled: shipped with runtime - - managed: user config dir (~/.openclaw/hooks by default) + - managed: user config dir (~/.oclaw/hooks by default) - workspace: /hooks (explicit opt-in by default) - extra: any extra managed dirs """ ws = Path(workspace_dir) - managed = Path(managed_hooks_dir) if managed_hooks_dir else (Path.home() / ".openclaw" / "hooks") + managed = Path(managed_hooks_dir) if managed_hooks_dir else (Path.home() / ".oclaw" / "hooks") workspace_hooks = ws / "hooks" entries: List[HookEntry] = [] @@ -113,13 +113,13 @@ def discover_workspace_hook_entries( if extra_dirs: for raw in extra_dirs: p = Path(os.path.expanduser(raw)).resolve() - entries.extend(load_hook_entries_from_dir(str(p), source="openclaw-managed")) + entries.extend(load_hook_entries_from_dir(str(p), source="oclaw-managed")) if bundled_hooks_dir: - entries.extend(load_hook_entries_from_dir(bundled_hooks_dir, source="openclaw-bundled")) + entries.extend(load_hook_entries_from_dir(bundled_hooks_dir, source="oclaw-bundled")) - entries.extend(load_hook_entries_from_dir(str(managed), source="openclaw-managed")) - entries.extend(load_hook_entries_from_dir(str(workspace_hooks), source="openclaw-workspace")) + entries.extend(load_hook_entries_from_dir(str(managed), source="oclaw-managed")) + entries.extend(load_hook_entries_from_dir(str(workspace_hooks), source="oclaw-workspace")) return entries diff --git a/openclaw_runtime/hooks_runtime.py b/runtime/hooks_runtime.py similarity index 89% rename from openclaw_runtime/hooks_runtime.py rename to runtime/hooks_runtime.py index 9f5e68ee..fe441218 100644 --- a/openclaw_runtime/hooks_runtime.py +++ b/runtime/hooks_runtime.py @@ -8,8 +8,9 @@ from dataclasses import dataclass from pathlib import Path from typing import Any -from oclaw.openclaw_runtime.skills import discover_workspace_skill_manifests +from oclaw.runtime.skills import discover_workspace_skill_manifests from oclaw.platform.config.paths import PROJECT_ROOT +from oclaw.platform.config.runtime_paths import runtime_hooks_bundled_root @dataclass @@ -24,11 +25,11 @@ class _HooksState: _STATE = _HooksState() -def _ensure_openclaw_path() -> Path: - openclaw_dir = (Path(PROJECT_ROOT) / "oclaw").resolve() - if str(openclaw_dir) not in sys.path: - sys.path.insert(0, str(openclaw_dir)) - return openclaw_dir +def _ensure_oclaw_path() -> Path: + oclaw_dir = (Path(PROJECT_ROOT) / "oclaw").resolve() + if str(oclaw_dir) not in sys.path: + sys.path.insert(0, str(oclaw_dir)) + return oclaw_dir def _default_runtime_config() -> dict[str, Any]: @@ -58,7 +59,7 @@ def _read_json_file(path: Path) -> dict[str, Any] | None: def resolve_runtime_config() -> dict[str, Any]: cfg = _default_runtime_config() - env_json = str(os.getenv("OPENCLAW_RUNTIME_CONFIG_JSON") or "").strip() + env_json = str(os.getenv("OCLAW_RUNTIME_CONFIG_JSON") or "").strip() if env_json: try: obj = json.loads(env_json) @@ -68,7 +69,7 @@ def resolve_runtime_config() -> dict[str, Any]: except Exception: pass - raw_path = str(os.getenv("OPENCLAW_CONFIG_PATH") or "").strip() + raw_path = str(os.getenv("OCLAW_CONFIG_PATH") or "").strip() if raw_path: p = Path(raw_path).expanduser() if not p.is_absolute(): @@ -89,10 +90,10 @@ def initialize_hooks_runtime(*, cfg: dict[str, Any] | None, workspace_dir: str) if _STATE.initialized: return int(_STATE.loaded_count or 0) try: - openclaw_dir = _ensure_openclaw_path() - import hooks as hooks_mod # type: ignore + oclaw_dir = _ensure_oclaw_path() + from oclaw.runtime import hooks as hooks_mod # type: ignore - bundled_dir = openclaw_dir / "hooks" / "bundled" + bundled_dir = runtime_hooks_bundled_root() resolved_cfg = dict(cfg or resolve_runtime_config() or {}) extra_dirs: list[str] = [] try: diff --git a/openclaw_runtime/memory_stage.py b/runtime/memory_stage.py similarity index 90% rename from openclaw_runtime/memory_stage.py rename to runtime/memory_stage.py index c3cc8c42..9450b42f 100644 --- a/openclaw_runtime/memory_stage.py +++ b/runtime/memory_stage.py @@ -4,9 +4,9 @@ import json from pathlib import Path from typing import Any -from oclaw.openclaw_runtime.types import OpenClawMemoryContext -from oclaw.orchestration.memory import semantic_retrieve, session_memory_digest -from oclaw.prompts.loader import render_openclaw_prompt +from oclaw.runtime.types import OclawMemoryContext +from oclaw.runtime.orchestration.memory import semantic_retrieve, session_memory_digest +from oclaw.prompts.loader import render_runtime_prompt _SPECIALIST_FLAGS_SETTING_KEY = "AIA_CHAT_SPECIALIST_FLAGS_JSON" @@ -20,7 +20,7 @@ def build_memory_context( query_text: str, short_limit: int = 6, semantic_top_k: int = 4, -) -> OpenClawMemoryContext: +) -> OclawMemoryContext: short_term: tuple[str, ...] = () semantic_hits: tuple[dict[str, Any], ...] = () @@ -47,10 +47,10 @@ def build_memory_context( except Exception: semantic_hits = () - return OpenClawMemoryContext(short_term=short_term, semantic_hits=semantic_hits) + return OclawMemoryContext(short_term=short_term, semantic_hits=semantic_hits) -def render_memory_context_block(ctx: OpenClawMemoryContext) -> str: +def render_memory_context_block(ctx: OclawMemoryContext) -> str: if not ctx.enabled: return "" short_lines: list[str] = [] @@ -68,7 +68,7 @@ def render_memory_context_block(ctx: OpenClawMemoryContext) -> str: except Exception: score_text = "" sem_lines.append(f"{i}.{score_text} {content[:500]}") - return render_openclaw_prompt( + return render_runtime_prompt( "runtime/memory_context_block.md", variables={ "short_term_block": "\n".join(short_lines).strip(), @@ -85,7 +85,7 @@ def assemble_memory_context( tenant_id: str, user_id: str, query_text: str, -) -> OpenClawMemoryContext: +) -> OclawMemoryContext: return build_memory_context( store=store, session_id=session_id, @@ -115,7 +115,7 @@ def compact_memory_context( session_id: str, tenant_id: str, user_id: str, -) -> OpenClawMemoryContext: +) -> OclawMemoryContext: # MVP compaction path: return fresh assembled short+semantic context. return assemble_memory_context( store=store, @@ -145,7 +145,7 @@ def after_turn_memory( assistant_text=assistant_text, ) try: - from oclaw.orchestration.memory import maybe_write_turn_memory + from oclaw.runtime.orchestration.memory import maybe_write_turn_memory from oclaw.platform.persistence.sqlite_store import SqliteStore if isinstance(store, SqliteStore): @@ -184,8 +184,8 @@ def after_turn_memory( "user_text": str(user_text or "")[:8000], "assistant_text": str(assistant_text or "")[:12000], } - if hasattr(store, "openclaw_task_create"): - store.openclaw_task_create( + if hasattr(store, "oclaw_task_create"): + store.oclaw_task_create( tenant_id=str(tenant_id or "default"), session_id=str(session_id or ""), task_type="wiki_capture", diff --git a/ops/__init__.py b/runtime/operations/__init__.py similarity index 100% rename from ops/__init__.py rename to runtime/operations/__init__.py diff --git a/ops/__main__.py b/runtime/operations/__main__.py similarity index 100% rename from ops/__main__.py rename to runtime/operations/__main__.py diff --git a/ops/main.py b/runtime/operations/main.py similarity index 83% rename from ops/main.py rename to runtime/operations/main.py index 60a57d7a..7d7cab80 100644 --- a/ops/main.py +++ b/runtime/operations/main.py @@ -4,9 +4,9 @@ import argparse import os import sys -from oclaw.ops.memory import register_memory_parser -from oclaw.ops.providers.registry import build_channel_registry -from oclaw.ops.stack import register_stack_parser +from oclaw.runtime.operations.memory import register_memory_parser +from oclaw.runtime.operations.providers.registry import build_channel_registry +from oclaw.runtime.operations.stack import register_stack_parser def _cmd_gateway_start(args: argparse.Namespace) -> int: @@ -14,7 +14,7 @@ def _cmd_gateway_start(args: argparse.Namespace) -> int: os.environ["AIA_ASSISTANT_GATEWAY_HOST"] = args.host if args.port is not None: os.environ["AIA_ASSISTANT_GATEWAY_PORT"] = str(args.port) - from oclaw.ops.mcp_env import apply_gateway_mcp_env_to_os + from oclaw.runtime.operations.mcp_env import apply_gateway_mcp_env_to_os apply_gateway_mcp_env_to_os() from oclaw.interfaces.http.fastapi_app import main as fastapi_main @@ -23,7 +23,7 @@ def _cmd_gateway_start(args: argparse.Namespace) -> int: def _build_parser() -> argparse.ArgumentParser: - p = argparse.ArgumentParser(prog="python -m oclaw.ops", description="Unified operational CLI") + p = argparse.ArgumentParser(prog="python -m oclaw.runtime.operations", description="Unified operational CLI") root_sub = p.add_subparsers(dest="root_cmd", required=True) registry = build_channel_registry() diff --git a/ops/mcp_env.py b/runtime/operations/mcp_env.py similarity index 100% rename from ops/mcp_env.py rename to runtime/operations/mcp_env.py diff --git a/ops/mcp_registry_export.py b/runtime/operations/mcp_registry_export.py similarity index 98% rename from ops/mcp_registry_export.py rename to runtime/operations/mcp_registry_export.py index 5c5c93c1..a91f2300 100644 --- a/ops/mcp_registry_export.py +++ b/runtime/operations/mcp_registry_export.py @@ -10,7 +10,7 @@ from typing import Any from oclaw.platform.config.paths import PROJECT_ROOT from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.mcp.registry import McpRegistry +from oclaw.runtime.tools.mcp.registry import McpRegistry _EXPORT_FILENAME = "mcp_registry_migrated.json" _LOCAL_DIR = (PROJECT_ROOT / "oclaw" / "_local").resolve() diff --git a/ops/memory.py b/runtime/operations/memory.py similarity index 100% rename from ops/memory.py rename to runtime/operations/memory.py diff --git a/channels/__init__.py b/runtime/operations/providers/__init__.py similarity index 100% rename from channels/__init__.py rename to runtime/operations/providers/__init__.py diff --git a/ops/providers/base.py b/runtime/operations/providers/base.py similarity index 100% rename from ops/providers/base.py rename to runtime/operations/providers/base.py diff --git a/ops/providers/registry.py b/runtime/operations/providers/registry.py similarity index 100% rename from ops/providers/registry.py rename to runtime/operations/providers/registry.py diff --git a/ops/providers/wecom.py b/runtime/operations/providers/wecom.py similarity index 95% rename from ops/providers/wecom.py rename to runtime/operations/providers/wecom.py index 8e145432..55130410 100644 --- a/ops/providers/wecom.py +++ b/runtime/operations/providers/wecom.py @@ -6,7 +6,7 @@ import secrets import sys from argparse import _SubParsersAction -from oclaw.channels.wecom.longconn_runner import run_forever as run_wecom_longconn +from oclaw.interfaces.channels.wecom.longconn_runner import run_forever as run_wecom_longconn from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore @@ -101,7 +101,7 @@ class WecomProvider(ChannelProvider): return run_wecom_longconn() def _cmd_status(self, _args: argparse.Namespace) -> int: - from scripts.wecom_status import main as status_main + from oclaw.runtime.operations.scripts.wecom_status import main as status_main return status_main() @@ -150,7 +150,7 @@ class WecomProvider(ChannelProvider): return 0 def _cmd_smoke(self, args: argparse.Namespace) -> int: - from scripts.wecom_smoke_test import main as smoke_main + from oclaw.runtime.operations.scripts.wecom_smoke_test import main as smoke_main old = sys.argv try: @@ -206,6 +206,6 @@ class WecomProvider(ChannelProvider): print("ok=1") print("mode=bot_api") print(f"bot_id={args.bot_id}") - print("next=python -m oclaw.ops channel wecom start") + print("next=python -m oclaw.runtime.operations channel wecom start") return 0 diff --git a/ops/runtime.py b/runtime/operations/runtime.py similarity index 97% rename from ops/runtime.py rename to runtime/operations/runtime.py index 1f3c915a..47994ce5 100644 --- a/ops/runtime.py +++ b/runtime/operations/runtime.py @@ -72,13 +72,13 @@ def _python_bin() -> str: def _service_signature(name: str) -> str | None: n = str(name or "").strip().lower() if n == "gateway": - return "-m oclaw.ops gateway start" + return "-m oclaw.runtime.operations gateway start" if n == "ui": - return "-m oclaw.ops ui start" + return "-m oclaw.runtime.operations ui start" if n.startswith("channel:"): ch = n.split(":", 1)[1].strip() if ch: - return f"-m oclaw.ops channel {ch} start" + return f"-m oclaw.runtime.operations channel {ch} start" return None diff --git a/runtime/operations/scripts/bootstrap_venv.ps1 b/runtime/operations/scripts/bootstrap_venv.ps1 new file mode 100644 index 00000000..d1f92b48 --- /dev/null +++ b/runtime/operations/scripts/bootstrap_venv.ps1 @@ -0,0 +1,51 @@ +param( + [string]$Python = "python", + [switch]$Recreate = $false +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +function Fail([string]$msg) { + Write-Host "[ERROR] $msg" -ForegroundColor Red + exit 1 +} + +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +Set-Location $repoRoot + +$venvDir = Join-Path $repoRoot "oclaw/.venv" +$venvPython = Join-Path $venvDir "Scripts/python.exe" + +$py = Get-Command $Python -ErrorAction SilentlyContinue +if (-not $py) { + Fail "python not found in PATH. Please install Python 3.10+ and retry." +} + +if ($Recreate -and (Test-Path $venvDir)) { + Write-Step "Removing existing venv: $venvDir" + Remove-Item -Recurse -Force $venvDir +} + +if (-not (Test-Path $venvPython)) { + Write-Step "Creating venv: $venvDir" + & $Python -m venv $venvDir +} + +if (-not (Test-Path $venvPython)) { + Fail "venv python not found after creation: $venvPython" +} + +Write-Step "Upgrading pip" +& $venvPython -m pip install -U pip + +Write-Step "Installing requirements" +& $venvPython -m pip install -r "requirements.txt" + +Write-Host "" +Write-Host "OK. Next:" -ForegroundColor Green +Write-Host " powershell -ExecutionPolicy Bypass -File .\\oclaw\\scripts\\start_gateway.ps1" +Write-Host "" diff --git a/scripts/create_bind_code.py b/runtime/operations/scripts/create_bind_code.py similarity index 100% rename from scripts/create_bind_code.py rename to runtime/operations/scripts/create_bind_code.py diff --git a/runtime/operations/scripts/generate_forwarders.py b/runtime/operations/scripts/generate_forwarders.py new file mode 100644 index 00000000..1ee3a21d --- /dev/null +++ b/runtime/operations/scripts/generate_forwarders.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from pathlib import Path + +from oclaw.platform.config.runtime_paths import oclaw_root + + +_PS1_FORWARDERS: dict[str, str] = { + "bootstrap_venv.ps1": "& $real @args", + "start_web.ps1": "& $real @args", + "status_all.ps1": "& $real @args", + "status_ops.ps1": "& $real @args", + "status_wiki_worker.ps1": "& $real @args", + "stop_all.ps1": "& $real @args", + "stop_desktop.ps1": "& $real @args", + "stop_gateway.ps1": "& $real @args", + "stop_ops.ps1": "& $real @args", + "stop_web.ps1": "& $real @args", + "stop_wiki_worker.ps1": "& $real @args", +} + + +def _ps1_template(name: str, invoke_line: str) -> str: + return ( + "param()\n\n" + "$ErrorActionPreference = \"Stop\"\n\n" + f"$real = Join-Path $PSScriptRoot \"..\\\\runtime\\\\operations\\\\scripts\\\\{name}\"\n" + "if (-not (Test-Path $real)) {\n" + " throw \"Forward script target not found: $real\"\n" + "}\n\n" + f"{invoke_line}\n" + ) + + +def _sh_template(name: str) -> str: + return ( + "#!/usr/bin/env bash\n" + "set -euo pipefail\n\n" + f"REAL_PATH=\"$(cd \"$(dirname \"${{BASH_SOURCE[0]}}\")/../runtime/operations/scripts\" && pwd)/{name}\"\n" + "if [[ ! -f \"$REAL_PATH\" ]]; then\n" + " echo \"[ERROR] Forward script target not found: $REAL_PATH\" >&2\n" + " exit 1\n" + "fi\n\n" + "exec bash \"$REAL_PATH\" \"$@\"\n" + ) + + +def generate_forwarders() -> list[Path]: + out: list[Path] = [] + scripts_root = (oclaw_root() / "scripts").resolve() + scripts_root.mkdir(parents=True, exist_ok=True) + for name, invoke in _PS1_FORWARDERS.items(): + p = scripts_root / name + p.write_text(_ps1_template(name, invoke), encoding="utf-8") + out.append(p) + + for name in ("start_ops.sh", "status_ops.sh", "stop_ops.sh"): + p = scripts_root / name + p.write_text(_sh_template(name), encoding="utf-8") + out.append(p) + return out + + +def main() -> int: + generated = generate_forwarders() + print(f"generated_forwarders={len(generated)}") + for p in generated: + print(str(p)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/scripts/install_extra_mcp_batch.py b/runtime/operations/scripts/install_extra_mcp_batch.py similarity index 95% rename from scripts/install_extra_mcp_batch.py rename to runtime/operations/scripts/install_extra_mcp_batch.py index b0b5e19f..7ba1e789 100644 --- a/scripts/install_extra_mcp_batch.py +++ b/runtime/operations/scripts/install_extra_mcp_batch.py @@ -13,7 +13,7 @@ sys.path.insert(0, str(ROOT)) from oclaw.platform.config.paths import db_path # noqa: E402 from oclaw.platform.persistence.sqlite_store import SqliteStore # noqa: E402 -from oclaw.tools.mcp.installer import McpServerManifest, _safe_server_id, install_mcp_server # noqa: E402 +from oclaw.runtime.tools.mcp.installer import McpServerManifest, _safe_server_id, install_mcp_server # noqa: E402 # (server_id_seed, npm package ref, entry_args for npx) diff --git a/scripts/install_mcp_context7.py b/runtime/operations/scripts/install_mcp_context7.py similarity index 96% rename from scripts/install_mcp_context7.py rename to runtime/operations/scripts/install_mcp_context7.py index 83808001..c1e6e408 100644 --- a/scripts/install_mcp_context7.py +++ b/runtime/operations/scripts/install_mcp_context7.py @@ -16,8 +16,8 @@ sys.path.insert(0, str(ROOT)) from oclaw.platform.config.paths import db_path # noqa: E402 from oclaw.platform.persistence.sqlite_store import SqliteStore # noqa: E402 -from oclaw.tools.mcp.installer import McpServerManifest, install_mcp_server # noqa: E402 -from oclaw.tools.mcp.runtime import McpProcessRuntime # noqa: E402 +from oclaw.runtime.tools.mcp.installer import McpServerManifest, install_mcp_server # noqa: E402 +from oclaw.runtime.tools.mcp.runtime import McpProcessRuntime # noqa: E402 _BINDING_KEY = "mcp_specialist_server_binding" diff --git a/scripts/install_mcp_sqlite_brave_calendar.py b/runtime/operations/scripts/install_mcp_sqlite_brave_calendar.py similarity index 97% rename from scripts/install_mcp_sqlite_brave_calendar.py rename to runtime/operations/scripts/install_mcp_sqlite_brave_calendar.py index 74efcd60..4dcde2ec 100644 --- a/scripts/install_mcp_sqlite_brave_calendar.py +++ b/runtime/operations/scripts/install_mcp_sqlite_brave_calendar.py @@ -15,8 +15,8 @@ sys.path.insert(0, str(ROOT)) from oclaw.platform.config.paths import db_path # noqa: E402 from oclaw.platform.persistence.sqlite_store import SqliteStore # noqa: E402 -from oclaw.tools.mcp.installer import McpServerManifest, install_mcp_server # noqa: E402 -from oclaw.tools.mcp.runtime import McpProcessRuntime # noqa: E402 +from oclaw.runtime.tools.mcp.installer import McpServerManifest, install_mcp_server # noqa: E402 +from oclaw.runtime.tools.mcp.runtime import McpProcessRuntime # noqa: E402 def _sync_tools(store: SqliteStore, server_id: str) -> bool: diff --git a/scripts/offline_eval.py b/runtime/operations/scripts/offline_eval.py similarity index 100% rename from scripts/offline_eval.py rename to runtime/operations/scripts/offline_eval.py diff --git a/scripts/seed_mcp_registry.py b/runtime/operations/scripts/seed_mcp_registry.py similarity index 97% rename from scripts/seed_mcp_registry.py rename to runtime/operations/scripts/seed_mcp_registry.py index 24ff0d5d..c9791162 100644 --- a/scripts/seed_mcp_registry.py +++ b/runtime/operations/scripts/seed_mcp_registry.py @@ -17,7 +17,7 @@ sys.path.insert(0, str(ROOT)) from oclaw.platform.config.paths import PROJECT_ROOT, db_path # noqa: E402 from oclaw.platform.persistence.sqlite_store import SqliteStore # noqa: E402 -from oclaw.tools.mcp.installer import McpServerManifest, _safe_server_id, install_mcp_server # noqa: E402 +from oclaw.runtime.tools.mcp.installer import McpServerManifest, _safe_server_id, install_mcp_server # noqa: E402 def _subst_repo(path_str: str) -> str: diff --git a/scripts/set_wecom_auto_bind.py b/runtime/operations/scripts/set_wecom_auto_bind.py similarity index 100% rename from scripts/set_wecom_auto_bind.py rename to runtime/operations/scripts/set_wecom_auto_bind.py diff --git a/scripts/set_wecom_config.py b/runtime/operations/scripts/set_wecom_config.py similarity index 100% rename from scripts/set_wecom_config.py rename to runtime/operations/scripts/set_wecom_config.py diff --git a/runtime/operations/scripts/start_all.ps1 b/runtime/operations/scripts/start_all.ps1 new file mode 100644 index 00000000..b4f4f413 --- /dev/null +++ b/runtime/operations/scripts/start_all.ps1 @@ -0,0 +1,49 @@ +param( + [string]$BindHost = "127.0.0.1", + [int]$Port = 8787, + [switch]$SkipInstall = $false, + [switch]$Background = $false, + [switch]$WithWeixin = $false, + [switch]$WithWikiWorker = $false, + [string]$WeixinChannelId = "oclaw-weixin", + [string]$WeixinGatewayBaseUrl = "" +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +Set-Location $repoRoot + +Write-Step "Starting gateway + desktop" + +& "$PSScriptRoot/start_gateway.ps1" -BindHost $BindHost -Port $Port -SkipInstall:$SkipInstall -Background:$Background + +# When gateway is started in foreground, the call above blocks; desktop won't start. +if (-not $Background) { + Write-Host "" + Write-Host "[INFO] Gateway started in foreground and will block. Use -Background to start both." -ForegroundColor Yellow + exit 0 +} + +& "$PSScriptRoot/start_desktop.ps1" -SkipInstall:$SkipInstall -Background + +if ($WithWeixin) { + $gwBase = ($WeixinGatewayBaseUrl | ForEach-Object { "$_".Trim() }) + if (-not $gwBase) { + $gwBase = "http://$BindHost`:$Port" + } + Write-Step "Starting weixin sidecar" + & "$PSScriptRoot/weixin_start.ps1" -ChannelId $WeixinChannelId -GatewayBaseUrl $gwBase +} +if ($WithWikiWorker) { + Write-Step "Starting wiki worker" + & "$PSScriptRoot/start_wiki_worker.ps1" -Background +} + +Write-Host "" +Write-Host "All started." -ForegroundColor Green + diff --git a/runtime/operations/scripts/start_desktop.ps1 b/runtime/operations/scripts/start_desktop.ps1 new file mode 100644 index 00000000..d921c37a --- /dev/null +++ b/runtime/operations/scripts/start_desktop.ps1 @@ -0,0 +1,49 @@ +param( + [switch]$SkipInstall = $false, + [switch]$Background = $false, + [switch]$KeepExistingGateway = $false +) + +$ErrorActionPreference = "Stop" +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +$desktopDir = Join-Path $repoRoot "oclaw\\desktop" +$runDir = Join-Path $PSScriptRoot ".run" +New-Item -ItemType Directory -Force -Path $runDir | Out-Null +$pidFile = Join-Path $runDir "desktop.pid" +$outLog = Join-Path $runDir "desktop.out.log" +$errLog = Join-Path $runDir "desktop.err.log" + +if (-not (Test-Path $desktopDir)) { + Write-Host "[ERROR] desktop directory not found: $desktopDir" -ForegroundColor Red + exit 1 +} + +Set-Location $desktopDir + +if (-not $KeepExistingGateway) { + Write-Host "==> Cleaning previous gateway listener" -ForegroundColor Cyan + try { + & "$PSScriptRoot/stop_gateway.ps1" -Force + } catch { + Write-Host "[WARN] Failed to pre-stop gateway: $($_.Exception.Message)" -ForegroundColor Yellow + } +} + +if (-not $SkipInstall) { + Write-Host "==> Installing desktop dependencies" -ForegroundColor Cyan + npm install +} + +Write-Host "==> Launching desktop app" -ForegroundColor Cyan +if ($Background) { + # Avoid launching npm.ps1 directly (can be file-associated and open in Notepad on some setups). + # Use hidden cmd and redirect logs to avoid black console window popup. + $p = Start-Process -FilePath "cmd.exe" -ArgumentList @("/c","npm.cmd","run","dev") -WorkingDirectory $desktopDir -PassThru -WindowStyle Hidden -RedirectStandardOutput $outLog -RedirectStandardError $errLog + Set-Content -Path $pidFile -Value "$($p.Id)" -Encoding ascii + Write-Host "desktop.pid = $pidFile" -ForegroundColor DarkGray + Write-Host "desktop.out = $outLog" -ForegroundColor DarkGray + Write-Host "desktop.err = $errLog" -ForegroundColor DarkGray + Write-Host "PID = $($p.Id)" -ForegroundColor Green + exit 0 +} +& "npm.cmd" "run" "dev" diff --git a/runtime/operations/scripts/start_gateway.ps1 b/runtime/operations/scripts/start_gateway.ps1 new file mode 100644 index 00000000..78b48cde --- /dev/null +++ b/runtime/operations/scripts/start_gateway.ps1 @@ -0,0 +1,72 @@ +param( + [string]$BindHost = "127.0.0.1", + [int]$Port = 8787, + [switch]$SkipInstall = $false, + [switch]$Background = $false +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +function Fail([string]$msg) { + Write-Host "[ERROR] $msg" -ForegroundColor Red + exit 1 +} + +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +Set-Location $repoRoot + +$runDir = Join-Path $PSScriptRoot ".run" +New-Item -ItemType Directory -Force -Path $runDir | Out-Null +$pidFile = Join-Path $runDir "gateway.pid" + +Write-Step "Project root: $repoRoot" + +$venvPython = Join-Path $repoRoot "oclaw/.venv/Scripts/python.exe" +if (Test-Path $venvPython) { + Write-Step "Using venv python: $venvPython" + $pythonExe = $venvPython +} else { + $pythonCmd = Get-Command python -ErrorAction SilentlyContinue + if (-not $pythonCmd) { + Fail "python not found in PATH. Please install Python and retry." + } + Write-Step "Python detected: $($pythonCmd.Source)" + $pythonExe = "python" +} + +if (-not $SkipInstall) { + Write-Step "Installing dependencies (pip install -r requirements.txt)" + & $pythonExe -m pip install -r "requirements.txt" +} else { + Write-Step "Skip dependency install" +} + +Write-Host "" +Write-Step "Gateway URL" +Write-Host "Admin: http://$BindHost`:$Port/admin" +Write-Host "Chat: http://$BindHost`:$Port/chat" +Write-Host "WS: ws://$BindHost`:$Port/ws" +Write-Host "" + +$configPath = Join-Path $repoRoot "oclaw\oclaw.json" +if (Test-Path $configPath) { + $env:OCLAW_CONFIG_PATH = $configPath + Write-Step "Using OCLAW_CONFIG_PATH: $configPath" +} + +if ($Background) { + Write-Step "Starting gateway in background" + $p = Start-Process -FilePath $pythonExe -ArgumentList @("-m","oclaw.runtime.operations","gateway","start","--host",$BindHost,"--port",$Port) -WorkingDirectory $repoRoot -PassThru -WindowStyle Hidden + Set-Content -Path $pidFile -Value "$($p.Id)" -Encoding ascii + Write-Host "gateway.pid = $pidFile" -ForegroundColor DarkGray + Write-Host "PID = $($p.Id)" -ForegroundColor Green + exit 0 +} + +Write-Step "Starting gateway (foreground)" +& $pythonExe -m oclaw.runtime.operations gateway start --host $BindHost --port $Port + diff --git a/runtime/operations/scripts/start_ops.ps1 b/runtime/operations/scripts/start_ops.ps1 new file mode 100644 index 00000000..658b09f8 --- /dev/null +++ b/runtime/operations/scripts/start_ops.ps1 @@ -0,0 +1,70 @@ +param( + [string]$Channel = "wecom", + [switch]$SkipInstall = $false, + [switch]$SkipConfigHint = $false +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +function Fail([string]$msg) { + Write-Host "[ERROR] $msg" -ForegroundColor Red + exit 1 +} + +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +Set-Location $repoRoot + +Write-Step "Project root: $repoRoot" + +$venvPython = Join-Path $repoRoot "oclaw/.venv/Scripts/python.exe" +if (Test-Path $venvPython) { + Write-Step "Using venv python: $venvPython" + $pythonExe = $venvPython +} else { + $pythonCmd = Get-Command python -ErrorAction SilentlyContinue + if (-not $pythonCmd) { + Fail "python not found in PATH. Please install Python 3.10+ and retry." + } + Write-Step "Python detected: $($pythonCmd.Source)" + $pythonExe = "python" +} + +if (-not $SkipInstall) { + Write-Step "Installing dependencies (pip install -r requirements.txt)" + & $pythonExe -m pip install -r "requirements.txt" +} else { + Write-Step "Skip dependency install" +} + +if (-not $SkipConfigHint) { + Write-Step "Current WeCom status" + try { + python -m oclaw.runtime.operations channel wecom status + } catch { + Write-Host "[WARN] Unable to read WeCom status yet." -ForegroundColor Yellow + } + Write-Host "" + Write-Host "If WeCom is not configured yet, run this first:" -ForegroundColor Yellow + Write-Host " python -m oclaw.runtime.operations channel wecom config --help" + Write-Host "" +} + +Write-Step "Stopping previous stack (safe if not running)" +& $pythonExe -m oclaw.runtime.operations stack down + +Write-Step "Starting stack: python -m oclaw.runtime.operations stack up --channel $Channel" +& $pythonExe -m oclaw.runtime.operations stack up --channel $Channel + +Write-Host "" +Write-Host "Started successfully." -ForegroundColor Green +Write-Host "Admin: http://127.0.0.1:8787/admin" +Write-Host "Chat: http://127.0.0.1:8787/chat" +Write-Host "" +Write-Host "Useful commands:" +Write-Host " python -m oclaw.runtime.operations stack status" +Write-Host " python -m oclaw.runtime.operations stack down" + diff --git a/runtime/operations/scripts/start_ops.sh b/runtime/operations/scripts/start_ops.sh new file mode 100644 index 00000000..b0c52e58 --- /dev/null +++ b/runtime/operations/scripts/start_ops.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +set -euo pipefail + +CHANNEL="wecom" +SKIP_INSTALL=0 +SKIP_CONFIG_HINT=0 + +while [[ $# -gt 0 ]]; do + case "$1" in + --no-ui) + # Kept for compatibility; stack no longer starts Streamlit. + shift + ;; + --channel) + CHANNEL="${2:-wecom}" + shift 2 + ;; + --skip-install) + SKIP_INSTALL=1 + shift + ;; + --skip-config-hint) + SKIP_CONFIG_HINT=1 + shift + ;; + *) + echo "Unknown arg: $1" + echo "Usage: $0 [--no-ui] [--channel wecom] [--skip-install] [--skip-config-hint]" + echo "Note: --no-ui is legacy; stack does not start Streamlit. Chat: http://127.0.0.1:8787/chat" + exit 1 + ;; + esac +done + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT_DIR" + +step() { printf '\n==> %s\n' "$1"; } + +if ! command -v python >/dev/null 2>&1; then + echo "[ERROR] python not found in PATH." >&2 + exit 1 +fi + +step "Project root: $ROOT_DIR" +step "Python: $(command -v python)" + +if [[ "$SKIP_INSTALL" -eq 0 ]]; then + step "Installing dependencies" + python -m pip install -r requirements.txt +else + step "Skip dependency install" +fi + +if [[ "$SKIP_CONFIG_HINT" -eq 0 ]]; then + step "Current WeCom status" + python -m oclaw.runtime.operations channel wecom status || true + cat <<'EOF' + +If WeCom is not configured yet, run: + python -m oclaw.runtime.operations channel wecom config --help +EOF +fi + +step "Stopping previous stack" +python -m oclaw.runtime.operations stack down || true + +step "Starting stack" +python -m oclaw.runtime.operations stack up --channel "$CHANNEL" + +cat <<'EOF' + +Started successfully. +Admin: http://127.0.0.1:8787/admin +Chat: http://127.0.0.1:8787/chat + +Useful commands: + python -m oclaw.runtime.operations stack status + python -m oclaw.runtime.operations stack down +EOF + diff --git a/runtime/operations/scripts/start_web.ps1 b/runtime/operations/scripts/start_web.ps1 new file mode 100644 index 00000000..0f5ed4d2 --- /dev/null +++ b/runtime/operations/scripts/start_web.ps1 @@ -0,0 +1,10 @@ +param( + [string]$BindHost = "127.0.0.1", + [int]$Port = 8787, + [switch]$SkipInstall = $false, + [switch]$Background = $false +) + +# 网页端(/admin、/chat)由网关进程提供,这里只是一个语义化别名。 +& "$PSScriptRoot/start_gateway.ps1" -BindHost $BindHost -Port $Port -SkipInstall:$SkipInstall -Background:$Background + diff --git a/runtime/operations/scripts/start_wiki_worker.ps1 b/runtime/operations/scripts/start_wiki_worker.ps1 new file mode 100644 index 00000000..6d6c90ca --- /dev/null +++ b/runtime/operations/scripts/start_wiki_worker.ps1 @@ -0,0 +1,37 @@ +param( + [switch]$Background = $true +) + +$ErrorActionPreference = "Stop" + +function Resolve-RepoRoot { + $here = Split-Path -Parent $PSCommandPath + return (Resolve-Path (Join-Path $here "..")).Path +} + +$repoRoot = Resolve-RepoRoot +$runDir = Join-Path $PSScriptRoot ".run" +$null = New-Item -ItemType Directory -Force -Path $runDir -ErrorAction SilentlyContinue +$pidFile = Join-Path $runDir "wiki_worker.pid" +$outLog = Join-Path $runDir "wiki_worker.out.log" +$errLog = Join-Path $runDir "wiki_worker.err.log" + +$venvPython = Join-Path $repoRoot "oclaw/.venv/Scripts/python.exe" +if (Test-Path $venvPython) { + $pythonExe = $venvPython +} else { + $pythonCmd = Get-Command python -ErrorAction SilentlyContinue + if (-not $pythonCmd) { + throw "python not found in PATH" + } + $pythonExe = "python" +} + +if (-not $Background) { + & $pythonExe -m oclaw.runtime.workers.wiki.main + exit 0 +} + +$p = Start-Process -FilePath $pythonExe -ArgumentList @("-m", "oclaw.runtime.workers.wiki.main") -WorkingDirectory $repoRoot -PassThru -WindowStyle Hidden -RedirectStandardOutput $outLog -RedirectStandardError $errLog +Set-Content -Path $pidFile -Value $p.Id -Encoding ascii +Write-Host "[ok] started wiki worker pid=$($p.Id) out=$outLog err=$errLog" diff --git a/runtime/operations/scripts/status_all.ps1 b/runtime/operations/scripts/status_all.ps1 new file mode 100644 index 00000000..168bbe1e --- /dev/null +++ b/runtime/operations/scripts/status_all.ps1 @@ -0,0 +1,140 @@ +param( + [string]$BindHost = "127.0.0.1", + [int]$Port = 8787, + [switch]$WithWeixin = $false, + [switch]$WithWikiWorker = $false, + [string]$WeixinChannelId = "oclaw-weixin" +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +function Warn([string]$msg) { + Write-Host "[WARN] $msg" -ForegroundColor Yellow +} + +$runDir = Join-Path $PSScriptRoot ".run" +$null = New-Item -ItemType Directory -Force -Path $runDir -ErrorAction SilentlyContinue +$gwPidFile = Join-Path $runDir "gateway.pid" +$desktopPidFile = Join-Path $runDir "desktop.pid" + +Write-Step "URLs" +Write-Host "Admin: http://$BindHost`:$Port/admin" +Write-Host "Chat: http://$BindHost`:$Port/chat" +Write-Host "WS: ws://$BindHost`:$Port/ws" +Write-Host "" + +Write-Step "PID files" +if (Test-Path $gwPidFile) { + $gwPid = (Get-Content $gwPidFile -ErrorAction SilentlyContinue | Select-Object -First 1) + Write-Host "gateway.pid: $gwPidFile => $gwPid" +} else { + Warn "gateway.pid not found: $gwPidFile" +} +if (Test-Path $desktopPidFile) { + $deskPid = (Get-Content $desktopPidFile -ErrorAction SilentlyContinue | Select-Object -First 1) + Write-Host "desktop.pid: $desktopPidFile => $deskPid" +} else { + Warn "desktop.pid not found: $desktopPidFile" +} +Write-Host "" + +Write-Step "Port listener" +try { + $conns = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue + if (-not $conns) { + Warn "No listener on port $Port" + exit 0 + } + $pids = $conns | Select-Object -ExpandProperty OwningProcess -Unique + Write-Host "Listening on port $Port; OwningProcess: $($pids -join ', ')" + + # Self-heal: if we have a single owning pid and no gateway.pid, write it back. + if (-not (Test-Path $gwPidFile)) { + $pidList = @() + foreach ($x in @($pids)) { + $n = 0 + if ([int]::TryParse([string]$x, [ref]$n) -and $n -gt 0) { + $pidList += $n + } + } + if ($pidList.Count -eq 1) { + $procId = [int]$pidList[0] + try { + $null = Get-Process -Id $procId -ErrorAction Stop + Set-Content -Path $gwPidFile -Value "$procId" -Encoding ascii + Write-Host "Wrote gateway.pid => $procId" -ForegroundColor Green + } catch { + Warn "Cannot verify process for PID=$procId; not writing pid file." + } + } else { + # Heuristic: try to pick the gateway python/uvicorn process. + $candidates = @() + foreach ($p in $pidList) { + try { + $proc = Get-Process -Id $p -ErrorAction Stop + $name = [string]$proc.ProcessName + } catch { + continue + } + $cmd = "" + try { + $wmi = Get-CimInstance Win32_Process -Filter "ProcessId=$p" -ErrorAction SilentlyContinue + if ($wmi -and $wmi.CommandLine) { $cmd = [string]$wmi.CommandLine } + } catch { + $cmd = "" + } + if ($cmd) { + $low = ([string]$cmd).ToLowerInvariant() + } else { + $low = "" + } + $score = 0 + if ($name -match "python") { $score += 2 } + if ($low -match "uvicorn") { $score += 2 } + if ($low -match "src\.ops") { $score += 2 } + if ($low -match "gateway\s+start") { $score += 2 } + if ($low -match "fastapi_app") { $score += 1 } + $candidates += [pscustomobject]@{ Pid = [int]$p; Name = $name; Score = [int]$score; Cmd = $cmd } + } + $best = $candidates | Sort-Object -Property Score -Descending | Select-Object -First 1 + if ($best -and $best.Score -ge 2) { + try { + $null = Get-Process -Id $best.Pid -ErrorAction Stop + Set-Content -Path $gwPidFile -Value "$($best.Pid)" -Encoding ascii + Write-Host "Wrote gateway.pid => $($best.Pid) ($($best.Name))" -ForegroundColor Green + } catch { + Warn "Cannot verify process for PID=$($best.Pid); not writing pid file." + } + } else { + Warn "Multiple owning processes for port $Port; cannot confidently pick gateway PID." + } + } + } +} catch { + Warn "Get-NetTCPConnection failed: $($_.Exception.Message)" +} + +if ($WithWeixin) { + Write-Host "" + Write-Step "Weixin sidecar" + try { + & "$PSScriptRoot/weixin_status.ps1" -ChannelId $WeixinChannelId + } catch { + Warn "weixin status failed: $($_.Exception.Message)" + } +} + +if ($WithWikiWorker) { + Write-Host "" + Write-Step "Wiki worker" + try { + & "$PSScriptRoot/status_wiki_worker.ps1" + } catch { + Warn "wiki worker status failed: $($_.Exception.Message)" + } +} + diff --git a/runtime/operations/scripts/status_ops.ps1 b/runtime/operations/scripts/status_ops.ps1 new file mode 100644 index 00000000..b95cb74a --- /dev/null +++ b/runtime/operations/scripts/status_ops.ps1 @@ -0,0 +1,21 @@ +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +Set-Location $repoRoot + +Write-Step "Project root: $repoRoot" +Write-Step "Stack status" +python -m oclaw.runtime.operations stack status + +Write-Host "" +Write-Step "WeCom status" +python -m oclaw.runtime.operations channel wecom status + +Write-Host "" +Write-Host "Admin: http://127.0.0.1:8787/admin" +Write-Host "Chat: http://127.0.0.1:8787/chat" + diff --git a/runtime/operations/scripts/status_ops.sh b/runtime/operations/scripts/status_ops.sh new file mode 100644 index 00000000..ff1255a1 --- /dev/null +++ b/runtime/operations/scripts/status_ops.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT_DIR" + +printf '==> Project root: %s\n' "$ROOT_DIR" +printf '==> Stack status\n' +python -m oclaw.runtime.operations stack status + +printf '\n==> WeCom status\n' +python -m oclaw.runtime.operations channel wecom status + +cat <<'EOF' + +Admin: http://127.0.0.1:8787/admin +Chat: http://127.0.0.1:8787/chat +EOF + diff --git a/runtime/operations/scripts/status_wiki_worker.ps1 b/runtime/operations/scripts/status_wiki_worker.ps1 new file mode 100644 index 00000000..ce9bcce7 --- /dev/null +++ b/runtime/operations/scripts/status_wiki_worker.ps1 @@ -0,0 +1,23 @@ +param() + +$ErrorActionPreference = "Stop" +$runDir = Join-Path $PSScriptRoot ".run" +$pidFile = Join-Path $runDir "wiki_worker.pid" + +if (-not (Test-Path $pidFile)) { + Write-Host "status=stopped" + exit 0 +} + +$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) +if (-not $procId) { + Write-Host "status=stopped" + exit 0 +} + +try { + Get-Process -Id $procId -ErrorAction Stop | Out-Null + Write-Host "status=running pid=$procId" +} catch { + Write-Host "status=stale_pid pid=$procId" +} diff --git a/runtime/operations/scripts/stop_all.ps1 b/runtime/operations/scripts/stop_all.ps1 new file mode 100644 index 00000000..97e339ac --- /dev/null +++ b/runtime/operations/scripts/stop_all.ps1 @@ -0,0 +1,48 @@ +param( + [int]$Port = 8787, + [switch]$Force = $false, + [switch]$WithWeixin = $false, + [switch]$WithWikiWorker = $false, + [string]$WeixinChannelId = "oclaw-weixin" +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +Write-Step "Stopping desktop" +try { + & "$PSScriptRoot/stop_desktop.ps1" -Force:$Force +} catch { + if (-not $Force) { throw } +} + +Write-Step "Stopping gateway/web" +try { + & "$PSScriptRoot/stop_gateway.ps1" -Port $Port -Force:$Force +} catch { + if (-not $Force) { throw } +} + +if ($WithWeixin) { + Write-Step "Stopping weixin sidecar" + try { + & "$PSScriptRoot/weixin_stop.ps1" -ChannelId $WeixinChannelId -Force:$Force + } catch { + if (-not $Force) { throw } + } +} +if ($WithWikiWorker) { + Write-Step "Stopping wiki worker" + try { + & "$PSScriptRoot/stop_wiki_worker.ps1" -Force:$Force + } catch { + if (-not $Force) { throw } + } +} + +Write-Host "" +Write-Host "All stopped." -ForegroundColor Green + diff --git a/runtime/operations/scripts/stop_desktop.ps1 b/runtime/operations/scripts/stop_desktop.ps1 new file mode 100644 index 00000000..8fc24cf3 --- /dev/null +++ b/runtime/operations/scripts/stop_desktop.ps1 @@ -0,0 +1,45 @@ +param( + [switch]$Force = $false +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +function Warn([string]$msg) { + Write-Host "[WARN] $msg" -ForegroundColor Yellow +} + +$runDir = Join-Path $PSScriptRoot ".run" +$pidFile = Join-Path $runDir "desktop.pid" + +Write-Step "Stopping desktop app" + +if (-not (Test-Path $pidFile)) { + Warn "No PID file found: $pidFile" + if (-not $Force) { exit 0 } +} + +$raw = (Get-Content $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) +$procId = 0 +[void][int]::TryParse([string]$raw, [ref]$procId) +if ($procId -le 0) { + Warn "Invalid PID in $pidFile" + if (-not $Force) { exit 1 } + exit 0 +} + +try { + # Kill the whole process tree (cmd -> npm -> electron/node) + & taskkill /PID $procId /T /F | Out-Null + Write-Host "Stopped PID=$procId (tree)" -ForegroundColor Green + Remove-Item $pidFile -Force -ErrorAction SilentlyContinue + exit 0 +} catch { + Warn "Failed to stop PID=$procId : $($_.Exception.Message)" + if (-not $Force) { exit 1 } + exit 0 +} + diff --git a/runtime/operations/scripts/stop_gateway.ps1 b/runtime/operations/scripts/stop_gateway.ps1 new file mode 100644 index 00000000..316f3807 --- /dev/null +++ b/runtime/operations/scripts/stop_gateway.ps1 @@ -0,0 +1,62 @@ +param( + [int]$Port = 8787, + [switch]$Force = $false +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +function Warn([string]$msg) { + Write-Host "[WARN] $msg" -ForegroundColor Yellow +} + +function Kill-ProcId([int]$procId) { + try { + Stop-Process -Id $procId -Force + Write-Host "Stopped PID=$procId" -ForegroundColor Green + return $true + } catch { + Warn "Failed to stop PID=$procId : $($_.Exception.Message)" + return $false + } +} + +$runDir = Join-Path $PSScriptRoot ".run" +$pidFile = Join-Path $runDir "gateway.pid" + +Write-Step "Stopping gateway" + +if (Test-Path $pidFile) { + $raw = (Get-Content $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) + $procId = 0 + [void][int]::TryParse([string]$raw, [ref]$procId) + if ($procId -gt 0) { + $ok = Kill-ProcId -procId $procId + if ($ok) { + Remove-Item $pidFile -Force -ErrorAction SilentlyContinue + exit 0 + } + if (-not $Force) { exit 1 } + } +} + +Write-Step "No PID file; attempting to stop by port $Port" +try { + $conns = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue + if (-not $conns) { + Warn "No listener found on port $Port" + exit 0 + } + $pids = $conns | Select-Object -ExpandProperty OwningProcess -Unique + foreach ($p in $pids) { + if ($p -gt 0) { [void](Kill-ProcId -procId $p) } + } + exit 0 +} catch { + Warn "Stop by port failed: $($_.Exception.Message)" + if (-not $Force) { exit 1 } +} + diff --git a/runtime/operations/scripts/stop_ops.ps1 b/runtime/operations/scripts/stop_ops.ps1 new file mode 100644 index 00000000..49a7e36e --- /dev/null +++ b/runtime/operations/scripts/stop_ops.ps1 @@ -0,0 +1,33 @@ +param( + [switch]$Force = $false +) + +$ErrorActionPreference = "Stop" + +function Write-Step([string]$msg) { + Write-Host "==> $msg" -ForegroundColor Cyan +} + +$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +Set-Location $repoRoot + +Write-Step "Project root: $repoRoot" +Write-Step "Stopping stack services..." + +try { + python -m oclaw.runtime.operations stack down + Write-Host "Stack stopped." -ForegroundColor Green +} catch { + Write-Host "[WARN] stack down failed: $($_.Exception.Message)" -ForegroundColor Yellow + if (-not $Force) { + exit 1 + } +} + +Write-Step "Current status" +try { + python -m oclaw.runtime.operations stack status +} catch { + Write-Host "[WARN] Unable to read status after stop." -ForegroundColor Yellow +} + diff --git a/runtime/operations/scripts/stop_ops.sh b/runtime/operations/scripts/stop_ops.sh new file mode 100644 index 00000000..bdf6fc9f --- /dev/null +++ b/runtime/operations/scripts/stop_ops.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT_DIR" + +printf '==> Project root: %s\n' "$ROOT_DIR" +printf '==> Stopping stack services...\n' +python -m oclaw.runtime.operations stack down || true + +printf '\n==> Current status\n' +python -m oclaw.runtime.operations stack status || true + diff --git a/runtime/operations/scripts/stop_web.ps1 b/runtime/operations/scripts/stop_web.ps1 new file mode 100644 index 00000000..ebba8f34 --- /dev/null +++ b/runtime/operations/scripts/stop_web.ps1 @@ -0,0 +1,8 @@ +param( + [int]$Port = 8787, + [switch]$Force = $false +) + +# 网页端(/admin、/chat)由网关进程提供,这里只是一个语义化别名。 +& "$PSScriptRoot/stop_gateway.ps1" -Port $Port -Force:$Force + diff --git a/runtime/operations/scripts/stop_wiki_worker.ps1 b/runtime/operations/scripts/stop_wiki_worker.ps1 new file mode 100644 index 00000000..80eef716 --- /dev/null +++ b/runtime/operations/scripts/stop_wiki_worker.ps1 @@ -0,0 +1,31 @@ +param( + [switch]$Force +) + +$ErrorActionPreference = "Stop" +$runDir = Join-Path $PSScriptRoot ".run" +$pidFile = Join-Path $runDir "wiki_worker.pid" + +if (-not (Test-Path $pidFile)) { + Write-Host "[ok] not running (no pid file)" + exit 0 +} + +$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) +if (-not $procId) { + Remove-Item -Force $pidFile -ErrorAction SilentlyContinue + Write-Host "[ok] not running (empty pid file)" + exit 0 +} + +try { + if ($Force) { + taskkill.exe /PID $procId /T /F | Out-Null + } else { + taskkill.exe /PID $procId /T | Out-Null + } +} catch { +} + +Remove-Item -Force $pidFile -ErrorAction SilentlyContinue +Write-Host "[ok] stopped pid=$procId" diff --git a/scripts/switch_wecom_bot.py b/runtime/operations/scripts/switch_wecom_bot.py similarity index 93% rename from scripts/switch_wecom_bot.py rename to runtime/operations/scripts/switch_wecom_bot.py index 9b0bb699..39c96894 100644 --- a/scripts/switch_wecom_bot.py +++ b/runtime/operations/scripts/switch_wecom_bot.py @@ -40,7 +40,7 @@ def main() -> int: print("mode=bot_api") print(f"bot_id={bot_id}") print("cleared=" + ",".join(_CLEAR_KEYS)) - print("next=python -m oclaw.ops stack up --channel wecom") + print("next=python -m oclaw.runtime.operations stack up --channel wecom") return 0 diff --git a/scripts/unbind_wecom_bot.py b/runtime/operations/scripts/unbind_wecom_bot.py similarity index 91% rename from scripts/unbind_wecom_bot.py rename to runtime/operations/scripts/unbind_wecom_bot.py index 57147be2..937322d9 100644 --- a/scripts/unbind_wecom_bot.py +++ b/runtime/operations/scripts/unbind_wecom_bot.py @@ -39,7 +39,7 @@ def main() -> int: print(f"deleted_channel_identity={ident_deleted}") print(f"deleted_channel_session={sess_deleted}") print("cleared_settings=" + ",".join(_CLEAR_KEYS)) - print("next=python -m oclaw.ops channel wecom config set --type bot --bot-id --bot-secret ") + print("next=python -m oclaw.runtime.operations channel wecom config set --type bot --bot-id --bot-secret ") return 0 diff --git a/runtime/operations/scripts/verify_forwarders.py b/runtime/operations/scripts/verify_forwarders.py new file mode 100644 index 00000000..3f6e99b2 --- /dev/null +++ b/runtime/operations/scripts/verify_forwarders.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from pathlib import Path + +from oclaw.platform.config.runtime_paths import oclaw_root, runtime_operations_scripts_root + + +def verify_forwarders() -> tuple[bool, list[str]]: + root_scripts = (oclaw_root() / "scripts").resolve() + real_scripts = runtime_operations_scripts_root() + errors: list[str] = [] + required = [ + "start_all.ps1", + "start_gateway.ps1", + "start_desktop.ps1", + "start_ops.ps1", + "start_ops.sh", + "status_ops.sh", + "stop_ops.sh", + ] + for name in required: + rp = root_scripts / name + if not rp.exists(): + errors.append(f"missing forwarder: {rp}") + continue + tp = real_scripts / name + if not tp.exists(): + errors.append(f"missing target: {tp}") + continue + text = rp.read_text(encoding="utf-8", errors="ignore") + normalized = text.replace("\\", "/") + while "//" in normalized: + normalized = normalized.replace("//", "/") + marker_exact = f"runtime/operations/scripts/{name}".replace("\\", "/") + marker_base = "runtime/operations/scripts" + if name.endswith(".sh"): + ok = (marker_base in normalized) and (f"/{name}" in normalized) + else: + ok = marker_exact in normalized + if not ok: + errors.append(f"forwarder target mismatch: {rp}") + return (len(errors) == 0), errors + + +def main() -> int: + ok, errors = verify_forwarders() + if ok: + print("forwarders_ok=1") + return 0 + print("forwarders_ok=0") + for err in errors: + print(err) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) + diff --git a/runtime/operations/scripts/wecom_smoke_test.py b/runtime/operations/scripts/wecom_smoke_test.py new file mode 100644 index 00000000..d7b15138 --- /dev/null +++ b/runtime/operations/scripts/wecom_smoke_test.py @@ -0,0 +1,60 @@ +from __future__ import annotations + +import json +import sys +import uuid + +from oclaw.platform.config.paths import db_path +from oclaw.platform.integrations.wecom_client import WeComClient +from oclaw.platform.persistence.sqlite_store import SqliteStore + + +def main() -> int: + store = SqliteStore(db_path()) + client = WeComClient(store) + try: + bot_id, _bot_secret = client.get_bot_credentials() + except Exception as exc: + print("ok=0") + print("step=read_bot_config") + print(f"error={type(exc).__name__}: {exc}") + return 1 + print("ok=1") + print("mode=bot_api") + print(f"bot_id={bot_id}") + if "--ws" not in sys.argv[1:]: + print("ws_skipped=1") + print("hint=use --ws for subscribe handshake test") + return 0 + try: + import websocket # type: ignore + except Exception: + print("ok=0") + print("step=import_websocket_client") + print("error=websocket-client not installed, run: pip install websocket-client") + return 1 + try: + ws = websocket.create_connection("wss://openws.work.weixin.qq.com", timeout=15) + ws.send( + json.dumps( + { + "cmd": "aibot_subscribe", + "headers": {"req_id": uuid.uuid4().hex}, + "body": {"bot_id": bot_id, "secret": client.get_bot_credentials()[1]}, + }, + ensure_ascii=False, + ) + ) + raw = ws.recv() + ws.close() + print("ws_subscribe_response=" + str(raw)) + return 0 + except Exception as exc: + print("ok=0") + print("step=ws_subscribe") + print(f"error={type(exc).__name__}: {exc}") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtime/operations/scripts/wecom_status.py b/runtime/operations/scripts/wecom_status.py new file mode 100644 index 00000000..e37b070b --- /dev/null +++ b/runtime/operations/scripts/wecom_status.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +import json +from datetime import datetime, timezone + +from oclaw.platform.config.paths import db_path +from oclaw.platform.integrations.wecom_client import WeComClient +from oclaw.platform.persistence.sqlite_store import SqliteStore + + +def _fmt_ts(ts: str) -> str: + try: + n = int(str(ts).strip()) + return datetime.fromtimestamp(n, tz=timezone.utc).astimezone().isoformat() + except Exception: + return "" + + +def main() -> int: + store = SqliteStore(db_path()) + client = WeComClient(store) + print("ok=1") + print(f"mode={client.mode()}") + bot_id = str(store.get_setting("wecom_bot_id") or "").strip() + print(f"bot_id={bot_id}") + + auto_bind = str(store.get_setting("wecom_auto_bind_enabled") or "1").strip() + auto_tenant = str(store.get_setting("wecom_auto_bind_tenant_name") or "Team").strip() or "Team" + auto_role = str(store.get_setting("wecom_auto_bind_role") or "member").strip() or "member" + print(f"auto_bind_enabled={auto_bind}") + print(f"auto_bind_tenant={auto_tenant}") + print(f"auto_bind_role={auto_role}") + + last_ts = str(store.get_setting("wecom_last_msg_ts") or "").strip() + last_user = str(store.get_setting("wecom_last_from_user") or "").strip() + last_cmd = str(store.get_setting("wecom_last_cmd") or "").strip() + last_raw_from_user = str(store.get_setting("wecom_last_raw_from_user") or "").strip() + last_parse_error = str(store.get_setting("wecom_last_parse_error") or "").strip() + last_outbound_mode = str(store.get_setting("wecom_last_outbound_mode") or "").strip() + last_outbound_error = str(store.get_setting("wecom_last_outbound_error") or "").strip() + print(f"last_from_user={last_user}") + print(f"last_raw_from_user={last_raw_from_user}") + print(f"last_cmd={last_cmd}") + print(f"last_parse_error={last_parse_error}") + print(f"last_outbound_mode={last_outbound_mode}") + print(f"last_outbound_error={last_outbound_error}") + print(f"last_msg_ts={last_ts}") + print(f"last_msg_local={_fmt_ts(last_ts)}") + + raw_recent = str(store.get_setting("wecom_recent_from_users") or "[]") + try: + recent = json.loads(raw_recent) + except Exception: + recent = [] + if not isinstance(recent, list): + recent = [] + print("recent_from_users=" + json.dumps(recent[:20], ensure_ascii=False)) + + idents = store.list_channel_identities(channel="wecom", limit=50) + print(f"bound_identities={len(idents)}") + for x in idents[:20]: + print( + "identity=" + + json.dumps( + { + "external_user_id": x.get("external_user_id"), + "display_name": x.get("display_name"), + "role": x.get("role"), + "tenant_id": x.get("tenant_id"), + }, + ensure_ascii=False, + ) + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/runtime/operations/scripts/weixin_install.ps1 b/runtime/operations/scripts/weixin_install.ps1 new file mode 100644 index 00000000..e1e4437f --- /dev/null +++ b/runtime/operations/scripts/weixin_install.ps1 @@ -0,0 +1,41 @@ +param( + [string]$ChannelId = "oclaw-weixin", + [string]$Package = "@tencent-weixin/oclaw-weixin@2.1.9", + [string]$OclawRuntime = "" +) + +$ErrorActionPreference = "Stop" + +function Resolve-RepoRoot { + $here = Split-Path -Parent $PSCommandPath + return (Resolve-Path (Join-Path $here "..")).Path +} + +$oclawRoot = Resolve-RepoRoot +$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" +$stateDir = Join-Path $sidecarRoot "state" + +New-Item -ItemType Directory -Force -Path $sidecarRoot | Out-Null +New-Item -ItemType Directory -Force -Path (Join-Path $sidecarRoot "logs") | Out-Null +New-Item -ItemType Directory -Force -Path $stateDir | Out-Null + +Push-Location $sidecarRoot +try { + if (-not (Test-Path (Join-Path $sidecarRoot "package.json"))) { + npm.cmd init -y | Out-Null + } + if (Test-Path (Join-Path $sidecarRoot "package-lock.json")) { + npm.cmd ci + } else { + # First-time setup: install exact versions for reproducible sidecar runtime. + if ($OclawRuntime) { + npm.cmd install --save-exact $Package $OclawRuntime tsx@4.21.0 typescript@6.0.3 + } else { + npm.cmd install --save-exact $Package tsx@4.21.0 typescript@6.0.3 + } + } + Write-Host "[ok] installed $Package into $sidecarRoot" +} finally { + Pop-Location +} + diff --git a/runtime/operations/scripts/weixin_login.ps1 b/runtime/operations/scripts/weixin_login.ps1 new file mode 100644 index 00000000..00a62f90 --- /dev/null +++ b/runtime/operations/scripts/weixin_login.ps1 @@ -0,0 +1,32 @@ +param( + [string]$ChannelId = "oclaw-weixin" +) + +$ErrorActionPreference = "Stop" + +function Resolve-RepoRoot { + $here = Split-Path -Parent $PSCommandPath + return (Resolve-Path (Join-Path $here "..")).Path +} + +$oclawRoot = Resolve-RepoRoot +$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" +$stateDir = Join-Path $sidecarRoot "state" + +if (-not (Test-Path $sidecarRoot)) { + throw "sidecar not installed: run .\\scripts\\weixin_install.ps1 first" +} + +New-Item -ItemType Directory -Force -Path $stateDir | Out-Null + +Push-Location $sidecarRoot +try { + $env:OCLAW_STATE_DIR = $stateDir + if (-not (Test-Path (Join-Path $sidecarRoot "login.ts"))) { + throw "missing login.ts" + } + npm.cmd exec -- tsx login.ts +} finally { + Pop-Location +} + diff --git a/runtime/operations/scripts/weixin_start.ps1 b/runtime/operations/scripts/weixin_start.ps1 new file mode 100644 index 00000000..54e4435c --- /dev/null +++ b/runtime/operations/scripts/weixin_start.ps1 @@ -0,0 +1,39 @@ +param( + [string]$ChannelId = "oclaw-weixin", + [string]$GatewayBaseUrl = "http://127.0.0.1:8787" +) + +$ErrorActionPreference = "Stop" + +function Resolve-RepoRoot { + $here = Split-Path -Parent $PSCommandPath + return (Resolve-Path (Join-Path $here "..")).Path +} + +$oclawRoot = Resolve-RepoRoot +$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" +$stateDir = Join-Path $sidecarRoot "state" +$logDir = Join-Path $sidecarRoot "logs" +$pidFile = Join-Path $sidecarRoot "pid.txt" + +if (-not (Test-Path $sidecarRoot)) { + throw "sidecar not installed: run .\\scripts\\weixin_install.ps1 first" +} +if (-not (Test-Path (Join-Path $sidecarRoot "runner.ts"))) { + throw "missing runner.ts (sidecar code). Re-run repo sync or restore file." +} +New-Item -ItemType Directory -Force -Path $logDir | Out-Null +New-Item -ItemType Directory -Force -Path $stateDir | Out-Null + +$logPath = Join-Path $logDir "weixin_sidecar.log" +$errPath = Join-Path $logDir "weixin_sidecar.err.log" +$cmd = "cmd.exe" +$args = @( + "/c", + "cd /d $sidecarRoot && set OCLAW_STATE_DIR=$stateDir&& set AIA_GATEWAY_BASE_URL=$GatewayBaseUrl&& npm.cmd exec -- tsx runner.ts" +) + +$p = Start-Process -FilePath $cmd -ArgumentList $args -WorkingDirectory $sidecarRoot -PassThru -WindowStyle Hidden -RedirectStandardOutput $logPath -RedirectStandardError $errPath +Set-Content -Path $pidFile -Value $p.Id +Write-Host "[ok] started weixin sidecar pid=$($p.Id) out=$logPath err=$errPath" + diff --git a/runtime/operations/scripts/weixin_status.ps1 b/runtime/operations/scripts/weixin_status.ps1 new file mode 100644 index 00000000..94fd46c2 --- /dev/null +++ b/runtime/operations/scripts/weixin_status.ps1 @@ -0,0 +1,38 @@ +param( + [string]$ChannelId = "oclaw-weixin" +) + +$ErrorActionPreference = "Stop" + +function Resolve-RepoRoot { + $here = Split-Path -Parent $PSCommandPath + return (Resolve-Path (Join-Path $here "..")).Path +} + +$oclawRoot = Resolve-RepoRoot +$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" +$pidFile = Join-Path $sidecarRoot "pid.txt" + +if (-not (Test-Path $pidFile)) { + Write-Host "status=stopped" + exit 0 +} + +$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) +if (-not $procId) { + Write-Host "status=stopped" + exit 0 +} + +$exists = $false +try { + Get-Process -Id $procId -ErrorAction Stop | Out-Null + $exists = $true +} catch {} + +if ($exists) { + Write-Host "status=running pid=$procId" +} else { + Write-Host "status=stale_pid pid=$procId" +} + diff --git a/runtime/operations/scripts/weixin_stop.ps1 b/runtime/operations/scripts/weixin_stop.ps1 new file mode 100644 index 00000000..bc565e73 --- /dev/null +++ b/runtime/operations/scripts/weixin_stop.ps1 @@ -0,0 +1,41 @@ +param( + [string]$ChannelId = "oclaw-weixin", + [switch]$Force +) + +$ErrorActionPreference = "Stop" + +function Resolve-RepoRoot { + $here = Split-Path -Parent $PSCommandPath + return (Resolve-Path (Join-Path $here "..")).Path +} + +$oclawRoot = Resolve-RepoRoot +$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" +$pidFile = Join-Path $sidecarRoot "pid.txt" + +if (-not (Test-Path $pidFile)) { + Write-Host "[ok] not running (no pid file)" + exit 0 +} + +$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) +if (-not $procId) { + Remove-Item -Force $pidFile -ErrorAction SilentlyContinue + Write-Host "[ok] not running (empty pid file)" + exit 0 +} + +try { + if ($Force) { + taskkill.exe /PID $procId /T /F | Out-Null + } else { + taskkill.exe /PID $procId /T | Out-Null + } +} catch { + # Ignore if already dead. +} + +Remove-Item -Force $pidFile -ErrorAction SilentlyContinue +Write-Host "[ok] stopped pid=$procId" + diff --git a/scripts/wiki_auto_smoke_test.py b/runtime/operations/scripts/wiki_auto_smoke_test.py similarity index 97% rename from scripts/wiki_auto_smoke_test.py rename to runtime/operations/scripts/wiki_auto_smoke_test.py index 91f1eb8e..4e7c07d0 100644 --- a/scripts/wiki_auto_smoke_test.py +++ b/runtime/operations/scripts/wiki_auto_smoke_test.py @@ -56,7 +56,7 @@ def main() -> int: "user_text": "smoke router vlan check", "assistant_text": "smoke response for wiki worker", } - task = store.openclaw_task_create( + task = store.oclaw_task_create( tenant_id="default", session_id=payload["session_id"], task_type="wiki_capture", @@ -67,7 +67,7 @@ def main() -> int: deadline = time.time() + max(5, int(args.timeout_s)) final = task while time.time() < deadline: - got = store.openclaw_task_get(task_id=task.id) + got = store.oclaw_task_get(task_id=task.id) if got is None: print("[smoke] task missing") return 2 diff --git a/scripts/ws_probe.py b/runtime/operations/scripts/ws_probe.py similarity index 100% rename from scripts/ws_probe.py rename to runtime/operations/scripts/ws_probe.py diff --git a/ops/stack.py b/runtime/operations/stack.py similarity index 94% rename from ops/stack.py rename to runtime/operations/stack.py index 4063aeca..9a690f4c 100644 --- a/ops/stack.py +++ b/runtime/operations/stack.py @@ -13,7 +13,7 @@ def cmd_stack_up(args: argparse.Namespace) -> int: gateway_cmd = default_python_command( [ "-m", - "oclaw.ops", + "oclaw.runtime.operations", "gateway", "start", "--host", @@ -27,7 +27,7 @@ def cmd_stack_up(args: argparse.Namespace) -> int: channel_cmd = default_python_command( [ "-m", - "oclaw.ops", + "oclaw.runtime.operations", "channel", args.channel, "start", @@ -51,7 +51,7 @@ def cmd_stack_up(args: argparse.Namespace) -> int: print("ok=1") for name, pid in started: print(f"{name}_pid={pid}") - print("hint=use `python -m oclaw.ops stack status` to check running services") + print("hint=use `python -m oclaw.runtime.operations stack status` to check running services") return 0 diff --git a/orchestration/__init__.py b/runtime/orchestration/__init__.py similarity index 100% rename from orchestration/__init__.py rename to runtime/orchestration/__init__.py diff --git a/orchestration/evaluation.py b/runtime/orchestration/evaluation.py similarity index 100% rename from orchestration/evaluation.py rename to runtime/orchestration/evaluation.py diff --git a/orchestration/inventory.py b/runtime/orchestration/inventory.py similarity index 94% rename from orchestration/inventory.py rename to runtime/orchestration/inventory.py index ca556ed3..0eb9b9ca 100644 --- a/orchestration/inventory.py +++ b/runtime/orchestration/inventory.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Any -from oclaw.tools import tool_inventory +from oclaw.runtime.tools import tool_inventory def inventory_snapshot() -> dict[str, Any]: diff --git a/orchestration/memory.py b/runtime/orchestration/memory.py similarity index 99% rename from orchestration/memory.py rename to runtime/orchestration/memory.py index 5151b8d2..2dbf8a04 100644 --- a/orchestration/memory.py +++ b/runtime/orchestration/memory.py @@ -7,7 +7,7 @@ from typing import Any from oclaw.platform.persistence.sqlite_store import SqliteStore from oclaw.platform.embeddings.embedding_client import build_default_embedding_client -from oclaw.orchestration.vector_store import ( +from oclaw.runtime.orchestration.vector_store import ( MemoryVectorItem, read_vector_memory_runtime, semantic_search, diff --git a/orchestration/policy.py b/runtime/orchestration/policy.py similarity index 100% rename from orchestration/policy.py rename to runtime/orchestration/policy.py diff --git a/orchestration/protocol.py b/runtime/orchestration/protocol.py similarity index 100% rename from orchestration/protocol.py rename to runtime/orchestration/protocol.py diff --git a/orchestration/security.py b/runtime/orchestration/security.py similarity index 96% rename from orchestration/security.py rename to runtime/orchestration/security.py index b8ff8a6f..42b77fd0 100644 --- a/orchestration/security.py +++ b/runtime/orchestration/security.py @@ -2,7 +2,7 @@ from __future__ import annotations from dataclasses import dataclass -from oclaw.orchestration.protocol import AgentTask +from oclaw.runtime.orchestration.protocol import AgentTask _HIGH_RISK_ACTIONS = ("删除", "drop", "重启", "批量", "扫描", "写入", "变更") diff --git a/orchestration/session_mapping.py b/runtime/orchestration/session_mapping.py similarity index 100% rename from orchestration/session_mapping.py rename to runtime/orchestration/session_mapping.py diff --git a/orchestration/tool_adapter.py b/runtime/orchestration/tool_adapter.py similarity index 100% rename from orchestration/tool_adapter.py rename to runtime/orchestration/tool_adapter.py diff --git a/orchestration/trace.py b/runtime/orchestration/trace.py similarity index 100% rename from orchestration/trace.py rename to runtime/orchestration/trace.py diff --git a/orchestration/vector_store.py b/runtime/orchestration/vector_store.py similarity index 100% rename from orchestration/vector_store.py rename to runtime/orchestration/vector_store.py diff --git a/openclaw_runtime/project_context_prompt.py b/runtime/project_context_prompt.py similarity index 93% rename from openclaw_runtime/project_context_prompt.py rename to runtime/project_context_prompt.py index e7c74d75..dbb772df 100644 --- a/openclaw_runtime/project_context_prompt.py +++ b/runtime/project_context_prompt.py @@ -4,10 +4,10 @@ import os from pathlib import Path from typing import Any -from oclaw.agents.agent_scope import resolve_agent_id_by_workspace_path -from oclaw.openclaw_runtime.hooks_runtime import get_active_hooks_config, trigger_hook_event +from oclaw.runtime.agents.agent_scope import resolve_agent_id_by_workspace_path +from oclaw.runtime.hooks_runtime import get_active_hooks_config, trigger_hook_event from oclaw.platform.config.paths import PROJECT_ROOT -from oclaw.prompts.loader import render_openclaw_prompt +from oclaw.prompts.loader import render_runtime_prompt _PROJECT_CONTEXT_FILES: tuple[str, ...] = ( "AGENTS.md", @@ -22,10 +22,10 @@ _PROJECT_CONTEXT_FILES: tuple[str, ...] = ( def _project_context_roots() -> tuple[Path, ...]: roots: list[Path] = [] - raw_ws = str(os.getenv("OPENCLAW_WORKSPACE") or "").strip() + raw_ws = str(os.getenv("OCLAW_WORKSPACE") or "").strip() if raw_ws: roots.append(Path(raw_ws).expanduser()) - roots.append(Path(PROJECT_ROOT) / "oclaw" / "agent" / "workspace-main") + roots.append(Path(PROJECT_ROOT) / "oclaw" / "runtime" / "assets" / "agent_workspaces" / "workspace-main") roots.append(Path(PROJECT_ROOT) / "oclaw" / "workspace-main") roots.append(Path(PROJECT_ROOT) / "oclaw" / "workspace") roots.append(Path(PROJECT_ROOT)) @@ -179,7 +179,7 @@ def build_project_context_block(*, store: Any, workspace_dir: str | None = None) if not lines: return "" - return render_openclaw_prompt( + return render_runtime_prompt( "runtime/project_context_block.md", variables={"project_context": "\n".join(lines).strip()}, strict=True, diff --git a/openclaw_runtime/relay_pointer.py b/runtime/relay_pointer.py similarity index 98% rename from openclaw_runtime/relay_pointer.py rename to runtime/relay_pointer.py index e7658c07..9937e500 100644 --- a/openclaw_runtime/relay_pointer.py +++ b/runtime/relay_pointer.py @@ -8,7 +8,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Any -from oclaw.openclaw_runtime.types import SharedAttachmentManifest, SharedFilePointer +from oclaw.runtime.types import SharedAttachmentManifest, SharedFilePointer _SAFE_FILE_NAME = re.compile(r"^[A-Za-z0-9._-]{1,128}$") _SAFE_SCOPE_ID = re.compile(r"^[A-Za-z0-9._-]{1,128}$") diff --git a/openclaw_runtime/router.py b/runtime/router.py similarity index 90% rename from openclaw_runtime/router.py rename to runtime/router.py index ea9497fa..96356785 100644 --- a/openclaw_runtime/router.py +++ b/runtime/router.py @@ -5,9 +5,9 @@ import os from dataclasses import dataclass from typing import Any -from oclaw.openclaw_runtime.types import StandardMessage -from oclaw.openclaw_runtime.types import normalize_interaction_mode, normalize_requested_specialist -from oclaw.prompts.loader import render_openclaw_prompt +from oclaw.runtime.types import StandardMessage +from oclaw.runtime.types import normalize_interaction_mode, normalize_requested_specialist +from oclaw.prompts.loader import render_runtime_prompt @dataclass(frozen=True) @@ -34,11 +34,11 @@ def _router_mode_from_store(store: Any | None) -> str: raw = "" if store is not None: try: - raw = str(store.get_setting("AIA_OPENCLAW_ROUTER_MODE") or "").strip().lower() + raw = str(store.get_setting("AIA_OCLAW_ROUTER_MODE") or "").strip().lower() except Exception: raw = "" if not raw: - raw = str(os.getenv("AIA_OPENCLAW_ROUTER_MODE", "") or "").strip().lower() + raw = str(os.getenv("AIA_OCLAW_ROUTER_MODE", "") or "").strip().lower() return raw or "rule" @@ -68,7 +68,7 @@ def _decide_llm_json(msg: StandardMessage, *, model: Any | None) -> RouterDecisi if model is None or not callable(getattr(model, "chat", None)): return None try: - user_block = render_openclaw_prompt( + user_block = render_runtime_prompt( "router/decide_route.md", variables={ "user_text": str(msg.text or "").strip(), diff --git a/openclaw_runtime/skill_executor.py b/runtime/skill_executor.py similarity index 95% rename from openclaw_runtime/skill_executor.py rename to runtime/skill_executor.py index f2b44144..9b7acc2a 100644 --- a/openclaw_runtime/skill_executor.py +++ b/runtime/skill_executor.py @@ -3,14 +3,14 @@ from __future__ import annotations from dataclasses import dataclass from typing import Any, Callable, Optional -from oclaw.chat.tool_runtime import ( +from oclaw.runtime.chat.tool_runtime import ( ToolExecutionConfig, ToolExecutionContext, ToolExecutor, ) -from oclaw.orchestration.trace import new_span_id +from oclaw.runtime.orchestration.trace import new_span_id from oclaw.platform.llm.chat_models import LLMToolCall -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.tools.base import ToolRegistry @dataclass(frozen=True) @@ -20,7 +20,7 @@ class SkillExecutionContext: session_id: str lang: str = "zh" user_text: str = "" - specialist: str = "openclaw" + specialist: str = "oclaw" trace_id: str | None = None parent_span_id: str | None = None workspace_owner_session_id: str | None = None @@ -52,7 +52,7 @@ class SkillExecutor: parent_span_id=ctx.parent_span_id, event_type=str(event_type), payload={ - "pipeline": "openclaw_skill_executor", + "pipeline": "oclaw_skill_executor", "trace_id": str(ctx.trace_id or ""), "run_id": str(ctx.run_id or ""), "attempt_no": int(ctx.attempt_no) if ctx.attempt_no is not None else None, diff --git a/openclaw_runtime/skill_installer.py b/runtime/skill_installer.py similarity index 96% rename from openclaw_runtime/skill_installer.py rename to runtime/skill_installer.py index 6ba883d5..5cebb1f0 100644 --- a/openclaw_runtime/skill_installer.py +++ b/runtime/skill_installer.py @@ -11,7 +11,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Any -from oclaw.openclaw_runtime.skills import ( +from oclaw.runtime.skills import ( default_skills_root, discover_workspace_skill_manifests, load_skill_manifest, @@ -98,7 +98,7 @@ def list_skills_with_status(*, store: Any, skills_root: str | Path | None = None "skill_file": m.skill_file, "enabled": m.name not in disabled, "install_specs": [dict(id=s.id, kind=s.kind, payload=s.payload) for s in m.install], - "metadata_openclaw": dict(m.metadata_openclaw), + "metadata_oclaw": dict(m.metadata_oclaw), "runtime": runtime, "executable": bool(runtime), } @@ -185,7 +185,7 @@ def _resolve_registry_archive_url(url: str) -> str: if len(segs) >= 2: candidates.append(f"{segs[0]}/{segs[1]}") try: - from oclaw.tools.skills.clawhub_client import get_skill_detail + from oclaw.runtime.tools.skills.clawhub_client import get_skill_detail for slug in candidates: detail = get_skill_detail(slug) @@ -249,7 +249,7 @@ def install_skill_from_registry_archive( req = urllib.request.Request( url, headers={ - "User-Agent": "OpenClaw-SkillInstaller/1.0 (+https://clawhub.ai)", + "User-Agent": "Oclaw-SkillInstaller/1.0 (+https://clawhub.ai)", "Accept": "*/*", }, ) @@ -303,7 +303,7 @@ def create_skill_from_template( name: str, description: str, body_markdown: str = "", - metadata_openclaw: dict[str, Any] | None = None, + metadata_oclaw: dict[str, Any] | None = None, skills_root: str | Path | None = None, overwrite: bool = False, ) -> SkillInstallResult: @@ -318,13 +318,13 @@ def create_skill_from_template( ec, rt = _classify_install_detail("already_exists") return SkillInstallResult(ok=False, name=nm, target_dir=str(target), detail="already_exists", error_code=ec, retryable=rt) target.mkdir(parents=True, exist_ok=True) - oc = dict(metadata_openclaw or {}) + oc = dict(metadata_oclaw or {}) front = { "name": nm, "description": str(description or f"{nm} skill"), "user-invocable": "true", "disable-model-invocation": "false", - "metadata": {"openclaw": oc}, + "metadata": {"oclaw": oc}, } text = ( "---\n" @@ -354,7 +354,7 @@ def auto_install_skill_from_payload( name = str(payload.get("name") or "").strip() description = str(payload.get("description") or "").strip() body = str(payload.get("body_markdown") or "").strip() - md = payload.get("metadata_openclaw") + md = payload.get("metadata_oclaw") md = dict(md) if isinstance(md, dict) else {} root = Path(skills_root).resolve() if skills_root else default_skills_root() target = root / name @@ -365,7 +365,7 @@ def auto_install_skill_from_payload( name=name, description=description, body_markdown=body, - metadata_openclaw=md, + metadata_oclaw=md, skills_root=skills_root, overwrite=False, ) diff --git a/openclaw_runtime/skill_role_binding.py b/runtime/skill_role_binding.py similarity index 96% rename from openclaw_runtime/skill_role_binding.py rename to runtime/skill_role_binding.py index 5cde9289..eb4e861f 100644 --- a/openclaw_runtime/skill_role_binding.py +++ b/runtime/skill_role_binding.py @@ -4,7 +4,7 @@ import json import os from typing import Any -from oclaw.agents.specialists import SPECIALISTS +from oclaw.runtime.agents.specialists import SPECIALISTS SKILL_ROLE_BINDING_KEY = "skill_role_binding" SKILL_ROLE_BINDING_ENABLED_SETTING = "AIA_SKILL_ROLE_BINDING_ENABLED" @@ -95,7 +95,7 @@ def allowed_workspace_skill_names_for_role(*, store: Any, role: str) -> set[str] def _all_installed_skill_names(store: Any) -> set[str]: - from oclaw.openclaw_runtime.skills import discover_workspace_skill_manifests + from oclaw.runtime.skills import discover_workspace_skill_manifests return {str(m.name).strip() for m in discover_workspace_skill_manifests() if str(m.name or "").strip()} diff --git a/openclaw_runtime/skills.py b/runtime/skills.py similarity index 93% rename from openclaw_runtime/skills.py rename to runtime/skills.py index 499bbede..3477aed1 100644 --- a/openclaw_runtime/skills.py +++ b/runtime/skills.py @@ -8,10 +8,11 @@ from pathlib import Path from typing import TYPE_CHECKING, Any from oclaw.platform.config.paths import PROJECT_ROOT +from oclaw.platform.config.runtime_paths import runtime_skills_root from oclaw.prompts.frontmatter import parse_frontmatter_dict, split_markdown_frontmatter if TYPE_CHECKING: - from oclaw.tools.base import ToolRegistry, ToolSpec + from oclaw.runtime.tools.base import ToolRegistry, ToolSpec @dataclass(frozen=True) @@ -52,7 +53,7 @@ class SkillManifest: skill_file: str user_invocable: bool = True disable_model_invocation: bool = False - metadata_openclaw: dict[str, Any] = field(default_factory=dict) + metadata_oclaw: dict[str, Any] = field(default_factory=dict) runtime: dict[str, Any] = field(default_factory=dict) install: tuple[SkillInstallSpec, ...] = () body: str = "" @@ -82,13 +83,16 @@ def default_skills_root() -> Path: raw = str(os.getenv("AIA_SKILLS_ROOT") or "").strip() if raw: return Path(raw).resolve() - preferred = (PROJECT_ROOT / "oclaw" / "skills").resolve() + preferred = runtime_skills_root() if preferred.exists() and preferred.is_dir(): return preferred + # Optional hard-disable for legacy fallback lookups. + if str(os.getenv("AIA_DISABLE_LEGACY_SKILLS_FALLBACK") or "").strip().lower() in {"1", "true", "yes", "on"}: + return preferred typo_legacy = (PROJECT_ROOT / "oclaw" / "sills").resolve() if typo_legacy.exists() and typo_legacy.is_dir(): return typo_legacy - legacy = (PROJECT_ROOT / "skills").resolve() + legacy = (PROJECT_ROOT / "oclaw" / "skills").resolve() if legacy.exists() and legacy.is_dir(): return legacy return preferred @@ -127,8 +131,8 @@ def _parse_skill_frontmatter_block(frontmatter_text: str) -> dict[str, Any]: return _normalize_skill_manifest_frontmatter(fm) -def _parse_install_specs(metadata_openclaw: dict[str, Any]) -> tuple[SkillInstallSpec, ...]: - raw = metadata_openclaw.get("install") +def _parse_install_specs(metadata_oclaw: dict[str, Any]) -> tuple[SkillInstallSpec, ...]: + raw = metadata_oclaw.get("install") if not isinstance(raw, list): return () out: list[SkillInstallSpec] = [] @@ -144,8 +148,8 @@ def _parse_install_specs(metadata_openclaw: dict[str, Any]) -> tuple[SkillInstal return tuple(out) -def _parse_runtime_spec(metadata_openclaw: dict[str, Any]) -> dict[str, Any]: - raw = metadata_openclaw.get("runtime") +def _parse_runtime_spec(metadata_oclaw: dict[str, Any]) -> dict[str, Any]: + raw = metadata_oclaw.get("runtime") if not isinstance(raw, dict): return {} out: dict[str, Any] = {} @@ -178,7 +182,7 @@ def load_skill_manifest(skill_dir: str | Path) -> SkillManifest | None: desc = str(fm.get("description") or "").strip() or f"{name} skill" md = fm.get("metadata") md = dict(md) if isinstance(md, dict) else {} - oc = md.get("openclaw") + oc = md.get("oclaw") oc = dict(oc) if isinstance(oc, dict) else {} return SkillManifest( name=name, @@ -187,7 +191,7 @@ def load_skill_manifest(skill_dir: str | Path) -> SkillManifest | None: skill_file=str(f), user_invocable=bool(fm.get("user-invocable", True)), disable_model_invocation=bool(fm.get("disable-model-invocation", False)), - metadata_openclaw=oc, + metadata_oclaw=oc, runtime=_parse_runtime_spec(oc), install=_parse_install_specs(oc), body=str(body or ""), diff --git a/skills/README.md b/runtime/skills/README.md similarity index 68% rename from skills/README.md rename to runtime/skills/README.md index 9c773001..a980e8eb 100644 --- a/skills/README.md +++ b/runtime/skills/README.md @@ -8,9 +8,9 @@ - 技能说明正文请优先使用中文,便于团队统一维护。 ## 示例结构 -- `oclaw/skills//SKILL.md` +- `oclaw/runtime/skills//SKILL.md` ## 兼容说明 -- 运行时优先读取 `oclaw/skills`。 +- 运行时优先读取 `oclaw/runtime/skills`。 - 若设置了环境变量 `AIA_SKILLS_ROOT`,以该变量为准。 -- 为兼容旧工程,仍可回退读取仓库根目录下的 `skills/`。 +- 为兼容旧工程,仍可回退读取旧路径 `oclaw/runtime/skills/`(如存在)。 diff --git a/skills/coding/SKILL.md b/runtime/skills/coding/SKILL.md similarity index 98% rename from skills/coding/SKILL.md rename to runtime/skills/coding/SKILL.md index 01b7ee78..bcbd7977 100644 --- a/skills/coding/SKILL.md +++ b/runtime/skills/coding/SKILL.md @@ -4,7 +4,7 @@ description: 研发实现技能,负责代码实现、缺陷修复与验证闭 user-invocable: true disable-model-invocation: false metadata: - openclaw: + oclaw: role: specialist owner: workspace-coding focus: diff --git a/skills/main/SKILL.md b/runtime/skills/main/SKILL.md similarity index 98% rename from skills/main/SKILL.md rename to runtime/skills/main/SKILL.md index 728e3f8b..ba5f25d7 100644 --- a/skills/main/SKILL.md +++ b/runtime/skills/main/SKILL.md @@ -4,7 +4,7 @@ description: 主编排技能,负责任务分派、验收与汇总输出。 user-invocable: true disable-model-invocation: false metadata: - openclaw: + oclaw: role: orchestrator owner: workspace-main --- diff --git a/skills/self-improvement/README.md b/runtime/skills/self-improvement/README.md similarity index 51% rename from skills/self-improvement/README.md rename to runtime/skills/self-improvement/README.md index 28cf918f..89508174 100644 --- a/skills/self-improvement/README.md +++ b/runtime/skills/self-improvement/README.md @@ -1,10 +1,10 @@ # self-improvement -Self-improvement skill for OpenClaw. It captures learnings, errors, and feature requests to support continuous improvement across sessions. +Self-improvement skill for Oclaw. It captures learnings, errors, and feature requests to support continuous improvement across sessions. ## Attribution -Remade for OpenClaw from the original repo: +Remade for Oclaw from the original repo: - https://github.com/pskoett/pskoett-ai-skills - https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement diff --git a/skills/self-improvement/SKILL.md b/runtime/skills/self-improvement/SKILL.md similarity index 93% rename from skills/self-improvement/SKILL.md rename to runtime/skills/self-improvement/SKILL.md index 1d46d0ac..bca1fb2d 100644 --- a/skills/self-improvement/SKILL.md +++ b/runtime/skills/self-improvement/SKILL.md @@ -38,13 +38,13 @@ If you want automatic reminders or setup assistance, use the opt-in hook workflo | Simplify/Harden recurring patterns | Log/update `.learnings/LEARNINGS.md` with `Source: simplify-and-harden` and a stable `Pattern-Key` | | Similar to existing entry | Link with `**See Also**`, consider priority bump | | Broadly applicable learning | Promote to `CLAUDE.md`, `AGENTS.md`, and/or `.github/copilot-instructions.md` | -| Workflow improvements | Promote to `AGENTS.md` (OpenClaw workspace) | -| Tool gotchas | Promote to `TOOLS.md` (OpenClaw workspace) | -| Behavioral patterns | Promote to `SOUL.md` (OpenClaw workspace) | +| Workflow improvements | Promote to `AGENTS.md` (Oclaw workspace) | +| Tool gotchas | Promote to `TOOLS.md` (Oclaw workspace) | +| Behavioral patterns | Promote to `SOUL.md` (Oclaw workspace) | -## OpenClaw Setup (Recommended) +## Oclaw Setup (Recommended) -OpenClaw is the primary platform for this skill. It uses workspace-based prompt injection with automatic skill loading. +Oclaw is the primary platform for this skill. It uses workspace-based prompt injection with automatic skill loading. ### Installation @@ -55,17 +55,17 @@ clawdhub install self-improving-agent **Manual:** ```bash -git clone https://github.com/peterskoett/self-improving-agent.git ~/.openclaw/skills/self-improving-agent +git clone https://github.com/peterskoett/self-improving-agent.git ~/.oclaw/runtime/skills/self-improving-agent ``` -Remade for openclaw from original repo : https://github.com/pskoett/pskoett-ai-skills - https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement +Remade for oclaw from original repo : https://github.com/pskoett/pskoett-ai-skills - https://github.com/pskoett/pskoett-ai-skills/tree/main/skills/self-improvement ### Workspace Structure -OpenClaw injects these files into every session: +Oclaw injects these files into every session: ``` -~/.openclaw/workspace/ +~/.oclaw/workspace/ ├── AGENTS.md # Multi-agent workflows, delegation patterns ├── SOUL.md # Behavioral guidelines, personality, principles ├── TOOLS.md # Tool capabilities, integration gotchas @@ -81,7 +81,7 @@ OpenClaw injects these files into every session: ### Create Learning Files ```bash -mkdir -p ~/.openclaw/workspace/.learnings +mkdir -p ~/.oclaw/workspace/.learnings ``` Then create the log files (or copy from `assets/`): @@ -101,7 +101,7 @@ When learnings prove broadly applicable, promote them to workspace files: ### Inter-Session Communication -OpenClaw provides tools to share learnings across sessions: +Oclaw provides tools to share learnings across sessions: - **sessions_list** — View active/recent sessions - **sessions_history** — Read another session's transcript @@ -115,14 +115,14 @@ Use these only in trusted environments and only when the user explicitly wants c For automatic reminders at session start: ```bash -# Copy hook to OpenClaw hooks directory -cp -r hooks/openclaw ~/.openclaw/hooks/self-improvement +# Copy hook to Oclaw hooks directory +cp -r hooks/oclaw ~/.oclaw/hooks/self-improvement # Enable it -openclaw hooks enable self-improvement +oclaw hooks enable self-improvement ``` -See `references/openclaw-integration.md` for complete details. +See `references/oclaw-integration.md` for complete details. --- @@ -297,8 +297,8 @@ When a learning is broadly applicable (not a one-off fix), promote it to permane | `CLAUDE.md` | Project facts, conventions, gotchas for all Claude interactions | | `AGENTS.md` | Agent-specific workflows, tool usage patterns, automation rules | | `.github/copilot-instructions.md` | Project context and conventions for GitHub Copilot | -| `SOUL.md` | Behavioral guidelines, communication style, principles (OpenClaw workspace) | -| `TOOLS.md` | Tool capabilities, usage patterns, integration gotchas (OpenClaw workspace) | +| `SOUL.md` | Behavioral guidelines, communication style, principles (Oclaw workspace) | +| `TOOLS.md` | Tool capabilities, usage patterns, integration gotchas (Oclaw workspace) | ### How to Promote @@ -375,7 +375,7 @@ Promotion targets: - `CLAUDE.md` - `AGENTS.md` - `.github/copilot-instructions.md` -- `SOUL.md` / `TOOLS.md` for OpenClaw workspace-level guidance when applicable +- `SOUL.md` / `TOOLS.md` for Oclaw workspace-level guidance when applicable Write promoted rules as short prevention rules (what to do before/while coding), not long incident write-ups. @@ -501,7 +501,7 @@ Create `.claude/settings.json` in your project: "matcher": "", "hooks": [{ "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" + "command": "./runtime/skills/self-improvement/scripts/activator.sh" }] }] } @@ -519,14 +519,14 @@ This injects a learning evaluation reminder after each prompt (~50-100 tokens ov "matcher": "", "hooks": [{ "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" + "command": "./runtime/skills/self-improvement/scripts/activator.sh" }] }], "PostToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", - "command": "./skills/self-improvement/scripts/error-detector.sh" + "command": "./runtime/skills/self-improvement/scripts/error-detector.sh" }] }] } @@ -565,8 +565,8 @@ A learning qualifies for skill extraction when ANY of these apply: 1. **Identify candidate**: Learning meets extraction criteria 2. **Run helper** (or create manually): ```bash - ./skills/self-improvement/scripts/extract-skill.sh skill-name --dry-run - ./skills/self-improvement/scripts/extract-skill.sh skill-name + ./runtime/skills/self-improvement/scripts/extract-skill.sh skill-name --dry-run + ./runtime/skills/self-improvement/scripts/extract-skill.sh skill-name ``` 3. **Customize SKILL.md**: Fill in template with learning content 4. **Update learning**: Set status to `promoted_to_skill`, add `Skill-Path` diff --git a/skills/self-improvement/_meta.json b/runtime/skills/self-improvement/_meta.json similarity index 100% rename from skills/self-improvement/_meta.json rename to runtime/skills/self-improvement/_meta.json diff --git a/skills/self-improvement/assets/ERRORS.md b/runtime/skills/self-improvement/assets/ERRORS.md similarity index 100% rename from skills/self-improvement/assets/ERRORS.md rename to runtime/skills/self-improvement/assets/ERRORS.md diff --git a/skills/self-improvement/assets/FEATURE_REQUESTS.md b/runtime/skills/self-improvement/assets/FEATURE_REQUESTS.md similarity index 100% rename from skills/self-improvement/assets/FEATURE_REQUESTS.md rename to runtime/skills/self-improvement/assets/FEATURE_REQUESTS.md diff --git a/skills/self-improvement/assets/LEARNINGS.md b/runtime/skills/self-improvement/assets/LEARNINGS.md similarity index 100% rename from skills/self-improvement/assets/LEARNINGS.md rename to runtime/skills/self-improvement/assets/LEARNINGS.md diff --git a/skills/self-improvement/assets/SKILL-TEMPLATE.md b/runtime/skills/self-improvement/assets/SKILL-TEMPLATE.md similarity index 98% rename from skills/self-improvement/assets/SKILL-TEMPLATE.md rename to runtime/skills/self-improvement/assets/SKILL-TEMPLATE.md index 0162134b..3f95e9f2 100644 --- a/skills/self-improvement/assets/SKILL-TEMPLATE.md +++ b/runtime/skills/self-improvement/assets/SKILL-TEMPLATE.md @@ -117,7 +117,7 @@ description: "What this skill does and when to use it." ### Automated (Recommended) \`\`\`bash -./skills/skill-name/scripts/helper.sh [args] +./runtime/skills/skill-name/scripts/helper.sh [args] \`\`\` ### Manual Steps diff --git a/skills/self-improvement/hooks/openclaw/HOOK.md b/runtime/skills/self-improvement/hooks/runtime/HOOK.md similarity index 81% rename from skills/self-improvement/hooks/openclaw/HOOK.md rename to runtime/skills/self-improvement/hooks/runtime/HOOK.md index df674050..f62ccf46 100644 --- a/skills/self-improvement/hooks/openclaw/HOOK.md +++ b/runtime/skills/self-improvement/hooks/runtime/HOOK.md @@ -1,7 +1,7 @@ --- name: self-improvement description: "Injects self-improvement reminder during agent bootstrap" -metadata: {"openclaw":{"emoji":"🧠","events":["agent:bootstrap"]}} +metadata: {"oclaw":{"emoji":"🧠","events":["agent:bootstrap"]}} --- # Self-Improvement Hook @@ -19,5 +19,5 @@ Injects a reminder to evaluate learnings during agent bootstrap. No configuration needed. Enable with: ```bash -openclaw hooks enable self-improvement +oclaw hooks enable self-improvement ``` diff --git a/skills/self-improvement/hooks/openclaw/handler.js b/runtime/skills/self-improvement/hooks/runtime/handler.js similarity index 98% rename from skills/self-improvement/hooks/openclaw/handler.js rename to runtime/skills/self-improvement/hooks/runtime/handler.js index bce5a29b..51a3c551 100644 --- a/skills/self-improvement/hooks/openclaw/handler.js +++ b/runtime/skills/self-improvement/hooks/runtime/handler.js @@ -1,5 +1,5 @@ /** - * Self-Improvement Hook for OpenClaw + * Self-Improvement Hook for Oclaw * * Injects a reminder to evaluate learnings during agent bootstrap. * Fires on agent:bootstrap event before workspace files are injected. diff --git a/skills/self-improvement/hooks/openclaw/handler.ts b/runtime/skills/self-improvement/hooks/runtime/handler.ts similarity index 97% rename from skills/self-improvement/hooks/openclaw/handler.ts rename to runtime/skills/self-improvement/hooks/runtime/handler.ts index 7d37191c..d5d7e5a2 100644 --- a/skills/self-improvement/hooks/openclaw/handler.ts +++ b/runtime/skills/self-improvement/hooks/runtime/handler.ts @@ -1,11 +1,11 @@ /** - * Self-Improvement Hook for OpenClaw + * Self-Improvement Hook for Oclaw * * Injects a reminder to evaluate learnings during agent bootstrap. * Fires on agent:bootstrap event before workspace files are injected. */ -import type { HookHandler } from 'openclaw/hooks'; +import type { HookHandler } from 'oclaw/hooks'; const REMINDER_NAME = 'SELF_IMPROVEMENT_REMINDER.md'; const REMINDER_PATH = REMINDER_NAME; diff --git a/skills/self-improvement/references/examples.md b/runtime/skills/self-improvement/references/examples.md similarity index 100% rename from skills/self-improvement/references/examples.md rename to runtime/skills/self-improvement/references/examples.md diff --git a/skills/self-improvement/references/hooks-setup.md b/runtime/skills/self-improvement/references/hooks-setup.md similarity index 86% rename from skills/self-improvement/references/hooks-setup.md rename to runtime/skills/self-improvement/references/hooks-setup.md index de1cb0bc..8bca3cd9 100644 --- a/skills/self-improvement/references/hooks-setup.md +++ b/runtime/skills/self-improvement/references/hooks-setup.md @@ -23,7 +23,7 @@ Create `.claude/settings.json` in your project root: "hooks": [ { "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" + "command": "./runtime/skills/self-improvement/scripts/activator.sh" } ] } @@ -34,7 +34,7 @@ Create `.claude/settings.json` in your project root: "hooks": [ { "type": "command", - "command": "./skills/self-improvement/scripts/error-detector.sh" + "command": "./runtime/skills/self-improvement/scripts/error-detector.sh" } ] } @@ -78,7 +78,7 @@ For lower overhead, use only the UserPromptSubmit hook: "hooks": [ { "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" + "command": "./runtime/skills/self-improvement/scripts/activator.sh" } ] } @@ -100,7 +100,7 @@ Codex uses the same hook system as Claude Code. Create `.codex/settings.json`: "hooks": [ { "type": "command", - "command": "./skills/self-improvement/scripts/activator.sh" + "command": "./runtime/skills/self-improvement/scripts/activator.sh" } ] } @@ -145,7 +145,7 @@ For high-value learnings that would benefit other sessions, consider skill extra ### Dry Run Extract Script ```bash -./skills/self-improvement/scripts/extract-skill.sh test-skill --dry-run +./runtime/skills/self-improvement/scripts/extract-skill.sh test-skill --dry-run ``` Expected output shows the skill scaffold that would be created. @@ -162,9 +162,9 @@ Expected output shows the skill scaffold that would be created. ### Permission Denied ```bash -chmod +x ./skills/self-improvement/scripts/activator.sh -chmod +x ./skills/self-improvement/scripts/error-detector.sh -chmod +x ./skills/self-improvement/scripts/extract-skill.sh +chmod +x ./runtime/skills/self-improvement/scripts/activator.sh +chmod +x ./runtime/skills/self-improvement/scripts/error-detector.sh +chmod +x ./runtime/skills/self-improvement/scripts/extract-skill.sh ``` ### Script Not Found @@ -173,7 +173,7 @@ If using relative paths, ensure you're in the correct directory or use absolute ```json { - "command": "/absolute/path/to/skills/self-improvement/scripts/activator.sh" + "command": "/absolute/path/to/runtime/skills/self-improvement/scripts/activator.sh" } ``` diff --git a/skills/self-improvement/references/openclaw-integration.md b/runtime/skills/self-improvement/references/oclaw-integration.md similarity index 88% rename from skills/self-improvement/references/openclaw-integration.md rename to runtime/skills/self-improvement/references/oclaw-integration.md index 1390a2e3..daafe9f7 100644 --- a/skills/self-improvement/references/openclaw-integration.md +++ b/runtime/skills/self-improvement/references/oclaw-integration.md @@ -1,15 +1,15 @@ -# OpenClaw Integration +# Oclaw Integration -Complete setup and usage guide for integrating the self-improvement skill with OpenClaw. +Complete setup and usage guide for integrating the self-improvement skill with Oclaw. ## Overview -OpenClaw uses workspace-based prompt injection combined with event-driven hooks. Context is injected from workspace files at session start, and hooks can trigger on lifecycle events. +Oclaw uses workspace-based prompt injection combined with event-driven hooks. Context is injected from workspace files at session start, and hooks can trigger on lifecycle events. ## Workspace Structure ``` -~/.openclaw/ +~/.oclaw/ ├── workspace/ # Working directory │ ├── AGENTS.md # Multi-agent coordination patterns │ ├── SOUL.md # Behavioral guidelines and personality @@ -37,21 +37,21 @@ clawdhub install self-improving-agent Or copy manually: ```bash -cp -r self-improving-agent ~/.openclaw/skills/ +cp -r self-improving-agent ~/.oclaw/runtime/skills/ ``` ### 2. Install the Hook (Optional) -Copy the hook to OpenClaw's hooks directory: +Copy the hook to Oclaw's hooks directory: ```bash -cp -r hooks/openclaw ~/.openclaw/hooks/self-improvement +cp -r hooks/oclaw ~/.oclaw/hooks/self-improvement ``` Enable the hook: ```bash -openclaw hooks enable self-improvement +oclaw hooks enable self-improvement ``` ### 3. Create Learning Files @@ -59,13 +59,13 @@ openclaw hooks enable self-improvement Create the `.learnings/` directory in your workspace: ```bash -mkdir -p ~/.openclaw/workspace/.learnings +mkdir -p ~/.oclaw/workspace/.learnings ``` Or in the skill directory: ```bash -mkdir -p ~/.openclaw/skills/self-improving-agent/.learnings +mkdir -p ~/.oclaw/runtime/skills/self-improving-agent/.learnings ``` ## Injected Prompt Files @@ -156,7 +156,7 @@ Is the learning project-specific? ## Inter-Agent Communication -OpenClaw provides tools for cross-session communication: +Oclaw provides tools for cross-session communication: Use these only when cross-session sharing is explicitly needed and the environment is trusted. Prefer short sanitized summaries over raw transcripts, command output, or secret-bearing content. @@ -210,7 +210,7 @@ sessions_spawn(task="Research X and report back", label="research") - API errors - Knowledge gaps -### OpenClaw-Specific Triggers +### Oclaw-Specific Triggers | Trigger | Action | |---------|--------| @@ -224,13 +224,13 @@ sessions_spawn(task="Research X and report back", label="research") Check hook is registered: ```bash -openclaw hooks list +oclaw hooks list ``` Check skill is loaded: ```bash -openclaw status +oclaw status ``` ## Troubleshooting @@ -251,4 +251,4 @@ openclaw status 1. Check skill is in skills directory 2. Verify SKILL.md has correct frontmatter -3. Run `openclaw status` to see loaded skills +3. Run `oclaw status` to see loaded skills diff --git a/skills/self-improvement/scripts/activator.sh b/runtime/skills/self-improvement/scripts/activator.sh similarity index 100% rename from skills/self-improvement/scripts/activator.sh rename to runtime/skills/self-improvement/scripts/activator.sh diff --git a/skills/self-improvement/scripts/error-detector.sh b/runtime/skills/self-improvement/scripts/error-detector.sh similarity index 100% rename from skills/self-improvement/scripts/error-detector.sh rename to runtime/skills/self-improvement/scripts/error-detector.sh diff --git a/skills/self-improvement/scripts/extract-skill.sh b/runtime/skills/self-improvement/scripts/extract-skill.sh similarity index 98% rename from skills/self-improvement/scripts/extract-skill.sh rename to runtime/skills/self-improvement/scripts/extract-skill.sh index ccae55a2..3abb799d 100644 --- a/skills/self-improvement/scripts/extract-skill.sh +++ b/runtime/skills/self-improvement/scripts/extract-skill.sh @@ -31,7 +31,7 @@ Options: Examples: $(basename "$0") docker-m1-fixes $(basename "$0") api-timeout-patterns --dry-run - $(basename "$0") pnpm-setup --output-dir ./skills/custom + $(basename "$0") pnpm-setup --output-dir ./runtime/skills/custom The skill will be created in: \$SKILLS_DIR// EOF diff --git a/skills/social/SKILL.md b/runtime/skills/social/SKILL.md similarity index 98% rename from skills/social/SKILL.md rename to runtime/skills/social/SKILL.md index a375acd3..ed4dc0c2 100644 --- a/skills/social/SKILL.md +++ b/runtime/skills/social/SKILL.md @@ -4,7 +4,7 @@ description: 对外沟通技能,负责可直接发布的中文文案与表达 user-invocable: true disable-model-invocation: false metadata: - openclaw: + oclaw: role: specialist owner: workspace-social focus: diff --git a/skills/weather/SKILL.md b/runtime/skills/weather/SKILL.md similarity index 99% rename from skills/weather/SKILL.md rename to runtime/skills/weather/SKILL.md index fb2e9d7e..734467ec 100644 --- a/skills/weather/SKILL.md +++ b/runtime/skills/weather/SKILL.md @@ -7,7 +7,7 @@ metadata: emoji: "🌤️" requires: bins: ["curl"] - openclaw: + oclaw: runtime: type: python entry: scripts/run.py diff --git a/skills/weather/_meta.json b/runtime/skills/weather/_meta.json similarity index 100% rename from skills/weather/_meta.json rename to runtime/skills/weather/_meta.json diff --git a/skills/weather/scripts/run.py b/runtime/skills/weather/scripts/run.py similarity index 94% rename from skills/weather/scripts/run.py rename to runtime/skills/weather/scripts/run.py index 5df4d25a..c779a218 100644 --- a/skills/weather/scripts/run.py +++ b/runtime/skills/weather/scripts/run.py @@ -34,7 +34,7 @@ def main() -> None: # wttr.in: GET /?format= loc_q = urllib.parse.quote(location, safe="") url = f"https://wttr.in/{loc_q}?format={urllib.parse.quote(fmt, safe='')}" - req = urllib.request.Request(url, headers={"User-Agent": "openclaw-skill-weather/1.0"}) + req = urllib.request.Request(url, headers={"User-Agent": "oclaw-skill-weather/1.0"}) try: with urllib.request.urlopen(req, timeout=10) as resp: text = resp.read().decode("utf-8", errors="ignore").strip() diff --git a/openclaw_runtime/skills_prompt.py b/runtime/skills_prompt.py similarity index 97% rename from openclaw_runtime/skills_prompt.py rename to runtime/skills_prompt.py index b8f598f1..cb776a8a 100644 --- a/openclaw_runtime/skills_prompt.py +++ b/runtime/skills_prompt.py @@ -4,16 +4,16 @@ import json import os from typing import Any -from oclaw.openclaw_runtime.skill_role_binding import ( +from oclaw.runtime.skill_role_binding import ( allowed_workspace_skill_names_for_role, should_apply_workspace_role_filter, ) -from oclaw.openclaw_runtime.skills import ( +from oclaw.runtime.skills import ( _allowed_tool_names_after_wire_policy, build_skill_manifest, discover_workspace_skill_manifests, ) -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.tools.base import ToolRegistry def _skill_runtime_enabled(store: Any) -> bool: diff --git a/openclaw_runtime/system_prompt.py b/runtime/system_prompt.py similarity index 62% rename from openclaw_runtime/system_prompt.py rename to runtime/system_prompt.py index 3e426597..ccd19506 100644 --- a/openclaw_runtime/system_prompt.py +++ b/runtime/system_prompt.py @@ -2,21 +2,21 @@ from __future__ import annotations from typing import Any -from oclaw.openclaw_runtime.memory_stage import render_memory_context_block -from oclaw.openclaw_runtime.project_context_prompt import build_project_context_block -from oclaw.openclaw_runtime.skills_prompt import build_skills_catalog_block -from oclaw.openclaw_runtime.types import OpenClawMemoryContext -from oclaw.prompts.loader import render_openclaw_prompt -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.memory_stage import render_memory_context_block +from oclaw.runtime.project_context_prompt import build_project_context_block +from oclaw.runtime.skills_prompt import build_skills_catalog_block +from oclaw.runtime.types import OclawMemoryContext +from oclaw.prompts.loader import render_runtime_prompt +from oclaw.runtime.tools.base import ToolRegistry -def build_openclaw_executor_system_prompt( +def build_executor_system_prompt( *, store: Any, tools: ToolRegistry | None, base_url: str, base_system: str, - memory_context: OpenClawMemoryContext | None, + memory_context: OclawMemoryContext | None, lang: str, workspace_dir: str | None = None, skill_binding_role: str | None = None, @@ -30,9 +30,9 @@ def build_openclaw_executor_system_prompt( project_block = build_project_context_block(store=store, workspace_dir=workspace_dir) if project_block: final_system = f"{final_system}\n\n{project_block}".strip() - mem_block = render_memory_context_block(memory_context or OpenClawMemoryContext()) + mem_block = render_memory_context_block(memory_context or OclawMemoryContext()) if mem_block: - final_system = render_openclaw_prompt( + final_system = render_runtime_prompt( "runtime/system_with_memory.md", variables={"system_prompt": final_system, "memory_context": mem_block}, strict=True, @@ -45,7 +45,7 @@ def build_openclaw_executor_system_prompt( skill_binding_role=skill_binding_role, ) if cat.strip(): - final_system = render_openclaw_prompt( + final_system = render_runtime_prompt( "runtime/system_with_skills.md", variables={"system_body": final_system, "skills_catalog": cat}, strict=True, @@ -53,4 +53,8 @@ def build_openclaw_executor_system_prompt( return final_system -__all__ = ["build_openclaw_executor_system_prompt"] +def build_oclaw_executor_system_prompt(**kwargs: Any) -> str: + return build_executor_system_prompt(**kwargs) + + +__all__ = ["build_executor_system_prompt", "build_oclaw_executor_system_prompt"] diff --git a/tools/__init__.py b/runtime/tools/__init__.py similarity index 89% rename from tools/__init__.py rename to runtime/tools/__init__.py index 6d2f64d7..3ee1aaeb 100644 --- a/tools/__init__.py +++ b/runtime/tools/__init__.py @@ -2,7 +2,7 @@ from __future__ import annotations -from oclaw.tools.catalog import ( +from oclaw.runtime.tools.catalog import ( TOOL_FACTORIES, default_registry, materialize_tool_specs, diff --git a/tools/base.py b/runtime/tools/base.py similarity index 100% rename from tools/base.py rename to runtime/tools/base.py diff --git a/tools/catalog.py b/runtime/tools/catalog.py similarity index 90% rename from tools/catalog.py rename to runtime/tools/catalog.py index 97358827..4310c479 100644 --- a/tools/catalog.py +++ b/runtime/tools/catalog.py @@ -7,14 +7,14 @@ import os from pathlib import Path from typing import Any -from oclaw.gateway.python_extensions_loader import build_python_extensions_registry +from oclaw.interfaces.gateway.python_extensions_loader import build_python_extensions_registry from oclaw.platform.config.paths import PROJECT_ROOT -from oclaw.tools.base import ToolRegistry, ToolSpec -from oclaw.tools.expert_registry import materialize_tools_for_expert -from oclaw.tools.mcp.adapter import materialize_mcp_tools_for_specialist -from oclaw.tools.public_registry import materialize_public_tools -from oclaw.tools.skills_runtime.materialize_skill_tools import materialize_executable_skill_tools -from oclaw.openclaw_runtime.skills import SkillSpec, materialize_skills_from_tool_specs +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.tools.expert_registry import materialize_tools_for_expert +from oclaw.runtime.tools.mcp.adapter import materialize_mcp_tools_for_specialist +from oclaw.runtime.tools.public_registry import materialize_public_tools +from oclaw.runtime.tools.skills_runtime.materialize_skill_tools import materialize_executable_skill_tools +from oclaw.runtime.skills import SkillSpec, materialize_skills_from_tool_specs logger = logging.getLogger(__name__) @@ -55,7 +55,7 @@ def _skill_management_tools(store: SqliteStore) -> list[ToolSpec]: name=str(args.get("name") or "").strip(), description=str(args.get("description") or "").strip(), body_markdown=str(args.get("body_markdown") or "").strip(), - metadata_openclaw=dict(args.get("metadata_openclaw") or {}) if isinstance(args.get("metadata_openclaw"), dict) else {}, + metadata_oclaw=dict(args.get("metadata_oclaw") or {}) if isinstance(args.get("metadata_oclaw"), dict) else {}, overwrite=bool(args.get("overwrite")), ) return {"ok": bool(out.ok), "name": out.name, "target_dir": out.target_dir, "detail": out.detail} @@ -65,7 +65,7 @@ def _skill_management_tools(store: SqliteStore) -> list[ToolSpec]: "name": str(args.get("name") or "").strip(), "description": str(args.get("description") or "").strip(), "body_markdown": str(args.get("body_markdown") or "").strip(), - "metadata_openclaw": dict(args.get("metadata_openclaw") or {}) if isinstance(args.get("metadata_openclaw"), dict) else {}, + "metadata_oclaw": dict(args.get("metadata_oclaw") or {}) if isinstance(args.get("metadata_oclaw"), dict) else {}, } out = auto_install_skill_from_payload(store=store, payload=payload) return {"ok": bool(out.ok), "name": out.name, "target_dir": out.target_dir, "detail": out.detail} @@ -80,7 +80,7 @@ def _skill_management_tools(store: SqliteStore) -> list[ToolSpec]: "name": {"type": "string"}, "description": {"type": "string"}, "body_markdown": {"type": "string"}, - "metadata_openclaw": {"type": "object"}, + "metadata_oclaw": {"type": "object"}, "overwrite": {"type": "boolean"}, }, "required": ["name", "description"], @@ -91,7 +91,7 @@ def _skill_management_tools(store: SqliteStore) -> list[ToolSpec]: description="Create a local oclaw skill package from template.", parameters=schema, handler=_create_skill_handler, - tags=frozenset({"skill", "openclaw", "builder"}), + tags=frozenset({"skill", "oclaw", "builder"}), risk_level="high", timeout_s=20.0, ), @@ -100,7 +100,7 @@ def _skill_management_tools(store: SqliteStore) -> list[ToolSpec]: description="Auto install a generated oclaw skill package.", parameters=schema, handler=_auto_install_skill_handler, - tags=frozenset({"skill", "openclaw", "installer"}), + tags=frozenset({"skill", "oclaw", "installer"}), risk_level="high", timeout_s=20.0, ), @@ -109,7 +109,7 @@ def _skill_management_tools(store: SqliteStore) -> list[ToolSpec]: description="List installed skills and status.", parameters={"type": "object", "properties": {}, "additionalProperties": False}, handler=_list_skills_handler, - tags=frozenset({"skill", "openclaw", "read"}), + tags=frozenset({"skill", "oclaw", "read"}), read_only=True, timeout_s=10.0, ), @@ -168,7 +168,7 @@ def materialize_tool_specs( except Exception as exc: logger.warning("expert tool load skipped: %s", exc) - # Load executable skills (declared via SKILL.md metadata.openclaw.runtime). + # Load executable skills (declared via SKILL.md metadata.oclaw.runtime). try: for spec in materialize_executable_skill_tools(store=store): if not isinstance(spec, ToolSpec): @@ -213,7 +213,7 @@ def materialize_tool_specs( app_cfg: dict[str, Any] = {} if store is not None: try: - app_cfg = dict(store.load_openclaw_config() or {}) + app_cfg = dict(store.load_oclaw_config() or {}) except Exception: app_cfg = {} plugin_registry = build_python_extensions_registry( diff --git a/evals/__init__.py b/runtime/tools/evals/__init__.py similarity index 100% rename from evals/__init__.py rename to runtime/tools/evals/__init__.py diff --git a/evals/assistant_runner.py b/runtime/tools/evals/assistant_runner.py similarity index 97% rename from evals/assistant_runner.py rename to runtime/tools/evals/assistant_runner.py index e3fea93d..95273e51 100644 --- a/evals/assistant_runner.py +++ b/runtime/tools/evals/assistant_runner.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from pathlib import Path from typing import Any -from oclaw.application.gateway import process_inbound_payload_usecase +from oclaw.runtime.application.gateway import process_inbound_payload_usecase from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore diff --git a/evals/runner.py b/runtime/tools/evals/runner.py similarity index 93% rename from evals/runner.py rename to runtime/tools/evals/runner.py index 7c26a73d..3deeffbb 100644 --- a/evals/runner.py +++ b/runtime/tools/evals/runner.py @@ -6,12 +6,12 @@ from pathlib import Path from dataclasses import dataclass from typing import Any -from oclaw.agents.factory import build_gateway_executor +from oclaw.runtime.agents.factory import build_gateway_executor from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.orchestration.evaluation import eval_summary +from oclaw.runtime.orchestration.evaluation import eval_summary from oclaw.platform.config.paths import db_path -from oclaw.openclaw_runtime.gateway import OpenClawGateway -from oclaw.openclaw_runtime.types import StandardMessage +from oclaw.runtime.gateway import OclawGateway +from oclaw.runtime.types import StandardMessage @dataclass(frozen=True) @@ -74,7 +74,7 @@ def run_eval( store = SqliteStore(db_path()) agent = build_gateway_executor(store) session = store.create_session("offline-eval") - gw = OpenClawGateway(store=store) + gw = OclawGateway(store=store) cases = _load_dataset(dataset_path) if limit is not None: cases = cases[: max(0, int(limit))] diff --git a/tools/expert_registry.py b/runtime/tools/expert_registry.py similarity index 97% rename from tools/expert_registry.py rename to runtime/tools/expert_registry.py index 282d6989..92bd811a 100644 --- a/tools/expert_registry.py +++ b/runtime/tools/expert_registry.py @@ -7,7 +7,7 @@ from collections.abc import Callable from pathlib import Path from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec logger = logging.getLogger(__name__) @@ -52,7 +52,7 @@ def discover_expert_tool_factories() -> dict[str, list[ToolFactory]]: for module_path in sorted(expert_dir.glob("*.py")): if module_path.name == "__init__.py": continue - mod_name = f"oclaw.tools.experts.{expert}.{module_path.stem}" + mod_name = f"oclaw.runtime.tools.experts.{expert}.{module_path.stem}" module = _load_module_from_path(module_path, mod_name) if module is None: continue @@ -145,7 +145,7 @@ def preview_expert_tools(expert: str | None) -> dict[str, Any]: for module_path in sorted(expert_dir.glob("*.py")): if module_path.name == "__init__.py": continue - mod_name = f"oclaw.tools.experts.{ex}.{module_path.stem}" + mod_name = f"oclaw.runtime.tools.experts.{ex}.{module_path.stem}" module = None try: module = _load_module_from_path(module_path, mod_name) diff --git a/tools/experts/__init__.py b/runtime/tools/experts/__init__.py similarity index 100% rename from tools/experts/__init__.py rename to runtime/tools/experts/__init__.py diff --git a/tools/experts/generalist/generalist_tools.py b/runtime/tools/experts/generalist/generalist_tools.py similarity index 92% rename from tools/experts/generalist/generalist_tools.py rename to runtime/tools/experts/generalist/generalist_tools.py index d47a4d56..ce2571a9 100644 --- a/tools/experts/generalist/generalist_tools.py +++ b/runtime/tools/experts/generalist/generalist_tools.py @@ -1,6 +1,6 @@ """通识专家工具清单。""" -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def system_info_tool() -> ToolSpec: diff --git a/tools/experts/generalist/geo_http.py b/runtime/tools/experts/generalist/geo_http.py similarity index 100% rename from tools/experts/generalist/geo_http.py rename to runtime/tools/experts/generalist/geo_http.py diff --git a/tools/experts/generalist/geo_info.py b/runtime/tools/experts/generalist/geo_info.py similarity index 98% rename from tools/experts/generalist/geo_info.py rename to runtime/tools/experts/generalist/geo_info.py index 1097d400..a6939f96 100644 --- a/tools/experts/generalist/geo_info.py +++ b/runtime/tools/experts/generalist/geo_info.py @@ -3,7 +3,7 @@ from __future__ import annotations import httpx from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec from .geo_http import DEFAULT_HTTP_TIMEOUT, ipapi_approximate_location, nominatim_reverse diff --git a/tools/experts/generalist/image_edit.py b/runtime/tools/experts/generalist/image_edit.py similarity index 99% rename from tools/experts/generalist/image_edit.py rename to runtime/tools/experts/generalist/image_edit.py index d60ef2b5..6debc100 100644 --- a/tools/experts/generalist/image_edit.py +++ b/runtime/tools/experts/generalist/image_edit.py @@ -8,7 +8,7 @@ from typing import Any from PIL import Image from oclaw.platform.files.attachment_assets import AttachmentAssetStore -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def image_edit_tool() -> ToolSpec: diff --git a/tools/experts/generalist/system_info.py b/runtime/tools/experts/generalist/system_info.py similarity index 95% rename from tools/experts/generalist/system_info.py rename to runtime/tools/experts/generalist/system_info.py index 4a3727d9..0e60ba8a 100644 --- a/tools/experts/generalist/system_info.py +++ b/runtime/tools/experts/generalist/system_info.py @@ -4,7 +4,7 @@ import datetime import time from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def system_info_tool() -> ToolSpec: diff --git a/tools/experts/generalist/tabular_query.py b/runtime/tools/experts/generalist/tabular_query.py similarity index 99% rename from tools/experts/generalist/tabular_query.py rename to runtime/tools/experts/generalist/tabular_query.py index a4fe6f8e..ebe48a77 100644 --- a/tools/experts/generalist/tabular_query.py +++ b/runtime/tools/experts/generalist/tabular_query.py @@ -8,7 +8,7 @@ from oclaw.platform.files.tabular_attachment_store import ( query_table, run_table_sql, ) -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def query_tabular_attachment_tool() -> ToolSpec: diff --git a/tools/experts/generalist/weather.py b/runtime/tools/experts/generalist/weather.py similarity index 99% rename from tools/experts/generalist/weather.py rename to runtime/tools/experts/generalist/weather.py index 4896962b..6f38554f 100644 --- a/tools/experts/generalist/weather.py +++ b/runtime/tools/experts/generalist/weather.py @@ -5,7 +5,7 @@ import re import unicodedata from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec from .geo_http import NOMINATIM_REQUEST_HEADERS, ipapi_approximate_location, nominatim_reverse _WEATHER_CODES: dict[int, str] = { diff --git a/tools/experts/generalist/web_search.py b/runtime/tools/experts/generalist/web_search.py similarity index 99% rename from tools/experts/generalist/web_search.py rename to runtime/tools/experts/generalist/web_search.py index 8f8184b2..a6e6b579 100644 --- a/tools/experts/generalist/web_search.py +++ b/runtime/tools/experts/generalist/web_search.py @@ -5,7 +5,7 @@ from __future__ import annotations from datetime import datetime, timezone from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec _MAX_SNIPPET = 800 _DDGS_TIMEOUT = 15 diff --git a/tools/experts/memory_curator/__init__.py b/runtime/tools/experts/memory_curator/__init__.py similarity index 100% rename from tools/experts/memory_curator/__init__.py rename to runtime/tools/experts/memory_curator/__init__.py diff --git a/tools/experts/memory_curator/wiki_curator_tools.py b/runtime/tools/experts/memory_curator/wiki_curator_tools.py similarity index 97% rename from tools/experts/memory_curator/wiki_curator_tools.py rename to runtime/tools/experts/memory_curator/wiki_curator_tools.py index 557981a8..4d4c2a23 100644 --- a/tools/experts/memory_curator/wiki_curator_tools.py +++ b/runtime/tools/experts/memory_curator/wiki_curator_tools.py @@ -6,7 +6,7 @@ from types import SimpleNamespace from typing import Any, Callable from oclaw.platform.config.paths import PROJECT_ROOT -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def _plugin_cfg() -> dict[str, Any]: @@ -26,7 +26,7 @@ def _plugin_cfg() -> dict[str, Any]: def _wiki_handlers() -> dict[str, Callable[[dict[str, Any]], dict[str, Any]]]: - api_path = (PROJECT_ROOT / "oclaw" / "extensions" / "memory-wiki" / "api.py").resolve() + api_path = (PROJECT_ROOT / "oclaw" / "runtime" / "extensions" / "memory-wiki" / "api.py").resolve() spec = importlib.util.spec_from_file_location("memory_curator_wiki_api", str(api_path)) if spec is None or spec.loader is None: return {} diff --git a/tools/experts/network_ops/config_diff.py b/runtime/tools/experts/network_ops/config_diff.py similarity index 97% rename from tools/experts/network_ops/config_diff.py rename to runtime/tools/experts/network_ops/config_diff.py index 3bd73063..8f63aabe 100644 --- a/tools/experts/network_ops/config_diff.py +++ b/runtime/tools/experts/network_ops/config_diff.py @@ -3,7 +3,7 @@ from __future__ import annotations import difflib from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def config_diff_tool() -> ToolSpec: diff --git a/tools/experts/network_ops/device_status.py b/runtime/tools/experts/network_ops/device_status.py similarity index 98% rename from tools/experts/network_ops/device_status.py rename to runtime/tools/experts/network_ops/device_status.py index a2cacaae..c73001cb 100644 --- a/tools/experts/network_ops/device_status.py +++ b/runtime/tools/experts/network_ops/device_status.py @@ -5,7 +5,7 @@ import subprocess import sys from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec _TTL_RE = re.compile(r"\bttl[= ]\d+\b", re.IGNORECASE) _AVG_WIN_RE = re.compile(r"Average\s*=\s*(\d+)\s*ms", re.IGNORECASE) diff --git a/tools/experts/network_ops/get_path.py b/runtime/tools/experts/network_ops/get_path.py similarity index 98% rename from tools/experts/network_ops/get_path.py rename to runtime/tools/experts/network_ops/get_path.py index 07f92d3d..f7de5251 100644 --- a/tools/experts/network_ops/get_path.py +++ b/runtime/tools/experts/network_ops/get_path.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import deque from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def _default_edges() -> list[tuple[str, str]]: diff --git a/tools/experts/network_ops/log_analysis.py b/runtime/tools/experts/network_ops/log_analysis.py similarity index 97% rename from tools/experts/network_ops/log_analysis.py rename to runtime/tools/experts/network_ops/log_analysis.py index b14ba153..8e1881f3 100644 --- a/tools/experts/network_ops/log_analysis.py +++ b/runtime/tools/experts/network_ops/log_analysis.py @@ -4,7 +4,7 @@ import re from collections import Counter from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec _LEVEL_RE = re.compile(r"\b(ERROR|WARN|WARNING|INFO|DEBUG)\b", re.IGNORECASE) diff --git a/tools/experts/network_ops/network_probe_tools.py b/runtime/tools/experts/network_ops/network_probe_tools.py similarity index 99% rename from tools/experts/network_ops/network_probe_tools.py rename to runtime/tools/experts/network_ops/network_probe_tools.py index 665d611f..2c3ce002 100644 --- a/tools/experts/network_ops/network_probe_tools.py +++ b/runtime/tools/experts/network_ops/network_probe_tools.py @@ -11,7 +11,7 @@ from typing import Any import httpx -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def dns_lookup_tool() -> ToolSpec: diff --git a/tools/experts/network_ops/ops_tools.py b/runtime/tools/experts/network_ops/ops_tools.py similarity index 97% rename from tools/experts/network_ops/ops_tools.py rename to runtime/tools/experts/network_ops/ops_tools.py index a517952a..5f28667e 100644 --- a/tools/experts/network_ops/ops_tools.py +++ b/runtime/tools/experts/network_ops/ops_tools.py @@ -1,6 +1,6 @@ """网络运维专家工具清单。""" -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def query_route_tool() -> ToolSpec: diff --git a/tools/experts/network_ops/query_route.py b/runtime/tools/experts/network_ops/query_route.py similarity index 97% rename from tools/experts/network_ops/query_route.py rename to runtime/tools/experts/network_ops/query_route.py index b67bf575..2e3ec0f5 100644 --- a/tools/experts/network_ops/query_route.py +++ b/runtime/tools/experts/network_ops/query_route.py @@ -3,7 +3,7 @@ from __future__ import annotations import ipaddress from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def _pick_route(ip: ipaddress.IPv4Address | ipaddress.IPv6Address) -> dict[str, Any]: diff --git a/tools/experts/network_ops/tabular_query.py b/runtime/tools/experts/network_ops/tabular_query.py similarity index 79% rename from tools/experts/network_ops/tabular_query.py rename to runtime/tools/experts/network_ops/tabular_query.py index 26ae0c46..40ade3b2 100644 --- a/tools/experts/network_ops/tabular_query.py +++ b/runtime/tools/experts/network_ops/tabular_query.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.tools.experts.generalist.tabular_query import ( +from oclaw.runtime.tools.experts.generalist.tabular_query import ( analyze_tabular_attachment_full_scan_tool, query_tabular_attachment_tool, run_tabular_sql_tool, diff --git a/ops/providers/__init__.py b/runtime/tools/experts/productivity/__init__.py similarity index 100% rename from ops/providers/__init__.py rename to runtime/tools/experts/productivity/__init__.py diff --git a/tools/experts/productivity/kb_tools.py b/runtime/tools/experts/productivity/kb_tools.py similarity index 97% rename from tools/experts/productivity/kb_tools.py rename to runtime/tools/experts/productivity/kb_tools.py index fe25fcc7..f1935e5f 100644 --- a/tools/experts/productivity/kb_tools.py +++ b/runtime/tools/experts/productivity/kb_tools.py @@ -6,7 +6,7 @@ from typing import Any from oclaw.platform.config.paths import db_path from oclaw.platform.embeddings.embedding_client import build_default_embedding_client from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def _chunk_id(source: str, text: str) -> str: @@ -64,7 +64,7 @@ def kb_search_tool() -> ToolSpec: if not tenant_id or not query: return {"ok": False, "error": "tenant_id and query are required"} store = SqliteStore(db_path()) - from oclaw.orchestration.memory import retrieve_context + from oclaw.runtime.orchestration.memory import retrieve_context rows = retrieve_context(store, query, limit=max(1, min(limit, 6))) filtered = [r for r in rows if str(r.get("source") or "").startswith(f"builtin:tenant:{tenant_id}:")] diff --git a/tools/experts/productivity/todo_tools.py b/runtime/tools/experts/productivity/todo_tools.py similarity index 99% rename from tools/experts/productivity/todo_tools.py rename to runtime/tools/experts/productivity/todo_tools.py index 4b998d82..c37901a8 100644 --- a/tools/experts/productivity/todo_tools.py +++ b/runtime/tools/experts/productivity/todo_tools.py @@ -4,7 +4,7 @@ from typing import Any from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def _require(s: str, name: str) -> str: diff --git a/tools/experts/productivity/__init__.py b/runtime/tools/experts/workspace/__init__.py similarity index 100% rename from tools/experts/productivity/__init__.py rename to runtime/tools/experts/workspace/__init__.py diff --git a/tools/experts/workspace/fs_tools.py b/runtime/tools/experts/workspace/fs_tools.py similarity index 97% rename from tools/experts/workspace/fs_tools.py rename to runtime/tools/experts/workspace/fs_tools.py index cc1a2ef6..63dafb85 100644 --- a/tools/experts/workspace/fs_tools.py +++ b/runtime/tools/experts/workspace/fs_tools.py @@ -3,8 +3,8 @@ from __future__ import annotations import hashlib from typing import Any -from oclaw.tools.base import ToolSpec -from oclaw.tools.experts.workspace.workspace_base import resolve_workspace_path +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.experts.workspace.workspace_base import resolve_workspace_path def read_file_tool() -> ToolSpec: diff --git a/tools/experts/workspace/git_tools.py b/runtime/tools/experts/workspace/git_tools.py similarity index 96% rename from tools/experts/workspace/git_tools.py rename to runtime/tools/experts/workspace/git_tools.py index b89af6da..8b905c6e 100644 --- a/tools/experts/workspace/git_tools.py +++ b/runtime/tools/experts/workspace/git_tools.py @@ -3,8 +3,8 @@ from __future__ import annotations import subprocess from typing import Any -from oclaw.tools.base import ToolSpec -from oclaw.tools.experts.workspace.workspace_base import resolve_workspace_path, truncate_text, sanitize_git_ref +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.experts.workspace.workspace_base import resolve_workspace_path, truncate_text, sanitize_git_ref def _git(command: str, *, cwd: str) -> dict[str, Any]: diff --git a/tools/experts/workspace/patch_tools.py b/runtime/tools/experts/workspace/patch_tools.py similarity index 93% rename from tools/experts/workspace/patch_tools.py rename to runtime/tools/experts/workspace/patch_tools.py index 0d90e7dd..cccfbbdc 100644 --- a/tools/experts/workspace/patch_tools.py +++ b/runtime/tools/experts/workspace/patch_tools.py @@ -3,8 +3,8 @@ from __future__ import annotations import hashlib from typing import Any -from oclaw.tools.base import ToolSpec -from oclaw.tools.experts.workspace.workspace_base import resolve_workspace_path +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.experts.workspace.workspace_base import resolve_workspace_path def apply_patch_tool() -> ToolSpec: diff --git a/tools/experts/workspace/search_tools.py b/runtime/tools/experts/workspace/search_tools.py similarity index 94% rename from tools/experts/workspace/search_tools.py rename to runtime/tools/experts/workspace/search_tools.py index dc33fbfc..8074868e 100644 --- a/tools/experts/workspace/search_tools.py +++ b/runtime/tools/experts/workspace/search_tools.py @@ -3,8 +3,8 @@ from __future__ import annotations import re from typing import Any -from oclaw.tools.base import ToolSpec -from oclaw.tools.experts.workspace.workspace_base import resolve_workspace_path +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.experts.workspace.workspace_base import resolve_workspace_path def grep_tool() -> ToolSpec: @@ -66,7 +66,7 @@ def index_workspace_tool() -> ToolSpec: try: from oclaw.platform.persistence.sqlite_store import SqliteStore from oclaw.platform.config.paths import db_path - from oclaw.indexing.workspace_indexer import index_workspace + from oclaw.runtime.tools.workspace_indexer import index_workspace store = SqliteStore(db_path()) st = index_workspace(store, max_files=max(1, min(max_files, 800))) diff --git a/tools/experts/workspace/shell_tools.py b/runtime/tools/experts/workspace/shell_tools.py similarity index 95% rename from tools/experts/workspace/shell_tools.py rename to runtime/tools/experts/workspace/shell_tools.py index f9e6838d..7672fb24 100644 --- a/tools/experts/workspace/shell_tools.py +++ b/runtime/tools/experts/workspace/shell_tools.py @@ -3,8 +3,8 @@ from __future__ import annotations import subprocess from typing import Any -from oclaw.tools.base import ToolSpec -from oclaw.tools.experts.workspace.workspace_base import resolve_workspace_path, truncate_text +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.experts.workspace.workspace_base import resolve_workspace_path, truncate_text def run_command_tool() -> ToolSpec: diff --git a/tools/experts/workspace/workspace_base.py b/runtime/tools/experts/workspace/workspace_base.py similarity index 100% rename from tools/experts/workspace/workspace_base.py rename to runtime/tools/experts/workspace/workspace_base.py diff --git a/tools/exposure_plan.py b/runtime/tools/exposure_plan.py similarity index 95% rename from tools/exposure_plan.py rename to runtime/tools/exposure_plan.py index 768508e4..cd60d076 100644 --- a/tools/exposure_plan.py +++ b/runtime/tools/exposure_plan.py @@ -12,11 +12,11 @@ from oclaw.platform.llm.tool_wire_policy import ( prepare_openai_tools_for_llm_api, wire_graduation_effective, ) -from oclaw.tools.base import ToolSpec -from oclaw.tools.catalog import _is_truthy -from oclaw.tools.expert_registry import materialize_tools_for_expert, preview_expert_tools -from oclaw.tools.mcp.adapter import materialize_mcp_tools_for_specialist -from oclaw.tools.public_registry import materialize_public_tools, preview_public_tools +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.catalog import _is_truthy +from oclaw.runtime.tools.expert_registry import materialize_tools_for_expert, preview_expert_tools +from oclaw.runtime.tools.mcp.adapter import materialize_mcp_tools_for_specialist +from oclaw.runtime.tools.public_registry import materialize_public_tools, preview_public_tools @dataclass(frozen=True) diff --git a/tools/mcp/__init__.py b/runtime/tools/mcp/__init__.py similarity index 100% rename from tools/mcp/__init__.py rename to runtime/tools/mcp/__init__.py diff --git a/tools/mcp/adapter.py b/runtime/tools/mcp/adapter.py similarity index 94% rename from tools/mcp/adapter.py rename to runtime/tools/mcp/adapter.py index 56122d6b..69b27907 100644 --- a/tools/mcp/adapter.py +++ b/runtime/tools/mcp/adapter.py @@ -5,11 +5,11 @@ import json import os from typing import Any -from oclaw.ops.mcp_env import mcp_env_allowlist_keys -from oclaw.openclaw_runtime.skills import SkillSpec, materialize_skills_from_tool_specs -from oclaw.tools.base import ToolSpec -from oclaw.tools.mcp.filesystem_argv import build_mcp_process_command -from oclaw.tools.mcp.runtime import McpProcessRuntime +from oclaw.runtime.operations.mcp_env import mcp_env_allowlist_keys +from oclaw.runtime.skills import SkillSpec, materialize_skills_from_tool_specs +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.mcp.filesystem_argv import build_mcp_process_command +from oclaw.runtime.tools.mcp.runtime import McpProcessRuntime @dataclass diff --git a/tools/mcp/filesystem_argv.py b/runtime/tools/mcp/filesystem_argv.py similarity index 100% rename from tools/mcp/filesystem_argv.py rename to runtime/tools/mcp/filesystem_argv.py diff --git a/tools/mcp/installer.py b/runtime/tools/mcp/installer.py similarity index 100% rename from tools/mcp/installer.py rename to runtime/tools/mcp/installer.py diff --git a/tools/mcp/manifest.py b/runtime/tools/mcp/manifest.py similarity index 100% rename from tools/mcp/manifest.py rename to runtime/tools/mcp/manifest.py diff --git a/tools/mcp/market.py b/runtime/tools/mcp/market.py similarity index 100% rename from tools/mcp/market.py rename to runtime/tools/mcp/market.py diff --git a/tools/mcp/registry.py b/runtime/tools/mcp/registry.py similarity index 95% rename from tools/mcp/registry.py rename to runtime/tools/mcp/registry.py index 27bd99b4..0dea490c 100644 --- a/tools/mcp/registry.py +++ b/runtime/tools/mcp/registry.py @@ -4,7 +4,7 @@ from dataclasses import asdict from typing import Any from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.mcp.manifest import McpServerManifest +from oclaw.runtime.tools.mcp.manifest import McpServerManifest class McpRegistry: diff --git a/tools/mcp/runtime.py b/runtime/tools/mcp/runtime.py similarity index 100% rename from tools/mcp/runtime.py rename to runtime/tools/mcp/runtime.py diff --git a/tools/plugin_loader.py b/runtime/tools/plugin_loader.py similarity index 98% rename from tools/plugin_loader.py rename to runtime/tools/plugin_loader.py index 0a18a86b..8767dac1 100644 --- a/tools/plugin_loader.py +++ b/runtime/tools/plugin_loader.py @@ -4,7 +4,7 @@ import logging from dataclasses import dataclass from typing import Any, Callable -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec logger = logging.getLogger(__name__) diff --git a/tools/public/__init__.py b/runtime/tools/public/__init__.py similarity index 100% rename from tools/public/__init__.py rename to runtime/tools/public/__init__.py diff --git a/tools/public/system_time_tool.py b/runtime/tools/public/system_time_tool.py similarity index 94% rename from tools/public/system_time_tool.py rename to runtime/tools/public/system_time_tool.py index 6b83423a..ca4f2274 100644 --- a/tools/public/system_time_tool.py +++ b/runtime/tools/public/system_time_tool.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import datetime, timezone from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec def system_time_tool() -> ToolSpec: diff --git a/tools/public_registry.py b/runtime/tools/public_registry.py similarity index 96% rename from tools/public_registry.py rename to runtime/tools/public_registry.py index b194fb45..ebea2076 100644 --- a/tools/public_registry.py +++ b/runtime/tools/public_registry.py @@ -7,7 +7,7 @@ from collections.abc import Callable from pathlib import Path from typing import Any -from oclaw.tools.base import ToolSpec +from oclaw.runtime.tools.base import ToolSpec logger = logging.getLogger(__name__) @@ -42,7 +42,7 @@ def discover_public_tool_factories() -> list[ToolFactory]: for module_path in sorted(_PUBLIC_ROOT.glob("*.py")): if module_path.name == "__init__.py": continue - mod_name = f"oclaw.tools.public.{module_path.stem}" + mod_name = f"oclaw.runtime.tools.public.{module_path.stem}" module = _load_module_from_path(module_path, mod_name) if module is None: continue @@ -92,7 +92,7 @@ def preview_public_tools() -> dict[str, Any]: for module_path in sorted(_PUBLIC_ROOT.glob("*.py")): if module_path.name == "__init__.py": continue - mod_name = f"oclaw.tools.public.{module_path.stem}" + mod_name = f"oclaw.runtime.tools.public.{module_path.stem}" module = None try: module = _load_module_from_path(module_path, mod_name) diff --git a/tools/skills/clawhub_client.py b/runtime/tools/skills/clawhub_client.py similarity index 100% rename from tools/skills/clawhub_client.py rename to runtime/tools/skills/clawhub_client.py diff --git a/tools/skills_runtime/__init__.py b/runtime/tools/skills_runtime/__init__.py similarity index 100% rename from tools/skills_runtime/__init__.py rename to runtime/tools/skills_runtime/__init__.py diff --git a/tools/skills_runtime/materialize_skill_tools.py b/runtime/tools/skills_runtime/materialize_skill_tools.py similarity index 84% rename from tools/skills_runtime/materialize_skill_tools.py rename to runtime/tools/skills_runtime/materialize_skill_tools.py index 95077042..8f999fbd 100644 --- a/tools/skills_runtime/materialize_skill_tools.py +++ b/runtime/tools/skills_runtime/materialize_skill_tools.py @@ -2,8 +2,8 @@ from __future__ import annotations from typing import Any -from oclaw.openclaw_runtime.skills import discover_workspace_skill_manifests -from oclaw.tools.base import ToolSpec +from oclaw.runtime.skills import discover_workspace_skill_manifests +from oclaw.runtime.tools.base import ToolSpec def materialize_executable_skill_tools(*, store: Any | None = None) -> list[ToolSpec]: @@ -27,7 +27,7 @@ def materialize_executable_skill_tools(*, store: Any | None = None) -> list[Tool continue def _handler(args: dict[str, Any], *, _manifest=m, _rt=rt) -> dict[str, Any]: - from oclaw.tools.skills_runtime.subprocess_exec import run_skill_runtime_entry + from oclaw.runtime.tools.skills_runtime.subprocess_exec import run_skill_runtime_entry return run_skill_runtime_entry( skill_name=str(_manifest.name or ""), @@ -42,7 +42,7 @@ def materialize_executable_skill_tools(*, store: Any | None = None) -> list[Tool description=str(m.description or ""), parameters=dict(schema), handler=_handler, - tags=frozenset({"skill", "openclaw", "runtime"}), + tags=frozenset({"skill", "oclaw", "runtime"}), risk_level="high", timeout_s=60.0, ) diff --git a/tools/skills_runtime/subprocess_exec.py b/runtime/tools/skills_runtime/subprocess_exec.py similarity index 99% rename from tools/skills_runtime/subprocess_exec.py rename to runtime/tools/skills_runtime/subprocess_exec.py index 5e581974..17d5dd7a 100644 --- a/tools/skills_runtime/subprocess_exec.py +++ b/runtime/tools/skills_runtime/subprocess_exec.py @@ -137,7 +137,7 @@ def _which(cmd: str) -> str | None: def _pick_shell() -> list[str] | None: # Prefer explicit env override, otherwise bash then sh. - raw = str(os.getenv("OPENCLAW_SHELL") or "").strip() + raw = str(os.getenv("OCLAW_SHELL") or "").strip() if raw: exe = _which(raw) return [exe] if exe else None diff --git a/tools/system_time_tool.py b/runtime/tools/system_time_tool.py similarity index 57% rename from tools/system_time_tool.py rename to runtime/tools/system_time_tool.py index 17786017..ccd8e0ac 100644 --- a/tools/system_time_tool.py +++ b/runtime/tools/system_time_tool.py @@ -1,7 +1,7 @@ from __future__ import annotations # Backward-compat import path. -from oclaw.tools.public.system_time_tool import system_time_tool +from oclaw.runtime.tools.public.system_time_tool import system_time_tool __all__ = ["system_time_tool"] diff --git a/tools/tool_validation.py b/runtime/tools/tool_validation.py similarity index 100% rename from tools/tool_validation.py rename to runtime/tools/tool_validation.py diff --git a/indexing/workspace_indexer.py b/runtime/tools/workspace_indexer.py similarity index 100% rename from indexing/workspace_indexer.py rename to runtime/tools/workspace_indexer.py diff --git a/openclaw_runtime/types.py b/runtime/types.py similarity index 98% rename from openclaw_runtime/types.py rename to runtime/types.py index 4f5a2d68..8fb1ab74 100644 --- a/openclaw_runtime/types.py +++ b/runtime/types.py @@ -52,7 +52,7 @@ def normalize_requested_specialist(raw: Any) -> SpecialistId: @dataclass(frozen=True) -class OpenClawSessionContext: +class OclawSessionContext: session_id: str tenant_id: str user_id: str @@ -64,7 +64,7 @@ class OpenClawSessionContext: @dataclass(frozen=True) -class OpenClawMemoryContext: +class OclawMemoryContext: short_term: tuple[str, ...] = () semantic_hits: tuple[dict[str, Any], ...] = () @@ -236,8 +236,8 @@ __all__ = [ "StandardMessage", "normalize_interaction_mode", "normalize_requested_specialist", - "OpenClawSessionContext", - "OpenClawMemoryContext", + "OclawSessionContext", + "OclawMemoryContext", "AttemptState", "RunState", "SharedFilePointer", diff --git a/openclaw_runtime/worker.py b/runtime/worker.py similarity index 91% rename from openclaw_runtime/worker.py rename to runtime/worker.py index 0350756f..2d1515fc 100644 --- a/openclaw_runtime/worker.py +++ b/runtime/worker.py @@ -6,11 +6,11 @@ import time import uuid from typing import Any -from oclaw.agents.factory import build_gateway_executor -from oclaw.openclaw_runtime.agent_core_run import AgentCoreRunInput, run_agent_core -from oclaw.openclaw_runtime.memory_stage import build_memory_context -from oclaw.openclaw_runtime.relay_pointer import build_acp_relay_result, validate_relay_share_envelope -from oclaw.openclaw_runtime.types import StandardMessage +from oclaw.runtime.agents.factory import build_gateway_executor +from oclaw.runtime.agent_core_run import AgentCoreRunInput, run_agent_core +from oclaw.runtime.memory_stage import build_memory_context +from oclaw.runtime.relay_pointer import build_acp_relay_result, validate_relay_share_envelope +from oclaw.runtime.types import StandardMessage _LOCK = threading.Lock() _THREAD: threading.Thread | None = None @@ -21,7 +21,7 @@ def ensure_worker_started(*, store: Any, poll_interval_s: float = 1.0) -> str: with _LOCK: if _THREAD and _THREAD.is_alive(): return _THREAD.name - wid = f"openclaw-worker-{uuid.uuid4().hex[:8]}" + wid = f"oclaw-worker-{uuid.uuid4().hex[:8]}" t = threading.Thread( target=_worker_loop, name=wid, @@ -37,7 +37,7 @@ def _worker_loop(*, store: Any, worker_id: str, poll_interval_s: float) -> None: while True: task = None try: - task = store.openclaw_task_claim(worker_id=worker_id, lease_seconds=90) + task = store.oclaw_task_claim(worker_id=worker_id, lease_seconds=90) except Exception: task = None if not task: @@ -89,7 +89,7 @@ def _worker_loop(*, store: Any, worker_id: str, poll_interval_s: float) -> None: "acp_parent_run_id": acp_parent_run_id, "acp_child_run_id": acp_child_run_id, } - store.openclaw_task_fail(task_id=task.id, error=str(env_err or "relay_envelope_invalid"), result=fail_result) + store.oclaw_task_fail(task_id=task.id, error=str(env_err or "relay_envelope_invalid"), result=fail_result) if trace_id: store.add_trace_event( session_id=session_id, @@ -173,8 +173,8 @@ def _worker_loop(*, store: Any, worker_id: str, poll_interval_s: float) -> None: max_tool_workers=max(1, min(max_tool_workers, 32)), max_attempts=2, memory_context=memory_ctx, - openclaw_task_id=str(task.id), - openclaw_worker_id=worker_id, + oclaw_task_id=str(task.id), + oclaw_worker_id=worker_id, ), ) base_result = { @@ -192,7 +192,7 @@ def _worker_loop(*, store: Any, worker_id: str, poll_interval_s: float) -> None: relay_envelope=relay_share_envelope, ) ) - store.openclaw_task_finish(task_id=task.id, result=base_result) + store.oclaw_task_finish(task_id=task.id, result=base_result) if trace_id: trace_payload = { "task_id": task.id, @@ -217,7 +217,7 @@ def _worker_loop(*, store: Any, worker_id: str, poll_interval_s: float) -> None: payload=trace_payload, ) except Exception as exc: - store.openclaw_task_fail(task_id=task.id, error=str(exc), result={"ok": False}) + store.oclaw_task_fail(task_id=task.id, error=str(exc), result={"ok": False}) try: if trace_id: store.add_trace_event( diff --git a/wiki_worker/__init__.py b/runtime/workers/wiki/__init__.py similarity index 100% rename from wiki_worker/__init__.py rename to runtime/workers/wiki/__init__.py diff --git a/wiki_worker/main.py b/runtime/workers/wiki/main.py similarity index 95% rename from wiki_worker/main.py rename to runtime/workers/wiki/main.py index 69fe40eb..38a166db 100644 --- a/wiki_worker/main.py +++ b/runtime/workers/wiki/main.py @@ -11,7 +11,7 @@ from types import SimpleNamespace from typing import Any from oclaw.platform.config.paths import PROJECT_ROOT, db_path -from oclaw.platform.persistence.sqlite_store import OpenClawTask, SqliteStore +from oclaw.platform.persistence.sqlite_store import OclawTask, SqliteStore def _load_oclaw_config() -> dict[str, Any]: @@ -97,7 +97,7 @@ def _tool_handlers(plugin_cfg: dict[str, Any]) -> dict[str, Any]: return out -def _payload(task: OpenClawTask) -> dict[str, Any]: +def _payload(task: OclawTask) -> dict[str, Any]: try: obj = json.loads(task.payload or "{}") return obj if isinstance(obj, dict) else {} @@ -157,7 +157,7 @@ def _save_merge_state(wiki_root: Path, state: dict[str, Any]) -> None: ) -def _capture_after_turn(*, wiki_root: Path, task: OpenClawTask, payload: dict[str, Any]) -> dict[str, Any]: +def _capture_after_turn(*, wiki_root: Path, task: OclawTask, payload: dict[str, Any]) -> dict[str, Any]: turn_uuid = str(payload.get("turn_uuid") or "").strip() user_text = str(payload.get("user_text") or "").strip() assistant_text = str(payload.get("assistant_text") or "").strip() @@ -292,7 +292,7 @@ def _lint_report(*, handlers: dict[str, Any]) -> dict[str, Any]: return {"ok": True, "issue_count": len(issues)} -def _process_task(*, task: OpenClawTask, handlers: dict[str, Any], plugin_cfg: dict[str, Any]) -> dict[str, Any]: +def _process_task(*, task: OclawTask, handlers: dict[str, Any], plugin_cfg: dict[str, Any]) -> dict[str, Any]: wiki_root = _resolve_wiki_root(plugin_cfg) wiki_root.mkdir(parents=True, exist_ok=True) payload = _payload(task) @@ -317,15 +317,15 @@ def run_worker() -> int: if not {"wiki_apply", "wiki_lint"}.issubset(set(handlers.keys())): time.sleep(interval_s) continue - task = store.openclaw_task_claim(worker_id=worker_id, lease_seconds=120, task_type="wiki_capture") + task = store.oclaw_task_claim(worker_id=worker_id, lease_seconds=120, task_type="wiki_capture") if task is None: time.sleep(interval_s) continue try: result = _process_task(task=task, handlers=handlers, plugin_cfg=plugin_cfg) - store.openclaw_task_finish(task_id=task.id, result=result) + store.oclaw_task_finish(task_id=task.id, result=result) except Exception as exc: - store.openclaw_task_fail(task_id=task.id, error=f"{type(exc).__name__}: {exc}", result={"ok": False}) + store.oclaw_task_fail(task_id=task.id, error=f"{type(exc).__name__}: {exc}", result={"ok": False}) time.sleep(interval_s) diff --git a/scripts/bootstrap_venv.ps1 b/scripts/bootstrap_venv.ps1 index d1f92b48..70604a14 100644 --- a/scripts/bootstrap_venv.ps1 +++ b/scripts/bootstrap_venv.ps1 @@ -1,51 +1,11 @@ -param( - [string]$Python = "python", - [switch]$Recreate = $false -) +param() $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\bootstrap_venv.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -function Fail([string]$msg) { - Write-Host "[ERROR] $msg" -ForegroundColor Red - exit 1 -} +& $real @args -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -Set-Location $repoRoot - -$venvDir = Join-Path $repoRoot "oclaw/.venv" -$venvPython = Join-Path $venvDir "Scripts/python.exe" - -$py = Get-Command $Python -ErrorAction SilentlyContinue -if (-not $py) { - Fail "python not found in PATH. Please install Python 3.10+ and retry." -} - -if ($Recreate -and (Test-Path $venvDir)) { - Write-Step "Removing existing venv: $venvDir" - Remove-Item -Recurse -Force $venvDir -} - -if (-not (Test-Path $venvPython)) { - Write-Step "Creating venv: $venvDir" - & $Python -m venv $venvDir -} - -if (-not (Test-Path $venvPython)) { - Fail "venv python not found after creation: $venvPython" -} - -Write-Step "Upgrading pip" -& $venvPython -m pip install -U pip - -Write-Step "Installing requirements" -& $venvPython -m pip install -r "requirements.txt" - -Write-Host "" -Write-Host "OK. Next:" -ForegroundColor Green -Write-Host " powershell -ExecutionPolicy Bypass -File .\\oclaw\\scripts\\start_gateway.ps1" -Write-Host "" diff --git a/scripts/start_all.ps1 b/scripts/start_all.ps1 index ad67fc0f..8c02e47a 100644 --- a/scripts/start_all.ps1 +++ b/scripts/start_all.ps1 @@ -5,45 +5,16 @@ param( [switch]$Background = $false, [switch]$WithWeixin = $false, [switch]$WithWikiWorker = $false, - [string]$WeixinChannelId = "openclaw-weixin", + [string]$WeixinChannelId = "oclaw-weixin", [string]$WeixinGatewayBaseUrl = "" ) $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\start_all.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -Set-Location $repoRoot - -Write-Step "Starting gateway + desktop" - -& "$PSScriptRoot/start_gateway.ps1" -BindHost $BindHost -Port $Port -SkipInstall:$SkipInstall -Background:$Background - -# When gateway is started in foreground, the call above blocks; desktop won't start. -if (-not $Background) { - Write-Host "" - Write-Host "[INFO] Gateway started in foreground and will block. Use -Background to start both." -ForegroundColor Yellow - exit 0 -} - -& "$PSScriptRoot/start_desktop.ps1" -SkipInstall:$SkipInstall -Background - -if ($WithWeixin) { - $gwBase = ($WeixinGatewayBaseUrl | ForEach-Object { "$_".Trim() }) - if (-not $gwBase) { - $gwBase = "http://$BindHost`:$Port" - } - Write-Step "Starting weixin sidecar" - & "$PSScriptRoot/weixin_start.ps1" -ChannelId $WeixinChannelId -GatewayBaseUrl $gwBase -} -if ($WithWikiWorker) { - Write-Step "Starting wiki worker" - & "$PSScriptRoot/start_wiki_worker.ps1" -Background -} - -Write-Host "" -Write-Host "All started." -ForegroundColor Green +& $real -BindHost $BindHost -Port $Port -SkipInstall:$SkipInstall -Background:$Background -WithWeixin:$WithWeixin -WithWikiWorker:$WithWikiWorker -WeixinChannelId $WeixinChannelId -WeixinGatewayBaseUrl $WeixinGatewayBaseUrl diff --git a/scripts/start_desktop.ps1 b/scripts/start_desktop.ps1 index d921c37a..07b1581d 100644 --- a/scripts/start_desktop.ps1 +++ b/scripts/start_desktop.ps1 @@ -1,49 +1,14 @@ param( [switch]$SkipInstall = $false, - [switch]$Background = $false, - [switch]$KeepExistingGateway = $false + [switch]$Background = $false ) $ErrorActionPreference = "Stop" -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -$desktopDir = Join-Path $repoRoot "oclaw\\desktop" -$runDir = Join-Path $PSScriptRoot ".run" -New-Item -ItemType Directory -Force -Path $runDir | Out-Null -$pidFile = Join-Path $runDir "desktop.pid" -$outLog = Join-Path $runDir "desktop.out.log" -$errLog = Join-Path $runDir "desktop.err.log" -if (-not (Test-Path $desktopDir)) { - Write-Host "[ERROR] desktop directory not found: $desktopDir" -ForegroundColor Red - exit 1 +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\start_desktop.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -Set-Location $desktopDir +& $real -SkipInstall:$SkipInstall -Background:$Background -if (-not $KeepExistingGateway) { - Write-Host "==> Cleaning previous gateway listener" -ForegroundColor Cyan - try { - & "$PSScriptRoot/stop_gateway.ps1" -Force - } catch { - Write-Host "[WARN] Failed to pre-stop gateway: $($_.Exception.Message)" -ForegroundColor Yellow - } -} - -if (-not $SkipInstall) { - Write-Host "==> Installing desktop dependencies" -ForegroundColor Cyan - npm install -} - -Write-Host "==> Launching desktop app" -ForegroundColor Cyan -if ($Background) { - # Avoid launching npm.ps1 directly (can be file-associated and open in Notepad on some setups). - # Use hidden cmd and redirect logs to avoid black console window popup. - $p = Start-Process -FilePath "cmd.exe" -ArgumentList @("/c","npm.cmd","run","dev") -WorkingDirectory $desktopDir -PassThru -WindowStyle Hidden -RedirectStandardOutput $outLog -RedirectStandardError $errLog - Set-Content -Path $pidFile -Value "$($p.Id)" -Encoding ascii - Write-Host "desktop.pid = $pidFile" -ForegroundColor DarkGray - Write-Host "desktop.out = $outLog" -ForegroundColor DarkGray - Write-Host "desktop.err = $errLog" -ForegroundColor DarkGray - Write-Host "PID = $($p.Id)" -ForegroundColor Green - exit 0 -} -& "npm.cmd" "run" "dev" diff --git a/scripts/start_gateway.ps1 b/scripts/start_gateway.ps1 index 475f8a28..31870c9b 100644 --- a/scripts/start_gateway.ps1 +++ b/scripts/start_gateway.ps1 @@ -7,66 +7,10 @@ param( $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\start_gateway.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -function Fail([string]$msg) { - Write-Host "[ERROR] $msg" -ForegroundColor Red - exit 1 -} - -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -Set-Location $repoRoot - -$runDir = Join-Path $PSScriptRoot ".run" -New-Item -ItemType Directory -Force -Path $runDir | Out-Null -$pidFile = Join-Path $runDir "gateway.pid" - -Write-Step "Project root: $repoRoot" - -$venvPython = Join-Path $repoRoot "oclaw/.venv/Scripts/python.exe" -if (Test-Path $venvPython) { - Write-Step "Using venv python: $venvPython" - $pythonExe = $venvPython -} else { - $pythonCmd = Get-Command python -ErrorAction SilentlyContinue - if (-not $pythonCmd) { - Fail "python not found in PATH. Please install Python and retry." - } - Write-Step "Python detected: $($pythonCmd.Source)" - $pythonExe = "python" -} - -if (-not $SkipInstall) { - Write-Step "Installing dependencies (pip install -r requirements.txt)" - & $pythonExe -m pip install -r "requirements.txt" -} else { - Write-Step "Skip dependency install" -} - -Write-Host "" -Write-Step "Gateway URL" -Write-Host "Admin: http://$BindHost`:$Port/admin" -Write-Host "Chat: http://$BindHost`:$Port/chat" -Write-Host "WS: ws://$BindHost`:$Port/ws" -Write-Host "" - -$configPath = Join-Path $repoRoot "oclaw\oclaw.json" -if (Test-Path $configPath) { - $env:OPENCLAW_CONFIG_PATH = $configPath - Write-Step "Using OPENCLAW_CONFIG_PATH: $configPath" -} - -if ($Background) { - Write-Step "Starting gateway in background" - $p = Start-Process -FilePath $pythonExe -ArgumentList @("-m","oclaw.ops","gateway","start","--host",$BindHost,"--port",$Port) -WorkingDirectory $repoRoot -PassThru -WindowStyle Hidden - Set-Content -Path $pidFile -Value "$($p.Id)" -Encoding ascii - Write-Host "gateway.pid = $pidFile" -ForegroundColor DarkGray - Write-Host "PID = $($p.Id)" -ForegroundColor Green - exit 0 -} - -Write-Step "Starting gateway (foreground)" -& $pythonExe -m oclaw.ops gateway start --host $BindHost --port $Port +& $real -BindHost $BindHost -Port $Port -SkipInstall:$SkipInstall -Background:$Background diff --git a/scripts/start_ops.ps1 b/scripts/start_ops.ps1 index 16d2b559..d6762077 100644 --- a/scripts/start_ops.ps1 +++ b/scripts/start_ops.ps1 @@ -1,70 +1,16 @@ param( [string]$Channel = "wecom", [switch]$SkipInstall = $false, - [switch]$SkipConfigHint = $false + [switch]$SkipConfigHint = $false, + [switch]$Background = $false ) $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\start_ops.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -function Fail([string]$msg) { - Write-Host "[ERROR] $msg" -ForegroundColor Red - exit 1 -} - -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -Set-Location $repoRoot - -Write-Step "Project root: $repoRoot" - -$venvPython = Join-Path $repoRoot "oclaw/.venv/Scripts/python.exe" -if (Test-Path $venvPython) { - Write-Step "Using venv python: $venvPython" - $pythonExe = $venvPython -} else { - $pythonCmd = Get-Command python -ErrorAction SilentlyContinue - if (-not $pythonCmd) { - Fail "python not found in PATH. Please install Python 3.10+ and retry." - } - Write-Step "Python detected: $($pythonCmd.Source)" - $pythonExe = "python" -} - -if (-not $SkipInstall) { - Write-Step "Installing dependencies (pip install -r requirements.txt)" - & $pythonExe -m pip install -r "requirements.txt" -} else { - Write-Step "Skip dependency install" -} - -if (-not $SkipConfigHint) { - Write-Step "Current WeCom status" - try { - python -m oclaw.ops channel wecom status - } catch { - Write-Host "[WARN] Unable to read WeCom status yet." -ForegroundColor Yellow - } - Write-Host "" - Write-Host "If WeCom is not configured yet, run this first:" -ForegroundColor Yellow - Write-Host " python -m oclaw.ops channel wecom config --help" - Write-Host "" -} - -Write-Step "Stopping previous stack (safe if not running)" -& $pythonExe -m oclaw.ops stack down - -Write-Step "Starting stack: python -m oclaw.ops stack up --channel $Channel" -& $pythonExe -m oclaw.ops stack up --channel $Channel - -Write-Host "" -Write-Host "Started successfully." -ForegroundColor Green -Write-Host "Admin: http://127.0.0.1:8787/admin" -Write-Host "Chat: http://127.0.0.1:8787/chat" -Write-Host "" -Write-Host "Useful commands:" -Write-Host " python -m oclaw.ops stack status" -Write-Host " python -m oclaw.ops stack down" +& $real -Channel $Channel -SkipInstall:$SkipInstall -SkipConfigHint:$SkipConfigHint -Background:$Background diff --git a/scripts/start_ops.sh b/scripts/start_ops.sh index 31b18b4a..f6595ebe 100644 --- a/scripts/start_ops.sh +++ b/scripts/start_ops.sh @@ -1,81 +1,11 @@ #!/usr/bin/env bash set -euo pipefail -CHANNEL="wecom" -SKIP_INSTALL=0 -SKIP_CONFIG_HINT=0 - -while [[ $# -gt 0 ]]; do - case "$1" in - --no-ui) - # Kept for compatibility; stack no longer starts Streamlit. - shift - ;; - --channel) - CHANNEL="${2:-wecom}" - shift 2 - ;; - --skip-install) - SKIP_INSTALL=1 - shift - ;; - --skip-config-hint) - SKIP_CONFIG_HINT=1 - shift - ;; - *) - echo "Unknown arg: $1" - echo "Usage: $0 [--no-ui] [--channel wecom] [--skip-install] [--skip-config-hint]" - echo "Note: --no-ui is legacy; stack does not start Streamlit. Chat: http://127.0.0.1:8787/chat" - exit 1 - ;; - esac -done - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$ROOT_DIR" - -step() { printf '\n==> %s\n' "$1"; } - -if ! command -v python >/dev/null 2>&1; then - echo "[ERROR] python not found in PATH." >&2 +REAL_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../runtime/operations/scripts" && pwd)/start_ops.sh" +if [[ ! -f "$REAL_PATH" ]]; then + echo "[ERROR] Forward script target not found: $REAL_PATH" >&2 exit 1 fi -step "Project root: $ROOT_DIR" -step "Python: $(command -v python)" - -if [[ "$SKIP_INSTALL" -eq 0 ]]; then - step "Installing dependencies" - python -m pip install -r requirements.txt -else - step "Skip dependency install" -fi - -if [[ "$SKIP_CONFIG_HINT" -eq 0 ]]; then - step "Current WeCom status" - python -m oclaw.ops channel wecom status || true - cat <<'EOF' - -If WeCom is not configured yet, run: - python -m oclaw.ops channel wecom config --help -EOF -fi - -step "Stopping previous stack" -python -m oclaw.ops stack down || true - -step "Starting stack" -python -m oclaw.ops stack up --channel "$CHANNEL" - -cat <<'EOF' - -Started successfully. -Admin: http://127.0.0.1:8787/admin -Chat: http://127.0.0.1:8787/chat - -Useful commands: - python -m oclaw.ops stack status - python -m oclaw.ops stack down -EOF +exec bash "$REAL_PATH" "$@" diff --git a/scripts/start_web.ps1 b/scripts/start_web.ps1 index 0f5ed4d2..11811779 100644 --- a/scripts/start_web.ps1 +++ b/scripts/start_web.ps1 @@ -1,10 +1,11 @@ -param( - [string]$BindHost = "127.0.0.1", - [int]$Port = 8787, - [switch]$SkipInstall = $false, - [switch]$Background = $false -) +param() -# 网页端(/admin、/chat)由网关进程提供,这里只是一个语义化别名。 -& "$PSScriptRoot/start_gateway.ps1" -BindHost $BindHost -Port $Port -SkipInstall:$SkipInstall -Background:$Background +$ErrorActionPreference = "Stop" + +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\start_web.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" +} + +& $real @args diff --git a/scripts/start_wiki_worker.ps1 b/scripts/start_wiki_worker.ps1 index d9e3f8b7..7a9695e8 100644 --- a/scripts/start_wiki_worker.ps1 +++ b/scripts/start_wiki_worker.ps1 @@ -1,37 +1,13 @@ param( - [switch]$Background = $true + [switch]$Background = $false ) $ErrorActionPreference = "Stop" -function Resolve-RepoRoot { - $here = Split-Path -Parent $PSCommandPath - return (Resolve-Path (Join-Path $here "..")).Path +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\start_wiki_worker.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$repoRoot = Resolve-RepoRoot -$runDir = Join-Path $PSScriptRoot ".run" -$null = New-Item -ItemType Directory -Force -Path $runDir -ErrorAction SilentlyContinue -$pidFile = Join-Path $runDir "wiki_worker.pid" -$outLog = Join-Path $runDir "wiki_worker.out.log" -$errLog = Join-Path $runDir "wiki_worker.err.log" +& $real -Background:$Background -$venvPython = Join-Path $repoRoot "oclaw/.venv/Scripts/python.exe" -if (Test-Path $venvPython) { - $pythonExe = $venvPython -} else { - $pythonCmd = Get-Command python -ErrorAction SilentlyContinue - if (-not $pythonCmd) { - throw "python not found in PATH" - } - $pythonExe = "python" -} - -if (-not $Background) { - & $pythonExe -m src.wiki_worker.main - exit 0 -} - -$p = Start-Process -FilePath $pythonExe -ArgumentList @("-m", "src.wiki_worker.main") -WorkingDirectory $repoRoot -PassThru -WindowStyle Hidden -RedirectStandardOutput $outLog -RedirectStandardError $errLog -Set-Content -Path $pidFile -Value $p.Id -Encoding ascii -Write-Host "[ok] started wiki worker pid=$($p.Id) out=$outLog err=$errLog" diff --git a/scripts/status_all.ps1 b/scripts/status_all.ps1 index 27eeca92..f04da674 100644 --- a/scripts/status_all.ps1 +++ b/scripts/status_all.ps1 @@ -1,140 +1,11 @@ -param( - [string]$BindHost = "127.0.0.1", - [int]$Port = 8787, - [switch]$WithWeixin = $false, - [switch]$WithWikiWorker = $false, - [string]$WeixinChannelId = "openclaw-weixin" -) +param() $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\status_all.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -function Warn([string]$msg) { - Write-Host "[WARN] $msg" -ForegroundColor Yellow -} - -$runDir = Join-Path $PSScriptRoot ".run" -$null = New-Item -ItemType Directory -Force -Path $runDir -ErrorAction SilentlyContinue -$gwPidFile = Join-Path $runDir "gateway.pid" -$desktopPidFile = Join-Path $runDir "desktop.pid" - -Write-Step "URLs" -Write-Host "Admin: http://$BindHost`:$Port/admin" -Write-Host "Chat: http://$BindHost`:$Port/chat" -Write-Host "WS: ws://$BindHost`:$Port/ws" -Write-Host "" - -Write-Step "PID files" -if (Test-Path $gwPidFile) { - $gwPid = (Get-Content $gwPidFile -ErrorAction SilentlyContinue | Select-Object -First 1) - Write-Host "gateway.pid: $gwPidFile => $gwPid" -} else { - Warn "gateway.pid not found: $gwPidFile" -} -if (Test-Path $desktopPidFile) { - $deskPid = (Get-Content $desktopPidFile -ErrorAction SilentlyContinue | Select-Object -First 1) - Write-Host "desktop.pid: $desktopPidFile => $deskPid" -} else { - Warn "desktop.pid not found: $desktopPidFile" -} -Write-Host "" - -Write-Step "Port listener" -try { - $conns = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue - if (-not $conns) { - Warn "No listener on port $Port" - exit 0 - } - $pids = $conns | Select-Object -ExpandProperty OwningProcess -Unique - Write-Host "Listening on port $Port; OwningProcess: $($pids -join ', ')" - - # Self-heal: if we have a single owning pid and no gateway.pid, write it back. - if (-not (Test-Path $gwPidFile)) { - $pidList = @() - foreach ($x in @($pids)) { - $n = 0 - if ([int]::TryParse([string]$x, [ref]$n) -and $n -gt 0) { - $pidList += $n - } - } - if ($pidList.Count -eq 1) { - $procId = [int]$pidList[0] - try { - $null = Get-Process -Id $procId -ErrorAction Stop - Set-Content -Path $gwPidFile -Value "$procId" -Encoding ascii - Write-Host "Wrote gateway.pid => $procId" -ForegroundColor Green - } catch { - Warn "Cannot verify process for PID=$procId; not writing pid file." - } - } else { - # Heuristic: try to pick the gateway python/uvicorn process. - $candidates = @() - foreach ($p in $pidList) { - try { - $proc = Get-Process -Id $p -ErrorAction Stop - $name = [string]$proc.ProcessName - } catch { - continue - } - $cmd = "" - try { - $wmi = Get-CimInstance Win32_Process -Filter "ProcessId=$p" -ErrorAction SilentlyContinue - if ($wmi -and $wmi.CommandLine) { $cmd = [string]$wmi.CommandLine } - } catch { - $cmd = "" - } - if ($cmd) { - $low = ([string]$cmd).ToLowerInvariant() - } else { - $low = "" - } - $score = 0 - if ($name -match "python") { $score += 2 } - if ($low -match "uvicorn") { $score += 2 } - if ($low -match "src\.ops") { $score += 2 } - if ($low -match "gateway\s+start") { $score += 2 } - if ($low -match "fastapi_app") { $score += 1 } - $candidates += [pscustomobject]@{ Pid = [int]$p; Name = $name; Score = [int]$score; Cmd = $cmd } - } - $best = $candidates | Sort-Object -Property Score -Descending | Select-Object -First 1 - if ($best -and $best.Score -ge 2) { - try { - $null = Get-Process -Id $best.Pid -ErrorAction Stop - Set-Content -Path $gwPidFile -Value "$($best.Pid)" -Encoding ascii - Write-Host "Wrote gateway.pid => $($best.Pid) ($($best.Name))" -ForegroundColor Green - } catch { - Warn "Cannot verify process for PID=$($best.Pid); not writing pid file." - } - } else { - Warn "Multiple owning processes for port $Port; cannot confidently pick gateway PID." - } - } - } -} catch { - Warn "Get-NetTCPConnection failed: $($_.Exception.Message)" -} - -if ($WithWeixin) { - Write-Host "" - Write-Step "Weixin sidecar" - try { - & "$PSScriptRoot/weixin_status.ps1" -ChannelId $WeixinChannelId - } catch { - Warn "weixin status failed: $($_.Exception.Message)" - } -} - -if ($WithWikiWorker) { - Write-Host "" - Write-Step "Wiki worker" - try { - & "$PSScriptRoot/status_wiki_worker.ps1" - } catch { - Warn "wiki worker status failed: $($_.Exception.Message)" - } -} +& $real @args diff --git a/scripts/status_ops.ps1 b/scripts/status_ops.ps1 index ba8df6fd..8a2501c7 100644 --- a/scripts/status_ops.ps1 +++ b/scripts/status_ops.ps1 @@ -1,21 +1,11 @@ +param() + $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\status_ops.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -Set-Location $repoRoot - -Write-Step "Project root: $repoRoot" -Write-Step "Stack status" -python -m oclaw.ops stack status - -Write-Host "" -Write-Step "WeCom status" -python -m oclaw.ops channel wecom status - -Write-Host "" -Write-Host "Admin: http://127.0.0.1:8787/admin" -Write-Host "Chat: http://127.0.0.1:8787/chat" +& $real @args diff --git a/scripts/status_ops.sh b/scripts/status_ops.sh index 031c7285..a3b51801 100644 --- a/scripts/status_ops.sh +++ b/scripts/status_ops.sh @@ -1,19 +1,11 @@ #!/usr/bin/env bash set -euo pipefail -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$ROOT_DIR" +REAL_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../runtime/operations/scripts" && pwd)/status_ops.sh" +if [[ ! -f "$REAL_PATH" ]]; then + echo "[ERROR] Forward script target not found: $REAL_PATH" >&2 + exit 1 +fi -printf '==> Project root: %s\n' "$ROOT_DIR" -printf '==> Stack status\n' -python -m oclaw.ops stack status - -printf '\n==> WeCom status\n' -python -m oclaw.ops channel wecom status - -cat <<'EOF' - -Admin: http://127.0.0.1:8787/admin -Chat: http://127.0.0.1:8787/chat -EOF +exec bash "$REAL_PATH" "$@" diff --git a/scripts/status_wiki_worker.ps1 b/scripts/status_wiki_worker.ps1 index ce9bcce7..9385d95a 100644 --- a/scripts/status_wiki_worker.ps1 +++ b/scripts/status_wiki_worker.ps1 @@ -1,23 +1,11 @@ param() $ErrorActionPreference = "Stop" -$runDir = Join-Path $PSScriptRoot ".run" -$pidFile = Join-Path $runDir "wiki_worker.pid" -if (-not (Test-Path $pidFile)) { - Write-Host "status=stopped" - exit 0 +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\status_wiki_worker.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) -if (-not $procId) { - Write-Host "status=stopped" - exit 0 -} +& $real @args -try { - Get-Process -Id $procId -ErrorAction Stop | Out-Null - Write-Host "status=running pid=$procId" -} catch { - Write-Host "status=stale_pid pid=$procId" -} diff --git a/scripts/stop_all.ps1 b/scripts/stop_all.ps1 index 91429003..3fd7c8b8 100644 --- a/scripts/stop_all.ps1 +++ b/scripts/stop_all.ps1 @@ -1,48 +1,11 @@ -param( - [int]$Port = 8787, - [switch]$Force = $false, - [switch]$WithWeixin = $false, - [switch]$WithWikiWorker = $false, - [string]$WeixinChannelId = "openclaw-weixin" -) +param() $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\stop_all.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -Write-Step "Stopping desktop" -try { - & "$PSScriptRoot/stop_desktop.ps1" -Force:$Force -} catch { - if (-not $Force) { throw } -} - -Write-Step "Stopping gateway/web" -try { - & "$PSScriptRoot/stop_gateway.ps1" -Port $Port -Force:$Force -} catch { - if (-not $Force) { throw } -} - -if ($WithWeixin) { - Write-Step "Stopping weixin sidecar" - try { - & "$PSScriptRoot/weixin_stop.ps1" -ChannelId $WeixinChannelId -Force:$Force - } catch { - if (-not $Force) { throw } - } -} -if ($WithWikiWorker) { - Write-Step "Stopping wiki worker" - try { - & "$PSScriptRoot/stop_wiki_worker.ps1" -Force:$Force - } catch { - if (-not $Force) { throw } - } -} - -Write-Host "" -Write-Host "All stopped." -ForegroundColor Green +& $real @args diff --git a/scripts/stop_desktop.ps1 b/scripts/stop_desktop.ps1 index 8fc24cf3..040dc566 100644 --- a/scripts/stop_desktop.ps1 +++ b/scripts/stop_desktop.ps1 @@ -1,45 +1,11 @@ -param( - [switch]$Force = $false -) +param() $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\stop_desktop.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -function Warn([string]$msg) { - Write-Host "[WARN] $msg" -ForegroundColor Yellow -} - -$runDir = Join-Path $PSScriptRoot ".run" -$pidFile = Join-Path $runDir "desktop.pid" - -Write-Step "Stopping desktop app" - -if (-not (Test-Path $pidFile)) { - Warn "No PID file found: $pidFile" - if (-not $Force) { exit 0 } -} - -$raw = (Get-Content $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) -$procId = 0 -[void][int]::TryParse([string]$raw, [ref]$procId) -if ($procId -le 0) { - Warn "Invalid PID in $pidFile" - if (-not $Force) { exit 1 } - exit 0 -} - -try { - # Kill the whole process tree (cmd -> npm -> electron/node) - & taskkill /PID $procId /T /F | Out-Null - Write-Host "Stopped PID=$procId (tree)" -ForegroundColor Green - Remove-Item $pidFile -Force -ErrorAction SilentlyContinue - exit 0 -} catch { - Warn "Failed to stop PID=$procId : $($_.Exception.Message)" - if (-not $Force) { exit 1 } - exit 0 -} +& $real @args diff --git a/scripts/stop_gateway.ps1 b/scripts/stop_gateway.ps1 index 316f3807..edc48f44 100644 --- a/scripts/stop_gateway.ps1 +++ b/scripts/stop_gateway.ps1 @@ -1,62 +1,11 @@ -param( - [int]$Port = 8787, - [switch]$Force = $false -) +param() $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\stop_gateway.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -function Warn([string]$msg) { - Write-Host "[WARN] $msg" -ForegroundColor Yellow -} - -function Kill-ProcId([int]$procId) { - try { - Stop-Process -Id $procId -Force - Write-Host "Stopped PID=$procId" -ForegroundColor Green - return $true - } catch { - Warn "Failed to stop PID=$procId : $($_.Exception.Message)" - return $false - } -} - -$runDir = Join-Path $PSScriptRoot ".run" -$pidFile = Join-Path $runDir "gateway.pid" - -Write-Step "Stopping gateway" - -if (Test-Path $pidFile) { - $raw = (Get-Content $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) - $procId = 0 - [void][int]::TryParse([string]$raw, [ref]$procId) - if ($procId -gt 0) { - $ok = Kill-ProcId -procId $procId - if ($ok) { - Remove-Item $pidFile -Force -ErrorAction SilentlyContinue - exit 0 - } - if (-not $Force) { exit 1 } - } -} - -Write-Step "No PID file; attempting to stop by port $Port" -try { - $conns = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue - if (-not $conns) { - Warn "No listener found on port $Port" - exit 0 - } - $pids = $conns | Select-Object -ExpandProperty OwningProcess -Unique - foreach ($p in $pids) { - if ($p -gt 0) { [void](Kill-ProcId -procId $p) } - } - exit 0 -} catch { - Warn "Stop by port failed: $($_.Exception.Message)" - if (-not $Force) { exit 1 } -} +& $real @args diff --git a/scripts/stop_ops.ps1 b/scripts/stop_ops.ps1 index 38afb83d..f075ce23 100644 --- a/scripts/stop_ops.ps1 +++ b/scripts/stop_ops.ps1 @@ -1,33 +1,11 @@ -param( - [switch]$Force = $false -) +param() $ErrorActionPreference = "Stop" -function Write-Step([string]$msg) { - Write-Host "==> $msg" -ForegroundColor Cyan +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\stop_ops.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) -Set-Location $repoRoot - -Write-Step "Project root: $repoRoot" -Write-Step "Stopping stack services..." - -try { - python -m oclaw.ops stack down - Write-Host "Stack stopped." -ForegroundColor Green -} catch { - Write-Host "[WARN] stack down failed: $($_.Exception.Message)" -ForegroundColor Yellow - if (-not $Force) { - exit 1 - } -} - -Write-Step "Current status" -try { - python -m oclaw.ops stack status -} catch { - Write-Host "[WARN] Unable to read status after stop." -ForegroundColor Yellow -} +& $real @args diff --git a/scripts/stop_ops.sh b/scripts/stop_ops.sh index 78354774..5becd9d7 100644 --- a/scripts/stop_ops.sh +++ b/scripts/stop_ops.sh @@ -1,13 +1,11 @@ #!/usr/bin/env bash set -euo pipefail -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$ROOT_DIR" +REAL_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../runtime/operations/scripts" && pwd)/stop_ops.sh" +if [[ ! -f "$REAL_PATH" ]]; then + echo "[ERROR] Forward script target not found: $REAL_PATH" >&2 + exit 1 +fi -printf '==> Project root: %s\n' "$ROOT_DIR" -printf '==> Stopping stack services...\n' -python -m oclaw.ops stack down || true - -printf '\n==> Current status\n' -python -m oclaw.ops stack status || true +exec bash "$REAL_PATH" "$@" diff --git a/scripts/stop_web.ps1 b/scripts/stop_web.ps1 index ebba8f34..27c45bd8 100644 --- a/scripts/stop_web.ps1 +++ b/scripts/stop_web.ps1 @@ -1,8 +1,11 @@ -param( - [int]$Port = 8787, - [switch]$Force = $false -) +param() -# 网页端(/admin、/chat)由网关进程提供,这里只是一个语义化别名。 -& "$PSScriptRoot/stop_gateway.ps1" -Port $Port -Force:$Force +$ErrorActionPreference = "Stop" + +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\stop_web.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" +} + +& $real @args diff --git a/scripts/stop_wiki_worker.ps1 b/scripts/stop_wiki_worker.ps1 index 80eef716..b21e5675 100644 --- a/scripts/stop_wiki_worker.ps1 +++ b/scripts/stop_wiki_worker.ps1 @@ -1,31 +1,11 @@ -param( - [switch]$Force -) +param() $ErrorActionPreference = "Stop" -$runDir = Join-Path $PSScriptRoot ".run" -$pidFile = Join-Path $runDir "wiki_worker.pid" -if (-not (Test-Path $pidFile)) { - Write-Host "[ok] not running (no pid file)" - exit 0 +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\stop_wiki_worker.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) -if (-not $procId) { - Remove-Item -Force $pidFile -ErrorAction SilentlyContinue - Write-Host "[ok] not running (empty pid file)" - exit 0 -} +& $real @args -try { - if ($Force) { - taskkill.exe /PID $procId /T /F | Out-Null - } else { - taskkill.exe /PID $procId /T | Out-Null - } -} catch { -} - -Remove-Item -Force $pidFile -ErrorAction SilentlyContinue -Write-Host "[ok] stopped pid=$procId" diff --git a/scripts/wecom_smoke_test.py b/scripts/wecom_smoke_test.py index d7b15138..d4a207bd 100644 --- a/scripts/wecom_smoke_test.py +++ b/scripts/wecom_smoke_test.py @@ -1,60 +1,8 @@ from __future__ import annotations -import json -import sys -import uuid - -from oclaw.platform.config.paths import db_path -from oclaw.platform.integrations.wecom_client import WeComClient -from oclaw.platform.persistence.sqlite_store import SqliteStore - - -def main() -> int: - store = SqliteStore(db_path()) - client = WeComClient(store) - try: - bot_id, _bot_secret = client.get_bot_credentials() - except Exception as exc: - print("ok=0") - print("step=read_bot_config") - print(f"error={type(exc).__name__}: {exc}") - return 1 - print("ok=1") - print("mode=bot_api") - print(f"bot_id={bot_id}") - if "--ws" not in sys.argv[1:]: - print("ws_skipped=1") - print("hint=use --ws for subscribe handshake test") - return 0 - try: - import websocket # type: ignore - except Exception: - print("ok=0") - print("step=import_websocket_client") - print("error=websocket-client not installed, run: pip install websocket-client") - return 1 - try: - ws = websocket.create_connection("wss://openws.work.weixin.qq.com", timeout=15) - ws.send( - json.dumps( - { - "cmd": "aibot_subscribe", - "headers": {"req_id": uuid.uuid4().hex}, - "body": {"bot_id": bot_id, "secret": client.get_bot_credentials()[1]}, - }, - ensure_ascii=False, - ) - ) - raw = ws.recv() - ws.close() - print("ws_subscribe_response=" + str(raw)) - return 0 - except Exception as exc: - print("ok=0") - print("step=ws_subscribe") - print(f"error={type(exc).__name__}: {exc}") - return 1 +from oclaw.runtime.operations.scripts.wecom_smoke_test import main if __name__ == "__main__": raise SystemExit(main()) + diff --git a/scripts/wecom_status.py b/scripts/wecom_status.py index e37b070b..6feda157 100644 --- a/scripts/wecom_status.py +++ b/scripts/wecom_status.py @@ -1,78 +1,8 @@ from __future__ import annotations -import json -from datetime import datetime, timezone - -from oclaw.platform.config.paths import db_path -from oclaw.platform.integrations.wecom_client import WeComClient -from oclaw.platform.persistence.sqlite_store import SqliteStore - - -def _fmt_ts(ts: str) -> str: - try: - n = int(str(ts).strip()) - return datetime.fromtimestamp(n, tz=timezone.utc).astimezone().isoformat() - except Exception: - return "" - - -def main() -> int: - store = SqliteStore(db_path()) - client = WeComClient(store) - print("ok=1") - print(f"mode={client.mode()}") - bot_id = str(store.get_setting("wecom_bot_id") or "").strip() - print(f"bot_id={bot_id}") - - auto_bind = str(store.get_setting("wecom_auto_bind_enabled") or "1").strip() - auto_tenant = str(store.get_setting("wecom_auto_bind_tenant_name") or "Team").strip() or "Team" - auto_role = str(store.get_setting("wecom_auto_bind_role") or "member").strip() or "member" - print(f"auto_bind_enabled={auto_bind}") - print(f"auto_bind_tenant={auto_tenant}") - print(f"auto_bind_role={auto_role}") - - last_ts = str(store.get_setting("wecom_last_msg_ts") or "").strip() - last_user = str(store.get_setting("wecom_last_from_user") or "").strip() - last_cmd = str(store.get_setting("wecom_last_cmd") or "").strip() - last_raw_from_user = str(store.get_setting("wecom_last_raw_from_user") or "").strip() - last_parse_error = str(store.get_setting("wecom_last_parse_error") or "").strip() - last_outbound_mode = str(store.get_setting("wecom_last_outbound_mode") or "").strip() - last_outbound_error = str(store.get_setting("wecom_last_outbound_error") or "").strip() - print(f"last_from_user={last_user}") - print(f"last_raw_from_user={last_raw_from_user}") - print(f"last_cmd={last_cmd}") - print(f"last_parse_error={last_parse_error}") - print(f"last_outbound_mode={last_outbound_mode}") - print(f"last_outbound_error={last_outbound_error}") - print(f"last_msg_ts={last_ts}") - print(f"last_msg_local={_fmt_ts(last_ts)}") - - raw_recent = str(store.get_setting("wecom_recent_from_users") or "[]") - try: - recent = json.loads(raw_recent) - except Exception: - recent = [] - if not isinstance(recent, list): - recent = [] - print("recent_from_users=" + json.dumps(recent[:20], ensure_ascii=False)) - - idents = store.list_channel_identities(channel="wecom", limit=50) - print(f"bound_identities={len(idents)}") - for x in idents[:20]: - print( - "identity=" - + json.dumps( - { - "external_user_id": x.get("external_user_id"), - "display_name": x.get("display_name"), - "role": x.get("role"), - "tenant_id": x.get("tenant_id"), - }, - ensure_ascii=False, - ) - ) - return 0 +from oclaw.runtime.operations.scripts.wecom_status import main if __name__ == "__main__": raise SystemExit(main()) + diff --git a/scripts/weixin_install.ps1 b/scripts/weixin_install.ps1 index 8ca965bc..61cf4ce8 100644 --- a/scripts/weixin_install.ps1 +++ b/scripts/weixin_install.ps1 @@ -1,41 +1,13 @@ param( - [string]$ChannelId = "openclaw-weixin", - [string]$Package = "@tencent-weixin/openclaw-weixin@2.1.9", - [string]$OpenClawRuntime = "" + [string]$ChannelId = "oclaw-weixin" ) $ErrorActionPreference = "Stop" -function Resolve-RepoRoot { - $here = Split-Path -Parent $PSCommandPath - return (Resolve-Path (Join-Path $here "..")).Path +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\weixin_install.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$oclawRoot = Resolve-RepoRoot -$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" -$stateDir = Join-Path $sidecarRoot "state" - -New-Item -ItemType Directory -Force -Path $sidecarRoot | Out-Null -New-Item -ItemType Directory -Force -Path (Join-Path $sidecarRoot "logs") | Out-Null -New-Item -ItemType Directory -Force -Path $stateDir | Out-Null - -Push-Location $sidecarRoot -try { - if (-not (Test-Path (Join-Path $sidecarRoot "package.json"))) { - npm.cmd init -y | Out-Null - } - if (Test-Path (Join-Path $sidecarRoot "package-lock.json")) { - npm.cmd ci - } else { - # First-time setup: install exact versions for reproducible sidecar runtime. - if ($OpenClawRuntime) { - npm.cmd install --save-exact $Package $OpenClawRuntime tsx@4.21.0 typescript@6.0.3 - } else { - npm.cmd install --save-exact $Package tsx@4.21.0 typescript@6.0.3 - } - } - Write-Host "[ok] installed $Package into $sidecarRoot" -} finally { - Pop-Location -} +& $real -ChannelId $ChannelId diff --git a/scripts/weixin_login.ps1 b/scripts/weixin_login.ps1 index 7fd1db10..c17bd168 100644 --- a/scripts/weixin_login.ps1 +++ b/scripts/weixin_login.ps1 @@ -1,32 +1,13 @@ param( - [string]$ChannelId = "openclaw-weixin" + [string]$ChannelId = "oclaw-weixin" ) $ErrorActionPreference = "Stop" -function Resolve-RepoRoot { - $here = Split-Path -Parent $PSCommandPath - return (Resolve-Path (Join-Path $here "..")).Path +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\weixin_login.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$oclawRoot = Resolve-RepoRoot -$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" -$stateDir = Join-Path $sidecarRoot "state" - -if (-not (Test-Path $sidecarRoot)) { - throw "sidecar not installed: run .\\scripts\\weixin_install.ps1 first" -} - -New-Item -ItemType Directory -Force -Path $stateDir | Out-Null - -Push-Location $sidecarRoot -try { - $env:OPENCLAW_STATE_DIR = $stateDir - if (-not (Test-Path (Join-Path $sidecarRoot "login.ts"))) { - throw "missing login.ts" - } - npm.cmd exec -- tsx login.ts -} finally { - Pop-Location -} +& $real -ChannelId $ChannelId diff --git a/scripts/weixin_start.ps1 b/scripts/weixin_start.ps1 index f49af5e9..3ed8e51a 100644 --- a/scripts/weixin_start.ps1 +++ b/scripts/weixin_start.ps1 @@ -1,39 +1,14 @@ param( - [string]$ChannelId = "openclaw-weixin", - [string]$GatewayBaseUrl = "http://127.0.0.1:8787" + [string]$ChannelId = "oclaw-weixin", + [string]$GatewayBaseUrl = "http://127.0.0.1:8787" ) $ErrorActionPreference = "Stop" -function Resolve-RepoRoot { - $here = Split-Path -Parent $PSCommandPath - return (Resolve-Path (Join-Path $here "..")).Path +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\weixin_start.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$oclawRoot = Resolve-RepoRoot -$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" -$stateDir = Join-Path $sidecarRoot "state" -$logDir = Join-Path $sidecarRoot "logs" -$pidFile = Join-Path $sidecarRoot "pid.txt" - -if (-not (Test-Path $sidecarRoot)) { - throw "sidecar not installed: run .\\scripts\\weixin_install.ps1 first" -} -if (-not (Test-Path (Join-Path $sidecarRoot "runner.ts"))) { - throw "missing runner.ts (sidecar code). Re-run repo sync or restore file." -} -New-Item -ItemType Directory -Force -Path $logDir | Out-Null -New-Item -ItemType Directory -Force -Path $stateDir | Out-Null - -$logPath = Join-Path $logDir "weixin_sidecar.log" -$errPath = Join-Path $logDir "weixin_sidecar.err.log" -$cmd = "cmd.exe" -$args = @( - "/c", - "cd /d $sidecarRoot && set OPENCLAW_STATE_DIR=$stateDir&& set AIA_GATEWAY_BASE_URL=$GatewayBaseUrl&& npm.cmd exec -- tsx runner.ts" -) - -$p = Start-Process -FilePath $cmd -ArgumentList $args -WorkingDirectory $sidecarRoot -PassThru -WindowStyle Hidden -RedirectStandardOutput $logPath -RedirectStandardError $errPath -Set-Content -Path $pidFile -Value $p.Id -Write-Host "[ok] started weixin sidecar pid=$($p.Id) out=$logPath err=$errPath" +& $real -ChannelId $ChannelId -GatewayBaseUrl $GatewayBaseUrl diff --git a/scripts/weixin_status.ps1 b/scripts/weixin_status.ps1 index 0df929f1..985b3186 100644 --- a/scripts/weixin_status.ps1 +++ b/scripts/weixin_status.ps1 @@ -1,38 +1,13 @@ param( - [string]$ChannelId = "openclaw-weixin" + [string]$ChannelId = "oclaw-weixin" ) $ErrorActionPreference = "Stop" -function Resolve-RepoRoot { - $here = Split-Path -Parent $PSCommandPath - return (Resolve-Path (Join-Path $here "..")).Path +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\weixin_status.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$oclawRoot = Resolve-RepoRoot -$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" -$pidFile = Join-Path $sidecarRoot "pid.txt" - -if (-not (Test-Path $pidFile)) { - Write-Host "status=stopped" - exit 0 -} - -$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) -if (-not $procId) { - Write-Host "status=stopped" - exit 0 -} - -$exists = $false -try { - Get-Process -Id $procId -ErrorAction Stop | Out-Null - $exists = $true -} catch {} - -if ($exists) { - Write-Host "status=running pid=$procId" -} else { - Write-Host "status=stale_pid pid=$procId" -} +& $real -ChannelId $ChannelId diff --git a/scripts/weixin_stop.ps1 b/scripts/weixin_stop.ps1 index 8a01df3d..7637cbe8 100644 --- a/scripts/weixin_stop.ps1 +++ b/scripts/weixin_stop.ps1 @@ -1,41 +1,13 @@ param( - [string]$ChannelId = "openclaw-weixin", - [switch]$Force + [string]$ChannelId = "oclaw-weixin" ) $ErrorActionPreference = "Stop" -function Resolve-RepoRoot { - $here = Split-Path -Parent $PSCommandPath - return (Resolve-Path (Join-Path $here "..")).Path +$real = Join-Path $PSScriptRoot "..\\runtime\\operations\\scripts\\weixin_stop.ps1" +if (-not (Test-Path $real)) { + throw "Forward script target not found: $real" } -$oclawRoot = Resolve-RepoRoot -$sidecarRoot = Join-Path $oclawRoot "data\\channel_sidecar\\$ChannelId" -$pidFile = Join-Path $sidecarRoot "pid.txt" - -if (-not (Test-Path $pidFile)) { - Write-Host "[ok] not running (no pid file)" - exit 0 -} - -$procId = (Get-Content -Path $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1) -if (-not $procId) { - Remove-Item -Force $pidFile -ErrorAction SilentlyContinue - Write-Host "[ok] not running (empty pid file)" - exit 0 -} - -try { - if ($Force) { - taskkill.exe /PID $procId /T /F | Out-Null - } else { - taskkill.exe /PID $procId /T | Out-Null - } -} catch { - # Ignore if already dead. -} - -Remove-Item -Force $pidFile -ErrorAction SilentlyContinue -Write-Host "[ok] stopped pid=$procId" +& $real -ChannelId $ChannelId diff --git a/shared/__init__.py b/shared/__init__.py deleted file mode 100644 index 35af1df5..00000000 --- a/shared/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Shared cross-layer utilities and types.""" - diff --git a/tests/test_admin_chat_stream_async_task.py b/tests/test_admin_chat_stream_async_task.py index 75f8419d..c675c081 100644 --- a/tests/test_admin_chat_stream_async_task.py +++ b/tests/test_admin_chat_stream_async_task.py @@ -145,7 +145,7 @@ class AdminChatStreamAsyncTaskTests(unittest.TestCase): self.assertEqual(str(body.get("mode") or ""), "async_task") task_id = str(body.get("task_id") or "") self.assertTrue(task_id.strip()) - task = self.store.openclaw_task_get(task_id=task_id, tenant_id=self.tenant_id) + task = self.store.oclaw_task_get(task_id=task_id, tenant_id=self.tenant_id) self.assertIsNotNone(task) payload = json.loads(str(task.payload or "{}")) self.assertEqual(str(payload.get("selected_specialist") or ""), "generalist") @@ -202,7 +202,7 @@ class AdminChatStreamAsyncTaskTests(unittest.TestCase): self.assertTrue(body.get("ok"), body) task_id = str(body.get("task_id") or "") self.assertTrue(task_id.strip()) - task = self.store.openclaw_task_get(task_id=task_id, tenant_id=self.tenant_id) + task = self.store.oclaw_task_get(task_id=task_id, tenant_id=self.tenant_id) self.assertIsNotNone(task) payload = json.loads(str(task.payload or "{}")) self.assertEqual(str(payload.get("interaction_mode") or ""), "expert") diff --git a/tests/test_admin_chat_wiki_events_api.py b/tests/test_admin_chat_wiki_events_api.py index 7c627372..8652582c 100644 --- a/tests/test_admin_chat_wiki_events_api.py +++ b/tests/test_admin_chat_wiki_events_api.py @@ -68,13 +68,13 @@ class AdminChatWikiEventsApiTests(unittest.TestCase): def test_wiki_events_endpoint_returns_finished_wiki_capture_tasks(self) -> None: token = self._login() - task = self.store.openclaw_task_create( + task = self.store.oclaw_task_create( tenant_id=self.tenant_id, session_id=str(self.session_id), task_type="wiki_capture", payload={"kind": "captureAfterTurn", "session_id": str(self.session_id)}, ) - self.assertTrue(self.store.openclaw_task_finish(task_id=task.id, result={"ok": True, "dedupMerge": {"merged_count": 1, "skipped_dup": 0}})) + self.assertTrue(self.store.oclaw_task_finish(task_id=task.id, result={"ok": True, "dedupMerge": {"merged_count": 1, "skipped_dup": 0}})) headers = {"authorization": f"Bearer {token}", "accept": "application/json"} resp = self.client.get( f"/admin/api/chat/sessions/{self.session_id}/wiki-events?limit=10", diff --git a/tests/test_admin_llm_preview_planner_consistency.py b/tests/test_admin_llm_preview_planner_consistency.py index e970cbe0..bcf410f8 100644 --- a/tests/test_admin_llm_preview_planner_consistency.py +++ b/tests/test_admin_llm_preview_planner_consistency.py @@ -10,7 +10,7 @@ from fastapi.testclient import TestClient from oclaw.interfaces.http.fastapi_app import create_app from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.exposure_plan import build_llm_tools_plan +from oclaw.runtime.tools.exposure_plan import build_llm_tools_plan class AdminLLMPreviewPlannerConsistencyTests(unittest.TestCase): diff --git a/tests/test_admin_openclaw_runs_api.py b/tests/test_admin_oclaw_runs_api.py similarity index 89% rename from tests/test_admin_openclaw_runs_api.py rename to tests/test_admin_oclaw_runs_api.py index e3ddc9b0..0e04d83d 100644 --- a/tests/test_admin_openclaw_runs_api.py +++ b/tests/test_admin_oclaw_runs_api.py @@ -12,7 +12,7 @@ from oclaw.interfaces.http.fastapi_app import create_app from oclaw.platform.persistence.sqlite_store import SqliteStore -class AdminOpenClawRunsApiTests(unittest.TestCase): +class AdminOclawRunsApiTests(unittest.TestCase): def setUp(self) -> None: self._tmp = tempfile.TemporaryDirectory(ignore_cleanup_errors=True) self.db = Path(self._tmp.name) / "ops.sqlite" @@ -35,14 +35,14 @@ class AdminOpenClawRunsApiTests(unittest.TestCase): tenant_id=self.tenant_id, user_id=self.user_id, ).id - self.store.openclaw_run_upsert( + self.store.oclaw_run_upsert( run_id="run-1", tenant_id=self.tenant_id, session_id=self.session_id, status="failed", payload={"stop_reason": "non_retryable_error"}, ) - self.store.openclaw_attempt_append( + self.store.oclaw_attempt_append( run_id="run-1", tenant_id=self.tenant_id, session_id=self.session_id, @@ -69,7 +69,7 @@ class AdminOpenClawRunsApiTests(unittest.TestCase): def test_runs_list_and_get(self) -> None: token = self._login() headers = {"authorization": f"Bearer {token}"} - r1 = self.client.get(f"/admin/api/openclaw/runs?session_id={self.session_id}&include_attempts=1", headers=headers) + r1 = self.client.get(f"/admin/api/oclaw/runs?session_id={self.session_id}&include_attempts=1", headers=headers) self.assertEqual(r1.status_code, 200) b1 = r1.json() self.assertTrue(b1.get("ok")) @@ -79,7 +79,7 @@ class AdminOpenClawRunsApiTests(unittest.TestCase): self.assertEqual(str(rows[0].get("run_id") or ""), "run-1") self.assertTrue(isinstance(rows[0].get("attempts"), list)) - r2 = self.client.get("/admin/api/openclaw/runs?run_id=run-1&include_attempts=1", headers=headers) + r2 = self.client.get("/admin/api/oclaw/runs?run_id=run-1&include_attempts=1", headers=headers) self.assertEqual(r2.status_code, 200) b2 = r2.json() self.assertTrue(b2.get("ok")) diff --git a/tests/test_admin_openclaw_tasks_api.py b/tests/test_admin_oclaw_tasks_api.py similarity index 86% rename from tests/test_admin_openclaw_tasks_api.py rename to tests/test_admin_oclaw_tasks_api.py index 11a04308..f36e3321 100644 --- a/tests/test_admin_openclaw_tasks_api.py +++ b/tests/test_admin_oclaw_tasks_api.py @@ -12,7 +12,7 @@ from oclaw.interfaces.http.fastapi_app import create_app from oclaw.platform.persistence.sqlite_store import SqliteStore -class AdminOpenClawTasksApiTests(unittest.TestCase): +class AdminOclawTasksApiTests(unittest.TestCase): def setUp(self) -> None: self._tmp = tempfile.TemporaryDirectory(ignore_cleanup_errors=True) self.db = Path(self._tmp.name) / "ops.sqlite" @@ -35,7 +35,7 @@ class AdminOpenClawTasksApiTests(unittest.TestCase): tenant_id=self.tenant_id, user_id=self.user_id, ).id - self.task_a = self.store.openclaw_task_create( + self.task_a = self.store.oclaw_task_create( tenant_id=self.tenant_id, session_id=self.session_a, payload={"text": "hello"}, @@ -56,7 +56,7 @@ class AdminOpenClawTasksApiTests(unittest.TestCase): tenant_id=other_tenant, user_id=str(other_user["id"]), ).id - self.store.openclaw_task_create(tenant_id=other_tenant, session_id=other_session, payload={"text": "secret"}) + self.store.oclaw_task_create(tenant_id=other_tenant, session_id=other_session, payload={"text": "secret"}) self.client = TestClient(create_app()) self.client.post("/admin/api/auth/bootstrap", json={}) @@ -78,10 +78,10 @@ class AdminOpenClawTasksApiTests(unittest.TestCase): self.assertTrue(data.get("ok"), data) return str(data.get("token") or "") - def test_openclaw_tasks_api_lists_only_current_tenant(self) -> None: + def test_oclaw_tasks_api_lists_only_current_tenant(self) -> None: token = self._login() headers = {"authorization": f"Bearer {token}"} - r = self.client.get("/admin/api/openclaw/tasks?limit=50", headers=headers) + r = self.client.get("/admin/api/oclaw/tasks?limit=50", headers=headers) self.assertEqual(r.status_code, 200) body = r.json() self.assertTrue(body.get("ok")) @@ -90,10 +90,10 @@ class AdminOpenClawTasksApiTests(unittest.TestCase): self.assertTrue(all(str(x.get("tenant_id") or "") == self.tenant_id for x in tasks)) self.assertTrue(any(str(x.get("id") or "") == self.task_a.id for x in tasks)) - def test_openclaw_task_get_by_id(self) -> None: + def test_oclaw_task_get_by_id(self) -> None: token = self._login() headers = {"authorization": f"Bearer {token}"} - r = self.client.get(f"/admin/api/openclaw/tasks?task_id={self.task_a.id}", headers=headers) + r = self.client.get(f"/admin/api/oclaw/tasks?task_id={self.task_a.id}", headers=headers) self.assertEqual(r.status_code, 200) body = r.json() self.assertTrue(body.get("ok")) diff --git a/tests/test_admin_tool_policy_openclaw_retry_codes.py b/tests/test_admin_tool_policy_oclaw_retry_codes.py similarity index 82% rename from tests/test_admin_tool_policy_openclaw_retry_codes.py rename to tests/test_admin_tool_policy_oclaw_retry_codes.py index 6b45cd1d..def320b5 100644 --- a/tests/test_admin_tool_policy_openclaw_retry_codes.py +++ b/tests/test_admin_tool_policy_oclaw_retry_codes.py @@ -12,7 +12,7 @@ from oclaw.interfaces.http.fastapi_app import create_app from oclaw.platform.persistence.sqlite_store import SqliteStore -class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): +class AdminToolPolicyOclawRetryCodesTests(unittest.TestCase): def setUp(self) -> None: self._tmp = tempfile.TemporaryDirectory(ignore_cleanup_errors=True) self.db = Path(self._tmp.name) / "ops.sqlite" @@ -44,15 +44,15 @@ class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): self.assertTrue(body.get("ok"), body) return str(body.get("token") or "") - def test_tool_policy_get_and_save_openclaw_retry_codes(self) -> None: + def test_tool_policy_get_and_save_oclaw_retry_codes(self) -> None: token = self._login() headers = {"authorization": f"Bearer {token}"} r1 = self.client.get("/admin/api/tool-policy", headers=headers) self.assertEqual(r1.status_code, 200) b1 = r1.json() self.assertTrue(b1.get("ok")) - self.assertTrue(str(b1.get("openclaw_retryable_error_codes") or "").strip()) - self.assertFalse(bool(b1.get("openclaw_retry_codes_strict_mode"))) + self.assertTrue(str(b1.get("oclaw_retryable_error_codes") or "").strip()) + self.assertFalse(bool(b1.get("oclaw_retry_codes_strict_mode"))) payload = { "turn_max_tool_workers": 8, @@ -65,8 +65,8 @@ class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): "tool_llm_message_max_chars": 0, "mcp_filesystem_extra_roots": "", "mcp_env_allowlist": "", - "openclaw_retryable_error_codes": "provider_timeout, context_overflow", - "openclaw_retry_codes_strict_mode": False, + "oclaw_retryable_error_codes": "provider_timeout, context_overflow", + "oclaw_retry_codes_strict_mode": False, "wecom_longconn_workers": 2, "wecom_longconn_inbound_queue_maxsize": 200, } @@ -74,8 +74,8 @@ class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): self.assertEqual(r2.status_code, 200) b2 = r2.json() self.assertTrue(b2.get("ok")) - self.assertEqual(str(b2.get("openclaw_retryable_error_codes") or ""), "provider_timeout,context_overflow") - self.assertFalse(bool(b2.get("openclaw_retry_codes_strict_mode"))) + self.assertEqual(str(b2.get("oclaw_retryable_error_codes") or ""), "provider_timeout,context_overflow") + self.assertFalse(bool(b2.get("oclaw_retry_codes_strict_mode"))) self.assertEqual(list(b2.get("unknown_retryable_error_codes") or []), []) def test_tool_policy_retry_codes_unknown_filtered(self) -> None: @@ -92,8 +92,8 @@ class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): "tool_llm_message_max_chars": 0, "mcp_filesystem_extra_roots": "", "mcp_env_allowlist": "", - "openclaw_retryable_error_codes": "provider_timeout, typo_code_x, context_overflow", - "openclaw_retry_codes_strict_mode": False, + "oclaw_retryable_error_codes": "provider_timeout, typo_code_x, context_overflow", + "oclaw_retry_codes_strict_mode": False, "wecom_longconn_workers": 2, "wecom_longconn_inbound_queue_maxsize": 200, } @@ -101,7 +101,7 @@ class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): self.assertEqual(r.status_code, 200) b = r.json() self.assertTrue(b.get("ok")) - self.assertEqual(str(b.get("openclaw_retryable_error_codes") or ""), "provider_timeout,context_overflow") + self.assertEqual(str(b.get("oclaw_retryable_error_codes") or ""), "provider_timeout,context_overflow") self.assertEqual(list(b.get("unknown_retryable_error_codes") or []), ["typo_code_x"]) def test_tool_policy_retry_codes_unknown_rejected_in_strict_mode(self) -> None: @@ -118,8 +118,8 @@ class AdminToolPolicyOpenClawRetryCodesTests(unittest.TestCase): "tool_llm_message_max_chars": 0, "mcp_filesystem_extra_roots": "", "mcp_env_allowlist": "", - "openclaw_retryable_error_codes": "provider_timeout, typo_code_x", - "openclaw_retry_codes_strict_mode": True, + "oclaw_retryable_error_codes": "provider_timeout, typo_code_x", + "oclaw_retry_codes_strict_mode": True, "wecom_longconn_workers": 2, "wecom_longconn_inbound_queue_maxsize": 200, } diff --git a/tests/test_catalog_expert_role_loading.py b/tests/test_catalog_expert_role_loading.py index c7608399..8c322e9e 100644 --- a/tests/test_catalog_expert_role_loading.py +++ b/tests/test_catalog_expert_role_loading.py @@ -2,8 +2,8 @@ from __future__ import annotations from unittest.mock import patch -from oclaw.tools.base import ToolSpec -from oclaw.tools.catalog import materialize_tool_specs +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.catalog import materialize_tool_specs def _mk_spec(name: str) -> ToolSpec: @@ -18,10 +18,10 @@ def _mk_spec(name: str) -> ToolSpec: def test_catalog_loads_role_scoped_expert_tools() -> None: with patch( - "oclaw.tools.catalog.materialize_tools_for_expert", + "oclaw.runtime.tools.catalog.materialize_tools_for_expert", return_value=[_mk_spec("custom_tool_a"), _mk_spec("custom_tool_b")], ): - with patch("oclaw.tools.catalog.materialize_public_tools", return_value=[_mk_spec("system_time")]): + with patch("oclaw.runtime.tools.catalog.materialize_public_tools", return_value=[_mk_spec("system_time")]): specs = materialize_tool_specs(expert="generalist") names = {x.name for x in specs} assert "system_time" in names @@ -31,10 +31,10 @@ def test_catalog_loads_role_scoped_expert_tools() -> None: def test_catalog_deduplicates_same_tool_name() -> None: with patch( - "oclaw.tools.catalog.materialize_tools_for_expert", + "oclaw.runtime.tools.catalog.materialize_tools_for_expert", return_value=[_mk_spec("system_time"), _mk_spec("custom_tool_a")], ): - with patch("oclaw.tools.catalog.materialize_public_tools", return_value=[_mk_spec("system_time")]): + with patch("oclaw.runtime.tools.catalog.materialize_public_tools", return_value=[_mk_spec("system_time")]): specs = materialize_tool_specs(expert="generalist") names = [x.name for x in specs] assert names.count("system_time") == 1 diff --git a/tests/test_catalog_mcp_integration.py b/tests/test_catalog_mcp_integration.py index 5545b201..66921f83 100644 --- a/tests/test_catalog_mcp_integration.py +++ b/tests/test_catalog_mcp_integration.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.catalog import materialize_tool_specs +from oclaw.runtime.tools.catalog import materialize_tool_specs def _seed_single_mcp_tool(store: SqliteStore) -> None: diff --git a/tests/test_clawhub_client.py b/tests/test_clawhub_client.py index 883f6ebd..69c4cf58 100644 --- a/tests/test_clawhub_client.py +++ b/tests/test_clawhub_client.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.tools.skills import clawhub_client +from oclaw.runtime.tools.skills import clawhub_client def test_get_skill_detail_handles_latest_version_object(monkeypatch) -> None: diff --git a/tests/test_frontmatter_yaml.py b/tests/test_frontmatter_yaml.py index 7fa46860..b4d9b32b 100644 --- a/tests/test_frontmatter_yaml.py +++ b/tests/test_frontmatter_yaml.py @@ -7,13 +7,13 @@ from oclaw.prompts.frontmatter import parse_frontmatter_dict, parse_markdown_doc def test_parse_yaml_frontmatter_nested_metadata() -> None: fm = parse_frontmatter_dict( - "name: demo\ndescription: x\nmetadata:\n openclaw:\n install:\n - id: i1\n kind: node\n", + "name: demo\ndescription: x\nmetadata:\n oclaw:\n install:\n - id: i1\n kind: node\n", source="test", ) assert fm.get("name") == "demo" md = fm.get("metadata") assert isinstance(md, dict) - assert isinstance(md.get("openclaw"), dict) + assert isinstance(md.get("oclaw"), dict) def test_parse_markdown_document_roundtrip() -> None: diff --git a/tests/test_gateway_image_generation_chain.py b/tests/test_gateway_image_generation_chain.py index e9a401ca..f310d006 100644 --- a/tests/test_gateway_image_generation_chain.py +++ b/tests/test_gateway_image_generation_chain.py @@ -3,11 +3,11 @@ from __future__ import annotations import importlib.util from pathlib import Path -from oclaw.gateway.server_plugins import load_gateway_plugins +from oclaw.interfaces.gateway.server_plugins import load_gateway_plugins def _load_image_core_generate_image(): - file_path = (Path(__file__).resolve().parents[1] / "extensions" / "image-generation-core" / "api.py").resolve() + file_path = (Path(__file__).resolve().parents[1] / "runtime" / "extensions" / "image-generation-core" / "api.py").resolve() spec = importlib.util.spec_from_file_location("test_image_generation_core_api", str(file_path)) if spec is None or spec.loader is None: raise RuntimeError("failed to load image-generation-core api module") diff --git a/tests/test_gateway_server_methods.py b/tests/test_gateway_server_methods.py index e466168c..2340a480 100644 --- a/tests/test_gateway_server_methods.py +++ b/tests/test_gateway_server_methods.py @@ -2,36 +2,36 @@ from __future__ import annotations from typing import Any -from oclaw.gateway.server_methods.agent import agent_handlers -from oclaw.gateway.server_methods.agents import agents_handlers -from oclaw.gateway.server_methods.chat import chat_handlers -from oclaw.gateway.server_methods.send import send_handlers -from oclaw.gateway.server_methods.image import image_handlers -from oclaw.gateway.server_methods.skills import skills_handlers -from oclaw.gateway.server_methods.system import system_handlers -from oclaw.gateway.server_methods.cron import cron_handlers -from oclaw.gateway.server_methods.devices import device_handlers -from oclaw.gateway.server_methods.models import models_handlers -from oclaw.gateway.server_methods.models_auth_status import ( +from oclaw.interfaces.gateway.server_methods.agent import agent_handlers +from oclaw.interfaces.gateway.server_methods.agents import agents_handlers +from oclaw.interfaces.gateway.server_methods.chat import chat_handlers +from oclaw.interfaces.gateway.server_methods.send import send_handlers +from oclaw.interfaces.gateway.server_methods.image import image_handlers +from oclaw.interfaces.gateway.server_methods.skills import skills_handlers +from oclaw.interfaces.gateway.server_methods.system import system_handlers +from oclaw.interfaces.gateway.server_methods.cron import cron_handlers +from oclaw.interfaces.gateway.server_methods.devices import device_handlers +from oclaw.interfaces.gateway.server_methods.models import models_handlers +from oclaw.interfaces.gateway.server_methods.models_auth_status import ( invalidate_model_auth_status_cache, models_auth_status_handlers, ) -from oclaw.gateway.server_methods.push import push_handlers -from oclaw.gateway.server_methods.channels import channels_handlers -from oclaw.gateway.server_methods.update import update_handlers -from oclaw.gateway.server_methods.voicewake import voicewake_handlers -from oclaw.gateway.server_methods.wizard import wizard_handlers -from oclaw.gateway.server_methods.tts import tts_handlers -from oclaw.gateway.server_methods.web import web_handlers -from oclaw.gateway.server_methods.tools_catalog import tools_catalog_handlers -from oclaw.gateway.server_methods.tools_effective import tools_effective_handlers -from oclaw.gateway.server_methods.talk import talk_handlers -from oclaw.gateway.server_methods.usage import usage_handlers -from oclaw.gateway.server_methods.exec_approvals import exec_approvals_handlers -from oclaw.gateway.server_methods.nodes_pending import node_pending_handlers -from oclaw.gateway.server_methods.nodes import node_handlers -from oclaw.gateway.server_methods.sessions import sessions_handlers -from oclaw.gateway.server_methods.doctor import doctor_handlers +from oclaw.interfaces.gateway.server_methods.push import push_handlers +from oclaw.interfaces.gateway.server_methods.channels import channels_handlers +from oclaw.interfaces.gateway.server_methods.update import update_handlers +from oclaw.interfaces.gateway.server_methods.voicewake import voicewake_handlers +from oclaw.interfaces.gateway.server_methods.wizard import wizard_handlers +from oclaw.interfaces.gateway.server_methods.tts import tts_handlers +from oclaw.interfaces.gateway.server_methods.web import web_handlers +from oclaw.interfaces.gateway.server_methods.tools_catalog import tools_catalog_handlers +from oclaw.interfaces.gateway.server_methods.tools_effective import tools_effective_handlers +from oclaw.interfaces.gateway.server_methods.talk import talk_handlers +from oclaw.interfaces.gateway.server_methods.usage import usage_handlers +from oclaw.interfaces.gateway.server_methods.exec_approvals import exec_approvals_handlers +from oclaw.interfaces.gateway.server_methods.nodes_pending import node_pending_handlers +from oclaw.interfaces.gateway.server_methods.nodes import node_handlers +from oclaw.interfaces.gateway.server_methods.sessions import sessions_handlers +from oclaw.interfaces.gateway.server_methods.doctor import doctor_handlers def _call(handler, *, params: dict[str, Any], context: dict[str, Any] | None = None, client: dict[str, Any] | None = None): @@ -271,7 +271,7 @@ def test_skills_status_requires_store_then_lists_skills() -> None: assert ok["ok"] is True payload = ok["payload"] or {} assert isinstance(payload.get("skills"), list) - # Repo ships oclaw/skills/{main,coding,social} + # Repo ships oclaw/runtime/skills/{main,coding,social} names = {str(x.get("name")) for x in payload["skills"] if isinstance(x, dict)} assert "main" in names @@ -310,7 +310,7 @@ def test_cron_add_and_run_fallback() -> None: def test_image_generate_falls_back_to_registered_openai_provider() -> None: # Get providers via plugin load, then inject into gateway context. - from oclaw.gateway.server_plugins import load_gateway_plugins + from oclaw.interfaces.gateway.server_plugins import load_gateway_plugins plugin_out = load_gateway_plugins( cfg={"plugins": {"enabled": ["openai"]}}, @@ -532,9 +532,9 @@ def test_voicewake_set_requires_triggers_array() -> None: bad = _call(voicewake_handlers["voicewake.set"], params={"triggers": "hey"}) assert bad["ok"] is False assert bad["error"]["code"] == "INVALID_REQUEST" - ok = _call(voicewake_handlers["voicewake.set"], params={"triggers": ["Hey OpenClaw", " ", "hey openclaw"]}) + ok = _call(voicewake_handlers["voicewake.set"], params={"triggers": ["Hey Oclaw", " ", "hey oclaw"]}) assert ok["ok"] is True - assert (ok["payload"] or {}).get("triggers") == ["Hey OpenClaw"] + assert (ok["payload"] or {}).get("triggers") == ["Hey Oclaw"] def test_wizard_start_next_status_cancel_flow() -> None: diff --git a/tests/test_gateway_telegram_send_normalize.py b/tests/test_gateway_telegram_send_normalize.py index 723b6443..601abc3f 100644 --- a/tests/test_gateway_telegram_send_normalize.py +++ b/tests/test_gateway_telegram_send_normalize.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.gateway.server_methods.telegram_send_normalize import normalize_transport_target_for_channel +from oclaw.interfaces.gateway.server_methods.telegram_send_normalize import normalize_transport_target_for_channel def test_normalize_transport_target_for_channel_non_telegram_passthrough() -> None: diff --git a/tests/test_get_messages_preserve_tool_pairing.py b/tests/test_get_messages_preserve_tool_pairing.py index 174b7c28..cfc626f6 100644 --- a/tests/test_get_messages_preserve_tool_pairing.py +++ b/tests/test_get_messages_preserve_tool_pairing.py @@ -3,7 +3,7 @@ from __future__ import annotations import json from pathlib import Path -from oclaw.chat.agent_messages import build_llm_messages +from oclaw.runtime.chat.agent_messages import build_llm_messages from oclaw.platform.llm.chat_models import RuleBasedChatModel from oclaw.platform.persistence.sqlite_store import SqliteStore diff --git a/tests/test_mcp_adapter.py b/tests/test_mcp_adapter.py index a0ab5219..2572be5a 100644 --- a/tests/test_mcp_adapter.py +++ b/tests/test_mcp_adapter.py @@ -6,7 +6,7 @@ import unittest from pathlib import Path from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.mcp.adapter import materialize_mcp_tools, materialize_mcp_tools_for_specialist +from oclaw.runtime.tools.mcp.adapter import materialize_mcp_tools, materialize_mcp_tools_for_specialist class McpAdapterTests(unittest.TestCase): @@ -147,7 +147,7 @@ class McpAdapterTests(unittest.TestCase): self.assertIn("mcp__echo-b__ping", names) def test_mcp_local_env_file_path_prefers_src_local(self) -> None: - from oclaw.ops import mcp_env + from oclaw.runtime.operations import mcp_env p = mcp_env.mcp_local_env_file_path() self.assertEqual(p.name, "mcp_local.env") @@ -156,7 +156,7 @@ class McpAdapterTests(unittest.TestCase): self.assertIn("_local", parts) def test_mcp_env_allowlist_keys_default_when_unset(self) -> None: - from oclaw.ops import mcp_env + from oclaw.runtime.operations import mcp_env old = os.environ.pop("OPS_MCP_ENV_ALLOWLIST", None) try: diff --git a/tests/test_mcp_e2e_probe.py b/tests/test_mcp_e2e_probe.py index b34677f8..cb354a32 100644 --- a/tests/test_mcp_e2e_probe.py +++ b/tests/test_mcp_e2e_probe.py @@ -2,8 +2,8 @@ from __future__ import annotations import unittest -from oclaw.admin.mcp_e2e_probe import build_mcp_e2e_probe_plans, parse_mcp_bound_tool_name -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.interfaces.admin.mcp_e2e_probe import build_mcp_e2e_probe_plans, parse_mcp_bound_tool_name +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec class McpE2eProbeTests(unittest.TestCase): diff --git a/tests/test_mcp_filesystem_argv.py b/tests/test_mcp_filesystem_argv.py index 6e9b2305..52a19c94 100644 --- a/tests/test_mcp_filesystem_argv.py +++ b/tests/test_mcp_filesystem_argv.py @@ -8,7 +8,7 @@ from pathlib import Path from unittest import mock from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.mcp.filesystem_argv import augment_filesystem_mcp_argv, build_mcp_process_command +from oclaw.runtime.tools.mcp.filesystem_argv import augment_filesystem_mcp_argv, build_mcp_process_command def _wincase_set(items: list[str]) -> set[str]: diff --git a/tests/test_mcp_installer.py b/tests/test_mcp_installer.py index aab88b7a..ac9e6195 100644 --- a/tests/test_mcp_installer.py +++ b/tests/test_mcp_installer.py @@ -2,14 +2,14 @@ from __future__ import annotations import unittest -from oclaw.tools.mcp.installer import ( +from oclaw.runtime.tools.mcp.installer import ( _safe_server_id, detect_local_dependencies, install_mcp_server, preflight_mcp_server, uninstall_mcp_server, ) -from oclaw.tools.mcp.manifest import McpServerManifest +from oclaw.runtime.tools.mcp.manifest import McpServerManifest class McpInstallerTests(unittest.TestCase): diff --git a/tests/test_mcp_market.py b/tests/test_mcp_market.py index 55955b40..ede3abb4 100644 --- a/tests/test_mcp_market.py +++ b/tests/test_mcp_market.py @@ -3,13 +3,13 @@ from __future__ import annotations import unittest from unittest.mock import patch -from oclaw.tools.mcp.market import infer_install_template, search_mcp_market, trending_mcp_market +from oclaw.runtime.tools.mcp.market import infer_install_template, search_mcp_market, trending_mcp_market class McpMarketTests(unittest.TestCase): - @patch("oclaw.tools.mcp.market.search_github_repos") - @patch("oclaw.tools.mcp.market.search_npm_packages") - @patch("oclaw.tools.mcp.market.search_pypi_packages") + @patch("oclaw.runtime.tools.mcp.market.search_github_repos") + @patch("oclaw.runtime.tools.mcp.market.search_npm_packages") + @patch("oclaw.runtime.tools.mcp.market.search_pypi_packages") def test_aggregate_results(self, pypi_mock, npm_mock, gh_mock) -> None: gh_mock.return_value = [{"source_type": "github", "name": "a"}] npm_mock.return_value = [{"source_type": "npm", "name": "b"}] @@ -28,7 +28,7 @@ class McpMarketTests(unittest.TestCase): self.assertEqual(t2["entry_command"], "python") self.assertEqual(t2["entry_args"][:1], ["-m"]) - @patch("oclaw.tools.mcp.market.search_mcp_market") + @patch("oclaw.runtime.tools.mcp.market.search_mcp_market") def test_trending_cache(self, search_mock) -> None: search_mock.return_value = [{"source_type": "github", "name": "a", "stars": 9}] rows = trending_mcp_market(force_refresh=True, per_source_limit=2) diff --git a/tests/test_mcp_registry.py b/tests/test_mcp_registry.py index 6b2fa084..8a51038f 100644 --- a/tests/test_mcp_registry.py +++ b/tests/test_mcp_registry.py @@ -5,8 +5,8 @@ import unittest from pathlib import Path from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.mcp.manifest import McpServerManifest -from oclaw.tools.mcp.registry import McpRegistry +from oclaw.runtime.tools.mcp.manifest import McpServerManifest +from oclaw.runtime.tools.mcp.registry import McpRegistry class McpRegistryTests(unittest.TestCase): diff --git a/tests/test_mcp_runtime.py b/tests/test_mcp_runtime.py index be152bc3..6dc6a78a 100644 --- a/tests/test_mcp_runtime.py +++ b/tests/test_mcp_runtime.py @@ -5,7 +5,7 @@ import textwrap import unittest from pathlib import Path -from oclaw.tools.mcp.runtime import McpProcessRuntime +from oclaw.runtime.tools.mcp.runtime import McpProcessRuntime class McpRuntimeTests(unittest.TestCase): diff --git a/tests/test_mcp_skill_adapter.py b/tests/test_mcp_skill_adapter.py index 10a924af..652b2e96 100644 --- a/tests/test_mcp_skill_adapter.py +++ b/tests/test_mcp_skill_adapter.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.tools.mcp.adapter import materialize_mcp_skills_for_specialist +from oclaw.runtime.tools.mcp.adapter import materialize_mcp_skills_for_specialist class _Store: diff --git a/tests/test_mcp_workspace_filesystem_e2e.py b/tests/test_mcp_workspace_filesystem_e2e.py index 8103ed39..3cf95a70 100644 --- a/tests/test_mcp_workspace_filesystem_e2e.py +++ b/tests/test_mcp_workspace_filesystem_e2e.py @@ -11,12 +11,12 @@ import tempfile import unittest from pathlib import Path -from oclaw.agents.network_ops_agent import NetworkOpsAgent +from oclaw.runtime.agents.network_ops_agent import NetworkOpsAgent from oclaw.platform.llm.chat_models import RuleBasedChatModel from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.mcp.adapter import materialize_mcp_tools_for_specialist -from oclaw.tools.mcp.filesystem_argv import build_mcp_process_command, collect_filesystem_mcp_extra_roots -from oclaw.tools.mcp.runtime import McpProcessRuntime +from oclaw.runtime.tools.mcp.adapter import materialize_mcp_tools_for_specialist +from oclaw.runtime.tools.mcp.filesystem_argv import build_mcp_process_command, collect_filesystem_mcp_extra_roots +from oclaw.runtime.tools.mcp.runtime import McpProcessRuntime class McpWorkspaceFilesystemE2ETests(unittest.TestCase): diff --git a/tests/test_memory_vector.py b/tests/test_memory_vector.py index 2bc40193..40ec63d2 100644 --- a/tests/test_memory_vector.py +++ b/tests/test_memory_vector.py @@ -4,7 +4,7 @@ import tempfile import unittest from pathlib import Path -from oclaw.orchestration.memory import maybe_write_turn_memory, semantic_retrieve +from oclaw.runtime.orchestration.memory import maybe_write_turn_memory, semantic_retrieve from oclaw.platform.persistence.sqlite_store import SqliteStore diff --git a/tests/test_memory_wiki_plugin.py b/tests/test_memory_wiki_plugin.py index 50d614c4..7d3be10f 100644 --- a/tests/test_memory_wiki_plugin.py +++ b/tests/test_memory_wiki_plugin.py @@ -5,13 +5,13 @@ import os from pathlib import Path import sys -from oclaw.gateway.python_extensions_loader import build_python_extensions_registry -from oclaw.gateway.server_plugins import load_gateway_plugins -from oclaw.tools.catalog import materialize_tool_specs +from oclaw.interfaces.gateway.python_extensions_loader import build_python_extensions_registry +from oclaw.interfaces.gateway.server_plugins import load_gateway_plugins +from oclaw.runtime.tools.catalog import materialize_tool_specs def _load_memory_wiki_api_module(): - p = Path("d:/project/chatgpt/oclaw/extensions/memory-wiki/api.py") + p = Path("d:/project/chatgpt/oclaw/runtime/extensions/memory-wiki/api.py") module_name = "test_memory_wiki_api" spec = importlib.util.spec_from_file_location(module_name, str(p)) assert spec is not None and spec.loader is not None diff --git a/tests/test_openclaw_acp_pointer_relay.py b/tests/test_oclaw_acp_pointer_relay.py similarity index 94% rename from tests/test_openclaw_acp_pointer_relay.py rename to tests/test_oclaw_acp_pointer_relay.py index 6bf1a91e..b1e3148c 100644 --- a/tests/test_openclaw_acp_pointer_relay.py +++ b/tests/test_oclaw_acp_pointer_relay.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.openclaw_runtime.relay_pointer import build_acp_relay_result, validate_relay_share_envelope +from oclaw.runtime.relay_pointer import build_acp_relay_result, validate_relay_share_envelope def test_validate_relay_share_envelope_ok() -> None: diff --git a/tests/test_openclaw_agent_core.py b/tests/test_oclaw_agent_core.py similarity index 93% rename from tests/test_openclaw_agent_core.py rename to tests/test_oclaw_agent_core.py index aca842fa..7e3ea2c9 100644 --- a/tests/test_openclaw_agent_core.py +++ b/tests/test_oclaw_agent_core.py @@ -2,12 +2,12 @@ from __future__ import annotations from pathlib import Path -from oclaw.openclaw_runtime.agent_core_run import AgentCoreRunInput, run_agent_core -from oclaw.openclaw_runtime.gateway import OpenClawGateway -from oclaw.openclaw_runtime.types import StandardMessage +from oclaw.runtime.agent_core_run import AgentCoreRunInput, run_agent_core +from oclaw.runtime.gateway import OclawGateway +from oclaw.runtime.types import StandardMessage from oclaw.platform.llm.chat_models import LLMResponse, StaticTextChatModel from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.tools.base import ToolRegistry class _FailOnceModel: @@ -58,7 +58,7 @@ def test_agent_core_run_retries_then_succeeds(tmp_path: Path) -> None: assert out.run_state.attempts[0].status == "retry" assert out.run_state.attempts[1].status == "success" - rows = store.openclaw_attempt_list(run_id=out.run_state.run_id) + rows = store.oclaw_attempt_list(run_id=out.run_state.run_id) assert len(rows) == 2 @@ -146,7 +146,7 @@ def test_agent_core_relay_envelope_error_not_retryable(tmp_path: Path) -> None: def test_gateway_fail_closed_when_executor_missing_model_tools(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "b.sqlite")) sess = store.create_session("t") - gw = OpenClawGateway(store=store) + gw = OclawGateway(store=store) class _BadExecutor: pass diff --git a/tests/test_openclaw_agent_core_trace.py b/tests/test_oclaw_agent_core_trace.py similarity index 89% rename from tests/test_openclaw_agent_core_trace.py rename to tests/test_oclaw_agent_core_trace.py index 2bc7e9d1..ccd7e5a5 100644 --- a/tests/test_openclaw_agent_core_trace.py +++ b/tests/test_oclaw_agent_core_trace.py @@ -4,11 +4,11 @@ from pathlib import Path import pytest -from oclaw.openclaw_runtime.agent_core_run import AgentCoreRunInput, run_agent_core -from oclaw.openclaw_runtime.types import StandardMessage +from oclaw.runtime.agent_core_run import AgentCoreRunInput, run_agent_core +from oclaw.runtime.types import StandardMessage from oclaw.platform.llm.chat_models import StaticTextChatModel from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolRegistry +from oclaw.runtime.tools.base import ToolRegistry def _msg(session_id: str, *, metadata: dict | None = None) -> StandardMessage: @@ -48,21 +48,21 @@ def test_agent_core_run_started_trace_payload_keys(tmp_path: Path, monkeypatch: trace_id=trace_id, parent_span_id=None, max_attempts=1, - openclaw_task_id="task-99", - openclaw_worker_id="worker-zz", + oclaw_task_id="task-99", + oclaw_worker_id="worker-zz", ), ) started = [e for e in events if e.get("event_type") == "run_started"] assert started, events pl = started[0].get("payload") or {} - assert pl.get("pipeline") == "openclaw_agent_core" + assert pl.get("pipeline") == "oclaw_agent_core" assert pl.get("oc_stage") == "run_start" assert pl.get("trace_id") == trace_id assert pl.get("lang") == "zh" assert "run_id" in pl - assert pl.get("openclaw_task_id") == "task-99" - assert pl.get("openclaw_worker_id") == "worker-zz" + assert pl.get("oclaw_task_id") == "task-99" + assert pl.get("oclaw_worker_id") == "worker-zz" assert pl.get("relay_envelope_present") is False assert pl.get("relay_envelope_pointer_count") == 0 diff --git a/tests/test_openclaw_command_parser.py b/tests/test_oclaw_command_parser.py similarity index 90% rename from tests/test_openclaw_command_parser.py rename to tests/test_oclaw_command_parser.py index 746a59b3..9aa20ff7 100644 --- a/tests/test_openclaw_command_parser.py +++ b/tests/test_oclaw_command_parser.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.openclaw_runtime.command_parser import parse_internal_command +from oclaw.runtime.command_parser import parse_internal_command def test_parse_internal_command_basic() -> None: diff --git a/tests/test_openclaw_dynamic_expert.py b/tests/test_oclaw_dynamic_expert.py similarity index 90% rename from tests/test_openclaw_dynamic_expert.py rename to tests/test_oclaw_dynamic_expert.py index ecf5d86c..5b41dbf8 100644 --- a/tests/test_openclaw_dynamic_expert.py +++ b/tests/test_oclaw_dynamic_expert.py @@ -1,7 +1,7 @@ from __future__ import annotations -from oclaw.tools.base import ToolSpec -from oclaw.tools.catalog import _apply_declared_tool_policy +from oclaw.runtime.tools.base import ToolSpec +from oclaw.runtime.tools.catalog import _apply_declared_tool_policy def _tool(name: str, tags: set[str]) -> ToolSpec: diff --git a/tests/test_openclaw_extensions_webhooks_py.py b/tests/test_oclaw_extensions_webhooks_py.py similarity index 98% rename from tests/test_openclaw_extensions_webhooks_py.py rename to tests/test_oclaw_extensions_webhooks_py.py index ce8e5b9b..7d9eba8a 100644 --- a/tests/test_openclaw_extensions_webhooks_py.py +++ b/tests/test_oclaw_extensions_webhooks_py.py @@ -17,7 +17,7 @@ def _load_module(module_name: str, path: Path): def _mount_webhooks_package(): - root = Path(__file__).resolve().parents[1] / "extensions" / "webhooks" + root = Path(__file__).resolve().parents[1] / "runtime" / "extensions" / "webhooks" pkg_root = types.ModuleType("ocx") pkg_root.__path__ = [str(root.parent)] sys.modules["ocx"] = pkg_root @@ -150,7 +150,7 @@ def test_webhooks_handler_auth_and_actions() -> None: missing = handler( { "path": "/hooks/a", - "headers": {"x-openclaw-webhook-secret": "sekret"}, + "headers": {"x-oclaw-webhook-secret": "sekret"}, "json": {"action": "request_cancel", "flowId": "missing", "expectedRevision": 1}, } ) diff --git a/tests/test_openclaw_extensions_whatsapp_py.py b/tests/test_oclaw_extensions_whatsapp_py.py similarity index 96% rename from tests/test_openclaw_extensions_whatsapp_py.py rename to tests/test_oclaw_extensions_whatsapp_py.py index b0132d24..ce5e19b3 100644 --- a/tests/test_openclaw_extensions_whatsapp_py.py +++ b/tests/test_oclaw_extensions_whatsapp_py.py @@ -17,7 +17,7 @@ def _load_module(module_name: str, path: Path): def _mount_whatsapp_module(): - root = Path(__file__).resolve().parents[1] / "extensions" / "whatsapp" + root = Path(__file__).resolve().parents[1] / "runtime" / "extensions" / "whatsapp" pkg_root = types.ModuleType("ocxw") pkg_root.__path__ = [str(root.parent)] sys.modules["ocxw"] = pkg_root diff --git a/tests/test_oclaw_gateway_optimizations.py b/tests/test_oclaw_gateway_optimizations.py index f5308489..28f18693 100644 --- a/tests/test_oclaw_gateway_optimizations.py +++ b/tests/test_oclaw_gateway_optimizations.py @@ -16,7 +16,7 @@ def test_wait_for_agent_job_is_non_blocking_pending(monkeypatch) -> None: return False class DummyStore: - def openclaw_run_get(self, run_id: str) -> None: + def oclaw_run_get(self, run_id: str) -> None: return None def get_messages(self, session_id: str, limit: int) -> list[Any]: @@ -100,7 +100,7 @@ def test_turn_runner_emits_delta_only(monkeypatch) -> None: monkeypatch.setattr(turn_runner, "SqliteStore", lambda _p: DummyStore()) monkeypatch.setattr(turn_runner, "db_path", lambda: "dummy.sqlite") - monkeypatch.setattr(turn_runner, "OpenClawGateway", lambda store: DummyGateway()) + monkeypatch.setattr(turn_runner, "OclawGateway", lambda store: DummyGateway()) monkeypatch.setattr(turn_runner, "build_gateway_executor", lambda *a, **k: object()) conn = DummyConn() diff --git a/tests/test_openclaw_gateway_trace.py b/tests/test_oclaw_gateway_trace.py similarity index 87% rename from tests/test_openclaw_gateway_trace.py rename to tests/test_oclaw_gateway_trace.py index 171629b1..76252a95 100644 --- a/tests/test_openclaw_gateway_trace.py +++ b/tests/test_oclaw_gateway_trace.py @@ -4,8 +4,8 @@ import json import pytest from oclaw.platform.llm.chat_models import LLMResponse -from oclaw.openclaw_runtime.gateway import OpenClawGateway -from oclaw.openclaw_runtime.types import StandardMessage +from oclaw.runtime.gateway import OclawGateway +from oclaw.runtime.types import StandardMessage def test_gateway_async_trace_payload_has_pipeline_and_oc_stage(monkeypatch: pytest.MonkeyPatch) -> None: @@ -18,7 +18,7 @@ def test_gateway_async_trace_payload_has_pipeline_and_oc_stage(monkeypatch: pyte def add_trace_event(self, **kwargs: object) -> None: events.append(dict(kwargs)) - def openclaw_task_create(self, **_kwargs: object) -> object: + def oclaw_task_create(self, **_kwargs: object) -> object: class T: id = "task-1" task_type = "async_turn" @@ -26,9 +26,9 @@ def test_gateway_async_trace_payload_has_pipeline_and_oc_stage(monkeypatch: pyte return T() - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.ensure_worker_started", lambda store: "worker-1") + monkeypatch.setattr("oclaw.runtime.gateway.ensure_worker_started", lambda store: "worker-1") - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-1", tenant_id="t1", @@ -44,7 +44,7 @@ def test_gateway_async_trace_payload_has_pipeline_and_oc_stage(monkeypatch: pyte sent = [e for e in events if e.get("event_type") == "response_sent"] assert sent payload = sent[-1].get("payload") or {} - assert payload.get("pipeline") == "openclaw_gateway" + assert payload.get("pipeline") == "oclaw_gateway" assert payload.get("oc_stage") == "response" assert "elapsed_ms_since_gateway_start" in payload @@ -59,7 +59,7 @@ def test_gateway_received_trace_includes_relay_pointer_stats(monkeypatch: pytest def add_trace_event(self, **kwargs: object) -> None: events.append(dict(kwargs)) - def openclaw_task_create(self, **_kwargs: object) -> object: + def oclaw_task_create(self, **_kwargs: object) -> object: class T: id = "task-2" task_type = "async_turn" @@ -67,9 +67,9 @@ def test_gateway_received_trace_includes_relay_pointer_stats(monkeypatch: pytest return T() - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.ensure_worker_started", lambda store: "worker-1") + monkeypatch.setattr("oclaw.runtime.gateway.ensure_worker_started", lambda store: "worker-1") - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-2", tenant_id="t1", @@ -104,7 +104,7 @@ def test_gateway_async_task_payload_preserves_relay_envelope(monkeypatch: pytest def add_trace_event(self, **_kwargs: object) -> None: return None - def openclaw_task_create(self, **kwargs: object) -> object: + def oclaw_task_create(self, **kwargs: object) -> object: captured.update(kwargs) class T: @@ -114,8 +114,8 @@ def test_gateway_async_task_payload_preserves_relay_envelope(monkeypatch: pytest return T() - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.ensure_worker_started", lambda store: "worker-1") - gw = OpenClawGateway(store=Store()) + monkeypatch.setattr("oclaw.runtime.gateway.ensure_worker_started", lambda store: "worker-1") + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-3", tenant_id="t1", @@ -150,7 +150,7 @@ def test_gateway_async_task_payload_preserves_acp_ids(monkeypatch: pytest.Monkey def add_trace_event(self, **_kwargs: object) -> None: return None - def openclaw_task_create(self, **kwargs: object) -> object: + def oclaw_task_create(self, **kwargs: object) -> object: captured.update(kwargs) class T: @@ -160,8 +160,8 @@ def test_gateway_async_task_payload_preserves_acp_ids(monkeypatch: pytest.Monkey return T() - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.ensure_worker_started", lambda store: "worker-1") - gw = OpenClawGateway(store=Store()) + monkeypatch.setattr("oclaw.runtime.gateway.ensure_worker_started", lambda store: "worker-1") + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-4", tenant_id="t1", @@ -196,7 +196,7 @@ def test_gateway_expert_mode_uses_requested_specialist() -> None: chosen["sid"] = sid return _Exec() - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-e1", tenant_id="t1", @@ -236,7 +236,7 @@ def test_gateway_comprehensive_mode_manager_first_selects_specialist(monkeypatch chosen["sid"] = sid return _Exec() - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-c1", tenant_id="t1", @@ -276,9 +276,9 @@ def test_gateway_comprehensive_mode_dynamic_agent_fallback_success(monkeypatch: self.model = model self.tools = None - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.build_ephemeral_executor", lambda *args, **kwargs: _Exec()) + monkeypatch.setattr("oclaw.runtime.gateway.build_ephemeral_executor", lambda *args, **kwargs: _Exec()) - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-d1", tenant_id="t1", @@ -323,7 +323,7 @@ def test_gateway_comprehensive_mode_dynamic_agent_invalid_falls_back_generalist( picked["sid"] = sid return _Exec() - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-d2", tenant_id="t1", @@ -350,7 +350,7 @@ def test_gateway_command_hook_uses_parsed_command_and_context(monkeypatch: pytes def add_trace_event(self, **_kwargs: object) -> None: return None - def openclaw_task_create(self, **_kwargs: object) -> object: + def oclaw_task_create(self, **_kwargs: object) -> object: class T: id = "task-cmd-1" task_type = "async_turn" @@ -358,19 +358,19 @@ def test_gateway_command_hook_uses_parsed_command_and_context(monkeypatch: pytes return T() - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.ensure_worker_started", lambda store: "worker-1") - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.initialize_hooks_runtime", lambda **kwargs: 0) + monkeypatch.setattr("oclaw.runtime.gateway.ensure_worker_started", lambda store: "worker-1") + monkeypatch.setattr("oclaw.runtime.gateway.initialize_hooks_runtime", lambda **kwargs: 0) monkeypatch.setattr( - "oclaw.openclaw_runtime.gateway.get_active_hooks_config", + "oclaw.runtime.gateway.get_active_hooks_config", lambda: {"hooks": {"internal": {"enabled": True, "entries": {"session-memory": {"messages": 33}}}}}, ) def _capture(**kwargs): calls.append(dict(kwargs)) - monkeypatch.setattr("oclaw.openclaw_runtime.gateway.trigger_hook_event", _capture) + monkeypatch.setattr("oclaw.runtime.gateway.trigger_hook_event", _capture) - gw = OpenClawGateway(store=Store()) + gw = OclawGateway(store=Store()) msg = StandardMessage( session_id="sid-cmd-1", tenant_id="t1", @@ -420,8 +420,8 @@ def test_tabular_system_hint_uses_configured_preview_and_rows_read( cfg_path.write_text(json.dumps(cfg, ensure_ascii=False), encoding="utf-8") monkeypatch.setenv("AIA_OCLAW_CONFIG_PATH", str(cfg_path)) - zh_hint = OpenClawGateway._tabular_query_system_hint("zh") - en_hint = OpenClawGateway._tabular_query_system_hint("en") + zh_hint = OclawGateway._tabular_query_system_hint("zh") + en_hint = OclawGateway._tabular_query_system_hint("en") assert "前30行" in zh_hint assert "5000行" in zh_hint diff --git a/tests/test_openclaw_generalist_executor.py b/tests/test_oclaw_generalist_executor.py similarity index 93% rename from tests/test_openclaw_generalist_executor.py rename to tests/test_oclaw_generalist_executor.py index 1567d5ed..d183aeaf 100644 --- a/tests/test_openclaw_generalist_executor.py +++ b/tests/test_oclaw_generalist_executor.py @@ -3,7 +3,7 @@ from __future__ import annotations import os from pathlib import Path -from oclaw.agents.factory import build_gateway_executor +from oclaw.runtime.agents.factory import build_gateway_executor from oclaw.platform.persistence.sqlite_store import SqliteStore diff --git a/tests/test_openclaw_hooks_runtime.py b/tests/test_oclaw_hooks_runtime.py similarity index 74% rename from tests/test_openclaw_hooks_runtime.py rename to tests/test_oclaw_hooks_runtime.py index 76d874fa..1b93baab 100644 --- a/tests/test_openclaw_hooks_runtime.py +++ b/tests/test_oclaw_hooks_runtime.py @@ -3,28 +3,28 @@ from __future__ import annotations import json from pathlib import Path -from oclaw.openclaw_runtime.hooks_runtime import resolve_runtime_config +from oclaw.runtime.hooks_runtime import resolve_runtime_config def test_resolve_runtime_config_from_env_json(monkeypatch) -> None: monkeypatch.setenv( - "OPENCLAW_RUNTIME_CONFIG_JSON", + "OCLAW_RUNTIME_CONFIG_JSON", json.dumps({"hooks": {"internal": {"enabled": False, "entries": {"x": {"enabled": False}}}}}), ) - monkeypatch.delenv("OPENCLAW_CONFIG_PATH", raising=False) + monkeypatch.delenv("OCLAW_CONFIG_PATH", raising=False) cfg = resolve_runtime_config() assert ((cfg.get("hooks") or {}).get("internal") or {}).get("enabled") is False assert ((cfg.get("hooks") or {}).get("internal") or {}).get("entries", {}).get("x", {}).get("enabled") is False def test_resolve_runtime_config_from_config_path(monkeypatch, tmp_path: Path) -> None: - cfg_path = tmp_path / "openclaw.custom.json" + cfg_path = tmp_path / "oclaw.custom.json" cfg_path.write_text( json.dumps({"hooks": {"internal": {"enabled": True, "entries": {"session-memory": {"messages": 99}}}}}), encoding="utf-8", ) - monkeypatch.delenv("OPENCLAW_RUNTIME_CONFIG_JSON", raising=False) - monkeypatch.setenv("OPENCLAW_CONFIG_PATH", str(cfg_path)) + monkeypatch.delenv("OCLAW_RUNTIME_CONFIG_JSON", raising=False) + monkeypatch.setenv("OCLAW_CONFIG_PATH", str(cfg_path)) cfg = resolve_runtime_config() assert ((cfg.get("hooks") or {}).get("internal") or {}).get("enabled") is True assert ( diff --git a/tests/test_openclaw_memory_stage.py b/tests/test_oclaw_memory_stage.py similarity index 81% rename from tests/test_openclaw_memory_stage.py rename to tests/test_oclaw_memory_stage.py index 068f09ed..c181693a 100644 --- a/tests/test_openclaw_memory_stage.py +++ b/tests/test_oclaw_memory_stage.py @@ -4,14 +4,14 @@ from pathlib import Path import pytest -from oclaw.openclaw_runtime.memory_stage import after_turn_memory, render_memory_context_block -from oclaw.openclaw_runtime.types import OpenClawMemoryContext +from oclaw.runtime.memory_stage import after_turn_memory, render_memory_context_block +from oclaw.runtime.types import OclawMemoryContext from oclaw.platform.persistence.sqlite_store import SqliteStore def test_render_memory_context_block_contains_sections() -> None: text = render_memory_context_block( - OpenClawMemoryContext( + OclawMemoryContext( short_term=("user prefers concise summary",), semantic_hits=({"content": "meeting summary style: bullets only", "score": 0.77},), ) @@ -28,7 +28,7 @@ def test_after_turn_memory_invokes_maybe_write_turn_memory(tmp_path: Path, monke calls.append({"store": store, **kwargs}) return {"ok": True, "written": 0, "reason": "stub"} - monkeypatch.setattr("oclaw.orchestration.memory.maybe_write_turn_memory", _fake_maybe_write) + monkeypatch.setattr("oclaw.runtime.orchestration.memory.maybe_write_turn_memory", _fake_maybe_write) store = SqliteStore(str(tmp_path / "m.sqlite")) store.create_session("s1") diff --git a/tests/test_openclaw_project_context_prompt.py b/tests/test_oclaw_project_context_prompt.py similarity index 75% rename from tests/test_openclaw_project_context_prompt.py rename to tests/test_oclaw_project_context_prompt.py index fd3366b6..5ac6a4d9 100644 --- a/tests/test_openclaw_project_context_prompt.py +++ b/tests/test_oclaw_project_context_prompt.py @@ -2,7 +2,7 @@ from __future__ import annotations from pathlib import Path -from oclaw.openclaw_runtime.project_context_prompt import build_project_context_block +from oclaw.runtime.project_context_prompt import build_project_context_block class _Store: @@ -14,11 +14,11 @@ class _Store: def test_build_project_context_block_reads_workspace_bootstrap(monkeypatch, tmp_path: Path) -> None: - ws = tmp_path / "oclaw" / "agent" / "workspace-main" + ws = tmp_path / "oclaw" / "runtime" / "assets" / "agent_workspaces" / "workspace-main" ws.mkdir(parents=True, exist_ok=True) (ws / "AGENTS.md").write_text("agents rules", encoding="utf-8") (ws / "SOUL.md").write_text("soul voice", encoding="utf-8") - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) out = build_project_context_block(store=_Store()) assert "[project_context]" in out assert "[AGENTS.md]" in out @@ -26,14 +26,14 @@ def test_build_project_context_block_reads_workspace_bootstrap(monkeypatch, tmp_ def test_build_project_context_block_empty_when_no_files(monkeypatch, tmp_path: Path) -> None: - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) out = build_project_context_block(store=_Store()) assert out == "" def test_build_project_context_block_fallback_to_project_root(monkeypatch, tmp_path: Path) -> None: (tmp_path / "AGENTS.md").write_text("legacy root agents", encoding="utf-8") - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) out = build_project_context_block(store=_Store()) assert "[AGENTS.md]" in out assert "legacy root agents" in out @@ -43,7 +43,7 @@ def test_build_project_context_block_legacy_workspace_fallback(monkeypatch, tmp_ ws = tmp_path / "oclaw" / "workspace" ws.mkdir(parents=True, exist_ok=True) (ws / "AGENTS.md").write_text("legacy workspace agents", encoding="utf-8") - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) out = build_project_context_block(store=_Store()) assert "[AGENTS.md]" in out assert "legacy workspace agents" in out @@ -53,29 +53,29 @@ def test_build_project_context_block_workspace_main_fallback(monkeypatch, tmp_pa ws = tmp_path / "oclaw" / "workspace-main" ws.mkdir(parents=True, exist_ok=True) (ws / "AGENTS.md").write_text("legacy workspace-main agents", encoding="utf-8") - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) out = build_project_context_block(store=_Store()) assert "[AGENTS.md]" in out assert "legacy workspace-main agents" in out def test_build_project_context_block_triggers_bootstrap_for_each_root(monkeypatch, tmp_path: Path) -> None: - ws_agent = tmp_path / "oclaw" / "agent" / "workspace-main" + ws_agent = tmp_path / "oclaw" / "runtime" / "assets" / "agent_workspaces" / "workspace-main" ws_main = tmp_path / "oclaw" / "workspace-main" ws_agent.mkdir(parents=True, exist_ok=True) ws_main.mkdir(parents=True, exist_ok=True) (ws_agent / "AGENTS.md").write_text("agent root", encoding="utf-8") (ws_main / "AGENTS.md").write_text("main root", encoding="utf-8") - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) calls: list[dict] = [] def _capture(**kwargs): calls.append(dict(kwargs)) - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.trigger_hook_event", _capture) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.trigger_hook_event", _capture) monkeypatch.setattr( - "oclaw.openclaw_runtime.project_context_prompt.get_active_hooks_config", + "oclaw.runtime.project_context_prompt.get_active_hooks_config", lambda: {"hooks": {"internal": {"enabled": True}}}, ) _ = build_project_context_block(store=_Store()) @@ -87,22 +87,22 @@ def test_build_project_context_block_triggers_bootstrap_for_each_root(monkeypatc def test_project_context_bootstrap_includes_agent_id_when_config_matches(monkeypatch, tmp_path: Path) -> None: - ws_agent = tmp_path / "oclaw" / "agent" / "workspace-main" + ws_agent = tmp_path / "oclaw" / "runtime" / "assets" / "agent_workspaces" / "workspace-main" ws_social = tmp_path / "oclaw" / "workspace-social" ws_agent.mkdir(parents=True, exist_ok=True) ws_social.mkdir(parents=True, exist_ok=True) (ws_agent / "AGENTS.md").write_text("agent root", encoding="utf-8") (ws_social / "AGENTS.md").write_text("social root", encoding="utf-8") - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.PROJECT_ROOT", tmp_path) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.PROJECT_ROOT", tmp_path) calls: list[dict] = [] def _capture(**kwargs): calls.append(dict(kwargs)) - monkeypatch.setattr("oclaw.openclaw_runtime.project_context_prompt.trigger_hook_event", _capture) + monkeypatch.setattr("oclaw.runtime.project_context_prompt.trigger_hook_event", _capture) monkeypatch.setattr( - "oclaw.openclaw_runtime.project_context_prompt.get_active_hooks_config", + "oclaw.runtime.project_context_prompt.get_active_hooks_config", lambda: { "hooks": {"internal": {"enabled": True}}, "agents": { diff --git a/tests/test_openclaw_relay_pointer_protocol.py b/tests/test_oclaw_relay_pointer_protocol.py similarity index 92% rename from tests/test_openclaw_relay_pointer_protocol.py rename to tests/test_oclaw_relay_pointer_protocol.py index 50d55f06..b29549fd 100644 --- a/tests/test_openclaw_relay_pointer_protocol.py +++ b/tests/test_oclaw_relay_pointer_protocol.py @@ -1,7 +1,7 @@ from __future__ import annotations -from oclaw.openclaw_runtime.relay_pointer import build_manifest_from_attachment_refs -from oclaw.openclaw_runtime.types import RelayShareEnvelope, SharedAttachmentManifest, SharedFilePointer +from oclaw.runtime.relay_pointer import build_manifest_from_attachment_refs +from oclaw.runtime.types import RelayShareEnvelope, SharedAttachmentManifest, SharedFilePointer def test_shared_pointer_roundtrip_with_extra_fields() -> None: diff --git a/tests/test_openclaw_relay_pointer_security.py b/tests/test_oclaw_relay_pointer_security.py similarity index 96% rename from tests/test_openclaw_relay_pointer_security.py rename to tests/test_oclaw_relay_pointer_security.py index 522b3ffb..d07fd037 100644 --- a/tests/test_openclaw_relay_pointer_security.py +++ b/tests/test_oclaw_relay_pointer_security.py @@ -5,7 +5,7 @@ from pathlib import Path import pytest -from oclaw.openclaw_runtime.relay_pointer import ( +from oclaw.runtime.relay_pointer import ( RelayPointerError, build_pointer_uri, decode_base64_payload, diff --git a/tests/test_openclaw_role_prompt_paths.py b/tests/test_oclaw_role_prompt_paths.py similarity index 70% rename from tests/test_openclaw_role_prompt_paths.py rename to tests/test_oclaw_role_prompt_paths.py index 29fccc96..6a1a0265 100644 --- a/tests/test_openclaw_role_prompt_paths.py +++ b/tests/test_oclaw_role_prompt_paths.py @@ -1,9 +1,9 @@ from __future__ import annotations -from oclaw.agents.specialists import default_system_prefix_for_specialist +from oclaw.runtime.agents.specialists import default_system_prefix_for_specialist -def test_specialist_prompts_loaded_from_openclaw_tree() -> None: +def test_specialist_prompts_loaded_from_oclaw_tree() -> None: ops = default_system_prefix_for_specialist("ops", "zh") gen = default_system_prefix_for_specialist("generalist", "zh") img = default_system_prefix_for_specialist("image", "zh") diff --git a/tests/test_openclaw_router.py b/tests/test_oclaw_router.py similarity index 92% rename from tests/test_openclaw_router.py rename to tests/test_oclaw_router.py index 1a1a3e89..d42a537d 100644 --- a/tests/test_openclaw_router.py +++ b/tests/test_oclaw_router.py @@ -1,8 +1,8 @@ from __future__ import annotations from oclaw.platform.llm.chat_models import LLMResponse -from oclaw.openclaw_runtime.router import decide_route -from oclaw.openclaw_runtime.types import StandardMessage, normalize_interaction_mode, normalize_requested_specialist +from oclaw.runtime.router import decide_route +from oclaw.runtime.types import StandardMessage, normalize_interaction_mode, normalize_requested_specialist def _msg(text: str, attachments: list[dict] | None = None) -> StandardMessage: @@ -41,7 +41,7 @@ class _RouterJsonModel: def test_router_llm_json_mode_uses_model() -> None: class Store: def get_setting(self, key: str) -> str: - if key == "AIA_OPENCLAW_ROUTER_MODE": + if key == "AIA_OCLAW_ROUTER_MODE": return "llm_json" return "" @@ -57,7 +57,7 @@ def test_router_llm_json_mode_uses_model() -> None: def test_router_llm_json_invalid_json_falls_back_to_rule() -> None: class Store: def get_setting(self, key: str) -> str: - if key == "AIA_OPENCLAW_ROUTER_MODE": + if key == "AIA_OCLAW_ROUTER_MODE": return "llm_json" return "" @@ -69,7 +69,7 @@ def test_router_llm_json_invalid_json_falls_back_to_rule() -> None: def test_router_llm_json_tolerates_extra_dynamic_agent_fields() -> None: class Store: def get_setting(self, key: str) -> str: - if key == "AIA_OPENCLAW_ROUTER_MODE": + if key == "AIA_OCLAW_ROUTER_MODE": return "llm_json" return "" diff --git a/tests/test_openclaw_skill_manifest.py b/tests/test_oclaw_skill_manifest.py similarity index 83% rename from tests/test_openclaw_skill_manifest.py rename to tests/test_oclaw_skill_manifest.py index 69d0994b..91974d16 100644 --- a/tests/test_openclaw_skill_manifest.py +++ b/tests/test_oclaw_skill_manifest.py @@ -2,11 +2,11 @@ from __future__ import annotations from pathlib import Path -from oclaw.openclaw_runtime.skills import build_skill_manifest, discover_workspace_skill_manifests, load_skill_manifest -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.skills import build_skill_manifest, discover_workspace_skill_manifests, load_skill_manifest +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec -def test_load_skill_manifest_with_openclaw_metadata(tmp_path: Path) -> None: +def test_load_skill_manifest_with_oclaw_metadata(tmp_path: Path) -> None: d = tmp_path / "skills" / "demo" d.mkdir(parents=True, exist_ok=True) (d / "SKILL.md").write_text( @@ -15,7 +15,7 @@ name: demo description: demo skill user-invocable: true disable-model-invocation: false -metadata: {"openclaw":{"install":[{"id":"i1","kind":"node","package":"foo"}]}} +metadata: {"oclaw":{"install":[{"id":"i1","kind":"node","package":"foo"}]}} --- # Demo diff --git a/tests/test_openclaw_startup_workspace_resolution.py b/tests/test_oclaw_startup_workspace_resolution.py similarity index 100% rename from tests/test_openclaw_startup_workspace_resolution.py rename to tests/test_oclaw_startup_workspace_resolution.py diff --git a/tests/test_openclaw_system_prompt.py b/tests/test_oclaw_system_prompt.py similarity index 59% rename from tests/test_openclaw_system_prompt.py rename to tests/test_oclaw_system_prompt.py index ba446c64..f2a545c1 100644 --- a/tests/test_openclaw_system_prompt.py +++ b/tests/test_oclaw_system_prompt.py @@ -2,10 +2,10 @@ from __future__ import annotations from pathlib import Path -from oclaw.openclaw_runtime.system_prompt import build_openclaw_executor_system_prompt -from oclaw.openclaw_runtime.types import OpenClawMemoryContext +from oclaw.runtime.system_prompt import build_oclaw_executor_system_prompt +from oclaw.runtime.types import OclawMemoryContext from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec def _spec(name: str) -> ToolSpec: @@ -18,45 +18,45 @@ def _spec(name: str) -> ToolSpec: ) -def test_build_openclaw_executor_system_prompt_includes_skills_block(tmp_path: Path) -> None: +def test_build_oclaw_executor_system_prompt_includes_skills_block(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "sp.sqlite")) store.create_session("s") reg = ToolRegistry([_spec("demo_tool")]) - out = build_openclaw_executor_system_prompt( + out = build_oclaw_executor_system_prompt( store=store, tools=reg, base_url="", base_system="You are helpful.", - memory_context=OpenClawMemoryContext(), + memory_context=OclawMemoryContext(), lang="zh", ) assert "" in out -def test_build_openclaw_executor_system_prompt_without_tools_has_no_skills_block(tmp_path: Path) -> None: +def test_build_oclaw_executor_system_prompt_without_tools_has_no_skills_block(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "sp1.sqlite")) store.create_session("s") - out = build_openclaw_executor_system_prompt( + out = build_oclaw_executor_system_prompt( store=store, tools=None, base_url="", base_system=" plain ", - memory_context=OpenClawMemoryContext(), + memory_context=OclawMemoryContext(), lang="zh", ) assert "" not in out -def test_build_openclaw_executor_system_prompt_with_tools_has_skills(tmp_path: Path) -> None: +def test_build_oclaw_executor_system_prompt_with_tools_has_skills(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "sp2.sqlite")) store.create_session("s") reg = ToolRegistry([_spec("x")]) - out = build_openclaw_executor_system_prompt( + out = build_oclaw_executor_system_prompt( store=store, tools=reg, base_url="", base_system="base", - memory_context=OpenClawMemoryContext(), + memory_context=OclawMemoryContext(), lang="zh", ) assert "" in out diff --git a/tests/test_openclaw_task_queue.py b/tests/test_oclaw_task_queue.py similarity index 67% rename from tests/test_openclaw_task_queue.py rename to tests/test_oclaw_task_queue.py index 45ab1c02..6697e73d 100644 --- a/tests/test_openclaw_task_queue.py +++ b/tests/test_oclaw_task_queue.py @@ -5,25 +5,25 @@ from pathlib import Path from oclaw.platform.persistence.sqlite_store import SqliteStore -def test_openclaw_task_status_flow(tmp_path: Path) -> None: +def test_oclaw_task_status_flow(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "task.sqlite")) - task = store.openclaw_task_create( + task = store.oclaw_task_create( tenant_id="tenant-a", session_id="session-a", payload={"text": "hello"}, ) assert task.status == "queued" - claimed = store.openclaw_task_claim(worker_id="worker-1") + claimed = store.oclaw_task_claim(worker_id="worker-1") assert claimed is not None assert claimed.id == task.id assert claimed.status == "claimed" assert claimed.claimed_by == "worker-1" assert claimed.attempt_count >= 1 - ok = store.openclaw_task_finish(task_id=task.id, result={"reply_text": "done"}) + ok = store.oclaw_task_finish(task_id=task.id, result={"reply_text": "done"}) assert ok - done = store.openclaw_task_get(task_id=task.id) + done = store.oclaw_task_get(task_id=task.id) assert done is not None assert done.status == "done" assert "done" in done.result diff --git a/tests/test_openclaw_tool_result_guard.py b/tests/test_oclaw_tool_result_guard.py similarity index 78% rename from tests/test_openclaw_tool_result_guard.py rename to tests/test_oclaw_tool_result_guard.py index 7a0fcdeb..3e1757fa 100644 --- a/tests/test_openclaw_tool_result_guard.py +++ b/tests/test_oclaw_tool_result_guard.py @@ -2,12 +2,12 @@ from __future__ import annotations from pathlib import Path -from oclaw.openclaw_runtime.direct_loop import _OPENCLAW_TOOL_RESULT_HARD_CAP_CHARS, _build_model_context +from oclaw.runtime.direct_loop import _OCLAW_TOOL_RESULT_HARD_CAP_CHARS, _build_model_context from oclaw.platform.llm.chat_models import RuleBasedChatModel from oclaw.platform.persistence.sqlite_store import SqliteStore -def test_openclaw_tool_result_context_guard_truncates_large_tool_message(tmp_path: Path) -> None: +def test_oclaw_tool_result_context_guard_truncates_large_tool_message(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "ops.sqlite")) sess = store.create_session("t") # Ensure the tool message is not a dangling orphan (some providers require pairing). @@ -17,7 +17,7 @@ def test_openclaw_tool_result_context_guard_truncates_large_tool_message(tmp_pat content="", tool_calls=[{"id": "c1", "name": "echo", "arguments": {"x": 1}}], ) - huge = "X" * (_OPENCLAW_TOOL_RESULT_HARD_CAP_CHARS + 5000) + huge = "X" * (_OCLAW_TOOL_RESULT_HARD_CAP_CHARS + 5000) store.add_message( session_id=sess.id, role="tool", @@ -38,6 +38,6 @@ def test_openclaw_tool_result_context_guard_truncates_large_tool_message(tmp_pat tool_msgs = [m for m in msgs if m.get("role") == "tool"] assert tool_msgs, msgs guarded = str(tool_msgs[-1].get("content") or "") - assert len(guarded) <= _OPENCLAW_TOOL_RESULT_HARD_CAP_CHARS + 2000 + assert len(guarded) <= _OCLAW_TOOL_RESULT_HARD_CAP_CHARS + 2000 assert "_tool_result_guarded" in guarded diff --git a/tests/test_ops_cli.py b/tests/test_ops_cli.py index 7e09e54d..b3b752c9 100644 --- a/tests/test_ops_cli.py +++ b/tests/test_ops_cli.py @@ -2,7 +2,7 @@ from __future__ import annotations from pathlib import Path -import oclaw.ops.main as ops_main +import oclaw.runtime.operations.main as ops_main def test_gateway_start_uses_fastapi_only(monkeypatch) -> None: diff --git a/tests/test_prompt_ci_guard.py b/tests/test_prompt_ci_guard.py index ac9eb88c..a7d0500d 100644 --- a/tests/test_prompt_ci_guard.py +++ b/tests/test_prompt_ci_guard.py @@ -6,16 +6,16 @@ from oclaw.platform.config.paths import PROJECT_ROOT _PROMPT_CRITICAL_FILES = ( - "oclaw/chat/agent.py", - "oclaw/agents/network_ops_agent.py", - "oclaw/agents/factory.py", - "oclaw/chat/agent_messages.py", - "oclaw/openclaw_runtime/system_prompt.py", - "oclaw/openclaw_runtime/memory_stage.py", - "oclaw/openclaw_runtime/project_context_prompt.py", - "oclaw/chat/agent_errors.py", + "oclaw/runtime/chat/agent.py", + "oclaw/runtime/agents/network_ops_agent.py", + "oclaw/runtime/agents/factory.py", + "oclaw/runtime/chat/agent_messages.py", + "oclaw/runtime/system_prompt.py", + "oclaw/runtime/memory_stage.py", + "oclaw/runtime/project_context_prompt.py", + "oclaw/runtime/chat/agent_errors.py", "oclaw/platform/llm/image_message_client.py", - "oclaw/openclaw_runtime/gateway.py", + "oclaw/runtime/gateway.py", ) @@ -25,7 +25,7 @@ def test_prompt_critical_paths_use_prompt_templates() -> None: assert ( "render_prompt(" in text or "render_prompt_for_lang(" in text - or "render_openclaw_prompt(" in text + or "render_runtime_prompt(" in text ), ( f"{rel} must render prompts from oclaw/prompts*.md" ) @@ -34,7 +34,7 @@ def test_prompt_critical_paths_use_prompt_templates() -> None: def test_prompt_markdown_frontmatter_keys_present() -> None: roots = [ PROJECT_ROOT / "oclaw" / "prompts", - PROJECT_ROOT / "oclaw" / "prompts_openclaw", + PROJECT_ROOT / "oclaw" / "prompts_runtime", ] for prompts_root in roots: for p in prompts_root.rglob("*.md"): diff --git a/tests/test_prompt_hardcode_guard.py b/tests/test_prompt_hardcode_guard.py index 6d4a7b83..1bdc32c8 100644 --- a/tests/test_prompt_hardcode_guard.py +++ b/tests/test_prompt_hardcode_guard.py @@ -12,18 +12,18 @@ def _read(rel: str) -> str: def test_no_legacy_inline_manager_prompts() -> None: # Manager orchestrator was removed; keep this test as a smoke-guard for any new inline # manager-style prompt leakage into the runtime entrypoints. - content = _read("oclaw/openclaw_runtime/gateway.py") + content = _read("oclaw/runtime/gateway.py") assert "【重要:只返回合法 JSON】" not in content assert "你是 AI 助手的路由(Routing)智能体" not in content def test_no_legacy_inline_specialist_prompts() -> None: - content = _read("oclaw/agents/specialists.py") + content = _read("oclaw/runtime/agents/specialists.py") assert "【最高优先级执行规则(必须遵守)】" not in content assert "【职责范围】" not in content def test_no_legacy_inline_runtime_prompt() -> None: - content = _read("oclaw/chat/agent.py") + content = _read("oclaw/runtime/chat/agent.py") assert "你是一个通用 AI 助手。" not in content diff --git a/tests/test_prompt_loader.py b/tests/test_prompt_loader.py index 3193b0c8..74fed43a 100644 --- a/tests/test_prompt_loader.py +++ b/tests/test_prompt_loader.py @@ -2,7 +2,7 @@ from __future__ import annotations import pytest -from oclaw.prompts.loader import load_openclaw_prompt_doc, load_prompt_doc, render_openclaw_prompt, render_prompt +from oclaw.prompts.loader import load_runtime_prompt_doc, load_prompt_doc, render_runtime_prompt, render_prompt def test_load_prompt_doc_with_frontmatter() -> None: @@ -26,14 +26,14 @@ def test_render_prompt_success() -> None: assert "ok=true" in out -def test_load_openclaw_prompt_doc_with_frontmatter() -> None: - doc = load_openclaw_prompt_doc("roles/specialists/ops/system.md") +def test_load_runtime_prompt_doc_with_frontmatter() -> None: + doc = load_runtime_prompt_doc("roles/specialists/ops/system.md") assert doc.frontmatter.get("title") == "oclaw_role_specialist_ops_system" assert "你是网络运维专家" in doc.body -def test_render_openclaw_prompt_success() -> None: - out = render_openclaw_prompt( +def test_render_runtime_prompt_success() -> None: + out = render_runtime_prompt( "runtime/project_context_block.md", variables={"project_context": "[AGENTS.md]\nfoo"}, strict=True, diff --git a/tests/test_prompt_runtime_paths.py b/tests/test_prompt_runtime_paths.py index 6a66f192..102c108a 100644 --- a/tests/test_prompt_runtime_paths.py +++ b/tests/test_prompt_runtime_paths.py @@ -1,12 +1,12 @@ from __future__ import annotations -from oclaw.openclaw_runtime.memory_stage import render_memory_context_block -from oclaw.openclaw_runtime.types import OpenClawMemoryContext -from oclaw.prompts.loader import render_openclaw_prompt +from oclaw.runtime.memory_stage import render_memory_context_block +from oclaw.runtime.types import OclawMemoryContext +from oclaw.prompts.loader import render_runtime_prompt def test_router_prompt_renders() -> None: - txt = render_openclaw_prompt( + txt = render_runtime_prompt( "router/decide_route.md", variables={"user_text": "hi", "has_attachments": "no"}, strict=True, @@ -17,7 +17,7 @@ def test_router_prompt_renders() -> None: def test_memory_context_markdown_block() -> None: - ctx = OpenClawMemoryContext( + ctx = OclawMemoryContext( short_term=("用户想排查网络问题",), semantic_hits=({"content": "上次 DNS 配置异常", "score": 0.91},), ) diff --git a/tests/test_replay_tool_call_id.py b/tests/test_replay_tool_call_id.py index aa2be8d9..089fb58f 100644 --- a/tests/test_replay_tool_call_id.py +++ b/tests/test_replay_tool_call_id.py @@ -1,4 +1,4 @@ -"""Regression tests for OpenClaw-style tool_call_id replay (see src/platform/llm/tool_call_id.py).""" +"""Regression tests for Oclaw-style tool_call_id replay (see src/platform/llm/tool_call_id.py).""" from __future__ import annotations diff --git a/tests/test_runtime_streaming_contract.py b/tests/test_runtime_streaming_contract.py index 4cfe726e..90bf554f 100644 --- a/tests/test_runtime_streaming_contract.py +++ b/tests/test_runtime_streaming_contract.py @@ -2,9 +2,9 @@ from __future__ import annotations from pathlib import Path -from oclaw.openclaw_runtime.direct_loop import run_openclaw_direct_loop +from oclaw.runtime.direct_loop import run_oclaw_direct_loop from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec from oclaw.platform.llm.transports.base import LLMResponse, LLMToolCall @@ -36,7 +36,7 @@ def test_direct_loop_propagates_on_token_streaming(tmp_path: Path) -> None: store = SqliteStore(str(tmp_path / "s.sqlite")) sess = store.create_session("t") seen: list[str] = [] - out = run_openclaw_direct_loop( + out = run_oclaw_direct_loop( store=store, session_id=sess.id, lang="zh", @@ -68,7 +68,7 @@ def test_direct_loop_supports_multi_round_tool_then_final(tmp_path: Path) -> Non ) ] ) - out = run_openclaw_direct_loop( + out = run_oclaw_direct_loop( store=store, session_id=sess.id, lang="zh", diff --git a/tests/test_skill_executor_trace.py b/tests/test_skill_executor_trace.py index 44e79687..54ba03f8 100644 --- a/tests/test_skill_executor_trace.py +++ b/tests/test_skill_executor_trace.py @@ -2,8 +2,8 @@ from __future__ import annotations from types import SimpleNamespace -from oclaw.openclaw_runtime.skill_executor import SkillExecutionContext, SkillExecutor -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.skill_executor import SkillExecutionContext, SkillExecutor +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec class _DummyStore: diff --git a/tests/test_skill_installer.py b/tests/test_skill_installer.py index 5110469b..7f42438a 100644 --- a/tests/test_skill_installer.py +++ b/tests/test_skill_installer.py @@ -3,7 +3,7 @@ from __future__ import annotations from pathlib import Path import zipfile -from oclaw.openclaw_runtime.skill_installer import ( +from oclaw.runtime.skill_installer import ( auto_install_skill_from_payload, create_skill_from_template, install_skill_from_local_dir, @@ -61,7 +61,7 @@ def test_install_skill_from_registry_archive_file_url(tmp_path: Path) -> None: inner = pkg_dir / "demo" inner.mkdir(parents=True, exist_ok=True) (inner / "SKILL.md").write_text( - "---\nname: reg_demo\ndescription: x\nmetadata: {\"openclaw\":{}}\n---\n", + "---\nname: reg_demo\ndescription: x\nmetadata: {\"oclaw\":{}}\n---\n", encoding="utf-8", ) archive = tmp_path / "reg.zip" @@ -83,7 +83,7 @@ def test_install_skill_from_clawhub_page_url(tmp_path: Path, monkeypatch) -> Non inner = pkg_dir / "demo" inner.mkdir(parents=True, exist_ok=True) (inner / "SKILL.md").write_text( - "---\nname: reg_clawhub_demo\ndescription: x\nmetadata: {\"openclaw\":{}}\n---\n", + "---\nname: reg_clawhub_demo\ndescription: x\nmetadata: {\"oclaw\":{}}\n---\n", encoding="utf-8", ) archive = tmp_path / "reg_clawhub.zip" @@ -96,7 +96,7 @@ def test_install_skill_from_clawhub_page_url(tmp_path: Path, monkeypatch) -> Non captured["slug"] = slug return {"archiveUrl": archive.resolve().as_uri()} - monkeypatch.setattr("oclaw.tools.skills.clawhub_client.get_skill_detail", _mock_get_detail) + monkeypatch.setattr("oclaw.runtime.tools.skills.clawhub_client.get_skill_detail", _mock_get_detail) out = install_skill_from_registry_archive( store=store, @@ -114,7 +114,7 @@ def test_install_local_allows_sh_files(tmp_path: Path) -> None: src = tmp_path / "local_skill" src.mkdir(parents=True, exist_ok=True) (src / "SKILL.md").write_text( - "---\nname: local_with_sh\ndescription: x\nmetadata: {\"openclaw\":{}}\n---\n", + "---\nname: local_with_sh\ndescription: x\nmetadata: {\"oclaw\":{}}\n---\n", encoding="utf-8", ) (src / "activator.sh").write_text("#!/usr/bin/env bash\necho ok\n", encoding="utf-8") diff --git a/tests/test_skill_role_binding_catalog.py b/tests/test_skill_role_binding_catalog.py index 0a0fd0c5..a789b6c4 100644 --- a/tests/test_skill_role_binding_catalog.py +++ b/tests/test_skill_role_binding_catalog.py @@ -3,15 +3,15 @@ from __future__ import annotations import json from pathlib import Path -from oclaw.openclaw_runtime.skill_role_binding import ( +from oclaw.runtime.skill_role_binding import ( SKILL_ROLE_BINDING_ENABLED_SETTING, SKILL_ROLE_BINDING_KEY, normalize_skill_role_binding, ordered_binding_roles, ) -from oclaw.openclaw_runtime.skills_prompt import collect_skill_catalog_entries +from oclaw.runtime.skills_prompt import collect_skill_catalog_entries from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.catalog import default_registry +from oclaw.runtime.tools.catalog import default_registry def _write_skill(root: Path, name: str) -> None: diff --git a/tests/test_skill_runtime_metadata_and_tools.py b/tests/test_skill_runtime_metadata_and_tools.py index af274be2..0cccb985 100644 --- a/tests/test_skill_runtime_metadata_and_tools.py +++ b/tests/test_skill_runtime_metadata_and_tools.py @@ -5,8 +5,8 @@ import tempfile import unittest from pathlib import Path -from oclaw.openclaw_runtime.skills import discover_workspace_skill_manifests -from oclaw.tools.skills_runtime.materialize_skill_tools import materialize_executable_skill_tools +from oclaw.runtime.skills import discover_workspace_skill_manifests +from oclaw.runtime.tools.skills_runtime.materialize_skill_tools import materialize_executable_skill_tools class SkillRuntimeMetadataAndToolsTests(unittest.TestCase): @@ -30,7 +30,7 @@ class SkillRuntimeMetadataAndToolsTests(unittest.TestCase): "name: demo_runtime\n" "description: demo\n" "metadata:\n" - " openclaw:\n" + " oclaw:\n" " runtime:\n" " type: python\n" " entry: scripts/run.py\n" @@ -62,7 +62,7 @@ class SkillRuntimeMetadataAndToolsTests(unittest.TestCase): "name: demo_runtime_tool\n" "description: demo\n" "metadata:\n" - " openclaw:\n" + " oclaw:\n" " runtime:\n" " type: python\n" " entry: scripts/run.py\n" diff --git a/tests/test_skill_runtime_restricted_fs_permissions.py b/tests/test_skill_runtime_restricted_fs_permissions.py index da54833d..4ad447ac 100644 --- a/tests/test_skill_runtime_restricted_fs_permissions.py +++ b/tests/test_skill_runtime_restricted_fs_permissions.py @@ -5,7 +5,7 @@ import tempfile import unittest from pathlib import Path -from oclaw.tools.skills_runtime.subprocess_exec import run_skill_runtime_entry +from oclaw.runtime.tools.skills_runtime.subprocess_exec import run_skill_runtime_entry class SkillRuntimeRestrictedFsPermissionTests(unittest.TestCase): diff --git a/tests/test_skills_prompt.py b/tests/test_skills_prompt.py index 3f9086b0..fb93ca4b 100644 --- a/tests/test_skills_prompt.py +++ b/tests/test_skills_prompt.py @@ -1,6 +1,6 @@ from __future__ import annotations -from oclaw.openclaw_runtime.skills_prompt import format_skills_for_prompt +from oclaw.runtime.skills_prompt import format_skills_for_prompt def test_format_skills_for_prompt_xml_shape() -> None: diff --git a/tests/test_tool_batch_partition.py b/tests/test_tool_batch_partition.py index 1ba25c37..908d18ad 100644 --- a/tests/test_tool_batch_partition.py +++ b/tests/test_tool_batch_partition.py @@ -1,8 +1,8 @@ from __future__ import annotations -from oclaw.chat.tool_runtime import partition_tool_use_batches +from oclaw.runtime.chat.tool_runtime import partition_tool_use_batches from oclaw.platform.llm.chat_models import LLMToolCall -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec def _spec(name: str, *, read_only: bool) -> ToolSpec: diff --git a/tests/test_tool_llm_truncation.py b/tests/test_tool_llm_truncation.py index 53c39fe3..df0918d7 100644 --- a/tests/test_tool_llm_truncation.py +++ b/tests/test_tool_llm_truncation.py @@ -3,7 +3,7 @@ from __future__ import annotations import json import unittest -from oclaw.chat.tool_runtime import truncate_tool_result_for_llm_messages, tool_llm_message_max_chars +from oclaw.runtime.chat.tool_runtime import truncate_tool_result_for_llm_messages, tool_llm_message_max_chars class ToolLlmTruncationTests(unittest.TestCase): diff --git a/tests/test_tool_loop_guard.py b/tests/test_tool_loop_guard.py index 421992ca..23e8f039 100644 --- a/tests/test_tool_loop_guard.py +++ b/tests/test_tool_loop_guard.py @@ -3,10 +3,10 @@ from __future__ import annotations import json from pathlib import Path -from oclaw.chat.tool_runtime import ToolExecutionContext, ToolExecutor +from oclaw.runtime.chat.tool_runtime import ToolExecutionContext, ToolExecutor from oclaw.platform.llm.chat_models import LLMToolCall from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.base import ToolRegistry, ToolSpec +from oclaw.runtime.tools.base import ToolRegistry, ToolSpec def test_tool_loop_guard_blocks_repeated_signature(tmp_path: Path) -> None: diff --git a/tests/test_tool_pairing_messages.py b/tests/test_tool_pairing_messages.py index f84af056..07ffffa3 100644 --- a/tests/test_tool_pairing_messages.py +++ b/tests/test_tool_pairing_messages.py @@ -2,7 +2,7 @@ from __future__ import annotations import json -from oclaw.chat.agent_messages import build_llm_messages +from oclaw.runtime.chat.agent_messages import build_llm_messages from oclaw.platform.llm.chat_models import RuleBasedChatModel diff --git a/tests/test_wecom_longconn_outbound_drain.py b/tests/test_wecom_longconn_outbound_drain.py index 4217de3a..3d1b127b 100644 --- a/tests/test_wecom_longconn_outbound_drain.py +++ b/tests/test_wecom_longconn_outbound_drain.py @@ -11,7 +11,7 @@ import pytest def test_wecom_longconn_drains_outbound_on_idle_timeout(monkeypatch: pytest.MonkeyPatch) -> None: # Import module under test. - import oclaw.channels.wecom.longconn_runner as m + import oclaw.interfaces.channels.wecom.longconn_runner as m # Build a fake websocket that raises timeout once, then exits by raising KeyboardInterrupt. class _TimeoutOnceWs: diff --git a/tests/test_wiki_auto_inject_hook.py b/tests/test_wiki_auto_inject_hook.py index 3f3ea83f..6f36191c 100644 --- a/tests/test_wiki_auto_inject_hook.py +++ b/tests/test_wiki_auto_inject_hook.py @@ -6,7 +6,7 @@ from pathlib import Path def _load_handler_module(): - p = Path("d:/project/chatgpt/oclaw/hooks/bundled/wiki-auto-inject/handler.py").resolve() + p = Path("d:/project/chatgpt/oclaw/runtime/hooks/bundled/wiki-auto-inject/handler.py").resolve() spec = importlib.util.spec_from_file_location("wiki_auto_inject_handler_test", str(p)) assert spec and spec.loader mod = importlib.util.module_from_spec(spec) diff --git a/tests/test_wiki_worker_pipeline.py b/tests/test_wiki_worker_pipeline.py index fdef3f55..cfa4516a 100644 --- a/tests/test_wiki_worker_pipeline.py +++ b/tests/test_wiki_worker_pipeline.py @@ -3,12 +3,12 @@ from __future__ import annotations import json from pathlib import Path -from oclaw.platform.persistence.sqlite_store import OpenClawTask -from oclaw.wiki_worker.main import _capture_after_turn, _dedup_merge, _process_task +from oclaw.platform.persistence.sqlite_store import OclawTask +from oclaw.runtime.workers.wiki.main import _capture_after_turn, _dedup_merge, _process_task -def _task(task_id: str = "t1") -> OpenClawTask: - return OpenClawTask( +def _task(task_id: str = "t1") -> OclawTask: + return OclawTask( id=task_id, tenant_id="tenant-1", session_id="session-1", @@ -68,7 +68,7 @@ def test_process_task_writes_index_and_lint_report(tmp_path: Path) -> None: "assistant_text": "answer", } task = _task("t-proc") - task = OpenClawTask(**{**task.__dict__, "payload": json.dumps(payload, ensure_ascii=False)}) + task = OclawTask(**{**task.__dict__, "payload": json.dumps(payload, ensure_ascii=False)}) def _wiki_apply(args: dict) -> dict: rel = str(args.get("path") or "").replace("\\", "/").strip("/") diff --git a/tests/test_workspace_path_guard.py b/tests/test_workspace_path_guard.py index 0288527c..b1c98451 100644 --- a/tests/test_workspace_path_guard.py +++ b/tests/test_workspace_path_guard.py @@ -12,8 +12,8 @@ from fastapi.testclient import TestClient from oclaw.interfaces.http.fastapi_app import create_app from oclaw.platform.config.paths import db_path from oclaw.platform.persistence.sqlite_store import SqliteStore -from oclaw.tools.experts.workspace.fs_tools import list_files_tool -from oclaw.tools.experts.workspace.workspace_base import ( +from oclaw.runtime.tools.experts.workspace.fs_tools import list_files_tool +from oclaw.runtime.tools.experts.workspace.workspace_base import ( access_from_env, build_workspace_path_access, clear_workspace_path_access_for_tests, diff --git a/tests/test_ws_gateway.py b/tests/test_ws_gateway.py index b5e7337f..44727c21 100644 --- a/tests/test_ws_gateway.py +++ b/tests/test_ws_gateway.py @@ -4,7 +4,7 @@ from unittest import mock from fastapi.testclient import TestClient from oclaw.interfaces.http.fastapi_app import create_app -from oclaw.openclaw_runtime.gateway import OpenClawGatewayResult +from oclaw.runtime.gateway import OclawGatewayResult def _connect_params() -> dict: @@ -72,7 +72,7 @@ class WsGatewayTests(unittest.TestCase): on_tool_ui("skill", {"ok": True}) if callable(on_token): on_token("hello") - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=str(rid), reply_text="done", trace_id="trace_test", @@ -83,7 +83,7 @@ class WsGatewayTests(unittest.TestCase): interaction_mode="comprehensive", ) - with mock.patch("oclaw.openclaw_runtime.gateway.OpenClawGateway.handle_turn", new=_fake_handle_turn): + with mock.patch("oclaw.runtime.gateway.OclawGateway.handle_turn", new=_fake_handle_turn): with self.client.websocket_connect("/ws") as ws: ws.receive_json() # connect.challenge ws.send_json({"type": "req", "id": "c1", "method": "connect", "params": _connect_params()}) @@ -129,7 +129,7 @@ class WsGatewayTests(unittest.TestCase): def test_ws_sessions_send_routes_to_agent_flow(self) -> None: def _fake_handle_turn(self, **kwargs): # noqa: ANN001 rid = kwargs.get("run_id") or "run_test" - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=str(rid), reply_text="done", trace_id="trace_sessions_send", @@ -140,7 +140,7 @@ class WsGatewayTests(unittest.TestCase): interaction_mode="comprehensive", ) - with mock.patch("oclaw.openclaw_runtime.gateway.OpenClawGateway.handle_turn", new=_fake_handle_turn): + with mock.patch("oclaw.runtime.gateway.OclawGateway.handle_turn", new=_fake_handle_turn): with self.client.websocket_connect("/ws") as ws: ws.receive_json() ws.send_json({"type": "req", "id": "c1", "method": "connect", "params": _connect_params()}) @@ -166,7 +166,7 @@ class WsGatewayTests(unittest.TestCase): def test_ws_agent_run_alias_works(self) -> None: def _fake_handle_turn(self, **kwargs): # noqa: ANN001 rid = kwargs.get("run_id") or "run_alias" - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=str(rid), reply_text="ok", trace_id="trace_alias", @@ -177,7 +177,7 @@ class WsGatewayTests(unittest.TestCase): interaction_mode="comprehensive", ) - with mock.patch("oclaw.openclaw_runtime.gateway.OpenClawGateway.handle_turn", new=_fake_handle_turn): + with mock.patch("oclaw.runtime.gateway.OclawGateway.handle_turn", new=_fake_handle_turn): with self.client.websocket_connect("/ws") as ws: ws.receive_json() ws.send_json({"type": "req", "id": "c1", "method": "connect", "params": _connect_params()}) @@ -208,7 +208,7 @@ class WsGatewayTests(unittest.TestCase): if callable(on_token): on_token("he") on_token("llo") - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=str(rid), reply_text="hello", trace_id="trace_chat_send", @@ -219,7 +219,7 @@ class WsGatewayTests(unittest.TestCase): interaction_mode="comprehensive", ) - with mock.patch("oclaw.openclaw_runtime.gateway.OpenClawGateway.handle_turn", new=_fake_handle_turn): + with mock.patch("oclaw.runtime.gateway.OclawGateway.handle_turn", new=_fake_handle_turn): with self.client.websocket_connect("/ws") as ws: ws.receive_json() ws.send_json({"type": "req", "id": "c1", "method": "connect", "params": _connect_params()}) @@ -266,7 +266,7 @@ class WsGatewayTests(unittest.TestCase): if callable(on_token): on_token("Hel") on_token("lo") - return OpenClawGatewayResult( + return OclawGatewayResult( run_id=str(rid), reply_text="Hello", trace_id="trace_chat_send_stream", @@ -277,7 +277,7 @@ class WsGatewayTests(unittest.TestCase): interaction_mode="comprehensive", ) - with mock.patch("oclaw.openclaw_runtime.gateway.OpenClawGateway.handle_turn", new=_fake_handle_turn): + with mock.patch("oclaw.runtime.gateway.OclawGateway.handle_turn", new=_fake_handle_turn): with self.client.websocket_connect("/ws") as ws: ws.receive_json() ws.send_json({"type": "req", "id": "c1", "method": "connect", "params": _connect_params()}) diff --git a/tools/experts/workspace/__init__.py b/tools/experts/workspace/__init__.py deleted file mode 100644 index a04cd9d0..00000000 --- a/tools/experts/workspace/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from __future__ import annotations - -__all__ = [] - diff --git a/ui/__init__.py b/ui/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ui/streamlit/__init__.py b/ui/streamlit/__init__.py deleted file mode 100644 index e69de29b..00000000