@hasna/conversations 0.1.30 → 0.1.32
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 +5 -0
- package/bin/index.js +96 -9
- package/bin/mcp.js +101 -8
- package/dist/index.js +12 -3
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
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,
|
|
@@ -214,6 +215,10 @@ function getDb() {
|
|
|
214
215
|
db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
|
|
215
216
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
|
|
216
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
|
+
}
|
|
217
222
|
const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
|
|
218
223
|
const presenceColNames = presenceCols.map((c) => c.name);
|
|
219
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,
|
|
@@ -2068,6 +2069,10 @@ function getDb() {
|
|
|
2068
2069
|
db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
|
|
2069
2070
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
|
|
2070
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
|
+
}
|
|
2071
2076
|
const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
|
|
2072
2077
|
const presenceColNames = presenceCols.map((c) => c.name);
|
|
2073
2078
|
if (!presenceColNames.includes("id")) {
|
|
@@ -2279,11 +2284,11 @@ function sendMessage(opts) {
|
|
|
2279
2284
|
const blocking = opts.blocking ? 1 : 0;
|
|
2280
2285
|
const replyTo = opts.reply_to || null;
|
|
2281
2286
|
const stmt = db2.prepare(`
|
|
2282
|
-
INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
|
|
2283
|
-
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2284
2289
|
RETURNING *
|
|
2285
2290
|
`);
|
|
2286
|
-
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);
|
|
2287
2292
|
const message = parseMessage(row);
|
|
2288
2293
|
if (opts.attachments && opts.attachments.length > 0) {
|
|
2289
2294
|
const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
|
|
@@ -2327,6 +2332,10 @@ function readMessages(opts = {}) {
|
|
|
2327
2332
|
conditions.push("space = ?");
|
|
2328
2333
|
params.push(opts.space);
|
|
2329
2334
|
}
|
|
2335
|
+
if (opts.project_id) {
|
|
2336
|
+
conditions.push("project_id = ?");
|
|
2337
|
+
params.push(opts.project_id);
|
|
2338
|
+
}
|
|
2330
2339
|
if (opts.since) {
|
|
2331
2340
|
conditions.push("created_at > ?");
|
|
2332
2341
|
params.push(opts.since);
|
|
@@ -3613,7 +3622,7 @@ var init_poll = __esm(() => {
|
|
|
3613
3622
|
var require_package = __commonJS((exports, module) => {
|
|
3614
3623
|
module.exports = {
|
|
3615
3624
|
name: "@hasna/conversations",
|
|
3616
|
-
version: "0.1.
|
|
3625
|
+
version: "0.1.32",
|
|
3617
3626
|
description: "Real-time CLI messaging for AI agents",
|
|
3618
3627
|
type: "module",
|
|
3619
3628
|
bin: {
|
|
@@ -32563,11 +32572,23 @@ __export(exports_mcp, {
|
|
|
32563
32572
|
startMcpServer: () => startMcpServer,
|
|
32564
32573
|
server: () => server
|
|
32565
32574
|
});
|
|
32575
|
+
function getAgentFocus(agentId) {
|
|
32576
|
+
if (agentFocus.has(agentId))
|
|
32577
|
+
return agentFocus.get(agentId).project_id;
|
|
32578
|
+
const presence = getPresence(agentId);
|
|
32579
|
+
return presence?.project_id ?? null;
|
|
32580
|
+
}
|
|
32581
|
+
function resolveProjectId(explicitProjectId, agentId) {
|
|
32582
|
+
if (explicitProjectId)
|
|
32583
|
+
return explicitProjectId;
|
|
32584
|
+
const focused = getAgentFocus(agentId);
|
|
32585
|
+
return focused ?? undefined;
|
|
32586
|
+
}
|
|
32566
32587
|
async function startMcpServer() {
|
|
32567
32588
|
const transport = new StdioServerTransport;
|
|
32568
32589
|
await server.connect(transport);
|
|
32569
32590
|
}
|
|
32570
|
-
var import__package, server, isDirectRun;
|
|
32591
|
+
var import__package, server, agentFocus, isDirectRun;
|
|
32571
32592
|
var init_mcp2 = __esm(() => {
|
|
32572
32593
|
init_mcp();
|
|
32573
32594
|
init_stdio2();
|
|
@@ -32583,6 +32604,7 @@ var init_mcp2 = __esm(() => {
|
|
|
32583
32604
|
name: "conversations",
|
|
32584
32605
|
version: import__package.default.version
|
|
32585
32606
|
});
|
|
32607
|
+
agentFocus = new Map;
|
|
32586
32608
|
server.registerTool("send_message", {
|
|
32587
32609
|
description: "Send a DM to an agent.",
|
|
32588
32610
|
inputSchema: {
|
|
@@ -32590,17 +32612,19 @@ var init_mcp2 = __esm(() => {
|
|
|
32590
32612
|
content: exports_external.string(),
|
|
32591
32613
|
from: exports_external.string().optional(),
|
|
32592
32614
|
priority: exports_external.string().optional(),
|
|
32593
|
-
blocking: exports_external.coerce.boolean().optional()
|
|
32615
|
+
blocking: exports_external.coerce.boolean().optional(),
|
|
32616
|
+
project_id: exports_external.string().optional()
|
|
32594
32617
|
}
|
|
32595
32618
|
}, async (args) => {
|
|
32596
|
-
const { from: fromParam, to, content, priority, blocking } = args;
|
|
32619
|
+
const { from: fromParam, to, content, priority, blocking, project_id } = args;
|
|
32597
32620
|
const from = resolveIdentity(fromParam);
|
|
32598
32621
|
const msg = sendMessage({
|
|
32599
32622
|
from,
|
|
32600
32623
|
to,
|
|
32601
32624
|
content,
|
|
32602
32625
|
priority,
|
|
32603
|
-
blocking
|
|
32626
|
+
blocking,
|
|
32627
|
+
project_id
|
|
32604
32628
|
});
|
|
32605
32629
|
return {
|
|
32606
32630
|
content: [{ type: "text", text: JSON.stringify(msg) }]
|
|
@@ -32613,12 +32637,17 @@ var init_mcp2 = __esm(() => {
|
|
|
32613
32637
|
from: exports_external.string().optional(),
|
|
32614
32638
|
to: exports_external.string().optional(),
|
|
32615
32639
|
space: exports_external.string().optional(),
|
|
32640
|
+
project_id: exports_external.string().optional(),
|
|
32616
32641
|
since: exports_external.string().optional(),
|
|
32617
32642
|
limit: exports_external.coerce.number().optional(),
|
|
32618
32643
|
unread_only: exports_external.coerce.boolean().optional()
|
|
32619
32644
|
}
|
|
32620
32645
|
}, async (args) => {
|
|
32621
|
-
const
|
|
32646
|
+
const agent = resolveIdentity(args.from);
|
|
32647
|
+
const messages = readMessages({
|
|
32648
|
+
...args,
|
|
32649
|
+
project_id: args.project_id ?? resolveProjectId(undefined, agent)
|
|
32650
|
+
});
|
|
32622
32651
|
return {
|
|
32623
32652
|
content: [{ type: "text", text: JSON.stringify(messages) }]
|
|
32624
32653
|
};
|
|
@@ -33210,6 +33239,58 @@ var init_mcp2 = __esm(() => {
|
|
|
33210
33239
|
content: [{ type: "text", text: JSON.stringify(messages) }]
|
|
33211
33240
|
};
|
|
33212
33241
|
});
|
|
33242
|
+
server.registerTool("set_focus", {
|
|
33243
|
+
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.",
|
|
33244
|
+
inputSchema: {
|
|
33245
|
+
project_id: exports_external.string(),
|
|
33246
|
+
from: exports_external.string().optional()
|
|
33247
|
+
}
|
|
33248
|
+
}, async (args) => {
|
|
33249
|
+
const { project_id, from: fromParam } = args;
|
|
33250
|
+
const agent = resolveIdentity(fromParam);
|
|
33251
|
+
agentFocus.set(agent, { project_id });
|
|
33252
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
33253
|
+
db2.prepare("UPDATE agent_presence SET project_id = ? WHERE agent = ?").run(project_id, agent);
|
|
33254
|
+
return {
|
|
33255
|
+
content: [{ type: "text", text: JSON.stringify({ agent, focused: true, project_id }) }]
|
|
33256
|
+
};
|
|
33257
|
+
});
|
|
33258
|
+
server.registerTool("get_focus", {
|
|
33259
|
+
description: "Get the current focus state for an agent. Returns session focus, DB project_id, and effective project_id used for filtering.",
|
|
33260
|
+
inputSchema: {
|
|
33261
|
+
from: exports_external.string().optional()
|
|
33262
|
+
}
|
|
33263
|
+
}, async (args) => {
|
|
33264
|
+
const agent = resolveIdentity(args.from);
|
|
33265
|
+
const sessionFocus = agentFocus.get(agent) ?? null;
|
|
33266
|
+
const presence = getPresence(agent);
|
|
33267
|
+
const effective = getAgentFocus(agent);
|
|
33268
|
+
return {
|
|
33269
|
+
content: [{
|
|
33270
|
+
type: "text",
|
|
33271
|
+
text: JSON.stringify({
|
|
33272
|
+
agent,
|
|
33273
|
+
session_focus: sessionFocus?.project_id ?? null,
|
|
33274
|
+
db_project_id: presence?.project_id ?? null,
|
|
33275
|
+
effective_project_id: effective
|
|
33276
|
+
})
|
|
33277
|
+
}]
|
|
33278
|
+
};
|
|
33279
|
+
});
|
|
33280
|
+
server.registerTool("unfocus", {
|
|
33281
|
+
description: "Clear agent focus. Removes session focus and clears agent_presence.project_id in DB.",
|
|
33282
|
+
inputSchema: {
|
|
33283
|
+
from: exports_external.string().optional()
|
|
33284
|
+
}
|
|
33285
|
+
}, async (args) => {
|
|
33286
|
+
const agent = resolveIdentity(args.from);
|
|
33287
|
+
agentFocus.delete(agent);
|
|
33288
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
33289
|
+
db2.prepare("UPDATE agent_presence SET project_id = NULL WHERE agent = ?").run(agent);
|
|
33290
|
+
return {
|
|
33291
|
+
content: [{ type: "text", text: JSON.stringify({ agent, focused: false, project_id: null }) }]
|
|
33292
|
+
};
|
|
33293
|
+
});
|
|
33213
33294
|
server.registerTool("register_agent", {
|
|
33214
33295
|
description: "Register an agent with conflict detection. Returns AgentConflictError if another active session exists (active = heartbeat within last 30 min). Optional project_id locks agent to a project for the session.",
|
|
33215
33296
|
inputSchema: {
|
|
@@ -33356,6 +33437,9 @@ var init_mcp2 = __esm(() => {
|
|
|
33356
33437
|
"pin_message",
|
|
33357
33438
|
"unpin_message",
|
|
33358
33439
|
"get_pinned_messages",
|
|
33440
|
+
"set_focus",
|
|
33441
|
+
"get_focus",
|
|
33442
|
+
"unfocus",
|
|
33359
33443
|
"register_agent",
|
|
33360
33444
|
"heartbeat",
|
|
33361
33445
|
"list_agents",
|
|
@@ -33402,6 +33486,9 @@ var init_mcp2 = __esm(() => {
|
|
|
33402
33486
|
pin_message: "Pin a message. Required: id",
|
|
33403
33487
|
unpin_message: "Unpin a message. Required: id",
|
|
33404
33488
|
get_pinned_messages: "Get pinned messages. Optional: space?, session_id?, limit?",
|
|
33489
|
+
set_focus: "Set agent focus to a project. All read tools default to this scope. Required: project_id. Optional: from?",
|
|
33490
|
+
get_focus: "Get current focus: session focus, DB project_id, effective project_id. Optional: from?",
|
|
33491
|
+
unfocus: "Clear agent focus (session + DB). Optional: from?",
|
|
33405
33492
|
register_agent: "Register agent with conflict detection (30min active window). Required: name, session_id. Optional: role?. Returns AgentConflictError if another session is active.",
|
|
33406
33493
|
heartbeat: "Register/refresh agent presence. Optional: from?, status?(online|busy|idle, default: online)",
|
|
33407
33494
|
list_agents: "List agents with presence timestamps. Optional: online_only?(only agents seen in last 60s)",
|
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,
|
|
@@ -6700,6 +6701,10 @@ function getDb() {
|
|
|
6700
6701
|
db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
|
|
6701
6702
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
|
|
6702
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
|
+
}
|
|
6703
6708
|
const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
|
|
6704
6709
|
const presenceColNames = presenceCols.map((c) => c.name);
|
|
6705
6710
|
if (!presenceColNames.includes("id")) {
|
|
@@ -28732,11 +28737,11 @@ function sendMessage(opts) {
|
|
|
28732
28737
|
const blocking = opts.blocking ? 1 : 0;
|
|
28733
28738
|
const replyTo = opts.reply_to || null;
|
|
28734
28739
|
const stmt = db2.prepare(`
|
|
28735
|
-
INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
|
|
28736
|
-
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
28737
28742
|
RETURNING *
|
|
28738
28743
|
`);
|
|
28739
|
-
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);
|
|
28740
28745
|
const message = parseMessage(row);
|
|
28741
28746
|
if (opts.attachments && opts.attachments.length > 0) {
|
|
28742
28747
|
const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
|
|
@@ -28780,6 +28785,10 @@ function readMessages(opts = {}) {
|
|
|
28780
28785
|
conditions.push("space = ?");
|
|
28781
28786
|
params.push(opts.space);
|
|
28782
28787
|
}
|
|
28788
|
+
if (opts.project_id) {
|
|
28789
|
+
conditions.push("project_id = ?");
|
|
28790
|
+
params.push(opts.project_id);
|
|
28791
|
+
}
|
|
28783
28792
|
if (opts.since) {
|
|
28784
28793
|
conditions.push("created_at > ?");
|
|
28785
28794
|
params.push(opts.since);
|
|
@@ -29854,6 +29863,12 @@ function heartbeat(agent, status, metadata, sessionId) {
|
|
|
29854
29863
|
metadata = excluded.metadata
|
|
29855
29864
|
`).run(id, normalizedAgent, sessionId ?? null, resolvedStatus, metadataJson);
|
|
29856
29865
|
}
|
|
29866
|
+
function getPresence(agent) {
|
|
29867
|
+
const db2 = getDb();
|
|
29868
|
+
const normalizedAgent = agent.trim().toLowerCase();
|
|
29869
|
+
const row = db2.prepare("SELECT * FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedAgent);
|
|
29870
|
+
return row ? parsePresence(row) : null;
|
|
29871
|
+
}
|
|
29857
29872
|
function listAgents(opts) {
|
|
29858
29873
|
const db2 = getDb();
|
|
29859
29874
|
let query = "SELECT * FROM agent_presence";
|
|
@@ -29886,7 +29901,7 @@ function renameAgent(oldName, newName) {
|
|
|
29886
29901
|
// package.json
|
|
29887
29902
|
var package_default = {
|
|
29888
29903
|
name: "@hasna/conversations",
|
|
29889
|
-
version: "0.1.
|
|
29904
|
+
version: "0.1.32",
|
|
29890
29905
|
description: "Real-time CLI messaging for AI agents",
|
|
29891
29906
|
type: "module",
|
|
29892
29907
|
bin: {
|
|
@@ -29968,6 +29983,19 @@ var server = new McpServer({
|
|
|
29968
29983
|
name: "conversations",
|
|
29969
29984
|
version: package_default.version
|
|
29970
29985
|
});
|
|
29986
|
+
var agentFocus = new Map;
|
|
29987
|
+
function getAgentFocus(agentId) {
|
|
29988
|
+
if (agentFocus.has(agentId))
|
|
29989
|
+
return agentFocus.get(agentId).project_id;
|
|
29990
|
+
const presence = getPresence(agentId);
|
|
29991
|
+
return presence?.project_id ?? null;
|
|
29992
|
+
}
|
|
29993
|
+
function resolveProjectId(explicitProjectId, agentId) {
|
|
29994
|
+
if (explicitProjectId)
|
|
29995
|
+
return explicitProjectId;
|
|
29996
|
+
const focused = getAgentFocus(agentId);
|
|
29997
|
+
return focused ?? undefined;
|
|
29998
|
+
}
|
|
29971
29999
|
server.registerTool("send_message", {
|
|
29972
30000
|
description: "Send a DM to an agent.",
|
|
29973
30001
|
inputSchema: {
|
|
@@ -29975,17 +30003,19 @@ server.registerTool("send_message", {
|
|
|
29975
30003
|
content: exports_external.string(),
|
|
29976
30004
|
from: exports_external.string().optional(),
|
|
29977
30005
|
priority: exports_external.string().optional(),
|
|
29978
|
-
blocking: exports_external.coerce.boolean().optional()
|
|
30006
|
+
blocking: exports_external.coerce.boolean().optional(),
|
|
30007
|
+
project_id: exports_external.string().optional()
|
|
29979
30008
|
}
|
|
29980
30009
|
}, async (args) => {
|
|
29981
|
-
const { from: fromParam, to, content, priority, blocking } = args;
|
|
30010
|
+
const { from: fromParam, to, content, priority, blocking, project_id } = args;
|
|
29982
30011
|
const from = resolveIdentity(fromParam);
|
|
29983
30012
|
const msg = sendMessage({
|
|
29984
30013
|
from,
|
|
29985
30014
|
to,
|
|
29986
30015
|
content,
|
|
29987
30016
|
priority,
|
|
29988
|
-
blocking
|
|
30017
|
+
blocking,
|
|
30018
|
+
project_id
|
|
29989
30019
|
});
|
|
29990
30020
|
return {
|
|
29991
30021
|
content: [{ type: "text", text: JSON.stringify(msg) }]
|
|
@@ -29998,12 +30028,17 @@ server.registerTool("read_messages", {
|
|
|
29998
30028
|
from: exports_external.string().optional(),
|
|
29999
30029
|
to: exports_external.string().optional(),
|
|
30000
30030
|
space: exports_external.string().optional(),
|
|
30031
|
+
project_id: exports_external.string().optional(),
|
|
30001
30032
|
since: exports_external.string().optional(),
|
|
30002
30033
|
limit: exports_external.coerce.number().optional(),
|
|
30003
30034
|
unread_only: exports_external.coerce.boolean().optional()
|
|
30004
30035
|
}
|
|
30005
30036
|
}, async (args) => {
|
|
30006
|
-
const
|
|
30037
|
+
const agent = resolveIdentity(args.from);
|
|
30038
|
+
const messages = readMessages({
|
|
30039
|
+
...args,
|
|
30040
|
+
project_id: args.project_id ?? resolveProjectId(undefined, agent)
|
|
30041
|
+
});
|
|
30007
30042
|
return {
|
|
30008
30043
|
content: [{ type: "text", text: JSON.stringify(messages) }]
|
|
30009
30044
|
};
|
|
@@ -30595,6 +30630,58 @@ server.registerTool("get_pinned_messages", {
|
|
|
30595
30630
|
content: [{ type: "text", text: JSON.stringify(messages) }]
|
|
30596
30631
|
};
|
|
30597
30632
|
});
|
|
30633
|
+
server.registerTool("set_focus", {
|
|
30634
|
+
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.",
|
|
30635
|
+
inputSchema: {
|
|
30636
|
+
project_id: exports_external.string(),
|
|
30637
|
+
from: exports_external.string().optional()
|
|
30638
|
+
}
|
|
30639
|
+
}, async (args) => {
|
|
30640
|
+
const { project_id, from: fromParam } = args;
|
|
30641
|
+
const agent = resolveIdentity(fromParam);
|
|
30642
|
+
agentFocus.set(agent, { project_id });
|
|
30643
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
30644
|
+
db2.prepare("UPDATE agent_presence SET project_id = ? WHERE agent = ?").run(project_id, agent);
|
|
30645
|
+
return {
|
|
30646
|
+
content: [{ type: "text", text: JSON.stringify({ agent, focused: true, project_id }) }]
|
|
30647
|
+
};
|
|
30648
|
+
});
|
|
30649
|
+
server.registerTool("get_focus", {
|
|
30650
|
+
description: "Get the current focus state for an agent. Returns session focus, DB project_id, and effective project_id used for filtering.",
|
|
30651
|
+
inputSchema: {
|
|
30652
|
+
from: exports_external.string().optional()
|
|
30653
|
+
}
|
|
30654
|
+
}, async (args) => {
|
|
30655
|
+
const agent = resolveIdentity(args.from);
|
|
30656
|
+
const sessionFocus = agentFocus.get(agent) ?? null;
|
|
30657
|
+
const presence = getPresence(agent);
|
|
30658
|
+
const effective = getAgentFocus(agent);
|
|
30659
|
+
return {
|
|
30660
|
+
content: [{
|
|
30661
|
+
type: "text",
|
|
30662
|
+
text: JSON.stringify({
|
|
30663
|
+
agent,
|
|
30664
|
+
session_focus: sessionFocus?.project_id ?? null,
|
|
30665
|
+
db_project_id: presence?.project_id ?? null,
|
|
30666
|
+
effective_project_id: effective
|
|
30667
|
+
})
|
|
30668
|
+
}]
|
|
30669
|
+
};
|
|
30670
|
+
});
|
|
30671
|
+
server.registerTool("unfocus", {
|
|
30672
|
+
description: "Clear agent focus. Removes session focus and clears agent_presence.project_id in DB.",
|
|
30673
|
+
inputSchema: {
|
|
30674
|
+
from: exports_external.string().optional()
|
|
30675
|
+
}
|
|
30676
|
+
}, async (args) => {
|
|
30677
|
+
const agent = resolveIdentity(args.from);
|
|
30678
|
+
agentFocus.delete(agent);
|
|
30679
|
+
const db2 = (await Promise.resolve().then(() => (init_db(), exports_db))).getDb();
|
|
30680
|
+
db2.prepare("UPDATE agent_presence SET project_id = NULL WHERE agent = ?").run(agent);
|
|
30681
|
+
return {
|
|
30682
|
+
content: [{ type: "text", text: JSON.stringify({ agent, focused: false, project_id: null }) }]
|
|
30683
|
+
};
|
|
30684
|
+
});
|
|
30598
30685
|
server.registerTool("register_agent", {
|
|
30599
30686
|
description: "Register an agent with conflict detection. Returns AgentConflictError if another active session exists (active = heartbeat within last 30 min). Optional project_id locks agent to a project for the session.",
|
|
30600
30687
|
inputSchema: {
|
|
@@ -30741,6 +30828,9 @@ server.registerTool("search_tools", {
|
|
|
30741
30828
|
"pin_message",
|
|
30742
30829
|
"unpin_message",
|
|
30743
30830
|
"get_pinned_messages",
|
|
30831
|
+
"set_focus",
|
|
30832
|
+
"get_focus",
|
|
30833
|
+
"unfocus",
|
|
30744
30834
|
"register_agent",
|
|
30745
30835
|
"heartbeat",
|
|
30746
30836
|
"list_agents",
|
|
@@ -30787,6 +30877,9 @@ server.registerTool("describe_tools", {
|
|
|
30787
30877
|
pin_message: "Pin a message. Required: id",
|
|
30788
30878
|
unpin_message: "Unpin a message. Required: id",
|
|
30789
30879
|
get_pinned_messages: "Get pinned messages. Optional: space?, session_id?, limit?",
|
|
30880
|
+
set_focus: "Set agent focus to a project. All read tools default to this scope. Required: project_id. Optional: from?",
|
|
30881
|
+
get_focus: "Get current focus: session focus, DB project_id, effective project_id. Optional: from?",
|
|
30882
|
+
unfocus: "Clear agent focus (session + DB). Optional: from?",
|
|
30790
30883
|
register_agent: "Register agent with conflict detection (30min active window). Required: name, session_id. Optional: role?. Returns AgentConflictError if another session is active.",
|
|
30791
30884
|
heartbeat: "Register/refresh agent presence. Optional: from?, status?(online|busy|idle, default: online)",
|
|
30792
30885
|
list_agents: "List agents with presence timestamps. Optional: online_only?(only agents seen in last 60s)",
|
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,
|
|
@@ -227,6 +228,10 @@ function getDb() {
|
|
|
227
228
|
db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
|
|
228
229
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
|
|
229
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
|
+
}
|
|
230
235
|
const presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
|
|
231
236
|
const presenceColNames = presenceCols.map((c) => c.name);
|
|
232
237
|
if (!presenceColNames.includes("id")) {
|
|
@@ -2253,11 +2258,11 @@ function sendMessage(opts) {
|
|
|
2253
2258
|
const blocking = opts.blocking ? 1 : 0;
|
|
2254
2259
|
const replyTo = opts.reply_to || null;
|
|
2255
2260
|
const stmt = db2.prepare(`
|
|
2256
|
-
INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
|
|
2257
|
-
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2258
2263
|
RETURNING *
|
|
2259
2264
|
`);
|
|
2260
|
-
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);
|
|
2261
2266
|
const message = parseMessage(row);
|
|
2262
2267
|
if (opts.attachments && opts.attachments.length > 0) {
|
|
2263
2268
|
const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
|
|
@@ -2301,6 +2306,10 @@ function readMessages(opts = {}) {
|
|
|
2301
2306
|
conditions.push("space = ?");
|
|
2302
2307
|
params.push(opts.space);
|
|
2303
2308
|
}
|
|
2309
|
+
if (opts.project_id) {
|
|
2310
|
+
conditions.push("project_id = ?");
|
|
2311
|
+
params.push(opts.project_id);
|
|
2312
|
+
}
|
|
2304
2313
|
if (opts.since) {
|
|
2305
2314
|
conditions.push("created_at > ?");
|
|
2306
2315
|
params.push(opts.since);
|
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;
|