@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.
- package/index.ts +52 -11
- package/key-resolver.ts +10 -24
- 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
|
|
92
|
-
/\b(
|
|
93
|
-
/\b(decided|agreed|
|
|
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 ??
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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:
|
|
253
|
-
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
4
|
-
"description": "OpenClaw memory plugin for Flair
|
|
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": [
|
|
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.
|
|
45
|
+
"@tpsdev-ai/flair-client": "^0.2.0"
|
|
44
46
|
}
|
|
45
|
-
}
|
|
47
|
+
}
|