opencode-lcm 0.13.2 → 0.13.5

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 CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.13.5] - 2026-04-11
11
+
12
+ ### Fixed
13
+ - Restored schema-v2 compatibility for persisted stores after the PR #6 merge
14
+ - Removed the unfinished `llm-cli` summary configuration surface before shipping it
15
+ - Restored Biome-clean formatting so the full cross-platform CI matrix passes again
16
+
17
+ ## [0.13.4] - 2026-04-09
18
+
19
+ ### Fixed
20
+ - Publish validation now keeps the Bun-on-Windows lightweight capture regression test portable across non-Windows CI runners
21
+ - Republish the current malformed-message and Bun Windows capture fixes to npm after the failed 0.13.3 release check
22
+
23
+ ## [0.13.3] - 2026-04-09
24
+
25
+ ### Fixed
26
+ - Bun on Windows now tolerates part-update capture when the parent message has not been materialized yet
27
+ - Additional malformed-message hardening now covers `message.info.time.created` reads and grep scan fallback paths
28
+ - Restored CI-clean formatting after the PR #5 merge so release validation and publish can complete
29
+
10
30
  ## [0.13.2] - 2026-04-08
11
31
 
12
32
  ### Fixed
@@ -5,7 +5,7 @@
5
5
  export declare const SUMMARY_LEAF_MESSAGES = 6;
6
6
  export declare const SUMMARY_BRANCH_FACTOR = 3;
7
7
  export declare const SUMMARY_NODE_CHAR_LIMIT = 260;
8
- export declare const STORE_SCHEMA_VERSION = 1;
8
+ export declare const STORE_SCHEMA_VERSION = 2;
9
9
  export declare const EXPAND_MESSAGE_LIMIT = 6;
10
10
  export declare const AUTOMATIC_RETRIEVAL_QUERY_TOKENS = 8;
11
11
  export declare const AUTOMATIC_RETRIEVAL_RECENT_MESSAGES = 3;
package/dist/constants.js CHANGED
@@ -7,7 +7,7 @@ export const SUMMARY_LEAF_MESSAGES = 6;
7
7
  export const SUMMARY_BRANCH_FACTOR = 3;
8
8
  export const SUMMARY_NODE_CHAR_LIMIT = 260;
9
9
  // Store schema
10
- export const STORE_SCHEMA_VERSION = 1;
10
+ export const STORE_SCHEMA_VERSION = 2;
11
11
  // Message retrieval limits
12
12
  export const EXPAND_MESSAGE_LIMIT = 6;
13
13
  // Automatic retrieval configuration
package/dist/options.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- import type { OpencodeLcmOptions } from './types.js';
1
+ import type { OpencodeLcmOptions, SummaryV2Options } from './types.js';
2
+ export declare const DEFAULT_SUMMARY_V2: SummaryV2Options;
2
3
  export declare const DEFAULT_OPTIONS: OpencodeLcmOptions;
3
4
  export declare function resolveOptions(raw: unknown): OpencodeLcmOptions;
package/dist/options.js CHANGED
@@ -36,6 +36,10 @@ const DEFAULT_AUTOMATIC_RETRIEVAL = {
36
36
  stopOnFirstScopeWithHits: false,
37
37
  },
38
38
  };
39
+ export const DEFAULT_SUMMARY_V2 = {
40
+ strategy: 'deterministic-v2',
41
+ perMessageBudget: 110,
42
+ };
39
43
  export const DEFAULT_OPTIONS = {
40
44
  interop: DEFAULT_INTEROP,
41
45
  scopeDefaults: DEFAULT_SCOPE_DEFAULTS,
@@ -60,6 +64,7 @@ export const DEFAULT_OPTIONS = {
60
64
  'zip-metadata',
61
65
  ],
62
66
  previewBytePeek: 16,
67
+ summaryV2: DEFAULT_SUMMARY_V2,
63
68
  };
64
69
  function asRecord(value) {
65
70
  if (!value || typeof value !== 'object' || Array.isArray(value))
@@ -184,6 +189,16 @@ function asAutomaticRetrievalStopOptions(value, fallback) {
184
189
  stopOnFirstScopeWithHits: asBoolean(record?.stopOnFirstScopeWithHits, fallback.stopOnFirstScopeWithHits),
185
190
  };
186
191
  }
