@hasna/conversations 0.1.29 → 0.1.30

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/bin/hook.js CHANGED
@@ -125,6 +125,20 @@ function getDb() {
125
125
  metadata TEXT
126
126
  )
127
127
  `);
128
+ db.exec(`
129
+ CREATE TABLE IF NOT EXISTS resource_locks (
130
+ resource_type TEXT NOT NULL,
131
+ resource_id TEXT NOT NULL,
132
+ agent_id TEXT NOT NULL,
133
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
134
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
135
+ expires_at TEXT NOT NULL,
136
+ UNIQUE(resource_type, resource_id, lock_type)
137
+ )
138
+ `);
139
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
140
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
141
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
128
142
  db.exec(`
129
143
  CREATE TABLE IF NOT EXISTS reactions (
130
144
  id INTEGER PRIMARY KEY AUTOINCREMENT,
package/bin/index.js CHANGED
@@ -1979,6 +1979,20 @@ function getDb() {
1979
1979
  metadata TEXT
1980
1980
  )
1981
1981
  `);
1982
+ db.exec(`
1983
+ CREATE TABLE IF NOT EXISTS resource_locks (
1984
+ resource_type TEXT NOT NULL,
1985
+ resource_id TEXT NOT NULL,
1986
+ agent_id TEXT NOT NULL,
1987
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
1988
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
1989
+ expires_at TEXT NOT NULL,
1990
+ UNIQUE(resource_type, resource_id, lock_type)
1991
+ )
1992
+ `);
1993
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
1994
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
1995
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
1982
1996
  db.exec(`
1983
1997
  CREATE TABLE IF NOT EXISTS reactions (
1984
1998
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -3599,7 +3613,7 @@ var init_poll = __esm(() => {
3599
3613
  var require_package = __commonJS((exports, module) => {
3600
3614
  module.exports = {
3601
3615
  name: "@hasna/conversations",
3602
- version: "0.1.29",
3616
+ version: "0.1.30",
3603
3617
  description: "Real-time CLI messaging for AI agents",
3604
3618
  type: "module",
3605
3619
  bin: {
package/bin/mcp.js CHANGED
@@ -6611,6 +6611,20 @@ function getDb() {
6611
6611
  metadata TEXT
6612
6612
  )
6613
6613
  `);
6614
+ db.exec(`
6615
+ CREATE TABLE IF NOT EXISTS resource_locks (
6616
+ resource_type TEXT NOT NULL,
6617
+ resource_id TEXT NOT NULL,
6618
+ agent_id TEXT NOT NULL,
6619
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
6620
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
6621
+ expires_at TEXT NOT NULL,
6622
+ UNIQUE(resource_type, resource_id, lock_type)
6623
+ )
6624
+ `);
6625
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
6626
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
6627
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
6614
6628
  db.exec(`
6615
6629
  CREATE TABLE IF NOT EXISTS reactions (
6616
6630
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -29872,7 +29886,7 @@ function renameAgent(oldName, newName) {
29872
29886
  // package.json
29873
29887
  var package_default = {
29874
29888
  name: "@hasna/conversations",
29875
- version: "0.1.29",
29889
+ version: "0.1.30",
29876
29890
  description: "Real-time CLI messaging for AI agents",
29877
29891
  type: "module",
29878
29892
  bin: {
package/dist/index.d.ts CHANGED
@@ -19,4 +19,6 @@ export { resolveIdentity, requireIdentity, } from "./lib/identity.js";
19
19
  export { addReaction, removeReaction, getReactions, getReactionSummary, } from "./lib/reactions.js";
20
20
  export { fireWebhooks, } from "./lib/webhooks.js";
21
21
  export { heartbeat, registerAgent, isAgentConflict, getPresence, listAgents, removePresence, renameAgent, } from "./lib/presence.js";
22
+ export { acquireLock, releaseLock, checkLock, cleanExpiredLocks, listLocks, } from "./lib/locks.js";
23
+ export type { ResourceLock } from "./lib/locks.js";
22
24
  export type { Message, Session, Space, SpaceInfo, SpaceMember, Project, ProjectInfo, Priority, SendMessageOptions, ReadMessagesOptions, SearchMessagesOptions, AgentPresence, AgentConflictError, RegisterAgentResult, Reaction, Attachment, } from "./types.js";
package/dist/index.js CHANGED
@@ -138,6 +138,20 @@ function getDb() {
138
138
  metadata TEXT
139
139
  )
140
140
  `);
141
+ db.exec(`
142
+ CREATE TABLE IF NOT EXISTS resource_locks (
143
+ resource_type TEXT NOT NULL,
144
+ resource_id TEXT NOT NULL,
145
+ agent_id TEXT NOT NULL,
146
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
147
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
148
+ expires_at TEXT NOT NULL,
149
+ UNIQUE(resource_type, resource_id, lock_type)
150
+ )
151
+ `);
152
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
153
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
154
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
141
155
  db.exec(`
142
156
  CREATE TABLE IF NOT EXISTS reactions (
143
157
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -3557,6 +3571,80 @@ function renameAgent(oldName, newName) {
3557
3571
  db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
3558
3572
  return true;
3559
3573
  }
3574
+ // src/lib/locks.ts
3575
+ init_db();
3576
+ var DEFAULT_LOCK_EXPIRY_MS = 5 * 60 * 1000;
3577
+ function acquireLock(resourceType, resourceId, agentId, lockType = "advisory", expiryMs = DEFAULT_LOCK_EXPIRY_MS) {
3578
+ const db2 = getDb();
3579
+ return db2.transaction(() => {
3580
+ cleanExpiredLocks();
3581
+ const existing = db2.prepare(`
3582
+ SELECT * FROM resource_locks
3583
+ WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
3584
+ `).get(resourceType, resourceId, lockType);
3585
+ if (existing) {
3586
+ if (existing.agent_id !== agentId) {
3587
+ return { acquired: false, lock: null, held_by: existing.agent_id };
3588
+ }
3589
+ const expiresAt = new Date(Date.now() + expiryMs).toISOString().replace("T", "T").replace("Z", "");
3590
+ db2.prepare(`
3591
+ UPDATE resource_locks SET expires_at = ?, locked_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3592
+ WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
3593
+ `).run(expiresAt, resourceType, resourceId, lockType);
3594
+ } else {
3595
+ const expiresAt = new Date(Date.now() + expiryMs).toISOString().slice(0, -1);
3596
+ db2.prepare(`
3597
+ INSERT INTO resource_locks (resource_type, resource_id, agent_id, lock_type, locked_at, expires_at)
3598
+ VALUES (?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'), ?)
3599
+ `).run(resourceType, resourceId, agentId, lockType, expiresAt);
3600
+ }
3601
+ const lock = db2.prepare(`
3602
+ SELECT * FROM resource_locks WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
3603
+ `).get(resourceType, resourceId, lockType);
3604
+ return { acquired: true, lock };
3605
+ }).immediate();
3606
+ }
3607
+ function releaseLock(resourceType, resourceId, agentId) {
3608
+ const db2 = getDb();
3609
+ const result = db2.prepare(`
3610
+ DELETE FROM resource_locks
3611
+ WHERE resource_type = ? AND resource_id = ? AND agent_id = ?
3612
+ `).run(resourceType, resourceId, agentId);
3613
+ return result.changes > 0;
3614
+ }
3615
+ function checkLock(resourceType, resourceId) {
3616
+ const db2 = getDb();
3617
+ cleanExpiredLocks();
3618
+ return db2.prepare(`
3619
+ SELECT * FROM resource_locks
3620
+ WHERE resource_type = ? AND resource_id = ?
3621
+ ORDER BY locked_at ASC
3622
+ LIMIT 1
3623
+ `).get(resourceType, resourceId);
3624
+ }
3625
+ function cleanExpiredLocks() {
3626
+ const db2 = getDb();
3627
+ const result = db2.prepare(`
3628
+ DELETE FROM resource_locks WHERE expires_at < strftime('%Y-%m-%dT%H:%M:%f', 'now')
3629
+ `).run();
3630
+ return result.changes;
3631
+ }
3632
+ function listLocks(opts) {
3633
+ const db2 = getDb();
3634
+ cleanExpiredLocks();
3635
+ let query = "SELECT * FROM resource_locks WHERE 1=1";
3636
+ const params = [];
3637
+ if (opts?.resource_type) {
3638
+ query += " AND resource_type = ?";
3639
+ params.push(opts.resource_type);
3640
+ }
3641
+ if (opts?.agent_id) {
3642
+ query += " AND agent_id = ?";
3643
+ params.push(opts.agent_id);
3644
+ }
3645
+ query += " ORDER BY locked_at ASC";
3646
+ return db2.prepare(query).all(...params);
3647
+ }
3560
3648
  export {
3561
3649
  useSpaceMessages,
3562
3650
  updateSpace,
@@ -3571,6 +3659,7 @@ export {
3571
3659
  renameAgent,
3572
3660
  removeReaction,
3573
3661
  removePresence,
3662
+ releaseLock,
3574
3663
  registerAgent,
3575
3664
  readMessages,
3576
3665
  pinMessage,
@@ -3581,6 +3670,7 @@ export {
3581
3670
  listSpaces,
3582
3671
  listSessions,
3583
3672
  listProjects,
3673
+ listLocks,
3584
3674
  listAgents,
3585
3675
  leaveSpace,
3586
3676
  joinSpace,
@@ -3610,6 +3700,9 @@ export {
3610
3700
  createSpace,
3611
3701
  createProject,
3612
3702
  closeDb,
3703
+ cleanExpiredLocks,
3704
+ checkLock,
3613
3705
  archiveSpace,
3614
- addReaction
3706
+ addReaction,
3707
+ acquireLock
3615
3708
  };
@@ -0,0 +1,20 @@
1
+ export interface ResourceLock {
2
+ resource_type: string;
3
+ resource_id: string;
4
+ agent_id: string;
5
+ lock_type: "advisory" | "exclusive";
6
+ locked_at: string;
7
+ expires_at: string;
8
+ }
9
+ export declare function acquireLock(resourceType: string, resourceId: string, agentId: string, lockType?: "advisory" | "exclusive", expiryMs?: number): {
10
+ acquired: boolean;
11
+ lock: ResourceLock | null;
12
+ held_by?: string;
13
+ };
14
+ export declare function releaseLock(resourceType: string, resourceId: string, agentId: string): boolean;
15
+ export declare function checkLock(resourceType: string, resourceId: string): ResourceLock | null;
16
+ export declare function cleanExpiredLocks(): number;
17
+ export declare function listLocks(opts?: {
18
+ resource_type?: string;
19
+ agent_id?: string;
20
+ }): ResourceLock[];
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {