Commit Graph
408 Commits
Author SHA1 Message Date
btc69m979y-dotcom 898dbbecd4 fix: always prefer UTF-8 when strict decode succeeds in proxy encoding detection
The scoring heuristic for GB18030 vs UTF-8 could false-positive on longer
CJK strings because UTF-8 CJK bytes reinterpreted as GB18030 also produce
CJK characters with comparable scores, defeating the margin.

Since genuine GB18030 CJK byte sequences are almost never valid UTF-8
(strict mode), a successful UTF-8 strict decode means the body IS UTF-8.
Remove the scoring heuristic and always return utf8Decoded when strict
decode passes. GB18030 fallback still applies when UTF-8 decode fails.
2026-03-19 15:14:19 +08:00
btc69m979y-dotcomandClaude Opus 4.6 dd7312f1ee fix: also prevent history burst when forward-match misjudges firstNewIdx
When cursor > 0, prefer cursor over forward-match as the continuation
point. forward-match compares local entries sequentially against history
entries, but when the local store has been polluted with duplicates (from
a prior mis-sync), it can return a wildly low firstNewIdx — causing a
burst of already-synced messages to reappear in the UI.

tail-overlap and last-user-anchor remain the primary content-based
strategies for sliding-window detection; forward-match is now only used
for the initial sync (cursor == 0).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-19 14:44:59 +08:00
btc69m979y-dotcomandClaude Opus 4.6 07a9f2a2bf fix: prevent infinite re-sync loop in DingTalk channel message sync
When cursor >= historyEntries.length (no new gateway messages), the
forward-match fallback strategy could produce false positives if the
local store contained duplicate entries from a previous mis-sync.
This caused the same user message to be re-added every 10-second poll
cycle, creating an infinite loop visible as repeated messages in the UI.

Add a cursor-stable guard before forward-match so that when the gateway
history count has not grown, we trust the cursor and return early.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-19 14:21:43 +08:00
btc69m979y-dotcom 9c818d5754 fix: resolve gateway PATH case-sensitivity issue on Windows
On Windows, spreading process.env produces a "Path" key (not "PATH").
The cliShimDir logic then created a second "PATH" key referencing the
non-existent env.PATH (undefined), resulting in a PATH containing only
cliShimDir. This caused gateway child processes to fail with ENOENT
when spawning System32 executables like taskkill.

Fix: read env.PATH || env.Path to capture the actual full path before
prepending cliShimDir.
2026-03-18 20:05:40 +08:00
liuzhq abeb2f6bbc fix: 修复合并冲突遗留的 IMSettings 语法错误 2026-03-18 20:01:24 +08:00
liuzhq f3353d9084 Merge branch 'main' into pr-473
解决合并冲突:保留 NIM OpenClaw 迁移改动,同步 main 的 setConfig options 参数
2026-03-18 19:57:20 +08:00
liuzhq 3a8b2a1bdb Merge pull request #477 from nmgwddj/bugfix/migrate-scheduled-tasks
fix: improve scheduled task migration and add main-process i18n
2026-03-18 19:47:46 +08:00
wulei 40dd2df92b Merge remote-tracking branch 'upstream/main' 2026-03-18 18:47:35 +08:00
wulei 19b56173eb fix: cowork 会话期间修改 IM 配置导致 UI 卡死
问题:在 cowork 会话运行期间修改 IM 机器人配置,gateway 被重启,
活跃会话断连后 UI 永久卡在 streaming 状态,用户无法发送新消息。

根因:1) continueSession/startSession 的 .catch 只打了 console.error,
没有向渲染进程发送 error 事件,导致 isStreaming 永远不被清除;
2) syncOpenClawConfig 在有活跃会话时仍然写入 openclaw.json 并重启
gateway,OpenClaw 的 file-watcher 也会通过 SIGUSR1 触发热重载。

