@remnic/plugin-openclaw 9.69.52 → 9.69.54

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/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  import { loadDaySummaryPrompt } from "@remnic/core/day-summary";
18
18
  import OpenAI from "openai";
19
19
  import { createRequire } from "module";
20
- import { createHash as createHash4 } from "crypto";
20
+ import { createHash as createHash3 } from "crypto";
21
21
  import { parseConfig } from "@remnic/core/config";
22
22
  import { initLogger, log as log8 } from "@remnic/core/logger";
23
23
  import {
@@ -31,9 +31,9 @@ import {
31
31
  import { beginCodexSubscriptionShutdown, getCodexSubscriptionRunnerForOwner, terminateActiveCodexSubscriptionChildren } from "@remnic/core";
32
32
 
33
33
  // ../../src/tools.ts
34
- import { createHash as createHash2 } from "crypto";
34
+ import { createHash } from "crypto";
35
35
  import { Type as Type2 } from "@sinclair/typebox";
36
- import { indexMemoryAsync as indexMemoryAsync2, indexesExistAsync as indexesExistAsync2 } from "@remnic/core/temporal-index";
36
+ import { indexMemoryAsync, indexesExistAsync } from "@remnic/core/temporal-index";
37
37
 
38
38
  // ../../src/explicit-capture.ts
39
39
  import {
@@ -59,105 +59,8 @@ function mergeInlineCaptureDedupeKeys(messageKeys, fallbackDedupeKeys = []) {
59
59
 
60
60
  // ../../src/tools.ts
61
61
  import { log } from "@remnic/core/logger";
62
- import { composeSalvagedEnvelope as composeSalvagedEnvelope2 } from "@remnic/core/salvage-envelope";
63
-
64
- // ../../src/memory-promote.ts
65
- import { createHash } from "crypto";
66
62
  import { composeSalvagedEnvelope } from "@remnic/core/salvage-envelope";
67
- import {
68
- STRUCTURED_ATTRIBUTE_LIMITS,
69
- TAG_LIMITS
70
- } from "@remnic/core/write-envelope";
71
- import { stripAttributesSuffix } from "@remnic/core/storage";
72
- import { isSupportPassportPrivateMemory } from "@remnic/core/support-passport";
73
- import { displayErrorDetail } from "@remnic/core/runtime/better-sqlite";
74
- import { isSafeRouteNamespace } from "@remnic/core/routing/engine";
75
- import { indexMemoryAsync, indexesExistAsync } from "@remnic/core/temporal-index";
76
- function buildPromotionOrigin(srcNamespace, memoryId) {
77
- const memoryIdHash = createHash("sha256").update(memoryId).digest("hex");
78
- const fullTag = `promotedFrom:${srcNamespace}:${memoryId}`;
79
- return {
80
- tag: fullTag.length <= TAG_LIMITS.maxTagLength ? fullTag : `promotedFromHash:${memoryIdHash}`,
81
- structuredAttributes: {
82
- promotedFromNamespace: srcNamespace,
83
- promotedFromMemoryIdHash: memoryIdHash,
84
- ...memoryId.length <= STRUCTURED_ATTRIBUTE_LIMITS.maxValueLength ? { promotedFromMemoryId: memoryId } : {}
85
- }
86
- };
87
- }
88
- async function executeMemoryPromote(orchestrator, params) {
89
- const { memoryId, fromNamespace, toNamespace, note } = params;
90
- if (typeof memoryId !== "string" || memoryId.length === 0) {
91
- return `Invalid memoryId: ${JSON.stringify(memoryId)} \u2014 expected a non-empty memory ID.`;
92
- }
93
- for (const [name, value] of [
94
- ["fromNamespace", fromNamespace],
95
- ["toNamespace", toNamespace]
96
- ]) {
97
- if (value !== void 0 && value !== null && (typeof value !== "string" || !isSafeRouteNamespace(value))) {
98
- return `Invalid ${name}: ${JSON.stringify(value)} \u2014 expected a path-safe namespace name.`;
99
- }
100
- }
101
- const srcNs = fromNamespace ?? orchestrator.config.defaultNamespace;
102
- const dstNs = toNamespace ?? orchestrator.config.sharedNamespace;
103
- const src = await orchestrator.getStorage(srcNs);
104
- const mem = await src.getMemoryById(memoryId);
105
- if (!mem || isSupportPassportPrivateMemory(mem)) {
106
- return `Memory not found in ${srcNs}: ${memoryId}`;
107
- }
108
- const dst = await orchestrator.getStorage(dstNs);
109
- const promotionOrigin = buildPromotionOrigin(srcNs, memoryId);
110
- const promoteEnvelope = composeSalvagedEnvelope(
111
- "promote",
112
- {
113
- content: mem.frontmatter.structuredAttributes ? stripAttributesSuffix(mem.content) : mem.content,
114
- category: mem.frontmatter.category,
115
- confidence: mem.frontmatter.confidence,
116
- tags: (() => {
117
- const promotionTags = [
118
- "promoted",
119
- promotionOrigin.tag,
120
- ...note ? [`note:${note}`] : []
121
- ];
122
- const sourceTags = mem.frontmatter.tags ?? [];
123
- return Array.from(/* @__PURE__ */ new Set([...promotionTags, ...sourceTags])).slice(
124
- 0,
125
- TAG_LIMITS.maxTags
126
- );
127
- })(),
128
- structuredAttributes: promotionOrigin.structuredAttributes,
129
- entityRef: mem.frontmatter.entityRef
130
- },
131
- { source: "promote" }
132
- );
133
- const { id: newId, tombstoneBlocked } = await dst.writeSealedMemory(
134
- promoteEnvelope,
135
- {
136
- importance: mem.frontmatter.importance,
137
- supersedes: mem.frontmatter.supersedes,
138
- links: mem.frontmatter.links
139
- }
140
- );
141
- if (tombstoneBlocked) {
142
- return `Promotion of ${srcNs}:${memoryId} \u2192 ${dstNs}:${newId} is queued for review (tombstone-blocked): no active promoted copy was created.`;
143
- }
144
- try {
145
- if (orchestrator.config.queryAwareIndexingEnabled && await indexesExistAsync(orchestrator.config.memoryDir)) {
146
- const promoted = await dst.getMemoryById(newId).catch(() => null);
147
- if (promoted?.path && promoted.frontmatter?.created) {
148
- await indexMemoryAsync(
149
- orchestrator.config.memoryDir,
150
- promoted.path,
151
- promoted.frontmatter.created,
152
- promoted.frontmatter.tags ?? []
153
- );
154
- }
155
- }
156
- } catch (err) {
157
- return `Promoted ${srcNs}:${memoryId} \u2192 ${dstNs}:${newId} (index refresh failed \u2014 recall picks it up on the next maintenance pass: ${displayErrorDetail(err)})`;
158
- }
159
- return `Promoted ${srcNs}:${memoryId} \u2192 ${dstNs}:${newId}`;
160
- }
63
+ import { executeMemoryPromote } from "@remnic/plugin-openclaw/plugin/memory-promote";
161
64
 
162
65
  // ../../src/shared-context-tools.ts
163
66
  import { Type } from "@sinclair/typebox";
@@ -372,49 +275,11 @@ import { wrapWorkLayerContext } from "@remnic/core/work/boundary";
372
275
  import { VALID_MEMORY_CATEGORIES } from "@remnic/core/config";
373
276
  import { formatProfileTraceAscii } from "@remnic/core/profiling";
374
277
  import { runMemoryGovernance } from "@remnic/core/maintenance/memory-governance";
375
-
376
- // ../../src/memory-action-target.ts
377
- import { isSupportPassportPrivateMemory as isSupportPassportPrivateMemory2 } from "@remnic/core";
378
- function clampUnitInterval(value, fallback) {
379
- if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
380
- if (value < 0) return 0;
381
- if (value > 1) return 1;
382
- return value;
383
- }
384
- function normalizeEligibilitySource(value) {
385
- switch (value) {
386
- case "extraction":
387
- case "consolidation":
388
- case "replay":
389
- case "manual":
390
- return value;
391
- default:
392
- return "unknown";
393
- }
394
- }
395
- function deriveMemoryActionPolicyEligibility(memory) {
396
- if (!memory) return void 0;
397
- const frontmatter = memory.frontmatter;
398
- return {
399
- confidence: clampUnitInterval(frontmatter.confidence, 0),
400
- lifecycleState: frontmatter.status === "archived" ? "archived" : frontmatter.lifecycleState ?? "candidate",
401
- importance: clampUnitInterval(frontmatter.importance?.score, 0),
402
- source: normalizeEligibilitySource(frontmatter.source)
403
- };
404
- }
405
- async function readReferencedMemoryForPolicyEligibility(storage, memoryId) {
406
- if (!memoryId) return void 0;
407
- const direct = await storage.getMemoryById?.(memoryId);
408
- if (direct) return direct;
409
- const active = (await storage.readAllMemories?.())?.find((memory) => memory.frontmatter.id === memoryId);
410
- if (active) return active;
411
- return (await storage.readArchivedMemories?.())?.find((memory) => memory.frontmatter.id === memoryId);
412
- }
413
- function blocksSupportPassportMutation(action, memory) {
414
- return (action === "update_note" || action === "discard" || action === "link_graph") && Boolean(memory && isSupportPassportPrivateMemory2(memory));
415
- }
416
-
417
- // ../../src/tools.ts
278
+ import {
279
+ blocksSupportPassportMutation,
280
+ deriveMemoryActionPolicyEligibility,
281
+ readReferencedMemoryForPolicyEligibility
282
+ } from "@remnic/plugin-openclaw/plugin/memory-action-target";
418
283
  function toolResult2(text) {
419
284
  return { content: [{ type: "text", text }], details: void 0 };
420
285
  }
@@ -599,7 +464,7 @@ function registerTools(api, orchestrator, hostRuntimeAgentId) {
599
464
  const actionTypeSet = new Set(actionTypes);
600
465
  function promptHashForTelemetry(input) {
601
466
  if (typeof input !== "string" || input.trim().length === 0) return void 0;
602
- return createHash2("sha256").update(input).digest("hex").slice(0, 16);
467
+ return createHash("sha256").update(input).digest("hex").slice(0, 16);
603
468
  }
604
469
  function normalizeMemoryCategory(value, fallback) {
605
470
  const normalized = asNonEmptyString(value);
@@ -654,11 +519,11 @@ function registerTools(api, orchestrator, hostRuntimeAgentId) {
654
519
  Content: ${candidate.content}`
655
520
  );
656
521
  }
657
- if (!result.duplicateOf && orchestrator.config.queryAwareIndexingEnabled && await indexesExistAsync2(orchestrator.config.memoryDir)) {
522
+ if (!result.duplicateOf && orchestrator.config.queryAwareIndexingEnabled && await indexesExistAsync(orchestrator.config.memoryDir)) {
658
523
  const storage = await orchestrator.getStorage(candidate.namespace);
659
524
  const mem = await storage.getMemoryById(result.id).catch(() => null);
660
525
  if (mem?.path && mem.frontmatter?.created) {
661
- await indexMemoryAsync2(orchestrator.config.memoryDir, mem.path, mem.frontmatter.created, mem.frontmatter.tags ?? []);
526
+ await indexMemoryAsync(orchestrator.config.memoryDir, mem.path, mem.frontmatter.created, mem.frontmatter.tags ?? []);
662
527
  }
663
528
  }
664
529
  orchestrator.requestQmdMaintenanceForTool(maintenanceReason);
@@ -1832,7 +1697,7 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
1832
1697
  let queuedForReview = false;
1833
1698
  switch (action) {
1834
1699
  case "store_episode": {
1835
- const episodeEnvelope = composeSalvagedEnvelope2(
1700
+ const episodeEnvelope = composeSalvagedEnvelope(
1836
1701
  "memory-action",
1837
1702
  { content: contentValue, category: normalizedCategory ?? "fact" },
1838
1703
  { source: "memory_action_apply" }
@@ -1851,7 +1716,7 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
1851
1716
  break;
1852
1717
  }
1853
1718
  case "store_note": {
1854
- const noteEnvelope = composeSalvagedEnvelope2(
1719
+ const noteEnvelope = composeSalvagedEnvelope(
1855
1720
  "memory-action",
1856
1721
  { content: contentValue, category: normalizedCategory ?? "fact" },
1857
1722
  { source: "memory_action_apply" }
@@ -1909,7 +1774,7 @@ NOTE: You did not provide sessionKey; under concurrency this may not match your
1909
1774
  break;
1910
1775
  }
1911
1776
  case "summarize_node": {
1912
- const summaryEnvelope = composeSalvagedEnvelope2(
1777
+ const summaryEnvelope = composeSalvagedEnvelope(
1913
1778
  "memory-action",
1914
1779
  { content: contentValue, category: normalizedCategory ?? "fact" },
1915
1780
  { source: "memory_action_apply" }
@@ -3165,7 +3030,7 @@ import path11 from "path";
3165
3030
  import os from "os";
3166
3031
  import { createOpikExporter } from "@remnic/core/opik-exporter";
3167
3032
  import { readEnvVar, resolveHomeDir as resolveHomeDir3 } from "@remnic/core/runtime/env";
3168
- import { displayErrorDetail as displayErrorDetail2 } from "@remnic/core/runtime/better-sqlite";
3033
+ import { displayErrorDetail } from "@remnic/core/runtime/better-sqlite";
3169
3034
  import { migrateFromEngram } from "@remnic/core/migrate/from-engram";
3170
3035
 
3171
3036
  // ../../src/user-message-cleaning.ts
@@ -3434,7 +3299,7 @@ function buildMemorySearchTool(orchestrator, options = {}) {
3434
3299
  }
3435
3300
 
3436
3301
  // src/runtime-surfaces.ts
3437
- import { composeSalvagedEnvelope as composeSalvagedEnvelope3 } from "@remnic/core/salvage-envelope";
3302
+ import { composeSalvagedEnvelope as composeSalvagedEnvelope2 } from "@remnic/core/salvage-envelope";
3438
3303
  import {
3439
3304
  gatewayTaskChainOptions
3440
3305
  } from "@remnic/core/fallback-llm";
@@ -3647,7 +3512,7 @@ async function syncDreamSurfaceEntries(params) {
3647
3512
  const existing = findSurfaceMemoryByAttribute(memories, DREAM_ENTRY_ID_KEY, entry.id);
3648
3513
  if (!existing) {
3649
3514
  const { id: memoryId } = await storage.writeSealedMemory(
3650
- composeSalvagedEnvelope3(
3515
+ composeSalvagedEnvelope2(
3651
3516
  "dreams-surface",
3652
3517
  { content, category: "moment", confidence: 0.85, tags, structuredAttributes },
3653
3518
  { source: "dreams.md" }
@@ -3707,7 +3572,7 @@ async function syncHeartbeatSurfaceEntries(params) {
3707
3572
  const existing = findSurfaceMemoryByAttribute(memories, HEARTBEAT_ENTRY_ID_KEY, entry.id) ?? findUniqueSurfaceMemoryBySlug(memories, HEARTBEAT_SURFACE_TYPE, entry.slug);
3708
3573
  if (!existing) {
3709
3574
  const { id: memoryId } = await storage.writeSealedMemory(
3710
- composeSalvagedEnvelope3(
3575
+ composeSalvagedEnvelope2(
3711
3576
  "heartbeat-surface",
3712
3577
  { content, category: "principle", confidence: 0.95, tags, structuredAttributes },
3713
3578
  { source: "heartbeat.md" }
@@ -4213,7 +4078,7 @@ function validateSlotSelection(ctx) {
4213
4078
 
4214
4079
  // ../../src/openclaw-flush-plan-lifecycle.ts
4215
4080
  import path2 from "path";
4216
- import { createHash as createHash3, randomUUID } from "crypto";
4081
+ import { createHash as createHash2, randomUUID } from "crypto";
4217
4082
  import {
4218
4083
  lstat as lstat2,
4219
4084
  mkdir,
@@ -4346,7 +4211,7 @@ function isPathInsideOrEqual(parentPath, childPath) {
4346
4211
  return relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative);
4347
4212
  }
4348
4213
  function hashContent(content) {
4349
- return createHash3("sha256").update(content, "utf8").digest("hex");
4214
+ return createHash2("sha256").update(content, "utf8").digest("hex");
4350
4215
  }
4351
4216
  function utf8PrefixByByteLength(content, byteLength) {
4352
4217
  const buffer = Buffer.from(content, "utf8");
@@ -8147,15 +8012,26 @@ function cwdFrom(event, ctx, fallback) {
8147
8012
  }
8148
8013
  return fallback;
8149
8014
  }
8015
+ function readRecallDegradation(candidate) {
8016
+ if (!("degradation" in candidate)) return void 0;
8017
+ const value = candidate.degradation;
8018
+ if (typeof value !== "object" || value === null) return void 0;
8019
+ const rec = value;
8020
+ if (typeof rec.state !== "string" || typeof rec.reason !== "string") return void 0;
8021
+ return value;
8022
+ }
8150
8023
  function readContextComposition(response, fallbackContext) {
8151
8024
  const candidate = response.contextComposition;
8152
8025
  if (typeof candidate !== "object" || candidate === null || !("context" in candidate) || typeof candidate.context !== "string") {
8153
8026
  return { context: fallbackContext };
8154
8027
  }
8028
+ const composition = { context: candidate.context };
8155
8029
  if ("footer" in candidate && typeof candidate.footer === "string") {
8156
- return { context: candidate.context, footer: candidate.footer };
8030
+ composition.footer = candidate.footer;
8157
8031
  }
8158
- return { context: candidate.context };
8032
+ const degradation = readRecallDegradation(candidate);
8033
+ if (degradation) composition.degradation = degradation;
8034
+ return composition;
8159
8035
  }
8160
8036
  function registerDelegateRuntime(api, options) {
8161
8037
  const { target, namespace, namespaceBindings } = options;
@@ -8296,8 +8172,9 @@ function registerDelegateRuntime(api, options) {
8296
8172
  if (typeof rawContext !== "string" || rawContext.trim().length === 0) {
8297
8173
  return void 0;
8298
8174
  }
8175
+ const composition = readContextComposition(response ?? {}, rawContext);
8299
8176
  const rendered = renderMemoryContextPrompt({
8300
- ...readContextComposition(response ?? {}, rawContext),
8177
+ ...composition,
8301
8178
  maxChars: options.recallBudgetChars
8302
8179
  });
8303
8180
  if (!rendered) return void 0;
@@ -8306,7 +8183,10 @@ function registerDelegateRuntime(api, options) {
8306
8183
  promptLinesBySession.set(sessionKey, rendered.lines);
8307
8184
  return void 0;
8308
8185
  }
8309
- return { prependSystemContext: prompt };
8186
+ return {
8187
+ prependSystemContext: prompt,
8188
+ ...composition.degradation ? { degradation: composition.degradation } : {}
8189
+ };
8310
8190
  } catch (err) {
8311
8191
  log7.warn(`delegate recall failed: ${String(err)}`);
8312
8192
  return void 0;
@@ -8898,7 +8778,7 @@ function buildTurnFingerprint(input) {
8898
8778
  import { planRecallMode } from "@remnic/core/intent";
8899
8779
  import {
8900
8780
  expandTildePath as expandTildePath3,
8901
- isSupportPassportPrivateMemory as isSupportPassportPrivateMemory3,
8781
+ isSupportPassportPrivateMemory,
8902
8782
  renderMemoryContextPrompt as renderSharedMemoryContextPrompt,
8903
8783
  resolveAgentAccessAuthToken,
8904
8784
  resolvePrincipal,
@@ -9618,7 +9498,7 @@ function stableOpenClawConfigSignature(value, seen = /* @__PURE__ */ new WeakSet
9618
9498
  return JSON.stringify(String(value));
9619
9499
  }
9620
9500
  function openClawHostEmbeddingConfigSignature(cfg, apiConfig, workspaceDir) {
9621
- return `sha256:${createHash4("sha256").update(JSON.stringify({
9501
+ return `sha256:${createHash3("sha256").update(JSON.stringify({
9622
9502
  enabled: cfg.hostEmbeddingProviderEnabled !== false,
9623
9503
  memoryDir: cfg.memoryDir,
9624
9504
  providerId: cfg.hostEmbeddingProviderId ?? "",
@@ -10016,7 +9896,7 @@ var pluginDefinition = {
10016
9896
  );
10017
9897
  }
10018
9898
  } catch (error) {
10019
- const detail = displayErrorDetail2(error) || "unknown error";
9899
+ const detail = displayErrorDetail(error) || "unknown error";
10020
9900
  log8.warn(`OpenClaw flush-plan processing failed: ${detail}`);
10021
9901
  }
10022
9902
  }
@@ -12159,7 +12039,7 @@ Keep the reflection grounded in the evidence below.
12159
12039
  const resolved = await readMemoryByLookup(lookup, agentSessionKey);
12160
12040
  if (!resolved) return null;
12161
12041
  const { memory, displayPath } = resolved;
12162
- if (isMemoryArtifactPath(displayPath) || isMemoryArtifactPath(memory.path) || isSupportPassportPrivateMemory3(memory)) {
12042
+ if (isMemoryArtifactPath(displayPath) || isMemoryArtifactPath(memory.path) || isSupportPassportPrivateMemory(memory)) {
12163
12043
  return null;
12164
12044
  }
12165
12045
  const allLines = memory.content.split(/\r?\n/);
@@ -12554,7 +12434,7 @@ function buildOpenClawInboundContentFingerprint(content, message, event, ctx, se
12554
12434
  normalizeThreadId(message.threadId) ?? normalizeThreadId(event.threadId) ?? normalizeThreadId(ctx.threadId) ?? "",
12555
12435
  512
12556
12436
  );
12557
- const contentHash = createHash4("sha256").update(normalizedContent).digest("hex");
12437
+ const contentHash = createHash3("sha256").update(normalizedContent).digest("hex");
12558
12438
  return [
12559
12439
  sessionPart,
12560
12440
  "content",
@@ -12568,7 +12448,7 @@ function buildOpenClawSparseInboundContentFingerprint(content, sessionKey) {
12568
12448
  const normalizedContent = content.trim();
12569
12449
  if (!normalizedContent) return null;
12570
12450
  const sessionPart = truncateMetadataValue(sessionKey || "default", 512);
12571
- const contentHash = createHash4("sha256").update(normalizedContent).digest("hex");
12451
+ const contentHash = createHash3("sha256").update(normalizedContent).digest("hex");
12572
12452
  return `${sessionPart}\0sparse-content\0${contentHash}`;
12573
12453
  }
12574
12454
  function withReplyExtractionHint(content, metadata) {
@@ -0,0 +1,11 @@
1
+ import { MemoryActionType, MemoryFile, MemoryActionEligibilityContext } from '@remnic/core/types';
2
+
3
+ declare function deriveMemoryActionPolicyEligibility(memory: Pick<MemoryFile, "frontmatter"> | null | undefined): MemoryActionEligibilityContext | undefined;
4
+ declare function readReferencedMemoryForPolicyEligibility(storage: {
5
+ getMemoryById?: (id: string) => Promise<MemoryFile | null>;
6
+ readAllMemories?: () => Promise<MemoryFile[]>;
7
+ readArchivedMemories?: () => Promise<MemoryFile[]>;
8
+ }, memoryId: string | undefined): Promise<MemoryFile | null | undefined>;
9
+ declare function blocksSupportPassportMutation(action: MemoryActionType, memory: Pick<MemoryFile, "frontmatter"> | null | undefined): boolean;
10
+
11
+ export { blocksSupportPassportMutation, deriveMemoryActionPolicyEligibility, readReferencedMemoryForPolicyEligibility };
@@ -0,0 +1,47 @@
1
+ import "../chunk-I2KLQ2HA.js";
2
+
3
+ // src/plugin/memory-action-target.ts
4
+ import { isSupportPassportPrivateMemory } from "@remnic/core";
5
+ function clampUnitInterval(value, fallback) {
6
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
7
+ if (value < 0) return 0;
8
+ if (value > 1) return 1;
9
+ return value;
10
+ }
11
+ function normalizeEligibilitySource(value) {
12
+ switch (value) {
13
+ case "extraction":
14
+ case "consolidation":
15
+ case "replay":
16
+ case "manual":
17
+ return value;
18
+ default:
19
+ return "unknown";
20
+ }
21
+ }
22
+ function deriveMemoryActionPolicyEligibility(memory) {
23
+ if (!memory) return void 0;
24
+ const frontmatter = memory.frontmatter;
25
+ return {
26
+ confidence: clampUnitInterval(frontmatter.confidence, 0),
27
+ lifecycleState: frontmatter.status === "archived" ? "archived" : frontmatter.lifecycleState ?? "candidate",
28
+ importance: clampUnitInterval(frontmatter.importance?.score, 0),
29
+ source: normalizeEligibilitySource(frontmatter.source)
30
+ };
31
+ }
32
+ async function readReferencedMemoryForPolicyEligibility(storage, memoryId) {
33
+ if (!memoryId) return void 0;
34
+ const direct = await storage.getMemoryById?.(memoryId);
35
+ if (direct) return direct;
36
+ const active = (await storage.readAllMemories?.())?.find((memory) => memory.frontmatter.id === memoryId);
37
+ if (active) return active;
38
+ return (await storage.readArchivedMemories?.())?.find((memory) => memory.frontmatter.id === memoryId);
39
+ }
40
+ function blocksSupportPassportMutation(action, memory) {
41
+ return (action === "update_note" || action === "discard" || action === "link_graph") && Boolean(memory && isSupportPassportPrivateMemory(memory));
42
+ }
43
+ export {
44
+ blocksSupportPassportMutation,
45
+ deriveMemoryActionPolicyEligibility,
46
+ readReferencedMemoryForPolicyEligibility
47
+ };
@@ -0,0 +1,10 @@
1
+ import { Orchestrator } from '@remnic/core/orchestrator';
2
+
3
+ declare function executeMemoryPromote(orchestrator: Orchestrator, params: {
4
+ memoryId: string;
5
+ fromNamespace?: string;
6
+ toNamespace?: string;
7
+ note?: string;
8
+ }): Promise<string>;
9
+
10
+ export { executeMemoryPromote };
@@ -0,0 +1,102 @@
1
+ import "../chunk-I2KLQ2HA.js";
2
+
3
+ // src/plugin/memory-promote.ts
4
+ import { createHash } from "crypto";
5
+ import { composeSalvagedEnvelope } from "@remnic/core/salvage-envelope";
6
+ import {
7
+ STRUCTURED_ATTRIBUTE_LIMITS,
8
+ TAG_LIMITS
9
+ } from "@remnic/core/write-envelope";
10
+ import { stripAttributesSuffix } from "@remnic/core/storage";
11
+ import { isSupportPassportPrivateMemory } from "@remnic/core/support-passport";
12
+ import { displayErrorDetail } from "@remnic/core/runtime/better-sqlite";
13
+ import { isSafeRouteNamespace } from "@remnic/core/routing/engine";
14
+ import { indexMemoryAsync, indexesExistAsync } from "@remnic/core/temporal-index";
15
+ function buildPromotionOrigin(srcNamespace, memoryId) {
16
+ const memoryIdHash = createHash("sha256").update(memoryId).digest("hex");
17
+ const fullTag = `promotedFrom:${srcNamespace}:${memoryId}`;
18
+ return {
19
+ tag: fullTag.length <= TAG_LIMITS.maxTagLength ? fullTag : `promotedFromHash:${memoryIdHash}`,
20
+ structuredAttributes: {
21
+ promotedFromNamespace: srcNamespace,
22
+ promotedFromMemoryIdHash: memoryIdHash,
23
+ ...memoryId.length <= STRUCTURED_ATTRIBUTE_LIMITS.maxValueLength ? { promotedFromMemoryId: memoryId } : {}
24
+ }
25
+ };
26
+ }
27
+ async function executeMemoryPromote(orchestrator, params) {
28
+ const { memoryId, fromNamespace, toNamespace, note } = params;
29
+ if (typeof memoryId !== "string" || memoryId.length === 0) {
30
+ return `Invalid memoryId: ${JSON.stringify(memoryId)} \u2014 expected a non-empty memory ID.`;
31
+ }
32
+ for (const [name, value] of [
33
+ ["fromNamespace", fromNamespace],
34
+ ["toNamespace", toNamespace]
35
+ ]) {
36
+ if (value !== void 0 && value !== null && (typeof value !== "string" || !isSafeRouteNamespace(value))) {
37
+ return `Invalid ${name}: ${JSON.stringify(value)} \u2014 expected a path-safe namespace name.`;
38
+ }
39
+ }
40
+ const srcNs = fromNamespace ?? orchestrator.config.defaultNamespace;
41
+ const dstNs = toNamespace ?? orchestrator.config.sharedNamespace;
42
+ const src = await orchestrator.getStorage(srcNs);
43
+ const mem = await src.getMemoryById(memoryId);
44
+ if (!mem || isSupportPassportPrivateMemory(mem)) {
45
+ return `Memory not found in ${srcNs}: ${memoryId}`;
46
+ }
47
+ const dst = await orchestrator.getStorage(dstNs);
48
+ const promotionOrigin = buildPromotionOrigin(srcNs, memoryId);
49
+ const promoteEnvelope = composeSalvagedEnvelope(
50
+ "promote",
51
+ {
52
+ content: mem.frontmatter.structuredAttributes ? stripAttributesSuffix(mem.content) : mem.content,
53
+ category: mem.frontmatter.category,
54
+ confidence: mem.frontmatter.confidence,
55
+ tags: (() => {
56
+ const promotionTags = [
57
+ "promoted",
58
+ promotionOrigin.tag,
59
+ ...note ? [`note:${note}`] : []
60
+ ];
61
+ const sourceTags = mem.frontmatter.tags ?? [];
62
+ return Array.from(/* @__PURE__ */ new Set([...promotionTags, ...sourceTags])).slice(
63
+ 0,
64
+ TAG_LIMITS.maxTags
65
+ );
66
+ })(),
67
+ structuredAttributes: promotionOrigin.structuredAttributes,
68
+ entityRef: mem.frontmatter.entityRef
69
+ },
70
+ { source: "promote" }
71
+ );
72
+ const { id: newId, tombstoneBlocked } = await dst.writeSealedMemory(
73
+ promoteEnvelope,
74
+ {
75
+ importance: mem.frontmatter.importance,
76
+ supersedes: mem.frontmatter.supersedes,
77
+ links: mem.frontmatter.links
78
+ }
79
+ );
80
+ if (tombstoneBlocked) {
81
+ return `Promotion of ${srcNs}:${memoryId} \u2192 ${dstNs}:${newId} is queued for review (tombstone-blocked): no active promoted copy was created.`;
82
+ }
83
+ try {
84
+ if (orchestrator.config.queryAwareIndexingEnabled && await indexesExistAsync(orchestrator.config.memoryDir)) {
85
+ const promoted = await dst.getMemoryById(newId).catch(() => null);
86
+ if (promoted?.path && promoted.frontmatter?.created) {
87
+ await indexMemoryAsync(
88
+ orchestrator.config.memoryDir,
89
+ promoted.path,
90
+ promoted.frontmatter.created,
91
+ promoted.frontmatter.tags ?? []
92
+ );
93
+ }
94
+ }
95
+ } catch (err) {
96
+ return `Promoted ${srcNs}:${memoryId} \u2192 ${dstNs}:${newId} (index refresh failed \u2014 recall picks it up on the next maintenance pass: ${displayErrorDetail(err)})`;
97
+ }
98
+ return `Promoted ${srcNs}:${memoryId} \u2192 ${dstNs}:${newId}`;
99
+ }
100
+ export {
101
+ executeMemoryPromote
102
+ };