openclaw-memory-atmem 2.2.0 → 2.2.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.
- package/README.md +2 -2
- package/dist/index.js +44 -13
- package/dist/src/rpc-client.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,11 +5,11 @@ This npm package is the host bridge for AtMem. It is not a standalone memory eng
|
|
|
5
5
|
Use the Python-owned installer:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
python -m pip install --upgrade atmem==2.2.
|
|
8
|
+
python -m pip install --upgrade atmem==2.2.3
|
|
9
9
|
atmem openclaw install
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
The installer pins `openclaw-memory-atmem@2.2.
|
|
12
|
+
The installer pins `openclaw-memory-atmem@2.2.3`, binds the exact `atmem` executable, copies existing OpenClaw memory, configures shadow mode, restarts the gateway and verifies the loaded plugin. Direct npm installation cannot perform or prove those steps.
|
|
13
13
|
|
|
14
14
|
Existing AtMem 2.1 users run `atmem openclaw upgrade` after upgrading the Python
|
|
15
15
|
package. This preserves the current memory mode and migration, verifies the new
|
package/dist/index.js
CHANGED
|
@@ -326,6 +326,7 @@ function register(api) {
|
|
|
326
326
|
// Per-turn recall state. Semantic admission uses a short-lived SQLite handoff
|
|
327
327
|
// because OpenClaw may run prompt hooks and agent tools in separate runtimes.
|
|
328
328
|
const pendingPrompts = new Map();
|
|
329
|
+
const observedTurnInputs = new Map();
|
|
329
330
|
const inboundAttachments = new Map();
|
|
330
331
|
const inboundAttachmentGeneration = new Map();
|
|
331
332
|
let nextAttachmentGeneration = 0;
|
|
@@ -432,6 +433,41 @@ function register(api) {
|
|
|
432
433
|
ttl_seconds: 600,
|
|
433
434
|
}, cfg.recall.timeoutMs);
|
|
434
435
|
};
|
|
436
|
+
const turnInputKey = (ctx) => scopedKey(flightRunId(undefined, ctx), ctx);
|
|
437
|
+
const observeTurnInput = async (prompt, ctx, sourceHook, imagesCount) => {
|
|
438
|
+
if (!prompt.trim())
|
|
439
|
+
return;
|
|
440
|
+
const key = turnInputKey(ctx);
|
|
441
|
+
const promptSha256 = digestText(prompt);
|
|
442
|
+
const existing = observedTurnInputs.get(key);
|
|
443
|
+
if (existing) {
|
|
444
|
+
if (existing.promptSha256 !== promptSha256) {
|
|
445
|
+
api.logger.warn(`${TAG} ${sourceHook} exposed different prompt bytes for an already observed turn; ` +
|
|
446
|
+
"the first authenticated turn input remains authoritative");
|
|
447
|
+
}
|
|
448
|
+
await existing.pending;
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const pending = (async () => {
|
|
452
|
+
await recordBlackbox("turn.input", undefined, ctx, {
|
|
453
|
+
prompt_sha256: promptSha256,
|
|
454
|
+
prompt_chars: prompt.length,
|
|
455
|
+
images_count: imagesCount,
|
|
456
|
+
});
|
|
457
|
+
try {
|
|
458
|
+
await stageInbound(prompt, ctx);
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
api.logger.warn(`${TAG} semantic source handoff unavailable; memory writes will fail closed: ${error instanceof Error ? error.message : String(error)}`);
|
|
462
|
+
}
|
|
463
|
+
})();
|
|
464
|
+
observedTurnInputs.set(key, {
|
|
465
|
+
promptSha256,
|
|
466
|
+
observedAt: Date.now(),
|
|
467
|
+
pending,
|
|
468
|
+
});
|
|
469
|
+
await pending;
|
|
470
|
+
};
|
|
435
471
|
const bindInboundAttachments = async (keys, paths, types) => {
|
|
436
472
|
const generation = ++nextAttachmentGeneration;
|
|
437
473
|
for (const key of keys) {
|
|
@@ -509,19 +545,7 @@ function register(api) {
|
|
|
509
545
|
// OpenClaw documents this as the current prompt before model selection. It
|
|
510
546
|
// is a typed per-turn input surface, not a rendered transcript or history.
|
|
511
547
|
api.on("before_model_resolve", async (event, ctx) => {
|
|
512
|
-
|
|
513
|
-
return;
|
|
514
|
-
await recordBlackbox("turn.input", undefined, ctx, {
|
|
515
|
-
prompt_sha256: digestText(event.prompt),
|
|
516
|
-
prompt_chars: event.prompt.length,
|
|
517
|
-
images_count: Array.isArray(event.attachments) ? event.attachments.length : 0,
|
|
518
|
-
});
|
|
519
|
-
try {
|
|
520
|
-
await stageInbound(event.prompt, ctx);
|
|
521
|
-
}
|
|
522
|
-
catch (error) {
|
|
523
|
-
api.logger.warn(`${TAG} semantic source handoff unavailable; memory writes will fail closed: ${error instanceof Error ? error.message : String(error)}`);
|
|
524
|
-
}
|
|
548
|
+
await observeTurnInput(event.prompt, ctx, "before_model_resolve", Array.isArray(event.attachments) ? event.attachments.length : 0);
|
|
525
549
|
});
|
|
526
550
|
api.on("llm_input", async (event, ctx) => {
|
|
527
551
|
await recordBlackbox("model.input", event.runId, ctx, {
|
|
@@ -620,6 +644,10 @@ function register(api) {
|
|
|
620
644
|
if (now - value.ts > PROMPT_CACHE_TTL_MS)
|
|
621
645
|
pendingPrompts.delete(key);
|
|
622
646
|
}
|
|
647
|
+
for (const [key, value] of observedTurnInputs) {
|
|
648
|
+
if (now - value.observedAt > PROMPT_CACHE_TTL_MS)
|
|
649
|
+
observedTurnInputs.delete(key);
|
|
650
|
+
}
|
|
623
651
|
for (const [key, value] of inboundAttachments) {
|
|
624
652
|
if (now - value.ts > PROMPT_CACHE_TTL_MS)
|
|
625
653
|
inboundAttachments.delete(key);
|
|
@@ -655,6 +683,7 @@ function register(api) {
|
|
|
655
683
|
const userText = event.prompt;
|
|
656
684
|
if (!userText)
|
|
657
685
|
return;
|
|
686
|
+
await observeTurnInput(userText, ctx, "before_prompt_build");
|
|
658
687
|
const sessionKey = scopedKey(ctx.sessionKey ?? ctx.sessionId ?? "default-session", ctx);
|
|
659
688
|
const takeoverGuidance = cfg.takeoverActive ? TAKEOVER_GUIDANCE : "";
|
|
660
689
|
pendingPrompts.set(sessionKey, { text: userText, ts: Date.now() });
|
|
@@ -878,6 +907,7 @@ function register(api) {
|
|
|
878
907
|
// ---- auto-capture: user turn through the pipeline, assistant as digest -
|
|
879
908
|
api.on("agent_end", async (event, ctx) => {
|
|
880
909
|
const sessionKey = scopedKey(ctx.sessionKey ?? ctx.sessionId ?? "default-session", ctx);
|
|
910
|
+
const observedTurnInputKey = turnInputKey(ctx);
|
|
881
911
|
const cached = pendingPrompts.get(sessionKey);
|
|
882
912
|
pendingPrompts.delete(sessionKey);
|
|
883
913
|
const userText = cached?.text?.replace(INJECT_RE, "").trim();
|
|
@@ -978,6 +1008,7 @@ function register(api) {
|
|
|
978
1008
|
contextEventId: cached?.contextEventId,
|
|
979
1009
|
contextReceiptId: cached?.contextReceiptId,
|
|
980
1010
|
});
|
|
1011
|
+
observedTurnInputs.delete(observedTurnInputKey);
|
|
981
1012
|
}
|
|
982
1013
|
});
|
|
983
1014
|
// ---- keep injected blocks out of persisted history ---------------------
|
package/dist/src/rpc-client.js
CHANGED
package/openclaw.plugin.json
CHANGED