@ephemushroom/opencode-claude-mem 0.4.4 → 0.5.0

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.
@@ -0,0 +1,7 @@
1
+ export { ClaudeMemPlugin } from './index';
2
+ declare const _default: {
3
+ id: string;
4
+ server: import("@opencode-ai/plugin").Plugin;
5
+ setup: (context: import("@opencode-ai/plugin-v2/promise/plugin").Context) => Promise<import("@opencode-ai/plugin-v2/promise/plugin").Cleanup | void> | import("@opencode-ai/plugin-v2/promise/plugin").Cleanup | void;
6
+ };
7
+ export default _default;
@@ -0,0 +1,10 @@
1
+ // @bun
2
+ // src/package-server.ts
3
+ import v1 from "./index";
4
+ import v2 from "./v2";
5
+ import { ClaudeMemPlugin } from "./index";
6
+ var package_server_default = { id: v2.id, server: v1.server, setup: v2.setup };
7
+ export {
8
+ ClaudeMemPlugin,
9
+ package_server_default as default
10
+ };
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ id: string;
3
+ tui: (api: any) => Promise<void>;
4
+ setup: (context: import("@opencode-ai/plugin-v2/tui/context").Context) => Promise<import("@opencode-ai/plugin-v2/tui/plugin").Cleanup | void> | import("@opencode-ai/plugin-v2/tui/plugin").Cleanup | void;
5
+ };
6
+ export default _default;
@@ -0,0 +1,8 @@
1
+ // @bun
2
+ // src/package-tui.ts
3
+ import v1 from "./tui";
4
+ import v2 from "./cli";
5
+ var package_tui_default = { id: v2.id, tui: v1.tui, setup: v2.setup };
6
+ export {
7
+ package_tui_default as default
8
+ };
@@ -0,0 +1,14 @@
1
+ export declare const MAX_OBSERVATION_BYTES: number;
2
+ export declare const MAX_TAG_REPLACEMENTS = 100;
3
+ export declare const META_TOOLS: Set<string>;
4
+ export declare function stripTaggedContent(text: string): string;
5
+ export declare function sanitizeObservationValue(value: unknown): unknown;
6
+ export declare function truncateUtf8Bytes(text: string, maxBytes: number): string;
7
+ export declare function shouldSkipObservationTool(toolName: string): boolean;
8
+ export declare function normalizeToolOutput(output: unknown): string;
9
+ /**
10
+ * Extract text content from message parts.
11
+ * Parts can be TextPart, ToolCallPart, etc. We only want text.
12
+ * Skips synthetic/ignored parts to match what is shown to the user.
13
+ */
14
+ export declare function extractTextFromParts(parts: any[]): string;
@@ -0,0 +1,50 @@
1
+ export declare const POLL_INTERVAL_MS = 5000;
2
+ export declare const OBSERVATION_TYPE_ICONS: Record<string, string>;
3
+ export interface MemSidebarView {
4
+ readonly healthy: boolean;
5
+ readonly project: string;
6
+ readonly observations: number | null;
7
+ readonly sessions: number | null;
8
+ readonly summaries: number | null;
9
+ readonly queueDepth: number | null;
10
+ readonly isProcessing: boolean;
11
+ readonly recentSummaries: readonly {
12
+ id: number;
13
+ request: string;
14
+ }[];
15
+ readonly recentObservations: readonly {
16
+ id: number;
17
+ type: string;
18
+ title: string;
19
+ }[];
20
+ }
21
+ export interface ViewNode {
22
+ readonly kind: 'box' | 'text';
23
+ readonly props: Record<string, unknown>;
24
+ readonly children?: readonly ViewNode[];
25
+ readonly text?: string;
26
+ }
27
+ export interface Theme {
28
+ readonly text: unknown;
29
+ readonly textMuted: unknown;
30
+ readonly info: unknown;
31
+ readonly success: unknown;
32
+ readonly warning: unknown;
33
+ readonly error: unknown;
34
+ readonly borderSubtle: unknown;
35
+ }
36
+ export declare function box(props: Record<string, unknown>, children: readonly ViewNode[]): ViewNode;
37
+ export declare function text(props: Record<string, unknown>, value: string): ViewNode;
38
+ export declare function decodeEntities(value: string): string;
39
+ export declare function truncate(value: string): string;
40
+ export declare function formatCount(value: number | null): string;
41
+ export declare function summaryLine(view: MemSidebarView): {
42
+ label: string;
43
+ tone: 'ok' | 'warn' | 'err';
44
+ };
45
+ export declare function toneColor(tone: 'ok' | 'warn' | 'err', theme: Theme): unknown;
46
+ export declare function bulletRow(icon: string, iconColor: unknown, label: string, theme: Theme): ViewNode;
47
+ export declare function expandedRows(view: MemSidebarView, theme: Theme): ViewNode[];
48
+ export declare function buildMemNodes(view: MemSidebarView, theme: Theme, collapsed: boolean, onToggle: () => void): ViewNode[];
49
+ export declare function viewKey(view: MemSidebarView): string;
50
+ export declare function readMemView(project: string, collapsed: boolean): Promise<MemSidebarView>;
@@ -7,3 +7,10 @@ export type TuiRegistrationResult = 'added' | 'already-present' | 'no-server-ent
7
7
  * target instead of being replaced by a regular file.
