@ubundi/openclaw-cortex 0.2.0

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 (59) hide show
  1. package/README.md +157 -0
  2. package/dist/client.d.ts +48 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +73 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/config.d.ts +68 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +27 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/hooks/capture.d.ts +24 -0
  11. package/dist/hooks/capture.d.ts.map +1 -0
  12. package/dist/hooks/capture.js +66 -0
  13. package/dist/hooks/capture.js.map +1 -0
  14. package/dist/hooks/recall.d.ts +28 -0
  15. package/dist/hooks/recall.d.ts.map +1 -0
  16. package/dist/hooks/recall.js +68 -0
  17. package/dist/hooks/recall.js.map +1 -0
  18. package/dist/index.d.ts +66 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +94 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/services/reflect.d.ts +19 -0
  23. package/dist/services/reflect.d.ts.map +1 -0
  24. package/dist/services/reflect.js +38 -0
  25. package/dist/services/reflect.js.map +1 -0
  26. package/dist/sync/daily-logs.d.ts +21 -0
  27. package/dist/sync/daily-logs.d.ts.map +1 -0
  28. package/dist/sync/daily-logs.js +43 -0
  29. package/dist/sync/daily-logs.js.map +1 -0
  30. package/dist/sync/memory-md.d.ts +25 -0
  31. package/dist/sync/memory-md.d.ts.map +1 -0
  32. package/dist/sync/memory-md.js +67 -0
  33. package/dist/sync/memory-md.js.map +1 -0
  34. package/dist/sync/transcripts.d.ts +21 -0
  35. package/dist/sync/transcripts.d.ts.map +1 -0
  36. package/dist/sync/transcripts.js +51 -0
  37. package/dist/sync/transcripts.js.map +1 -0
  38. package/dist/sync/watcher.d.ts +28 -0
  39. package/dist/sync/watcher.d.ts.map +1 -0
  40. package/dist/sync/watcher.js +85 -0
  41. package/dist/sync/watcher.js.map +1 -0
  42. package/dist/utils/format.d.ts +3 -0
  43. package/dist/utils/format.d.ts.map +1 -0
  44. package/dist/utils/format.js +7 -0
  45. package/dist/utils/format.js.map +1 -0
  46. package/dist/utils/metrics.d.ts +19 -0
  47. package/dist/utils/metrics.d.ts.map +1 -0
  48. package/dist/utils/metrics.js +45 -0
  49. package/dist/utils/metrics.js.map +1 -0
  50. package/dist/utils/retry-queue.d.ts +27 -0
  51. package/dist/utils/retry-queue.d.ts.map +1 -0
  52. package/dist/utils/retry-queue.js +67 -0
  53. package/dist/utils/retry-queue.js.map +1 -0
  54. package/dist/utils/transcript-cleaner.d.ts +24 -0
  55. package/dist/utils/transcript-cleaner.d.ts.map +1 -0
  56. package/dist/utils/transcript-cleaner.js +96 -0
  57. package/dist/utils/transcript-cleaner.js.map +1 -0
  58. package/openclaw.plugin.json +95 -0
  59. package/package.json +41 -0
