@ubundi/openclaw-cortex 0.7.2 → 1.0.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 (52) hide show
  1. package/README.md +24 -4
  2. package/dist/adapters/cortex/client.d.ts +1 -7
  3. package/dist/adapters/cortex/client.d.ts.map +1 -1
  4. package/dist/adapters/cortex/client.js +2 -4
  5. package/dist/adapters/cortex/client.js.map +1 -1
  6. package/dist/core/config/schema.d.ts +61 -0
  7. package/dist/core/config/schema.d.ts.map +1 -0
  8. package/dist/core/config/schema.js +39 -0
  9. package/dist/core/config/schema.js.map +1 -0
  10. package/dist/core/plugin.d.ts +61 -0
  11. package/dist/core/plugin.d.ts.map +1 -0
  12. package/dist/core/plugin.js +147 -0
  13. package/dist/core/plugin.js.map +1 -0
  14. package/dist/cortex/client.d.ts +142 -0
  15. package/dist/cortex/client.d.ts.map +1 -0
  16. package/dist/cortex/client.js +145 -0
  17. package/dist/cortex/client.js.map +1 -0
  18. package/dist/features/capture/handler.d.ts +1 -1
  19. package/dist/features/capture/handler.d.ts.map +1 -1
  20. package/dist/features/capture/handler.js +6 -30
  21. package/dist/features/capture/handler.js.map +1 -1
  22. package/dist/features/reflect/service.d.ts +19 -0
  23. package/dist/features/reflect/service.d.ts.map +1 -0
  24. package/dist/features/reflect/service.js +38 -0
  25. package/dist/features/reflect/service.js.map +1 -0
  26. package/dist/internal/identity/api-key.js +1 -1
  27. package/dist/plugin/config/schema.d.ts +0 -5
  28. package/dist/plugin/config/schema.d.ts.map +1 -1
  29. package/dist/plugin/config/schema.js +1 -2
  30. package/dist/plugin/config/schema.js.map +1 -1
  31. package/dist/plugin/index.d.ts +0 -2
  32. package/dist/plugin/index.d.ts.map +1 -1
  33. package/dist/plugin/index.js +9 -25
  34. package/dist/plugin/index.js.map +1 -1
  35. package/dist/shared/fs/safe-path.d.ts +8 -0
  36. package/dist/shared/fs/safe-path.d.ts.map +1 -0
  37. package/dist/shared/fs/safe-path.js +32 -0
  38. package/dist/shared/fs/safe-path.js.map +1 -0
  39. package/dist/shared/metrics/latency-metrics.d.ts +19 -0
  40. package/dist/shared/metrics/latency-metrics.d.ts.map +1 -0
  41. package/dist/shared/metrics/latency-metrics.js +45 -0
  42. package/dist/shared/metrics/latency-metrics.js.map +1 -0
  43. package/dist/shared/queue/retry-queue.d.ts +31 -0
  44. package/dist/shared/queue/retry-queue.d.ts.map +1 -0
  45. package/dist/shared/queue/retry-queue.js +115 -0
  46. package/dist/shared/queue/retry-queue.js.map +1 -0
  47. package/dist/shared/transcript/cleaner.d.ts +24 -0
  48. package/dist/shared/transcript/cleaner.d.ts.map +1 -0
  49. package/dist/shared/transcript/cleaner.js +96 -0
  50. package/dist/shared/transcript/cleaner.js.map +1 -0
  51. package/openclaw.plugin.json +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,32 @@
