@agenr/openclaw-plugin 2026.6.2 → 2026.6.3

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,6 +1,6 @@
1
1
  import {
2
2
  formatTargetSelectorFromParams
3
- } from "./chunk-LDJN7CVU.js";
3
+ } from "./chunk-U74RE3L7.js";
4
4
 
5
5
  // src/adapters/openclaw/transcript/parser.ts
6
6
  import { createHash } from "crypto";
@@ -28,7 +28,7 @@ import {
28
28
  parseExpiry,
29
29
  parseRecallMode,
30
30
  storeDurablesDetailed
31
- } from "./chunk-LDJN7CVU.js";
31
+ } from "./chunk-U74RE3L7.js";
32
32
  import {
33
33
  containsAgenrMemoryContext,
34
34
  createSessionStartRepository,
@@ -768,6 +768,17 @@ function buildSessionEndSourceRef(source) {
768
768
  return `${SESSION_END_SOURCE_REF_PREFIX}${source.trim()}`;
769
769
  }
770
770
 
771
+ // src/adapters/shared/light-dream-trigger-deps.ts
772
+ function buildLightDreamTriggerDeps(services) {
773
+ return {
774
+ port: services.dreaming,
775
+ dbPath: services.config.dbPath,
776
+ config: services.agenrConfig,
777
+ embedding: services.embedding,
778
+ ...services.claimExtraction ? { createClaimExtractionLlm: () => services.claimExtraction.llm } : {}
779
+ };
780
+ }
781
+
771
782
  // src/app/dreaming/background-triggers.ts
772
783
  var MINUTE_MS = 60 * 1e3;
773
784
  async function maybeRunLightDream(input, deps) {
@@ -852,6 +863,36 @@ function hasEvidence(scan) {
852
863
  return scan.episodesSinceLastRun > 0 || scan.ingestFilesSinceLastRun > 0 || scan.durablesCreatedSinceLastRun > 0;
853
864
  }
854
865
 
866
+ // src/adapters/shared/post-session-light-dream.ts
867
+ async function runPostSessionLightDream(params) {
868
+ const contextSuffix = params.sessionContext ? ` for ${params.sessionContext}` : "";
869
+ try {
870
+ const result = await maybeRunLightDream({ trigger: "post_session" }, params.deps);
871
+ if (result.status === "ran") {
872
+ params.logger.info(`[agenr] ${params.scope} light dream completed${contextSuffix} run=${result.result.runId}`);
873
+ return;
874
+ }
875
+ if (result.reason === "run_in_progress" || result.reason === "episode_write_in_progress") {
876
+ params.logger.info(`[agenr] ${params.scope} light dream skipped${contextSuffix} reason=${result.reason}`);
877
+ return;
878
+ }
879
+ params.logger.debug?.(`[agenr] ${params.scope} light dream skipped${contextSuffix} reason=${result.reason}`);
880
+ } catch (error) {
881
+ params.logger.warn(`[agenr] ${params.scope} light dream failed${contextSuffix}: ${formatErrorMessage(error)}`);
882
+ }
883
+ }
884
+
885
+ // src/adapters/shared/guarded-post-session-episode-capture.ts
886
+ async function runGuardedPostSessionEpisodeCapture(params) {
887
+ await withEpisodeWriteGuard({ port: params.services.dreaming, dbPath: params.services.config.dbPath }, params.writeEpisode);
888
+ await runPostSessionLightDream({
889
+ deps: buildLightDreamTriggerDeps(params.services),
890
+ logger: params.logger,
891
+ scope: params.scope,
892
+ sessionContext: params.sessionContext
893
+ });
894
+ }
895
+
855
896
  // src/adapters/plugin-runtime/claim-extraction.ts
856
897
  async function buildClaimExtractionRuntime(config, resolveLlm) {
857
898
  const claimExtractionConfig = resolveClaimExtractionConfig(config);
@@ -2042,6 +2083,12 @@ async function createHostMemoryServices(featureFlags, options = {}) {
2042
2083
  };
2043
2084
  }
2044
2085
 
2086
+ // src/adapters/shared/shutdown-episode-threshold.ts
2087
+ var HOST_SHUTDOWN_EPISODE_ACTIVITY_THRESHOLD = {
2088
+ minMaterialTurns: 8,
2089
+ minDurationMs: 20 * 60 * 1e3
2090
+ };
2091
+
2045
2092
  // src/adapters/shared/bounded-episode-embedding.ts
2046
2093
  var EPISODE_EMBEDDING_RACE_TIMEOUT = /* @__PURE__ */ Symbol("episode-embedding-race-timeout");
2047
2094
  var EPISODE_EMBEDDING_MIN_HEADROOM_MS = 5e3;
@@ -2982,6 +3029,7 @@ export {
2982
3029
  isPluginEpisodeWriteEnabled,
2983
3030
  resolveAgenrFeatureFlags,
2984
3031
  resolveCompactionPromptContext,
3032
+ HOST_SHUTDOWN_EPISODE_ACTIVITY_THRESHOLD,
2985
3033
  embedEpisodeSummaryWithinBudget,
2986
3034
  EPISODE_SUMMARY_TIMEOUT_MS,
2987
3035
  writeBoundedSingleTranscriptEpisode,
@@ -2989,7 +3037,9 @@ export {
2989
3037
  buildSessionFileSourceRef,
2990
3038
  buildCompactionSourceRef,
2991
3039
  buildSessionEndSourceRef,
3040
+ buildLightDreamTriggerDeps,
2992
3041
  maybeRunLightDream,
3042
+ runGuardedPostSessionEpisodeCapture,
2993
3043
  buildClaimExtractionRuntime,
2994
3044
  createClaimExtractionFromAgenrConfig,
2995
3045
  createEmbedQuery,
@@ -343,64 +343,6 @@ function sanitizeFetchToolParams(params) {
343
343
  };
344
344
  }
345
345
 
346
- // src/app/episode-ingest/activity-threshold.ts
347
- function resolveEpisodeActivityEligibility(materialTurns, startedAt, endedAt, threshold) {
348
- const durationMs = resolveTranscriptDurationMs(startedAt, endedAt);
349
- if (materialTurns >= threshold.minMaterialTurns || durationMs >= threshold.minDurationMs) {
350
- return {
351
- eligible: true,
352
- materialTurns,
353
- durationMs
354
- };
355
- }
356
- return {
357
- eligible: false,
358
- reason: "below_activity_threshold",
359
- materialTurns,
360
- durationMs
361
- };
362
- }
363
- function resolveTranscriptDurationMs(startedAt, endedAt) {
364
- if (!startedAt || !endedAt) {
365
- return 0;
366
- }
367
- const started = Date.parse(startedAt);
368
- const ended = Date.parse(endedAt);
369
- return Number.isFinite(started) && Number.isFinite(ended) && ended > started ? ended - started : 0;
370
- }
371
-
372
- // src/core/episode/transcript-render.ts
373
- var MIN_EPISODE_MESSAGES = 4;
374
- var MAX_EPISODE_TRANSCRIPT_CHARS = 14e3;
375
- function countMaterialTranscriptTurns(messages) {
376
- return messages.filter((message) => message.text.trim().length > 0).length;
377
- }
378
- function renderTranscript(messages) {
379
- return messages.map((message) => `${message.role === "user" ? "User" : "Assistant"}: ${message.text.trim()}`).join("\n");
380
- }
381
- function capEpisodeTranscript(transcript, maxChars) {
382
- if (transcript.length <= maxChars) {
383
- return transcript;
384
- }
385
- const omissionMarker = "\n\n[Earlier middle transcript omitted for brevity]\n\n";
386
- const headBudget = Math.max(0, Math.floor((maxChars - omissionMarker.length) * 0.35));
387
- const tailBudget = Math.max(0, maxChars - omissionMarker.length - headBudget);
388
- const head = trimToBoundary(transcript.slice(0, headBudget), false);
389
- const tail = trimToBoundary(transcript.slice(-tailBudget), true);
390
- return `${head}${omissionMarker}${tail}`.trim();
391
- }
392
- function trimToBoundary(value, fromStart) {
393
- if (value.length === 0) {
394
- return value;
395
- }
396
- if (fromStart) {
397
- const boundary = value.search(/\s/u);
398
- return boundary >= 0 ? value.slice(boundary).trimStart() : value.trim();
399
- }
400
- const reversedBoundary = value.trimEnd().search(/\s\S*$/u);
401
- return reversedBoundary >= 0 ? value.slice(0, reversedBoundary).trimEnd() : value.trim();
402
- }
403
-
404
346
  // src/app/episode-ingest/single-transcript.ts
405
347
  function createSingleTranscriptDiscoveryPort(filePath) {
406
348
  return {
@@ -693,6 +635,66 @@ async function generateEpisodeSummary(transcript, llm) {
693
635
 
694
636
  // src/app/episode-ingest/service/preflight.ts
695
637
  import path from "path";
638
+
639
+ // src/app/episode-ingest/activity-threshold.ts
640
+ function resolveEpisodeActivityEligibility(materialTurns, startedAt, endedAt, threshold) {
641
+ const durationMs = resolveTranscriptDurationMs(startedAt, endedAt);
642
+ if (materialTurns >= threshold.minMaterialTurns || durationMs >= threshold.minDurationMs) {
643
+ return {
644
+ eligible: true,
645
+ materialTurns,
646
+ durationMs
647
+ };
648
+ }
649
+ return {
650
+ eligible: false,
651
+ reason: "below_activity_threshold",
652
+ materialTurns,
653
+ durationMs
654
+ };
655
+ }
656
+ function resolveTranscriptDurationMs(startedAt, endedAt) {
657
+ if (!startedAt || !endedAt) {
658
+ return 0;
659
+ }
660
+ const started = Date.parse(startedAt);
661
+ const ended = Date.parse(endedAt);
662
+ return Number.isFinite(started) && Number.isFinite(ended) && ended > started ? ended - started : 0;
663
+ }
664
+
665
+ // src/core/episode/transcript-render.ts
666
+ var MIN_EPISODE_MESSAGES = 4;
667
+ var MAX_EPISODE_TRANSCRIPT_CHARS = 14e3;
668
+ function countMaterialTranscriptTurns(messages) {
669
+ return messages.filter((message) => message.text.trim().length > 0).length;
670
+ }
671
+ function renderTranscript(messages) {
672
+ return messages.map((message) => `${message.role === "user" ? "User" : "Assistant"}: ${message.text.trim()}`).join("\n");
673
+ }
674
+ function capEpisodeTranscript(transcript, maxChars) {
675
+ if (transcript.length <= maxChars) {
676
+ return transcript;
677
+ }
678
+ const omissionMarker = "\n\n[Earlier middle transcript omitted for brevity]\n\n";
679
+ const headBudget = Math.max(0, Math.floor((maxChars - omissionMarker.length) * 0.35));
680
+ const tailBudget = Math.max(0, maxChars - omissionMarker.length - headBudget);
681
+ const head = trimToBoundary(transcript.slice(0, headBudget), false);
682
+ const tail = trimToBoundary(transcript.slice(-tailBudget), true);
683
+ return `${head}${omissionMarker}${tail}`.trim();
684
+ }
685
+ function trimToBoundary(value, fromStart) {
686
+ if (value.length === 0) {
687
+ return value;
688
+ }
689
+ if (fromStart) {
690
+ const boundary = value.search(/\s/u);
691
+ return boundary >= 0 ? value.slice(boundary).trimStart() : value.trim();
692
+ }
693
+ const reversedBoundary = value.trimEnd().search(/\s\S*$/u);
694
+ return reversedBoundary >= 0 ? value.slice(0, reversedBoundary).trimEnd() : value.trim();
695
+ }
696
+
697
+ // src/app/episode-ingest/service/preflight.ts
696
698
  var ACTIVE_SESSION_WINDOW_MS = 5 * 60 * 1e3;
697
699
  async function prepareEpisodeIngest(targetPath, ports, options = {}) {
698
700
  const files = await ports.files.discoverFiles(targetPath);
package/dist/index.js CHANGED
@@ -6,15 +6,17 @@ import {
6
6
  openClawTranscriptParser,
7
7
  parseJsonObjectLineWithDiagnostics,
8
8
  parseTuiSessionKey
9
- } from "./chunk-Q5UTJXHZ.js";
9
+ } from "./chunk-IBPS64W3.js";
10
10
  import {
11
11
  EPISODE_SUMMARY_TIMEOUT_MS,
12
12
  FETCH_TOOL_PARAMETERS,
13
+ HOST_SHUTDOWN_EPISODE_ACTIVITY_THRESHOLD,
13
14
  RECALL_TOOL_PARAMETERS,
14
15
  STORE_TOOL_PARAMETERS,
15
16
  UPDATE_TOOL_PARAMETERS,
16
17
  buildClaimExtractionRuntime,
17
18
  buildCompactionSourceRef,
19
+ buildLightDreamTriggerDeps,
18
20
  buildRecallToolServices,
19
21
  buildSessionEndSourceRef,
20
22
  buildSessionFileSourceRef,
@@ -48,12 +50,13 @@ import {
48
50
  resolveSessionStartPolicy,
49
51
  routeSessionMemoryTriggerSafely,
50
52
  runFetchMemoryTool,
53
+ runGuardedPostSessionEpisodeCapture,
51
54
  runRecallMemoryTool,
52
55
  runStoreMemoryTool,
53
56
  runUpdateMemoryTool,
54
57
  scheduleGuardedEpisodeWrite,
55
58
  writeBoundedSingleTranscriptEpisode
56
- } from "./chunk-NBS62ES5.js";
59
+ } from "./chunk-SIY3JA7T.js";
57
60
  import "./chunk-OJSIZDZD.js";
58
61
  import "./chunk-NSLTJBUC.js";
59
62
  import {
@@ -68,7 +71,7 @@ import {
68
71
  formatTargetSelector,
69
72
  sanitizeFetchToolParams,
70
73
  sanitizeUpdateToolParams
71
- } from "./chunk-LDJN7CVU.js";
74
+ } from "./chunk-U74RE3L7.js";
72
75
  import {
73
76
  formatAgenrBeforeTurnRecall,
74
77
  runBeforeTurn,
@@ -84,9 +87,7 @@ import {
84
87
  sanitizeStoreToolParams
85
88
  } from "./chunk-5TIP2EPP.js";
86
89
  import "./chunk-GAERET5Q.js";
87
- import {
88
- withEpisodeWriteGuard
89
- } from "./chunk-SOQW7356.js";
90
+ import "./chunk-SOQW7356.js";
90
91
  import "./chunk-VTHBPXDQ.js";
91
92
  import "./chunk-VBPYU7GO.js";
92
93
 
@@ -294,7 +295,7 @@ function createAgenrStoreTool(ctx, servicesPromise, logger) {
294
295
  session: ctx,
295
296
  sourcePrefix: "openclaw-session",
296
297
  defaultSourceContext: "Stored via agenr_store from OpenClaw.",
297
- onWarning: (warning) => logger.warn(`[agenr] tool=agenr_store session=${ctx.sessionId ?? "unknown"} warning: ${warning}`)
298
+ onWarning: (warning) => logger.info(`[agenr] tool=agenr_store session=${ctx.sessionId ?? "unknown"} note: ${warning}`)
298
299
  });
299
300
  triggerOpenClawImportanceLightDream(services, logger, outcome.details.status);
300
301
  return toOpenClawToolResult(outcome);
@@ -309,16 +310,7 @@ function triggerOpenClawImportanceLightDream(services, logger, status) {
309
310
  if (status !== "stored") {
310
311
  return;
311
312
  }
312
- void maybeRunLightDream(
313
- { trigger: "importance" },
314
- {
315
- port: services.dreaming,
316
- dbPath: services.config.dbPath,
317
- config: services.agenrConfig,
318
- embedding: services.embedding,
319
- ...services.claimExtraction ? { createClaimExtractionLlm: () => services.claimExtraction.llm } : {}
320
- }
321
- ).then((result) => {
313
+ void maybeRunLightDream({ trigger: "importance" }, buildLightDreamTriggerDeps(services)).then((result) => {
322
314
  if (result.status === "ran") {
323
315
  logger.info(`[agenr] importance light dream completed run=${result.result.runId}`);
324
316
  } else if (result.reason === "run_in_progress" || result.reason === "episode_write_in_progress") {
@@ -385,7 +377,7 @@ function registerAgenrOpenClawTools(api, servicesPromise, logger) {
385
377
  var openclaw_plugin_default = {
386
378
  id: "agenr",
387
379
  name: "agenr",
388
- version: "2026.6.2",
380
+ version: "2026.6.3",
389
381
  description: "agenr memory plugin for OpenClaw",
390
382
  kind: "memory",
391
383
  contracts: {
@@ -1133,17 +1125,18 @@ function normalizeOptionalString(value) {
1133
1125
  var OPENCLAW_EPISODE_GENERATOR_VERSION = "openclaw-episodic-summary-v1";
1134
1126
 
1135
1127
  // src/adapters/openclaw/episode/episode-writer.ts
1136
- async function writeOpenClawCurrentSessionEpisode(params) {
1128
+ async function writeOpenClawSessionEndEpisode(params) {
1137
1129
  await writeOpenClawSessionEpisode({
1138
1130
  ctx: params.ctx,
1139
- target: params.current,
1131
+ target: params.target,
1140
1132
  services: params.services,
1141
1133
  logger: params.logger,
1142
1134
  actionLabel: "session-end episode write",
1143
- sourceSessionId: params.current.sessionId,
1135
+ sourceSessionId: params.target.sessionId,
1144
1136
  fileField: "file",
1145
1137
  shortCountField: "materialTurns",
1146
- embeddingSkipLogContext: `[agenr] session-end episode embedding skipped for ${formatSessionContext(params.ctx.sessionId, params.ctx.sessionKey)} file=${params.current.sessionFile}`
1138
+ activityGate: "host-shutdown",
1139
+ embeddingSkipLogContext: `[agenr] session-end episode embedding skipped for ${formatSessionContext(params.ctx.sessionId, params.ctx.sessionKey)} file=${params.target.sessionFile}`
1147
1140
  });
1148
1141
  }
1149
1142
  async function writeOpenClawPreCompactionEpisode(params) {
@@ -1159,6 +1152,7 @@ async function writeOpenClawPreCompactionEpisode(params) {
1159
1152
  sourceSessionId: `${params.sessionId}:pre-compaction:${params.messageCount}`,
1160
1153
  fileField: "file",
1161
1154
  shortCountField: "materialTurns",
1155
+ activityGate: "none",
1162
1156
  embeddingSkipLogContext: `[agenr] pre-compaction episode embedding skipped for ${formatSessionContext(params.ctx.sessionId, params.ctx.sessionKey)} file=${params.sessionFile}`
1163
1157
  });
1164
1158
  }
@@ -1223,6 +1217,7 @@ async function writeOpenClawSessionEpisode(params) {
1223
1217
  ingestOptions: {
1224
1218
  genVersion: OPENCLAW_EPISODE_GENERATOR_VERSION,
1225
1219
  skipActiveSessionCheck: true,
1220
+ ...params.activityGate === "host-shutdown" ? { activityThreshold: HOST_SHUTDOWN_EPISODE_ACTIVITY_THRESHOLD } : {},
1226
1221
  candidateOverrides: {
1227
1222
  sessionId: params.sourceSessionId,
1228
1223
  agentId: trimOptionalString(params.ctx.agentId) ?? null,
@@ -1415,9 +1410,6 @@ function buildOpenClawSessionShutdownTriggerEvent(scope, event) {
1415
1410
  function shouldRouteOpenClawSessionTreeTrigger(reason) {
1416
1411
  return reason !== void 0 && TREE_SESSION_END_REASONS.has(reason);
1417
1412
  }
1418
- function shouldSkipOpenClawSessionEndMemoryTrigger(reason) {
1419
- return reason === "compaction";
1420
- }
1421
1413
  function buildOpenClawCompactionCheckpointArtifact(sessionKey, event, compactionEntry) {
1422
1414
  const sourceId = compactionEntry?.id?.trim() || event.sessionFile?.trim() || `${sessionKey}:${event.compactedCount}`;
1423
1415
  const summary = compactionEntry?.summary.trim() || `Compacted ${event.compactedCount} messages; ${event.messageCount} messages remain.`;
@@ -1848,32 +1840,25 @@ function resolveAgentIdFromSessionKey(sessionKey) {
1848
1840
  return match?.[1]?.trim() || void 0;
1849
1841
  }
1850
1842
 
1851
- // src/adapters/openclaw/hooks/session-end.ts
1852
- async function handleAgenrSessionEnd(event, params) {
1853
- params.midSessionTracker.clear(event.sessionId, event.sessionKey);
1854
- const sessionContext = formatSessionContext(event.sessionId, event.sessionKey);
1855
- const scopeContext = {
1856
- sessionId: event.sessionId,
1857
- ...event.sessionKey ? { sessionKey: event.sessionKey } : {}
1858
- };
1859
- if (!shouldSkipOpenClawSessionEndMemoryTrigger(event.reason)) {
1860
- if (shouldRouteOpenClawSessionTreeTrigger(event.reason)) {
1861
- await routeOpenClawSessionMemoryTrigger(params.servicesPromise, scopeContext, (scope) => buildOpenClawSessionTreeTriggerEvent(scope, event));
1862
- } else {
1863
- await routeOpenClawSessionMemoryTrigger(params.servicesPromise, scopeContext, (scope) => buildOpenClawSessionShutdownTriggerEvent(scope, event));
1864
- }
1843
+ // src/adapters/openclaw/episode/session-end-episode-write.ts
1844
+ function resolveOpenClawSessionEndEpisodeTarget(event) {
1845
+ const sessionId = event.sessionId.trim();
1846
+ const sessionFile = event.sessionFile?.trim();
1847
+ if (!sessionId || !sessionFile) {
1848
+ return void 0;
1865
1849
  }
1850
+ return { sessionId, sessionFile };
1851
+ }
1852
+ async function runOpenClawSessionEndEpisodeCapture(params) {
1853
+ const sessionContext = formatSessionContext(params.event.sessionId, params.event.sessionKey);
1854
+ const syncTarget = resolveOpenClawSessionEndEpisodeTarget(params.event);
1866
1855
  try {
1867
1856
  const services = await params.servicesPromise;
1868
1857
  if (!isPluginEpisodeWriteEnabled(services.pluginConfig.memoryPolicy)) {
1869
1858
  params.logger.debug?.(`[agenr] session-end episode write skipped for ${sessionContext} reason=memory_policy_disabled`);
1870
1859
  return;
1871
1860
  }
1872
- const ctx = {
1873
- sessionId: event.sessionId,
1874
- ...event.sessionKey ? { sessionKey: event.sessionKey } : {}
1875
- };
1876
- const target = await resolveOpenClawCurrentSessionTarget(ctx, {
1861
+ const target = syncTarget ?? await resolveOpenClawCurrentSessionTarget(params.ctx, {
1877
1862
  resolveStateDir: services.openClaw.runtime.state.resolveStateDir,
1878
1863
  logger: params.logger
1879
1864
  });
@@ -1881,37 +1866,60 @@ async function handleAgenrSessionEnd(event, params) {
1881
1866
  params.logger.info(`[agenr] session-end episode write skipped for ${sessionContext} reason=no_current_session`);
1882
1867
  return;
1883
1868
  }
1884
- await withEpisodeWriteGuard(
1885
- { port: services.dreaming, dbPath: services.config.dbPath },
1886
- async () => writeOpenClawCurrentSessionEpisode({ ctx, current: target, services, logger: params.logger })
1887
- );
1888
- await runOpenClawPostSessionLightDream(services, params.logger, sessionContext);
1869
+ await runGuardedPostSessionEpisodeCapture({
1870
+ services,
1871
+ writeEpisode: () => writeOpenClawSessionEndEpisode({
1872
+ ctx: params.ctx,
1873
+ target,
1874
+ services,
1875
+ logger: params.logger
1876
+ }),
1877
+ logger: params.logger,
1878
+ scope: "session-end",
1879
+ sessionContext
1880
+ });
1889
1881
  } catch (error) {
1890
- params.logger.warn(`[agenr] session-end episode write failed for ${sessionContext}: ${formatErrorMessage2(error)}`);
1882
+ params.logger.warn(`[agenr] session-end episode write failed for ${sessionContext}: ${formatErrorMessage(error)}`);
1891
1883
  }
1892
1884
  }
1893
- async function runOpenClawPostSessionLightDream(services, logger, sessionContext) {
1894
- try {
1895
- const result = await maybeRunLightDream(
1896
- { trigger: "post_session" },
1897
- {
1898
- port: services.dreaming,
1899
- dbPath: services.config.dbPath,
1900
- config: services.agenrConfig,
1901
- embedding: services.embedding,
1902
- ...services.claimExtraction ? { createClaimExtractionLlm: () => services.claimExtraction.llm } : {}
1903
- }
1904
- );
1905
- if (result.status === "ran") {
1906
- logger.info(`[agenr] session-end light dream completed for ${sessionContext} run=${result.result.runId}`);
1907
- } else if (result.reason === "run_in_progress" || result.reason === "episode_write_in_progress") {
1908
- logger.info(`[agenr] session-end light dream skipped for ${sessionContext} reason=${result.reason}`);
1885
+
1886
+ // src/adapters/openclaw/session-end-policy.ts
1887
+ function resolveOpenClawSessionEndPolicy(reason) {
1888
+ const isCompaction = reason === "compaction";
1889
+ return {
1890
+ routeMemoryIntake: !isCompaction,
1891
+ captureEpisode: !isCompaction
1892
+ };
1893
+ }
1894
+
1895
+ // src/adapters/openclaw/hooks/session-end.ts
1896
+ async function handleAgenrSessionEnd(event, params) {
1897
+ params.midSessionTracker.clear(event.sessionId, event.sessionKey);
1898
+ const scopeContext = {
1899
+ sessionId: event.sessionId,
1900
+ ...event.sessionKey ? { sessionKey: event.sessionKey } : {}
1901
+ };
1902
+ const policy = resolveOpenClawSessionEndPolicy(event.reason);
1903
+ if (policy.routeMemoryIntake) {
1904
+ if (shouldRouteOpenClawSessionTreeTrigger(event.reason)) {
1905
+ await routeOpenClawSessionMemoryTrigger(params.servicesPromise, scopeContext, (scope) => buildOpenClawSessionTreeTriggerEvent(scope, event));
1909
1906
  } else {
1910
- logger.debug?.(`[agenr] session-end light dream skipped for ${sessionContext} reason=${result.reason}`);
1907
+ await routeOpenClawSessionMemoryTrigger(params.servicesPromise, scopeContext, (scope) => buildOpenClawSessionShutdownTriggerEvent(scope, event));
1911
1908
  }
1912
- } catch (error) {
1913
- logger.warn(`[agenr] session-end light dream failed for ${sessionContext}: ${formatErrorMessage2(error)}`);
1914
1909
  }
1910
+ if (!policy.captureEpisode) {
1911
+ return;
1912
+ }
1913
+ const ctx = {
1914
+ sessionId: event.sessionId,
1915
+ ...event.sessionKey ? { sessionKey: event.sessionKey } : {}
1916
+ };
1917
+ await runOpenClawSessionEndEpisodeCapture({
1918
+ event,
1919
+ ctx,
1920
+ servicesPromise: params.servicesPromise,
1921
+ logger: params.logger
1922
+ });
1915
1923
  }
1916
1924
 
1917
1925
  // src/adapters/openclaw/memory/flush-plan.ts
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "agenr",
3
3
  "name": "agenr",
4
- "version": "2026.6.2",
4
+ "version": "2026.6.3",
5
5
  "description": "agenr memory plugin for OpenClaw",
6
6
  "kind": "memory",
7
7
  "contracts": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenr/openclaw-plugin",
3
- "version": "2026.6.2",
3
+ "version": "2026.6.3",
4
4
  "description": "agenr memory plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "exports": {