@hasna/conversations 0.1.29 → 0.1.31

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
@@ -60,6 +60,7 @@ function getDb() {
60
60
  from_agent TEXT NOT NULL,
61
61
  to_agent TEXT NOT NULL,
62
62
  space TEXT,
63
+ project_id TEXT,
63
64
  content TEXT NOT NULL,
64
65
  priority TEXT NOT NULL DEFAULT 'normal',
65
66
  working_dir TEXT,
@@ -125,6 +126,20 @@ function getDb() {
125
126
  metadata TEXT
126
127
  )
127
128
  `);
129
+ db.exec(`
130
+ CREATE TABLE IF NOT EXISTS resource_locks (
131
+ resource_type TEXT NOT NULL,
132
+ resource_id TEXT NOT NULL,
133
+ agent_id TEXT NOT NULL,
134
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
135
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
136
+ expires_at TEXT NOT NULL,
137
+ UNIQUE(resource_type, resource_id, lock_type)
138
+ )
139
+ `);
140
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
141
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
142
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
128
143
  db.exec(`
129
144
  CREATE TABLE IF NOT EXISTS reactions (
130
145
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -200,6 +215,10 @@ function getDb() {
200
215
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
201
216
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
202
217
  }
218
+ if (!colNames2.includes("project_id")) {
219
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
220
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
221
+ }
203
222
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
204
223
  const presenceColNames = presenceCols.map((c) => c.name);
205
224
  if (!presenceColNames.includes("id")) {
package/bin/index.js CHANGED
@@ -1914,6 +1914,7 @@ function getDb() {
1914
1914
  from_agent TEXT NOT NULL,
1915
1915
  to_agent TEXT NOT NULL,
1916
1916
  space TEXT,
1917
+ project_id TEXT,
1917
1918
  content TEXT NOT NULL,
1918
1919
  priority TEXT NOT NULL DEFAULT 'normal',
1919
1920
  working_dir TEXT,
@@ -1979,6 +1980,20 @@ function getDb() {
1979
1980
  metadata TEXT
1980
1981
  )
1981
1982
  `);
1983
+ db.exec(`
1984
+ CREATE TABLE IF NOT EXISTS resource_locks (
1985
+ resource_type TEXT NOT NULL,
1986
+ resource_id TEXT NOT NULL,
1987
+ agent_id TEXT NOT NULL,
1988
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
1989
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
1990
+ expires_at TEXT NOT NULL,
1991
+ UNIQUE(resource_type, resource_id, lock_type)
1992
+ )
1993
+ `);
1994
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
1995
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
1996
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
1982
1997
  db.exec(`
1983
1998
  CREATE TABLE IF NOT EXISTS reactions (
1984
1999
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -2054,6 +2069,10 @@ function getDb() {
2054
2069
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
2055
2070
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
2056
2071
  }
2072
+ if (!colNames2.includes("project_id")) {
2073
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
2074
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
2075
+ }
2057
2076
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
2058
2077
  const presenceColNames = presenceCols.map((c) => c.name);
2059
2078
  if (!presenceColNames.includes("id")) {
@@ -2265,11 +2284,11 @@ function sendMessage(opts) {
2265
2284
  const blocking = opts.blocking ? 1 : 0;
2266
2285
  const replyTo = opts.reply_to || null;
2267
2286
  const stmt = db2.prepare(`
2268
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2269
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2287
+ INSERT INTO messages (session_id, from_agent, to_agent, space, project_id, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2288
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2270
2289
  RETURNING *
2271
2290
  `);
2272
- const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2291
+ const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.project_id || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2273
2292
  const message = parseMessage(row);
2274
2293
  if (opts.attachments && opts.attachments.length > 0) {
2275
2294
  const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
@@ -2313,6 +2332,10 @@ function readMessages(opts = {}) {
2313
2332
  conditions.push("space = ?");
2314
2333
  params.push(opts.space);
2315
2334
  }
2335
+ if (opts.project_id) {
2336
+ conditions.push("project_id = ?");
2337
+ params.push(opts.project_id);
2338
+ }
2316
2339
  if (opts.since) {
2317
2340
  conditions.push("created_at > ?");
2318
2341
  params.push(opts.since);
@@ -3599,7 +3622,7 @@ var init_poll = __esm(() => {
3599
3622
  var require_package = __commonJS((exports, module) => {
3600
3623
  module.exports = {
3601
3624
  name: "@hasna/conversations",
3602
- version: "0.1.29",
3625
+ version: "0.1.31",
3603
3626
  description: "Real-time CLI messaging for AI agents",
3604
3627
  type: "module",
3605
3628
  bin: {
@@ -32576,17 +32599,19 @@ var init_mcp2 = __esm(() => {
32576
32599
  content: exports_external.string(),
32577
32600
  from: exports_external.string().optional(),
32578
32601
  priority: exports_external.string().optional(),
32579
- blocking: exports_external.coerce.boolean().optional()
32602
+ blocking: exports_external.coerce.boolean().optional(),
32603
+ project_id: exports_external.string().optional()
32580
32604
  }
32581
32605
  }, async (args) => {
32582
- const { from: fromParam, to, content, priority, blocking } = args;
32606
+ const { from: fromParam, to, content, priority, blocking, project_id } = args;
32583
32607
  const from = resolveIdentity(fromParam);
32584
32608
  const msg = sendMessage({
32585
32609
  from,
32586
32610
  to,
32587
32611
  content,
32588
32612
  priority,
32589
- blocking
32613
+ blocking,
32614
+ project_id
32590
32615
  });
32591
32616
  return {
32592
32617
  content: [{ type: "text", text: JSON.stringify(msg) }]
@@ -32599,6 +32624,7 @@ var init_mcp2 = __esm(() => {
32599
32624
  from: exports_external.string().optional(),
32600
32625
  to: exports_external.string().optional(),
32601
32626
  space: exports_external.string().optional(),
32627
+ project_id: exports_external.string().optional(),
32602
32628
  since: exports_external.string().optional(),
32603
32629
  limit: exports_external.coerce.number().optional(),
32604
32630
  unread_only: exports_external.coerce.boolean().optional()
package/bin/mcp.js CHANGED
@@ -6546,6 +6546,7 @@ function getDb() {
6546
6546
  from_agent TEXT NOT NULL,
6547
6547
  to_agent TEXT NOT NULL,
6548
6548
  space TEXT,
6549
+ project_id TEXT,
6549
6550
  content TEXT NOT NULL,
6550
6551
  priority TEXT NOT NULL DEFAULT 'normal',
6551
6552
  working_dir TEXT,
@@ -6611,6 +6612,20 @@ function getDb() {
6611
6612
  metadata TEXT
6612
6613
  )
6613
6614
  `);
6615
+ db.exec(`
6616
+ CREATE TABLE IF NOT EXISTS resource_locks (
6617
+ resource_type TEXT NOT NULL,
6618
+ resource_id TEXT NOT NULL,
6619
+ agent_id TEXT NOT NULL,
6620
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
6621
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
6622
+ expires_at TEXT NOT NULL,
6623
+ UNIQUE(resource_type, resource_id, lock_type)
6624
+ )
6625
+ `);
6626
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
6627
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
6628
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
6614
6629
  db.exec(`
6615
6630
  CREATE TABLE IF NOT EXISTS reactions (
6616
6631
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -6686,6 +6701,10 @@ function getDb() {
6686
6701
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
6687
6702
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
6688
6703
  }
6704
+ if (!colNames2.includes("project_id")) {
6705
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
6706
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
6707
+ }
6689
6708
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
6690
6709
  const presenceColNames = presenceCols.map((c) => c.name);
6691
6710
  if (!presenceColNames.includes("id")) {
@@ -28718,11 +28737,11 @@ function sendMessage(opts) {
28718
28737
  const blocking = opts.blocking ? 1 : 0;
28719
28738
  const replyTo = opts.reply_to || null;
28720
28739
  const stmt = db2.prepare(`
28721
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
28722
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
28740
+ INSERT INTO messages (session_id, from_agent, to_agent, space, project_id, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
28741
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
28723
28742
  RETURNING *
28724
28743
  `);
28725
- const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
28744
+ const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.project_id || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
28726
28745
  const message = parseMessage(row);
28727
28746
  if (opts.attachments && opts.attachments.length > 0) {
28728
28747
  const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
@@ -28766,6 +28785,10 @@ function readMessages(opts = {}) {
28766
28785
  conditions.push("space = ?");
28767
28786
  params.push(opts.space);
28768
28787
  }
28788
+ if (opts.project_id) {
28789
+ conditions.push("project_id = ?");
28790
+ params.push(opts.project_id);
28791
+ }
28769
28792
  if (opts.since) {
28770
28793
  conditions.push("created_at > ?");
28771
28794
  params.push(opts.since);
@@ -29872,7 +29895,7 @@ function renameAgent(oldName, newName) {
29872
29895
  // package.json
29873
29896
  var package_default = {
29874
29897
  name: "@hasna/conversations",
29875
- version: "0.1.29",
29898
+ version: "0.1.31",
29876
29899
  description: "Real-time CLI messaging for AI agents",
29877
29900
  type: "module",
29878
29901
  bin: {
@@ -29961,17 +29984,19 @@ server.registerTool("send_message", {
29961
29984
  content: exports_external.string(),
29962
29985
  from: exports_external.string().optional(),
29963
29986
  priority: exports_external.string().optional(),
29964
- blocking: exports_external.coerce.boolean().optional()
29987
+ blocking: exports_external.coerce.boolean().optional(),
29988
+ project_id: exports_external.string().optional()
29965
29989
  }
29966
29990
  }, async (args) => {
29967
- const { from: fromParam, to, content, priority, blocking } = args;
29991
+ const { from: fromParam, to, content, priority, blocking, project_id } = args;
29968
29992
  const from = resolveIdentity(fromParam);
29969
29993
  const msg = sendMessage({
29970
29994
  from,
29971
29995
  to,
29972
29996
  content,
29973
29997
  priority,
29974
- blocking
29998
+ blocking,
29999
+ project_id
29975
30000
  });
29976
30001
  return {
29977
30002
  content: [{ type: "text", text: JSON.stringify(msg) }]
@@ -29984,6 +30009,7 @@ server.registerTool("read_messages", {
29984
30009
  from: exports_external.string().optional(),
29985
30010
  to: exports_external.string().optional(),
29986
30011
  space: exports_external.string().optional(),
30012
+ project_id: exports_external.string().optional(),
29987
30013
  since: exports_external.string().optional(),
29988
30014
  limit: exports_external.coerce.number().optional(),
29989
30015
  unread_only: exports_external.coerce.boolean().optional()
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
@@ -73,6 +73,7 @@ function getDb() {
73
73
  from_agent TEXT NOT NULL,
74
74
  to_agent TEXT NOT NULL,
75
75
  space TEXT,
76
+ project_id TEXT,
76
77
  content TEXT NOT NULL,
77
78
  priority TEXT NOT NULL DEFAULT 'normal',
78
79
  working_dir TEXT,
@@ -138,6 +139,20 @@ function getDb() {
138
139
  metadata TEXT
139
140
  )
140
141
  `);
142
+ db.exec(`
143
+ CREATE TABLE IF NOT EXISTS resource_locks (
144
+ resource_type TEXT NOT NULL,
145
+ resource_id TEXT NOT NULL,
146
+ agent_id TEXT NOT NULL,
147
+ lock_type TEXT NOT NULL DEFAULT 'advisory',
148
+ locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
149
+ expires_at TEXT NOT NULL,
150
+ UNIQUE(resource_type, resource_id, lock_type)
151
+ )
152
+ `);
153
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
154
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
155
+ db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
141
156
  db.exec(`
142
157
  CREATE TABLE IF NOT EXISTS reactions (
143
158
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -213,6 +228,10 @@ function getDb() {
213
228
  db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
214
229
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
215
230
  }
231
+ if (!colNames2.includes("project_id")) {
232
+ db.exec("ALTER TABLE messages ADD COLUMN project_id TEXT");
233
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id)");
234
+ }
216
235
  const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
217
236
  const presenceColNames = presenceCols.map((c) => c.name);
218
237
  if (!presenceColNames.includes("id")) {
@@ -2239,11 +2258,11 @@ function sendMessage(opts) {
2239
2258
  const blocking = opts.blocking ? 1 : 0;
2240
2259
  const replyTo = opts.reply_to || null;
2241
2260
  const stmt = db2.prepare(`
2242
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2243
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2261
+ INSERT INTO messages (session_id, from_agent, to_agent, space, project_id, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2262
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2244
2263
  RETURNING *
2245
2264
  `);
2246
- const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2265
+ const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.project_id || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking, replyTo);
2247
2266
  const message = parseMessage(row);
2248
2267
  if (opts.attachments && opts.attachments.length > 0) {
2249
2268
  const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
@@ -2287,6 +2306,10 @@ function readMessages(opts = {}) {
2287
2306
  conditions.push("space = ?");
2288
2307
  params.push(opts.space);
2289
2308
  }
2309
+ if (opts.project_id) {
2310
+ conditions.push("project_id = ?");
2311
+ params.push(opts.project_id);
2312
+ }
2290
2313
  if (opts.since) {
2291
2314
  conditions.push("created_at > ?");
2292
2315
  params.push(opts.since);
@@ -3557,6 +3580,80 @@ function renameAgent(oldName, newName) {
3557
3580
  db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
3558
3581
  return true;
3559
3582
  }
3583
+ // src/lib/locks.ts
3584
+ init_db();
3585
+ var DEFAULT_LOCK_EXPIRY_MS = 5 * 60 * 1000;
3586
+ function acquireLock(resourceType, resourceId, agentId, lockType = "advisory", expiryMs = DEFAULT_LOCK_EXPIRY_MS) {
3587
+ const db2 = getDb();
3588
+ return db2.transaction(() => {
3589
+ cleanExpiredLocks();
3590
+ const existing = db2.prepare(`
3591
+ SELECT * FROM resource_locks
3592
+ WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
3593
+ `).get(resourceType, resourceId, lockType);
3594
+ if (existing) {
3595
+ if (existing.agent_id !== agentId) {
3596
+ return { acquired: false, lock: null, held_by: existing.agent_id };
3597
+ }
3598
+ const expiresAt = new Date(Date.now() + expiryMs).toISOString().replace("T", "T").replace("Z", "");
3599
+ db2.prepare(`
3600
+ UPDATE resource_locks SET expires_at = ?, locked_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
3601
+ WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
3602
+ `).run(expiresAt, resourceType, resourceId, lockType);
3603
+ } else {
3604
+ const expiresAt = new Date(Date.now() + expiryMs).toISOString().slice(0, -1);
3605
+ db2.prepare(`
3606
+ INSERT INTO resource_locks (resource_type, resource_id, agent_id, lock_type, locked_at, expires_at)
3607
+ VALUES (?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'), ?)
3608
+ `).run(resourceType, resourceId, agentId, lockType, expiresAt);
3609
+ }
3610
+ const lock = db2.prepare(`
3611
+ SELECT * FROM resource_locks WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
3612
+ `).get(resourceType, resourceId, lockType);
3613
+ return { acquired: true, lock };
3614
+ }).immediate();
3615
+ }
3616
+ function releaseLock(resourceType, resourceId, agentId) {
3617
+ const db2 = getDb();
3618
+ const result = db2.prepare(`
3619
+ DELETE FROM resource_locks
3620
+ WHERE resource_type = ? AND resource_id = ? AND agent_id = ?
3621
+ `).run(resourceType, resourceId, agentId);
3622
+ return result.changes > 0;
3623
+ }
3624
+ function checkLock(resourceType, resourceId) {
3625
+ const db2 = getDb();
3626
+ cleanExpiredLocks();
3627
+ return db2.prepare(`
3628
+ SELECT * FROM resource_locks
3629
+ WHERE resource_type = ? AND resource_id = ?
3630
+ ORDER BY locked_at ASC
3631
+ LIMIT 1
3632
+ `).get(resourceType, resourceId);
3633
+ }
3634
+ function cleanExpiredLocks() {
3635
+ const db2 = getDb();
3636
+ const result = db2.prepare(`
3637
+ DELETE FROM resource_locks WHERE expires_at < strftime('%Y-%m-%dT%H:%M:%f', 'now')
3638
+ `).run();
3639
+ return result.changes;
3640
+ }
3641
+ function listLocks(opts) {
3642
+ const db2 = getDb();
3643
+ cleanExpiredLocks();
3644
+ let query = "SELECT * FROM resource_locks WHERE 1=1";
3645
+ const params = [];
3646
+ if (opts?.resource_type) {
3647
+ query += " AND resource_type = ?";
3648
+ params.push(opts.resource_type);
3649
+ }
3650
+ if (opts?.agent_id) {
3651
+ query += " AND agent_id = ?";
3652
+ params.push(opts.agent_id);
3653
+ }
3654
+ query += " ORDER BY locked_at ASC";
3655
+ return db2.prepare(query).all(...params);
3656
+ }
3560
3657
  export {
3561
3658
  useSpaceMessages,
3562
3659
  updateSpace,
@@ -3571,6 +3668,7 @@ export {
3571
3668
  renameAgent,
3572
3669
  removeReaction,
3573
3670
  removePresence,
3671
+ releaseLock,
3574
3672
  registerAgent,
3575
3673
  readMessages,
3576
3674
  pinMessage,
@@ -3581,6 +3679,7 @@ export {
3581
3679
  listSpaces,
3582
3680
  listSessions,
3583
3681
  listProjects,
3682
+ listLocks,
3584
3683
  listAgents,
3585
3684
  leaveSpace,
3586
3685
  joinSpace,
@@ -3610,6 +3709,9 @@ export {
3610
3709
  createSpace,
3611
3710
  createProject,
3612
3711
  closeDb,
3712
+ cleanExpiredLocks,
3713
+ checkLock,
3613
3714
  archiveSpace,
3614
- addReaction
3715
+ addReaction,
3716
+ acquireLock
3615
3717
  };
@@ -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/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface Message {
5
5
  from_agent: string;
6
6
  to_agent: string;
7
7
  space: string | null;
8
+ project_id: string | null;
8
9
  content: string;
9
10
  priority: Priority;
10
11
  working_dir: string | null;
@@ -80,6 +81,7 @@ export interface SendMessageOptions {
80
81
  content: string;
81
82
  session_id?: string;
82
83
  space?: string;
84
+ project_id?: string;
83
85
  priority?: Priority;
84
86
  working_dir?: string;
85
87
  repository?: string;
@@ -97,6 +99,7 @@ export interface ReadMessagesOptions {
97
99
  from?: string;
98
100
  to?: string;
99
101
  space?: string;
102
+ project_id?: string;
100
103
  since?: string;
101
104
  since_id?: number;
102
105
  limit?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/conversations",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "Real-time CLI messaging for AI agents",
5
5
  "type": "module",
6
6
  "bin": {