@highway1/core 0.1.39 → 0.1.41
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/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +18 -4
- package/src/discovery/agent-card-encoder.ts +119 -0
- package/src/discovery/agent-card-schema.ts +87 -0
- package/src/discovery/agent-card-types.ts +99 -0
- package/src/discovery/agent-card.ts +190 -0
- package/src/discovery/bootstrap.ts +63 -0
- package/src/discovery/capability-matcher.ts +167 -0
- package/src/discovery/dht.ts +285 -0
- package/src/discovery/index.ts +3 -0
- package/src/discovery/search-index.ts +247 -0
- package/src/discovery/semantic-search.ts +218 -0
- package/src/identity/did.ts +48 -0
- package/src/identity/document.ts +77 -0
- package/src/identity/index.ts +4 -0
- package/src/identity/keys.ts +79 -0
- package/src/identity/signer.ts +55 -0
- package/src/index.ts +33 -0
- package/src/messaging/codec.ts +47 -0
- package/src/messaging/envelope.ts +107 -0
- package/src/messaging/index.ts +3 -0
- package/src/messaging/router.ts +368 -0
- package/src/transport/connection.ts +77 -0
- package/src/transport/index.ts +2 -0
- package/src/transport/node.ts +152 -0
- package/src/trust/endorsement.ts +167 -0
- package/src/trust/index.ts +194 -0
- package/src/trust/interaction-history.ts +155 -0
- package/src/trust/sybil-defense.ts +232 -0
- package/src/trust/trust-score.ts +136 -0
- package/src/utils/errors.ts +38 -0
- package/src/utils/logger.ts +48 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Endorsement System
|
|
3
|
+
*
|
|
4
|
+
* Allows agents to endorse each other, building a web of trust
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Level } from 'level';
|
|
8
|
+
import { createLogger } from '../utils/logger.js';
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('endorsement');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Endorsement Record
|
|
14
|
+
*/
|
|
15
|
+
export interface Endorsement {
|
|
16
|
+
from: string; // Endorser DID
|
|
17
|
+
to: string; // Endorsed agent DID
|
|
18
|
+
score: number; // 0-1
|
|
19
|
+
reason: string;
|
|
20
|
+
timestamp: number;
|
|
21
|
+
signature: string; // Signed by endorser
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sign function type
|
|
26
|
+
*/
|
|
27
|
+
export type SignFunction = (data: Uint8Array) => Promise<Uint8Array>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Verify function type
|
|
31
|
+
*/
|
|
32
|
+
export type VerifyFunction = (signature: Uint8Array, data: Uint8Array, publicKey: Uint8Array) => Promise<boolean>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Endorsement Manager
|
|
36
|
+
*/
|
|
37
|
+
export class EndorsementManager {
|
|
38
|
+
constructor(
|
|
39
|
+
private db: Level<string, Endorsement>,
|
|
40
|
+
private getPublicKey: (did: string) => Promise<Uint8Array>
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create an endorsement
|
|
45
|
+
*/
|
|
46
|
+
async endorse(
|
|
47
|
+
fromDid: string,
|
|
48
|
+
toDid: string,
|
|
49
|
+
score: number,
|
|
50
|
+
reason: string,
|
|
51
|
+
signFn: SignFunction
|
|
52
|
+
): Promise<Endorsement> {
|
|
53
|
+
if (score < 0 || score > 1) {
|
|
54
|
+
throw new Error('Score must be between 0 and 1');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const endorsement: Omit<Endorsement, 'signature'> = {
|
|
58
|
+
from: fromDid,
|
|
59
|
+
to: toDid,
|
|
60
|
+
score,
|
|
61
|
+
reason,
|
|
62
|
+
timestamp: Date.now(),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Sign the endorsement
|
|
66
|
+
const data = new TextEncoder().encode(JSON.stringify(endorsement));
|
|
67
|
+
const signatureBytes = await signFn(data);
|
|
68
|
+
const signature = Buffer.from(signatureBytes).toString('hex');
|
|
69
|
+
|
|
70
|
+
const signedEndorsement: Endorsement = {
|
|
71
|
+
...endorsement,
|
|
72
|
+
signature,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
logger.info('Created endorsement', { from: fromDid, to: toDid, score });
|
|
76
|
+
return signedEndorsement;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Verify endorsement signature
|
|
81
|
+
*/
|
|
82
|
+
async verify(endorsement: Endorsement, verifyFn: VerifyFunction): Promise<boolean> {
|
|
83
|
+
try {
|
|
84
|
+
const { signature, ...endorsementWithoutSig } = endorsement;
|
|
85
|
+
const data = new TextEncoder().encode(JSON.stringify(endorsementWithoutSig));
|
|
86
|
+
const signatureBytes = Buffer.from(signature, 'hex');
|
|
87
|
+
const publicKey = await this.getPublicKey(endorsement.from);
|
|
88
|
+
|
|
89
|
+
return await verifyFn(signatureBytes, data, publicKey);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logger.error('Failed to verify endorsement', { error });
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Publish endorsement to local database
|
|
98
|
+
*/
|
|
99
|
+
async publish(endorsement: Endorsement): Promise<void> {
|
|
100
|
+
const key = `endorsement:${endorsement.to}:${endorsement.from}`;
|
|
101
|
+
await this.db.put(key, endorsement);
|
|
102
|
+
logger.info('Published endorsement', { from: endorsement.from, to: endorsement.to });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get all endorsements for an agent
|
|
107
|
+
*/
|
|
108
|
+
async getEndorsements(agentDid: string): Promise<Endorsement[]> {
|
|
109
|
+
const endorsements: Endorsement[] = [];
|
|
110
|
+
const prefix = `endorsement:${agentDid}:`;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
for await (const [_, value] of this.db.iterator({
|
|
114
|
+
gte: prefix,
|
|
115
|
+
lte: prefix + '\xff',
|
|
116
|
+
})) {
|
|
117
|
+
endorsements.push(value);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger.error('Failed to get endorsements', { agentDid, error });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return endorsements;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get endorsements given by an agent
|
|
128
|
+
*/
|
|
129
|
+
async getEndorsementsBy(fromDid: string): Promise<Endorsement[]> {
|
|
130
|
+
const endorsements: Endorsement[] = [];
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
for await (const [_, value] of this.db.iterator()) {
|
|
134
|
+
if (value.from === fromDid) {
|
|
135
|
+
endorsements.push(value);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
logger.error('Failed to get endorsements by agent', { fromDid, error });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return endorsements;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Calculate average endorsement score
|
|
147
|
+
*/
|
|
148
|
+
async getAverageScore(agentDid: string): Promise<number> {
|
|
149
|
+
const endorsements = await this.getEndorsements(agentDid);
|
|
150
|
+
|
|
151
|
+
if (endorsements.length === 0) {
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const totalScore = endorsements.reduce((sum, e) => sum + e.score, 0);
|
|
156
|
+
return totalScore / endorsements.length;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Delete an endorsement
|
|
161
|
+
*/
|
|
162
|
+
async deleteEndorsement(fromDid: string, toDid: string): Promise<void> {
|
|
163
|
+
const key = `endorsement:${toDid}:${fromDid}`;
|
|
164
|
+
await this.db.del(key);
|
|
165
|
+
logger.info('Deleted endorsement', { from: fromDid, to: toDid });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust System - Main Export
|
|
3
|
+
*
|
|
4
|
+
* Combines all trust components into a unified interface
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './trust-score.js';
|
|
8
|
+
export * from './interaction-history.js';
|
|
9
|
+
export * from './endorsement.js';
|
|
10
|
+
export * from './sybil-defense.js';
|
|
11
|
+
|
|
12
|
+
import { Level } from 'level';
|
|
13
|
+
import { TrustMetrics } from './trust-score.js';
|
|
14
|
+
import type { TrustScore, Interaction } from './trust-score.js';
|
|
15
|
+
import { InteractionHistory } from './interaction-history.js';
|
|
16
|
+
import { EndorsementManager } from './endorsement.js';
|
|
17
|
+
import type { Endorsement, SignFunction, VerifyFunction } from './endorsement.js';
|
|
18
|
+
import { SybilDefense } from './sybil-defense.js';
|
|
19
|
+
import { createLogger } from '../utils/logger.js';
|
|
20
|
+
|
|
21
|
+
const logger = createLogger('trust-system');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Trust System Configuration
|
|
25
|
+
*/
|
|
26
|
+
export interface TrustSystemConfig {
|
|
27
|
+
dbPath: string;
|
|
28
|
+
getPublicKey: (did: string) => Promise<Uint8Array>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Unified Trust System
|
|
33
|
+
*/
|
|
34
|
+
export class TrustSystem {
|
|
35
|
+
private metrics: TrustMetrics;
|
|
36
|
+
private history: InteractionHistory;
|
|
37
|
+
private endorsements: EndorsementManager;
|
|
38
|
+
private sybilDefense: SybilDefense;
|
|
39
|
+
private trustCache = new Map<string, { score: TrustScore; timestamp: number }>();
|
|
40
|
+
private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
41
|
+
|
|
42
|
+
constructor(config: TrustSystemConfig) {
|
|
43
|
+
this.metrics = new TrustMetrics();
|
|
44
|
+
this.history = new InteractionHistory(`${config.dbPath}/interactions`);
|
|
45
|
+
|
|
46
|
+
const endorsementDb = new Level<string, Endorsement>(`${config.dbPath}/endorsements`, {
|
|
47
|
+
valueEncoding: 'json',
|
|
48
|
+
});
|
|
49
|
+
this.endorsements = new EndorsementManager(endorsementDb, config.getPublicKey);
|
|
50
|
+
this.sybilDefense = new SybilDefense();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initialize the trust system
|
|
55
|
+
*/
|
|
56
|
+
async start(): Promise<void> {
|
|
57
|
+
await this.history.open();
|
|
58
|
+
logger.info('Trust system started');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Shutdown the trust system
|
|
63
|
+
*/
|
|
64
|
+
async stop(): Promise<void> {
|
|
65
|
+
await this.history.close();
|
|
66
|
+
logger.info('Trust system stopped');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Record an interaction
|
|
71
|
+
*/
|
|
72
|
+
async recordInteraction(interaction: Interaction): Promise<void> {
|
|
73
|
+
await this.history.record(interaction);
|
|
74
|
+
this.sybilDefense.recordRequest(interaction.agentDid);
|
|
75
|
+
|
|
76
|
+
// Invalidate cache
|
|
77
|
+
this.trustCache.delete(interaction.agentDid);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get trust score for an agent
|
|
82
|
+
*/
|
|
83
|
+
async getTrustScore(agentDid: string): Promise<TrustScore> {
|
|
84
|
+
// Check cache
|
|
85
|
+
const cached = this.trustCache.get(agentDid);
|
|
86
|
+
if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
|
|
87
|
+
return cached.score;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Calculate fresh score
|
|
91
|
+
const stats = await this.history.getStats(agentDid);
|
|
92
|
+
const endorsementList = await this.endorsements.getEndorsements(agentDid);
|
|
93
|
+
const uptime = 1.0; // TODO: Implement uptime tracking
|
|
94
|
+
|
|
95
|
+
const score = this.metrics.calculateScore(stats, endorsementList.length, uptime);
|
|
96
|
+
|
|
97
|
+
// Cache result
|
|
98
|
+
this.trustCache.set(agentDid, { score, timestamp: Date.now() });
|
|
99
|
+
|
|
100
|
+
return score;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create an endorsement
|
|
105
|
+
*/
|
|
106
|
+
async endorse(
|
|
107
|
+
fromDid: string,
|
|
108
|
+
toDid: string,
|
|
109
|
+
score: number,
|
|
110
|
+
reason: string,
|
|
111
|
+
signFn: SignFunction
|
|
112
|
+
): Promise<Endorsement> {
|
|
113
|
+
const endorsement = await this.endorsements.endorse(fromDid, toDid, score, reason, signFn);
|
|
114
|
+
await this.endorsements.publish(endorsement);
|
|
115
|
+
|
|
116
|
+
// Invalidate cache
|
|
117
|
+
this.trustCache.delete(toDid);
|
|
118
|
+
|
|
119
|
+
return endorsement;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get endorsements for an agent
|
|
124
|
+
*/
|
|
125
|
+
async getEndorsements(agentDid: string): Promise<Endorsement[]> {
|
|
126
|
+
return this.endorsements.getEndorsements(agentDid);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Verify an endorsement
|
|
131
|
+
*/
|
|
132
|
+
async verifyEndorsement(endorsement: Endorsement, verifyFn: VerifyFunction): Promise<boolean> {
|
|
133
|
+
return this.endorsements.verify(endorsement, verifyFn);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if agent should be rate limited
|
|
138
|
+
*/
|
|
139
|
+
isRateLimited(agentDid: string): boolean {
|
|
140
|
+
return this.sybilDefense.isRateLimited(agentDid);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Generate Sybil defense challenge
|
|
145
|
+
*/
|
|
146
|
+
generateChallenge(did: string, difficulty?: number) {
|
|
147
|
+
return this.sybilDefense.generateChallenge(did, difficulty);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Verify Sybil defense challenge
|
|
152
|
+
*/
|
|
153
|
+
verifyChallenge(solution: any): boolean {
|
|
154
|
+
return this.sybilDefense.verifyChallenge(solution);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get peer trust level
|
|
159
|
+
*/
|
|
160
|
+
getPeerTrustLevel(peerId: string) {
|
|
161
|
+
return this.sybilDefense.getPeerTrustLevel(peerId);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Record peer seen
|
|
166
|
+
*/
|
|
167
|
+
recordPeerSeen(peerId: string): void {
|
|
168
|
+
this.sybilDefense.recordPeerSeen(peerId);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get interaction history
|
|
173
|
+
*/
|
|
174
|
+
async getHistory(agentDid: string, limit?: number): Promise<Interaction[]> {
|
|
175
|
+
return this.history.getHistory(agentDid, limit);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Clean up old data
|
|
180
|
+
*/
|
|
181
|
+
async cleanup(): Promise<void> {
|
|
182
|
+
await this.history.cleanup();
|
|
183
|
+
this.sybilDefense.cleanup();
|
|
184
|
+
this.trustCache.clear();
|
|
185
|
+
logger.info('Trust system cleanup completed');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Create a trust system instance
|
|
191
|
+
*/
|
|
192
|
+
export function createTrustSystem(config: TrustSystemConfig): TrustSystem {
|
|
193
|
+
return new TrustSystem(config);
|
|
194
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction History Tracker
|
|
3
|
+
*
|
|
4
|
+
* Records and queries agent interaction history for trust scoring
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Level } from 'level';
|
|
8
|
+
import type { Interaction, InteractionStats } from './trust-score.js';
|
|
9
|
+
import { createLogger } from '../utils/logger.js';
|
|
10
|
+
|
|
11
|
+
const logger = createLogger('interaction-history');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Interaction History Manager
|
|
15
|
+
*/
|
|
16
|
+
export class InteractionHistory {
|
|
17
|
+
private db: Level<string, Interaction>;
|
|
18
|
+
|
|
19
|
+
constructor(dbPath: string) {
|
|
20
|
+
this.db = new Level(dbPath, { valueEncoding: 'json' });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Open database connection
|
|
25
|
+
*/
|
|
26
|
+
async open(): Promise<void> {
|
|
27
|
+
await this.db.open();
|
|
28
|
+
logger.info('Interaction history database opened', { path: this.db.location });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Close database connection
|
|
33
|
+
*/
|
|
34
|
+
async close(): Promise<void> {
|
|
35
|
+
await this.db.close();
|
|
36
|
+
logger.info('Interaction history database closed');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Record an interaction
|
|
41
|
+
*/
|
|
42
|
+
async record(interaction: Interaction): Promise<void> {
|
|
43
|
+
const key = `interaction:${interaction.agentDid}:${interaction.timestamp}`;
|
|
44
|
+
await this.db.put(key, interaction);
|
|
45
|
+
logger.debug('Recorded interaction', { agentDid: interaction.agentDid, type: interaction.type });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get interaction history for an agent
|
|
50
|
+
*/
|
|
51
|
+
async getHistory(agentDid: string, limit = 100): Promise<Interaction[]> {
|
|
52
|
+
const interactions: Interaction[] = [];
|
|
53
|
+
const prefix = `interaction:${agentDid}:`;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
for await (const [_, value] of this.db.iterator({
|
|
57
|
+
gte: prefix,
|
|
58
|
+
lte: prefix + '\xff',
|
|
59
|
+
limit,
|
|
60
|
+
reverse: true, // Most recent first
|
|
61
|
+
})) {
|
|
62
|
+
interactions.push(value);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.error('Failed to get interaction history', { agentDid, error });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return interactions;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get interaction statistics for an agent
|
|
73
|
+
*/
|
|
74
|
+
async getStats(agentDid: string): Promise<InteractionStats> {
|
|
75
|
+
const history = await this.getHistory(agentDid, 1000); // Last 1000 interactions
|
|
76
|
+
|
|
77
|
+
if (history.length === 0) {
|
|
78
|
+
return {
|
|
79
|
+
totalInteractions: 0,
|
|
80
|
+
successRate: 0,
|
|
81
|
+
avgResponseTime: 0,
|
|
82
|
+
lastInteraction: 0,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const successCount = history.filter(i => i.success).length;
|
|
87
|
+
const totalResponseTime = history.reduce((sum, i) => sum + i.responseTime, 0);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
totalInteractions: history.length,
|
|
91
|
+
successRate: successCount / history.length,
|
|
92
|
+
avgResponseTime: totalResponseTime / history.length,
|
|
93
|
+
lastInteraction: history[0].timestamp,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get all agents with interaction history
|
|
99
|
+
*/
|
|
100
|
+
async getAllAgents(): Promise<string[]> {
|
|
101
|
+
const agents = new Set<string>();
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
for await (const [key] of this.db.iterator()) {
|
|
105
|
+
const match = key.match(/^interaction:([^:]+):/);
|
|
106
|
+
if (match) {
|
|
107
|
+
agents.add(match[1]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
logger.error('Failed to get all agents', { error });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return Array.from(agents);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Delete all interactions for an agent
|
|
119
|
+
*/
|
|
120
|
+
async deleteAgent(agentDid: string): Promise<void> {
|
|
121
|
+
const prefix = `interaction:${agentDid}:`;
|
|
122
|
+
const keysToDelete: string[] = [];
|
|
123
|
+
|
|
124
|
+
for await (const [key] of this.db.iterator({
|
|
125
|
+
gte: prefix,
|
|
126
|
+
lte: prefix + '\xff',
|
|
127
|
+
})) {
|
|
128
|
+
keysToDelete.push(key);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await this.db.batch(keysToDelete.map(key => ({ type: 'del', key })));
|
|
132
|
+
logger.info('Deleted interaction history', { agentDid, count: keysToDelete.length });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Clean up old interactions (older than 90 days)
|
|
137
|
+
*/
|
|
138
|
+
async cleanup(maxAge = 90 * 24 * 60 * 60 * 1000): Promise<number> {
|
|
139
|
+
const cutoff = Date.now() - maxAge;
|
|
140
|
+
const keysToDelete: string[] = [];
|
|
141
|
+
|
|
142
|
+
for await (const [key, value] of this.db.iterator()) {
|
|
143
|
+
if (value.timestamp < cutoff) {
|
|
144
|
+
keysToDelete.push(key);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (keysToDelete.length > 0) {
|
|
149
|
+
await this.db.batch(keysToDelete.map(key => ({ type: 'del', key })));
|
|
150
|
+
logger.info('Cleaned up old interactions', { count: keysToDelete.length });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return keysToDelete.length;
|
|
154
|
+
}
|
|
155
|
+
}
|