-
Notifications
You must be signed in to change notification settings - Fork 604
feat(setup): add session end lifecycle for OpenCode, Gemini, and Codex #527
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c745112
ef02c59
c775989
a568354
49097aa
6e52fd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -89,7 +89,10 @@ Also search memory PROACTIVELY when: | |||||||||||||||||||||||||||||||||||||||||
| ### SESSION CLOSE PROTOCOL (mandatory) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Before ending a session or saying "done" / "that's it", you MUST: | ||||||||||||||||||||||||||||||||||||||||||
| 1. Call \`mem_session_summary\` with this structure: | ||||||||||||||||||||||||||||||||||||||||||
| 1. Call \`mem_session_summary\` with the structure below. | ||||||||||||||||||||||||||||||||||||||||||
| 2. Call \`mem_session_end\` with the active session id and an optional summary of work completed. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| \`mem_session_summary\` structure: | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| ## Goal | ||||||||||||||||||||||||||||||||||||||||||
| [What we were working on this session] | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -133,6 +136,9 @@ async function engramFetch( | |||||||||||||||||||||||||||||||||||||||||
| headers: opts.body ? { "Content-Type": "application/json" } : undefined, | ||||||||||||||||||||||||||||||||||||||||||
| body: opts.body ? JSON.stringify(opts.body) : undefined, | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
| // Treat non-2xx as failure so callers can avoid false-positive side effects | ||||||||||||||||||||||||||||||||||||||||||
| // (e.g. marking a session known when POST /sessions did not succeed). | ||||||||||||||||||||||||||||||||||||||||||
| if (!res.ok) return null | ||||||||||||||||||||||||||||||||||||||||||
| return await res.json() | ||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||
| // Engram server not running — silently fail | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -225,15 +231,18 @@ export const Engram: Plugin = async (ctx) => { | |||||||||||||||||||||||||||||||||||||||||
| if (!sessionId || knownSessions.has(sessionId)) return | ||||||||||||||||||||||||||||||||||||||||||
| // Do not register sub-agent sessions in Engram (issue #116). | ||||||||||||||||||||||||||||||||||||||||||
| if (subAgentSessions.has(sessionId)) return | ||||||||||||||||||||||||||||||||||||||||||
| knownSessions.add(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| await engramFetch("/sessions", { | ||||||||||||||||||||||||||||||||||||||||||
| const created = await engramFetch("/sessions", { | ||||||||||||||||||||||||||||||||||||||||||
| method: "POST", | ||||||||||||||||||||||||||||||||||||||||||
| body: { | ||||||||||||||||||||||||||||||||||||||||||
| id: sessionId, | ||||||||||||||||||||||||||||||||||||||||||
| project, | ||||||||||||||||||||||||||||||||||||||||||
| directory: ctx.directory, | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||
| // Only mark known after confirmed create so failed requests can retry. | ||||||||||||||||||||||||||||||||||||||||||
| if (created != null) { | ||||||||||||||||||||||||||||||||||||||||||
| knownSessions.add(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| // Try to start engram server if not running | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -316,8 +325,24 @@ export const Engram: Plugin = async (ctx) => { | |||||||||||||||||||||||||||||||||||||||||
| const info = (event.properties as any)?.info | ||||||||||||||||||||||||||||||||||||||||||
| const sessionId = info?.id | ||||||||||||||||||||||||||||||||||||||||||
| if (sessionId) { | ||||||||||||||||||||||||||||||||||||||||||
| if (knownSessions.has(sessionId)) { | ||||||||||||||||||||||||||||||||||||||||||
| // session.deleted fires once — retry once inline, and only clear | ||||||||||||||||||||||||||||||||||||||||||
| // knownSessions after a confirmed /end (do not pretend it closed). | ||||||||||||||||||||||||||||||||||||||||||
| let ended = await engramFetch( | ||||||||||||||||||||||||||||||||||||||||||
| `/sessions/${encodeURIComponent(sessionId)}/end`, | ||||||||||||||||||||||||||||||||||||||||||
| { method: "POST" } | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| if (ended == null) { | ||||||||||||||||||||||||||||||||||||||||||
| ended = await engramFetch( | ||||||||||||||||||||||||||||||||||||||||||
| `/sessions/${encodeURIComponent(sessionId)}/end`, | ||||||||||||||||||||||||||||||||||||||||||
| { method: "POST" } | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| if (ended != null) { | ||||||||||||||||||||||||||||||||||||||||||
| knownSessions.delete(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+329
to
+343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Remove the inline retry loop to prevent memory leaks and keep the adapter thin. Because
♻️ Proposed fix- // session.deleted fires once — retry once inline, and only clear
- // knownSessions after a confirmed /end (do not pretend it closed).
- let ended = await engramFetch(
- `/sessions/${encodeURIComponent(sessionId)}/end`,
- { method: "POST" }
- )
- if (ended == null) {
- ended = await engramFetch(
- `/sessions/${encodeURIComponent(sessionId)}/end`,
- { method: "POST" }
- )
- }
- if (ended != null) {
- knownSessions.delete(sessionId)
- }
+ await engramFetch(
+ `/sessions/${encodeURIComponent(sessionId)}/end`,
+ { method: "POST" }
+ )
+ knownSessions.delete(sessionId)📝 Committable suggestion
Suggested change
📍 Affects 2 files
🤖 Prompt for AI AgentsSources: Coding guidelines, Path instructions |
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| toolCounts.delete(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| knownSessions.delete(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| subAgentSessions.delete(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| lastNudgeTime.delete(sessionId) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
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
Handle empty JSON responses gracefully.
If the API returns a successful response (e.g.,
204 No Contentor200 OKwith an empty body),res.json()will throw aSyntaxError. This error is caught by the blanketcatchblock, causing the function to incorrectly returnnulland treat the successful request as a failure. Both files contain the exact same vulnerable fetch implementation.internal/setup/plugins/opencode/engram.ts#L139-L142: Parse the response text first to safely handle empty bodies.plugin/opencode/engram.ts#L139-L142: Parse the response text first to safely handle empty bodies.🛠️ Proposed fix
📝 Committable suggestion
📍 Affects 2 files
internal/setup/plugins/opencode/engram.ts#L139-L142(this comment)plugin/opencode/engram.ts#L139-L142🤖 Prompt for AI Agents