openclaw-memory-atmem 1.0.0 → 2.0.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.
- package/README.md +2 -2
- package/dist/index.js +163 -27
- 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 atmem==
|
|
8
|
+
python -m pip install atmem==2.0.1
|
|
9
9
|
atmem openclaw install
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
The installer pins `openclaw-memory-atmem@
|
|
12
|
+
The installer pins `openclaw-memory-atmem@2.0.1`, 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
|
In shadow mode the bridge observes native-memory changes without injecting AtMem context. In active mode it exposes compatible memory search/get tools, model-semantic capture, bounded recall and native-path protection. `atmem control restore` restores the saved OpenClaw configuration and native memory.
|
|
15
15
|
|
package/dist/index.js
CHANGED
|
@@ -333,7 +333,13 @@ function register(api) {
|
|
|
333
333
|
}
|
|
334
334
|
};
|
|
335
335
|
const flightRunId = (eventRunId, ctx) => eventRunId ?? ctx.runId ?? ctx.sessionKey ?? ctx.sessionId ?? "unidentified-run";
|
|
336
|
-
const
|
|
336
|
+
const canonicalToolName = (value) => {
|
|
337
|
+
const normalized = value.trim();
|
|
338
|
+
return normalized.startsWith("openclaw") && normalized.length > "openclaw".length
|
|
339
|
+
? normalized.slice("openclaw".length)
|
|
340
|
+
: normalized;
|
|
341
|
+
};
|
|
342
|
+
const recordBlackbox = async (eventType, eventRunId, ctx, payload, toolCallId, correlation = {}) => {
|
|
337
343
|
if (!cfg.controlPlane.blackboxEnabled)
|
|
338
344
|
return;
|
|
339
345
|
try {
|
|
@@ -342,6 +348,11 @@ function register(api) {
|
|
|
342
348
|
run_id: flightRunId(eventRunId, ctx),
|
|
343
349
|
session_id: ctx.sessionId ?? ctx.sessionKey,
|
|
344
350
|
tool_call_id: toolCallId,
|
|
351
|
+
turn_id: correlation.turnId ?? flightRunId(eventRunId, ctx),
|
|
352
|
+
retrieval_id: correlation.retrievalId,
|
|
353
|
+
context_event_id: correlation.contextEventId,
|
|
354
|
+
context_receipt_id: correlation.contextReceiptId,
|
|
355
|
+
outcome_id: correlation.outcomeId,
|
|
345
356
|
payload,
|
|
346
357
|
}, cfg.recall.timeoutMs);
|
|
347
358
|
}
|
|
@@ -485,13 +496,28 @@ function register(api) {
|
|
|
485
496
|
});
|
|
486
497
|
api.on("llm_output", async (event, ctx) => {
|
|
487
498
|
const responses = Array.isArray(event.assistantTexts) ? event.assistantTexts : [];
|
|
499
|
+
const visibleText = responses.map(String).join("");
|
|
500
|
+
const assistantVisibleTextSha256 = digestText(visibleText);
|
|
501
|
+
const modelOutputBundleSha256 = digestJson(responses);
|
|
502
|
+
const sessionKey = ctx.sessionKey ?? ctx.sessionId ?? event.sessionId ?? "default-session";
|
|
503
|
+
const pending = pendingPrompts.get(sessionKey);
|
|
504
|
+
if (pending) {
|
|
505
|
+
pendingPrompts.set(sessionKey, {
|
|
506
|
+
...pending,
|
|
507
|
+
assistantVisibleTextSha256,
|
|
508
|
+
modelOutputBundleSha256,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
488
511
|
await recordBlackbox("model.output", event.runId, ctx, {
|
|
489
512
|
provider: event.provider,
|
|
490
513
|
model: event.model,
|
|
491
514
|
resolved_ref: event.resolvedRef,
|
|
492
515
|
harness_id: event.harnessId,
|
|
493
|
-
response_sha256:
|
|
494
|
-
|
|
516
|
+
response_sha256: assistantVisibleTextSha256,
|
|
517
|
+
assistant_visible_text_sha256: assistantVisibleTextSha256,
|
|
518
|
+
model_output_bundle_sha256: modelOutputBundleSha256,
|
|
519
|
+
response_digest_profile: "atmem-assistant-visible-text-utf8-v1",
|
|
520
|
+
response_chars: visibleText.length,
|
|
495
521
|
response_count: responses.length,
|
|
496
522
|
usage: event.usage ?? {},
|
|
497
523
|
reasoning_effort: event.reasoningEffort,
|
|
@@ -558,10 +584,10 @@ function register(api) {
|
|
|
558
584
|
};
|
|
559
585
|
async function personaBlock(sessionKey) {
|
|
560
586
|
if (!cfg.persona.enabled)
|
|
561
|
-
return "";
|
|
587
|
+
return { block: "", recordIds: [] };
|
|
562
588
|
const now = Date.now();
|
|
563
589
|
if (personaCache && now - personaCache.ts < cfg.persona.ttlSeconds * 1000) {
|
|
564
|
-
return personaCache
|
|
590
|
+
return personaCache;
|
|
565
591
|
}
|
|
566
592
|
const result = (await client.callTool("memory_persona", {
|
|
567
593
|
session_id: sessionKey,
|
|
@@ -570,8 +596,13 @@ function register(api) {
|
|
|
570
596
|
? "compact"
|
|
571
597
|
: "full",
|
|
572
598
|
}, cfg.recall.timeoutMs));
|
|
573
|
-
personaCache = {
|
|
574
|
-
|
|
599
|
+
personaCache = {
|
|
600
|
+
block: result?.block ?? "",
|
|
601
|
+
recordIds: result?.record_ids ?? [],
|
|
602
|
+
contextEventId: result?.context_event_id,
|
|
603
|
+
ts: now,
|
|
604
|
+
};
|
|
605
|
+
return personaCache;
|
|
575
606
|
}
|
|
576
607
|
// ---- auto-recall: persona + bounded, audited recall injection ---------
|
|
577
608
|
api.on("before_prompt_build", async (event, ctx) => {
|
|
@@ -589,13 +620,26 @@ function register(api) {
|
|
|
589
620
|
text: userText,
|
|
590
621
|
ts: Date.now(),
|
|
591
622
|
exposureId: prepared.exposure_id,
|
|
623
|
+
contextReceiptId: prepared.context_receipt_id,
|
|
592
624
|
});
|
|
593
|
-
await recordBlackbox(
|
|
625
|
+
await recordBlackbox("context.disposition", undefined, ctx, {
|
|
626
|
+
disposition: prepared.inject && prepared.context
|
|
627
|
+
? "injected"
|
|
628
|
+
: prepared.mode === "shadow" && (prepared.preview_context ?? "")
|
|
629
|
+
? "withheld_by_policy"
|
|
630
|
+
: "no_relevant_memory",
|
|
594
631
|
context_sha256: digestText(prepared.context ?? ""),
|
|
632
|
+
context_block_sha256: digestText(prepared.context ?? ""),
|
|
633
|
+
context_envelope_sha256: digestJson({ appendContext: prepared.context ?? "" }),
|
|
634
|
+
context_receipt_sha256: prepared.manifest_sha256,
|
|
635
|
+
digest_profile: "atmem-context-envelope-canonical-json-v1",
|
|
595
636
|
context_chars: (prepared.context ?? "").length,
|
|
596
637
|
candidate_ids: prepared.candidate_ids ?? [],
|
|
597
638
|
exposure_id: prepared.exposure_id,
|
|
598
639
|
mode: prepared.mode,
|
|
640
|
+
context_location: prepared.inject ? "appendContext" : "none",
|
|
641
|
+
}, undefined, {
|
|
642
|
+
contextReceiptId: prepared.context_receipt_id,
|
|
599
643
|
});
|
|
600
644
|
if (prepared.inject && prepared.context) {
|
|
601
645
|
api.logger.info(`${TAG} memory control plane ${prepared.mode ?? "active"} context exposed`);
|
|
@@ -604,21 +648,52 @@ function register(api) {
|
|
|
604
648
|
return;
|
|
605
649
|
}
|
|
606
650
|
catch (error) {
|
|
651
|
+
await recordBlackbox("context.disposition", undefined, ctx, {
|
|
652
|
+
disposition: "recall_failed",
|
|
653
|
+
context_block_sha256: digestText(""),
|
|
654
|
+
context_envelope_sha256: digestJson({}),
|
|
655
|
+
digest_profile: "atmem-context-envelope-canonical-json-v1",
|
|
656
|
+
context_chars: 0,
|
|
657
|
+
candidate_ids: [],
|
|
658
|
+
mode: "control-plane",
|
|
659
|
+
context_location: "none",
|
|
660
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
661
|
+
});
|
|
607
662
|
api.logger.warn(`${TAG} memory control plane failed closed: ${error instanceof Error ? error.message : String(error)}`);
|
|
608
663
|
return;
|
|
609
664
|
}
|
|
610
665
|
}
|
|
611
666
|
if (!cfg.recall.enabled && !cfg.persona.enabled) {
|
|
612
|
-
|
|
613
|
-
|
|
667
|
+
const result = takeoverGuidance
|
|
668
|
+
? { appendSystemContext: takeoverGuidance }
|
|
669
|
+
: {};
|
|
670
|
+
await recordBlackbox("context.disposition", undefined, ctx, {
|
|
671
|
+
disposition: "not_applicable",
|
|
672
|
+
context_block_sha256: digestText(""),
|
|
673
|
+
context_envelope_sha256: digestJson(result),
|
|
674
|
+
digest_profile: "atmem-context-envelope-canonical-json-v1",
|
|
675
|
+
context_chars: 0,
|
|
676
|
+
candidate_ids: [],
|
|
677
|
+
mode: "direct",
|
|
678
|
+
context_location: "none",
|
|
679
|
+
});
|
|
680
|
+
if (Object.keys(result).length)
|
|
681
|
+
return result;
|
|
614
682
|
return;
|
|
615
683
|
}
|
|
616
684
|
let persona = "";
|
|
685
|
+
let personaRecordIds = [];
|
|
686
|
+
let personaContextEventId;
|
|
617
687
|
let recall = "";
|
|
688
|
+
let recallFailed = false;
|
|
618
689
|
try {
|
|
619
|
-
|
|
690
|
+
const personaResult = await personaBlock(sessionKey);
|
|
691
|
+
persona = personaResult.block;
|
|
692
|
+
personaRecordIds = personaResult.recordIds;
|
|
693
|
+
personaContextEventId = personaResult.contextEventId;
|
|
620
694
|
}
|
|
621
695
|
catch (error) {
|
|
696
|
+
recallFailed = true;
|
|
622
697
|
api.logger.warn(`${TAG} persona skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
623
698
|
}
|
|
624
699
|
try {
|
|
@@ -643,6 +718,7 @@ function register(api) {
|
|
|
643
718
|
injectedRecordIds: result.record_ids ?? [],
|
|
644
719
|
retrievalId: result.retrieval_id,
|
|
645
720
|
contextEventId: result.context_event_id,
|
|
721
|
+
contextReceiptId: result.context_event_id,
|
|
646
722
|
});
|
|
647
723
|
}
|
|
648
724
|
}
|
|
@@ -650,25 +726,68 @@ function register(api) {
|
|
|
650
726
|
}
|
|
651
727
|
catch (error) {
|
|
652
728
|
// Never block the turn on recall problems.
|
|
729
|
+
recallFailed = true;
|
|
653
730
|
api.logger.warn(`${TAG} auto-recall skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
654
731
|
}
|
|
732
|
+
let result = {};
|
|
733
|
+
let contextLocation = "none";
|
|
655
734
|
if (cfg.cacheAware.enabled) {
|
|
656
|
-
const result = {};
|
|
657
735
|
const systemParts = [takeoverGuidance, persona].filter(Boolean);
|
|
658
736
|
if (systemParts.length)
|
|
659
737
|
result.appendSystemContext = systemParts.join("\n\n");
|
|
660
738
|
if (recall)
|
|
661
739
|
result.appendContext = recall;
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
740
|
+
contextLocation = recall
|
|
741
|
+
? "appendContext"
|
|
742
|
+
: persona
|
|
743
|
+
? "appendSystemContext"
|
|
744
|
+
: "none";
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
const parts = [persona, recall].filter(Boolean);
|
|
748
|
+
if (parts.length)
|
|
749
|
+
result.prependContext = parts.join("\n\n") + "\n\n";
|
|
750
|
+
if (takeoverGuidance)
|
|
751
|
+
result.appendSystemContext = takeoverGuidance;
|
|
752
|
+
contextLocation = parts.length ? "prependContext" : "none";
|
|
753
|
+
}
|
|
754
|
+
const memoryContext = [persona, recall].filter(Boolean).join("\n\n");
|
|
755
|
+
const current = pendingPrompts.get(sessionKey);
|
|
756
|
+
const candidateIds = [...new Set([
|
|
757
|
+
...personaRecordIds,
|
|
758
|
+
...(current?.injectedRecordIds ?? []),
|
|
759
|
+
])];
|
|
760
|
+
const componentEventIds = [...new Set([
|
|
761
|
+
personaContextEventId,
|
|
762
|
+
current?.contextEventId,
|
|
763
|
+
].filter((value) => Boolean(value)))];
|
|
764
|
+
const contextEnvelopeSha256 = digestJson(result);
|
|
765
|
+
const contextReceiptId = componentEventIds.length
|
|
766
|
+
? `ctxr_${digestJson({ componentEventIds, contextEnvelopeSha256 })}`
|
|
767
|
+
: undefined;
|
|
768
|
+
if (current) {
|
|
769
|
+
pendingPrompts.set(sessionKey, { ...current, contextReceiptId });
|
|
665
770
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
771
|
+
await recordBlackbox("context.disposition", undefined, ctx, {
|
|
772
|
+
disposition: memoryContext
|
|
773
|
+
? "injected"
|
|
774
|
+
: recallFailed
|
|
775
|
+
? "recall_failed"
|
|
776
|
+
: "no_relevant_memory",
|
|
777
|
+
context_sha256: digestText(memoryContext),
|
|
778
|
+
context_block_sha256: digestText(memoryContext),
|
|
779
|
+
context_envelope_sha256: contextEnvelopeSha256,
|
|
780
|
+
digest_profile: "atmem-context-envelope-canonical-json-v1",
|
|
781
|
+
context_chars: memoryContext.length,
|
|
782
|
+
candidate_ids: candidateIds,
|
|
783
|
+
context_component_event_ids: componentEventIds,
|
|
784
|
+
mode: "direct",
|
|
785
|
+
context_location: contextLocation,
|
|
786
|
+
}, undefined, {
|
|
787
|
+
retrievalId: current?.retrievalId,
|
|
788
|
+
contextEventId: current?.contextEventId ?? personaContextEventId,
|
|
789
|
+
contextReceiptId,
|
|
790
|
+
});
|
|
672
791
|
if (Object.keys(result).length)
|
|
673
792
|
return result;
|
|
674
793
|
});
|
|
@@ -676,6 +795,7 @@ function register(api) {
|
|
|
676
795
|
api.on("before_tool_call", async (event, ctx) => {
|
|
677
796
|
await recordBlackbox("tool.requested", event.runId, ctx, {
|
|
678
797
|
tool_name: event.toolName,
|
|
798
|
+
tool_canonical_name: canonicalToolName(event.toolName),
|
|
679
799
|
tool_kind: event.toolKind,
|
|
680
800
|
params_sha256: digestJson(event.params ?? {}),
|
|
681
801
|
param_keys: Object.keys(event.params ?? {}).sort(),
|
|
@@ -690,6 +810,7 @@ function register(api) {
|
|
|
690
810
|
"and memory_search or memory_get for recall.";
|
|
691
811
|
await recordBlackbox("tool.completed", event.runId, ctx, {
|
|
692
812
|
tool_name: event.toolName,
|
|
813
|
+
tool_canonical_name: canonicalToolName(event.toolName),
|
|
693
814
|
outcome: "error",
|
|
694
815
|
error_category: "blocked_by_memory_boundary",
|
|
695
816
|
result_sha256: digestText(reason),
|
|
@@ -701,6 +822,7 @@ function register(api) {
|
|
|
701
822
|
api.on("after_tool_call", async (event, ctx) => {
|
|
702
823
|
await recordBlackbox("tool.completed", event.runId, ctx, {
|
|
703
824
|
tool_name: event.toolName,
|
|
825
|
+
tool_canonical_name: canonicalToolName(event.toolName),
|
|
704
826
|
result_sha256: digestJson(event.result ?? null),
|
|
705
827
|
outcome: event.error ? "error" : "completed",
|
|
706
828
|
error_category: event.error ? "tool_error" : undefined,
|
|
@@ -734,15 +856,19 @@ function register(api) {
|
|
|
734
856
|
continue;
|
|
735
857
|
const responseText = messageText(message.content);
|
|
736
858
|
if (responseText) {
|
|
859
|
+
const assistantVisibleTextSha256 = cached.assistantVisibleTextSha256 ?? digestText(responseText);
|
|
737
860
|
await client.callTool("memory_log_action", {
|
|
738
861
|
action_type: "agent.response_after_memory",
|
|
739
862
|
payload: {
|
|
740
|
-
response_sha256:
|
|
741
|
-
|
|
742
|
-
|
|
863
|
+
response_sha256: assistantVisibleTextSha256,
|
|
864
|
+
assistant_visible_text_sha256: assistantVisibleTextSha256,
|
|
865
|
+
model_output_bundle_sha256: cached.modelOutputBundleSha256,
|
|
866
|
+
response_digest_profile: "atmem-assistant-visible-text-utf8-v1",
|
|
743
867
|
injected_record_ids: cached.injectedRecordIds,
|
|
744
868
|
retrieval_id: cached.retrievalId,
|
|
745
869
|
context_event_id: cached.contextEventId,
|
|
870
|
+
context_receipt_id: cached.contextReceiptId,
|
|
871
|
+
run_id: flightRunId(event.runId, ctx),
|
|
746
872
|
response_content_stored: false,
|
|
747
873
|
success: event.success !== false,
|
|
748
874
|
},
|
|
@@ -788,13 +914,23 @@ function register(api) {
|
|
|
788
914
|
api.logger.warn(`${TAG} auto-capture failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
789
915
|
}
|
|
790
916
|
finally {
|
|
917
|
+
const cancelled = event.cancelled === true;
|
|
918
|
+
const turnMessagesSha256 = digestJson(event.messages ?? []);
|
|
791
919
|
await recordBlackbox("turn.ended", event.runId, ctx, {
|
|
792
|
-
success: event.success
|
|
793
|
-
cancelled
|
|
920
|
+
success: event.success === true && !cancelled,
|
|
921
|
+
cancelled,
|
|
794
922
|
error_category: event.error ? "agent_error" : undefined,
|
|
923
|
+
failure_kind: cancelled ? "cancelled" : event.error ? "agent_error" : undefined,
|
|
924
|
+
reason: event.error,
|
|
795
925
|
duration_ms: event.durationMs ?? 0,
|
|
796
|
-
messages_sha256:
|
|
926
|
+
messages_sha256: turnMessagesSha256,
|
|
927
|
+
turn_messages_sha256: turnMessagesSha256,
|
|
928
|
+
digest_profile: "atmem-turn-messages-canonical-json-v1",
|
|
797
929
|
messages_count: Array.isArray(event.messages) ? event.messages.length : 0,
|
|
930
|
+
}, undefined, {
|
|
931
|
+
retrievalId: cached?.retrievalId,
|
|
932
|
+
contextEventId: cached?.contextEventId,
|
|
933
|
+
contextReceiptId: cached?.contextReceiptId,
|
|
798
934
|
});
|
|
799
935
|
}
|
|
800
936
|
});
|
package/dist/src/rpc-client.js
CHANGED
package/openclaw.plugin.json
CHANGED