8
8
  */
9
9
  export declare function ensureTuiPluginEntry(): TuiRegistrationResult;
10
+ /**
11
+ * V2 self-heal: register the V2 CLI entry in `~/.config/opencode/cli.json`
12
+ * (the V2 CLI/TUI config file) so the Memory sidebar loads in the V2 TUI
13
+ * without manual configuration. The CLI writes strict JSON (2-space indent),
14
+ * so a comment-bearing file is left untouched ('malformed').
15
+ */
16
+ export declare function ensureCliPluginEntry(): TuiRegistrationResult;
package/dist/tui.d.ts CHANGED
@@ -1,39 +1,3 @@
1
- export interface MemSidebarView {
2
- readonly healthy: boolean;
3
- readonly project: string;
4
- readonly observations: number | null;
5
- readonly sessions: number | null;
6
- readonly summaries: number | null;
7
- readonly queueDepth: number | null;
8
- readonly isProcessing: boolean;
9
- readonly recentSummaries: readonly {
10
- id: number;
11
- request: string;
12
- }[];
13
- readonly recentObservations: readonly {
14
- id: number;
15
- type: string;
16
- title: string;
17
- }[];
18
- }
19
- interface ViewNode {
20
- readonly kind: 'box' | 'text';
21
- readonly props: Record<string, unknown>;
22
- readonly children?: readonly ViewNode[];
23
- readonly text?: string;
24
- }
25
- interface Theme {
26
- readonly text: string;
27
- readonly textMuted: string;
28
- readonly info: string;
29
- readonly success: string;
30
- readonly warning: string;
31
- readonly error: string;
32
- readonly borderSubtle: string;
33
- }
34
- export declare function buildMemNodes(view: MemSidebarView, theme: Theme, collapsed: boolean, onToggle: () => void): ViewNode[];
35
- export declare function viewKey(view: MemSidebarView): string;
36
- export declare function readMemView(project: string, collapsed: boolean): Promise<MemSidebarView>;
37
1
  declare const tuiModule: {
38
2
  id: string;
39
3
  tui: (api: any) => Promise<void>;
package/dist/tui.js CHANGED
@@ -1,12 +1,11 @@
1
1
  // @bun
2
- var __require = import.meta.require;
3
-
4
2
  // src/worker-client.ts
5
3
  import { existsSync, readFileSync } from "fs";
6
4
  import { homedir } from "os";
7
5
  import { join } from "path";
8
6
  import { spawn } from "child_process";
9
7
  var HEALTH_TIMEOUT_MS = 1000;
8
+ var WRITE_TIMEOUT_MS = 1e4;
10
9
  var AUTOSTART_POLL_INTERVAL_MS = 500;
11
10
  var AUTOSTART_POLL_MAX_ATTEMPTS = 16;
12
11
  var DEFAULT_WORKER_HOST = "127.0.0.1";
@@ -92,14 +91,14 @@ function extractTextContent(value) {
92
91
  return content.trim() || null;
93
92
  }
94
93
  if (Array.isArray(content)) {
95
- const text2 = content.map((part) => {
94
+ const text = content.map((part) => {
96
95
  if (!isRecord(part) || part["type"] !== "text" || typeof part["text"] !== "string") {
97
96
  return "";
98
97
  }
99
98
  return part["text"];
100
99
  }).filter(Boolean).join(`
101
100
  `).trim();
102
- return text2 || null;
101
+ return text || null;
103
102
  }
104
103
  const text = JSON.stringify(value, null, 2);
105
104
  return text === "{}" || text === "null" ? null : text;
@@ -110,20 +109,17 @@ class WorkerClient {
110
109
  static autoStartPromise = null;
111
110
  static async isHealthy() {
112
111
  try {
113
- const controller = new AbortController;
114
- const timeoutId = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
115
112
  const response = await fetch(`${this.BASE_URL}/api/health`, {
116
- signal: controller.signal
113
+ signal: AbortSignal.timeout(HEALTH_TIMEOUT_MS)
117
114
  });
118
- clearTimeout(timeoutId);
119
115
  return response.ok;
120
116
  } catch {
121
117
  return false;
122
118
  }
123
119
  }
124
120
  static async ensureRunning() {
125
- const result = await this.tryAutoStart();
126
- return result === "already-running" || result === "started";
121
+ await this.tryAutoStart();
122
+ return this.isHealthy();
127
123
  }
128
124
  static tryAutoStart() {
129
125
  if (!this.autoStartPromise) {
@@ -191,6 +187,7 @@ class WorkerClient {
191
187
  try {
192
188
  const response = await fetch(`${this.BASE_URL}/api/sessions/init`, {
193
189
  method: "POST",
190
+ signal: AbortSignal.timeout(WRITE_TIMEOUT_MS),
194
191
  headers: { "Content-Type": "application/json" },
195
192
  body: JSON.stringify({
196
193
  contentSessionId,
@@ -207,10 +204,11 @@ class WorkerClient {
207
204
  return null;
208
205
  }
209
206
  }
210
- static async sendObservation(contentSessionId, toolName, toolInput, toolResponse, cwd) {
207
+ static async sendObservation(contentSessionId, toolName, toolInput, toolResponse, cwd, toolUseId) {
211
208
  try {
212
- await fetch(`${this.BASE_URL}/api/sessions/observations`, {
209
+ const response = await fetch(`${this.BASE_URL}/api/sessions/observations`, {
213
210
  method: "POST",
211
+ signal: AbortSignal.timeout(WRITE_TIMEOUT_MS),
214
212
  headers: { "Content-Type": "application/json" },
215
213
  body: JSON.stringify({
216
214
  contentSessionId,
@@ -218,33 +216,34 @@ class WorkerClient {
218
216
  tool_input: toolInput,
219
217
  tool_response: toolResponse,
220
218
  cwd,
221
- platformSource: PLATFORM_SOURCE
219
+ platformSource: PLATFORM_SOURCE,
220
+ tool_use_id: toolUseId
222
221
  })
223
222
  });
224
- } catch {}
223
+ return response.ok;
224
+ } catch {
225
+ return false;
226
+ }
225
227
  }
226
- static async summarize(contentSessionId, lastUserMessage, lastAssistantMessage) {
228
+ static async summarize(contentSessionId, lastUserMessage, lastAssistantMessage, metadata = {}) {
227
229
  try {
228
- await fetch(`${this.BASE_URL}/api/sessions/summarize`, {
230
+ const response = await fetch(`${this.BASE_URL}/api/sessions/summarize`, {
229
231
  method: "POST",
232
+ signal: AbortSignal.timeout(WRITE_TIMEOUT_MS),
230
233
  headers: { "Content-Type": "application/json" },
231
234
  body: JSON.stringify({
232
235
  contentSessionId,
233
236
  last_user_message: lastUserMessage,
234
237
  last_assistant_message: lastAssistantMessage,
235
- platformSource: PLATFORM_SOURCE
238
+ platformSource: PLATFORM_SOURCE,
239
+ observedModel: metadata.observedModel,
240
+ observedBilling: metadata.observedBilling
236
241
  })
237
242
  });
238
- } catch {}
239
- }
240
- static async completeSession(contentSessionId) {
241
- try {
242
- await fetch(`${this.BASE_URL}/api/sessions/complete`, {
243
- method: "POST",
244
- headers: { "Content-Type": "application/json" },
245
- body: JSON.stringify({ contentSessionId, platformSource: PLATFORM_SOURCE })
246
- });
247
- } catch {}
243
+ return response.ok;
244
+ } catch {
245
+ return false;
246
+ }
248
247
  }
249
248
  static async getContext(project) {
250
249
  try {
@@ -399,7 +398,7 @@ class WorkerClient {
399
398
  }
400
399
  }
401
400
 
402
- // src/tui.ts
401
+ // src/sidebar-model.ts
403
402
  var POLL_INTERVAL_MS = 5000;
404
403
  var LABEL_MAX = 40;
405
404
  var RECENT_SUMMARIES = 3;
@@ -537,6 +536,8 @@ async function readMemView(project, collapsed) {
537
536
  recentObservations
538
537
  };
539
538
  }
539
+
540
+ // src/tui.ts
540
541
  async function loadSolidCore() {
541
542
  try {
542
543
  const mod = await import("solid-js");
@@ -652,8 +653,5 @@ var tuiModule = {
652
653
  };
653
654
  var tui_default = tuiModule;
654
655
  export {
655
- viewKey,
656
- readMemView,
657
- tui_default as default,
658
- buildMemNodes
656
+ tui_default as default
659
657
  };
package/dist/v2.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * OpenCode V2 plugin for Claude-Mem.
3
+ *
4
+ * V2 is a breaking API change from V1 — plugins are `Plugin.define({ id,
5
+ * setup })` modules with a server-like context instead of a `{ server }`
6
+ * module of hook handlers. This entrypoint is published under the package's
7
+ * `./v2` export; V1 users keep loading the default export (see src/index.ts).
8
+ * Both entrypoints are thin adapters over the same WorkerClient + shared
9
+ * helpers, so V1 and V2 can coexist and talk to the same Claude-Mem worker.
10
+ *
11
+ * V2 API mapping (vs V1):
12
+ * - `experimental.chat.system.transform` → `ctx.session.hook('context')` (push
13
+ * a SystemPart into `event.system`)
14
+ * - `tool.execute.after` → `ctx.tool.hook('execute.after')`
15
+ * - `tool` definitions → `ctx.tool.transform((tools) => tools.add(...))`
16
+ * - `event` lifecycle → `ctx.event.subscribe()` stream
17
+ * - Assistant text capture uses `session.text.ended` events (the event carries
18
+ * the complete text — no debounced re-fetch needed like V1's message.updated)
19
+ * - Summarization fires on `session.execution.succeeded` (V1's session.idle)
20
+ */
21
+ declare const _default: import("@opencode-ai/plugin-v2/promise/plugin").Plugin;
22
+ export default _default;