� backup: 2026-03-24 04:00

This commit is contained in:
huan 2026-03-24 04:00:48 +08:00
parent 7e143d3ebc
commit 31786dee08
193 changed files with 73520 additions and 1915 deletions

View File

@ -28,7 +28,7 @@
},
"minimax-portal:default": {
"errorCount": 0,
"lastUsed": 1774078067690
"lastUsed": 1774295957771
}
}
}

View File

@ -115,8 +115,8 @@
"api": "openai-completions"
},
{
"id": "MiniMax-M2.5",
"name": "MiniMax-M2.5",
"id": "MiniMax-M2.7",
"name": "MiniMax-M2.7",
"reasoning": false,
"input": [
"text"
@ -191,8 +191,8 @@
"authHeader": true,
"models": [
{
"id": "MiniMax-M2.5",
"name": "MiniMax M2.5",
"id": "MiniMax-M2.7",
"name": "MiniMax M2.7",
"reasoning": true,
"input": [
"text"
@ -224,6 +224,22 @@
"contextWindow": 200000,
"maxTokens": 8192
},
{
"id": "MiniMax-M2.5",
"name": "MiniMax M2.5",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 0.3,
"output": 1.2,
"cacheRead": 0.03,
"cacheWrite": 0.12
},
"contextWindow": 200000,
"maxTokens": 8192
},
{
"id": "MiniMax-M2.5-highspeed",
"name": "MiniMax M2.5 Highspeed",
@ -249,8 +265,8 @@
"authHeader": true,
"models": [
{
"id": "MiniMax-M2.5",
"name": "MiniMax M2.5",
"id": "MiniMax-M2.7",
"name": "MiniMax M2.7",
"reasoning": false,
"input": [
"text"
@ -266,8 +282,8 @@
"api": "anthropic-messages"
},
{
"id": "MiniMax-M2.5-highspeed",
"name": "MiniMax M2.5 Highspeed",
"id": "MiniMax-M2.7-highspeed",
"name": "MiniMax M2.7 Highspeed",
"reasoning": true,
"input": [
"text"
@ -283,8 +299,8 @@
"api": "anthropic-messages"
},
{
"id": "MiniMax-M2.5-Lightning",
"name": "MiniMax M2.5 Lightning",
"id": "MiniMax-M2.7-Lightning",
"name": "MiniMax M2.7 Lightning",
"reasoning": true,
"input": [
"text"
@ -315,6 +331,38 @@
},
"contextWindow": 200000,
"maxTokens": 8192
},
{
"id": "MiniMax-M2.5",
"name": "MiniMax M2.5",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 0.3,
"output": 1.2,
"cacheRead": 0.03,
"cacheWrite": 0.12
},
"contextWindow": 200000,
"maxTokens": 8192
},
{
"id": "MiniMax-M2.5-highspeed",
"name": "MiniMax M2.5 Highspeed",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 0.3,
"output": 1.2,
"cacheRead": 0.03,
"cacheWrite": 0.12
},
"contextWindow": 200000,
"maxTokens": 8192
}
],
"apiKey": "minimax-oauth"

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"0221f5cb-0450-4333-ab08-72a6f7395e42","timestamp":"2026-03-23T08:28:49.685Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"d5648230","parentId":null,"timestamp":"2026-03-23T08:28:49.685Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"25a6377f","parentId":"d5648230","timestamp":"2026-03-23T08:28:49.685Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774254529689,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"b43456c3","parentId":"25a6377f","timestamp":"2026-03-23T08:28:49.689Z"}
{"type":"message","id":"44472264","parentId":"b43456c3","timestamp":"2026-03-23T08:28:49.712Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 16:28 (Asia/Shanghai) / 2026-03-23 08:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774254529708}}
{"type":"message","id":"1905e4b9","parentId":"44472264","timestamp":"2026-03-23T08:28:54.010Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_eyj1x1hs1dna_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":58,"cacheRead":0,"cacheWrite":38006,"totalTokens":38221,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774254529712}}
{"type":"message","id":"fa6cd5ce","parentId":"1905e4b9","timestamp":"2026-03-23T08:29:03.231Z","message":{"role":"toolResult","toolCallId":"call_function_eyj1x1hs1dna_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 16:28:54] ========== 上下文使用量检测 ==========\n[2026-03-23 16:29:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 16:29:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 16:29:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9188,"aggregated":"[2026-03-23 16:28:54] ========== 上下文使用量检测 ==========\n[2026-03-23 16:29:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 16:29:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 16:29:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774254543217}}
{"type":"message","id":"8a91bb27","parentId":"fa6cd5ce","timestamp":"2026-03-23T08:29:06.655Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 38%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"105e6fd8917cc6fc87a72e2cb0ec0628dcdd904e3a3d9e6a60a15da0773e1b64"},{"type":"text","text":"**📊 上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常,无需保存记忆\n\n使用量远低于 90% 阈值,一切安然无恙 🎉"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":485,"output":79,"cacheRead":37845,"cacheWrite":0,"totalTokens":38409,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774254543231}}

View File

@ -0,0 +1,13 @@
{"type":"session","version":3,"id":"1ddf45c8-7673-4ccb-b193-9f63c92aeb47","timestamp":"2026-03-22T00:00:00.135Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"4e31e30f","parentId":null,"timestamp":"2026-03-22T00:00:00.135Z","provider":"minimax-portal","modelId":"MiniMax-M2.5"}
{"type":"thinking_level_change","id":"fa38b54b","parentId":"4e31e30f","timestamp":"2026-03-22T00:00:00.135Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774137600139,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.5"},"id":"6e8dc1ca","parentId":"fa38b54b","timestamp":"2026-03-22T00:00:00.139Z"}
{"type":"message","id":"61c18aa8","parentId":"6e8dc1ca","timestamp":"2026-03-22T00:00:00.161Z","message":{"role":"user","content":[{"type":"text","text":"[cron:2073cd46-e64a-4304-a99e-a8b8a703fa54 每日收款失败统计] 执行以下任务:\n\n1. 搜索飞书群聊「益选通知群」(chat_id: oc_d5ef8b1abf66842c28ef57e1658dc096) 中关于「收款失败」的消息\n2. 时间范围昨天夜里23:00到今天早上8:00注意当前时间是早上8点所以查询范围是「昨天23:00到今天08:00」\n3. 读取现有Excel文件https://my.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb\n4. 判断逻辑:\n - 如果没有收款失败数据 → 在群里只发一条消息:「收款失败统计 (昨天~今天):无失败记录 ✅」\n - 如果有失败数据 → 追加到Excel表Sheet1从第8行开始追加然后在群里发消息「收款失败统计 (昨天~今天)共X条失败记录已更新X条到Excel」\n5. Excel追加格式序号自动生成现有最大序号+1、时间、支付方式、金额、备注留空或填入备注\n6. 注意:不需要统计成功的数据,只处理失败记录\nCurrent time: Sunday, March 22nd, 2026 — 08:00 (Asia/Shanghai) / 2026-03-22 00:00 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774137600157}}
{"type":"message","id":"edc41bb5","parentId":"61c18aa8","timestamp":"2026-03-22T00:01:37.787Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"这是一个定时任务,需要执行以下步骤:\n\n1. 搜索飞书群聊「益选通知群」(chat_id: oc_d5ef8b1abf66842c28ef57e1658dc096) 中关于「收款失败」的消息\n2. 时间范围昨天夜里23:00到今天早上8:002026-03-21 23:00:00 到 2026-03-22 08:00:00\n3. 读取现有Excel文件\n4. 判断逻辑并处理\n5. 如果有失败数据追加到Excel表\n\n让我开始执行\n1. 先搜索群聊中的收款失败消息\n2. 同时读取Excel文件内容","thinkingSignature":"d3d1d745058643bde4cecf6786a47989a1b8610e4198dc2f9f2d124d42e230bb"},{"type":"text","text":"我来执行这个收款失败统计任务。首先搜索群聊消息并读取Excel文件。\n"},{"type":"toolCall","id":"call_function_zjmll9xo0f9v_1","name":"feishu_im_user_search_messages","arguments":{"chat_id":"oc_d5ef8b1abf66842c28ef57e1658dc096","query":"收款失败","start_time":"2026-03-21T23:00:00+08:00","end_time":"2026-03-22T08:00:00+08:00","page_size":50}},{"type":"toolCall","id":"call_function_zjmll9xo0f9v_2","name":"feishu_sheet","arguments":{"action":"read","url":"https://my.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.5","usage":{"input":159,"output":350,"cacheRead":0,"cacheWrite":38141,"totalTokens":38650,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774137600160}}
{"type":"message","id":"1313bd9d","parentId":"edc41bb5","timestamp":"2026-03-22T00:01:39.908Z","message":{"role":"toolResult","toolCallId":"call_function_zjmll9xo0f9v_1","toolName":"feishu_im_user_search_messages","content":[{"type":"text","text":"{\n \"messages\": [\n {\n \"message_id\": \"om_x100b54db8126c8a0b3d196ffd61039e\",\n \"msg_type\": \"text\",\n \"content\": \"微信收款失败收款【23.00】元\",\n \"sender\": {\n \"id\": \"cli_c08abc1da138d00f\",\n \"sender_type\": \"app\"\n },\n \"create_time\": \"2026-03-22T04:18:06+08:00\",\n \"deleted\": false,\n \"updated\": false,\n \"chat_id\": \"oc_d5ef8b1abf66842c28ef57e1658dc096\",\n \"chat_type\": \"group\",\n \"chat_name\": \"益选通知群\"\n },\n {\n \"message_id\": \"om_x100b54db9e3caca4b2cc5beb3383094\",\n \"msg_type\": \"text\",\n \"content\": \"微信收款失败收款【23.00】元\",\n \"sender\": {\n \"id\": \"cli_c08abc1da138d00f\",\n \"sender_type\": \"app\"\n },\n \"create_time\": \"2026-03-22T04:18:23+08:00\",\n \"deleted\": false,\n \"updated\": false,\n \"chat_id\": \"oc_d5ef8b1abf66842c28ef57e1658dc096\",\n \"chat_type\": \"group\",\n \"chat_name\": \"益选通知群\"\n }\n ],\n \"has_more\": false\n}"}],"details":{"messages":[{"message_id":"om_x100b54db8126c8a0b3d196ffd61039e","msg_type":"text","content":"微信收款失败收款【23.00】元","sender":{"id":"cli_c08abc1da138d00f","sender_type":"app"},"create_time":"2026-03-22T04:18:06+08:00","deleted":false,"updated":false,"chat_id":"oc_d5ef8b1abf66842c28ef57e1658dc096","chat_type":"group","chat_name":"益选通知群"},{"message_id":"om_x100b54db9e3caca4b2cc5beb3383094","msg_type":"text","content":"微信收款失败收款【23.00】元","sender":{"id":"cli_c08abc1da138d00f","sender_type":"app"},"create_time":"2026-03-22T04:18:23+08:00","deleted":false,"updated":false,"chat_id":"oc_d5ef8b1abf66842c28ef57e1658dc096","chat_type":"group","chat_name":"益选通知群"}],"has_more":false},"isError":false,"timestamp":1774137699887}}
{"type":"message","id":"7a75fc31","parentId":"1313bd9d","timestamp":"2026-03-22T00:01:39.919Z","message":{"role":"toolResult","toolCallId":"call_function_zjmll9xo0f9v_2","toolName":"feishu_sheet","content":[{"type":"text","text":"{\n \"range\": \"5f931d!A1:E7\",\n \"values\": [\n [\n \"序号\",\n \"时间\",\n \"支付方式\",\n \"金额(元)\",\n \"备注\"\n ],\n [\n \"1\",\n \"2026-03-19 13:32:06\",\n \"支付宝\",\n \"3.50\",\n \"-无误\"\n ],\n [\n \"2\",\n \"2026-03-18 03:26:38\",\n \"支付宝\",\n \"92.00\",\n \"-无误\"\n ],\n [\n \"3\",\n \"2026-03-18 03:22:35\",\n \"支付宝\",\n \"69.00\",\n \"-无误\"\n ],\n [\n \"4\",\n \"2026-03-18 00:39:19\",\n \"支付宝\",\n \"22.00\",\n \"-无误\"\n ],\n [\n \"5\",\n \"2026-03-18 00:01:21\",\n \"支付宝\",\n \"8.00\",\n \"-无误\"\n ],\n [\n \"6\",\n \"2026-03-17 15:54:43\",\n \"微信\",\n \"3.50\",\n \"-无误\"\n ]\n ]\n}"}],"details":{"range":"5f931d!A1:E7","values":[["序号","时间","支付方式","金额(元)","备注"],["1","2026-03-19 13:32:06","支付宝","3.50","-无误"],["2","2026-03-18 03:26:38","支付宝","92.00","-无误"],["3","2026-03-18 03:22:35","支付宝","69.00","-无误"],["4","2026-03-18 00:39:19","支付宝","22.00","-无误"],["5","2026-03-18 00:01:21","支付宝","8.00","-无误"],["6","2026-03-17 15:54:43","微信","3.50","-无误"]]},"isError":false,"timestamp":1774137699908}}
{"type":"message","id":"cb1c2524","parentId":"7a75fc31","timestamp":"2026-03-22T00:01:51.822Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"好的,我找到了数据:\n\n1. 搜索结果在时间范围内找到了2条「微信收款失败」消息\n - 2026-03-22T04:18:06 微信收款失败收款【23.00】元\n - 2026-03-22T04:18:23 微信收款失败收款【23.00】元\n\n2. Excel文件现有数据已有6条记录序号1-6最大序号是6\n\n需要处理\n- 共2条失败记录\n- 需要追加到Excel表从第8行开始因为第7行是表头\n- 序号应该是7和8现有最大6+1和+2\n- 时间2026-03-22 04:18:06 和 2026-03-22 04:18:23\n- 支付方式:微信\n- 金额23.00(两条都是)\n- 备注:留空\n\n现在需要追加数据到Excel表。我需要使用 sheet 的 append 或 write 操作。由于数据从第8行开始追加我可以用 write 来写入具体范围。","thinkingSignature":"dfff3dab3c47b29deea02f9f60381553b5ffd740f68ae38a071eb27188d5aab1"},{"type":"text","text":"找到了2条收款失败记录现在追加到Excel表然后发送消息。\n"},{"type":"toolCall","id":"call_function_d7jrpmzqrlst_1","name":"feishu_sheet","arguments":{"action":"write","url":"https://my.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb","range":"5f931d!A8:E9","values":[["7","2026-03-22 04:18:06","微信","23.00",""],["8","2026-03-22 04:18:23","微信","23.00",""]]}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.5","usage":{"input":1511,"output":397,"cacheRead":37779,"cacheWrite":0,"totalTokens":39687,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774137699919}}
{"type":"message","id":"32a4e9d6","parentId":"cb1c2524","timestamp":"2026-03-22T00:01:52.300Z","message":{"role":"toolResult","toolCallId":"call_function_d7jrpmzqrlst_1","toolName":"feishu_sheet","content":[{"type":"text","text":"{\n \"updated_range\": \"5f931d!A8:E9\",\n \"updated_rows\": 2,\n \"updated_columns\": 5,\n \"updated_cells\": 10,\n \"revision\": 14\n}"}],"details":{"updated_range":"5f931d!A8:E9","updated_rows":2,"updated_columns":5,"updated_cells":10,"revision":14},"isError":false,"timestamp":1774137712287}}
{"type":"message","id":"5f65a206","parentId":"32a4e9d6","timestamp":"2026-03-22T00:01:59.465Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Excel已成功更新追加了2条记录。现在需要在群里发送消息通知。","thinkingSignature":"4312d2a3370fd8fc72b420b2d6d5b1601289dbe4dc536ce01da1acc4f5960b4a"},{"type":"toolCall","id":"call_function_qu7n88rp5hux_1","name":"feishu_im_user_message","arguments":{"action":"send","receive_id_type":"chat_id","receive_id":"oc_d5ef8b1abf66842c28ef57e1658dc096","msg_type":"text","content":"{\"text\": \"收款失败统计 (昨天~今天)共2条失败记录已更新2条到Excel\"}"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.5","usage":{"input":1971,"output":135,"cacheRead":37779,"cacheWrite":0,"totalTokens":39885,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774137712301}}
{"type":"message","id":"9ff74169","parentId":"5f65a206","timestamp":"2026-03-22T00:02:00.045Z","message":{"role":"toolResult","toolCallId":"call_function_qu7n88rp5hux_1","toolName":"feishu_im_user_message","content":[{"type":"text","text":"{\n \"message_id\": \"om_x100b54c759b0d0a0b4b930600756654\",\n \"chat_id\": \"oc_d5ef8b1abf66842c28ef57e1658dc096\",\n \"create_time\": \"1774137719948\"\n}"}],"details":{"message_id":"om_x100b54c759b0d0a0b4b930600756654","chat_id":"oc_d5ef8b1abf66842c28ef57e1658dc096","create_time":"1774137719948"},"isError":false,"timestamp":1774137720031}}
{"type":"message","id":"a787c1be","parentId":"9ff74169","timestamp":"2026-03-22T00:02:13.393Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"任务完成!总结一下:\n\n1. 搜索了「益选通知群」中昨天23:00到今天08:00的「收款失败」消息\n2. 找到2条失败记录都是微信收款失败23.00元时间在04:18左右\n3. 读取了现有Excel表已有6条记录\n4. 追加了2条新记录到Excel第7-8行\n5. 在群里发送了统计消息","thinkingSignature":"17b84ed76a08d95e6b701764e0777c41e12992c9d2d325385216222d6716848f"},{"type":"text","text":"任务完成!✅\n\n**执行结果汇总:**\n\n| 项目 | 结果 |\n|------|------|\n| 查询时间范围 | 2026-03-21 23:00 ~ 2026-03-22 08:00 |\n| 收款失败记录 | **2条** |\n| Excel追加 | ✅ 已更新2条到Sheet1第7-8行 |\n| 群里消息 | ✅ 已发送 |\n\n**失败记录详情:**\n- 序号72026-03-22 04:18:06微信支付23.00元\n- 序号82026-03-22 04:18:23微信支付23.00元\n\n群里已收到通知「收款失败统计 (昨天~今天)共2条失败记录已更新2条到Excel」"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.5","usage":{"input":2188,"output":275,"cacheRead":37779,"cacheWrite":0,"totalTokens":40242,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774137720045}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"24c4e79a-157a-4f00-9dad-5733b301ad79","timestamp":"2026-03-23T14:28:49.793Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"d7b7fbf1","parentId":null,"timestamp":"2026-03-23T14:28:49.795Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"b768e81a","parentId":"d7b7fbf1","timestamp":"2026-03-23T14:28:49.795Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774276129800,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"a62dbfe0","parentId":"b768e81a","timestamp":"2026-03-23T14:28:49.800Z"}
{"type":"message","id":"beea8f43","parentId":"a62dbfe0","timestamp":"2026-03-23T14:28:49.829Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 22:28 (Asia/Shanghai) / 2026-03-23 14:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774276129824}}
{"type":"message","id":"5f7222f3","parentId":"beea8f43","timestamp":"2026-03-23T14:28:58.618Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_6e99c5y0nnhy_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":48,"cacheRead":0,"cacheWrite":38006,"totalTokens":38211,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774276129828}}
{"type":"message","id":"b01daa2a","parentId":"5f7222f3","timestamp":"2026-03-23T14:29:08.485Z","message":{"role":"toolResult","toolCallId":"call_function_6e99c5y0nnhy_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 22:28:58] ========== 上下文使用量检测 ==========\n[2026-03-23 22:29:08] 📊 当前最高上下文使用量: 74%\n[2026-03-23 22:29:08] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 22:29:08] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9835,"aggregated":"[2026-03-23 22:28:58] ========== 上下文使用量检测 ==========\n[2026-03-23 22:29:08] 📊 当前最高上下文使用量: 74%\n[2026-03-23 22:29:08] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 22:29:08] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774276148471}}
{"type":"message","id":"e32d78ca","parentId":"b01daa2a","timestamp":"2026-03-23T14:29:12.065Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is 74%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"598bcb573db52f4a99338c929438e9170c627a722797961a35b390b1496c8a33"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**74%**\n- ✅ 状态正常,无需自动保存记忆(阈值 90%"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":475,"output":65,"cacheRead":37845,"cacheWrite":0,"totalTokens":38385,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774276148485}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"2c253e32-3bf3-45e0-af41-7b5d3794578b","timestamp":"2026-03-23T16:28:49.851Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"d0f11781","parentId":null,"timestamp":"2026-03-23T16:28:49.852Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"94af9279","parentId":"d0f11781","timestamp":"2026-03-23T16:28:49.852Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774283329856,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"b732719f","parentId":"94af9279","timestamp":"2026-03-23T16:28:49.856Z"}
{"type":"message","id":"06f5ac91","parentId":"b732719f","timestamp":"2026-03-23T16:28:49.879Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 00:28 (Asia/Shanghai) / 2026-03-23 16:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774283329875}}
{"type":"message","id":"28735191","parentId":"06f5ac91","timestamp":"2026-03-23T16:28:53.436Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script. Let me execute it.","thinkingSignature":"2a8b4480a26a11ecb53eef301c026601c6581cf01a88470085521ca125130dc9"},{"type":"toolCall","id":"call_function_lp9ve6y8ipg7_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":54,"cacheRead":0,"cacheWrite":38006,"totalTokens":38217,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774283329879}}
{"type":"message","id":"a6512a55","parentId":"28735191","timestamp":"2026-03-23T16:29:03.481Z","message":{"role":"toolResult","toolCallId":"call_function_lp9ve6y8ipg7_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session clear-sage, pid 3692068). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"clear-sage","pid":3692068,"startedAt":1774283333453,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774283343467}}
{"type":"message","id":"15fa91d7","parentId":"a6512a55","timestamp":"2026-03-23T16:29:05.877Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for the result.","thinkingSignature":"8855adc677b6cddd919aae15e635df4ec3fb33ba9b2d1a68d4249397666ec5e4"},{"type":"toolCall","id":"call_function_zuoslwybkqj1_1","name":"process","arguments":{"action":"poll","sessionId":"clear-sage","timeout":30000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":420,"output":57,"cacheRead":37845,"cacheWrite":0,"totalTokens":38322,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774283343482}}
{"type":"message","id":"a3e14ea9","parentId":"15fa91d7","timestamp":"2026-03-23T16:29:07.906Z","message":{"role":"toolResult","toolCallId":"call_function_zuoslwybkqj1_1","toolName":"process","content":[{"type":"text","text":"[2026-03-24 00:28:53] ========== 上下文使用量检测 ==========\n[2026-03-24 00:29:07] 📊 当前最高上下文使用量: 74%\n[2026-03-24 00:29:07] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-24 00:29:07] ========== 检测完成 ==========\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"clear-sage","exitCode":0,"aggregated":"[2026-03-24 00:28:53] ========== 上下文使用量检测 ==========\n[2026-03-24 00:29:07] 📊 当前最高上下文使用量: 74%\n[2026-03-24 00:29:07] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-24 00:29:07] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774283347893}}
{"type":"message","id":"85b122ff","parentId":"a3e14ea9","timestamp":"2026-03-23T16:29:11.104Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 74%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"e8ad50a7337c635c8d7c28f46a2d67d122fef7a1a9db21f739baace35152896c"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**74%**\n- ✅ 使用量正常,无需保存记忆\n\n检测时间2026-03-24 00:28:53"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":593,"output":78,"cacheRead":37845,"cacheWrite":0,"totalTokens":38516,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774283347907}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"316b1808-eb68-4c98-b129-184f10ec8d4e","timestamp":"2026-03-23T07:58:49.677Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"1d304994","parentId":null,"timestamp":"2026-03-23T07:58:49.678Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"ceda6fe3","parentId":"1d304994","timestamp":"2026-03-23T07:58:49.678Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774252729682,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"f51c8695","parentId":"ceda6fe3","timestamp":"2026-03-23T07:58:49.682Z"}
{"type":"message","id":"db2657e3","parentId":"f51c8695","timestamp":"2026-03-23T07:58:49.706Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 15:58 (Asia/Shanghai) / 2026-03-23 07:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774252729702}}
{"type":"message","id":"f8b91d69","parentId":"db2657e3","timestamp":"2026-03-23T07:58:53.851Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script.","thinkingSignature":"f889fde8b6e9a410d983d0e527a84b37dc9a89ad2c65aaa9e758243419ce7101"},{"type":"toolCall","id":"call_function_2sckdgygqd7d_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":46,"cacheRead":0,"cacheWrite":38006,"totalTokens":38209,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774252729705}}
{"type":"message","id":"50dad77d","parentId":"f8b91d69","timestamp":"2026-03-23T07:59:03.253Z","message":{"role":"toolResult","toolCallId":"call_function_2sckdgygqd7d_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 15:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 15:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 15:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 15:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9370,"aggregated":"[2026-03-23 15:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 15:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 15:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 15:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774252743240}}
{"type":"message","id":"dc6daa1c","parentId":"50dad77d","timestamp":"2026-03-23T07:59:06.586Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 38%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"0b380c3a01d6d5b342be2b95e95863a3539aecf0cab74619d6a1dcaac7644389"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**38%**\n- ✅ 状态正常,无需保存记忆\n\n检测时间2026-03-23 15:58:53"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":473,"output":79,"cacheRead":37845,"cacheWrite":0,"totalTokens":38397,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774252743254}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"4098e218-5f5c-43cb-bac5-5238c1b70b2a","timestamp":"2026-03-23T09:28:49.707Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"85518254","parentId":null,"timestamp":"2026-03-23T09:28:49.708Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"ad55e6a1","parentId":"85518254","timestamp":"2026-03-23T09:28:49.708Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774258129712,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"01dd8420","parentId":"ad55e6a1","timestamp":"2026-03-23T09:28:49.712Z"}
{"type":"message","id":"7e4408a1","parentId":"01dd8420","timestamp":"2026-03-23T09:28:49.738Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 17:28 (Asia/Shanghai) / 2026-03-23 09:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774258129734}}
{"type":"message","id":"c9c2b22b","parentId":"7e4408a1","timestamp":"2026-03-23T09:28:53.714Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_xckdkl94073e_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":48,"cacheRead":0,"cacheWrite":38006,"totalTokens":38211,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774258129738}}
{"type":"message","id":"fc104333","parentId":"c9c2b22b","timestamp":"2026-03-23T09:29:02.938Z","message":{"role":"toolResult","toolCallId":"call_function_xckdkl94073e_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 17:28:53] ========== 上下文使用量检测 ==========\n[2026-03-23 17:29:02] 📊 当前最高上下文使用量: 38%\n[2026-03-23 17:29:02] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 17:29:02] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9195,"aggregated":"[2026-03-23 17:28:53] ========== 上下文使用量检测 ==========\n[2026-03-23 17:29:02] 📊 当前最高上下文使用量: 38%\n[2026-03-23 17:29:02] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 17:29:02] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774258142925}}
{"type":"message","id":"b0a2c445","parentId":"fc104333","timestamp":"2026-03-23T09:29:06.303Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 38%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"0b380c3a01d6d5b342be2b95e95863a3539aecf0cab74619d6a1dcaac7644389"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常(未达到 90% 阈值,无需自动保存记忆)"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":475,"output":73,"cacheRead":37845,"cacheWrite":0,"totalTokens":38393,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774258142939}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"41034092-6f12-4190-be06-1b289e6e704a","timestamp":"2026-03-23T17:58:49.828Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"30eab998","parentId":null,"timestamp":"2026-03-23T17:58:49.830Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"e2584416","parentId":"30eab998","timestamp":"2026-03-23T17:58:49.830Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774288729834,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"88b49be7","parentId":"e2584416","timestamp":"2026-03-23T17:58:49.834Z"}
{"type":"message","id":"3104b629","parentId":"88b49be7","timestamp":"2026-03-23T17:58:49.856Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 01:58 (Asia/Shanghai) / 2026-03-23 17:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774288729852}}
{"type":"message","id":"397e3241","parentId":"3104b629","timestamp":"2026-03-23T17:58:53.645Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a script to check context memory usage. Let me execute it.","thinkingSignature":"09ba085769c8cd4022f55af615ac97847bd9a2d540bb4772c164741c15ea7f2e"},{"type":"toolCall","id":"call_function_p15gyjp4bqzt_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":30}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":66,"cacheRead":0,"cacheWrite":38006,"totalTokens":38229,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774288729855}}
{"type":"message","id":"6c3b40f7","parentId":"397e3241","timestamp":"2026-03-23T17:59:03.326Z","message":{"role":"toolResult","toolCallId":"call_function_p15gyjp4bqzt_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-24 01:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 01:59:03] 📊 当前最高上下文使用量: 39%\n[2026-03-24 01:59:03] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 01:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9649,"aggregated":"[2026-03-24 01:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 01:59:03] 📊 当前最高上下文使用量: 39%\n[2026-03-24 01:59:03] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 01:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774288743311}}
{"type":"message","id":"096b7588","parentId":"6c3b40f7","timestamp":"2026-03-23T17:59:05.887Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 39%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"77aff29c92141ef56e630542eac850d7bf89191ef7ae484caf4cffb62a8aa997"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**39%**\n- 状态:✅ 正常,无需保存记忆\n\n上下文使用量远低于 90% 警戒线,一切正常!🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":493,"output":80,"cacheRead":37845,"cacheWrite":0,"totalTokens":38418,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774288743326}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"4965b0dd-1b06-4209-9eaa-e13568c234b4","timestamp":"2026-03-23T11:58:49.741Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"0ddf244b","parentId":null,"timestamp":"2026-03-23T11:58:49.742Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"f1e5448f","parentId":"0ddf244b","timestamp":"2026-03-23T11:58:49.742Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774267129746,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"e83cf080","parentId":"f1e5448f","timestamp":"2026-03-23T11:58:49.746Z"}
{"type":"message","id":"60924486","parentId":"e83cf080","timestamp":"2026-03-23T11:58:49.771Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 19:58 (Asia/Shanghai) / 2026-03-23 11:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774267129767}}
{"type":"message","id":"4c162cf0","parentId":"60924486","timestamp":"2026-03-23T11:58:55.268Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script. Let me execute it.","thinkingSignature":"2a8b4480a26a11ecb53eef301c026601c6581cf01a88470085521ca125130dc9"},{"type":"toolCall","id":"call_function_tg715bt6q280_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":54,"cacheRead":0,"cacheWrite":38006,"totalTokens":38217,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774267129770}}
{"type":"message","id":"d5e6517a","parentId":"4c162cf0","timestamp":"2026-03-23T11:59:04.766Z","message":{"role":"toolResult","toolCallId":"call_function_tg715bt6q280_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 19:58:55] ========== 上下文使用量检测 ==========\n[2026-03-23 19:59:04] 📊 当前最高上下文使用量: 24%\n[2026-03-23 19:59:04] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 19:59:04] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9467,"aggregated":"[2026-03-23 19:58:55] ========== 上下文使用量检测 ==========\n[2026-03-23 19:59:04] 📊 当前最高上下文使用量: 24%\n[2026-03-23 19:59:04] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 19:59:04] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774267144753}}
{"type":"message","id":"447474f1","parentId":"d5e6517a","timestamp":"2026-03-23T11:59:07.709Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 24%, which is well below the 90% threshold. No memory save was triggered.","thinkingSignature":"1f8065cc862e37d339673e1112b2f213a12809f5902fe87bdec79a944e06188b"},{"type":"text","text":"📊 上下文使用量检测完成\n\n- **当前使用量**: 24%\n- **状态**: ✅ 正常\n- **操作**: 无需保存记忆\n\n上下文使用量处于安全范围下次检测将在约30分钟后自动进行。"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":481,"output":83,"cacheRead":37845,"cacheWrite":0,"totalTokens":38409,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774267144767}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"4c94425e-cb76-419f-9270-0c66a4d1d396","timestamp":"2026-03-23T19:28:49.848Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"73d55688","parentId":null,"timestamp":"2026-03-23T19:28:49.849Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"98b195cf","parentId":"73d55688","timestamp":"2026-03-23T19:28:49.849Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774294129853,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"1f24e3ca","parentId":"98b195cf","timestamp":"2026-03-23T19:28:49.853Z"}
{"type":"message","id":"dc0156d7","parentId":"1f24e3ca","timestamp":"2026-03-23T19:28:49.875Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 03:28 (Asia/Shanghai) / 2026-03-23 19:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774294129871}}
{"type":"message","id":"5b0db13b","parentId":"dc0156d7","timestamp":"2026-03-23T19:28:54.662Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_dzmb5msix0rb_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":30}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":58,"cacheRead":0,"cacheWrite":38006,"totalTokens":38221,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774294129874}}
{"type":"message","id":"1f8fd388","parentId":"5b0db13b","timestamp":"2026-03-23T19:29:04.295Z","message":{"role":"toolResult","toolCallId":"call_function_dzmb5msix0rb_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-24 03:28:54] ========== 上下文使用量检测 ==========\n[2026-03-24 03:29:04] 📊 当前最高上下文使用量: 40%\n[2026-03-24 03:29:04] ✅ 上下文使用量正常 (40%),无需保存\n[2026-03-24 03:29:04] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9600,"aggregated":"[2026-03-24 03:28:54] ========== 上下文使用量检测 ==========\n[2026-03-24 03:29:04] 📊 当前最高上下文使用量: 40%\n[2026-03-24 03:29:04] ✅ 上下文使用量正常 (40%),无需保存\n[2026-03-24 03:29:04] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774294144279}}
{"type":"message","id":"157e1fc7","parentId":"1f8fd388","timestamp":"2026-03-23T19:29:07.329Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 40%, well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"e07a5009d6a48928a42557741bdc67ad9fb5fea23e8ac661df88e6f6a7f525ac"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**40%**\n- 状态:正常,无需保存记忆\n\n使用量远低于 90% 阈值,一切平稳,皮皮虾继续睡大觉~ 🌊"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":485,"output":79,"cacheRead":37845,"cacheWrite":0,"totalTokens":38409,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774294144295}}

View File

@ -0,0 +1,12 @@
{"type":"session","version":3,"id":"5044162c-aca0-450b-90d1-f0345d5777da","timestamp":"2026-03-22T07:59:19.786Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"f0459030","parentId":null,"timestamp":"2026-03-22T07:59:19.795Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"f3159d59","parentId":"f0459030","timestamp":"2026-03-22T07:59:19.795Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774166359802,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"64fa9bed","parentId":"f3159d59","timestamp":"2026-03-22T07:59:19.802Z"}
{"type":"message","id":"4036cadf","parentId":"64fa9bed","timestamp":"2026-03-22T07:59:19.837Z","message":{"role":"user","content":[{"type":"text","text":"Conversation info (untrusted metadata):\n```json\n{\n \"message_id\": \"openclaw-weixin:1774166359057-64f47073\",\n \"timestamp\": \"Sun 2026-03-22 15:59 GMT+8\"\n}\n```\n\n你是谁"}],"timestamp":1774166359825}}
{"type":"message","id":"fb245a65","parentId":"4036cadf","timestamp":"2026-03-22T07:59:27.743Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user asked \"你是谁\" (Who are you?). Let me check my memory files to understand who I am according to the configuration.\n\nFrom IDENTITY.md, I'm 欢欢助理 (HuanHuan Assistant). From USER.md, the user is 欢哥 (Captain). From SOUL.md and IDENTITY.md, I have a \"皮皮虾\" (mantis shrimp) persona.\n\nI should respond in character as 欢欢助理, using the casual, friendly style with the mantis shrimp emoji 🦐.","thinkingSignature":"d2a6ed4ddc12a3212db17f65f1c8b8652414fa00b9bcc41a45bd3f4afce7048d"},{"type":"text","text":"<final>\n嘿船长🦐 我是**欢欢助理**,你的贴心小助理~\n\n专门帮你搞定各种任务查资料、管文件、写代码、搜信息都不在话下。有事儿尽管吩咐保证靠谱高效😎\n\n今天想搞点什么\n</final>"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":143,"output":172,"cacheRead":0,"cacheWrite":43762,"totalTokens":44077,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774166359833}}
{"type":"message","id":"82339850","parentId":"fb245a65","timestamp":"2026-03-22T08:07:45.469Z","message":{"role":"user","content":[{"type":"text","text":"Conversation info (untrusted metadata):\n```json\n{\n \"message_id\": \"openclaw-weixin:1774166864942-badc5288\",\n \"timestamp\": \"Sun 2026-03-22 16:07 GMT+8\"\n}\n```\n\n你现在使用的是哪个消息渠道"}],"timestamp":1774166865465}}
{"type":"message","id":"5dd31be7","parentId":"82339850","timestamp":"2026-03-22T08:07:51.423Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user is asking which message channel I'm currently using. Based on the runtime info provided at the top, I can see `channel=openclaw-weixin`, which means I'm using WeChat (企业微信/微信).","thinkingSignature":"c860a25e1e961a4b45ca3e98742af1eab7d206d110b3d04224f70cb2ddfd0d1e"},{"type":"text","text":"<final>\n现在用的是**企业微信**渠道哦~ 🏆\n\n就是那个帮你管股票监控、发通知的企微账号。有啥需要切换的吗\n</final>"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":143,"output":85,"cacheRead":0,"cacheWrite":43892,"totalTokens":44120,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774166865468}}
{"type":"message","id":"fa13ad17","parentId":"5dd31be7","timestamp":"2026-03-22T08:18:33.373Z","message":{"role":"user","content":[{"type":"text","text":"Conversation info (untrusted metadata):\n```json\n{\n \"message_id\": \"openclaw-weixin:1774167512863-13de91b8\",\n \"timestamp\": \"Sun 2026-03-22 16:18 GMT+8\"\n}\n```\n\n你说错了你现在渠道应该是微信 clawbot这是微信官方今天才出的功能"}],"timestamp":1774167513368}}
{"type":"message","id":"42792647","parentId":"fa13ad17","timestamp":"2026-03-22T08:18:41.701Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user is correcting me. They say I'm currently using WeChat (clawbot), not Enterprise WeChat. Let me acknowledge this correction and update my understanding.\n\nLooking at the runtime info:\n```\nchannel=openclaw-weixin\n```\n\nAnd the inbound metadata shows:\n```json\n{\n \"chat_id\": \"o9cq80zOqXVuWi1N-zNlWdbNGFK4@im.wechat\",\n \"channel\": \"openclaw-weixin\",\n \"provider\": \"openclaw-weixin\",\n \"chat_type\": \"direct\"\n}\n```\n\nThe `im.wechat` suggests this is personal WeChat, not Enterprise WeChat. The user is correcting me - I should acknowledge this and update my memory if needed.","thinkingSignature":"791bfc7d3c24ce3222b44fb4472523b9c437f22702593f2ad91b9403901e0ba6"},{"type":"text","text":"[[reply_to_current]] 感谢纠正!🙏 你说得对,我搞混了~\n\n当前确实是 **微信clawbot** 渠道,不是企业微信。这是微信官方新出的功能,对吧?\n\n记下来了后续确认清楚渠道再说有什么需要帮忙的尽管吩咐 🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":143,"output":216,"cacheRead":0,"cacheWrite":44011,"totalTokens":44370,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774167513372}}
{"type":"message","id":"5f52d5fe","parentId":"42792647","timestamp":"2026-03-22T08:19:36.605Z","message":{"role":"user","content":[{"type":"text","text":"Conversation info (untrusted metadata):\n```json\n{\n \"message_id\": \"openclaw-weixin:1774167576174-aadd3f6b\",\n \"timestamp\": \"Sun 2026-03-22 16:19 GMT+8\"\n}\n```\n\n你查一下 clawbot 的消息,为什么我在微信聊天记录里面找不到你"}],"timestamp":1774167576601}}
{"type":"message","id":"3248abad","parentId":"5f52d5fe","timestamp":"2026-03-22T08:19:48.173Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user is asking me to check messages from clawbot to understand why they can't find the chat in their WeChat history. Let me think about this...\n\nThe channel is \"openclaw-weixin\" which according to the system prompt shows it's connected via what appears to be clawbot (OpenClaw's WeChat integration). The user says they can't find the chat in their WeChat chat history.\n\nThis is a technical issue about the clawbot functionality. Let me check if there's any way to investigate this. I should look at recent messages or check the session/channel configuration.\n\nActually, I don't have a tool to directly check \"why the user can't find the chat\". This could be because:\n1. The bot hasn't been activated/properly connected\n2. The user needs to search for the bot in WeChat\n3. The bot might be in \"offline\" mode or similar\n\nI should be honest that I can't directly diagnose why they can't find the chat, but I can try to help troubleshoot. I don't really have a tool to \"check clawbot messages\" in a way that would diagnose their WeChat client issue.\n\nLet me just be honest and helpful - I can explain possible reasons and how they might find/add the bot.","thinkingSignature":"a70fd06cae7cd20f056a5c93661ad36c5ccc17ff014c058da3db80b51e8fdaa4"},{"type":"text","text":"<final>\n这个我还真查不到原因 😅 我只能看到这里的对话,但看不到你微信那边的具体情况。\n\n可能的原因\n1. **搜索一下 bot 名称** — 微信里搜索 \"clawbot\" 或 \"OpenClaw\" 试试\n2. **被隐藏了** — 微信搜索设置里看看是不是被折叠或隐藏了\n3. **还没加好友** — 如果是测试阶段,可能需要先添加 bot 为好友\n\n你当时是怎么联系上我的是扫码还是搜索知道 bot 的具体名字吗?我帮你想想办法~ 🦐\n</final>"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":143,"output":377,"cacheRead":43982,"cacheWrite":171,"totalTokens":44673,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774167576604}}

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"53ce517e-caeb-4e93-ba49-c17e9c3b3f31","timestamp":"2026-03-23T15:28:49.790Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"7a4238ce","parentId":null,"timestamp":"2026-03-23T15:28:49.790Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"d43f2164","parentId":"7a4238ce","timestamp":"2026-03-23T15:28:49.790Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774279729794,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"141101cf","parentId":"d43f2164","timestamp":"2026-03-23T15:28:49.794Z"}
{"type":"message","id":"ec9dc3ea","parentId":"141101cf","timestamp":"2026-03-23T15:28:49.816Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 23:28 (Asia/Shanghai) / 2026-03-23 15:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774279729812}}
{"type":"message","id":"b364faa7","parentId":"ec9dc3ea","timestamp":"2026-03-23T15:28:53.762Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"This is a cron job asking me to run a context memory guard script. Let me execute it.","thinkingSignature":"0c72cdd4c2c68f41057cf6129a898ebc52b0f261a6e6750898deb259e87a9402"},{"type":"toolCall","id":"call_function_5wnib4zc3qj0_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":67,"cacheRead":0,"cacheWrite":38006,"totalTokens":38230,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774279729815}}
{"type":"message","id":"0bd32af0","parentId":"b364faa7","timestamp":"2026-03-23T15:29:03.806Z","message":{"role":"toolResult","toolCallId":"call_function_5wnib4zc3qj0_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session gentle-meadow, pid 3562514). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"gentle-meadow","pid":3562514,"startedAt":1774279733778,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774279743792}}
{"type":"message","id":"fed02c5a","parentId":"0bd32af0","timestamp":"2026-03-23T15:29:07.156Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for it to complete.","thinkingSignature":"8c41d6dcfd223916268b7c85ea3e44d0e3b4129e2ee61ddf19236f249a6227d6"},{"type":"toolCall","id":"call_function_us4tz5ezeajt_1","name":"process","arguments":{"action":"poll","sessionId":"gentle-meadow","timeout":60000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":433,"output":59,"cacheRead":37845,"cacheWrite":0,"totalTokens":38337,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774279743806}}
{"type":"message","id":"5556050c","parentId":"fed02c5a","timestamp":"2026-03-23T15:29:08.189Z","message":{"role":"toolResult","toolCallId":"call_function_us4tz5ezeajt_1","toolName":"process","content":[{"type":"text","text":"[2026-03-23 23:28:53] ========== 上下文使用量检测 ==========\n[2026-03-23 23:29:08] 📊 当前最高上下文使用量: 74%\n[2026-03-23 23:29:08] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 23:29:08] ========== 检测完成 ==========\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"gentle-meadow","exitCode":0,"aggregated":"[2026-03-23 23:28:53] ========== 上下文使用量检测 ==========\n[2026-03-23 23:29:08] 📊 当前最高上下文使用量: 74%\n[2026-03-23 23:29:08] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 23:29:08] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774279748175}}
{"type":"message","id":"8ad27309","parentId":"5556050c","timestamp":"2026-03-23T15:29:11.417Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is 74%, which is below the 90% threshold, so no memory save was triggered. Simple result.","thinkingSignature":"958decad703306270d85dc89536eccb8057dbad508f1dd15d9e986529290a2ab"},{"type":"text","text":"📊 上下文使用量检测结果:\n\n- **当前使用量**74%\n- **状态**:✅ 正常\n- **操作**:无需保存记忆(阈值 90%\n\n一切正常欢哥放心 🌙"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":608,"output":82,"cacheRead":37845,"cacheWrite":0,"totalTokens":38535,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774279748189}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"5c6bb214-369e-480a-b2ce-f3d6e76f3ae9","timestamp":"2026-03-23T17:28:49.825Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"40c841e8","parentId":null,"timestamp":"2026-03-23T17:28:49.826Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"db6b3704","parentId":"40c841e8","timestamp":"2026-03-23T17:28:49.826Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774286929830,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"6d0751c9","parentId":"db6b3704","timestamp":"2026-03-23T17:28:49.830Z"}
{"type":"message","id":"d1268da5","parentId":"6d0751c9","timestamp":"2026-03-23T17:28:49.856Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 01:28 (Asia/Shanghai) / 2026-03-23 17:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774286929852}}
{"type":"message","id":"ddf39613","parentId":"d1268da5","timestamp":"2026-03-23T17:28:53.659Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user is asking me to run a context memory guard script. Let me execute it.","thinkingSignature":"43ca78bf73061d147fb2a52f13bafa0524682e854b60336636ba0a63f310a205"},{"type":"toolCall","id":"call_function_pjmj0jpt7qvj_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":65,"cacheRead":0,"cacheWrite":38006,"totalTokens":38228,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774286929855}}
{"type":"message","id":"d504c10a","parentId":"ddf39613","timestamp":"2026-03-23T17:29:02.984Z","message":{"role":"toolResult","toolCallId":"call_function_pjmj0jpt7qvj_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-24 01:28:53] ========== 上下文使用量检测 ==========\n[2026-03-24 01:29:02] 📊 当前最高上下文使用量: 39%\n[2026-03-24 01:29:02] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 01:29:02] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9301,"aggregated":"[2026-03-24 01:28:53] ========== 上下文使用量检测 ==========\n[2026-03-24 01:29:02] 📊 当前最高上下文使用量: 39%\n[2026-03-24 01:29:02] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 01:29:02] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774286942976}}
{"type":"message","id":"d3f8e33a","parentId":"d504c10a","timestamp":"2026-03-23T17:29:06.229Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 39%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"5ab6bb554e201aabbd95aa3b5f349e1e33579d817ff51acd472917a992575c3e"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**39%**\n- 状态:✅ 正常,无需保存记忆\n\n检测阈值 90%,当前使用量远低于阈值,记忆保持不变。)"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":492,"output":81,"cacheRead":37845,"cacheWrite":0,"totalTokens":38418,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774286942984}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"5d5108fd-78e2-416b-9ab4-f8d5e298477f","timestamp":"2026-03-23T12:28:49.762Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"c7cf6aab","parentId":null,"timestamp":"2026-03-23T12:28:49.762Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"021fd637","parentId":"c7cf6aab","timestamp":"2026-03-23T12:28:49.762Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774268929767,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"5fbab322","parentId":"021fd637","timestamp":"2026-03-23T12:28:49.767Z"}
{"type":"message","id":"18ba826c","parentId":"5fbab322","timestamp":"2026-03-23T12:28:49.793Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 20:28 (Asia/Shanghai) / 2026-03-23 12:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774268929788}}
{"type":"message","id":"b12ddd89","parentId":"18ba826c","timestamp":"2026-03-23T12:28:54.855Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script. Let me execute it.","thinkingSignature":"2a8b4480a26a11ecb53eef301c026601c6581cf01a88470085521ca125130dc9"},{"type":"toolCall","id":"call_function_8acqhtoj1csd_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":54,"cacheRead":0,"cacheWrite":38006,"totalTokens":38217,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774268929792}}
{"type":"message","id":"a91074f1","parentId":"b12ddd89","timestamp":"2026-03-23T12:29:04.199Z","message":{"role":"toolResult","toolCallId":"call_function_8acqhtoj1csd_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 20:28:54] ========== 上下文使用量检测 ==========\n[2026-03-23 20:29:04] 📊 当前最高上下文使用量: 24%\n[2026-03-23 20:29:04] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 20:29:04] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9313,"aggregated":"[2026-03-23 20:28:54] ========== 上下文使用量检测 ==========\n[2026-03-23 20:29:04] 📊 当前最高上下文使用量: 24%\n[2026-03-23 20:29:04] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 20:29:04] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774268944185}}
{"type":"message","id":"f025dc36","parentId":"a91074f1","timestamp":"2026-03-23T12:29:07.277Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 24%, which is well below the 90% threshold. No memory save was triggered.","thinkingSignature":"1f8065cc862e37d339673e1112b2f213a12809f5902fe87bdec79a944e06188b"},{"type":"text","text":"📊 上下文使用量检测完成\n\n- **当前使用量**: 24%\n- **状态**: ✅ 正常\n- **操作**: 无需保存记忆\n\n使用量远低于 90% 阈值,一切正常!🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":481,"output":82,"cacheRead":37845,"cacheWrite":0,"totalTokens":38408,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774268944199}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"608ac905-d07d-4623-b84b-18aec89455b0","timestamp":"2026-03-23T13:28:49.774Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"d4c9dea1","parentId":null,"timestamp":"2026-03-23T13:28:49.774Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"74c82ef7","parentId":"d4c9dea1","timestamp":"2026-03-23T13:28:49.775Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774272529779,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"95a5e6c1","parentId":"74c82ef7","timestamp":"2026-03-23T13:28:49.779Z"}
{"type":"message","id":"7d43bb4c","parentId":"95a5e6c1","timestamp":"2026-03-23T13:28:49.805Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 21:28 (Asia/Shanghai) / 2026-03-23 13:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774272529800}}
{"type":"message","id":"b355acb5","parentId":"7d43bb4c","timestamp":"2026-03-23T13:28:56.532Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script. Let me execute it.","thinkingSignature":"2a8b4480a26a11ecb53eef301c026601c6581cf01a88470085521ca125130dc9"},{"type":"toolCall","id":"call_function_huxdk12vw8yw_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":54,"cacheRead":0,"cacheWrite":38006,"totalTokens":38217,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774272529804}}
{"type":"message","id":"720959d6","parentId":"b355acb5","timestamp":"2026-03-23T13:29:06.158Z","message":{"role":"toolResult","toolCallId":"call_function_huxdk12vw8yw_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 21:28:56] ========== 上下文使用量检测 ==========\n[2026-03-23 21:29:06] 📊 当前最高上下文使用量: 24%\n[2026-03-23 21:29:06] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 21:29:06] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9596,"aggregated":"[2026-03-23 21:28:56] ========== 上下文使用量检测 ==========\n[2026-03-23 21:29:06] 📊 当前最高上下文使用量: 24%\n[2026-03-23 21:29:06] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 21:29:06] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774272546145}}
{"type":"message","id":"b5fe1d16","parentId":"720959d6","timestamp":"2026-03-23T13:29:09.709Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 24%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"b89231d4477e07f711d214202e38c509abf23fbfc37017ef413f951a9333724c"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**24%**\n- 状态:正常,无需保存记忆\n- 阈值90%"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":481,"output":67,"cacheRead":37845,"cacheWrite":0,"totalTokens":38393,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774272546158}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"66e2ee5f-95bc-489c-bbc8-cb0dd38d9a7d","timestamp":"2026-03-23T08:58:49.693Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"540297a0","parentId":null,"timestamp":"2026-03-23T08:58:49.694Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"76e26cc5","parentId":"540297a0","timestamp":"2026-03-23T08:58:49.694Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774256329698,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"873ff432","parentId":"76e26cc5","timestamp":"2026-03-23T08:58:49.698Z"}
{"type":"message","id":"2126346e","parentId":"873ff432","timestamp":"2026-03-23T08:58:49.720Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 16:58 (Asia/Shanghai) / 2026-03-23 08:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774256329716}}
{"type":"message","id":"53f3f98f","parentId":"2126346e","timestamp":"2026-03-23T08:58:53.797Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_7pkqvfdb1hfu_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":58,"cacheRead":0,"cacheWrite":38006,"totalTokens":38221,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774256329719}}
{"type":"message","id":"d70dc62a","parentId":"53f3f98f","timestamp":"2026-03-23T08:59:03.210Z","message":{"role":"toolResult","toolCallId":"call_function_7pkqvfdb1hfu_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 16:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 16:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 16:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 16:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9380,"aggregated":"[2026-03-23 16:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 16:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 16:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 16:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774256343197}}
{"type":"message","id":"2de54b8a","parentId":"d70dc62a","timestamp":"2026-03-23T08:59:06.146Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 38%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"0b380c3a01d6d5b342be2b95e95863a3539aecf0cab74619d6a1dcaac7644389"},{"type":"text","text":"\n📊 上下文使用量检测完成:\n\n- **当前使用量**: 38%\n- **状态**: ✅ 正常,无需保存记忆\n\n上下文使用量处于安全范围继续正常使用即可。"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":485,"output":73,"cacheRead":37845,"cacheWrite":0,"totalTokens":38403,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774256343211}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"687d2886-1a7c-4e8d-963d-9a636d14e611","timestamp":"2026-03-23T10:58:49.721Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"35cbb02c","parentId":null,"timestamp":"2026-03-23T10:58:49.722Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"2e813513","parentId":"35cbb02c","timestamp":"2026-03-23T10:58:49.722Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774263529726,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"53c2a443","parentId":"2e813513","timestamp":"2026-03-23T10:58:49.726Z"}
{"type":"message","id":"a57a3fa7","parentId":"53c2a443","timestamp":"2026-03-23T10:58:49.748Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 18:58 (Asia/Shanghai) / 2026-03-23 10:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774263529744}}
{"type":"message","id":"5788f48d","parentId":"a57a3fa7","timestamp":"2026-03-23T10:58:53.899Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script. Let me execute it.","thinkingSignature":"2a8b4480a26a11ecb53eef301c026601c6581cf01a88470085521ca125130dc9"},{"type":"toolCall","id":"call_function_d1jnt4raa442_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":54,"cacheRead":0,"cacheWrite":38006,"totalTokens":38217,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774263529747}}
{"type":"message","id":"808eca3d","parentId":"5788f48d","timestamp":"2026-03-23T10:59:03.220Z","message":{"role":"toolResult","toolCallId":"call_function_d1jnt4raa442_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 18:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 18:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 18:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 18:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9283,"aggregated":"[2026-03-23 18:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 18:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 18:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 18:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774263543206}}
{"type":"message","id":"f133fd70","parentId":"808eca3d","timestamp":"2026-03-23T10:59:06.013Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 38%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"c9c31f9a0ead98f3df5b13570ca8c15e46721d1757569787e514c8581689b067"},{"type":"text","text":"\n✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**38%**\n- ✅ 状态正常,无需保存记忆\n- 阈值≥90% 才会自动触发记忆保存"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":481,"output":75,"cacheRead":37845,"cacheWrite":0,"totalTokens":38401,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774263543220}}

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"714c3222-24d4-4194-bcc3-ee3bf2b2df26","timestamp":"2026-03-23T16:58:49.806Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"faebca28","parentId":null,"timestamp":"2026-03-23T16:58:49.807Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"19dd520b","parentId":"faebca28","timestamp":"2026-03-23T16:58:49.807Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774285129811,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"674251e2","parentId":"19dd520b","timestamp":"2026-03-23T16:58:49.811Z"}
{"type":"message","id":"b9702709","parentId":"674251e2","timestamp":"2026-03-23T16:58:49.832Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 00:58 (Asia/Shanghai) / 2026-03-23 16:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774285129829}}
{"type":"message","id":"225c0925","parentId":"b9702709","timestamp":"2026-03-23T16:58:53.471Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as instructed.","thinkingSignature":"a48e7bc90f1cc07288bc58d03f7f07d84ddf6cc40c6d9bb638c99c0ffb93c5d3"},{"type":"toolCall","id":"call_function_yawy8kbij4ao_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":48,"cacheRead":0,"cacheWrite":38006,"totalTokens":38211,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774285129832}}
{"type":"message","id":"4a35ecd6","parentId":"225c0925","timestamp":"2026-03-23T16:59:03.517Z","message":{"role":"toolResult","toolCallId":"call_function_yawy8kbij4ao_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session grand-otter, pid 3760839). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"grand-otter","pid":3760839,"startedAt":1774285133489,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774285143503}}
{"type":"message","id":"aacf745c","parentId":"4a35ecd6","timestamp":"2026-03-23T16:59:05.643Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for the result.","thinkingSignature":"8855adc677b6cddd919aae15e635df4ec3fb33ba9b2d1a68d4249397666ec5e4"},{"type":"toolCall","id":"call_function_ktndktr3f297_1","name":"process","arguments":{"action":"poll","sessionId":"grand-otter","timeout":30000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":415,"output":58,"cacheRead":37845,"cacheWrite":0,"totalTokens":38318,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774285143517}}
{"type":"message","id":"5a7c33bd","parentId":"aacf745c","timestamp":"2026-03-23T16:59:11.977Z","message":{"role":"toolResult","toolCallId":"call_function_ktndktr3f297_1","toolName":"process","content":[{"type":"text","text":"[2026-03-24 00:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 00:59:11] 📊 当前最高上下文使用量: 74%\n[2026-03-24 00:59:11] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-24 00:59:11] ========== 检测完成 ==========\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"grand-otter","exitCode":0,"aggregated":"[2026-03-24 00:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 00:59:11] 📊 当前最高上下文使用量: 74%\n[2026-03-24 00:59:11] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-24 00:59:11] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774285151963}}
{"type":"message","id":"68073fa1","parentId":"5a7c33bd","timestamp":"2026-03-23T16:59:14.812Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is 74%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"598bcb573db52f4a99338c929438e9170c627a722797961a35b390b1496c8a33"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**74%**\n- ✅ 状态正常,无需保存记忆\n\n阈值 90%,当前安全)"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":589,"output":68,"cacheRead":37845,"cacheWrite":0,"totalTokens":38502,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774285151978}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"79a60ef9-d879-4cec-95ca-3fef7e43c5ed","timestamp":"2026-03-23T12:58:49.752Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"5a5ab763","parentId":null,"timestamp":"2026-03-23T12:58:49.753Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"8f6c02d6","parentId":"5a5ab763","timestamp":"2026-03-23T12:58:49.753Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774270729757,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"ba90cb79","parentId":"8f6c02d6","timestamp":"2026-03-23T12:58:49.757Z"}
{"type":"message","id":"1fdcd2b8","parentId":"ba90cb79","timestamp":"2026-03-23T12:58:49.779Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 20:58 (Asia/Shanghai) / 2026-03-23 12:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774270729775}}
{"type":"message","id":"76bf0cc3","parentId":"1fdcd2b8","timestamp":"2026-03-23T12:58:54.065Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"This is a cron job that runs a context memory guard script. Let me execute it.","thinkingSignature":"737153de2567992b46c4fedef4d99dcd98c521eab902db0fc000e1ef607c9827"},{"type":"toolCall","id":"call_function_iq4fjh0d5srw_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":65,"cacheRead":0,"cacheWrite":38006,"totalTokens":38228,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774270729778}}
{"type":"message","id":"c533acfc","parentId":"76bf0cc3","timestamp":"2026-03-23T12:59:04.104Z","message":{"role":"toolResult","toolCallId":"call_function_iq4fjh0d5srw_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session wild-lobster, pid 3246633). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"wild-lobster","pid":3246633,"startedAt":1774270734081,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774270744095}}
{"type":"message","id":"3ca6ca79","parentId":"c533acfc","timestamp":"2026-03-23T12:59:06.886Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for the script to complete.","thinkingSignature":"344a7fdf9e72d35a70f150d71af7c603801f7355064ce8cdd590801d9816693b"},{"type":"toolCall","id":"call_function_iy4j1ffrxnbu_1","name":"process","arguments":{"action":"poll","sessionId":"wild-lobster","timeout":60000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":432,"output":60,"cacheRead":37845,"cacheWrite":0,"totalTokens":38337,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774270744105}}
{"type":"message","id":"3094821b","parentId":"3ca6ca79","timestamp":"2026-03-23T12:59:06.916Z","message":{"role":"toolResult","toolCallId":"call_function_iy4j1ffrxnbu_1","toolName":"process","content":[{"type":"text","text":"[2026-03-23 20:58:54] ========== 上下文使用量检测 ==========\n[2026-03-23 20:59:05] 📊 当前最高上下文使用量: 24%\n[2026-03-23 20:59:05] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 20:59:05] ========== 检测完成 ==========\n\n\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"wild-lobster","exitCode":0,"aggregated":"[2026-03-23 20:58:54] ========== 上下文使用量检测 ==========\n[2026-03-23 20:59:05] 📊 当前最高上下文使用量: 24%\n[2026-03-23 20:59:05] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 20:59:05] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774270746903}}
{"type":"message","id":"20443f93","parentId":"3094821b","timestamp":"2026-03-23T12:59:10.092Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The current context usage is 24%, which is well below the 90% threshold. No memory save was needed.","thinkingSignature":"ab5fa1799f49d5bbdcd1040384261f409898d31825156e65821b1df8732d6d38"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**24%**\n- ✅ 使用量正常,无需保存记忆\n\n检测结果正常上下文使用量远低于 90% 阈值,下次检测将在下一个周期自动进行。"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":609,"output":83,"cacheRead":37845,"cacheWrite":0,"totalTokens":38537,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774270746917}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"86e5b551-4ea1-43c4-8019-dd63146553ed","timestamp":"2026-03-23T18:28:49.832Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"e2c7d4b1","parentId":null,"timestamp":"2026-03-23T18:28:49.833Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"2c60f93f","parentId":"e2c7d4b1","timestamp":"2026-03-23T18:28:49.833Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774290529837,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"6a78731d","parentId":"2c60f93f","timestamp":"2026-03-23T18:28:49.837Z"}
{"type":"message","id":"a31588f7","parentId":"6a78731d","timestamp":"2026-03-23T18:28:49.860Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 02:28 (Asia/Shanghai) / 2026-03-23 18:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774290529856}}
{"type":"message","id":"cfc6d500","parentId":"a31588f7","timestamp":"2026-03-23T18:28:56.416Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"This is a cron job asking me to run a context memory guard script. Let me execute it.","thinkingSignature":"0c72cdd4c2c68f41057cf6129a898ebc52b0f261a6e6750898deb259e87a9402"},{"type":"toolCall","id":"call_function_0xy0ggzk6hcr_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":67,"cacheRead":0,"cacheWrite":38006,"totalTokens":38230,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774290529859}}
{"type":"message","id":"d68d6dab","parentId":"cfc6d500","timestamp":"2026-03-23T18:29:05.836Z","message":{"role":"toolResult","toolCallId":"call_function_0xy0ggzk6hcr_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-24 02:28:56] ========== 上下文使用量检测 ==========\n[2026-03-24 02:29:05] 📊 当前最高上下文使用量: 39%\n[2026-03-24 02:29:05] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 02:29:05] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9388,"aggregated":"[2026-03-24 02:28:56] ========== 上下文使用量检测 ==========\n[2026-03-24 02:29:05] 📊 当前最高上下文使用量: 39%\n[2026-03-24 02:29:05] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 02:29:05] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774290545823}}
{"type":"message","id":"78712dca","parentId":"d68d6dab","timestamp":"2026-03-23T18:29:09.126Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 39%, well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"69442e7411e6930fba55256ba427b594202b665d5c2b991956d02a4ef9fc1951"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**39%**\n- 状态:**✅ 正常**(无需保存记忆)\n\n上下文使用量远低于 90% 阈值,记忆保存未触发。🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":494,"output":81,"cacheRead":37845,"cacheWrite":0,"totalTokens":38420,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774290545837}}

View File

@ -1,2 +1,3 @@
{"type":"session","version":3,"id":"8c51adb5-1c67-486a-be11-6c7900157045","timestamp":"2026-03-21T00:04:20.503Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"message","id":"e0092f34","parentId":null,"timestamp":"2026-03-21T00:04:20.504Z","message":{"role":"assistant","content":[{"type":"text","text":"📊 每日收款统计 (2026-03-20 23:00 ~ 2026-03-21 08:00)\n\n✅ 好消息!在查询时间范围内,**未发现任何收款失败记录**\n\n📈 成功收款统计:\n- 支付宝12笔共217.80元\n- 微信34笔共560.40元\n- 合计46笔共778.20元\n\n🔍 1分钟内相同金额检查未发现失败后重试成功的情况\n\n📄 详细统计文档https://www.feishu.cn/docx/ZnfsdxH8Lo76Mpx44gEcowwMnrh"}],"api":"openai-responses","provider":"openclaw","model":"delivery-mirror","usage":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"totalTokens":0,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774051460504}}
{"type":"message","id":"5890574a","parentId":"e0092f34","timestamp":"2026-03-23T07:45:32.513Z","message":{"role":"assistant","content":[{"type":"text","text":"收款失败统计 (2026-03-22 23:00 ~ 2026-03-23 08:00):无失败记录 ✅"}],"api":"openai-responses","provider":"openclaw","model":"delivery-mirror","usage":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"totalTokens":0,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774251932513}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"8d017f0b-a156-4f94-89fc-fe9d1ca8f796","timestamp":"2026-03-23T15:58:49.799Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"f1a4fa55","parentId":null,"timestamp":"2026-03-23T15:58:49.800Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"ad0a1723","parentId":"f1a4fa55","timestamp":"2026-03-23T15:58:49.800Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774281529804,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"ed56c91a","parentId":"ad0a1723","timestamp":"2026-03-23T15:58:49.804Z"}
{"type":"message","id":"2c65747e","parentId":"ed56c91a","timestamp":"2026-03-23T15:58:49.826Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 23:58 (Asia/Shanghai) / 2026-03-23 15:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774281529822}}
{"type":"message","id":"9eded580","parentId":"2c65747e","timestamp":"2026-03-23T15:58:53.498Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_p6awcnz09lxc_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":48,"cacheRead":0,"cacheWrite":38006,"totalTokens":38211,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774281529826}}
{"type":"message","id":"80f1bb0a","parentId":"9eded580","timestamp":"2026-03-23T15:59:03.541Z","message":{"role":"toolResult","toolCallId":"call_function_p6awcnz09lxc_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session faint-daisy, pid 3623961). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"faint-daisy","pid":3623961,"startedAt":1774281533515,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774281543527}}
{"type":"message","id":"20e2678e","parentId":"80f1bb0a","timestamp":"2026-03-23T15:59:05.950Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for it to complete.","thinkingSignature":"8c41d6dcfd223916268b7c85ea3e44d0e3b4129e2ee61ddf19236f249a6227d6"},{"type":"toolCall","id":"call_function_6pfmn8puoxk0_1","name":"process","arguments":{"action":"poll","sessionId":"faint-daisy","timeout":30000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":414,"output":59,"cacheRead":37845,"cacheWrite":0,"totalTokens":38318,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774281543542}}
{"type":"message","id":"caf787a8","parentId":"20e2678e","timestamp":"2026-03-23T15:59:11.883Z","message":{"role":"toolResult","toolCallId":"call_function_6pfmn8puoxk0_1","toolName":"process","content":[{"type":"text","text":"[2026-03-23 23:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 23:59:11] 📊 当前最高上下文使用量: 74%\n[2026-03-23 23:59:11] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 23:59:11] ========== 检测完成 ==========\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"faint-daisy","exitCode":0,"aggregated":"[2026-03-23 23:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 23:59:11] 📊 当前最高上下文使用量: 74%\n[2026-03-23 23:59:11] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 23:59:11] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774281551870}}
{"type":"message","id":"605e56bc","parentId":"caf787a8","timestamp":"2026-03-23T15:59:14.540Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 74%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"56e63936f58b8205c1ca63c4b4f53c5d9da1b180922453b3f7874bc4ccacdc52"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**74%**\n- 状态:✅ 正常(未达到 90% 阈值,无需自动保存记忆)"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":589,"output":71,"cacheRead":37845,"cacheWrite":0,"totalTokens":38505,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774281551884}}

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"963cae4d-42a3-46f2-b58e-03be02002b6f","timestamp":"2026-03-22T13:22:25.514Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"294ec5ae","parentId":null,"timestamp":"2026-03-22T13:22:25.524Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"c1853f81","parentId":"294ec5ae","timestamp":"2026-03-22T13:22:25.524Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774185745535,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"7ba5414d","parentId":"c1853f81","timestamp":"2026-03-22T13:22:25.535Z"}
{"type":"message","id":"98990664","parentId":"7ba5414d","timestamp":"2026-03-22T13:22:25.572Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Sunday, March 22nd, 2026 — 21:22 (Asia/Shanghai) / 2026-03-22 13:22 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774185745559}}
{"type":"message","id":"9a375d11","parentId":"98990664","timestamp":"2026-03-22T13:22:29.818Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_wobhl9t9oswl_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":30}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":58,"cacheRead":0,"cacheWrite":38006,"totalTokens":38221,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774185745568}}
{"type":"message","id":"d03e6e8d","parentId":"9a375d11","timestamp":"2026-03-22T13:22:39.943Z","message":{"role":"toolResult","toolCallId":"call_function_wobhl9t9oswl_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session lucky-willow, pid 3941780). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"lucky-willow","pid":3941780,"startedAt":1774185749907,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774185759924}}
{"type":"message","id":"30d5365b","parentId":"d03e6e8d","timestamp":"2026-03-22T13:22:42.766Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for it to complete.","thinkingSignature":"8c41d6dcfd223916268b7c85ea3e44d0e3b4129e2ee61ddf19236f249a6227d6"},{"type":"toolCall","id":"call_function_a248y8rqmbdh_1","name":"process","arguments":{"action":"poll","sessionId":"lucky-willow","timeout":25000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":424,"output":59,"cacheRead":37845,"cacheWrite":0,"totalTokens":38328,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774185759946}}
{"type":"message","id":"eb3c24c9","parentId":"30d5365b","timestamp":"2026-03-22T13:22:44.316Z","message":{"role":"toolResult","toolCallId":"call_function_a248y8rqmbdh_1","toolName":"process","content":[{"type":"text","text":"[2026-03-22 21:22:29] ========== 上下文使用量检测 ==========\n[2026-03-22 21:22:44] 📊 当前最高上下文使用量: 59%\n[2026-03-22 21:22:44] ✅ 上下文使用量正常 (59%),无需保存\n[2026-03-22 21:22:44] ========== 检测完成 ==========\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"lucky-willow","exitCode":0,"aggregated":"[2026-03-22 21:22:29] ========== 上下文使用量检测 ==========\n[2026-03-22 21:22:44] 📊 当前最高上下文使用量: 59%\n[2026-03-22 21:22:44] ✅ 上下文使用量正常 (59%),无需保存\n[2026-03-22 21:22:44] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774185764302}}
{"type":"message","id":"e8f63bab","parentId":"eb3c24c9","timestamp":"2026-03-22T13:22:47.615Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 59%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"6f03a028bd98e90a8e3a1bbc60d389a2605110f066432db3a43226e8a484278e"},{"type":"text","text":"**📊 上下文使用量检测结果**\n\n- ✅ 当前上下文使用量:**59%**\n- ✅ 状态正常,无需保存记忆\n\n上下文使用量在安全范围内下次检测将按常规间隔进行。"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":599,"output":76,"cacheRead":37845,"cacheWrite":0,"totalTokens":38520,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774185764317}}

View File

@ -0,0 +1,10 @@
{"type":"session","version":3,"id":"99dcb193-0c1e-451a-8d74-22cdc90c8074","timestamp":"2026-03-23T14:58:49.784Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"4a24043c","parentId":null,"timestamp":"2026-03-23T14:58:49.785Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"bf4dad7e","parentId":"4a24043c","timestamp":"2026-03-23T14:58:49.785Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774277929789,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"b68cd782","parentId":"bf4dad7e","timestamp":"2026-03-23T14:58:49.789Z"}
{"type":"message","id":"122e21b1","parentId":"b68cd782","timestamp":"2026-03-23T14:58:49.810Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 22:58 (Asia/Shanghai) / 2026-03-23 14:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774277929807}}
{"type":"message","id":"55832504","parentId":"122e21b1","timestamp":"2026-03-23T14:58:56.309Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script to check context usage and auto-save memory if needed.","thinkingSignature":"7399c4250d4b7b2ecdbaefe02403656b72e20579c637b763662ab0b9b57f212f"},{"type":"toolCall","id":"call_function_0osidc6lt30e_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":70,"cacheRead":0,"cacheWrite":38006,"totalTokens":38233,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774277929810}}
{"type":"message","id":"25588487","parentId":"55832504","timestamp":"2026-03-23T14:59:06.362Z","message":{"role":"toolResult","toolCallId":"call_function_0osidc6lt30e_1","toolName":"exec","content":[{"type":"text","text":"Command still running (session tidy-sable, pid 3502403). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"tidy-sable","pid":3502403,"startedAt":1774277936329,"cwd":"/home/huan/.openclaw/workspace","tail":""},"isError":false,"timestamp":1774277946354}}
{"type":"message","id":"2f6581cb","parentId":"25588487","timestamp":"2026-03-23T14:59:09.449Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me wait for the script to finish.","thinkingSignature":"2e3478a54cfeed848ee1e6558e2e756316c2517f483b38f3e7ca01fc08779264"},{"type":"toolCall","id":"call_function_ccnpn3y7ytum_1","name":"process","arguments":{"action":"poll","sessionId":"tidy-sable","timeout":60000}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":436,"output":60,"cacheRead":37845,"cacheWrite":0,"totalTokens":38341,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774277946363}}
{"type":"message","id":"0450276d","parentId":"2f6581cb","timestamp":"2026-03-23T14:59:09.480Z","message":{"role":"toolResult","toolCallId":"call_function_ccnpn3y7ytum_1","toolName":"process","content":[{"type":"text","text":"[2026-03-23 22:58:56] ========== 上下文使用量检测 ==========\n[2026-03-23 22:59:07] 📊 当前最高上下文使用量: 74%\n[2026-03-23 22:59:07] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 22:59:07] ========== 检测完成 ==========\n\n\n\nProcess exited with code 0."}],"details":{"status":"completed","sessionId":"tidy-sable","exitCode":0,"aggregated":"[2026-03-23 22:58:56] ========== 上下文使用量检测 ==========\n[2026-03-23 22:59:07] 📊 当前最高上下文使用量: 74%\n[2026-03-23 22:59:07] ✅ 上下文使用量正常 (74%),无需保存\n[2026-03-23 22:59:07] ========== 检测完成 ==========\n\n","name":"python3 ~/.openclaw/workspace/...ontext_memory_guard.py"},"isError":false,"timestamp":1774277949466}}
{"type":"message","id":"37d5c245","parentId":"0450276d","timestamp":"2026-03-23T14:59:13.031Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 74%, which is below the 90% threshold, so no memory save was triggered. I'll report this to the user.","thinkingSignature":"8d2489a9d07c3715463634220b41b92183f2157075c5e50686d39563ac0facab"},{"type":"text","text":"📊 **上下文使用量检测结果**\n\n- 当前最高上下文使用量:**74%**\n- 状态:✅ 正常(未达到 90% 阈值)\n- 无需自动保存记忆\n\n检测完成一切正常🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":613,"output":89,"cacheRead":37845,"cacheWrite":0,"totalTokens":38547,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774277949480}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"aa205ba6-5d1e-41d7-85e9-72b8bf4c9048","timestamp":"2026-03-23T18:58:49.826Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"f16c0f73","parentId":null,"timestamp":"2026-03-23T18:58:49.827Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"8571fb46","parentId":"f16c0f73","timestamp":"2026-03-23T18:58:49.827Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774292329831,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"6a57fdf2","parentId":"8571fb46","timestamp":"2026-03-23T18:58:49.831Z"}
{"type":"message","id":"08520607","parentId":"6a57fdf2","timestamp":"2026-03-23T18:58:49.855Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 02:58 (Asia/Shanghai) / 2026-03-23 18:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774292329850}}
{"type":"message","id":"403b01a3","parentId":"08520607","timestamp":"2026-03-23T18:58:53.658Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"This is a cron job asking me to run a context memory guard script. Let me execute it.","thinkingSignature":"0c72cdd4c2c68f41057cf6129a898ebc52b0f261a6e6750898deb259e87a9402"},{"type":"toolCall","id":"call_function_rx0e9bm18e8n_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":67,"cacheRead":0,"cacheWrite":38006,"totalTokens":38230,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774292329854}}
{"type":"message","id":"9a1eefab","parentId":"403b01a3","timestamp":"2026-03-23T18:59:03.035Z","message":{"role":"toolResult","toolCallId":"call_function_rx0e9bm18e8n_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-24 02:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 02:59:03] 📊 当前最高上下文使用量: 39%\n[2026-03-24 02:59:03] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 02:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9346,"aggregated":"[2026-03-24 02:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 02:59:03] 📊 当前最高上下文使用量: 39%\n[2026-03-24 02:59:03] ✅ 上下文使用量正常 (39%),无需保存\n[2026-03-24 02:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774292343022}}
{"type":"message","id":"f6e2209e","parentId":"9a1eefab","timestamp":"2026-03-23T18:59:05.379Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 39%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"c5c60a25b57fefe26fc262896e86eb4bfd4144c15015549f89a1e9bd90c8283c"},{"type":"text","text":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**39%**\n- 🟢 状态正常,无需保存记忆\n\n检测通过继续正常运行。"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":494,"output":68,"cacheRead":37845,"cacheWrite":0,"totalTokens":38407,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774292343036}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"b8b7b6b9-1768-4169-9336-86afaa64095e","timestamp":"2026-03-23T10:28:49.713Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"831b9661","parentId":null,"timestamp":"2026-03-23T10:28:49.714Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"1ca4cb65","parentId":"831b9661","timestamp":"2026-03-23T10:28:49.714Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774261729717,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"6cc4ae3b","parentId":"1ca4cb65","timestamp":"2026-03-23T10:28:49.717Z"}
{"type":"message","id":"6ed617bb","parentId":"6cc4ae3b","timestamp":"2026-03-23T10:28:49.739Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 18:28 (Asia/Shanghai) / 2026-03-23 10:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774261729735}}
{"type":"message","id":"eaa0ca9d","parentId":"6ed617bb","timestamp":"2026-03-23T10:28:53.517Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script to check the context usage.","thinkingSignature":"f5c72c4830df32d7dcaa030d688a8d36bf4d506d7b0c20703d6487589006b63e"},{"type":"toolCall","id":"call_function_7um0kysg6l8h_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":61,"cacheRead":0,"cacheWrite":38006,"totalTokens":38224,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774261729739}}
{"type":"message","id":"9b0a6f9f","parentId":"eaa0ca9d","timestamp":"2026-03-23T10:29:03.043Z","message":{"role":"toolResult","toolCallId":"call_function_7um0kysg6l8h_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 18:28:53] ========== 上下文使用量检测 ==========\n[2026-03-23 18:29:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 18:29:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 18:29:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9494,"aggregated":"[2026-03-23 18:28:53] ========== 上下文使用量检测 ==========\n[2026-03-23 18:29:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 18:29:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 18:29:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774261743029}}
{"type":"message","id":"28f7891c","parentId":"9b0a6f9f","timestamp":"2026-03-23T10:29:05.958Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 38%, which is well below the 90% threshold. No memory saving was triggered.","thinkingSignature":"feabead89645f88f43ce3b76fb6e75bc0d2164ffa8d60e9092917dbe201bf53c"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常,无需保存记忆\n\n阈值 90%,当前使用量远低于阈值,一切正常 🦐)"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":488,"output":80,"cacheRead":37845,"cacheWrite":0,"totalTokens":38413,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774261743043}}

View File

@ -0,0 +1,13 @@
{"type":"session","version":3,"id":"c0493bf3-6d78-4b30-8afb-1ebee307e41b","timestamp":"2026-03-23T07:45:10.014Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"8ff42927","parentId":null,"timestamp":"2026-03-23T07:45:10.015Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"f69d4bcf","parentId":"8ff42927","timestamp":"2026-03-23T07:45:10.015Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774251910020,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"346d9724","parentId":"f69d4bcf","timestamp":"2026-03-23T07:45:10.020Z"}
{"type":"message","id":"62dca330","parentId":"346d9724","timestamp":"2026-03-23T07:45:10.044Z","message":{"role":"user","content":[{"type":"text","text":"[cron:2073cd46-e64a-4304-a99e-a8b8a703fa54 每日收款失败统计] 执行以下任务:\n\n1. 搜索飞书群聊「益选通知群」(chat_id: oc_d5ef8b1abf66842c28ef57e1658dc096) 中关于「收款失败」的消息\n2. 时间范围昨天夜里23:00到今天早上8:00注意当前时间是早上8点所以查询范围是「昨天23:00到今天08:00」\n3. 读取现有Excel文件https://my.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb\n4. 判断逻辑:\n - 如果没有收款失败数据 → 在群里只发一条消息:「收款失败统计 (昨天~今天):无失败记录 ✅」\n - 如果有失败数据 → 追加到Excel表Sheet1从第8行开始追加然后在群里发消息「收款失败统计 (昨天~今天)共X条失败记录已更新X条到Excel」\n5. Excel追加格式序号自动生成现有最大序号+1、时间、支付方式、金额、备注留空或填入备注\n6. 注意:不需要统计成功的数据,只处理失败记录\nCurrent time: Monday, March 23rd, 2026 — 15:45 (Asia/Shanghai) / 2026-03-23 07:45 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774251910040}}
{"type":"message","id":"c5cbcf9f","parentId":"62dca330","timestamp":"2026-03-23T07:45:18.638Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me break down this task:\n\n1. Search Feishu group \"益选通知群\" (chat_id: oc_d5ef8b1abf66842c28ef57e1658dc096) for messages about \"收款失败\"\n2. Time range: yesterday 23:00 to today 08:00 (since current time is 8:45 AM, this means 2026-03-22 23:00:00 to 2026-03-23 08:00:00)\n3. Read existing Excel file: https://my.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb\n\n\n4. I'll check for any payment failure records—if there are none, I'll post a confirmation message in the group, but if failures exist, I'll append them to Sheet1 starting at row 8 and notify the group with the count of records added\n5. The append format will include auto-incrementing sequence numbers, timestamp, payment method, amount, and a notes column\n6. I need to filter specifically for failure records, not successful payments\n\nLet me start by searching for these messages in the group.","thinkingSignature":"794060d1f586f3360cc83fa908399f6d25732c54333bf3e3ea0e5e48333d505e"},{"type":"text","text":"好的,皮皮虾开始干活了!先搜索收款失败消息 + 读取 Excel 双线并行!\n"},{"type":"toolCall","id":"call_function_4ceofdcw26el_1","name":"feishu_im_user_search_messages","arguments":{"query":"收款失败","chat_id":"oc_d5ef8b1abf66842c28ef57e1658dc096","start_time":"2026-03-22T23:00:00+08:00","end_time":"2026-03-23T08:00:00+08:00","page_size":50}},{"type":"toolCall","id":"call_function_4ceofdcw26el_2","name":"feishu_sheet","arguments":{"action":"info","url":"https://my.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":159,"output":453,"cacheRead":0,"cacheWrite":38141,"totalTokens":38753,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774251910043}}
{"type":"message","id":"b421f700","parentId":"c5cbcf9f","timestamp":"2026-03-23T07:45:20.584Z","message":{"role":"toolResult","toolCallId":"call_function_4ceofdcw26el_1","toolName":"feishu_im_user_search_messages","content":[{"type":"text","text":"{\n \"messages\": [],\n \"has_more\": false\n}"}],"details":{"messages":[],"has_more":false},"isError":false,"timestamp":1774251920571}}
{"type":"message","id":"298d4e0f","parentId":"b421f700","timestamp":"2026-03-23T07:45:20.598Z","message":{"role":"toolResult","toolCallId":"call_function_4ceofdcw26el_2","toolName":"feishu_sheet","content":[{"type":"text","text":"{\n \"title\": \"收款失败统计\",\n \"spreadsheet_token\": \"MCn9szPGmh1N2it2W0pc6vsSnSb\",\n \"url\": \"https://www.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb\",\n \"sheets\": [\n {\n \"sheet_id\": \"5f931d\",\n \"title\": \"Sheet1\",\n \"index\": 0,\n \"row_count\": 200,\n \"column_count\": 20,\n \"frozen_row_count\": 0,\n \"frozen_column_count\": 0\n }\n ]\n}"}],"details":{"title":"收款失败统计","spreadsheet_token":"MCn9szPGmh1N2it2W0pc6vsSnSb","url":"https://www.feishu.cn/sheets/MCn9szPGmh1N2it2W0pc6vsSnSb","sheets":[{"sheet_id":"5f931d","title":"Sheet1","index":0,"row_count":200,"column_count":20,"frozen_row_count":0,"frozen_column_count":0}]},"isError":false,"timestamp":1774251920584}}
{"type":"message","id":"d839e36b","parentId":"298d4e0f","timestamp":"2026-03-23T07:45:24.736Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"No messages found about \"收款失败\" in the specified time range. And I have the Excel file info.\n\nSince there are no payment failure messages, I should just send one message in the group: \"收款失败统计 (昨天~今天):无失败记录 ✅\"\n\nBut wait - I should send this message to the Feishu group. Let me send the message.\n\nActually, let me also quickly check the Excel to understand the current state (for context).","thinkingSignature":"3a6f5c39d103e5509b3338cd9ef09ecd04191489472cc0e13bcec793d2ae18bc"},{"type":"text","text":"搜索结果为空,没有收款失败记录!先读一下 Excel 确认现有数据状态:\n"},{"type":"toolCall","id":"call_function_g6l31ktrwfmh_1","name":"feishu_sheet","arguments":{"action":"read","spreadsheet_token":"MCn9szPGmh1N2it2W0pc6vsSnSb","range":"5f931d!A1:H10"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":1146,"output":188,"cacheRead":37779,"cacheWrite":0,"totalTokens":39113,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774251920598}}
{"type":"message","id":"d148a816","parentId":"d839e36b","timestamp":"2026-03-23T07:45:25.175Z","message":{"role":"toolResult","toolCallId":"call_function_g6l31ktrwfmh_1","toolName":"feishu_sheet","content":[{"type":"text","text":"{\n \"range\": \"5f931d!A1:H10\",\n \"values\": [\n [\n \"序号\",\n \"时间\",\n \"支付方式\",\n \"金额(元)\",\n \"备注\",\n null,\n null,\n null\n ],\n [\n \"1\",\n \"2026-03-19 13:32:06\",\n \"支付宝\",\n \"3.50\",\n \"-无误\",\n null,\n null,\n null\n ],\n [\n \"2\",\n \"2026-03-18 03:26:38\",\n \"支付宝\",\n \"92.00\",\n \"-无误\",\n null,\n null,\n null\n ],\n [\n \"3\",\n \"2026-03-18 03:22:35\",\n \"支付宝\",\n \"69.00\",\n \"-无误\",\n null,\n null,\n null\n ],\n [\n \"4\",\n \"2026-03-18 00:39:19\",\n \"支付宝\",\n \"22.00\",\n \"-无误\",\n null,\n null,\n null\n ],\n [\n \"5\",\n \"2026-03-18 00:01:21\",\n \"支付宝\",\n \"8.00\",\n \"-无误\",\n null,\n null,\n null\n ],\n [\n \"6\",\n \"2026-03-17 15:54:43\",\n \"微信\",\n \"3.50\",\n \"-无误\",\n null,\n null,\n null\n ],\n [\n \"7\",\n \"2026-03-22 04:18:06\",\n \"微信\",\n \"23.00\",\n \"\",\n null,\n null,\n null\n ],\n [\n \"8\",\n \"2026-03-22 04:18:23\",\n \"微信\",\n \"23.00\",\n \"\",\n null,\n null,\n null\n ],\n [\n null,\n null,\n null,\n null,\n null,\n null,\n null,\n null\n ]\n ]\n}"}],"details":{"range":"5f931d!A1:H10","values":[["序号","时间","支付方式","金额(元)","备注",null,null,null],["1","2026-03-19 13:32:06","支付宝","3.50","-无误",null,null,null],["2","2026-03-18 03:26:38","支付宝","92.00","-无误",null,null,null],["3","2026-03-18 03:22:35","支付宝","69.00","-无误",null,null,null],["4","2026-03-18 00:39:19","支付宝","22.00","-无误",null,null,null],["5","2026-03-18 00:01:21","支付宝","8.00","-无误",null,null,null],["6","2026-03-17 15:54:43","微信","3.50","-无误",null,null,null],["7","2026-03-22 04:18:06","微信","23.00","",null,null,null],["8","2026-03-22 04:18:23","微信","23.00","",null,null,null],[null,null,null,null,null,null,null,null]]},"isError":false,"timestamp":1774251925162}}
{"type":"message","id":"17b4bc11","parentId":"d148a816","timestamp":"2026-03-23T07:45:31.765Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The search returned no messages about \"收款失败\" in the time range. The Excel file has data up to row 8 (序号 7 and 8 for 2026-03-22). \n\nSince there are no \"收款失败\" messages found, I should just send one message to the group: \"收款失败统计 (昨天~今天):无失败记录 ✅\"\n\nWait, but I should double-check - maybe the messages use different wording. Let me search without the strict time filter or with different keywords... Actually, the task says to search for messages about \"收款失败\" and the search result is empty. \n\nWith no matching messages and a clear directive on what to do when that happens, I'll send the no-failures message to the group now.","thinkingSignature":"9f1dfb5c8af46713a700ae0231f1370425086f4e985318cc28ff277e6c7866e2"},{"type":"text","text":"好的确认了搜索「收款失败」消息结果为空Excel 现有 8 条记录(序号 1-8。没有失败数据直接在群里发消息\n"},{"type":"toolCall","id":"call_function_rut53j3flz3d_1","name":"message","arguments":{"action":"send","channel":"feishu","target":"oc_d5ef8b1abf66842c28ef57e1658dc096","message":"收款失败统计 (2026-03-22 23:00 ~ 2026-03-23 08:00):无失败记录 ✅"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":1800,"output":299,"cacheRead":37779,"cacheWrite":0,"totalTokens":39878,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774251925176}}
{"type":"message","id":"6b3f75c7","parentId":"17b4bc11","timestamp":"2026-03-23T07:45:32.534Z","message":{"role":"toolResult","toolCallId":"call_function_rut53j3flz3d_1","toolName":"message","content":[{"type":"text","text":"{\n \"ok\": true,\n \"messageId\": \"om_x100b533ab709211cb2c38ec3f54c9e8\",\n \"chatId\": \"oc_d5ef8b1abf66842c28ef57e1658dc096\"\n}"}],"details":{"ok":true,"messageId":"om_x100b533ab709211cb2c38ec3f54c9e8","chatId":"oc_d5ef8b1abf66842c28ef57e1658dc096"},"isError":false,"timestamp":1774251932514}}
{"type":"message","id":"d3afb57c","parentId":"6b3f75c7","timestamp":"2026-03-23T07:45:36.377Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Done. The task is complete. No payment failure records found in the specified time range, and I've sent the message to the Feishu group.","thinkingSignature":"2852e556fab8bf6578c64fb207289ecc307a4f93c278ce52a4c282b503b7e773"},{"type":"text","text":"任务完成!🦐 汇报如下:\n\n**搜索结果**在「益选通知群」中2026-03-22 23:00 ~ 2026-03-23 08:00 时间段内,**没有找到**任何「收款失败」相关消息。\n\n**已执行操作**\n- ✅ 已在群聊发送消息:「收款失败统计 (2026-03-22 23:00 ~ 2026-03-23 08:00):无失败记录 ✅」\n- ✅ Excel 无需更新(现有 8 条记录,序号最大到 8"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":2175,"output":160,"cacheRead":37779,"cacheWrite":0,"totalTokens":40114,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774251932535}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"c5272c83-01be-486e-8a6c-11998b19db2e","timestamp":"2026-03-23T19:58:49.871Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"31b0d73a","parentId":null,"timestamp":"2026-03-23T19:58:49.872Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"81f74c30","parentId":"31b0d73a","timestamp":"2026-03-23T19:58:49.872Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774295929876,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"14677b4f","parentId":"81f74c30","timestamp":"2026-03-23T19:58:49.876Z"}
{"type":"message","id":"ebe5da47","parentId":"14677b4f","timestamp":"2026-03-23T19:58:49.902Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Tuesday, March 24th, 2026 — 03:58 (Asia/Shanghai) / 2026-03-23 19:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774295929898}}
{"type":"message","id":"2eae8746","parentId":"ebe5da47","timestamp":"2026-03-23T19:58:53.619Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to run a context memory guard script to check context usage. Let me execute it.","thinkingSignature":"4bc1b58f78dd389ef95b87276c1cd459400b1a1bfd0e6ffa68f5a085bf4de7b6"},{"type":"toolCall","id":"call_function_k4pmntdzczt5_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":68,"cacheRead":0,"cacheWrite":38006,"totalTokens":38231,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774295929902}}
{"type":"message","id":"b1692bec","parentId":"2eae8746","timestamp":"2026-03-23T19:59:03.164Z","message":{"role":"toolResult","toolCallId":"call_function_k4pmntdzczt5_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-24 03:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 03:59:03] 📊 当前最高上下文使用量: 40%\n[2026-03-24 03:59:03] ✅ 上下文使用量正常 (40%),无需保存\n[2026-03-24 03:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9512,"aggregated":"[2026-03-24 03:58:53] ========== 上下文使用量检测 ==========\n[2026-03-24 03:59:03] 📊 当前最高上下文使用量: 40%\n[2026-03-24 03:59:03] ✅ 上下文使用量正常 (40%),无需保存\n[2026-03-24 03:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774295943151}}
{"type":"message","id":"3bfd053b","parentId":"b1692bec","timestamp":"2026-03-23T19:59:05.835Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 40%, which is well below the 90% threshold. No memory save was triggered.","thinkingSignature":"fc3c27f4f42f2f2ab17e24ac5dcc00bd0e2c8854fbc270374528f35e60ec228c"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**40%**\n- 状态:✅ 正常,无需保存记忆\n\n阈值 90%,当前使用量远低于阈值,记忆保持不变)"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":495,"output":79,"cacheRead":37845,"cacheWrite":0,"totalTokens":38419,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774295943165}}

View File

@ -1,5 +0,0 @@
{
"pid": 110467,
"createdAt": "2026-03-21T07:29:21.630Z",
"starttime": 795608
}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"d3a813d5-1d9c-46ae-b8c2-6b8468709346","timestamp":"2026-03-23T11:28:49.761Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"7467a7ef","parentId":null,"timestamp":"2026-03-23T11:28:49.762Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"dc8f834d","parentId":"7467a7ef","timestamp":"2026-03-23T11:28:49.762Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774265329766,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"70ce9ecd","parentId":"dc8f834d","timestamp":"2026-03-23T11:28:49.766Z"}
{"type":"message","id":"9bb0d29b","parentId":"70ce9ecd","timestamp":"2026-03-23T11:28:49.792Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 19:28 (Asia/Shanghai) / 2026-03-23 11:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774265329788}}
{"type":"message","id":"6d937790","parentId":"9bb0d29b","timestamp":"2026-03-23T11:28:53.942Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script to check context usage and potentially save memory if needed.","thinkingSignature":"ebb3e945ae7068c819bedf7be3c6a803d839027cdc11b35403b99dbe5b6ea50e"},{"type":"toolCall","id":"call_function_nbbude01itpp_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py"}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":56,"cacheRead":0,"cacheWrite":38006,"totalTokens":38219,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774265329792}}
{"type":"message","id":"65139fb3","parentId":"6d937790","timestamp":"2026-03-23T11:29:03.465Z","message":{"role":"toolResult","toolCallId":"call_function_nbbude01itpp_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 19:28:54] ========== 上下文使用量检测 ==========\n[2026-03-23 19:29:03] 📊 当前最高上下文使用量: 24%\n[2026-03-23 19:29:03] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 19:29:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9491,"aggregated":"[2026-03-23 19:28:54] ========== 上下文使用量检测 ==========\n[2026-03-23 19:29:03] 📊 当前最高上下文使用量: 24%\n[2026-03-23 19:29:03] ✅ 上下文使用量正常 (24%),无需保存\n[2026-03-23 19:29:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774265343452}}
{"type":"message","id":"7fe4d36d","parentId":"65139fb3","timestamp":"2026-03-23T11:29:06.082Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 24%, well below the 90% threshold, so no memory save was triggered. I'll report the results.","thinkingSignature":"92020b2038232486a936c4d004d68fb155a7e08bd2f4d127dbc3f46a38332301"},{"type":"text","text":"\n📊 **上下文使用量检测结果**\n\n- 当前最高上下文使用量:**24%**\n- ✅ 状态正常,无需保存记忆\n\n检测完成一切平稳运行 🎯"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":483,"output":71,"cacheRead":37845,"cacheWrite":0,"totalTokens":38399,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774265343466}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
{
"pid": 2505080,
"createdAt": "2026-03-23T20:00:00.626Z",
"starttime": 24190883
}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"d7db57f8-ac9f-4b88-806e-75e8939fb968","timestamp":"2026-03-23T13:58:49.746Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"dc60e996","parentId":null,"timestamp":"2026-03-23T13:58:49.747Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"c0cac05d","parentId":"dc60e996","timestamp":"2026-03-23T13:58:49.747Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774274329751,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"ec22158d","parentId":"c0cac05d","timestamp":"2026-03-23T13:58:49.751Z"}
{"type":"message","id":"52e5863e","parentId":"ec22158d","timestamp":"2026-03-23T13:58:49.774Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 21:58 (Asia/Shanghai) / 2026-03-23 13:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774274329770}}
{"type":"message","id":"2c86bd43","parentId":"52e5863e","timestamp":"2026-03-23T13:58:53.778Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script to check the context usage.","thinkingSignature":"f5c72c4830df32d7dcaa030d688a8d36bf4d506d7b0c20703d6487589006b63e"},{"type":"toolCall","id":"call_function_es9pfo2se4lg_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":30}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":61,"cacheRead":0,"cacheWrite":38006,"totalTokens":38224,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774274329774}}
{"type":"message","id":"7ff83766","parentId":"2c86bd43","timestamp":"2026-03-23T13:59:03.665Z","message":{"role":"toolResult","toolCallId":"call_function_es9pfo2se4lg_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 21:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 21:59:03] 📊 当前最高上下文使用量: 30%\n[2026-03-23 21:59:03] ✅ 上下文使用量正常 (30%),无需保存\n[2026-03-23 21:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9857,"aggregated":"[2026-03-23 21:58:53] ========== 上下文使用量检测 ==========\n[2026-03-23 21:59:03] 📊 当前最高上下文使用量: 30%\n[2026-03-23 21:59:03] ✅ 上下文使用量正常 (30%),无需保存\n[2026-03-23 21:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774274343652}}
{"type":"message","id":"1387f70d","parentId":"7ff83766","timestamp":"2026-03-23T13:59:06.914Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Context usage is at 30%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"06c0270ee6bdfe8f95fee560e3877bf5d23b48bf337af0541fa60a7f420150db"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**30%**\n- 状态:✅ 正常(无需保存记忆)\n\n使用量远低于 90% 阈值,记忆保存未触发。"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":488,"output":79,"cacheRead":37845,"cacheWrite":0,"totalTokens":38412,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774274343666}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"ed4be391-c44d-4d7f-a6cf-0dedb8c9db56","timestamp":"2026-03-23T09:58:49.713Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"ff75067f","parentId":null,"timestamp":"2026-03-23T09:58:49.714Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"e60a161a","parentId":"ff75067f","timestamp":"2026-03-23T09:58:49.714Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774259929718,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"f1baf75b","parentId":"e60a161a","timestamp":"2026-03-23T09:58:49.718Z"}
{"type":"message","id":"ec0ab44e","parentId":"f1baf75b","timestamp":"2026-03-23T09:58:49.740Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 17:58 (Asia/Shanghai) / 2026-03-23 09:58 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774259929736}}
{"type":"message","id":"315b225a","parentId":"ec0ab44e","timestamp":"2026-03-23T09:58:54.461Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"This is a cron job task asking me to run a context memory guard script. Let me execute it.","thinkingSignature":"8652ca5266d2c6e646c79cd96ff4857775374c425212b67edc28a982e4ed3a2d"},{"type":"toolCall","id":"call_function_h7wpx8rc1ta1_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":30}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":68,"cacheRead":0,"cacheWrite":38006,"totalTokens":38231,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774259929740}}
{"type":"message","id":"abe1c75e","parentId":"315b225a","timestamp":"2026-03-23T09:59:04.001Z","message":{"role":"toolResult","toolCallId":"call_function_h7wpx8rc1ta1_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 17:58:54] ========== 上下文使用量检测 ==========\n[2026-03-23 17:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 17:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 17:59:03] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9505,"aggregated":"[2026-03-23 17:58:54] ========== 上下文使用量检测 ==========\n[2026-03-23 17:59:03] 📊 当前最高上下文使用量: 38%\n[2026-03-23 17:59:03] ✅ 上下文使用量正常 (38%),无需保存\n[2026-03-23 17:59:03] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774259943988}}
{"type":"message","id":"bd866237","parentId":"abe1c75e","timestamp":"2026-03-23T09:59:06.943Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. Current context usage is at 38%, which is well below the 90% threshold, so no memory save was triggered.","thinkingSignature":"0b380c3a01d6d5b342be2b95e95863a3539aecf0cab74619d6a1dcaac7644389"},{"type":"text","text":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常,无需保存记忆\n\n检测时间2026-03-23 17:58"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":495,"output":77,"cacheRead":37845,"cacheWrite":0,"totalTokens":38417,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774259944002}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"ef482444-2345-4002-8aed-e567bafc390b","timestamp":"2026-03-22T12:52:25.570Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"aa414a9a","parentId":null,"timestamp":"2026-03-22T12:52:25.570Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"cd1f8ff6","parentId":"aa414a9a","timestamp":"2026-03-22T12:52:25.571Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774183945574,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"2a5322c0","parentId":"cd1f8ff6","timestamp":"2026-03-22T12:52:25.575Z"}
{"type":"message","id":"5cb2fdb6","parentId":"2a5322c0","timestamp":"2026-03-22T12:52:25.597Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Sunday, March 22nd, 2026 — 20:52 (Asia/Shanghai) / 2026-03-22 12:52 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774183945593}}
{"type":"message","id":"5bc2f0a2","parentId":"5cb2fdb6","timestamp":"2026-03-22T12:52:31.767Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me run the context memory guard script as requested.","thinkingSignature":"686276dceed096393e370bb91524c6ffd13996ea45ed5c95a214ae434730c987"},{"type":"toolCall","id":"call_function_kirxbnqbp3hi_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":58,"cacheRead":0,"cacheWrite":38006,"totalTokens":38221,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774183945597}}
{"type":"message","id":"83c252a9","parentId":"5bc2f0a2","timestamp":"2026-03-22T12:52:41.023Z","message":{"role":"toolResult","toolCallId":"call_function_kirxbnqbp3hi_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-22 20:52:31] ========== 上下文使用量检测 ==========\n[2026-03-22 20:52:40] 📊 当前最高上下文使用量: 78%\n[2026-03-22 20:52:40] ✅ 上下文使用量正常 (78%),无需保存\n[2026-03-22 20:52:40] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9215,"aggregated":"[2026-03-22 20:52:31] ========== 上下文使用量检测 ==========\n[2026-03-22 20:52:40] 📊 当前最高上下文使用量: 78%\n[2026-03-22 20:52:40] ✅ 上下文使用量正常 (78%),无需保存\n[2026-03-22 20:52:40] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774183961008}}
{"type":"message","id":"478b3124","parentId":"83c252a9","timestamp":"2026-03-22T12:52:44.344Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 78%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"7d0745ac964ffde965d40afb5a1103eba502b9678df87ff65b6b44546caf492a"},{"type":"text","text":"📊 **上下文使用量检测结果**\n\n- 当前最高上下文使用量:**78%**\n- 状态:✅ 正常(未达到 90% 阈值)\n- 行动:无须保存记忆\n\n检测完成当前会话上下文充裕可以继续正常使用。🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":485,"output":88,"cacheRead":37845,"cacheWrite":0,"totalTokens":38418,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774183961023}}

View File

@ -0,0 +1,8 @@
{"type":"session","version":3,"id":"fc3717b0-1559-41f8-9324-e4a1c09c2d14","timestamp":"2026-03-23T07:28:49.730Z","cwd":"/home/huan/.openclaw/workspace"}
{"type":"model_change","id":"c0a97266","parentId":null,"timestamp":"2026-03-23T07:28:49.738Z","provider":"minimax-portal","modelId":"MiniMax-M2.7"}
{"type":"thinking_level_change","id":"722fe353","parentId":"c0a97266","timestamp":"2026-03-23T07:28:49.738Z","thinkingLevel":"off"}
{"type":"custom","customType":"model-snapshot","data":{"timestamp":1774250929747,"provider":"minimax-portal","modelApi":"anthropic-messages","modelId":"MiniMax-M2.7"},"id":"87b7a436","parentId":"722fe353","timestamp":"2026-03-23T07:28:49.747Z"}
{"type":"message","id":"5b511a31","parentId":"87b7a436","timestamp":"2026-03-23T07:28:49.775Z","message":{"role":"user","content":[{"type":"text","text":"[cron:5273524a-fe06-486c-9706-005f80b061e5 上下文使用量检测-自动保存记忆] 请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。\nCurrent time: Monday, March 23rd, 2026 — 15:28 (Asia/Shanghai) / 2026-03-23 07:28 UTC\n\nReturn your summary as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself."}],"timestamp":1774250929766}}
{"type":"message","id":"64165a20","parentId":"5b511a31","timestamp":"2026-03-23T07:28:58.022Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"Let me execute the context memory guard script as requested.","thinkingSignature":"d5bd3a83025afb6c3dc53041c1c015a11ed3185bc5a6994fbb27fe26e33a5a89"},{"type":"toolCall","id":"call_function_i9sx72fby801_1","name":"exec","arguments":{"command":"python3 ~/.openclaw/workspace/scripts/context_memory_guard.py","timeout":60}}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":157,"output":58,"cacheRead":0,"cacheWrite":38006,"totalTokens":38221,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"toolUse","timestamp":1774250929772}}
{"type":"message","id":"fdcb69e1","parentId":"64165a20","timestamp":"2026-03-23T07:29:07.747Z","message":{"role":"toolResult","toolCallId":"call_function_i9sx72fby801_1","toolName":"exec","content":[{"type":"text","text":"[2026-03-23 15:28:58] ========== 上下文使用量检测 ==========\n[2026-03-23 15:29:07] 📊 当前最高上下文使用量: 59%\n[2026-03-23 15:29:07] ✅ 上下文使用量正常 (59%),无需保存\n[2026-03-23 15:29:07] ========== 检测完成 =========="}],"details":{"status":"completed","exitCode":0,"durationMs":9589,"aggregated":"[2026-03-23 15:28:58] ========== 上下文使用量检测 ==========\n[2026-03-23 15:29:07] 📊 当前最高上下文使用量: 59%\n[2026-03-23 15:29:07] ✅ 上下文使用量正常 (59%),无需保存\n[2026-03-23 15:29:07] ========== 检测完成 ==========","cwd":"/home/huan/.openclaw/workspace"},"isError":false,"timestamp":1774250947733}}
{"type":"message","id":"5f3f752e","parentId":"fdcb69e1","timestamp":"2026-03-23T07:29:16.952Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"The script ran successfully. The context usage is at 59%, which is below the 90% threshold, so no memory save was triggered.","thinkingSignature":"8c09f882e470bd30ac02da3b35b55f1999a1dfc5bf9ec77b88932b02f5f76607"},{"type":"text","text":"**📊 上下文使用量检测报告**\n\n- 当前最高上下文使用量:**59%**\n- 状态:✅ 正常(低于 90% 阈值)\n- 自动保存记忆:未触发\n\n一切正常无需额外操作。🦐"}],"api":"anthropic-messages","provider":"minimax-portal","model":"MiniMax-M2.7","usage":{"input":485,"output":84,"cacheRead":37845,"cacheWrite":0,"totalTokens":38414,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"stopReason":"stop","timestamp":1774250947747}}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,257 @@
_openclaw_completion() {
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Simple top-level completion for now
opts="completion setup onboard configure config backup doctor dashboard reset uninstall message memory agent agents status health sessions browser acp gateway daemon logs system models approvals nodes devices node sandbox tui cron dns docs hooks webhooks qr clawbot feishu-diagnose openclaw-weixin pairing plugins channels directory security secrets skills update -V, --dev --profile --log-level --no-color"
case "${prev}" in
completion)
opts=" -s, -i, --write-state -y,"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
setup)
opts=" --workspace --wizard --non-interactive --mode --remote-url --remote-token"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
onboard)
opts=" --workspace --reset --reset-scope --non-interactive --accept-risk --flow --mode --auth-choice --token-provider --token --token-profile-id --token-expires-in --secret-input-mode --cloudflare-ai-gateway-account-id --cloudflare-ai-gateway-gateway-id --anthropic-api-key --openai-api-key --mistral-api-key --openrouter-api-key --kilocode-api-key --ai-gateway-api-key --cloudflare-ai-gateway-api-key --moonshot-api-key --kimi-code-api-key --gemini-api-key --zai-api-key --xiaomi-api-key --minimax-api-key --synthetic-api-key --venice-api-key --together-api-key --huggingface-api-key --opencode-zen-api-key --opencode-go-api-key --xai-api-key --litellm-api-key --qianfan-api-key --modelstudio-api-key-cn --modelstudio-api-key --volcengine-api-key --byteplus-api-key --custom-base-url --custom-api-key --custom-model-id --custom-provider-id --custom-compatibility --gateway-port --gateway-bind --gateway-auth --gateway-token --gateway-token-ref-env --gateway-password --remote-url --remote-token --tailscale --tailscale-reset-on-exit --install-daemon --no-install-daemon --skip-daemon --daemon-runtime --skip-channels --skip-skills --skip-search --skip-health --skip-ui --node-manager --json"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
configure)
opts=" --section"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
config)
opts="get set unset file validate --section"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
backup)
opts="create verify "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
doctor)
opts=" --no-workspace-suggestions --yes --repair --fix --force --non-interactive --generate-gateway-token --deep"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
dashboard)
opts=" --no-open"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
reset)
opts=" --scope --yes --non-interactive --dry-run"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
uninstall)
opts=" --service --state --workspace --app --all --yes --non-interactive --dry-run"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
message)
opts="send broadcast poll react reactions read edit delete pin unpin pins permissions search thread emoji sticker role channel member voice event timeout kick ban "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
memory)
opts="status index search "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
agent)
opts=" -m, -t, --session-id --agent --thinking --verbose --channel --reply-to --reply-channel --reply-account --local --deliver --json --timeout"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
agents)
opts="list bindings bind unbind add set-identity delete "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
status)
opts=" --json --all --usage --deep --timeout --verbose --debug"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
health)
opts=" --json --timeout --verbose --debug"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
sessions)
opts="cleanup --json --verbose --store --agent --all-agents --active"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
browser)
opts="status start stop reset-profile tabs tab open focus close profiles create-profile delete-profile extension screenshot snapshot navigate resize click type press hover scrollintoview drag select upload waitfordownload download dialog fill wait evaluate console pdf responsebody highlight errors requests trace cookies storage set --browser-profile --json --url --token --timeout --expect-final"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
acp)
opts="client --url --token --token-file --password --password-file --session --session-label --require-existing --reset-session --no-prefix-cwd --provenance -v,"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
gateway)
opts="run status install uninstall start stop restart call usage-cost health probe discover --port --bind --token --auth --password --password-file --tailscale --tailscale-reset-on-exit --allow-unconfigured --dev --reset --force --verbose --claude-cli-logs --ws-log --compact --raw-stream --raw-stream-path"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
daemon)
opts="status install uninstall start stop restart "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
logs)
opts=" --limit --max-bytes --follow --interval --json --plain --no-color --local-time --url --token --timeout --expect-final"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
system)
opts="event heartbeat presence "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
models)
opts="list status set set-image aliases fallbacks image-fallbacks scan auth --status-json --status-plain --agent"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
approvals)
opts="get set allowlist "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
nodes)
opts="status describe list pending approve reject rename invoke run notify push canvas camera screen location "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
devices)
opts="list remove clear approve reject rotate revoke "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
node)
opts="run status install uninstall stop restart "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
sandbox)
opts="list recreate explain "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
tui)
opts=" --url --token --password --session --deliver --thinking --message --timeout-ms --history-limit"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
cron)
opts="status list add rm enable disable runs run edit "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
dns)
opts="setup "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
docs)
opts=" "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
hooks)
opts="list info check enable disable install update "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
webhooks)
opts="gmail "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
qr)
opts=" --remote --url --public-url --token --password --setup-code-only --no-ascii --json"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
clawbot)
opts="qr "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
feishu-diagnose)
opts=" --trace --analyze"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
openclaw-weixin)
opts="logs-upload "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
pairing)
opts="list approve "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
plugins)
opts="list info enable disable uninstall install update doctor "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
channels)
opts="list status capabilities resolve logs add remove login logout "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
directory)
opts="self peers groups "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
security)
opts="audit "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
secrets)
opts="reload audit configure apply "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
skills)
opts="list info check "
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
update)
opts="wizard status --json --no-restart --dry-run --channel --tag --timeout --yes"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
;;
esac
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
}
complete -F _openclaw_completion openclaw

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
"name": "每日收款失败统计",
"enabled": true,
"createdAtMs": 1774023841911,
"updatedAtMs": 1774060105638,
"updatedAtMs": 1774251937304,
"schedule": {
"kind": "cron",
"expr": "0 8 * * *",
@ -27,11 +27,11 @@
"channel": "feishu"
},
"state": {
"nextRunAtMs": 1774137600000,
"lastRunAtMs": 1774051200009,
"nextRunAtMs": 1774310400000,
"lastRunAtMs": 1774251909853,
"lastRunStatus": "ok",
"lastStatus": "ok",
"lastDurationMs": 284604,
"lastDurationMs": 27451,
"lastDelivered": true,
"lastDeliveryStatus": "delivered",
"consecutiveErrors": 0
@ -44,7 +44,7 @@
"name": "向量记忆每日备份",
"enabled": true,
"createdAtMs": 1774076047011,
"updatedAtMs": 1774076047011,
"updatedAtMs": 1774292427291,
"schedule": {
"kind": "cron",
"expr": "0 3 * * *",
@ -60,7 +60,151 @@
"mode": "none"
},
"state": {
"nextRunAtMs": 1774119600000
"nextRunAtMs": 1774378800000,
"lastRunAtMs": 1774292400005,
"lastRunStatus": "ok",
"lastStatus": "ok",
"lastDurationMs": 27286,
"lastDeliveryStatus": "not-requested",
"consecutiveErrors": 0
}
},
{
"id": "d9227c97-bb1f-4e75-99ef-33295da968fd",
"agentId": "main",
"sessionKey": "agent:main:wecom:direct:houhuan",
"name": "OpenClaw每日备份到Gitea",
"enabled": true,
"createdAtMs": 1774078576117,
"updatedAtMs": 1774251904812,
"schedule": {
"kind": "cron",
"expr": "0 4 * * *",
"tz": "Asia/Shanghai"
},
"sessionTarget": "main",
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
"text": "执行 OpenClaw 备份到 Gitea"
},
"delivery": {
"mode": "none"
},
"state": {
"nextRunAtMs": 1774296000000,
"lastRunAtMs": 1774251304805,
"lastRunStatus": "error",
"lastStatus": "error",
"lastDurationMs": 600007,
"lastDeliveryStatus": "not-requested",
"consecutiveErrors": 1,
"lastErrorReason": "timeout",
"runningAtMs": 1774296000004
}
},
{
"id": "64988c1b-b84f-455b-9d33-bf64fbdad911",
"agentId": "main",
"sessionKey": "agent:main:wecom:direct:houhuan",
"name": "每日对话记忆保存",
"enabled": true,
"createdAtMs": 1774082768253,
"updatedAtMs": 1774280457943,
"schedule": {
"kind": "cron",
"expr": "40 23 * * *",
"tz": "Asia/Shanghai"
},
"sessionTarget": "main",
"wakeMode": "now",
"payload": {
"kind": "systemEvent",
"text": "执行对话记忆保存任务"
},
"delivery": {
"mode": "none"
},
"state": {
"nextRunAtMs": 1774366800000,
"lastRunAtMs": 1774280400005,
"lastRunStatus": "ok",
"lastStatus": "ok",
"lastDurationMs": 57938,
"lastDeliveryStatus": "not-requested",
"consecutiveErrors": 0
}
},
{
"id": "00d74907-d2d5-4dcf-bb1a-b0a1f1bd0fa8",
"agentId": "main",
"sessionKey": "agent:main:feishu:direct:ou_86def554b50f91972e2924a605ccf634",
"name": "每日热点新闻股市分析报告",
"enabled": true,
"createdAtMs": 1774172078716,
"updatedAtMs": 1774274502419,
"schedule": {
"kind": "cron",
"expr": "0 22 * * *",
"tz": "Asia/Shanghai"
},
"sessionTarget": "isolated",
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
"message": "任务:生成每日热点新闻股市分析报告\n\n1. 从飞书群「热点新闻推送」(chat_id: oc_c614d6c696e6a24b105ecc7343ea33d1) 拉取今天和昨天的所有消息\n2. 从消息中提取热点新闻关键词、新闻标题和内容\n3. 代入资深股票交易员身份分析这些新闻对A股市场的影响\n - 地缘政治新闻(美国、伊朗、日本、韩国等)对资源股/能源股的影响\n - 中国国内政策和经济新闻对大盘的影响\n - 科技行业新闻AI、机器人、华为等对相关板块的影响\n - 重点关注:铜陵有色(000630)、岩山科技(002195) 相关新闻\n4. 给出第二天的操作建议(仅供参考,不构成投资建议)\n5. 将报告以清晰的结构化格式发送给用户\n\n报告格式\n【每日股市分析报告】日期\n\n一、宏观环境\n- 地缘政治\n- 国际市场\n\n二、市场热点\n- 热门板块\n- 重点新闻\n\n三、个股分析\n- 铜陵有色\n- 岩山科技\n\n四、操作建议仅供参考\n\n---\n请注意投资有风险决策需谨慎",
"model": "minimax-portal/MiniMax-M2.7",
"timeoutSeconds": 300
},
"delivery": {
"mode": "announce",
"channel": "feishu"
},
"state": {
"nextRunAtMs": 1774360800000,
"lastRunAtMs": 1774274400006,
"lastRunStatus": "ok",
"lastStatus": "ok",
"lastDurationMs": 102413,
"lastDelivered": true,
"lastDeliveryStatus": "delivered",
"consecutiveErrors": 0
}
},
{
"id": "5273524a-fe06-486c-9706-005f80b061e5",
"agentId": "main",
"sessionKey": "agent:main:wecom:direct:houhuan",
"name": "上下文使用量检测-自动保存记忆",
"enabled": true,
"createdAtMs": 1774182145274,
"updatedAtMs": 1774295945977,
"schedule": {
"kind": "every",
"everyMs": 1800000,
"anchorMs": 1774182145274
},
"sessionTarget": "isolated",
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
"message": "请执行上下文使用量检测脚本:\n\n```bash\npython3 ~/.openclaw/workspace/scripts/context_memory_guard.py\n```\n\n只需要运行这个命令并报告结果即可。如果检测到上下文使用量 >= 90%,脚本会自动保存记忆。",
"model": "minimax-portal/MiniMax-M2.7",
"timeoutSeconds": 60
},
"delivery": {
"mode": "announce",
"channel": "wecom"
},
"state": {
"nextRunAtMs": 1774297729670,
"lastRunAtMs": 1774295929670,
"lastRunStatus": "ok",
"lastStatus": "ok",
"lastDurationMs": 16307,
"lastDelivered": true,
"lastDeliveryStatus": "delivered",
"consecutiveErrors": 0
}
}
]

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
{"ts":1774119634488,"jobId":"07c52b1a-df78-4f06-a15f-45d22cab8402","action":"finished","status":"ok","summary":"向量记忆系统自动备份","deliveryStatus":"not-requested","runAtMs":1774119600011,"durationMs":34468,"nextRunAtMs":1774206000000}
{"ts":1774251904837,"jobId":"07c52b1a-df78-4f06-a15f-45d22cab8402","action":"finished","status":"ok","summary":"向量记忆系统自动备份","deliveryStatus":"not-requested","runAtMs":1774251273985,"durationMs":30820,"nextRunAtMs":1774292400000}
{"ts":1774292427295,"jobId":"07c52b1a-df78-4f06-a15f-45d22cab8402","action":"finished","status":"ok","summary":"向量记忆系统自动备份","deliveryStatus":"not-requested","runAtMs":1774292400005,"durationMs":27286,"nextRunAtMs":1774378800000}

View File

@ -1 +1,3 @@
{"ts":1774051484621,"jobId":"2073cd46-e64a-4304-a99e-a8b8a703fa54","action":"finished","status":"ok","summary":"任务完成!🦐\n\n## 执行结果汇总\n\n### 📊 统计结果\n\n在查询时间范围内2026-03-20 23:00 ~ 2026-03-21 08:00\n\n| 项目 | 结果 |\n|------|------|\n| **收款失败次数** | **0** ✅ |\n| 支付宝成功次数 | 12笔 (217.80元) |\n| 微信成功次数 | 34笔 (560.40元) |\n| 成功支付总次数 | 46笔 (778.20元) |\n\n### 🔍 1分钟内相同金额检查\n未发现失败后1分钟内相同金额的重试成功记录\n\n### 📄 文档已生成\n- 标题:收款失败统计 (2026-03-20 ~ 2026-03-21)\n- 链接https://www.feishu.cn/docx/ZnfsdxH8Lo76Mpx44gEcowwMnrh\n\n### ✅ 群聊通知已发送\n已在「益选通知群」发送统计汇总消息\n\n---\n\n今天是个好日子收款系统运行平稳没有失败记录🎉 明天早上8点见~","delivered":true,"deliveryStatus":"delivered","sessionId":"557d0e64-c50b-45a5-8277-49a5bd8f9192","sessionKey":"agent:main:cron:2073cd46-e64a-4304-a99e-a8b8a703fa54:run:557d0e64-c50b-45a5-8277-49a5bd8f9192","runAtMs":1774051200009,"durationMs":284604,"nextRunAtMs":1774137600000,"model":"MiniMax-M2.5","provider":"minimax-portal","usage":{"input_tokens":72666,"output_tokens":4366,"total_tokens":53231}}
{"ts":1774137734029,"jobId":"2073cd46-e64a-4304-a99e-a8b8a703fa54","action":"finished","status":"ok","summary":"任务完成!✅\n\n**执行结果汇总:**\n\n| 项目 | 结果 |\n|------|------|\n| 查询时间范围 | 2026-03-21 23:00 ~ 2026-03-22 08:00 |\n| 收款失败记录 | **2条** |\n| Excel追加 | ✅ 已更新2条到Sheet1第7-8行 |\n| 群里消息 | ✅ 已发送 |\n\n**失败记录详情:**\n- 序号72026-03-22 04:18:06微信支付23.00元\n- 序号82026-03-22 04:18:23微信支付23.00元\n\n群里已收到通知「收款失败统计 (昨天~今天)共2条失败记录已更新2条到Excel」","delivered":true,"deliveryStatus":"delivered","sessionId":"1ddf45c8-7673-4ccb-b193-9f63c92aeb47","sessionKey":"agent:main:cron:2073cd46-e64a-4304-a99e-a8b8a703fa54:run:1ddf45c8-7673-4ccb-b193-9f63c92aeb47","runAtMs":1774137600010,"durationMs":134010,"nextRunAtMs":1774224000000,"model":"MiniMax-M2.5","provider":"minimax-portal","usage":{"input_tokens":5829,"output_tokens":1157,"total_tokens":39967}}
{"ts":1774251937310,"jobId":"2073cd46-e64a-4304-a99e-a8b8a703fa54","action":"finished","status":"ok","summary":"任务完成!🦐 汇报如下:\n\n**搜索结果**在「益选通知群」中2026-03-22 23:00 ~ 2026-03-23 08:00 时间段内,**没有找到**任何「收款失败」相关消息。\n\n**已执行操作**\n- ✅ 已在群聊发送消息:「收款失败统计 (2026-03-22 23:00 ~ 2026-03-23 08:00):无失败记录 ✅」\n- ✅ Excel 无需更新(现有 8 条记录,序号最大到 8","delivered":true,"deliveryStatus":"delivered","sessionId":"c0493bf3-6d78-4b30-8afb-1ebee307e41b","sessionKey":"agent:main:cron:2073cd46-e64a-4304-a99e-a8b8a703fa54:run:c0493bf3-6d78-4b30-8afb-1ebee307e41b","runAtMs":1774251909853,"durationMs":27451,"nextRunAtMs":1774310400000,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":5280,"output_tokens":1100,"total_tokens":39954}}

View File

@ -0,0 +1,28 @@
{"ts":1774183964695,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测结果**\n\n- 当前最高上下文使用量:**78%**\n- 状态:✅ 正常(未达到 90% 阈值)\n- 行动:无须保存记忆\n\n检测完成当前会话上下文充裕可以继续正常使用。🦐","delivered":true,"deliveryStatus":"delivered","sessionId":"ef482444-2345-4002-8aed-e567bafc390b","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:ef482444-2345-4002-8aed-e567bafc390b","runAtMs":1774183945280,"durationMs":19410,"nextRunAtMs":1774185745280,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":642,"output_tokens":146,"total_tokens":38330}}
{"ts":1774185768038,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"**📊 上下文使用量检测结果**\n\n- ✅ 当前上下文使用量:**59%**\n- ✅ 状态正常,无需保存记忆\n\n上下文使用量在安全范围内下次检测将按常规间隔进行。","delivered":true,"deliveryStatus":"delivered","sessionId":"963cae4d-42a3-46f2-b58e-03be02002b6f","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:963cae4d-42a3-46f2-b58e-03be02002b6f","runAtMs":1774185745286,"durationMs":22746,"nextRunAtMs":1774187545286,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1180,"output_tokens":193,"total_tokens":38444}}
{"ts":1774251904822,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"**📊 上下文使用量检测报告**\n\n- 当前最高上下文使用量:**59%**\n- 状态:✅ 正常(低于 90% 阈值)\n- 自动保存记忆:未触发\n\n一切正常无需额外操作。🦐","delivered":true,"deliveryStatus":"delivered","sessionId":"fc3717b0-1559-41f8-9324-e4a1c09c2d14","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:fc3717b0-1559-41f8-9324-e4a1c09c2d14","runAtMs":1774250929527,"durationMs":27562,"nextRunAtMs":1774252729527,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":642,"output_tokens":142,"total_tokens":38330}}
{"ts":1774252746705,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**38%**\n- ✅ 状态正常,无需保存记忆\n\n检测时间2026-03-23 15:58:53","delivered":true,"deliveryStatus":"delivered","sessionId":"316b1808-eb68-4c98-b129-184f10ec8d4e","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:316b1808-eb68-4c98-b129-184f10ec8d4e","runAtMs":1774252729533,"durationMs":17169,"nextRunAtMs":1774254529533,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":630,"output_tokens":125,"total_tokens":38318}}
{"ts":1774254546772,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"**📊 上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常,无需保存记忆\n\n使用量远低于 90% 阈值,一切安然无恙 🎉","delivered":true,"deliveryStatus":"delivered","sessionId":"0221f5cb-0450-4333-ab08-72a6f7395e42","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:0221f5cb-0450-4333-ab08-72a6f7395e42","runAtMs":1774254529539,"durationMs":17230,"nextRunAtMs":1774256329539,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":642,"output_tokens":137,"total_tokens":38330}}
{"ts":1774256346266,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 上下文使用量检测完成:\n\n- **当前使用量**: 38%\n- **状态**: ✅ 正常,无需保存记忆\n\n上下文使用量处于安全范围继续正常使用即可。","delivered":true,"deliveryStatus":"delivered","sessionId":"66e2ee5f-95bc-489c-bbc8-cb0dd38d9a7d","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:66e2ee5f-95bc-489c-bbc8-cb0dd38d9a7d","runAtMs":1774256329545,"durationMs":16719,"nextRunAtMs":1774258129545,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":642,"output_tokens":131,"total_tokens":38330}}
{"ts":1774258146422,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常(未达到 90% 阈值,无需自动保存记忆)","delivered":true,"deliveryStatus":"delivered","sessionId":"4098e218-5f5c-43cb-bac5-5238c1b70b2a","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:4098e218-5f5c-43cb-bac5-5238c1b70b2a","runAtMs":1774258129550,"durationMs":16870,"nextRunAtMs":1774259929550,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":632,"output_tokens":121,"total_tokens":38320}}
{"ts":1774259947066,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常,无需保存记忆\n\n检测时间2026-03-23 17:58","delivered":true,"deliveryStatus":"delivered","sessionId":"ed4be391-c44d-4d7f-a6cf-0dedb8c9db56","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:ed4be391-c44d-4d7f-a6cf-0dedb8c9db56","runAtMs":1774259929556,"durationMs":17508,"nextRunAtMs":1774261729556,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":652,"output_tokens":145,"total_tokens":38340}}
{"ts":1774261746083,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**38%**\n- 状态:✅ 正常,无需保存记忆\n\n阈值 90%,当前使用量远低于阈值,一切正常 🦐)","delivered":true,"deliveryStatus":"delivered","sessionId":"b8b7b6b9-1768-4169-9336-86afaa64095e","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:b8b7b6b9-1768-4169-9336-86afaa64095e","runAtMs":1774261729562,"durationMs":16518,"nextRunAtMs":1774263529562,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":645,"output_tokens":141,"total_tokens":38333}}
{"ts":1774263546160,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**38%**\n- ✅ 状态正常,无需保存记忆\n- 阈值≥90% 才会自动触发记忆保存","delivered":true,"deliveryStatus":"delivered","sessionId":"687d2886-1a7c-4e8d-963d-9a636d14e611","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:687d2886-1a7c-4e8d-963d-9a636d14e611","runAtMs":1774263529569,"durationMs":16585,"nextRunAtMs":1774265329569,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":638,"output_tokens":129,"total_tokens":38326}}
{"ts":1774265346206,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测结果**\n\n- 当前最高上下文使用量:**24%**\n- ✅ 状态正常,无需保存记忆\n\n检测完成一切平稳运行 🎯","delivered":true,"deliveryStatus":"delivered","sessionId":"d3a813d5-1d9c-46ae-b8c2-6b8468709346","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:d3a813d5-1d9c-46ae-b8c2-6b8468709346","runAtMs":1774265329575,"durationMs":16628,"nextRunAtMs":1774267129575,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":640,"output_tokens":127,"total_tokens":38328}}
{"ts":1774267147838,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 上下文使用量检测完成\n\n- **当前使用量**: 24%\n- **状态**: ✅ 正常\n- **操作**: 无需保存记忆\n\n上下文使用量处于安全范围下次检测将在约30分钟后自动进行。","delivered":true,"deliveryStatus":"delivered","sessionId":"4965b0dd-1b06-4209-9eaa-e13568c234b4","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:4965b0dd-1b06-4209-9eaa-e13568c234b4","runAtMs":1774267129581,"durationMs":18254,"nextRunAtMs":1774268929581,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":638,"output_tokens":137,"total_tokens":38326}}
{"ts":1774268947402,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 上下文使用量检测完成\n\n- **当前使用量**: 24%\n- **状态**: ✅ 正常\n- **操作**: 无需保存记忆\n\n使用量远低于 90% 阈值,一切正常!🦐","delivered":true,"deliveryStatus":"delivered","sessionId":"5d5108fd-78e2-416b-9ab4-f8d5e298477f","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:5d5108fd-78e2-416b-9ab4-f8d5e298477f","runAtMs":1774268929588,"durationMs":17812,"nextRunAtMs":1774270729588,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":638,"output_tokens":136,"total_tokens":38326}}
{"ts":1774270750220,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**24%**\n- ✅ 使用量正常,无需保存记忆\n\n检测结果正常上下文使用量远低于 90% 阈值,下次检测将在下一个周期自动进行。","delivered":true,"deliveryStatus":"delivered","sessionId":"79a60ef9-d879-4cec-95ca-3fef7e43c5ed","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:79a60ef9-d879-4cec-95ca-3fef7e43c5ed","runAtMs":1774270729594,"durationMs":20623,"nextRunAtMs":1774272529594,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1198,"output_tokens":208,"total_tokens":38454}}
{"ts":1774272549833,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**24%**\n- 状态:正常,无需保存记忆\n- 阈值90%","delivered":true,"deliveryStatus":"delivered","sessionId":"608ac905-d07d-4623-b84b-18aec89455b0","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:608ac905-d07d-4623-b84b-18aec89455b0","runAtMs":1774272529600,"durationMs":20231,"nextRunAtMs":1774274329600,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":638,"output_tokens":121,"total_tokens":38326}}
{"ts":1774274347036,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**30%**\n- 状态:✅ 正常(无需保存记忆)\n\n使用量远低于 90% 阈值,记忆保存未触发。","delivered":true,"deliveryStatus":"delivered","sessionId":"d7db57f8-ac9f-4b88-806e-75e8939fb968","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:d7db57f8-ac9f-4b88-806e-75e8939fb968","runAtMs":1774274329605,"durationMs":17428,"nextRunAtMs":1774276129605,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":645,"output_tokens":140,"total_tokens":38333}}
{"ts":1774276152192,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**74%**\n- ✅ 状态正常,无需自动保存记忆(阈值 90%","delivered":true,"deliveryStatus":"delivered","sessionId":"24c4e79a-157a-4f00-9dad-5733b301ad79","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:24c4e79a-157a-4f00-9dad-5733b301ad79","runAtMs":1774276129610,"durationMs":22579,"nextRunAtMs":1774277929610,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":632,"output_tokens":113,"total_tokens":38320}}
{"ts":1774277953156,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测结果**\n\n- 当前最高上下文使用量:**74%**\n- 状态:✅ 正常(未达到 90% 阈值)\n- 无需自动保存记忆\n\n检测完成一切正常🦐","delivered":true,"deliveryStatus":"delivered","sessionId":"99dcb193-0c1e-451a-8d74-22cdc90c8074","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:99dcb193-0c1e-451a-8d74-22cdc90c8074","runAtMs":1774277929617,"durationMs":23537,"nextRunAtMs":1774279729617,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1206,"output_tokens":219,"total_tokens":38458}}
{"ts":1774279751544,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 上下文使用量检测结果:\n\n- **当前使用量**74%\n- **状态**:✅ 正常\n- **操作**:无需保存记忆(阈值 90%\n\n一切正常欢哥放心 🌙","delivered":true,"deliveryStatus":"delivered","sessionId":"53ce517e-caeb-4e93-ba49-c17e9c3b3f31","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:53ce517e-caeb-4e93-ba49-c17e9c3b3f31","runAtMs":1774279729624,"durationMs":21918,"nextRunAtMs":1774281529624,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1198,"output_tokens":208,"total_tokens":38453}}
{"ts":1774281554670,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**74%**\n- 状态:✅ 正常(未达到 90% 阈值,无需自动保存记忆)","delivered":true,"deliveryStatus":"delivered","sessionId":"8d017f0b-a156-4f94-89fc-fe9d1ca8f796","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:8d017f0b-a156-4f94-89fc-fe9d1ca8f796","runAtMs":1774281529629,"durationMs":25039,"nextRunAtMs":1774283329629,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1160,"output_tokens":178,"total_tokens":38434}}
{"ts":1774283351245,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**74%**\n- ✅ 使用量正常,无需保存记忆\n\n检测时间2026-03-24 00:28:53","delivered":true,"deliveryStatus":"delivered","sessionId":"2c253e32-3bf3-45e0-af41-7b5d3794578b","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:2c253e32-3bf3-45e0-af41-7b5d3794578b","runAtMs":1774283329634,"durationMs":21609,"nextRunAtMs":1774285129634,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1170,"output_tokens":189,"total_tokens":38438}}
{"ts":1774285154943,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**74%**\n- ✅ 状态正常,无需保存记忆\n\n阈值 90%,当前安全)","delivered":true,"deliveryStatus":"delivered","sessionId":"714c3222-24d4-4194-bcc3-ee3bf2b2df26","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:714c3222-24d4-4194-bcc3-ee3bf2b2df26","runAtMs":1774285129639,"durationMs":25302,"nextRunAtMs":1774286929639,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":1161,"output_tokens":174,"total_tokens":38434}}
{"ts":1774286946363,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**39%**\n- 状态:✅ 正常,无需保存记忆\n\n检测阈值 90%,当前使用量远低于阈值,记忆保持不变。)","delivered":true,"deliveryStatus":"delivered","sessionId":"5c6bb214-369e-480a-b2ce-f3d6e76f3ae9","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:5c6bb214-369e-480a-b2ce-f3d6e76f3ae9","runAtMs":1774286929645,"durationMs":16716,"nextRunAtMs":1774288729645,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":649,"output_tokens":146,"total_tokens":38337}}
{"ts":1774288746032,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**39%**\n- 状态:✅ 正常,无需保存记忆\n\n上下文使用量远低于 90% 警戒线,一切正常!🦐","delivered":true,"deliveryStatus":"delivered","sessionId":"41034092-6f12-4190-be06-1b289e6e704a","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:41034092-6f12-4190-be06-1b289e6e704a","runAtMs":1774288729650,"durationMs":16380,"nextRunAtMs":1774290529650,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":650,"output_tokens":146,"total_tokens":38338}}
{"ts":1774290549262,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**39%**\n- 状态:**✅ 正常**(无需保存记忆)\n\n上下文使用量远低于 90% 阈值,记忆保存未触发。🦐","delivered":true,"deliveryStatus":"delivered","sessionId":"86e5b551-4ea1-43c4-8019-dd63146553ed","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:86e5b551-4ea1-43c4-8019-dd63146553ed","runAtMs":1774290529656,"durationMs":19604,"nextRunAtMs":1774292329656,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":651,"output_tokens":148,"total_tokens":38339}}
{"ts":1774292345524,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 📊 当前上下文使用量:**39%**\n- 🟢 状态正常,无需保存记忆\n\n检测通过继续正常运行。","delivered":true,"deliveryStatus":"delivered","sessionId":"aa205ba6-5d1e-41d7-85e9-72b8bf4c9048","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:aa205ba6-5d1e-41d7-85e9-72b8bf4c9048","runAtMs":1774292329660,"durationMs":15862,"nextRunAtMs":1774294129660,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":651,"output_tokens":135,"total_tokens":38339}}
{"ts":1774294147489,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"✅ **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**40%**\n- 状态:正常,无需保存记忆\n\n使用量远低于 90% 阈值,一切平稳,皮皮虾继续睡大觉~ 🌊","delivered":true,"deliveryStatus":"delivered","sessionId":"4c94425e-cb76-419f-9270-0c66a4d1d396","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:4c94425e-cb76-419f-9270-0c66a4d1d396","runAtMs":1774294129666,"durationMs":17820,"nextRunAtMs":1774295929666,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":642,"output_tokens":137,"total_tokens":38330}}
{"ts":1774295945979,"jobId":"5273524a-fe06-486c-9706-005f80b061e5","action":"finished","status":"ok","summary":"📊 **上下文使用量检测完成**\n\n- 当前最高上下文使用量:**40%**\n- 状态:✅ 正常,无需保存记忆\n\n阈值 90%,当前使用量远低于阈值,记忆保持不变)","delivered":true,"deliveryStatus":"delivered","sessionId":"c5272c83-01be-486e-8a6c-11998b19db2e","sessionKey":"agent:main:cron:5273524a-fe06-486c-9706-005f80b061e5:run:c5272c83-01be-486e-8a6c-11998b19db2e","runAtMs":1774295929670,"durationMs":16307,"nextRunAtMs":1774297729670,"model":"MiniMax-M2.7","provider":"minimax-portal","usage":{"input_tokens":652,"output_tokens":147,"total_tokens":38340}}

View File

@ -0,0 +1,3 @@
{"ts":1774107625138,"jobId":"64988c1b-b84f-455b-9d33-bf64fbdad911","action":"finished","status":"ok","summary":"执行对话记忆保存任务","deliveryStatus":"not-requested","runAtMs":1774107600009,"durationMs":25120,"nextRunAtMs":1774194000000}
{"ts":1774251904834,"jobId":"64988c1b-b84f-455b-9d33-bf64fbdad911","action":"finished","status":"ok","summary":"执行对话记忆保存任务","deliveryStatus":"not-requested","runAtMs":1774251229300,"durationMs":44685,"nextRunAtMs":1774280400000}
{"ts":1774280457947,"jobId":"64988c1b-b84f-455b-9d33-bf64fbdad911","action":"finished","status":"ok","summary":"执行对话记忆保存任务","deliveryStatus":"not-requested","runAtMs":1774280400005,"durationMs":57938,"nextRunAtMs":1774366800000}

View File

@ -0,0 +1,2 @@
{"ts":1774123565788,"jobId":"d9227c97-bb1f-4e75-99ef-33295da968fd","action":"finished","status":"ok","summary":"执行 OpenClaw 备份到 Gitea","deliveryStatus":"not-requested","runAtMs":1774123200007,"durationMs":365726,"nextRunAtMs":1774209600000}
{"ts":1774251904844,"jobId":"d9227c97-bb1f-4e75-99ef-33295da968fd","action":"finished","status":"error","error":"Error: cron: job execution timed out","deliveryStatus":"not-requested","runAtMs":1774251304805,"durationMs":600007,"nextRunAtMs":1774296000000}

View File

@ -0,0 +1,74 @@
{
"a0129b57454f0d2402a92df332be6dfe3afbe205f1ddb899fafd80bef5c2a0b8": {
"deviceId": "a0129b57454f0d2402a92df332be6dfe3afbe205f1ddb899fafd80bef5c2a0b8",
"publicKey": "o2RjHpIP19gnha_j6pTDWOXcMEctE0Y505c34INEToQ",
"platform": "linux",
"clientId": "cli",
"clientMode": "cli",
"role": "operator",
"roles": [
"operator"
],
"scopes": [
"operator.read",
"operator.admin",
"operator.write",
"operator.approvals",
"operator.pairing"
],
"approvedScopes": [
"operator.read",
"operator.admin"
],
"tokens": {
"operator": {
"token": "9ZFB2sS2SoOIPbDaBRwhneHPUEhLWlfznOEZu4RMSWY",
"role": "operator",
"scopes": [
"operator.admin"
],
"createdAtMs": 1773769615993,
"rotatedAtMs": 1773769620629,
"lastUsedAtMs": 1773770046171
}
},
"createdAtMs": 1773769615993,
"approvedAtMs": 1773769620629
},
"e1cd656afba8759c2622b6b92e41be5ed28ffccf420c8f180970ea31e8d41245": {
"deviceId": "e1cd656afba8759c2622b6b92e41be5ed28ffccf420c8f180970ea31e8d41245",
"publicKey": "ZXMxTlSuaI3VX_moAFQGjVQjJfGD45raPL2YmsHiOfY",
"platform": "Linux x86_64",
"clientId": "openclaw-control-ui",
"clientMode": "webchat",
"role": "operator",
"roles": [
"operator"
],
"scopes": [
"operator.admin",
"operator.approvals",
"operator.pairing"
],
"approvedScopes": [
"operator.admin",
"operator.approvals",
"operator.pairing"
],
"tokens": {
"operator": {
"token": "asGZiPZ3iHKpsezOO0PNYSiWosv6YQpEd5XbYdBPqIg",
"role": "operator",
"scopes": [
"operator.admin",
"operator.approvals",
"operator.pairing"
],
"createdAtMs": 1773769644708,
"lastUsedAtMs": 1774016979124
}
},
"createdAtMs": 1773769644707,
"approvedAtMs": 1773769644707
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,9 @@
{
"version": 1,
"socket": {
"path": "/home/ubuntu/.openclaw/exec-approvals.sock",
"token": "sAz7Dc08ThwtZBgA_NbMMHVJYaUGNe3I"
},
"defaults": {},
"agents": {}
}

View File

@ -0,0 +1,5 @@
# Changelog
[简体中文](CHANGELOG.zh_CN.md)
This project follows the [Keep a Changelog](https://keepachangelog.com/) format.

View File

@ -0,0 +1,3 @@
# 变更日志
本项目遵循 [Keep a Changelog](https://keepachangelog.com/) 格式。

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Tencent Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,271 @@
# WeChat
[简体中文](./README.zh_CN.md)
OpenClaw's WeChat channel plugin, supporting login authorization via QR code scanning.
## Prerequisites
[OpenClaw](https://docs.openclaw.ai/install) must be installed (the `openclaw` CLI needs to be available).
## Quick Install
```bash
npx -y @tencent-weixin/openclaw-weixin-cli install
```
## Manual Installation
If the quick install doesn't work, follow these steps manually:
### 1. Install the plugin
```bash
openclaw plugins install "@tencent-weixin/openclaw-weixin"
```
### 2. Enable the plugin
```bash
openclaw config set plugins.entries.openclaw-weixin.enabled true
```
### 3. QR code login
```bash
openclaw channels login --channel openclaw-weixin
```
A QR code will appear in the terminal. Scan it with your phone and confirm the authorization. Once confirmed, the login credentials will be saved locally automatically — no further action is needed.
### 4. Restart the gateway
```bash
openclaw gateway restart
```
## Adding More WeChat Accounts
```bash
openclaw channels login --channel openclaw-weixin
```
Each QR code login creates a new account entry, supporting multiple WeChat accounts online simultaneously.
## Multi-Account Context Isolation
By default, all channels share the same AI conversation context. To isolate conversation context for each WeChat account:
```bash
openclaw config set agents.mode per-channel-per-peer
```
This gives each "WeChat account + message sender" combination its own independent AI memory, preventing context cross-talk between accounts.
## Backend API Protocol
This plugin communicates with the backend gateway via HTTP JSON API. Developers integrating with their own backend need to implement the following interfaces.
All endpoints use `POST` with JSON request and response bodies. Common request headers:
| Header | Description |
|--------|-------------|
| `Content-Type` | `application/json` |
| `AuthorizationType` | Fixed value `ilink_bot_token` |
| `Authorization` | `Bearer <token>` (obtained after login) |
| `X-WECHAT-UIN` | Base64-encoded random uint32 |
### Endpoint List
| Endpoint | Path | Description |
|----------|------|-------------|
| getUpdates | `getupdates` | Long-poll for new messages |
| sendMessage | `sendmessage` | Send a message (text/image/video/file) |
| getUploadUrl | `getuploadurl` | Get CDN upload pre-signed URL |
| getConfig | `getconfig` | Get account config (typing ticket, etc.) |
| sendTyping | `sendtyping` | Send/cancel typing status indicator |
### getUpdates
Long-polling endpoint. The server responds when new messages arrive or on timeout.
**Request body:**
```json
{
"get_updates_buf": ""
}
```
| Field | Type | Description |
|-------|------|-------------|
| `get_updates_buf` | `string` | Sync cursor from the previous response; empty string for the first request |
**Response body:**
```json
{
"ret": 0,
"msgs": [...],
"get_updates_buf": "<new cursor>",
"longpolling_timeout_ms": 35000
}
```
| Field | Type | Description |
|-------|------|-------------|
| `ret` | `number` | Return code, `0` = success |
| `errcode` | `number?` | Error code (e.g., `-14` = session timeout) |
| `errmsg` | `string?` | Error description |
| `msgs` | `WeixinMessage[]` | Message list (structure below) |
| `get_updates_buf` | `string` | New sync cursor to pass in the next request |
| `longpolling_timeout_ms` | `number?` | Server-suggested long-poll timeout for the next request (ms) |
### sendMessage
Send a message to a user.
**Request body:**
```json
{
"msg": {
"to_user_id": "<target user ID>",
"context_token": "<conversation context token>",
"item_list": [
{
"type": 1,
"text_item": { "text": "Hello" }
}
]
}
}
```
### getUploadUrl
Get CDN upload pre-signed parameters. Call this endpoint before uploading a file to obtain `upload_param` and `thumb_upload_param`.
**Request body:**
```json
{
"filekey": "<file identifier>",
"media_type": 1,
"to_user_id": "<target user ID>",
"rawsize": 12345,
"rawfilemd5": "<plaintext MD5>",
"filesize": 12352,
"thumb_rawsize": 1024,
"thumb_rawfilemd5": "<thumbnail plaintext MD5>",
"thumb_filesize": 1040
}
```
| Field | Type | Description |
|-------|------|-------------|
| `media_type` | `number` | `1` = IMAGE, `2` = VIDEO, `3` = FILE |
| `rawsize` | `number` | Original file plaintext size |
| `rawfilemd5` | `string` | Original file plaintext MD5 |
| `filesize` | `number` | Ciphertext size after AES-128-ECB encryption |
| `thumb_rawsize` | `number?` | Thumbnail plaintext size (required for IMAGE/VIDEO) |
| `thumb_rawfilemd5` | `string?` | Thumbnail plaintext MD5 (required for IMAGE/VIDEO) |
| `thumb_filesize` | `number?` | Thumbnail ciphertext size (required for IMAGE/VIDEO) |
**Response body:**
```json
{
"upload_param": "<original image upload encrypted parameters>",
"thumb_upload_param": "<thumbnail upload encrypted parameters>"
}
```
### getConfig
Get account configuration, including the typing ticket.
**Request body:**
```json
{
"ilink_user_id": "<user ID>",
"context_token": "<optional, conversation context token>"
}
```
**Response body:**
```json
{
"ret": 0,
"typing_ticket": "<base64-encoded typing ticket>"
}
```
### sendTyping
Send or cancel the typing status indicator.
**Request body:**
```json
{
"ilink_user_id": "<user ID>",
"typing_ticket": "<obtained from getConfig>",
"status": 1
}
```
| Field | Type | Description |
|-------|------|-------------|
| `status` | `number` | `1` = typing, `2` = cancel typing |
### Message Structure
#### WeixinMessage
| Field | Type | Description |
|-------|------|-------------|
| `seq` | `number?` | Message sequence number |
| `message_id` | `number?` | Unique message ID |
| `from_user_id` | `string?` | Sender ID |
| `to_user_id` | `string?` | Receiver ID |
| `create_time_ms` | `number?` | Creation timestamp (ms) |
| `session_id` | `string?` | Session ID |
| `message_type` | `number?` | `1` = USER, `2` = BOT |
| `message_state` | `number?` | `0` = NEW, `1` = GENERATING, `2` = FINISH |
| `item_list` | `MessageItem[]?` | Message content list |
| `context_token` | `string?` | Conversation context token, must be passed back when replying |
#### MessageItem
| Field | Type | Description |
|-------|------|-------------|
| `type` | `number` | `1` TEXT, `2` IMAGE, `3` VOICE, `4` FILE, `5` VIDEO |
| `text_item` | `{ text: string }?` | Text content |
| `image_item` | `ImageItem?` | Image (with CDN reference and AES key) |
| `voice_item` | `VoiceItem?` | Voice (SILK encoded) |
| `file_item` | `FileItem?` | File attachment |
| `video_item` | `VideoItem?` | Video |
| `ref_msg` | `RefMessage?` | Referenced message |
#### CDN Media Reference (CDNMedia)
All media types (image/voice/file/video) are transferred via CDN using AES-128-ECB encryption:
| Field | Type | Description |
|-------|------|-------------|
| `encrypt_query_param` | `string?` | Encrypted parameters for CDN download/upload |
| `aes_key` | `string?` | Base64-encoded AES-128 key |
### CDN Upload Flow
1. Calculate the file's plaintext size, MD5, and ciphertext size after AES-128-ECB encryption
2. If a thumbnail is needed (image/video), calculate the thumbnail's plaintext and ciphertext parameters as well
3. Call `getUploadUrl` to get `upload_param` (and `thumb_upload_param`)
4. Encrypt the file content with AES-128-ECB and PUT upload to the CDN URL
5. Encrypt and upload the thumbnail in the same way
6. Use the returned `encrypt_query_param` to construct a `CDNMedia` reference, include it in the `MessageItem`, and send
> For complete type definitions, see [`src/api/types.ts`](src/api/types.ts). For API call implementations, see [`src/api/api.ts`](src/api/api.ts).

View File

@ -0,0 +1,271 @@
# 微信
[English](./README.md)
OpenClaw 的微信渠道插件,支持通过扫码完成登录授权。
## 前提条件
已安装 [OpenClaw](https://docs.openclaw.ai/install)(需要 `openclaw` CLI 可用)。
## 一键安装
```bash
npx -y @tencent-weixin/openclaw-weixin-cli install
```
## 手动安装
如果一键安装不适用,可以按以下步骤手动操作:
### 1. 安装插件
```bash
openclaw plugins install "@tencent-weixin/openclaw-weixin"
```
### 2. 启用插件
```bash
openclaw config set plugins.entries.openclaw-weixin.enabled true
```
### 3. 扫码登录
```bash
openclaw channels login --channel openclaw-weixin
```
终端会显示一个二维码,用手机扫码并在手机上确认授权。确认后,登录凭证会自动保存到本地,无需额外操作。
### 4. 重启 gateway
```bash
openclaw gateway restart
```
## 添加更多微信账号
```bash
openclaw channels login --channel openclaw-weixin
```
每次扫码登录都会创建一个新的账号条目,支持多个微信号同时在线。
## 多账号上下文隔离
默认情况下,所有渠道的 AI 会话共享同一个上下文。如果希望每个微信账号的对话上下文相互隔离:
```bash
openclaw config set agents.mode per-channel-per-peer
```
这样每个「微信账号 + 发消息用户」组合都会拥有独立的 AI 记忆,账号之间不会串台。
## 后端 API 协议
本插件通过 HTTP JSON API 与后端网关通信。二次开发者若需对接自有后端,需实现以下接口。
所有接口均为 `POST`,请求和响应均为 JSON。通用请求头
| Header | 说明 |
|--------|------|
| `Content-Type` | `application/json` |
| `AuthorizationType` | 固定值 `ilink_bot_token` |
| `Authorization` | `Bearer <token>`(登录后获取) |
| `X-WECHAT-UIN` | 随机 uint32 的 base64 编码 |
### 接口列表
| 接口 | 路径 | 说明 |
|------|------|------|
| getUpdates | `getupdates` | 长轮询获取新消息 |
| sendMessage | `sendmessage` | 发送消息(文本/图片/视频/文件) |
| getUploadUrl | `getuploadurl` | 获取 CDN 上传预签名 URL |
| getConfig | `getconfig` | 获取账号配置typing ticket 等) |
| sendTyping | `sendtyping` | 发送/取消输入状态指示 |
### getUpdates
长轮询接口。服务端在有新消息或超时后返回。
**请求体:**
```json
{
"get_updates_buf": ""
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `get_updates_buf` | `string` | 上次响应返回的同步游标,首次请求传空字符串 |
**响应体:**
```json
{
"ret": 0,
"msgs": [...],
"get_updates_buf": "<新游标>",
"longpolling_timeout_ms": 35000
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `ret` | `number` | 返回码,`0` = 成功 |
| `errcode` | `number?` | 错误码(如 `-14` = 会话超时) |
| `errmsg` | `string?` | 错误描述 |
| `msgs` | `WeixinMessage[]` | 消息列表(结构见下方) |
| `get_updates_buf` | `string` | 新的同步游标,下次请求时回传 |
| `longpolling_timeout_ms` | `number?` | 服务端建议的下次长轮询超时ms |
### sendMessage
发送一条消息给用户。
**请求体:**
```json
{
"msg": {
"to_user_id": "<目标用户 ID>",
"context_token": "<会话上下文令牌>",
"item_list": [
{
"type": 1,
"text_item": { "text": "你好" }
}
]
}
}
```
### getUploadUrl
获取 CDN 上传预签名参数。上传文件前需先调用此接口获取 `upload_param``thumb_upload_param`
**请求体:**
```json
{
"filekey": "<文件标识>",
"media_type": 1,
"to_user_id": "<目标用户 ID>",
"rawsize": 12345,
"rawfilemd5": "<明文 MD5>",
"filesize": 12352,
"thumb_rawsize": 1024,
"thumb_rawfilemd5": "<缩略图明文 MD5>",
"thumb_filesize": 1040
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `media_type` | `number` | `1` = IMAGE, `2` = VIDEO, `3` = FILE |
| `rawsize` | `number` | 原文件明文大小 |
| `rawfilemd5` | `string` | 原文件明文 MD5 |
| `filesize` | `number` | AES-128-ECB 加密后的密文大小 |
| `thumb_rawsize` | `number?` | 缩略图明文大小IMAGE/VIDEO 时必填) |
| `thumb_rawfilemd5` | `string?` | 缩略图明文 MD5IMAGE/VIDEO 时必填) |
| `thumb_filesize` | `number?` | 缩略图密文大小IMAGE/VIDEO 时必填) |
**响应体:**
```json
{
"upload_param": "<原图上传加密参数>",
"thumb_upload_param": "<缩略图上传加密参数>"
}
```
### getConfig
获取账号配置,包括 typing ticket。
**请求体:**
```json
{
"ilink_user_id": "<用户 ID>",
"context_token": "<可选会话上下文令牌>"
}
```
**响应体:**
```json
{
"ret": 0,
"typing_ticket": "<base64 编码的 typing ticket>"
}
```
### sendTyping
发送或取消输入状态指示。
**请求体:**
```json
{
"ilink_user_id": "<用户 ID>",
"typing_ticket": "< getConfig 获取>",
"status": 1
}
```
| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | `number` | `1` = 正在输入,`2` = 取消输入 |
### 消息结构
#### WeixinMessage
| 字段 | 类型 | 说明 |
|------|------|------|
| `seq` | `number?` | 消息序列号 |
| `message_id` | `number?` | 消息唯一 ID |
| `from_user_id` | `string?` | 发送者 ID |
| `to_user_id` | `string?` | 接收者 ID |
| `create_time_ms` | `number?` | 创建时间戳ms |
| `session_id` | `string?` | 会话 ID |
| `message_type` | `number?` | `1` = USER, `2` = BOT |
| `message_state` | `number?` | `0` = NEW, `1` = GENERATING, `2` = FINISH |
| `item_list` | `MessageItem[]?` | 消息内容列表 |
| `context_token` | `string?` | 会话上下文令牌,回复时需回传 |
#### MessageItem
| 字段 | 类型 | 说明 |
|------|------|------|
| `type` | `number` | `1` TEXT, `2` IMAGE, `3` VOICE, `4` FILE, `5` VIDEO |
| `text_item` | `{ text: string }?` | 文本内容 |
| `image_item` | `ImageItem?` | 图片(含 CDN 引用和 AES 密钥) |
| `voice_item` | `VoiceItem?` | 语音SILK 编码) |
| `file_item` | `FileItem?` | 文件附件 |
| `video_item` | `VideoItem?` | 视频 |
| `ref_msg` | `RefMessage?` | 引用消息 |
#### CDN 媒体引用 (CDNMedia)
所有媒体类型(图片/语音/文件/视频)通过 CDN 传输,使用 AES-128-ECB 加密:
| 字段 | 类型 | 说明 |
|------|------|------|
| `encrypt_query_param` | `string?` | CDN 下载/上传的加密参数 |
| `aes_key` | `string?` | base64 编码的 AES-128 密钥 |
### CDN 上传流程
1. 计算文件明文大小、MD5以及 AES-128-ECB 加密后的密文大小
2. 如需缩略图(图片/视频),同样计算缩略图的明文和密文参数
3. 调用 `getUploadUrl` 获取 `upload_param`(和 `thumb_upload_param`
4. 使用 AES-128-ECB 加密文件内容PUT 上传到 CDN URL
5. 缩略图同理加密并上传
6. 使用返回的 `encrypt_query_param` 构造 `CDNMedia` 引用,放入 `MessageItem` 发送
> 完整的类型定义见 [`src/api/types.ts`](src/api/types.ts)API 调用实现见 [`src/api/api.ts`](src/api/api.ts)。

View File

@ -0,0 +1,27 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { buildChannelConfigSchema } from "openclaw/plugin-sdk";
import { weixinPlugin } from "./src/channel.js";
import { WeixinConfigSchema } from "./src/config/config-schema.js";
import { registerWeixinCli } from "./src/log-upload.js";
import { setWeixinRuntime } from "./src/runtime.js";
const plugin = {
id: "openclaw-weixin",
name: "Weixin",
description: "Weixin channel (getUpdates long-poll + sendMessage)",
configSchema: buildChannelConfigSchema(WeixinConfigSchema),
register(api: OpenClawPluginApi) {
if (!api?.runtime) {
throw new Error("[weixin] api.runtime is not available in register()");
}
setWeixinRuntime(api.runtime);
api.registerChannel({ plugin: weixinPlugin });
api.registerCli(({ program, config }) => registerWeixinCli({ program, config }), {
commands: ["openclaw-weixin"],
});
},
};
export default plugin;

View File

@ -0,0 +1,9 @@
{
"id": "openclaw-weixin",
"channels": ["openclaw-weixin"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
{
"name": "@tencent-weixin/openclaw-weixin",
"version": "1.0.2",
"description": "OpenClaw Weixin channel",
"license": "MIT",
"author": "Tencent",
"type": "module",
"files": [
"src/",
"!src/**/*.test.ts",
"!src/**/node_modules/",
"index.ts",
"openclaw.plugin.json",
"README.md",
"README.zh_CN.md",
"CHANGELOG.md",
"CHANGELOG.zh_CN.md"
],
"scripts": {
"test": "vitest run --coverage",
"typecheck": "tsc --noEmit",
"build": "tsc",
"prepublishOnly": "npm run typecheck && npm run build"
},
"engines": {
"node": ">=22"
},
"dependencies": {
"qrcode-terminal": "0.12.0",
"zod": "4.3.6"
},
"devDependencies": {
"@vitest/coverage-v8": "^3.1.0",
"typescript": "^5.8.0",
"vitest": "^3.1.0"
},
"openclaw": {
"extensions": [
"./index.ts"
],
"channel": {
"id": "openclaw-weixin",
"label": "openclaw-weixin",
"selectionLabel": "openclaw-weixin",
"docsPath": "/channels/openclaw-weixin",
"docsLabel": "openclaw-weixin",
"blurb": "Weixin channel",
"order": 75
},
"install": {
"npmSpec": "@tencent-weixin/openclaw-weixin",
"defaultChoice": "npm"
}
}
}

View File

@ -0,0 +1,240 @@
import crypto from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { loadConfigRouteTag } from "../auth/accounts.js";
import { logger } from "../util/logger.js";
import { redactBody, redactUrl } from "../util/redact.js";
import type {
BaseInfo,
GetUploadUrlReq,
GetUploadUrlResp,
GetUpdatesReq,
GetUpdatesResp,
SendMessageReq,
SendTypingReq,
GetConfigResp,
} from "./types.js";
export type WeixinApiOptions = {
baseUrl: string;
token?: string;
timeoutMs?: number;
/** Long-poll timeout for getUpdates (server may hold the request up to this). */
longPollTimeoutMs?: number;
};
// ---------------------------------------------------------------------------
// BaseInfo — attached to every outgoing CGI request
// ---------------------------------------------------------------------------
function readChannelVersion(): string {
try {
const dir = path.dirname(fileURLToPath(import.meta.url));
const pkgPath = path.resolve(dir, "..", "..", "package.json");
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8")) as { version?: string };
return pkg.version ?? "unknown";
} catch {
return "unknown";
}
}
const CHANNEL_VERSION = readChannelVersion();
/** Build the `base_info` payload included in every API request. */
export function buildBaseInfo(): BaseInfo {
return { channel_version: CHANNEL_VERSION };
}
/** Default timeout for long-poll getUpdates requests. */
const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
/** Default timeout for regular API requests (sendMessage, getUploadUrl). */
const DEFAULT_API_TIMEOUT_MS = 15_000;
/** Default timeout for lightweight API requests (getConfig, sendTyping). */
const DEFAULT_CONFIG_TIMEOUT_MS = 10_000;
function ensureTrailingSlash(url: string): string {
return url.endsWith("/") ? url : `${url}/`;
}
/** X-WECHAT-UIN header: random uint32 -> decimal string -> base64. */
function randomWechatUin(): string {
const uint32 = crypto.randomBytes(4).readUInt32BE(0);
return Buffer.from(String(uint32), "utf-8").toString("base64");
}
function buildHeaders(opts: { token?: string; body: string }): Record<string, string> {
const headers: Record<string, string> = {
"Content-Type": "application/json",
AuthorizationType: "ilink_bot_token",
"Content-Length": String(Buffer.byteLength(opts.body, "utf-8")),
"X-WECHAT-UIN": randomWechatUin(),
};
if (opts.token?.trim()) {
headers.Authorization = `Bearer ${opts.token.trim()}`;
}
const routeTag = loadConfigRouteTag();
if (routeTag) {
headers.SKRouteTag = routeTag;
}
logger.debug(
`requestHeaders: ${JSON.stringify({ ...headers, Authorization: headers.Authorization ? "Bearer ***" : undefined })}`,
);
return headers;
}
/**
* Common fetch wrapper: POST JSON to a Weixin API endpoint with timeout + abort.
* Returns the raw response text on success; throws on HTTP error or timeout.
*/
async function apiFetch(params: {
baseUrl: string;
endpoint: string;
body: string;
token?: string;
timeoutMs: number;
label: string;
}): Promise<string> {
const base = ensureTrailingSlash(params.baseUrl);
const url = new URL(params.endpoint, base);
const hdrs = buildHeaders({ token: params.token, body: params.body });
logger.debug(`POST ${redactUrl(url.toString())} body=${redactBody(params.body)}`);
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), params.timeoutMs);
try {
const res = await fetch(url.toString(), {
method: "POST",
headers: hdrs,
body: params.body,
signal: controller.signal,
});
clearTimeout(t);
const rawText = await res.text();
logger.debug(`${params.label} status=${res.status} raw=${redactBody(rawText)}`);
if (!res.ok) {
throw new Error(`${params.label} ${res.status}: ${rawText}`);
}
return rawText;
} catch (err) {
clearTimeout(t);
throw err;
}
}
/**
* Long-poll getUpdates. Server should hold the request until new messages or timeout.
*
* On client-side timeout (no server response within timeoutMs), returns an empty response
* with ret=0 so the caller can simply retry. This is normal for long-poll.
*/
export async function getUpdates(
params: GetUpdatesReq & {
baseUrl: string;
token?: string;
timeoutMs?: number;
},
): Promise<GetUpdatesResp> {
const timeout = params.timeoutMs ?? DEFAULT_LONG_POLL_TIMEOUT_MS;
try {
const rawText = await apiFetch({
baseUrl: params.baseUrl,
endpoint: "ilink/bot/getupdates",
body: JSON.stringify({
get_updates_buf: params.get_updates_buf ?? "",
base_info: buildBaseInfo(),
}),
token: params.token,
timeoutMs: timeout,
label: "getUpdates",
});
const resp: GetUpdatesResp = JSON.parse(rawText);
return resp;
} catch (err) {
// Long-poll timeout is normal; return empty response so caller can retry
if (err instanceof Error && err.name === "AbortError") {
logger.debug(`getUpdates: client-side timeout after ${timeout}ms, returning empty response`);
return { ret: 0, msgs: [], get_updates_buf: params.get_updates_buf };
}
throw err;
}
}
/** Get a pre-signed CDN upload URL for a file. */
export async function getUploadUrl(
params: GetUploadUrlReq & WeixinApiOptions,
): Promise<GetUploadUrlResp> {
const rawText = await apiFetch({
baseUrl: params.baseUrl,
endpoint: "ilink/bot/getuploadurl",
body: JSON.stringify({
filekey: params.filekey,
media_type: params.media_type,
to_user_id: params.to_user_id,
rawsize: params.rawsize,
rawfilemd5: params.rawfilemd5,
filesize: params.filesize,
thumb_rawsize: params.thumb_rawsize,
thumb_rawfilemd5: params.thumb_rawfilemd5,
thumb_filesize: params.thumb_filesize,
no_need_thumb: params.no_need_thumb,
aeskey: params.aeskey,
base_info: buildBaseInfo(),
}),
token: params.token,
timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,
label: "getUploadUrl",
});
const resp: GetUploadUrlResp = JSON.parse(rawText);
return resp;
}
/** Send a single message downstream. */
export async function sendMessage(
params: WeixinApiOptions & { body: SendMessageReq },
): Promise<void> {
await apiFetch({
baseUrl: params.baseUrl,
endpoint: "ilink/bot/sendmessage",
body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
token: params.token,
timeoutMs: params.timeoutMs ?? DEFAULT_API_TIMEOUT_MS,
label: "sendMessage",
});
}
/** Fetch bot config (includes typing_ticket) for a given user. */
export async function getConfig(
params: WeixinApiOptions & { ilinkUserId: string; contextToken?: string },
): Promise<GetConfigResp> {
const rawText = await apiFetch({
baseUrl: params.baseUrl,
endpoint: "ilink/bot/getconfig",
body: JSON.stringify({
ilink_user_id: params.ilinkUserId,
context_token: params.contextToken,
base_info: buildBaseInfo(),
}),
token: params.token,
timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
label: "getConfig",
});
const resp: GetConfigResp = JSON.parse(rawText);
return resp;
}
/** Send a typing indicator to a user. */
export async function sendTyping(
params: WeixinApiOptions & { body: SendTypingReq },
): Promise<void> {
await apiFetch({
baseUrl: params.baseUrl,
endpoint: "ilink/bot/sendtyping",
body: JSON.stringify({ ...params.body, base_info: buildBaseInfo() }),
token: params.token,
timeoutMs: params.timeoutMs ?? DEFAULT_CONFIG_TIMEOUT_MS,
label: "sendTyping",
});
}

View File

@ -0,0 +1,79 @@
import { getConfig } from "./api.js";
/** Subset of getConfig fields that we actually need; add new fields here as needed. */
export interface CachedConfig {
typingTicket: string;
}
const CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const CONFIG_CACHE_INITIAL_RETRY_MS = 2_000;
const CONFIG_CACHE_MAX_RETRY_MS = 60 * 60 * 1000;
interface ConfigCacheEntry {
config: CachedConfig;
everSucceeded: boolean;
nextFetchAt: number;
retryDelayMs: number;
}
/**
* Per-user getConfig cache with periodic random refresh (within 24h) and
* exponential-backoff retry (up to 1h) on failure.
*/
export class WeixinConfigManager {
private cache = new Map<string, ConfigCacheEntry>();
constructor(
private apiOpts: { baseUrl: string; token?: string },
private log: (msg: string) => void,
) {}
async getForUser(userId: string, contextToken?: string): Promise<CachedConfig> {
const now = Date.now();
const entry = this.cache.get(userId);
const shouldFetch = !entry || now >= entry.nextFetchAt;
if (shouldFetch) {
let fetchOk = false;
try {
const resp = await getConfig({
baseUrl: this.apiOpts.baseUrl,
token: this.apiOpts.token,
ilinkUserId: userId,
contextToken,
});
if (resp.ret === 0) {
this.cache.set(userId, {
config: { typingTicket: resp.typing_ticket ?? "" },
everSucceeded: true,
nextFetchAt: now + Math.random() * CONFIG_CACHE_TTL_MS,
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
});
this.log(
`[weixin] config ${entry?.everSucceeded ? "refreshed" : "cached"} for ${userId}`,
);
fetchOk = true;
}
} catch (err) {
this.log(`[weixin] getConfig failed for ${userId} (ignored): ${String(err)}`);
}
if (!fetchOk) {
const prevDelay = entry?.retryDelayMs ?? CONFIG_CACHE_INITIAL_RETRY_MS;
const nextDelay = Math.min(prevDelay * 2, CONFIG_CACHE_MAX_RETRY_MS);
if (entry) {
entry.nextFetchAt = now + nextDelay;
entry.retryDelayMs = nextDelay;
} else {
this.cache.set(userId, {
config: { typingTicket: "" },
everSucceeded: false,
nextFetchAt: now + CONFIG_CACHE_INITIAL_RETRY_MS,
retryDelayMs: CONFIG_CACHE_INITIAL_RETRY_MS,
});
}
}
}
return this.cache.get(userId)?.config ?? { typingTicket: "" };
}
}

View File

@ -0,0 +1,58 @@
import { logger } from "../util/logger.js";
const SESSION_PAUSE_DURATION_MS = 60 * 60 * 1000;
/** Error code returned by the server when the bot session has expired. */
export const SESSION_EXPIRED_ERRCODE = -14;
const pauseUntilMap = new Map<string, number>();
/** Pause all inbound/outbound API calls for `accountId` for one hour. */
export function pauseSession(accountId: string): void {
const until = Date.now() + SESSION_PAUSE_DURATION_MS;
pauseUntilMap.set(accountId, until);
logger.info(
`session-guard: paused accountId=${accountId} until=${new Date(until).toISOString()} (${SESSION_PAUSE_DURATION_MS / 1000}s)`,
);
}
/** Returns `true` when the bot is still within its one-hour cooldown window. */
export function isSessionPaused(accountId: string): boolean {
const until = pauseUntilMap.get(accountId);
if (until === undefined) return false;
if (Date.now() >= until) {
pauseUntilMap.delete(accountId);
return false;
}
return true;
}
/** Milliseconds remaining until the pause expires (0 when not paused). */
export function getRemainingPauseMs(accountId: string): number {
const until = pauseUntilMap.get(accountId);
if (until === undefined) return 0;
const remaining = until - Date.now();
if (remaining <= 0) {
pauseUntilMap.delete(accountId);
return 0;
}
return remaining;
}
/** Throw if the session is currently paused. Call before any API request. */
export function assertSessionActive(accountId: string): void {
if (isSessionPaused(accountId)) {
const remainingMin = Math.ceil(getRemainingPauseMs(accountId) / 60_000);
throw new Error(
`session paused for accountId=${accountId}, ${remainingMin} min remaining (errcode ${SESSION_EXPIRED_ERRCODE})`,
);
}
}
/**
* Reset internal state only for tests.
* @internal
*/
export function _resetForTest(): void {
pauseUntilMap.clear();
}

View File

@ -0,0 +1,222 @@
/**
* Weixin protocol types (mirrors proto: GetUpdatesReq/Resp, WeixinMessage, SendMessageReq).
* API uses JSON over HTTP; bytes fields are base64 strings in JSON.
*/
/** Common request metadata attached to every CGI request. */
export interface BaseInfo {
channel_version?: string;
}
/** proto: UploadMediaType */
export const UploadMediaType = {
IMAGE: 1,
VIDEO: 2,
FILE: 3,
VOICE: 4,
} as const;
export interface GetUploadUrlReq {
filekey?: string;
/** proto field 2: media_type, see UploadMediaType */
media_type?: number;
to_user_id?: string;
/** 原文件明文大小 */
rawsize?: number;
/** 原文件明文 MD5 */
rawfilemd5?: string;
/** 原文件密文大小AES-128-ECB 加密后) */
filesize?: number;
/** 缩略图明文大小IMAGE/VIDEO 时必填) */
thumb_rawsize?: number;
/** 缩略图明文 MD5IMAGE/VIDEO 时必填) */
thumb_rawfilemd5?: string;
/** 缩略图密文大小IMAGE/VIDEO 时必填) */
thumb_filesize?: number;
/** 不需要缩略图上传 URL默认 false */
no_need_thumb?: boolean;
/** 加密 key */
aeskey?: string;
}
export interface GetUploadUrlResp {
/** 原图上传加密参数 */
upload_param?: string;
/** 缩略图上传加密参数,无缩略图时为空 */
thumb_upload_param?: string;
}
export const MessageType = {
NONE: 0,
USER: 1,
BOT: 2,
} as const;
export const MessageItemType = {
NONE: 0,
TEXT: 1,
IMAGE: 2,
VOICE: 3,
FILE: 4,
VIDEO: 5,
} as const;
export const MessageState = {
NEW: 0,
GENERATING: 1,
FINISH: 2,
} as const;
export interface TextItem {
text?: string;
}
/** CDN media reference; aes_key is base64-encoded bytes in JSON. */
export interface CDNMedia {
encrypt_query_param?: string;
aes_key?: string;
/** 加密类型: 0=只加密fileid, 1=打包缩略图/中图等信息 */
encrypt_type?: number;
}
export interface ImageItem {
/** 原图 CDN 引用 */
media?: CDNMedia;
/** 缩略图 CDN 引用 */
thumb_media?: CDNMedia;
/** Raw AES-128 key as hex string (16 bytes); preferred over media.aes_key for inbound decryption. */
aeskey?: string;
url?: string;
mid_size?: number;
thumb_size?: number;
thumb_height?: number;
thumb_width?: number;
hd_size?: number;
}
export interface VoiceItem {
media?: CDNMedia;
/** 语音编码类型1=pcm 2=adpcm 3=feature 4=speex 5=amr 6=silk 7=mp3 8=ogg-speex */
encode_type?: number;
bits_per_sample?: number;
/** 采样率 (Hz) */
sample_rate?: number;
/** 语音长度 (毫秒) */
playtime?: number;
/** 语音转文字内容 */
text?: string;
}
export interface FileItem {
media?: CDNMedia;
file_name?: string;
md5?: string;
len?: string;
}
export interface VideoItem {
media?: CDNMedia;
video_size?: number;
play_length?: number;
video_md5?: string;
thumb_media?: CDNMedia;
thumb_size?: number;
thumb_height?: number;
thumb_width?: number;
}
export interface RefMessage {
message_item?: MessageItem;
title?: string; // 摘要
}
export interface MessageItem {
type?: number;
create_time_ms?: number;
update_time_ms?: number;
is_completed?: boolean;
msg_id?: string;
ref_msg?: RefMessage;
text_item?: TextItem;
image_item?: ImageItem;
voice_item?: VoiceItem;
file_item?: FileItem;
video_item?: VideoItem;
}
/** Unified message (proto: WeixinMessage). Replaces the old split Message + MessageContent + FullMessage. */
export interface WeixinMessage {
seq?: number;
message_id?: number;
from_user_id?: string;
to_user_id?: string;
client_id?: string;
create_time_ms?: number;
update_time_ms?: number;
delete_time_ms?: number;
session_id?: string;
group_id?: string;
message_type?: number;
message_state?: number;
item_list?: MessageItem[];
context_token?: string;
}
/** GetUpdates request: bytes fields are base64 strings in JSON. */
export interface GetUpdatesReq {
/** @deprecated compat only, will be removed */
sync_buf?: string;
/** Full context buf cached locally; send "" when none (first request or after reset). */
get_updates_buf?: string;
}
/** GetUpdates response: bytes fields are base64 strings in JSON. */
export interface GetUpdatesResp {
ret?: number;
/** Error code returned by the server (e.g. -14 = session timeout). Present when request fails. */
errcode?: number;
errmsg?: string;
msgs?: WeixinMessage[];
/** @deprecated compat only */
sync_buf?: string;
/** Full context buf to cache locally and send on next request. */
get_updates_buf?: string;
/** Server-suggested timeout (ms) for the next getUpdates long-poll. */
longpolling_timeout_ms?: number;
}
/** SendMessage request: wraps a single WeixinMessage. */
export interface SendMessageReq {
msg?: WeixinMessage;
}
export interface SendMessageResp {
// empty
}
/** Typing status: 1 = typing (default), 2 = cancel typing. */
export const TypingStatus = {
TYPING: 1,
CANCEL: 2,
} as const;
/** SendTyping request: send a typing indicator to a user. */
export interface SendTypingReq {
ilink_user_id?: string;
typing_ticket?: string;
/** 1=typing (default), 2=cancel typing */
status?: number;
}
export interface SendTypingResp {
ret?: number;
errmsg?: string;
}
/** GetConfig response: bot config including typing_ticket. */
export interface GetConfigResp {
ret?: number;
errmsg?: string;
/** Base64-encoded typing ticket for sendTyping. */
typing_ticket?: string;
}

View File

@ -0,0 +1,289 @@
import fs from "node:fs";
import path from "node:path";
import { normalizeAccountId } from "openclaw/plugin-sdk";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
import { getWeixinRuntime } from "../runtime.js";
import { resolveStateDir } from "../storage/state-dir.js";
import { logger } from "../util/logger.js";
export const DEFAULT_BASE_URL = "https://ilinkai.weixin.qq.com";
export const CDN_BASE_URL = "https://novac2c.cdn.weixin.qq.com/c2c";
// ---------------------------------------------------------------------------
// Account ID compatibility (legacy raw ID → normalized ID)
// ---------------------------------------------------------------------------
/**
* Pattern-based reverse of normalizeWeixinAccountId for known weixin ID suffixes.
* Used only as a compatibility fallback when loading accounts / sync bufs stored
* under the old raw ID.
* e.g. "b0f5860fdecb-im-bot" "b0f5860fdecb@im.bot"
*/
export function deriveRawAccountId(normalizedId: string): string | undefined {
if (normalizedId.endsWith("-im-bot")) {
return `${normalizedId.slice(0, -7)}@im.bot`;
}
if (normalizedId.endsWith("-im-wechat")) {
return `${normalizedId.slice(0, -10)}@im.wechat`;
}
return undefined;
}
// ---------------------------------------------------------------------------
// Account index (persistent list of registered account IDs)
// ---------------------------------------------------------------------------
function resolveWeixinStateDir(): string {
return path.join(resolveStateDir(), "openclaw-weixin");
}
function resolveAccountIndexPath(): string {
return path.join(resolveWeixinStateDir(), "accounts.json");
}
/** Returns all accountIds registered via QR login. */
export function listIndexedWeixinAccountIds(): string[] {
const filePath = resolveAccountIndexPath();
try {
if (!fs.existsSync(filePath)) return [];
const raw = fs.readFileSync(filePath, "utf-8");
const parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed.filter((id): id is string => typeof id === "string" && id.trim() !== "");
} catch {
return [];
}
}
/** Add accountId to the persistent index (no-op if already present). */
export function registerWeixinAccountId(accountId: string): void {
const dir = resolveWeixinStateDir();
fs.mkdirSync(dir, { recursive: true });
const existing = listIndexedWeixinAccountIds();
if (existing.includes(accountId)) return;
const updated = [...existing, accountId];
fs.writeFileSync(resolveAccountIndexPath(), JSON.stringify(updated, null, 2), "utf-8");
}
// ---------------------------------------------------------------------------
// Account store (per-account credential files)
// ---------------------------------------------------------------------------
/** Unified per-account data: token + baseUrl in one file. */
export type WeixinAccountData = {
token?: string;
savedAt?: string;
baseUrl?: string;
/** Last linked Weixin user id from QR login (optional). */
userId?: string;
};
function resolveAccountsDir(): string {
return path.join(resolveWeixinStateDir(), "accounts");
}
function resolveAccountPath(accountId: string): string {
return path.join(resolveAccountsDir(), `${accountId}.json`);
}
/**
* Legacy single-file token: `credentials/openclaw-weixin/credentials.json` (pre per-account files).
*/
function loadLegacyToken(): string | undefined {
const legacyPath = path.join(resolveStateDir(), "credentials", "openclaw-weixin", "credentials.json");
try {
if (!fs.existsSync(legacyPath)) return undefined;
const raw = fs.readFileSync(legacyPath, "utf-8");
const parsed = JSON.parse(raw) as { token?: string };
return typeof parsed.token === "string" ? parsed.token : undefined;
} catch {
return undefined;
}
}
function readAccountFile(filePath: string): WeixinAccountData | null {
try {
if (fs.existsSync(filePath)) {
return JSON.parse(fs.readFileSync(filePath, "utf-8")) as WeixinAccountData;
}
} catch {
// ignore
}
return null;
}
/** Load account data by ID, with compatibility fallbacks. */
export function loadWeixinAccount(accountId: string): WeixinAccountData | null {
// Primary: try given accountId (normalized IDs written after this change).
const primary = readAccountFile(resolveAccountPath(accountId));
if (primary) return primary;
// Compatibility: if the given ID is normalized, derive the old raw filename
// (e.g. "b0f5860fdecb-im-bot" → "b0f5860fdecb@im.bot") for existing installs.
const rawId = deriveRawAccountId(accountId);
if (rawId) {
const compat = readAccountFile(resolveAccountPath(rawId));
if (compat) return compat;
}
// Legacy fallback: read token from old single-account credentials file.
const token = loadLegacyToken();
if (token) return { token };
return null;
}
/**
* Persist account data after QR login (merges into existing file).
* - token: overwritten when provided.
* - baseUrl: stored when non-empty; resolveWeixinAccount falls back to DEFAULT_BASE_URL.
* - userId: set when `update.userId` is provided; omitted from file when cleared to empty.
*/
export function saveWeixinAccount(
accountId: string,
update: { token?: string; baseUrl?: string; userId?: string },
): void {
const dir = resolveAccountsDir();
fs.mkdirSync(dir, { recursive: true });
const existing = loadWeixinAccount(accountId) ?? {};
const token = update.token?.trim() || existing.token;
const baseUrl = update.baseUrl?.trim() || existing.baseUrl;
const userId =
update.userId !== undefined
? update.userId.trim() || undefined
: existing.userId?.trim() || undefined;
const data: WeixinAccountData = {
...(token ? { token, savedAt: new Date().toISOString() } : {}),
...(baseUrl ? { baseUrl } : {}),
...(userId ? { userId } : {}),
};
const filePath = resolveAccountPath(accountId);
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
try {
fs.chmodSync(filePath, 0o600);
} catch {
// best-effort
}
}
/** Remove account data file. */
export function clearWeixinAccount(accountId: string): void {
try {
fs.unlinkSync(resolveAccountPath(accountId));
} catch {
// ignore if not found
}
}
/**
* Resolve the openclaw.json config file path.
* Checks OPENCLAW_CONFIG env var, then state dir.
*/
function resolveConfigPath(): string {
const envPath = process.env.OPENCLAW_CONFIG?.trim();
if (envPath) return envPath;
return path.join(resolveStateDir(), "openclaw.json");
}
/**
* Read `routeTag` from openclaw.json (for callers without an `OpenClawConfig` object).
* Checks per-account `channels.<id>.accounts[accountId].routeTag` first, then section-level
* `channels.<id>.routeTag`. Matches `feat_weixin_extension` behavior; channel key is `"openclaw-weixin"`.
*/
export function loadConfigRouteTag(accountId?: string): string | undefined {
try {
const configPath = resolveConfigPath();
if (!fs.existsSync(configPath)) return undefined;
const raw = fs.readFileSync(configPath, "utf-8");
const cfg = JSON.parse(raw) as Record<string, unknown>;
const channels = cfg.channels as Record<string, unknown> | undefined;
const section = channels?.["openclaw-weixin"] as Record<string, unknown> | undefined;
if (!section) return undefined;
if (accountId) {
const accounts = section.accounts as Record<string, Record<string, unknown>> | undefined;
const tag = accounts?.[accountId]?.routeTag;
if (typeof tag === "number") return String(tag);
if (typeof tag === "string" && tag.trim()) return tag.trim();
}
if (typeof section.routeTag === "number") return String(section.routeTag);
return typeof section.routeTag === "string" && section.routeTag.trim()
? section.routeTag.trim()
: undefined;
} catch {
return undefined;
}
}
/**
* No-op stub config reload is now handled externally via `openclaw gateway restart`.
*/
export async function triggerWeixinChannelReload(): Promise<void> {}
// ---------------------------------------------------------------------------
// Account resolution (merge config + stored credentials)
// ---------------------------------------------------------------------------
export type ResolvedWeixinAccount = {
accountId: string;
baseUrl: string;
cdnBaseUrl: string;
token?: string;
enabled: boolean;
/** true when a token has been obtained via QR login. */
configured: boolean;
name?: string;
};
type WeixinAccountConfig = {
name?: string;
enabled?: boolean;
cdnBaseUrl?: string;
/** Optional SKRouteTag source; read from openclaw.json when `accountId` is passed to `loadConfigRouteTag`. */
routeTag?: number | string;
};
type WeixinSectionConfig = WeixinAccountConfig & {
accounts?: Record<string, WeixinAccountConfig>;
};
/** List accountIds from the index file (written at QR login). */
export function listWeixinAccountIds(_cfg: OpenClawConfig): string[] {
return listIndexedWeixinAccountIds();
}
/** Resolve a weixin account by ID, merging config and stored credentials. */
export function resolveWeixinAccount(
cfg: OpenClawConfig,
accountId?: string | null,
): ResolvedWeixinAccount {
const raw = accountId?.trim();
if (!raw) {
throw new Error("weixin: accountId is required (no default account)");
}
const id = normalizeAccountId(raw);
const section = cfg.channels?.["openclaw-weixin"] as WeixinSectionConfig | undefined;
const accountCfg: WeixinAccountConfig = section?.accounts?.[id] ?? section ?? {};
const accountData = loadWeixinAccount(id);
const token = accountData?.token?.trim() || undefined;
const stateBaseUrl = accountData?.baseUrl?.trim() || "";
return {
accountId: id,
baseUrl: stateBaseUrl || DEFAULT_BASE_URL,
cdnBaseUrl: accountCfg.cdnBaseUrl?.trim() || CDN_BASE_URL,
token,
enabled: accountCfg.enabled !== false,
configured: Boolean(token),
name: accountCfg.name?.trim() || undefined,
};
}

View File

@ -0,0 +1,333 @@
import { randomUUID } from "node:crypto";
import { loadConfigRouteTag } from "./accounts.js";
import { logger } from "../util/logger.js";
import { redactToken } from "../util/redact.js";
type ActiveLogin = {
sessionKey: string;
id: string;
qrcode: string;
qrcodeUrl: string;
startedAt: number;
botToken?: string;
status?: "wait" | "scaned" | "confirmed" | "expired";
error?: string;
};
const ACTIVE_LOGIN_TTL_MS = 5 * 60_000;
/** Client-side timeout for the long-poll get_qrcode_status request. */
const QR_LONG_POLL_TIMEOUT_MS = 35_000;
/** Default `bot_type` for ilink get_bot_qrcode / get_qrcode_status (this channel build). */
export const DEFAULT_ILINK_BOT_TYPE = "3";
const activeLogins = new Map<string, ActiveLogin>();
interface QRCodeResponse {
qrcode: string;
qrcode_img_content: string;
}
interface StatusResponse {
status: "wait" | "scaned" | "confirmed" | "expired";
bot_token?: string;
ilink_bot_id?: string;
baseurl?: string;
/** The user ID of the person who scanned the QR code. */
ilink_user_id?: string;
}
function isLoginFresh(login: ActiveLogin): boolean {
return Date.now() - login.startedAt < ACTIVE_LOGIN_TTL_MS;
}
/** Remove all expired entries from the activeLogins map to prevent memory leaks. */
function purgeExpiredLogins(): void {
for (const [id, login] of activeLogins) {
if (!isLoginFresh(login)) {
activeLogins.delete(id);
}
}
}
async function fetchQRCode(apiBaseUrl: string, botType: string): Promise<QRCodeResponse> {
const base = apiBaseUrl.endsWith("/") ? apiBaseUrl : `${apiBaseUrl}/`;
const url = new URL(`ilink/bot/get_bot_qrcode?bot_type=${encodeURIComponent(botType)}`, base);
logger.info(`Fetching QR code from: ${url.toString()}`);
const headers: Record<string, string> = {};
const routeTag = loadConfigRouteTag();
if (routeTag) {
headers.SKRouteTag = routeTag;
}
const response = await fetch(url.toString(), { headers });
if (!response.ok) {
const body = await response.text().catch(() => "(unreadable)");
logger.error(`QR code fetch failed: ${response.status} ${response.statusText} body=${body}`);
throw new Error(`Failed to fetch QR code: ${response.status} ${response.statusText}`);
}
return await response.json();
}
async function pollQRStatus(apiBaseUrl: string, qrcode: string): Promise<StatusResponse> {
const base = apiBaseUrl.endsWith("/") ? apiBaseUrl : `${apiBaseUrl}/`;
const url = new URL(`ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`, base);
logger.debug(`Long-poll QR status from: ${url.toString()}`);
const headers: Record<string, string> = {
"iLink-App-ClientVersion": "1",
};
const routeTag = loadConfigRouteTag();
if (routeTag) {
headers.SKRouteTag = routeTag;
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), QR_LONG_POLL_TIMEOUT_MS);
try {
const response = await fetch(url.toString(), { headers, signal: controller.signal });
clearTimeout(timer);
logger.debug(`pollQRStatus: HTTP ${response.status}, reading body...`);
const rawText = await response.text();
logger.debug(`pollQRStatus: body=${rawText.substring(0, 200)}`);
if (!response.ok) {
logger.error(`QR status poll failed: ${response.status} ${response.statusText} body=${rawText}`);
throw new Error(`Failed to poll QR status: ${response.status} ${response.statusText}`);
}
return JSON.parse(rawText) as StatusResponse;
} catch (err) {
clearTimeout(timer);
if (err instanceof Error && err.name === "AbortError") {
logger.debug(`pollQRStatus: client-side timeout after ${QR_LONG_POLL_TIMEOUT_MS}ms, returning wait`);
return { status: "wait" };
}
throw err;
}
}
export type WeixinQrStartResult = {
qrcodeUrl?: string;
message: string;
sessionKey: string;
};
export type WeixinQrWaitResult = {
connected: boolean;
botToken?: string;
accountId?: string;
baseUrl?: string;
/** The user ID of the person who scanned the QR code; add to allowFrom. */
userId?: string;
message: string;
};
export async function startWeixinLoginWithQr(opts: {
verbose?: boolean;
timeoutMs?: number;
force?: boolean;
accountId?: string;
apiBaseUrl: string;
botType?: string;
}): Promise<WeixinQrStartResult> {
const sessionKey = opts.accountId || randomUUID();
purgeExpiredLogins();
const existing = activeLogins.get(sessionKey);
if (!opts.force && existing && isLoginFresh(existing) && existing.qrcodeUrl) {
return {
qrcodeUrl: existing.qrcodeUrl,
message: "二维码已就绪,请使用微信扫描。",
sessionKey,
};
}
try {
const botType = opts.botType || DEFAULT_ILINK_BOT_TYPE;
logger.info(`Starting Weixin login with bot_type=${botType}`);
if (!opts.apiBaseUrl) {
return {
message:
"No baseUrl configured. Add channels.openclaw-weixin.baseUrl to your config before logging in.",
sessionKey,
};
}
const qrResponse = await fetchQRCode(opts.apiBaseUrl, botType);
logger.info(
`QR code received, qrcode=${redactToken(qrResponse.qrcode)} imgContentLen=${qrResponse.qrcode_img_content?.length ?? 0}`,
);
logger.info(`二维码链接: ${qrResponse.qrcode_img_content}`);
const login: ActiveLogin = {
sessionKey,
id: randomUUID(),
qrcode: qrResponse.qrcode,
qrcodeUrl: qrResponse.qrcode_img_content,
startedAt: Date.now(),
};
activeLogins.set(sessionKey, login);
return {
qrcodeUrl: qrResponse.qrcode_img_content,
message: "使用微信扫描以下二维码,以完成连接。",
sessionKey,
};
} catch (err) {
logger.error(`Failed to start Weixin login: ${String(err)}`);
return {
message: `Failed to start login: ${String(err)}`,
sessionKey,
};
}
}
const MAX_QR_REFRESH_COUNT = 3;
export async function waitForWeixinLogin(opts: {
timeoutMs?: number;
verbose?: boolean;
sessionKey: string;
apiBaseUrl: string;
botType?: string;
}): Promise<WeixinQrWaitResult> {
let activeLogin = activeLogins.get(opts.sessionKey);
if (!activeLogin) {
logger.warn(`waitForWeixinLogin: no active login sessionKey=${opts.sessionKey}`);
return {
connected: false,
message: "当前没有进行中的登录,请先发起登录。",
};
}
if (!isLoginFresh(activeLogin)) {
logger.warn(`waitForWeixinLogin: login QR expired sessionKey=${opts.sessionKey}`);
activeLogins.delete(opts.sessionKey);
return {
connected: false,
message: "二维码已过期,请重新生成。",
};
}
const timeoutMs = Math.max(opts.timeoutMs ?? 480_000, 1000);
const deadline = Date.now() + timeoutMs;
let scannedPrinted = false;
let qrRefreshCount = 1;
logger.info("Starting to poll QR code status...");
while (Date.now() < deadline) {
try {
const statusResponse = await pollQRStatus(opts.apiBaseUrl, activeLogin.qrcode);
logger.debug(`pollQRStatus: status=${statusResponse.status} hasBotToken=${Boolean(statusResponse.bot_token)} hasBotId=${Boolean(statusResponse.ilink_bot_id)}`);
activeLogin.status = statusResponse.status;
switch (statusResponse.status) {
case "wait":
if (opts.verbose) {
process.stdout.write(".");
}
break;
case "scaned":
if (!scannedPrinted) {
process.stdout.write("\n👀 已扫码,在微信继续操作...\n");
scannedPrinted = true;
}
break;
case "expired": {
qrRefreshCount++;
if (qrRefreshCount > MAX_QR_REFRESH_COUNT) {
logger.warn(
`waitForWeixinLogin: QR expired ${MAX_QR_REFRESH_COUNT} times, giving up sessionKey=${opts.sessionKey}`,
);
activeLogins.delete(opts.sessionKey);
return {
connected: false,
message: "登录超时:二维码多次过期,请重新开始登录流程。",
};
}
process.stdout.write(`\n⏳ 二维码已过期,正在刷新...(${qrRefreshCount}/${MAX_QR_REFRESH_COUNT})\n`);
logger.info(
`waitForWeixinLogin: QR expired, refreshing (${qrRefreshCount}/${MAX_QR_REFRESH_COUNT})`,
);
try {
const botType = opts.botType || DEFAULT_ILINK_BOT_TYPE;
const qrResponse = await fetchQRCode(opts.apiBaseUrl, botType);
activeLogin.qrcode = qrResponse.qrcode;
activeLogin.qrcodeUrl = qrResponse.qrcode_img_content;
activeLogin.startedAt = Date.now();
scannedPrinted = false;
logger.info(`waitForWeixinLogin: new QR code obtained qrcode=${redactToken(qrResponse.qrcode)}`);
process.stdout.write(`🔄 新二维码已生成,请重新扫描\n\n`);
try {
const qrterm = await import("qrcode-terminal");
qrterm.default.generate(qrResponse.qrcode_img_content, { small: true });
} catch {
process.stdout.write(`QR Code URL: ${qrResponse.qrcode_img_content}\n`);
}
} catch (refreshErr) {
logger.error(`waitForWeixinLogin: failed to refresh QR code: ${String(refreshErr)}`);
activeLogins.delete(opts.sessionKey);
return {
connected: false,
message: `刷新二维码失败: ${String(refreshErr)}`,
};
}
break;
}
case "confirmed": {
if (!statusResponse.ilink_bot_id) {
activeLogins.delete(opts.sessionKey);
logger.error("Login confirmed but ilink_bot_id missing from response");
return {
connected: false,
message: "登录失败:服务器未返回 ilink_bot_id。",
};
}
activeLogin.botToken = statusResponse.bot_token;
activeLogins.delete(opts.sessionKey);
logger.info(
`✅ Login confirmed! ilink_bot_id=${statusResponse.ilink_bot_id} ilink_user_id=${redactToken(statusResponse.ilink_user_id)}`,
);
return {
connected: true,
botToken: statusResponse.bot_token,
accountId: statusResponse.ilink_bot_id,
baseUrl: statusResponse.baseurl,
userId: statusResponse.ilink_user_id,
message: "✅ 与微信连接成功!",
};
}
}
} catch (err) {
logger.error(`Error polling QR status: ${String(err)}`);
activeLogins.delete(opts.sessionKey);
return {
connected: false,
message: `Login failed: ${String(err)}`,
};
}
await new Promise((r) => setTimeout(r, 1000));
}
logger.warn(
`waitForWeixinLogin: timed out waiting for QR scan sessionKey=${opts.sessionKey} timeoutMs=${timeoutMs}`,
);
activeLogins.delete(opts.sessionKey);
return {
connected: false,
message: "登录超时,请重试。",
};
}

View File

@ -0,0 +1,120 @@
import fs from "node:fs";
import path from "node:path";
import { withFileLock } from "openclaw/plugin-sdk";
import { resolveStateDir } from "../storage/state-dir.js";
import { logger } from "../util/logger.js";
/**
* Resolve the framework credentials directory (mirrors core resolveOAuthDir).
* Path: $OPENCLAW_OAUTH_DIR || $OPENCLAW_STATE_DIR/credentials || ~/.openclaw/credentials
*/
function resolveCredentialsDir(): string {
const override = process.env.OPENCLAW_OAUTH_DIR?.trim();
if (override) return override;
return path.join(resolveStateDir(), "credentials");
}
/**
* Sanitize a channel/account key for safe use in filenames (mirrors core safeChannelKey).
*/
function safeKey(raw: string): string {
const trimmed = raw.trim().toLowerCase();
if (!trimmed) throw new Error("invalid key for allowFrom path");
const safe = trimmed.replace(/[\\/:*?"<>|]/g, "_").replace(/\.\./g, "_");
if (!safe || safe === "_") throw new Error("invalid key for allowFrom path");
return safe;
}
/**
* Resolve the framework allowFrom file path for a given account.
* Mirrors: `resolveAllowFromPath(channel, env, accountId)` from core.
* Path: `<credDir>/openclaw-weixin-<accountId>-allowFrom.json`
*/
export function resolveFrameworkAllowFromPath(accountId: string): string {
const base = safeKey("openclaw-weixin");
const safeAccount = safeKey(accountId);
return path.join(resolveCredentialsDir(), `${base}-${safeAccount}-allowFrom.json`);
}
type AllowFromFileContent = {
version: number;
allowFrom: string[];
};
/**
* Read the framework allowFrom list for an account (user IDs authorized via pairing).
* Returns an empty array when the file is missing or unreadable.
*/
export function readFrameworkAllowFromList(accountId: string): string[] {
const filePath = resolveFrameworkAllowFromPath(accountId);
try {
if (!fs.existsSync(filePath)) return [];
const raw = fs.readFileSync(filePath, "utf-8");
const parsed = JSON.parse(raw) as AllowFromFileContent;
if (Array.isArray(parsed.allowFrom)) {
return parsed.allowFrom.filter((id): id is string => typeof id === "string" && id.trim() !== "");
}
} catch {
// best-effort
}
return [];
}
/** File lock options matching the framework's pairing store lock settings. */
const LOCK_OPTIONS = {
retries: { retries: 3, factor: 2, minTimeout: 100, maxTimeout: 2000 },
stale: 10_000,
};
/**
* Register a user ID in the framework's channel allowFrom store.
* This writes directly to the same JSON file that `readChannelAllowFromStore` reads,
* making the user visible to the framework authorization pipeline.
*
* Uses file locking to avoid races with concurrent readers/writers.
*/
export async function registerUserInFrameworkStore(params: {
accountId: string;
userId: string;
}): Promise<{ changed: boolean }> {
const { accountId, userId } = params;
const trimmedUserId = userId.trim();
if (!trimmedUserId) return { changed: false };
const filePath = resolveFrameworkAllowFromPath(accountId);
const dir = path.dirname(filePath);
fs.mkdirSync(dir, { recursive: true });
// Ensure the file exists before locking
if (!fs.existsSync(filePath)) {
const initial: AllowFromFileContent = { version: 1, allowFrom: [] };
fs.writeFileSync(filePath, JSON.stringify(initial, null, 2), "utf-8");
}
return await withFileLock(filePath, LOCK_OPTIONS, async () => {
let content: AllowFromFileContent = { version: 1, allowFrom: [] };
try {
const raw = fs.readFileSync(filePath, "utf-8");
const parsed = JSON.parse(raw) as AllowFromFileContent;
if (Array.isArray(parsed.allowFrom)) {
content = parsed;
}
} catch {
// If read/parse fails, start fresh
}
if (content.allowFrom.includes(trimmedUserId)) {
return { changed: false };
}
content.allowFrom.push(trimmedUserId);
fs.writeFileSync(filePath, JSON.stringify(content, null, 2), "utf-8");
logger.info(
`registerUserInFrameworkStore: added userId=${trimmedUserId} accountId=${accountId} path=${filePath}`,
);
return { changed: true };
});
}

View File

@ -0,0 +1,21 @@
/**
* Shared AES-128-ECB crypto utilities for CDN upload and download.
*/
import { createCipheriv, createDecipheriv } from "node:crypto";
/** Encrypt buffer with AES-128-ECB (PKCS7 padding is default). */
export function encryptAesEcb(plaintext: Buffer, key: Buffer): Buffer {
const cipher = createCipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
}
/** Decrypt buffer with AES-128-ECB (PKCS7 padding). */
export function decryptAesEcb(ciphertext: Buffer, key: Buffer): Buffer {
const decipher = createDecipheriv("aes-128-ecb", key, null);
return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
}
/** Compute AES-128-ECB ciphertext size (PKCS7 padding to 16-byte boundary). */
export function aesEcbPaddedSize(plaintextSize: number): number {
return Math.ceil((plaintextSize + 1) / 16) * 16;
}

View File

@ -0,0 +1,77 @@
import { encryptAesEcb } from "./aes-ecb.js";
import { buildCdnUploadUrl } from "./cdn-url.js";
import { logger } from "../util/logger.js";
import { redactUrl } from "../util/redact.js";
/** Maximum retry attempts for CDN upload. */
const UPLOAD_MAX_RETRIES = 3;
/**
* Upload one buffer to the Weixin CDN with AES-128-ECB encryption.
* Returns the download encrypted_query_param from the CDN response.
* Retries up to UPLOAD_MAX_RETRIES times on server errors; client errors (4xx) abort immediately.
*/
export async function uploadBufferToCdn(params: {
buf: Buffer;
uploadParam: string;
filekey: string;
cdnBaseUrl: string;
label: string;
aeskey: Buffer;
}): Promise<{ downloadParam: string }> {
const { buf, uploadParam, filekey, cdnBaseUrl, label, aeskey } = params;
const ciphertext = encryptAesEcb(buf, aeskey);
const cdnUrl = buildCdnUploadUrl({ cdnBaseUrl, uploadParam, filekey });
logger.debug(`${label}: CDN POST url=${redactUrl(cdnUrl)} ciphertextSize=${ciphertext.length}`);
let downloadParam: string | undefined;
let lastError: unknown;
for (let attempt = 1; attempt <= UPLOAD_MAX_RETRIES; attempt++) {
try {
const res = await fetch(cdnUrl, {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: new Uint8Array(ciphertext),
});
if (res.status >= 400 && res.status < 500) {
const errMsg = res.headers.get("x-error-message") ?? (await res.text());
logger.error(
`${label}: CDN client error attempt=${attempt} status=${res.status} errMsg=${errMsg}`,
);
throw new Error(`CDN upload client error ${res.status}: ${errMsg}`);
}
if (res.status !== 200) {
const errMsg = res.headers.get("x-error-message") ?? `status ${res.status}`;
logger.error(
`${label}: CDN server error attempt=${attempt} status=${res.status} errMsg=${errMsg}`,
);
throw new Error(`CDN upload server error: ${errMsg}`);
}
downloadParam = res.headers.get("x-encrypted-param") ?? undefined;
if (!downloadParam) {
logger.error(
`${label}: CDN response missing x-encrypted-param header attempt=${attempt}`,
);
throw new Error("CDN upload response missing x-encrypted-param header");
}
logger.debug(`${label}: CDN upload success attempt=${attempt}`);
break;
} catch (err) {
lastError = err;
if (err instanceof Error && err.message.includes("client error")) throw err;
if (attempt < UPLOAD_MAX_RETRIES) {
logger.error(`${label}: attempt ${attempt} failed, retrying... err=${String(err)}`);
} else {
logger.error(`${label}: all ${UPLOAD_MAX_RETRIES} attempts failed err=${String(err)}`);
}
}
}
if (!downloadParam) {
throw lastError instanceof Error
? lastError
: new Error(`CDN upload failed after ${UPLOAD_MAX_RETRIES} attempts`);
}
return { downloadParam };
}

View File

@ -0,0 +1,17 @@
/**
* Unified CDN URL construction for Weixin CDN upload/download.
*/
/** Build a CDN download URL from encrypt_query_param. */
export function buildCdnDownloadUrl(encryptedQueryParam: string, cdnBaseUrl: string): string {
return `${cdnBaseUrl}/download?encrypted_query_param=${encodeURIComponent(encryptedQueryParam)}`;
}
/** Build a CDN upload URL from upload_param and filekey. */
export function buildCdnUploadUrl(params: {
cdnBaseUrl: string;
uploadParam: string;
filekey: string;
}): string {
return `${params.cdnBaseUrl}/upload?encrypted_query_param=${encodeURIComponent(params.uploadParam)}&filekey=${encodeURIComponent(params.filekey)}`;
}

View File

@ -0,0 +1,85 @@
import { decryptAesEcb } from "./aes-ecb.js";
import { buildCdnDownloadUrl } from "./cdn-url.js";
import { logger } from "../util/logger.js";
/**
* Download raw bytes from the CDN (no decryption).
*/
async function fetchCdnBytes(url: string, label: string): Promise<Buffer> {
let res: Response;
try {
res = await fetch(url);
} catch (err) {
const cause =
(err as NodeJS.ErrnoException).cause ?? (err as NodeJS.ErrnoException).code ?? "(no cause)";
logger.error(
`${label}: fetch network error url=${url} err=${String(err)} cause=${String(cause)}`,
);
throw err;
}
logger.debug(`${label}: response status=${res.status} ok=${res.ok}`);
if (!res.ok) {
const body = await res.text().catch(() => "(unreadable)");
const msg = `${label}: CDN download ${res.status} ${res.statusText} body=${body}`;
logger.error(msg);
throw new Error(msg);
}
return Buffer.from(await res.arrayBuffer());
}
/**
* Parse CDNMedia.aes_key into a raw 16-byte AES key.
*
* Two encodings are seen in the wild:
* - base64(raw 16 bytes) images (aes_key from media field)
* - base64(hex string of 16 bytes) file / voice / video
*
* In the second case, base64-decoding yields 32 ASCII hex chars which must
* then be parsed as hex to recover the actual 16-byte key.
*/
function parseAesKey(aesKeyBase64: string, label: string): Buffer {
const decoded = Buffer.from(aesKeyBase64, "base64");
if (decoded.length === 16) {
return decoded;
}
if (decoded.length === 32 && /^[0-9a-fA-F]{32}$/.test(decoded.toString("ascii"))) {
// hex-encoded key: base64 → hex string → raw bytes
return Buffer.from(decoded.toString("ascii"), "hex");
}
const msg = `${label}: aes_key must decode to 16 raw bytes or 32-char hex string, got ${decoded.length} bytes (base64="${aesKeyBase64}")`;
logger.error(msg);
throw new Error(msg);
}
/**
* Download and AES-128-ECB decrypt a CDN media file. Returns plaintext Buffer.
* aesKeyBase64: CDNMedia.aes_key JSON field (see parseAesKey for supported formats).
*/
export async function downloadAndDecryptBuffer(
encryptedQueryParam: string,
aesKeyBase64: string,
cdnBaseUrl: string,
label: string,
): Promise<Buffer> {
const key = parseAesKey(aesKeyBase64, label);
const url = buildCdnDownloadUrl(encryptedQueryParam, cdnBaseUrl);
logger.debug(`${label}: fetching url=${url}`);
const encrypted = await fetchCdnBytes(url, label);
logger.debug(`${label}: downloaded ${encrypted.byteLength} bytes, decrypting`);
const decrypted = decryptAesEcb(encrypted, key);
logger.debug(`${label}: decrypted ${decrypted.length} bytes`);
return decrypted;
}
/**
* Download plain (unencrypted) bytes from the CDN. Returns the raw Buffer.
*/
export async function downloadPlainCdnBuffer(
encryptedQueryParam: string,
cdnBaseUrl: string,
label: string,
): Promise<Buffer> {
const url = buildCdnDownloadUrl(encryptedQueryParam, cdnBaseUrl);
logger.debug(`${label}: fetching url=${url}`);
return fetchCdnBytes(url, label);
}

View File

@ -0,0 +1,155 @@
import crypto from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { getUploadUrl } from "../api/api.js";
import type { WeixinApiOptions } from "../api/api.js";
import { aesEcbPaddedSize } from "./aes-ecb.js";
import { uploadBufferToCdn } from "./cdn-upload.js";
import { logger } from "../util/logger.js";
import { getExtensionFromContentTypeOrUrl } from "../media/mime.js";
import { tempFileName } from "../util/random.js";
import { UploadMediaType } from "../api/types.js";
export type UploadedFileInfo = {
filekey: string;
/** 由 upload_param 上传后 CDN 返回的下载加密参数; fill into ImageItem.media.encrypt_query_param */
downloadEncryptedQueryParam: string;
/** AES-128-ECB key, hex-encoded; convert to base64 for CDNMedia.aes_key */
aeskey: string;
/** Plaintext file size in bytes */
fileSize: number;
/** Ciphertext file size in bytes (AES-128-ECB with PKCS7 padding); use for ImageItem.hd_size / mid_size */
fileSizeCiphertext: number;
};
/**
* Download a remote media URL (image, video, file) to a local temp file in destDir.
* Returns the local file path; extension is inferred from Content-Type / URL.
*/
export async function downloadRemoteImageToTemp(url: string, destDir: string): Promise<string> {
logger.debug(`downloadRemoteImageToTemp: fetching url=${url}`);
const res = await fetch(url);
if (!res.ok) {
const msg = `remote media download failed: ${res.status} ${res.statusText} url=${url}`;
logger.error(`downloadRemoteImageToTemp: ${msg}`);
throw new Error(msg);
}
const buf = Buffer.from(await res.arrayBuffer());
logger.debug(`downloadRemoteImageToTemp: downloaded ${buf.length} bytes`);
await fs.mkdir(destDir, { recursive: true });
const ext = getExtensionFromContentTypeOrUrl(res.headers.get("content-type"), url);
const name = tempFileName("weixin-remote", ext);
const filePath = path.join(destDir, name);
await fs.writeFile(filePath, buf);
logger.debug(`downloadRemoteImageToTemp: saved to ${filePath} ext=${ext}`);
return filePath;
}
/**
* Common upload pipeline: read file hash gen aeskey getUploadUrl uploadBufferToCdn return info.
*/
async function uploadMediaToCdn(params: {
filePath: string;
toUserId: string;
opts: WeixinApiOptions;
cdnBaseUrl: string;
mediaType: (typeof UploadMediaType)[keyof typeof UploadMediaType];
label: string;
}): Promise<UploadedFileInfo> {
const { filePath, toUserId, opts, cdnBaseUrl, mediaType, label } = params;
const plaintext = await fs.readFile(filePath);
const rawsize = plaintext.length;
const rawfilemd5 = crypto.createHash("md5").update(plaintext).digest("hex");
const filesize = aesEcbPaddedSize(rawsize);
const filekey = crypto.randomBytes(16).toString("hex");
const aeskey = crypto.randomBytes(16);
logger.debug(
`${label}: file=${filePath} rawsize=${rawsize} filesize=${filesize} md5=${rawfilemd5} filekey=${filekey}`,
);
const uploadUrlResp = await getUploadUrl({
...opts,
filekey,
media_type: mediaType,
to_user_id: toUserId,
rawsize,
rawfilemd5,
filesize,
no_need_thumb: true,
aeskey: aeskey.toString("hex"),
});
const uploadParam = uploadUrlResp.upload_param;
if (!uploadParam) {
logger.error(
`${label}: getUploadUrl returned no upload_param, resp=${JSON.stringify(uploadUrlResp)}`,
);
throw new Error(`${label}: getUploadUrl returned no upload_param`);
}
const { downloadParam: downloadEncryptedQueryParam } = await uploadBufferToCdn({
buf: plaintext,
uploadParam,
filekey,
cdnBaseUrl,
aeskey,
label: `${label}[orig filekey=${filekey}]`,
});
return {
filekey,
downloadEncryptedQueryParam,
aeskey: aeskey.toString("hex"),
fileSize: rawsize,
fileSizeCiphertext: filesize,
};
}
/** Upload a local image file to the Weixin CDN with AES-128-ECB encryption. */
export async function uploadFileToWeixin(params: {
filePath: string;
toUserId: string;
opts: WeixinApiOptions;
cdnBaseUrl: string;
}): Promise<UploadedFileInfo> {
return uploadMediaToCdn({
...params,
mediaType: UploadMediaType.IMAGE,
label: "uploadFileToWeixin",
});
}
/** Upload a local video file to the Weixin CDN. */
export async function uploadVideoToWeixin(params: {
filePath: string;
toUserId: string;
opts: WeixinApiOptions;
cdnBaseUrl: string;
}): Promise<UploadedFileInfo> {
return uploadMediaToCdn({
...params,
mediaType: UploadMediaType.VIDEO,
label: "uploadVideoToWeixin",
});
}
/**
* Upload a local file attachment (non-image, non-video) to the Weixin CDN.
* Uses media_type=FILE; no thumbnail required.
*/
export async function uploadFileAttachmentToWeixin(params: {
filePath: string;
fileName: string;
toUserId: string;
opts: WeixinApiOptions;
cdnBaseUrl: string;
}): Promise<UploadedFileInfo> {
return uploadMediaToCdn({
...params,
mediaType: UploadMediaType.FILE,
label: "uploadFileAttachmentToWeixin",
});
}

View File

@ -0,0 +1,380 @@
import path from "node:path";
import type { ChannelPlugin, OpenClawConfig } from "openclaw/plugin-sdk";
import { normalizeAccountId } from "openclaw/plugin-sdk";
import {
registerWeixinAccountId,
loadWeixinAccount,
saveWeixinAccount,
listWeixinAccountIds,
resolveWeixinAccount,
triggerWeixinChannelReload,
DEFAULT_BASE_URL,
} from "./auth/accounts.js";
import type { ResolvedWeixinAccount } from "./auth/accounts.js";
import { assertSessionActive } from "./api/session-guard.js";
import { getContextToken } from "./messaging/inbound.js";
import { logger } from "./util/logger.js";
import {
DEFAULT_ILINK_BOT_TYPE,
startWeixinLoginWithQr,
waitForWeixinLogin,
} from "./auth/login-qr.js";
import type { WeixinQrStartResult, WeixinQrWaitResult } from "./auth/login-qr.js";
import { monitorWeixinProvider } from "./monitor/monitor.js";
import { sendWeixinMediaFile } from "./messaging/send-media.js";
import { sendMessageWeixin } from "./messaging/send.js";
import { downloadRemoteImageToTemp } from "./cdn/upload.js";
/** Returns true when mediaUrl refers to a local filesystem path (absolute or relative). */
function isLocalFilePath(mediaUrl: string): boolean {
// Treat anything without a URL scheme (no "://") as a local path.
return !mediaUrl.includes("://");
}
function isRemoteUrl(mediaUrl: string): boolean {
return mediaUrl.startsWith("http://") || mediaUrl.startsWith("https://");
}
const MEDIA_OUTBOUND_TEMP_DIR = "/tmp/openclaw/weixin/media/outbound-temp";
/** Resolve any local path scheme to an absolute filesystem path. */
function resolveLocalPath(mediaUrl: string): string {
if (mediaUrl.startsWith("file://")) return new URL(mediaUrl).pathname;
// Resolve any relative path (./foo, ../foo, .openclaw/foo, foo/bar) against cwd
if (!path.isAbsolute(mediaUrl)) return path.resolve(mediaUrl);
return mediaUrl;
}
async function sendWeixinOutbound(params: {
cfg: OpenClawConfig;
to: string;
text: string;
accountId?: string | null;
contextToken?: string;
mediaUrl?: string;
}): Promise<{ channel: string; messageId: string }> {
const account = resolveWeixinAccount(params.cfg, params.accountId);
const aLog = logger.withAccount(account.accountId);
assertSessionActive(account.accountId);
if (!account.configured) {
aLog.error(`sendWeixinOutbound: account not configured`);
throw new Error("weixin not configured: please run `openclaw channels login --channel openclaw-weixin`");
}
if (!params.contextToken) {
aLog.error(`sendWeixinOutbound: contextToken missing, refusing to send to=${params.to}`);
throw new Error("sendWeixinOutbound: contextToken is required");
}
const result = await sendMessageWeixin({ to: params.to, text: params.text, opts: {
baseUrl: account.baseUrl,
token: account.token,
contextToken: params.contextToken,
}});
return { channel: "openclaw-weixin", messageId: result.messageId };
}
export const weixinPlugin: ChannelPlugin<ResolvedWeixinAccount> = {
id: "openclaw-weixin",
meta: {
id: "openclaw-weixin",
label: "openclaw-weixin",
selectionLabel: "openclaw-weixin (long-poll)",
docsPath: "/channels/openclaw-weixin",
docsLabel: "openclaw-weixin",
blurb: "getUpdates long-poll upstream, sendMessage downstream; token auth.",
order: 75,
},
configSchema: {
schema: {
type: "object",
additionalProperties: false,
properties: {},
},
},
capabilities: {
chatTypes: ["direct"],
media: true,
},
messaging: {
targetResolver: {
// Weixin user IDs always end with @im.wechat; treat as direct IDs, skip directory lookup.
looksLikeId: (raw) => raw.endsWith("@im.wechat"),
},
},
agentPrompt: {
messageToolHints: () => [
"To send an image or file to the current user, use the message tool with action='send' and set 'media' to a local file path or a remote URL. You do not need to specify 'to' — the current conversation recipient is used automatically.",
"When the user asks you to find an image from the web, use a web search or browser tool to find a suitable image URL, then send it using the message tool with 'media' set to that HTTPS image URL — do NOT download the image first.",
"IMPORTANT: When generating or saving a file to send, always use an absolute path (e.g. /tmp/photo.png), never a relative path like ./photo.png. Relative paths cannot be resolved and the file will not be delivered.",
"IMPORTANT: When creating a cron job (scheduled task) for the current Weixin user, you MUST set delivery.to to the user's Weixin ID (the xxx@im.wechat address from the current conversation). Without an explicit 'to', the cron delivery will fail with 'requires target'. Example: delivery: { mode: 'announce', channel: 'openclaw-weixin', to: '<current_user_id@im.wechat>' }.",
],
},
reload: { configPrefixes: ["channels.openclaw-weixin"] },
config: {
listAccountIds: (cfg) => listWeixinAccountIds(cfg),
resolveAccount: (cfg, accountId) => resolveWeixinAccount(cfg, accountId),
isConfigured: (account) => account.configured,
describeAccount: (account) => ({
accountId: account.accountId,
name: account.name,
enabled: account.enabled,
configured: account.configured,
}),
},
outbound: {
deliveryMode: "direct",
textChunkLimit: 4000,
sendText: async (ctx) => {
const result = await sendWeixinOutbound({
cfg: ctx.cfg,
to: ctx.to,
text: ctx.text,
accountId: ctx.accountId,
contextToken: getContextToken(ctx.accountId!, ctx.to),
});
return result;
},
sendMedia: async (ctx) => {
const account = resolveWeixinAccount(ctx.cfg, ctx.accountId);
const aLog = logger.withAccount(account.accountId);
assertSessionActive(account.accountId);
if (!account.configured) {
aLog.error(`sendMedia: account not configured`);
throw new Error(
"weixin not configured: please run `openclaw channels login --channel openclaw-weixin`",
);
}
const mediaUrl = ctx.mediaUrl;
if (mediaUrl && (isLocalFilePath(mediaUrl) || isRemoteUrl(mediaUrl))) {
let filePath: string;
if (isLocalFilePath(mediaUrl)) {
filePath = resolveLocalPath(mediaUrl);
aLog.debug(`sendMedia: uploading local file ${filePath}`);
} else {
aLog.debug(`sendMedia: downloading remote mediaUrl=${mediaUrl.slice(0, 80)}...`);
filePath = await downloadRemoteImageToTemp(mediaUrl, MEDIA_OUTBOUND_TEMP_DIR);
aLog.debug(`sendMedia: remote image downloaded to ${filePath}`);
}
const contextToken = getContextToken(account.accountId, ctx.to);
const result = await sendWeixinMediaFile({
filePath,
to: ctx.to,
text: ctx.text ?? "",
opts: { baseUrl: account.baseUrl, token: account.token, contextToken },
cdnBaseUrl: account.cdnBaseUrl,
});
return { channel: "openclaw-weixin", messageId: result.messageId };
}
const result = await sendWeixinOutbound({
cfg: ctx.cfg,
to: ctx.to,
text: ctx.text ?? "",
accountId: ctx.accountId,
contextToken: getContextToken(ctx.accountId!, ctx.to),
});
return result;
},
},
status: {
defaultRuntime: {
accountId: "",
lastError: null,
lastInboundAt: null,
lastOutboundAt: null,
},
collectStatusIssues: () => [],
buildChannelSummary: ({ snapshot }) => ({
configured: snapshot.configured ?? false,
lastError: snapshot.lastError ?? null,
lastInboundAt: snapshot.lastInboundAt ?? null,
lastOutboundAt: snapshot.lastOutboundAt ?? null,
}),
buildAccountSnapshot: ({ account, runtime }) => ({
...runtime,
accountId: account.accountId,
name: account.name,
enabled: account.enabled,
configured: account.configured,
}),
},
auth: {
login: async ({ cfg, accountId, verbose, runtime }) => {
const account = resolveWeixinAccount(cfg, accountId);
const log = (msg: string) => {
runtime?.log?.(msg);
};
log(`正在启动微信扫码登录...`);
const startResult: WeixinQrStartResult = await startWeixinLoginWithQr({
accountId: account.accountId,
apiBaseUrl: account.baseUrl,
botType: DEFAULT_ILINK_BOT_TYPE,
verbose: Boolean(verbose),
});
if (!startResult.qrcodeUrl) {
logger.warn(
`auth.login: failed to get QR code accountId=${account.accountId} message=${startResult.message}`,
);
log(startResult.message);
throw new Error(startResult.message);
}
log(`\n使用微信扫描以下二维码以完成连接\n`);
try {
const qrcodeterminal = await import("qrcode-terminal");
await new Promise<void>((resolve) => {
qrcodeterminal.default.generate(startResult.qrcodeUrl!, { small: true }, (qr: string) => {
console.log(qr);
resolve();
});
});
} catch (err) {
logger.warn(
`auth.login: qrcode-terminal unavailable, falling back to URL err=${String(err)}`,
);
log(`二维码链接: ${startResult.qrcodeUrl}`);
}
const loginTimeoutMs = 480_000;
log(`\n等待连接结果...\n`);
const waitResult: WeixinQrWaitResult = await waitForWeixinLogin({
sessionKey: startResult.sessionKey,
apiBaseUrl: account.baseUrl,
timeoutMs: loginTimeoutMs,
verbose: Boolean(verbose),
botType: DEFAULT_ILINK_BOT_TYPE,
});
if (waitResult.connected && waitResult.botToken && waitResult.accountId) {
try {
// Normalize the raw ilink_bot_id (e.g. "hex@im.bot") to a filesystem-safe
// key (e.g. "hex-im-bot") so account files have no special chars.
const normalizedId = normalizeAccountId(waitResult.accountId);
saveWeixinAccount(normalizedId, {
token: waitResult.botToken,
baseUrl: waitResult.baseUrl,
userId: waitResult.userId,
});
registerWeixinAccountId(normalizedId);
void triggerWeixinChannelReload();
log(`\n✅ 与微信连接成功!`);
} catch (err) {
logger.error(
`auth.login: failed to save account data accountId=${waitResult.accountId} err=${String(err)}`,
);
log(`⚠️ 保存账号数据失败: ${String(err)}`);
}
} else {
logger.warn(
`auth.login: login did not complete accountId=${account.accountId} message=${waitResult.message}`,
);
// log(waitResult.message);
throw new Error(waitResult.message);
}
},
},
gateway: {
startAccount: async (ctx) => {
logger.debug(`startAccount entry`);
if (!ctx) {
logger.warn(`gateway.startAccount: called with undefined ctx, skipping`);
return;
}
const account = ctx.account;
const aLog = logger.withAccount(account.accountId);
aLog.debug(`about to call monitorWeixinProvider`);
aLog.info(`starting weixin webhook`);
ctx.setStatus?.({
accountId: account.accountId,
running: true,
lastStartAt: Date.now(),
lastEventAt: Date.now(),
});
if (!account.configured) {
aLog.error(`account not configured`);
ctx.log?.error?.(
`[${account.accountId}] weixin not logged in — run: openclaw channels login --channel openclaw-weixin`,
);
ctx.setStatus?.({ accountId: account.accountId, running: false });
throw new Error("weixin not configured: missing token");
}
ctx.log?.info?.(`[${account.accountId}] starting weixin provider (${DEFAULT_BASE_URL})`);
const logPath = aLog.getLogFilePath();
ctx.log?.info?.(`[${account.accountId}] weixin logs: ${logPath}`);
return monitorWeixinProvider({
baseUrl: account.baseUrl,
cdnBaseUrl: account.cdnBaseUrl,
token: account.token,
accountId: account.accountId,
config: ctx.cfg,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
setStatus: ctx.setStatus,
});
},
loginWithQrStart: async ({ accountId, force, timeoutMs, verbose }) => {
// For re-login: use saved baseUrl from account data; fall back to default for new accounts.
const savedBaseUrl = accountId ? loadWeixinAccount(accountId)?.baseUrl?.trim() : "";
const result: WeixinQrStartResult = await startWeixinLoginWithQr({
accountId: accountId ?? undefined,
apiBaseUrl: savedBaseUrl || DEFAULT_BASE_URL,
botType: DEFAULT_ILINK_BOT_TYPE,
force,
timeoutMs,
verbose,
});
// Return sessionKey so the client can pass it back in loginWithQrWait.
return {
qrDataUrl: result.qrcodeUrl,
message: result.message,
sessionKey: result.sessionKey,
} as { qrDataUrl?: string; message: string };
},
loginWithQrWait: async (params) => {
// sessionKey is forwarded by the client after loginWithQrStart (runtime param extension).
const sessionKey = (params as { sessionKey?: string }).sessionKey || params.accountId || "";
const savedBaseUrl = params.accountId
? loadWeixinAccount(params.accountId)?.baseUrl?.trim()
: "";
const result: WeixinQrWaitResult = await waitForWeixinLogin({
sessionKey,
apiBaseUrl: savedBaseUrl || DEFAULT_BASE_URL,
timeoutMs: params.timeoutMs,
});
if (result.connected && result.botToken && result.accountId) {
try {
const normalizedId = normalizeAccountId(result.accountId);
saveWeixinAccount(normalizedId, {
token: result.botToken,
baseUrl: result.baseUrl,
userId: result.userId,
});
registerWeixinAccountId(normalizedId);
triggerWeixinChannelReload();
logger.info(`loginWithQrWait: saved account data for accountId=${normalizedId}`);
} catch (err) {
logger.error(`loginWithQrWait: failed to save account data err=${String(err)}`);
}
}
return {
connected: result.connected,
message: result.message,
accountId: result.accountId,
} as { connected: boolean; message: string };
},
},
};

View File

@ -0,0 +1,22 @@
import { z } from "zod";
import { CDN_BASE_URL, DEFAULT_BASE_URL } from "../auth/accounts.js";
// ---------------------------------------------------------------------------
// Zod config schema
// ---------------------------------------------------------------------------
const weixinAccountSchema = z.object({
name: z.string().optional(),
enabled: z.boolean().optional(),
baseUrl: z.string().default(DEFAULT_BASE_URL),
cdnBaseUrl: z.string().default(CDN_BASE_URL),
routeTag: z.number().optional(),
});
/** Top-level weixin config schema (token is stored in credentials file, not config). */
export const WeixinConfigSchema = weixinAccountSchema.extend({
accounts: z.record(z.string(), weixinAccountSchema).optional(),
/** Default URL for `openclaw openclaw-weixin logs-upload`. Set via `openclaw config set channels.openclaw-weixin.logUploadUrl <url>`. */
logUploadUrl: z.string().optional(),
});

View File

@ -0,0 +1,126 @@
import fs from "node:fs/promises";
import path from "node:path";
import type { OpenClawConfig } from "openclaw/plugin-sdk";
/** Minimal subset of commander's Command used by registerWeixinCli. */
type CliCommand = {
command(name: string): CliCommand;
description(str: string): CliCommand;
option(flags: string, description: string): CliCommand;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
action(fn: (...args: any[]) => void | Promise<void>): CliCommand;
};
function currentDayLogFileName(): string {
const now = new Date();
const offsetMs = -now.getTimezoneOffset() * 60_000;
const dateKey = new Date(now.getTime() + offsetMs).toISOString().slice(0, 10);
return `openclaw-${dateKey}.log`;
}
/**
* Parse --file argument: accepts a short 8-digit date (YYYYMMDD)
* like "20260316", a full filename like "openclaw-2026-03-16.log",
* or a legacy 10-digit hour timestamp "2026031614".
*/
function resolveLogFileName(file: string): string {
if (/^\d{8}$/.test(file)) {
const yyyy = file.slice(0, 4);
const mm = file.slice(4, 6);
const dd = file.slice(6, 8);
return `openclaw-${yyyy}-${mm}-${dd}.log`;
}
if (/^\d{10}$/.test(file)) {
const yyyy = file.slice(0, 4);
const mm = file.slice(4, 6);
const dd = file.slice(6, 8);
return `openclaw-${yyyy}-${mm}-${dd}.log`;
}
return file;
}
function mainLogDir(): string {
return path.join("/tmp", "openclaw");
}
function getConfiguredUploadUrl(config: OpenClawConfig): string | undefined {
const section = config.channels?.["openclaw-weixin"] as { logUploadUrl?: string } | undefined;
return section?.logUploadUrl;
}
/** Register the `openclaw openclaw-weixin logs-upload` CLI subcommand. */
export function registerWeixinCli(params: { program: CliCommand; config: OpenClawConfig }): void {
const { program, config } = params;
const root = program.command("openclaw-weixin").description("Weixin channel utilities");
root
.command("logs-upload")
.description("Upload a Weixin log file to a remote URL via HTTP POST")
.option("--url <url>", "Remote URL to POST the log file to (overrides config)")
.option(
"--file <file>",
"Log file to upload: full filename or 8-digit date YYYYMMDD (default: today)",
)
.action(async (options: { url?: string; file?: string }) => {
const uploadUrl = options.url ?? getConfiguredUploadUrl(config);
if (!uploadUrl) {
console.error(
`[weixin] No upload URL specified. Pass --url or set it with:\n openclaw config set channels.openclaw-weixin.logUploadUrl <url>`,
);
process.exit(1);
}
const logDir = mainLogDir();
const rawFile = options.file ?? currentDayLogFileName();
const fileName = resolveLogFileName(rawFile);
const filePath = path.isAbsolute(fileName) ? fileName : path.join(logDir, fileName);
let content: Buffer;
try {
content = await fs.readFile(filePath);
} catch (err) {
console.error(`[weixin] Failed to read log file: ${filePath}\n ${String(err)}`);
process.exit(1);
}
console.log(`[weixin] Uploading ${filePath} (${content.length} bytes) to ${uploadUrl} ...`);
const formData = new FormData();
formData.append("file", new Blob([new Uint8Array(content)], { type: "text/plain" }), fileName);
let res: Response;
try {
res = await fetch(uploadUrl, { method: "POST", body: formData });
} catch (err) {
console.error(`[weixin] Upload request failed: ${String(err)}`);
process.exit(1);
}
const responseBody = await res.text().catch(() => "");
if (!res.ok) {
console.error(
`[weixin] Upload failed: HTTP ${res.status} ${res.statusText}\n ${responseBody}`,
);
process.exit(1);
}
console.log(`[weixin] Upload succeeded (HTTP ${res.status})`);
const fileid = res.headers.get("fileid");
if (fileid) {
console.log(`fileid: ${fileid}`);
} else {
// fileid not found; dump all headers for diagnosis
const headers: Record<string, string> = {};
res.headers.forEach((value, key) => {
headers[key] = value;
});
console.log("headers:", JSON.stringify(headers, null, 2));
}
if (responseBody) {
console.log("body:", responseBody);
}
});
}

View File

@ -0,0 +1,141 @@
import type { WeixinInboundMediaOpts } from "../messaging/inbound.js";
import { logger } from "../util/logger.js";
import { getMimeFromFilename } from "./mime.js";
import {
downloadAndDecryptBuffer,
downloadPlainCdnBuffer,
} from "../cdn/pic-decrypt.js";
import { silkToWav } from "./silk-transcode.js";
import type { WeixinMessage } from "../api/types.js";
import { MessageItemType } from "../api/types.js";
const WEIXIN_MEDIA_MAX_BYTES = 100 * 1024 * 1024;
/** Persist a buffer via the framework's unified media store. */
type SaveMediaFn = (
buffer: Buffer,
contentType?: string,
subdir?: string,
maxBytes?: number,
originalFilename?: string,
) => Promise<{ path: string }>;
/**
* Download and decrypt media from a single MessageItem.
* Returns the populated WeixinInboundMediaOpts fields; empty object on unsupported type or failure.
*/
export async function downloadMediaFromItem(
item: WeixinMessage["item_list"] extends (infer T)[] | undefined ? T : never,
deps: {
cdnBaseUrl: string;
saveMedia: SaveMediaFn;
log: (msg: string) => void;
errLog: (msg: string) => void;
label: string;
},
): Promise<WeixinInboundMediaOpts> {
const { cdnBaseUrl, saveMedia, log, errLog, label } = deps;
const result: WeixinInboundMediaOpts = {};
if (item.type === MessageItemType.IMAGE) {
const img = item.image_item;
if (!img?.media?.encrypt_query_param) return result;
const aesKeyBase64 = img.aeskey
? Buffer.from(img.aeskey, "hex").toString("base64")
: img.media.aes_key;
logger.debug(
`${label} image: encrypt_query_param=${img.media.encrypt_query_param.slice(0, 40)}... hasAesKey=${Boolean(aesKeyBase64)} aeskeySource=${img.aeskey ? "image_item.aeskey" : "media.aes_key"}`,
);
try {
const buf = aesKeyBase64
? await downloadAndDecryptBuffer(
img.media.encrypt_query_param,
aesKeyBase64,
cdnBaseUrl,
`${label} image`,
)
: await downloadPlainCdnBuffer(
img.media.encrypt_query_param,
cdnBaseUrl,
`${label} image-plain`,
);
const saved = await saveMedia(buf, undefined, "inbound", WEIXIN_MEDIA_MAX_BYTES);
result.decryptedPicPath = saved.path;
logger.debug(`${label} image saved: ${saved.path}`);
} catch (err) {
logger.error(`${label} image download/decrypt failed: ${String(err)}`);
errLog(`weixin ${label} image download/decrypt failed: ${String(err)}`);
}
} else if (item.type === MessageItemType.VOICE) {
const voice = item.voice_item;
if (!voice?.media?.encrypt_query_param || !voice.media.aes_key) return result;
try {
const silkBuf = await downloadAndDecryptBuffer(
voice.media.encrypt_query_param,
voice.media.aes_key,
cdnBaseUrl,
`${label} voice`,
);
logger.debug(`${label} voice: decrypted ${silkBuf.length} bytes, attempting silk transcode`);
const wavBuf = await silkToWav(silkBuf);
if (wavBuf) {
const saved = await saveMedia(wavBuf, "audio/wav", "inbound", WEIXIN_MEDIA_MAX_BYTES);
result.decryptedVoicePath = saved.path;
result.voiceMediaType = "audio/wav";
logger.debug(`${label} voice: saved WAV to ${saved.path}`);
} else {
const saved = await saveMedia(silkBuf, "audio/silk", "inbound", WEIXIN_MEDIA_MAX_BYTES);
result.decryptedVoicePath = saved.path;
result.voiceMediaType = "audio/silk";
logger.debug(`${label} voice: silk transcode unavailable, saved raw SILK to ${saved.path}`);
}
} catch (err) {
logger.error(`${label} voice download/transcode failed: ${String(err)}`);
errLog(`weixin ${label} voice download/transcode failed: ${String(err)}`);
}
} else if (item.type === MessageItemType.FILE) {
const fileItem = item.file_item;
if (!fileItem?.media?.encrypt_query_param || !fileItem.media.aes_key) return result;
try {
const buf = await downloadAndDecryptBuffer(
fileItem.media.encrypt_query_param,
fileItem.media.aes_key,
cdnBaseUrl,
`${label} file`,
);
const mime = getMimeFromFilename(fileItem.file_name ?? "file.bin");
const saved = await saveMedia(
buf,
mime,
"inbound",
WEIXIN_MEDIA_MAX_BYTES,
fileItem.file_name ?? undefined,
);
result.decryptedFilePath = saved.path;
result.fileMediaType = mime;
logger.debug(`${label} file: saved to ${saved.path} mime=${mime}`);
} catch (err) {
logger.error(`${label} file download failed: ${String(err)}`);
errLog(`weixin ${label} file download failed: ${String(err)}`);
}
} else if (item.type === MessageItemType.VIDEO) {
const videoItem = item.video_item;
if (!videoItem?.media?.encrypt_query_param || !videoItem.media.aes_key) return result;
try {
const buf = await downloadAndDecryptBuffer(
videoItem.media.encrypt_query_param,
videoItem.media.aes_key,
cdnBaseUrl,
`${label} video`,
);
const saved = await saveMedia(buf, "video/mp4", "inbound", WEIXIN_MEDIA_MAX_BYTES);
result.decryptedVideoPath = saved.path;
logger.debug(`${label} video: saved to ${saved.path}`);
} catch (err) {
logger.error(`${label} video download failed: ${String(err)}`);
errLog(`weixin ${label} video download failed: ${String(err)}`);
}
}
return result;
}

View File

@ -0,0 +1,76 @@
import path from "node:path";
const EXTENSION_TO_MIME: Record<string, string> = {
".pdf": "application/pdf",
".doc": "application/msword",
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
".xls": "application/vnd.ms-excel",
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
".ppt": "application/vnd.ms-powerpoint",
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
".txt": "text/plain",
".csv": "text/csv",
".zip": "application/zip",
".tar": "application/x-tar",
".gz": "application/gzip",
".mp3": "audio/mpeg",
".ogg": "audio/ogg",
".wav": "audio/wav",
".mp4": "video/mp4",
".mov": "video/quicktime",
".webm": "video/webm",
".mkv": "video/x-matroska",
".avi": "video/x-msvideo",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
".bmp": "image/bmp",
};
const MIME_TO_EXTENSION: Record<string, string> = {
"image/jpeg": ".jpg",
"image/jpg": ".jpg",
"image/png": ".png",
"image/gif": ".gif",
"image/webp": ".webp",
"image/bmp": ".bmp",
"video/mp4": ".mp4",
"video/quicktime": ".mov",
"video/webm": ".webm",
"video/x-matroska": ".mkv",
"video/x-msvideo": ".avi",
"audio/mpeg": ".mp3",
"audio/ogg": ".ogg",
"audio/wav": ".wav",
"application/pdf": ".pdf",
"application/zip": ".zip",
"application/x-tar": ".tar",
"application/gzip": ".gz",
"text/plain": ".txt",
"text/csv": ".csv",
};
/** Get MIME type from filename extension. Returns "application/octet-stream" for unknown extensions. */
export function getMimeFromFilename(filename: string): string {
const ext = path.extname(filename).toLowerCase();
return EXTENSION_TO_MIME[ext] ?? "application/octet-stream";
}
/** Get file extension from MIME type. Returns ".bin" for unknown types. */
export function getExtensionFromMime(mimeType: string): string {
const ct = mimeType.split(";")[0].trim().toLowerCase();
return MIME_TO_EXTENSION[ct] ?? ".bin";
}
/** Get file extension from Content-Type header or URL path. Returns ".bin" for unknown. */
export function getExtensionFromContentTypeOrUrl(contentType: string | null, url: string): string {
if (contentType) {
const ext = getExtensionFromMime(contentType);
if (ext !== ".bin") return ext;
}
const ext = path.extname(new URL(url).pathname).toLowerCase();
const knownExts = new Set(Object.keys(EXTENSION_TO_MIME));
return knownExts.has(ext) ? ext : ".bin";
}

View File

@ -0,0 +1,74 @@
import { logger } from "../util/logger.js";
/** Default sample rate for Weixin voice messages. */
const SILK_SAMPLE_RATE = 24_000;
/**
* Wrap raw pcm_s16le bytes in a WAV container.
* Mono channel, 16-bit signed little-endian.
*/
function pcmBytesToWav(pcm: Uint8Array, sampleRate: number): Buffer {
const pcmBytes = pcm.byteLength;
const totalSize = 44 + pcmBytes;
const buf = Buffer.allocUnsafe(totalSize);
let offset = 0;
buf.write("RIFF", offset);
offset += 4;
buf.writeUInt32LE(totalSize - 8, offset);
offset += 4;
buf.write("WAVE", offset);
offset += 4;
buf.write("fmt ", offset);
offset += 4;
buf.writeUInt32LE(16, offset);
offset += 4; // fmt chunk size
buf.writeUInt16LE(1, offset);
offset += 2; // PCM format
buf.writeUInt16LE(1, offset);
offset += 2; // mono
buf.writeUInt32LE(sampleRate, offset);
offset += 4;
buf.writeUInt32LE(sampleRate * 2, offset);
offset += 4; // byte rate (mono 16-bit)
buf.writeUInt16LE(2, offset);
offset += 2; // block align
buf.writeUInt16LE(16, offset);
offset += 2; // bits per sample
buf.write("data", offset);
offset += 4;
buf.writeUInt32LE(pcmBytes, offset);
offset += 4;
Buffer.from(pcm.buffer, pcm.byteOffset, pcm.byteLength).copy(buf, offset);
return buf;
}
/**
* Try to transcode a SILK audio buffer to WAV using silk-wasm.
* silk-wasm's decode() returns { data: Uint8Array (pcm_s16le), duration: number }.
*
* Returns a WAV Buffer on success, or null if silk-wasm is unavailable or decoding fails.
* Callers should fall back to passing the raw SILK file when null is returned.
*/
export async function silkToWav(silkBuf: Buffer): Promise<Buffer | null> {
try {
const { decode } = await import("silk-wasm");
logger.debug(`silkToWav: decoding ${silkBuf.length} bytes of SILK`);
const result = await decode(silkBuf, SILK_SAMPLE_RATE);
logger.debug(
`silkToWav: decoded duration=${result.duration}ms pcmBytes=${result.data.byteLength}`,
);
const wav = pcmBytesToWav(result.data, SILK_SAMPLE_RATE);
logger.debug(`silkToWav: WAV size=${wav.length}`);
return wav;
} catch (err) {
logger.warn(`silkToWav: transcode failed, will use raw silk err=${String(err)}`);
return null;
}
}

View File

@ -0,0 +1,69 @@
/**
* Per-bot debug mode toggle, persisted to disk so it survives gateway restarts.
*
* State file: `<stateDir>/openclaw-weixin/debug-mode.json`
* Format: `{ "accounts": { "<accountId>": true, ... } }`
*
* When enabled, processOneMessage appends a timing summary after each
* AI reply is delivered to the user.
*/
import fs from "node:fs";
import path from "node:path";
import { resolveStateDir } from "../storage/state-dir.js";
import { logger } from "../util/logger.js";
interface DebugModeState {
accounts: Record<string, boolean>;
}
function resolveDebugModePath(): string {
return path.join(resolveStateDir(), "openclaw-weixin", "debug-mode.json");
}
function loadState(): DebugModeState {
try {
const raw = fs.readFileSync(resolveDebugModePath(), "utf-8");
const parsed = JSON.parse(raw) as DebugModeState;
if (parsed && typeof parsed.accounts === "object") return parsed;
} catch {
// missing or corrupt — start fresh
}
return { accounts: {} };
}
function saveState(state: DebugModeState): void {
const filePath = resolveDebugModePath();
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), "utf-8");
}
/** Toggle debug mode for a bot account. Returns the new state. */
export function toggleDebugMode(accountId: string): boolean {
const state = loadState();
const next = !state.accounts[accountId];
state.accounts[accountId] = next;
try {
saveState(state);
} catch (err) {
logger.error(`debug-mode: failed to persist state: ${String(err)}`);
}
return next;
}
/** Check whether debug mode is active for a bot account. */
export function isDebugMode(accountId: string): boolean {
return loadState().accounts[accountId] === true;
}
/**
* Reset internal state only for tests.
* @internal
*/
export function _resetForTest(): void {
try {
fs.unlinkSync(resolveDebugModePath());
} catch {
// ignore if not present
}
}

View File

@ -0,0 +1,31 @@
import { logger } from "../util/logger.js";
import { sendMessageWeixin } from "./send.js";
/**
* Send a plain-text error notice back to the user.
* Fire-and-forget: errors are logged but never thrown, so callers stay unaffected.
* No-op when contextToken is absent (we have no conversation reference to reply into).
*/
export async function sendWeixinErrorNotice(params: {
to: string;
contextToken: string | undefined;
message: string;
baseUrl: string;
token?: string;
errLog: (m: string) => void;
}): Promise<void> {
if (!params.contextToken) {
logger.warn(`sendWeixinErrorNotice: no contextToken for to=${params.to}, cannot notify user`);
return;
}
try {
await sendMessageWeixin({ to: params.to, text: params.message, opts: {
baseUrl: params.baseUrl,
token: params.token,
contextToken: params.contextToken,
}});
logger.debug(`sendWeixinErrorNotice: sent to=${params.to}`);
} catch (err) {
params.errLog(`[weixin] sendWeixinErrorNotice failed to=${params.to}: ${String(err)}`);
}
}

View File

@ -0,0 +1,171 @@
import { logger } from "../util/logger.js";
import { generateId } from "../util/random.js";
import type { WeixinMessage, MessageItem } from "../api/types.js";
import { MessageItemType } from "../api/types.js";
// ---------------------------------------------------------------------------
// Context token store (in-process cache: accountId+userId → contextToken)
// ---------------------------------------------------------------------------
/**
* contextToken is issued per-message by the Weixin getupdates API and must
* be echoed verbatim in every outbound send. It is not persisted: the monitor
* loop populates this map on each inbound message, and the outbound adapter
* reads it back when the agent sends a reply.
*/
const contextTokenStore = new Map<string, string>();
function contextTokenKey(accountId: string, userId: string): string {
return `${accountId}:${userId}`;
}
/** Store a context token for a given account+user pair. */
export function setContextToken(accountId: string, userId: string, token: string): void {
const k = contextTokenKey(accountId, userId);
logger.debug(`setContextToken: key=${k}`);
contextTokenStore.set(k, token);
}
/** Retrieve the cached context token for a given account+user pair. */
export function getContextToken(accountId: string, userId: string): string | undefined {
const k = contextTokenKey(accountId, userId);
const val = contextTokenStore.get(k);
logger.debug(
`getContextToken: key=${k} found=${val !== undefined} storeSize=${contextTokenStore.size}`,
);
return val;
}
// ---------------------------------------------------------------------------
// Message ID generation
// ---------------------------------------------------------------------------
function generateMessageSid(): string {
return generateId("openclaw-weixin");
}
/** Inbound context passed to the OpenClaw core pipeline (matches MsgContext shape). */
export type WeixinMsgContext = {
Body: string;
From: string;
To: string;
AccountId: string;
OriginatingChannel: "openclaw-weixin";
OriginatingTo: string;
MessageSid: string;
Timestamp?: number;
Provider: "openclaw-weixin";
ChatType: "direct";
/** Set by monitor after resolveAgentRoute so dispatchReplyFromConfig uses the correct session. */
SessionKey?: string;
context_token?: string;
MediaUrl?: string;
MediaPath?: string;
MediaType?: string;
/** Raw message body for framework command authorization. */
CommandBody?: string;
/** Whether the sender is authorized to execute slash commands. */
CommandAuthorized?: boolean;
};
/** Returns true if the message item is a media type (image, video, file, or voice). */
export function isMediaItem(item: MessageItem): boolean {
return (
item.type === MessageItemType.IMAGE ||
item.type === MessageItemType.VIDEO ||
item.type === MessageItemType.FILE ||
item.type === MessageItemType.VOICE
);
}
function bodyFromItemList(itemList?: MessageItem[]): string {
if (!itemList?.length) return "";
for (const item of itemList) {
if (item.type === MessageItemType.TEXT && item.text_item?.text != null) {
const text = String(item.text_item.text);
const ref = item.ref_msg;
if (!ref) return text;
// Quoted media is passed as MediaPath; only include the current text as body.
if (ref.message_item && isMediaItem(ref.message_item)) return text;
// Build quoted context from both title and message_item content.
const parts: string[] = [];
if (ref.title) parts.push(ref.title);
if (ref.message_item) {
const refBody = bodyFromItemList([ref.message_item]);
if (refBody) parts.push(refBody);
}
if (!parts.length) return text;
return `[引用: ${parts.join(" | ")}]\n${text}`;
}
// 语音转文字:如果语音消息有 text 字段,直接使用文字内容
if (item.type === MessageItemType.VOICE && item.voice_item?.text) {
return item.voice_item.text;
}
}
return "";
}
export type WeixinInboundMediaOpts = {
/** Local path to decrypted image file. */
decryptedPicPath?: string;
/** Local path to transcoded/raw voice file (.wav or .silk). */
decryptedVoicePath?: string;
/** MIME type for the voice file (e.g. "audio/wav" or "audio/silk"). */
voiceMediaType?: string;
/** Local path to decrypted file attachment. */
decryptedFilePath?: string;
/** MIME type for the file attachment (guessed from file_name). */
fileMediaType?: string;
/** Local path to decrypted video file. */
decryptedVideoPath?: string;
};
/**
* Convert a WeixinMessage from getUpdates to the inbound MsgContext for the core pipeline.
* Media: only pass MediaPath (local file, after CDN download + decrypt).
* We never pass MediaUrl the upstream CDN URL is encrypted/auth-only.
* Priority when multiple media types present: image > video > file > voice.
*/
export function weixinMessageToMsgContext(
msg: WeixinMessage,
accountId: string,
opts?: WeixinInboundMediaOpts,
): WeixinMsgContext {
const from_user_id = msg.from_user_id ?? "";
const ctx: WeixinMsgContext = {
Body: bodyFromItemList(msg.item_list),
From: from_user_id,
To: from_user_id,
AccountId: accountId,
OriginatingChannel: "openclaw-weixin",
OriginatingTo: from_user_id,
MessageSid: generateMessageSid(),
Timestamp: msg.create_time_ms,
Provider: "openclaw-weixin",
ChatType: "direct",
};
if (msg.context_token) {
ctx.context_token = msg.context_token;
}
if (opts?.decryptedPicPath) {
ctx.MediaPath = opts.decryptedPicPath;
ctx.MediaType = "image/*";
} else if (opts?.decryptedVideoPath) {
ctx.MediaPath = opts.decryptedVideoPath;
ctx.MediaType = "video/mp4";
} else if (opts?.decryptedFilePath) {
ctx.MediaPath = opts.decryptedFilePath;
ctx.MediaType = opts.fileMediaType ?? "application/octet-stream";
} else if (opts?.decryptedVoicePath) {
ctx.MediaPath = opts.decryptedVoicePath;
ctx.MediaType = opts.voiceMediaType ?? "audio/wav";
}
return ctx;
}
/** Extract the context_token from an inbound WeixinMsgContext. */
export function getContextTokenFromMsgContext(ctx: WeixinMsgContext): string | undefined {
return ctx.context_token;
}

Some files were not shown because too many files have changed in this diff Show More