@almadar/agent 1.1.4 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +3 -2
- package/dist/agent/index.js +258 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/{firestore-checkpointer-DxbQ10ve.d.ts → firestore-checkpointer-CkNKXoun.d.ts} +1 -1
- package/dist/{index-wLhxy6Gb.d.ts → index-D-Ahuo6F.d.ts} +433 -99
- package/dist/index.d.ts +649 -57
- package/dist/index.js +1411 -1
- package/dist/index.js.map +1 -1
- package/dist/persistence/index.d.ts +2 -2
- package/package.json +17 -16
- package/LICENSE +0 -72
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { MemorySaver } from '@langchain/langgraph';
|
|
|
18
18
|
export { Command } from '@langchain/langgraph';
|
|
19
19
|
import { v4 } from 'uuid';
|
|
20
20
|
import { BaseCheckpointSaver, BaseStore } from '@langchain/langgraph-checkpoint';
|
|
21
|
+
import { EventEmitter } from 'events';
|
|
21
22
|
|
|
22
23
|
var __defProp = Object.defineProperty;
|
|
23
24
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -4431,9 +4432,13 @@ var SessionManager = class {
|
|
|
4431
4432
|
constructor(options = {}) {
|
|
4432
4433
|
this.firestoreCheckpointer = null;
|
|
4433
4434
|
this.firestoreSessionStore = null;
|
|
4435
|
+
this.memoryManager = null;
|
|
4436
|
+
this.compactionConfig = null;
|
|
4434
4437
|
this.mode = options.mode ?? "memory";
|
|
4435
4438
|
this.memoryBackend = new MemorySessionBackend();
|
|
4436
4439
|
this.memoryCheckpointers = /* @__PURE__ */ new Map();
|
|
4440
|
+
this.memoryManager = options.memoryManager ?? null;
|
|
4441
|
+
this.compactionConfig = options.compactionConfig ?? null;
|
|
4437
4442
|
if (this.mode === "firestore" && options.firestoreDb) {
|
|
4438
4443
|
this.firestoreCheckpointer = new FirestoreCheckpointer({ db: options.firestoreDb });
|
|
4439
4444
|
this.firestoreSessionStore = new FirestoreSessionStore({ db: options.firestoreDb });
|
|
@@ -4533,6 +4538,260 @@ var SessionManager = class {
|
|
|
4533
4538
|
}
|
|
4534
4539
|
return this.memoryBackend.list();
|
|
4535
4540
|
}
|
|
4541
|
+
// ============================================================================
|
|
4542
|
+
// Session → Memory Sync (GAP-002D)
|
|
4543
|
+
// ============================================================================
|
|
4544
|
+
/**
|
|
4545
|
+
* Sync a completed session to orbital memory.
|
|
4546
|
+
* This enables the agent to learn from past sessions.
|
|
4547
|
+
*
|
|
4548
|
+
* @param threadId - The session thread ID
|
|
4549
|
+
* @param userId - The user ID for memory association
|
|
4550
|
+
* @param sessionData - Additional session data to record
|
|
4551
|
+
* @returns Promise that resolves when sync is complete
|
|
4552
|
+
*/
|
|
4553
|
+
async syncSessionToMemory(threadId, userId, sessionData) {
|
|
4554
|
+
if (!this.memoryManager) {
|
|
4555
|
+
console.warn("[SessionManager] No memory manager configured, skipping session sync");
|
|
4556
|
+
return;
|
|
4557
|
+
}
|
|
4558
|
+
const metadata = this.get(threadId);
|
|
4559
|
+
if (!metadata) {
|
|
4560
|
+
console.warn(`[SessionManager] Session ${threadId} not found, skipping sync`);
|
|
4561
|
+
return;
|
|
4562
|
+
}
|
|
4563
|
+
try {
|
|
4564
|
+
await this.memoryManager.recordGeneration(userId, {
|
|
4565
|
+
threadId,
|
|
4566
|
+
prompt: sessionData.inputDescription,
|
|
4567
|
+
skill: metadata.skill,
|
|
4568
|
+
generatedSchema: sessionData.generatedOrbital ? { name: sessionData.generatedOrbital } : void 0,
|
|
4569
|
+
patterns: sessionData.patternsUsed ?? [],
|
|
4570
|
+
entities: sessionData.entities ?? [],
|
|
4571
|
+
success: sessionData.success,
|
|
4572
|
+
completedAt: /* @__PURE__ */ new Date()
|
|
4573
|
+
});
|
|
4574
|
+
if (sessionData.patternsUsed && sessionData.patternsUsed.length > 0) {
|
|
4575
|
+
await this.memoryManager.updateUserPreferences(userId, {
|
|
4576
|
+
preferredPatterns: sessionData.patternsUsed,
|
|
4577
|
+
commonEntities: sessionData.entities
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
if (sessionData.entities && sessionData.entities.length > 0) {
|
|
4581
|
+
await this.memoryManager.updateProjectContext(sessionData.appId, {
|
|
4582
|
+
userId,
|
|
4583
|
+
existingEntities: sessionData.entities
|
|
4584
|
+
});
|
|
4585
|
+
}
|
|
4586
|
+
console.log(`[SessionManager] Session ${threadId} synced to memory for user ${userId}`);
|
|
4587
|
+
} catch (error) {
|
|
4588
|
+
console.error("[SessionManager] Failed to sync session to memory:", error);
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
// ============================================================================
|
|
4592
|
+
// Interrupt → Memory Sync (GAP-003: Interrupt Memory)
|
|
4593
|
+
// ============================================================================
|
|
4594
|
+
/**
|
|
4595
|
+
* Record an interrupt decision to memory.
|
|
4596
|
+
* This enables learning from HITL (Human-in-the-Loop) decisions.
|
|
4597
|
+
*
|
|
4598
|
+
* @param sessionId - The session thread ID
|
|
4599
|
+
* @param userId - The user who made the decision
|
|
4600
|
+
* @param interruptData - The interrupt decision data
|
|
4601
|
+
* @returns Promise that resolves when sync is complete
|
|
4602
|
+
*/
|
|
4603
|
+
async recordInterruptDecision(sessionId, userId, interruptData) {
|
|
4604
|
+
if (!this.memoryManager) {
|
|
4605
|
+
console.warn("[SessionManager] No memory manager configured, skipping interrupt sync");
|
|
4606
|
+
return;
|
|
4607
|
+
}
|
|
4608
|
+
try {
|
|
4609
|
+
await this.memoryManager.recordInterruptDecision(sessionId, userId, interruptData);
|
|
4610
|
+
console.log(`[SessionManager] Interrupt recorded for user ${userId}: ${interruptData.toolName} ${interruptData.decision}`);
|
|
4611
|
+
} catch (error) {
|
|
4612
|
+
console.error("[SessionManager] Failed to record interrupt:", error);
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
/**
|
|
4616
|
+
* Get interrupt history for a session.
|
|
4617
|
+
*/
|
|
4618
|
+
async getSessionInterrupts(sessionId) {
|
|
4619
|
+
if (!this.memoryManager) {
|
|
4620
|
+
return [];
|
|
4621
|
+
}
|
|
4622
|
+
return this.memoryManager.getSessionInterrupts(sessionId);
|
|
4623
|
+
}
|
|
4624
|
+
/**
|
|
4625
|
+
* Check if a tool should be auto-approved for a user.
|
|
4626
|
+
*/
|
|
4627
|
+
async shouldAutoApproveTool(userId, toolName) {
|
|
4628
|
+
if (!this.memoryManager) {
|
|
4629
|
+
return false;
|
|
4630
|
+
}
|
|
4631
|
+
return this.memoryManager.shouldAutoApproveTool(userId, toolName);
|
|
4632
|
+
}
|
|
4633
|
+
// ============================================================================
|
|
4634
|
+
// Checkpoint Management (GAP-004: Checkpoint → Memory)
|
|
4635
|
+
// ============================================================================
|
|
4636
|
+
/**
|
|
4637
|
+
* Record a checkpoint to memory for learning.
|
|
4638
|
+
*
|
|
4639
|
+
* @param userId - The user who owns this checkpoint
|
|
4640
|
+
* @param checkpointData - Checkpoint information
|
|
4641
|
+
* @returns Promise that resolves when checkpoint is recorded
|
|
4642
|
+
*/
|
|
4643
|
+
async recordCheckpoint(userId, checkpointData) {
|
|
4644
|
+
if (!this.memoryManager) {
|
|
4645
|
+
console.warn("[SessionManager] No memory manager configured, skipping checkpoint record");
|
|
4646
|
+
return;
|
|
4647
|
+
}
|
|
4648
|
+
try {
|
|
4649
|
+
await this.memoryManager.recordCheckpoint(userId, checkpointData);
|
|
4650
|
+
console.log(`[SessionManager] Checkpoint ${checkpointData.checkpointId} recorded for user ${userId}`);
|
|
4651
|
+
} catch (error) {
|
|
4652
|
+
console.error("[SessionManager] Failed to record checkpoint:", error);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
/**
|
|
4656
|
+
* Record a rollback to a checkpoint.
|
|
4657
|
+
*
|
|
4658
|
+
* @param userId - The user who performed the rollback
|
|
4659
|
+
* @param checkpointId - The checkpoint rolled back to
|
|
4660
|
+
* @param reason - Optional reason for rollback
|
|
4661
|
+
* @returns Promise that resolves when rollback is recorded
|
|
4662
|
+
*/
|
|
4663
|
+
async recordRollback(userId, checkpointId, reason) {
|
|
4664
|
+
if (!this.memoryManager) {
|
|
4665
|
+
console.warn("[SessionManager] No memory manager configured, skipping rollback record");
|
|
4666
|
+
return;
|
|
4667
|
+
}
|
|
4668
|
+
try {
|
|
4669
|
+
await this.memoryManager.recordRollback(userId, checkpointId, reason);
|
|
4670
|
+
console.log(`[SessionManager] Rollback to ${checkpointId} recorded for user ${userId}`);
|
|
4671
|
+
} catch (error) {
|
|
4672
|
+
console.error("[SessionManager] Failed to record rollback:", error);
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
/**
|
|
4676
|
+
* Mark a checkpoint as successful (terminal state).
|
|
4677
|
+
*
|
|
4678
|
+
* @param userId - The user who owns this checkpoint
|
|
4679
|
+
* @param checkpointId - The checkpoint that was successful
|
|
4680
|
+
* @returns Promise that resolves when success is recorded
|
|
4681
|
+
*/
|
|
4682
|
+
async markCheckpointSuccessful(userId, checkpointId) {
|
|
4683
|
+
if (!this.memoryManager) {
|
|
4684
|
+
console.warn("[SessionManager] No memory manager configured, skipping success mark");
|
|
4685
|
+
return;
|
|
4686
|
+
}
|
|
4687
|
+
try {
|
|
4688
|
+
await this.memoryManager.markCheckpointSuccessful(userId, checkpointId);
|
|
4689
|
+
console.log(`[SessionManager] Checkpoint ${checkpointId} marked as successful for user ${userId}`);
|
|
4690
|
+
} catch (error) {
|
|
4691
|
+
console.error("[SessionManager] Failed to mark checkpoint as successful:", error);
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
/**
|
|
4695
|
+
* Get checkpoint history for a thread.
|
|
4696
|
+
*
|
|
4697
|
+
* @param threadId - The thread to get checkpoints for
|
|
4698
|
+
* @returns Array of checkpoint records
|
|
4699
|
+
*/
|
|
4700
|
+
async getThreadCheckpoints(threadId) {
|
|
4701
|
+
if (!this.memoryManager) {
|
|
4702
|
+
return [];
|
|
4703
|
+
}
|
|
4704
|
+
return this.memoryManager.getThreadCheckpoints(threadId);
|
|
4705
|
+
}
|
|
4706
|
+
/**
|
|
4707
|
+
* Get frequently rolled-back checkpoints (problem areas).
|
|
4708
|
+
*
|
|
4709
|
+
* @param userId - The user to get problem checkpoints for
|
|
4710
|
+
* @param minRollbackCount - Minimum rollback count (default: 2)
|
|
4711
|
+
* @returns Array of checkpoint records with rollback issues
|
|
4712
|
+
*/
|
|
4713
|
+
async getProblemCheckpoints(userId, minRollbackCount = 2) {
|
|
4714
|
+
if (!this.memoryManager) {
|
|
4715
|
+
return [];
|
|
4716
|
+
}
|
|
4717
|
+
return this.memoryManager.getProblemCheckpoints(userId, minRollbackCount);
|
|
4718
|
+
}
|
|
4719
|
+
// ============================================================================
|
|
4720
|
+
// Context Compaction (GAP-005)
|
|
4721
|
+
// ============================================================================
|
|
4722
|
+
/**
|
|
4723
|
+
* Get the context compaction configuration.
|
|
4724
|
+
* @returns The compaction configuration or null if not configured.
|
|
4725
|
+
*/
|
|
4726
|
+
getCompactionConfig() {
|
|
4727
|
+
return this.compactionConfig;
|
|
4728
|
+
}
|
|
4729
|
+
/**
|
|
4730
|
+
* Check if a session's messages need compaction based on token count.
|
|
4731
|
+
* Uses character-based estimation for quick checks.
|
|
4732
|
+
*
|
|
4733
|
+
* @param messages - Array of messages to check
|
|
4734
|
+
* @returns True if compaction is recommended
|
|
4735
|
+
*/
|
|
4736
|
+
shouldCompactMessages(messages) {
|
|
4737
|
+
if (!this.compactionConfig) {
|
|
4738
|
+
return false;
|
|
4739
|
+
}
|
|
4740
|
+
const totalChars = messages.reduce((sum, msg) => {
|
|
4741
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
4742
|
+
return sum + content.length;
|
|
4743
|
+
}, 0);
|
|
4744
|
+
const estimatedTokens = totalChars / 4;
|
|
4745
|
+
const threshold = this.compactionConfig.maxTokens ?? 15e4;
|
|
4746
|
+
return estimatedTokens > threshold * 0.8;
|
|
4747
|
+
}
|
|
4748
|
+
/**
|
|
4749
|
+
* Record a compaction event for a session.
|
|
4750
|
+
* This helps track when and why compaction occurs.
|
|
4751
|
+
*
|
|
4752
|
+
* @param threadId - The session thread ID
|
|
4753
|
+
* @param originalMessageCount - Number of messages before compaction
|
|
4754
|
+
* @param compactedMessageCount - Number of messages after compaction
|
|
4755
|
+
* @param reason - Reason for compaction
|
|
4756
|
+
*/
|
|
4757
|
+
async recordCompaction(threadId, originalMessageCount, compactedMessageCount, reason) {
|
|
4758
|
+
if (!this.memoryManager) {
|
|
4759
|
+
console.warn("[SessionManager] No memory manager configured, skipping compaction record");
|
|
4760
|
+
return;
|
|
4761
|
+
}
|
|
4762
|
+
try {
|
|
4763
|
+
const metadata = this.get(threadId);
|
|
4764
|
+
if (metadata) {
|
|
4765
|
+
const compactionInfo = {
|
|
4766
|
+
timestamp: Date.now(),
|
|
4767
|
+
originalMessageCount,
|
|
4768
|
+
compactedMessageCount,
|
|
4769
|
+
reason: reason ?? "token_limit"
|
|
4770
|
+
};
|
|
4771
|
+
const meta = metadata;
|
|
4772
|
+
const existingCompactions = meta.compactions ?? [];
|
|
4773
|
+
meta.compactions = [...existingCompactions, compactionInfo];
|
|
4774
|
+
this.store(threadId, metadata);
|
|
4775
|
+
}
|
|
4776
|
+
console.log(`[SessionManager] Compaction recorded for ${threadId}: ${originalMessageCount} \u2192 ${compactedMessageCount} messages`);
|
|
4777
|
+
} catch (error) {
|
|
4778
|
+
console.error("[SessionManager] Failed to record compaction:", error);
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
/**
|
|
4782
|
+
* Get compaction history for a session.
|
|
4783
|
+
*
|
|
4784
|
+
* @param threadId - The session thread ID
|
|
4785
|
+
* @returns Array of compaction events
|
|
4786
|
+
*/
|
|
4787
|
+
getCompactionHistory(threadId) {
|
|
4788
|
+
const metadata = this.get(threadId);
|
|
4789
|
+
if (!metadata) {
|
|
4790
|
+
return [];
|
|
4791
|
+
}
|
|
4792
|
+
const meta = metadata;
|
|
4793
|
+
return meta.compactions ?? [];
|
|
4794
|
+
}
|
|
4536
4795
|
};
|
|
4537
4796
|
|
|
4538
4797
|
// src/agent/interrupt-config.ts
|
|
@@ -5458,6 +5717,9 @@ var MemoryManager = class {
|
|
|
5458
5717
|
this.projectsCollection = options.projectsCollection ?? "agent_memory_projects";
|
|
5459
5718
|
this.patternsCollection = options.patternsCollection ?? "agent_memory_patterns";
|
|
5460
5719
|
this.feedbackCollection = options.feedbackCollection ?? "agent_memory_feedback";
|
|
5720
|
+
this.interruptsCollection = options.interruptsCollection ?? "agent_memory_interrupts";
|
|
5721
|
+
this.toolPreferencesCollection = options.toolPreferencesCollection ?? "agent_memory_tool_preferences";
|
|
5722
|
+
this.checkpointsCollection = options.checkpointsCollection ?? "agent_memory_checkpoints";
|
|
5461
5723
|
}
|
|
5462
5724
|
// ============================================================================
|
|
5463
5725
|
// User Preferences
|
|
@@ -5742,6 +6004,178 @@ var MemoryManager = class {
|
|
|
5742
6004
|
serializeContext(context) {
|
|
5743
6005
|
return { ...context };
|
|
5744
6006
|
}
|
|
6007
|
+
// ============================================================================
|
|
6008
|
+
// Interrupt Tracking (GAP-003: Interrupt Memory)
|
|
6009
|
+
// ============================================================================
|
|
6010
|
+
/**
|
|
6011
|
+
* Record an interrupt decision for learning
|
|
6012
|
+
*/
|
|
6013
|
+
async recordInterruptDecision(sessionId, userId, interruptData) {
|
|
6014
|
+
const interruptId = `int_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
6015
|
+
const record = {
|
|
6016
|
+
interruptId,
|
|
6017
|
+
sessionId,
|
|
6018
|
+
userId,
|
|
6019
|
+
toolName: interruptData.toolName,
|
|
6020
|
+
toolArgs: interruptData.toolArgs,
|
|
6021
|
+
decision: interruptData.decision,
|
|
6022
|
+
modifiedArgs: interruptData.modifiedArgs,
|
|
6023
|
+
reason: interruptData.reason,
|
|
6024
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
6025
|
+
};
|
|
6026
|
+
await this.db.collection(this.interruptsCollection).doc(interruptId).set(record);
|
|
6027
|
+
await this.updateToolApprovalPreference(userId, interruptData.toolName, interruptData.decision);
|
|
6028
|
+
}
|
|
6029
|
+
/**
|
|
6030
|
+
* Get interrupt history for a session
|
|
6031
|
+
*/
|
|
6032
|
+
async getSessionInterrupts(sessionId) {
|
|
6033
|
+
const snapshot = await this.db.collection(this.interruptsCollection).where("sessionId", "==", sessionId).orderBy("timestamp", "desc").get();
|
|
6034
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6035
|
+
}
|
|
6036
|
+
/**
|
|
6037
|
+
* Get interrupt history for a user
|
|
6038
|
+
*/
|
|
6039
|
+
async getUserInterrupts(userId, limit = 50) {
|
|
6040
|
+
const snapshot = await this.db.collection(this.interruptsCollection).where("userId", "==", userId).orderBy("timestamp", "desc").limit(limit).get();
|
|
6041
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6042
|
+
}
|
|
6043
|
+
/**
|
|
6044
|
+
* Get tool approval preference for a user
|
|
6045
|
+
*/
|
|
6046
|
+
async getToolApprovalPreference(userId, toolName) {
|
|
6047
|
+
const prefId = `${userId}_${toolName}`;
|
|
6048
|
+
const doc = await this.db.collection(this.toolPreferencesCollection).doc(prefId).get();
|
|
6049
|
+
if (!doc.exists) return null;
|
|
6050
|
+
return doc.data();
|
|
6051
|
+
}
|
|
6052
|
+
/**
|
|
6053
|
+
* Update tool approval preference based on interrupt decisions
|
|
6054
|
+
*/
|
|
6055
|
+
async updateToolApprovalPreference(userId, toolName, decision) {
|
|
6056
|
+
const prefId = `${userId}_${toolName}`;
|
|
6057
|
+
const existing = await this.db.collection(this.toolPreferencesCollection).doc(prefId).get();
|
|
6058
|
+
if (!existing.exists) {
|
|
6059
|
+
const pref = {
|
|
6060
|
+
userId,
|
|
6061
|
+
toolName,
|
|
6062
|
+
autoApprove: decision === "approved",
|
|
6063
|
+
confidence: decision === "approved" ? 0.5 : 0,
|
|
6064
|
+
approvedCount: decision === "approved" ? 1 : 0,
|
|
6065
|
+
rejectedCount: decision === "rejected" ? 1 : 0,
|
|
6066
|
+
lastDecisionAt: /* @__PURE__ */ new Date()
|
|
6067
|
+
};
|
|
6068
|
+
await this.db.collection(this.toolPreferencesCollection).doc(prefId).set(pref);
|
|
6069
|
+
} else {
|
|
6070
|
+
const data = existing.data();
|
|
6071
|
+
const approvedCount = data.approvedCount + (decision === "approved" ? 1 : 0);
|
|
6072
|
+
const rejectedCount = data.rejectedCount + (decision === "rejected" ? 1 : 0);
|
|
6073
|
+
const total = approvedCount + rejectedCount;
|
|
6074
|
+
const approvalRate = approvedCount / total;
|
|
6075
|
+
const pref = {
|
|
6076
|
+
...data,
|
|
6077
|
+
approvedCount,
|
|
6078
|
+
rejectedCount,
|
|
6079
|
+
// Auto-approve if approval rate > 80% and at least 5 decisions
|
|
6080
|
+
autoApprove: total >= 5 && approvalRate > 0.8,
|
|
6081
|
+
confidence: Math.min(0.95, total * 0.1),
|
|
6082
|
+
lastDecisionAt: /* @__PURE__ */ new Date()
|
|
6083
|
+
};
|
|
6084
|
+
await this.db.collection(this.toolPreferencesCollection).doc(prefId).set(pref);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
/**
|
|
6088
|
+
* Get all tool approval preferences for a user
|
|
6089
|
+
*/
|
|
6090
|
+
async getUserToolPreferences(userId) {
|
|
6091
|
+
const snapshot = await this.db.collection(this.toolPreferencesCollection).where("userId", "==", userId).get();
|
|
6092
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6093
|
+
}
|
|
6094
|
+
/**
|
|
6095
|
+
* Check if a tool should be auto-approved for a user
|
|
6096
|
+
*/
|
|
6097
|
+
async shouldAutoApproveTool(userId, toolName) {
|
|
6098
|
+
const pref = await this.getToolApprovalPreference(userId, toolName);
|
|
6099
|
+
return pref?.autoApprove ?? false;
|
|
6100
|
+
}
|
|
6101
|
+
// ============================================================================
|
|
6102
|
+
// Checkpoint Tracking (GAP-004: Checkpoint → Memory)
|
|
6103
|
+
// ============================================================================
|
|
6104
|
+
/**
|
|
6105
|
+
* Record a checkpoint for learning
|
|
6106
|
+
*/
|
|
6107
|
+
async recordCheckpoint(userId, checkpointData) {
|
|
6108
|
+
const record = {
|
|
6109
|
+
checkpointId: checkpointData.checkpointId,
|
|
6110
|
+
threadId: checkpointData.threadId,
|
|
6111
|
+
userId,
|
|
6112
|
+
parentCheckpointId: checkpointData.parentCheckpointId,
|
|
6113
|
+
metadata: checkpointData.metadata ?? {},
|
|
6114
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
6115
|
+
rollbackCount: 0,
|
|
6116
|
+
wasSuccessful: false
|
|
6117
|
+
};
|
|
6118
|
+
const docId = `${userId}_${checkpointData.checkpointId}`;
|
|
6119
|
+
await this.db.collection(this.checkpointsCollection).doc(docId).set(record);
|
|
6120
|
+
}
|
|
6121
|
+
/**
|
|
6122
|
+
* Record a rollback to a checkpoint
|
|
6123
|
+
*/
|
|
6124
|
+
async recordRollback(userId, checkpointId, reason) {
|
|
6125
|
+
const docId = `${userId}_${checkpointId}`;
|
|
6126
|
+
const doc = await this.db.collection(this.checkpointsCollection).doc(docId).get();
|
|
6127
|
+
if (doc.exists) {
|
|
6128
|
+
const data = doc.data();
|
|
6129
|
+
await this.db.collection(this.checkpointsCollection).doc(docId).set({
|
|
6130
|
+
...data,
|
|
6131
|
+
rollbackCount: data.rollbackCount + 1,
|
|
6132
|
+
lastRollbackReason: reason,
|
|
6133
|
+
lastRollbackAt: /* @__PURE__ */ new Date()
|
|
6134
|
+
});
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
/**
|
|
6138
|
+
* Mark a checkpoint as successful (terminal state)
|
|
6139
|
+
*/
|
|
6140
|
+
async markCheckpointSuccessful(userId, checkpointId) {
|
|
6141
|
+
const docId = `${userId}_${checkpointId}`;
|
|
6142
|
+
const doc = await this.db.collection(this.checkpointsCollection).doc(docId).get();
|
|
6143
|
+
if (doc.exists) {
|
|
6144
|
+
const data = doc.data();
|
|
6145
|
+
await this.db.collection(this.checkpointsCollection).doc(docId).set({
|
|
6146
|
+
...data,
|
|
6147
|
+
wasSuccessful: true
|
|
6148
|
+
});
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
6151
|
+
/**
|
|
6152
|
+
* Get checkpoint history for a user
|
|
6153
|
+
*/
|
|
6154
|
+
async getUserCheckpoints(userId, limit = 50) {
|
|
6155
|
+
const snapshot = await this.db.collection(this.checkpointsCollection).where("userId", "==", userId).orderBy("createdAt", "desc").limit(limit).get();
|
|
6156
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6157
|
+
}
|
|
6158
|
+
/**
|
|
6159
|
+
* Get checkpoints for a thread
|
|
6160
|
+
*/
|
|
6161
|
+
async getThreadCheckpoints(threadId) {
|
|
6162
|
+
const snapshot = await this.db.collection(this.checkpointsCollection).where("threadId", "==", threadId).orderBy("createdAt", "desc").get();
|
|
6163
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6164
|
+
}
|
|
6165
|
+
/**
|
|
6166
|
+
* Get frequently rolled-back checkpoints (problem areas)
|
|
6167
|
+
*/
|
|
6168
|
+
async getProblemCheckpoints(userId, minRollbackCount = 2) {
|
|
6169
|
+
const snapshot = await this.db.collection(this.checkpointsCollection).where("userId", "==", userId).where("rollbackCount", ">=", minRollbackCount).orderBy("rollbackCount", "desc").get();
|
|
6170
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6171
|
+
}
|
|
6172
|
+
/**
|
|
6173
|
+
* Get successful checkpoint patterns for learning
|
|
6174
|
+
*/
|
|
6175
|
+
async getSuccessfulCheckpoints(userId, limit = 20) {
|
|
6176
|
+
const snapshot = await this.db.collection(this.checkpointsCollection).where("userId", "==", userId).where("wasSuccessful", "==", true).orderBy("createdAt", "desc").limit(limit).get();
|
|
6177
|
+
return snapshot.docs.map((doc) => doc.data());
|
|
6178
|
+
}
|
|
5745
6179
|
};
|
|
5746
6180
|
var PreferenceLearner = class {
|
|
5747
6181
|
constructor(options) {
|
|
@@ -5966,11 +6400,987 @@ Respond in JSON format:
|
|
|
5966
6400
|
function createPreferenceLearner(options) {
|
|
5967
6401
|
return new PreferenceLearner(options);
|
|
5968
6402
|
}
|
|
6403
|
+
|
|
6404
|
+
// src/memory/agentic-search.ts
|
|
6405
|
+
var AgenticSearchEngine = class {
|
|
6406
|
+
constructor(memoryManager) {
|
|
6407
|
+
this.memoryManager = memoryManager;
|
|
6408
|
+
}
|
|
6409
|
+
/**
|
|
6410
|
+
* Perform agentic search through memory
|
|
6411
|
+
*/
|
|
6412
|
+
async search(params) {
|
|
6413
|
+
const startTime = Date.now();
|
|
6414
|
+
const strategy = params.strategy ?? "hybrid";
|
|
6415
|
+
const depth = params.depth ?? 3;
|
|
6416
|
+
const limit = params.limit ?? 10;
|
|
6417
|
+
let results = [];
|
|
6418
|
+
switch (strategy) {
|
|
6419
|
+
case "temporal":
|
|
6420
|
+
results = await this.temporalSearch(params, depth, limit);
|
|
6421
|
+
break;
|
|
6422
|
+
case "semantic":
|
|
6423
|
+
results = await this.semanticSearch(params, depth, limit);
|
|
6424
|
+
break;
|
|
6425
|
+
case "pattern":
|
|
6426
|
+
results = await this.patternSearch(params, depth, limit);
|
|
6427
|
+
break;
|
|
6428
|
+
case "hybrid":
|
|
6429
|
+
default:
|
|
6430
|
+
results = await this.hybridSearch(params, depth, limit);
|
|
6431
|
+
break;
|
|
6432
|
+
}
|
|
6433
|
+
results.sort((a, b) => b.relevance - a.relevance);
|
|
6434
|
+
const insights = this.generateInsights(results, params.query);
|
|
6435
|
+
const duration = Date.now() - startTime;
|
|
6436
|
+
return {
|
|
6437
|
+
results: results.slice(0, limit),
|
|
6438
|
+
insights,
|
|
6439
|
+
metadata: {
|
|
6440
|
+
strategy,
|
|
6441
|
+
depth,
|
|
6442
|
+
duration,
|
|
6443
|
+
totalResults: results.length
|
|
6444
|
+
}
|
|
6445
|
+
};
|
|
6446
|
+
}
|
|
6447
|
+
// ============================================================================
|
|
6448
|
+
// Search Strategies
|
|
6449
|
+
// ============================================================================
|
|
6450
|
+
/**
|
|
6451
|
+
* Temporal search - "What did I do last week?"
|
|
6452
|
+
* Navigates memory by time relationships
|
|
6453
|
+
*/
|
|
6454
|
+
async temporalSearch(params, _depth, limit) {
|
|
6455
|
+
const results = [];
|
|
6456
|
+
const isRecentQuery = /recent|last|latest|newest/i.test(params.query);
|
|
6457
|
+
/week|month|day|ago/i.test(params.query);
|
|
6458
|
+
const sessions = await this.memoryManager.getUserGenerationHistory(params.userId, limit * 2);
|
|
6459
|
+
for (const session of sessions) {
|
|
6460
|
+
let relevance = 0.5;
|
|
6461
|
+
let reasoning = "Recent generation session";
|
|
6462
|
+
if (isRecentQuery) {
|
|
6463
|
+
const daysAgo = (Date.now() - session.createdAt.getTime()) / (1e3 * 60 * 60 * 24);
|
|
6464
|
+
relevance = Math.max(0.1, 1 - daysAgo / 30);
|
|
6465
|
+
reasoning = `Created ${Math.round(daysAgo)} days ago`;
|
|
6466
|
+
}
|
|
6467
|
+
const sessionText = `${session.prompt} ${session.entities.join(" ")} ${session.patterns.join(" ")}`.toLowerCase();
|
|
6468
|
+
const queryTerms = params.query.toLowerCase().split(/\s+/);
|
|
6469
|
+
const termMatches = queryTerms.filter((term) => sessionText.includes(term)).length;
|
|
6470
|
+
relevance += termMatches * 0.1;
|
|
6471
|
+
if (relevance > 0.3) {
|
|
6472
|
+
results.push({
|
|
6473
|
+
type: "session",
|
|
6474
|
+
data: session,
|
|
6475
|
+
relevance: Math.min(1, relevance),
|
|
6476
|
+
reasoning,
|
|
6477
|
+
source: "generation_history",
|
|
6478
|
+
timestamp: session.createdAt
|
|
6479
|
+
});
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
return results;
|
|
6483
|
+
}
|
|
6484
|
+
/**
|
|
6485
|
+
* Semantic search - "How did I handle user roles?"
|
|
6486
|
+
* Reasoning-based understanding of structure
|
|
6487
|
+
*/
|
|
6488
|
+
async semanticSearch(params, _depth, limit) {
|
|
6489
|
+
const results = [];
|
|
6490
|
+
const concepts = this.extractConcepts(params.query);
|
|
6491
|
+
const prefs = await this.memoryManager.getUserPreferences(params.userId);
|
|
6492
|
+
if (prefs) {
|
|
6493
|
+
let relevance = 0.3;
|
|
6494
|
+
const matchedConcepts = [];
|
|
6495
|
+
if (concepts.naming && prefs.namingConvention) {
|
|
6496
|
+
relevance += 0.2;
|
|
6497
|
+
matchedConcepts.push(`uses ${prefs.namingConvention}`);
|
|
6498
|
+
}
|
|
6499
|
+
if (concepts.patterns && prefs.preferredPatterns.length > 0) {
|
|
6500
|
+
const matched = prefs.preferredPatterns.filter(
|
|
6501
|
+
(p) => concepts.patterns?.some((cp) => p.toLowerCase().includes(cp.toLowerCase()))
|
|
6502
|
+
);
|
|
6503
|
+
relevance += matched.length * 0.1;
|
|
6504
|
+
matchedConcepts.push(`patterns: ${matched.join(", ")}`);
|
|
6505
|
+
}
|
|
6506
|
+
if (concepts.entities && prefs.commonEntities.length > 0) {
|
|
6507
|
+
const matched = prefs.commonEntities.filter(
|
|
6508
|
+
(e) => concepts.entities?.some((ce) => e.toLowerCase().includes(ce.toLowerCase()))
|
|
6509
|
+
);
|
|
6510
|
+
relevance += matched.length * 0.1;
|
|
6511
|
+
matchedConcepts.push(`entities: ${matched.join(", ")}`);
|
|
6512
|
+
}
|
|
6513
|
+
if (relevance > 0.4) {
|
|
6514
|
+
results.push({
|
|
6515
|
+
type: "preference",
|
|
6516
|
+
data: prefs,
|
|
6517
|
+
relevance: Math.min(1, relevance),
|
|
6518
|
+
reasoning: `User preferences match query concepts: ${matchedConcepts.join("; ")}`,
|
|
6519
|
+
source: "user_preferences",
|
|
6520
|
+
timestamp: prefs.learnedAt
|
|
6521
|
+
});
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6524
|
+
if (params.appId) {
|
|
6525
|
+
const project = await this.memoryManager.getProjectContext(params.appId);
|
|
6526
|
+
if (project) {
|
|
6527
|
+
let relevance = 0.3;
|
|
6528
|
+
const projectText = `${project.projectName ?? ""} ${project.description ?? ""} ${project.existingEntities.join(" ")}`.toLowerCase();
|
|
6529
|
+
const queryTerms = params.query.toLowerCase().split(/\s+/);
|
|
6530
|
+
const termMatches = queryTerms.filter((term) => projectText.includes(term)).length;
|
|
6531
|
+
relevance += termMatches * 0.15;
|
|
6532
|
+
if (relevance > 0.4) {
|
|
6533
|
+
results.push({
|
|
6534
|
+
type: "project",
|
|
6535
|
+
data: project,
|
|
6536
|
+
relevance: Math.min(1, relevance),
|
|
6537
|
+
reasoning: `Project context contains ${termMatches} matching terms`,
|
|
6538
|
+
source: "project_context",
|
|
6539
|
+
timestamp: project.lastUpdatedAt
|
|
6540
|
+
});
|
|
6541
|
+
}
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
const sessions = await this.memoryManager.getUserGenerationHistory(params.userId, limit);
|
|
6545
|
+
for (const session of sessions) {
|
|
6546
|
+
const sessionText = `${session.prompt} ${session.entities.join(" ")}`.toLowerCase();
|
|
6547
|
+
let relevance = 0.3;
|
|
6548
|
+
if (concepts.entities) {
|
|
6549
|
+
const entityMatches = session.entities.filter(
|
|
6550
|
+
(e) => concepts.entities?.some((ce) => e.toLowerCase().includes(ce.toLowerCase()))
|
|
6551
|
+
);
|
|
6552
|
+
relevance += entityMatches.length * 0.15;
|
|
6553
|
+
}
|
|
6554
|
+
if (concepts.patterns) {
|
|
6555
|
+
const patternMatches = session.patterns.filter(
|
|
6556
|
+
(p) => concepts.patterns?.some((cp) => p.toLowerCase().includes(cp.toLowerCase()))
|
|
6557
|
+
);
|
|
6558
|
+
relevance += patternMatches.length * 0.1;
|
|
6559
|
+
}
|
|
6560
|
+
const queryTerms = params.query.toLowerCase().split(/\s+/);
|
|
6561
|
+
const termMatches = queryTerms.filter((term) => sessionText.includes(term)).length;
|
|
6562
|
+
relevance += termMatches * 0.05;
|
|
6563
|
+
if (relevance > 0.4) {
|
|
6564
|
+
results.push({
|
|
6565
|
+
type: "session",
|
|
6566
|
+
data: session,
|
|
6567
|
+
relevance: Math.min(1, relevance),
|
|
6568
|
+
reasoning: `Session contains relevant entities, patterns, or keywords`,
|
|
6569
|
+
source: "generation_history",
|
|
6570
|
+
timestamp: session.createdAt
|
|
6571
|
+
});
|
|
6572
|
+
}
|
|
6573
|
+
}
|
|
6574
|
+
return results;
|
|
6575
|
+
}
|
|
6576
|
+
/**
|
|
6577
|
+
* Pattern search - "Show me all list views I've built"
|
|
6578
|
+
* Searches for specific patterns and effects
|
|
6579
|
+
*/
|
|
6580
|
+
async patternSearch(params, _depth, limit) {
|
|
6581
|
+
const results = [];
|
|
6582
|
+
const patternTerms = ["list", "form", "detail", "card", "table", "chart", "map", "calendar"];
|
|
6583
|
+
const matchedTerms = patternTerms.filter(
|
|
6584
|
+
(term) => params.query.toLowerCase().includes(term)
|
|
6585
|
+
);
|
|
6586
|
+
const patterns = await this.memoryManager.getUserPatterns(params.userId);
|
|
6587
|
+
for (const pattern of patterns) {
|
|
6588
|
+
let relevance = 0.3;
|
|
6589
|
+
let reasoning = `Pattern usage: ${pattern.usageCount} times`;
|
|
6590
|
+
if (matchedTerms.some((term) => pattern.patternId.toLowerCase().includes(term))) {
|
|
6591
|
+
relevance += 0.3;
|
|
6592
|
+
reasoning += ", matches query pattern type";
|
|
6593
|
+
}
|
|
6594
|
+
const successRate = pattern.usageCount > 0 ? pattern.successCount / pattern.usageCount : 0;
|
|
6595
|
+
relevance += successRate * 0.2;
|
|
6596
|
+
if (relevance > 0.4) {
|
|
6597
|
+
results.push({
|
|
6598
|
+
type: "pattern",
|
|
6599
|
+
data: pattern,
|
|
6600
|
+
relevance: Math.min(1, relevance),
|
|
6601
|
+
reasoning: `${reasoning}, ${Math.round(successRate * 100)}% success rate`,
|
|
6602
|
+
source: "pattern_affinity",
|
|
6603
|
+
timestamp: pattern.lastUsedAt
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
6606
|
+
}
|
|
6607
|
+
const sessions = await this.memoryManager.getUserGenerationHistory(params.userId, limit);
|
|
6608
|
+
for (const session of sessions) {
|
|
6609
|
+
const matchingPatterns = session.patterns.filter(
|
|
6610
|
+
(p) => matchedTerms.some((term) => p.toLowerCase().includes(term))
|
|
6611
|
+
);
|
|
6612
|
+
if (matchingPatterns.length > 0) {
|
|
6613
|
+
results.push({
|
|
6614
|
+
type: "session",
|
|
6615
|
+
data: session,
|
|
6616
|
+
relevance: 0.5 + matchingPatterns.length * 0.1,
|
|
6617
|
+
reasoning: `Session uses patterns: ${matchingPatterns.join(", ")}`,
|
|
6618
|
+
source: "generation_history",
|
|
6619
|
+
timestamp: session.createdAt
|
|
6620
|
+
});
|
|
6621
|
+
}
|
|
6622
|
+
}
|
|
6623
|
+
return results;
|
|
6624
|
+
}
|
|
6625
|
+
/**
|
|
6626
|
+
* Hybrid search - combines all strategies
|
|
6627
|
+
*/
|
|
6628
|
+
async hybridSearch(params, depth, limit) {
|
|
6629
|
+
const [temporal, semantic, pattern] = await Promise.all([
|
|
6630
|
+
this.temporalSearch(params, depth, Math.ceil(limit / 2)),
|
|
6631
|
+
this.semanticSearch(params, depth, Math.ceil(limit / 2)),
|
|
6632
|
+
this.patternSearch(params, depth, Math.ceil(limit / 2))
|
|
6633
|
+
]);
|
|
6634
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6635
|
+
const combined = [];
|
|
6636
|
+
for (const result of [...temporal, ...semantic, ...pattern]) {
|
|
6637
|
+
const key = `${result.type}-${JSON.stringify(result.data)}`;
|
|
6638
|
+
if (!seen.has(key)) {
|
|
6639
|
+
seen.add(key);
|
|
6640
|
+
combined.push(result);
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
return combined;
|
|
6644
|
+
}
|
|
6645
|
+
// ============================================================================
|
|
6646
|
+
// Helpers
|
|
6647
|
+
// ============================================================================
|
|
6648
|
+
/**
|
|
6649
|
+
* Extract semantic concepts from query
|
|
6650
|
+
*/
|
|
6651
|
+
extractConcepts(query) {
|
|
6652
|
+
const lower = query.toLowerCase();
|
|
6653
|
+
return {
|
|
6654
|
+
naming: /naming|case|pascal|camel|snake/i.test(lower),
|
|
6655
|
+
validation: /validat|schema|required|optional/i.test(lower),
|
|
6656
|
+
patterns: ["entity", "list", "form", "detail", "card"].filter((p) => lower.includes(p)),
|
|
6657
|
+
entities: this.extractEntityNames(lower),
|
|
6658
|
+
actions: ["create", "update", "delete", "list", "view"].filter((a) => lower.includes(a))
|
|
6659
|
+
};
|
|
6660
|
+
}
|
|
6661
|
+
/**
|
|
6662
|
+
* Extract potential entity names from query
|
|
6663
|
+
*/
|
|
6664
|
+
extractEntityNames(query) {
|
|
6665
|
+
const words = query.split(/\s+/);
|
|
6666
|
+
const entities = [];
|
|
6667
|
+
for (const word of words) {
|
|
6668
|
+
const clean = word.replace(/[^a-zA-Z]/g, "");
|
|
6669
|
+
if (clean.length > 2 && clean[0] === clean[0].toUpperCase()) {
|
|
6670
|
+
entities.push(clean);
|
|
6671
|
+
}
|
|
6672
|
+
}
|
|
6673
|
+
return entities;
|
|
6674
|
+
}
|
|
6675
|
+
/**
|
|
6676
|
+
* Generate insights from search results
|
|
6677
|
+
*/
|
|
6678
|
+
generateInsights(results, query) {
|
|
6679
|
+
const summary = `Found ${results.length} relevant memory items for "${query}"`;
|
|
6680
|
+
const patternResults = results.filter((r) => r.type === "pattern");
|
|
6681
|
+
const patterns = patternResults.length > 0 ? patternResults.slice(0, 3).map((r) => r.data.patternId) : ["No dominant patterns identified"];
|
|
6682
|
+
const sessions = results.filter((r) => r.type === "session");
|
|
6683
|
+
const trends = [];
|
|
6684
|
+
if (sessions.length > 0) {
|
|
6685
|
+
const timestamps = sessions.filter((s) => s.timestamp).map((s) => s.timestamp.getTime()).sort((a, b) => b - a);
|
|
6686
|
+
if (timestamps.length >= 2) {
|
|
6687
|
+
const avgGap = (timestamps[0] - timestamps[timestamps.length - 1]) / (timestamps.length - 1);
|
|
6688
|
+
const daysGap = avgGap / (1e3 * 60 * 60 * 24);
|
|
6689
|
+
if (daysGap < 7) {
|
|
6690
|
+
trends.push("High activity - multiple sessions per week");
|
|
6691
|
+
} else if (daysGap > 30) {
|
|
6692
|
+
trends.push("Sporadic usage - sessions spread out over time");
|
|
6693
|
+
}
|
|
6694
|
+
}
|
|
6695
|
+
}
|
|
6696
|
+
const suggestions = [];
|
|
6697
|
+
if (patterns.length > 0 && !patterns[0].includes("No dominant")) {
|
|
6698
|
+
suggestions.push(`Consider reusing pattern: ${patterns[0]}`);
|
|
6699
|
+
}
|
|
6700
|
+
const projectResults = results.filter((r) => r.type === "project");
|
|
6701
|
+
if (projectResults.length > 0) {
|
|
6702
|
+
suggestions.push("Review existing project conventions for consistency");
|
|
6703
|
+
}
|
|
6704
|
+
const interruptResults = results.filter((r) => r.type === "interrupt");
|
|
6705
|
+
if (interruptResults.length > 3) {
|
|
6706
|
+
suggestions.push("High interrupt frequency - consider adjusting auto-approval settings");
|
|
6707
|
+
}
|
|
6708
|
+
if (suggestions.length === 0) {
|
|
6709
|
+
suggestions.push("No specific suggestions based on current memory");
|
|
6710
|
+
}
|
|
6711
|
+
return { summary, patterns, trends, suggestions };
|
|
6712
|
+
}
|
|
6713
|
+
};
|
|
6714
|
+
function createAgenticSearchEngine(memoryManager) {
|
|
6715
|
+
return new AgenticSearchEngine(memoryManager);
|
|
6716
|
+
}
|
|
6717
|
+
|
|
6718
|
+
// src/observability.ts
|
|
6719
|
+
var ObservabilityCollector = class {
|
|
6720
|
+
constructor(options = {}) {
|
|
6721
|
+
this.events = [];
|
|
6722
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
6723
|
+
this.maxEvents = options.maxEvents ?? 1e4;
|
|
6724
|
+
}
|
|
6725
|
+
/**
|
|
6726
|
+
* Record an observable event
|
|
6727
|
+
*/
|
|
6728
|
+
recordEvent(event) {
|
|
6729
|
+
const fullEvent = {
|
|
6730
|
+
...event,
|
|
6731
|
+
timestamp: Date.now()
|
|
6732
|
+
};
|
|
6733
|
+
this.events.push(fullEvent);
|
|
6734
|
+
if (this.events.length > this.maxEvents) {
|
|
6735
|
+
this.events = this.events.slice(-this.maxEvents);
|
|
6736
|
+
}
|
|
6737
|
+
this.updateSessionTelemetry(fullEvent);
|
|
6738
|
+
}
|
|
6739
|
+
/**
|
|
6740
|
+
* Start session tracking
|
|
6741
|
+
*/
|
|
6742
|
+
startSession(sessionId, userId) {
|
|
6743
|
+
this.sessions.set(sessionId, {
|
|
6744
|
+
sessionId,
|
|
6745
|
+
userId,
|
|
6746
|
+
startedAt: Date.now(),
|
|
6747
|
+
toolCallCount: 0,
|
|
6748
|
+
llmCallCount: 0,
|
|
6749
|
+
interruptCount: 0,
|
|
6750
|
+
checkpointCount: 0,
|
|
6751
|
+
totalTokens: 0,
|
|
6752
|
+
errors: [],
|
|
6753
|
+
status: "running"
|
|
6754
|
+
});
|
|
6755
|
+
this.recordEvent({
|
|
6756
|
+
type: "session_start",
|
|
6757
|
+
sessionId,
|
|
6758
|
+
userId,
|
|
6759
|
+
payload: { startedAt: Date.now() }
|
|
6760
|
+
});
|
|
6761
|
+
}
|
|
6762
|
+
/**
|
|
6763
|
+
* End session tracking
|
|
6764
|
+
*/
|
|
6765
|
+
endSession(sessionId, status = "completed") {
|
|
6766
|
+
const session = this.sessions.get(sessionId);
|
|
6767
|
+
if (session) {
|
|
6768
|
+
session.endedAt = Date.now();
|
|
6769
|
+
session.status = status;
|
|
6770
|
+
this.recordEvent({
|
|
6771
|
+
type: "session_end",
|
|
6772
|
+
sessionId,
|
|
6773
|
+
userId: session.userId,
|
|
6774
|
+
payload: {
|
|
6775
|
+
status,
|
|
6776
|
+
duration: session.endedAt - session.startedAt,
|
|
6777
|
+
toolCalls: session.toolCallCount,
|
|
6778
|
+
llmCalls: session.llmCallCount
|
|
6779
|
+
},
|
|
6780
|
+
duration: session.endedAt - session.startedAt
|
|
6781
|
+
});
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
/**
|
|
6785
|
+
* Record tool call
|
|
6786
|
+
*/
|
|
6787
|
+
recordToolCall(sessionId, toolName, args, duration) {
|
|
6788
|
+
this.recordEvent({
|
|
6789
|
+
type: "tool_call",
|
|
6790
|
+
sessionId,
|
|
6791
|
+
payload: { tool: toolName, args },
|
|
6792
|
+
duration
|
|
6793
|
+
});
|
|
6794
|
+
const session = this.sessions.get(sessionId);
|
|
6795
|
+
if (session) {
|
|
6796
|
+
session.toolCallCount++;
|
|
6797
|
+
}
|
|
6798
|
+
}
|
|
6799
|
+
/**
|
|
6800
|
+
* Record LLM call
|
|
6801
|
+
*/
|
|
6802
|
+
recordLLMCall(sessionId, model, tokens, duration) {
|
|
6803
|
+
this.recordEvent({
|
|
6804
|
+
type: "llm_call",
|
|
6805
|
+
sessionId,
|
|
6806
|
+
payload: { model, tokens },
|
|
6807
|
+
duration
|
|
6808
|
+
});
|
|
6809
|
+
const session = this.sessions.get(sessionId);
|
|
6810
|
+
if (session) {
|
|
6811
|
+
session.llmCallCount++;
|
|
6812
|
+
session.totalTokens += tokens.input + tokens.output;
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6815
|
+
/**
|
|
6816
|
+
* Record error
|
|
6817
|
+
*/
|
|
6818
|
+
recordError(sessionId, error, context) {
|
|
6819
|
+
this.recordEvent({
|
|
6820
|
+
type: "error",
|
|
6821
|
+
sessionId,
|
|
6822
|
+
payload: { ...context, message: error.message },
|
|
6823
|
+
error: {
|
|
6824
|
+
message: error.message,
|
|
6825
|
+
code: error.code,
|
|
6826
|
+
stack: error.stack
|
|
6827
|
+
}
|
|
6828
|
+
});
|
|
6829
|
+
const session = this.sessions.get(sessionId);
|
|
6830
|
+
if (session) {
|
|
6831
|
+
session.errors.push({
|
|
6832
|
+
timestamp: Date.now(),
|
|
6833
|
+
message: error.message,
|
|
6834
|
+
type: error.constructor.name
|
|
6835
|
+
});
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
/**
|
|
6839
|
+
* Get events by type
|
|
6840
|
+
*/
|
|
6841
|
+
getEvents(type, sessionId) {
|
|
6842
|
+
let filtered = this.events;
|
|
6843
|
+
if (type) {
|
|
6844
|
+
filtered = filtered.filter((e) => e.type === type);
|
|
6845
|
+
}
|
|
6846
|
+
if (sessionId) {
|
|
6847
|
+
filtered = filtered.filter((e) => e.sessionId === sessionId);
|
|
6848
|
+
}
|
|
6849
|
+
return filtered;
|
|
6850
|
+
}
|
|
6851
|
+
/**
|
|
6852
|
+
* Get session telemetry
|
|
6853
|
+
*/
|
|
6854
|
+
getSessionTelemetry(sessionId) {
|
|
6855
|
+
return this.sessions.get(sessionId);
|
|
6856
|
+
}
|
|
6857
|
+
/**
|
|
6858
|
+
* Get all active sessions
|
|
6859
|
+
*/
|
|
6860
|
+
getActiveSessions() {
|
|
6861
|
+
return Array.from(this.sessions.values()).filter((s) => s.status === "running");
|
|
6862
|
+
}
|
|
6863
|
+
/**
|
|
6864
|
+
* Get performance snapshot
|
|
6865
|
+
*/
|
|
6866
|
+
getPerformanceSnapshot() {
|
|
6867
|
+
const now = Date.now();
|
|
6868
|
+
const sessions = Array.from(this.sessions.values());
|
|
6869
|
+
const activeSessions = sessions.filter((s) => s.status === "running");
|
|
6870
|
+
const last24h = sessions.filter((s) => s.startedAt > now - 24 * 60 * 60 * 1e3);
|
|
6871
|
+
const completed24h = last24h.filter((s) => s.status === "completed");
|
|
6872
|
+
const failed24h = last24h.filter((s) => s.status === "failed");
|
|
6873
|
+
const successRate24h = last24h.length > 0 ? completed24h.length / last24h.length * 100 : 0;
|
|
6874
|
+
const errorRate24h = last24h.length > 0 ? failed24h.length / last24h.length * 100 : 0;
|
|
6875
|
+
const completedSessions = sessions.filter((s) => s.endedAt);
|
|
6876
|
+
const avgSessionDuration = completedSessions.length > 0 ? completedSessions.reduce((sum, s) => sum + ((s.endedAt || now) - s.startedAt), 0) / completedSessions.length : 0;
|
|
6877
|
+
const sessionsWithTokens = sessions.filter((s) => s.totalTokens > 0);
|
|
6878
|
+
const avgTokensPerSession = sessionsWithTokens.length > 0 ? sessionsWithTokens.reduce((sum, s) => sum + s.totalTokens, 0) / sessionsWithTokens.length : 0;
|
|
6879
|
+
return {
|
|
6880
|
+
timestamp: now,
|
|
6881
|
+
activeSessions: activeSessions.length,
|
|
6882
|
+
totalSessions: sessions.length,
|
|
6883
|
+
avgSessionDuration,
|
|
6884
|
+
successRate24h,
|
|
6885
|
+
errorRate24h,
|
|
6886
|
+
avgTokensPerSession
|
|
6887
|
+
};
|
|
6888
|
+
}
|
|
6889
|
+
/**
|
|
6890
|
+
* Perform health check
|
|
6891
|
+
*/
|
|
6892
|
+
async healthCheck() {
|
|
6893
|
+
const checks = [];
|
|
6894
|
+
const now = Date.now();
|
|
6895
|
+
checks.push({
|
|
6896
|
+
component: "event_buffer",
|
|
6897
|
+
status: this.events.length < this.maxEvents * 0.9 ? "healthy" : "degraded",
|
|
6898
|
+
responseTime: 0,
|
|
6899
|
+
checkedAt: now
|
|
6900
|
+
});
|
|
6901
|
+
const activeSessions = this.getActiveSessions();
|
|
6902
|
+
const staleSessions = activeSessions.filter(
|
|
6903
|
+
(s) => now - s.startedAt > 30 * 60 * 1e3
|
|
6904
|
+
// 30 minutes
|
|
6905
|
+
);
|
|
6906
|
+
checks.push({
|
|
6907
|
+
component: "sessions",
|
|
6908
|
+
status: staleSessions.length < 10 ? "healthy" : "degraded",
|
|
6909
|
+
responseTime: 0,
|
|
6910
|
+
checkedAt: now,
|
|
6911
|
+
error: staleSessions.length > 0 ? `${staleSessions.length} stale sessions` : void 0
|
|
6912
|
+
});
|
|
6913
|
+
return checks;
|
|
6914
|
+
}
|
|
6915
|
+
/**
|
|
6916
|
+
* Export metrics for external systems
|
|
6917
|
+
*/
|
|
6918
|
+
exportMetrics() {
|
|
6919
|
+
return {
|
|
6920
|
+
events: this.events.slice(-1e3),
|
|
6921
|
+
// Last 1000 events
|
|
6922
|
+
sessions: Array.from(this.sessions.values()),
|
|
6923
|
+
snapshot: this.getPerformanceSnapshot(),
|
|
6924
|
+
health: []
|
|
6925
|
+
// Populated by healthCheck
|
|
6926
|
+
};
|
|
6927
|
+
}
|
|
6928
|
+
/**
|
|
6929
|
+
* Clear all data
|
|
6930
|
+
*/
|
|
6931
|
+
clear() {
|
|
6932
|
+
this.events = [];
|
|
6933
|
+
this.sessions.clear();
|
|
6934
|
+
}
|
|
6935
|
+
// ============================================================================
|
|
6936
|
+
// Private Helpers
|
|
6937
|
+
// ============================================================================
|
|
6938
|
+
updateSessionTelemetry(event) {
|
|
6939
|
+
const session = this.sessions.get(event.sessionId);
|
|
6940
|
+
if (!session) return;
|
|
6941
|
+
switch (event.type) {
|
|
6942
|
+
case "interrupt":
|
|
6943
|
+
session.interruptCount++;
|
|
6944
|
+
break;
|
|
6945
|
+
case "checkpoint_save":
|
|
6946
|
+
session.checkpointCount++;
|
|
6947
|
+
break;
|
|
6948
|
+
}
|
|
6949
|
+
}
|
|
6950
|
+
};
|
|
6951
|
+
var globalCollector = null;
|
|
6952
|
+
function getObservabilityCollector() {
|
|
6953
|
+
if (!globalCollector) {
|
|
6954
|
+
globalCollector = new ObservabilityCollector();
|
|
6955
|
+
}
|
|
6956
|
+
return globalCollector;
|
|
6957
|
+
}
|
|
6958
|
+
function resetObservabilityCollector() {
|
|
6959
|
+
globalCollector = null;
|
|
6960
|
+
}
|
|
6961
|
+
function recordEvent(event) {
|
|
6962
|
+
getObservabilityCollector().recordEvent(event);
|
|
6963
|
+
}
|
|
6964
|
+
function startObservabilitySession(sessionId, userId) {
|
|
6965
|
+
getObservabilityCollector().startSession(sessionId, userId);
|
|
6966
|
+
}
|
|
6967
|
+
function endObservabilitySession(sessionId, status) {
|
|
6968
|
+
getObservabilityCollector().endSession(sessionId, status);
|
|
6969
|
+
}
|
|
6970
|
+
function getPerformanceSnapshot() {
|
|
6971
|
+
return getObservabilityCollector().getPerformanceSnapshot();
|
|
6972
|
+
}
|
|
6973
|
+
|
|
6974
|
+
// src/multi-user.ts
|
|
6975
|
+
var MultiUserManager = class {
|
|
6976
|
+
constructor() {
|
|
6977
|
+
this.sessionOwnership = /* @__PURE__ */ new Map();
|
|
6978
|
+
// threadId -> userId
|
|
6979
|
+
this.userSessions = /* @__PURE__ */ new Map();
|
|
6980
|
+
}
|
|
6981
|
+
// userId -> Set<threadId>
|
|
6982
|
+
/**
|
|
6983
|
+
* Check if a user owns a session
|
|
6984
|
+
*/
|
|
6985
|
+
isSessionOwner(threadId, userId) {
|
|
6986
|
+
const owner = this.sessionOwnership.get(threadId);
|
|
6987
|
+
return owner === userId;
|
|
6988
|
+
}
|
|
6989
|
+
/**
|
|
6990
|
+
* Check if a user can access a session
|
|
6991
|
+
*/
|
|
6992
|
+
canAccessSession(threadId, userContext) {
|
|
6993
|
+
const owner = this.sessionOwnership.get(threadId);
|
|
6994
|
+
if (!owner) {
|
|
6995
|
+
return { allowed: true };
|
|
6996
|
+
}
|
|
6997
|
+
if (owner === userContext.userId) {
|
|
6998
|
+
return { allowed: true };
|
|
6999
|
+
}
|
|
7000
|
+
if (userContext.roles?.includes("admin")) {
|
|
7001
|
+
return { allowed: true };
|
|
7002
|
+
}
|
|
7003
|
+
if (userContext.sessionScope?.includes(threadId)) {
|
|
7004
|
+
return { allowed: true };
|
|
7005
|
+
}
|
|
7006
|
+
return {
|
|
7007
|
+
allowed: false,
|
|
7008
|
+
reason: "Session does not belong to user",
|
|
7009
|
+
requiredRole: "owner or admin"
|
|
7010
|
+
};
|
|
7011
|
+
}
|
|
7012
|
+
/**
|
|
7013
|
+
* Assign session ownership
|
|
7014
|
+
*/
|
|
7015
|
+
assignSessionOwnership(threadId, userId) {
|
|
7016
|
+
const previousOwner = this.sessionOwnership.get(threadId);
|
|
7017
|
+
if (previousOwner) {
|
|
7018
|
+
const previousSessions = this.userSessions.get(previousOwner);
|
|
7019
|
+
if (previousSessions) {
|
|
7020
|
+
previousSessions.delete(threadId);
|
|
7021
|
+
}
|
|
7022
|
+
}
|
|
7023
|
+
this.sessionOwnership.set(threadId, userId);
|
|
7024
|
+
if (!this.userSessions.has(userId)) {
|
|
7025
|
+
this.userSessions.set(userId, /* @__PURE__ */ new Set());
|
|
7026
|
+
}
|
|
7027
|
+
this.userSessions.get(userId).add(threadId);
|
|
7028
|
+
}
|
|
7029
|
+
/**
|
|
7030
|
+
* Get all sessions for a user
|
|
7031
|
+
*/
|
|
7032
|
+
getUserSessions(userId) {
|
|
7033
|
+
const sessions = this.userSessions.get(userId);
|
|
7034
|
+
return sessions ? Array.from(sessions) : [];
|
|
7035
|
+
}
|
|
7036
|
+
/**
|
|
7037
|
+
* Get session owner
|
|
7038
|
+
*/
|
|
7039
|
+
getSessionOwner(threadId) {
|
|
7040
|
+
return this.sessionOwnership.get(threadId);
|
|
7041
|
+
}
|
|
7042
|
+
/**
|
|
7043
|
+
* Remove session ownership
|
|
7044
|
+
*/
|
|
7045
|
+
removeSession(threadId) {
|
|
7046
|
+
const owner = this.sessionOwnership.get(threadId);
|
|
7047
|
+
if (owner) {
|
|
7048
|
+
const sessions = this.userSessions.get(owner);
|
|
7049
|
+
if (sessions) {
|
|
7050
|
+
sessions.delete(threadId);
|
|
7051
|
+
}
|
|
7052
|
+
}
|
|
7053
|
+
this.sessionOwnership.delete(threadId);
|
|
7054
|
+
}
|
|
7055
|
+
/**
|
|
7056
|
+
* Check if user has any sessions
|
|
7057
|
+
*/
|
|
7058
|
+
hasSessions(userId) {
|
|
7059
|
+
const sessions = this.userSessions.get(userId);
|
|
7060
|
+
return sessions ? sessions.size > 0 : false;
|
|
7061
|
+
}
|
|
7062
|
+
/**
|
|
7063
|
+
* Get user session count
|
|
7064
|
+
*/
|
|
7065
|
+
getSessionCount(userId) {
|
|
7066
|
+
const sessions = this.userSessions.get(userId);
|
|
7067
|
+
return sessions ? sessions.size : 0;
|
|
7068
|
+
}
|
|
7069
|
+
/**
|
|
7070
|
+
* Transfer session ownership
|
|
7071
|
+
*/
|
|
7072
|
+
transferOwnership(threadId, fromUserId, toUserId) {
|
|
7073
|
+
const owner = this.sessionOwnership.get(threadId);
|
|
7074
|
+
if (owner !== fromUserId) {
|
|
7075
|
+
return {
|
|
7076
|
+
allowed: false,
|
|
7077
|
+
reason: "Only the owner can transfer session ownership"
|
|
7078
|
+
};
|
|
7079
|
+
}
|
|
7080
|
+
this.assignSessionOwnership(threadId, toUserId);
|
|
7081
|
+
return { allowed: true };
|
|
7082
|
+
}
|
|
7083
|
+
/**
|
|
7084
|
+
* Share session with another user
|
|
7085
|
+
*/
|
|
7086
|
+
shareSession(threadId, ownerId, targetUserId, permission = "read") {
|
|
7087
|
+
const owner = this.sessionOwnership.get(threadId);
|
|
7088
|
+
if (owner !== ownerId) {
|
|
7089
|
+
return {
|
|
7090
|
+
allowed: false,
|
|
7091
|
+
reason: "Only the owner can share a session"
|
|
7092
|
+
};
|
|
7093
|
+
}
|
|
7094
|
+
return { allowed: true };
|
|
7095
|
+
}
|
|
7096
|
+
/**
|
|
7097
|
+
* Get all user IDs with sessions
|
|
7098
|
+
*/
|
|
7099
|
+
getAllUsers() {
|
|
7100
|
+
return Array.from(this.userSessions.keys());
|
|
7101
|
+
}
|
|
7102
|
+
/**
|
|
7103
|
+
* Clear all data (for testing)
|
|
7104
|
+
*/
|
|
7105
|
+
clear() {
|
|
7106
|
+
this.sessionOwnership.clear();
|
|
7107
|
+
this.userSessions.clear();
|
|
7108
|
+
}
|
|
7109
|
+
/**
|
|
7110
|
+
* Create scoped session metadata
|
|
7111
|
+
*/
|
|
7112
|
+
createScopedMetadata(metadata, userContext) {
|
|
7113
|
+
return {
|
|
7114
|
+
...metadata,
|
|
7115
|
+
userId: userContext.userId,
|
|
7116
|
+
orgId: userContext.orgId,
|
|
7117
|
+
createdBy: userContext.userId,
|
|
7118
|
+
lastAccessedBy: userContext.userId,
|
|
7119
|
+
acl: {
|
|
7120
|
+
readers: [userContext.userId],
|
|
7121
|
+
writers: [userContext.userId],
|
|
7122
|
+
isPublic: false
|
|
7123
|
+
}
|
|
7124
|
+
};
|
|
7125
|
+
}
|
|
7126
|
+
/**
|
|
7127
|
+
* Validate user context
|
|
7128
|
+
*/
|
|
7129
|
+
validateUserContext(userContext) {
|
|
7130
|
+
if (!userContext || typeof userContext !== "object") {
|
|
7131
|
+
return false;
|
|
7132
|
+
}
|
|
7133
|
+
const ctx = userContext;
|
|
7134
|
+
if (!ctx.userId || typeof ctx.userId !== "string") {
|
|
7135
|
+
return false;
|
|
7136
|
+
}
|
|
7137
|
+
if (ctx.orgId !== void 0 && typeof ctx.orgId !== "string") {
|
|
7138
|
+
return false;
|
|
7139
|
+
}
|
|
7140
|
+
if (ctx.roles !== void 0 && !Array.isArray(ctx.roles)) {
|
|
7141
|
+
return false;
|
|
7142
|
+
}
|
|
7143
|
+
return true;
|
|
7144
|
+
}
|
|
7145
|
+
};
|
|
7146
|
+
var globalMultiUserManager = null;
|
|
7147
|
+
function getMultiUserManager() {
|
|
7148
|
+
if (!globalMultiUserManager) {
|
|
7149
|
+
globalMultiUserManager = new MultiUserManager();
|
|
7150
|
+
}
|
|
7151
|
+
return globalMultiUserManager;
|
|
7152
|
+
}
|
|
7153
|
+
function resetMultiUserManager() {
|
|
7154
|
+
globalMultiUserManager = null;
|
|
7155
|
+
}
|
|
7156
|
+
function createUserContext(userId, options = {}) {
|
|
7157
|
+
return {
|
|
7158
|
+
userId,
|
|
7159
|
+
orgId: options.orgId,
|
|
7160
|
+
roles: options.roles ?? ["user"]
|
|
7161
|
+
};
|
|
7162
|
+
}
|
|
7163
|
+
function isAdmin(userContext) {
|
|
7164
|
+
return userContext.roles?.includes("admin") ?? false;
|
|
7165
|
+
}
|
|
7166
|
+
function requireOwnership(threadId, userContext, manager = getMultiUserManager()) {
|
|
7167
|
+
const check = manager.canAccessSession(threadId, userContext);
|
|
7168
|
+
if (!check.allowed) {
|
|
7169
|
+
throw new Error(`Access denied: ${check.reason}`);
|
|
7170
|
+
}
|
|
7171
|
+
}
|
|
7172
|
+
var StateSyncManager = class extends EventEmitter {
|
|
7173
|
+
constructor(config = {}) {
|
|
7174
|
+
super();
|
|
7175
|
+
this.sequenceNumber = 0;
|
|
7176
|
+
this.pendingChanges = [];
|
|
7177
|
+
this.throttleTimer = null;
|
|
7178
|
+
this.config = {
|
|
7179
|
+
enabled: config.enabled ?? true,
|
|
7180
|
+
conflictStrategy: config.conflictStrategy ?? "last_write_wins",
|
|
7181
|
+
throttleInterval: config.throttleInterval ?? 100,
|
|
7182
|
+
maxRetries: config.maxRetries ?? 3,
|
|
7183
|
+
clientId: config.clientId ?? this.generateClientId()
|
|
7184
|
+
};
|
|
7185
|
+
}
|
|
7186
|
+
/**
|
|
7187
|
+
* Notify that state has changed (called by agent internals)
|
|
7188
|
+
*/
|
|
7189
|
+
notifyStateChange(type, threadId, payload, userId) {
|
|
7190
|
+
if (!this.config.enabled) return;
|
|
7191
|
+
const event = {
|
|
7192
|
+
type,
|
|
7193
|
+
threadId,
|
|
7194
|
+
userId,
|
|
7195
|
+
timestamp: Date.now(),
|
|
7196
|
+
payload,
|
|
7197
|
+
version: this.createVersionVector(),
|
|
7198
|
+
sourceClientId: this.config.clientId
|
|
7199
|
+
};
|
|
7200
|
+
this.pendingChanges.push(event);
|
|
7201
|
+
this.emit("stateChange", event);
|
|
7202
|
+
this.scheduleSync();
|
|
7203
|
+
}
|
|
7204
|
+
/**
|
|
7205
|
+
* Receive state change from server (called by server transport)
|
|
7206
|
+
*/
|
|
7207
|
+
receiveRemoteChange(event) {
|
|
7208
|
+
if (event.sourceClientId === this.config.clientId) return;
|
|
7209
|
+
const conflicts = this.findConflicts(event);
|
|
7210
|
+
if (conflicts.length > 0) {
|
|
7211
|
+
const resolution = this.resolveConflicts(event, conflicts);
|
|
7212
|
+
if (resolution.resolved) {
|
|
7213
|
+
this.emit("stateReconciled", resolution.winningState, event);
|
|
7214
|
+
} else {
|
|
7215
|
+
this.emit("conflictDetected", conflicts, event);
|
|
7216
|
+
}
|
|
7217
|
+
} else {
|
|
7218
|
+
this.emit("remoteChange", event);
|
|
7219
|
+
}
|
|
7220
|
+
}
|
|
7221
|
+
/**
|
|
7222
|
+
* Create sync snapshot for current state
|
|
7223
|
+
*/
|
|
7224
|
+
createSnapshot(threadId, state) {
|
|
7225
|
+
return {
|
|
7226
|
+
threadId,
|
|
7227
|
+
checkpoint: state.checkpoint,
|
|
7228
|
+
sessionMetadata: state.sessionMetadata,
|
|
7229
|
+
memoryState: state.memoryState,
|
|
7230
|
+
version: this.createVersionVector(),
|
|
7231
|
+
lastUpdated: Date.now()
|
|
7232
|
+
};
|
|
7233
|
+
}
|
|
7234
|
+
/**
|
|
7235
|
+
* Compare two version vectors
|
|
7236
|
+
* Returns: -1 if a < b, 0 if concurrent/equal, 1 if a > b
|
|
7237
|
+
*/
|
|
7238
|
+
compareVersions(a, b) {
|
|
7239
|
+
if (a.timestamp < b.timestamp) return -1;
|
|
7240
|
+
if (a.timestamp > b.timestamp) return 1;
|
|
7241
|
+
if (a.sequence < b.sequence) return -1;
|
|
7242
|
+
if (a.sequence > b.sequence) return 1;
|
|
7243
|
+
return 0;
|
|
7244
|
+
}
|
|
7245
|
+
/**
|
|
7246
|
+
* Check if there are pending changes to sync
|
|
7247
|
+
*/
|
|
7248
|
+
hasPendingChanges() {
|
|
7249
|
+
return this.pendingChanges.length > 0;
|
|
7250
|
+
}
|
|
7251
|
+
/**
|
|
7252
|
+
* Get and clear pending changes
|
|
7253
|
+
*/
|
|
7254
|
+
flushPendingChanges() {
|
|
7255
|
+
const changes = [...this.pendingChanges];
|
|
7256
|
+
this.pendingChanges = [];
|
|
7257
|
+
return changes;
|
|
7258
|
+
}
|
|
7259
|
+
/**
|
|
7260
|
+
* Get sync configuration
|
|
7261
|
+
*/
|
|
7262
|
+
getConfig() {
|
|
7263
|
+
return { ...this.config };
|
|
7264
|
+
}
|
|
7265
|
+
/**
|
|
7266
|
+
* Update sync configuration
|
|
7267
|
+
*/
|
|
7268
|
+
updateConfig(config) {
|
|
7269
|
+
this.config = { ...this.config, ...config };
|
|
7270
|
+
this.emit("configUpdated", this.config);
|
|
7271
|
+
}
|
|
7272
|
+
/**
|
|
7273
|
+
* Dispose resources
|
|
7274
|
+
*/
|
|
7275
|
+
dispose() {
|
|
7276
|
+
if (this.throttleTimer) {
|
|
7277
|
+
clearTimeout(this.throttleTimer);
|
|
7278
|
+
this.throttleTimer = null;
|
|
7279
|
+
}
|
|
7280
|
+
this.removeAllListeners();
|
|
7281
|
+
this.pendingChanges = [];
|
|
7282
|
+
}
|
|
7283
|
+
// ============================================================================
|
|
7284
|
+
// Private Helpers
|
|
7285
|
+
// ============================================================================
|
|
7286
|
+
generateClientId() {
|
|
7287
|
+
return `client_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
7288
|
+
}
|
|
7289
|
+
createVersionVector() {
|
|
7290
|
+
this.sequenceNumber++;
|
|
7291
|
+
return {
|
|
7292
|
+
timestamp: Date.now(),
|
|
7293
|
+
sequence: this.sequenceNumber,
|
|
7294
|
+
nodeId: this.config.clientId
|
|
7295
|
+
};
|
|
7296
|
+
}
|
|
7297
|
+
scheduleSync() {
|
|
7298
|
+
if (this.throttleTimer) return;
|
|
7299
|
+
this.throttleTimer = setTimeout(() => {
|
|
7300
|
+
this.throttleTimer = null;
|
|
7301
|
+
if (this.pendingChanges.length > 0) {
|
|
7302
|
+
this.emit("syncRequired", this.flushPendingChanges());
|
|
7303
|
+
}
|
|
7304
|
+
}, this.config.throttleInterval);
|
|
7305
|
+
}
|
|
7306
|
+
findConflicts(incoming) {
|
|
7307
|
+
return this.pendingChanges.filter(
|
|
7308
|
+
(local) => local.threadId === incoming.threadId && local.type === incoming.type && this.compareVersions(local.version, incoming.version) !== 0
|
|
7309
|
+
);
|
|
7310
|
+
}
|
|
7311
|
+
resolveConflicts(incoming, conflicts) {
|
|
7312
|
+
switch (this.config.conflictStrategy) {
|
|
7313
|
+
case "last_write_wins":
|
|
7314
|
+
let winningEvent = incoming;
|
|
7315
|
+
for (const conflict of conflicts) {
|
|
7316
|
+
if (this.compareVersions(conflict.version, winningEvent.version) > 0) {
|
|
7317
|
+
winningEvent = conflict;
|
|
7318
|
+
}
|
|
7319
|
+
}
|
|
7320
|
+
return {
|
|
7321
|
+
resolved: true,
|
|
7322
|
+
strategy: "last_write_wins"
|
|
7323
|
+
};
|
|
7324
|
+
case "manual":
|
|
7325
|
+
return {
|
|
7326
|
+
resolved: false,
|
|
7327
|
+
strategy: "manual",
|
|
7328
|
+
conflicts: conflicts.map((c) => ({ clientA: c, clientB: incoming }))
|
|
7329
|
+
};
|
|
7330
|
+
case "merge":
|
|
7331
|
+
default:
|
|
7332
|
+
return {
|
|
7333
|
+
resolved: conflicts.length === 0,
|
|
7334
|
+
strategy: conflicts.length === 0 ? "merge" : "manual",
|
|
7335
|
+
conflicts: conflicts.length > 0 ? conflicts.map((c) => ({ clientA: c, clientB: incoming })) : void 0
|
|
7336
|
+
};
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
};
|
|
7340
|
+
var globalStateSyncManager = null;
|
|
7341
|
+
function getStateSyncManager(config) {
|
|
7342
|
+
if (!globalStateSyncManager) {
|
|
7343
|
+
globalStateSyncManager = new StateSyncManager(config);
|
|
7344
|
+
}
|
|
7345
|
+
return globalStateSyncManager;
|
|
7346
|
+
}
|
|
7347
|
+
function resetStateSyncManager() {
|
|
7348
|
+
globalStateSyncManager?.dispose();
|
|
7349
|
+
globalStateSyncManager = null;
|
|
7350
|
+
}
|
|
7351
|
+
function withSync(fn, syncManager, options) {
|
|
7352
|
+
return ((...args) => {
|
|
7353
|
+
const result = fn(...args);
|
|
7354
|
+
syncManager.notifyStateChange(
|
|
7355
|
+
options.type,
|
|
7356
|
+
options.threadId,
|
|
7357
|
+
options.getPayload(...args)
|
|
7358
|
+
);
|
|
7359
|
+
return result;
|
|
7360
|
+
});
|
|
7361
|
+
}
|
|
7362
|
+
function debounceSync(syncManager, delay = 100) {
|
|
7363
|
+
let timeout = null;
|
|
7364
|
+
let pending = null;
|
|
7365
|
+
return (type, threadId, payload) => {
|
|
7366
|
+
pending = { type, threadId, payload };
|
|
7367
|
+
if (timeout) {
|
|
7368
|
+
clearTimeout(timeout);
|
|
7369
|
+
}
|
|
7370
|
+
timeout = setTimeout(() => {
|
|
7371
|
+
if (pending) {
|
|
7372
|
+
syncManager.notifyStateChange(pending.type, pending.threadId, pending.payload);
|
|
7373
|
+
pending = null;
|
|
7374
|
+
}
|
|
7375
|
+
timeout = null;
|
|
7376
|
+
}, delay);
|
|
7377
|
+
};
|
|
7378
|
+
}
|
|
5969
7379
|
var export_applySectionUpdate = domain_language_exports.applySectionUpdate;
|
|
5970
7380
|
var export_convertDomainToSchema = domain_language_exports.convertDomainToSchema;
|
|
5971
7381
|
var export_convertSchemaToDomain = domain_language_exports.convertSchemaToDomain;
|
|
5972
7382
|
var export_deleteSection = domain_language_exports.deleteSection;
|
|
5973
7383
|
|
|
5974
|
-
export { ContinueRequestSchema, DEFAULT_COMPACTION_CONFIG, EVENT_BUDGETS, ExtractedRequirementsSchema, FirestoreCheckpointer, FirestoreSessionStore, FirestoreStore, GenerateRequestSchema, MemoryManager, MemoryOrbitalSchema, MemorySessionBackend, MetricsCollector, PreferenceLearner, ResumeRequestSchema, SessionManager, analyzeFailures, export_applySectionUpdate as applySectionUpdate, combineOrbitals, combineOrbitalsToSchema, export_convertDomainToSchema as convertDomainToSchema, export_convertSchemaToDomain as convertSchemaToDomain, createAgentTools, createApplyChunkTool, createCombineSchemasTool, createCompactSystemPrompt, createConstructCombinedDomainTool, createDomainOrbitalTools, createErrorFixerSubagent, createExecuteTool, createExtractChunkTool, createFinishTaskTool, createGenerateOrbitalDomainTool, createGenerateSchemaTool, createOrbitalSubagentTool, createPreferenceLearner, createQuerySchemaStructureTool, createSSEEvent, createSchemaChunkingTools, createSchemaGeneratorSubagent, createSkillAgent, createSubagentConfigs, createSubagentEventWrapper, createSubagents, createSummaryPrompt, createSystemPrompt, createTestAnalyzerSubagent, createTraitEventWrapper, createTraitSubagentTool, createValidateSchemaTool, export_deleteSection as deleteSection, estimateCacheSavings, estimateCombineComplexity, estimateTokens, extractFileOperation, extractInterruptData, formatSSEEvent, formatSummary, generateFullOrbital, getBudgetWarningMessage, getEventBudget, getInterruptConfig, hasInterrupt, isCompleteEvent, isErrorEvent, isExecutionEvent, isFileOperation, isSSECompleteEvent, isSSEErrorEvent, isSSEGenerationLogEvent, isSSEInterruptEvent, isSSEMessageEvent, isSSEStartEvent, isSSESubagentEvent, isSSETodoDetailEvent, isSSETodoUpdateEvent, isSSEToolCallEvent, isSchemaEvent, isStartEvent, isTodoUpdate, needsCompaction, parseDeepAgentEvent, parseSSEEvent, resumeSkillAgent, transformAgentEvent, transformAgentEventMulti, validateCommandPaths };
|
|
7384
|
+
export { AgenticSearchEngine, ContinueRequestSchema, DEFAULT_COMPACTION_CONFIG, EVENT_BUDGETS, ExtractedRequirementsSchema, FirestoreCheckpointer, FirestoreSessionStore, FirestoreStore, GenerateRequestSchema, MemoryManager, MemoryOrbitalSchema, MemorySessionBackend, MetricsCollector, MultiUserManager, ObservabilityCollector, PreferenceLearner, ResumeRequestSchema, SessionManager, StateSyncManager, analyzeFailures, export_applySectionUpdate as applySectionUpdate, combineOrbitals, combineOrbitalsToSchema, export_convertDomainToSchema as convertDomainToSchema, export_convertSchemaToDomain as convertSchemaToDomain, createAgentTools, createAgenticSearchEngine, createApplyChunkTool, createCombineSchemasTool, createCompactSystemPrompt, createConstructCombinedDomainTool, createDomainOrbitalTools, createErrorFixerSubagent, createExecuteTool, createExtractChunkTool, createFinishTaskTool, createGenerateOrbitalDomainTool, createGenerateSchemaTool, createOrbitalSubagentTool, createPreferenceLearner, createQuerySchemaStructureTool, createSSEEvent, createSchemaChunkingTools, createSchemaGeneratorSubagent, createSkillAgent, createSubagentConfigs, createSubagentEventWrapper, createSubagents, createSummaryPrompt, createSystemPrompt, createTestAnalyzerSubagent, createTraitEventWrapper, createTraitSubagentTool, createUserContext, createValidateSchemaTool, debounceSync, export_deleteSection as deleteSection, endObservabilitySession, estimateCacheSavings, estimateCombineComplexity, estimateTokens, extractFileOperation, extractInterruptData, formatSSEEvent, formatSummary, generateFullOrbital, getBudgetWarningMessage, getEventBudget, getInterruptConfig, getMultiUserManager, getObservabilityCollector, getPerformanceSnapshot, getStateSyncManager, hasInterrupt, isAdmin, isCompleteEvent, isErrorEvent, isExecutionEvent, isFileOperation, isSSECompleteEvent, isSSEErrorEvent, isSSEGenerationLogEvent, isSSEInterruptEvent, isSSEMessageEvent, isSSEStartEvent, isSSESubagentEvent, isSSETodoDetailEvent, isSSETodoUpdateEvent, isSSEToolCallEvent, isSchemaEvent, isStartEvent, isTodoUpdate, needsCompaction, parseDeepAgentEvent, parseSSEEvent, recordEvent, requireOwnership, resetMultiUserManager, resetObservabilityCollector, resetStateSyncManager, resumeSkillAgent, startObservabilitySession, transformAgentEvent, transformAgentEventMulti, validateCommandPaths, withSync };
|
|
5975
7385
|
//# sourceMappingURL=index.js.map
|
|
5976
7386
|
//# sourceMappingURL=index.js.map
|