opencode-memory-plugin 0.1.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 (102) hide show
  1. package/README.md +73 -0
  2. package/dist/compression/compressor.d.ts +86 -0
  3. package/dist/compression/compressor.d.ts.map +1 -0
  4. package/dist/compression/compressor.js +142 -0
  5. package/dist/compression/compressor.js.map +1 -0
  6. package/dist/compression/parser.d.ts +73 -0
  7. package/dist/compression/parser.d.ts.map +1 -0
  8. package/dist/compression/parser.js +139 -0
  9. package/dist/compression/parser.js.map +1 -0
  10. package/dist/compression/pipeline.d.ts +73 -0
  11. package/dist/compression/pipeline.d.ts.map +1 -0
  12. package/dist/compression/pipeline.js +205 -0
  13. package/dist/compression/pipeline.js.map +1 -0
  14. package/dist/compression/privacy.d.ts +8 -0
  15. package/dist/compression/privacy.d.ts.map +1 -0
  16. package/dist/compression/privacy.js +30 -0
  17. package/dist/compression/privacy.js.map +1 -0
  18. package/dist/compression/prompts.d.ts +24 -0
  19. package/dist/compression/prompts.d.ts.map +1 -0
  20. package/dist/compression/prompts.js +106 -0
  21. package/dist/compression/prompts.js.map +1 -0
  22. package/dist/compression/quality.d.ts +48 -0
  23. package/dist/compression/quality.d.ts.map +1 -0
  24. package/dist/compression/quality.js +159 -0
  25. package/dist/compression/quality.js.map +1 -0
  26. package/dist/config.d.ts +114 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +265 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/context/generator.d.ts +28 -0
  31. package/dist/context/generator.d.ts.map +1 -0
  32. package/dist/context/generator.js +80 -0
  33. package/dist/context/generator.js.map +1 -0
  34. package/dist/hooks/chat-message.d.ts +14 -0
  35. package/dist/hooks/chat-message.d.ts.map +1 -0
  36. package/dist/hooks/chat-message.js +35 -0
  37. package/dist/hooks/chat-message.js.map +1 -0
  38. package/dist/hooks/compaction.d.ts +13 -0
  39. package/dist/hooks/compaction.d.ts.map +1 -0
  40. package/dist/hooks/compaction.js +22 -0
  41. package/dist/hooks/compaction.js.map +1 -0
  42. package/dist/hooks/events.d.ts +52 -0
  43. package/dist/hooks/events.d.ts.map +1 -0
  44. package/dist/hooks/events.js +138 -0
  45. package/dist/hooks/events.js.map +1 -0
  46. package/dist/hooks/system-transform.d.ts +14 -0
  47. package/dist/hooks/system-transform.d.ts.map +1 -0
  48. package/dist/hooks/system-transform.js +26 -0
  49. package/dist/hooks/system-transform.js.map +1 -0
  50. package/dist/hooks/tool-after.d.ts +26 -0
  51. package/dist/hooks/tool-after.d.ts.map +1 -0
  52. package/dist/hooks/tool-after.js +88 -0
  53. package/dist/hooks/tool-after.js.map +1 -0
  54. package/dist/index.d.ts +11 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +79 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/logger.d.ts +60 -0
  59. package/dist/logger.d.ts.map +1 -0
  60. package/dist/logger.js +91 -0
  61. package/dist/logger.js.map +1 -0
  62. package/dist/storage/db.d.ts +22 -0
  63. package/dist/storage/db.d.ts.map +1 -0
  64. package/dist/storage/db.js +198 -0
  65. package/dist/storage/db.js.map +1 -0
  66. package/dist/storage/schema.d.ts +2473 -0
  67. package/dist/storage/schema.d.ts.map +1 -0
  68. package/dist/storage/schema.js +100 -0
  69. package/dist/storage/schema.js.map +1 -0
  70. package/dist/storage/store.d.ts +376 -0
  71. package/dist/storage/store.d.ts.map +1 -0
  72. package/dist/storage/store.js +1025 -0
  73. package/dist/storage/store.js.map +1 -0
  74. package/dist/tools/memory-forget.d.ts +11 -0
  75. package/dist/tools/memory-forget.d.ts.map +1 -0
  76. package/dist/tools/memory-forget.js +249 -0
  77. package/dist/tools/memory-forget.js.map +1 -0
  78. package/dist/tools/memory-get.d.ts +10 -0
  79. package/dist/tools/memory-get.d.ts.map +1 -0
  80. package/dist/tools/memory-get.js +50 -0
  81. package/dist/tools/memory-get.js.map +1 -0
  82. package/dist/tools/memory-search.d.ts +11 -0
  83. package/dist/tools/memory-search.d.ts.map +1 -0
  84. package/dist/tools/memory-search.js +38 -0
  85. package/dist/tools/memory-search.js.map +1 -0
  86. package/dist/tools/memory-stats.d.ts +39 -0
  87. package/dist/tools/memory-stats.d.ts.map +1 -0
  88. package/dist/tools/memory-stats.js +121 -0
  89. package/dist/tools/memory-stats.js.map +1 -0
  90. package/dist/tools/memory-timeline.d.ts +10 -0
  91. package/dist/tools/memory-timeline.d.ts.map +1 -0
  92. package/dist/tools/memory-timeline.js +49 -0
  93. package/dist/tools/memory-timeline.js.map +1 -0
  94. package/dist/types.d.ts +178 -0
  95. package/dist/types.d.ts.map +1 -0
  96. package/dist/types.js +4 -0
  97. package/dist/types.js.map +1 -0
  98. package/dist/utils.d.ts +130 -0
  99. package/dist/utils.d.ts.map +1 -0
  100. package/dist/utils.js +308 -0
  101. package/dist/utils.js.map +1 -0
  102. package/package.json +36 -0
