om-memory-system 5.3.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 (55) 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/tui-recall.js +13 -3
  5. package/dist/adapters/pi/extension.d.ts +1 -0
  6. package/dist/adapters/pi/extension.js +28 -11
  7. package/dist/adapters/pi/recall-rows.d.ts +31 -0
  8. package/dist/adapters/pi/recall-rows.js +148 -0
  9. package/dist/adapters/pi/session-context.d.ts +22 -0
  10. package/dist/adapters/pi/session-context.js +125 -0
  11. package/dist/config-template.d.ts +1 -1
  12. package/dist/config-template.js +18 -3
  13. package/dist/config.d.ts +6 -0
  14. package/dist/config.js +5 -4
  15. package/dist/core/context-budget.d.ts +2 -0
  16. package/dist/core/context-budget.js +2 -1
  17. package/dist/core/opening-context.d.ts +45 -0
  18. package/dist/core/opening-context.js +60 -0
  19. package/dist/core/recall-summary.d.ts +11 -7
  20. package/dist/core/recall-summary.js +14 -8
  21. package/dist/core/retrieval.d.ts +16 -0
  22. package/dist/core/retrieval.js +33 -11
  23. package/dist/importer/claude-hook-api.d.ts +5 -4
  24. package/dist/importer/claude-hook-api.js +58 -41
  25. package/dist/importer/claude-hook-context.d.ts +49 -0
  26. package/dist/importer/claude-hook-context.js +264 -0
  27. package/dist/services/ai/local-api-url.d.ts +2 -0
  28. package/dist/services/ai/local-api-url.js +12 -0
  29. package/dist/services/ai/provider-config.d.ts +2 -0
  30. package/dist/services/ai/provider-config.js +3 -1
  31. package/dist/services/ai/providers/google-gemini.js +2 -1
  32. package/dist/services/ai/providers/openai-responses.js +2 -1
  33. package/dist/services/chat/chat-runtime.js +5 -1
  34. package/dist/services/chat/chat-status.js +4 -1
  35. package/dist/services/global-config-writer.js +39 -14
  36. package/dist/services/injection-options-config.d.ts +53 -0
  37. package/dist/services/injection-options-config.js +56 -0
  38. package/dist/services/settings-snapshot.js +44 -12
  39. package/dist/services/visible-recall-config.d.ts +7 -2
  40. package/dist/services/visible-recall-config.js +2 -1
  41. package/dist/utils/memory-limits.d.ts +2 -1
  42. package/dist/utils/memory-limits.js +6 -3
  43. package/dist/v2/adapter.d.ts +2 -0
  44. package/dist/v2/adapter.js +107 -33
  45. package/dist/v2/memory-bridge.d.ts +13 -0
  46. package/dist/v2/memory-bridge.js +17 -0
  47. package/dist/v2/recall-reporter.d.ts +6 -3
  48. package/dist/v2/recall-reporter.js +4 -27
  49. package/dist/v2/session-parent.d.ts +14 -0
  50. package/dist/v2/session-parent.js +26 -0
  51. package/dist/web/assets/{index-CJ_K-hMG.js → index-BhQR88tU.js} +16 -16
  52. package/dist/web/assets/index-BmFUZbGr.css +2 -0
  53. package/dist/web/index.html +2 -2
  54. package/package.json +1 -1
  55. package/dist/web/assets/index-ccH97VE2.css +0 -2
@@ -1,6 +1,7 @@
1
1
  import { CONFIG, isConfigured, refreshConfigIfChanged } from "../config.js";
2
2
  import { buildRetrievalSection, formatMemoriesForCompaction, retrievalWrapperBytes, wrapRetrievalSection, } from "../core/retrieval.js";
3
3
  import { DEFAULT_RETRIEVAL_MAX_TOKENS, tokensToByteCeiling } from "../core/context-budget.js";
