@agent-diaries/core 1.1.1 → 1.1.2

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.
@@ -0,0 +1,16 @@
1
+ import { StorageAdapter } from '../storage';
2
+ import { Collection } from 'mongodb';
3
+ interface MongoStorageOptions {
4
+ collection: Collection;
5
+ }
6
+ export declare class MongoStorage<T> implements StorageAdapter<T> {
7
+ private collection;
8
+ private static indexedCollections;
9
+ constructor(options: MongoStorageOptions);
10
+ private ensureIndex;
11
+ private hashString;
12
+ get(key: string): Promise<T | null>;
13
+ set(key: string, data: T): Promise<void>;
14
+ withLock<R>(key: string, fn: () => Promise<R>): Promise<R>;
15
+ }
16
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MongoStorage = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ class MongoStorage {
9
+ collection;
10
+ static indexedCollections = new Set();
11
+ constructor(options) {
12
+ this.collection = options.collection;
13
+ }
14
+ async ensureIndex() {
15
+ const ns = this.collection.namespace;
16
+ if (MongoStorage.indexedCollections.has(ns))
17
+ return;
18
+ try {
19
+ await this.collection.createIndex({ lockedAt: 1 }, { expireAfterSeconds: 10, partialFilterExpression: { lockedAt: { $exists: true } } });
20
+ }
21
+ catch (e) {
22
+ console.warn('[MongoStorage] Failed to create TTL index:', e);
23
+ }
24
+ MongoStorage.indexedCollections.add(ns);
25
+ }
26
+ hashString(str) {
27
+ return crypto_1.default.createHash('sha256').update(str).digest('hex');
28
+ }
29
+ async get(key) {
30
+ const hash = this.hashString(key);
31
+ const doc = await this.collection.findOne({ _id: hash });
32
+ if (!doc || !doc.data)
33
+ return null;
34
+ return JSON.parse(doc.data);
35
+ }
36
+ async set(key, data) {
37
+ const hash = this.hashString(key);
38
+ await this.collection.updateOne({ _id: hash }, { $set: { data: JSON.stringify(data) } }, { upsert: true });
39
+ }
40
+ async withLock(key, fn) {
41
+ await this.ensureIndex();
42
+ const hash = this.hashString(key);
43
+ const lockId = `lock:${hash}`;
44
+ const acquireLock = async () => {
45
+ try {
46
+ await this.collection.insertOne({ _id: lockId, lockedAt: new Date() });
47
+ return true;
48
+ }
49
+ catch (error) {
50
+ if (error.code === 11000)
51
+ return false;
52
+ throw error;
53
+ }
54
+ };
55
+ let attempt = 0;
56
+ while (!(await acquireLock())) {
57
+ const backoff = Math.min(10 * Math.pow(2, attempt), 500);
58
+ const jitter = Math.random() * 50;
59
+ await new Promise(resolve => setTimeout(resolve, backoff + jitter));
60
+ attempt++;
61
+ if (attempt > 150) {
62
+ throw new Error(`[MongoStorage] Lock timeout on key: ${key}`);
63
+ }
64
+ }
65
+ try {
66
+ return await fn();
67
+ }
68
+ finally {
69
+ await this.collection.deleteOne({ _id: lockId });
70
+ }
71
+ }
72
+ }
73
+ exports.MongoStorage = MongoStorage;
@@ -0,0 +1,14 @@
1
+ import { StorageAdapter } from '../storage';
2
+ import Redis from 'ioredis';
3
+ export declare class RedisStorage<T> implements StorageAdapter<T> {
4
+ private redis;
5
+ private prefix;
6
+ constructor(options: {
7
+ redis: Redis;
8
+ prefix?: string;
9
+ });
10
+ private getKey;
11
+ get(key: string): Promise<T | null>;
12
+ set(key: string, value: T): Promise<void>;
13
+ withLock<R>(key: string, fn: () => Promise<R>): Promise<R>;
14
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisStorage = void 0;
4
+ const crypto_1 = require("crypto");
5
+ class RedisStorage {
6
+ redis;
7
+ prefix;
8
+ constructor(options) {
9
+ this.redis = options.redis;
10
+ this.prefix = options.prefix || 'agent-diaries:';
11
+ }
12
+ getKey(key) {
13
+ return `${this.prefix}${key}`;
14
+ }
15
+ async get(key) {
16
+ const data = await this.redis.get(this.getKey(key));
17
+ if (!data)
18
+ return null;
19
+ try {
20
+ return JSON.parse(data);
21
+ }
22
+ catch (e) {
23
+ console.error(`[RedisStorage] Failed to parse JSON for key ${key}`);
24
+ return null;
25
+ }
26
+ }
27
+ async set(key, value) {
28
+ const data = JSON.stringify(value);
29
+ await this.redis.set(this.getKey(key), data);
30
+ }
31
+ async withLock(key, fn) {
32
+ const lockKey = `${this.getKey(key)}:lock`;
33
+ const lockValue = (0, crypto_1.randomUUID)();
34
+ const lockTtlMs = 10000; // 10 seconds max lock
35
+ const acquireLock = async () => {
36
+ const result = await this.redis.set(lockKey, lockValue, 'PX', lockTtlMs, 'NX');
37
+ return result === 'OK';
38
+ };
39
+ let attempt = 0;
40
+ while (!(await acquireLock())) {
41
+ const backoff = Math.min(10 * Math.pow(2, attempt), 500);
42
+ const jitter = Math.random() * 50;
43
+ await new Promise(resolve => setTimeout(resolve, backoff + jitter));
44
+ attempt++;
45
+ if (attempt > 60)
46
+ throw new Error(`[RedisStorage] Lock timeout on key: ${key}`);
47
+ }
48
+ try {
49
+ return await fn();
50
+ }
51
+ finally {
52
+ // Safe release: ensure we only delete the lock if we still own it
53
+ const luaScript = `
54
+ if redis.call("get", KEYS[1]) == ARGV[1] then
55
+ return redis.call("del", KEYS[1])
56
+ else
57
+ return 0
58
+ end
59
+ `;
60
+ await this.redis.eval(luaScript, 1, lockKey, lockValue);
61
+ }
62
+ }
63
+ }
64
+ exports.RedisStorage = RedisStorage;
@@ -0,0 +1,16 @@
1
+ import { StorageAdapter } from '../storage';
2
+ import type { Database } from 'better-sqlite3';
3
+ export interface SqliteStorageOptions {
4
+ db: Database;
5
+ tableName?: string;
6
+ locksTableName?: string;
7
+ }
8
+ export declare class SqliteStorage<T> implements StorageAdapter<T> {
9
+ private db;
10
+ private tableName;
11
+ private locksTableName;
12
+ constructor(options: SqliteStorageOptions);
13
+ get(key: string): Promise<T | null>;
14
+ set(key: string, value: T): Promise<void>;
15
+ withLock<R>(key: string, fn: () => Promise<R>): Promise<R>;
16
+ }
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SqliteStorage = void 0;
4
+ class SqliteStorage {
5
+ db;
6
+ tableName;
7
+ locksTableName;
8
+ constructor(options) {
9
+ if (!options.db) {
10
+ throw new Error('[SqliteStorage] database instance (db) is required.');
11
+ }
12
+ this.db = options.db;
13
+ this.tableName = options.tableName || 'agent_diaries_storage';
14
+ this.locksTableName = options.locksTableName || 'agent_diaries_locks';
15
+ // Initialize tables synchronously as better-sqlite3 is fully synchronous
16
+ this.db.exec(`
17
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
18
+ key TEXT PRIMARY KEY,
19
+ value TEXT
20
+ );
21
+ CREATE TABLE IF NOT EXISTS ${this.locksTableName} (
22
+ key TEXT PRIMARY KEY,
23
+ locked_at INTEGER
24
+ );
25
+ `);
26
+ }
27
+ async get(key) {
28
+ try {
29
+ const row = this.db
30
+ .prepare(`SELECT value FROM ${this.tableName} WHERE key = ?`)
31
+ .get(key);
32
+ if (!row)
33
+ return null;
34
+ return JSON.parse(row.value);
35
+ }
36
+ catch (e) {
37
+ console.error(`[SqliteStorage] Failed to get key ${key}:`, e);
38
+ return null;
39
+ }
40
+ }
41
+ async set(key, value) {
42
+ try {
43
+ const serialized = JSON.stringify(value);
44
+ this.db
45
+ .prepare(`
46
+ INSERT INTO ${this.tableName} (key, value)
47
+ VALUES (?, ?)
48
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value
49
+ `)
50
+ .run(key, serialized);
51
+ }
52
+ catch (e) {
53
+ console.error(`[SqliteStorage] Failed to set key ${key}:`, e);
54
+ throw e;
55
+ }
56
+ }
57
+ async withLock(key, fn) {
58
+ const lockKey = `lock:${key}`;
59
+ const lockTtlMs = 10000; // 10 seconds max lock
60
+ const acquireLock = () => {
61
+ const now = Date.now();
62
+ try {
63
+ this.db
64
+ .prepare(`INSERT INTO ${this.locksTableName} (key, locked_at) VALUES (?, ?)`)
65
+ .run(lockKey, now);
66
+ return true;
67
+ }
68
+ catch (error) {
69
+ if (error.code === 'SQLITE_CONSTRAINT_PRIMARYKEY' ||
70
+ error.code === 'SQLITE_CONSTRAINT' ||
71
+ (error.message && error.message.includes('UNIQUE constraint failed'))) {
72
+ // Check if the lock has expired
73
+ const existing = this.db
74
+ .prepare(`SELECT locked_at FROM ${this.locksTableName} WHERE key = ?`)
75
+ .get(lockKey);
76
+ if (existing && now - existing.locked_at > lockTtlMs) {
77
+ // Attempt to clear the expired lock and acquire it
78
+ try {
79
+ // Wrap cleanup and acquire in a transaction for atomicity
80
+ const runCleanupAndAcquire = this.db.transaction(() => {
81
+ this.db
82
+ .prepare(`DELETE FROM ${this.locksTableName} WHERE key = ? AND locked_at = ?`)
83
+ .run(lockKey, existing.locked_at);
84
+ this.db
85
+ .prepare(`INSERT INTO ${this.locksTableName} (key, locked_at) VALUES (?, ?)`)
86
+ .run(lockKey, now);
87
+ });
88
+ runCleanupAndAcquire();
89
+ return true;
90
+ }
91
+ catch (retryError) {
92
+ // Conflict during retry, lock is still held or acquired by another concurrent process
93
+ return false;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+ throw error;
99
+ }
100
+ };
101
+ let attempt = 0;
102
+ while (!acquireLock()) {
103
+ const backoff = Math.min(10 * Math.pow(2, attempt), 500);
104
+ const jitter = Math.random() * 50;
105
+ await new Promise(resolve => setTimeout(resolve, backoff + jitter));
106
+ attempt++;
107
+ if (attempt > 60) {
108
+ throw new Error(`[SqliteStorage] Lock timeout on key: ${key}`);
109
+ }
110
+ }
111
+ try {
112
+ return await fn();
113
+ }
114
+ finally {
115
+ try {
116
+ this.db
117
+ .prepare(`DELETE FROM ${this.locksTableName} WHERE key = ?`)
118
+ .run(lockKey);
119
+ }
120
+ catch (e) {
121
+ console.error(`[SqliteStorage] Failed to release lock on key ${key}:`, e);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ exports.SqliteStorage = SqliteStorage;
@@ -0,0 +1,59 @@
1
+ import { StorageAdapter } from './storage';
2
+ export interface TaskRecord {
3
+ title: string;
4
+ signature: string;
5
+ result?: string;
6
+ timestamp: number;
7
+ }
8
+ export interface AgentState {
9
+ lastRun: number;
10
+ seenSignatures: string[];
11
+ runCount: number;
12
+ history: TaskRecord[];
13
+ }
14
+ export interface AgentDiaryOptions {
15
+ agentId: string;
16
+ storage?: StorageAdapter<AgentState>;
17
+ maxHistory?: number;
18
+ }
19
+ export declare class AgentDiary {
20
+ private agentId;
21
+ private storage;
22
+ private maxHistory;
23
+ constructor(options: AgentDiaryOptions);
24
+ private emptyState;
25
+ /**
26
+ * Generates a normalized signature for a task title.
27
+ */
28
+ static normalizeSignature(title: string): string;
29
+ /**
30
+ * Reads the current diary state (without locking).
31
+ */
32
+ readDiary(): Promise<AgentState>;
33
+ /**
34
+ * Atomically attempts to claim a task.
35
+ * Returns true if successfully claimed (first time seen), false if already claimed/processed.
36
+ */
37
+ claimTask(title: string): Promise<boolean>;
38
+ /**
39
+ * Checks if a task has already been processed by the agent.
40
+ */
41
+ hasProcessedTask(title: string): Promise<boolean>;
42
+ /**
43
+ * Retrieves the stored result of a previously processed task, if available.
44
+ */
45
+ getTaskResult(title: string): Promise<string | undefined>;
46
+ /**
47
+ * Filters out items that the agent has already processed.
48
+ *
49
+ * ⚠️ WARNING: filterNewTasks() is a non-atomic snapshot. Always follow it with claimTask()
50
+ * to atomically claim ownership. Never act on filterNewTasks() results directly without claiming them first.
51
+ */
52
+ filterNewTasks<T extends {
53
+ title: string;
54
+ }>(tasks: T[]): Promise<T[]>;
55
+ /**
56
+ * Updates a claimed task with its final result.
57
+ */
58
+ writeTaskResult(title: string, result?: string): Promise<void>;
59
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentDiary = void 0;
4
+ const storage_1 = require("./storage");
5
+ class AgentDiary {
6
+ agentId;
7
+ storage;
8
+ maxHistory;
9
+ constructor(options) {
10
+ this.agentId = options.agentId;
11
+ // Use local file storage by default if none provided
12
+ this.storage = options.storage || new storage_1.LocalFileStorage();
13
+ this.maxHistory = options.maxHistory || 500;
14
+ }
15
+ emptyState() {
16
+ return {
17
+ lastRun: 0,
18
+ seenSignatures: [],
19
+ runCount: 0,
20
+ history: [],
21
+ };
22
+ }
23
+ /**
24
+ * Generates a normalized signature for a task title.
25
+ */
26
+ static normalizeSignature(title) {
27
+ return (title || '').toLowerCase().trim().replace(/\s+/g, ' ');
28
+ }
29
+ /**
30
+ * Reads the current diary state (without locking).
31
+ */
32
+ async readDiary() {
33
+ const state = await this.storage.get(`diary_${this.agentId}`);
34
+ return state ?? this.emptyState();
35
+ }
36
+ /**
37
+ * Atomically attempts to claim a task.
38
+ * Returns true if successfully claimed (first time seen), false if already claimed/processed.
39
+ */
40
+ async claimTask(title) {
41
+ const signature = AgentDiary.normalizeSignature(title);
42
+ return await this.storage.withLock(`diary_${this.agentId}`, async () => {
43
+ const state = await this.readDiary();
44
+ const seenSet = new Set(state.seenSignatures);
45
+ if (seenSet.has(signature)) {
46
+ return false; // Task already exists
47
+ }
48
+ // Claim it immediately to prevent race conditions
49
+ const record = {
50
+ title,
51
+ signature,
52
+ timestamp: Date.now()
53
+ };
54
+ state.history = [record, ...state.history].slice(0, this.maxHistory);
55
+ // Fix Desync Bug: seenSignatures is strictly derived from the sliced history
56
+ state.seenSignatures = state.history.map(r => r.signature);
57
+ state.runCount += 1;
58
+ state.lastRun = Date.now();
59
+ await this.storage.set(`diary_${this.agentId}`, state);
60
+ return true;
61
+ });
62
+ }
63
+ /**
64
+ * Checks if a task has already been processed by the agent.
65
+ */
66
+ async hasProcessedTask(title) {
67
+ const signature = AgentDiary.normalizeSignature(title);
68
+ const state = await this.readDiary();
69
+ const seenSet = new Set(state.seenSignatures);
70
+ return seenSet.has(signature);
71
+ }
72
+ /**
73
+ * Retrieves the stored result of a previously processed task, if available.
74
+ */
75
+ async getTaskResult(title) {
76
+ const signature = AgentDiary.normalizeSignature(title);
77
+ const state = await this.readDiary();
78
+ const record = state.history.find(r => r.signature === signature);
79
+ return record?.result;
80
+ }
81
+ /**
82
+ * Filters out items that the agent has already processed.
83
+ *
84
+ * ⚠️ WARNING: filterNewTasks() is a non-atomic snapshot. Always follow it with claimTask()
85
+ * to atomically claim ownership. Never act on filterNewTasks() results directly without claiming them first.
86
+ */
87
+ async filterNewTasks(tasks) {
88
+ const state = await this.readDiary();
89
+ const seenSet = new Set(state.seenSignatures);
90
+ return tasks.filter(task => {
91
+ const signature = AgentDiary.normalizeSignature(task.title);
92
+ return !seenSet.has(signature);
93
+ });
94
+ }
95
+ /**
96
+ * Updates a claimed task with its final result.
97
+ */
98
+ async writeTaskResult(title, result) {
99
+ const signature = AgentDiary.normalizeSignature(title);
100
+ await this.storage.withLock(`diary_${this.agentId}`, async () => {
101
+ const state = await this.readDiary();
102
+ const recordIndex = state.history.findIndex(r => r.signature === signature);
103
+ if (recordIndex !== -1) {
104
+ state.history[recordIndex].result = result;
105
+ state.history[recordIndex].timestamp = Date.now(); // update timestamp
106
+ state.lastRun = Date.now();
107
+ await this.storage.set(`diary_${this.agentId}`, state);
108
+ }
109
+ else {
110
+ // If not claimed first, we throw a loud error
111
+ throw new Error(`[AgentDiary] Task "${title}" was not claimed. Call claimTask() before writeTaskResult().`);
112
+ }
113
+ });
114
+ }
115
+ }
116
+ exports.AgentDiary = AgentDiary;
@@ -0,0 +1,2 @@
1
+ export * from './storage';
2
+ export * from './diary';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./storage"), exports);
18
+ __exportStar(require("./diary"), exports);
@@ -0,0 +1,18 @@
1
+ export interface StorageAdapter<T> {
2
+ get(key: string): Promise<T | null>;
3
+ set(key: string, value: T): Promise<void>;
4
+ /**
5
+ * Acquires a lock on the key, executes the critical section, and releases the lock.
6
+ */
7
+ withLock<R>(key: string, fn: () => Promise<R>): Promise<R>;
8
+ }
9
+ export declare class LocalFileStorage<T> implements StorageAdapter<T> {
10
+ private baseDir;
11
+ constructor(options?: {
12
+ baseDir?: string;
13
+ });
14
+ private getFilePath;
15
+ get(key: string): Promise<T | null>;
16
+ set(key: string, value: T): Promise<void>;
17
+ withLock<R>(key: string, fn: () => Promise<R>): Promise<R>;
18
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LocalFileStorage = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const lockfile = __importStar(require("proper-lockfile"));
40
+ class LocalFileStorage {
41
+ baseDir;
42
+ constructor(options = {}) {
43
+ this.baseDir = options.baseDir || path.join(process.cwd(), '.agent-diaries');
44
+ if (!fs.existsSync(this.baseDir)) {
45
+ fs.mkdirSync(this.baseDir, { recursive: true });
46
+ }
47
+ }
48
+ getFilePath(key) {
49
+ const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
50
+ return path.join(this.baseDir, `${safeKey}.json`);
51
+ }
52
+ async get(key) {
53
+ const filePath = this.getFilePath(key);
54
+ if (!fs.existsSync(filePath)) {
55
+ return null;
56
+ }
57
+ try {
58
+ const data = await fs.promises.readFile(filePath, 'utf-8');
59
+ return JSON.parse(data);
60
+ }
61
+ catch (e) {
62
+ console.error(`[LocalFileStorage] Error reading key ${key}:`, e);
63
+ return null;
64
+ }
65
+ }
66
+ async set(key, value) {
67
+ const filePath = this.getFilePath(key);
68
+ try {
69
+ const data = JSON.stringify(value, null, 2);
70
+ await fs.promises.writeFile(filePath, data, 'utf-8');
71
+ }
72
+ catch (e) {
73
+ console.error(`[LocalFileStorage] Error writing key ${key}:`, e);
74
+ throw e;
75
+ }
76
+ }
77
+ async withLock(key, fn) {
78
+ const filePath = this.getFilePath(key);
79
+ // Ensure the file exists so we can lock it (proper-lockfile requires the file/dir to exist)
80
+ if (!fs.existsSync(filePath)) {
81
+ await fs.promises.writeFile(filePath, "null", 'utf-8');
82
+ }
83
+ const release = await lockfile.lock(filePath, {
84
+ retries: { retries: 100, minTimeout: 10, maxTimeout: 100 },
85
+ realpath: false
86
+ });
87
+ try {
88
+ return await fn();
89
+ }
90
+ finally {
91
+ await release();
92
+ }
93
+ }
94
+ }
95
+ exports.LocalFileStorage = LocalFileStorage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-diaries/core",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "The lightweight, framework-agnostic memory layer for edge AI agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,17 +35,16 @@
35
35
  "homepage": "https://github.com/swapwarick/agent-diaries-core#readme",
36
36
  "license": "MIT",
37
37
  "devDependencies": {
38
- "@types/better-sqlite3": "^7.6.11",
39
- "@types/dotenv": "^6.1.1",
40
- "@types/node": "^20.0.0",
38
+ "@types/better-sqlite3": "^7.6.13",
39
+ "@types/node": "^25.9.1",
41
40
  "@types/proper-lockfile": "^4.1.4",
42
- "@vitest/coverage-v8": "^4.1.5",
41
+ "@vitest/coverage-v8": "^4.1.7",
43
42
  "dotenv": "^17.4.2",
44
- "ioredis": "^5.5.0",
45
- "mongodb": "^6.13.0",
43
+ "ioredis": "^5.10.1",
44
+ "mongodb": "^7.2.0",
46
45
  "ts-node": "^10.9.2",
47
- "typescript": "^5.0.0",
48
- "vitest": "^4.1.5"
46
+ "typescript": "^6.0.3",
47
+ "vitest": "^4.1.7"
49
48
  },
50
49
  "dependencies": {
51
50
  "proper-lockfile": "^4.1.2"
@@ -66,4 +65,4 @@
66
65
  "optional": true
67
66
  }
68
67
  }
69
- }
68
+ }