修复:
- .catch 中发送 cowork:stream:error IPC 事件,UI 正确清除 streaming 状态
- 有活跃会话时延迟整个 config sync(含文件写入),等会话结束后再执行
- 渲染进程 onStreamError 中添加可见的错误消息,用户能看到具体错误
- syncOpenClawConfig 日志加上 reason 参数便于排查
- OpenClawRuntimeAdapter 新增 hasActiveSessions() 方法
2026-03-18 18:46:33 +08:00
nathan 32e8df4717 Merge pull request #486 from netease-youdao/fix/im-config-sync
fix: im配置变更,只有点击保存时,才同步openclaw并重启gateway
2026-03-18 17:38:57 +08:00
liuganglg 1c32e55b07 fix: im配置变更,只有点击保存时,才同步openclaw并重启gateway 2026-03-18 17:38:22 +08:00
liuzhq 3d82758c41 feat: 网关启动失败时显示重新启动网关按钮并国际化状态文案 2026-03-18 15:32:21 +08:00
liuzhq 34a4b3dad3 feat: 添加openclaw gateway 启动失败后重启功能 2026-03-18 15:14:58 +08:00
Dylan d0c850578e feat: display human-readable cron schedules in task list
Parse standard 5-field cron expressions into localized descriptions
instead of showing raw expressions like "Cron · 55 11 * * *".

Supported patterns:
- Fixed daily time: "每天 11:55" / "Every day at 11:55"
- Minute intervals: "每 5 分钟" / "Every 5 minutes"
- Hour intervals: "每 2 小时" / "Every 2 hours"
- Single weekday: "每周一 09:30" / "Every Monday at 09:30"
- Weekday range: "工作日 09:30" / "Weekdays at 09:30"
- Monthly day: "每月 15日 08:00" / "Monthly on the 15th at 08:00"
- Complex expressions fall back to "Cron · expr"

All labels use i18n keys (12 new keys added for zh/en) with no
hardcoded language conditionals. Time is displayed in HH:MM format.
2026-03-18 12:07:23 +08:00
Dylan 552346f807 fix: improve scheduled task migration and add main-process i18n
- Append local timezone offset to legacy `at` datetime strings that lack
  one, preventing the OpenClaw gateway from interpreting them as UTC
  (8-hour drift for UTC+8 users).
- Skip one-time `at` tasks whose scheduled time is already in the past,
  since the gateway rejects them and they would never fire again.
- Add `agentId: "main"` to migrated tasks so gateway routing works.
- Expand cron session key regex to handle `agent:{id}:cron:{jobId}` format
  generated by the gateway when agentId is set.
- Create human-readable cron session titles (e.g. `[定时] 任务名`) using
  a job name cache populated during CronJobService polling.
- Introduce `src/main/i18n.ts` as a lightweight main-process i18n module
  with direct `t()` export, replacing hardcoded language conditionals in
  trayManager and openclawChannelSessionSync.
2026-03-18 11:24:52 +08:00
Dylan b5fd353943 refactor: migrate NIM gateway from direct SDK to OpenClaw plugin architecture
Migrate the NIM (NetEase IM) channel from a locally-managed native SDK gateway
to running through the OpenClaw runtime plugin (`openclaw-nim`), aligning it
with the existing pattern used by DingTalk, Feishu, Telegram, Discord, QQ, and
WeCom channels.

Key changes:
- **OpenClaw plugin registration**: Add `[email protected]` to `package.json`
  channel plugins; register `nim` in channel-platform maps and config sync.
- **Gateway manager**: Remove direct NIM SDK lifecycle management (start/stop,
  reconnect, event forwarding, credential hot-reload, probe mutex). NIM now
  delegates to OpenClaw gateway via `syncOpenClawConfig` /
  `ensureOpenClawGatewayConnected`, matching other OpenClaw channels. Add
  `testNimOpenClawConnectivity()` for config-level validation. Expose
  `getOpenClawConfigSchema()` to retrieve JSON Schema + uiHints from gateway.
- **NimConfig restructuring**: Replace flat config fields (`accountWhitelist`,
  `teamPolicy`, `teamAllowlist`, `qchatEnabled`, `qchatServerIds`, `debug`)
  with structured sub-objects (`p2p`, `team`, `qchat`, `advanced`), each using
  `{ policy, allowFrom }` pattern. Update `nimGateway.ts` to consume the new
  shape.