192
+ function asSummaryStrategy(value, fallback) {
193
+ return value === 'deterministic-v1' || value === 'deterministic-v2' ? value : fallback;
194
+ }
195
+ function asSummaryV2Options(value, fallback) {
196
+ const record = asRecord(value);
197
+ return {
198
+ strategy: asSummaryStrategy(record?.strategy, fallback.strategy),
199
+ perMessageBudget: asNumber(record?.perMessageBudget, fallback.perMessageBudget),
200
+ };
201
+ }
187
202
  export function resolveOptions(raw) {
188
203
  const options = asRecord(raw);
189
204
  const interop = asRecord(options?.interop);
@@ -213,5 +228,6 @@ export function resolveOptions(raw) {
213
228
  artifactViewChars: asNumber(options?.artifactViewChars, DEFAULT_OPTIONS.artifactViewChars),
214
229
  binaryPreviewProviders: asStringArray(options?.binaryPreviewProviders, DEFAULT_OPTIONS.binaryPreviewProviders),
215
230
  previewBytePeek: asNumber(options?.previewBytePeek, DEFAULT_OPTIONS.previewBytePeek),
231
+ summaryV2: asSummaryV2Options(options?.summaryV2, DEFAULT_SUMMARY_V2),
216
232
  };
217
233
  }
