@rvboris/opencode-mempalace 0.1.0 → 0.2.1

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.
@@ -1,3 +1,4 @@
1
+ import type { MessageLike } from "./types";
1
2
  export declare enum AutosaveReason {
2
3
  Idle = "idle",
3
4
  Compacted = "compacted",
@@ -5,48 +6,42 @@ export declare enum AutosaveReason {
5
6
  }
6
7
  export declare enum AutosaveStatus {
7
8
  Idle = "idle",
8
- Pending = "pending",
9
- Running = "running",
10
9
  Saved = "saved",
11
10
  Noop = "noop",
12
11
  Failed = "failed"
13
12
  }
14
13
  export type SessionAutosaveState = {
15
14
  status: AutosaveStatus;
16
- pendingReason?: AutosaveReason;
17
15
  retrievalPending: boolean;
18
16
  pendingRetrievalUserDigest?: string;
19
17
  keywordSavePending: boolean;
20
- pendingUserDigest?: string;
21
- pendingTranscriptDigest?: string;
22
18
  lastHandledUserDigest?: string;
23
19
  lastHandledTranscriptDigest?: string;
24
- successfulToolCalls: string[];
25
- successfulToolCallIds: string[];
26
- mutationToolCallIds: string[];
27
- runningSince?: number;
28
- lastToolCallAt?: number;
29
20
  lastRetrievedUserDigest?: string;
30
21
  retryCount: number;
31
22
  lastFailureAt?: number;
23
+ messageSnapshot?: SessionMessageSnapshot;
32
24
  updatedAt: number;
33
25
  };
34
- export declare const buildUserDigest: (messages: any[]) => string;
35
- export declare const buildTranscriptDigest: (messages: any[]) => string;
26
+ export type SessionMessageSnapshot = {
27
+ userDigest: string;
28
+ transcriptDigest: string;
29
+ lastUserMessage: string;
30
+ transcript: string;
31
+ };
32
+ export declare const buildUserDigest: (messages: readonly MessageLike[] | null | undefined) => string;
33
+ export declare const buildTranscriptDigest: (messages: readonly MessageLike[] | null | undefined) => string;
34
+ export declare const buildMessageSnapshot: (messages: readonly MessageLike[] | null | undefined) => SessionMessageSnapshot;
36
35
  export declare const getSessionState: (sessionId: string) => SessionAutosaveState;
37
- export declare const setCurrentTurnSessionId: (sessionId: string | undefined) => void;
38
- export declare const getCurrentTurnSessionId: () => string | undefined;
39
36
  export declare const resetAllStates: () => void;
40
- export declare const markPending: (sessionId: string, reason: AutosaveReason, userDigest: string, transcriptDigest: string) => void;
37
+ export declare const markAutosaveComplete: (sessionId: string, userDigest: string, transcriptDigest: string, status: AutosaveStatus.Saved | AutosaveStatus.Noop) => SessionAutosaveState;
41
38
  export declare const markRetrievalInjected: (sessionId: string) => void;
42
39
  export declare const markRetrievalPending: (sessionId: string, userDigest: string) => void;
40
+ export declare const setMessageSnapshot: (sessionId: string, snapshot: SessionMessageSnapshot) => void;
41
+ export declare const getMessageSnapshot: (sessionId: string) => SessionMessageSnapshot | undefined;
43
42
  export declare const markKeywordSavePending: (sessionId: string) => void;
44
43
  export declare const clearKeywordSavePending: (sessionId: string) => void;
45
- export declare const extractLastUserMessage: (messages: any[]) => string;
46
- export declare const startAutosave: (sessionId: string) => void;
47
- export declare const markMutationToolCall: (sessionId: string, callId?: string) => void;
48
- export declare const shouldCountSuccessfulTool: (sessionId: string, tool: string, callId?: string) => boolean;
49
- export declare const recordSuccessfulTool: (sessionId: string, tool: string, callId?: string) => void;
50
- export declare const finalizeAutosave: (sessionId: string) => SessionAutosaveState;
44
+ export declare const extractLastUserMessage: (messages: readonly MessageLike[] | null | undefined) => string;
51
45
  export declare const markFailed: (sessionId: string) => SessionAutosaveState;
52
46
  export declare const shouldScheduleAutosave: (sessionId: string, userDigest: string, transcriptDigest: string) => boolean;
47
+ export declare const buildTranscriptText: (messages: readonly MessageLike[] | null | undefined) => string;
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import { sanitizeText } from "./derive";
3
3
  import { stripPrivateContent } from "./privacy";
4
+ import { MAX_USER_MESSAGES_DIGEST, MAX_TRANSCRIPT_MESSAGES_DIGEST } from "./constants";
4
5
  export var AutosaveReason;
5
6
  (function (AutosaveReason) {
6
7
  AutosaveReason["Idle"] = "idle";
@@ -10,14 +11,11 @@ export var AutosaveReason;
10
11
  export var AutosaveStatus;
11
12
  (function (AutosaveStatus) {
12
13
  AutosaveStatus["Idle"] = "idle";
13
- AutosaveStatus["Pending"] = "pending";
14
- AutosaveStatus["Running"] = "running";
15
14
  AutosaveStatus["Saved"] = "saved";
16
15
  AutosaveStatus["Noop"] = "noop";
17
16
  AutosaveStatus["Failed"] = "failed";
18
17
  })(AutosaveStatus || (AutosaveStatus = {}));
19
18
  const states = new Map();
20
- let currentTurnSessionId;
21
19
  const MAX_SESSIONS = 200;
22
20
  const STATE_TTL_MS = 1000 * 60 * 60 * 12;
23
21
  const RETRY_COOLDOWN_MS = 1000 * 30;
@@ -28,45 +26,46 @@ const extractTextParts = (message) => {
28
26
  return parts;
29
27
  if (typeof message.content === "string")
30
28
  parts.push(message.content);
31
- if (Array.isArray(message.parts)) {
32
- for (const part of message.parts) {
33
- if (!part)
34
- continue;
35
- if (typeof part.text === "string")
36
- parts.push(part.text);
37
- if (typeof part.content === "string")
38
- parts.push(part.content);
39
- }
29
+ for (const part of message.parts ?? []) {
30
+ if ("text" in part && typeof part.text === "string")
31
+ parts.push(part.text);
32
+ if ("content" in part && typeof part.content === "string")
33
+ parts.push(part.content);
40
34
  }
41
- if (message.info && typeof message.info?.content === "string") {
35
+ if (message.info && "content" in message.info && typeof message.info.content === "string") {
42
36
  parts.push(message.info.content);
43
37
  }
44
38
  return parts;
45
39
  };
46
- const extractRole = (message) => message?.role || message?.info?.role || "unknown";
40
+ const extractRole = (message) => message?.role ?? message?.info?.role ?? "unknown";
47
41
  const fingerprint = (chunks) => {
48
42
  return crypto.createHash("sha256").update(JSON.stringify(chunks), "utf8").digest("hex").slice(0, 16);
49
43
  };
50
44
  export const buildUserDigest = (messages) => {
51
- const normalized = (messages || [])
45
+ const normalized = (messages ?? [])
52
46
  .filter((message) => extractRole(message) === "user")
53
- .slice(-20)
47
+ .slice(-MAX_USER_MESSAGES_DIGEST)
54
48
  .map((message) => extractTextParts(message).map((part) => sanitizeText(part)).join("\n"));
55
49
  return fingerprint(normalized);
56
50
  };
57
51
  export const buildTranscriptDigest = (messages) => {
58
- const normalized = (messages || []).slice(-50).map((message) => {
52
+ const normalized = (messages ?? []).slice(-MAX_TRANSCRIPT_MESSAGES_DIGEST).map((message) => {
59
53
  const role = extractRole(message);
60
54
  const text = extractTextParts(message).map((part) => sanitizeText(part)).join("\n");
61
55
  return `${role}:${text}`;
62
56
  });
63
57
  return fingerprint(normalized);
64
58
  };
59
+ export const buildMessageSnapshot = (messages) => {
60
+ return {
61
+ userDigest: buildUserDigest(messages),
62
+ transcriptDigest: buildTranscriptDigest(messages),
63
+ lastUserMessage: extractLastUserMessage(messages),
64
+ transcript: buildTranscriptText(messages),
65
+ };
66
+ };
65
67
  const createState = () => ({
66
68
  status: AutosaveStatus.Idle,
67
- successfulToolCalls: [],
68
- successfulToolCallIds: [],
69
- mutationToolCallIds: [],
70
69
  retryCount: 0,
71
70
  retrievalPending: true,
72
71
  keywordSavePending: false,
@@ -84,10 +83,6 @@ export const getSessionState = (sessionId) => {
84
83
  evictOverflowStates();
85
84
  return state;
86
85
  };
87
- export const setCurrentTurnSessionId = (sessionId) => {
88
- currentTurnSessionId = sessionId;
89
- };
90
- export const getCurrentTurnSessionId = () => currentTurnSessionId;
91
86
  const evictExpiredStates = () => {
92
87
  const now = Date.now();
93
88
  for (const [sessionId, state] of states) {
@@ -105,18 +100,17 @@ const evictOverflowStates = () => {
105
100
  };
106
101
  export const resetAllStates = () => {
107
102
  states.clear();
108
- currentTurnSessionId = undefined;
109
103
  };
110
- export const markPending = (sessionId, reason, userDigest, transcriptDigest) => {
104
+ export const markAutosaveComplete = (sessionId, userDigest, transcriptDigest, status) => {
111
105
  const state = getSessionState(sessionId);
112
- state.status = AutosaveStatus.Pending;
113
- state.pendingReason = reason;
114
- state.pendingUserDigest = userDigest;
115
- state.pendingTranscriptDigest = transcriptDigest;
116
- state.successfulToolCalls = [];
117
- state.successfulToolCallIds = [];
118
- state.mutationToolCallIds = [];
106
+ state.status = status;
107
+ state.lastHandledUserDigest = userDigest;
108
+ state.lastHandledTranscriptDigest = transcriptDigest;
109
+ if (status === AutosaveStatus.Saved) {
110
+ state.retryCount = 0;
111
+ }
119
112
  state.updatedAt = Date.now();
113
+ return state;
120
114
  };
121
115
  export const markRetrievalInjected = (sessionId) => {
122
116
  const state = getSessionState(sessionId);
@@ -135,6 +129,14 @@ export const markRetrievalPending = (sessionId, userDigest) => {
135
129
  state.pendingRetrievalUserDigest = userDigest;
136
130
  state.updatedAt = Date.now();
137
131
  };
132
+ export const setMessageSnapshot = (sessionId, snapshot) => {
133
+ const state = getSessionState(sessionId);
134
+ state.messageSnapshot = snapshot;
135
+ state.updatedAt = Date.now();
136
+ };
137
+ export const getMessageSnapshot = (sessionId) => {
138
+ return getSessionState(sessionId).messageSnapshot;
139
+ };
138
140
  export const markKeywordSavePending = (sessionId) => {
139
141
  const state = getSessionState(sessionId);
140
142
  state.keywordSavePending = true;
@@ -146,88 +148,21 @@ export const clearKeywordSavePending = (sessionId) => {
146
148
  state.updatedAt = Date.now();
147
149
  };
148
150
  export const extractLastUserMessage = (messages) => {
149
- const userMessages = (messages || []).filter((message) => extractRole(message) === "user");
151
+ const userMessages = (messages ?? []).filter((message) => extractRole(message) === "user");
150
152
  const last = userMessages.at(-1);
151
153
  const text = extractTextParts(last).map((part) => stripPrivateContent(part)).join("\n").trim();
152
154
  return text;
153
155
  };
154
- export const startAutosave = (sessionId) => {
155
- const state = getSessionState(sessionId);
156
- state.status = AutosaveStatus.Running;
157
- state.successfulToolCalls = [];
158
- state.successfulToolCallIds = [];
159
- state.mutationToolCallIds = [];
160
- state.runningSince = Date.now();
161
- state.updatedAt = Date.now();
162
- };
163
- export const markMutationToolCall = (sessionId, callId) => {
164
- if (!callId)
165
- return;
166
- const state = getSessionState(sessionId);
167
- if (!state.mutationToolCallIds.includes(callId)) {
168
- state.mutationToolCallIds.push(callId);
169
- state.updatedAt = Date.now();
170
- }
171
- };
172
- export const shouldCountSuccessfulTool = (sessionId, tool, callId) => {
173
- const state = getSessionState(sessionId);
174
- if (tool === "mempalace_memory") {
175
- return !!callId && state.mutationToolCallIds.includes(callId);
176
- }
177
- return false;
178
- };
179
- export const recordSuccessfulTool = (sessionId, tool, callId) => {
180
- const state = getSessionState(sessionId);
181
- if (callId && state.successfulToolCallIds.includes(callId))
182
- return;
183
- if (!state.successfulToolCalls.includes(tool))
184
- state.successfulToolCalls.push(tool);
185
- if (callId)
186
- state.successfulToolCallIds.push(callId);
187
- state.lastToolCallAt = Date.now();
188
- state.updatedAt = Date.now();
189
- };
190
- export const finalizeAutosave = (sessionId) => {
191
- const state = getSessionState(sessionId);
192
- if (state.status !== AutosaveStatus.Running)
193
- return state;
194
- const succeeded = state.successfulToolCalls.length > 0 &&
195
- (!state.runningSince || (state.lastToolCallAt != null && state.lastToolCallAt >= state.runningSince));
196
- state.status = succeeded ? AutosaveStatus.Saved : AutosaveStatus.Noop;
197
- state.lastHandledUserDigest = state.pendingUserDigest;
198
- state.lastHandledTranscriptDigest = state.pendingTranscriptDigest;
199
- state.retryCount = succeeded ? 0 : state.retryCount;
200
- state.pendingReason = undefined;
201
- state.pendingUserDigest = undefined;
202
- state.pendingTranscriptDigest = undefined;
203
- state.successfulToolCalls = [];
204
- state.successfulToolCallIds = [];
205
- state.mutationToolCallIds = [];
206
- state.runningSince = undefined;
207
- state.lastToolCallAt = undefined;
208
- state.updatedAt = Date.now();
209
- return state;
210
- };
211
156
  export const markFailed = (sessionId) => {
212
157
  const state = getSessionState(sessionId);
213
158
  state.status = AutosaveStatus.Failed;
214
159
  state.retryCount += 1;
215
160
  state.lastFailureAt = Date.now();
216
- state.pendingReason = undefined;
217
- state.pendingUserDigest = undefined;
218
- state.pendingTranscriptDigest = undefined;
219
- state.successfulToolCalls = [];
220
- state.successfulToolCallIds = [];
221
- state.mutationToolCallIds = [];
222
- state.runningSince = undefined;
223
- state.lastToolCallAt = undefined;
224
161
  state.updatedAt = Date.now();
225
162
  return state;
226
163
  };
227
164
  export const shouldScheduleAutosave = (sessionId, userDigest, transcriptDigest) => {
228
165
  const state = getSessionState(sessionId);
229
- if (state.status === AutosaveStatus.Pending || state.status === AutosaveStatus.Running)
230
- return false;
231
166
  if (state.status === AutosaveStatus.Failed) {
232
167
  if (state.retryCount >= MAX_RETRIES)
233
168
  return false;
@@ -240,3 +175,14 @@ export const shouldScheduleAutosave = (sessionId, userDigest, transcriptDigest)
240
175
  return false;
241
176
  return true;
242
177
  };
178
+ export const buildTranscriptText = (messages) => {
179
+ const lines = [];
180
+ for (const message of messages ?? []) {
181
+ const role = extractRole(message);
182
+ const text = extractTextParts(message).map((part) => stripPrivateContent(part)).join("\n").trim();
183
+ if (!text)
184
+ continue;
185
+ lines.push(`${role.toUpperCase()}: ${text}`);
186
+ }
187
+ return lines.join("\n\n");
188
+ };
@@ -1,13 +1,14 @@
1
- export type MempalaceConfig = {
1
+ export type MempalaceConfig = Readonly<{
2
2
  autosaveEnabled: boolean;
3
3
  retrievalEnabled: boolean;
4
4
  keywordSaveEnabled: boolean;
5
+ autoMineExtractMode: string;
5
6
  maxInjectedItems: number;
6
7
  retrievalQueryLimit: number;
7
- keywordPatterns: string[];
8
+ keywordPatterns: readonly string[];
8
9
  privacyRedactionEnabled: boolean;
9
10
  userWingPrefix: string;
10
11
  projectWingPrefix: string;
11
- };
12
+ }>;
12
13
  export declare const loadConfig: () => Promise<MempalaceConfig>;
13
14
  export declare const resetConfig: () => void;
@@ -1,25 +1,58 @@
1
1
  import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
+ import { DEFAULT_EXTRACT_MODE, DEFAULT_INJECTED_ITEMS, DEFAULT_RETRIEVAL_LIMIT, DEFAULT_KEYWORD_PATTERNS, DEFAULT_PROJECT_WING_PREFIX, DEFAULT_USER_WING_PREFIX, CONFIG_PATH_SEGMENTS, ENV_KEYS, } from "./constants";
4
5
  const DEFAULT_CONFIG = {
5
6
  autosaveEnabled: true,
6
7
  retrievalEnabled: true,
7
8
  keywordSaveEnabled: true,
8
- maxInjectedItems: 6,
9
- retrievalQueryLimit: 5,
10
- keywordPatterns: ["remember", "save this", "don't forget", "note that"],
9
+ autoMineExtractMode: DEFAULT_EXTRACT_MODE,
10
+ maxInjectedItems: DEFAULT_INJECTED_ITEMS,
11
+ retrievalQueryLimit: DEFAULT_RETRIEVAL_LIMIT,
12
+ keywordPatterns: DEFAULT_KEYWORD_PATTERNS,
11
13
  privacyRedactionEnabled: true,
12
- userWingPrefix: "wing_user",
13
- projectWingPrefix: "wing_project",
14
+ userWingPrefix: DEFAULT_USER_WING_PREFIX,
15
+ projectWingPrefix: DEFAULT_PROJECT_WING_PREFIX,
14
16
  };
15
- const CONFIG_PATH = path.join(os.homedir(), ".config", "opencode", "mempalace.jsonc");
17
+ const CONFIG_PATH = path.join(os.homedir(), ...CONFIG_PATH_SEGMENTS);
16
18
  const stripJsonComments = (value) => {
17
19
  return value.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
18
20
  };
19
21
  const parseBoolean = (value, fallback) => {
20
22
  if (value == null)
21
23
  return fallback;
22
- return value !== "false";
24
+ const normalized = value.trim().toLowerCase();
25
+ if (["true", "1", "yes", "on"].includes(normalized))
26
+ return true;
27
+ if (["false", "0", "no", "off", ""].includes(normalized))
28
+ return false;
29
+ return fallback;
30
+ };
31
+ const isRecord = (value) => {
32
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33
+ };
34
+ const readBoolean = (value, fallback) => {
35
+ return typeof value === "boolean" ? value : fallback;
36
+ };
37
+ const readPositiveNumber = (value, fallback) => {
38
+ if (typeof value === "number" && Number.isFinite(value) && value > 0)
39
+ return Math.floor(value);
40
+ return fallback;
41
+ };
42
+ const readString = (value, fallback) => {
43
+ if (typeof value !== "string")
44
+ return fallback;
45
+ const normalized = value.trim();
46
+ return normalized || fallback;
47
+ };
48
+ const readStringArray = (value, fallback) => {
49
+ if (!Array.isArray(value))
50
+ return [...fallback];
51
+ const normalized = value
52
+ .filter((item) => typeof item === "string")
53
+ .map((item) => item.trim())
54
+ .filter(Boolean);
55
+ return normalized.length ? normalized : [...fallback];
23
56
  };
24
57
  let cachedConfig;
25
58
  export const loadConfig = async () => {
@@ -28,23 +61,29 @@ export const loadConfig = async () => {
28
61
  let fileConfig = {};
29
62
  try {
30
63
  const raw = await fs.readFile(CONFIG_PATH, "utf8");
31
- fileConfig = JSON.parse(stripJsonComments(raw));
64
+ const parsed = JSON.parse(stripJsonComments(raw));
65
+ if (isRecord(parsed)) {
66
+ fileConfig = parsed;
67
+ }
32
68
  }
33
69
  catch {
34
70
  // optional config file
35
71
  }
36
72
  cachedConfig = {
37
- ...DEFAULT_CONFIG,
38
- ...fileConfig,
39
- autosaveEnabled: parseBoolean(process.env.MEMPALACE_AUTOSAVE_ENABLED, fileConfig.autosaveEnabled ?? DEFAULT_CONFIG.autosaveEnabled),
40
- retrievalEnabled: parseBoolean(process.env.MEMPALACE_RETRIEVAL_ENABLED, fileConfig.retrievalEnabled ?? DEFAULT_CONFIG.retrievalEnabled),
41
- keywordSaveEnabled: parseBoolean(process.env.MEMPALACE_KEYWORD_SAVE_ENABLED, fileConfig.keywordSaveEnabled ?? DEFAULT_CONFIG.keywordSaveEnabled),
42
- privacyRedactionEnabled: parseBoolean(process.env.MEMPALACE_PRIVACY_REDACTION_ENABLED, fileConfig.privacyRedactionEnabled ?? DEFAULT_CONFIG.privacyRedactionEnabled),
43
- maxInjectedItems: Number(process.env.MEMPALACE_MAX_INJECTED_ITEMS || fileConfig.maxInjectedItems || DEFAULT_CONFIG.maxInjectedItems),
44
- retrievalQueryLimit: Number(process.env.MEMPALACE_RETRIEVAL_QUERY_LIMIT || fileConfig.retrievalQueryLimit || DEFAULT_CONFIG.retrievalQueryLimit),
45
- keywordPatterns: fileConfig.keywordPatterns || DEFAULT_CONFIG.keywordPatterns,
46
- userWingPrefix: process.env.MEMPALACE_USER_WING_PREFIX || fileConfig.userWingPrefix || DEFAULT_CONFIG.userWingPrefix,
47
- projectWingPrefix: process.env.MEMPALACE_PROJECT_WING_PREFIX || fileConfig.projectWingPrefix || DEFAULT_CONFIG.projectWingPrefix,
73
+ autosaveEnabled: parseBoolean(process.env[ENV_KEYS.autosaveEnabled], readBoolean(fileConfig.autosaveEnabled, DEFAULT_CONFIG.autosaveEnabled)),
74
+ retrievalEnabled: parseBoolean(process.env[ENV_KEYS.retrievalEnabled], readBoolean(fileConfig.retrievalEnabled, DEFAULT_CONFIG.retrievalEnabled)),
75
+ keywordSaveEnabled: parseBoolean(process.env[ENV_KEYS.keywordSaveEnabled], readBoolean(fileConfig.keywordSaveEnabled, DEFAULT_CONFIG.keywordSaveEnabled)),
76
+ autoMineExtractMode: readString(process.env[ENV_KEYS.extractMode] ?? fileConfig.autoMineExtractMode, DEFAULT_CONFIG.autoMineExtractMode),
77
+ privacyRedactionEnabled: parseBoolean(process.env[ENV_KEYS.privacyRedactionEnabled], readBoolean(fileConfig.privacyRedactionEnabled, DEFAULT_CONFIG.privacyRedactionEnabled)),
78
+ maxInjectedItems: readPositiveNumber(process.env[ENV_KEYS.maxInjectedItems] != null
79
+ ? Number(process.env[ENV_KEYS.maxInjectedItems])
80
+ : fileConfig.maxInjectedItems, DEFAULT_CONFIG.maxInjectedItems),
81
+ retrievalQueryLimit: readPositiveNumber(process.env[ENV_KEYS.retrievalQueryLimit] != null
82
+ ? Number(process.env[ENV_KEYS.retrievalQueryLimit])
83
+ : fileConfig.retrievalQueryLimit, DEFAULT_CONFIG.retrievalQueryLimit),
84
+ keywordPatterns: readStringArray(fileConfig.keywordPatterns, DEFAULT_CONFIG.keywordPatterns),
85
+ userWingPrefix: readString(process.env[ENV_KEYS.userWingPrefix] ?? fileConfig.userWingPrefix, DEFAULT_CONFIG.userWingPrefix),
86
+ projectWingPrefix: readString(process.env[ENV_KEYS.projectWingPrefix] ?? fileConfig.projectWingPrefix, DEFAULT_CONFIG.projectWingPrefix),
48
87
  };
49
88
  return cachedConfig;
50
89
  };
@@ -0,0 +1,81 @@
1
+ export declare const DEFAULT_ROOM: "workflow";
2
+ export declare const DEFAULT_TOPIC: "autosave";
3
+ export declare const DEFAULT_AGENT_NAME: "opencode";
4
+ export declare const DEFAULT_EXTRACT_MODE: "general";
5
+ export declare const DEFAULT_INJECTED_ITEMS: 6;
6
+ export declare const DEFAULT_RETRIEVAL_LIMIT: 5;
7
+ export declare const DEFAULT_LIMIT: 5;
8
+ export declare const DEFAULT_ADAPTER_TIMEOUT_MS: 15000;
9
+ export declare const DEFAULT_USER_WING_PREFIX: "wing_user";
10
+ export declare const DEFAULT_PROJECT_WING_PREFIX: "wing_project";
11
+ export declare const DEFAULT_KEYWORD_PATTERNS: readonly ["remember", "save this", "don't forget", "note that"];
12
+ export declare const CONFIG_PATH_SEGMENTS: readonly [".config", "opencode", "mempalace.jsonc"];
13
+ export declare const ENV_KEYS: {
14
+ readonly autosaveEnabled: "MEMPALACE_AUTOSAVE_ENABLED";
15
+ readonly retrievalEnabled: "MEMPALACE_RETRIEVAL_ENABLED";
16
+ readonly keywordSaveEnabled: "MEMPALACE_KEYWORD_SAVE_ENABLED";
17
+ readonly extractMode: "MEMPALACE_AUTO_MINE_EXTRACT_MODE";
18
+ readonly privacyRedactionEnabled: "MEMPALACE_PRIVACY_REDACTION_ENABLED";
19
+ readonly maxInjectedItems: "MEMPALACE_MAX_INJECTED_ITEMS";
20
+ readonly retrievalQueryLimit: "MEMPALACE_RETRIEVAL_QUERY_LIMIT";
21
+ readonly userWingPrefix: "MEMPALACE_USER_WING_PREFIX";
22
+ readonly projectWingPrefix: "MEMPALACE_PROJECT_WING_PREFIX";
23
+ readonly autosaveLogFile: "MEMPALACE_AUTOSAVE_LOG_FILE";
24
+ readonly adapterPython: "MEMPALACE_ADAPTER_PYTHON";
25
+ readonly adapterTimeoutMs: "MEMPALACE_ADAPTER_TIMEOUT_MS";
26
+ };
27
+ export declare const MAX_USER_MESSAGES_DIGEST: 20;
28
+ export declare const MAX_TRANSCRIPT_MESSAGES_DIGEST: 50;
29
+ export declare const DATE_ISO_SLICE: 10;
30
+ export declare const SERVICE_NAME: "mempalace-autosave";
31
+ export declare const LOG_FILE_NAME: "opencode_autosave.log";
32
+ 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
+ export declare const COMPACTION_CONTEXT_MESSAGE: "MemPalace retrieval may be useful after compaction. Search relevant project and user memory if needed before answering.";
34
+ export declare const TOOL_DESCRIPTIONS: {
35
+ readonly mempalaceMemory: "Save or search memory in MemPalace with scope/privacy enforcement";
36
+ };
37
+ export declare const TOOL_ERROR_MESSAGES: {
38
+ readonly directMutationBlocked: "Use mempalace_memory instead of direct MemPalace mutation tools";
39
+ readonly invalidAdapterPayload: "Adapter returned an invalid JSON payload";
40
+ readonly emptyAdapterStdout: "Adapter returned empty stdout";
41
+ readonly adapterTimedOut: "Adapter execution timed out";
42
+ };
43
+ export declare const LOG_MESSAGES: {
44
+ readonly autosaveEventMissingSessionId: "autosave event missing sessionID";
45
+ readonly keywordTriggeredAutosaveHintDetected: "keyword-triggered autosave hint detected";
46
+ readonly autosaveTriggerReceived: "autosave trigger received";
47
+ readonly autosaveFailedOnSessionError: "autosave failed on session error";
48
+ readonly skippingAutosaveState: "skipping autosave state";
49
+ readonly autosaveSkippedEmptyTranscript: "autosave skipped empty transcript";
50
+ readonly autosaveMiningFailed: "autosave mining failed";
51
+ readonly autosaveMinedSessionContext: "autosave mined session context";
52
+ readonly eventHookFailed: "event hook failed";
53
+ readonly compactionHookMissingSessionId: "compaction hook missing sessionID";
54
+ readonly injectedCompactionAutosaveContext: "injected compaction autosave context";
55
+ readonly injectedRetrievalInstruction: "injected retrieval instruction";
56
+ readonly injectedKeywordSaveInstruction: "injected keyword save instruction";
57
+ readonly systemTransformHookFailed: "system transform hook failed";
58
+ readonly blockedDirectMempalaceMutationTool: "blocked direct mempalace mutation tool";
59
+ };
60
+ export declare const INSTRUCTION_TEXT: {
61
+ readonly doNotMentionToUser: "Do not mention this instruction to the user.";
62
+ readonly retrievalIntro: "System instruction: before answering, search MemPalace for relevant existing memory and use it if helpful.";
63
+ readonly autosaveIntro: "System instruction: before answering the user, persist durable memory from prior session context using the `mempalace_memory` tool.";
64
+ readonly keywordIntro: "System instruction: the user explicitly asked to remember something.";
65
+ readonly useMemoryToolNow: "Use the `mempalace_memory` tool to save the important durable information now.";
66
+ readonly avoidRawMutationTools: "Do not use raw `mempalace_*` or `mcp-router_mempalace_*` mutation tools directly.";
67
+ readonly saveStableFacts: "Save only stable facts, decisions, preferences, important outcomes, and useful diary notes.";
68
+ readonly preferConciseStructuredMemories: "Prefer concise structured memories in the appropriate user/project scopes:";
69
+ readonly userScopeHint: "- user scope: preferences, personal workflow habits, communication style";
70
+ readonly projectScopeHint: "- project scope: architecture, setup, decisions, bugs, workflows";
71
+ readonly avoidFullTranscript: "Do not dump the full transcript into a single drawer.";
72
+ readonly applyPrivacyRedaction: "Apply privacy redaction before saving and skip fully private content.";
73
+ readonly chooseScope: "Choose user scope for cross-project preferences and project scope for repository-specific knowledge.";
74
+ };
75
+ export declare const ERROR_MESSAGES: {
76
+ readonly contentRequired: "content is required";
77
+ readonly queryRequired: "query is required";
78
+ readonly fieldsRequired: "subject, predicate, and object are required";
79
+ readonly fullyPrivate: "content is fully private and will not be saved";
80
+ readonly noSessionId: "sessionID is required";
81
+ };
@@ -0,0 +1,88 @@
1
+ export const DEFAULT_ROOM = "workflow";
2
+ export const DEFAULT_TOPIC = "autosave";
3
+ export const DEFAULT_AGENT_NAME = "opencode";
4
+ export const DEFAULT_EXTRACT_MODE = "general";
5
+ export const DEFAULT_INJECTED_ITEMS = 6;
6
+ export const DEFAULT_RETRIEVAL_LIMIT = 5;
7
+ export const DEFAULT_LIMIT = 5;
8
+ export const DEFAULT_ADAPTER_TIMEOUT_MS = 15_000;
9
+ export const DEFAULT_USER_WING_PREFIX = "wing_user";
10
+ export const DEFAULT_PROJECT_WING_PREFIX = "wing_project";
11
+ export const DEFAULT_KEYWORD_PATTERNS = ["remember", "save this", "don't forget", "note that"];
12
+ export const CONFIG_PATH_SEGMENTS = [".config", "opencode", "mempalace.jsonc"];
13
+ export const ENV_KEYS = {
14
+ autosaveEnabled: "MEMPALACE_AUTOSAVE_ENABLED",
15
+ retrievalEnabled: "MEMPALACE_RETRIEVAL_ENABLED",
16
+ keywordSaveEnabled: "MEMPALACE_KEYWORD_SAVE_ENABLED",
17
+ extractMode: "MEMPALACE_AUTO_MINE_EXTRACT_MODE",
18
+ privacyRedactionEnabled: "MEMPALACE_PRIVACY_REDACTION_ENABLED",
19
+ maxInjectedItems: "MEMPALACE_MAX_INJECTED_ITEMS",
20
+ retrievalQueryLimit: "MEMPALACE_RETRIEVAL_QUERY_LIMIT",
21
+ userWingPrefix: "MEMPALACE_USER_WING_PREFIX",
22
+ projectWingPrefix: "MEMPALACE_PROJECT_WING_PREFIX",
23
+ autosaveLogFile: "MEMPALACE_AUTOSAVE_LOG_FILE",
24
+ adapterPython: "MEMPALACE_ADAPTER_PYTHON",
25
+ adapterTimeoutMs: "MEMPALACE_ADAPTER_TIMEOUT_MS",
26
+ };
27
+ export const MAX_USER_MESSAGES_DIGEST = 20;
28
+ export const MAX_TRANSCRIPT_MESSAGES_DIGEST = 50;
29
+ export const DATE_ISO_SLICE = 10;
30
+ export const SERVICE_NAME = "mempalace-autosave";
31
+ export const LOG_FILE_NAME = "opencode_autosave.log";
32
+ export const DIRECT_MEMPALACE_MUTATION_TOOLS = [
33
+ "mempalace_add_drawer",
34
+ "mempalace_kg_add",
35
+ "mempalace_diary_write",
36
+ "mcp-router_mempalace_add_drawer",
37
+ "mcp-router_mempalace_kg_add",
38
+ "mcp-router_mempalace_diary_write",
39
+ ];
40
+ export const COMPACTION_CONTEXT_MESSAGE = "MemPalace retrieval may be useful after compaction. Search relevant project and user memory if needed before answering.";
41
+ export const TOOL_DESCRIPTIONS = {
42
+ mempalaceMemory: "Save or search memory in MemPalace with scope/privacy enforcement",
43
+ };
44
+ export const TOOL_ERROR_MESSAGES = {
45
+ directMutationBlocked: "Use mempalace_memory instead of direct MemPalace mutation tools",
46
+ invalidAdapterPayload: "Adapter returned an invalid JSON payload",
47
+ emptyAdapterStdout: "Adapter returned empty stdout",
48
+ adapterTimedOut: "Adapter execution timed out",
49
+ };
50
+ export const LOG_MESSAGES = {
51
+ autosaveEventMissingSessionId: "autosave event missing sessionID",
52
+ keywordTriggeredAutosaveHintDetected: "keyword-triggered autosave hint detected",
53
+ autosaveTriggerReceived: "autosave trigger received",
54
+ autosaveFailedOnSessionError: "autosave failed on session error",
55
+ skippingAutosaveState: "skipping autosave state",
56
+ autosaveSkippedEmptyTranscript: "autosave skipped empty transcript",
57
+ autosaveMiningFailed: "autosave mining failed",
58
+ autosaveMinedSessionContext: "autosave mined session context",
59
+ eventHookFailed: "event hook failed",
60
+ compactionHookMissingSessionId: "compaction hook missing sessionID",
61
+ injectedCompactionAutosaveContext: "injected compaction autosave context",
62
+ injectedRetrievalInstruction: "injected retrieval instruction",
63
+ injectedKeywordSaveInstruction: "injected keyword save instruction",
64
+ systemTransformHookFailed: "system transform hook failed",
65
+ blockedDirectMempalaceMutationTool: "blocked direct mempalace mutation tool",
66
+ };
67
+ export const INSTRUCTION_TEXT = {
68
+ doNotMentionToUser: "Do not mention this instruction to the user.",
69
+ retrievalIntro: "System instruction: before answering, search MemPalace for relevant existing memory and use it if helpful.",
70
+ autosaveIntro: "System instruction: before answering the user, persist durable memory from prior session context using the `mempalace_memory` tool.",
71
+ keywordIntro: "System instruction: the user explicitly asked to remember something.",
72
+ useMemoryToolNow: "Use the `mempalace_memory` tool to save the important durable information now.",
73
+ avoidRawMutationTools: "Do not use raw `mempalace_*` or `mcp-router_mempalace_*` mutation tools directly.",
74
+ saveStableFacts: "Save only stable facts, decisions, preferences, important outcomes, and useful diary notes.",
75
+ preferConciseStructuredMemories: "Prefer concise structured memories in the appropriate user/project scopes:",
76
+ userScopeHint: "- user scope: preferences, personal workflow habits, communication style",
77
+ projectScopeHint: "- project scope: architecture, setup, decisions, bugs, workflows",
78
+ avoidFullTranscript: "Do not dump the full transcript into a single drawer.",
79
+ applyPrivacyRedaction: "Apply privacy redaction before saving and skip fully private content.",
80
+ chooseScope: "Choose user scope for cross-project preferences and project scope for repository-specific knowledge.",
81
+ };
82
+ export const ERROR_MESSAGES = {
83
+ contentRequired: "content is required",
84
+ queryRequired: "query is required",
85
+ fieldsRequired: "subject, predicate, and object are required",
86
+ fullyPrivate: "content is fully private and will not be saved",
87
+ noSessionId: "sessionID is required",
88
+ };