- **Schema-driven settings UI**: Add `SchemaForm.tsx` — a generic component
  that dynamically renders form fields from JSON Schema + uiHints. Replace the
  ~230 lines of hardcoded NIM settings fields in `IMSettings.tsx` with a
  `SchemaForm` instance driven by the OpenClaw config schema at runtime.
- **IPC + service plumbing**: Add `im:openclaw:config-schema` IPC channel,
  preload binding, renderer service method, and TypeScript declarations for
  the new schema API and restructured NIM types.

Signed-off-by: Dylan <[email protected]>
2026-03-17 20:15:02 +08:00
liuzhq 77c6b5fe2d Merge pull request #472 from winsan-zhang/feature/moltbot-pop
- 新增 POPO聊天机器人接入,作为 OpenClaw 插件 (moltbot-popo) 集成
2026-03-17 19:46:58 +08:00
zhangxiaochang 3ca83092c4 feat: add popo 2026-03-17 19:28:02 +08:00
btc69m979y-dotcom 2340d343c2 Merge pull request #469 from nmgwddj/feature/openclaw
计划任务数据迁移
2026-03-17 17:57:56 +08:00
btc69m979y-dotcom 468faa4f65 Merge pull request #465 from wuleihenbang/feature/openclaw
fix: 修复钉钉通道定时任务消息无法投递
2026-03-17 17:57:37 +08:00
liuzhq 079c3fdbf1 Merge branch 'feature/openclaw' into liuzhq/openclaw-skills 2026-03-17 17:37:07 +08:00
liuzhq 3c5c2557b3 fix: 修正 skills entries 合并顺序并补充错误日志
- 反转 MANAGED_SKILL_ENTRY_OVERRIDES 展开顺序,确保管控禁用规则不被用户 skill 状态覆盖
- resolveSkillsExtraDirs 区分 ENOENT 与其他 I/O 错误,非预期异常打印 warn 日志
2026-03-17 17:35:31 +08:00
btc69m979y-dotcomandClaude Opus 4.6 80ecbce347 fix: provide placeholder API key for providers that don't require auth
OpenClaw's gateway requires a non-empty apiKey for every provider,
even local servers (Ollama, vLLM, etc.) that don't enforce auth.
Previously, only Ollama with Anthropic format got a dummy key
('sk-ollama-local'), leaving Ollama with OpenAI format (and any
other no-auth provider) with an empty string. This caused the
gateway to reject requests with "No API key found for provider".

Now both resolveCurrentApiConfig and resolveRawApiConfig supply
'sk-lobsterai-local' as a placeholder whenever the provider does
not require an API key and none is configured.

Closes #452

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-17 17:09:39 +08:00
DylanandClaude Sonnet 4.6 dbfce44e10 feat: 迁移定时任务历史记录到 OpenClaw cron/runs/ JSONL 文件
## 问题

