@simbimbo/memory-ocmemog 0.1.20 → 0.1.21

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 (3) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/index.ts +169 -0
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.1.21 — 2026-03-30
6
+
7
+ OpenClaw memory-runtime compatibility release.
8
+
9
+ ### Highlights
10
+ - fixed `memory-ocmemog` so it registers the primary OpenClaw memory runtime instead of exposing only tool surfaces
11
+ - restored compatibility with current OpenClaw host status/runtime expectations, resolving the `Memory ... unavailable` state while keeping the ocmemog sidecar healthy on `127.0.0.1:17891`
12
+ - kept the release intentionally minimal and scoped to the real host/plugin integration fix
13
+
5
14
  ## 0.1.20 — 2026-03-29
6
15
 
7
16
  Operational-artifact canonicalization, dead-lane retrieval hardening, and rehydratable-memory recall fixes.
package/index.ts CHANGED
@@ -12,6 +12,41 @@ type PluginConfig = {
12
12
  token?: string;
13
13
  };
14
14
 
15
+ type MemoryManagerStatus = {
16
+ backend: string;
17
+ provider: string;
18
+ model?: string;
19
+ requestedProvider?: string;
20
+ files?: number;
21
+ chunks?: number;
22
+ dirty?: boolean;
23
+ workspaceDir?: string;
24
+ dbPath?: string;
25
+ vector?: { enabled?: boolean; available?: boolean; error?: string };
26
+ fts?: { enabled?: boolean; available?: boolean; error?: string };
27
+ custom?: Record<string, unknown>;
28
+ };
29
+
30
+ type MemorySearchManager = {
31
+ search(query: string, opts?: Record<string, unknown>): Promise<unknown>;
32
+ readFile(params: { relPath?: string; path?: string; from?: number; lines?: number }): Promise<{ text: string; path: string }>;
33
+ status(): MemoryManagerStatus;
34
+ sync?(params?: Record<string, unknown>): Promise<void>;
35
+ probeEmbeddingAvailability?(): Promise<{ ok: boolean; error?: string }>;
36
+ probeVectorAvailability?(): Promise<boolean>;
37
+ close?(): Promise<void>;
38
+ };
39
+
40
+ type MemoryRuntime = {
41
+ getMemorySearchManager(params: Record<string, unknown>): Promise<{ manager: MemorySearchManager | null; error?: string }>;
42
+ resolveMemoryBackendConfig(params: Record<string, unknown>): Record<string, unknown>;
43
+ closeAllMemorySearchManagers(): Promise<void>;
44
+ };
45
+
46
+ type OpenClawPluginApiCompat = OpenClawPluginApi & {
47
+ registerMemoryRuntime?: (runtime: MemoryRuntime) => void;
48
+ };
49
+
15
50
  const DURABLE_OUTBOX_ENABLED = !["0", "false", "no"].includes(
16
51
  String(process.env.OCMEMOG_DURABLE_OUTBOX ?? "true").trim().toLowerCase(),
17
52
  );
@@ -56,6 +91,16 @@ type RecentResponse = {
56
91
  error?: string;
57
92
  };
58
93
 
94
+ type SidecarHealthResponse = {
95
+ ok?: boolean;
96
+ version?: string;
97
+ service?: string;
98
+ vector_provider?: string;
99
+ vector_available?: boolean;
100
+ database_path?: string;
101
+ [key: string]: unknown;
102
+ };
103
+
59
104
  type ConversationHydrateResponse = {
60
105
  ok: boolean;
61
106
  recent_turns?: Array<Record<string, unknown>>;
@@ -719,6 +764,122 @@ function registerAutomaticContinuityHooks(api: OpenClawPluginApi, config: Plugin
719
764
  });
720
765
  }
721
766
 
