opencode-lcm 0.13.5 → 0.13.6
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/CHANGELOG.md +5 -0
- package/README.md +3 -0
- package/dist/index.js +52 -0
- package/dist/options.js +11 -0
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
- package/src/index.ts +65 -0
- package/src/options.ts +17 -0
- package/src/types.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.6] - 2026-04-11
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Bun on Windows now leaves the plugin in a pre-SQLite safe mode by default and requires an explicit override to enable the full archive hooks
|
|
14
|
+
|
|
10
15
|
## [0.13.5] - 2026-04-11
|
|
11
16
|
|
|
12
17
|
### Fixed
|
package/README.md
CHANGED
|
@@ -105,6 +105,9 @@ Add `opencode-lcm` to your `opencode.json` (project or global `~/.config/opencod
|
|
|
105
105
|
> [!IMPORTANT]
|
|
106
106
|
> All defaults are applied automatically. Expand below only if you need to override settings.
|
|
107
107
|
|
|
108
|
+
> [!IMPORTANT]
|
|
109
|
+
> On Bun for Windows, `opencode-lcm` now starts in a pre-SQLite safe mode and only exposes `lcm_status` by default. This avoids a Bun/Windows native-crash path seen in the field. To force-enable the full plugin anyway, set `"runtimeSafety": { "allowUnsafeBunWindows": true }` in the plugin config or export `OPENCODE_LCM_ALLOW_UNSAFE_BUN_WINDOWS=1` before starting OpenCode.
|
|
110
|
+
|
|
108
111
|
<details>
|
|
109
112
|
<summary><strong>Full Configuration</strong> (click to expand)</summary>
|
|
110
113
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,59 @@
|
|
|
1
1
|
import { tool } from '@opencode-ai/plugin';
|
|
2
2
|
import { resolveOptions } from './options.js';
|
|
3
3
|
import { SqliteLcmStore } from './store.js';
|
|
4
|
+
const ALLOW_UNSAFE_BUN_WINDOWS_ENV = 'OPENCODE_LCM_ALLOW_UNSAFE_BUN_WINDOWS';
|
|
5
|
+
function isTruthyEnvFlag(value) {
|
|
6
|
+
if (!value)
|
|
7
|
+
return false;
|
|
8
|
+
const normalized = value.trim().toLowerCase();
|
|
9
|
+
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
10
|
+
}
|
|
11
|
+
function isBunRuntime() {
|
|
12
|
+
return typeof globalThis === 'object' && globalThis !== null && 'Bun' in globalThis;
|
|
13
|
+
}
|
|
14
|
+
function isUnsafeBunWindowsRuntime() {
|
|
15
|
+
return isBunRuntime() && process.platform === 'win32';
|
|
16
|
+
}
|
|
17
|
+
function resolveAllowUnsafeBunWindows(options) {
|
|
18
|
+
return (options.runtimeSafety.allowUnsafeBunWindows ||
|
|
19
|
+
isTruthyEnvFlag(process.env[ALLOW_UNSAFE_BUN_WINDOWS_ENV]));
|
|
20
|
+
}
|
|
21
|
+
function buildSafeModeStatus(allowUnsafeBunWindows) {
|
|
22
|
+
return [
|
|
23
|
+
'status=disabled',
|
|
24
|
+
'reason=bun_windows_runtime_guard',
|
|
25
|
+
'available_tools=lcm_status',
|
|
26
|
+
`platform=${process.platform}`,
|
|
27
|
+
`bun_runtime=${isBunRuntime()}`,
|
|
28
|
+
`runtime_safety_allow_unsafe_bun_windows=${allowUnsafeBunWindows}`,
|
|
29
|
+
'override_config=runtimeSafety.allowUnsafeBunWindows=true',
|
|
30
|
+
`override_env=${ALLOW_UNSAFE_BUN_WINDOWS_ENV}=1`,
|
|
31
|
+
'message=opencode-lcm disabled itself before opening SQLite because Bun on Windows has reported native crashes in this path',
|
|
32
|
+
].join('\n');
|
|
33
|
+
}
|
|
34
|
+
function createSafeModeHooks(allowUnsafeBunWindows) {
|
|
35
|
+
return {
|
|
36
|
+
event: async () => { },
|
|
37
|
+
tool: {
|
|
38
|
+
lcm_status: tool({
|
|
39
|
+
description: 'Show archived LCM capture stats',
|
|
40
|
+
args: {},
|
|
41
|
+
async execute() {
|
|
42
|
+
return buildSafeModeStatus(allowUnsafeBunWindows);
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
'experimental.chat.messages.transform': async () => { },
|
|
47
|
+
'experimental.chat.system.transform': async () => { },
|
|
48
|
+
'experimental.session.compacting': async () => { },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
4
51
|
export const OpencodeLcmPlugin = async (ctx, rawOptions) => {
|
|
5
52
|
const options = resolveOptions(rawOptions);
|
|
53
|
+
const allowUnsafeBunWindows = resolveAllowUnsafeBunWindows(options);
|
|
54
|
+
if (isUnsafeBunWindowsRuntime() && !allowUnsafeBunWindows) {
|
|
55
|
+
return createSafeModeHooks(allowUnsafeBunWindows);
|
|
56
|
+
}
|
|
6
57
|
const store = new SqliteLcmStore(ctx.directory, options);
|
|
7
58
|
await store.init();
|
|
8
59
|
return {
|
|
@@ -49,6 +100,7 @@ export const OpencodeLcmPlugin = async (ctx, rawOptions) => {
|
|
|
49
100
|
`fresh_tail_messages=${options.freshTailMessages}`,
|
|
50
101
|
`min_messages_for_transform=${options.minMessagesForTransform}`,
|
|
51
102
|
`large_content_threshold=${options.largeContentThreshold}`,
|
|
103
|
+
`runtime_safety_allow_unsafe_bun_windows=${allowUnsafeBunWindows}`,
|
|
52
104
|
`binary_preview_providers=${options.binaryPreviewProviders.join(',')}`,
|
|
53
105
|
`preview_byte_peek=${options.previewBytePeek}`,
|
|
54
106
|
`privacy_exclude_tool_prefixes=${options.privacy.excludeToolPrefixes.join(',')}`,
|
package/dist/options.js
CHANGED
|
@@ -40,6 +40,9 @@ export const DEFAULT_SUMMARY_V2 = {
|
|
|
40
40
|
strategy: 'deterministic-v2',
|
|
41
41
|
perMessageBudget: 110,
|
|
42
42
|
};
|
|
43
|
+
const DEFAULT_RUNTIME_SAFETY = {
|
|
44
|
+
allowUnsafeBunWindows: false,
|
|
45
|
+
};
|
|
43
46
|
export const DEFAULT_OPTIONS = {
|
|
44
47
|
interop: DEFAULT_INTEROP,
|
|
45
48
|
scopeDefaults: DEFAULT_SCOPE_DEFAULTS,
|
|
@@ -65,6 +68,7 @@ export const DEFAULT_OPTIONS = {
|
|
|
65
68
|
],
|
|
66
69
|
previewBytePeek: 16,
|
|
67
70
|
summaryV2: DEFAULT_SUMMARY_V2,
|
|
71
|
+
runtimeSafety: DEFAULT_RUNTIME_SAFETY,
|
|
68
72
|
};
|
|
69
73
|
function asRecord(value) {
|
|
70
74
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
@@ -199,6 +203,12 @@ function asSummaryV2Options(value, fallback) {
|
|
|
199
203
|
perMessageBudget: asNumber(record?.perMessageBudget, fallback.perMessageBudget),
|
|
200
204
|
};
|
|
201
205
|
}
|
|
206
|
+
function asRuntimeSafetyOptions(value, fallback) {
|
|
207
|
+
const record = asRecord(value);
|
|
208
|
+
return {
|
|
209
|
+
allowUnsafeBunWindows: asBoolean(record?.allowUnsafeBunWindows, fallback.allowUnsafeBunWindows),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
202
212
|
export function resolveOptions(raw) {
|
|
203
213
|
const options = asRecord(raw);
|
|
204
214
|
const interop = asRecord(options?.interop);
|
|
@@ -229,5 +239,6 @@ export function resolveOptions(raw) {
|
|
|
229
239
|
binaryPreviewProviders: asStringArray(options?.binaryPreviewProviders, DEFAULT_OPTIONS.binaryPreviewProviders),
|
|
230
240
|
previewBytePeek: asNumber(options?.previewBytePeek, DEFAULT_OPTIONS.previewBytePeek),
|
|
231
241
|
summaryV2: asSummaryV2Options(options?.summaryV2, DEFAULT_SUMMARY_V2),
|
|
242
|
+
runtimeSafety: asRuntimeSafetyOptions(options?.runtimeSafety, DEFAULT_RUNTIME_SAFETY),
|
|
232
243
|
};
|
|
233
244
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -50,6 +50,9 @@ export type SummaryV2Options = {
|
|
|
50
50
|
strategy: SummaryStrategyName;
|
|
51
51
|
perMessageBudget: number;
|
|
52
52
|
};
|
|
53
|
+
export type RuntimeSafetyOptions = {
|
|
54
|
+
allowUnsafeBunWindows: boolean;
|
|
55
|
+
};
|
|
53
56
|
export type OpencodeLcmOptions = {
|
|
54
57
|
interop: InteropOptions;
|
|
55
58
|
scopeDefaults: ScopeDefaults;
|
|
@@ -70,6 +73,7 @@ export type OpencodeLcmOptions = {
|
|
|
70
73
|
binaryPreviewProviders: string[];
|
|
71
74
|
previewBytePeek: number;
|
|
72
75
|
summaryV2: SummaryV2Options;
|
|
76
|
+
runtimeSafety: RuntimeSafetyOptions;
|
|
73
77
|
};
|
|
74
78
|
export type CapturedEvent = {
|
|
75
79
|
id: string;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -2,11 +2,75 @@ import { type Hooks, type PluginInput, tool } from '@opencode-ai/plugin';
|
|
|
2
2
|
|
|
3
3
|
import { resolveOptions } from './options.js';
|
|
4
4
|
import { SqliteLcmStore } from './store.js';
|
|
5
|
+
import type { OpencodeLcmOptions } from './types.js';
|
|
5
6
|
|
|
6
7
|
type PluginWithOptions = (ctx: PluginInput, rawOptions?: unknown) => Promise<Hooks>;
|
|
7
8
|
|
|
9
|
+
const ALLOW_UNSAFE_BUN_WINDOWS_ENV = 'OPENCODE_LCM_ALLOW_UNSAFE_BUN_WINDOWS';
|
|
10
|
+
|
|
11
|
+
function isTruthyEnvFlag(value: string | undefined): boolean {
|
|
12
|
+
if (!value) return false;
|
|
13
|
+
const normalized = value.trim().toLowerCase();
|
|
14
|
+
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isBunRuntime(): boolean {
|
|
18
|
+
return typeof globalThis === 'object' && globalThis !== null && 'Bun' in globalThis;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isUnsafeBunWindowsRuntime(): boolean {
|
|
22
|
+
return isBunRuntime() && process.platform === 'win32';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function resolveAllowUnsafeBunWindows(options: OpencodeLcmOptions): boolean {
|
|
26
|
+
return (
|
|
27
|
+
options.runtimeSafety.allowUnsafeBunWindows ||
|
|
28
|
+
isTruthyEnvFlag(process.env[ALLOW_UNSAFE_BUN_WINDOWS_ENV])
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildSafeModeStatus(allowUnsafeBunWindows: boolean): string {
|
|
33
|
+
return [
|
|
34
|
+
'status=disabled',
|
|
35
|
+
'reason=bun_windows_runtime_guard',
|
|
36
|
+
'available_tools=lcm_status',
|
|
37
|
+
`platform=${process.platform}`,
|
|
38
|
+
`bun_runtime=${isBunRuntime()}`,
|
|
39
|
+
`runtime_safety_allow_unsafe_bun_windows=${allowUnsafeBunWindows}`,
|
|
40
|
+
'override_config=runtimeSafety.allowUnsafeBunWindows=true',
|
|
41
|
+
`override_env=${ALLOW_UNSAFE_BUN_WINDOWS_ENV}=1`,
|
|
42
|
+
'message=opencode-lcm disabled itself before opening SQLite because Bun on Windows has reported native crashes in this path',
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createSafeModeHooks(allowUnsafeBunWindows: boolean): Hooks {
|
|
47
|
+
return {
|
|
48
|
+
event: async () => {},
|
|
49
|
+
|
|
50
|
+
tool: {
|
|
51
|
+
lcm_status: tool({
|
|
52
|
+
description: 'Show archived LCM capture stats',
|
|
53
|
+
args: {},
|
|
54
|
+
async execute() {
|
|
55
|
+
return buildSafeModeStatus(allowUnsafeBunWindows);
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
'experimental.chat.messages.transform': async () => {},
|
|
61
|
+
'experimental.chat.system.transform': async () => {},
|
|
62
|
+
'experimental.session.compacting': async () => {},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
8
66
|
export const OpencodeLcmPlugin: PluginWithOptions = async (ctx, rawOptions) => {
|
|
9
67
|
const options = resolveOptions(rawOptions);
|
|
68
|
+
const allowUnsafeBunWindows = resolveAllowUnsafeBunWindows(options);
|
|
69
|
+
|
|
70
|
+
if (isUnsafeBunWindowsRuntime() && !allowUnsafeBunWindows) {
|
|
71
|
+
return createSafeModeHooks(allowUnsafeBunWindows);
|
|
72
|
+
}
|
|
73
|
+
|
|
10
74
|
const store = new SqliteLcmStore(ctx.directory, options);
|
|
11
75
|
|
|
12
76
|
await store.init();
|
|
@@ -56,6 +120,7 @@ export const OpencodeLcmPlugin: PluginWithOptions = async (ctx, rawOptions) => {
|
|
|
56
120
|
`fresh_tail_messages=${options.freshTailMessages}`,
|
|
57
121
|
`min_messages_for_transform=${options.minMessagesForTransform}`,
|
|
58
122
|
`large_content_threshold=${options.largeContentThreshold}`,
|
|
123
|
+
`runtime_safety_allow_unsafe_bun_windows=${allowUnsafeBunWindows}`,
|
|
59
124
|
`binary_preview_providers=${options.binaryPreviewProviders.join(',')}`,
|
|
60
125
|
`preview_byte_peek=${options.previewBytePeek}`,
|
|
61
126
|
`privacy_exclude_tool_prefixes=${options.privacy.excludeToolPrefixes.join(',')}`,
|
package/src/options.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
OpencodeLcmOptions,
|
|
7
7
|
PrivacyOptions,
|
|
8
8
|
RetentionPolicyOptions,
|
|
9
|
+
RuntimeSafetyOptions,
|
|
9
10
|
ScopeDefaults,
|
|
10
11
|
ScopeName,
|
|
11
12
|
ScopeProfile,
|
|
@@ -61,6 +62,10 @@ export const DEFAULT_SUMMARY_V2: SummaryV2Options = {
|
|
|
61
62
|
perMessageBudget: 110,
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
const DEFAULT_RUNTIME_SAFETY: RuntimeSafetyOptions = {
|
|
66
|
+
allowUnsafeBunWindows: false,
|
|
67
|
+
};
|
|
68
|
+
|
|
64
69
|
export const DEFAULT_OPTIONS: OpencodeLcmOptions = {
|
|
65
70
|
interop: DEFAULT_INTEROP,
|
|
66
71
|
scopeDefaults: DEFAULT_SCOPE_DEFAULTS,
|
|
@@ -86,6 +91,7 @@ export const DEFAULT_OPTIONS: OpencodeLcmOptions = {
|
|
|
86
91
|
],
|
|
87
92
|
previewBytePeek: 16,
|
|
88
93
|
summaryV2: DEFAULT_SUMMARY_V2,
|
|
94
|
+
runtimeSafety: DEFAULT_RUNTIME_SAFETY,
|
|
89
95
|
};
|
|
90
96
|
|
|
91
97
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
@@ -258,6 +264,16 @@ function asSummaryV2Options(value: unknown, fallback: SummaryV2Options): Summary
|
|
|
258
264
|
};
|
|
259
265
|
}
|
|
260
266
|
|
|
267
|
+
function asRuntimeSafetyOptions(
|
|
268
|
+
value: unknown,
|
|
269
|
+
fallback: RuntimeSafetyOptions,
|
|
270
|
+
): RuntimeSafetyOptions {
|
|
271
|
+
const record = asRecord(value);
|
|
272
|
+
return {
|
|
273
|
+
allowUnsafeBunWindows: asBoolean(record?.allowUnsafeBunWindows, fallback.allowUnsafeBunWindows),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
261
277
|
export function resolveOptions(raw: unknown): OpencodeLcmOptions {
|
|
262
278
|
const options = asRecord(raw);
|
|
263
279
|
const interop = asRecord(options?.interop);
|
|
@@ -314,5 +330,6 @@ export function resolveOptions(raw: unknown): OpencodeLcmOptions {
|
|
|
314
330
|
),
|
|
315
331
|
previewBytePeek: asNumber(options?.previewBytePeek, DEFAULT_OPTIONS.previewBytePeek),
|
|
316
332
|
summaryV2: asSummaryV2Options(options?.summaryV2, DEFAULT_SUMMARY_V2),
|
|
333
|
+
runtimeSafety: asRuntimeSafetyOptions(options?.runtimeSafety, DEFAULT_RUNTIME_SAFETY),
|
|
317
334
|
};
|
|
318
335
|
}
|
package/src/types.ts
CHANGED
|
@@ -62,6 +62,10 @@ export type SummaryV2Options = {
|
|
|
62
62
|
perMessageBudget: number;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
export type RuntimeSafetyOptions = {
|
|
66
|
+
allowUnsafeBunWindows: boolean;
|
|
67
|
+
};
|
|
68
|
+
|
|
65
69
|
export type OpencodeLcmOptions = {
|
|
66
70
|
interop: InteropOptions;
|
|
67
71
|
scopeDefaults: ScopeDefaults;
|
|
@@ -82,6 +86,7 @@ export type OpencodeLcmOptions = {
|
|
|
82
86
|
binaryPreviewProviders: string[];
|
|
83
87
|
previewBytePeek: number;
|
|
84
88
|
summaryV2: SummaryV2Options;
|
|
89
|
+
runtimeSafety: RuntimeSafetyOptions;
|
|
85
90
|
};
|
|
86
91
|
|
|
87
92
|
export type CapturedEvent = {
|