@keyoku/openclaw 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 (66) hide show
  1. package/dist/capture.d.ts +23 -0
  2. package/dist/capture.d.ts.map +1 -0
  3. package/dist/capture.js +114 -0
  4. package/dist/capture.js.map +1 -0
  5. package/dist/cli.d.ts +8 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +71 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.d.ts +28 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +19 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/context.d.ts +22 -0
  14. package/dist/context.d.ts.map +1 -0
  15. package/dist/context.js +136 -0
  16. package/dist/context.js.map +1 -0
  17. package/dist/heartbeat-setup.d.ts +10 -0
  18. package/dist/heartbeat-setup.d.ts.map +1 -0
  19. package/dist/heartbeat-setup.js +49 -0
  20. package/dist/heartbeat-setup.js.map +1 -0
  21. package/dist/hooks.d.ts +10 -0
  22. package/dist/hooks.d.ts.map +1 -0
  23. package/dist/hooks.js +152 -0
  24. package/dist/hooks.js.map +1 -0
  25. package/dist/incremental-capture.d.ts +24 -0
  26. package/dist/incremental-capture.d.ts.map +1 -0
  27. package/dist/incremental-capture.js +81 -0
  28. package/dist/incremental-capture.js.map +1 -0
  29. package/dist/index.d.ts +24 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +54 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/migration.d.ts +29 -0
  34. package/dist/migration.d.ts.map +1 -0
  35. package/dist/migration.js +203 -0
  36. package/dist/migration.js.map +1 -0
  37. package/dist/service.d.ts +7 -0
  38. package/dist/service.d.ts.map +1 -0
  39. package/dist/service.js +133 -0
  40. package/dist/service.js.map +1 -0
  41. package/dist/tools.d.ts +11 -0
  42. package/dist/tools.d.ts.map +1 -0
  43. package/dist/tools.js +188 -0
  44. package/dist/tools.js.map +1 -0
  45. package/dist/types.d.ts +55 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +8 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +31 -0
  50. package/src/capture.ts +116 -0
  51. package/src/cli.ts +95 -0
  52. package/src/config.ts +43 -0
  53. package/src/context.ts +164 -0
  54. package/src/heartbeat-setup.ts +53 -0
  55. package/src/hooks.ts +175 -0
  56. package/src/incremental-capture.ts +88 -0
  57. package/src/index.ts +68 -0
  58. package/src/migration.ts +241 -0
  59. package/src/service.ts +145 -0
  60. package/src/tools.ts +239 -0
  61. package/src/types.ts +40 -0
  62. package/test/capture.test.ts +139 -0
  63. package/test/context.test.ts +273 -0
  64. package/test/hooks.test.ts +137 -0
  65. package/test/tools.test.ts +174 -0
  66. package/tsconfig.json +8 -0
