om-memory-system 5.2.0 → 5.4.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.
Files changed (69) hide show
  1. package/README.md +4 -0
  2. package/dist/adapters/claude-code/hook-client.d.ts +1 -1
  3. package/dist/adapters/claude-code/hook-client.js +15 -2
  4. package/dist/adapters/opencode/in-process-client.d.ts +2 -0
  5. package/dist/adapters/opencode/in-process-client.js +16 -0
  6. package/dist/adapters/opencode/opencode-host-config.d.ts +3 -0
  7. package/dist/adapters/opencode/opencode-host-config.js +2 -0
  8. package/dist/adapters/opencode/tui-recall.d.ts +68 -0
  9. package/dist/adapters/opencode/tui-recall.js +312 -0
  10. package/dist/adapters/pi/extension.d.ts +1 -0
  11. package/dist/adapters/pi/extension.js +28 -11
  12. package/dist/adapters/pi/recall-rows.d.ts +31 -0
  13. package/dist/adapters/pi/recall-rows.js +148 -0
  14. package/dist/adapters/pi/session-context.d.ts +22 -0
  15. package/dist/adapters/pi/session-context.js +125 -0
  16. package/dist/config-template.d.ts +1 -1
  17. package/dist/config-template.js +20 -0
  18. package/dist/config.d.ts +8 -0
  19. package/dist/config.js +8 -4
  20. package/dist/core/context-budget.d.ts +2 -0
  21. package/dist/core/context-budget.js +2 -1
  22. package/dist/core/opening-context.d.ts +45 -0
  23. package/dist/core/opening-context.js +60 -0
  24. package/dist/core/recall-summary.d.ts +32 -0
  25. package/dist/core/recall-summary.js +77 -0
  26. package/dist/core/retrieval.d.ts +16 -0
  27. package/dist/core/retrieval.js +33 -11
  28. package/dist/importer/claude-hook-api.d.ts +5 -4
  29. package/dist/importer/claude-hook-api.js +58 -41
  30. package/dist/importer/claude-hook-context.d.ts +49 -0
  31. package/dist/importer/claude-hook-context.js +264 -0
  32. package/dist/index.js +1 -1
  33. package/dist/services/ai/local-api-url.d.ts +2 -0
  34. package/dist/services/ai/local-api-url.js +12 -0
  35. package/dist/services/ai/provider-config.d.ts +2 -0
  36. package/dist/services/ai/provider-config.js +3 -1
  37. package/dist/services/ai/providers/google-gemini.js +2 -1
  38. package/dist/services/ai/providers/openai-responses.js +2 -1
  39. package/dist/services/chat/chat-runtime.js +5 -1
  40. package/dist/services/chat/chat-status.js +4 -1
  41. package/dist/services/global-config-writer.js +39 -14
  42. package/dist/services/injection-options-config.d.ts +53 -0
  43. package/dist/services/injection-options-config.js +56 -0
  44. package/dist/services/settings-snapshot.js +44 -12
  45. package/dist/services/single-flight.d.ts +2 -0
  46. package/dist/services/single-flight.js +12 -0
  47. package/dist/services/visible-recall-config.d.ts +39 -0
  48. package/dist/services/visible-recall-config.js +28 -0
  49. package/dist/services/web-server.d.ts +3 -0
  50. package/dist/services/web-server.js +26 -8
  51. package/dist/utils/memory-limits.d.ts +2 -1
  52. package/dist/utils/memory-limits.js +6 -3
  53. package/dist/v2/adapter.d.ts +2 -0
  54. package/dist/v2/adapter.js +110 -31
  55. package/dist/v2/legacy-client.js +2 -1
  56. package/dist/v2/memory-bridge.d.ts +15 -0
  57. package/dist/v2/memory-bridge.js +21 -0
  58. package/dist/v2/recall-reporter.d.ts +27 -0
  59. package/dist/v2/recall-reporter.js +105 -0
  60. package/dist/v2/recall-rpc.d.ts +51 -0
  61. package/dist/v2/recall-rpc.js +38 -0
  62. package/dist/v2/session-parent.d.ts +14 -0
  63. package/dist/v2/session-parent.js +26 -0
  64. package/dist/web/assets/{index-CJ_K-hMG.js → index-BhQR88tU.js} +16 -16
  65. package/dist/web/assets/index-BmFUZbGr.css +2 -0
  66. package/dist/web/index.html +2 -2
  67. package/opencode/tui.tsx +22 -3
  68. package/package.json +1 -1
  69. package/dist/web/assets/index-ccH97VE2.css +0 -2
