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.
- package/README.md +4 -0
- package/dist/adapters/claude-code/hook-client.d.ts +1 -1
- package/dist/adapters/claude-code/hook-client.js +15 -2
- package/dist/adapters/opencode/tui-recall.js +13 -3
- package/dist/adapters/pi/extension.d.ts +1 -0
- package/dist/adapters/pi/extension.js +28 -11
- package/dist/adapters/pi/recall-rows.d.ts +31 -0
- package/dist/adapters/pi/recall-rows.js +148 -0
- package/dist/adapters/pi/session-context.d.ts +22 -0
- package/dist/adapters/pi/session-context.js +125 -0
- package/dist/config-template.d.ts +1 -1
- package/dist/config-template.js +18 -3
- package/dist/config.d.ts +6 -0
- package/dist/config.js +5 -4
- package/dist/core/context-budget.d.ts +2 -0
- package/dist/core/context-budget.js +2 -1
- package/dist/core/opening-context.d.ts +45 -0
- package/dist/core/opening-context.js +60 -0
- package/dist/core/recall-summary.d.ts +11 -7
- package/dist/core/recall-summary.js +14 -8
- package/dist/core/retrieval.d.ts +16 -0
- package/dist/core/retrieval.js +33 -11
- package/dist/importer/claude-hook-api.d.ts +5 -4
- package/dist/importer/claude-hook-api.js +58 -41
- package/dist/importer/claude-hook-context.d.ts +49 -0
- package/dist/importer/claude-hook-context.js +264 -0
- package/dist/services/ai/local-api-url.d.ts +2 -0
- package/dist/services/ai/local-api-url.js +12 -0
- package/dist/services/ai/provider-config.d.ts +2 -0
- package/dist/services/ai/provider-config.js +3 -1
- package/dist/services/ai/providers/google-gemini.js +2 -1
- package/dist/services/ai/providers/openai-responses.js +2 -1
- package/dist/services/chat/chat-runtime.js +5 -1
- package/dist/services/chat/chat-status.js +4 -1
- package/dist/services/global-config-writer.js +39 -14
- package/dist/services/injection-options-config.d.ts +53 -0
- package/dist/services/injection-options-config.js +56 -0
- package/dist/services/settings-snapshot.js +44 -12
- package/dist/services/visible-recall-config.d.ts +7 -2
- package/dist/services/visible-recall-config.js +2 -1
- package/dist/utils/memory-limits.d.ts +2 -1
- package/dist/utils/memory-limits.js +6 -3
- package/dist/v2/adapter.d.ts +2 -0
- package/dist/v2/adapter.js +107 -33
- package/dist/v2/memory-bridge.d.ts +13 -0
- package/dist/v2/memory-bridge.js +17 -0
- package/dist/v2/recall-reporter.d.ts +6 -3
- package/dist/v2/recall-reporter.js +4 -27
- package/dist/v2/session-parent.d.ts +14 -0
- package/dist/v2/session-parent.js +26 -0
- package/dist/web/assets/{index-CJ_K-hMG.js → index-BhQR88tU.js} +16 -16
- package/dist/web/assets/index-BmFUZbGr.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-ccH97VE2.css +0 -2
package/dist/v2/memory-bridge.js
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
+
}
|