openclaw-memory-atmem 2.2.5 → 2.2.6-beta.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 +9 -2
- package/dist/index.js +92 -4
- package/dist/src/rpc-client.js +1 -1
- package/openclaw.plugin.json +17 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,11 +5,18 @@ 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 --pre --upgrade atmem==2.2.6b1
|
|
9
9
|
atmem openclaw install
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
The installer pins
|
|
12
|
+
The installer pins the matching OpenClaw bridge, 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
|
+
|
|
14
|
+
The bridge also supports AtMem's optional delegated context-provider mode. It
|
|
15
|
+
supplies authenticated owner/workspace bindings, contributes a verified result
|
|
16
|
+
as exactly one `prependContext` segment, confirms that segment at `llm_input`,
|
|
17
|
+
and suppresses native AtMem recall for the turn. Delegation is enabled only by
|
|
18
|
+
an AtMem registration; the bridge's `delegatedContext.userId` is an identity
|
|
19
|
+
mapping, not an activation switch.
|
|
13
20
|
|
|
14
21
|
Existing AtMem 2.1 users run `atmem openclaw upgrade` after upgrading the Python
|
|
15
22
|
package. This preserves the current memory mode and migration, verifies the new
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,10 @@ function parseConfig(raw) {
|
|
|
61
61
|
statePath: expandHome(String(cfg.controlPlane?.statePath ?? "~/.atmem/control-plane.json")),
|
|
62
62
|
blackboxEnabled: cfg.controlPlane?.enabled === true || cfg.controlPlane?.blackboxEnabled === true,
|
|
63
63
|
};
|
|
64
|
+
const delegatedContext = {
|
|
65
|
+
userId: String(cfg.delegatedContext?.userId ?? "").trim(),
|
|
66
|
+
requireOwner: cfg.delegatedContext?.requireOwner !== false,
|
|
67
|
+
};
|
|
64
68
|
return {
|
|
65
69
|
command: String(cfg.command ?? "atmem"),
|
|
66
70
|
commandArgs: controlPlane.enabled
|
|
@@ -98,6 +102,7 @@ function parseConfig(raw) {
|
|
|
98
102
|
},
|
|
99
103
|
tools: { enabled: cfg.tools?.enabled !== false },
|
|
100
104
|
controlPlane,
|
|
105
|
+
delegatedContext,
|
|
101
106
|
};
|
|
102
107
|
}
|
|
103
108
|
const FILE_TOOL_HINTS = [
|
|
@@ -347,6 +352,17 @@ function register(api) {
|
|
|
347
352
|
}
|
|
348
353
|
return cfg.subject;
|
|
349
354
|
};
|
|
355
|
+
const workspaceIdFor = (ctx) => {
|
|
356
|
+
const workspace = cfg.agentWorkspaces[agentIdFor(ctx)];
|
|
357
|
+
return workspace ? `ws_${digestText(workspace).slice(0, 16)}` : undefined;
|
|
358
|
+
};
|
|
359
|
+
const delegatedUserIdFor = (ctx) => {
|
|
360
|
+
if (!cfg.delegatedContext.userId)
|
|
361
|
+
return undefined;
|
|
362
|
+
if (cfg.delegatedContext.requireOwner && ctx.senderIsOwner !== true)
|
|
363
|
+
return undefined;
|
|
364
|
+
return cfg.delegatedContext.userId;
|
|
365
|
+
};
|
|
350
366
|
const scopedKey = (value, ctx) => `${agentIdFor(ctx)}:${value}`;
|
|
351
367
|
const contextIds = (ctx) => [...new Set([ctx.runId, ctx.sessionKey, ctx.sessionId].filter((value) => Boolean(value)).map((value) => scopedKey(value, ctx)))];
|
|
352
368
|
const callFor = (ctx, name, args, timeoutMs = cfg.recall.timeoutMs) => {
|
|
@@ -389,6 +405,7 @@ function register(api) {
|
|
|
389
405
|
event_type: eventType,
|
|
390
406
|
run_id: flightRunId(eventRunId, ctx),
|
|
391
407
|
agent_id: agentIdFor(ctx),
|
|
408
|
+
workspace_id: workspaceIdFor(ctx),
|
|
392
409
|
subject_id: Object.keys(cfg.agentSubjects).length ? subjectFor(ctx) : undefined,
|
|
393
410
|
session_id: ctx.sessionId ?? ctx.sessionKey,
|
|
394
411
|
tool_call_id: toolCallId,
|
|
@@ -548,6 +565,37 @@ function register(api) {
|
|
|
548
565
|
await observeTurnInput(event.prompt, ctx, "before_model_resolve", Array.isArray(event.attachments) ? event.attachments.length : 0);
|
|
549
566
|
});
|
|
550
567
|
api.on("llm_input", async (event, ctx) => {
|
|
568
|
+
const sessionKey = scopedKey(ctx.sessionKey ?? ctx.sessionId ?? event.sessionId ?? "default-session", ctx);
|
|
569
|
+
const pending = pendingPrompts.get(sessionKey);
|
|
570
|
+
if (pending?.delegatedContext !== undefined) {
|
|
571
|
+
const exact = pending.delegatedContext;
|
|
572
|
+
const promptOccurrences = exact ? event.prompt.split(exact).length - 1 : 0;
|
|
573
|
+
const systemOccurrences = exact
|
|
574
|
+
? (event.systemPrompt ?? "").split(exact).length - 1
|
|
575
|
+
: 0;
|
|
576
|
+
const occurrences = promptOccurrences + systemOccurrences;
|
|
577
|
+
const deliveredLocation = promptOccurrences === 1
|
|
578
|
+
? "prompt"
|
|
579
|
+
: systemOccurrences === 1
|
|
580
|
+
? "systemPrompt"
|
|
581
|
+
: "none";
|
|
582
|
+
const delivered = occurrences === 1 && digestText(exact) === pending.delegatedContextSha256;
|
|
583
|
+
await recordBlackbox("context.injected", event.runId, ctx, {
|
|
584
|
+
disposition: delivered ? "injected" : "recall_failed",
|
|
585
|
+
provider: pending.delegatedAuthority,
|
|
586
|
+
result_sha256: pending.delegatedResultSha256,
|
|
587
|
+
context_sha256: pending.delegatedContextSha256,
|
|
588
|
+
context_chars: exact.length,
|
|
589
|
+
context_byte_length: Buffer.byteLength(exact, "utf8"),
|
|
590
|
+
context_location: deliveredLocation,
|
|
591
|
+
success: delivered,
|
|
592
|
+
reason: delivered ? undefined : `expected one exact delegated segment; observed ${occurrences}`,
|
|
593
|
+
}, undefined, { contextReceiptId: pending.contextReceiptId });
|
|
594
|
+
if (delivered && pending.exposureId) {
|
|
595
|
+
await callFor(ctx, "control_exposure_shown", { exposure_id: pending.exposureId }, cfg.recall.timeoutMs);
|
|
596
|
+
}
|
|
597
|
+
pendingPrompts.set(sessionKey, { ...pending, delegatedContext: undefined });
|
|
598
|
+
}
|
|
551
599
|
await recordBlackbox("model.input", event.runId, ctx, {
|
|
552
600
|
provider: event.provider,
|
|
553
601
|
model: event.model,
|
|
@@ -690,13 +738,47 @@ function register(api) {
|
|
|
690
738
|
sweep();
|
|
691
739
|
if (cfg.controlPlane.enabled) {
|
|
692
740
|
try {
|
|
693
|
-
const prepared = (await callFor(ctx, "control_prepare", {
|
|
741
|
+
const prepared = (await callFor(ctx, "control_prepare", {
|
|
742
|
+
query: userText,
|
|
743
|
+
session_id: sessionKey,
|
|
744
|
+
host_run_id: ctx.runId,
|
|
745
|
+
turn_id: ctx.runId,
|
|
746
|
+
agent_id: agentIdFor(ctx),
|
|
747
|
+
user_id: delegatedUserIdFor(ctx),
|
|
748
|
+
workspace_id: workspaceIdFor(ctx),
|
|
749
|
+
}, cfg.recall.timeoutMs));
|
|
694
750
|
pendingPrompts.set(sessionKey, {
|
|
695
751
|
text: userText,
|
|
696
752
|
ts: Date.now(),
|
|
697
753
|
exposureId: prepared.exposure_id,
|
|
698
754
|
contextReceiptId: prepared.context_receipt_id,
|
|
755
|
+
delegatedContext: prepared.authority === "delegated" && prepared.inject
|
|
756
|
+
? prepared.context ?? ""
|
|
757
|
+
: undefined,
|
|
758
|
+
delegatedContextSha256: prepared.authority === "delegated" ? prepared.context_sha256 : undefined,
|
|
759
|
+
delegatedAuthority: prepared.authority,
|
|
760
|
+
delegatedResultSha256: prepared.result_sha256,
|
|
699
761
|
});
|
|
762
|
+
if (prepared.authority === "delegated" &&
|
|
763
|
+
prepared.inject &&
|
|
764
|
+
(!prepared.context ||
|
|
765
|
+
!prepared.context_sha256 ||
|
|
766
|
+
digestText(prepared.context) !== prepared.context_sha256)) {
|
|
767
|
+
throw new Error("delegated context failed exact handoff digest validation");
|
|
768
|
+
}
|
|
769
|
+
if (prepared.authority === "delegated" || prepared.authority === "atmem_fallback") {
|
|
770
|
+
await recordBlackbox("context.provider_authorization", undefined, ctx, {
|
|
771
|
+
disposition: prepared.decision ?? "provider_failure",
|
|
772
|
+
provider: prepared.provider?.id,
|
|
773
|
+
mode: prepared.authority,
|
|
774
|
+
result_sha256: prepared.result_sha256,
|
|
775
|
+
context_sha256: prepared.context_sha256,
|
|
776
|
+
context_byte_length: Buffer.byteLength(prepared.context ?? "", "utf8"),
|
|
777
|
+
context_receipt_sha256: prepared.receipt?.sha256,
|
|
778
|
+
context_chars: (prepared.context ?? "").length,
|
|
779
|
+
success: prepared.decision === "inject" || prepared.decision === "withhold",
|
|
780
|
+
}, undefined, { contextReceiptId: prepared.context_receipt_id });
|
|
781
|
+
}
|
|
700
782
|
await recordBlackbox("context.disposition", undefined, ctx, {
|
|
701
783
|
disposition: prepared.inject && prepared.context
|
|
702
784
|
? "injected"
|
|
@@ -705,20 +787,26 @@ function register(api) {
|
|
|
705
787
|
: "no_relevant_memory",
|
|
706
788
|
context_sha256: digestText(prepared.context ?? ""),
|
|
707
789
|
context_block_sha256: digestText(prepared.context ?? ""),
|
|
708
|
-
context_envelope_sha256: digestJson(
|
|
790
|
+
context_envelope_sha256: digestJson(prepared.authority === "delegated"
|
|
791
|
+
? { prependContext: prepared.context ?? "" }
|
|
792
|
+
: { appendContext: prepared.context ?? "" }),
|
|
709
793
|
context_receipt_sha256: prepared.manifest_sha256,
|
|
710
794
|
digest_profile: "atmem-context-envelope-canonical-json-v1",
|
|
711
795
|
context_chars: (prepared.context ?? "").length,
|
|
712
796
|
candidate_ids: prepared.candidate_ids ?? [],
|
|
713
797
|
exposure_id: prepared.exposure_id,
|
|
714
798
|
mode: prepared.mode,
|
|
715
|
-
context_location: prepared.inject
|
|
799
|
+
context_location: prepared.inject
|
|
800
|
+
? prepared.authority === "delegated" ? "prependContext" : "appendContext"
|
|
801
|
+
: "none",
|
|
716
802
|
}, undefined, {
|
|
717
803
|
contextReceiptId: prepared.context_receipt_id,
|
|
718
804
|
});
|
|
719
805
|
if (prepared.inject && prepared.context) {
|
|
720
806
|
api.logger.info(`${TAG} memory control plane ${prepared.mode ?? "active"} context exposed`);
|
|
721
|
-
return
|
|
807
|
+
return prepared.authority === "delegated"
|
|
808
|
+
? { prependContext: prepared.context }
|
|
809
|
+
: { appendContext: prepared.context };
|
|
722
810
|
}
|
|
723
811
|
return;
|
|
724
812
|
}
|
package/dist/src/rpc-client.js
CHANGED
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "memory-atmem",
|
|
3
3
|
"name": "Memory (atmem)",
|
|
4
4
|
"description": "OpenClaw bridge installed and managed by the AtMem memory control plane.",
|
|
5
|
-
"version": "2.2.
|
|
5
|
+
"version": "2.2.6-beta.1",
|
|
6
6
|
"commandAliases": ["memory-atmem"],
|
|
7
7
|
"activation": {
|
|
8
8
|
"onStartup": true,
|
|
@@ -135,6 +135,22 @@
|
|
|
135
135
|
"description": "Record content-minimizing agent flights through the private control protocol. Managed automatically by the AtMem installer."
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
+
},
|
|
139
|
+
"delegatedContext": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"description": "Identity mapping used only when AtMem has an explicitly enabled delegated context-provider registration. This does not enable delegation.",
|
|
142
|
+
"properties": {
|
|
143
|
+
"userId": {
|
|
144
|
+
"type": "string",
|
|
145
|
+
"default": "",
|
|
146
|
+
"description": "Opaque authenticated user ID registered with AtMem. Leave empty unless delegated authority is configured."
|
|
147
|
+
},
|
|
148
|
+
"requireOwner": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"default": true,
|
|
151
|
+
"description": "Send the mapped user ID only when OpenClaw marks the sender as owner."
|
|
152
|
+
}
|
|
153
|
+
}
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
156
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-memory-atmem",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.6-beta.1",
|
|
4
4
|
"description": "OpenClaw Agent Black Box and memory control-plane bridge for AtMem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -p tsconfig.json",
|
|
42
42
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
43
|
-
"test": "node test/setup.mjs && node test/hooks.mjs",
|
|
43
|
+
"test": "node test/setup.mjs && node test/hooks.mjs && node test/delegated-context-contract.mjs",
|
|
44
44
|
"smoke": "node test/smoke.mjs",
|
|
45
45
|
"prepack": "npm run build && npm run typecheck && npm test && npm run smoke"
|
|
46
46
|
},
|