@usecortex_ai/openclaw-cortex-ai 0.0.1 → 0.1.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/tools/store.ts CHANGED
@@ -3,23 +3,32 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
3
3
  import type { CortexClient } from "../client.ts"
4
4
  import type { CortexPluginConfig } from "../config.ts"
5
5
  import { log } from "../log.ts"
6
- import { toSourceId } from "../session.ts"
6
+ import { extractAllTurns, filterIgnoredTurns } from "../messages.ts"
7
+ import { toToolSourceId } from "../session.ts"
8
+ import type { ConversationTurn } from "../types/cortex.ts"
9
+
10
+ const MAX_STORE_TURNS = 10
11
+
12
+ function removeInjectedBlocks(text: string): string {
13
+ return text.replace(/<cortex-context>[\s\S]*?<\/cortex-context>\s*/g, "").trim()
14
+ }
7
15
 
8
16
  export function registerStoreTool(
9
17
  api: OpenClawPluginApi,
10
18
  client: CortexClient,
11
- _cfg: CortexPluginConfig,
12
- getSessionKey: () => string | undefined,
19
+ cfg: CortexPluginConfig,
20
+ getSessionId: () => string | undefined,
21
+ getMessages: () => unknown[],
13
22
  ): void {
14
23
  api.registerTool(
15
24
  {
16
25
  name: "cortex_store",
17
26
  label: "Cortex Store",
18
27
  description:
19
- "Save important information to Cortex long-term memory. Use this to persist facts, preferences, or decisions the user wants remembered.",
28
+ "Save the full conversation history to Cortex AI memory. Use this to persist facts, preferences, or decisions the user wants remembered. The complete chat history will be sent for context-rich storage.",
20
29
  parameters: Type.Object({
21
30
  text: Type.String({
22
- description: "The information to store in memory",
31
+ description: "A brief summary or note about what is being saved",
23
32
  }),
24
33
  title: Type.Optional(
25
34
  Type.String({
@@ -31,10 +40,60 @@ export function registerStoreTool(
31
40
  _toolCallId: string,
32
41
  params: { text: string; title?: string },
33
42
  ) {
34
- const sk = getSessionKey()
35
- const sourceId = sk ? toSourceId(sk) : undefined
43
+ const sid = getSessionId()
44
+ const sourceId = sid ? toToolSourceId(sid) : undefined
45
+ const messages = getMessages()
46
+
47
+ log.debug(`[store] tool called — sid=${sid ?? "none"} msgs=${messages.length} text="${params.text.slice(0, 50)}"`)
48
+
49
+ const rawTurns = extractAllTurns(messages)
50
+ const filteredTurns = filterIgnoredTurns(rawTurns, cfg.ignoreTerm)
51
+ const recentTurns = filteredTurns.slice(-MAX_STORE_TURNS)
52
+ const turns: ConversationTurn[] = recentTurns.map((t) => ({
53
+ user: removeInjectedBlocks(t.user),
54
+ assistant: removeInjectedBlocks(t.assistant),
55
+ }))
56
+
57
+ log.debug(`[store] extracted ${rawTurns.length} total turns, ${rawTurns.length - filteredTurns.length} ignored, using last ${turns.length} (MAX_STORE_TURNS=${MAX_STORE_TURNS})`)
58
+
59
+ if (turns.length > 0 && sourceId) {
60
+ const now = new Date()
61
+ const readableTime = now.toLocaleString("en-US", {
62
+ weekday: "short",
63
+ year: "numeric",
64
+ month: "short",
65
+ day: "numeric",
66
+ hour: "2-digit",
67
+ minute: "2-digit",
68
+ timeZoneName: "short",
69
+ })
36
70
 
37
- log.debug(`store tool: "${params.text.slice(0, 50)}" - \nsourceId: ${sourceId}`)
71
+ const annotatedTurns = turns.map((t, i) => ({
72
+ user: i === 0 ? `[Temporal details: ${readableTime}]\n\n${t.user}` : t.user,
73
+ assistant: t.assistant,
74
+ }))
75
+
76
+ log.debug(`[store] ingesting ${annotatedTurns.length} conversation turns -> ${sourceId}`)
77
+
78
+ await client.ingestConversation(annotatedTurns, sourceId, {
79
+ metadata: {
80
+ captured_at: now.toISOString(),
81
+ source: "openclaw_tool",
82
+ note: params.text,
83
+ },
84
+ })
85
+
86
+ return {
87
+ content: [
88
+ {
89
+ type: "text" as const,
90
+ text: `Saved ${annotatedTurns.length} conversation turns to Cortex (${sourceId}). Note: "${params.text.length > 80 ? `${params.text.slice(0, 80)}…` : params.text}"`,
91
+ },
92
+ ],
93
+ }
94
+ }
95
+
96
+ log.debug("[store] no conversation turns found, falling back to text ingestion")
38
97
 
39
98
  await client.ingestText(params.text, {
40
99
  sourceId,
@@ -42,16 +101,11 @@ export function registerStoreTool(
42
101
  infer: true,
43
102
  })
44
103
 
45
- const preview =
46
- params.text.length > 80
47
- ? `${params.text.slice(0, 80)}…`
48
- : params.text
49
-
50
104
  return {
51
105
  content: [
52
106
  {
53
107
  type: "text" as const,
54
- text: `Saved to Cortex: "${preview}"`,
108
+ text: `Saved to Cortex: "${params.text.length > 80 ? `${params.text.slice(0, 80)}…` : params.text}"`,
55
109
  },
56
110
  ],
57
111
  }
package/types/cortex.ts CHANGED
@@ -13,6 +13,8 @@ export type MemoryPayload = {
13
13
  source_id?: string
14
14
  title?: string
15
15
  expiry_time?: number
16
+ document_metadata?: string
17
+ tenant_metadata?: string
16
18
  }
17
19
 
18
20
  export type AddMemoryRequest = {