claude-cognitive 0.3.2 → 0.4.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.
- package/README.md +57 -35
- package/dist/agents/context.d.ts.map +1 -1
- package/dist/agents/context.js +7 -7
- package/dist/agents/context.js.map +1 -1
- package/dist/cli/commands/feedback-stats.d.ts +10 -0
- package/dist/cli/commands/feedback-stats.d.ts.map +1 -0
- package/dist/cli/commands/feedback-stats.js +70 -0
- package/dist/cli/commands/feedback-stats.js.map +1 -0
- package/dist/cli/commands/feedback-sync.d.ts +10 -0
- package/dist/cli/commands/feedback-sync.d.ts.map +1 -0
- package/dist/cli/commands/feedback-sync.js +117 -0
- package/dist/cli/commands/feedback-sync.js.map +1 -0
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +130 -4
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/semantic.d.ts +4 -2
- package/dist/cli/commands/semantic.d.ts.map +1 -1
- package/dist/cli/commands/semantic.js +8 -57
- package/dist/cli/commands/semantic.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +0 -2
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync.d.ts +4 -2
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +9 -250
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +106 -1
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/output.d.ts +0 -2
- package/dist/cli/utils/output.d.ts.map +1 -1
- package/dist/cli/utils/output.js +0 -2
- package/dist/cli/utils/output.js.map +1 -1
- package/dist/client.d.ts +121 -22
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +209 -36
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +30 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/feedback/constants.d.ts +72 -0
- package/dist/feedback/constants.d.ts.map +1 -0
- package/dist/feedback/constants.js +145 -0
- package/dist/feedback/constants.js.map +1 -0
- package/dist/feedback/detector.d.ts +75 -0
- package/dist/feedback/detector.d.ts.map +1 -0
- package/dist/feedback/detector.js +393 -0
- package/dist/feedback/detector.js.map +1 -0
- package/dist/feedback/index.d.ts +85 -0
- package/dist/feedback/index.d.ts.map +1 -0
- package/dist/feedback/index.js +194 -0
- package/dist/feedback/index.js.map +1 -0
- package/dist/feedback/offline-queue.d.ts +110 -0
- package/dist/feedback/offline-queue.d.ts.map +1 -0
- package/dist/feedback/offline-queue.js +188 -0
- package/dist/feedback/offline-queue.js.map +1 -0
- package/dist/feedback/scorer.d.ts +81 -0
- package/dist/feedback/scorer.d.ts.map +1 -0
- package/dist/feedback/scorer.js +194 -0
- package/dist/feedback/scorer.js.map +1 -0
- package/dist/feedback/similarity.d.ts +19 -0
- package/dist/feedback/similarity.d.ts.map +1 -0
- package/dist/feedback/similarity.js +49 -0
- package/dist/feedback/similarity.js.map +1 -0
- package/dist/feedback/tracker.d.ts +104 -0
- package/dist/feedback/tracker.d.ts.map +1 -0
- package/dist/feedback/tracker.js +324 -0
- package/dist/feedback/tracker.js.map +1 -0
- package/dist/hooks/process-session.d.ts.map +1 -1
- package/dist/hooks/process-session.js +17 -8
- package/dist/hooks/process-session.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/learn/index.d.ts.map +1 -1
- package/dist/learn/index.js +6 -2
- package/dist/learn/index.js.map +1 -1
- package/dist/mcp/handlers.d.ts +9 -1
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js +66 -0
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +42 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +77 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +30 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/types.d.ts +16 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mind.d.ts +47 -28
- package/dist/mind.d.ts.map +1 -1
- package/dist/mind.js +351 -104
- package/dist/mind.js.map +1 -1
- package/dist/offline.d.ts +111 -0
- package/dist/offline.d.ts.map +1 -0
- package/dist/offline.js +198 -0
- package/dist/offline.js.map +1 -0
- package/dist/types.d.ts +171 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/mind.js
CHANGED
|
@@ -9,8 +9,9 @@ import { HindsightClient } from "./client.js";
|
|
|
9
9
|
import { loadConfig } from "./config.js";
|
|
10
10
|
import { HindsightError } from "./errors.js";
|
|
11
11
|
import { TypedEventEmitter } from "./events.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { createFeedbackService } from "./feedback/index.js";
|
|
13
|
+
import { OfflineFeedbackQueue } from "./feedback/offline-queue.js";
|
|
14
|
+
import { OfflineMemoryStore } from "./offline.js";
|
|
14
15
|
// ============================================
|
|
15
16
|
// Default Values
|
|
16
17
|
// ============================================
|
|
@@ -63,10 +64,14 @@ export class Mind extends TypedEventEmitter {
|
|
|
63
64
|
bankId = "";
|
|
64
65
|
disposition = DEFAULT_DISPOSITION;
|
|
65
66
|
background;
|
|
66
|
-
/** Path to semantic memory file. Used in Phase 3. */
|
|
67
|
-
semanticPath = ".claude/memory.md";
|
|
68
67
|
// Client (nullable for graceful degradation)
|
|
69
68
|
client = null;
|
|
69
|
+
// Offline memory store (always available)
|
|
70
|
+
offlineStore = null;
|
|
71
|
+
// Feedback service (optional, enabled via config)
|
|
72
|
+
feedbackService = null;
|
|
73
|
+
// Offline feedback queue (for degraded mode)
|
|
74
|
+
feedbackQueue = null;
|
|
70
75
|
// State
|
|
71
76
|
initialized = false;
|
|
72
77
|
initializing = false;
|
|
@@ -74,11 +79,12 @@ export class Mind extends TypedEventEmitter {
|
|
|
74
79
|
sessionActive = false;
|
|
75
80
|
/** Session start time. Used for duration tracking. */
|
|
76
81
|
sessionStartTime = null;
|
|
82
|
+
/** Current session ID for feedback tracking */
|
|
83
|
+
sessionId = null;
|
|
77
84
|
// Agent templates (loaded in init())
|
|
78
85
|
customAgents = [];
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
promotionManager = null;
|
|
86
|
+
// Context settings
|
|
87
|
+
recentMemoryLimit = 3;
|
|
82
88
|
/**
|
|
83
89
|
* Create a new Mind instance.
|
|
84
90
|
*
|
|
@@ -138,9 +144,6 @@ export class Mind extends TypedEventEmitter {
|
|
|
138
144
|
if (this.pendingOptions.background !== undefined) {
|
|
139
145
|
overrides.background = this.pendingOptions.background;
|
|
140
146
|
}
|
|
141
|
-
if (this.pendingOptions.semanticPath !== undefined) {
|
|
142
|
-
overrides.semantic = { path: this.pendingOptions.semanticPath };
|
|
143
|
-
}
|
|
144
147
|
// Load config with pending options as overrides
|
|
145
148
|
const config = await loadConfig(this.projectPath, overrides);
|
|
146
149
|
// Resolve configuration
|
|
@@ -149,7 +152,7 @@ export class Mind extends TypedEventEmitter {
|
|
|
149
152
|
if (config.background) {
|
|
150
153
|
this.background = config.background;
|
|
151
154
|
}
|
|
152
|
-
this.
|
|
155
|
+
this.recentMemoryLimit = config.context?.recentMemoryLimit ?? 3;
|
|
153
156
|
// Create HindsightClient
|
|
154
157
|
const clientOptions = {
|
|
155
158
|
host: config.hindsight.host,
|
|
@@ -173,21 +176,17 @@ export class Mind extends TypedEventEmitter {
|
|
|
173
176
|
}
|
|
174
177
|
// Load custom agents from .claude/agents/
|
|
175
178
|
this.customAgents = await loadCustomAgents(this.projectPath);
|
|
176
|
-
//
|
|
177
|
-
this.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (this.semantic.isLoaded()) {
|
|
187
|
-
this.promotionManager = new PromotionManager(this.semantic, this, {
|
|
188
|
-
threshold: DEFAULT_PROMOTION_THRESHOLD,
|
|
179
|
+
// Initialize offline store (always available)
|
|
180
|
+
this.offlineStore = new OfflineMemoryStore({
|
|
181
|
+
projectPath: this.projectPath,
|
|
182
|
+
});
|
|
183
|
+
// Initialize feedback service if enabled
|
|
184
|
+
if (config.feedback?.enabled) {
|
|
185
|
+
this.feedbackService = createFeedbackService(config.feedback, this.projectPath);
|
|
186
|
+
// Initialize offline feedback queue alongside the service
|
|
187
|
+
this.feedbackQueue = new OfflineFeedbackQueue({
|
|
188
|
+
projectPath: this.projectPath,
|
|
189
189
|
});
|
|
190
|
-
this.promotionManager.startListening();
|
|
191
190
|
}
|
|
192
191
|
// Mark initialized
|
|
193
192
|
this.initialized = true;
|
|
@@ -273,30 +272,43 @@ export class Mind extends TypedEventEmitter {
|
|
|
273
272
|
}
|
|
274
273
|
this.sessionActive = true;
|
|
275
274
|
this.sessionStartTime = new Date();
|
|
275
|
+
this.sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
276
276
|
const contextParts = [];
|
|
277
277
|
// Add agent orchestration instructions
|
|
278
278
|
const agentInstructions = this.formatAgentInstructions();
|
|
279
279
|
if (agentInstructions.trim().length > 0) {
|
|
280
280
|
contextParts.push(agentInstructions);
|
|
281
281
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
// Recall recent experiences
|
|
283
|
+
// Only fetch a small number (default 3) to keep context small
|
|
284
|
+
if (this.recentMemoryLimit > 0) {
|
|
285
|
+
if (!this.degraded && this.client) {
|
|
286
|
+
// Online mode: fetch from Hindsight
|
|
287
|
+
try {
|
|
288
|
+
const recent = await this.client.recent(this.bankId, this.recentMemoryLimit);
|
|
289
|
+
if (recent.length > 0) {
|
|
290
|
+
this.emit("memory:recalled", recent);
|
|
291
|
+
contextParts.push(this.formatRecentMemories(recent));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
this.handleError(error, "onSessionStart recall");
|
|
296
|
+
// Fall through to offline on error
|
|
296
297
|
}
|
|
297
298
|
}
|
|
298
|
-
|
|
299
|
-
|
|
299
|
+
// Offline/degraded mode: fetch from local storage
|
|
300
|
+
if (this.degraded && this.offlineStore) {
|
|
301
|
+
try {
|
|
302
|
+
const offlineRecent = await this.offlineStore.getRecent(this.recentMemoryLimit);
|
|
303
|
+
if (offlineRecent.length > 0) {
|
|
304
|
+
const memories = offlineRecent.map(OfflineMemoryStore.toMemory);
|
|
305
|
+
this.emit("memory:recalled", memories);
|
|
306
|
+
contextParts.push(this.formatRecentMemories(memories));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
311
|
+
}
|
|
300
312
|
}
|
|
301
313
|
}
|
|
302
314
|
return contextParts.join("\n\n");
|
|
@@ -305,8 +317,8 @@ export class Mind extends TypedEventEmitter {
|
|
|
305
317
|
* Called at session end. Reflects on session and stores observations.
|
|
306
318
|
*
|
|
307
319
|
* This method:
|
|
308
|
-
* 1. Retains transcript if provided
|
|
309
|
-
* 2. Reflects on the session
|
|
320
|
+
* 1. Retains transcript if provided (online or offline)
|
|
321
|
+
* 2. Reflects on the session (online only)
|
|
310
322
|
* 3. Emits opinion events
|
|
311
323
|
*
|
|
312
324
|
* @param transcript - Optional session transcript to store
|
|
@@ -315,20 +327,80 @@ export class Mind extends TypedEventEmitter {
|
|
|
315
327
|
async onSessionEnd(transcript) {
|
|
316
328
|
this.assertInitialized();
|
|
317
329
|
// Retain transcript if provided
|
|
318
|
-
if (transcript
|
|
330
|
+
if (transcript) {
|
|
331
|
+
if (!this.degraded && this.client) {
|
|
332
|
+
// Online mode: store to Hindsight
|
|
333
|
+
try {
|
|
334
|
+
await this.client.retain({
|
|
335
|
+
bankId: this.bankId,
|
|
336
|
+
content: transcript,
|
|
337
|
+
context: "Session transcript",
|
|
338
|
+
});
|
|
339
|
+
this.emit("memory:retained", transcript);
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
this.handleError(error, "onSessionEnd retain");
|
|
343
|
+
// Fall through to offline on error
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Degraded/offline mode: store locally
|
|
347
|
+
if (this.degraded && this.offlineStore) {
|
|
348
|
+
try {
|
|
349
|
+
await this.offlineStore.retain(transcript, "experience", {
|
|
350
|
+
context: "Session transcript",
|
|
351
|
+
});
|
|
352
|
+
this.emit("memory:retained", transcript);
|
|
353
|
+
this.emit("offline:stored", {
|
|
354
|
+
content: transcript,
|
|
355
|
+
factType: "experience",
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Process feedback if enabled
|
|
364
|
+
if (this.feedbackService && this.sessionId && transcript) {
|
|
319
365
|
try {
|
|
320
|
-
await this.
|
|
321
|
-
|
|
366
|
+
const feedbackResult = await this.feedbackService.processFeedback(this.sessionId, {
|
|
367
|
+
conversationText: transcript,
|
|
368
|
+
});
|
|
369
|
+
if (feedbackResult.success && feedbackResult.feedback.length > 0) {
|
|
370
|
+
if (!this.degraded && this.client) {
|
|
371
|
+
// Online mode: Submit feedback signals to Hindsight
|
|
372
|
+
await this.client.signal({
|
|
373
|
+
bankId: this.bankId,
|
|
374
|
+
signals: feedbackResult.feedback,
|
|
375
|
+
});
|
|
376
|
+
this.emit("feedback:processed", {
|
|
377
|
+
sessionId: this.sessionId,
|
|
378
|
+
summary: feedbackResult.summary,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
else if (this.feedbackQueue) {
|
|
382
|
+
// Degraded mode: Queue signals for later sync
|
|
383
|
+
await this.feedbackQueue.enqueueBatch(feedbackResult.feedback);
|
|
384
|
+
this.emit("feedback:queued", {
|
|
385
|
+
sessionId: this.sessionId,
|
|
386
|
+
count: feedbackResult.feedback.length,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
322
390
|
}
|
|
323
391
|
catch (error) {
|
|
324
|
-
|
|
392
|
+
// Silently ignore feedback processing errors - non-critical
|
|
393
|
+
this.emit("error", error instanceof Error ? error : new Error("Feedback processing failed"));
|
|
325
394
|
}
|
|
326
395
|
}
|
|
327
396
|
// Reflect on session (if not degraded)
|
|
328
397
|
let result = null;
|
|
329
398
|
if (!this.degraded && this.client) {
|
|
330
399
|
try {
|
|
331
|
-
result = await this.client.reflect(
|
|
400
|
+
result = await this.client.reflect({
|
|
401
|
+
bankId: this.bankId,
|
|
402
|
+
query: "What insights can I draw from this session?",
|
|
403
|
+
});
|
|
332
404
|
// Emit opinion events
|
|
333
405
|
for (const opinion of result.opinions) {
|
|
334
406
|
this.emit("opinion:formed", opinion);
|
|
@@ -338,13 +410,10 @@ export class Mind extends TypedEventEmitter {
|
|
|
338
410
|
this.handleError(error, "onSessionEnd reflect");
|
|
339
411
|
}
|
|
340
412
|
}
|
|
341
|
-
// Clear promotion cache for next session
|
|
342
|
-
if (this.promotionManager) {
|
|
343
|
-
this.promotionManager.clearCache();
|
|
344
|
-
}
|
|
345
413
|
// Reset session state
|
|
346
414
|
this.sessionActive = false;
|
|
347
415
|
this.sessionStartTime = null;
|
|
416
|
+
this.sessionId = null;
|
|
348
417
|
return result;
|
|
349
418
|
}
|
|
350
419
|
// ============================================
|
|
@@ -353,7 +422,7 @@ export class Mind extends TypedEventEmitter {
|
|
|
353
422
|
/**
|
|
354
423
|
* Search memories for relevant context.
|
|
355
424
|
*
|
|
356
|
-
* In degraded mode,
|
|
425
|
+
* In degraded mode, searches offline storage (text-based).
|
|
357
426
|
*
|
|
358
427
|
* @param query - What to search for
|
|
359
428
|
* @param options - Search options (budget, factType, etc.)
|
|
@@ -361,19 +430,67 @@ export class Mind extends TypedEventEmitter {
|
|
|
361
430
|
*/
|
|
362
431
|
async recall(query, options) {
|
|
363
432
|
this.assertInitialized();
|
|
364
|
-
//
|
|
365
|
-
if (this.degraded
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
433
|
+
// Online mode: use Hindsight
|
|
434
|
+
if (!this.degraded && this.client) {
|
|
435
|
+
try {
|
|
436
|
+
// Build recall input, only including defined properties
|
|
437
|
+
const recallInput = {
|
|
438
|
+
bankId: this.bankId,
|
|
439
|
+
query,
|
|
440
|
+
};
|
|
441
|
+
if (options?.budget !== undefined)
|
|
442
|
+
recallInput.budget = options.budget;
|
|
443
|
+
if (options?.factType !== undefined)
|
|
444
|
+
recallInput.factType = options.factType;
|
|
445
|
+
if (options?.maxTokens !== undefined)
|
|
446
|
+
recallInput.maxTokens = options.maxTokens;
|
|
447
|
+
if (options?.includeEntities !== undefined)
|
|
448
|
+
recallInput.includeEntities = options.includeEntities;
|
|
449
|
+
if (options?.boostByUsefulness !== undefined)
|
|
450
|
+
recallInput.boostByUsefulness = options.boostByUsefulness;
|
|
451
|
+
if (options?.usefulnessWeight !== undefined)
|
|
452
|
+
recallInput.usefulnessWeight = options.usefulnessWeight;
|
|
453
|
+
if (options?.minUsefulness !== undefined)
|
|
454
|
+
recallInput.minUsefulness = options.minUsefulness;
|
|
455
|
+
const memories = await this.client.recall(recallInput);
|
|
456
|
+
this.emit("memory:recalled", memories);
|
|
457
|
+
// Track recall for feedback if enabled
|
|
458
|
+
if (this.feedbackService && this.sessionId && memories.length > 0) {
|
|
459
|
+
try {
|
|
460
|
+
await this.feedbackService.trackRecall(this.sessionId, query, memories);
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
// Silently ignore feedback tracking errors
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return memories;
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
this.handleError(error, "recall");
|
|
470
|
+
// Fall through to offline on error
|
|
471
|
+
}
|
|
372
472
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
473
|
+
// Degraded/offline mode: use local storage
|
|
474
|
+
if (this.offlineStore) {
|
|
475
|
+
try {
|
|
476
|
+
const recallOptions = {
|
|
477
|
+
limit: options?.maxTokens ? Math.floor(options.maxTokens / 100) : 10,
|
|
478
|
+
};
|
|
479
|
+
if (options?.factType) {
|
|
480
|
+
recallOptions.factType = options.factType;
|
|
481
|
+
}
|
|
482
|
+
const offlineMemories = await this.offlineStore.recall(query, recallOptions);
|
|
483
|
+
const memories = offlineMemories.map(OfflineMemoryStore.toMemory);
|
|
484
|
+
if (memories.length > 0) {
|
|
485
|
+
this.emit("memory:recalled", memories);
|
|
486
|
+
}
|
|
487
|
+
return memories;
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
491
|
+
}
|
|
376
492
|
}
|
|
493
|
+
return [];
|
|
377
494
|
}
|
|
378
495
|
/**
|
|
379
496
|
* Reason about accumulated knowledge and form opinions.
|
|
@@ -388,30 +505,78 @@ export class Mind extends TypedEventEmitter {
|
|
|
388
505
|
if (this.degraded || !this.client) {
|
|
389
506
|
throw new HindsightError("reflect() requires Hindsight connection", "HINDSIGHT_UNAVAILABLE", { isRetryable: false });
|
|
390
507
|
}
|
|
391
|
-
const result = await this.client.reflect(
|
|
508
|
+
const result = await this.client.reflect({
|
|
509
|
+
bankId: this.bankId,
|
|
510
|
+
query,
|
|
511
|
+
});
|
|
392
512
|
// Emit opinion events
|
|
393
513
|
for (const opinion of result.opinions) {
|
|
394
514
|
this.emit("opinion:formed", opinion);
|
|
395
515
|
}
|
|
396
516
|
return result;
|
|
397
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Submit feedback signals for recalled facts.
|
|
520
|
+
*
|
|
521
|
+
* @param signals - Array of signal items with factId, signalType, etc.
|
|
522
|
+
* @returns Signal result
|
|
523
|
+
* @throws {HindsightError} If in degraded mode (signal requires Hindsight)
|
|
524
|
+
*/
|
|
525
|
+
async signal(signals) {
|
|
526
|
+
this.assertInitialized();
|
|
527
|
+
if (this.degraded || !this.client) {
|
|
528
|
+
throw new HindsightError("signal() requires Hindsight connection", "HINDSIGHT_UNAVAILABLE", { isRetryable: false });
|
|
529
|
+
}
|
|
530
|
+
return this.client.signal({
|
|
531
|
+
bankId: this.bankId,
|
|
532
|
+
signals,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
398
535
|
/**
|
|
399
536
|
* Store content in memory.
|
|
400
537
|
*
|
|
401
|
-
* In degraded mode,
|
|
538
|
+
* In degraded mode, stores to offline storage for later sync.
|
|
402
539
|
*
|
|
403
540
|
* @param content - Content to store
|
|
404
541
|
* @param context - Optional additional context
|
|
542
|
+
* @param factType - Memory type (default: experience)
|
|
405
543
|
*/
|
|
406
|
-
async retain(content, context) {
|
|
544
|
+
async retain(content, context, factType = "experience") {
|
|
407
545
|
this.assertInitialized();
|
|
408
|
-
//
|
|
409
|
-
if (this.degraded
|
|
410
|
-
|
|
411
|
-
|
|
546
|
+
// Online mode: use Hindsight
|
|
547
|
+
if (!this.degraded && this.client) {
|
|
548
|
+
try {
|
|
549
|
+
// Build retain input, only including defined properties
|
|
550
|
+
const retainInput = {
|
|
551
|
+
bankId: this.bankId,
|
|
552
|
+
content,
|
|
553
|
+
};
|
|
554
|
+
if (context !== undefined)
|
|
555
|
+
retainInput.context = context;
|
|
556
|
+
await this.client.retain(retainInput);
|
|
557
|
+
this.emit("memory:retained", content);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
this.handleError(error, "retain");
|
|
562
|
+
// Fall through to offline on error
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// Degraded/offline mode: store locally
|
|
566
|
+
if (this.offlineStore) {
|
|
567
|
+
try {
|
|
568
|
+
const retainOptions = {};
|
|
569
|
+
if (context) {
|
|
570
|
+
retainOptions.context = context;
|
|
571
|
+
}
|
|
572
|
+
await this.offlineStore.retain(content, factType, retainOptions);
|
|
573
|
+
this.emit("memory:retained", content);
|
|
574
|
+
this.emit("offline:stored", { content, factType });
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
578
|
+
}
|
|
412
579
|
}
|
|
413
|
-
await this.client.retain(this.bankId, content, context);
|
|
414
|
-
this.emit("memory:retained", content);
|
|
415
580
|
}
|
|
416
581
|
// ============================================
|
|
417
582
|
// Learn Operation
|
|
@@ -522,6 +687,7 @@ ${template.outputFormat}
|
|
|
522
687
|
}
|
|
523
688
|
/**
|
|
524
689
|
* Attempt to recover from degraded mode.
|
|
690
|
+
* If successful, syncs offline memories to Hindsight.
|
|
525
691
|
*
|
|
526
692
|
* @returns True if recovery was successful
|
|
527
693
|
*/
|
|
@@ -533,10 +699,112 @@ ${template.outputFormat}
|
|
|
533
699
|
if (health.healthy) {
|
|
534
700
|
this.exitDegradedMode();
|
|
535
701
|
await this.ensureBank();
|
|
702
|
+
// Auto-sync offline memories
|
|
703
|
+
await this.syncOfflineMemories();
|
|
704
|
+
// Auto-sync offline feedback signals
|
|
705
|
+
await this.syncOfflineFeedback();
|
|
536
706
|
return true;
|
|
537
707
|
}
|
|
538
708
|
return false;
|
|
539
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* Sync offline memories to Hindsight.
|
|
712
|
+
* Called automatically on recovery from degraded mode.
|
|
713
|
+
*
|
|
714
|
+
* @returns Number of memories synced
|
|
715
|
+
*/
|
|
716
|
+
async syncOfflineMemories() {
|
|
717
|
+
if (!this.client || this.degraded || !this.offlineStore) {
|
|
718
|
+
return 0;
|
|
719
|
+
}
|
|
720
|
+
try {
|
|
721
|
+
await this.offlineStore.recordSyncAttempt();
|
|
722
|
+
const unsynced = await this.offlineStore.getUnsynced();
|
|
723
|
+
if (unsynced.length === 0) {
|
|
724
|
+
return 0;
|
|
725
|
+
}
|
|
726
|
+
const syncedIds = [];
|
|
727
|
+
for (const memory of unsynced) {
|
|
728
|
+
try {
|
|
729
|
+
// Build retain input, only including defined properties
|
|
730
|
+
const retainInput = {
|
|
731
|
+
bankId: this.bankId,
|
|
732
|
+
content: memory.text,
|
|
733
|
+
};
|
|
734
|
+
if (memory.context !== undefined)
|
|
735
|
+
retainInput.context = memory.context;
|
|
736
|
+
await this.client.retain(retainInput);
|
|
737
|
+
syncedIds.push(memory.id);
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
// Stop on error - don't want to skip memories
|
|
741
|
+
this.emit("error", error instanceof Error
|
|
742
|
+
? error
|
|
743
|
+
: new Error(`Failed to sync memory ${memory.id}`));
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (syncedIds.length > 0) {
|
|
748
|
+
await this.offlineStore.markSynced(syncedIds);
|
|
749
|
+
this.emit("offline:synced", { count: syncedIds.length });
|
|
750
|
+
// Clear synced memories to save space
|
|
751
|
+
await this.offlineStore.clearSynced();
|
|
752
|
+
}
|
|
753
|
+
return syncedIds.length;
|
|
754
|
+
}
|
|
755
|
+
catch (error) {
|
|
756
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
757
|
+
return 0;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Sync offline feedback signals to Hindsight.
|
|
762
|
+
* Called automatically on recovery from degraded mode.
|
|
763
|
+
*
|
|
764
|
+
* @returns Number of feedback signals synced
|
|
765
|
+
*/
|
|
766
|
+
async syncOfflineFeedback() {
|
|
767
|
+
if (!this.client || this.degraded || !this.feedbackQueue) {
|
|
768
|
+
return 0;
|
|
769
|
+
}
|
|
770
|
+
try {
|
|
771
|
+
await this.feedbackQueue.recordSyncAttempt();
|
|
772
|
+
const unsynced = await this.feedbackQueue.getUnsynced();
|
|
773
|
+
if (unsynced.length === 0) {
|
|
774
|
+
return 0;
|
|
775
|
+
}
|
|
776
|
+
// Convert offline signals back to SignalItem format
|
|
777
|
+
const signals = unsynced.map(OfflineFeedbackQueue.toSignalItem);
|
|
778
|
+
// Submit all signals in one batch
|
|
779
|
+
await this.client.signal({
|
|
780
|
+
bankId: this.bankId,
|
|
781
|
+
signals,
|
|
782
|
+
});
|
|
783
|
+
// Mark all as synced
|
|
784
|
+
const ids = unsynced.map((s) => s.id);
|
|
785
|
+
await this.feedbackQueue.markSynced(ids);
|
|
786
|
+
this.emit("feedback:synced", { count: ids.length });
|
|
787
|
+
// Clear synced signals to save space
|
|
788
|
+
await this.feedbackQueue.clearSynced();
|
|
789
|
+
return ids.length;
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
this.emit("error", error instanceof Error ? error : new Error(String(error)));
|
|
793
|
+
return 0;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Get offline feedback queue (for CLI commands).
|
|
798
|
+
*/
|
|
799
|
+
getOfflineFeedbackQueue() {
|
|
800
|
+
return this.feedbackQueue;
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Get offline memory store (for CLI commands).
|
|
804
|
+
*/
|
|
805
|
+
getOfflineStore() {
|
|
806
|
+
return this.offlineStore;
|
|
807
|
+
}
|
|
540
808
|
/**
|
|
541
809
|
* Enter degraded mode.
|
|
542
810
|
* @internal
|
|
@@ -603,28 +871,6 @@ ${template.outputFormat}
|
|
|
603
871
|
isSessionActive() {
|
|
604
872
|
return this.sessionActive;
|
|
605
873
|
}
|
|
606
|
-
/**
|
|
607
|
-
* Get the path to semantic memory file.
|
|
608
|
-
*/
|
|
609
|
-
getSemanticPath() {
|
|
610
|
-
return this.semanticPath;
|
|
611
|
-
}
|
|
612
|
-
/**
|
|
613
|
-
* Get the SemanticMemory instance.
|
|
614
|
-
*
|
|
615
|
-
* @returns SemanticMemory or null if not initialized
|
|
616
|
-
*/
|
|
617
|
-
getSemanticMemory() {
|
|
618
|
-
return this.semantic;
|
|
619
|
-
}
|
|
620
|
-
/**
|
|
621
|
-
* Get the PromotionManager instance.
|
|
622
|
-
*
|
|
623
|
-
* @returns PromotionManager or null if not initialized
|
|
624
|
-
*/
|
|
625
|
-
getPromotionManager() {
|
|
626
|
-
return this.promotionManager;
|
|
627
|
-
}
|
|
628
874
|
/**
|
|
629
875
|
* Get the session start time, or null if no session is active.
|
|
630
876
|
*/
|
|
@@ -642,15 +888,18 @@ ${template.outputFormat}
|
|
|
642
888
|
}
|
|
643
889
|
/**
|
|
644
890
|
* Format recent memories as context string.
|
|
891
|
+
* Shows fuller context since we only fetch a few memories.
|
|
645
892
|
* @internal
|
|
646
893
|
*/
|
|
647
894
|
formatRecentMemories(memories) {
|
|
648
895
|
if (memories.length === 0)
|
|
649
896
|
return "";
|
|
650
|
-
const lines = ["
|
|
651
|
-
for (const mem of memories
|
|
897
|
+
const lines = ["## Recent Activity"];
|
|
898
|
+
for (const mem of memories) {
|
|
652
899
|
const date = new Date(mem.createdAt).toLocaleDateString();
|
|
653
|
-
|
|
900
|
+
// Show more text since we're fetching fewer memories
|
|
901
|
+
const maxLen = 200;
|
|
902
|
+
const text = mem.text.length > maxLen ? `${mem.text.slice(0, maxLen)}...` : mem.text;
|
|
654
903
|
lines.push(`- ${date}: ${text}`);
|
|
655
904
|
}
|
|
656
905
|
return lines.join("\n");
|
|
@@ -714,19 +963,17 @@ ${template.outputFormat}
|
|
|
714
963
|
* After calling dispose(), the Mind instance should not be used.
|
|
715
964
|
*/
|
|
716
965
|
dispose() {
|
|
717
|
-
// Stop promotion manager event listeners
|
|
718
|
-
if (this.promotionManager) {
|
|
719
|
-
this.promotionManager.stopListening();
|
|
720
|
-
this.promotionManager = null;
|
|
721
|
-
}
|
|
722
966
|
// Clear references
|
|
723
|
-
this.semantic = null;
|
|
724
967
|
this.client = null;
|
|
968
|
+
this.offlineStore = null;
|
|
969
|
+
this.feedbackService = null;
|
|
970
|
+
this.feedbackQueue = null;
|
|
725
971
|
this.customAgents = [];
|
|
726
972
|
// Reset state
|
|
727
973
|
this.initialized = false;
|
|
728
974
|
this.sessionActive = false;
|
|
729
975
|
this.sessionStartTime = null;
|
|
976
|
+
this.sessionId = null;
|
|
730
977
|
this.degraded = false;
|
|
731
978
|
}
|
|
732
979
|
}
|