1
+ import { realpath, lstat } from "node:fs/promises";
2
+ /**
3
+ * Resolves a file path and verifies it stays within the allowed root directory.
4
+ * Rejects symlinks and path traversal attempts.
5
+ *
6
+ * Returns the canonical path if safe, or null if the path should be rejected.
7
+ */
8
+ export async function safePath(filePath, allowedRoot) {
9
+ try {
10
+ // Check for symlinks before resolving
11
+ const stats = await lstat(filePath);
12
+ if (stats.isSymbolicLink()) {
13
+ return null;
14
+ }
15
+ // Resolve to canonical path (resolves .., ., etc.)
16
+ const canonical = await realpath(filePath);
17
+ const canonicalRoot = await realpath(allowedRoot);
18
+ // Ensure the resolved path is within the allowed root
19
+ const normalizedRoot = canonicalRoot.endsWith("/")
20
+ ? canonicalRoot
21
+ : canonicalRoot + "/";
22
+ if (canonical !== canonicalRoot && !canonical.startsWith(normalizedRoot)) {
23
+ return null;
24
+ }
25
+ return canonical;
26
+ }
27
+ catch {
28
+ // File doesn't exist or is inaccessible
29
+ return null;
30
+ }
31
+ }
32
+ //# sourceMappingURL=safe-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-path.js","sourceRoot":"","sources":["../../../src/shared/fs/safe-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAgB,EAChB,WAAmB;IAEnB,IAAI,CAAC;QACH,sCAAsC;QACtC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,mDAAmD;QACnD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAElD,sDAAsD;QACtD,MAAM,cAAc,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC;YAChD,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,aAAa,GAAG,GAAG,CAAC;QAExB,IAAI,SAAS,KAAK,aAAa,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YACzE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;AACH,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=latency-metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latency-metrics.d.ts","sourceRoot":"","sources":["../../../src/shared/metrics/latency-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=latency-metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latency-metrics.js","sourceRoot":"","sources":["../../../src/shared/metrics/latency-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,31 @@
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 maxCapacity;
17
+ private tasksById;
18
+ private taskOrder;
19
+ private timer;
20
+ private taskCounter;
21
+ private isFlushing;
22
+ constructor(logger: Logger, maxRetries?: number, maxCapacity?: number);
23
+ start(): void;
24
+ stop(): void;
25
+ enqueue(execute: () => Promise<void>, label?: string): void;
26
+ get pending(): number;
27
+ private flush;
28
+ private remove;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=retry-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-queue.d.ts","sourceRoot":"","sources":["../../../src/shared/queue/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;AAQD,qBAAa,UAAU;IAQnB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,WAAW;IATrB,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAS;gBAGjB,MAAM,EAAE,MAAM,EACd,UAAU,SAAc,EACxB,WAAW,SAAe;IAGpC,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;IAcZ,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAmC3D,IAAI,OAAO,IAAI,MAAM,CAEpB;YAEa,KAAK;IAwCnB,OAAO,CAAC,MAAM;CAOf"}
@@ -0,0 +1,115 @@
1
+ const BASE_DELAY_MS = 1000;
2
+ const MAX_DELAY_MS = 60_000;
3
+ const MAX_RETRIES = 5;
4
+ const MAX_CAPACITY = 100;
5
+ const FLUSH_INTERVAL_MS = 5000;
6
+ export class RetryQueue {
7
+ logger;
8
+ maxRetries;
9
+ maxCapacity;
10
+ tasksById = new Map();
11
+ taskOrder = [];
12
+ timer = null;
13
+ taskCounter = 0;
14
+ isFlushing = false;
15
+ constructor(logger, maxRetries = MAX_RETRIES, maxCapacity = MAX_CAPACITY) {
16
+ this.logger = logger;
17
+ this.maxRetries = maxRetries;
18
+ this.maxCapacity = maxCapacity;
19
+ }
20
+ start() {
21
+ if (this.timer)
22
+ return;
23
+ this.timer = setInterval(() => {
24
+ void this.flush();
25
+ }, FLUSH_INTERVAL_MS);
26
+ }
27
+ stop() {
28
+ if (this.timer) {
29
+ clearInterval(this.timer);
30
+ this.timer = null;
31
+ }
32
+ const pendingCount = this.pending;
33
+ if (pendingCount > 0) {
34
+ this.logger.warn(`Retry queue stopped with ${pendingCount} pending tasks`);
35
+ }
36
+ this.tasksById.clear();
37
+ this.taskOrder = [];
38
+ this.isFlushing = false;
39
+ }
40
+ enqueue(execute, label) {
41
+ const id = label ?? `task-${++this.taskCounter}`;
42
+ // Deduplicate: if a task with the same label exists, replace it
43
+ const existing = this.tasksById.get(id);
44
+ if (existing) {
45
+ existing.execute = execute;
46
+ existing.retries = 0;
47
+ existing.nextAttemptAt = Date.now();
48
+ this.logger.debug?.(`Retry queue: deduplicated ${id}`);
49
+ return;
50
+ }
51
+ // Capacity check: drop oldest task if at limit
52
+ if (this.pending >= this.maxCapacity) {
53
+ const droppedId = this.taskOrder.shift();
54
+ const dropped = droppedId ? this.tasksById.get(droppedId) : undefined;
55
+ if (droppedId) {
56
+ this.tasksById.delete(droppedId);
57
+ }
58
+ this.logger.warn(`Retry queue: at capacity (${this.maxCapacity}), dropped oldest task ${dropped?.id ?? "unknown"}`);
59
+ }
60
+ this.tasksById.set(id, {
61
+ id,
62
+ execute,
63
+ retries: 0,
64
+ nextAttemptAt: Date.now(),
65
+ });
66
+ this.taskOrder.push(id);
67
+ this.logger.debug?.(`Retry queue: enqueued ${id}`);
68
+ }
69
+ get pending() {
70
+ return this.tasksById.size;
71
+ }
72
+ async flush() {
73
+ if (this.isFlushing)
74
+ return;
75
+ this.isFlushing = true;
76
+ const now = Date.now();
77
+ const ids = [...this.taskOrder];
78
+ try {
79
+ for (const id of ids) {
80
+ const task = this.tasksById.get(id);
81
+ if (!task || task.nextAttemptAt > now)
82
+ continue;
83
+ try {
84
+ await task.execute();
85
+ this.remove(id);
86
+ this.logger.debug?.(`Retry queue: ${task.id} succeeded`);
87
+ }
88
+ catch (err) {
89
+ task.retries++;
90
+ if (task.retries >= this.maxRetries) {
91
+ this.remove(id);
92
+ this.logger.warn(`Retry queue: ${task.id} failed after ${this.maxRetries} retries, dropping: ${String(err)}`);
93
+ }
94
+ else {
95
+ const delay = Math.min(BASE_DELAY_MS * Math.pow(2, task.retries), MAX_DELAY_MS);
96
+ task.nextAttemptAt = Date.now() + delay;
97
+ this.logger.debug?.(`Retry queue: ${task.id} retry ${task.retries}/${this.maxRetries} in ${delay}ms`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ finally {
103
+ this.isFlushing = false;
104
+ }
105
+ }
106
+ remove(id) {
107
+ if (!this.tasksById.delete(id))
108
+ return;
109
+ const idx = this.taskOrder.indexOf(id);
110
+ if (idx !== -1) {
111
+ this.taskOrder.splice(idx, 1);
112
+ }
113
+ }
114
+ }
115
+ //# sourceMappingURL=retry-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-queue.js","sourceRoot":"","sources":["../../../src/shared/queue/retry-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ConversationMessage } from "../../cortex/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=cleaner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleaner.d.ts","sourceRoot":"","sources":["../../../src/shared/transcript/cleaner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAkElE;;;;;;;;;;;;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=cleaner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleaner.js","sourceRoot":"","sources":["../../../src/shared/transcript/cleaner.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-cortex",
3
3
  "name": "Cortex Memory",
4
4
  "description": "Long-term memory powered by Cortex — Auto-Recall, Auto-Capture, agent tools, /memories command, transcript ingestion, and background file sync",
5
- "version": "0.7.2",
5
+ "version": "1.0.0",
6
6
  "kind": "memory",
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubundi/openclaw-cortex",
3
- "version": "0.7.2",
3
+ "version": "1.0.0",
4
4
  "description": "OpenClaw plugin for Cortex memory — Auto-Recall, Auto-Capture, and file sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",