feat(integrations): add WeCom (企业微信) intelligent robot bridge#3370
feat(integrations): add WeCom (企业微信) intelligent robot bridge#3370pkeging wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
Thanks @pkeging for taking the time to contribute. This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered. Please read |
There was a problem hiding this comment.
Code Review
This pull request introduces the wecom-bridge integration, enabling WeCom (企业微信) users to control a local CodeWhale runtime via WeCom's smart robot WebSocket API. Key feedback focuses on improving robustness and security: wrapping command execution and SSE JSON parsing in try-catch blocks to prevent silent failures or crashes, utilizing extracted status/error variables to notify users of failed turns, removing unused imports and variables, and restricting file permissions on the persisted thread map to prevent unauthorized local access.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const command = parseCommand(scoped.text); | ||
| await handleCommand(identity.chatId, command, frame); | ||
| } |
There was a problem hiding this comment.
If any command execution (like runPrompt, sendStatus, etc.) fails and throws an error, the error is only logged to the console at the top level, and the user receives no feedback. Wrapping the command execution in a try-catch block allows us to send a friendly error message back to the user.
const command = parseCommand(scoped.text);
try {
await handleCommand(identity.chatId, command, frame);
} catch (error) {
console.error("Failed to execute command:", error);
await replyText(frame, `执行命令失败: ${error.message}`);
}
}| preservedChatStateFields, | ||
| requiredEnv, | ||
| splitMessage, | ||
| stripGroupPrefix, | ||
| ThreadStore |
| let responseText = ""; | ||
| let latestSeq = sinceSeq; | ||
| let sentProgressAt = Date.now(); |
|
|
||
| for await (const event of readSse(response)) { | ||
| if (!event.data) continue; | ||
| const record = JSON.parse(event.data); |
There was a problem hiding this comment.
Parsing event.data using JSON.parse can throw an error if the runtime sends malformed JSON. Wrapping it in a try-catch block prevents a single malformed event from crashing the entire SSE stream.
let record;
try {
record = JSON.parse(event.data);
} catch (err) {
console.error("Failed to parse SSE event data as JSON:", err, event.data);
continue;
}| if (record.event === "turn.completed") { | ||
| const turn = record.payload?.turn || {}; | ||
| const status = turn.status || "completed"; | ||
| const error = turn.error ? `\n${turn.error}` : ""; | ||
| await client.replyStream(frame, streamId, responseText.trim() || "Turn completed.", true); | ||
| return; | ||
| } |
There was a problem hiding this comment.
The variables status and error are extracted from the payload but never used. If the turn completes with a non-completed status or an error, the user won't be notified of the failure. We should use these variables to construct a proper final message.
if (record.event === "turn.completed") {
const turn = record.payload?.turn || {};
const status = turn.status || "completed";
const error = turn.error ? `\\n h[turn.error}` : "";
const finalMsg = status !== "completed"
? `Turn ${status}.${error}`.trim()
: (responseText.trim() || "Turn completed.");
await client.replyStream(frame, streamId, finalMsg, true);
return;
}| async save() { | ||
| const dir = path.dirname(this.filePath); | ||
| await mkdir(dir, { recursive: true }); | ||
| const tmp = `${this.filePath}.tmp`; | ||
| await writeFile(tmp, `${JSON.stringify(this.data, null, 2)}\n`); | ||
| await rename(tmp, this.filePath); | ||
| } |
There was a problem hiding this comment.
The thread map file contains sensitive conversation state and identifiers. To prevent other local users on a shared system from reading this sensitive data, the directory and file should be created with restricted permissions (0o700 for the directory and 0o600 for the file), consistent with the Feishu and WeChat bridges.
| async save() { | |
| const dir = path.dirname(this.filePath); | |
| await mkdir(dir, { recursive: true }); | |
| const tmp = `${this.filePath}.tmp`; | |
| await writeFile(tmp, `${JSON.stringify(this.data, null, 2)}\n`); | |
| await rename(tmp, this.filePath); | |
| } | |
| async save() { | |
| const dir = path.dirname(this.filePath); | |
| await mkdir(dir, { recursive: true, mode: 0o700 }); | |
| const tmp = `${this.filePath}.tmp`; | |
| await writeFile(tmp, `${JSON.stringify(this.data, null, 2)}\\n`, { mode: 0o600 }); | |
| await rename(tmp, this.filePath); | |
| } |
|
Thanks @pkeging — I carried the WeCom bridge into the v0.8.64 integration branch with attribution:
I kept the integration isolated under Verified with:
Appreciate the fast integration work. |
Structured memory with tag support: - MemoryEntry parser for timestamped bullets with #tag extraction - Tag indexing via MemoryIndex (inverted index for tags + full-text) - auto_tag() for automatic suggestion from capitalized terms - append_entry() extracts inline tags and accepts extra_tags parameter New CLI commands: - /memory tags — list tags with occurrence counts - /memory search <query> — full-text search across body and tags - /memory search --tag <tag> — exact tag filter Tool integration: - Remember tool accepts optional tags parameter - Auto-tag fallback when model provides no explicit tags Seam enrichment: - extract_topic_tags() scans messages for capitalized technical terms - MemoryIndex context injected into layered_context_checkpoint (max 20 entries) - memory_context section added to soft-seam and recompact prompts - SeamMetadata.tags populated for cross-reference
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
…e dead_code - search_by_tags: case-insensitive matching (was case-sensitive in memory.rs, inconsistent with memory_index.rs) - auto_tag: change redundant if/if to if/else for capitalized+special words - memory_index: remove stale #[allow(dead_code)] on search_by_tags (used by engine.rs)
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
Summary
Testing
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-featurescargo test --workspace --all-featuresChecklist