@@ -0,0 +1,27 @@
1
+ import type { Context } from "@opencode/plugin/promise/plugin";
2
+ import { type RecallSummary } from "../core/recall-summary.js";
3
+ import type { V2MemoryBridge } from "./memory-bridge.js";
4
+ import type { SessionParents } from "./session-parent.js";
5
+ /** Latest summaries kept per session. The TUI shows recent rows, so older ones are dropped. */
6
+ export declare const MAX_RECALL_SUMMARIES = 20;
7
+ /** Longest query kept per summary. The TUI shows at most 300 characters of it. */
8
+ export declare const MAX_RECALL_QUERY_CHARS = 300;
9
+ type Bound = <V>(map: Map<string, V>, key: string, value: V) => void;
10
+ export interface RecallReporter {
11
+ /**
12
+ * Record the summary of one section as `mode`. `token` identifies the section: model steps
13
+ * that reuse it record once. A section with no memory entries records nothing, and neither
14
+ * does a subagent session (one with a parent). The call does not wait for the session lookup.
15
+ * The query is kept for `search` and `opening`, and dropped for `restore`.
16
+ */
17
+ record(sessionID: string, token: object, mode: RecallSummary["mode"], query: string | null, section: string | null): void;
18
+ forget(sessionID: string): void;
19
+ clear(): void;
20
+ }
21
+ /**
22
+ * Keeps a bounded per-session ring of recall summaries and serves it over plugin RPC to the TUI
23
+ * entrypoint. It never throws and never touches what is injected. Logs carry the mode, count and
24
+ * session id only, never memory text or the query.
25
+ */
26
+ export declare function createRecallReporter(ctx: Context, memory: V2MemoryBridge, bound: Bound, parents: SessionParents): Promise<RecallReporter>;
27
+ export {};
@@ -0,0 +1,105 @@
1
+ import { buildRecallSummary } from "../core/recall-summary.js";
2
+ import { log } from "../services/logger.js";
3
+ import { recallRpc } from "./recall-rpc.js";
4
+ /** Latest summaries kept per session. The TUI shows recent rows, so older ones are dropped. */
5
+ export const MAX_RECALL_SUMMARIES = 20;
6
+ /** Longest query kept per summary. The TUI shows at most 300 characters of it. */
7
+ export const MAX_RECALL_QUERY_CHARS = 300;
8
+ /**
9
+ * Keeps a bounded per-session ring of recall summaries and serves it over plugin RPC to the TUI
10
+ * entrypoint. It never throws and never touches what is injected. Logs carry the mode, count and
11
+ * session id only, never memory text or the query.
12
+ */
13
+ export async function createRecallReporter(ctx, memory, bound, parents) {
14
+ const rings = new Map();
15
+ // Last number given per session. It keeps rising when the ring drops old entries.
16
+ const sequences = new Map();
17
+ const recorded = new WeakSet();
18
+ // Records waiting on a session lookup. `forget` and `clear` cancel them, so a late lookup cannot
19
+ // store a summary for a session whose state is gone. Entries leave when their lookup settles.
20
+ const pending = new Map();
21
+ let emit;
22
+ try {
23
+ const registration = await ctx.rpc.register(recallRpc, {
24
+ // The contract is plain JSON schema, so the host types the input as unknown.
25
+ list: async (input) => ({
26
+ enabled: memory.isVisibleRecallEnabled(),
27
+ items: [...(rings.get(input.sessionID) ?? [])],
28
+ }),
29
+ });
30
+ emit = (sessionID) => registration.events.emit("recalled", { sessionID });
31
+ }
32
+ catch (error) {
33
+ log("v2 recall: rpc registration failed", { error: String(error) }, "error");
34
+ }
35
+ const notify = (sessionID) => {
36
+ try {
37
+ emit?.(sessionID).catch((error) => log("v2 recall: push failed", { sessionID, error: String(error) }, "error"));
38
+ }
39
+ catch (error) {
40
+ log("v2 recall: push failed", { sessionID, error: String(error) }, "error");
41
+ }
42
+ };
43
+ const store = (sessionID, built) => {
44
+ const seq = (sequences.get(sessionID) ?? 0) + 1;
45
+ bound(sequences, sessionID, seq);
46
+ const summary = { ...built, seq };
47
+ const ring = [...(rings.get(sessionID) ?? []), summary].slice(-MAX_RECALL_SUMMARIES);
48
+ bound(rings, sessionID, ring);
49
+ log("v2 recall: summary recorded", {
50
+ sessionID,
51
+ mode: summary.mode,
52
+ count: summary.items.length,
53
+ });
54
+ notify(sessionID);
55
+ };
56
+ return {
57
+ record(sessionID, token, mode, query, section) {
58
+ if (recorded.has(token))
59
+ return;
60
+ recorded.add(token);
61
+ try {
62
+ const summary = section
63
+ ? buildRecallSummary(section, mode, query?.slice(0, MAX_RECALL_QUERY_CHARS) ?? null)
64
+ : null;
65
+ if (!summary)
66
+ return;
67
+ const ticket = { cancelled: false };
68
+ const tickets = pending.get(sessionID) ?? new Set();
69
+ tickets.add(ticket);
70
+ pending.set(sessionID, tickets);
71
+ void parents.isSubagent(sessionID).then((subagent) => {
72
+ tickets.delete(ticket);
73
+ if (tickets.size === 0 && pending.get(sessionID) === tickets)
74
+ pending.delete(sessionID);
75
+ if (subagent || ticket.cancelled)
76
+ return;
77
+ try {
78
+ store(sessionID, summary);
79
+ }
80
+ catch (error) {
81
+ log("v2 recall: could not record summary", { sessionID, error: String(error) }, "error");
82
+ }
83
+ });
84
+ }
85
+ catch (error) {
86
+ log("v2 recall: could not record summary", { sessionID, error: String(error) }, "error");
87
+ }
88
+ },
89
+ forget: (sessionID) => {
90
+ for (const ticket of pending.get(sessionID) ?? [])
91
+ ticket.cancelled = true;
92
+ pending.delete(sessionID);
93
+ rings.delete(sessionID);
94
+ sequences.delete(sessionID);
95
+ },
96
+ clear: () => {
97
+ for (const tickets of pending.values())
98
+ for (const ticket of tickets)
99
+ ticket.cancelled = true;
100
+ pending.clear();
101
+ rings.clear();
102
+ sequences.clear();
103
+ },
104
+ };
105
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Plugin RPC contract between the OpenCode v2 server entrypoint (records recall summaries) and the
3
+ * terminal TUI entrypoint (draws them). Plain data on purpose: no host SDK import, so both sides and
4
+ * the tests can load it. The TUI pulls `list` and treats `recalled` as a refresh hint only; a missing
5
+ * push never breaks the rows (TDR-060).
6
+ */
7
+ export declare const RECALL_RPC_ID = "omms-recall";
8
+ export declare const recallRpc: {
9
+ readonly id: "omms-recall";
10
+ readonly methods: {
11
+ readonly list: {
12
+ readonly input: {
13
+ readonly type: "object";
14
+ readonly properties: {
15
+ readonly sessionID: {
16
+ readonly type: "string";
17
+ };
18
+ };
19
+ readonly required: readonly ["sessionID"];
20
+ };
21
+ readonly output: {
22
+ readonly type: "object";
23
+ readonly properties: {
24
+ readonly enabled: {
25
+ readonly type: "boolean";
26
+ };
27
+ readonly items: {
28
+ readonly type: "array";
29
+ readonly items: {
30
+ readonly type: "object";
31
+ };
32
+ };
33
+ };
34
+ readonly required: readonly ["enabled", "items"];
35
+ };
36
+ };
37
+ };
38
+ readonly events: {
39
+ readonly recalled: {
40
+ readonly schema: {
41
+ readonly type: "object";
42
+ readonly properties: {
43
+ readonly sessionID: {
44
+ readonly type: "string";
45
+ };
46
+ };
47
+ readonly required: readonly ["sessionID"];
48
+ };
49
+ };
50
+ };
51
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Plugin RPC contract between the OpenCode v2 server entrypoint (records recall summaries) and the
3
+ * terminal TUI entrypoint (draws them). Plain data on purpose: no host SDK import, so both sides and
4
+ * the tests can load it. The TUI pulls `list` and treats `recalled` as a refresh hint only; a missing
5
+ * push never breaks the rows (TDR-060).
6
+ */
7
+ export const RECALL_RPC_ID = "omms-recall";
8
+ export const recallRpc = {
9
+ id: RECALL_RPC_ID,
10
+ methods: {
11
+ // `enabled` is the effective `visibleRecall` setting for the server's project, so the TUI never
12
+ // reads config. Summaries are recorded and served even when it is false.
13
+ list: {
14
+ input: {
15
+ type: "object",
16
+ properties: { sessionID: { type: "string" } },
17
+ required: ["sessionID"],
18
+ },
19
+ output: {
20
+ type: "object",
21
+ properties: {
22
+ enabled: { type: "boolean" },
23
+ items: { type: "array", items: { type: "object" } },
24
+ },
25
+ required: ["enabled", "items"],
26
+ },
27
+ },
28
+ },
29
+ events: {
30
+ recalled: {
31
+ schema: {
32
+ type: "object",
33
+ properties: { sessionID: { type: "string" } },
34
+ required: ["sessionID"],
35
+ },
36
+ },
37
+ },
38
+ };
@@ -0,0 +1,14 @@
1
+ import type { Context } from "@opencode/plugin/promise/plugin";
2
+ type Bound = <V>(map: Map<string, V>, key: string, value: V) => void;
3
+ export interface SessionParents {
4
+ /**
5
+ * Whether the session has a parent, which makes it a subagent session. One lookup per session;
6
+ * the promise is cached so concurrent callers share it. A failed lookup counts as a root session.
7
+ */
8
+ isSubagent(sessionID: string): Promise<boolean>;
9
+ forget(sessionID: string): void;
10
+ clear(): void;
11
+ }
12
+ /** The cached parent lookup shared by the recall reporter and the adapter's subagent gate. */
13
+ export declare function createSessionParents(ctx: Context, bound: Bound): SessionParents;
14
+ export {};
@@ -0,0 +1,26 @@
1
+ import { log } from "../services/logger.js";
2
+ /** The cached parent lookup shared by the recall reporter and the adapter's subagent gate. */
3
+ export function createSessionParents(ctx, bound) {
4
+ const lookups = new Map();
5
+ return {
6
+ isSubagent(sessionID) {
7
+ const known = lookups.get(sessionID);
8
+ if (known)
9
+ return known;
10
+ const lookup = (async () => {
11
+ try {
12
+ const info = await ctx.session.get({ sessionID });
13
+ return Boolean(info?.parentID);
14
+ }
15
+ catch (error) {
16
+ log("v2 session: parent lookup failed", { sessionID, error: String(error) }, "error");
17
+ return false;
18
+ }
19
+ })();
20
+ bound(lookups, sessionID, lookup);
21
+ return lookup;
22
+ },
23
+ forget: (sessionID) => void lookups.delete(sessionID),
24
+ clear: () => lookups.clear(),
25
+ };
26
+ }