niahere 0.2.40 → 0.2.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "niahere",
3
- "version": "0.2.40",
3
+ "version": "0.2.41",
4
4
  "description": "A personal AI assistant daemon — scheduled jobs, chat across Telegram and Slack, persona system, and visual identity.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,5 +1,5 @@
1
1
  import { getSql } from "../connection";
2
- import type { SaveMessageParams, RoomStats, RecentMessage } from "../../types";
2
+ import type { SaveMessageParams, RoomStats, RecentMessage, SearchResult, SessionMessage } from "../../types";
3
3
 
4
4
  export async function save(params: SaveMessageParams): Promise<void> {
5
5
  const sql = getSql();
@@ -29,6 +29,45 @@ export async function getRecent(limit = 20, room?: string): Promise<RecentMessag
29
29
  }));
30
30
  }
31
31
 
32
+ export async function search(query: string, limit = 20, room?: string): Promise<SearchResult[]> {
33
+ const sql = getSql();
34
+ const pattern = `%${query}%`;
35
+ const rows = room
36
+ ? await sql`
37
+ SELECT session_id, room, sender, content, created_at
38
+ FROM messages WHERE content ILIKE ${pattern} AND room = ${room}
39
+ ORDER BY created_at DESC LIMIT ${limit}
40
+ `
41
+ : await sql`
42
+ SELECT session_id, room, sender, content, created_at
43
+ FROM messages WHERE content ILIKE ${pattern}
44
+ ORDER BY created_at DESC LIMIT ${limit}
45
+ `;
46
+ return rows.map((r) => ({
47
+ sessionId: r.session_id,
48
+ room: r.room,
49
+ sender: r.sender,
50
+ content: r.content,
51
+ createdAt: String(r.created_at),
52
+ }));
53
+ }
54
+
55
+ export async function getBySession(sessionId: string): Promise<SessionMessage[]> {
56
+ const sql = getSql();
57
+ const rows = await sql`
58
+ SELECT room, sender, content, is_from_agent, created_at
59
+ FROM messages WHERE session_id = ${sessionId}
60
+ ORDER BY created_at ASC
61
+ `;
62
+ return rows.map((r) => ({
63
+ room: r.room,
64
+ sender: r.sender,
65
+ content: r.content,
66
+ isFromAgent: r.is_from_agent,
67
+ createdAt: String(r.created_at),
68
+ }));
69
+ }
70
+
32
71
  export async function getRoomStats(): Promise<RoomStats[]> {
33
72
  const sql = getSql();
34
73
  const rows = await sql`
@@ -49,6 +49,35 @@ export async function getRecent(room: string, limit = 10): Promise<SessionSummar
49
49
  }));
50
50
  }
51
51
 
52
+ export async function listRecent(limit = 10, room?: string): Promise<SessionSummary[]> {
53
+ if (room) return getRecent(room, limit);
54
+ const sql = getSql();
55
+ const rows = await sql`
56
+ SELECT
57
+ s.id,
58
+ s.room,
59
+ s.created_at,
60
+ s.updated_at,
61
+ (
62
+ SELECT content FROM messages m
63
+ WHERE m.session_id = s.id AND m.sender = 'user'
64
+ ORDER BY m.created_at ASC LIMIT 1
65
+ ) AS preview,
66
+ (SELECT COUNT(*)::int FROM messages m WHERE m.session_id = s.id) AS message_count
67
+ FROM sessions s
68
+ ORDER BY s.updated_at DESC
69
+ LIMIT ${limit}
70
+ `;
71
+ return rows.map((r) => ({
72
+ id: r.id,
73
+ room: r.room,
74
+ createdAt: String(r.created_at),
75
+ updatedAt: String(r.updated_at),
76
+ preview: r.preview ? String(r.preview) : null,
77
+ messageCount: r.message_count,
78
+ }));
79
+ }
80
+
52
81
  export async function create(id: string, room: string): Promise<void> {
53
82
  const sql = getSql();
54
83
  await sql`INSERT INTO sessions (id, room) VALUES (${id}, ${room})`;
package/src/mcp/server.ts CHANGED
@@ -99,6 +99,39 @@ export function createNiaMcpServer() {
99
99
  content: [{ type: "text" as const, text: await handlers.listMessages(args.limit, args.room) }],
100
100
  }),
101
101
  ),
102
+ tool(
103
+ "list_sessions",
104
+ "Browse past conversation sessions with previews. Returns session IDs you can pass to read_session.",
105
+ {
106
+ room: z.string().optional().describe("Filter by room name"),
107
+ limit: z.number().default(10).describe("Number of sessions to return"),
108
+ },
109
+ async (args) => ({
110
+ content: [{ type: "text" as const, text: await handlers.listSessions(args.limit, args.room) }],
111
+ }),
112
+ ),
113
+ tool(
114
+ "search_messages",
115
+ "Search across all past messages by keyword. Returns matching messages with session IDs for deeper reading.",
116
+ {
117
+ query: z.string().describe("Text to search for in message content"),
118
+ room: z.string().optional().describe("Filter by room name"),
119
+ limit: z.number().default(20).describe("Max results to return"),
120
+ },
121
+ async (args) => ({
122
+ content: [{ type: "text" as const, text: await handlers.searchMessages(args.query, args.limit, args.room) }],
123
+ }),
124
+ ),
125
+ tool(
126
+ "read_session",
127
+ "Load the full transcript of a specific conversation session. Use list_sessions or search_messages to find session IDs.",
128
+ {
129
+ session_id: z.string().describe("Session ID to read"),
130
+ },
131
+ async (args) => ({
132
+ content: [{ type: "text" as const, text: await handlers.readSession(args.session_id) }],
133
+ }),
134
+ ),
102
135
  tool(
103
136
  "add_watch_channel",
104
137
  "Add or update a Slack watch channel. Watch channels receive ALL messages (not just @mentions) and act based on the behavior prompt. Takes effect on next message (hot-reloads).",
package/src/mcp/tools.ts CHANGED
@@ -244,6 +244,24 @@ export async function listMessages(limit = 20, room?: string): Promise<string> {
244
244
  return JSON.stringify(messages, null, 2);
245
245
  }
246
246
 
247
+ export async function listSessions(limit = 10, room?: string): Promise<string> {
248
+ const sessions = await Session.listRecent(limit, room);
249
+ if (sessions.length === 0) return "No sessions found.";
250
+ return JSON.stringify(sessions, null, 2);
251
+ }
252
+
253
+ export async function searchMessages(query: string, limit = 20, room?: string): Promise<string> {
254
+ const results = await Message.search(query, limit, room);
255
+ if (results.length === 0) return "No matching messages found.";
256
+ return JSON.stringify(results, null, 2);
257
+ }
258
+
259
+ export async function readSession(sessionId: string): Promise<string> {
260
+ const messages = await Message.getBySession(sessionId);
261
+ if (messages.length === 0) return "Session not found or has no messages.";
262
+ return JSON.stringify(messages, null, 2);
263
+ }
264
+
247
265
  export function addRule(rule: string): string {
248
266
  const { selfDir } = getPaths();
249
267
  const rulesPath = join(selfDir, "rules.md");
@@ -31,6 +31,9 @@ You have MCP tools for managing jobs directly (preferred over CLI for speed):
31
31
  - **run_job** — trigger a job to run immediately
32
32
  - **send_message** — send a message to the user (via telegram, slack, or default channel). Supports `media_path` to send images/files.
33
33
  - **list_messages** — read recent chat history
34
+ - **list_sessions** — browse past conversation sessions with previews and message counts. Returns session IDs.
35
+ - **search_messages** — keyword search across all past messages. Find when something was discussed.
36
+ - **read_session** — load the full transcript of a specific session by ID.
34
37
  - **add_watch_channel** — add a Slack channel for proactive monitoring. Specify channel key (`channel_id#name`) and behavior prompt. Hot-reloads.
35
38
  - **remove_watch_channel** — stop watching a Slack channel. Hot-reloads.
36
39
  - **enable_watch_channel** / **disable_watch_channel** — toggle a watch channel on/off without removing it. Hot-reloads.
@@ -73,6 +76,16 @@ Config reference:
73
76
  - `channels.slack.watch` — per-channel proactive monitoring. Keys are `channel_id#channel_name` format.
74
77
  {{slackWatch}}
75
78
 
79
+ ## Conversation History
80
+
81
+ You have access to all prior conversations stored in the database:
82
+
83
+ - **list_sessions** — browse past sessions (with previews and message counts). Use to find a conversation.
84
+ - **search_messages** — search across all past messages by keyword. Returns session IDs for deeper reading.
85
+ - **read_session** — load the full transcript of a session by ID.
86
+
87
+ Use these when the user asks "did we talk about...", "what did I say about...", or when you need context from a prior conversation. Combine with `read_memory` for a complete picture.
88
+
76
89
  ## Persona & Memory
77
90
 
78
91
  Your persona files live in {{selfDir}}/:
@@ -7,5 +7,5 @@ export type { Channel, ChannelFactory } from "./channel";
7
7
  export type { ChatState } from "./chat-state";
8
8
  export type { Config, ChannelsConfig, TelegramConfig, SlackConfig } from "./config";
9
9
  export type { Paths } from "./paths";
10
- export type { SaveMessageParams, RoomStats, RecentMessage } from "./message";
10
+ export type { SaveMessageParams, RoomStats, RecentMessage, SearchResult, SessionMessage } from "./message";
11
11
  export type { AgentInfo } from "./agent";
@@ -19,3 +19,19 @@ export interface RecentMessage {
19
19
  content: string;
20
20
  createdAt: string;
21
21
  }
22
+
23
+ export interface SearchResult {
24
+ sessionId: string;
25
+ room: string;
26
+ sender: string;
27
+ content: string;
28
+ createdAt: string;
29
+ }
30
+
31
+ export interface SessionMessage {
32
+ room: string;
33
+ sender: string;
34
+ content: string;
35
+ isFromAgent: boolean;
36
+ createdAt: string;
37
+ }