767
+ async function getSidecarHealth(config: PluginConfig): Promise<SidecarHealthResponse> {
768
+ return await getJson<SidecarHealthResponse>(config, "/healthz");
769
+ }
770
+
771
+ function normalizeReadPath(params: { relPath?: string; path?: string }): string {
772
+ const value = String(params.relPath ?? params.path ?? "").trim();
773
+ return value;
774
+ }
775
+
776
+ function createOcmemogSearchManager(api: OpenClawPluginApi, config: PluginConfig): MemorySearchManager {
777
+ let closed = false;
778
+
779
+ return {
780
+ async search(query: string, opts?: Record<string, unknown>): Promise<unknown> {
781
+ if (closed) throw new Error("memory manager closed");
782
+ const payload = await postJson<SearchResponse>(config, "/memory/search", {
783
+ query,
784
+ limit: opts?.limit,
785
+ categories: opts?.categories,
786
+ metadata_filters: opts?.metadataFilters,
787
+ lane: opts?.lane,
788
+ });
789
+ return payload;
790
+ },
791
+ async readFile(params: { relPath?: string; path?: string; from?: number; lines?: number }): Promise<{ text: string; path: string }> {
792
+ if (closed) throw new Error("memory manager closed");
793
+ const relPath = normalizeReadPath(params);
794
+ if (!relPath) throw new Error("path required");
795
+ if (!relPath.endsWith('.md')) throw new Error('path required');
796
+ const absPath = path.resolve(api.workspaceDir ?? process.cwd(), relPath);
797
+ let text = "";
798
+ try {
799
+ text = await fs.readFile(absPath, "utf8");
800
+ } catch (error) {
801
+ const code = (error as NodeJS.ErrnoException)?.code;
802
+ if (code === "ENOENT") return { text: "", path: relPath };
803
+ throw error;
804
+ }
805
+ if (params.from !== undefined || params.lines !== undefined) {
806
+ const lines = text.split("\n");
807
+ const start = Math.max(1, Number(params.from ?? 1));
808
+ const count = Math.max(1, Number(params.lines ?? lines.length));
809
+ text = lines.slice(start - 1, start - 1 + count).join("\n");
810
+ }
811
+ return { text, path: relPath };
812
+ },
813
+ status(): MemoryManagerStatus {
814
+ return {
815
+ backend: "ocmemog",
816
+ provider: "ocmemog",
817
+ model: "ocmemog",
818
+ requestedProvider: "ocmemog",
819
+ dirty: false,
820
+ workspaceDir: api.workspaceDir,
821
+ vector: {
822
+ enabled: true,
823
+ available: true,
824
+ },
825
+ fts: {
826
+ enabled: true,
827
+ available: true,
828
+ },
829
+ custom: {
830
+ endpoint: config.endpoint,
831
+ runtime: "ocmemog-sidecar-adapter",
832
+ sidecar: "ocmemog",
833
+ },
834
+ };
835
+ },
836
+ async probeEmbeddingAvailability(): Promise<{ ok: boolean; error?: string }> {
837
+ try {
838
+ const health = await getSidecarHealth(config);
839
+ return { ok: Boolean(health.ok ?? true) };
840
+ } catch (error) {
841
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
842
+ }
843
+ },
844
+ async probeVectorAvailability(): Promise<boolean> {
845
+ try {
846
+ const health = await getSidecarHealth(config);
847
+ return Boolean(health.vector_available ?? true);
848
+ } catch {
849
+ return false;
850
+ }
851
+ },
852
+ async close(): Promise<void> {
853
+ closed = true;
854
+ },
855
+ };
856
+ }
857
+
858
+ function createOcmemogMemoryRuntime(api: OpenClawPluginApi, config: PluginConfig): MemoryRuntime {
859
+ let manager: MemorySearchManager | null = null;
860
+ return {
861
+ async getMemorySearchManager(_params: Record<string, unknown>): Promise<{ manager: MemorySearchManager | null; error?: string }> {
862
+ try {
863
+ manager ??= createOcmemogSearchManager(api, config);
864
+ return { manager };
865
+ } catch (error) {
866
+ return { manager: null, error: error instanceof Error ? error.message : String(error) };
867
+ }
868
+ },
869
+ resolveMemoryBackendConfig(_params: Record<string, unknown>): Record<string, unknown> {
870
+ return {
871
+ backend: "ocmemog",
872
+ endpoint: config.endpoint,
873
+ timeoutMs: config.timeoutMs,
874
+ };
875
+ },
876
+ async closeAllMemorySearchManagers(): Promise<void> {
877
+ await manager?.close?.();
878
+ manager = null;
879
+ },
880
+ };
881
+ }
882
+
722
883
  const ocmemogPlugin = {
723
884
  id: "memory-ocmemog",
724
885
  name: "Memory (OCMemog)",
@@ -726,6 +887,14 @@ const ocmemogPlugin = {
726
887
  kind: "memory",
727
888
  register(api: OpenClawPluginApi) {
728
889
  const config = readConfig(api.pluginConfig);
890
+ const apiCompat = api as OpenClawPluginApiCompat;
891
+
892
+ if (typeof apiCompat.registerMemoryRuntime === "function") {
893
+ apiCompat.registerMemoryRuntime(createOcmemogMemoryRuntime(api, config));
894
+ api.logger.info(`ocmemog registered primary memory runtime via sidecar adapter (${config.endpoint})`);
895
+ } else {
896
+ api.logger.info("ocmemog host API does not expose registerMemoryRuntime; continuing in legacy tool-only compatibility mode");
897
+ }
729
898
 
730
899
  registerAutomaticContinuityHooks(api, config);
731
900
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simbimbo/memory-ocmemog",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "Advanced OpenClaw memory plugin with durable recall, transcript-backed continuity, and sidecar APIs",
5
5
  "license": "MIT",
6
6
  "repository": {