@@ -0,0 +1,114 @@
1
+ import type { Config, OpencodeClient } from "@opencode-ai/sdk";
2
+ import type { LogLevel, PluginConfig } from "./types";
3
+ import { parseModelString } from "./utils";
4
+ interface PartialPluginConfig extends Partial<Omit<PluginConfig, "configPaths">> {
5
+ }
6
+ /**
7
+ * Loads the memory plugin configuration from project files, global files, and environment variables.
8
+ *
9
+ * @param input - Context needed to resolve configuration files and defaults.
10
+ * @returns The merged plugin configuration.
11
+ */
12
+ export declare function loadConfig(input: {
13
+ directory: string;
14
+ worktree: string;
15
+ }): Promise<PluginConfig>;
16
+ /**
17
+ * Selects the model string that should be used for background compression.
18
+ *
19
+ * @param pluginConfig - Plugin configuration.
20
+ * @param runtimeConfig - OpenCode runtime configuration.
21
+ * @returns The preferred model string or null.
22
+ */
23
+ export declare function selectCompressionModelString(pluginConfig: PluginConfig, runtimeConfig: Config): string | null;
24
+ /**
25
+ * Parses the configured compression model into the SDK shape.
26
+ *
27
+ * @param pluginConfig - Plugin configuration.
28
+ * @param runtimeConfig - OpenCode runtime configuration.
29
+ * @returns The parsed model selection or null.
30
+ */
31
+ export declare function selectCompressionModel(pluginConfig: PluginConfig, runtimeConfig: Config): ReturnType<typeof parseModelString>;
32
+ /**
33
+ * Reads the merged OpenCode runtime configuration for the current project.
34
+ *
35
+ * @param client - OpenCode SDK client.
36
+ * @param directory - Project directory.
37
+ * @returns The merged runtime configuration.
38
+ */
39
+ export declare function loadOpenCodeConfig(client: OpencodeClient, directory: string): Promise<Config>;
40
+ /**
41
+ * Resolves the ordered set of plugin config files that should be read.
42
+ *
43
+ * @param worktree - Project root.
44
+ * @returns A list of existing config file paths.
45
+ */
46
+ export declare function findConfigPaths(worktree: string): Promise<string[]>;
47
+ /**
48
+ * Merges a list of config files in order.
49
+ *
50
+ * @param configPaths - Existing config file paths.
51
+ * @returns The merged partial configuration.
52
+ */
53
+ export declare function loadConfigFiles(configPaths: string[]): Promise<PartialPluginConfig>;
54
+ /**
55
+ * Reads plugin configuration from environment variables.
56
+ *
57
+ * @returns The environment-derived partial configuration.
58
+ */
59
+ export declare function loadEnvironmentConfig(): PartialPluginConfig;
60
+ /**
61
+ * Removes keys with undefined values from a partial plugin config.
62
+ *
63
+ * @param value - Partial configuration object.
64
+ * @returns A copy without undefined values.
65
+ */
66
+ export declare function compactConfig(value: PartialPluginConfig): PartialPluginConfig;
67
+ /**
68
+ * Validates and normalizes the merged configuration.
69
+ *
70
+ * @param value - Raw merged plugin configuration.
71
+ * @param runtimeConfig - Current OpenCode runtime config.
72
+ * @returns The normalized plugin configuration.
73
+ */
74
+ export declare function normalizeConfig(value: PartialPluginConfig, runtimeConfig?: Config): Omit<PluginConfig, "configPaths">;
75
+ /**
76
+ * Parses a numeric environment variable.
77
+ *
78
+ * @param value - Raw environment value.
79
+ * @returns The parsed integer or undefined.
80
+ */
81
+ export declare function parseInteger(value: string | undefined): number | undefined;
82
+ /**
83
+ * Parses a boolean environment variable.
84
+ *
85
+ * @param value - Raw environment value.
86
+ * @returns The parsed boolean or undefined.
87
+ */
88
+ export declare function parseBoolean(value: string | undefined): boolean | undefined;
89
+ /**
90
+ * Parses a log level environment variable.
91
+ *
92
+ * @param value - Raw environment value.
93
+ * @returns The normalized log level or undefined.
94
+ */
95
+ export declare function parseLogLevel(value: string | undefined): LogLevel | undefined;
96
+ /**
97
+ * Clamps an optional number into a safe range with a fallback value.
98
+ *
99
+ * @param value - Candidate value.
100
+ * @param minimum - Minimum supported value.
101
+ * @param maximum - Maximum supported value.
102
+ * @param fallback - Fallback when the candidate is invalid.
103
+ * @returns A safe numeric value.
104
+ */
105
+ export declare function clampNumber(value: number | undefined, minimum: number, maximum: number, fallback: number): number;
106
+ /**
107
+ * Checks whether a file path exists.
108
+ *
109
+ * @param filePath - Path to inspect.
110
+ * @returns True when the file exists.
111
+ */
112
+ export declare function fileExists(filePath: string): Promise<boolean>;
113
+ export {};
114
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAqD,gBAAgB,EAAiC,MAAM,SAAS,CAAA;AAE5H,UAAU,mBAAoB,SAAQ,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;CAAG;AAsBnF;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB,GAAG,OAAO,CAAC,YAAY,CAAC,CAgBxB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CAEf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,YAAY,EAC1B,aAAa,EAAE,MAAM,GACpB,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAErC;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOnG;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBzE;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAYzF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,mBAAmB,CAoB3D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,GAAG,mBAAmB,CAG7E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,mBAAmB,EAC1B,aAAa,GAAE,MAAW,GACzB,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAqEnC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO1E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAc3E;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAW7E;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE"}
package/dist/config.js ADDED
@@ -0,0 +1,265 @@
1
+ import { access } from "node:fs/promises";
2
+ import { constants } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { ensureParentDirectory, getOpenCodeConfigDirectory, parseModelString, readJsonFile, resolveHomePath } from "./utils";
5
+ const DEFAULT_CONFIG = {
6
+ dbPath: "~/.config/opencode/memory/memory.db",
7
+ indexSize: 50,
8
+ sampleSize: 5,
9
+ maxPendingRetries: 3,
10
+ compressionModel: null,
11
+ maxRawContentSize: 50_000,
12
+ enableSemanticSearch: false,
13
+ privacyStrip: true,
14
+ minContentLength: 100,
15
+ compressionBatchSize: 10,
16
+ retentionDays: 90,
17
+ contextMaxTokens: 2_000,
18
+ summaryLookback: 3,
19
+ orphanThresholdMs: 5 * 60_000,
20
+ queuePollIntervalMs: 250,
21
+ sessionSummaryDebounceMs: 1_500,
22
+ logLevel: "info",
23
+ };
24
+ /**
25
+ * Loads the memory plugin configuration from project files, global files, and environment variables.
26
+ *
27
+ * @param input - Context needed to resolve configuration files and defaults.
28
+ * @returns The merged plugin configuration.
29
+ */
30
+ export async function loadConfig(input) {
31
+ const configPaths = await findConfigPaths(input.worktree);
32
+ const fileConfig = await loadConfigFiles(configPaths);
33
+ const envConfig = loadEnvironmentConfig();
34
+ const config = normalizeConfig({
35
+ ...DEFAULT_CONFIG,
36
+ ...compactConfig(fileConfig),
37
+ ...compactConfig(envConfig),
38
+ });
39
+ await ensureParentDirectory(config.dbPath);
40
+ return {
41
+ ...config,
42
+ configPaths,
43
+ };
44
+ }
45
+ /**
46
+ * Selects the model string that should be used for background compression.
47
+ *
48
+ * @param pluginConfig - Plugin configuration.
49
+ * @param runtimeConfig - OpenCode runtime configuration.
50
+ * @returns The preferred model string or null.
51
+ */
52
+ export function selectCompressionModelString(pluginConfig, runtimeConfig) {
53
+ return pluginConfig.compressionModel ?? runtimeConfig.small_model ?? runtimeConfig.model ?? null;
54
+ }
55
+ /**
56
+ * Parses the configured compression model into the SDK shape.
57
+ *
58
+ * @param pluginConfig - Plugin configuration.
59
+ * @param runtimeConfig - OpenCode runtime configuration.
60
+ * @returns The parsed model selection or null.
61
+ */
62
+ export function selectCompressionModel(pluginConfig, runtimeConfig) {
63
+ return parseModelString(selectCompressionModelString(pluginConfig, runtimeConfig));
64
+ }
65
+ /**
66
+ * Reads the merged OpenCode runtime configuration for the current project.
67
+ *
68
+ * @param client - OpenCode SDK client.
69
+ * @param directory - Project directory.
70
+ * @returns The merged runtime configuration.
71
+ */
72
+ export async function loadOpenCodeConfig(client, directory) {
73
+ const result = await client.config.get({ query: { directory } });
74
+ if (result.error || !result.data) {
75
+ return {};
76
+ }
77
+ return result.data;
78
+ }
79
+ /**
80
+ * Resolves the ordered set of plugin config files that should be read.
81
+ *
82
+ * @param worktree - Project root.
83
+ * @returns A list of existing config file paths.
84
+ */
85
+ export async function findConfigPaths(worktree) {
86
+ const globalRoot = getOpenCodeConfigDirectory();
87
+ const candidates = [
88
+ process.env.OPENCODE_MEMORY_CONFIG,
89
+ join(globalRoot, "memory", "config.json"),
90
+ join(globalRoot, "memory", "config.jsonc"),
91
+ join(worktree, ".opencode", "memory.json"),
92
+ join(worktree, ".opencode", "memory.jsonc"),
93
+ join(worktree, "opencode-memory.json"),
94
+ join(worktree, "opencode-memory.jsonc"),
95
+ ].filter((value) => Boolean(value));
96
+ const existing = [];
97
+ for (const candidate of candidates) {
98
+ const resolved = resolveHomePath(candidate);
99
+ if (await fileExists(resolved)) {
100
+ existing.push(resolved);
101
+ }
102
+ }
103
+ return existing;
104
+ }
105
+ /**
106
+ * Merges a list of config files in order.
107
+ *
108
+ * @param configPaths - Existing config file paths.
109
+ * @returns The merged partial configuration.
110
+ */
111
+ export async function loadConfigFiles(configPaths) {
112
+ let merged = {};
113
+ for (const configPath of configPaths) {
114
+ const parsed = await readJsonFile(configPath);
115
+ merged = {
116
+ ...merged,
117
+ ...parsed,
118
+ };
119
+ }
120
+ return merged;
121
+ }
122
+ /**
123
+ * Reads plugin configuration from environment variables.
124
+ *
125
+ * @returns The environment-derived partial configuration.
126
+ */
127
+ export function loadEnvironmentConfig() {
128
+ return {
129
+ dbPath: process.env.OPENCODE_MEMORY_DB_PATH,
130
+ compressionModel: process.env.OPENCODE_MEMORY_COMPRESSION_MODEL,
131
+ indexSize: parseInteger(process.env.OPENCODE_MEMORY_INDEX_SIZE),
132
+ sampleSize: parseInteger(process.env.OPENCODE_MEMORY_SAMPLE_SIZE),
133
+ maxPendingRetries: parseInteger(process.env.OPENCODE_MEMORY_MAX_PENDING_RETRIES),
134
+ maxRawContentSize: parseInteger(process.env.OPENCODE_MEMORY_MAX_RAW_CONTENT_SIZE),
135
+ enableSemanticSearch: parseBoolean(process.env.OPENCODE_MEMORY_ENABLE_SEMANTIC_SEARCH),
136
+ privacyStrip: parseBoolean(process.env.OPENCODE_MEMORY_PRIVACY_STRIP),
137
+ minContentLength: parseInteger(process.env.OPENCODE_MEMORY_MIN_CONTENT_LENGTH),
138
+ compressionBatchSize: parseInteger(process.env.OPENCODE_MEMORY_BATCH_SIZE),
139
+ retentionDays: parseInteger(process.env.OPENCODE_MEMORY_RETENTION_DAYS),
140
+ contextMaxTokens: parseInteger(process.env.OPENCODE_MEMORY_CONTEXT_MAX_TOKENS),
141
+ summaryLookback: parseInteger(process.env.OPENCODE_MEMORY_SUMMARY_LOOKBACK),
142
+ orphanThresholdMs: parseInteger(process.env.OPENCODE_MEMORY_ORPHAN_THRESHOLD_MS),
143
+ queuePollIntervalMs: parseInteger(process.env.OPENCODE_MEMORY_QUEUE_POLL_INTERVAL_MS),
144
+ sessionSummaryDebounceMs: parseInteger(process.env.OPENCODE_MEMORY_SUMMARY_DEBOUNCE_MS),
145
+ logLevel: parseLogLevel(process.env.OPENCODE_MEMORY_LOG_LEVEL),
146
+ };
147
+ }
148
+ /**
149
+ * Removes keys with undefined values from a partial plugin config.
150
+ *
151
+ * @param value - Partial configuration object.
152
+ * @returns A copy without undefined values.
153
+ */
154
+ export function compactConfig(value) {
155
+ const entries = Object.entries(value).filter((entry) => entry[1] !== undefined);
156
+ return Object.fromEntries(entries);
157
+ }
158
+ /**
159
+ * Validates and normalizes the merged configuration.
160
+ *
161
+ * @param value - Raw merged plugin configuration.
162
+ * @param runtimeConfig - Current OpenCode runtime config.
163
+ * @returns The normalized plugin configuration.
164
+ */
165
+ export function normalizeConfig(value, runtimeConfig = {}) {
166
+ const rawDbPath = value.dbPath ?? DEFAULT_CONFIG.dbPath;
167
+ const resolvedDbPath = resolveHomePath(rawDbPath);
168
+ return {
169
+ dbPath: resolvedDbPath,
170
+ indexSize: clampNumber(value.indexSize, 1, 200, DEFAULT_CONFIG.indexSize),
171
+ sampleSize: clampNumber(value.sampleSize, 0, 20, DEFAULT_CONFIG.sampleSize),
172
+ maxPendingRetries: clampNumber(value.maxPendingRetries, 1, 10, DEFAULT_CONFIG.maxPendingRetries),
173
+ compressionModel: value.compressionModel ?? runtimeConfig.small_model ?? runtimeConfig.model ?? null,
174
+ maxRawContentSize: clampNumber(value.maxRawContentSize, 1_000, 500_000, DEFAULT_CONFIG.maxRawContentSize),
175
+ enableSemanticSearch: value.enableSemanticSearch ?? DEFAULT_CONFIG.enableSemanticSearch,
176
+ privacyStrip: value.privacyStrip ?? DEFAULT_CONFIG.privacyStrip,
177
+ minContentLength: clampNumber(value.minContentLength, 0, 10_000, DEFAULT_CONFIG.minContentLength),
178
+ compressionBatchSize: clampNumber(value.compressionBatchSize, 1, 50, DEFAULT_CONFIG.compressionBatchSize),
179
+ retentionDays: clampNumber(value.retentionDays, 1, 3650, DEFAULT_CONFIG.retentionDays),
180
+ contextMaxTokens: clampNumber(value.contextMaxTokens, 250, 8_000, DEFAULT_CONFIG.contextMaxTokens),
181
+ summaryLookback: clampNumber(value.summaryLookback, 0, 20, DEFAULT_CONFIG.summaryLookback),
182
+ orphanThresholdMs: clampNumber(value.orphanThresholdMs, 5_000, 86_400_000, DEFAULT_CONFIG.orphanThresholdMs),
183
+ queuePollIntervalMs: clampNumber(value.queuePollIntervalMs, 25, 5_000, DEFAULT_CONFIG.queuePollIntervalMs),
184
+ sessionSummaryDebounceMs: clampNumber(value.sessionSummaryDebounceMs, 0, 60_000, DEFAULT_CONFIG.sessionSummaryDebounceMs),
185
+ logLevel: value.logLevel ?? DEFAULT_CONFIG.logLevel,
186
+ };
187
+ }
188
+ /**
189
+ * Parses a numeric environment variable.
190
+ *
191
+ * @param value - Raw environment value.
192
+ * @returns The parsed integer or undefined.
193
+ */
194
+ export function parseInteger(value) {
195
+ if (!value) {
196
+ return undefined;
197
+ }
198
+ const parsed = Number.parseInt(value, 10);
199
+ return Number.isFinite(parsed) ? parsed : undefined;
200
+ }
201
+ /**
202
+ * Parses a boolean environment variable.
203
+ *
204
+ * @param value - Raw environment value.
205
+ * @returns The parsed boolean or undefined.
206
+ */
207
+ export function parseBoolean(value) {
208
+ if (!value) {
209
+ return undefined;
210
+ }
211
+ if (["1", "true", "yes", "on"].includes(value.toLowerCase())) {
212
+ return true;
213
+ }
214
+ if (["0", "false", "no", "off"].includes(value.toLowerCase())) {
215
+ return false;
216
+ }
217
+ return undefined;
218
+ }
219
+ /**
220
+ * Parses a log level environment variable.
221
+ *
222
+ * @param value - Raw environment value.
223
+ * @returns The normalized log level or undefined.
224
+ */
225
+ export function parseLogLevel(value) {
226
+ if (!value) {
227
+ return undefined;
228
+ }
229
+ const normalized = value.toLowerCase();
230
+ if (normalized === "debug" || normalized === "info" || normalized === "warn" || normalized === "error") {
231
+ return normalized;
232
+ }
233
+ return undefined;
234
+ }
235
+ /**
236
+ * Clamps an optional number into a safe range with a fallback value.
237
+ *
238
+ * @param value - Candidate value.
239
+ * @param minimum - Minimum supported value.
240
+ * @param maximum - Maximum supported value.
241
+ * @param fallback - Fallback when the candidate is invalid.
242
+ * @returns A safe numeric value.
243
+ */
244
+ export function clampNumber(value, minimum, maximum, fallback) {
245
+ if (typeof value !== "number" || Number.isNaN(value)) {
246
+ return fallback;
247
+ }
248
+ return Math.max(minimum, Math.min(maximum, value));
249
+ }
250
+ /**
251
+ * Checks whether a file path exists.
252
+ *
253
+ * @param filePath - Path to inspect.
254
+ * @returns True when the file exists.
255
+ */
256
+ export async function fileExists(filePath) {
257
+ try {
258
+ await access(filePath, constants.F_OK);
259
+ return true;
260
+ }
261
+ catch {
262
+ return false;
263
+ }
264
+ }
265
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { MemoryStore } from "../storage/store";
2
+ import type { PluginConfig } from "../types";
3
+ /**
4
+ * Generates the memory context injected into the system prompt.
5
+ *
6
+ * @param store - Memory store.
7
+ * @param config - Plugin configuration.
8
+ * @param now - Clock function.
9
+ * @returns A context block or an empty string.
10
+ */
11
+ export declare function generateSessionContext(store: MemoryStore, config: PluginConfig, now: () => number): Promise<string>;
12
+ /**
13
+ * Generates a shorter context block used during compaction.
14
+ *
15
+ * @param store - Memory store.
16
+ * @param now - Clock function.
17
+ * @returns Compaction context.
18
+ */
19
+ export declare function generateCompactionContext(store: MemoryStore, now: () => number): Promise<string>;
20
+ /**
21
+ * Truncates a context string to a token budget using a simple heuristic.
22
+ *
23
+ * @param value - Context text.
24
+ * @param maxTokens - Maximum allowed tokens.
25
+ * @returns Truncated or original text.
26
+ */
27
+ export declare function truncateToBudget(value: string, maxTokens: number): string;
28
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/context/generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAG5C;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,YAAY,EACpB,GAAG,EAAE,MAAM,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CA4CjB;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAatG;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAOzE"}
@@ -0,0 +1,80 @@
1
+ import { formatSummaryBlock } from "../compression/prompts";
2
+ import { MemoryStore } from "../storage/store";
3
+ import { formatRelativeTime, normalizeWhitespace } from "../utils";
4
+ /**
5
+ * Generates the memory context injected into the system prompt.
6
+ *
7
+ * @param store - Memory store.
8
+ * @param config - Plugin configuration.
9
+ * @param now - Clock function.
10
+ * @returns A context block or an empty string.
11
+ */
12
+ export async function generateSessionContext(store, config, now) {
13
+ const recentObservations = await store.getRecentObservations(config.indexSize);
14
+ if (!recentObservations.length) {
15
+ return "";
16
+ }
17
+ const summaries = await store.getRecentSummaries(config.summaryLookback);
18
+ const indexLines = recentObservations.map((observation) => `- [${observation.id}] ${observation.title} — ${observation.subtitle ?? "No subtitle"} (${observation.type}, ${formatRelativeTime(observation.createdAt, now)})`);
19
+ const detailedSamples = recentObservations.slice(0, config.sampleSize).map((observation) => {
20
+ const facts = observation.facts.map((fact) => ` - ${fact}`).join("\n") || " - none";
21
+ const files = observation.filesInvolved.join(", ") || "none";
22
+ return `### ${observation.title}\n${observation.narrative}\nFacts:\n${facts}\nFiles: ${files}`;
23
+ });
24
+ const summaryBlock = summaries.length
25
+ ? `## Recent Session Summaries\n${formatSummaryBlock(summaries)}`
26
+ : "";
27
+ const sections = [
28
+ "<memory_context>",
29
+ "You have access to persistent memory from previous OpenCode sessions for this project.",
30
+ "",
31
+ `## Recent Observation Index (${recentObservations.length} entries)`,
32
+ indexLines.join("\n"),
33
+ "",
34
+ "## Detailed Recent Observations",
35
+ detailedSamples.join("\n\n"),
36
+ summaryBlock,
37
+ "",
38
+ "## Available Memory Tools",
39
+ "- memory_search: search prior observations by keyword and type.",
40
+ "- memory_timeline: browse memories chronologically with filters.",
41
+ "- memory_get: fetch full details for specific observation IDs.",
42
+ "- memory_forget: preview and delete observations by criteria.",
43
+ "- memory_stats: inspect memory health and usage metrics.",
44
+ "Use memory tools when the user references prior work, asks what changed before, or when historical context could improve correctness.",
45
+ "</memory_context>",
46
+ ].filter(Boolean);
47
+ return truncateToBudget(sections.join("\n"), config.contextMaxTokens);
48
+ }
49
+ /**
50
+ * Generates a shorter context block used during compaction.
51
+ *
52
+ * @param store - Memory store.
53
+ * @param now - Clock function.
54
+ * @returns Compaction context.
55
+ */
56
+ export async function generateCompactionContext(store, now) {
57
+ const recent = await store.getRecentObservations(8);
58
+ if (!recent.length) {
59
+ return "";
60
+ }
61
+ return [
62
+ "Persistent project memory highlights:",
63
+ ...recent.map((observation) => `- ${observation.title} (${observation.type}, ${formatRelativeTime(observation.createdAt, now)}): ${normalizeWhitespace(observation.narrative)}`),
64
+ ].join("\n");
65
+ }
66
+ /**
67
+ * Truncates a context string to a token budget using a simple heuristic.
68
+ *
69
+ * @param value - Context text.
70
+ * @param maxTokens - Maximum allowed tokens.
71
+ * @returns Truncated or original text.
72
+ */
73
+ export function truncateToBudget(value, maxTokens) {
74
+ const maxCharacters = maxTokens * 4;
75
+ if (value.length <= maxCharacters) {
76
+ return value;
77
+ }
78
+ return `${value.slice(0, maxCharacters - 32).trim()}\n...`;
79
+ }
80
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/context/generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Hooks } from "@opencode-ai/plugin";
2
+ import { MemoryStore } from "../storage/store";
3
+ import type { ProjectScope, RuntimeState } from "../types";
4
+ /**
5
+ * Creates the hook that captures user prompts for later summarization.
6
+ *
7
+ * @param store - Memory store.
8
+ * @param scope - Project scope.
9
+ * @param state - Runtime state.
10
+ * @param now - Clock function.
11
+ * @returns Hook implementation.
12
+ */
13
+ export declare function createChatMessageHook(store: MemoryStore, scope: ProjectScope, state: RuntimeState, now: () => number): NonNullable<Hooks["chat.message"]>;
14
+ //# sourceMappingURL=chat-message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../src/hooks/chat-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE1D;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,MAAM,MAAM,GAChB,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CA0BpC"}
@@ -0,0 +1,35 @@
1
+ import { MemoryStore } from "../storage/store";
2
+ /**
3
+ * Creates the hook that captures user prompts for later summarization.
4
+ *
5
+ * @param store - Memory store.
6
+ * @param scope - Project scope.
7
+ * @param state - Runtime state.
8
+ * @param now - Clock function.
9
+ * @returns Hook implementation.
10
+ */
11
+ export function createChatMessageHook(store, scope, state, now) {
12
+ return async (input, output) => {
13
+ if (state.internalSessionIds.has(input.sessionID)) {
14
+ return;
15
+ }
16
+ const content = output.parts
17
+ .filter((part) => part.type === "text")
18
+ .map((part) => part.text)
19
+ .join("\n")
20
+ .trim();
21
+ if (!content) {
22
+ return;
23
+ }
24
+ await store.saveUserPrompt({
25
+ id: store.createId(),
26
+ projectId: scope.projectId,
27
+ projectRoot: scope.projectRoot,
28
+ sessionId: input.sessionID,
29
+ messageId: output.message.id,
30
+ content,
31
+ createdAt: now(),
32
+ });
33
+ };
34
+ }
35
+ //# sourceMappingURL=chat-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-message.js","sourceRoot":"","sources":["../../src/hooks/chat-message.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAG9C;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAkB,EAClB,KAAmB,EACnB,KAAmB,EACnB,GAAiB;IAEjB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC7B,IAAI,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YAClD,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK;aACzB,MAAM,CAAC,CAAC,IAAI,EAA+C,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;aACnF,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;aACxB,IAAI,CAAC,IAAI,CAAC;aACV,IAAI,EAAE,CAAA;QAET,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAM;QACR,CAAC;QAED,MAAM,KAAK,CAAC,cAAc,CAAC;YACzB,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE;YACpB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE;YAC5B,OAAO;YACP,SAAS,EAAE,GAAG,EAAE;SACjB,CAAC,CAAA;IACJ,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Hooks } from "@opencode-ai/plugin";
2
+ import { MemoryStore } from "../storage/store";
3
+ import type { RuntimeState } from "../types";
4
+ /**
5
+ * Creates the compaction hook that preserves persistent memory context.
6
+ *
7
+ * @param store - Memory store.
8
+ * @param state - Runtime state.
9
+ * @param now - Clock function.
10
+ * @returns Hook implementation.
11
+ */
12
+ export declare function createCompactionHook(store: MemoryStore, state: RuntimeState, now: () => number): NonNullable<Hooks["experimental.session.compacting"]>;
13
+ //# sourceMappingURL=compaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/hooks/compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEhD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,MAAM,MAAM,GAChB,WAAW,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAWvD"}
@@ -0,0 +1,22 @@
1
+ import { generateCompactionContext } from "../context/generator";
2
+ import { MemoryStore } from "../storage/store";
3
+ /**
4
+ * Creates the compaction hook that preserves persistent memory context.
5
+ *
6
+ * @param store - Memory store.
7
+ * @param state - Runtime state.
8
+ * @param now - Clock function.
9
+ * @returns Hook implementation.
10
+ */
11
+ export function createCompactionHook(store, state, now) {
12
+ return async (input, output) => {
13
+ if (state.internalSessionIds.has(input.sessionID)) {
14
+ return;
15
+ }
16
+ const context = await generateCompactionContext(store, now);
17
+ if (context) {
18
+ output.context.push(context);
19
+ }
20
+ };
21
+ }
22
+ //# sourceMappingURL=compaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/hooks/compaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAG9C;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAkB,EAClB,KAAmB,EACnB,GAAiB;IAEjB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC7B,IAAI,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YAClD,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,yBAAyB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAC3D,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}