从旧版升级后,SQLite 中保存的定时任务执行历史无法在 OpenClaw
历史记录页面查询,因为 OpenClaw 的运行历史存储在独立的
\`{stateDir}/cron/runs/{jobId}.jsonl\` 文件中,与 SQLite 完全隔离。

## 解决方案

新增 \`migrateScheduledTaskRunsToOpenclaw()\` 函数(\`migrateScheduledTasks.ts\`):

1. **任务 ID 映射**:读取 \`jobs.json\` 中的当前 OpenClaw 任务,
   与 SQLite \`scheduled_tasks\` 按任务名称进行匹配,
   建立旧 \`task_id → 新 jobId\` 的映射关系

2. **格式转换**:将 SQLite run 记录转换为 OpenClaw JSONL 格式:
   - \`status: 'success'\` → \`status: 'ok'\`
   - \`started_at / finished_at\` ISO 字符串 → \`runAtMs / ts\` 毫秒时间戳
   - 保留 \`sessionId\`、\`duration_ms\`、\`error\` 字段

3. **幂等写入**:追加写入前读取已有 JSONL 文件,
   以 \`ts\`(完成时间戳)去重,避免重复导入

4. **独立于 gateway**:纯文件 I/O,不依赖 OpenClaw gateway 启动状态,
   窗口就绪后立即执行

通过 kv 标记 \`scheduled_task_runs_migrated_to_openclaw_v1\` 保证只执行一次。

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-17 17:01:43 +08:00
DylanandClaude Sonnet 4.6 a4db471127 fix: 直接对话创建的定时任务无法正确触发
## 问题原因

OpenClaw gateway 在执行 `sessionTarget: 'isolated'` 的定时任务时,
会生成格式为 `cron:{jobId}` 的 sessionKey。该格式未被
`openclawChannelSessionSync.ts` 中的任何解析函数识别:

- `parseManagedSessionKey()` — 要求 `lobsterai:` 或 `agent:...lobsterai:...` 前缀
- `parseChannelSessionKey()` — 要求 IM 平台名称前缀(telegram/dingtalk 等)
- `MAIN_AGENT_SESSION_RE` — 要求 `agent:{agentId}:main` 格式

三个函数均返回 null,导致 `openclawRuntimeAdapter.ts` 中的
`handleAgentEvent` 无法解析到本地会话,事件被丢弃:

```
[ChannelSessionSync] parse failed: not a recognized channel key: cron:fb8cf959-...
[Debug:handleAgentEvent] no sessionId, dropping event.
```

## 解决方案

### `src/main/libs/openclawChannelSessionSync.ts`

- 新增 `CRON_SESSION_KEY_RE = /^cron:[0-9a-f-]+$/i` 正则
- 新增 `isCronSessionKey()` 导出函数
- 新增 `resolveOrCreateCronSession()` 方法:识别 `cron:` 前缀的 key,
  为每个定时任务创建一个持久化的本地 Cowork 会话(以 jobId 为标识复用,
  所有运行历史共享同一会话)
- 更新 `isChannelSessionKey()` 纳入 cron key 识别

### `src/main/libs/agentEngine/openclawRuntimeAdapter.ts`

在 3 处 channel session 解析调用链中加入
`resolveOrCreateCronSession(sessionKey)`,使定时任务事件能够
正确路由到本地会话并在 UI 中展示。

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-17 17:01:43 +08:00
DylanandClaude Sonnet 4.6 d28fcffcb7 feat: 升级到 openclaw 内核后自动迁移定时任务
## 问题原因

切换到 openclaw 内核后,定时任务的存储层发生了根本性变化:

- **旧版(main 分支)**:任务存储在 SQLite 的 `scheduled_tasks` 表,由 `ScheduledTaskStore` 管理
- **新版(openclaw 分支)**:SQLite 表已移除,任务改为通过 `CronJobService` 调用 OpenClaw gateway RPC(`cron.add/list/update` 等)管理

用户从旧版升级后,SQLite 数据库文件中仍保留原有任务数据,但新代码完全不读取该表,导致所有定时任务在升级后丢失。

## 解决方案

新增一次性迁移逻辑,在应用窗口就绪后自动执行:

### 新增 `src/main/libs/migrateScheduledTasks.ts`

- **幂等保护**:通过 kv 表中的 `scheduled_tasks_migrated_to_openclaw_v1` 标记防止重复执行
- **按需触发**:检测 `sqlite_master` 中是否存在旧表,新安装用户直接跳过
- **格式转换**:
  - `schedule.type: 'at'` → `{ kind: 'at', at: datetime }`
  - `schedule.type: 'interval'` → `{ kind: 'every', everyMs: intervalMs }`
  - `schedule.type: 'cron'` → `{ kind: 'cron', expr: expression }`
  - `prompt` → `payload: { kind: 'agentTurn', message: prompt }`
  - `notifyPlatforms[0]` → `delivery: { mode: 'announce', channel }` 或 `mode: 'none'`
  - `sessionTarget` 固定为 `'isolated'`(`main` session 仅支持 `systemEvent` payload,不适用于有 prompt 的迁移任务)
- **重试机制**:仅在 gateway 零错误时写入完成标记,网关临时故障时下次启动可自动重试

### 修改 `src/main/main.ts`

在 `ready-to-show` 回调的 polling 启动后异步调用迁移,不阻塞启动流程。

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-17 17:01:43 +08:00
liuzhq 48db5ad58f Merge branch 'feature/openclaw' into liuzhq/openclaw-skills 2026-03-17 16:57:17 +08:00
liuzhq 6b00358764 feat: 使用 OpenClaw 原生 Skills 加载替代 AGENTS.md 内嵌方式
通过 openclaw.json 的 skills.load.extraDirs 配置让 OpenClaw 直接扫描
SKILLs 目录,利用其原生 token 预算管理,避免 AGENTS.md 随 skills 增多
而无限膨胀。
2026-03-17 16:55:10 +08:00
liuganglg 6e4a2b9cb5 fix: im会话管理命令恢复 2026-03-17 16:42:54 +08:00
btc69m979y-dotcom 87563c803f Merge pull request #464 from netease-youdao/fix/narrow-base-url-strip-scope
refactor: narrow base URL stripping to /chat/completions only
2026-03-17 16:22:28 +08:00
btc69m979y-dotcomandClaude Opus 4.6 0fce1efdec style: update Custom provider base URL hint text with SiliconFlow examples
Show both Anthropic and OpenAI compatible URL examples side by side,
using SiliconFlow as the example provider.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-17 16:21:22 +08:00
liuganglg e0d9dde206 fix: openclaw运行时支持系统代理 2026-03-17 16:04:41 +08:00
wulei 9ecc813532 Merge remote-tracking branch 'upstream/feature/openclaw' into feature/openclaw 2026-03-17 15:56:15 +08:00
wulei f8b9b62010 fix: 修复钉钉通道定时任务消息无法投递
问题:通过钉钉创建的定时提醒,触发后消息不会投递到钉钉会话,而飞书等其他通道正常。

根因:钉钉 session key 使用 JSON 格式(agent:main:openai-user:{"channel":"dingtalk-connector","peerid":"xxx",...}),
OpenClaw 的 inferDeliveryFromSessionKey 函数只能解析冒号分隔格式的 session key,
无法从 JSON 格式中提取 delivery 路由信息(channel/to),导致 cron 任务创建时 delivery 为空。

修复:在 openclawRuntimeHotfix 的 inferDeliveryFromSessionKey 补丁中,新增 JSON session key 解析逻辑,
在 persistedDelivery 查找失败后、冒号分割解析前,尝试从 session key 中提取 JSON 对象,
解析出 channel、peerid、chattype 等字段构建正确的 delivery 路由。
2026-03-17 15:46:14 +08:00
btc69m979y-dotcomandClaude Opus 4.6 500c65c0c9 refactor: narrow base URL stripping to /chat/completions only
The previous fix (#462) stripped multiple endpoint suffixes
(/chat/completions, /messages, /completions, /responses) which was
overly broad. Only /chat/completions has been confirmed to cause
the duplication issue. Narrow the scope to match the detection logic
in buildOpenAIChatCompletionsURL (coworkFormatTransform.ts).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-17 15:43:14 +08:00
liuzhq c2be8d4b96 Merge branch 'feature/openclaw' into liuzhq/openclaw-upgrade 2026-03-17 15:33:45 +08:00
btc69m979y-dotcom 7a7c82e39a Merge pull request #462 from netease-youdao/fix/openclaw-base-url-normalization
fix: strip endpoint suffix from base URL in OpenClaw config
2026-03-17 15:24:44 +08:00
btc69m979y-dotcomandClaude Opus 4.6 b032a46e85 fix: strip endpoint suffix from base URL before writing OpenClaw config
When users configure a Custom provider with a full endpoint URL like
`https://gw.example.com/v1/chat/completions`, the OpenClaw gateway
appends `/chat/completions` again, producing a doubled path that
returns HTTP 404.

