opencode-claude-memory 1.7.6 → 1.7.8
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 +104 -213
- package/dist/agents.d.ts +12 -0
- package/dist/agents.js +69 -0
- package/dist/config.d.ts +112 -0
- package/dist/config.js +79 -0
- package/dist/extraction/ExtractionCoordinator.d.ts +62 -0
- package/dist/extraction/ExtractionCoordinator.js +386 -0
- package/dist/extraction/autodream.d.ts +33 -0
- package/dist/extraction/autodream.js +85 -0
- package/dist/extraction/forkSession.d.ts +37 -0
- package/dist/extraction/forkSession.js +133 -0
- package/dist/extraction/lock.d.ts +29 -0
- package/dist/extraction/lock.js +191 -0
- package/dist/extraction/prompts.d.ts +5 -0
- package/dist/extraction/prompts.js +78 -0
- package/dist/extraction/state.d.ts +38 -0
- package/dist/extraction/state.js +159 -0
- package/dist/hooks/ignore.d.ts +6 -0
- package/dist/hooks/ignore.js +53 -0
- package/dist/hooks/messages.d.ts +14 -0
- package/dist/hooks/messages.js +96 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +50 -633
- package/dist/prompt/sections.d.ts +7 -0
- package/dist/{prompt.js → prompt/sections.js} +16 -72
- package/dist/prompt/systemPrompt.d.ts +7 -0
- package/dist/prompt/systemPrompt.js +64 -0
- package/dist/recall/RecallCoordinator.d.ts +36 -0
- package/dist/recall/RecallCoordinator.js +140 -0
- package/dist/recall/format.d.ts +17 -0
- package/dist/{recall.js → recall/format.js} +13 -36
- package/dist/{recallSelector.d.ts → recall/selector.d.ts} +26 -16
- package/dist/recall/selector.js +92 -0
- package/dist/sdk.d.ts +12 -0
- package/dist/sdk.js +8 -0
- package/dist/store/MemoryStore.d.ts +41 -0
- package/dist/store/MemoryStore.js +105 -0
- package/dist/store/frontmatter.d.ts +25 -0
- package/dist/store/frontmatter.js +61 -0
- package/dist/store/indexFile.d.ts +15 -0
- package/dist/store/indexFile.js +153 -0
- package/dist/{paths.d.ts → store/paths.d.ts} +7 -6
- package/dist/store/paths.js +187 -0
- package/dist/store/scan.d.ts +29 -0
- package/dist/store/scan.js +112 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.js +142 -0
- package/dist/util/exclusiveFile.d.ts +11 -0
- package/dist/util/exclusiveFile.js +108 -0
- package/dist/util/legacyShellHook.d.ts +3 -0
- package/dist/util/legacyShellHook.js +23 -0
- package/dist/util/log.d.ts +6 -0
- package/dist/util/log.js +29 -0
- package/dist/util/ownedSessions.d.ts +7 -0
- package/dist/util/ownedSessions.js +39 -0
- package/dist/util/timeout.d.ts +5 -0
- package/dist/util/timeout.js +39 -0
- package/package.json +15 -8
- package/bin/opencode-memory +0 -1436
- package/dist/memory.d.ts +0 -25
- package/dist/memory.js +0 -203
- package/dist/memoryScan.d.ts +0 -20
- package/dist/memoryScan.js +0 -106
- package/dist/nativeExtraction.d.ts +0 -2
- package/dist/nativeExtraction.js +0 -38
- package/dist/paths.js +0 -141
- package/dist/prompt.d.ts +0 -4
- package/dist/recall.d.ts +0 -12
- package/dist/recallSelector.js +0 -154
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// "Ignore memory" handling. Claude Code's semantics are session-scoped: once the user asks to
|
|
2
|
+
// ignore memory, it stays ignored until they explicitly ask for it back.
|
|
3
|
+
import { AUTO_MEMORY_MARKER } from "../prompt/systemPrompt.js";
|
|
4
|
+
import { extractUserQuery, roleOf } from "./messages.js";
|
|
5
|
+
export function detectIgnoreMemory(query) {
|
|
6
|
+
if (!query)
|
|
7
|
+
return false;
|
|
8
|
+
const normalized = query.toLowerCase();
|
|
9
|
+
return (/(ignore|don't use|do not use|without|skip)\s+(the\s+|your\s+)?memory/.test(normalized) ||
|
|
10
|
+
/memory\s+(should be|must be)?\s*ignored/.test(normalized));
|
|
11
|
+
}
|
|
12
|
+
export function detectResumeMemory(query) {
|
|
13
|
+
if (!query)
|
|
14
|
+
return false;
|
|
15
|
+
const normalized = query.toLowerCase();
|
|
16
|
+
return (/(use|enable|resume|restore|bring back|turn on)\s+(the\s+|your\s+)?memory(\s+again)?/.test(normalized) ||
|
|
17
|
+
/memory\s+(back\s+)?on\b/.test(normalized) ||
|
|
18
|
+
/stop ignoring\s+(the\s+|your\s+)?memory/.test(normalized));
|
|
19
|
+
}
|
|
20
|
+
// Replays every user message in order and returns whether memory is ignored at the end. Used to
|
|
21
|
+
// rebuild session state that the coordinator no longer holds (process restart, cache eviction), so
|
|
22
|
+
// an "ignore memory" said earlier in the session keeps applying without the user repeating it.
|
|
23
|
+
export function deriveIgnoredFromHistory(messages) {
|
|
24
|
+
let ignored = false;
|
|
25
|
+
for (const message of messages) {
|
|
26
|
+
if (roleOf(message) !== "user")
|
|
27
|
+
continue;
|
|
28
|
+
const query = extractUserQuery(message);
|
|
29
|
+
if (detectIgnoreMemory(query))
|
|
30
|
+
ignored = true;
|
|
31
|
+
else if (ignored && detectResumeMemory(query))
|
|
32
|
+
ignored = false;
|
|
33
|
+
}
|
|
34
|
+
return ignored;
|
|
35
|
+
}
|
|
36
|
+
export function isAutoMemoryPart(part) {
|
|
37
|
+
if (!part || typeof part !== "object")
|
|
38
|
+
return false;
|
|
39
|
+
const text = part.text;
|
|
40
|
+
return typeof text === "string" && text.trimStart().startsWith(AUTO_MEMORY_MARKER);
|
|
41
|
+
}
|
|
42
|
+
// Drops the plugin's own system segment from system-role messages; messages left without parts
|
|
43
|
+
// are removed entirely.
|
|
44
|
+
export function stripAutoMemoryParts(messages) {
|
|
45
|
+
return messages
|
|
46
|
+
.map((message) => {
|
|
47
|
+
if (roleOf(message) !== "system" || !Array.isArray(message.parts))
|
|
48
|
+
return message;
|
|
49
|
+
const parts = message.parts.filter((part) => !isAutoMemoryPart(part));
|
|
50
|
+
return parts.length === message.parts.length ? message : { ...message, parts };
|
|
51
|
+
})
|
|
52
|
+
.filter((message) => !Array.isArray(message.parts) || message.parts.length > 0);
|
|
53
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ChatMessage } from "../sdk.js";
|
|
2
|
+
export type TurnInfo = {
|
|
3
|
+
sessionID?: string;
|
|
4
|
+
query?: string;
|
|
5
|
+
messageID?: string;
|
|
6
|
+
messageIndex?: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function roleOf(message: ChatMessage): string;
|
|
9
|
+
export declare function extractUserQuery(message: ChatMessage): string | undefined;
|
|
10
|
+
export declare function getLastUserQuery(messages: readonly ChatMessage[]): TurnInfo;
|
|
11
|
+
export declare function buildTurnID(sessionID: string, turn: TurnInfo): string;
|
|
12
|
+
export declare function extractSurfacedMemoryKeys(systemText: string): Set<string>;
|
|
13
|
+
export declare function collectSurfacedMemoryKeys(messages: readonly ChatMessage[]): Set<string>;
|
|
14
|
+
export declare function extractRecentTools(messages: readonly ChatMessage[]): string[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Pure helpers over the SDK message shape passed to `experimental.chat.messages.transform`.
|
|
2
|
+
import { RECALLED_MEMORIES_HEADING } from "../prompt/systemPrompt.js";
|
|
3
|
+
// Older runtimes and tests may hand over messages whose `info` lacks the full SDK shape; only the
|
|
4
|
+
// fields actually used are read, defensively.
|
|
5
|
+
export function roleOf(message) {
|
|
6
|
+
return String(message.info?.role ?? "");
|
|
7
|
+
}
|
|
8
|
+
function partText(part) {
|
|
9
|
+
if (!part || typeof part !== "object")
|
|
10
|
+
return undefined;
|
|
11
|
+
const text = part.text;
|
|
12
|
+
return typeof text === "string" ? text : undefined;
|
|
13
|
+
}
|
|
14
|
+
export function extractUserQuery(message) {
|
|
15
|
+
if (!Array.isArray(message.parts))
|
|
16
|
+
return undefined;
|
|
17
|
+
const text = message.parts
|
|
18
|
+
.filter((part) => part.type === "text")
|
|
19
|
+
.map((part) => partText(part) ?? "")
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join("\n")
|
|
22
|
+
.trim();
|
|
23
|
+
return text || undefined;
|
|
24
|
+
}
|
|
25
|
+
export function getLastUserQuery(messages) {
|
|
26
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
27
|
+
const message = messages[i];
|
|
28
|
+
if (!message || roleOf(message) !== "user")
|
|
29
|
+
continue;
|
|
30
|
+
const info = message.info;
|
|
31
|
+
return {
|
|
32
|
+
query: extractUserQuery(message),
|
|
33
|
+
sessionID: typeof info?.sessionID === "string" ? info.sessionID : undefined,
|
|
34
|
+
messageID: typeof info?.id === "string" ? info.id : undefined,
|
|
35
|
+
messageIndex: i,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
// One user turn may drive several LLM calls (tool loops); they share a turn ID so recall runs once.
|
|
41
|
+
export function buildTurnID(sessionID, turn) {
|
|
42
|
+
return `${sessionID}:${turn.messageID ?? `${turn.messageIndex ?? -1}:${turn.query ?? ""}`}`;
|
|
43
|
+
}
|
|
44
|
+
// Parses "### <name> (<type>)" headers from the ## Recalled Memories section
|
|
45
|
+
// of system prompts. After compaction old system messages disappear, so
|
|
46
|
+
// the returned set naturally shrinks — no manual reset needed.
|
|
47
|
+
export function extractSurfacedMemoryKeys(systemText) {
|
|
48
|
+
const keys = new Set();
|
|
49
|
+
const recalledSection = systemText.indexOf(RECALLED_MEMORIES_HEADING);
|
|
50
|
+
if (recalledSection === -1)
|
|
51
|
+
return keys;
|
|
52
|
+
const headerPattern = /^### (.+?) \((\w+)\)/gm;
|
|
53
|
+
const section = systemText.slice(recalledSection);
|
|
54
|
+
for (let match = headerPattern.exec(section); match !== null; match = headerPattern.exec(section)) {
|
|
55
|
+
keys.add(`${match[1]}|${match[2]}`);
|
|
56
|
+
}
|
|
57
|
+
return keys;
|
|
58
|
+
}
|
|
59
|
+
export function collectSurfacedMemoryKeys(messages) {
|
|
60
|
+
const keys = new Set();
|
|
61
|
+
for (const message of messages) {
|
|
62
|
+
if (roleOf(message) !== "system" || !Array.isArray(message.parts))
|
|
63
|
+
continue;
|
|
64
|
+
for (const part of message.parts) {
|
|
65
|
+
const text = partText(part);
|
|
66
|
+
if (!text)
|
|
67
|
+
continue;
|
|
68
|
+
for (const key of extractSurfacedMemoryKeys(text))
|
|
69
|
+
keys.add(key);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return keys;
|
|
73
|
+
}
|
|
74
|
+
// Only completed tools — matches Claude Code's collectRecentSuccessfulTools().
|
|
75
|
+
export function extractRecentTools(messages) {
|
|
76
|
+
const tools = [];
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
for (const message of messages) {
|
|
79
|
+
if (!Array.isArray(message.parts))
|
|
80
|
+
continue;
|
|
81
|
+
for (const part of message.parts) {
|
|
82
|
+
if (!part || typeof part !== "object")
|
|
83
|
+
continue;
|
|
84
|
+
const p = part;
|
|
85
|
+
if (p.type !== "tool" || !p.tool)
|
|
86
|
+
continue;
|
|
87
|
+
if (p.state?.status !== "completed")
|
|
88
|
+
continue;
|
|
89
|
+
if (seen.has(p.tool))
|
|
90
|
+
continue;
|
|
91
|
+
seen.add(p.tool);
|
|
92
|
+
tools.push(p.tool);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return tools;
|
|
96
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
1
|
+
import type { Plugin, PluginModule } from "@opencode-ai/plugin";
|
|
2
|
+
export declare const PLUGIN_ID = "opencode-claude-memory";
|
|
3
|
+
export declare const createMemoryPlugin: (env?: NodeJS.ProcessEnv, homeDir?: string) => Plugin;
|
|
2
4
|
export declare const MemoryPlugin: Plugin;
|
|
5
|
+
declare const plugin: PluginModule;
|
|
6
|
+
export default plugin;
|
|
7
|
+
export { MEMORY_AGENTS, type MemoryConfig, type MemoryOptions, MemoryOptionsSchema } from "./config.js";
|
|
8
|
+
export { MemoryStore } from "./store/MemoryStore.js";
|