@rvboris/opencode-mempalace 0.2.1 → 0.3.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 +137 -96
- package/README.ru.md +136 -95
- package/dist/bridge/mempalace_adapter.py +5 -1
- package/dist/plugin/hooks/event.js +37 -3
- package/dist/plugin/hooks/system.js +2 -0
- package/dist/plugin/index.js +2 -0
- package/dist/plugin/lib/constants.d.ts +3 -0
- package/dist/plugin/lib/constants.js +3 -0
- package/dist/plugin/lib/status.d.ts +105 -0
- package/dist/plugin/lib/status.js +537 -0
- package/dist/plugin/lib/types.d.ts +2 -2
- package/dist/plugin/lib/types.js +2 -2
- package/dist/plugin/tools/mempalace-memory.js +36 -1
- package/dist/plugin/tools/mempalace-status.d.ts +11 -0
- package/dist/plugin/tools/mempalace-status.js +18 -0
- package/dist/plugin/tui/hud.d.ts +3 -0
- package/dist/plugin/tui/hud.js +54 -0
- package/dist/plugin/tui/index.d.ts +6 -0
- package/dist/plugin/tui/index.js +9 -0
- package/package.json +14 -1
|
@@ -2,9 +2,11 @@ import { AutosaveReason, AutosaveStatus, buildMessageSnapshot, getMessageSnapsho
|
|
|
2
2
|
import { executeAdapter } from "../lib/adapter";
|
|
3
3
|
import { loadConfig } from "../lib/config";
|
|
4
4
|
import { COMPACTION_CONTEXT_MESSAGE, DEFAULT_AGENT_NAME, LOG_MESSAGES } from "../lib/constants";
|
|
5
|
+
import { sanitizeText } from "../lib/derive";
|
|
5
6
|
import { getProjectName, loadSessionMessages } from "../lib/opencode";
|
|
6
7
|
import { redactSecrets } from "../lib/privacy";
|
|
7
8
|
import { getProjectScope } from "../lib/scope";
|
|
9
|
+
import { recordAutosave } from "../lib/status";
|
|
8
10
|
import { writeLog } from "../lib/log";
|
|
9
11
|
import { SESSION_EVENT_TYPES } from "../lib/types";
|
|
10
12
|
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -65,6 +67,12 @@ export const eventHooks = (ctx) => {
|
|
|
65
67
|
});
|
|
66
68
|
if (event.type === "session.error") {
|
|
67
69
|
const failed = markFailed(sessionId);
|
|
70
|
+
await recordAutosave({
|
|
71
|
+
sessionId,
|
|
72
|
+
outcome: "failed",
|
|
73
|
+
reason: toReason(event.type),
|
|
74
|
+
sourcePreview: getMessageSnapshot(sessionId)?.lastUserMessage,
|
|
75
|
+
});
|
|
68
76
|
await writeLog("ERROR", LOG_MESSAGES.autosaveFailedOnSessionError, {
|
|
69
77
|
sessionId,
|
|
70
78
|
retryCount: failed.retryCount,
|
|
@@ -80,6 +88,12 @@ export const eventHooks = (ctx) => {
|
|
|
80
88
|
}
|
|
81
89
|
const { transcript, transcriptDigest, userDigest } = snapshot;
|
|
82
90
|
if (!shouldScheduleAutosave(sessionId, userDigest, transcriptDigest)) {
|
|
91
|
+
await recordAutosave({
|
|
92
|
+
sessionId,
|
|
93
|
+
outcome: "skipped",
|
|
94
|
+
reason: toReason(event.type),
|
|
95
|
+
sourcePreview: snapshot.lastUserMessage,
|
|
96
|
+
});
|
|
83
97
|
await writeLog("INFO", LOG_MESSAGES.skippingAutosaveState, {
|
|
84
98
|
sessionId,
|
|
85
99
|
reason: toReason(event.type),
|
|
@@ -89,22 +103,35 @@ export const eventHooks = (ctx) => {
|
|
|
89
103
|
});
|
|
90
104
|
return;
|
|
91
105
|
}
|
|
92
|
-
const
|
|
93
|
-
if (!
|
|
106
|
+
const sanitizedTranscript = sanitizeText(redactSecrets(transcript));
|
|
107
|
+
if (!sanitizedTranscript.trim()) {
|
|
94
108
|
markAutosaveComplete(sessionId, userDigest, transcriptDigest, AutosaveStatus.Noop);
|
|
109
|
+
await recordAutosave({
|
|
110
|
+
sessionId,
|
|
111
|
+
outcome: "skipped",
|
|
112
|
+
reason: toReason(event.type),
|
|
113
|
+
sourcePreview: snapshot.lastUserMessage,
|
|
114
|
+
});
|
|
95
115
|
await writeLog("INFO", LOG_MESSAGES.autosaveSkippedEmptyTranscript, { sessionId });
|
|
96
116
|
return;
|
|
97
117
|
}
|
|
98
118
|
const wing = getProjectScope(getProjectName(ctx.project), config.projectWingPrefix).wing;
|
|
99
119
|
const result = await executeAdapter(ctx.$, {
|
|
100
120
|
mode: "mine_messages",
|
|
101
|
-
transcript:
|
|
121
|
+
transcript: sanitizedTranscript,
|
|
102
122
|
wing,
|
|
103
123
|
extract_mode: config.autoMineExtractMode,
|
|
104
124
|
agent: DEFAULT_AGENT_NAME,
|
|
105
125
|
});
|
|
106
126
|
if (result?.success === false) {
|
|
107
127
|
const failed = markFailed(sessionId);
|
|
128
|
+
await recordAutosave({
|
|
129
|
+
sessionId,
|
|
130
|
+
outcome: "failed",
|
|
131
|
+
reason: toReason(event.type),
|
|
132
|
+
wing,
|
|
133
|
+
sourcePreview: snapshot.lastUserMessage,
|
|
134
|
+
});
|
|
108
135
|
await writeLog("ERROR", LOG_MESSAGES.autosaveMiningFailed, {
|
|
109
136
|
sessionId,
|
|
110
137
|
retryCount: failed.retryCount,
|
|
@@ -113,6 +140,13 @@ export const eventHooks = (ctx) => {
|
|
|
113
140
|
return;
|
|
114
141
|
}
|
|
115
142
|
const completed = markAutosaveComplete(sessionId, userDigest, transcriptDigest, AutosaveStatus.Saved);
|
|
143
|
+
await recordAutosave({
|
|
144
|
+
sessionId,
|
|
145
|
+
outcome: "saved",
|
|
146
|
+
reason: toReason(event.type),
|
|
147
|
+
wing,
|
|
148
|
+
sourcePreview: snapshot.lastUserMessage,
|
|
149
|
+
});
|
|
116
150
|
await writeLog("INFO", LOG_MESSAGES.autosaveMinedSessionContext, {
|
|
117
151
|
sessionId,
|
|
118
152
|
reason: toReason(event.type),
|
|
@@ -4,6 +4,7 @@ import { LOG_MESSAGES } from "../lib/constants";
|
|
|
4
4
|
import { buildKeywordSaveInstruction, buildRetrievalInstruction } from "../lib/context";
|
|
5
5
|
import { writeLog } from "../lib/log";
|
|
6
6
|
import { getProjectName, loadSessionMessages } from "../lib/opencode";
|
|
7
|
+
import { recordRetrievalPrompt } from "../lib/status";
|
|
7
8
|
export const systemHooks = (ctx) => {
|
|
8
9
|
return {
|
|
9
10
|
"experimental.chat.system.transform": async (input, output) => {
|
|
@@ -29,6 +30,7 @@ export const systemHooks = (ctx) => {
|
|
|
29
30
|
lastUserMessage,
|
|
30
31
|
}));
|
|
31
32
|
markRetrievalInjected(sessionId);
|
|
33
|
+
await recordRetrievalPrompt({ sessionId, queryPreview: lastUserMessage });
|
|
32
34
|
await writeLog("INFO", LOG_MESSAGES.injectedRetrievalInstruction, { sessionId });
|
|
33
35
|
}
|
|
34
36
|
if (state.keywordSavePending) {
|
package/dist/plugin/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { systemHooks } from "./hooks/system";
|
|
|
3
3
|
import { toolHooks } from "./hooks/tool";
|
|
4
4
|
import { setLogger } from "./lib/log";
|
|
5
5
|
import { mempalaceMemoryTool } from "./tools/mempalace-memory";
|
|
6
|
+
import { mempalaceStatusTool } from "./tools/mempalace-status";
|
|
6
7
|
export const MempalaceAutosavePlugin = async (ctx) => {
|
|
7
8
|
setLogger(ctx.client);
|
|
8
9
|
return {
|
|
@@ -11,6 +12,7 @@ export const MempalaceAutosavePlugin = async (ctx) => {
|
|
|
11
12
|
...toolHooks(),
|
|
12
13
|
tool: {
|
|
13
14
|
mempalace_memory: mempalaceMemoryTool(ctx),
|
|
15
|
+
mempalace_status: mempalaceStatusTool(),
|
|
14
16
|
},
|
|
15
17
|
};
|
|
16
18
|
};
|
|
@@ -21,6 +21,7 @@ export declare const ENV_KEYS: {
|
|
|
21
21
|
readonly userWingPrefix: "MEMPALACE_USER_WING_PREFIX";
|
|
22
22
|
readonly projectWingPrefix: "MEMPALACE_PROJECT_WING_PREFIX";
|
|
23
23
|
readonly autosaveLogFile: "MEMPALACE_AUTOSAVE_LOG_FILE";
|
|
24
|
+
readonly statusFile: "MEMPALACE_STATUS_FILE";
|
|
24
25
|
readonly adapterPython: "MEMPALACE_ADAPTER_PYTHON";
|
|
25
26
|
readonly adapterTimeoutMs: "MEMPALACE_ADAPTER_TIMEOUT_MS";
|
|
26
27
|
};
|
|
@@ -29,10 +30,12 @@ export declare const MAX_TRANSCRIPT_MESSAGES_DIGEST: 50;
|
|
|
29
30
|
export declare const DATE_ISO_SLICE: 10;
|
|
30
31
|
export declare const SERVICE_NAME: "mempalace-autosave";
|
|
31
32
|
export declare const LOG_FILE_NAME: "opencode_autosave.log";
|
|
33
|
+
export declare const STATUS_FILE_NAME: "opencode_status.json";
|
|
32
34
|
export declare const DIRECT_MEMPALACE_MUTATION_TOOLS: readonly ["mempalace_add_drawer", "mempalace_kg_add", "mempalace_diary_write", "mcp-router_mempalace_add_drawer", "mcp-router_mempalace_kg_add", "mcp-router_mempalace_diary_write"];
|
|
33
35
|
export declare const COMPACTION_CONTEXT_MESSAGE: "MemPalace retrieval may be useful after compaction. Search relevant project and user memory if needed before answering.";
|
|
34
36
|
export declare const TOOL_DESCRIPTIONS: {
|
|
35
37
|
readonly mempalaceMemory: "Save or search memory in MemPalace with scope/privacy enforcement";
|
|
38
|
+
readonly mempalaceStatus: "Show recent visible evidence that MemPalace retrieval or autosave helped";
|
|
36
39
|
};
|
|
37
40
|
export declare const TOOL_ERROR_MESSAGES: {
|
|
38
41
|
readonly directMutationBlocked: "Use mempalace_memory instead of direct MemPalace mutation tools";
|
|
@@ -21,6 +21,7 @@ export const ENV_KEYS = {
|
|
|
21
21
|
userWingPrefix: "MEMPALACE_USER_WING_PREFIX",
|
|
22
22
|
projectWingPrefix: "MEMPALACE_PROJECT_WING_PREFIX",
|
|
23
23
|
autosaveLogFile: "MEMPALACE_AUTOSAVE_LOG_FILE",
|
|
24
|
+
statusFile: "MEMPALACE_STATUS_FILE",
|
|
24
25
|
adapterPython: "MEMPALACE_ADAPTER_PYTHON",
|
|
25
26
|
adapterTimeoutMs: "MEMPALACE_ADAPTER_TIMEOUT_MS",
|
|
26
27
|
};
|
|
@@ -29,6 +30,7 @@ export const MAX_TRANSCRIPT_MESSAGES_DIGEST = 50;
|
|
|
29
30
|
export const DATE_ISO_SLICE = 10;
|
|
30
31
|
export const SERVICE_NAME = "mempalace-autosave";
|
|
31
32
|
export const LOG_FILE_NAME = "opencode_autosave.log";
|
|
33
|
+
export const STATUS_FILE_NAME = "opencode_status.json";
|
|
32
34
|
export const DIRECT_MEMPALACE_MUTATION_TOOLS = [
|
|
33
35
|
"mempalace_add_drawer",
|
|
34
36
|
"mempalace_kg_add",
|
|
@@ -40,6 +42,7 @@ export const DIRECT_MEMPALACE_MUTATION_TOOLS = [
|
|
|
40
42
|
export const COMPACTION_CONTEXT_MESSAGE = "MemPalace retrieval may be useful after compaction. Search relevant project and user memory if needed before answering.";
|
|
41
43
|
export const TOOL_DESCRIPTIONS = {
|
|
42
44
|
mempalaceMemory: "Save or search memory in MemPalace with scope/privacy enforcement",
|
|
45
|
+
mempalaceStatus: "Show recent visible evidence that MemPalace retrieval or autosave helped",
|
|
43
46
|
};
|
|
44
47
|
export const TOOL_ERROR_MESSAGES = {
|
|
45
48
|
directMutationBlocked: "Use mempalace_memory instead of direct MemPalace mutation tools",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { AdapterResponse, MemoryScope } from "./types";
|
|
2
|
+
export type StatusCounters = {
|
|
3
|
+
retrievalPrompts: number;
|
|
4
|
+
retrievalSearches: number;
|
|
5
|
+
retrievalHits: number;
|
|
6
|
+
autosavesCompleted: number;
|
|
7
|
+
autosavesSkipped: number;
|
|
8
|
+
autosavesFailed: number;
|
|
9
|
+
manualWrites: number;
|
|
10
|
+
};
|
|
11
|
+
export type StatusSessionCounters = {
|
|
12
|
+
retrievalSearches: number;
|
|
13
|
+
retrievalHits: number;
|
|
14
|
+
autosavesCompleted: number;
|
|
15
|
+
autosavesSkipped: number;
|
|
16
|
+
autosavesFailed: number;
|
|
17
|
+
manualWrites: number;
|
|
18
|
+
};
|
|
19
|
+
export type StatusRetrievalPrompt = {
|
|
20
|
+
sessionId?: string;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
queryPreview: string;
|
|
23
|
+
};
|
|
24
|
+
export type StatusRetrieval = {
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
timestamp: string;
|
|
27
|
+
scope: MemoryScope;
|
|
28
|
+
room?: string;
|
|
29
|
+
queryPreview: string;
|
|
30
|
+
resultCount?: number;
|
|
31
|
+
previews: string[];
|
|
32
|
+
};
|
|
33
|
+
export type StatusAutosaveOutcome = "saved" | "skipped" | "failed";
|
|
34
|
+
export type StatusAutosave = {
|
|
35
|
+
sessionId: string;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
outcome: StatusAutosaveOutcome;
|
|
38
|
+
reason: string;
|
|
39
|
+
wing?: string;
|
|
40
|
+
sourcePreview?: string;
|
|
41
|
+
};
|
|
42
|
+
export type StatusWrite = {
|
|
43
|
+
timestamp: string;
|
|
44
|
+
mode: "save" | "kg_add" | "diary_write";
|
|
45
|
+
scope?: MemoryScope;
|
|
46
|
+
room?: string;
|
|
47
|
+
preview: string;
|
|
48
|
+
};
|
|
49
|
+
export type StatusSessionState = {
|
|
50
|
+
updatedAt: string;
|
|
51
|
+
counters: StatusSessionCounters;
|
|
52
|
+
lastRetrievalPrompt?: StatusRetrievalPrompt;
|
|
53
|
+
lastRetrieval?: StatusRetrieval;
|
|
54
|
+
lastAutosave?: StatusAutosave;
|
|
55
|
+
lastWrite?: StatusWrite;
|
|
56
|
+
};
|
|
57
|
+
export type StatusState = {
|
|
58
|
+
version: 2;
|
|
59
|
+
updatedAt: string;
|
|
60
|
+
counters: StatusCounters;
|
|
61
|
+
helpedSessionIds: string[];
|
|
62
|
+
sessions: Record<string, StatusSessionState>;
|
|
63
|
+
lastRetrievalPrompt?: StatusRetrievalPrompt;
|
|
64
|
+
lastRetrieval?: StatusRetrieval;
|
|
65
|
+
lastAutosave?: StatusAutosave;
|
|
66
|
+
lastWrite?: StatusWrite;
|
|
67
|
+
};
|
|
68
|
+
type SearchResultSummary = {
|
|
69
|
+
resultCount?: number;
|
|
70
|
+
previews: string[];
|
|
71
|
+
};
|
|
72
|
+
export declare const summarizeSearchResult: (result: AdapterResponse) => SearchResultSummary;
|
|
73
|
+
export declare const recordRetrievalPrompt: (input: {
|
|
74
|
+
sessionId?: string;
|
|
75
|
+
queryPreview: string;
|
|
76
|
+
}) => Promise<void>;
|
|
77
|
+
export declare const recordRetrievalSearch: (input: {
|
|
78
|
+
sessionId?: string;
|
|
79
|
+
scope: MemoryScope;
|
|
80
|
+
room?: string;
|
|
81
|
+
query: string;
|
|
82
|
+
result: AdapterResponse;
|
|
83
|
+
}) => Promise<void>;
|
|
84
|
+
export declare const recordMemoryWrite: (input: {
|
|
85
|
+
sessionId?: string;
|
|
86
|
+
mode: "save" | "kg_add" | "diary_write";
|
|
87
|
+
scope?: MemoryScope;
|
|
88
|
+
room?: string;
|
|
89
|
+
preview: string;
|
|
90
|
+
}) => Promise<void>;
|
|
91
|
+
export declare const recordAutosave: (input: {
|
|
92
|
+
sessionId: string;
|
|
93
|
+
outcome: StatusAutosaveOutcome;
|
|
94
|
+
reason: string;
|
|
95
|
+
wing?: string;
|
|
96
|
+
sourcePreview?: string;
|
|
97
|
+
}) => Promise<void>;
|
|
98
|
+
export declare const readStatusState: () => Promise<StatusState>;
|
|
99
|
+
export declare const resetStatusState: () => Promise<void>;
|
|
100
|
+
export declare const formatSessionHud: (state: StatusState, sessionId: string) => string;
|
|
101
|
+
export declare const formatStatusSummary: (state: StatusState, sessionId?: string, options?: {
|
|
102
|
+
verbose?: boolean;
|
|
103
|
+
compact?: boolean;
|
|
104
|
+
}) => string;
|
|
105
|
+
export {};
|