fix(plugin-lifecycle): OpenCode Zod schemas, OpenClaw isolation, session drift, lifecycle filter#2063
fix(plugin-lifecycle): OpenCode Zod schemas, OpenClaw isolation, session drift, lifecycle filter#2063thedotmack wants to merge 2 commits intomainfrom
Conversation
…ion drift, lifecycle filter - #1947: Convert OpenCode plugin tool args from plain objects to Zod schemas - #1948: Pre-populate plugins.allow before install to fix chicken-and-egg - #1949: Scope getContextForPrompt and slash commands to agent-specific project - #1950: Prefer current sessionKey over stale aliases, fallback workspaceDir - #1951: Filter lifecycle events from observations, exempt heartbeat sessions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 3 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Code Review — PR #2063Good set of targeted fixes across five related issues. The PR description is clear and the test plan is well-structured. Below is my analysis by area.
|
| Area | Status |
|---|---|
| Zod schemas (OpenCode) | ✅ Correct, check zod dep placement |
| install.sh pre-populate | ✅ Correct, atomic write recommended |
| Session drift fix | ✅ Sound |
Agent isolation in getContextForPrompt |
✅ Correct |
| Agent isolation in slash commands | baseProjectName not getProjectName(ctx) |
| Lifecycle filtering | ✅ Good, minor gaps noted |
system_session worker handling |
❓ Not visible in diff — confirm worker handles it |
systemSessionIds memory leak |
|
workspaceDir fallback |
"__unknown__" instead of $HOME |
The slash command agentProject = baseProjectName inconsistency is the most important item to resolve before merge. Everything else is lower severity.
🤖 Generated with Claude Code
Greptile SummaryThis PR bundles five bug fixes across OpenCode/OpenClaw integration: Zod schema conversion for tool args, pre-populating
Confidence Score: 4/5Two P1 logic gaps should be addressed before merging to fully deliver the isolation and filtering goals. The Zod schema fix, install.sh chicken-and-egg fix, workspaceDir fallback, and session-drift canonicalization are all correct. However, the lifecycle-session detection in before_agent_start covers only 3 of 5 excluded types, and both slash commands scope to baseProjectName instead of the agent-specific project — the same isolation regression the PR set out to fix. openclaw/src/index.ts — before_agent_start lifecycle detection (line 857) and slash command project scoping (lines 1169, 1260). Important Files Changed
Sequence DiagramsequenceDiagram
participant OC as OpenClaw Runtime
participant P as claude-mem Plugin
participant W as Worker
OC->>P: session_start
P->>P: clearSessionContext
P->>P: rememberSessionContext
OC->>P: before_agent_start(prompt)
P->>P: check prompt vs lifecycle patterns
P->>P: mark systemSession if heartbeat/no_reply/lifecycle_event
P->>W: POST sessions/init
OC->>P: tool_result_persist(toolName)
alt toolName in EXCLUDED_LIFECYCLE_EVENT_TYPES
P-->>OC: skip observation
else content matches EXCLUDED_LIFECYCLE_CONTENT_PATTERNS
P-->>OC: skip observation
else workspaceDir missing
P->>P: fallback to home dir
P->>W: POST sessions/observations
else
P->>W: POST sessions/observations
end
OC->>P: agent_end
alt contentSessionId in systemSessionIds
P->>P: delete from systemSessionIds
P->>W: scheduleSessionComplete (no summary)
else
P->>W: POST sessions/summary
P->>W: scheduleSessionComplete
end
Reviews (1): Last reviewed commit: "fix(plugin-lifecycle): OpenCode Zod sche..." | Re-trigger Greptile |
| if (promptLower === "heartbeat" || promptLower === "no_reply" || promptLower === "lifecycle_event") { | ||
| systemSessionIds.add(contentSessionId); | ||
| } |
There was a problem hiding this comment.
Incomplete lifecycle session detection
The before_agent_start check only covers 3 of the 5 types defined in EXCLUDED_LIFECYCLE_EVENT_TYPES. Sessions arriving with a prompt of "user_re_engagement" or "session_start" will not be added to systemSessionIds, so they will proceed through normal summarization at agent_end despite being lifecycle noise — the exact problem this fix aims to prevent.
| if (promptLower === "heartbeat" || promptLower === "no_reply" || promptLower === "lifecycle_event") { | |
| systemSessionIds.add(contentSessionId); | |
| } | |
| if ( | |
| promptLower === "heartbeat" || | |
| promptLower === "no_reply" || | |
| promptLower === "no reply" || | |
| promptLower === "lifecycle_event" || | |
| promptLower === "user_re_engagement" || | |
| promptLower === "session_start" | |
| ) { | |
| systemSessionIds.add(contentSessionId); | |
| } |
| const query = hasTrailingLimit ? pieces.slice(0, -1).join(" ") : raw; | ||
|
|
||
| // Scope search to the current agent's project for per-agent isolation | ||
| const agentProject = baseProjectName; |
There was a problem hiding this comment.
Slash-command project scoping uses base project, not agent-specific project
The PR description states these commands add "per-agent isolation", but both /claude-mem-search and /claude-mem-timeline hardcode agentProject = baseProjectName instead of calling getProjectName(ctx). When invoked inside an agent that has an agentId, this returns results from the shared base project (e.g. "openclaw") rather than the agent-scoped one (e.g. "openclaw-abc123"), leaking cross-agent observations — the same problem getContextForPrompt was fixed to avoid.
| const agentProject = baseProjectName; | |
| const agentProject = getProjectName(ctx); |
|
|
||
| const query = parts.join(" "); | ||
| // Scope timeline to the current agent's project for per-agent isolation | ||
| const agentProject = baseProjectName; |
There was a problem hiding this comment.
Same base-project scoping issue in
/claude-mem-timeline
Same concern as /claude-mem-search above: agentProject = baseProjectName should be getProjectName(ctx) to honour per-agent isolation for timeline lookups.
| const agentProject = baseProjectName; | |
| const agentProject = getProjectName(ctx); |
| if (!workspaceDir) { | ||
| api.logger.warn(`[claude-mem] Skipping observation persist because workspaceDir is unavailable: session=${canonicalKey} tool=${toolName}`); | ||
| return; | ||
| const homeDir = typeof process !== "undefined" && process.env?.HOME ? process.env.HOME : "/tmp"; | ||
| api.logger.warn(`[claude-mem] workspaceDir unavailable for session=${canonicalKey} tool=${toolName}, falling back to ${homeDir}`); | ||
| workspaceDir = homeDir; |
There was a problem hiding this comment.
/tmp fallback may silently mix observations across agents
When process.env.HOME is unset (e.g. some CI/containerised environments), every agent falls back to "/tmp" as workspaceDir. Observations from all agents are then bucketed under the same path, defeating per-agent isolation. Consider falling back to an agent- or session-scoped sub-directory (e.g. path.join("/tmp", canonicalKey)) or surfacing a clearer warning.
…h commands, safe fallback
1. Expand before_agent_start lifecycle detection to check all EXCLUDED_LIFECYCLE_EVENT_TYPES
and content patterns instead of only 3 hardcoded strings (covers user_re_engagement,
session_start in addition to heartbeat, no_reply, lifecycle_event)
2-3. Fix /claude-mem-search and /claude-mem-timeline to use agent-specific project names
(e.g., openclaw-<agentId>) instead of the base project name, via session→project
tracking populated by before_agent_start
4. Use unique /tmp/claude-mem-<sessionId> subdirectory when HOME is unset to prevent
cross-agent observation mixing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code ReviewOverviewThis PR addresses five distinct issues: OpenCode Zod schema compatibility, OpenClaw installation ordering, agent context isolation, session drift/key canonicalization, and lifecycle event noise filtering. The changes are well-scoped and the PR description is thorough. Bugs / Issues1. Memory leak —
// cleanup handler (~line 1044) — add this:
sessionProjectNames.clear();2. Race condition —
Consider initializing 3. Stale session aliases are not evicted on key change In Minor Issues4. Redundant dual case-check for EXCLUDED_LIFECYCLE_EVENT_TYPES.has(promptLower) ||
EXCLUDED_LIFECYCLE_EVENT_TYPES.has(promptText.trim()) ||The set contains both lowercase entries ( 5. The fallback to 6. In a Bun/Node runtime, Positives
SummaryTwo concrete bugs worth fixing before merge: the |
|
Closing to start fresh from main — will redo fixes isolated in Docker container. |
Summary
z.string(), not{type: "string"})plugins.allowwithclaude-memBEFORE callingplugins install, solving the chicken-and-egg where config validation rejects the install commandgetContextForPromptto agent-specific project only (not shared base), add project filtering to/claude-mem-searchand/claude-mem-timelinecommandsctx.sessionKeyas canonical key over stale alias mappings; fall back to$HOMEwhenworkspaceDiris unavailable instead of dropping observations; clear stale mappings on session startuser_re_engagement,session_start,heartbeat,NO_REPLY,lifecycle_event) and content patterns; track system sessions and exempt them from summarization inagent_endTest plan
NO_REPLYare filtered from observation storageCloses #1947, closes #1948, closes #1949, closes #1950, closes #1951
🤖 Generated with Claude Code