Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions plugin/opencode/agentmemory-capture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ async function observe(
hookType: string,
data: Record<string, unknown>,
): Promise<void> {
const project = sessionProjects.get(sessionId) || projectPath;
await post("/observe", {
hookType,
sessionId,
project: projectPath,
cwd: projectPath,
project,
cwd: project,
timestamp: new Date().toISOString(),
data,
});
Expand All @@ -60,6 +61,7 @@ async function observe(
let activeSessionId: string | null = null;
let pendingConfig: Record<string, unknown> | null = null;
let projectPath: string | null = null;
const sessionProjects = new Map<string, string>();
const stashedFiles = new Map<string, Set<string>>();
const seenSubtaskIds = new Map<string, Set<string>>();
const seenToolCallIds = new Map<string, Set<string>>();
Expand Down Expand Up @@ -93,6 +95,13 @@ function pruneSessionMaps(sid: string): void {
stashedFiles.delete(sid);
seenSubtaskIds.delete(sid);
seenToolCallIds.delete(sid);
sessionProjects.delete(sid);
}

async function updateSessionProject(sessionId: string, cwd: unknown): Promise<void> {
if (typeof cwd !== "string" || cwd.length === 0) return;
if (sessionProjects.get(sessionId) === cwd) return;
sessionProjects.set(sessionId, cwd);
}
Comment on lines +101 to 105

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

updateSessionProject() ignores AGENTMEMORY_PROJECT, unlike every other update site.

session.created (193-197), session.updated (257-259), and assistant message.updated (320-323) all prefer process.env.AGENTMEMORY_PROJECT before the discovered path. updateSessionProject() — the only function invoked by the new shell.env handler (Lines 604-608) — writes cwd unconditionally, so a shell command's directory can silently clobber a user's explicit AGENTMEMORY_PROJECT override. This reintroduces the "wrong project directory picked up" failure mode this PR is fixing.

🐛 Proposed fix
 async function updateSessionProject(sessionId: string, cwd: unknown): Promise<void> {
   if (typeof cwd !== "string" || cwd.length === 0) return;
-  if (sessionProjects.get(sessionId) === cwd) return;
-  sessionProjects.set(sessionId, cwd);
+  const resolved = process.env.AGENTMEMORY_PROJECT || cwd;
+  if (sessionProjects.get(sessionId) === resolved) return;
+  sessionProjects.set(sessionId, resolved);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async function updateSessionProject(sessionId: string, cwd: unknown): Promise<void> {
if (typeof cwd !== "string" || cwd.length === 0) return;
if (sessionProjects.get(sessionId) === cwd) return;
sessionProjects.set(sessionId, cwd);
}
async function updateSessionProject(sessionId: string, cwd: unknown): Promise<void> {
if (typeof cwd !== "string" || cwd.length === 0) return;
const resolved = process.env.AGENTMEMORY_PROJECT || cwd;
if (sessionProjects.get(sessionId) === resolved) return;
sessionProjects.set(sessionId, resolved);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/opencode/agentmemory-capture.ts` around lines 101 - 105,
updateSessionProject() is bypassing the AGENTMEMORY_PROJECT override and can
overwrite the chosen project with shell.cwd. Update updateSessionProject() in
agentmemory-capture.ts to resolve the project path the same way session.created,
session.updated, and message.updated do: prefer process.env.AGENTMEMORY_PROJECT
when present, otherwise fall back to cwd. Keep the existing guards and
sessionProjects.set logic, and make sure the shell.env handler path uses this
consistent precedence.


function safeSlice(v: unknown, max: number): string {
Expand Down Expand Up @@ -168,7 +177,8 @@ function extractErrorMessage(err: unknown): string {
}

export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
projectPath = ctx.worktree || ctx.project?.id || process.cwd();
const currentDirectory = (ctx as any).directory || process.cwd();
projectPath = process.env.AGENTMEMORY_PROJECT || currentDirectory || ctx.project?.id || ctx.worktree || process.cwd();
Comment on lines +180 to +181

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Broken fallback precedence — ctx.project?.id/ctx.worktree become unreachable dead code.

currentDirectory already falls back to process.cwd() when ctx.directory is absent, so it's always truthy. That means in line 181, ctx.project?.id || ctx.worktree can never be evaluated — the intended precedence order (directory → project id → worktree → cwd) is silently broken.

🐛 Proposed fix
-  const currentDirectory = (ctx as any).directory || process.cwd();
-  projectPath = process.env.AGENTMEMORY_PROJECT || currentDirectory || ctx.project?.id || ctx.worktree || process.cwd();
+  projectPath = process.env.AGENTMEMORY_PROJECT || (ctx as any).directory || ctx.project?.id || ctx.worktree || process.cwd();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const currentDirectory = (ctx as any).directory || process.cwd();
projectPath = process.env.AGENTMEMORY_PROJECT || currentDirectory || ctx.project?.id || ctx.worktree || process.cwd();
projectPath = process.env.AGENTMEMORY_PROJECT || (ctx as any).directory || ctx.project?.id || ctx.worktree || process.cwd();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/opencode/agentmemory-capture.ts` around lines 180 - 181, The fallback
chain in agentmemory-capture.ts is broken because currentDirectory always
resolves to a truthy value, making ctx.project?.id and ctx.worktree unreachable
in the projectPath assignment. Update the precedence logic in the agentmemory
capture flow so the order is truly directory → project id → worktree → cwd, and
avoid pre-falling back currentDirectory to process.cwd() before the full chain
is evaluated.


return {
event: async ({ event }) => {
Expand All @@ -180,6 +190,11 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
const info = props.info as Record<string, unknown> | undefined;
activeSessionId = (info?.id as string) || props.sessionID || null;
if (!activeSessionId) return;
const sessionProject = process.env.AGENTMEMORY_PROJECT
|| (typeof info?.directory === "string" && info.directory.length > 0 ? info.directory : "")
|| projectPath
|| process.cwd();
sessionProjects.set(activeSessionId, sessionProject);
stashedFiles.set(activeSessionId, new Set());
seenSubtaskIds.delete(activeSessionId);
seenToolCallIds.delete(activeSessionId);
Expand All @@ -190,11 +205,11 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
const sessionId = activeSessionId;
const startResult = await postJson("/session/start", {
sessionId,
title: info?.title ?? null,
title: info?.title ?? sessionProject,
parentID: info?.parentID ?? null,
version: info?.version ?? null,
project: projectPath,
cwd: projectPath,
project: sessionProject,
cwd: sessionProject,
Comment on lines +208 to +212

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Session title now defaults to a raw filesystem path.

title: info?.title ?? sessionProject means, absent a real title, the dashboard will display an absolute local path (potentially containing a username, e.g. /Users/jdoe/...) as the session title instead of a human-readable placeholder or null.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/opencode/agentmemory-capture.ts` around lines 208 - 212, The session
title fallback in the agent memory capture flow is using a raw filesystem path,
which can expose local details in the dashboard. Update the title assignment in
agentmemory-capture’s session payload so it does not default to sessionProject;
instead, keep a human-readable placeholder or null when info?.title is absent,
while leaving the rest of the captured fields unchanged.

});
// cache the context returned at session/start so the
// chat.system.transform hook injects it without a second fetch.
Expand Down Expand Up @@ -239,6 +254,9 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
const info = props.info as Record<string, unknown> | undefined;
const sid = (info?.id as string) || props.sessionID || activeSessionId;
if (!sid) return;
if (typeof info?.directory === "string" && info.directory.length > 0) {
sessionProjects.set(sid, process.env.AGENTMEMORY_PROJECT || info.directory);
}
await observe(sid, "session_updated", {
title: info?.title ?? null,
parentID: info?.parentID ?? null,
Expand Down Expand Up @@ -299,6 +317,10 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
if (info.role === "assistant") {
const sid = props.sessionID || (info.sessionID as string) || activeSessionId;
if (!sid) return;
const cwd = (info as any).path?.cwd;
if (typeof cwd === "string" && cwd.length > 0) {
sessionProjects.set(sid, process.env.AGENTMEMORY_PROJECT || cwd);
}
const tokens = info.tokens as Record<string, unknown> | undefined;
const error = info.error ? extractErrorMessage(info.error) : null;
await observe(sid, "assistant_message", {
Expand Down Expand Up @@ -579,6 +601,11 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
});
},

// ── shell.env ──
"shell.env": async (input) => {
if (input.sessionID) await updateSessionProject(input.sessionID, input.cwd);
},

// ── tool.execute.before ──
"tool.execute.before": async (input, output) => {
if (!FILE_TOOLS.has(input.tool)) return;
Expand Down Expand Up @@ -612,7 +639,7 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
if (typeof ctx !== "string" || ctx.length === 0) {
const result = await postJson("/context", {
sessionId: sid,
project: projectPath,
project: sessionProjects.get(sid) || projectPath,
});
ctx = (result as any)?.context;
} else {
Expand Down Expand Up @@ -650,7 +677,7 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {

const result = await postJson("/context", {
sessionId: sid,
project: projectPath,
project: sessionProjects.get(sid) || projectPath,
});
const ctx = (result as any)?.context;
if (typeof ctx === "string" && ctx.length > 0) {
Expand Down