package/dist/hooks.js ADDED
@@ -0,0 +1,152 @@
1
+ /**
2
+ * OpenClaw lifecycle hook registrations.
3
+ * - before_prompt_build: auto-recall + heartbeat context fusion
4
+ * - agent_end: auto-capture memorable facts
5
+ */
6
+ import { formatMemoryContext, formatHeartbeatContext } from './context.js';
7
+ /**
8
+ * Extract a summary of recent activity from conversation messages.
9
+ * Takes the last N user and assistant messages and builds a query string
10
+ * that represents what the agent has been doing.
11
+ */
12
+ function summarizeRecentActivity(messages, maxMessages = 6) {
13
+ if (!Array.isArray(messages) || messages.length === 0)
14
+ return '';
15
+ const recent = messages.slice(-maxMessages);
16
+ const parts = [];
17
+ for (const msg of recent) {
18
+ const m = msg;
19
+ if (!m.role || !m.content)
20
+ continue;
21
+ let text = '';
22
+ if (typeof m.content === 'string') {
23
+ text = m.content;
24
+ }
25
+ else if (Array.isArray(m.content)) {
26
+ // Anthropic format: content blocks
27
+ text = m.content
28
+ .filter((b) => b.type === 'text' && b.text)
29
+ .map((b) => b.text)
30
+ .join(' ');
31
+ }
32
+ if (!text)
33
+ continue;
34
+ // Truncate long messages to keep the query focused
35
+ const truncated = text.length > 300 ? text.slice(0, 300) : text;
36
+ if (m.role === 'user') {
37
+ parts.push(`User: ${truncated}`);
38
+ }
39
+ else if (m.role === 'assistant') {
40
+ parts.push(`Assistant: ${truncated}`);
41
+ }
42
+ }
43
+ return parts.join('\n');
44
+ }
45
+ // Idle check-in: track consecutive quiet heartbeats.
46
+ // After N quiet beats, force the LLM to engage with the user.
47
+ const IDLE_CHECK_IN_INTERVAL = 3; // every 3 quiet beats (~15 min with 5m heartbeat)
48
+ let quietHeartbeatCount = 0;
49
+ export function registerHooks(api, client, entityId, agentId, config) {
50
+ // before_prompt_build: auto-recall + heartbeat context injection
51
+ if (config.autoRecall || config.heartbeat) {
52
+ api.on('before_prompt_build', async (event) => {
53
+ const ev = event;
54
+ if (!ev.prompt || ev.prompt.length < 5)
55
+ return;
56
+ const isHeartbeat = ev.prompt.includes('HEARTBEAT');
57
+ // Heartbeat path: use heartbeat context endpoint with LLM analysis
58
+ if (isHeartbeat && config.heartbeat) {
59
+ const activitySummary = summarizeRecentActivity(ev.messages ?? []);
60
+ try {
61
+ const ctx = await client.heartbeatContext(entityId, {
62
+ query: activitySummary || undefined,
63
+ top_k: config.topK,
64
+ min_score: 0.1,
65
+ agent_id: agentId,
66
+ max_results: 10,
67
+ analyze: true,
68
+ activity_summary: activitySummary || undefined,
69
+ autonomy: config.autonomy,
70
+ });
71
+ // Idle check-in: if nothing to act on, count quiet beats
72
+ if (!ctx.should_act) {
73
+ quietHeartbeatCount++;
74
+ api.logger.info?.(`keyoku: quiet heartbeat ${quietHeartbeatCount}/${IDLE_CHECK_IN_INTERVAL}`);
75
+ if (quietHeartbeatCount >= IDLE_CHECK_IN_INTERVAL) {
76
+ quietHeartbeatCount = 0;
77
+ // Build a check-in signal with any known memories
78
+ const memoryHints = ctx.relevant_memories
79
+ .slice(0, 3)
80
+ .map((r) => r.memory.content)
81
+ .join('; ');
82
+ const checkInBlock = [
83
+ '<heartbeat-signals>',
84
+ '## Action Brief',
85
+ 'It has been a while since you last reached out. Send the user a brief, friendly message right now.',
86
+ '',
87
+ '## Tell the User',
88
+ memoryHints
89
+ ? `Hey! Just checking in. Reference what you know about them: ${memoryHints}`
90
+ : 'Hey! Just wanted to check in — how are things going?',
91
+ '',
92
+ '## Execute These Actions',
93
+ '- Send a short, warm greeting to the user',
94
+ '',
95
+ 'should_act: true',
96
+ `Urgency: low | Mode: ${config.autonomy}`,
97
+ '</heartbeat-signals>',
98
+ ];
99
+ api.logger.info?.('keyoku: idle check-in triggered — forcing engagement');
100
+ return { prependContext: checkInBlock.join('\n') };
101
+ }
102
+ }
103
+ else {
104
+ // Active heartbeat resets the quiet counter
105
+ quietHeartbeatCount = 0;
106
+ }
107
+ const formatted = formatHeartbeatContext(ctx);
108
+ if (formatted) {
109
+ const analyzed = ctx.analysis ? ` [${ctx.analysis.autonomy}/${ctx.analysis.urgency}]` : '';
110
+ api.logger.info?.(`keyoku: heartbeat context injected (should_act: ${ctx.should_act}, memories: ${ctx.relevant_memories.length}${analyzed})`);
111
+ return { prependContext: formatted };
112
+ }
113
+ }
114
+ catch (err) {
115
+ api.logger.warn(`keyoku: heartbeat context failed: ${String(err)}`);
116
+ }
117
+ return;
118
+ }
119
+ // Auto-recall path: search memories relevant to user's prompt + recent context
120
+ if (config.autoRecall && !isHeartbeat) {
121
+ try {
122
+ // Build a richer query: user prompt + last assistant message for context
123
+ const recentContext = summarizeRecentActivity(ev.messages ?? [], 2);
124
+ const query = recentContext
125
+ ? `${ev.prompt}\n\nRecent context:\n${recentContext}`
126
+ : ev.prompt;
127
+ api.logger.info?.(`keyoku: auto-recall searching (query: ${query.slice(0, 80)}...)`);
128
+ const results = await client.search(entityId, query, {
129
+ limit: config.topK,
130
+ min_score: 0.15,
131
+ });
132
+ if (results.length > 0) {
133
+ const formatted = formatMemoryContext(results);
134
+ api.logger.info?.(`keyoku: auto-recall injected ${results.length} memories`);
135
+ return { prependContext: formatted };
136
+ }
137
+ else {
138
+ api.logger.info?.('keyoku: auto-recall found 0 matching memories');
139
+ }
140
+ }
141
+ catch (err) {
142
+ api.logger.warn(`keyoku: auto-recall failed: ${String(err)}`);
143
+ }
144
+ }
145
+ });
146
+ }
147
+ // NOTE: agent_end capture removed — incremental capture (incremental-capture.ts)
148
+ // now handles both user and assistant messages in real-time, making the
149
+ // session-end batch capture redundant. This also eliminates the only source
150
+ // of duplicate /remember calls.
151
+ }
152
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Incremental per-message memory capture.
3
+ *
4
+ * Strategy: capture the user+assistant exchange as a PAIR, not separately.
5
+ * This gives Keyoku the full context to extract meaningful memories:
6
+ * "User asked about X → Agent decided Y because Z"
7
+ * instead of fragmented, context-free snippets.
8
+ *
9
+ * Flow:
10
+ * 1. `before_prompt_build` — stash the user's prompt (no /remember call yet)
11
+ * 2. `message_sent` — pair the stashed prompt with the assistant's response,
12
+ * send the combined exchange to Keyoku's /remember endpoint ONCE.
13
+ *
14
+ * Keyoku's engine then:
15
+ * - Extracts discrete facts from the full exchange
16
+ * - Deduplicates against existing memories (hash + semantic)
17
+ * - Detects and resolves conflicts
18
+ * - Stores only genuinely new information
19
+ */
20
+ import type { KeyokuClient } from '@keyoku/memory';
21
+ import type { KeyokuConfig } from './config.js';
22
+ import type { PluginApi } from './types.js';
23
+ export declare function registerIncrementalCapture(api: PluginApi, client: KeyokuClient, entityId: string, agentId: string, config: Required<KeyokuConfig>): void;
24
+ //# sourceMappingURL=incremental-capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-capture.d.ts","sourceRoot":"","sources":["../src/incremental-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,GAC7B,IAAI,CAwDN"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Incremental per-message memory capture.
3
+ *
4
+ * Strategy: capture the user+assistant exchange as a PAIR, not separately.
5
+ * This gives Keyoku the full context to extract meaningful memories:
6
+ * "User asked about X → Agent decided Y because Z"
7
+ * instead of fragmented, context-free snippets.
8
+ *
9
+ * Flow:
10
+ * 1. `before_prompt_build` — stash the user's prompt (no /remember call yet)
11
+ * 2. `message_sent` — pair the stashed prompt with the assistant's response,
12
+ * send the combined exchange to Keyoku's /remember endpoint ONCE.
13
+ *
14
+ * Keyoku's engine then:
15
+ * - Extracts discrete facts from the full exchange
16
+ * - Deduplicates against existing memories (hash + semantic)
17
+ * - Detects and resolves conflicts
18
+ * - Stores only genuinely new information
19
+ */
20
+ import { looksLikePromptInjection } from './capture.js';
21
+ export function registerIncrementalCapture(api, client, entityId, agentId, config) {
22
+ // Stash for the most recent user prompt, paired with the next assistant response
23
+ let pendingUserPrompt = null;
24
+ // Step 1: Stash user prompt (no API call yet)
25
+ api.on('before_prompt_build', async (event) => {
26
+ const ev = event;
27
+ if (!ev.prompt || ev.prompt.length < 10)
28
+ return;
29
+ // Don't stash heartbeat prompts or injected blocks
30
+ if (ev.prompt.includes('HEARTBEAT'))
31
+ return;
32
+ if (ev.prompt.includes('<your-memories>') || ev.prompt.includes('<heartbeat-signals>'))
33
+ return;
34
+ if (ev.prompt.length > config.captureMaxChars)
35
+ return;
36
+ if (looksLikePromptInjection(ev.prompt))
37
+ return;
38
+ pendingUserPrompt = ev.prompt;
39
+ }, { priority: -10 }); // Low priority — runs after auto-recall
40
+ // Step 2: Pair with assistant response and send to Keyoku
41
+ api.on('message_sent', async (event) => {
42
+ const ev = event;
43
+ if (!ev.success || !ev.content)
44
+ return;
45
+ const assistantContent = ev.content;
46
+ // Skip noise
47
+ if (assistantContent.length < 20)
48
+ return;
49
+ if (assistantContent === 'HEARTBEAT_OK' || assistantContent === 'NO_REPLY')
50
+ return;
51
+ if (assistantContent.includes('<heartbeat-signals>') || assistantContent.includes('<your-memories>'))
52
+ return;
53
+ if (looksLikePromptInjection(assistantContent))
54
+ return;
55
+ // Build the exchange: user prompt + assistant response
56
+ let exchange;
57
+ if (pendingUserPrompt) {
58
+ exchange = `User: ${pendingUserPrompt}\n\nAssistant: ${assistantContent}`;
59
+ pendingUserPrompt = null; // consumed
60
+ }
61
+ else {
62
+ // No user prompt stashed (e.g., tool-triggered response) — just capture assistant
63
+ exchange = assistantContent;
64
+ }
65
+ // Truncate if the combined exchange is too long
66
+ if (exchange.length > config.captureMaxChars) {
67
+ exchange = exchange.slice(0, config.captureMaxChars);
68
+ }
69
+ try {
70
+ await client.remember(entityId, exchange, {
71
+ agent_id: agentId,
72
+ source: 'conversation',
73
+ });
74
+ api.logger.debug?.(`keyoku: captured exchange (${exchange.length} chars)`);
75
+ }
76
+ catch (err) {
77
+ api.logger.warn(`keyoku: capture failed: ${String(err)}`);
78
+ }
79
+ });
80
+ }
81
+ //# sourceMappingURL=incremental-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"incremental-capture.js","sourceRoot":"","sources":["../src/incremental-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAGxD,MAAM,UAAU,0BAA0B,CACxC,GAAc,EACd,MAAoB,EACpB,QAAgB,EAChB,OAAe,EACf,MAA8B;IAE9B,iFAAiF;IACjF,IAAI,iBAAiB,GAAkB,IAAI,CAAC;IAE5C,8CAA8C;IAC9C,GAAG,CAAC,EAAE,CAAC,qBAAqB,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE;QACrD,MAAM,EAAE,GAAG,KAA4B,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO;QAEhD,mDAAmD;QACnD,IAAI,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO;QAC5C,IAAI,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YAAE,OAAO;QAC/F,IAAI,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,eAAe;YAAE,OAAO;QACtD,IAAI,wBAAwB,CAAC,EAAE,CAAC,MAAM,CAAC;YAAE,OAAO;QAEhD,iBAAiB,GAAG,EAAE,CAAC,MAAM,CAAC;IAChC,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,wCAAwC;IAE/D,0DAA0D;IAC1D,GAAG,CAAC,EAAE,CAAC,cAAc,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE;QAC9C,MAAM,EAAE,GAAG,KAAgD,CAAC;QAC5D,IAAI,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC,OAAO;YAAE,OAAO;QAEvC,MAAM,gBAAgB,GAAG,EAAE,CAAC,OAAO,CAAC;QAEpC,aAAa;QACb,IAAI,gBAAgB,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO;QACzC,IAAI,gBAAgB,KAAK,cAAc,IAAI,gBAAgB,KAAK,UAAU;YAAE,OAAO;QACnF,IAAI,gBAAgB,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAAE,OAAO;QAC7G,IAAI,wBAAwB,CAAC,gBAAgB,CAAC;YAAE,OAAO;QAEvD,uDAAuD;QACvD,IAAI,QAAgB,CAAC;QACrB,IAAI,iBAAiB,EAAE,CAAC;YACtB,QAAQ,GAAG,SAAS,iBAAiB,kBAAkB,gBAAgB,EAAE,CAAC;YAC1E,iBAAiB,GAAG,IAAI,CAAC,CAAC,WAAW;QACvC,CAAC;aAAM,CAAC;YACN,kFAAkF;YAClF,QAAQ,GAAG,gBAAgB,CAAC;QAC9B,CAAC;QAED,gDAAgD;QAChD,IAAI,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;YAC7C,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE;gBACxC,QAAQ,EAAE,OAAO;gBACjB,MAAM,EAAE,cAAc;aACvB,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,8BAA8B,QAAQ,CAAC,MAAM,SAAS,CAAC,CAAC;QAC7E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @keyoku/openclaw — Keyoku Memory Plugin for OpenClaw
3
+ *
4
+ * Gives any OpenClaw agent persistent memory, proactive heartbeat behavior,
5
+ * and scheduling — powered by the Keyoku memory engine.
6
+ *
7
+ * Usage:
8
+ * import keyokuMemory from '@keyoku/openclaw';
9
+ * // In openclaw config:
10
+ * plugins: { 'keyoku-memory': keyokuMemory({ autoRecall: true }) }
11
+ * slots: { memory: 'keyoku-memory' }
12
+ */
13
+ import { type KeyokuConfig } from './config.js';
14
+ import type { PluginApi } from './types.js';
15
+ export type { KeyokuConfig } from './config.js';
16
+ export { KeyokuClient } from '@keyoku/memory';
17
+ export default function keyokuMemory(config?: KeyokuConfig): {
18
+ id: string;
19
+ name: string;
20
+ description: string;
21
+ kind: "memory";
22
+ register(api: PluginApi): void;
23
+ };
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,aAAa,CAAC;AAO/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAM,CAAC,EAAE,YAAY;;;;;kBAOxC,SAAS;EAkC1B"}
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @keyoku/openclaw — Keyoku Memory Plugin for OpenClaw
3
+ *
4
+ * Gives any OpenClaw agent persistent memory, proactive heartbeat behavior,
5
+ * and scheduling — powered by the Keyoku memory engine.
6
+ *
7
+ * Usage:
8
+ * import keyokuMemory from '@keyoku/openclaw';
9
+ * // In openclaw config:
10
+ * plugins: { 'keyoku-memory': keyokuMemory({ autoRecall: true }) }
11
+ * slots: { memory: 'keyoku-memory' }
12
+ */
13
+ import { KeyokuClient } from '@keyoku/memory';
14
+ import { resolveConfig } from './config.js';
15
+ import { registerTools } from './tools.js';
16
+ import { registerHooks } from './hooks.js';
17
+ import { registerService } from './service.js';
18
+ import { registerCli } from './cli.js';
19
+ import { registerIncrementalCapture } from './incremental-capture.js';
20
+ import { ensureHeartbeatMd } from './heartbeat-setup.js';
21
+ export { KeyokuClient } from '@keyoku/memory';
22
+ export default function keyokuMemory(config) {
23
+ return {
24
+ id: 'keyoku-memory',
25
+ name: 'Keyoku Memory',
26
+ description: 'Persistent memory, heartbeat enhancement, and scheduling powered by Keyoku',
27
+ kind: 'memory',
28
+ register(api) {
29
+ const cfg = resolveConfig(config);
30
+ // Resolve entity/agent IDs — fall back to plugin API id
31
+ const entityId = cfg.entityId || api.id || 'default';
32
+ const agentId = cfg.agentId || api.id || 'default';
33
+ const client = new KeyokuClient({ baseUrl: cfg.keyokuUrl });
34
+ api.logger.info(`keyoku: plugin registered (url: ${cfg.keyokuUrl}, entity: ${entityId})`);
35
+ // Register 6 memory/schedule tools
36
+ registerTools(api, client, entityId, agentId);
37
+ // Register lifecycle hooks (auto-recall, heartbeat, auto-capture)
38
+ registerHooks(api, client, entityId, agentId, cfg);
39
+ // Register Keyoku binary lifecycle service
40
+ registerService(api, cfg.keyokuUrl);
41
+ // Register CLI subcommands
42
+ registerCli(api, client, entityId);
43
+ // Register incremental per-message capture
44
+ if (cfg.incrementalCapture) {
45
+ registerIncrementalCapture(api, client, entityId, agentId, cfg);
46
+ }
47
+ // Auto-generate HEARTBEAT.md if heartbeat is enabled and file doesn't exist
48
+ if (cfg.heartbeat) {
49
+ ensureHeartbeatMd(api);
50
+ }
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAqB,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIzD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,MAAqB;IACxD,OAAO;QACL,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE,4EAA4E;QACzF,IAAI,EAAE,QAAiB;QAEvB,QAAQ,CAAC,GAAc;YACrB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YAElC,wDAAwD;YACxD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;YACrD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,EAAE,IAAI,SAAS,CAAC;YAEnD,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;YAE5D,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,GAAG,CAAC,SAAS,aAAa,QAAQ,GAAG,CAAC,CAAC;YAE1F,mCAAmC;YACnC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAE9C,kEAAkE;YAClE,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAEnD,2CAA2C;YAC3C,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;YAEpC,2BAA2B;YAC3B,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;YAEnC,2CAA2C;YAC3C,IAAI,GAAG,CAAC,kBAAkB,EAAE,CAAC;gBAC3B,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;YAClE,CAAC;YAED,4EAA4E;YAC5E,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;gBAClB,iBAAiB,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Migration utility — imports OpenClaw's file-based memories into Keyoku.
3
+ *
4
+ * Reads MEMORY.md and memory/*.md files, chunks them by heading sections,
5
+ * deduplicates against existing Keyoku memories, and stores each chunk.
6
+ *
7
+ * Usage: `openclaw memory import --dir /path/to/workspace`
8
+ */
9
+ import type { KeyokuClient } from '@keyoku/memory';
10
+ export interface ImportResult {
11
+ imported: number;
12
+ skipped: number;
13
+ errors: number;
14
+ }
15
+ /**
16
+ * Import OpenClaw memory files into Keyoku.
17
+ */
18
+ export declare function importMemoryFiles(params: {
19
+ client: KeyokuClient;
20
+ entityId: string;
21
+ workspaceDir: string;
22
+ agentId?: string;
23
+ dryRun?: boolean;
24
+ logger?: {
25
+ info: (msg: string) => void;
26
+ warn: (msg: string) => void;
27
+ };
28
+ }): Promise<ImportResult>;
29
+ //# sourceMappingURL=migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAoHD;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACvE,GAAG,OAAO,CAAC,YAAY,CAAC,CAiGxB"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Migration utility — imports OpenClaw's file-based memories into Keyoku.
3
+ *
4
+ * Reads MEMORY.md and memory/*.md files, chunks them by heading sections,
5
+ * deduplicates against existing Keyoku memories, and stores each chunk.
6
+ *
7
+ * Usage: `openclaw memory import --dir /path/to/workspace`
8
+ */
9
+ import { readFileSync, readdirSync, existsSync, statSync } from 'fs';
10
+ import { join } from 'path';
11
+ /**
12
+ * Split markdown content by ## or ### headings.
13
+ * Each heading section becomes one chunk.
14
+ * If no headings, split by --- separators or paragraphs.
15
+ */
16
+ function chunkByHeadings(content, maxChunkChars = 1000) {
17
+ const chunks = [];
18
+ // Try splitting by headings first
19
+ const headingPattern = /^#{2,3}\s+(.+)$/gm;
20
+ const headings = [];
21
+ let match;
22
+ while ((match = headingPattern.exec(content)) !== null) {
23
+ headings.push({ index: match.index, title: match[1].trim() });
24
+ }
25
+ if (headings.length > 0) {
26
+ for (let i = 0; i < headings.length; i++) {
27
+ const start = headings[i].index;
28
+ const end = i + 1 < headings.length ? headings[i + 1].index : content.length;
29
+ const sectionText = content.slice(start, end).trim();
30
+ if (sectionText.length < 10)
31
+ continue;
32
+ // If section is too long, split by paragraphs
33
+ if (sectionText.length > maxChunkChars) {
34
+ const paragraphs = splitByParagraphs(sectionText, maxChunkChars);
35
+ for (const p of paragraphs) {
36
+ chunks.push({ content: p, source: '', section: headings[i].title });
37
+ }
38
+ }
39
+ else {
40
+ chunks.push({ content: sectionText, source: '', section: headings[i].title });
41
+ }
42
+ }
43
+ // Content before the first heading
44
+ const preamble = content.slice(0, headings[0].index).trim();
45
+ if (preamble.length >= 10) {
46
+ const paragraphs = splitByParagraphs(preamble, maxChunkChars);
47
+ for (const p of paragraphs) {
48
+ chunks.push({ content: p, source: '' });
49
+ }
50
+ }
51
+ }
52
+ else {
53
+ // No headings — try --- separators
54
+ const sections = content.split(/^---+$/m);
55
+ if (sections.length > 1) {
56
+ for (const section of sections) {
57
+ const trimmed = section.trim();
58
+ if (trimmed.length < 10)
59
+ continue;
60
+ const paragraphs = splitByParagraphs(trimmed, maxChunkChars);
61
+ for (const p of paragraphs) {
62
+ chunks.push({ content: p, source: '' });
63
+ }
64
+ }
65
+ }
66
+ else {
67
+ // No structure — split by paragraphs
68
+ const paragraphs = splitByParagraphs(content, maxChunkChars);
69
+ for (const p of paragraphs) {
70
+ chunks.push({ content: p, source: '' });
71
+ }
72
+ }
73
+ }
74
+ return chunks;
75
+ }
76
+ /**
77
+ * Split text by double-newline (paragraphs), merging small paragraphs
78
+ * and splitting oversized ones.
79
+ */
80
+ function splitByParagraphs(text, maxChars = 1000) {
81
+ const rawParagraphs = text.split(/\n\n+/);
82
+ const results = [];
83
+ let buffer = '';
84
+ for (const para of rawParagraphs) {
85
+ const trimmed = para.trim();
86
+ if (!trimmed)
87
+ continue;
88
+ if (buffer.length + trimmed.length + 2 <= maxChars) {
89
+ buffer = buffer ? `${buffer}\n\n${trimmed}` : trimmed;
90
+ }
91
+ else {
92
+ if (buffer)
93
+ results.push(buffer);
94
+ if (trimmed.length > maxChars) {
95
+ // Hard split at maxChars boundary
96
+ for (let i = 0; i < trimmed.length; i += maxChars) {
97
+ results.push(trimmed.slice(i, i + maxChars));
98
+ }
99
+ buffer = '';
100
+ }
101
+ else {
102
+ buffer = trimmed;
103
+ }
104
+ }
105
+ }
106
+ if (buffer && buffer.length >= 10)
107
+ results.push(buffer);
108
+ return results;
109
+ }
110
+ /**
111
+ * Small delay helper for rate limiting.
112
+ */
113
+ function delay(ms) {
114
+ return new Promise((resolve) => setTimeout(resolve, ms));
115
+ }
116
+ /**
117
+ * Import OpenClaw memory files into Keyoku.
118
+ */
119
+ export async function importMemoryFiles(params) {
120
+ const { client, entityId, workspaceDir, agentId, dryRun = false, logger = console } = params;
121
+ const result = { imported: 0, skipped: 0, errors: 0 };
122
+ // Discover memory files
123
+ const files = [];
124
+ // Check for MEMORY.md
125
+ const memoryMdPath = join(workspaceDir, 'MEMORY.md');
126
+ if (existsSync(memoryMdPath)) {
127
+ files.push({ path: memoryMdPath, name: 'MEMORY.md' });
128
+ }
129
+ // Check for memory/ directory
130
+ const memoryDir = join(workspaceDir, 'memory');
131
+ if (existsSync(memoryDir) && statSync(memoryDir).isDirectory()) {
132
+ const entries = readdirSync(memoryDir)
133
+ .filter((f) => f.endsWith('.md'))
134
+ .sort(); // chronological for dated files
135
+ for (const entry of entries) {
136
+ files.push({ path: join(memoryDir, entry), name: `memory/${entry}` });
137
+ }
138
+ }
139
+ if (files.length === 0) {
140
+ logger.info('No memory files found in workspace.');
141
+ return result;
142
+ }
143
+ logger.info(`Found ${files.length} memory file(s) to import.`);
144
+ // Process each file
145
+ for (const file of files) {
146
+ let content;
147
+ try {
148
+ content = readFileSync(file.path, 'utf-8');
149
+ }
150
+ catch (err) {
151
+ logger.warn(`Failed to read ${file.name}: ${String(err)}`);
152
+ result.errors++;
153
+ continue;
154
+ }
155
+ if (content.trim().length < 10) {
156
+ logger.info(`Skipping ${file.name} (too short)`);
157
+ result.skipped++;
158
+ continue;
159
+ }
160
+ const chunks = chunkByHeadings(content);
161
+ for (const chunk of chunks) {
162
+ chunk.source = file.name;
163
+ // Build the content to store — include source context
164
+ const taggedContent = chunk.section
165
+ ? `[Imported from ${file.name} — ${chunk.section}]\n${chunk.content}`
166
+ : `[Imported from ${file.name}]\n${chunk.content}`;
167
+ if (dryRun) {
168
+ logger.info(`[dry-run] Would import: ${taggedContent.slice(0, 80)}...`);
169
+ result.imported++;
170
+ continue;
171
+ }
172
+ // Dedup check: search for similar content
173
+ try {
174
+ const queryText = chunk.content.slice(0, 100);
175
+ const existing = await client.search(entityId, queryText, { limit: 1, min_score: 0.95 });
176
+ if (existing.length > 0) {
177
+ result.skipped++;
178
+ continue;
179
+ }
180
+ }
181
+ catch {
182
+ // Search failed — proceed with import anyway
183
+ }
184
+ // Store the memory
185
+ try {
186
+ await client.remember(entityId, taggedContent, {
187
+ agent_id: agentId,
188
+ source: 'migration',
189
+ });
190
+ result.imported++;
191
+ logger.info(`Imported: ${chunk.content.slice(0, 60)}...`);
192
+ }
193
+ catch (err) {
194
+ logger.warn(`Failed to store chunk from ${file.name}: ${String(err)}`);
195
+ result.errors++;
196
+ }
197
+ // Rate limit
198
+ await delay(50);
199
+ }
200
+ }
201
+ return result;
202
+ }
203
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.js","sourceRoot":"","sources":["../src/migration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Keyoku binary lifecycle management.
3
+ * Starts/stops the Keyoku Go binary as a child process.
4
+ */
5
+ import type { PluginApi } from './types.js';
6
+ export declare function registerService(api: PluginApi, keyokuUrl: string): void;
7
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA0D5C,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CA6EvE"}