@scitrera/memorylayer-opencode-plugin 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +178 -0
  2. package/dist/src/hooks/event.d.ts +15 -0
  3. package/dist/src/hooks/event.d.ts.map +1 -0
  4. package/dist/src/hooks/event.js +53 -0
  5. package/dist/src/hooks/event.js.map +1 -0
  6. package/dist/src/hooks/message.d.ts +20 -0
  7. package/dist/src/hooks/message.d.ts.map +1 -0
  8. package/dist/src/hooks/message.js +128 -0
  9. package/dist/src/hooks/message.js.map +1 -0
  10. package/dist/src/hooks/session.d.ts +20 -0
  11. package/dist/src/hooks/session.d.ts.map +1 -0
  12. package/dist/src/hooks/session.js +121 -0
  13. package/dist/src/hooks/session.js.map +1 -0
  14. package/dist/src/hooks/tool.d.ts +23 -0
  15. package/dist/src/hooks/tool.d.ts.map +1 -0
  16. package/dist/src/hooks/tool.js +168 -0
  17. package/dist/src/hooks/tool.js.map +1 -0
  18. package/dist/src/index.d.ts +31 -0
  19. package/dist/src/index.d.ts.map +1 -0
  20. package/dist/src/index.js +176 -0
  21. package/dist/src/index.js.map +1 -0
  22. package/dist/src/shared/client.d.ts +27 -0
  23. package/dist/src/shared/client.d.ts.map +1 -0
  24. package/dist/src/shared/client.js +69 -0
  25. package/dist/src/shared/client.js.map +1 -0
  26. package/dist/src/shared/formatters.d.ts +33 -0
  27. package/dist/src/shared/formatters.d.ts.map +1 -0
  28. package/dist/src/shared/formatters.js +187 -0
  29. package/dist/src/shared/formatters.js.map +1 -0
  30. package/dist/src/shared/observation.d.ts +59 -0
  31. package/dist/src/shared/observation.d.ts.map +1 -0
  32. package/dist/src/shared/observation.js +406 -0
  33. package/dist/src/shared/observation.js.map +1 -0
  34. package/dist/src/shared/state.d.ts +66 -0
  35. package/dist/src/shared/state.d.ts.map +1 -0
  36. package/dist/src/shared/state.js +144 -0
  37. package/dist/src/shared/state.js.map +1 -0
  38. package/dist/src/shared/types.d.ts +141 -0
  39. package/dist/src/shared/types.d.ts.map +1 -0
  40. package/dist/src/shared/types.js +9 -0
  41. package/dist/src/shared/types.js.map +1 -0
  42. package/opencode.example.json +15 -0
  43. package/package.json +52 -0
  44. package/src/commands/recall.md +55 -0
  45. package/src/commands/remember.md +37 -0
  46. package/src/commands/setup.md +47 -0
  47. package/src/commands/status.md +59 -0
  48. package/src/hooks/event.ts +63 -0
  49. package/src/hooks/message.ts +147 -0
  50. package/src/hooks/session.ts +127 -0
  51. package/src/hooks/tool.ts +207 -0
  52. package/src/index.ts +197 -0
  53. package/src/shared/client.ts +73 -0
  54. package/src/shared/formatters.ts +237 -0
  55. package/src/shared/observation.ts +447 -0
  56. package/src/shared/state.ts +159 -0
  57. package/src/shared/types.ts +144 -0
  58. package/tsconfig.json +24 -0
