@codemem/opencode-plugin 0.40.0 → 0.40.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.
@@ -15,7 +15,7 @@ import {
15
15
 
16
16
  const TRUTHY_VALUES = ["1", "true", "yes"];
17
17
  const DISABLED_VALUES = ["0", "false", "off"];
18
- const PINNED_BACKEND_VERSION = "0.40.0";
18
+ const PINNED_BACKEND_VERSION = "0.40.1";
19
19
  const COMPAT_CHECK_DELAY_MS = 1500;
20
20
  const COMPAT_CHECK_CACHE_TTL_MS = 5 * 60 * 1000;
21
21
  const CODEMEM_CONTEXT_PART_ID_PREFIX = "codemem-context-";
@@ -91,6 +91,45 @@ const rejectsInternalLedgerFlag = (result) => {
91
91
  && /(?:unknown|unsupported|unrecognized|invalid)\s+(?:option|argument)/i.test(diagnostics);
92
92
  };
93
93
 
94
+ const parseFallbackStructuredError = (stdout) => {
95
+ const lines = String(stdout || "").trim().split(/\r?\n/).reverse();
96
+ for (const line of lines) {
97
+ try {
98
+ const parsed = JSON.parse(line);
99
+ if (parsed && typeof parsed === "object") {
100
+ return {
101
+ code: typeof parsed.error === "string" ? parsed.error : "",
102
+ message: typeof parsed.message === "string" ? parsed.message : "",
103
+ };
104
+ }
105
+ } catch {
106
+ // Continue past non-JSON CLI output.
107
+ }
108
+ }
109
+ return { code: "", message: "" };
110
+ };
111
+
112
+ const classifyFallbackCommandResult = (result) => {
113
+ const structured = parseFallbackStructuredError(result?.stdout);
114
+ const diagnostic = [structured.code, structured.message, result?.stderr, result?.stdout]
115
+ .filter(Boolean)
116
+ .join("\n");
117
+ if (/SQLITE_(?:BUSY|LOCKED)|database(?: table)? (?:is )?(?:busy|locked)/i.test(diagnostic)) {
118
+ return { retryable: true, cause: "SQLite database is locked" };
119
+ }
120
+ if (result?.exitCode == null && /(?:timeout|timed out|ETIMEDOUT)/i.test(diagnostic)) {
121
+ return { retryable: true, cause: "enqueue-raw-event command timeout" };
122
+ }
123
+ if (structured.code === "validation_error" || /invalid raw event|session id required/i.test(diagnostic)) {
124
+ return { retryable: false, cause: "enqueue-raw-event validation failed" };
125
+ }
126
+ if (/unknown command ['\"]?enqueue-raw-event|command not found|\bENOENT\b/i.test(diagnostic)) {
127
+ return { retryable: false, cause: "enqueue-raw-event command unavailable" };
128
+ }
129
+ const exitCode = result?.exitCode ?? "unknown";
130
+ return { retryable: false, cause: `enqueue-raw-event failed (${exitCode})` };
131
+ };
132
+
94
133
  const DEFAULT_LOG_PATH = (homeDir, cwd) => `${homeDir || cwd}/.codemem/plugin.log`;
95
134
 
96
135
  const resolveLogPath = (logPathEnvRaw, cwd, homeDir) => {
@@ -1369,13 +1408,24 @@ export const OpencodeMemPlugin = async ({
1369
1408
  };
1370
1409
 
1371
1410
  const queueRawEventViaCli = async (body) => {
1372
- const result = await runCli(["enqueue-raw-event"], {
1411
+ const runFallback = () => runCli(["enqueue-raw-event"], {
1373
1412
  stdinText: JSON.stringify(body),
1374
1413
  });
1414
+ let result = await runFallback();
1415
+ let classification = classifyFallbackCommandResult(result);
1416
+ let attemptedRetry = false;
1417
+ if (result?.exitCode !== 0 && classification.retryable) {
1418
+ attemptedRetry = true;
1419
+ result = await runFallback();
1420
+ classification = classifyFallbackCommandResult(result);
1421
+ }
1375
1422
  if (result?.exitCode !== 0) {
1376
- throw new Error(
1377
- `enqueue-raw-event failed (${result?.exitCode ?? "unknown"})`
1423
+ const retryExhausted = attemptedRetry && classification.retryable;
1424
+ const error = new Error(
1425
+ retryExhausted ? `${classification.cause} after retry` : classification.cause
1378
1426
  );
1427
+ error.retryable = classification.retryable && !attemptedRetry;
1428
+ throw error;
1379
1429
  }
1380
1430
  return true;
1381
1431
  };
@@ -1434,6 +1484,9 @@ export const OpencodeMemPlugin = async ({
1434
1484
  }
1435
1485
  return true;
1436
1486
  } catch (fallbackErr) {
1487
+ if (payload && typeof payload === "object") {
1488
+ payload._raw_fallback_terminal = fallbackErr?.retryable !== true;
1489
+ }
1437
1490
  await logLine(
1438
1491
  `raw_events.fallback.error sessionID=${sessionID} type=${type} err=${String(
1439
1492
  fallbackErr
@@ -1525,6 +1578,9 @@ export const OpencodeMemPlugin = async ({
1525
1578
  await queueRawEventViaCli(body);
1526
1579
  fallbackOk = true;
1527
1580
  } catch (fallbackErr) {
1581
+ if (payload && typeof payload === "object") {
1582
+ payload._raw_fallback_terminal = fallbackErr?.retryable !== true;
1583
+ }
1528
1584
  await logLine(
1529
1585
  `raw_events.fallback.error sessionID=${sessionID} type=${type} err=${String(
1530
1586
  fallbackErr
@@ -1570,6 +1626,7 @@ export const OpencodeMemPlugin = async ({
1570
1626
 
1571
1627
  if (!streamErrorNoted) {
1572
1628
  streamErrorNoted = true;
1629
+ fallbackFailureNoted = true;
1573
1630
  try {
1574
1631
  await client.app.log({
1575
1632
  service: "codemem",
@@ -2726,10 +2783,18 @@ export const OpencodeMemPlugin = async ({
2726
2783
  }
2727
2784
 
2728
2785
  const failed = [];
2786
+ let droppedCount = 0;
2729
2787
  for (const queuedEvent of batch) {
2730
2788
  if (queuedEvent && typeof queuedEvent === "object" && queuedEvent._raw_enqueued) {
2731
2789
  continue;
2732
2790
  }
2791
+ if (queuedEvent && typeof queuedEvent === "object" && queuedEvent._raw_fallback_terminal) {
2792
+ droppedCount += 1;
2793
+ await logLine(
2794
+ `flush.drop terminal_fallback event_id=${queuedEvent?._raw_event_id || "unknown"}`
2795
+ );
2796
+ continue;
2797
+ }
2733
2798
  const queuedSessionID =
2734
2799
  queuedEvent?._raw_session_id ||
2735
2800
  queuedEvent?.properties?.sessionID ||
@@ -2740,6 +2805,13 @@ export const OpencodeMemPlugin = async ({
2740
2805
  payload: queuedEvent,
2741
2806
  });
2742
2807
  if (!ok) {
2808
+ if (queuedEvent?._raw_fallback_terminal) {
2809
+ droppedCount += 1;
2810
+ await logLine(
2811
+ `flush.drop terminal_fallback event_id=${queuedEvent?._raw_event_id || "unknown"}`
2812
+ );
2813
+ continue;
2814
+ }
2743
2815
  const currentRetry =
2744
2816
  typeof queuedEvent?._raw_retry_count === "number" && Number.isFinite(queuedEvent._raw_retry_count)
2745
2817
  ? queuedEvent._raw_retry_count
@@ -2764,7 +2836,7 @@ export const OpencodeMemPlugin = async ({
2764
2836
  await logLine(
2765
2837
  `flush.stream_only finalize count=${batch.length} tools=${sessionContext.toolCount} prompts=${sessionContext.promptCount} duration=${Math.round(durationMs / 1000)}s`
2766
2838
  );
2767
- await logLine(`flush.ok count=${batch.length}`);
2839
+ await logLine(`flush.ok count=${batch.length - droppedCount} dropped=${droppedCount}`);
2768
2840
  sessionStartedAt = null;
2769
2841
  resetSessionContext();
2770
2842
  };
@@ -3203,6 +3275,7 @@ export const __testUtils = {
3203
3275
  hashPromptPackQuery,
3204
3276
  redactPackCommand,
3205
3277
  rejectsInternalLedgerFlag,
3278
+ classifyFallbackCommandResult,
3206
3279
  parsePackText,
3207
3280
  parsePackMetrics,
3208
3281
  resolveInjectSurface,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemem/opencode-plugin",
3
- "version": "0.40.0",
3
+ "version": "0.40.1",
4
4
  "description": "CodeMem plugin for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./index.js",