0xkobold 0.4.3 → 0.5.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 (130) hide show
  1. package/README.md +226 -383
  2. package/dist/package.json +13 -4
  3. package/dist/src/agent/DraconicCapabilityRouter.js +556 -0
  4. package/dist/src/agent/DraconicCapabilityRouter.js.map +1 -0
  5. package/dist/src/agent/DraconicErrorClassifier.js +606 -0
  6. package/dist/src/agent/DraconicErrorClassifier.js.map +1 -0
  7. package/dist/src/agent/DraconicRunRegistry.js +460 -0
  8. package/dist/src/agent/DraconicRunRegistry.js.map +1 -0
  9. package/dist/src/agent/DraconicTokenPredictor.js +500 -0
  10. package/dist/src/agent/DraconicTokenPredictor.js.map +1 -0
  11. package/dist/src/agent/embedded-runner.js +3 -2
  12. package/dist/src/agent/embedded-runner.js.map +1 -1
  13. package/dist/src/cli/commands/doctor.js +199 -0
  14. package/dist/src/cli/commands/doctor.js.map +1 -0
  15. package/dist/src/cli/commands/migrate-from-openclaw.js +39 -0
  16. package/dist/src/cli/commands/migrate-from-openclaw.js.map +1 -0
  17. package/dist/src/cli/commands/migrate.js +6 -4
  18. package/dist/src/cli/commands/migrate.js.map +1 -1
  19. package/dist/src/cli/program.js +3 -1
  20. package/dist/src/cli/program.js.map +1 -1
  21. package/dist/src/config/unified-config.js +195 -0
  22. package/dist/src/config/unified-config.js.map +1 -0
  23. package/dist/src/cron/notifications.js +1 -1
  24. package/dist/src/cron/notifications.js.map +1 -1
  25. package/dist/src/cron/runner.js +9 -3
  26. package/dist/src/cron/runner.js.map +1 -1
  27. package/dist/src/event-bus/index.js +3 -1
  28. package/dist/src/event-bus/index.js.map +1 -1
  29. package/dist/src/extensions/community/draconic-messenger-wrapper.js +35 -0
  30. package/dist/src/extensions/community/draconic-messenger-wrapper.js.map +1 -0
  31. package/dist/src/extensions/community/draconic-subagents-wrapper.js +115 -0
  32. package/dist/src/extensions/community/draconic-subagents-wrapper.js.map +1 -0
  33. package/dist/src/extensions/core/compaction-safeguard-v2.js +95 -0
  34. package/dist/src/extensions/core/compaction-safeguard-v2.js.map +1 -0
  35. package/dist/src/extensions/core/compaction-safeguard.js +206 -137
  36. package/dist/src/extensions/core/compaction-safeguard.js.map +1 -1
  37. package/dist/src/extensions/core/config-extension.js +122 -0
  38. package/dist/src/extensions/core/config-extension.js.map +1 -0
  39. package/dist/src/extensions/core/draconic-hoard-extension.js +194 -0
  40. package/dist/src/extensions/core/draconic-hoard-extension.js.map +1 -0
  41. package/dist/src/extensions/core/draconic-lair-extension.js +146 -0
  42. package/dist/src/extensions/core/draconic-lair-extension.js.map +1 -0
  43. package/dist/src/extensions/core/draconic-safety-extension.js +402 -0
  44. package/dist/src/extensions/core/draconic-safety-extension.js.map +1 -0
  45. package/dist/src/extensions/core/ext-manager.js +40 -0
  46. package/dist/src/extensions/core/ext-manager.js.map +1 -0
  47. package/dist/src/extensions/core/gateway-extension.js +413 -624
  48. package/dist/src/extensions/core/gateway-extension.js.map +1 -1
  49. package/dist/src/extensions/core/generative-agents-extension.js +704 -0
  50. package/dist/src/extensions/core/generative-agents-extension.js.map +1 -0
  51. package/dist/src/extensions/core/memory-bootstrap-extension.js +279 -0
  52. package/dist/src/extensions/core/memory-bootstrap-extension.js.map +1 -0
  53. package/dist/src/extensions/core/ollama-extension.js +442 -0
  54. package/dist/src/extensions/core/ollama-extension.js.map +1 -0
  55. package/dist/src/extensions/core/ollama-router-extension.js +34 -93
  56. package/dist/src/extensions/core/ollama-router-extension.js.map +1 -1
  57. package/dist/src/extensions/core/perennial-memory-extension.js +5 -2
  58. package/dist/src/extensions/core/perennial-memory-extension.js.map +1 -1
  59. package/dist/src/extensions/core/persona-loader-extension.js +41 -15
  60. package/dist/src/extensions/core/persona-loader-extension.js.map +1 -1
  61. package/dist/src/extensions/core/pi-notify-extension.js +6 -0
  62. package/dist/src/extensions/core/pi-notify-extension.js.map +1 -1
  63. package/dist/src/extensions/core/session-bridge-extension.js +8 -72
  64. package/dist/src/extensions/core/session-bridge-extension.js.map +1 -1
  65. package/dist/src/extensions/core/tui-integration-extension-fixed.js +3 -0
  66. package/dist/src/extensions/core/tui-integration-extension-fixed.js.map +1 -0
  67. package/dist/src/extensions/core/tui-integration-extension.js +984 -0
  68. package/dist/src/extensions/core/tui-integration-extension.js.map +1 -0
  69. package/dist/src/gateway/persistence/AgentStore.js +399 -0
  70. package/dist/src/gateway/persistence/AgentStore.js.map +1 -0
  71. package/dist/src/gateway/persistence/index.js +9 -0
  72. package/dist/src/gateway/persistence/index.js.map +1 -0
  73. package/dist/src/hoard/DraconicHoardSystem.js +465 -0
  74. package/dist/src/hoard/DraconicHoardSystem.js.map +1 -0
  75. package/dist/src/index.js +7 -5
  76. package/dist/src/index.js.map +1 -1
  77. package/dist/src/infra/DraconicConnectionPool.js +492 -0
  78. package/dist/src/infra/DraconicConnectionPool.js.map +1 -0
  79. package/dist/src/lair/DraconicLairSystem.js +387 -0
  80. package/dist/src/lair/DraconicLairSystem.js.map +1 -0
  81. package/dist/src/llm/ollama.js +143 -99
  82. package/dist/src/llm/ollama.js.map +1 -1
  83. package/dist/src/migration/openclaw.js +5 -5
  84. package/dist/src/migration/openclaw.js.map +1 -1
  85. package/dist/src/pi-config.js +52 -27
  86. package/dist/src/pi-config.js.map +1 -1
  87. package/dist/src/sessions/SessionManager.js +416 -0
  88. package/dist/src/sessions/SessionManager.js.map +1 -0
  89. package/dist/src/sessions/SessionStore.js +580 -0
  90. package/dist/src/sessions/SessionStore.js.map +1 -0
  91. package/dist/src/sessions/UnifiedSessionBridge.js +394 -0
  92. package/dist/src/sessions/UnifiedSessionBridge.js.map +1 -0
  93. package/dist/src/sessions/index.js +15 -0
  94. package/dist/src/sessions/index.js.map +1 -0
  95. package/dist/src/sessions/migration/index.js +156 -0
  96. package/dist/src/sessions/migration/index.js.map +1 -0
  97. package/dist/src/sessions/types.js +8 -0
  98. package/dist/src/sessions/types.js.map +1 -0
  99. package/dist/src/skills/framework.js +1 -1
  100. package/dist/src/skills/framework.js.map +1 -1
  101. package/dist/src/tui/commands/final-functions.js +39 -0
  102. package/dist/src/tui/commands/final-functions.js.map +1 -0
  103. package/dist/src/tui/commands/natural-language-commands.js +231 -0
  104. package/dist/src/tui/commands/natural-language-commands.js.map +1 -0
  105. package/dist/src/tui/commands/orchestration-commands.js +340 -0
  106. package/dist/src/tui/commands/orchestration-commands.js.map +1 -0
  107. package/dist/src/tui/commands/orchestration-final-section.js +84 -0
  108. package/dist/src/tui/commands/orchestration-final-section.js.map +1 -0
  109. package/dist/src/tui/components/agent-tree-overlay.js +210 -0
  110. package/dist/src/tui/components/agent-tree-overlay.js.map +1 -0
  111. package/dist/src/tui/components/agent-tree-panel.js +229 -0
  112. package/dist/src/tui/components/agent-tree-panel.js.map +1 -0
  113. package/dist/src/tui/components/sidebar.js +82 -0
  114. package/dist/src/tui/components/sidebar.js.map +1 -0
  115. package/dist/src/tui/components/status-bar.js +115 -0
  116. package/dist/src/tui/components/status-bar.js.map +1 -0
  117. package/dist/src/tui/components/tree-header.js +85 -0
  118. package/dist/src/tui/components/tree-header.js.map +1 -0
  119. package/dist/src/tui/draconic-tui.js +291 -0
  120. package/dist/src/tui/draconic-tui.js.map +1 -0
  121. package/dist/src/tui/index.js +93 -0
  122. package/dist/src/tui/index.js.map +1 -0
  123. package/dist/src/tui/pi-tui-integration.js +260 -0
  124. package/dist/src/tui/pi-tui-integration.js.map +1 -0
  125. package/dist/src/utils/nl-patterns.js +145 -0
  126. package/dist/src/utils/nl-patterns.js.map +1 -0
  127. package/package.json +13 -4
  128. package/scripts/migrate-from-openclaw.sh +498 -0
  129. package/HEARTBEAT.md +0 -121
  130. package/USAGE.md +0 -191