4
+ import { buildFirstPromptSections, openingQueryFor } from "../core/opening-context.js";
4
5
  import { isInternalPrompt, recordUserPrompt } from "../adapters/opencode/user-prompt.js";
5
6
  import { memoryClient } from "../services/client.js";
6
7
  import { V2_RETRIEVAL_TIMEOUT_MS } from "../services/request-timeouts.js";
@@ -15,6 +16,7 @@ export function createV2MemoryBridge(directory) {
15
16
  refreshConfigIfChanged(directory);
16
17
  return CONFIG.visibleRecall !== false;
17
18
  },
19
+ isSubagentMemoryEnabled: () => CONFIG.subagentMemory !== false,
18
20
  isInternalPrompt,
19
21
  async recordPrompt(sessionID, messageID, text) {
20
22
  await recordUserPrompt(sessionID, messageID, directory, text);
@@ -32,6 +34,21 @@ export function createV2MemoryBridge(directory) {
32
34
  });
33
35
  return section ? wrapRetrievalSection(section) : null;
34
36
  },
37
+ async retrieveFirstPrompt(prompt, sessionID, budgetBytes) {
38
+ const openingQuery = openingQueryFor({
39
+ openingContext: CONFIG.openingContext,
40
+ openingContextQuery: CONFIG.openingContextQuery,
41
+ });
42
+ const sections = await buildFirstPromptSections({
43
+ prompt,
44
+ openingQuery,
45
+ directory,
46
+ sessionId: sessionID,
47
+ budget: { maxBytes: budgetBytes, wrapperBytes: retrievalWrapperBytes() },
48
+ openingReserveBytes: 0,
49
+ });
50
+ return { ...sections, openingQuery };
51
+ },
35
52
  async restoreSession(sessionID) {
36
53
  const tags = getTags(directory);
37
54
  const result = await memoryClient.searchMemoriesBySessionID(sessionID, tags.project.tag, CONFIG.compaction.memoryLimit);
@@ -1,5 +1,7 @@
1
1
  import type { Context } from "@opencode/plugin/promise/plugin";
2
+ import { type RecallSummary } from "../core/recall-summary.js";
2
3
  import type { V2MemoryBridge } from "./memory-bridge.js";
4
+ import type { SessionParents } from "./session-parent.js";
3
5
  /** Latest summaries kept per session. The TUI shows recent rows, so older ones are dropped. */
4
6
  export declare const MAX_RECALL_SUMMARIES = 20;
5
7
  /** Longest query kept per summary. The TUI shows at most 300 characters of it. */
@@ -7,11 +9,12 @@ export declare const MAX_RECALL_QUERY_CHARS = 300;
7
9
  type Bound = <V>(map: Map<string, V>, key: string, value: V) => void;
8
10
  export interface RecallReporter {
9
11
  /**
10
- * Record the summary of one prompt retrieval. `token` identifies the retrieval: model steps
12
+ * Record the summary of one section as `mode`. `token` identifies the section: model steps
11
13
  * that reuse it record once. A section with no memory entries records nothing, and neither
12
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`.
13
16
  */
14
- record(sessionID: string, token: object, query: string, section: string | null): void;
17
+ record(sessionID: string, token: object, mode: RecallSummary["mode"], query: string | null, section: string | null): void;
15
18
  forget(sessionID: string): void;
16
19
  clear(): void;
17
20
  }
@@ -20,5 +23,5 @@ export interface RecallReporter {
20
23
  * entrypoint. It never throws and never touches what is injected. Logs carry the mode, count and
21
24
  * session id only, never memory text or the query.
22
25
  */
23
- export declare function createRecallReporter(ctx: Context, memory: V2MemoryBridge, bound: Bound): Promise<RecallReporter>;
26
+ export declare function createRecallReporter(ctx: Context, memory: V2MemoryBridge, bound: Bound, parents: SessionParents): Promise<RecallReporter>;
24
27
  export {};
@@ -10,13 +10,11 @@ export const MAX_RECALL_QUERY_CHARS = 300;
10
10
  * entrypoint. It never throws and never touches what is injected. Logs carry the mode, count and
11
11
  * session id only, never memory text or the query.
12
12
  */
13
- export async function createRecallReporter(ctx, memory, bound) {
13
+ export async function createRecallReporter(ctx, memory, bound, parents) {
14
14
  const rings = new Map();
15
15
  // Last number given per session. It keeps rising when the ring drops old entries.
16
16
  const sequences = new Map();
17
17
  const recorded = new WeakSet();
18
- // One lookup per session. The promise is cached so concurrent records keep their order.
19
- const subagents = new Map();
20
18
  // Records waiting on a session lookup. `forget` and `clear` cancel them, so a late lookup cannot
21
19
  // store a summary for a session whose state is gone. Entries leave when their lookup settles.
22
20
  const pending = new Map();
@@ -42,25 +40,6 @@ export async function createRecallReporter(ctx, memory, bound) {
42
40
  log("v2 recall: push failed", { sessionID, error: String(error) }, "error");
43
41
  }
44
42
  };
45
- // A session with a parent is a subagent session. A failed lookup counts as a root session, so
46
- // the summary still shows.
47
- const isSubagent = (sessionID) => {
48
- const known = subagents.get(sessionID);
49
- if (known)
50
- return known;
51
- const lookup = (async () => {
52
- try {
53
- const info = await ctx.session.get({ sessionID });
54
- return Boolean(info?.parentID);
55
- }
56
- catch (error) {
57
- log("v2 recall: session lookup failed", { sessionID, error: String(error) }, "error");
58
- return false;
59
- }
60
- })();
61
- bound(subagents, sessionID, lookup);
62
- return lookup;
63
- };
64
43
  const store = (sessionID, built) => {
65
44
  const seq = (sequences.get(sessionID) ?? 0) + 1;
66
45
  bound(sequences, sessionID, seq);
@@ -75,13 +54,13 @@ export async function createRecallReporter(ctx, memory, bound) {
75
54
  notify(sessionID);
76
55
  };
77
56
  return {
78
- record(sessionID, token, query, section) {
57
+ record(sessionID, token, mode, query, section) {
79
58
  if (recorded.has(token))
80
59
  return;
81
60
  recorded.add(token);
82
61
  try {
83
62
  const summary = section
84
- ? buildRecallSummary(section, "search", query.slice(0, MAX_RECALL_QUERY_CHARS))
63
+ ? buildRecallSummary(section, mode, query?.slice(0, MAX_RECALL_QUERY_CHARS) ?? null)
85
64
  : null;
86
65
  if (!summary)
87
66
  return;
@@ -89,7 +68,7 @@ export async function createRecallReporter(ctx, memory, bound) {
89
68
  const tickets = pending.get(sessionID) ?? new Set();
90
69
  tickets.add(ticket);
91
70
  pending.set(sessionID, tickets);
92
- void isSubagent(sessionID).then((subagent) => {
71
+ void parents.isSubagent(sessionID).then((subagent) => {
93
72
  tickets.delete(ticket);
94
73
  if (tickets.size === 0 && pending.get(sessionID) === tickets)
95
74
  pending.delete(sessionID);
@@ -113,7 +92,6 @@ export async function createRecallReporter(ctx, memory, bound) {
113
92
  pending.delete(sessionID);
114
93
  rings.delete(sessionID);
115
94
  sequences.delete(sessionID);
116
- subagents.delete(sessionID);
117
95
  },
118
96
  clear: () => {
119
97
  for (const tickets of pending.values())
@@ -122,7 +100,6 @@ export async function createRecallReporter(ctx, memory, bound) {
122
100
  pending.clear();
123
101
  rings.clear();
124
102
  sequences.clear();
125
- subagents.clear();
126
103
  },
127
104
  };
128
105
  }
@@ -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
+ }