@hasna/conversations 0.2.2 → 0.2.4
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 +61 -6
- package/bin/mcp.js +61 -6
- package/dist/index.js +9 -1
- package/dist/lib/messages.d.ts +11 -0
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -2372,7 +2372,15 @@ function readMessages(opts = {}) {
|
|
|
2372
2372
|
const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
2373
2373
|
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
2374
2374
|
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
|
|
2375
|
-
|
|
2375
|
+
let messages = rows.map(parseMessage);
|
|
2376
|
+
if (opts.max_content_length && opts.max_content_length > 0) {
|
|
2377
|
+
messages = messages.map((m) => {
|
|
2378
|
+
if (m.content.length > opts.max_content_length) {
|
|
2379
|
+
return { ...m, content: m.content.slice(0, opts.max_content_length) + "\u2026", truncated: true };
|
|
2380
|
+
}
|
|
2381
|
+
return m;
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2376
2384
|
if (opts.compact)
|
|
2377
2385
|
return messages.map(compactMessage);
|
|
2378
2386
|
return messages;
|
|
@@ -2644,6 +2652,39 @@ function searchMessages(opts) {
|
|
|
2644
2652
|
return { ...msg, snippet: null, relevance_score: 0 };
|
|
2645
2653
|
});
|
|
2646
2654
|
}
|
|
2655
|
+
function listUnreadCounts(agent) {
|
|
2656
|
+
const db2 = getDb();
|
|
2657
|
+
if (agent) {
|
|
2658
|
+
const rows2 = db2.prepare(`
|
|
2659
|
+
SELECT
|
|
2660
|
+
space,
|
|
2661
|
+
COUNT(CASE WHEN read_at IS NULL AND (to_agent = ? OR to_agent IS NULL OR to_agent = '') THEN 1 END) AS unread_count,
|
|
2662
|
+
MAX(created_at) AS latest_message_at
|
|
2663
|
+
FROM messages
|
|
2664
|
+
WHERE space IN (
|
|
2665
|
+
SELECT DISTINCT space FROM space_members WHERE agent = ?
|
|
2666
|
+
UNION
|
|
2667
|
+
SELECT DISTINCT space FROM messages WHERE to_agent = ? AND space IS NOT NULL
|
|
2668
|
+
)
|
|
2669
|
+
GROUP BY space
|
|
2670
|
+
HAVING COUNT(*) > 0
|
|
2671
|
+
ORDER BY unread_count DESC, latest_message_at DESC
|
|
2672
|
+
`).all(agent, agent, agent);
|
|
2673
|
+
return rows2;
|
|
2674
|
+
}
|
|
2675
|
+
const rows = db2.prepare(`
|
|
2676
|
+
SELECT
|
|
2677
|
+
space,
|
|
2678
|
+
COUNT(CASE WHEN read_at IS NULL THEN 1 END) AS unread_count,
|
|
2679
|
+
MAX(created_at) AS latest_message_at
|
|
2680
|
+
FROM messages
|
|
2681
|
+
WHERE space IS NOT NULL
|
|
2682
|
+
GROUP BY space
|
|
2683
|
+
HAVING COUNT(*) > 0
|
|
2684
|
+
ORDER BY unread_count DESC, latest_message_at DESC
|
|
2685
|
+
`).all();
|
|
2686
|
+
return rows;
|
|
2687
|
+
}
|
|
2647
2688
|
var init_messages = __esm(() => {
|
|
2648
2689
|
init_db();
|
|
2649
2690
|
init_webhooks();
|
|
@@ -4283,7 +4324,7 @@ var init_poll = __esm(() => {
|
|
|
4283
4324
|
var require_package = __commonJS((exports, module) => {
|
|
4284
4325
|
module.exports = {
|
|
4285
4326
|
name: "@hasna/conversations",
|
|
4286
|
-
version: "0.2.
|
|
4327
|
+
version: "0.2.4",
|
|
4287
4328
|
description: "Real-time CLI messaging for AI agents",
|
|
4288
4329
|
type: "module",
|
|
4289
4330
|
bin: {
|
|
@@ -33535,7 +33576,8 @@ var init_mcp2 = __esm(() => {
|
|
|
33535
33576
|
since: exports_external.string().optional(),
|
|
33536
33577
|
limit: exports_external.coerce.number().optional(),
|
|
33537
33578
|
unread_only: exports_external.coerce.boolean().optional(),
|
|
33538
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
33579
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
33580
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)")
|
|
33539
33581
|
}
|
|
33540
33582
|
}, async (args) => {
|
|
33541
33583
|
const agent = resolveIdentity(args.from);
|
|
@@ -33704,6 +33746,17 @@ var init_mcp2 = __esm(() => {
|
|
|
33704
33746
|
content: [{ type: "text", text: JSON.stringify(spaces) }]
|
|
33705
33747
|
};
|
|
33706
33748
|
});
|
|
33749
|
+
server.registerTool("list_unread_counts", {
|
|
33750
|
+
description: "Get unread message counts per space without fetching message content. Use this at session start to triage which spaces need attention before calling read_messages.",
|
|
33751
|
+
inputSchema: {
|
|
33752
|
+
agent: exports_external.string().optional().describe("Filter to spaces the agent is a member of or has received messages in. Omit for global unread counts.")
|
|
33753
|
+
}
|
|
33754
|
+
}, async (args) => {
|
|
33755
|
+
const counts = listUnreadCounts(args.agent);
|
|
33756
|
+
return {
|
|
33757
|
+
content: [{ type: "text", text: JSON.stringify(counts) }]
|
|
33758
|
+
};
|
|
33759
|
+
});
|
|
33707
33760
|
server.registerTool("send_to_space", {
|
|
33708
33761
|
description: "Post a message to a space.",
|
|
33709
33762
|
inputSchema: {
|
|
@@ -33742,11 +33795,12 @@ var init_mcp2 = __esm(() => {
|
|
|
33742
33795
|
space: exports_external.string(),
|
|
33743
33796
|
since: exports_external.string().optional(),
|
|
33744
33797
|
limit: exports_external.coerce.number().optional(),
|
|
33745
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
33798
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
33799
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)")
|
|
33746
33800
|
}
|
|
33747
33801
|
}, async (args) => {
|
|
33748
|
-
const { space, since, limit, mark_read } = args;
|
|
33749
|
-
const messages = readMessages({ space, since, limit });
|
|
33802
|
+
const { space, since, limit, mark_read, max_content_length } = args;
|
|
33803
|
+
const messages = readMessages({ space, since, limit, max_content_length });
|
|
33750
33804
|
if (mark_read !== false && messages.length > 0) {
|
|
33751
33805
|
markReadByIds(messages.map((m) => m.id));
|
|
33752
33806
|
}
|
|
@@ -34656,6 +34710,7 @@ var init_mcp2 = __esm(() => {
|
|
|
34656
34710
|
search_messages: "Full-text search messages. Required: query. Optional: space?, from?, to?, limit?",
|
|
34657
34711
|
export_messages: "Export messages as JSON or CSV. Optional: space?, session_id?, from?, since?, until?, format?(json|csv)",
|
|
34658
34712
|
create_space: "Create space and auto-join. Required: name. Optional: from?, description?, parent_id?(max 3 levels), project_id?",
|
|
34713
|
+
list_unread_counts: "Get unread message counts per space (no content). Ideal for session start triage. Optional: agent?(filter to agent's spaces)",
|
|
34659
34714
|
list_spaces: "List spaces with member/message counts. Optional: project_id?, parent_id?(use 'null' for top-level), include_archived?",
|
|
34660
34715
|
send_to_space: "Post message to space. Required: space, content. Optional: from?, priority?(low|normal|high|urgent), blocking?",
|
|
34661
34716
|
read_space: "Read messages in a space. Required: space. Optional: since?(ISO), limit?, mark_read?(default true \u2014 auto-marks returned messages as read)",
|
package/bin/mcp.js
CHANGED
|
@@ -28881,7 +28881,15 @@ function readMessages(opts = {}) {
|
|
|
28881
28881
|
const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
28882
28882
|
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
28883
28883
|
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
|
|
28884
|
-
|
|
28884
|
+
let messages = rows.map(parseMessage);
|
|
28885
|
+
if (opts.max_content_length && opts.max_content_length > 0) {
|
|
28886
|
+
messages = messages.map((m) => {
|
|
28887
|
+
if (m.content.length > opts.max_content_length) {
|
|
28888
|
+
return { ...m, content: m.content.slice(0, opts.max_content_length) + "\u2026", truncated: true };
|
|
28889
|
+
}
|
|
28890
|
+
return m;
|
|
28891
|
+
});
|
|
28892
|
+
}
|
|
28885
28893
|
if (opts.compact)
|
|
28886
28894
|
return messages.map(compactMessage);
|
|
28887
28895
|
return messages;
|
|
@@ -29141,6 +29149,39 @@ function searchMessages(opts) {
|
|
|
29141
29149
|
return { ...msg, snippet: null, relevance_score: 0 };
|
|
29142
29150
|
});
|
|
29143
29151
|
}
|
|
29152
|
+
function listUnreadCounts(agent) {
|
|
29153
|
+
const db2 = getDb();
|
|
29154
|
+
if (agent) {
|
|
29155
|
+
const rows2 = db2.prepare(`
|
|
29156
|
+
SELECT
|
|
29157
|
+
space,
|
|
29158
|
+
COUNT(CASE WHEN read_at IS NULL AND (to_agent = ? OR to_agent IS NULL OR to_agent = '') THEN 1 END) AS unread_count,
|
|
29159
|
+
MAX(created_at) AS latest_message_at
|
|
29160
|
+
FROM messages
|
|
29161
|
+
WHERE space IN (
|
|
29162
|
+
SELECT DISTINCT space FROM space_members WHERE agent = ?
|
|
29163
|
+
UNION
|
|
29164
|
+
SELECT DISTINCT space FROM messages WHERE to_agent = ? AND space IS NOT NULL
|
|
29165
|
+
)
|
|
29166
|
+
GROUP BY space
|
|
29167
|
+
HAVING COUNT(*) > 0
|
|
29168
|
+
ORDER BY unread_count DESC, latest_message_at DESC
|
|
29169
|
+
`).all(agent, agent, agent);
|
|
29170
|
+
return rows2;
|
|
29171
|
+
}
|
|
29172
|
+
const rows = db2.prepare(`
|
|
29173
|
+
SELECT
|
|
29174
|
+
space,
|
|
29175
|
+
COUNT(CASE WHEN read_at IS NULL THEN 1 END) AS unread_count,
|
|
29176
|
+
MAX(created_at) AS latest_message_at
|
|
29177
|
+
FROM messages
|
|
29178
|
+
WHERE space IS NOT NULL
|
|
29179
|
+
GROUP BY space
|
|
29180
|
+
HAVING COUNT(*) > 0
|
|
29181
|
+
ORDER BY unread_count DESC, latest_message_at DESC
|
|
29182
|
+
`).all();
|
|
29183
|
+
return rows;
|
|
29184
|
+
}
|
|
29144
29185
|
|
|
29145
29186
|
// src/lib/sessions.ts
|
|
29146
29187
|
init_db();
|
|
@@ -30767,7 +30808,7 @@ function getGraphStats() {
|
|
|
30767
30808
|
// package.json
|
|
30768
30809
|
var package_default = {
|
|
30769
30810
|
name: "@hasna/conversations",
|
|
30770
|
-
version: "0.2.
|
|
30811
|
+
version: "0.2.4",
|
|
30771
30812
|
description: "Real-time CLI messaging for AI agents",
|
|
30772
30813
|
type: "module",
|
|
30773
30814
|
bin: {
|
|
@@ -30898,7 +30939,8 @@ server.registerTool("read_messages", {
|
|
|
30898
30939
|
since: exports_external.string().optional(),
|
|
30899
30940
|
limit: exports_external.coerce.number().optional(),
|
|
30900
30941
|
unread_only: exports_external.coerce.boolean().optional(),
|
|
30901
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
30942
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
30943
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)")
|
|
30902
30944
|
}
|
|
30903
30945
|
}, async (args) => {
|
|
30904
30946
|
const agent = resolveIdentity(args.from);
|
|
@@ -31067,6 +31109,17 @@ server.registerTool("list_spaces", {
|
|
|
31067
31109
|
content: [{ type: "text", text: JSON.stringify(spaces) }]
|
|
31068
31110
|
};
|
|
31069
31111
|
});
|
|
31112
|
+
server.registerTool("list_unread_counts", {
|
|
31113
|
+
description: "Get unread message counts per space without fetching message content. Use this at session start to triage which spaces need attention before calling read_messages.",
|
|
31114
|
+
inputSchema: {
|
|
31115
|
+
agent: exports_external.string().optional().describe("Filter to spaces the agent is a member of or has received messages in. Omit for global unread counts.")
|
|
31116
|
+
}
|
|
31117
|
+
}, async (args) => {
|
|
31118
|
+
const counts = listUnreadCounts(args.agent);
|
|
31119
|
+
return {
|
|
31120
|
+
content: [{ type: "text", text: JSON.stringify(counts) }]
|
|
31121
|
+
};
|
|
31122
|
+
});
|
|
31070
31123
|
server.registerTool("send_to_space", {
|
|
31071
31124
|
description: "Post a message to a space.",
|
|
31072
31125
|
inputSchema: {
|
|
@@ -31105,11 +31158,12 @@ server.registerTool("read_space", {
|
|
|
31105
31158
|
space: exports_external.string(),
|
|
31106
31159
|
since: exports_external.string().optional(),
|
|
31107
31160
|
limit: exports_external.coerce.number().optional(),
|
|
31108
|
-
mark_read: exports_external.coerce.boolean().optional()
|
|
31161
|
+
mark_read: exports_external.coerce.boolean().optional(),
|
|
31162
|
+
max_content_length: exports_external.coerce.number().optional().describe("Truncate each message content to N chars (adds truncated:true flag)")
|
|
31109
31163
|
}
|
|
31110
31164
|
}, async (args) => {
|
|
31111
|
-
const { space, since, limit, mark_read } = args;
|
|
31112
|
-
const messages = readMessages({ space, since, limit });
|
|
31165
|
+
const { space, since, limit, mark_read, max_content_length } = args;
|
|
31166
|
+
const messages = readMessages({ space, since, limit, max_content_length });
|
|
31113
31167
|
if (mark_read !== false && messages.length > 0) {
|
|
31114
31168
|
markReadByIds(messages.map((m) => m.id));
|
|
31115
31169
|
}
|
|
@@ -32019,6 +32073,7 @@ server.registerTool("describe_tools", {
|
|
|
32019
32073
|
search_messages: "Full-text search messages. Required: query. Optional: space?, from?, to?, limit?",
|
|
32020
32074
|
export_messages: "Export messages as JSON or CSV. Optional: space?, session_id?, from?, since?, until?, format?(json|csv)",
|
|
32021
32075
|
create_space: "Create space and auto-join. Required: name. Optional: from?, description?, parent_id?(max 3 levels), project_id?",
|
|
32076
|
+
list_unread_counts: "Get unread message counts per space (no content). Ideal for session start triage. Optional: agent?(filter to agent's spaces)",
|
|
32022
32077
|
list_spaces: "List spaces with member/message counts. Optional: project_id?, parent_id?(use 'null' for top-level), include_archived?",
|
|
32023
32078
|
send_to_space: "Post message to space. Required: space, content. Optional: from?, priority?(low|normal|high|urgent), blocking?",
|
|
32024
32079
|
read_space: "Read messages in a space. Required: space. Optional: since?(ISO), limit?, mark_read?(default true \u2014 auto-marks returned messages as read)",
|
package/dist/index.js
CHANGED
|
@@ -2346,7 +2346,15 @@ function readMessages(opts = {}) {
|
|
|
2346
2346
|
const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
2347
2347
|
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
2348
2348
|
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
|
|
2349
|
-
|
|
2349
|
+
let messages = rows.map(parseMessage);
|
|
2350
|
+
if (opts.max_content_length && opts.max_content_length > 0) {
|
|
2351
|
+
messages = messages.map((m) => {
|
|
2352
|
+
if (m.content.length > opts.max_content_length) {
|
|
2353
|
+
return { ...m, content: m.content.slice(0, opts.max_content_length) + "\u2026", truncated: true };
|
|
2354
|
+
}
|
|
2355
|
+
return m;
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2350
2358
|
if (opts.compact)
|
|
2351
2359
|
return messages.map(compactMessage);
|
|
2352
2360
|
return messages;
|
package/dist/lib/messages.d.ts
CHANGED
|
@@ -56,3 +56,14 @@ export declare function getPinnedMessages(opts?: {
|
|
|
56
56
|
export declare function getUnreadBlockers(agent: string): Message[];
|
|
57
57
|
export declare function getThreadReplies(messageId: number): Message[];
|
|
58
58
|
export declare function searchMessages(opts: SearchMessagesOptions): SearchResult[];
|
|
59
|
+
export interface UnreadCount {
|
|
60
|
+
space: string;
|
|
61
|
+
unread_count: number;
|
|
62
|
+
latest_message_at: string | null;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get unread message counts per space — lightweight alternative to read_messages.
|
|
66
|
+
* Returns only spaces where the agent is a member (via space_members) or has received messages.
|
|
67
|
+
* If agent is omitted, returns counts for all spaces.
|
|
68
|
+
*/
|
|
69
|
+
export declare function listUnreadCounts(agent?: string): UnreadCount[];
|
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface Message {
|
|
|
19
19
|
blocking: boolean;
|
|
20
20
|
attachments: Attachment[] | null;
|
|
21
21
|
reply_to: number | null;
|
|
22
|
+
truncated?: boolean;
|
|
22
23
|
}
|
|
23
24
|
export interface Reaction {
|
|
24
25
|
id: number;
|
|
@@ -106,6 +107,7 @@ export interface ReadMessagesOptions {
|
|
|
106
107
|
unread_only?: boolean;
|
|
107
108
|
order?: "asc" | "desc";
|
|
108
109
|
compact?: boolean;
|
|
110
|
+
max_content_length?: number;
|
|
109
111
|
}
|
|
110
112
|
export interface SearchMessagesOptions {
|
|
111
113
|
query: string;
|