Strip well-known endpoint suffixes (/chat/completions, /messages,
/completions, /responses) from the base URL before writing it to
openclaw.json so the gateway can append its own path correctly.

Closes #xxx

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-17 15:16:56 +08:00
btc69m979y-dotcomandClaude Opus 4.6 5b439c29e1 fix: prevent GB18030 false positive in proxy encoding detection and add debug logging
The readRequestBody scoring heuristic incorrectly chose GB18030 over UTF-8
for short CJK strings like "你好" (score 11 vs 10), causing garbled session
titles (e.g. "浣犲ソ"). Add a score margin of 5 so GB18030 only wins when
the evidence is strong.

Also add diagnostic logging for API request URLs and title generation to
aid future debugging.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-17 15:07:42 +08:00
liuzhq 3144f68f70 feat: 手动检查更新使用独立接口 update-manual,自动更新保持不变 2026-03-17 14:45:28 +08:00
liuzhq 2b66f7cbe2 Merge branch 'feature/openclaw' into liuzhq/openclaw-email 2026-03-17 14:12:27 +08:00
liuzhq 5c495452b8 fix: IM渠道邮箱skill无法执行的问题
在AGENTS.md提示词中为imap-smtp-email skill添加exec例外声明,
使模型在IM渠道会话中不再因exec限制提示而跳过邮箱脚本执行。
2026-03-17 14:11:36 +08:00
btc69m979y-dotcom e768785072 Merge pull request #457 from netease-youdao/fix/remove-model-probe-on-send
fix: remove model probe and fix electron dev startup race
2026-03-17 11:59:23 +08:00
btc69m979y-dotcom 37827f3534 Merge pull request #451 from wuleihenbang/feature/openclaw
fix: 修复频道消息顺序错乱,用户消息显示在AI回复之后
2026-03-17 11:59:14 +08:00
btc69m979y-dotcom fdba07273b fix: only inject system prompt on first turn of OpenClaw session
Previously lastSystemPromptBySession was cleared after every turn,
causing the full system prompt to be re-injected into the user message
on each subsequent turn. Persist the cache across turns so the prompt
is only sent on the first message (or when it actually changes).