@@ -273,7 +273,7 @@ export async function externalizeMessage(bindings, message) {
273
273
  const storedInfo = parseJson(JSON.stringify(message.info));
274
274
  const storedParts = [];
275
275
  for (const part of message.parts) {
276
- const { storedPart, artifacts: nextArtifacts } = await externalizePart(bindings, part, message.info.time.created);
276
+ const { storedPart, artifacts: nextArtifacts } = await externalizePart(bindings, part, message.info?.time?.created ?? 0);
277
277
  artifacts.push(...nextArtifacts);
278
278
  storedParts.push(storedPart);
279
279
  }
@@ -292,7 +292,7 @@ export async function externalizeSession(bindings, session) {
292
292
  const storedInfo = parseJson(JSON.stringify(message.info));
293
293
  const storedParts = [];
294
294
  for (const part of message.parts) {
295
- const { storedPart, artifacts: nextArtifacts } = await externalizePart(bindings, part, message.info.time.created);
295
+ const { storedPart, artifacts: nextArtifacts } = await externalizePart(bindings, part, message.info?.time?.created ?? 0);
296
296
  artifacts.push(...nextArtifacts);
297
297
  storedParts.push(storedPart);
298
298
  }
@@ -335,7 +335,7 @@ export function persistStoredSessionSync(bindings, storedSession, artifacts) {
335
335
  const insertMessage = db.prepare('INSERT INTO messages (message_id, session_id, created_at, info_json) VALUES (?, ?, ?, ?)');
336
336
  const insertPart = db.prepare('INSERT INTO parts (part_id, session_id, message_id, sort_key, part_json) VALUES (?, ?, ?, ?, ?)');
337
337
  for (const message of storedSession.messages) {
338
- insertMessage.run(message.info.id, storedSession.sessionID, message.info.time.created, JSON.stringify(message.info));
338
+ insertMessage.run(message.info.id, storedSession.sessionID, message.info?.time?.created ?? 0, JSON.stringify(message.info));
339
339
  message.parts.forEach((part, index) => {
340
340
  insertPart.run(part.id, storedSession.sessionID, part.messageID, index, JSON.stringify(part));
341
341
  });
@@ -202,7 +202,7 @@ export function searchByScan(deps, query, sessionIDs, limit = 5) {
202
202
  id: message.info.id,
203
203
  type: message.info.role,
204
204
  sessionID: session.sessionID,
205
- timestamp: message.info.time.created,
205
+ timestamp: message.info?.time?.created ?? 0,
206
206
  snippet: buildSnippet(blob, query),
207
207
  content: blob,
208
208
  sourceKind: 'message',
@@ -255,7 +255,7 @@ function insertMessageSearchRowsSync(deps, session) {
255
255
  const content = deps.guessMessageText(message, deps.ignoreToolPrefixes);
256
256
  if (!content)
257
257
  continue;
258
- insert.run(session.sessionID, message.info.id, message.info.role, String(message.info.time.created), content);
258
+ insert.run(session.sessionID, message.info.id, message.info.role, String(message.info?.time?.created ?? 0), content);
259
259
  }
260
260
  }
261
261
  export function replaceMessageSearchRowSync(deps, sessionID, message) {
@@ -264,7 +264,7 @@ export function replaceMessageSearchRowSync(deps, sessionID, message) {
264
264
  const content = deps.guessMessageText(message, deps.ignoreToolPrefixes);
265
265
  if (!content)
266
266
  return;
267
- db.prepare('INSERT INTO message_fts (session_id, message_id, role, created_at, content) VALUES (?, ?, ?, ?, ?)').run(sessionID, message.info.id, message.info.role, String(message.info.time.created), content);
267
+ db.prepare('INSERT INTO message_fts (session_id, message_id, role, created_at, content) VALUES (?, ?, ?, ?, ?)').run(sessionID, message.info.id, message.info.role, String(message.info?.time?.created ?? 0), content);
268
268
  }
269
269
  export function replaceSummarySearchRowsSync(deps, sessionIDs) {
270
270
  const db = deps.getDb();
@@ -1,4 +1,5 @@
1
1
  import type { SqlDatabaseLike } from './store-types.js';
2
+ import type { SummaryStrategyName } from './types.js';
2
3
  export type SnapshotScope = 'session' | 'root' | 'worktree' | 'all';
3
4
  export type SnapshotWorktreeMode = 'auto' | 'preserve' | 'current';
4
5
  export type SessionRow = {
@@ -38,6 +39,7 @@ export type SummaryNodeRow = {
38
39
  end_index: number;
39
40
  message_ids_json: string;
40
41
  summary_text: string;
42
+ strategy: SummaryStrategyName;
41
43
  created_at: number;
42
44
  };
43
45
  export type SummaryEdgeRow = {
@@ -81,8 +81,8 @@ export async function importStoreSnapshot(bindings, input) {
81
81
  const insertBlob = db.prepare(`INSERT OR REPLACE INTO artifact_blobs (content_hash, content_text, char_count, created_at) VALUES (?, ?, ?, ?)`);
82
82
  const insertArtifact = db.prepare(`INSERT OR REPLACE INTO artifacts (artifact_id, session_id, message_id, part_id, artifact_kind, field_name, preview_text, content_text, content_hash, metadata_json, char_count, created_at)
83
83
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
84
- const insertNode = db.prepare(`INSERT OR REPLACE INTO summary_nodes (node_id, session_id, level, node_kind, start_index, end_index, message_ids_json, summary_text, created_at)
85
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
84
+ const insertNode = db.prepare(`INSERT OR REPLACE INTO summary_nodes (node_id, session_id, level, node_kind, start_index, end_index, message_ids_json, summary_text, strategy, created_at)
85
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
86
86
  const insertEdge = db.prepare(`INSERT OR REPLACE INTO summary_edges (session_id, parent_id, child_id, child_position) VALUES (?, ?, ?, ?)`);
87
87
  const insertState = db.prepare(`INSERT OR REPLACE INTO summary_state (session_id, archived_count, latest_message_created, archived_signature, root_node_ids_json, updated_at)
88
88
  VALUES (?, ?, ?, ?, ?, ?)`);
@@ -101,7 +101,7 @@ export async function importStoreSnapshot(bindings, input) {
101
101
  insertArtifact.run(row.artifact_id, row.session_id, row.message_id, row.part_id, row.artifact_kind, row.field_name, row.preview_text, row.content_text, row.content_hash, row.metadata_json, row.char_count, row.created_at);
102
102
  }
103
103
  for (const row of snapshot.summary_nodes) {
104
- insertNode.run(row.node_id, row.session_id, row.level, row.node_kind, row.start_index, row.end_index, row.message_ids_json, row.summary_text, row.created_at);
104
+ insertNode.run(row.node_id, row.session_id, row.level, row.node_kind, row.start_index, row.end_index, row.message_ids_json, row.summary_text, row.strategy, row.created_at);
105
105
  }
106
106
  for (const row of snapshot.summary_edges)
107
107
  insertEdge.run(row.session_id, row.parent_id, row.child_id, row.child_position);
@@ -232,6 +232,7 @@ function parseSummaryNodeRow(value) {
232
232
  end_index: expectNumber(row.end_index, 'summary_nodes[].end_index'),
233
233
  message_ids_json: expectString(row.message_ids_json, 'summary_nodes[].message_ids_json'),
234
234
  summary_text: expectString(row.summary_text, 'summary_nodes[].summary_text'),
235
+ strategy: expectString(row.strategy ?? 'deterministic-v1', 'summary_nodes[].strategy'),
235
236
  created_at: expectNumber(row.created_at, 'summary_nodes[].created_at'),
236
237
  };
237
238
  }
package/dist/store.d.ts CHANGED
@@ -264,6 +264,8 @@ export declare class SqliteLcmStore {
264
264
  private compactMessageInPlace;
265
265
  private shouldIgnoreTool;
266
266
  private summarizeMessages;
267
+ private summarizeMessagesDeterministicV1;
268
+ private summarizeMessagesDeterministicV2;
267
269
  private listTools;
268
270
  private buildArchivedSignature;
269
271
  private getArchivedMessages;
@@ -294,6 +296,7 @@ export declare class SqliteLcmStore {
294
296
  private refreshSearchIndexesSync;
295
297
  private ensureSessionColumnsSync;
296
298
  private ensureSummaryStateColumnsSync;
299
+ private ensureSummaryNodeColumnsSync;
297
300
  private ensureArtifactColumnsSync;
298
301
  private backfillArtifactBlobsSync;
299
302
  private refreshAllLineageSync;