@tpsdev-ai/openclaw-flair 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/index.ts +52 -11
  2. package/key-resolver.ts +10 -24
  3. package/package.json +7 -5
package/index.ts CHANGED
@@ -87,13 +87,19 @@ async function syncWorkspaceToFlair(
87
87
 
88
88
  // ─── Auto-capture helpers ─────────────────────────────────────────────────────
89
89
 
90
+ // Auto-capture triggers — conservative patterns that indicate genuinely
91
+ // important context, not casual conversation. The LLM has memory_store
92
+ // for explicit saves; auto-capture is a safety net for things it misses.
90
93
  const CAPTURE_TRIGGERS = [
91
- /\b(remember|note that|important:|keep in mind|don't forget)\b/i,
92
- /\b(preference|prefer|always|never|my name is|i am|i'm)\b/i,
93
- /\b(decided|agreed|confirmed|finalized)\b/i,
94
+ /\b(remember this|note for future|important lesson|key decision|for the record)\b/i,
95
+ /\b(my name is|call me|i go by)\b/i,
96
+ /\b(we decided|final decision|agreed to|commitment:)\b/i,
94
97
  ];
95
98
 
99
+ const MIN_CAPTURE_LENGTH = 30; // skip very short messages
100
+
96
101
  function shouldCapture(text: string): boolean {
102
+ if (text.length < MIN_CAPTURE_LENGTH) return false;
97
103
  return CAPTURE_TRIGGERS.some((re) => re.test(text));
98
104
  }
99
105
 
@@ -118,8 +124,8 @@ export default {
118
124
  const fallbackAgentId = resolveAgentId();
119
125
 
120
126
  function getClient(agentId?: string): FlairClient {
121
- const id = agentId || cfg.agentId || fallbackAgentId;
122
- if (!id || id === "auto") throw new Error("no agentId available — set agentId in plugin config, FLAIR_AGENT_ID env var, or ensure OpenClaw provides it via session context");
127
+ const id = agentId || (cfg.agentId && cfg.agentId !== "auto" ? cfg.agentId : null) || currentAgentId || fallbackAgentId;
128
+ if (!id || id === "auto") throw new Error("no agentId available — set agentId in plugin config, FLAIR_AGENT_ID env var, or ensure OpenClaw provides it via session context (before_agent_start)");
123
129
  let client = clientPool.get(id);
124
130
  if (!client) {
125
131
  client = new FlairClient({
@@ -144,14 +150,27 @@ export default {
144
150
  api.logger.info("openclaw-flair: auto mode — agentId will be resolved from session context");
145
151
  }
146
152
  const maxRecall = cfg.maxRecallResults ?? DEFAULT_MAX_RECALL;
147
- const autoCapture = cfg.autoCapture ?? true;
153
+ const autoCapture = cfg.autoCapture ?? false; // opt-in — trust the LLM to use memory_store
148
154
  const autoRecall = cfg.autoRecall ?? true;
149
155
 
150
- let currentAgentId: string | undefined = isAutoMode ? fallbackAgentId ?? undefined : cfg.agentId;
156
+ // Per-session agentId resolved from session context at runtime.
157
+ // In auto mode, each session gets its own agentId from before_agent_start.
158
+ // With explicit config, all sessions use the configured agentId.
159
+ let currentAgentId: string | undefined = isAutoMode ? (fallbackAgentId ?? undefined) : cfg.agentId;
160
+ const configuredAgentId = cfg.agentId && cfg.agentId !== "auto" ? cfg.agentId : null;
151
161
 
152
162
  api.on("before_agent_start", async (event: any, ctx: any) => {
153
163
  const eventAgentId = ctx?.agentId || (event as any).agentId;
154
- if (eventAgentId) currentAgentId = eventAgentId;
164
+ if (!eventAgentId) return;
165
+
166
+ if (isAutoMode) {
167
+ // Auto mode: always adopt the session's agentId — each session is its own agent
168
+ currentAgentId = eventAgentId;
169
+ api.logger.info(`openclaw-flair: session agentId="${eventAgentId}"`);
170
+ } else if (eventAgentId === configuredAgentId) {
171
+ // Explicit mode: only accept matching agentId
172
+ currentAgentId = eventAgentId;
173
+ }
155
174
 
156
175
  if (eventAgentId) {
157
176
  try {
@@ -242,15 +261,37 @@ export default {
242
261
  try {
243
262
  const client = getCurrentClient();
244
263
  const memId = `${client.agentId}-${Date.now()}`;
245
- await client.memory.write(text, {
264
+ // If superseding an old memory, archive it
265
+ if (supersedes) {
266
+ try {
267
+ const old = await client.memory.get(supersedes);
268
+ if (old) {
269
+ await client.request("PUT", `/Memory/${supersedes}`, {
270
+ ...old,
271
+ archived: true,
272
+ archivedAt: new Date().toISOString(),
273
+ supersededBy: memId,
274
+ });
275
+ }
276
+ } catch {
277
+ // Old memory not found — continue with the write
278
+ }
279
+ }
280
+
281
+ const result = await client.memory.write(text, {
246
282
  id: memId,
247
283
  tags,
248
284
  durability: durability as any,
249
285
  type: type as any,
286
+ dedup: !supersedes, // skip dedup when explicitly superseding
287
+ dedupThreshold: 0.7,
250
288
  });
289
+ const wasDeduped = result.id !== memId;
251
290
  return {
252
- content: [{ type: "text", text: `Memory stored (id: ${memId})` }],
253
- details: { id: memId },
291
+ content: [{ type: "text", text: wasDeduped
292
+ ? `Similar memory already exists (id: ${result.id}): ${result.content?.slice(0, 200)}`
293
+ : `Memory stored (id: ${memId})` }],
294
+ details: { id: result.id, deduplicated: wasDeduped },
254
295
  };
255
296
  } catch (err: any) {
256
297
  api.logger.warn(`openclaw-flair: store failed: ${err.message}`);
package/key-resolver.ts CHANGED
@@ -2,36 +2,22 @@
2
2
  * OpenClaw-specific identity resolution.
3
3
  *
4
4
  * Key path and private key loading are now handled by @tpsdev-ai/flair-client.
5
- * This file only contains resolveAgentId() which reads OpenClaw config something
6
- * the generic flair-client shouldn't know about.
5
+ * This file only contains resolveAgentId() which reads environment variables
6
+ * something the generic flair-client shouldn't know about.
7
7
  */
8
8
 
9
- import { existsSync, readFileSync } from "node:fs";
10
- import { homedir } from "node:os";
11
- import { resolve } from "node:path";
12
-
13
9
  /**
14
- * Resolve agent ID when not explicitly configured.
15
- * Priority: FLAIR_AGENT_ID env > OpenClaw config file > null
10
+ * Resolve agent ID from environment when not explicitly configured.
11
+ *
12
+ * IMPORTANT: Does NOT read from openclaw.json agents list. On a multi-agent
13
+ * gateway, the first agent in the list is NOT necessarily the current session's
14
+ * agent. The plugin must get the real agentId from the session context via
15
+ * before_agent_start, not from config guessing.
16
+ *
17
+ * Priority: FLAIR_AGENT_ID env > null (let session context provide it)
16
18
  */
17
19
  export function resolveAgentId(): string | null {
18
- // 1. Explicit env var
19
20
  const envId = process.env.FLAIR_AGENT_ID;
20
21
  if (envId) return envId;
21
-
22
- // 2. Read from OpenClaw config — first agent name
23
- try {
24
- const configPath = resolve(homedir(), ".openclaw", "openclaw.json");
25
- if (!existsSync(configPath)) return null;
26
- const config = JSON.parse(readFileSync(configPath, "utf-8"));
27
- const agents = config?.agents?.list;
28
- if (Array.isArray(agents) && agents.length > 0) {
29
- const name = agents[0]?.name;
30
- if (typeof name === "string" && name) return name.toLowerCase();
31
- }
32
- } catch {
33
- // Config unreadable — fall through
34
- }
35
-
36
22
  return null;
37
23
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tpsdev-ai/openclaw-flair",
3
- "version": "0.3.0",
4
- "description": "OpenClaw memory plugin for Flair agent identity and semantic memory",
3
+ "version": "0.4.1",
4
+ "description": "OpenClaw memory plugin for Flair \u2014 agent identity and semantic memory",
5
5
  "type": "module",
6
6
  "main": "index.ts",
7
7
  "exports": {
@@ -30,7 +30,9 @@
30
30
  },
31
31
  "homepage": "https://github.com/tpsdev-ai/flair/tree/main/plugins/openclaw-flair",
32
32
  "openclaw": {
33
- "extensions": ["./index.ts"]
33
+ "extensions": [
34
+ "./index.ts"
35
+ ]
34
36
  },
35
37
  "peerDependencies": {
36
38
  "openclaw": ">=2026.3.7"
@@ -40,6 +42,6 @@
40
42
  },
41
43
  "dependencies": {
42
44
  "@sinclair/typebox": "^0.34.0",
43
- "@tpsdev-ai/flair-client": "^0.1.0"
45
+ "@tpsdev-ai/flair-client": "^0.2.0"
44
46
  }
45
- }
47
+ }