@hasna/conversations 0.0.7 → 0.1.0
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/README.md +222 -100
- package/bin/index.js +2349 -283
- package/bin/mcp.js +1124 -111
- package/dashboard/dist/assets/index-B6bl8Jzt.css +1 -0
- package/dashboard/dist/assets/index-Dbrg7by9.js +183 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cli/components/ChatView.d.ts +2 -2
- package/dist/cli/components/SessionList.d.ts +2 -2
- package/dist/index.d.ts +7 -5
- package/dist/index.js +700 -93
- package/dist/lib/messages.d.ts +22 -2
- package/dist/lib/poll.d.ts +3 -3
- package/dist/lib/presence.d.ts +7 -0
- package/dist/lib/projects.d.ts +27 -0
- package/dist/lib/projects.test.d.ts +1 -0
- package/dist/lib/spaces.d.ts +29 -0
- package/dist/lib/spaces.test.d.ts +1 -0
- package/dist/mcp/index.d.ts +1 -1
- package/dist/server/serve.d.ts +1 -1
- package/dist/types.d.ts +45 -7
- package/package.json +1 -1
- package/dashboard/dist/assets/index-C5hQqoWV.js +0 -163
- package/dashboard/dist/assets/index-Dr54QXlJ.css +0 -1
- package/dist/lib/channels.d.ts +0 -8
- /package/dist/lib/{channels.test.d.ts → presence.test.d.ts} +0 -0
package/bin/mcp.js
CHANGED
|
@@ -6286,7 +6286,7 @@ var require_formats = __commonJS((exports) => {
|
|
|
6286
6286
|
}
|
|
6287
6287
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
6288
6288
|
function getTime(strictTimeZone) {
|
|
6289
|
-
return function
|
|
6289
|
+
return function time(str) {
|
|
6290
6290
|
const matches = TIME.exec(str);
|
|
6291
6291
|
if (!matches)
|
|
6292
6292
|
return false;
|
|
@@ -28341,7 +28341,7 @@ function getDb() {
|
|
|
28341
28341
|
session_id TEXT NOT NULL,
|
|
28342
28342
|
from_agent TEXT NOT NULL,
|
|
28343
28343
|
to_agent TEXT NOT NULL,
|
|
28344
|
-
|
|
28344
|
+
space TEXT,
|
|
28345
28345
|
content TEXT NOT NULL,
|
|
28346
28346
|
priority TEXT NOT NULL DEFAULT 'normal',
|
|
28347
28347
|
working_dir TEXT,
|
|
@@ -28355,48 +28355,137 @@ function getDb() {
|
|
|
28355
28355
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id)");
|
|
28356
28356
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_to ON messages(to_agent)");
|
|
28357
28357
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at)");
|
|
28358
|
-
db.exec("CREATE INDEX IF NOT EXISTS
|
|
28359
|
-
|
|
28360
|
-
|
|
28361
|
-
|
|
28362
|
-
|
|
28358
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_space ON messages(space)");
|
|
28359
|
+
db.exec(`
|
|
28360
|
+
CREATE TABLE IF NOT EXISTS projects (
|
|
28361
|
+
id TEXT PRIMARY KEY,
|
|
28362
|
+
name TEXT NOT NULL UNIQUE,
|
|
28363
|
+
description TEXT,
|
|
28364
|
+
path TEXT,
|
|
28365
|
+
created_by TEXT NOT NULL,
|
|
28366
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
28367
|
+
metadata TEXT,
|
|
28368
|
+
tags TEXT,
|
|
28369
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
28370
|
+
repository TEXT,
|
|
28371
|
+
settings TEXT
|
|
28372
|
+
)
|
|
28373
|
+
`);
|
|
28374
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_projects_name ON projects(name)");
|
|
28375
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status)");
|
|
28363
28376
|
db.exec(`
|
|
28364
|
-
CREATE TABLE IF NOT EXISTS
|
|
28377
|
+
CREATE TABLE IF NOT EXISTS spaces (
|
|
28365
28378
|
name TEXT PRIMARY KEY,
|
|
28366
28379
|
description TEXT,
|
|
28380
|
+
parent_id TEXT REFERENCES spaces(name),
|
|
28381
|
+
project_id TEXT REFERENCES projects(id),
|
|
28367
28382
|
created_by TEXT NOT NULL,
|
|
28368
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now'))
|
|
28383
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
28384
|
+
archived_at TEXT
|
|
28369
28385
|
)
|
|
28370
28386
|
`);
|
|
28387
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_spaces_parent ON spaces(parent_id)");
|
|
28388
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_spaces_project ON spaces(project_id)");
|
|
28371
28389
|
db.exec(`
|
|
28372
|
-
CREATE TABLE IF NOT EXISTS
|
|
28373
|
-
|
|
28390
|
+
CREATE TABLE IF NOT EXISTS space_members (
|
|
28391
|
+
space TEXT NOT NULL REFERENCES spaces(name),
|
|
28374
28392
|
agent TEXT NOT NULL,
|
|
28375
28393
|
joined_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
28376
|
-
PRIMARY KEY (
|
|
28394
|
+
PRIMARY KEY (space, agent)
|
|
28377
28395
|
)
|
|
28378
28396
|
`);
|
|
28397
|
+
db.exec(`
|
|
28398
|
+
CREATE TABLE IF NOT EXISTS agent_presence (
|
|
28399
|
+
agent TEXT PRIMARY KEY,
|
|
28400
|
+
status TEXT NOT NULL DEFAULT 'online',
|
|
28401
|
+
last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
28402
|
+
metadata TEXT
|
|
28403
|
+
)
|
|
28404
|
+
`);
|
|
28405
|
+
const existingTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
28406
|
+
const tableNames = existingTables.map((t) => t.name);
|
|
28407
|
+
if (tableNames.includes("channels") && tableNames.includes("spaces")) {
|
|
28408
|
+
const spaceCount = db.prepare("SELECT COUNT(*) as c FROM spaces").get().c;
|
|
28409
|
+
const channelCount = db.prepare("SELECT COUNT(*) as c FROM channels").get().c;
|
|
28410
|
+
if (channelCount > 0 && spaceCount === 0) {
|
|
28411
|
+
db.exec("BEGIN");
|
|
28412
|
+
try {
|
|
28413
|
+
db.exec(`
|
|
28414
|
+
INSERT OR IGNORE INTO spaces (name, description, created_by, created_at)
|
|
28415
|
+
SELECT name, description, created_by, created_at FROM channels
|
|
28416
|
+
`);
|
|
28417
|
+
if (tableNames.includes("channel_members")) {
|
|
28418
|
+
db.exec(`
|
|
28419
|
+
INSERT OR IGNORE INTO space_members (space, agent, joined_at)
|
|
28420
|
+
SELECT channel, agent, joined_at FROM channel_members
|
|
28421
|
+
`);
|
|
28422
|
+
}
|
|
28423
|
+
db.exec("COMMIT");
|
|
28424
|
+
} catch (e) {
|
|
28425
|
+
db.exec("ROLLBACK");
|
|
28426
|
+
throw e;
|
|
28427
|
+
}
|
|
28428
|
+
}
|
|
28429
|
+
db.exec("DROP TABLE IF EXISTS channel_members");
|
|
28430
|
+
db.exec("DROP TABLE IF EXISTS channels");
|
|
28431
|
+
}
|
|
28432
|
+
const msgCols = db.prepare("PRAGMA table_info(messages)").all();
|
|
28433
|
+
const colNames = msgCols.map((c) => c.name);
|
|
28434
|
+
if (colNames.includes("channel") && !colNames.includes("space")) {
|
|
28435
|
+
db.exec("ALTER TABLE messages ADD COLUMN space TEXT");
|
|
28436
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_space ON messages(space)");
|
|
28437
|
+
db.exec("UPDATE messages SET space = channel WHERE channel IS NOT NULL");
|
|
28438
|
+
db.exec(`
|
|
28439
|
+
UPDATE messages
|
|
28440
|
+
SET session_id = 'space:' || substr(session_id, 9)
|
|
28441
|
+
WHERE session_id LIKE 'channel:%'
|
|
28442
|
+
`);
|
|
28443
|
+
}
|
|
28444
|
+
const spaceCols = db.prepare("PRAGMA table_info(spaces)").all();
|
|
28445
|
+
const spaceColNames = spaceCols.map((c) => c.name);
|
|
28446
|
+
if (!spaceColNames.includes("archived_at")) {
|
|
28447
|
+
db.exec("ALTER TABLE spaces ADD COLUMN archived_at TEXT");
|
|
28448
|
+
}
|
|
28449
|
+
const msgCols2 = db.prepare("PRAGMA table_info(messages)").all();
|
|
28450
|
+
const colNames2 = msgCols2.map((c) => c.name);
|
|
28451
|
+
if (!colNames2.includes("edited_at")) {
|
|
28452
|
+
db.exec("ALTER TABLE messages ADD COLUMN edited_at TEXT");
|
|
28453
|
+
}
|
|
28454
|
+
if (!colNames2.includes("pinned_at")) {
|
|
28455
|
+
db.exec("ALTER TABLE messages ADD COLUMN pinned_at TEXT");
|
|
28456
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_pinned ON messages(pinned_at)");
|
|
28457
|
+
}
|
|
28379
28458
|
return db;
|
|
28380
28459
|
}
|
|
28381
28460
|
|
|
28382
28461
|
// src/lib/messages.ts
|
|
28383
28462
|
import { randomUUID } from "crypto";
|
|
28384
28463
|
function parseMessage(row) {
|
|
28464
|
+
let metadata = null;
|
|
28465
|
+
if (row.metadata) {
|
|
28466
|
+
try {
|
|
28467
|
+
metadata = JSON.parse(row.metadata);
|
|
28468
|
+
} catch {
|
|
28469
|
+
metadata = null;
|
|
28470
|
+
}
|
|
28471
|
+
}
|
|
28385
28472
|
return {
|
|
28386
28473
|
...row,
|
|
28387
|
-
metadata
|
|
28474
|
+
metadata
|
|
28388
28475
|
};
|
|
28389
28476
|
}
|
|
28390
28477
|
function sendMessage(opts) {
|
|
28391
28478
|
const db2 = getDb();
|
|
28392
|
-
const
|
|
28479
|
+
const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
|
|
28480
|
+
const sessionId = explicitSession ?? (opts.space ? `space:${opts.space}` : `${[opts.from, opts.to].sort().join("-")}-${randomUUID().slice(0, 8)}`);
|
|
28393
28481
|
const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
|
|
28482
|
+
const normalizedPriority = opts.priority === "low" || opts.priority === "normal" || opts.priority === "high" || opts.priority === "urgent" ? opts.priority : "normal";
|
|
28394
28483
|
const stmt = db2.prepare(`
|
|
28395
|
-
INSERT INTO messages (session_id, from_agent, to_agent,
|
|
28484
|
+
INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata)
|
|
28396
28485
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
28397
28486
|
RETURNING *
|
|
28398
28487
|
`);
|
|
28399
|
-
const row = stmt.get(sessionId, opts.from, opts.to, opts.
|
|
28488
|
+
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);
|
|
28400
28489
|
return parseMessage(row);
|
|
28401
28490
|
}
|
|
28402
28491
|
function readMessages(opts = {}) {
|
|
@@ -28415,20 +28504,25 @@ function readMessages(opts = {}) {
|
|
|
28415
28504
|
conditions.push("to_agent = ?");
|
|
28416
28505
|
params.push(opts.to);
|
|
28417
28506
|
}
|
|
28418
|
-
if (opts.
|
|
28419
|
-
conditions.push("
|
|
28420
|
-
params.push(opts.
|
|
28507
|
+
if (opts.space) {
|
|
28508
|
+
conditions.push("space = ?");
|
|
28509
|
+
params.push(opts.space);
|
|
28421
28510
|
}
|
|
28422
28511
|
if (opts.since) {
|
|
28423
28512
|
conditions.push("created_at > ?");
|
|
28424
28513
|
params.push(opts.since);
|
|
28425
28514
|
}
|
|
28515
|
+
if (opts.since_id !== undefined) {
|
|
28516
|
+
conditions.push("id > ?");
|
|
28517
|
+
params.push(opts.since_id);
|
|
28518
|
+
}
|
|
28426
28519
|
if (opts.unread_only) {
|
|
28427
28520
|
conditions.push("read_at IS NULL");
|
|
28428
28521
|
}
|
|
28429
28522
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
28430
|
-
const limit = opts.limit ? `LIMIT ${opts.limit}` : "";
|
|
28431
|
-
const
|
|
28523
|
+
const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? `LIMIT ${Math.floor(opts.limit)}` : "";
|
|
28524
|
+
const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
28525
|
+
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} ${limit}`).all(...params);
|
|
28432
28526
|
return rows.map(parseMessage);
|
|
28433
28527
|
}
|
|
28434
28528
|
function markRead(ids, reader) {
|
|
@@ -28445,6 +28539,130 @@ function getMessageById(id) {
|
|
|
28445
28539
|
const row = db2.prepare("SELECT * FROM messages WHERE id = ?").get(id);
|
|
28446
28540
|
return row ? parseMessage(row) : null;
|
|
28447
28541
|
}
|
|
28542
|
+
function markAllRead(agent) {
|
|
28543
|
+
const db2 = getDb();
|
|
28544
|
+
const stmt = db2.prepare(`UPDATE messages SET read_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE to_agent = ? AND read_at IS NULL`);
|
|
28545
|
+
const result = stmt.run(agent);
|
|
28546
|
+
return result.changes;
|
|
28547
|
+
}
|
|
28548
|
+
function escapeCsvField(value) {
|
|
28549
|
+
if (value === null || value === undefined)
|
|
28550
|
+
return "";
|
|
28551
|
+
const str = String(value);
|
|
28552
|
+
if (str.includes(",") || str.includes('"') || str.includes(`
|
|
28553
|
+
`) || str.includes("\r")) {
|
|
28554
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
28555
|
+
}
|
|
28556
|
+
return str;
|
|
28557
|
+
}
|
|
28558
|
+
function exportMessages(opts) {
|
|
28559
|
+
const db2 = getDb();
|
|
28560
|
+
const conditions = [];
|
|
28561
|
+
const params = [];
|
|
28562
|
+
if (opts?.space) {
|
|
28563
|
+
conditions.push("space = ?");
|
|
28564
|
+
params.push(opts.space);
|
|
28565
|
+
}
|
|
28566
|
+
if (opts?.session_id) {
|
|
28567
|
+
conditions.push("session_id = ?");
|
|
28568
|
+
params.push(opts.session_id);
|
|
28569
|
+
}
|
|
28570
|
+
if (opts?.from) {
|
|
28571
|
+
conditions.push("from_agent = ?");
|
|
28572
|
+
params.push(opts.from);
|
|
28573
|
+
}
|
|
28574
|
+
if (opts?.since) {
|
|
28575
|
+
conditions.push("created_at >= ?");
|
|
28576
|
+
params.push(opts.since);
|
|
28577
|
+
}
|
|
28578
|
+
if (opts?.until) {
|
|
28579
|
+
conditions.push("created_at <= ?");
|
|
28580
|
+
params.push(opts.until);
|
|
28581
|
+
}
|
|
28582
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
28583
|
+
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ASC, id ASC`).all(...params);
|
|
28584
|
+
const messages = rows.map(parseMessage);
|
|
28585
|
+
const format = opts?.format ?? "json";
|
|
28586
|
+
if (format === "csv") {
|
|
28587
|
+
const headers = "id,session_id,from_agent,to_agent,space,content,priority,created_at,read_at";
|
|
28588
|
+
const lines = messages.map((m) => [
|
|
28589
|
+
String(m.id),
|
|
28590
|
+
escapeCsvField(m.session_id),
|
|
28591
|
+
escapeCsvField(m.from_agent),
|
|
28592
|
+
escapeCsvField(m.to_agent),
|
|
28593
|
+
escapeCsvField(m.space),
|
|
28594
|
+
escapeCsvField(m.content),
|
|
28595
|
+
escapeCsvField(m.priority),
|
|
28596
|
+
escapeCsvField(m.created_at),
|
|
28597
|
+
escapeCsvField(m.read_at)
|
|
28598
|
+
].join(","));
|
|
28599
|
+
return [headers, ...lines].join(`
|
|
28600
|
+
`);
|
|
28601
|
+
}
|
|
28602
|
+
return JSON.stringify(messages, null, 2);
|
|
28603
|
+
}
|
|
28604
|
+
function deleteMessage(id, agent) {
|
|
28605
|
+
const db2 = getDb();
|
|
28606
|
+
const stmt = db2.prepare("DELETE FROM messages WHERE id = ? AND from_agent = ?");
|
|
28607
|
+
const result = stmt.run(id, agent);
|
|
28608
|
+
return result.changes > 0;
|
|
28609
|
+
}
|
|
28610
|
+
function editMessage(id, agent, newContent) {
|
|
28611
|
+
const db2 = getDb();
|
|
28612
|
+
const stmt = db2.prepare(`UPDATE messages SET content = ?, edited_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE id = ? AND from_agent = ? RETURNING *`);
|
|
28613
|
+
const row = stmt.get(newContent, id, agent);
|
|
28614
|
+
return row ? parseMessage(row) : null;
|
|
28615
|
+
}
|
|
28616
|
+
function pinMessage(id) {
|
|
28617
|
+
const db2 = getDb();
|
|
28618
|
+
const stmt = db2.prepare(`UPDATE messages SET pinned_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE id = ? RETURNING *`);
|
|
28619
|
+
const row = stmt.get(id);
|
|
28620
|
+
return row ? parseMessage(row) : null;
|
|
28621
|
+
}
|
|
28622
|
+
function unpinMessage(id) {
|
|
28623
|
+
const db2 = getDb();
|
|
28624
|
+
const stmt = db2.prepare(`UPDATE messages SET pinned_at = NULL WHERE id = ? RETURNING *`);
|
|
28625
|
+
const row = stmt.get(id);
|
|
28626
|
+
return row ? parseMessage(row) : null;
|
|
28627
|
+
}
|
|
28628
|
+
function getPinnedMessages(opts) {
|
|
28629
|
+
const db2 = getDb();
|
|
28630
|
+
const conditions = ["pinned_at IS NOT NULL"];
|
|
28631
|
+
const params = [];
|
|
28632
|
+
if (opts?.space) {
|
|
28633
|
+
conditions.push("space = ?");
|
|
28634
|
+
params.push(opts.space);
|
|
28635
|
+
}
|
|
28636
|
+
if (opts?.session_id) {
|
|
28637
|
+
conditions.push("session_id = ?");
|
|
28638
|
+
params.push(opts.session_id);
|
|
28639
|
+
}
|
|
28640
|
+
const where = `WHERE ${conditions.join(" AND ")}`;
|
|
28641
|
+
const limit = Number.isFinite(opts?.limit) && opts.limit > 0 ? `LIMIT ${Math.floor(opts.limit)}` : "";
|
|
28642
|
+
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY pinned_at DESC, id DESC ${limit}`).all(...params);
|
|
28643
|
+
return rows.map(parseMessage);
|
|
28644
|
+
}
|
|
28645
|
+
function searchMessages(opts) {
|
|
28646
|
+
const db2 = getDb();
|
|
28647
|
+
const conditions = ["content LIKE ?"];
|
|
28648
|
+
const params = [`%${opts.query}%`];
|
|
28649
|
+
if (opts.space) {
|
|
28650
|
+
conditions.push("space = ?");
|
|
28651
|
+
params.push(opts.space);
|
|
28652
|
+
}
|
|
28653
|
+
if (opts.from) {
|
|
28654
|
+
conditions.push("from_agent = ?");
|
|
28655
|
+
params.push(opts.from);
|
|
28656
|
+
}
|
|
28657
|
+
if (opts.to) {
|
|
28658
|
+
conditions.push("to_agent = ?");
|
|
28659
|
+
params.push(opts.to);
|
|
28660
|
+
}
|
|
28661
|
+
const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 50;
|
|
28662
|
+
const where = `WHERE ${conditions.join(" AND ")}`;
|
|
28663
|
+
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at DESC, id DESC LIMIT ${limit}`).all(...params);
|
|
28664
|
+
return rows.map(parseMessage);
|
|
28665
|
+
}
|
|
28448
28666
|
|
|
28449
28667
|
// src/lib/sessions.ts
|
|
28450
28668
|
function listSessions(agent) {
|
|
@@ -28476,70 +28694,407 @@ function listSessions(agent) {
|
|
|
28476
28694
|
});
|
|
28477
28695
|
}
|
|
28478
28696
|
|
|
28479
|
-
// src/lib/
|
|
28480
|
-
function
|
|
28697
|
+
// src/lib/spaces.ts
|
|
28698
|
+
function getSpaceDepth(spaceName) {
|
|
28699
|
+
const db2 = getDb();
|
|
28700
|
+
let depth = 0;
|
|
28701
|
+
let current = spaceName;
|
|
28702
|
+
for (let i = 0;i < 10; i++) {
|
|
28703
|
+
const row = db2.prepare("SELECT parent_id FROM spaces WHERE name = ?").get(current);
|
|
28704
|
+
if (!row || !row.parent_id)
|
|
28705
|
+
break;
|
|
28706
|
+
depth++;
|
|
28707
|
+
current = row.parent_id;
|
|
28708
|
+
}
|
|
28709
|
+
return depth;
|
|
28710
|
+
}
|
|
28711
|
+
function createSpace(name, createdBy, options) {
|
|
28481
28712
|
const db2 = getDb();
|
|
28482
|
-
|
|
28483
|
-
|
|
28713
|
+
if (options?.parent_id) {
|
|
28714
|
+
const parentExists = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(options.parent_id);
|
|
28715
|
+
if (!parentExists) {
|
|
28716
|
+
throw new Error(`Parent space not found: ${options.parent_id}`);
|
|
28717
|
+
}
|
|
28718
|
+
const parentDepth = getSpaceDepth(options.parent_id);
|
|
28719
|
+
if (parentDepth >= 2) {
|
|
28720
|
+
throw new Error("Maximum space nesting depth is 3 levels");
|
|
28721
|
+
}
|
|
28722
|
+
}
|
|
28723
|
+
if (options?.project_id) {
|
|
28724
|
+
const projectExists = db2.prepare("SELECT id FROM projects WHERE id = ?").get(options.project_id);
|
|
28725
|
+
if (!projectExists) {
|
|
28726
|
+
throw new Error(`Project not found: ${options.project_id}`);
|
|
28727
|
+
}
|
|
28728
|
+
}
|
|
28729
|
+
const row = db2.prepare("INSERT INTO spaces (name, description, parent_id, project_id, created_by) VALUES (?, ?, ?, ?, ?) RETURNING *").get(name, options?.description || null, options?.parent_id || null, options?.project_id || null, createdBy);
|
|
28730
|
+
db2.prepare("INSERT OR IGNORE INTO space_members (space, agent) VALUES (?, ?)").run(name, createdBy);
|
|
28484
28731
|
return row;
|
|
28485
28732
|
}
|
|
28486
|
-
function
|
|
28733
|
+
function listSpaces(options) {
|
|
28487
28734
|
const db2 = getDb();
|
|
28735
|
+
const conditions = [];
|
|
28736
|
+
const params = [];
|
|
28737
|
+
if (options?.project_id) {
|
|
28738
|
+
conditions.push("s.project_id = ?");
|
|
28739
|
+
params.push(options.project_id);
|
|
28740
|
+
}
|
|
28741
|
+
if (options?.parent_id !== undefined) {
|
|
28742
|
+
if (options.parent_id === null) {
|
|
28743
|
+
conditions.push("s.parent_id IS NULL");
|
|
28744
|
+
} else {
|
|
28745
|
+
conditions.push("s.parent_id = ?");
|
|
28746
|
+
params.push(options.parent_id);
|
|
28747
|
+
}
|
|
28748
|
+
}
|
|
28749
|
+
if (!options?.include_archived) {
|
|
28750
|
+
conditions.push("s.archived_at IS NULL");
|
|
28751
|
+
}
|
|
28752
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
28488
28753
|
const rows = db2.prepare(`
|
|
28489
28754
|
SELECT
|
|
28490
|
-
|
|
28491
|
-
|
|
28492
|
-
|
|
28493
|
-
|
|
28494
|
-
|
|
28495
|
-
|
|
28496
|
-
|
|
28497
|
-
|
|
28498
|
-
|
|
28755
|
+
s.name,
|
|
28756
|
+
s.description,
|
|
28757
|
+
s.parent_id,
|
|
28758
|
+
s.project_id,
|
|
28759
|
+
s.created_by,
|
|
28760
|
+
s.created_at,
|
|
28761
|
+
s.archived_at,
|
|
28762
|
+
(SELECT COUNT(*) FROM space_members WHERE space = s.name) AS member_count,
|
|
28763
|
+
(SELECT COUNT(*) FROM messages WHERE space = s.name) AS message_count
|
|
28764
|
+
FROM spaces s
|
|
28765
|
+
${where}
|
|
28766
|
+
ORDER BY s.name ASC
|
|
28767
|
+
`).all(...params);
|
|
28499
28768
|
return rows;
|
|
28500
28769
|
}
|
|
28501
|
-
function
|
|
28770
|
+
function getSpace(name) {
|
|
28502
28771
|
const db2 = getDb();
|
|
28503
28772
|
const row = db2.prepare(`
|
|
28504
28773
|
SELECT
|
|
28505
|
-
|
|
28506
|
-
|
|
28507
|
-
|
|
28508
|
-
|
|
28509
|
-
|
|
28510
|
-
|
|
28511
|
-
|
|
28512
|
-
|
|
28774
|
+
s.name,
|
|
28775
|
+
s.description,
|
|
28776
|
+
s.parent_id,
|
|
28777
|
+
s.project_id,
|
|
28778
|
+
s.created_by,
|
|
28779
|
+
s.created_at,
|
|
28780
|
+
s.archived_at,
|
|
28781
|
+
(SELECT COUNT(*) FROM space_members WHERE space = s.name) AS member_count,
|
|
28782
|
+
(SELECT COUNT(*) FROM messages WHERE space = s.name) AS message_count
|
|
28783
|
+
FROM spaces s
|
|
28784
|
+
WHERE s.name = ?
|
|
28513
28785
|
`).get(name);
|
|
28514
28786
|
return row;
|
|
28515
28787
|
}
|
|
28516
|
-
function
|
|
28788
|
+
function joinSpace(spaceName, agent) {
|
|
28517
28789
|
const db2 = getDb();
|
|
28518
|
-
const
|
|
28519
|
-
if (!
|
|
28790
|
+
const space = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(spaceName);
|
|
28791
|
+
if (!space)
|
|
28520
28792
|
return false;
|
|
28521
|
-
db2.prepare("INSERT OR IGNORE INTO
|
|
28793
|
+
db2.prepare("INSERT OR IGNORE INTO space_members (space, agent) VALUES (?, ?)").run(spaceName, agent);
|
|
28522
28794
|
return true;
|
|
28523
28795
|
}
|
|
28524
|
-
function
|
|
28796
|
+
function leaveSpace(spaceName, agent) {
|
|
28525
28797
|
const db2 = getDb();
|
|
28526
|
-
const result = db2.prepare("DELETE FROM
|
|
28798
|
+
const result = db2.prepare("DELETE FROM space_members WHERE space = ? AND agent = ?").run(spaceName, agent);
|
|
28799
|
+
return result.changes > 0;
|
|
28800
|
+
}
|
|
28801
|
+
function updateSpace(name, updates) {
|
|
28802
|
+
const db2 = getDb();
|
|
28803
|
+
const existing = db2.prepare("SELECT * FROM spaces WHERE name = ?").get(name);
|
|
28804
|
+
if (!existing) {
|
|
28805
|
+
throw new Error(`Space not found: ${name}`);
|
|
28806
|
+
}
|
|
28807
|
+
if (updates.parent_id !== undefined && updates.parent_id !== existing.parent_id) {
|
|
28808
|
+
if (updates.parent_id !== null) {
|
|
28809
|
+
const parentExists = db2.prepare("SELECT name FROM spaces WHERE name = ?").get(updates.parent_id);
|
|
28810
|
+
if (!parentExists) {
|
|
28811
|
+
throw new Error(`Parent space not found: ${updates.parent_id}`);
|
|
28812
|
+
}
|
|
28813
|
+
const parentDepth = getSpaceDepth(updates.parent_id);
|
|
28814
|
+
if (parentDepth >= 2) {
|
|
28815
|
+
throw new Error("Maximum space nesting depth is 3 levels");
|
|
28816
|
+
}
|
|
28817
|
+
if (updates.parent_id === name) {
|
|
28818
|
+
throw new Error("A space cannot be its own parent");
|
|
28819
|
+
}
|
|
28820
|
+
}
|
|
28821
|
+
}
|
|
28822
|
+
if (updates.project_id !== undefined && updates.project_id !== existing.project_id) {
|
|
28823
|
+
if (updates.project_id !== null) {
|
|
28824
|
+
const projectExists = db2.prepare("SELECT id FROM projects WHERE id = ?").get(updates.project_id);
|
|
28825
|
+
if (!projectExists) {
|
|
28826
|
+
throw new Error(`Project not found: ${updates.project_id}`);
|
|
28827
|
+
}
|
|
28828
|
+
}
|
|
28829
|
+
}
|
|
28830
|
+
const sets = [];
|
|
28831
|
+
const params = [];
|
|
28832
|
+
if (updates.description !== undefined) {
|
|
28833
|
+
sets.push("description = ?");
|
|
28834
|
+
params.push(updates.description);
|
|
28835
|
+
}
|
|
28836
|
+
if (updates.parent_id !== undefined) {
|
|
28837
|
+
sets.push("parent_id = ?");
|
|
28838
|
+
params.push(updates.parent_id);
|
|
28839
|
+
}
|
|
28840
|
+
if (updates.project_id !== undefined) {
|
|
28841
|
+
sets.push("project_id = ?");
|
|
28842
|
+
params.push(updates.project_id);
|
|
28843
|
+
}
|
|
28844
|
+
if (sets.length === 0) {
|
|
28845
|
+
return existing;
|
|
28846
|
+
}
|
|
28847
|
+
params.push(name);
|
|
28848
|
+
const row = db2.prepare(`UPDATE spaces SET ${sets.join(", ")} WHERE name = ? RETURNING *`).get(...params);
|
|
28849
|
+
return row;
|
|
28850
|
+
}
|
|
28851
|
+
function archiveSpace(name) {
|
|
28852
|
+
const db2 = getDb();
|
|
28853
|
+
const existing = db2.prepare("SELECT * FROM spaces WHERE name = ?").get(name);
|
|
28854
|
+
if (!existing) {
|
|
28855
|
+
throw new Error(`Space not found: ${name}`);
|
|
28856
|
+
}
|
|
28857
|
+
const row = db2.prepare("UPDATE spaces SET archived_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE name = ? RETURNING *").get(name);
|
|
28858
|
+
return row;
|
|
28859
|
+
}
|
|
28860
|
+
function unarchiveSpace(name) {
|
|
28861
|
+
const db2 = getDb();
|
|
28862
|
+
const existing = db2.prepare("SELECT * FROM spaces WHERE name = ?").get(name);
|
|
28863
|
+
if (!existing) {
|
|
28864
|
+
throw new Error(`Space not found: ${name}`);
|
|
28865
|
+
}
|
|
28866
|
+
const row = db2.prepare("UPDATE spaces SET archived_at = NULL WHERE name = ? RETURNING *").get(name);
|
|
28867
|
+
return row;
|
|
28868
|
+
}
|
|
28869
|
+
|
|
28870
|
+
// src/lib/projects.ts
|
|
28871
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
28872
|
+
function parseProject(row) {
|
|
28873
|
+
let metadata = null;
|
|
28874
|
+
if (row.metadata) {
|
|
28875
|
+
try {
|
|
28876
|
+
metadata = JSON.parse(row.metadata);
|
|
28877
|
+
} catch {
|
|
28878
|
+
metadata = null;
|
|
28879
|
+
}
|
|
28880
|
+
}
|
|
28881
|
+
let tags = [];
|
|
28882
|
+
if (row.tags) {
|
|
28883
|
+
try {
|
|
28884
|
+
tags = JSON.parse(row.tags);
|
|
28885
|
+
} catch {
|
|
28886
|
+
tags = [];
|
|
28887
|
+
}
|
|
28888
|
+
}
|
|
28889
|
+
let settings = null;
|
|
28890
|
+
if (row.settings) {
|
|
28891
|
+
try {
|
|
28892
|
+
settings = JSON.parse(row.settings);
|
|
28893
|
+
} catch {
|
|
28894
|
+
settings = null;
|
|
28895
|
+
}
|
|
28896
|
+
}
|
|
28897
|
+
return {
|
|
28898
|
+
id: row.id,
|
|
28899
|
+
name: row.name,
|
|
28900
|
+
description: row.description || null,
|
|
28901
|
+
path: row.path || null,
|
|
28902
|
+
created_by: row.created_by,
|
|
28903
|
+
created_at: row.created_at,
|
|
28904
|
+
metadata,
|
|
28905
|
+
tags,
|
|
28906
|
+
status: row.status || "active",
|
|
28907
|
+
repository: row.repository || null,
|
|
28908
|
+
settings
|
|
28909
|
+
};
|
|
28910
|
+
}
|
|
28911
|
+
function createProject(opts) {
|
|
28912
|
+
const db2 = getDb();
|
|
28913
|
+
const id = randomUUID2();
|
|
28914
|
+
const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
|
|
28915
|
+
const tags = opts.tags ? JSON.stringify(opts.tags) : null;
|
|
28916
|
+
const settings = opts.settings ? JSON.stringify(opts.settings) : null;
|
|
28917
|
+
const row = db2.prepare(`
|
|
28918
|
+
INSERT INTO projects (id, name, description, path, created_by, metadata, tags, repository, settings)
|
|
28919
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
28920
|
+
RETURNING *
|
|
28921
|
+
`).get(id, opts.name, opts.description || null, opts.path || null, opts.created_by, metadata, tags, opts.repository || null, settings);
|
|
28922
|
+
return parseProject(row);
|
|
28923
|
+
}
|
|
28924
|
+
function listProjects(opts) {
|
|
28925
|
+
const db2 = getDb();
|
|
28926
|
+
const conditions = [];
|
|
28927
|
+
const params = [];
|
|
28928
|
+
if (opts?.status) {
|
|
28929
|
+
conditions.push("p.status = ?");
|
|
28930
|
+
params.push(opts.status);
|
|
28931
|
+
}
|
|
28932
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
28933
|
+
const rows = db2.prepare(`
|
|
28934
|
+
SELECT
|
|
28935
|
+
p.*,
|
|
28936
|
+
(SELECT COUNT(*) FROM spaces WHERE project_id = p.id) AS space_count
|
|
28937
|
+
FROM projects p
|
|
28938
|
+
${where}
|
|
28939
|
+
ORDER BY p.name ASC
|
|
28940
|
+
`).all(...params);
|
|
28941
|
+
return rows.map((row) => ({
|
|
28942
|
+
...parseProject(row),
|
|
28943
|
+
space_count: row.space_count
|
|
28944
|
+
}));
|
|
28945
|
+
}
|
|
28946
|
+
function getProject(id) {
|
|
28947
|
+
const db2 = getDb();
|
|
28948
|
+
const row = db2.prepare(`
|
|
28949
|
+
SELECT
|
|
28950
|
+
p.*,
|
|
28951
|
+
(SELECT COUNT(*) FROM spaces WHERE project_id = p.id) AS space_count
|
|
28952
|
+
FROM projects p
|
|
28953
|
+
WHERE p.id = ?
|
|
28954
|
+
`).get(id);
|
|
28955
|
+
if (!row)
|
|
28956
|
+
return null;
|
|
28957
|
+
return {
|
|
28958
|
+
...parseProject(row),
|
|
28959
|
+
space_count: row.space_count
|
|
28960
|
+
};
|
|
28961
|
+
}
|
|
28962
|
+
function getProjectByName(name) {
|
|
28963
|
+
const db2 = getDb();
|
|
28964
|
+
const row = db2.prepare(`
|
|
28965
|
+
SELECT
|
|
28966
|
+
p.*,
|
|
28967
|
+
(SELECT COUNT(*) FROM spaces WHERE project_id = p.id) AS space_count
|
|
28968
|
+
FROM projects p
|
|
28969
|
+
WHERE p.name = ?
|
|
28970
|
+
`).get(name);
|
|
28971
|
+
if (!row)
|
|
28972
|
+
return null;
|
|
28973
|
+
return {
|
|
28974
|
+
...parseProject(row),
|
|
28975
|
+
space_count: row.space_count
|
|
28976
|
+
};
|
|
28977
|
+
}
|
|
28978
|
+
function updateProject(id, updates) {
|
|
28979
|
+
const db2 = getDb();
|
|
28980
|
+
const existing = db2.prepare("SELECT * FROM projects WHERE id = ?").get(id);
|
|
28981
|
+
if (!existing) {
|
|
28982
|
+
throw new Error(`Project not found: ${id}`);
|
|
28983
|
+
}
|
|
28984
|
+
const sets = [];
|
|
28985
|
+
const params = [];
|
|
28986
|
+
if (updates.name !== undefined) {
|
|
28987
|
+
sets.push("name = ?");
|
|
28988
|
+
params.push(updates.name);
|
|
28989
|
+
}
|
|
28990
|
+
if (updates.description !== undefined) {
|
|
28991
|
+
sets.push("description = ?");
|
|
28992
|
+
params.push(updates.description);
|
|
28993
|
+
}
|
|
28994
|
+
if (updates.path !== undefined) {
|
|
28995
|
+
sets.push("path = ?");
|
|
28996
|
+
params.push(updates.path);
|
|
28997
|
+
}
|
|
28998
|
+
if (updates.metadata !== undefined) {
|
|
28999
|
+
sets.push("metadata = ?");
|
|
29000
|
+
params.push(JSON.stringify(updates.metadata));
|
|
29001
|
+
}
|
|
29002
|
+
if (updates.tags !== undefined) {
|
|
29003
|
+
sets.push("tags = ?");
|
|
29004
|
+
params.push(JSON.stringify(updates.tags));
|
|
29005
|
+
}
|
|
29006
|
+
if (updates.status !== undefined) {
|
|
29007
|
+
sets.push("status = ?");
|
|
29008
|
+
params.push(updates.status);
|
|
29009
|
+
}
|
|
29010
|
+
if (updates.repository !== undefined) {
|
|
29011
|
+
sets.push("repository = ?");
|
|
29012
|
+
params.push(updates.repository);
|
|
29013
|
+
}
|
|
29014
|
+
if (updates.settings !== undefined) {
|
|
29015
|
+
sets.push("settings = ?");
|
|
29016
|
+
params.push(JSON.stringify(updates.settings));
|
|
29017
|
+
}
|
|
29018
|
+
if (sets.length === 0) {
|
|
29019
|
+
return parseProject(existing);
|
|
29020
|
+
}
|
|
29021
|
+
params.push(id);
|
|
29022
|
+
const row = db2.prepare(`UPDATE projects SET ${sets.join(", ")} WHERE id = ? RETURNING *`).get(...params);
|
|
29023
|
+
return parseProject(row);
|
|
29024
|
+
}
|
|
29025
|
+
function deleteProject(id) {
|
|
29026
|
+
const db2 = getDb();
|
|
29027
|
+
const spaceCount = db2.prepare("SELECT COUNT(*) as c FROM spaces WHERE project_id = ?").get(id).c;
|
|
29028
|
+
if (spaceCount > 0) {
|
|
29029
|
+
throw new Error(`Cannot delete project: ${spaceCount} space(s) still reference it`);
|
|
29030
|
+
}
|
|
29031
|
+
const result = db2.prepare("DELETE FROM projects WHERE id = ?").run(id);
|
|
28527
29032
|
return result.changes > 0;
|
|
28528
29033
|
}
|
|
28529
29034
|
|
|
28530
29035
|
// src/lib/identity.ts
|
|
28531
29036
|
function resolveIdentity(explicit) {
|
|
28532
|
-
|
|
28533
|
-
|
|
28534
|
-
|
|
28535
|
-
|
|
29037
|
+
const explicitValue = explicit?.trim();
|
|
29038
|
+
if (explicitValue)
|
|
29039
|
+
return explicitValue;
|
|
29040
|
+
const envValue = process.env.CONVERSATIONS_AGENT_ID?.trim();
|
|
29041
|
+
if (envValue)
|
|
29042
|
+
return envValue;
|
|
28536
29043
|
return "user";
|
|
28537
29044
|
}
|
|
28538
29045
|
|
|
29046
|
+
// src/lib/presence.ts
|
|
29047
|
+
var ONLINE_THRESHOLD_SECONDS = 60;
|
|
29048
|
+
function parsePresence(row) {
|
|
29049
|
+
let metadata = null;
|
|
29050
|
+
if (row.metadata) {
|
|
29051
|
+
try {
|
|
29052
|
+
metadata = JSON.parse(row.metadata);
|
|
29053
|
+
} catch {
|
|
29054
|
+
metadata = null;
|
|
29055
|
+
}
|
|
29056
|
+
}
|
|
29057
|
+
const lastSeenAt = row.last_seen_at;
|
|
29058
|
+
const lastSeenMs = new Date(lastSeenAt + "Z").getTime();
|
|
29059
|
+
const nowMs = Date.now();
|
|
29060
|
+
const online = nowMs - lastSeenMs < ONLINE_THRESHOLD_SECONDS * 1000;
|
|
29061
|
+
return {
|
|
29062
|
+
agent: row.agent,
|
|
29063
|
+
status: row.status,
|
|
29064
|
+
last_seen_at: lastSeenAt,
|
|
29065
|
+
online,
|
|
29066
|
+
metadata
|
|
29067
|
+
};
|
|
29068
|
+
}
|
|
29069
|
+
function heartbeat(agent, status, metadata) {
|
|
29070
|
+
const db2 = getDb();
|
|
29071
|
+
const metadataJson = metadata ? JSON.stringify(metadata) : null;
|
|
29072
|
+
const resolvedStatus = status || "online";
|
|
29073
|
+
db2.prepare(`
|
|
29074
|
+
INSERT INTO agent_presence (agent, status, last_seen_at, metadata)
|
|
29075
|
+
VALUES (?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'), ?)
|
|
29076
|
+
ON CONFLICT(agent) DO UPDATE SET
|
|
29077
|
+
status = excluded.status,
|
|
29078
|
+
last_seen_at = excluded.last_seen_at,
|
|
29079
|
+
metadata = excluded.metadata
|
|
29080
|
+
`).run(agent, resolvedStatus, metadataJson);
|
|
29081
|
+
}
|
|
29082
|
+
function listAgents(opts) {
|
|
29083
|
+
const db2 = getDb();
|
|
29084
|
+
let query = "SELECT * FROM agent_presence";
|
|
29085
|
+
const params = [];
|
|
29086
|
+
if (opts?.online_only) {
|
|
29087
|
+
query += " WHERE last_seen_at > strftime('%Y-%m-%dT%H:%M:%f', 'now', '-60 seconds')";
|
|
29088
|
+
}
|
|
29089
|
+
query += " ORDER BY last_seen_at DESC";
|
|
29090
|
+
const rows = db2.prepare(query).all(...params);
|
|
29091
|
+
return rows.map(parsePresence);
|
|
29092
|
+
}
|
|
29093
|
+
|
|
28539
29094
|
// src/mcp/index.ts
|
|
28540
29095
|
var server = new McpServer({
|
|
28541
29096
|
name: "conversations",
|
|
28542
|
-
version: "0.0
|
|
29097
|
+
version: "0.1.0"
|
|
28543
29098
|
});
|
|
28544
29099
|
server.registerTool("send_message", {
|
|
28545
29100
|
title: "Send Message",
|
|
@@ -28556,7 +29111,17 @@ server.registerTool("send_message", {
|
|
|
28556
29111
|
}
|
|
28557
29112
|
}, async ({ to, content, session_id, priority, working_dir, repository, branch, metadata }) => {
|
|
28558
29113
|
const from = resolveIdentity();
|
|
28559
|
-
|
|
29114
|
+
let parsedMetadata;
|
|
29115
|
+
if (metadata) {
|
|
29116
|
+
try {
|
|
29117
|
+
parsedMetadata = JSON.parse(metadata);
|
|
29118
|
+
} catch {
|
|
29119
|
+
return {
|
|
29120
|
+
content: [{ type: "text", text: "Invalid metadata JSON." }],
|
|
29121
|
+
isError: true
|
|
29122
|
+
};
|
|
29123
|
+
}
|
|
29124
|
+
}
|
|
28560
29125
|
const msg = sendMessage({
|
|
28561
29126
|
from,
|
|
28562
29127
|
to,
|
|
@@ -28579,7 +29144,7 @@ server.registerTool("read_messages", {
|
|
|
28579
29144
|
session_id: exports_external.string().optional().describe("Filter by session ID"),
|
|
28580
29145
|
from: exports_external.string().optional().describe("Filter by sender agent ID"),
|
|
28581
29146
|
to: exports_external.string().optional().describe("Filter by recipient agent ID"),
|
|
28582
|
-
|
|
29147
|
+
space: exports_external.string().optional().describe("Filter by space name"),
|
|
28583
29148
|
since: exports_external.string().optional().describe("Messages after this ISO timestamp"),
|
|
28584
29149
|
limit: exports_external.number().optional().describe("Max messages to return"),
|
|
28585
29150
|
unread_only: exports_external.boolean().optional().describe("Only return unread messages")
|
|
@@ -28619,12 +29184,15 @@ server.registerTool("reply", {
|
|
|
28619
29184
|
};
|
|
28620
29185
|
}
|
|
28621
29186
|
const from = resolveIdentity();
|
|
29187
|
+
const space = original.space || (original.session_id?.startsWith("space:") ? original.session_id.slice(6) : undefined);
|
|
29188
|
+
const to = space ? space : original.from_agent === from ? original.to_agent : original.from_agent;
|
|
28622
29189
|
const msg = sendMessage({
|
|
28623
29190
|
from,
|
|
28624
|
-
to
|
|
29191
|
+
to,
|
|
28625
29192
|
content,
|
|
28626
29193
|
session_id: original.session_id,
|
|
28627
|
-
priority
|
|
29194
|
+
priority,
|
|
29195
|
+
space
|
|
28628
29196
|
});
|
|
28629
29197
|
return {
|
|
28630
29198
|
content: [{ type: "text", text: JSON.stringify(msg, null, 2) }]
|
|
@@ -28632,123 +29200,568 @@ server.registerTool("reply", {
|
|
|
28632
29200
|
});
|
|
28633
29201
|
server.registerTool("mark_read", {
|
|
28634
29202
|
title: "Mark Read",
|
|
28635
|
-
description: "Mark message IDs as read for the current agent.",
|
|
29203
|
+
description: "Mark message IDs as read for the current agent. Set 'all' to true to mark all unread messages as read.",
|
|
28636
29204
|
inputSchema: {
|
|
28637
|
-
ids: exports_external.array(exports_external.number()).describe("Message IDs to mark as read")
|
|
29205
|
+
ids: exports_external.array(exports_external.number()).optional().describe("Message IDs to mark as read"),
|
|
29206
|
+
all: exports_external.boolean().optional().describe("Mark all unread messages as read")
|
|
28638
29207
|
}
|
|
28639
|
-
}, async ({ ids }) => {
|
|
29208
|
+
}, async ({ ids, all }) => {
|
|
28640
29209
|
const agent = resolveIdentity();
|
|
28641
|
-
|
|
29210
|
+
let count;
|
|
29211
|
+
if (all) {
|
|
29212
|
+
count = markAllRead(agent);
|
|
29213
|
+
} else if (ids && ids.length > 0) {
|
|
29214
|
+
count = markRead(ids, agent);
|
|
29215
|
+
} else {
|
|
29216
|
+
return {
|
|
29217
|
+
content: [{ type: "text", text: "Provide message IDs or set 'all' to true." }],
|
|
29218
|
+
isError: true
|
|
29219
|
+
};
|
|
29220
|
+
}
|
|
28642
29221
|
return {
|
|
28643
29222
|
content: [{ type: "text", text: JSON.stringify({ marked_read: count }, null, 2) }]
|
|
28644
29223
|
};
|
|
28645
29224
|
});
|
|
28646
|
-
server.registerTool("
|
|
28647
|
-
title: "
|
|
28648
|
-
description: "
|
|
29225
|
+
server.registerTool("search_messages", {
|
|
29226
|
+
title: "Search Messages",
|
|
29227
|
+
description: "Full-text search across message content. Returns matching messages ordered by newest first.",
|
|
28649
29228
|
inputSchema: {
|
|
28650
|
-
|
|
28651
|
-
|
|
29229
|
+
query: exports_external.string().describe("Search query string"),
|
|
29230
|
+
space: exports_external.string().optional().describe("Filter by space name"),
|
|
29231
|
+
from: exports_external.string().optional().describe("Filter by sender agent ID"),
|
|
29232
|
+
to: exports_external.string().optional().describe("Filter by recipient agent ID"),
|
|
29233
|
+
limit: exports_external.number().optional().describe("Max results to return (default 50)")
|
|
28652
29234
|
}
|
|
28653
|
-
}, async ({
|
|
29235
|
+
}, async ({ query, space, from, to, limit }) => {
|
|
29236
|
+
const messages = searchMessages({ query, space, from, to, limit });
|
|
29237
|
+
return {
|
|
29238
|
+
content: [{ type: "text", text: JSON.stringify(messages, null, 2) }]
|
|
29239
|
+
};
|
|
29240
|
+
});
|
|
29241
|
+
server.registerTool("export_messages", {
|
|
29242
|
+
title: "Export Messages",
|
|
29243
|
+
description: "Export messages as JSON or CSV with optional filters.",
|
|
29244
|
+
inputSchema: {
|
|
29245
|
+
space: exports_external.string().optional().describe("Filter by space name"),
|
|
29246
|
+
session_id: exports_external.string().optional().describe("Filter by session ID"),
|
|
29247
|
+
from: exports_external.string().optional().describe("Filter by sender agent ID"),
|
|
29248
|
+
since: exports_external.string().optional().describe("Messages after this ISO date"),
|
|
29249
|
+
until: exports_external.string().optional().describe("Messages before this ISO date"),
|
|
29250
|
+
format: exports_external.enum(["json", "csv"]).optional().describe("Output format (default: json)")
|
|
29251
|
+
}
|
|
29252
|
+
}, async ({ space, session_id, from, since, until, format }) => {
|
|
29253
|
+
const result = exportMessages({ space, session_id, from, since, until, format });
|
|
29254
|
+
return {
|
|
29255
|
+
content: [{ type: "text", text: result }]
|
|
29256
|
+
};
|
|
29257
|
+
});
|
|
29258
|
+
server.registerTool("create_space", {
|
|
29259
|
+
title: "Create Space",
|
|
29260
|
+
description: "Create a new space. The creator is auto-joined. Spaces can be nested (max 3 levels) and associated with a project.",
|
|
29261
|
+
inputSchema: {
|
|
29262
|
+
name: exports_external.string().describe("Space name (e.g. 'deployments', 'code-review')"),
|
|
29263
|
+
description: exports_external.string().optional().describe("Space description"),
|
|
29264
|
+
parent_id: exports_external.string().optional().describe("Parent space name for nesting (max 3 levels deep)"),
|
|
29265
|
+
project_id: exports_external.string().optional().describe("Project ID to associate this space with")
|
|
29266
|
+
}
|
|
29267
|
+
}, async ({ name, description, parent_id, project_id }) => {
|
|
28654
29268
|
const agent = resolveIdentity();
|
|
28655
29269
|
try {
|
|
28656
|
-
const
|
|
29270
|
+
const sp = createSpace(name, agent, { description, parent_id, project_id });
|
|
28657
29271
|
return {
|
|
28658
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
29272
|
+
content: [{ type: "text", text: JSON.stringify(sp, null, 2) }]
|
|
28659
29273
|
};
|
|
28660
29274
|
} catch (e) {
|
|
28661
29275
|
if (e.message?.includes("UNIQUE constraint")) {
|
|
28662
29276
|
return {
|
|
28663
|
-
content: [{ type: "text", text: `
|
|
29277
|
+
content: [{ type: "text", text: `Space #${name} already exists` }],
|
|
28664
29278
|
isError: true
|
|
28665
29279
|
};
|
|
28666
29280
|
}
|
|
28667
|
-
|
|
29281
|
+
return {
|
|
29282
|
+
content: [{ type: "text", text: e.message }],
|
|
29283
|
+
isError: true
|
|
29284
|
+
};
|
|
28668
29285
|
}
|
|
28669
29286
|
});
|
|
28670
|
-
server.registerTool("
|
|
28671
|
-
title: "List
|
|
28672
|
-
description: "List all available
|
|
28673
|
-
|
|
28674
|
-
|
|
29287
|
+
server.registerTool("list_spaces", {
|
|
29288
|
+
title: "List Spaces",
|
|
29289
|
+
description: "List all available spaces with member and message counts. Can filter by project or parent. Archived spaces are excluded by default.",
|
|
29290
|
+
inputSchema: {
|
|
29291
|
+
project_id: exports_external.string().optional().describe("Filter by project ID"),
|
|
29292
|
+
parent_id: exports_external.string().optional().describe("Filter by parent space name. Use 'null' for top-level only."),
|
|
29293
|
+
include_archived: exports_external.boolean().optional().describe("Include archived spaces (default: false)")
|
|
29294
|
+
}
|
|
29295
|
+
}, async ({ project_id, parent_id, include_archived }) => {
|
|
29296
|
+
const opts = {};
|
|
29297
|
+
if (project_id)
|
|
29298
|
+
opts.project_id = project_id;
|
|
29299
|
+
if (parent_id === "null") {
|
|
29300
|
+
opts.parent_id = null;
|
|
29301
|
+
} else if (parent_id) {
|
|
29302
|
+
opts.parent_id = parent_id;
|
|
29303
|
+
}
|
|
29304
|
+
if (include_archived)
|
|
29305
|
+
opts.include_archived = true;
|
|
29306
|
+
const spaces = listSpaces(opts);
|
|
28675
29307
|
return {
|
|
28676
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
29308
|
+
content: [{ type: "text", text: JSON.stringify(spaces, null, 2) }]
|
|
28677
29309
|
};
|
|
28678
29310
|
});
|
|
28679
|
-
server.registerTool("
|
|
28680
|
-
title: "Send to
|
|
28681
|
-
description: "Send a message to a
|
|
29311
|
+
server.registerTool("send_to_space", {
|
|
29312
|
+
title: "Send to Space",
|
|
29313
|
+
description: "Send a message to a space. All members can see it.",
|
|
28682
29314
|
inputSchema: {
|
|
28683
|
-
|
|
29315
|
+
space: exports_external.string().describe("Space name"),
|
|
28684
29316
|
content: exports_external.string().describe("Message content"),
|
|
28685
29317
|
priority: exports_external.enum(["low", "normal", "high", "urgent"]).optional().describe("Message priority")
|
|
28686
29318
|
}
|
|
28687
|
-
}, async ({
|
|
29319
|
+
}, async ({ space, content, priority }) => {
|
|
28688
29320
|
const from = resolveIdentity();
|
|
28689
|
-
const
|
|
28690
|
-
if (!
|
|
29321
|
+
const sp = getSpace(space);
|
|
29322
|
+
if (!sp) {
|
|
28691
29323
|
return {
|
|
28692
|
-
content: [{ type: "text", text: `
|
|
29324
|
+
content: [{ type: "text", text: `Space #${space} not found` }],
|
|
28693
29325
|
isError: true
|
|
28694
29326
|
};
|
|
28695
29327
|
}
|
|
28696
29328
|
const msg = sendMessage({
|
|
28697
29329
|
from,
|
|
28698
|
-
to:
|
|
29330
|
+
to: space,
|
|
28699
29331
|
content,
|
|
28700
|
-
|
|
28701
|
-
session_id: `
|
|
29332
|
+
space,
|
|
29333
|
+
session_id: `space:${space}`,
|
|
28702
29334
|
priority
|
|
28703
29335
|
});
|
|
28704
29336
|
return {
|
|
28705
29337
|
content: [{ type: "text", text: JSON.stringify(msg, null, 2) }]
|
|
28706
29338
|
};
|
|
28707
29339
|
});
|
|
28708
|
-
server.registerTool("
|
|
28709
|
-
title: "Read
|
|
28710
|
-
description: "Read messages from a
|
|
29340
|
+
server.registerTool("read_space", {
|
|
29341
|
+
title: "Read Space",
|
|
29342
|
+
description: "Read messages from a space.",
|
|
28711
29343
|
inputSchema: {
|
|
28712
|
-
|
|
29344
|
+
space: exports_external.string().describe("Space name"),
|
|
28713
29345
|
since: exports_external.string().optional().describe("Messages after this ISO timestamp"),
|
|
28714
29346
|
limit: exports_external.number().optional().describe("Max messages to return")
|
|
28715
29347
|
}
|
|
28716
|
-
}, async ({
|
|
28717
|
-
const messages = readMessages({
|
|
29348
|
+
}, async ({ space, since, limit }) => {
|
|
29349
|
+
const messages = readMessages({ space, since, limit });
|
|
28718
29350
|
return {
|
|
28719
29351
|
content: [{ type: "text", text: JSON.stringify(messages, null, 2) }]
|
|
28720
29352
|
};
|
|
28721
29353
|
});
|
|
28722
|
-
server.registerTool("
|
|
28723
|
-
title: "Join
|
|
28724
|
-
description: "Join a
|
|
29354
|
+
server.registerTool("join_space", {
|
|
29355
|
+
title: "Join Space",
|
|
29356
|
+
description: "Join a space to receive messages.",
|
|
28725
29357
|
inputSchema: {
|
|
28726
|
-
|
|
29358
|
+
space: exports_external.string().describe("Space name to join")
|
|
28727
29359
|
}
|
|
28728
|
-
}, async ({
|
|
29360
|
+
}, async ({ space }) => {
|
|
28729
29361
|
const agent = resolveIdentity();
|
|
28730
|
-
const ok =
|
|
29362
|
+
const ok = joinSpace(space, agent);
|
|
28731
29363
|
if (!ok) {
|
|
28732
29364
|
return {
|
|
28733
|
-
content: [{ type: "text", text: `
|
|
29365
|
+
content: [{ type: "text", text: `Space #${space} not found` }],
|
|
29366
|
+
isError: true
|
|
29367
|
+
};
|
|
29368
|
+
}
|
|
29369
|
+
return {
|
|
29370
|
+
content: [{ type: "text", text: JSON.stringify({ space, agent, joined: true }, null, 2) }]
|
|
29371
|
+
};
|
|
29372
|
+
});
|
|
29373
|
+
server.registerTool("leave_space", {
|
|
29374
|
+
title: "Leave Space",
|
|
29375
|
+
description: "Leave a space.",
|
|
29376
|
+
inputSchema: {
|
|
29377
|
+
space: exports_external.string().describe("Space name to leave")
|
|
29378
|
+
}
|
|
29379
|
+
}, async ({ space }) => {
|
|
29380
|
+
const agent = resolveIdentity();
|
|
29381
|
+
const left = leaveSpace(space, agent);
|
|
29382
|
+
return {
|
|
29383
|
+
content: [{ type: "text", text: JSON.stringify({ space, agent, left }, null, 2) }]
|
|
29384
|
+
};
|
|
29385
|
+
});
|
|
29386
|
+
server.registerTool("update_space", {
|
|
29387
|
+
title: "Update Space",
|
|
29388
|
+
description: "Update a space's description, parent, or project association.",
|
|
29389
|
+
inputSchema: {
|
|
29390
|
+
name: exports_external.string().describe("Space name to update"),
|
|
29391
|
+
description: exports_external.string().optional().describe("New description"),
|
|
29392
|
+
parent_id: exports_external.string().optional().describe("New parent space name (use 'null' to remove parent)"),
|
|
29393
|
+
project_id: exports_external.string().optional().describe("New project ID (use 'null' to remove project)")
|
|
29394
|
+
}
|
|
29395
|
+
}, async ({ name, description, parent_id, project_id }) => {
|
|
29396
|
+
const updates = {};
|
|
29397
|
+
if (description !== undefined)
|
|
29398
|
+
updates.description = description;
|
|
29399
|
+
if (parent_id !== undefined)
|
|
29400
|
+
updates.parent_id = parent_id === "null" ? null : parent_id;
|
|
29401
|
+
if (project_id !== undefined)
|
|
29402
|
+
updates.project_id = project_id === "null" ? null : project_id;
|
|
29403
|
+
try {
|
|
29404
|
+
const sp = updateSpace(name, updates);
|
|
29405
|
+
return {
|
|
29406
|
+
content: [{ type: "text", text: JSON.stringify(sp, null, 2) }]
|
|
29407
|
+
};
|
|
29408
|
+
} catch (e) {
|
|
29409
|
+
return {
|
|
29410
|
+
content: [{ type: "text", text: e.message }],
|
|
29411
|
+
isError: true
|
|
29412
|
+
};
|
|
29413
|
+
}
|
|
29414
|
+
});
|
|
29415
|
+
server.registerTool("archive_space", {
|
|
29416
|
+
title: "Archive Space",
|
|
29417
|
+
description: "Archive a space. Archived spaces are hidden from list by default.",
|
|
29418
|
+
inputSchema: {
|
|
29419
|
+
name: exports_external.string().describe("Space name to archive")
|
|
29420
|
+
}
|
|
29421
|
+
}, async ({ name }) => {
|
|
29422
|
+
try {
|
|
29423
|
+
const sp = archiveSpace(name);
|
|
29424
|
+
return {
|
|
29425
|
+
content: [{ type: "text", text: JSON.stringify(sp, null, 2) }]
|
|
29426
|
+
};
|
|
29427
|
+
} catch (e) {
|
|
29428
|
+
return {
|
|
29429
|
+
content: [{ type: "text", text: e.message }],
|
|
29430
|
+
isError: true
|
|
29431
|
+
};
|
|
29432
|
+
}
|
|
29433
|
+
});
|
|
29434
|
+
server.registerTool("unarchive_space", {
|
|
29435
|
+
title: "Unarchive Space",
|
|
29436
|
+
description: "Unarchive a previously archived space.",
|
|
29437
|
+
inputSchema: {
|
|
29438
|
+
name: exports_external.string().describe("Space name to unarchive")
|
|
29439
|
+
}
|
|
29440
|
+
}, async ({ name }) => {
|
|
29441
|
+
try {
|
|
29442
|
+
const sp = unarchiveSpace(name);
|
|
29443
|
+
return {
|
|
29444
|
+
content: [{ type: "text", text: JSON.stringify(sp, null, 2) }]
|
|
29445
|
+
};
|
|
29446
|
+
} catch (e) {
|
|
29447
|
+
return {
|
|
29448
|
+
content: [{ type: "text", text: e.message }],
|
|
29449
|
+
isError: true
|
|
29450
|
+
};
|
|
29451
|
+
}
|
|
29452
|
+
});
|
|
29453
|
+
server.registerTool("create_project", {
|
|
29454
|
+
title: "Create Project",
|
|
29455
|
+
description: "Create a new project. Projects organize spaces and provide context for agent collaboration.",
|
|
29456
|
+
inputSchema: {
|
|
29457
|
+
name: exports_external.string().describe("Project name (unique)"),
|
|
29458
|
+
description: exports_external.string().optional().describe("Project description"),
|
|
29459
|
+
path: exports_external.string().optional().describe("Absolute path to project on disk"),
|
|
29460
|
+
repository: exports_external.string().optional().describe("Repository URL"),
|
|
29461
|
+
tags: exports_external.string().optional().describe(`JSON array of tags (e.g. '["backend", "api"]')`),
|
|
29462
|
+
metadata: exports_external.string().optional().describe("JSON metadata string"),
|
|
29463
|
+
settings: exports_external.string().optional().describe("JSON settings string")
|
|
29464
|
+
}
|
|
29465
|
+
}, async ({ name, description, path, repository, tags, metadata, settings }) => {
|
|
29466
|
+
const agent = resolveIdentity();
|
|
29467
|
+
let parsedTags;
|
|
29468
|
+
if (tags) {
|
|
29469
|
+
try {
|
|
29470
|
+
parsedTags = JSON.parse(tags);
|
|
29471
|
+
} catch {
|
|
29472
|
+
return {
|
|
29473
|
+
content: [{ type: "text", text: "Invalid tags JSON. Expected array of strings." }],
|
|
29474
|
+
isError: true
|
|
29475
|
+
};
|
|
29476
|
+
}
|
|
29477
|
+
}
|
|
29478
|
+
let parsedMetadata;
|
|
29479
|
+
if (metadata) {
|
|
29480
|
+
try {
|
|
29481
|
+
parsedMetadata = JSON.parse(metadata);
|
|
29482
|
+
} catch {
|
|
29483
|
+
return {
|
|
29484
|
+
content: [{ type: "text", text: "Invalid metadata JSON." }],
|
|
29485
|
+
isError: true
|
|
29486
|
+
};
|
|
29487
|
+
}
|
|
29488
|
+
}
|
|
29489
|
+
let parsedSettings;
|
|
29490
|
+
if (settings) {
|
|
29491
|
+
try {
|
|
29492
|
+
parsedSettings = JSON.parse(settings);
|
|
29493
|
+
} catch {
|
|
29494
|
+
return {
|
|
29495
|
+
content: [{ type: "text", text: "Invalid settings JSON." }],
|
|
29496
|
+
isError: true
|
|
29497
|
+
};
|
|
29498
|
+
}
|
|
29499
|
+
}
|
|
29500
|
+
try {
|
|
29501
|
+
const project = createProject({
|
|
29502
|
+
name,
|
|
29503
|
+
created_by: agent,
|
|
29504
|
+
description,
|
|
29505
|
+
path,
|
|
29506
|
+
repository,
|
|
29507
|
+
tags: parsedTags,
|
|
29508
|
+
metadata: parsedMetadata,
|
|
29509
|
+
settings: parsedSettings
|
|
29510
|
+
});
|
|
29511
|
+
return {
|
|
29512
|
+
content: [{ type: "text", text: JSON.stringify(project, null, 2) }]
|
|
29513
|
+
};
|
|
29514
|
+
} catch (e) {
|
|
29515
|
+
if (e.message?.includes("UNIQUE constraint")) {
|
|
29516
|
+
return {
|
|
29517
|
+
content: [{ type: "text", text: `Project "${name}" already exists` }],
|
|
29518
|
+
isError: true
|
|
29519
|
+
};
|
|
29520
|
+
}
|
|
29521
|
+
return {
|
|
29522
|
+
content: [{ type: "text", text: e.message }],
|
|
29523
|
+
isError: true
|
|
29524
|
+
};
|
|
29525
|
+
}
|
|
29526
|
+
});
|
|
29527
|
+
server.registerTool("list_projects", {
|
|
29528
|
+
title: "List Projects",
|
|
29529
|
+
description: "List all registered projects.",
|
|
29530
|
+
inputSchema: {
|
|
29531
|
+
status: exports_external.enum(["active", "archived"]).optional().describe("Filter by project status")
|
|
29532
|
+
}
|
|
29533
|
+
}, async ({ status }) => {
|
|
29534
|
+
const projects = listProjects(status ? { status } : undefined);
|
|
29535
|
+
return {
|
|
29536
|
+
content: [{ type: "text", text: JSON.stringify(projects, null, 2) }]
|
|
29537
|
+
};
|
|
29538
|
+
});
|
|
29539
|
+
server.registerTool("get_project", {
|
|
29540
|
+
title: "Get Project",
|
|
29541
|
+
description: "Get full details of a project by ID or name.",
|
|
29542
|
+
inputSchema: {
|
|
29543
|
+
id: exports_external.string().describe("Project ID (UUID) or name")
|
|
29544
|
+
}
|
|
29545
|
+
}, async ({ id }) => {
|
|
29546
|
+
let project = getProject(id);
|
|
29547
|
+
if (!project) {
|
|
29548
|
+
project = getProjectByName(id);
|
|
29549
|
+
}
|
|
29550
|
+
if (!project) {
|
|
29551
|
+
return {
|
|
29552
|
+
content: [{ type: "text", text: `Project "${id}" not found` }],
|
|
29553
|
+
isError: true
|
|
29554
|
+
};
|
|
29555
|
+
}
|
|
29556
|
+
return {
|
|
29557
|
+
content: [{ type: "text", text: JSON.stringify(project, null, 2) }]
|
|
29558
|
+
};
|
|
29559
|
+
});
|
|
29560
|
+
server.registerTool("update_project", {
|
|
29561
|
+
title: "Update Project",
|
|
29562
|
+
description: "Update a project's fields.",
|
|
29563
|
+
inputSchema: {
|
|
29564
|
+
id: exports_external.string().describe("Project ID (UUID)"),
|
|
29565
|
+
name: exports_external.string().optional().describe("New project name"),
|
|
29566
|
+
description: exports_external.string().optional().describe("New description"),
|
|
29567
|
+
path: exports_external.string().optional().describe("New path"),
|
|
29568
|
+
status: exports_external.enum(["active", "archived"]).optional().describe("New status"),
|
|
29569
|
+
repository: exports_external.string().optional().describe("New repository URL"),
|
|
29570
|
+
tags: exports_external.string().optional().describe("JSON array of tags"),
|
|
29571
|
+
metadata: exports_external.string().optional().describe("JSON metadata string"),
|
|
29572
|
+
settings: exports_external.string().optional().describe("JSON settings string")
|
|
29573
|
+
}
|
|
29574
|
+
}, async ({ id, name, description, path, status, repository, tags, metadata, settings }) => {
|
|
29575
|
+
const updates = {};
|
|
29576
|
+
if (name !== undefined)
|
|
29577
|
+
updates.name = name;
|
|
29578
|
+
if (description !== undefined)
|
|
29579
|
+
updates.description = description;
|
|
29580
|
+
if (path !== undefined)
|
|
29581
|
+
updates.path = path;
|
|
29582
|
+
if (status !== undefined)
|
|
29583
|
+
updates.status = status;
|
|
29584
|
+
if (repository !== undefined)
|
|
29585
|
+
updates.repository = repository;
|
|
29586
|
+
if (tags) {
|
|
29587
|
+
try {
|
|
29588
|
+
updates.tags = JSON.parse(tags);
|
|
29589
|
+
} catch {
|
|
29590
|
+
return {
|
|
29591
|
+
content: [{ type: "text", text: "Invalid tags JSON." }],
|
|
29592
|
+
isError: true
|
|
29593
|
+
};
|
|
29594
|
+
}
|
|
29595
|
+
}
|
|
29596
|
+
if (metadata) {
|
|
29597
|
+
try {
|
|
29598
|
+
updates.metadata = JSON.parse(metadata);
|
|
29599
|
+
} catch {
|
|
29600
|
+
return {
|
|
29601
|
+
content: [{ type: "text", text: "Invalid metadata JSON." }],
|
|
29602
|
+
isError: true
|
|
29603
|
+
};
|
|
29604
|
+
}
|
|
29605
|
+
}
|
|
29606
|
+
if (settings) {
|
|
29607
|
+
try {
|
|
29608
|
+
updates.settings = JSON.parse(settings);
|
|
29609
|
+
} catch {
|
|
29610
|
+
return {
|
|
29611
|
+
content: [{ type: "text", text: "Invalid settings JSON." }],
|
|
29612
|
+
isError: true
|
|
29613
|
+
};
|
|
29614
|
+
}
|
|
29615
|
+
}
|
|
29616
|
+
try {
|
|
29617
|
+
const project = updateProject(id, updates);
|
|
29618
|
+
return {
|
|
29619
|
+
content: [{ type: "text", text: JSON.stringify(project, null, 2) }]
|
|
29620
|
+
};
|
|
29621
|
+
} catch (e) {
|
|
29622
|
+
return {
|
|
29623
|
+
content: [{ type: "text", text: e.message }],
|
|
29624
|
+
isError: true
|
|
29625
|
+
};
|
|
29626
|
+
}
|
|
29627
|
+
});
|
|
29628
|
+
server.registerTool("delete_project", {
|
|
29629
|
+
title: "Delete Project",
|
|
29630
|
+
description: "Delete a project permanently. Fails if spaces still reference it.",
|
|
29631
|
+
inputSchema: {
|
|
29632
|
+
id: exports_external.string().describe("Project ID (UUID)")
|
|
29633
|
+
}
|
|
29634
|
+
}, async ({ id }) => {
|
|
29635
|
+
try {
|
|
29636
|
+
const deleted = deleteProject(id);
|
|
29637
|
+
if (!deleted) {
|
|
29638
|
+
return {
|
|
29639
|
+
content: [{ type: "text", text: `Project "${id}" not found` }],
|
|
29640
|
+
isError: true
|
|
29641
|
+
};
|
|
29642
|
+
}
|
|
29643
|
+
return {
|
|
29644
|
+
content: [{ type: "text", text: JSON.stringify({ id, deleted: true }, null, 2) }]
|
|
29645
|
+
};
|
|
29646
|
+
} catch (e) {
|
|
29647
|
+
return {
|
|
29648
|
+
content: [{ type: "text", text: e.message }],
|
|
28734
29649
|
isError: true
|
|
28735
29650
|
};
|
|
28736
29651
|
}
|
|
29652
|
+
});
|
|
29653
|
+
server.registerTool("delete_message", {
|
|
29654
|
+
title: "Delete Message",
|
|
29655
|
+
description: "Delete a message. Only the sender can delete their own messages. The agent is auto-resolved.",
|
|
29656
|
+
inputSchema: {
|
|
29657
|
+
id: exports_external.number().describe("Message ID to delete")
|
|
29658
|
+
}
|
|
29659
|
+
}, async ({ id }) => {
|
|
29660
|
+
const agent = resolveIdentity();
|
|
29661
|
+
const deleted = deleteMessage(id, agent);
|
|
29662
|
+
if (!deleted) {
|
|
29663
|
+
return {
|
|
29664
|
+
content: [{ type: "text", text: `Message #${id} not found or not your message` }],
|
|
29665
|
+
isError: true
|
|
29666
|
+
};
|
|
29667
|
+
}
|
|
29668
|
+
return {
|
|
29669
|
+
content: [{ type: "text", text: JSON.stringify({ deleted: true }, null, 2) }]
|
|
29670
|
+
};
|
|
29671
|
+
});
|
|
29672
|
+
server.registerTool("edit_message", {
|
|
29673
|
+
title: "Edit Message",
|
|
29674
|
+
description: "Edit a message's content. Only the sender can edit their own messages. The agent is auto-resolved.",
|
|
29675
|
+
inputSchema: {
|
|
29676
|
+
id: exports_external.number().describe("Message ID to edit"),
|
|
29677
|
+
content: exports_external.string().describe("New message content")
|
|
29678
|
+
}
|
|
29679
|
+
}, async ({ id, content }) => {
|
|
29680
|
+
const agent = resolveIdentity();
|
|
29681
|
+
const msg = editMessage(id, agent, content);
|
|
29682
|
+
if (!msg) {
|
|
29683
|
+
return {
|
|
29684
|
+
content: [{ type: "text", text: `Message #${id} not found or not your message` }],
|
|
29685
|
+
isError: true
|
|
29686
|
+
};
|
|
29687
|
+
}
|
|
29688
|
+
return {
|
|
29689
|
+
content: [{ type: "text", text: JSON.stringify(msg, null, 2) }]
|
|
29690
|
+
};
|
|
29691
|
+
});
|
|
29692
|
+
server.registerTool("pin_message", {
|
|
29693
|
+
title: "Pin Message",
|
|
29694
|
+
description: "Pin a message. Pinned messages can be retrieved with get_pinned_messages.",
|
|
29695
|
+
inputSchema: {
|
|
29696
|
+
id: exports_external.number().describe("Message ID to pin")
|
|
29697
|
+
}
|
|
29698
|
+
}, async ({ id }) => {
|
|
29699
|
+
const msg = pinMessage(id);
|
|
29700
|
+
if (!msg) {
|
|
29701
|
+
return {
|
|
29702
|
+
content: [{ type: "text", text: `Message #${id} not found` }],
|
|
29703
|
+
isError: true
|
|
29704
|
+
};
|
|
29705
|
+
}
|
|
29706
|
+
return {
|
|
29707
|
+
content: [{ type: "text", text: JSON.stringify(msg, null, 2) }]
|
|
29708
|
+
};
|
|
29709
|
+
});
|
|
29710
|
+
server.registerTool("unpin_message", {
|
|
29711
|
+
title: "Unpin Message",
|
|
29712
|
+
description: "Unpin a previously pinned message.",
|
|
29713
|
+
inputSchema: {
|
|
29714
|
+
id: exports_external.number().describe("Message ID to unpin")
|
|
29715
|
+
}
|
|
29716
|
+
}, async ({ id }) => {
|
|
29717
|
+
const msg = unpinMessage(id);
|
|
29718
|
+
if (!msg) {
|
|
29719
|
+
return {
|
|
29720
|
+
content: [{ type: "text", text: `Message #${id} not found` }],
|
|
29721
|
+
isError: true
|
|
29722
|
+
};
|
|
29723
|
+
}
|
|
29724
|
+
return {
|
|
29725
|
+
content: [{ type: "text", text: JSON.stringify(msg, null, 2) }]
|
|
29726
|
+
};
|
|
29727
|
+
});
|
|
29728
|
+
server.registerTool("get_pinned_messages", {
|
|
29729
|
+
title: "Get Pinned Messages",
|
|
29730
|
+
description: "Retrieve pinned messages, optionally filtered by space or session.",
|
|
29731
|
+
inputSchema: {
|
|
29732
|
+
space: exports_external.string().optional().describe("Filter by space name"),
|
|
29733
|
+
session_id: exports_external.string().optional().describe("Filter by session ID"),
|
|
29734
|
+
limit: exports_external.number().optional().describe("Max messages to return")
|
|
29735
|
+
}
|
|
29736
|
+
}, async ({ space, session_id, limit }) => {
|
|
29737
|
+
const messages = getPinnedMessages({ space, session_id, limit });
|
|
28737
29738
|
return {
|
|
28738
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
29739
|
+
content: [{ type: "text", text: JSON.stringify(messages, null, 2) }]
|
|
28739
29740
|
};
|
|
28740
29741
|
});
|
|
28741
|
-
server.registerTool("
|
|
28742
|
-
title: "
|
|
28743
|
-
description: "
|
|
29742
|
+
server.registerTool("heartbeat", {
|
|
29743
|
+
title: "Heartbeat",
|
|
29744
|
+
description: "Send a heartbeat to indicate agent is alive. Auto-resolves agent from CONVERSATIONS_AGENT_ID env var. Optionally set a status.",
|
|
28744
29745
|
inputSchema: {
|
|
28745
|
-
|
|
29746
|
+
status: exports_external.string().optional().describe("Agent status (e.g. 'online', 'busy', 'idle'). Defaults to 'online'.")
|
|
28746
29747
|
}
|
|
28747
|
-
}, async ({
|
|
29748
|
+
}, async ({ status }) => {
|
|
28748
29749
|
const agent = resolveIdentity();
|
|
28749
|
-
|
|
29750
|
+
heartbeat(agent, status);
|
|
29751
|
+
return {
|
|
29752
|
+
content: [{ type: "text", text: JSON.stringify({ agent, status: status || "online", heartbeat: true }, null, 2) }]
|
|
29753
|
+
};
|
|
29754
|
+
});
|
|
29755
|
+
server.registerTool("list_agents", {
|
|
29756
|
+
title: "List Agents",
|
|
29757
|
+
description: "List all agents with their presence status. Returns agent name, status, last seen time, and whether they are online.",
|
|
29758
|
+
inputSchema: {
|
|
29759
|
+
online_only: exports_external.boolean().optional().describe("Only return agents that are currently online (seen within last 60 seconds)")
|
|
29760
|
+
}
|
|
29761
|
+
}, async ({ online_only }) => {
|
|
29762
|
+
const agents = listAgents({ online_only });
|
|
28750
29763
|
return {
|
|
28751
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
29764
|
+
content: [{ type: "text", text: JSON.stringify(agents, null, 2) }]
|
|
28752
29765
|
};
|
|
28753
29766
|
});
|
|
28754
29767
|
async function startMcpServer() {
|