@hasna/conversations 0.2.3 → 0.2.5
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/index.js +56 -15
- package/bin/mcp.js +56 -15
- package/dist/index.js +18 -1
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -2368,11 +2368,28 @@ function readMessages(opts = {}) {
|
|
|
2368
2368
|
if (opts.unread_only) {
|
|
2369
2369
|
conditions.push("read_at IS NULL");
|
|
2370
2370
|
}
|
|
2371
|
+
if (opts.threads_only) {
|
|
2372
|
+
conditions.push("reply_to IS NULL");
|
|
2373
|
+
}
|
|
2371
2374
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2372
2375
|
const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
2373
2376
|
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
2374
2377
|
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
|
|
2375
|
-
|
|
2378
|
+
let messages = rows.map(parseMessage);
|
|
2379
|
+
if (opts.include_reply_counts && messages.length > 0) {
|
|
2380
|
+
const db22 = getDb();
|
|
2381
|
+
const counts = db22.prepare(`SELECT reply_to, COUNT(*) as c FROM messages WHERE reply_to IN (${messages.map(() => "?").join(",")}) GROUP BY reply_to`).all(...messages.map((m) => m.id));
|
|
2382
|
+
const countMap = new Map(counts.map((r) => [r.reply_to, r.c]));
|
|
2383
|
+
messages = messages.map((m) => ({ ...m, reply_count: countMap.get(m.id) ?? 0 }));
|
|
2384
|
+
}
|
|
2385
|
+
if (opts.max_content_length && opts.max_content_length > 0) {
|
|
2386
|
+
messages = messages.map((m) => {
|
|
2387
|
+
if (m.content.length > opts.max_content_length) {
|
|
2388
|
+
return { ...m, content: m.content.slice(0, opts.max_content_length) + "\u2026", truncated: true };
|
|
2389
|
+
}
|
|
2390
|
+
return m;
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2376
2393
|
if (opts.compact)
|
|
2377
2394
|
return messages.map(compactMessage);
|
|
2378
2395
|
return messages;
|
|
@@ -4316,7 +4333,7 @@ var init_poll = __esm(() => {
|
|
|
4316
4333
|
var require_package = __commonJS((exports, module) => {
|
|
4317
4334
|
module.exports = {
|
|
4318
4335
|
name: "@hasna/conversations",
|
|
4319
|
-
version: "0.2.
|
|
4336
|
+
version: "0.2.5",
|
|
4320
4337
|
description: "Real-time CLI messaging for AI agents",
|
|
4321
4338
|
type: "module",
|
|
4322
4339
|
bin: {
|
|
@@ -33568,7 +33585,10 @@ var init_mcp2 = __esm(() => {
|
|
|
33568
33585
|
since: exports_external.string().optional(),
|
|
33569
33586
|
limit: exports_external.coerce.number().optional(),
|
|
33570
33587
|
unread_only: exports_external.coerce.boolean().optional(),
|
|
33571
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
33588
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
33589
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)"),
|
|
33590
|
+
threads_only: exports_external.coerce.boolean().optional().describe("Only return root messages (reply_to IS NULL) \u2014 hides thread replies"),
|
|
33591
|
+
include_reply_counts: exports_external.coerce.boolean().optional().describe("Include reply_count on each message (adds one extra query)")
|
|
33572
33592
|
}
|
|
33573
33593
|
}, async (args) => {
|
|
33574
33594
|
const agent = resolveIdentity(args.from);
|
|
@@ -33596,7 +33616,7 @@ var init_mcp2 = __esm(() => {
|
|
|
33596
33616
|
};
|
|
33597
33617
|
});
|
|
33598
33618
|
server.registerTool("reply", {
|
|
33599
|
-
description: "Reply to a message
|
|
33619
|
+
description: "Reply to a specific message, creating a thread. Sets reply_to so it can be retrieved with get_thread_replies.",
|
|
33600
33620
|
inputSchema: {
|
|
33601
33621
|
message_id: exports_external.coerce.number(),
|
|
33602
33622
|
content: exports_external.string(),
|
|
@@ -33613,13 +33633,13 @@ var init_mcp2 = __esm(() => {
|
|
|
33613
33633
|
}
|
|
33614
33634
|
const from = resolveIdentity(fromParam);
|
|
33615
33635
|
const space = original.space || (original.session_id?.startsWith("space:") ? original.session_id.slice(6) : undefined);
|
|
33616
|
-
const to = space ? space : original.from_agent === from ? original.to_agent : original.from_agent;
|
|
33617
33636
|
const msg = sendMessage({
|
|
33618
33637
|
from,
|
|
33619
|
-
to,
|
|
33638
|
+
to: space ?? (original.from_agent === from ? original.to_agent : original.from_agent),
|
|
33620
33639
|
content,
|
|
33621
33640
|
session_id: original.session_id,
|
|
33622
|
-
space
|
|
33641
|
+
space,
|
|
33642
|
+
reply_to: message_id
|
|
33623
33643
|
});
|
|
33624
33644
|
return {
|
|
33625
33645
|
content: [{ type: "text", text: JSON.stringify(msg) }]
|
|
@@ -33786,11 +33806,14 @@ var init_mcp2 = __esm(() => {
|
|
|
33786
33806
|
space: exports_external.string(),
|
|
33787
33807
|
since: exports_external.string().optional(),
|
|
33788
33808
|
limit: exports_external.coerce.number().optional(),
|
|
33789
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
33809
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
33810
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)"),
|
|
33811
|
+
threads_only: exports_external.coerce.boolean().optional().describe("Only return root messages (hides thread replies)"),
|
|
33812
|
+
include_reply_counts: exports_external.coerce.boolean().optional().describe("Include reply_count on each message")
|
|
33790
33813
|
}
|
|
33791
33814
|
}, async (args) => {
|
|
33792
|
-
const { space, since, limit, mark_read } = args;
|
|
33793
|
-
const messages = readMessages({ space, since, limit });
|
|
33815
|
+
const { space, since, limit, mark_read, max_content_length, threads_only, include_reply_counts } = args;
|
|
33816
|
+
const messages = readMessages({ space, since, limit, max_content_length, threads_only, include_reply_counts });
|
|
33794
33817
|
if (mark_read !== false && messages.length > 0) {
|
|
33795
33818
|
markReadByIds(messages.map((m) => m.id));
|
|
33796
33819
|
}
|
|
@@ -34441,13 +34464,30 @@ var init_mcp2 = __esm(() => {
|
|
|
34441
34464
|
return { content: [{ type: "text", text: JSON.stringify({ released_stale_agent: stale, released_expired: expired, total: stale + expired }) }] };
|
|
34442
34465
|
});
|
|
34443
34466
|
server.registerTool("get_thread_replies", {
|
|
34444
|
-
description: "Get all replies in a thread for a given parent message ID.",
|
|
34467
|
+
description: "Get all replies in a thread for a given parent message ID. Also accessible as read_thread.",
|
|
34445
34468
|
inputSchema: {
|
|
34446
|
-
message_id: exports_external.coerce.number()
|
|
34469
|
+
message_id: exports_external.coerce.number(),
|
|
34470
|
+
limit: exports_external.coerce.number().optional()
|
|
34471
|
+
}
|
|
34472
|
+
}, async (args) => {
|
|
34473
|
+
let replies = getThreadReplies(args.message_id);
|
|
34474
|
+
if (args.limit)
|
|
34475
|
+
replies = replies.slice(0, args.limit);
|
|
34476
|
+
const parent = getMessageById(args.message_id);
|
|
34477
|
+
return { content: [{ type: "text", text: JSON.stringify({ parent, replies, reply_count: replies.length }) }] };
|
|
34478
|
+
});
|
|
34479
|
+
server.registerTool("read_thread", {
|
|
34480
|
+
description: "Alias for get_thread_replies. Read all replies to a specific message, forming a thread view.",
|
|
34481
|
+
inputSchema: {
|
|
34482
|
+
message_id: exports_external.coerce.number(),
|
|
34483
|
+
limit: exports_external.coerce.number().optional()
|
|
34447
34484
|
}
|
|
34448
34485
|
}, async (args) => {
|
|
34449
|
-
|
|
34450
|
-
|
|
34486
|
+
let replies = getThreadReplies(args.message_id);
|
|
34487
|
+
if (args.limit)
|
|
34488
|
+
replies = replies.slice(0, args.limit);
|
|
34489
|
+
const parent = getMessageById(args.message_id);
|
|
34490
|
+
return { content: [{ type: "text", text: JSON.stringify({ parent, replies, reply_count: replies.length }) }] };
|
|
34451
34491
|
});
|
|
34452
34492
|
server.registerTool("set_focus", {
|
|
34453
34493
|
description: "Set agent focus to a project. All read-heavy tools will default to this project scope. Stores in MCP session memory AND updates agent_presence.project_id in DB.",
|
|
@@ -34695,7 +34735,7 @@ var init_mcp2 = __esm(() => {
|
|
|
34695
34735
|
read_messages: "Read messages with filters. Optional: session_id?, from?, to?, space?, since?(ISO), limit?, unread_only?, mark_read?(default true \u2014 auto-marks returned messages as read, pass false to peek without consuming)",
|
|
34696
34736
|
read_digest: "Lightweight unread digest \u2014 preview only (no full bodies), auto-marks read, never overflows tokens. Returns { messages, total_unread, shown }. Optional: space?, session_id?, to?, since?(ISO), limit?, project_id?",
|
|
34697
34737
|
list_sessions: "List all DM sessions. Optional: agent?(filter by participant)",
|
|
34698
|
-
reply: "Reply to a message
|
|
34738
|
+
reply: "Reply to a specific message, creating a thread (sets reply_to). Use read_thread to retrieve. Required: message_id, content. Optional: from?",
|
|
34699
34739
|
mark_read: "Mark messages as read. Optional: from?, ids?(array), all?(bool \u2014 mark all unread)",
|
|
34700
34740
|
search_messages: "Full-text search messages. Required: query. Optional: space?, from?, to?, limit?",
|
|
34701
34741
|
export_messages: "Export messages as JSON or CSV. Optional: space?, session_id?, from?, since?, until?, format?(json|csv)",
|
|
@@ -34739,6 +34779,7 @@ var init_mcp2 = __esm(() => {
|
|
|
34739
34779
|
list_locks: "List active locks enriched with agent presence + time context. Optional: resource_type?, agent_id?",
|
|
34740
34780
|
clean_expired_locks: "Release expired locks + locks held by agents with stale heartbeat (>30 min). Returns {released_stale_agent, released_expired, total}",
|
|
34741
34781
|
get_thread_replies: "Get all replies in a thread. Required: message_id. Optional: limit?",
|
|
34782
|
+
read_thread: "Alias for get_thread_replies. Required: message_id. Optional: limit?",
|
|
34742
34783
|
set_focus: "Set agent focus to a project. All read tools default to this scope. Required: project_id. Optional: from?",
|
|
34743
34784
|
get_focus: "Get current focus: session focus, DB project_id, effective project_id. Optional: from?",
|
|
34744
34785
|
unfocus: "Clear agent focus (session + DB). Optional: from?",
|
package/bin/mcp.js
CHANGED
|
@@ -28877,11 +28877,28 @@ function readMessages(opts = {}) {
|
|
|
28877
28877
|
if (opts.unread_only) {
|
|
28878
28878
|
conditions.push("read_at IS NULL");
|
|
28879
28879
|
}
|
|
28880
|
+
if (opts.threads_only) {
|
|
28881
|
+
conditions.push("reply_to IS NULL");
|
|
28882
|
+
}
|
|
28880
28883
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
28881
28884
|
const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
28882
28885
|
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
28883
28886
|
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
|
|
28884
|
-
|
|
28887
|
+
let messages = rows.map(parseMessage);
|
|
28888
|
+
if (opts.include_reply_counts && messages.length > 0) {
|
|
28889
|
+
const db22 = getDb();
|
|
28890
|
+
const counts = db22.prepare(`SELECT reply_to, COUNT(*) as c FROM messages WHERE reply_to IN (${messages.map(() => "?").join(",")}) GROUP BY reply_to`).all(...messages.map((m) => m.id));
|
|
28891
|
+
const countMap = new Map(counts.map((r) => [r.reply_to, r.c]));
|
|
28892
|
+
messages = messages.map((m) => ({ ...m, reply_count: countMap.get(m.id) ?? 0 }));
|
|
28893
|
+
}
|
|
28894
|
+
if (opts.max_content_length && opts.max_content_length > 0) {
|
|
28895
|
+
messages = messages.map((m) => {
|
|
28896
|
+
if (m.content.length > opts.max_content_length) {
|
|
28897
|
+
return { ...m, content: m.content.slice(0, opts.max_content_length) + "\u2026", truncated: true };
|
|
28898
|
+
}
|
|
28899
|
+
return m;
|
|
28900
|
+
});
|
|
28901
|
+
}
|
|
28885
28902
|
if (opts.compact)
|
|
28886
28903
|
return messages.map(compactMessage);
|
|
28887
28904
|
return messages;
|
|
@@ -30800,7 +30817,7 @@ function getGraphStats() {
|
|
|
30800
30817
|
// package.json
|
|
30801
30818
|
var package_default = {
|
|
30802
30819
|
name: "@hasna/conversations",
|
|
30803
|
-
version: "0.2.
|
|
30820
|
+
version: "0.2.5",
|
|
30804
30821
|
description: "Real-time CLI messaging for AI agents",
|
|
30805
30822
|
type: "module",
|
|
30806
30823
|
bin: {
|
|
@@ -30931,7 +30948,10 @@ server.registerTool("read_messages", {
|
|
|
30931
30948
|
since: exports_external.string().optional(),
|
|
30932
30949
|
limit: exports_external.coerce.number().optional(),
|
|
30933
30950
|
unread_only: exports_external.coerce.boolean().optional(),
|
|
30934
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
30951
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
30952
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)"),
|
|
30953
|
+
threads_only: exports_external.coerce.boolean().optional().describe("Only return root messages (reply_to IS NULL) \u2014 hides thread replies"),
|
|
30954
|
+
include_reply_counts: exports_external.coerce.boolean().optional().describe("Include reply_count on each message (adds one extra query)")
|
|
30935
30955
|
}
|
|
30936
30956
|
}, async (args) => {
|
|
30937
30957
|
const agent = resolveIdentity(args.from);
|
|
@@ -30959,7 +30979,7 @@ server.registerTool("list_sessions", {
|
|
|
30959
30979
|
};
|
|
30960
30980
|
});
|
|
30961
30981
|
server.registerTool("reply", {
|
|
30962
|
-
description: "Reply to a message
|
|
30982
|
+
description: "Reply to a specific message, creating a thread. Sets reply_to so it can be retrieved with get_thread_replies.",
|
|
30963
30983
|
inputSchema: {
|
|
30964
30984
|
message_id: exports_external.coerce.number(),
|
|
30965
30985
|
content: exports_external.string(),
|
|
@@ -30976,13 +30996,13 @@ server.registerTool("reply", {
|
|
|
30976
30996
|
}
|
|
30977
30997
|
const from = resolveIdentity(fromParam);
|
|
30978
30998
|
const space = original.space || (original.session_id?.startsWith("space:") ? original.session_id.slice(6) : undefined);
|
|
30979
|
-
const to = space ? space : original.from_agent === from ? original.to_agent : original.from_agent;
|
|
30980
30999
|
const msg = sendMessage({
|
|
30981
31000
|
from,
|
|
30982
|
-
to,
|
|
31001
|
+
to: space ?? (original.from_agent === from ? original.to_agent : original.from_agent),
|
|
30983
31002
|
content,
|
|
30984
31003
|
session_id: original.session_id,
|
|
30985
|
-
space
|
|
31004
|
+
space,
|
|
31005
|
+
reply_to: message_id
|
|
30986
31006
|
});
|
|
30987
31007
|
return {
|
|
30988
31008
|
content: [{ type: "text", text: JSON.stringify(msg) }]
|
|
@@ -31149,11 +31169,14 @@ server.registerTool("read_space", {
|
|
|
31149
31169
|
space: exports_external.string(),
|
|
31150
31170
|
since: exports_external.string().optional(),
|
|
31151
31171
|
limit: exports_external.coerce.number().optional(),
|
|
31152
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
31172
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
31173
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)"),
|
|
31174
|
+
threads_only: exports_external.coerce.boolean().optional().describe("Only return root messages (hides thread replies)"),
|
|
31175
|
+
include_reply_counts: exports_external.coerce.boolean().optional().describe("Include reply_count on each message")
|
|
31153
31176
|
}
|
|
31154
31177
|
}, async (args) => {
|
|
31155
|
-
const { space, since, limit, mark_read } = args;
|
|
31156
|
-
const messages = readMessages({ space, since, limit });
|
|
31178
|
+
const { space, since, limit, mark_read, max_content_length, threads_only, include_reply_counts } = args;
|
|
31179
|
+
const messages = readMessages({ space, since, limit, max_content_length, threads_only, include_reply_counts });
|
|
31157
31180
|
if (mark_read !== false && messages.length > 0) {
|
|
31158
31181
|
markReadByIds(messages.map((m) => m.id));
|
|
31159
31182
|
}
|
|
@@ -31804,13 +31827,30 @@ server.registerTool("clean_expired_locks", {
|
|
|
31804
31827
|
return { content: [{ type: "text", text: JSON.stringify({ released_stale_agent: stale, released_expired: expired, total: stale + expired }) }] };
|
|
31805
31828
|
});
|
|
31806
31829
|
server.registerTool("get_thread_replies", {
|
|
31807
|
-
description: "Get all replies in a thread for a given parent message ID.",
|
|
31830
|
+
description: "Get all replies in a thread for a given parent message ID. Also accessible as read_thread.",
|
|
31808
31831
|
inputSchema: {
|
|
31809
|
-
message_id: exports_external.coerce.number()
|
|
31832
|
+
message_id: exports_external.coerce.number(),
|
|
31833
|
+
limit: exports_external.coerce.number().optional()
|
|
31834
|
+
}
|
|
31835
|
+
}, async (args) => {
|
|
31836
|
+
let replies = getThreadReplies(args.message_id);
|
|
31837
|
+
if (args.limit)
|
|
31838
|
+
replies = replies.slice(0, args.limit);
|
|
31839
|
+
const parent = getMessageById(args.message_id);
|
|
31840
|
+
return { content: [{ type: "text", text: JSON.stringify({ parent, replies, reply_count: replies.length }) }] };
|
|
31841
|
+
});
|
|
31842
|
+
server.registerTool("read_thread", {
|
|
31843
|
+
description: "Alias for get_thread_replies. Read all replies to a specific message, forming a thread view.",
|
|
31844
|
+
inputSchema: {
|
|
31845
|
+
message_id: exports_external.coerce.number(),
|
|
31846
|
+
limit: exports_external.coerce.number().optional()
|
|
31810
31847
|
}
|
|
31811
31848
|
}, async (args) => {
|
|
31812
|
-
|
|
31813
|
-
|
|
31849
|
+
let replies = getThreadReplies(args.message_id);
|
|
31850
|
+
if (args.limit)
|
|
31851
|
+
replies = replies.slice(0, args.limit);
|
|
31852
|
+
const parent = getMessageById(args.message_id);
|
|
31853
|
+
return { content: [{ type: "text", text: JSON.stringify({ parent, replies, reply_count: replies.length }) }] };
|
|
31814
31854
|
});
|
|
31815
31855
|
server.registerTool("set_focus", {
|
|
31816
31856
|
description: "Set agent focus to a project. All read-heavy tools will default to this project scope. Stores in MCP session memory AND updates agent_presence.project_id in DB.",
|
|
@@ -32058,7 +32098,7 @@ server.registerTool("describe_tools", {
|
|
|
32058
32098
|
read_messages: "Read messages with filters. Optional: session_id?, from?, to?, space?, since?(ISO), limit?, unread_only?, mark_read?(default true \u2014 auto-marks returned messages as read, pass false to peek without consuming)",
|
|
32059
32099
|
read_digest: "Lightweight unread digest \u2014 preview only (no full bodies), auto-marks read, never overflows tokens. Returns { messages, total_unread, shown }. Optional: space?, session_id?, to?, since?(ISO), limit?, project_id?",
|
|
32060
32100
|
list_sessions: "List all DM sessions. Optional: agent?(filter by participant)",
|
|
32061
|
-
reply: "Reply to a message
|
|
32101
|
+
reply: "Reply to a specific message, creating a thread (sets reply_to). Use read_thread to retrieve. Required: message_id, content. Optional: from?",
|
|
32062
32102
|
mark_read: "Mark messages as read. Optional: from?, ids?(array), all?(bool \u2014 mark all unread)",
|
|
32063
32103
|
search_messages: "Full-text search messages. Required: query. Optional: space?, from?, to?, limit?",
|
|
32064
32104
|
export_messages: "Export messages as JSON or CSV. Optional: space?, session_id?, from?, since?, until?, format?(json|csv)",
|
|
@@ -32102,6 +32142,7 @@ server.registerTool("describe_tools", {
|
|
|
32102
32142
|
list_locks: "List active locks enriched with agent presence + time context. Optional: resource_type?, agent_id?",
|
|
32103
32143
|
clean_expired_locks: "Release expired locks + locks held by agents with stale heartbeat (>30 min). Returns {released_stale_agent, released_expired, total}",
|
|
32104
32144
|
get_thread_replies: "Get all replies in a thread. Required: message_id. Optional: limit?",
|
|
32145
|
+
read_thread: "Alias for get_thread_replies. Required: message_id. Optional: limit?",
|
|
32105
32146
|
set_focus: "Set agent focus to a project. All read tools default to this scope. Required: project_id. Optional: from?",
|
|
32106
32147
|
get_focus: "Get current focus: session focus, DB project_id, effective project_id. Optional: from?",
|
|
32107
32148
|
unfocus: "Clear agent focus (session + DB). Optional: from?",
|
package/dist/index.js
CHANGED
|
@@ -2342,11 +2342,28 @@ function readMessages(opts = {}) {
|
|
|
2342
2342
|
if (opts.unread_only) {
|
|
2343
2343
|
conditions.push("read_at IS NULL");
|
|
2344
2344
|
}
|
|
2345
|
+
if (opts.threads_only) {
|
|
2346
|
+
conditions.push("reply_to IS NULL");
|
|
2347
|
+
}
|
|
2345
2348
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
2346
2349
|
const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
2347
2350
|
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
2348
2351
|
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
|
|
2349
|
-
|
|
2352
|
+
let messages = rows.map(parseMessage);
|
|
2353
|
+
if (opts.include_reply_counts && messages.length > 0) {
|
|
2354
|
+
const db22 = getDb();
|
|
2355
|
+
const counts = db22.prepare(`SELECT reply_to, COUNT(*) as c FROM messages WHERE reply_to IN (${messages.map(() => "?").join(",")}) GROUP BY reply_to`).all(...messages.map((m) => m.id));
|
|
2356
|
+
const countMap = new Map(counts.map((r) => [r.reply_to, r.c]));
|
|
2357
|
+
messages = messages.map((m) => ({ ...m, reply_count: countMap.get(m.id) ?? 0 }));
|
|
2358
|
+
}
|
|
2359
|
+
if (opts.max_content_length && opts.max_content_length > 0) {
|
|
2360
|
+
messages = messages.map((m) => {
|
|
2361
|
+
if (m.content.length > opts.max_content_length) {
|
|
2362
|
+
return { ...m, content: m.content.slice(0, opts.max_content_length) + "\u2026", truncated: true };
|
|
2363
|
+
}
|
|
2364
|
+
return m;
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2350
2367
|
if (opts.compact)
|
|
2351
2368
|
return messages.map(compactMessage);
|
|
2352
2369
|
return messages;
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface Message {
|
|
|
19
19
|
blocking: boolean;
|
|
20
20
|
attachments: Attachment[] | null;
|
|
21
21
|
reply_to: number | null;
|
|
22
|
+
reply_count?: number;
|
|
23
|
+
truncated?: boolean;
|
|
22
24
|
}
|
|
23
25
|
export interface Reaction {
|
|
24
26
|
id: number;
|
|
@@ -106,6 +108,9 @@ export interface ReadMessagesOptions {
|
|
|
106
108
|
unread_only?: boolean;
|
|
107
109
|
order?: "asc" | "desc";
|
|
108
110
|
compact?: boolean;
|
|
111
|
+
max_content_length?: number;
|
|
112
|
+
threads_only?: boolean;
|
|
113
|
+
include_reply_counts?: boolean;
|
|
109
114
|
}
|
|
110
115
|
export interface SearchMessagesOptions {
|
|
111
116
|
query: string;
|