@@ -0,0 +1,7 @@
1
+ export function formatMemories(results) {
2
+ if (!results.length)
3
+ return "";
4
+ const lines = results.map((r) => `- [${r.score.toFixed(2)}] ${r.content}`);
5
+ return `<cortex_memories>\n${lines.join("\n")}\n</cortex_memories>`;
6
+ }
7
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,OAAyB;IACtD,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAE/B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAChD,CAAC;IAEF,OAAO,sBAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC;AACtE,CAAC"}
@@ -0,0 +1,19 @@
1
+ export declare class LatencyMetrics {
2
+ private samples;
3
+ private windowSize;
4
+ constructor(windowSize?: number);
5
+ record(durationMs: number): void;
6
+ get count(): number;
7
+ percentile(p: number): number | null;
8
+ get p50(): number | null;
9
+ get p95(): number | null;
10
+ get p99(): number | null;
11
+ summary(): {
12
+ count: number;
13
+ p50: number | null;
14
+ p95: number | null;
15
+ p99: number | null;
16
+ };
17
+ reset(): void;
18
+ }
19
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../src/utils/metrics.ts"],"names":[],"mappings":"AAEA,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,SAAsB;IAI5C,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAOhC,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQpC,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,IAAI,GAAG,IAAI,MAAM,GAAG,IAAI,CAEvB;IAED,OAAO,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IASxF,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,45 @@
1
+ const DEFAULT_WINDOW_SIZE = 100;
2
+ export class LatencyMetrics {
3
+ samples = [];
4
+ windowSize;
5
+ constructor(windowSize = DEFAULT_WINDOW_SIZE) {
6
+ this.windowSize = windowSize;
7
+ }
8
+ record(durationMs) {
9
+ this.samples.push(durationMs);
10
+ if (this.samples.length > this.windowSize) {
11
+ this.samples.shift();
12
+ }
13
+ }
14
+ get count() {
15
+ return this.samples.length;
16
+ }
17
+ percentile(p) {
18
+ if (this.samples.length === 0)
19
+ return null;
20
+ const sorted = [...this.samples].sort((a, b) => a - b);
21
+ const index = Math.ceil((p / 100) * sorted.length) - 1;
22
+ return sorted[Math.max(0, index)];
23
+ }
24
+ get p50() {
25
+ return this.percentile(50);
26
+ }
27
+ get p95() {
28
+ return this.percentile(95);
29
+ }
30
+ get p99() {
31
+ return this.percentile(99);
32
+ }
33
+ summary() {
34
+ return {
35
+ count: this.count,
36
+ p50: this.p50,
37
+ p95: this.p95,
38
+ p99: this.p99,
39
+ };
40
+ }
41
+ reset() {
42
+ this.samples = [];
43
+ }
44
+ }
45
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/utils/metrics.ts"],"names":[],"mappings":"AAAA,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,MAAM,OAAO,cAAc;IACjB,OAAO,GAAa,EAAE,CAAC;IACvB,UAAU,CAAS;IAE3B,YAAY,UAAU,GAAG,mBAAmB;QAC1C,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,UAAkB;QACvB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED,UAAU,CAAC,CAAS;QAClB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAE3C,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACpB,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ type Logger = {
2
+ debug?(...args: unknown[]): void;
3
+ info(...args: unknown[]): void;
4
+ warn(...args: unknown[]): void;
5
+ error(...args: unknown[]): void;
6
+ };
7
+ export interface RetryTask {
8
+ id: string;
9
+ execute: () => Promise<void>;
10
+ retries: number;
11
+ nextAttemptAt: number;
12
+ }
13
+ export declare class RetryQueue {
14
+ private logger;
15
+ private maxRetries;
16
+ private queue;
17
+ private timer;
18
+ private taskCounter;
19
+ constructor(logger: Logger, maxRetries?: number);
20
+ start(): void;
21
+ stop(): void;
22
+ enqueue(execute: () => Promise<void>, label?: string): void;
23
+ get pending(): number;
24
+ private flush;
25
+ }
26
+ export {};
27
+ //# sourceMappingURL=retry-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-queue.d.ts","sourceRoot":"","sources":["../../src/utils/retry-queue.ts"],"names":[],"mappings":"AAAA,KAAK,MAAM,GAAG;IACZ,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAOD,qBAAa,UAAU;IAMnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU;IANpB,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,WAAW,CAAK;gBAGd,MAAM,EAAE,MAAM,EACd,UAAU,SAAc;IAGlC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAWZ,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAW3D,IAAI,OAAO,IAAI,MAAM,CAEpB;YAEa,KAAK;CA8BpB"}
@@ -0,0 +1,67 @@
1
+ const BASE_DELAY_MS = 1000;
2
+ const MAX_DELAY_MS = 60_000;
3
+ const MAX_RETRIES = 5;
4
+ const FLUSH_INTERVAL_MS = 5000;
5
+ export class RetryQueue {
6
+ logger;
7
+ maxRetries;
8
+ queue = [];
9
+ timer = null;
10
+ taskCounter = 0;
11
+ constructor(logger, maxRetries = MAX_RETRIES) {
12
+ this.logger = logger;
13
+ this.maxRetries = maxRetries;
14
+ }
15
+ start() {
16
+ if (this.timer)
17
+ return;
18
+ this.timer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
19
+ }
20
+ stop() {
21
+ if (this.timer) {
22
+ clearInterval(this.timer);
23
+ this.timer = null;
24
+ }
25
+ if (this.queue.length > 0) {
26
+ this.logger.warn(`Retry queue stopped with ${this.queue.length} pending tasks`);
27
+ }
28
+ this.queue = [];
29
+ }
30
+ enqueue(execute, label) {
31
+ const id = label ?? `task-${++this.taskCounter}`;
32
+ this.queue.push({
33
+ id,
34
+ execute,
35
+ retries: 0,
36
+ nextAttemptAt: Date.now(),
37
+ });
38
+ this.logger.debug?.(`Retry queue: enqueued ${id}`);
39
+ }
40
+ get pending() {
41
+ return this.queue.length;
42
+ }
43
+ async flush() {
44
+ const now = Date.now();
45
+ const ready = this.queue.filter((t) => t.nextAttemptAt <= now);
46
+ for (const task of ready) {
47
+ try {
48
+ await task.execute();
49
+ this.queue = this.queue.filter((t) => t.id !== task.id);
50
+ this.logger.debug?.(`Retry queue: ${task.id} succeeded`);
51
+ }
52
+ catch (err) {
53
+ task.retries++;
54
+ if (task.retries >= this.maxRetries) {
55
+ this.queue = this.queue.filter((t) => t.id !== task.id);
56
+ this.logger.warn(`Retry queue: ${task.id} failed after ${this.maxRetries} retries, dropping`, err);
57
+ }
58
+ else {
59
+ const delay = Math.min(BASE_DELAY_MS * Math.pow(2, task.retries), MAX_DELAY_MS);
60
+ task.nextAttemptAt = now + delay;
61
+ this.logger.debug?.(`Retry queue: ${task.id} retry ${task.retries}/${this.maxRetries} in ${delay}ms`);
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=retry-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-queue.js","sourceRoot":"","sources":["../../src/utils/retry-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ConversationMessage } from "../client.js";
2
+ /**
3
+ * Clean a raw JSONL transcript into a conversation suitable for Cortex ingestion.
4
+ *
5
+ * Strips:
6
+ * - System prompt messages (role: "system" / "developer")
7
+ * - Tool call/result events (tool_calls, tool role messages)
8
+ * - Base64 encoded images
9
+ * - Empty messages
10
+ *
11
+ * Preserves:
12
+ * - User messages with speaker attribution
13
+ * - Assistant messages with content
14
+ */
15
+ export declare function cleanTranscript(jsonlText: string): ConversationMessage[];
16
+ /**
17
+ * Clean only new JSONL lines (from an offset-based append).
18
+ * Returns cleaned messages and whether they're worth ingesting.
19
+ */
20
+ export declare function cleanTranscriptChunk(newLines: string): {
21
+ messages: ConversationMessage[];
22
+ worthIngesting: boolean;
23
+ };
24
+ //# sourceMappingURL=transcript-cleaner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript-cleaner.d.ts","sourceRoot":"","sources":["../../src/utils/transcript-cleaner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAkExD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE,CA2BxE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG;IACtD,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,cAAc,EAAE,OAAO,CAAC;CACzB,CAQA"}
@@ -0,0 +1,96 @@
1
+ const BASE64_PATTERN = /data:[^;]+;base64,[A-Za-z0-9+/=]{100,}/g;
2
+ const SYSTEM_ROLES = new Set(["system", "developer"]);
3
+ /**
4
+ * Parse JSONL text into individual events, skipping malformed lines.
5
+ */
6
+ function parseJsonl(text) {
7
+ const events = [];
8
+ for (const line of text.split("\n")) {
9
+ const trimmed = line.trim();
10
+ if (!trimmed)
11
+ continue;
12
+ try {
13
+ events.push(JSON.parse(trimmed));
14
+ }
15
+ catch {
16
+ // Skip malformed lines
17
+ }
18
+ }
19
+ return events;
20
+ }
21
+ /**
22
+ * Extract text content from OpenClaw's content field.
23
+ * Content can be a string, an array of content blocks, or nested structures.
24
+ */
25
+ function extractText(content) {
26
+ if (typeof content === "string")
27
+ return content;
28
+ if (Array.isArray(content)) {
29
+ return content
30
+ .filter((block) => typeof block === "object" &&
31
+ block !== null &&
32
+ "type" in block &&
33
+ "text" in block &&
34
+ block.type === "text")
35
+ .map((block) => block.text)
36
+ .join("\n");
37
+ }
38
+ return "";
39
+ }
40
+ /**
41
+ * Strip base64 image data, replacing with a placeholder.
42
+ */
43
+ function stripBase64(text) {
44
+ return text.replace(BASE64_PATTERN, "[base64 image]");
45
+ }
46
+ /**
47
+ * Clean a raw JSONL transcript into a conversation suitable for Cortex ingestion.
48
+ *
49
+ * Strips:
50
+ * - System prompt messages (role: "system" / "developer")
51
+ * - Tool call/result events (tool_calls, tool role messages)
52
+ * - Base64 encoded images
53
+ * - Empty messages
54
+ *
55
+ * Preserves:
56
+ * - User messages with speaker attribution
57
+ * - Assistant messages with content
58
+ */
59
+ export function cleanTranscript(jsonlText) {
60
+ const events = parseJsonl(jsonlText);
61
+ const messages = [];
62
+ for (const event of events) {
63
+ const role = event.role;
64
+ if (!role)
65
+ continue;
66
+ // Skip system prompts
67
+ if (SYSTEM_ROLES.has(role))
68
+ continue;
69
+ // Skip tool call results
70
+ if (role === "tool" || event.tool_call_id)
71
+ continue;
72
+ // Skip messages that are purely tool calls with no text content
73
+ if (event.tool_calls && !event.content)
74
+ continue;
75
+ const text = extractText(event.content);
76
+ if (!text.trim())
77
+ continue;
78
+ const cleaned = stripBase64(text).trim();
79
+ if (!cleaned || cleaned === "[base64 image]")
80
+ continue;
81
+ messages.push({ role, content: cleaned });
82
+ }
83
+ return messages;
84
+ }
85
+ /**
86
+ * Clean only new JSONL lines (from an offset-based append).
87
+ * Returns cleaned messages and whether they're worth ingesting.
88
+ */
89
+ export function cleanTranscriptChunk(newLines) {
90
+ const messages = cleanTranscript(newLines);
91
+ // Worth ingesting if there's at least one user + one assistant message
92
+ const hasUser = messages.some((m) => m.role === "user" && m.content.length > 20);
93
+ const hasAssistant = messages.some((m) => m.role === "assistant" && m.content.length > 20);
94
+ return { messages, worthIngesting: hasUser && hasAssistant };
95
+ }
96
+ //# sourceMappingURL=transcript-cleaner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript-cleaner.js","sourceRoot":"","sources":["../../src/utils/transcript-cleaner.ts"],"names":[],"mappings":"AAiBA,MAAM,cAAc,GAAG,yCAAyC,CAAC;AACjE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;AAEtD;;GAEG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAoB,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,OAAgB;IACnC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,MAAM,CACL,CAAC,KAAK,EAA2C,EAAE,CACjD,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACd,MAAM,IAAI,KAAK;YACf,MAAM,IAAI,KAAK;YACf,KAAK,CAAC,IAAI,KAAK,MAAM,CACxB;aACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1B,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAE3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,IAAI;YAAE,SAAS;QAEpB,sBAAsB;QACtB,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAErC,yBAAyB;QACzB,IAAI,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,YAAY;YAAE,SAAS;QAEpD,gEAAgE;QAChE,IAAI,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,OAAO;YAAE,SAAS;QAEjD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAS;QAE3B,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,gBAAgB;YAAE,SAAS;QAEvD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IAInD,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAE3C,uEAAuE;IACvE,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACjF,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAE3F,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,IAAI,YAAY,EAAE,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,95 @@
1
+ {
2
+ "id": "cortex-memory",
3
+ "name": "Cortex Memory",
4
+ "description": "Long-term memory powered by Cortex — Auto-Recall, Auto-Capture, transcript ingestion, periodic consolidation, and background file sync",
5
+ "version": "0.2.0",
6
+ "kind": "memory",
7
+ "configSchema": {
8
+ "type": "object",
9
+ "required": ["apiKey"],
10
+ "properties": {
11
+ "apiKey": {
12
+ "type": "string",
13
+ "description": "Cortex API key"
14
+ },
15
+ "baseUrl": {
16
+ "type": "string",
17
+ "description": "Cortex API base URL",
18
+ "default": "https://q5p64iw9c9.execute-api.us-east-1.amazonaws.com/prod"
19
+ },
20
+ "autoRecall": {
21
+ "type": "boolean",
22
+ "description": "Inject relevant memories before each agent turn",
23
+ "default": true
24
+ },
25
+ "autoCapture": {
26
+ "type": "boolean",
27
+ "description": "Extract facts from conversations after agent turns",
28
+ "default": true
29
+ },
30
+ "recallTopK": {
31
+ "type": "number",
32
+ "description": "Number of memories to retrieve per turn",
33
+ "default": 5
34
+ },
35
+ "recallTimeoutMs": {
36
+ "type": "number",
37
+ "description": "Maximum time to wait for memory recall (ms)",
38
+ "default": 500
39
+ },
40
+ "recallMode": {
41
+ "type": "string",
42
+ "enum": ["fast", "balanced", "full"],
43
+ "description": "Retrieval depth — fast (BM25 + semantic), balanced (+ light reranking), full (+ graph traversal + reranker)",
44
+ "default": "fast"
45
+ },
46
+ "fileSync": {
47
+ "type": "boolean",
48
+ "description": "Watch MEMORY.md and daily logs for background ingestion",
49
+ "default": true
50
+ },
51
+ "transcriptSync": {
52
+ "type": "boolean",
53
+ "description": "Watch sessions/*.jsonl and ingest cleaned transcripts",
54
+ "default": true
55
+ },
56
+ "reflectIntervalMs": {
57
+ "type": "number",
58
+ "description": "Interval for periodic memory consolidation (ms). Set to 0 to disable.",
59
+ "default": 3600000
60
+ }
61
+ }
62
+ },
63
+ "uiHints": {
64
+ "apiKey": {
65
+ "label": "API Key",
66
+ "sensitive": true,
67
+ "placeholder": "sk-cortex-..."
68
+ },
69
+ "baseUrl": {
70
+ "label": "Base URL",
71
+ "advanced": true
72
+ },
73
+ "recallTopK": {
74
+ "label": "Recall Top-K",
75
+ "advanced": true
76
+ },
77
+ "recallTimeoutMs": {
78
+ "label": "Recall Timeout (ms)",
79
+ "advanced": true
80
+ },
81
+ "recallMode": {
82
+ "label": "Recall Mode",
83
+ "helpText": "Fast is best for low-latency auto-recall. Full enables graph traversal for deeper retrieval at higher latency."
84
+ },
85
+ "transcriptSync": {
86
+ "label": "Transcript Sync",
87
+ "helpText": "Automatically ingest session transcripts (sessions/*.jsonl) after stripping system prompts and tool JSON."
88
+ },
89
+ "reflectIntervalMs": {
90
+ "label": "Reflect Interval (ms)",
91
+ "advanced": true,
92
+ "helpText": "How often to consolidate memories (SUPERSEDES chains, contradiction detection). Set to 0 to disable."
93
+ }
94
+ }
95
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@ubundi/openclaw-cortex",
3
+ "version": "0.2.0",
4
+ "description": "OpenClaw plugin for Cortex memory — Auto-Recall, Auto-Capture, and file sync",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "openclaw": {
9
+ "extensions": ["./dist/index.js"]
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "test": "vitest run --exclude test/integration/**",
14
+ "test:watch": "vitest --exclude test/integration/**",
15
+ "test:integration": "vitest run -c vitest.integration.config.ts",
16
+ "clean": "rm -rf dist"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "openclaw.plugin.json"
21
+ ],
22
+ "keywords": [
23
+ "openclaw",
24
+ "openclaw-plugin",
25
+ "cortex",
26
+ "memory",
27
+ "long-term-memory"
28
+ ],
29
+ "license": "MIT",
30
+ "peerDependencies": {
31
+ "openclaw": ">=0.1.0"
32
+ },
33
+ "dependencies": {
34
+ "zod": "^3.23.0"
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.5.0",
38
+ "vitest": "^2.0.0",
39
+ "@types/node": "^20.0.0"
40
+ }
41
+ }