Closes #453
2026-03-17 11:35:57 +08:00
btc69m979y-dotcomandClaude Opus 4.6 4993d594b5 fix: remove model validation probe before sending messages
The 20s probe request blocks message sending and times out when the API
endpoint is slow or behind a proxy, causing a confusing
"Model validation timed out after 20s" error. Let the engine handle API
errors directly instead.

Closes #454

Co-Authored-By: Claude Opus 4.6 <[email protected]>
2026-03-17 11:00:24 +08:00
wulei 3348a52e35 fix: 修复频道消息顺序错乱,用户消息显示在AI回复之后
问题:IM频道(钉钉/Telegram等)的用户消息有时显示在AI回复之后,顺序反转。

根因:当gateway的chat.history API响应延迟时,prefetchChannelUserMessages
重试超时后放弃,缓冲区为空导致assistant消息通过流式事件先被创建。后续
syncChannelUserMessages通过addMessage追加用户消息,sequence大于assistant,
导致UI显示顺序错误。此外computeChannelHistoryFirstNewIndex的forward-match
策略可能跳过本地缺失的用户消息。

修复:syncChannelUserMessages添加用户消息时,根据gateway history查找对应的
本地assistant消息,通过insertMessageBeforeId插入到其前面而非追加。同时增加
repair扫描,补全firstNewIdx之前被forward-match跳过的用户消息。
2026-03-16 23:40:56 +08:00
wuleiandClaude Sonnet 4.6 ea8c13d014 fix: sync channel history after error turns so IM replies appear in UI
When a channel turn (e.g. DingTalk) ends with stopReason='error' or a
chat error event, syncChannelAfterTurn was never called. This meant the
AI reply already delivered to the IM user was never pulled into the local
session, so LobsterAI showed an empty error session with no messages.

Save turn.sessionKey before cleanup and call syncChannelAfterTurn in
both handleChatFinal (stoppedByError path) and handleChatError so the
incremental gateway history sync runs regardless of whether the turn
succeeded or failed.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-16 21:43:52 +08:00
wuleiandClaude Sonnet 4.6 16edc18e8d chore: auto-commit before context compaction
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
2026-03-16 21:39:33 +08:00
wulei 2c939eb8db Merge remote-tracking branch 'upstream/feature/openclaw' into feature/openclaw 2026-03-16 21:34:35 +08:00