package/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # MemoryLayer OpenCode Plugin
2
+
3
+ Persistent memory for [OpenCode](https://github.com/sst/opencode) sessions. Gives your AI coding agent long-term memory that survives context compaction and persists across sessions.
4
+
5
+ ## What It Does
6
+
7
+ This plugin integrates [MemoryLayer](https://memorylayer.ai) into OpenCode via two mechanisms:
8
+
9
+ 1. **MCP Tools** (25 default / 38 in `full`) — The MemoryLayer MCP server gives the LLM direct access to remember, recall, reflect, manage sessions, drive the context sandbox / RLM, and work with chat threads, skills, and MCP-server registry entries.
10
+
11
+ 2. **Proactive Hooks** — The plugin automatically:
12
+ - Injects workspace briefing and user directives at session start
13
+ - Recalls relevant memories when you ask preference/convention questions
14
+ - Captures tool observations (file edits, searches, commands) as working memory
15
+ - Commits working memory before context compaction
16
+ - Cleans up sessions on exit
17
+
18
+ ## Quick Start
19
+
20
+ ### 1. Install the MemoryLayer server
21
+
22
+ ```bash
23
+ pip install memorylayer-server
24
+ memorylayer serve
25
+ ```
26
+
27
+ Or with Docker:
28
+
29
+ ```bash
30
+ docker run -d -p 61001:61001 -v memorylayer-data:/data scitrera/memorylayer-server
31
+ ```
32
+
33
+ ### 2. Configure OpenCode
34
+
35
+ Add to your `opencode.json`:
36
+
37
+ ```json
38
+ {
39
+ "mcp": {
40
+ "memorylayer": {
41
+ "type": "local",
42
+ "command": ["npx", "@scitrera/memorylayer-mcp-server"],
43
+ "environment": {
44
+ "MEMORYLAYER_URL": "{env:MEMORYLAYER_URL}",
45
+ "MEMORYLAYER_API_KEY": "{env:MEMORYLAYER_API_KEY}"
46
+ },
47
+ "enabled": true
48
+ }
49
+ },
50
+ "plugin": ["@scitrera/memorylayer-opencode-plugin"]
51
+ }
52
+ ```
53
+
54
+ ### 3. Set environment variables (optional)
55
+
56
+ ```bash
57
+ export MEMORYLAYER_URL=http://localhost:61001 # default
58
+ export MEMORYLAYER_API_KEY=your-key # optional, for authenticated servers
59
+ ```
60
+
61
+ ### 4. Verify
62
+
63
+ Start OpenCode and run `/memorylayer-status` to check the connection.
64
+
65
+ ## Slash Commands
66
+
67
+ | Command | Description |
68
+ |---------|-------------|
69
+ | `/memorylayer-remember <content>` | Store a memory with auto-detected type and importance |
70
+ | `/memorylayer-recall <query>` | Search memories by semantic query |
71
+ | `/memorylayer-status` | Check connection status and workspace info |
72
+ | `/memorylayer-setup` | Guided setup and verification |
73
+
74
+ ## How Hooks Work
75
+
76
+ ### Session Start (`experimental.chat.system.transform`)
77
+ On first interaction, loads workspace briefing, user directives, and any existing sandbox state into the system prompt.
78
+
79
+ ### User Messages (`chat.message`)
80
+ Detects 5 pattern categories in user messages and performs targeted recall:
81
+ - **Preference**: "which X should we use", "what's our convention"
82
+ - **Recall**: "remember", "what did we", "remind me"
83
+ - **Analysis**: "review", "analyze", "status"
84
+ - **Implementation**: "implement", "build", "fix"
85
+ - **Error**: "error", "bug", "broken"
86
+
87
+ ### Tool Execution (`tool.execute.before` / `tool.execute.after`)
88
+ - **Before**: Injects recalled context for edit/write and task/delegation tools
89
+ - **After**: Silently captures observations (files, facts, concepts, intent) as working memory
90
+
91
+ ### Context Compaction (`experimental.session.compacting`)
92
+ Commits working memory to long-term storage and checkpoints server-side sandbox state before the context window is trimmed.
93
+
94
+ ### Shell Environment (`shell.env`)
95
+ Propagates `MEMORYLAYER_URL` and `MEMORYLAYER_API_KEY` to shell commands.
96
+
97
+ ## Configuration
98
+
99
+ ### Environment Variables
100
+
101
+ | Variable | Default | Description |
102
+ |----------|---------|-------------|
103
+ | `MEMORYLAYER_URL` | `http://localhost:61001` | MemoryLayer server URL |
104
+ | `MEMORYLAYER_API_KEY` | — | API key (optional for local servers) |
105
+ | `MEMORYLAYER_WORKSPACE_ID` | auto-detected | Workspace identifier (auto-detected from git repo name) |
106
+
107
+ ### Workspace Auto-Detection
108
+
109
+ The workspace ID is automatically detected from:
110
+ 1. Git remote origin repository name
111
+ 2. Git root directory name
112
+ 3. Current working directory name
113
+
114
+ Override with the `MEMORYLAYER_WORKSPACE_ID` environment variable.
115
+
116
+ ## Memory Types
117
+
118
+ | Type | Description | Importance |
119
+ |------|-------------|------------|
120
+ | `semantic` | Facts, concepts, knowledge | 0.5-0.9 |
121
+ | `procedural` | How-to, solutions, patterns | 0.5-0.8 |
122
+ | `episodic` | Events, what happened | 0.5-0.7 |
123
+ | `working` | Current task context (auto-expires) | 0.3-0.6 |
124
+
125
+ ### Common Subtypes
126
+
127
+ `directive`, `decision`, `fix`, `solution`, `code_pattern`, `error`, `workflow`, `preference`, `problem`
128
+
129
+ ## Architecture
130
+
131
+ ```
132
+ OpenCode
133
+ ├── MCP Server (@scitrera/memorylayer-mcp-server)
134
+ │ └── 25 memory tools by default (38 in `full` profile)
135
+
136
+ ├── Plugin Hooks (@scitrera/memorylayer-opencode-plugin)
137
+ │ ├── system.transform → session briefing injection
138
+ │ ├── chat.message → pattern-based recall
139
+ │ ├── tool.before → pre-tool context injection
140
+ │ ├── tool.after → observation capture
141
+ │ ├── session.compacting → working memory commit
142
+ │ └── shell.env → env var propagation
143
+
144
+ └── MemoryLayer Server (memorylayer-server)
145
+ ├── Memory storage (SQLite + vector search)
146
+ ├── Knowledge graph (associations)
147
+ ├── Working memory (sessions)
148
+ └── Context sandbox (Python execution)
149
+ ```
150
+
151
+ ## Troubleshooting
152
+
153
+ ### Server not reachable
154
+ ```bash
155
+ # Check if server is running
156
+ curl http://localhost:61001/health
157
+
158
+ # Start the server
159
+ memorylayer serve
160
+
161
+ # Or with Docker
162
+ docker run -d -p 61001:61001 -v memorylayer-data:/data scitrera/memorylayer-server
163
+ ```
164
+
165
+ ### MCP tools not available
166
+ Verify `opencode.json` has the `mcp.memorylayer` configuration. Restart OpenCode after configuration changes.
167
+
168
+ ### Plugin not loading
169
+ Ensure the plugin is listed in `opencode.json`:
170
+ ```json
171
+ {
172
+ "plugin": ["@scitrera/memorylayer-opencode-plugin"]
173
+ }
174
+ ```
175
+
176
+ ## License
177
+
178
+ Apache-2.0
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Event hook for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Handles context compaction by preserving critical memory state
5
+ * and committing working memory before context is lost.
6
+ */
7
+ /**
8
+ * Handle session compacting — commit working memory and checkpoint sandbox.
9
+ *
10
+ * OpenCode's `experimental.session.compacting` hook is the equivalent
11
+ * of Claude Code's PreCompact. We commit working memory and return
12
+ * context strings that survive the compaction.
13
+ */
14
+ export declare function handleCompacting(_sessionID: string): Promise<string[]>;
15
+ //# sourceMappingURL=event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../../src/hooks/event.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA8C5E"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Event hook for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Handles context compaction by preserving critical memory state
5
+ * and committing working memory before context is lost.
6
+ */
7
+ import { getClient, checkHealth } from "../shared/client.js";
8
+ /**
9
+ * Handle session compacting — commit working memory and checkpoint sandbox.
10
+ *
11
+ * OpenCode's `experimental.session.compacting` hook is the equivalent
12
+ * of Claude Code's PreCompact. We commit working memory and return
13
+ * context strings that survive the compaction.
14
+ */
15
+ export async function handleCompacting(_sessionID) {
16
+ const context = [];
17
+ const healthy = await checkHealth();
18
+ if (!healthy)
19
+ return context;
20
+ const client = getClient();
21
+ const clientSessionId = client.getSessionId();
22
+ if (clientSessionId) {
23
+ // Commit working memory to long-term storage before compaction
24
+ try {
25
+ await client.commitSession(clientSessionId, { importance_threshold: 0.3 });
26
+ context.push("[MemoryLayer] Working memory committed to long-term storage before compaction. " +
27
+ "Use `memory_recall` to retrieve prior context. " +
28
+ "Use `memory_context_inspect` to check server-side sandbox variables.");
29
+ }
30
+ catch {
31
+ // Commit may fail, but we should still try to preserve context
32
+ }
33
+ // Checkpoint sandbox state if active
34
+ try {
35
+ const status = await client.contextStatus();
36
+ if (status.exists && (status.variable_count ?? 0) > 0) {
37
+ await client.contextCheckpoint();
38
+ context.push("[MemoryLayer] Server-side sandbox state checkpointed. " +
39
+ "Variables persist across compaction — use `memory_context_inspect` to re-orient.");
40
+ }
41
+ }
42
+ catch {
43
+ // Sandbox checkpoint is best-effort
44
+ }
45
+ }
46
+ // Always include recovery instructions
47
+ if (context.length === 0) {
48
+ context.push("[MemoryLayer] Context compaction occurred. Use `memory_recall` to retrieve " +
49
+ "prior context and `memory_context_inspect` to check sandbox state.");
50
+ }
51
+ return context;
52
+ }
53
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../../src/hooks/event.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAkB;IACvD,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,OAAO,OAAO,CAAC;IAE7B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,eAAe,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;IAE9C,IAAI,eAAe,EAAE,CAAC;QACpB,+DAA+D;QAC/D,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,aAAa,CAAC,eAAe,EAAE,EAAE,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;YAC3E,OAAO,CAAC,IAAI,CACV,iFAAiF;gBACjF,iDAAiD;gBACjD,sEAAsE,CACvE,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,+DAA+D;QACjE,CAAC;QAED,qCAAqC;QACrC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,EAAmD,CAAC;YAC7F,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtD,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CACV,wDAAwD;oBACxD,kFAAkF,CACnF,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;QACtC,CAAC;IACH,CAAC;IAED,uCAAuC;IACvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CACV,6EAA6E;YAC7E,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * User message hook for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Detects patterns in user messages and performs intelligent recall
5
+ * to inject relevant memories into the conversation context.
6
+ */
7
+ /**
8
+ * Extract user message text from message parts.
9
+ */
10
+ export declare function extractMessageText(parts: Array<{
11
+ type: string;
12
+ text?: string;
13
+ }>): string;
14
+ /**
15
+ * Handle a new user message — detect patterns and recall relevant memories.
16
+ *
17
+ * Returns additional context text to inject, or null if no recall needed.
18
+ */
19
+ export declare function handleUserMessage(messageText: string): Promise<string | null>;
20
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/hooks/message.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmFH;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAKxF;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2CnF"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * User message hook for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Detects patterns in user messages and performs intelligent recall
5
+ * to inject relevant memories into the conversation context.
6
+ */
7
+ import { getClient, checkHealth } from "../shared/client.js";
8
+ import { formatRecallResult } from "../shared/formatters.js";
9
+ import { wasRecallDoneThisTurn, markRecallDone, resetRecallStatus, setCurrentTopic, setCurrentPrompt, } from "../shared/state.js";
10
+ /** Pattern categories and their recall queries */
11
+ const PATTERNS = {
12
+ preference: {
13
+ regex: /\b(which\s+\w+\s+should|should\s+we\s+use|what\s+do\s+we\s+use|how\s+do\s+we|what'?s\s+our|preferred|convention|do\s+we\s+have\s+a|what'?s\s+the\s+(default|standard|preferred))\b/i,
14
+ queries: ["directive", "preference", "convention"],
15
+ },
16
+ recall: {
17
+ regex: /\b(remember|recall|what did we|how did we|remind me|what was the|what were the|what do you (know|remember))\b/i,
18
+ queries: ["context", "history", "decision"],
19
+ },
20
+ analysis: {
21
+ regex: /\b(review|assess|analyze|evaluate|audit|investigate|explore|research|compare|status|state of|readiness|gap analysis)\b/i,
22
+ queries: ["status", "assessment", "gaps", "problems"],
23
+ },
24
+ implementation: {
25
+ regex: /\b(implement|build|create|add|fix|refactor|update|change|modify)\b/i,
26
+ queries: ["patterns", "solutions", "issues"],
27
+ },
28
+ error: {
29
+ regex: /\b(error|bug|issue|broken|failing|crash|exception|doesn't work|not working)\b/i,
30
+ queries: ["fix", "error", "solution"],
31
+ },
32
+ };
33
+ /**
34
+ * Detect which pattern category matches the prompt
35
+ */
36
+ function detectPattern(prompt) {
37
+ for (const [category, config] of Object.entries(PATTERNS)) {
38
+ if (config.regex.test(prompt)) {
39
+ return category;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+ /**
45
+ * Extract key terms from the prompt for recall query
46
+ */
47
+ function extractKeyTerms(prompt) {
48
+ const stopWords = new Set([
49
+ "the", "a", "an", "is", "are", "was", "were", "be", "been",
50
+ "have", "has", "had", "do", "does", "did", "will", "would",
51
+ "could", "should", "may", "might", "must", "can", "this",
52
+ "that", "these", "those", "i", "you", "we", "they", "it",
53
+ "my", "your", "our", "their", "its", "please", "help", "me",
54
+ "want", "need", "like", "to", "for", "with", "on", "in", "at",
55
+ ]);
56
+ const words = prompt.toLowerCase()
57
+ .replace(/[^\w\s]/g, " ")
58
+ .split(/\s+/)
59
+ .filter(w => w.length > 2 && !stopWords.has(w));
60
+ return words.slice(0, 5).join(" ");
61
+ }
62
+ /**
63
+ * Get guidance text for each pattern type
64
+ */
65
+ function getPatternGuidance(pattern) {
66
+ const guidance = {
67
+ preference: "This is a preference/convention question. Relevant directives and decisions found:",
68
+ recall: "Recalled memories matching this request:",
69
+ analysis: "This request involves analysis/research. Relevant prior context found:",
70
+ implementation: "This request involves implementation. Relevant patterns/solutions found:",
71
+ error: "This request mentions an error/bug. Relevant prior fixes found:",
72
+ };
73
+ return guidance[pattern] || "Relevant memories found:";
74
+ }
75
+ /**
76
+ * Extract user message text from message parts.
77
+ */
78
+ export function extractMessageText(parts) {
79
+ return parts
80
+ .filter(p => p.type === "text" && p.text)
81
+ .map(p => p.text)
82
+ .join("\n");
83
+ }
84
+ /**
85
+ * Handle a new user message — detect patterns and recall relevant memories.
86
+ *
87
+ * Returns additional context text to inject, or null if no recall needed.
88
+ */
89
+ export async function handleUserMessage(messageText) {
90
+ if (!messageText)
91
+ return null;
92
+ // Reset recall status for new user turn
93
+ resetRecallStatus();
94
+ // Persist the user's prompt for intent detection in tool hooks
95
+ setCurrentPrompt(messageText);
96
+ // Skip if recall was already done this turn
97
+ if (wasRecallDoneThisTurn())
98
+ return null;
99
+ // Check server health
100
+ const healthy = await checkHealth();
101
+ if (!healthy)
102
+ return null;
103
+ // Store the user's topic for cross-hook context
104
+ const keyTerms = extractKeyTerms(messageText);
105
+ if (keyTerms) {
106
+ setCurrentTopic(keyTerms);
107
+ }
108
+ // Detect pattern category
109
+ const pattern = detectPattern(messageText);
110
+ if (!pattern)
111
+ return null;
112
+ try {
113
+ const patternQueries = PATTERNS[pattern].queries;
114
+ const query = `${keyTerms} ${patternQueries[0]}`.trim();
115
+ const client = getClient();
116
+ const result = await client.recall({ query, limit: 10 });
117
+ markRecallDone(query);
118
+ if (result.memories.length === 0)
119
+ return null;
120
+ const guidance = getPatternGuidance(pattern);
121
+ const recallOutput = formatRecallResult(result, query);
122
+ return `${guidance}\n\n${recallOutput}`;
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ }
128
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/hooks/message.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Session lifecycle hooks for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Handles session initialization (briefing, directives, sandbox state)
5
+ * and session teardown (commit working memory, end session).
6
+ */
7
+ /**
8
+ * Initialize a MemoryLayer session and return formatted context for system prompt injection.
9
+ *
10
+ * Called from the `experimental.chat.system.transform` hook on the first message
11
+ * of a session, or from the `event` hook on session start.
12
+ */
13
+ export declare function initializeSession(topic?: string): Promise<string | null>;
14
+ /**
15
+ * Finalize the MemoryLayer session — commit working memory and end session.
16
+ *
17
+ * Called when the OpenCode session ends. Best-effort: never throws.
18
+ */
19
+ export declare function finalizeSession(): Promise<void>;
20
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/hooks/session.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0D9E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CA4BrD"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Session lifecycle hooks for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Handles session initialization (briefing, directives, sandbox state)
5
+ * and session teardown (commit working memory, end session).
6
+ */
7
+ import { getClient, checkHealth } from "../shared/client.js";
8
+ import { formatSessionStart } from "../shared/formatters.js";
9
+ import { markRecallDone, resetRecallStatus, updateSessionInfo } from "../shared/state.js";
10
+ /**
11
+ * Check for existing sandbox state from a prior session or pre-compaction.
12
+ */
13
+ async function checkSandboxState(client) {
14
+ try {
15
+ const status = await client.contextStatus();
16
+ if (status.exists && (status.variable_count ?? 0) > 0) {
17
+ return await client.contextInspect({});
18
+ }
19
+ }
20
+ catch {
21
+ // Context environment may not be available
22
+ }
23
+ return null;
24
+ }
25
+ /**
26
+ * Initialize a MemoryLayer session and return formatted context for system prompt injection.
27
+ *
28
+ * Called from the `experimental.chat.system.transform` hook on the first message
29
+ * of a session, or from the `event` hook on session start.
30
+ */
31
+ export async function initializeSession(topic) {
32
+ const healthy = await checkHealth();
33
+ if (!healthy) {
34
+ return "MemoryLayer server not reachable. Memory features unavailable this session.";
35
+ }
36
+ try {
37
+ const client = getClient();
38
+ // Start server session
39
+ let sessionId;
40
+ try {
41
+ const sessionResult = await client.startSession({ ttl_seconds: 3600 });
42
+ sessionId = sessionResult.session_id;
43
+ const workspaceId = client.getWorkspaceId();
44
+ if (workspaceId) {
45
+ updateSessionInfo(workspaceId, sessionId);
46
+ }
47
+ }
48
+ catch {
49
+ // Session start failed, continue without session management
50
+ }
51
+ // Reset recall status for new session
52
+ resetRecallStatus();
53
+ // Run briefing, directive recall, and sandbox check in parallel
54
+ const [briefingResult, directiveResult, sandboxResult] = await Promise.allSettled([
55
+ client.getBriefing({ limit: 10, includeMemories: false }),
56
+ client.recall({
57
+ query: "user directives and preferences",
58
+ subtypes: ["directive", "preference"],
59
+ limit: 10,
60
+ }),
61
+ checkSandboxState(client),
62
+ ]);
63
+ const briefing = briefingResult.status === "fulfilled" ? briefingResult.value : null;
64
+ const directives = directiveResult.status === "fulfilled"
65
+ ? directiveResult.value.memories
66
+ : [];
67
+ const sandboxState = sandboxResult.status === "fulfilled" ? sandboxResult.value : null;
68
+ // If there's a topic, recall for it too
69
+ let topicRecall = null;
70
+ if (topic) {
71
+ try {
72
+ topicRecall = await client.recall({ query: topic, limit: 10, detail_level: "abstract" });
73
+ markRecallDone(topic);
74
+ }
75
+ catch {
76
+ // Topic recall is optional
77
+ }
78
+ }
79
+ return formatSessionStart(briefing, directives, topicRecall, topic, sandboxState);
80
+ }
81
+ catch (error) {
82
+ return `MemoryLayer session error: ${error instanceof Error ? error.message : error}`;
83
+ }
84
+ }
85
+ /**
86
+ * Finalize the MemoryLayer session — commit working memory and end session.
87
+ *
88
+ * Called when the OpenCode session ends. Best-effort: never throws.
89
+ */
90
+ export async function finalizeSession() {
91
+ const healthy = await checkHealth();
92
+ if (!healthy)
93
+ return;
94
+ const client = getClient();
95
+ const sessionId = client.getSessionId();
96
+ if (!sessionId)
97
+ return;
98
+ try {
99
+ // Commit working memory to long-term storage
100
+ try {
101
+ await client.commitSession(sessionId, { importance_threshold: 0.5 });
102
+ }
103
+ catch {
104
+ // Commit may fail if session expired
105
+ }
106
+ // End the server session
107
+ try {
108
+ await client.endSession(sessionId, {
109
+ commit: true,
110
+ importance_threshold: 0.5,
111
+ });
112
+ }
113
+ catch {
114
+ // Session may already be ended
115
+ }
116
+ }
117
+ catch {
118
+ // Best-effort — don't fail on shutdown
119
+ }
120
+ }
121
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/hooks/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Tool execution hooks for MemoryLayer OpenCode plugin.
3
+ *
4
+ * Before-tool: Injects relevant memory context for write/delegation tools.
5
+ * After-tool: Silently captures tool observations as working memory.
6
+ */
7
+ /**
8
+ * Handle before-tool execution.
9
+ *
10
+ * In OpenCode's hook system, this is called via "tool.execute.before" with
11
+ * (input, output) where we can mutate output.args. We use the return value
12
+ * pattern here and let the entry point handle injection.
13
+ *
14
+ * Returns additional context text, or null.
15
+ */
16
+ export declare function handleToolBefore(toolName: string, toolArgs: Record<string, unknown>): Promise<string | null>;
17
+ /**
18
+ * Handle after-tool execution — capture observations as working memory.
19
+ *
20
+ * Returns additional context guidance for significant events, or null.
21
+ */
22
+ export declare function handleToolAfter(toolName: string, toolArgs: Record<string, unknown>, toolOutput: string): Promise<string | null>;
23
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../src/hooks/tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoFH;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;AAyDD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyBxB"}