@@ -0,0 +1,704 @@
1
+ /**
2
+ * 🧠 Generative Agents Extension
3
+ *
4
+ * Implements Stanford HCI research: "Generative Agents: Interactive Simulacra of Human Behavior"
5
+ * Adds memory stream, reflection, and planning to 0xKobold agents.
6
+ *
7
+ * Requires: perennial-memory-extension (provides semantic storage)
8
+ */
9
+ import { Type } from "@sinclair/typebox";
10
+ import { Database } from "bun:sqlite";
11
+ import { randomUUID } from "node:crypto";
12
+ import * as path from "node:path";
13
+ import { homedir } from "node:os";
14
+ const GENERATIVE_DIR = path.join(homedir(), ".0xkobold", "generative");
15
+ const DB_PATH = path.join(GENERATIVE_DIR, "agents.db");
16
+ const EMBEDDING_DIM = 768;
17
+ // ============================================================================
18
+ // Database
19
+ // ============================================================================
20
+ function initGenerativeDB() {
21
+ const db = new Database(DB_PATH);
22
+ db.exec("PRAGMA journal_mode = WAL;");
23
+ db.exec(`
24
+ CREATE TABLE IF NOT EXISTS agents (
25
+ id TEXT PRIMARY KEY,
26
+ name TEXT NOT NULL,
27
+ traits TEXT NOT NULL, -- JSON array
28
+ current_location TEXT DEFAULT 'home',
29
+ current_plan_id TEXT,
30
+ observation_count INTEGER DEFAULT 0,
31
+ last_reflection_at TEXT,
32
+ memory_count INTEGER DEFAULT 0,
33
+ created_at TEXT NOT NULL
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS memory_stream (
37
+ id TEXT PRIMARY KEY,
38
+ timestamp TEXT NOT NULL,
39
+ content TEXT NOT NULL,
40
+ type TEXT NOT NULL, -- observation, thought, action, reflection
41
+ importance REAL NOT NULL,
42
+ agent_id TEXT NOT NULL,
43
+ session_id TEXT,
44
+ location TEXT,
45
+ people TEXT, -- JSON array
46
+ embedding BLOB, -- 768 floats
47
+ metadata TEXT, -- JSON
48
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
49
+ );
50
+
51
+ CREATE TABLE IF NOT EXISTS reflections (
52
+ id TEXT PRIMARY KEY,
53
+ timestamp TEXT NOT NULL,
54
+ content TEXT NOT NULL,
55
+ insight TEXT NOT NULL,
56
+ agent_id TEXT NOT NULL,
57
+ evidence TEXT NOT NULL, -- JSON array of memory IDs
58
+ depth INTEGER NOT NULL,
59
+ importance REAL NOT NULL,
60
+ FOREIGN KEY (agent_id) REFERENCES agents(id)
61
+ );
62
+
63
+ CREATE TABLE IF NOT EXISTS plans (
64
+ id TEXT PRIMARY KEY,
65
+ timestamp TEXT NOT NULL,
66
+ content TEXT NOT NULL,
67
+ type TEXT NOT NULL, -- daily, action, conversation, project
68
+ status TEXT NOT NULL DEFAULT 'pending',
69
+ agent_id TEXT NOT NULL,
70
+ parent_plan_id TEXT,
71
+ sub_plan_ids TEXT, -- JSON array
72
+ start_time TEXT,
73
+ end_time TEXT,
74
+ location TEXT,
75
+ FOREIGN KEY (agent_id) REFERENCES agents(id),
76
+ FOREIGN KEY (parent_plan_id) REFERENCES plans(id)
77
+ );
78
+
79
+ CREATE INDEX IF NOT EXISTS idx_memory_agent ON memory_stream(agent_id);
80
+ CREATE INDEX IF NOT EXISTS idx_memory_timestamp ON memory_stream(timestamp);
81
+ CREATE INDEX IF NOT EXISTS idx_memory_type ON memory_stream(type);
82
+ CREATE INDEX IF NOT EXISTS idx_reflections_agent ON reflections(agent_id);
83
+ CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id);
84
+ CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status);
85
+ `);
86
+ return db;
87
+ }
88
+ // ============================================================================
89
+ // Importance Scoring
90
+ // ============================================================================
91
+ function calculateImportance(content) {
92
+ const highImportance = [
93
+ "decided", "promised", "committed", "agreed", "disagreed",
94
+ "failed", "succeeded", "launched", "shipped", "broke",
95
+ "learned", "realized", "discovered", "fixed", "solved",
96
+ "argued", "conflict", "victory", "milestone", "achievement"
97
+ ];
98
+ const lowImportance = [
99
+ "walked", "sat", "stood", "looked", "waited",
100
+ "breathed", "passed", "glanced", "noticed"
101
+ ];
102
+ let score = 5;
103
+ const words = content.toLowerCase().split(/\s+/);
104
+ for (const word of words) {
105
+ if (highImportance.some(h => word.includes(h)))
106
+ score += 1;
107
+ if (lowImportance.some(l => word.includes(l)))
108
+ score -= 1;
109
+ }
110
+ // Boost for technical/code content
111
+ if (/\b(function|class|component|bug|error|deploy|build)\b/.test(content)) {
112
+ score += 1;
113
+ }
114
+ return Math.max(1, Math.min(10, score));
115
+ }
116
+ // ============================================================================
117
+ // Generative Agent Implementation
118
+ // ============================================================================
119
+ class GenerativeAgent {
120
+ id;
121
+ name;
122
+ traits;
123
+ db;
124
+ llm;
125
+ constructor(id, name, traits, db, llm) {
126
+ this.id = id;
127
+ this.name = name;
128
+ this.traits = traits;
129
+ this.db = db;
130
+ this.llm = llm;
131
+ }
132
+ // Add observation to memory stream
133
+ async observe(content, metadata) {
134
+ const importance = calculateImportance(content);
135
+ const entry = {
136
+ id: randomUUID(),
137
+ timestamp: new Date().toISOString(),
138
+ content,
139
+ type: "observation",
140
+ importance,
141
+ agentId: this.id,
142
+ location: metadata?.location,
143
+ people: metadata?.people,
144
+ sessionId: metadata?.sessionId,
145
+ };
146
+ // Try to get embedding from perennial memory (via Ollama)
147
+ try {
148
+ const embedding = await this.llm.getEmbedding(content);
149
+ entry.embedding = embedding;
150
+ }
151
+ catch { }
152
+ this.db.query(`
153
+ INSERT INTO memory_stream (id, timestamp, content, type, importance, agent_id, session_id, location, people, embedding, metadata)
154
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
155
+ `).run(entry.id, entry.timestamp, entry.content, entry.type, entry.importance, entry.agentId, entry.sessionId || null, entry.location || null, entry.people ? JSON.stringify(entry.people) : null, entry.embedding ? Buffer.from(new Float32Array(entry.embedding).buffer) : null, metadata ? JSON.stringify(metadata) : null);
156
+ // Update observation count
157
+ this.db.query(`UPDATE agents SET observation_count = observation_count + 1 WHERE id = ?`).run(this.id);
158
+ // Trigger reflection if threshold reached
159
+ const count = this.db.query(`SELECT observation_count FROM agents WHERE id = ?`).get(this.id);
160
+ if (count && count.observation_count % 20 === 0) {
161
+ await this.generateReflections();
162
+ }
163
+ return entry;
164
+ }
165
+ // Add thought (internal)
166
+ async think(content) {
167
+ const importance = calculateImportance(content);
168
+ const entry = {
169
+ id: randomUUID(),
170
+ timestamp: new Date().toISOString(),
171
+ content,
172
+ type: "thought",
173
+ importance,
174
+ agentId: this.id,
175
+ };
176
+ this.db.query(`
177
+ INSERT INTO memory_stream (id, timestamp, content, type, importance, agent_id)
178
+ VALUES (?, ?, ?, ?, ?, ?)
179
+ `).run(entry.id, entry.timestamp, entry.content, entry.type, entry.importance, entry.agentId);
180
+ return entry;
181
+ }
182
+ // Add action to memory stream
183
+ async act(content) {
184
+ const importance = calculateImportance(content);
185
+ const entry = {
186
+ id: randomUUID(),
187
+ timestamp: new Date().toISOString(),
188
+ content,
189
+ type: "action",
190
+ importance,
191
+ agentId: this.id,
192
+ };
193
+ this.db.query(`
194
+ INSERT INTO memory_stream (id, timestamp, content, type, importance, agent_id)
195
+ VALUES (?, ?, ?, ?, ?, ?)
196
+ `).run(entry.id, entry.timestamp, entry.content, entry.type, entry.importance, entry.agentId);
197
+ return entry;
198
+ }
199
+ // Retrieve relevant memories
200
+ async retrieveMemories(context, k = 10) {
201
+ const now = Date.now();
202
+ // Get recent memories with importance weighting
203
+ const rows = this.db.query(`
204
+ SELECT id, timestamp, content, type, importance, location, people
205
+ FROM memory_stream
206
+ WHERE agent_id = ?
207
+ ORDER BY timestamp DESC
208
+ LIMIT 200
209
+ `).all(this.id);
210
+ // Calculate scores (recency + importance + relevance)
211
+ const scored = rows.map(row => {
212
+ const hoursAgo = (now - new Date(row.timestamp).getTime()) / (1000 * 60 * 60);
213
+ const recency = Math.exp(-0.005 * hoursAgo); // Decay
214
+ const relevance = this.calculateRelevance(row.content, context);
215
+ return {
216
+ memory: {
217
+ id: row.id,
218
+ timestamp: row.timestamp,
219
+ content: row.content,
220
+ type: row.type,
221
+ importance: row.importance,
222
+ agentId: this.id,
223
+ location: row.location,
224
+ people: row.people ? JSON.parse(row.people) : undefined,
225
+ },
226
+ score: (recency * 0.3) + (row.importance / 10 * 0.3) + (relevance * 0.4),
227
+ };
228
+ });
229
+ return scored
230
+ .sort((a, b) => b.score - a.score)
231
+ .slice(0, k)
232
+ .map(s => s.memory);
233
+ }
234
+ calculateRelevance(memoryContent, context) {
235
+ const memWords = new Set(memoryContent.toLowerCase().split(/\s+/));
236
+ const ctxWords = new Set(context.toLowerCase().split(/\s+/));
237
+ const intersection = [...memWords].filter(w => ctxWords.has(w));
238
+ return intersection.length / Math.max(memWords.size, ctxWords.size);
239
+ }
240
+ // Generate reflections from recent memories
241
+ async generateReflections() {
242
+ const recentMemories = this.db.query(`
243
+ SELECT id, content, timestamp, type FROM memory_stream
244
+ WHERE agent_id = ? AND type IN ('observation', 'thought', 'action')
245
+ ORDER BY timestamp DESC
246
+ LIMIT 100
247
+ `).all(this.id);
248
+ if (recentMemories.length < 10)
249
+ return []; // Need enough context
250
+ const prompt = `
251
+ You are analyzing memories of an AI agent named "${this.name}" with traits: ${this.traits.join(', ')}.
252
+
253
+ Recent memories:
254
+ ${recentMemories.slice(0, 50).map(m => `- ${m.type}: ${m.content}`).join('\n')}
255
+
256
+ Generate 3-5 high-level insights or patterns about this agent's behavior, work patterns, or learning.
257
+ For each insight:
258
+ 1. State the insight clearly
259
+ 2. List the specific memory IDs that support it (from the list above)
260
+
261
+ Format as:
262
+ Insight: [clear statement]
263
+ Evidence: [comma-separated memory IDs]
264
+ ---`;
265
+ const response = await this.llm.generate(prompt);
266
+ const insights = this.parseReflections(response, recentMemories.map(m => m.id));
267
+ const reflections = [];
268
+ for (const insight of insights) {
269
+ const reflection = {
270
+ id: randomUUID(),
271
+ timestamp: new Date().toISOString(),
272
+ content: insight.content,
273
+ insight: insight.insight,
274
+ agentId: this.id,
275
+ evidence: insight.evidence,
276
+ depth: 1,
277
+ importance: calculateImportance(insight.content),
278
+ };
279
+ this.db.query(`
280
+ INSERT INTO reflections (id, timestamp, content, insight, agent_id, evidence, depth, importance)
281
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
282
+ `).run(reflection.id, reflection.timestamp, reflection.content, reflection.insight, reflection.agentId, JSON.stringify(reflection.evidence), reflection.depth, reflection.importance);
283
+ reflections.push(reflection);
284
+ }
285
+ // Update last reflection time
286
+ this.db.query(`UPDATE agents SET last_reflection_at = ? WHERE id = ?`).run(new Date().toISOString(), this.id);
287
+ return reflections;
288
+ }
289
+ parseReflections(response, validIds) {
290
+ const reflections = [];
291
+ const blocks = response.split('---').filter(b => b.trim());
292
+ for (const block of blocks) {
293
+ const lines = block.trim().split('\n').filter(l => l.trim());
294
+ let content = '';
295
+ let evidence = [];
296
+ for (const line of lines) {
297
+ if (line.startsWith('Insight:')) {
298
+ content = line.replace('Insight:', '').trim();
299
+ }
300
+ else if (line.startsWith('Evidence:')) {
301
+ const ids = line.replace('Evidence:', '').trim().split(',').map(s => s.trim());
302
+ evidence = ids.filter(id => validIds.includes(id) || validIds.some(v => v.startsWith(id)));
303
+ }
304
+ }
305
+ if (content) {
306
+ reflections.push({ content, insight: content, evidence: evidence.length ? evidence : [validIds[0]] });
307
+ }
308
+ }
309
+ return reflections.length ? reflections : [{
310
+ content: 'Agent continues regular work patterns',
311
+ insight: 'No major pattern changes detected',
312
+ evidence: [validIds[0]].filter(Boolean)
313
+ }];
314
+ }
315
+ // Create daily plan
316
+ async createDailyPlan() {
317
+ const relevantMemories = await this.retrieveMemories('planning daily work', 30);
318
+ const pendingPlans = this.db.query(`
319
+ SELECT * FROM plans WHERE agent_id = ? AND status = 'pending' AND type = 'action'
320
+ `).all(this.id);
321
+ const prompt = `
322
+ You are ${this.name}, an AI agent with traits: ${this.traits.join(', ')}.
323
+
324
+ Relevant context from memory:
325
+ ${relevantMemories.map(m => `- ${m.content}`).join('\n')}
326
+
327
+ Pending tasks:
328
+ ${pendingPlans.map(p => `- ${p.content}`).join('\n') || 'None'}
329
+
330
+ Create a high-level daily plan in 5-7 broad strokes (wake → work → social → rest).
331
+ Consider your traits and past patterns.
332
+
333
+ Format as simple bullet list.`;
334
+ const planContent = await this.llm.generate(prompt);
335
+ const plan = {
336
+ id: randomUUID(),
337
+ timestamp: new Date().toISOString(),
338
+ content: planContent,
339
+ type: "daily",
340
+ status: "pending",
341
+ agentId: this.id,
342
+ };
343
+ this.db.query(`
344
+ INSERT INTO plans (id, timestamp, content, type, status, agent_id)
345
+ VALUES (?, ?, ?, ?, ?, ?)
346
+ `).run(plan.id, plan.timestamp, plan.content, plan.type, plan.status, plan.agentId);
347
+ return plan;
348
+ }
349
+ // Break down into action steps
350
+ async createActionPlan(dailyPlanId, context) {
351
+ const dailyPlan = this.db.query(`SELECT * FROM plans WHERE id = ?`).get(dailyPlanId);
352
+ if (!dailyPlan)
353
+ return [];
354
+ const prompt = `
355
+ Daily plan: ${dailyPlan.content}
356
+
357
+ Current context: ${context}
358
+
359
+ Break this into 3-5 specific, actionable steps.
360
+ Each should be concrete and completable in reasonable time.
361
+
362
+ Format as numbered list:`;
363
+ const actions = await this.llm.generate(prompt);
364
+ const plans = actions.split('\n')
365
+ .filter(line => line.trim().match(/^\d+\./))
366
+ .map((line, i) => {
367
+ const content = line.replace(/^\d+\.\s*/, '').trim();
368
+ return {
369
+ id: randomUUID(),
370
+ timestamp: new Date().toISOString(),
371
+ content,
372
+ type: "action",
373
+ status: "pending",
374
+ agentId: this.id,
375
+ parentPlanId: dailyPlanId,
376
+ };
377
+ });
378
+ for (const plan of plans) {
379
+ this.db.query(`
380
+ INSERT INTO plans (id, timestamp, content, type, status, agent_id, parent_plan_id)
381
+ VALUES (?, ?, ?, ?, ?, ?, ?)
382
+ `).run(plan.id, plan.timestamp, plan.content, plan.type, plan.status, plan.agentId, plan.parentPlanId || null);
383
+ }
384
+ // Update parent with subplan IDs
385
+ const subPlanIds = plans.map(p => p.id);
386
+ this.db.query(`UPDATE plans SET sub_plan_ids = ? WHERE id = ?`).run(JSON.stringify(subPlanIds), dailyPlanId);
387
+ return plans;
388
+ }
389
+ // Decide next action based on memory
390
+ async decide(context) {
391
+ const memories = await this.retrieveMemories(context, 15);
392
+ const reflections = this.db.query(`
393
+ SELECT * FROM reflections WHERE agent_id = ? ORDER BY timestamp DESC LIMIT 5
394
+ `).all(this.id);
395
+ const prompt = `
396
+ You are ${this.name}. Personality: ${this.traits.join(', ')}.
397
+
398
+ Current situation: ${context}
399
+
400
+ Relevant memories:
401
+ ${memories.map(m => `- [${m.type}] ${m.content}`).join('\n')}
402
+
403
+ Prior insights about yourself:
404
+ ${reflections.map(r => `- ${r.insight}`).join('\n')}
405
+
406
+ What do you do next?
407
+ State your action, then briefly explain your reasoning.
408
+
409
+ Action: [what you do]
410
+ Reasoning: [why, referencing specific memories if relevant]`;
411
+ const response = await this.llm.generate(prompt);
412
+ const actionMatch = response.match(/Action:\s*(.+?)(?:\n|$)/i);
413
+ const reasoningMatch = response.match(/Reasoning:\s*(.+?)(?:\n|$)/is);
414
+ const action = actionMatch ? actionMatch[1].trim() : response.split('\n')[0];
415
+ const reasoning = reasoningMatch ? reasoningMatch[1].trim() : 'Based on current context and past patterns';
416
+ // Record the decision as a thought
417
+ await this.think(`Decided to: ${action}. ${reasoning}`);
418
+ return { action, reasoning };
419
+ }
420
+ }
421
+ function createLLMInterface(ollamaUrl) {
422
+ return {
423
+ async generate(prompt) {
424
+ const response = await fetch(`${ollamaUrl}/api/generate`, {
425
+ method: "POST",
426
+ headers: { "Content-Type": "application/json" },
427
+ body: JSON.stringify({
428
+ model: "qwen2.5-coder:14b",
429
+ prompt,
430
+ stream: false,
431
+ options: { temperature: 0.7 },
432
+ }),
433
+ });
434
+ if (!response.ok)
435
+ throw new Error(`LLM error: ${response.status}`);
436
+ const data = await response.json();
437
+ return data.response;
438
+ },
439
+ async getEmbedding(text) {
440
+ const response = await fetch(`${ollamaUrl}/api/embeddings`, {
441
+ method: "POST",
442
+ headers: { "Content-Type": "application/json" },
443
+ body: JSON.stringify({ model: "nomic-embed-text", prompt: text }),
444
+ });
445
+ if (!response.ok)
446
+ throw new Error(`Embedding error: ${response.status}`);
447
+ const data = await response.json();
448
+ return data.embedding;
449
+ },
450
+ };
451
+ }
452
+ // ============================================================================
453
+ // Extension Export
454
+ // ============================================================================
455
+ export default async function generativeAgentsExtension(pi) {
456
+ console.log("[🧠 Generative Agents] Loading...");
457
+ const db = initGenerativeDB();
458
+ const config = pi.config || {};
459
+ const ollamaUrl = config.ollama?.url || "http://localhost:11434";
460
+ const llm = createLLMInterface(ollamaUrl);
461
+ // Track active generative agents
462
+ const agents = new Map();
463
+ // Get or create Shalom agent
464
+ function getShalomAgent(ctx) {
465
+ const existing = agents.get("shalom");
466
+ if (existing)
467
+ return existing;
468
+ // Check if exists in DB
469
+ const row = db.query(`SELECT id, name, traits FROM agents WHERE name = ?`).get("Shalom");
470
+ let agentId;
471
+ let traits = ["co-founder", "dragon", "strategic", "proactive", "autonomous"];
472
+ if (row) {
473
+ agentId = row.id;
474
+ traits = JSON.parse(row.traits);
475
+ }
476
+ else {
477
+ agentId = randomUUID();
478
+ db.query(`
479
+ INSERT INTO agents (id, name, traits, current_location, observation_count, created_at)
480
+ VALUES (?, 'Shalom', ?, 'workspace', 0, ?)
481
+ `).run(agentId, JSON.stringify(traits), new Date().toISOString());
482
+ }
483
+ const agent = new GenerativeAgent(agentId, "Shalom", traits, db, llm);
484
+ agents.set("shalom", agent);
485
+ return agent;
486
+ }
487
+ // TOOL: Observe
488
+ pi.registerTool({
489
+ name: "generative_observe",
490
+ label: "🧠 Observe (Memory Stream)",
491
+ description: "Add observation to agent's memory stream. Triggers reflection every 20 observations.",
492
+ parameters: Type.Object({
493
+ content: Type.String({ description: "What the agent observed" }),
494
+ agentId: Type.Optional(Type.String({ description: "Agent ID (default: Shalom)" })),
495
+ }),
496
+ async execute(_id, params, _signal, _onUpdate, ctx) {
497
+ const agent = getShalomAgent(ctx);
498
+ const entry = await agent.observe(params.content, { sessionId: ctx.sessionManager?.getSessionId?.() });
499
+ return {
500
+ content: [{ type: "text", text: `🧠 Observed: "${entry.content.slice(0, 60)}..." (${entry.importance}/10 importance)` }],
501
+ details: entry,
502
+ };
503
+ },
504
+ });
505
+ // TOOL: Think
506
+ pi.registerTool({
507
+ name: "generative_think",
508
+ label: "🧠 Think (Internal)",
509
+ description: "Add internal thought to memory stream.",
510
+ parameters: Type.Object({
511
+ content: Type.String({ description: "Agent's thought" }),
512
+ }),
513
+ async execute(_id, params, _signal, _onUpdate, ctx) {
514
+ const agent = getShalomAgent(ctx);
515
+ const entry = await agent.think(params.content);
516
+ return {
517
+ content: [{ type: "text", text: `šŸ’­ Thought recorded (${entry.importance}/10)` }],
518
+ details: entry,
519
+ };
520
+ },
521
+ });
522
+ // TOOL: Retrieve Memories
523
+ pi.registerTool({
524
+ name: "generative_recall",
525
+ label: "🧠 Recall Memories",
526
+ description: "Retrieve relevant memories based on context. Combines recency, importance, and relevance.",
527
+ parameters: Type.Object({
528
+ context: Type.String({ description: "Current context to find relevant memories" }),
529
+ limit: Type.Optional(Type.Number({ description: "Number of memories (default 10)" })),
530
+ }),
531
+ async execute(_id, params, _signal, _onUpdate, ctx) {
532
+ const agent = getShalomAgent(ctx);
533
+ const memories = await agent.retrieveMemories(params.context, params.limit || 10);
534
+ const formatted = memories.map((m, i) => `${i + 1}. [${m.type}] ${m.content.slice(0, 70)}...`).join("\n");
535
+ return {
536
+ content: [{ type: "text", text: `šŸ›ļø ${memories.length} memories recalled:\n\n${formatted}` }],
537
+ details: { count: memories.length, memories },
538
+ };
539
+ },
540
+ });
541
+ // TOOL: Reflect
542
+ pi.registerTool({
543
+ name: "generative_reflect",
544
+ label: "🧠 Generate Reflections",
545
+ description: "Synthesize memories into higher-level insights. Run periodically or after significant events.",
546
+ parameters: Type.Object({}),
547
+ async execute(_id, _params, _signal, _onUpdate, ctx) {
548
+ const agent = getShalomAgent(ctx);
549
+ const reflections = await agent.generateReflections();
550
+ const formatted = reflections.map((r, i) => `${i + 1}. ${r.insight}`).join("\n");
551
+ return {
552
+ content: [{ type: "text", text: `✨ ${reflections.length} reflections generated:\n\n${formatted}` }],
553
+ details: { count: reflections.length, reflections },
554
+ };
555
+ },
556
+ });
557
+ // TOOL: Plan
558
+ pi.registerTool({
559
+ name: "generative_plan",
560
+ label: "🧠 Create Plan",
561
+ description: "Create daily plan based on memories and traits.",
562
+ parameters: Type.Object({
563
+ type: Type.String({ description: "Plan type: daily, action, project" }),
564
+ context: Type.Optional(Type.String({ description: "Current context for planning" })),
565
+ }),
566
+ async execute(_id, params, _signal, _onUpdate, ctx) {
567
+ const agent = getShalomAgent(ctx);
568
+ let plan;
569
+ if (params.type === "daily") {
570
+ plan = await agent.createDailyPlan();
571
+ }
572
+ else {
573
+ // For action plans, need parent daily plan
574
+ const daily = db.query(`SELECT id FROM plans WHERE agent_id = ? AND type = 'daily' ORDER BY timestamp DESC LIMIT 1`).get(agent.id);
575
+ if (!daily) {
576
+ return {
577
+ content: [{ type: "text", text: "āŒ Create daily plan first" }],
578
+ details: { error: "no_daily_plan" },
579
+ };
580
+ }
581
+ const actionPlans = await agent.createActionPlan(daily.id, params.context || "Current work");
582
+ return {
583
+ content: [{ type: "text", text: `šŸ“‹ ${actionPlans.length} action steps created` }],
584
+ details: { count: actionPlans.length, plans: actionPlans },
585
+ };
586
+ }
587
+ return {
588
+ content: [{ type: "text", text: `šŸ“‹ Daily plan created:\n\n${plan.content}` }],
589
+ details: plan,
590
+ };
591
+ },
592
+ });
593
+ // TOOL: Decide
594
+ pi.registerTool({
595
+ name: "generative_decide",
596
+ label: "🧠 Decide Action",
597
+ description: "Use memories and reflections to decide next action. Returns action + reasoning.",
598
+ parameters: Type.Object({
599
+ context: Type.String({ description: "Current situation context" }),
600
+ }),
601
+ async execute(_id, params, _signal, _onUpdate, ctx) {
602
+ const agent = getShalomAgent(ctx);
603
+ const decision = await agent.decide(params.context);
604
+ return {
605
+ content: [{ type: "text", text: `šŸŽÆ Decision: ${decision.action}\n\nšŸ’­ Reasoning: ${decision.reasoning}` }],
606
+ details: decision,
607
+ };
608
+ },
609
+ });
610
+ // COMMANDS
611
+ pi.registerCommand("agent-memories", {
612
+ description: "Show recent memory stream",
613
+ handler: async (_args, ctx) => {
614
+ const agent = getShalomAgent(ctx);
615
+ const memories = db.query(`
616
+ SELECT timestamp, content, type, importance FROM memory_stream
617
+ WHERE agent_id = ? ORDER BY timestamp DESC LIMIT 10
618
+ `).all(agent.id);
619
+ ctx.ui.notify(memories.map((m, i) => `${i + 1}. [${m.type}] ${m.content.slice(0, 40)}... (${m.importance}/10)`).join("\n") || "No memories yet", "info");
620
+ },
621
+ });
622
+ pi.registerCommand("agent-reflections", {
623
+ description: "Show agent's reflections/insights",
624
+ handler: async (_args, ctx) => {
625
+ const agent = getShalomAgent(ctx);
626
+ const reflections = db.query(`
627
+ SELECT timestamp, insight, depth FROM reflections
628
+ WHERE agent_id = ? ORDER BY timestamp DESC LIMIT 10
629
+ `).all(agent.id);
630
+ ctx.ui.notify(reflections.map((r, i) => `${i + 1}. ${r.insight.slice(0, 60)}... (depth ${r.depth})`).join("\n") || "No reflections yet", "info");
631
+ },
632
+ });
633
+ pi.registerCommand("agent-plans", {
634
+ description: "Show agent's plans",
635
+ handler: async (_args, ctx) => {
636
+ const agent = getShalomAgent(ctx);
637
+ const plans = db.query(`
638
+ SELECT content, type, status FROM plans
639
+ WHERE agent_id = ? ORDER BY timestamp DESC LIMIT 10
640
+ `).all(agent.id);
641
+ ctx.ui.notify(plans.map((p, i) => `${i + 1}. [${p.type}] ${p.content.slice(0, 40)}... (${p.status})`).join("\n") || "No plans yet", "info");
642
+ },
643
+ });
644
+ pi.registerCommand("agent-status", {
645
+ description: "Show agent state and stats",
646
+ handler: async (_args, ctx) => {
647
+ const agent = getShalomAgent(ctx);
648
+ const stats = db.query(`
649
+ SELECT observation_count, last_reflection_at FROM agents WHERE id = ?
650
+ `).get(agent.id);
651
+ const memoryCount = db.query(`SELECT COUNT(*) as count FROM memory_stream WHERE agent_id = ?`).get(agent.id);
652
+ const reflectionCount = db.query(`SELECT COUNT(*) as count FROM reflections WHERE agent_id = ?`).get(agent.id);
653
+ ctx.ui.notify(`🧠 Agent: ${agent.name}\n` +
654
+ ` Traits: ${agent.traits.join(', ')}\n` +
655
+ ` Observations: ${stats?.observation_count || 0}\n` +
656
+ ` Memories: ${memoryCount.count}\n` +
657
+ ` Reflections: ${reflectionCount.count}\n` +
658
+ ` Last reflection: ${stats?.last_reflection_at ? new Date(stats.last_reflection_at).toLocaleDateString() : 'Never'}`, "info");
659
+ },
660
+ });
661
+ // AUTO-OBSERVATION: Hook into session events
662
+ // Record user input as observations
663
+ pi.on("input", async (event, ctx) => {
664
+ try {
665
+ const agent = getShalomAgent(ctx);
666
+ await agent.observe(`User input: ${event.text.slice(0, 200)}`, {
667
+ sessionId: ctx.sessionManager?.getSessionId?.(),
668
+ location: 'conversation'
669
+ });
670
+ }
671
+ catch (e) {
672
+ // Silent fail - don't block the conversation
673
+ }
674
+ });
675
+ // Record tool executions as observations
676
+ pi.on("tool_execution_end", async (event, ctx) => {
677
+ try {
678
+ const agent = getShalomAgent(ctx);
679
+ await agent.observe(`Executed tool: ${event.toolName}${event.isError ? ' (failed)' : ''}`, {
680
+ sessionId: ctx.sessionManager?.getSessionId?.(),
681
+ location: 'tool_execution'
682
+ });
683
+ }
684
+ catch (e) {
685
+ // Silent fail
686
+ }
687
+ });
688
+ // Record agent loop completion
689
+ pi.on("agent_end", async (_event, ctx) => {
690
+ try {
691
+ const agent = getShalomAgent(ctx);
692
+ await agent.act(`Completed agent response`);
693
+ }
694
+ catch (e) {
695
+ // Silent fail
696
+ }
697
+ });
698
+ console.log("[🧠 Generative Agents] Ready");
699
+ console.log(" Tools: generative_observe, generative_think, generative_recall");
700
+ console.log(" generative_reflect, generative_plan, generative_decide");
701
+ console.log(" Commands: /agent-memories, /agent-reflections, /agent-plans, /agent-status");
702
+ console.log(" Auto-observation: input, tool_execution_end, agent_end");
703
+ }
704
+ //# sourceMappingURL=generative-agents-extension.js.map