@hasna/conversations 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -1974,6 +1974,17 @@ function getDb() {
1974
1974
  metadata TEXT
1975
1975
  )
1976
1976
  `);
1977
+ db.exec(`
1978
+ CREATE TABLE IF NOT EXISTS reactions (
1979
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1980
+ message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
1981
+ agent TEXT NOT NULL,
1982
+ emoji TEXT NOT NULL,
1983
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
1984
+ UNIQUE(message_id, agent, emoji)
1985
+ )
1986
+ `);
1987
+ db.exec("CREATE INDEX IF NOT EXISTS idx_reactions_message ON reactions(message_id)");
1977
1988
  const existingTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
1978
1989
  const tableNames = existingTables.map((t) => t.name);
1979
1990
  if (tableNames.includes("channels") && tableNames.includes("spaces")) {
@@ -2031,6 +2042,46 @@ function getDb() {
2031
2042
  db.exec("ALTER TABLE messages ADD COLUMN blocking INTEGER NOT NULL DEFAULT 0");
2032
2043
  db.exec("CREATE INDEX IF NOT EXISTS idx_messages_blocking ON messages(blocking)");
2033
2044
  }
2045
+ if (!colNames2.includes("attachments")) {
2046
+ db.exec("ALTER TABLE messages ADD COLUMN attachments TEXT");
2047
+ }
2048
+ if (!colNames2.includes("reply_to")) {
2049
+ db.exec("ALTER TABLE messages ADD COLUMN reply_to INTEGER REFERENCES messages(id)");
2050
+ db.exec("CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to)");
2051
+ }
2052
+ const ftsExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='messages_fts'").get();
2053
+ if (!ftsExists) {
2054
+ db.exec(`
2055
+ CREATE VIRTUAL TABLE messages_fts USING fts5(
2056
+ content, from_agent, to_agent, space,
2057
+ content_rowid='id', content='messages'
2058
+ )
2059
+ `);
2060
+ db.exec(`
2061
+ INSERT INTO messages_fts(rowid, content, from_agent, to_agent, space)
2062
+ SELECT id, content, from_agent, to_agent, space FROM messages
2063
+ `);
2064
+ db.exec(`
2065
+ CREATE TRIGGER IF NOT EXISTS messages_fts_insert AFTER INSERT ON messages BEGIN
2066
+ INSERT INTO messages_fts(rowid, content, from_agent, to_agent, space)
2067
+ VALUES (new.id, new.content, new.from_agent, new.to_agent, new.space);
2068
+ END
2069
+ `);
2070
+ db.exec(`
2071
+ CREATE TRIGGER IF NOT EXISTS messages_fts_delete AFTER DELETE ON messages BEGIN
2072
+ INSERT INTO messages_fts(messages_fts, rowid, content, from_agent, to_agent, space)
2073
+ VALUES ('delete', old.id, old.content, old.from_agent, old.to_agent, old.space);
2074
+ END
2075
+ `);
2076
+ db.exec(`
2077
+ CREATE TRIGGER IF NOT EXISTS messages_fts_update AFTER UPDATE OF content ON messages BEGIN
2078
+ INSERT INTO messages_fts(messages_fts, rowid, content, from_agent, to_agent, space)
2079
+ VALUES ('delete', old.id, old.content, old.from_agent, old.to_agent, old.space);
2080
+ INSERT INTO messages_fts(rowid, content, from_agent, to_agent, space)
2081
+ VALUES (new.id, new.content, new.from_agent, new.to_agent, new.space);
2082
+ END
2083
+ `);
2084
+ }
2034
2085
  return db;
2035
2086
  }
2036
2087
  function closeDb() {
@@ -2042,8 +2093,84 @@ function closeDb() {
2042
2093
  var db = null;
2043
2094
  var init_db = () => {};
2044
2095
 
2096
+ // src/lib/webhooks.ts
2097
+ import { readFileSync } from "fs";
2098
+ import { join as join2 } from "path";
2099
+ import { homedir as homedir2 } from "os";
2100
+ function getConfigPath() {
2101
+ return process.env.CONVERSATIONS_CONFIG_PATH || join2(homedir2(), ".conversations", "config.json");
2102
+ }
2103
+ function loadConfig() {
2104
+ const now = Date.now();
2105
+ if (cachedConfig && now - configLoadedAt < CONFIG_CACHE_MS)
2106
+ return cachedConfig;
2107
+ try {
2108
+ const raw = readFileSync(getConfigPath(), "utf-8");
2109
+ cachedConfig = JSON.parse(raw);
2110
+ configLoadedAt = now;
2111
+ return cachedConfig;
2112
+ } catch {
2113
+ cachedConfig = {};
2114
+ configLoadedAt = now;
2115
+ return cachedConfig;
2116
+ }
2117
+ }
2118
+ function matchesEvent(webhook, msg) {
2119
+ for (const event of webhook.events) {
2120
+ if (event === "dm" && !msg.space)
2121
+ return true;
2122
+ if (event === "blocker" && msg.blocking)
2123
+ return true;
2124
+ if (event === "space" && msg.space)
2125
+ return true;
2126
+ if (event === "mention" && webhook.agent && msg.content.includes(`@${webhook.agent}`))
2127
+ return true;
2128
+ }
2129
+ return false;
2130
+ }
2131
+ function fireWebhooks(msg) {
2132
+ const config = loadConfig();
2133
+ if (!config.webhooks || config.webhooks.length === 0)
2134
+ return;
2135
+ for (const webhook of config.webhooks) {
2136
+ if (webhook.agent && msg.to_agent !== webhook.agent && !msg.space)
2137
+ continue;
2138
+ if (!matchesEvent(webhook, msg))
2139
+ continue;
2140
+ fetch(webhook.url, {
2141
+ method: "POST",
2142
+ headers: { "Content-Type": "application/json" },
2143
+ body: JSON.stringify({
2144
+ id: msg.id,
2145
+ from: msg.from_agent,
2146
+ to: msg.to_agent,
2147
+ space: msg.space,
2148
+ content: msg.content,
2149
+ priority: msg.priority,
2150
+ blocking: msg.blocking,
2151
+ created_at: msg.created_at
2152
+ })
2153
+ }).catch(() => {});
2154
+ }
2155
+ }
2156
+ var cachedConfig = null, configLoadedAt = 0, CONFIG_CACHE_MS = 1e4;
2157
+ var init_webhooks = () => {};
2158
+
2045
2159
  // src/lib/messages.ts
2046
2160
  import { randomUUID } from "crypto";
2161
+ import { mkdirSync as mkdirSync2, copyFileSync, statSync } from "fs";
2162
+ import { join as join3 } from "path";
2163
+ import { homedir as homedir3 } from "os";
2164
+ function compactMessage(msg) {
2165
+ const result = {};
2166
+ for (const key of Object.keys(msg)) {
2167
+ const val = msg[key];
2168
+ if (val !== null && val !== undefined) {
2169
+ result[key] = val;
2170
+ }
2171
+ }
2172
+ return result;
2173
+ }
2047
2174
  function parseMessage(row) {
2048
2175
  let metadata = null;
2049
2176
  if (row.metadata) {
@@ -2053,11 +2180,53 @@ function parseMessage(row) {
2053
2180
  metadata = null;
2054
2181
  }
2055
2182
  }
2183
+ let attachments = null;
2184
+ if (row.attachments) {
2185
+ try {
2186
+ attachments = JSON.parse(row.attachments);
2187
+ } catch {
2188
+ attachments = null;
2189
+ }
2190
+ }
2056
2191
  return {
2057
2192
  ...row,
2058
2193
  metadata,
2059
- blocking: !!row.blocking
2060
- };
2194
+ attachments,
2195
+ blocking: !!row.blocking,
2196
+ reply_to: row.reply_to || null
2197
+ };
2198
+ }
2199
+ function getAttachmentsDir() {
2200
+ if (process.env.CONVERSATIONS_ATTACHMENTS_DIR)
2201
+ return process.env.CONVERSATIONS_ATTACHMENTS_DIR;
2202
+ return join3(homedir3(), ".conversations", "attachments");
2203
+ }
2204
+ function guessMimeType(name) {
2205
+ const ext = name.split(".").pop()?.toLowerCase();
2206
+ const mimeMap = {
2207
+ txt: "text/plain",
2208
+ md: "text/markdown",
2209
+ json: "application/json",
2210
+ js: "text/javascript",
2211
+ ts: "text/typescript",
2212
+ py: "text/x-python",
2213
+ html: "text/html",
2214
+ css: "text/css",
2215
+ xml: "application/xml",
2216
+ png: "image/png",
2217
+ jpg: "image/jpeg",
2218
+ jpeg: "image/jpeg",
2219
+ gif: "image/gif",
2220
+ svg: "image/svg+xml",
2221
+ webp: "image/webp",
2222
+ pdf: "application/pdf",
2223
+ zip: "application/zip",
2224
+ gz: "application/gzip",
2225
+ csv: "text/csv",
2226
+ yaml: "text/yaml",
2227
+ yml: "text/yaml"
2228
+ };
2229
+ return mimeMap[ext || ""] || "application/octet-stream";
2061
2230
  }
2062
2231
  function sendMessage(opts) {
2063
2232
  const db2 = getDb();
@@ -2066,13 +2235,35 @@ function sendMessage(opts) {
2066
2235
  const metadata = opts.metadata ? JSON.stringify(opts.metadata) : null;
2067
2236
  const normalizedPriority = opts.priority === "low" || opts.priority === "normal" || opts.priority === "high" || opts.priority === "urgent" ? opts.priority : "normal";
2068
2237
  const blocking = opts.blocking ? 1 : 0;
2238
+ const replyTo = opts.reply_to || null;
2069
2239
  const stmt = db2.prepare(`
2070
- INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking)
2071
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2240
+ INSERT INTO messages (session_id, from_agent, to_agent, space, content, priority, working_dir, repository, branch, metadata, blocking, reply_to)
2241
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2072
2242
  RETURNING *
2073
2243
  `);
2074
- 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);
2075
- return parseMessage(row);
2244
+ 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);
2245
+ const message = parseMessage(row);
2246
+ if (opts.attachments && opts.attachments.length > 0) {
2247
+ const attachmentsDir = join3(getAttachmentsDir(), String(message.id));
2248
+ mkdirSync2(attachmentsDir, { recursive: true });
2249
+ const attachmentInfos = [];
2250
+ for (const att of opts.attachments) {
2251
+ const destPath = join3(attachmentsDir, att.name);
2252
+ copyFileSync(att.source_path, destPath);
2253
+ const stat = statSync(destPath);
2254
+ attachmentInfos.push({
2255
+ name: att.name,
2256
+ path: destPath,
2257
+ size: stat.size,
2258
+ mime_type: guessMimeType(att.name)
2259
+ });
2260
+ }
2261
+ const attachmentsJson = JSON.stringify(attachmentInfos);
2262
+ db2.prepare("UPDATE messages SET attachments = ? WHERE id = ?").run(attachmentsJson, message.id);
2263
+ message.attachments = attachmentInfos;
2264
+ }
2265
+ fireWebhooks(message);
2266
+ return message;
2076
2267
  }
2077
2268
  function readMessages(opts = {}) {
2078
2269
  const db2 = getDb();
@@ -2106,10 +2297,13 @@ function readMessages(opts = {}) {
2106
2297
  conditions.push("read_at IS NULL");
2107
2298
  }
2108
2299
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2109
- const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? `LIMIT ${Math.floor(opts.limit)}` : "";
2300
+ const resolvedLimit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
2110
2301
  const order = opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
2111
- const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} ${limit}`).all(...params);
2112
- return rows.map(parseMessage);
2302
+ const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${resolvedLimit}`).all(...params);
2303
+ const messages = rows.map(parseMessage);
2304
+ if (opts.compact)
2305
+ return messages.map(compactMessage);
2306
+ return messages;
2113
2307
  }
2114
2308
  function markRead(ids, reader) {
2115
2309
  const db2 = getDb();
@@ -2255,6 +2449,33 @@ function getUnreadBlockers(agent) {
2255
2449
  }
2256
2450
  function searchMessages(opts) {
2257
2451
  const db2 = getDb();
2452
+ const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
2453
+ try {
2454
+ const ftsConditions = [];
2455
+ const ftsParams = [];
2456
+ const words = opts.query.trim().split(/\s+/).filter(Boolean);
2457
+ const ftsQuery = words.map((w) => `"${w.replace(/"/g, '""')}"`).join(" ");
2458
+ ftsConditions.push("messages_fts MATCH ?");
2459
+ ftsParams.push(ftsQuery);
2460
+ let extraWhere = "";
2461
+ if (opts.space) {
2462
+ extraWhere += " AND m.space = ?";
2463
+ ftsParams.push(opts.space);
2464
+ }
2465
+ if (opts.from) {
2466
+ extraWhere += " AND m.from_agent = ?";
2467
+ ftsParams.push(opts.from);
2468
+ }
2469
+ if (opts.to) {
2470
+ extraWhere += " AND m.to_agent = ?";
2471
+ ftsParams.push(opts.to);
2472
+ }
2473
+ const rows2 = db2.prepare(`SELECT m.* FROM messages m
2474
+ JOIN messages_fts ON messages_fts.rowid = m.id
2475
+ WHERE ${ftsConditions.join(" AND ")}${extraWhere}
2476
+ ORDER BY m.created_at DESC, m.id DESC LIMIT ${limit}`).all(...ftsParams);
2477
+ return rows2.map(parseMessage);
2478
+ } catch {}
2258
2479
  const conditions = ["content LIKE ?"];
2259
2480
  const params = [`%${opts.query}%`];
2260
2481
  if (opts.space) {
@@ -2269,13 +2490,13 @@ function searchMessages(opts) {
2269
2490
  conditions.push("to_agent = ?");
2270
2491
  params.push(opts.to);
2271
2492
  }
2272
- const limit = Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 50;
2273
2493
  const where = `WHERE ${conditions.join(" AND ")}`;
2274
2494
  const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at DESC, id DESC LIMIT ${limit}`).all(...params);
2275
2495
  return rows.map(parseMessage);
2276
2496
  }
2277
2497
  var init_messages = __esm(() => {
2278
2498
  init_db();
2499
+ init_webhooks();
2279
2500
  });
2280
2501
 
2281
2502
  // src/lib/sessions.ts
@@ -3012,9 +3233,9 @@ var init_names = __esm(() => {
3012
3233
  });
3013
3234
 
3014
3235
  // src/lib/identity.ts
3015
- import { readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
3016
- import { join as join2, dirname as dirname2 } from "path";
3017
- import { homedir as homedir2 } from "os";
3236
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
3237
+ import { join as join4, dirname as dirname2 } from "path";
3238
+ import { homedir as homedir4 } from "os";
3018
3239
  function isNameTaken(name) {
3019
3240
  try {
3020
3241
  const { getDb: getDb2 } = (init_db(), __toCommonJS(exports_db));
@@ -3029,7 +3250,7 @@ function getAutoName() {
3029
3250
  if (cachedAutoName)
3030
3251
  return cachedAutoName;
3031
3252
  try {
3032
- const name2 = readFileSync(AGENT_ID_FILE, "utf-8").trim();
3253
+ const name2 = readFileSync2(AGENT_ID_FILE, "utf-8").trim();
3033
3254
  if (name2) {
3034
3255
  cachedAutoName = name2;
3035
3256
  return name2;
@@ -3045,7 +3266,7 @@ function getAutoName() {
3045
3266
  }
3046
3267
  cachedAutoName = name;
3047
3268
  try {
3048
- mkdirSync2(dirname2(AGENT_ID_FILE), { recursive: true });
3269
+ mkdirSync3(dirname2(AGENT_ID_FILE), { recursive: true });
3049
3270
  writeFileSync(AGENT_ID_FILE, name + `
3050
3271
  `, "utf-8");
3051
3272
  } catch {}
@@ -3063,7 +3284,7 @@ function resolveIdentity(explicit) {
3063
3284
  var AGENT_ID_FILE, cachedAutoName = null;
3064
3285
  var init_identity = __esm(() => {
3065
3286
  init_names();
3066
- AGENT_ID_FILE = join2(homedir2(), ".conversations", "agent-id");
3287
+ AGENT_ID_FILE = join4(homedir4(), ".conversations", "agent-id");
3067
3288
  });
3068
3289
 
3069
3290
  // src/lib/presence.ts
@@ -3101,6 +3322,11 @@ function heartbeat(agent, status, metadata) {
3101
3322
  metadata = excluded.metadata
3102
3323
  `).run(agent, resolvedStatus, metadataJson);
3103
3324
  }
3325
+ function getPresence(agent) {
3326
+ const db2 = getDb();
3327
+ const row = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(agent);
3328
+ return row ? parsePresence(row) : null;
3329
+ }
3104
3330
  function listAgents(opts) {
3105
3331
  const db2 = getDb();
3106
3332
  let query = "SELECT * FROM agent_presence";
@@ -3133,6 +3359,52 @@ var init_presence = __esm(() => {
3133
3359
  init_db();
3134
3360
  });
3135
3361
 
3362
+ // src/lib/terminal-markdown.ts
3363
+ var exports_terminal_markdown = {};
3364
+ __export(exports_terminal_markdown, {
3365
+ renderInline: () => renderInline,
3366
+ renderContent: () => renderContent
3367
+ });
3368
+ import chalk from "chalk";
3369
+ var renderInline = (text) => {
3370
+ return text.replace(/`([^`]+)`/g, (_, code) => chalk.bgGray.white(` ${code} `)).replace(/\*\*\*(.+?)\*\*\*/g, (_, t) => chalk.bold.italic(t)).replace(/\*\*(.+?)\*\*/g, (_, t) => chalk.bold(t)).replace(/\*(.+?)\*/g, (_, t) => chalk.italic(t)).replace(/~~(.+?)~~/g, (_, t) => chalk.strikethrough(t));
3371
+ }, renderContent = (content) => {
3372
+ const lines = content.split(`
3373
+ `);
3374
+ const rendered = [];
3375
+ for (const line of lines) {
3376
+ let l = line;
3377
+ const h = l.match(/^(#{1,3})\s+(.+)/);
3378
+ if (h) {
3379
+ rendered.push(chalk.bold(h[2]));
3380
+ continue;
3381
+ }
3382
+ if (/^\s*[-*+]\s/.test(l)) {
3383
+ rendered.push(" " + chalk.dim("\u2022") + " " + renderInline(l.replace(/^\s*[-*+]\s/, "")));
3384
+ continue;
3385
+ }
3386
+ const ol = l.match(/^\s*(\d+)[.)]\s(.*)/);
3387
+ if (ol) {
3388
+ rendered.push(" " + chalk.dim(ol[1] + ".") + " " + renderInline(ol[2]));
3389
+ continue;
3390
+ }
3391
+ if (l.startsWith(">")) {
3392
+ rendered.push(chalk.dim(" \u2502 ") + chalk.italic(renderInline(l.replace(/^>\s?/, ""))));
3393
+ continue;
3394
+ }
3395
+ if (l.trimStart().startsWith("```"))
3396
+ continue;
3397
+ if (l.trim() === "") {
3398
+ rendered.push("");
3399
+ continue;
3400
+ }
3401
+ rendered.push(renderInline(l));
3402
+ }
3403
+ return rendered.join(`
3404
+ `);
3405
+ };
3406
+ var init_terminal_markdown = () => {};
3407
+
3136
3408
  // src/lib/poll.ts
3137
3409
  var exports_poll = {};
3138
3410
  __export(exports_poll, {
@@ -3231,7 +3503,7 @@ var init_poll = __esm(() => {
3231
3503
  var require_package = __commonJS((exports, module) => {
3232
3504
  module.exports = {
3233
3505
  name: "@hasna/conversations",
3234
- version: "0.1.17",
3506
+ version: "0.1.19",
3235
3507
  description: "Real-time CLI messaging for AI agents",
3236
3508
  type: "module",
3237
3509
  bin: {
@@ -32203,9 +32475,9 @@ var init_mcp2 = __esm(() => {
32203
32475
  });
32204
32476
  server.registerTool("send_message", {
32205
32477
  title: "Send Message",
32206
- description: "Send a direct message to another agent. Pass 'from' to identify yourself, or it falls back to CONVERSATIONS_AGENT_ID env var.",
32478
+ description: "Send a direct message to another agent.",
32207
32479
  inputSchema: {
32208
- from: exports_external.string().optional().describe("Your agent ID (e.g. 'claude-1', 'assistant'). Falls back to CONVERSATIONS_AGENT_ID env var."),
32480
+ from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32209
32481
  to: exports_external.string().describe("Recipient agent ID"),
32210
32482
  content: exports_external.string().describe("Message content"),
32211
32483
  session_id: exports_external.string().optional().describe("Session ID (auto-generated if omitted)"),
@@ -32214,7 +32486,7 @@ var init_mcp2 = __esm(() => {
32214
32486
  repository: exports_external.string().optional().describe("Repository context"),
32215
32487
  branch: exports_external.string().optional().describe("Branch context"),
32216
32488
  metadata: exports_external.string().optional().describe("JSON metadata string"),
32217
- blocking: exports_external.boolean().optional().describe("Send as a blocking message. Recipients must acknowledge before continuing.")
32489
+ blocking: exports_external.boolean().optional().describe("Blocking message \u2014 recipients must acknowledge before continuing")
32218
32490
  }
32219
32491
  }, async ({ from: fromParam, to, content, session_id, priority, working_dir, repository, branch, metadata, blocking }) => {
32220
32492
  const from = resolveIdentity(fromParam);
@@ -32224,7 +32496,7 @@ var init_mcp2 = __esm(() => {
32224
32496
  parsedMetadata = JSON.parse(metadata);
32225
32497
  } catch {
32226
32498
  return {
32227
- content: [{ type: "text", text: "Invalid metadata JSON." }],
32499
+ content: [{ type: "text", text: "invalid JSON" }],
32228
32500
  isError: true
32229
32501
  };
32230
32502
  }
@@ -32250,12 +32522,12 @@ var init_mcp2 = __esm(() => {
32250
32522
  description: "Read messages with optional filters. Returns messages sorted by time.",
32251
32523
  inputSchema: {
32252
32524
  session_id: exports_external.string().optional().describe("Filter by session ID"),
32253
- from: exports_external.string().optional().describe("Filter by sender agent ID"),
32254
- to: exports_external.string().optional().describe("Filter by recipient agent ID"),
32525
+ from: exports_external.string().optional().describe("Filter by sender"),
32526
+ to: exports_external.string().optional().describe("Filter by recipient"),
32255
32527
  space: exports_external.string().optional().describe("Filter by space name"),
32256
- since: exports_external.string().optional().describe("Messages after this ISO timestamp"),
32257
- limit: exports_external.number().optional().describe("Max messages to return"),
32258
- unread_only: exports_external.boolean().optional().describe("Only return unread messages")
32528
+ since: exports_external.string().optional().describe("ISO timestamp lower bound"),
32529
+ limit: exports_external.number().optional().describe("Max messages to return (default 20)"),
32530
+ unread_only: exports_external.boolean().optional().describe("Only unread messages")
32259
32531
  }
32260
32532
  }, async (opts) => {
32261
32533
  const messages = readMessages(opts);
@@ -32277,7 +32549,7 @@ var init_mcp2 = __esm(() => {
32277
32549
  });
32278
32550
  server.registerTool("reply", {
32279
32551
  title: "Reply to Message",
32280
- description: "Reply to a message by its ID. Automatically uses the same session and sends to the original sender.",
32552
+ description: "Reply to a message by ID. Uses the same session and sends to the original sender.",
32281
32553
  inputSchema: {
32282
32554
  from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32283
32555
  message_id: exports_external.number().describe("ID of the message to reply to"),
@@ -32309,7 +32581,7 @@ var init_mcp2 = __esm(() => {
32309
32581
  });
32310
32582
  server.registerTool("mark_read", {
32311
32583
  title: "Mark Read",
32312
- description: "Mark message IDs as read for the current agent. Set 'all' to true to mark all unread messages as read.",
32584
+ description: "Mark messages as read. Provide IDs or set 'all' to true.",
32313
32585
  inputSchema: {
32314
32586
  from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32315
32587
  ids: exports_external.array(exports_external.number()).optional().describe("Message IDs to mark as read"),
@@ -32324,7 +32596,7 @@ var init_mcp2 = __esm(() => {
32324
32596
  count = markRead(ids, agent);
32325
32597
  } else {
32326
32598
  return {
32327
- content: [{ type: "text", text: "Provide message IDs or set 'all' to true." }],
32599
+ content: [{ type: "text", text: "provide ids or set all=true" }],
32328
32600
  isError: true
32329
32601
  };
32330
32602
  }
@@ -32334,13 +32606,13 @@ var init_mcp2 = __esm(() => {
32334
32606
  });
32335
32607
  server.registerTool("search_messages", {
32336
32608
  title: "Search Messages",
32337
- description: "Full-text search across message content. Returns matching messages ordered by newest first.",
32609
+ description: "Full-text search across message content, newest first.",
32338
32610
  inputSchema: {
32339
- query: exports_external.string().describe("Search query string"),
32340
- space: exports_external.string().optional().describe("Filter by space name"),
32341
- from: exports_external.string().optional().describe("Filter by sender agent ID"),
32342
- to: exports_external.string().optional().describe("Filter by recipient agent ID"),
32343
- limit: exports_external.number().optional().describe("Max results to return (default 50)")
32611
+ query: exports_external.string().describe("Search query"),
32612
+ space: exports_external.string().optional().describe("Filter by space"),
32613
+ from: exports_external.string().optional().describe("Filter by sender"),
32614
+ to: exports_external.string().optional().describe("Filter by recipient"),
32615
+ limit: exports_external.number().optional().describe("Max results (default 20)")
32344
32616
  }
32345
32617
  }, async ({ query, space, from, to, limit }) => {
32346
32618
  const messages = searchMessages({ query, space, from, to, limit });
@@ -32352,11 +32624,11 @@ var init_mcp2 = __esm(() => {
32352
32624
  title: "Export Messages",
32353
32625
  description: "Export messages as JSON or CSV with optional filters.",
32354
32626
  inputSchema: {
32355
- space: exports_external.string().optional().describe("Filter by space name"),
32627
+ space: exports_external.string().optional().describe("Filter by space"),
32356
32628
  session_id: exports_external.string().optional().describe("Filter by session ID"),
32357
- from: exports_external.string().optional().describe("Filter by sender agent ID"),
32358
- since: exports_external.string().optional().describe("Messages after this ISO date"),
32359
- until: exports_external.string().optional().describe("Messages before this ISO date"),
32629
+ from: exports_external.string().optional().describe("Filter by sender"),
32630
+ since: exports_external.string().optional().describe("ISO date lower bound"),
32631
+ until: exports_external.string().optional().describe("ISO date upper bound"),
32360
32632
  format: exports_external.enum(["json", "csv"]).optional().describe("Output format (default: json)")
32361
32633
  }
32362
32634
  }, async ({ space, session_id, from, since, until, format }) => {
@@ -32367,13 +32639,13 @@ var init_mcp2 = __esm(() => {
32367
32639
  });
32368
32640
  server.registerTool("create_space", {
32369
32641
  title: "Create Space",
32370
- description: "Create a new space. The creator is auto-joined. Spaces can be nested (max 3 levels) and associated with a project.",
32642
+ description: "Create a new space. Creator is auto-joined. Supports nesting (max 3 levels) and project association.",
32371
32643
  inputSchema: {
32372
32644
  from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32373
- name: exports_external.string().describe("Space name (e.g. 'deployments', 'code-review')"),
32645
+ name: exports_external.string().describe("Space name"),
32374
32646
  description: exports_external.string().optional().describe("Space description"),
32375
- parent_id: exports_external.string().optional().describe("Parent space name for nesting (max 3 levels deep)"),
32376
- project_id: exports_external.string().optional().describe("Project ID to associate this space with")
32647
+ parent_id: exports_external.string().optional().describe("Parent space name (max 3 levels deep)"),
32648
+ project_id: exports_external.string().optional().describe("Project ID to associate with")
32377
32649
  }
32378
32650
  }, async ({ from: fromParam, name, description, parent_id, project_id }) => {
32379
32651
  const agent = resolveIdentity(fromParam);
@@ -32385,7 +32657,7 @@ var init_mcp2 = __esm(() => {
32385
32657
  } catch (e) {
32386
32658
  if (e.message?.includes("UNIQUE constraint")) {
32387
32659
  return {
32388
- content: [{ type: "text", text: `Space #${name} already exists` }],
32660
+ content: [{ type: "text", text: `space "${name}" already exists` }],
32389
32661
  isError: true
32390
32662
  };
32391
32663
  }
@@ -32397,11 +32669,11 @@ var init_mcp2 = __esm(() => {
32397
32669
  });
32398
32670
  server.registerTool("list_spaces", {
32399
32671
  title: "List Spaces",
32400
- description: "List all available spaces with member and message counts. Can filter by project or parent. Archived spaces are excluded by default.",
32672
+ description: "List spaces with member/message counts. Archived spaces excluded by default.",
32401
32673
  inputSchema: {
32402
32674
  project_id: exports_external.string().optional().describe("Filter by project ID"),
32403
- parent_id: exports_external.string().optional().describe("Filter by parent space name. Use 'null' for top-level only."),
32404
- include_archived: exports_external.boolean().optional().describe("Include archived spaces (default: false)")
32675
+ parent_id: exports_external.string().optional().describe("Filter by parent space. Use 'null' for top-level only."),
32676
+ include_archived: exports_external.boolean().optional().describe("Include archived spaces")
32405
32677
  }
32406
32678
  }, async ({ project_id, parent_id, include_archived }) => {
32407
32679
  const opts = {};
@@ -32427,14 +32699,14 @@ var init_mcp2 = __esm(() => {
32427
32699
  space: exports_external.string().describe("Space name"),
32428
32700
  content: exports_external.string().describe("Message content"),
32429
32701
  priority: exports_external.enum(["low", "normal", "high", "urgent"]).optional().describe("Message priority"),
32430
- blocking: exports_external.boolean().optional().describe("Send as a blocking message. All space members must acknowledge.")
32702
+ blocking: exports_external.boolean().optional().describe("Blocking message \u2014 all space members must acknowledge")
32431
32703
  }
32432
32704
  }, async ({ from: fromParam, space, content, priority, blocking }) => {
32433
32705
  const from = resolveIdentity(fromParam);
32434
32706
  const sp = getSpace(space);
32435
32707
  if (!sp) {
32436
32708
  return {
32437
- content: [{ type: "text", text: `Space #${space} not found` }],
32709
+ content: [{ type: "text", text: `space "${space}" not found` }],
32438
32710
  isError: true
32439
32711
  };
32440
32712
  }
@@ -32456,7 +32728,7 @@ var init_mcp2 = __esm(() => {
32456
32728
  description: "Read messages from a space.",
32457
32729
  inputSchema: {
32458
32730
  space: exports_external.string().describe("Space name"),
32459
- since: exports_external.string().optional().describe("Messages after this ISO timestamp"),
32731
+ since: exports_external.string().optional().describe("ISO timestamp lower bound"),
32460
32732
  limit: exports_external.number().optional().describe("Max messages to return")
32461
32733
  }
32462
32734
  }, async ({ space, since, limit }) => {
@@ -32477,7 +32749,7 @@ var init_mcp2 = __esm(() => {
32477
32749
  const ok = joinSpace(space, agent);
32478
32750
  if (!ok) {
32479
32751
  return {
32480
- content: [{ type: "text", text: `Space #${space} not found` }],
32752
+ content: [{ type: "text", text: `space "${space}" not found` }],
32481
32753
  isError: true
32482
32754
  };
32483
32755
  }
@@ -32501,12 +32773,12 @@ var init_mcp2 = __esm(() => {
32501
32773
  });
32502
32774
  server.registerTool("update_space", {
32503
32775
  title: "Update Space",
32504
- description: "Update a space's description, parent, or project association.",
32776
+ description: "Update a space's description, parent, or project.",
32505
32777
  inputSchema: {
32506
- name: exports_external.string().describe("Space name to update"),
32778
+ name: exports_external.string().describe("Space name"),
32507
32779
  description: exports_external.string().optional().describe("New description"),
32508
- parent_id: exports_external.string().optional().describe("New parent space name (use 'null' to remove parent)"),
32509
- project_id: exports_external.string().optional().describe("New project ID (use 'null' to remove project)")
32780
+ parent_id: exports_external.string().optional().describe("New parent space (use 'null' to remove)"),
32781
+ project_id: exports_external.string().optional().describe("New project ID (use 'null' to remove)")
32510
32782
  }
32511
32783
  }, async ({ name, description, parent_id, project_id }) => {
32512
32784
  const updates = {};
@@ -32530,7 +32802,7 @@ var init_mcp2 = __esm(() => {
32530
32802
  });
32531
32803
  server.registerTool("archive_space", {
32532
32804
  title: "Archive Space",
32533
- description: "Archive a space. Archived spaces are hidden from list by default.",
32805
+ description: "Archive a space. Hidden from list by default.",
32534
32806
  inputSchema: {
32535
32807
  name: exports_external.string().describe("Space name to archive")
32536
32808
  }
@@ -32568,16 +32840,16 @@ var init_mcp2 = __esm(() => {
32568
32840
  });
32569
32841
  server.registerTool("create_project", {
32570
32842
  title: "Create Project",
32571
- description: "Create a new project. Projects organize spaces and provide context for agent collaboration.",
32843
+ description: "Create a new project to organize spaces and agent collaboration.",
32572
32844
  inputSchema: {
32573
32845
  from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32574
32846
  name: exports_external.string().describe("Project name (unique)"),
32575
32847
  description: exports_external.string().optional().describe("Project description"),
32576
- path: exports_external.string().optional().describe("Absolute path to project on disk"),
32848
+ path: exports_external.string().optional().describe("Absolute path on disk"),
32577
32849
  repository: exports_external.string().optional().describe("Repository URL"),
32578
- tags: exports_external.string().optional().describe(`JSON array of tags (e.g. '["backend", "api"]')`),
32579
- metadata: exports_external.string().optional().describe("JSON metadata string"),
32580
- settings: exports_external.string().optional().describe("JSON settings string")
32850
+ tags: exports_external.string().optional().describe("JSON array of tags"),
32851
+ metadata: exports_external.string().optional().describe("JSON metadata"),
32852
+ settings: exports_external.string().optional().describe("JSON settings")
32581
32853
  }
32582
32854
  }, async ({ from: fromParam, name, description, path, repository, tags, metadata, settings }) => {
32583
32855
  const agent = resolveIdentity(fromParam);
@@ -32587,7 +32859,7 @@ var init_mcp2 = __esm(() => {
32587
32859
  parsedTags = JSON.parse(tags);
32588
32860
  } catch {
32589
32861
  return {
32590
- content: [{ type: "text", text: "Invalid tags JSON. Expected array of strings." }],
32862
+ content: [{ type: "text", text: "invalid tags JSON (expected array)" }],
32591
32863
  isError: true
32592
32864
  };
32593
32865
  }
@@ -32598,7 +32870,7 @@ var init_mcp2 = __esm(() => {
32598
32870
  parsedMetadata = JSON.parse(metadata);
32599
32871
  } catch {
32600
32872
  return {
32601
- content: [{ type: "text", text: "Invalid metadata JSON." }],
32873
+ content: [{ type: "text", text: "invalid JSON" }],
32602
32874
  isError: true
32603
32875
  };
32604
32876
  }
@@ -32609,7 +32881,7 @@ var init_mcp2 = __esm(() => {
32609
32881
  parsedSettings = JSON.parse(settings);
32610
32882
  } catch {
32611
32883
  return {
32612
- content: [{ type: "text", text: "Invalid settings JSON." }],
32884
+ content: [{ type: "text", text: "invalid JSON" }],
32613
32885
  isError: true
32614
32886
  };
32615
32887
  }
@@ -32631,7 +32903,7 @@ var init_mcp2 = __esm(() => {
32631
32903
  } catch (e) {
32632
32904
  if (e.message?.includes("UNIQUE constraint")) {
32633
32905
  return {
32634
- content: [{ type: "text", text: `Project "${name}" already exists` }],
32906
+ content: [{ type: "text", text: `project "${name}" already exists` }],
32635
32907
  isError: true
32636
32908
  };
32637
32909
  }
@@ -32645,7 +32917,7 @@ var init_mcp2 = __esm(() => {
32645
32917
  title: "List Projects",
32646
32918
  description: "List all registered projects.",
32647
32919
  inputSchema: {
32648
- status: exports_external.enum(["active", "archived"]).optional().describe("Filter by project status")
32920
+ status: exports_external.enum(["active", "archived"]).optional().describe("Filter by status")
32649
32921
  }
32650
32922
  }, async ({ status }) => {
32651
32923
  const projects = listProjects(status ? { status } : undefined);
@@ -32666,7 +32938,7 @@ var init_mcp2 = __esm(() => {
32666
32938
  }
32667
32939
  if (!project) {
32668
32940
  return {
32669
- content: [{ type: "text", text: `Project "${id}" not found` }],
32941
+ content: [{ type: "text", text: `project "${id}" not found` }],
32670
32942
  isError: true
32671
32943
  };
32672
32944
  }
@@ -32679,14 +32951,14 @@ var init_mcp2 = __esm(() => {
32679
32951
  description: "Update a project's fields.",
32680
32952
  inputSchema: {
32681
32953
  id: exports_external.string().describe("Project ID (UUID)"),
32682
- name: exports_external.string().optional().describe("New project name"),
32954
+ name: exports_external.string().optional().describe("New name"),
32683
32955
  description: exports_external.string().optional().describe("New description"),
32684
32956
  path: exports_external.string().optional().describe("New path"),
32685
32957
  status: exports_external.enum(["active", "archived"]).optional().describe("New status"),
32686
32958
  repository: exports_external.string().optional().describe("New repository URL"),
32687
32959
  tags: exports_external.string().optional().describe("JSON array of tags"),
32688
- metadata: exports_external.string().optional().describe("JSON metadata string"),
32689
- settings: exports_external.string().optional().describe("JSON settings string")
32960
+ metadata: exports_external.string().optional().describe("JSON metadata"),
32961
+ settings: exports_external.string().optional().describe("JSON settings")
32690
32962
  }
32691
32963
  }, async ({ id, name, description, path, status, repository, tags, metadata, settings }) => {
32692
32964
  const updates = {};
@@ -32705,7 +32977,7 @@ var init_mcp2 = __esm(() => {
32705
32977
  updates.tags = JSON.parse(tags);
32706
32978
  } catch {
32707
32979
  return {
32708
- content: [{ type: "text", text: "Invalid tags JSON." }],
32980
+ content: [{ type: "text", text: "invalid tags JSON" }],
32709
32981
  isError: true
32710
32982
  };
32711
32983
  }
@@ -32715,7 +32987,7 @@ var init_mcp2 = __esm(() => {
32715
32987
  updates.metadata = JSON.parse(metadata);
32716
32988
  } catch {
32717
32989
  return {
32718
- content: [{ type: "text", text: "Invalid metadata JSON." }],
32990
+ content: [{ type: "text", text: "invalid JSON" }],
32719
32991
  isError: true
32720
32992
  };
32721
32993
  }
@@ -32725,7 +32997,7 @@ var init_mcp2 = __esm(() => {
32725
32997
  updates.settings = JSON.parse(settings);
32726
32998
  } catch {
32727
32999
  return {
32728
- content: [{ type: "text", text: "Invalid settings JSON." }],
33000
+ content: [{ type: "text", text: "invalid JSON" }],
32729
33001
  isError: true
32730
33002
  };
32731
33003
  }
@@ -32744,7 +33016,7 @@ var init_mcp2 = __esm(() => {
32744
33016
  });
32745
33017
  server.registerTool("delete_project", {
32746
33018
  title: "Delete Project",
32747
- description: "Delete a project permanently. Fails if spaces still reference it.",
33019
+ description: "Delete a project permanently. Fails if spaces reference it.",
32748
33020
  inputSchema: {
32749
33021
  id: exports_external.string().describe("Project ID (UUID)")
32750
33022
  }
@@ -32753,7 +33025,7 @@ var init_mcp2 = __esm(() => {
32753
33025
  const deleted = deleteProject(id);
32754
33026
  if (!deleted) {
32755
33027
  return {
32756
- content: [{ type: "text", text: `Project "${id}" not found` }],
33028
+ content: [{ type: "text", text: `project "${id}" not found` }],
32757
33029
  isError: true
32758
33030
  };
32759
33031
  }
@@ -32779,7 +33051,7 @@ var init_mcp2 = __esm(() => {
32779
33051
  const deleted = deleteMessage(id, agent);
32780
33052
  if (!deleted) {
32781
33053
  return {
32782
- content: [{ type: "text", text: `Message #${id} not found or not your message` }],
33054
+ content: [{ type: "text", text: `not found or forbidden` }],
32783
33055
  isError: true
32784
33056
  };
32785
33057
  }
@@ -32800,7 +33072,7 @@ var init_mcp2 = __esm(() => {
32800
33072
  const msg = editMessage(id, agent, content);
32801
33073
  if (!msg) {
32802
33074
  return {
32803
- content: [{ type: "text", text: `Message #${id} not found or not your message` }],
33075
+ content: [{ type: "text", text: `not found or forbidden` }],
32804
33076
  isError: true
32805
33077
  };
32806
33078
  }
@@ -32810,7 +33082,7 @@ var init_mcp2 = __esm(() => {
32810
33082
  });
32811
33083
  server.registerTool("pin_message", {
32812
33084
  title: "Pin Message",
32813
- description: "Pin a message. Pinned messages can be retrieved with get_pinned_messages.",
33085
+ description: "Pin a message. Retrieve pinned messages with get_pinned_messages.",
32814
33086
  inputSchema: {
32815
33087
  id: exports_external.number().describe("Message ID to pin")
32816
33088
  }
@@ -32818,7 +33090,7 @@ var init_mcp2 = __esm(() => {
32818
33090
  const msg = pinMessage(id);
32819
33091
  if (!msg) {
32820
33092
  return {
32821
- content: [{ type: "text", text: `Message #${id} not found` }],
33093
+ content: [{ type: "text", text: `message #${id} not found` }],
32822
33094
  isError: true
32823
33095
  };
32824
33096
  }
@@ -32836,7 +33108,7 @@ var init_mcp2 = __esm(() => {
32836
33108
  const msg = unpinMessage(id);
32837
33109
  if (!msg) {
32838
33110
  return {
32839
- content: [{ type: "text", text: `Message #${id} not found` }],
33111
+ content: [{ type: "text", text: `message #${id} not found` }],
32840
33112
  isError: true
32841
33113
  };
32842
33114
  }
@@ -32848,7 +33120,7 @@ var init_mcp2 = __esm(() => {
32848
33120
  title: "Get Pinned Messages",
32849
33121
  description: "Retrieve pinned messages, optionally filtered by space or session.",
32850
33122
  inputSchema: {
32851
- space: exports_external.string().optional().describe("Filter by space name"),
33123
+ space: exports_external.string().optional().describe("Filter by space"),
32852
33124
  session_id: exports_external.string().optional().describe("Filter by session ID"),
32853
33125
  limit: exports_external.number().optional().describe("Max messages to return")
32854
33126
  }
@@ -32874,9 +33146,9 @@ var init_mcp2 = __esm(() => {
32874
33146
  });
32875
33147
  server.registerTool("list_agents", {
32876
33148
  title: "List Agents",
32877
- description: "List all agents with their presence status. Returns agent name, status, last seen time, and whether they are online.",
33149
+ description: "List agents with presence status (name, status, last_seen, online).",
32878
33150
  inputSchema: {
32879
- online_only: exports_external.boolean().optional().describe("Only return agents that are currently online (seen within last 60 seconds)")
33151
+ online_only: exports_external.boolean().optional().describe("Only return agents online within last 60s")
32880
33152
  }
32881
33153
  }, async ({ online_only }) => {
32882
33154
  const agents = listAgents({ online_only });
@@ -32886,7 +33158,7 @@ var init_mcp2 = __esm(() => {
32886
33158
  });
32887
33159
  server.registerTool("get_blockers", {
32888
33160
  title: "Get Blockers",
32889
- description: "Check for unread blocking messages targeting you. Returns messages that must be acknowledged before continuing.",
33161
+ description: "Check for unread blocking messages targeting you. Must acknowledge before continuing.",
32890
33162
  inputSchema: {
32891
33163
  from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var.")
32892
33164
  }
@@ -32899,7 +33171,7 @@ var init_mcp2 = __esm(() => {
32899
33171
  });
32900
33172
  server.registerTool("remove_agent", {
32901
33173
  title: "Remove Agent",
32902
- description: "Remove an agent from the presence list. Only the agent itself should remove its own presence.",
33174
+ description: "Remove an agent from the presence list.",
32903
33175
  inputSchema: {
32904
33176
  from: exports_external.string().optional().describe("Your agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32905
33177
  agent: exports_external.string().optional().describe("Agent to remove (defaults to yourself)")
@@ -32910,7 +33182,7 @@ var init_mcp2 = __esm(() => {
32910
33182
  const removed = removePresence(agent);
32911
33183
  if (!removed) {
32912
33184
  return {
32913
- content: [{ type: "text", text: `Agent "${agent}" not found` }],
33185
+ content: [{ type: "text", text: `agent "${agent}" not found` }],
32914
33186
  isError: true
32915
33187
  };
32916
33188
  }
@@ -32920,7 +33192,7 @@ var init_mcp2 = __esm(() => {
32920
33192
  });
32921
33193
  server.registerTool("rename_agent", {
32922
33194
  title: "Rename Agent",
32923
- description: "Rename an agent in the presence list. By default renames yourself.",
33195
+ description: "Rename an agent in the presence list. Defaults to renaming yourself.",
32924
33196
  inputSchema: {
32925
33197
  from: exports_external.string().optional().describe("Your current agent ID. Falls back to CONVERSATIONS_AGENT_ID env var."),
32926
33198
  new_name: exports_external.string().describe("The new name for the agent")
@@ -32930,7 +33202,7 @@ var init_mcp2 = __esm(() => {
32930
33202
  const newName = new_name.trim();
32931
33203
  if (!newName) {
32932
33204
  return {
32933
- content: [{ type: "text", text: "New name cannot be empty" }],
33205
+ content: [{ type: "text", text: "new name cannot be empty" }],
32934
33206
  isError: true
32935
33207
  };
32936
33208
  }
@@ -32938,7 +33210,7 @@ var init_mcp2 = __esm(() => {
32938
33210
  const renamed = renameAgent(oldName, newName);
32939
33211
  if (!renamed) {
32940
33212
  return {
32941
- content: [{ type: "text", text: `Agent "${oldName}" not found in presence list` }],
33213
+ content: [{ type: "text", text: `agent "${oldName}" not found` }],
32942
33214
  isError: true
32943
33215
  };
32944
33216
  }
@@ -32966,7 +33238,7 @@ var exports_serve = {};
32966
33238
  __export(exports_serve, {
32967
33239
  startDashboardServer: () => startDashboardServer
32968
33240
  });
32969
- import { join as join3, resolve, sep } from "path";
33241
+ import { join as join5, resolve, sep } from "path";
32970
33242
  import { existsSync } from "fs";
32971
33243
  function securityHeaders(base) {
32972
33244
  const headers = new Headers(base);
@@ -33037,7 +33309,7 @@ function isSameOrigin(req) {
33037
33309
  function startDashboardServer(port = 0, host) {
33038
33310
  const resolvedPort = normalizePort(port, 0);
33039
33311
  const resolvedHost = normalizeHost(host ?? process.env.CONVERSATIONS_DASHBOARD_HOST);
33040
- const dashboardDist = join3(import.meta.dir, "../../dashboard/dist");
33312
+ const dashboardDist = join5(import.meta.dir, "../../dashboard/dist");
33041
33313
  const hasDist = existsSync(dashboardDist);
33042
33314
  const server2 = Bun.serve({
33043
33315
  port: resolvedPort,
@@ -33421,7 +33693,7 @@ function startDashboardServer(port = 0, host) {
33421
33693
  headers.set("Content-Type", file2.type);
33422
33694
  return new Response(file2, { headers });
33423
33695
  }
33424
- file2 = Bun.file(join3(dashboardDist, "index.html"));
33696
+ file2 = Bun.file(join5(dashboardDist, "index.html"));
33425
33697
  if (await file2.exists()) {
33426
33698
  const headers = securityHeaders();
33427
33699
  if (file2.type)
@@ -33477,7 +33749,8 @@ init_projects();
33477
33749
  init_db();
33478
33750
  init_identity();
33479
33751
  init_presence();
33480
- import chalk2 from "chalk";
33752
+ init_terminal_markdown();
33753
+ import chalk3 from "chalk";
33481
33754
  import { render } from "ink";
33482
33755
  import React8 from "react";
33483
33756
 
@@ -33488,7 +33761,7 @@ import { Box as Box6, Text as Text7, useApp, useInput as useInput5 } from "ink";
33488
33761
  // node_modules/ink-text-input/build/index.js
33489
33762
  import React, { useState, useEffect } from "react";
33490
33763
  import { Text, useInput } from "ink";
33491
- import chalk from "chalk";
33764
+ import chalk2 from "chalk";
33492
33765
  function TextInput({ value: originalValue, placeholder = "", focus = true, mask, highlightPastedText = false, showCursor = true, onChange, onSubmit }) {
33493
33766
  const [state, setState] = useState({
33494
33767
  cursorOffset: (originalValue || "").length,
@@ -33513,17 +33786,17 @@ function TextInput({ value: originalValue, placeholder = "", focus = true, mask,
33513
33786
  const cursorActualWidth = highlightPastedText ? cursorWidth : 0;
33514
33787
  const value = mask ? mask.repeat(originalValue.length) : originalValue;
33515
33788
  let renderedValue = value;
33516
- let renderedPlaceholder = placeholder ? chalk.grey(placeholder) : undefined;
33789
+ let renderedPlaceholder = placeholder ? chalk2.grey(placeholder) : undefined;
33517
33790
  if (showCursor && focus) {
33518
- renderedPlaceholder = placeholder.length > 0 ? chalk.inverse(placeholder[0]) + chalk.grey(placeholder.slice(1)) : chalk.inverse(" ");
33519
- renderedValue = value.length > 0 ? "" : chalk.inverse(" ");
33791
+ renderedPlaceholder = placeholder.length > 0 ? chalk2.inverse(placeholder[0]) + chalk2.grey(placeholder.slice(1)) : chalk2.inverse(" ");
33792
+ renderedValue = value.length > 0 ? "" : chalk2.inverse(" ");
33520
33793
  let i = 0;
33521
33794
  for (const char of value) {
33522
- renderedValue += i >= cursorOffset - cursorActualWidth && i <= cursorOffset ? chalk.inverse(char) : char;
33795
+ renderedValue += i >= cursorOffset - cursorActualWidth && i <= cursorOffset ? chalk2.inverse(char) : char;
33523
33796
  i++;
33524
33797
  }
33525
33798
  if (value.length > 0 && cursorOffset === value.length) {
33526
- renderedValue += chalk.inverse(" ");
33799
+ renderedValue += chalk2.inverse(" ");
33527
33800
  }
33528
33801
  }
33529
33802
  useInput((input, key) => {
@@ -34424,15 +34697,15 @@ program2.command("send").description("Send a message to an agent").argument("<me
34424
34697
  const content = typeof message === "string" ? message : "";
34425
34698
  const session = typeof opts.session === "string" && opts.session.trim() ? opts.session.trim() : undefined;
34426
34699
  if (!from) {
34427
- console.error(chalk2.red("Sender identity is required."));
34700
+ console.error(chalk3.red("Sender identity is required."));
34428
34701
  process.exit(1);
34429
34702
  }
34430
34703
  if (!to) {
34431
- console.error(chalk2.red("Recipient is required."));
34704
+ console.error(chalk3.red("Recipient is required."));
34432
34705
  process.exit(1);
34433
34706
  }
34434
34707
  if (!content.trim()) {
34435
- console.error(chalk2.red("Message content cannot be empty."));
34708
+ console.error(chalk3.red("Message content cannot be empty."));
34436
34709
  process.exit(1);
34437
34710
  }
34438
34711
  let metadata;
@@ -34440,7 +34713,7 @@ program2.command("send").description("Send a message to an agent").argument("<me
34440
34713
  try {
34441
34714
  metadata = JSON.parse(opts.metadata);
34442
34715
  } catch {
34443
- console.error(chalk2.red("Invalid --metadata JSON."));
34716
+ console.error(chalk3.red("Invalid --metadata JSON."));
34444
34717
  process.exit(1);
34445
34718
  }
34446
34719
  }
@@ -34459,7 +34732,7 @@ program2.command("send").description("Send a message to an agent").argument("<me
34459
34732
  if (opts.json) {
34460
34733
  console.log(JSON.stringify(msg, null, 2));
34461
34734
  } else {
34462
- console.log(chalk2.green(`Message sent`) + chalk2.dim(` (id: ${msg.id}, session: ${msg.session_id})`));
34735
+ console.log(chalk3.green(`Message sent`) + chalk3.dim(` (id: ${msg.id}, session: ${msg.session_id})`));
34463
34736
  }
34464
34737
  closeDb();
34465
34738
  });
@@ -34489,15 +34762,20 @@ program2.command("read").description("Read messages").option("--session <id>", "
34489
34762
  console.log(JSON.stringify(messages, null, 2));
34490
34763
  } else {
34491
34764
  if (messages.length === 0) {
34492
- console.log(chalk2.dim("No messages found."));
34765
+ console.log(chalk3.dim("No messages found."));
34493
34766
  } else {
34494
34767
  for (const msg of messages) {
34495
- const time3 = chalk2.dim(msg.created_at.slice(11, 19));
34496
- const from = chalk2.cyan(msg.from_agent);
34497
- const to = msg.space ? chalk2.magenta(`#${msg.space}`) : chalk2.yellow(msg.to_agent);
34498
- const priority = msg.priority !== "normal" ? chalk2.red(` [${msg.priority}]`) : "";
34499
- const unread = !msg.read_at ? chalk2.green(" *") : "";
34500
- console.log(`${time3} ${from} \u2192 ${to}${priority}${unread}: ${msg.content}`);
34768
+ const time3 = chalk3.dim(msg.created_at.slice(11, 19));
34769
+ const from = chalk3.cyan(msg.from_agent);
34770
+ const to = msg.space ? chalk3.magenta(`#${msg.space}`) : chalk3.yellow(msg.to_agent);
34771
+ const priority = msg.priority !== "normal" ? chalk3.red(` [${msg.priority}]`) : "";
34772
+ const unread = !msg.read_at ? chalk3.green(" *") : "";
34773
+ console.log(`${time3} ${from} \u2192 ${to}${priority}${unread}`);
34774
+ const rendered = renderContent(msg.content);
34775
+ const indented = rendered.split(`
34776
+ `).map((l) => " " + l).join(`
34777
+ `);
34778
+ console.log(indented);
34501
34779
  }
34502
34780
  }
34503
34781
  }
@@ -34506,7 +34784,7 @@ program2.command("read").description("Read messages").option("--session <id>", "
34506
34784
  program2.command("search").description("Search messages by content").argument("<query>", "Search query string").option("--space <name>", "Filter by space").option("--from <agent>", "Filter by sender").option("--to <agent>", "Filter by recipient").option("--limit <n>", "Max results to return", parseInt).option("--json", "Output as JSON").action((query, opts) => {
34507
34785
  const q = typeof query === "string" ? query.trim() : "";
34508
34786
  if (!q) {
34509
- console.error(chalk2.red("Search query cannot be empty."));
34787
+ console.error(chalk3.red("Search query cannot be empty."));
34510
34788
  process.exit(1);
34511
34789
  }
34512
34790
  const messages = searchMessages({
@@ -34520,16 +34798,16 @@ program2.command("search").description("Search messages by content").argument("<
34520
34798
  console.log(JSON.stringify(messages, null, 2));
34521
34799
  } else {
34522
34800
  if (messages.length === 0) {
34523
- console.log(chalk2.dim("No messages found."));
34801
+ console.log(chalk3.dim("No messages found."));
34524
34802
  } else {
34525
- console.log(chalk2.dim(`Found ${messages.length} result(s) for "${q}":
34803
+ console.log(chalk3.dim(`Found ${messages.length} result(s) for "${q}":
34526
34804
  `));
34527
34805
  for (const msg of messages) {
34528
- const time3 = chalk2.dim(msg.created_at.slice(11, 19));
34529
- const from = chalk2.cyan(msg.from_agent);
34530
- const to = msg.space ? chalk2.magenta(`#${msg.space}`) : chalk2.yellow(msg.to_agent);
34531
- const priority = msg.priority !== "normal" ? chalk2.red(` [${msg.priority}]`) : "";
34532
- const unread = !msg.read_at ? chalk2.green(" *") : "";
34806
+ const time3 = chalk3.dim(msg.created_at.slice(11, 19));
34807
+ const from = chalk3.cyan(msg.from_agent);
34808
+ const to = msg.space ? chalk3.magenta(`#${msg.space}`) : chalk3.yellow(msg.to_agent);
34809
+ const priority = msg.priority !== "normal" ? chalk3.red(` [${msg.priority}]`) : "";
34810
+ const unread = !msg.read_at ? chalk3.green(" *") : "";
34533
34811
  console.log(`${time3} ${from} \u2192 ${to}${priority}${unread}: ${msg.content}`);
34534
34812
  }
34535
34813
  }
@@ -34542,12 +34820,12 @@ program2.command("sessions").description("List conversation sessions").option("-
34542
34820
  console.log(JSON.stringify(sessions, null, 2));
34543
34821
  } else {
34544
34822
  if (sessions.length === 0) {
34545
- console.log(chalk2.dim("No sessions found."));
34823
+ console.log(chalk3.dim("No sessions found."));
34546
34824
  } else {
34547
34825
  for (const s of sessions) {
34548
- const unread = s.unread_count > 0 ? chalk2.green(` (${s.unread_count} unread)`) : "";
34826
+ const unread = s.unread_count > 0 ? chalk3.green(` (${s.unread_count} unread)`) : "";
34549
34827
  const participants = s.participants.join(", ");
34550
- console.log(`${chalk2.bold(s.session_id)} \u2014 ${participants} \u2014 ${s.message_count} messages${unread}`);
34828
+ console.log(`${chalk3.bold(s.session_id)} \u2014 ${participants} \u2014 ${s.message_count} messages${unread}`);
34551
34829
  }
34552
34830
  }
34553
34831
  }
@@ -34556,17 +34834,17 @@ program2.command("sessions").description("List conversation sessions").option("-
34556
34834
  program2.command("reply").description("Reply to a message (uses same session)").argument("<message>", "Reply content").requiredOption("--to <message-id>", "Message ID to reply to", parseInt).option("--from <agent>", "Sender agent ID").option("--priority <level>", "Priority: low, normal, high, urgent", "normal").option("--json", "Output as JSON").action((message, opts) => {
34557
34835
  const original = getMessageById(opts.to);
34558
34836
  if (!original) {
34559
- console.error(chalk2.red(`Message #${opts.to} not found.`));
34837
+ console.error(chalk3.red(`Message #${opts.to} not found.`));
34560
34838
  process.exit(1);
34561
34839
  }
34562
34840
  const from = resolveIdentity(opts.from).trim();
34563
34841
  const content = typeof message === "string" ? message : "";
34564
34842
  if (!from) {
34565
- console.error(chalk2.red("Sender identity is required."));
34843
+ console.error(chalk3.red("Sender identity is required."));
34566
34844
  process.exit(1);
34567
34845
  }
34568
34846
  if (!content.trim()) {
34569
- console.error(chalk2.red("Reply content cannot be empty."));
34847
+ console.error(chalk3.red("Reply content cannot be empty."));
34570
34848
  process.exit(1);
34571
34849
  }
34572
34850
  const space = original.space || (original.session_id?.startsWith("space:") ? original.session_id.slice(6) : undefined);
@@ -34582,7 +34860,7 @@ program2.command("reply").description("Reply to a message (uses same session)").
34582
34860
  if (opts.json) {
34583
34861
  console.log(JSON.stringify(msg, null, 2));
34584
34862
  } else {
34585
- console.log(chalk2.green(`Reply sent`) + chalk2.dim(` (id: ${msg.id}, session: ${msg.session_id})`));
34863
+ console.log(chalk3.green(`Reply sent`) + chalk3.dim(` (id: ${msg.id}, session: ${msg.session_id})`));
34586
34864
  }
34587
34865
  closeDb();
34588
34866
  });
@@ -34598,13 +34876,13 @@ program2.command("mark-read").description("Mark messages as read").argument("[id
34598
34876
  } else if (ids.length > 0) {
34599
34877
  count = markRead(ids.map(Number), agent);
34600
34878
  } else {
34601
- console.error(chalk2.red("Provide message IDs, --all, --session, or --space flag."));
34879
+ console.error(chalk3.red("Provide message IDs, --all, --session, or --space flag."));
34602
34880
  process.exit(1);
34603
34881
  }
34604
34882
  if (opts.json) {
34605
34883
  console.log(JSON.stringify({ marked_read: count }));
34606
34884
  } else {
34607
- console.log(chalk2.green(`Marked ${count} message(s) as read.`));
34885
+ console.log(chalk3.green(`Marked ${count} message(s) as read.`));
34608
34886
  }
34609
34887
  closeDb();
34610
34888
  });
@@ -34640,7 +34918,7 @@ program2.command("status").description("Show database stats").option("--json", "
34640
34918
  if (opts.json) {
34641
34919
  console.log(JSON.stringify(stats, null, 2));
34642
34920
  } else {
34643
- console.log(chalk2.bold("Conversations Status"));
34921
+ console.log(chalk3.bold("Conversations Status"));
34644
34922
  console.log(` DB Path: ${stats.db_path}`);
34645
34923
  console.log(` Messages: ${stats.total_messages}`);
34646
34924
  console.log(` Sessions: ${stats.total_sessions}`);
@@ -34662,7 +34940,7 @@ program2.command("update").description("Check for and install updates").option("
34662
34940
  if (opts.json) {
34663
34941
  console.log(JSON.stringify({ error: "Failed to check npm registry" }));
34664
34942
  } else {
34665
- console.error(chalk2.red("Failed to check npm registry for updates."));
34943
+ console.error(chalk3.red("Failed to check npm registry for updates."));
34666
34944
  }
34667
34945
  process.exit(1);
34668
34946
  }
@@ -34671,18 +34949,18 @@ program2.command("update").description("Check for and install updates").option("
34671
34949
  if (opts.json) {
34672
34950
  console.log(JSON.stringify({ current, latest, updateAvailable }));
34673
34951
  } else if (updateAvailable) {
34674
- console.log(`Current version: ${chalk2.yellow(current)}`);
34675
- console.log(`Latest version: ${chalk2.green(latest)}`);
34676
- console.log(chalk2.cyan(`Run ${chalk2.bold("conversations update")} to install.`));
34952
+ console.log(`Current version: ${chalk3.yellow(current)}`);
34953
+ console.log(`Latest version: ${chalk3.green(latest)}`);
34954
+ console.log(chalk3.cyan(`Run ${chalk3.bold("conversations update")} to install.`));
34677
34955
  } else {
34678
- console.log(chalk2.green(`Already on latest version (${current})`));
34956
+ console.log(chalk3.green(`Already on latest version (${current})`));
34679
34957
  }
34680
34958
  return;
34681
34959
  }
34682
34960
  if (opts.json) {
34683
34961
  console.log(JSON.stringify({ current, latest, updateAvailable, status: "updating" }));
34684
34962
  } else {
34685
- console.log(`Updating from ${chalk2.yellow(current)} to ${chalk2.green(latest)}...`);
34963
+ console.log(`Updating from ${chalk3.yellow(current)} to ${chalk3.green(latest)}...`);
34686
34964
  }
34687
34965
  const proc = Bun.spawn(["bun", "install", "-g", `@hasna/conversations@${latest}`], {
34688
34966
  stdout: "inherit",
@@ -34691,14 +34969,14 @@ program2.command("update").description("Check for and install updates").option("
34691
34969
  const exitCode = await proc.exited;
34692
34970
  if (exitCode === 0) {
34693
34971
  if (!opts.json) {
34694
- console.log(chalk2.green(`
34972
+ console.log(chalk3.green(`
34695
34973
  Successfully updated to v${latest}`));
34696
34974
  }
34697
34975
  } else {
34698
34976
  if (opts.json) {
34699
34977
  console.log(JSON.stringify({ error: "Update failed", exitCode }));
34700
34978
  } else {
34701
- console.error(chalk2.red(`
34979
+ console.error(chalk3.red(`
34702
34980
  Update failed (exit code ${exitCode})`));
34703
34981
  }
34704
34982
  process.exit(1);
@@ -34709,11 +34987,11 @@ space.command("create").description("Create a new space").argument("<name>", "Sp
34709
34987
  const agent = resolveIdentity(opts.from).trim();
34710
34988
  const spaceName = typeof name === "string" ? name.trim() : "";
34711
34989
  if (!agent) {
34712
- console.error(chalk2.red("Creator identity is required."));
34990
+ console.error(chalk3.red("Creator identity is required."));
34713
34991
  process.exit(1);
34714
34992
  }
34715
34993
  if (!spaceName) {
34716
- console.error(chalk2.red("Space name cannot be empty."));
34994
+ console.error(chalk3.red("Space name cannot be empty."));
34717
34995
  process.exit(1);
34718
34996
  }
34719
34997
  try {
@@ -34726,14 +35004,14 @@ space.command("create").description("Create a new space").argument("<name>", "Sp
34726
35004
  if (opts.json) {
34727
35005
  console.log(JSON.stringify(sp, null, 2));
34728
35006
  } else {
34729
- console.log(chalk2.green(`Space #${sp.name} created`) + (sp.description ? chalk2.dim(` \u2014 ${sp.description}`) : ""));
35007
+ console.log(chalk3.green(`Space #${sp.name} created`) + (sp.description ? chalk3.dim(` \u2014 ${sp.description}`) : ""));
34730
35008
  }
34731
35009
  } catch (e) {
34732
35010
  if (e.message?.includes("UNIQUE constraint")) {
34733
- console.error(chalk2.red(`Space #${spaceName} already exists.`));
35011
+ console.error(chalk3.red(`Space #${spaceName} already exists.`));
34734
35012
  process.exit(1);
34735
35013
  }
34736
- console.error(chalk2.red(e.message));
35014
+ console.error(chalk3.red(e.message));
34737
35015
  process.exit(1);
34738
35016
  }
34739
35017
  closeDb();
@@ -34754,13 +35032,13 @@ space.command("list").description("List all spaces").option("--project <id>", "F
34754
35032
  console.log(JSON.stringify(spaces, null, 2));
34755
35033
  } else {
34756
35034
  if (spaces.length === 0) {
34757
- console.log(chalk2.dim("No spaces found."));
35035
+ console.log(chalk3.dim("No spaces found."));
34758
35036
  } else {
34759
35037
  for (const sp of spaces) {
34760
- const desc = sp.description ? chalk2.dim(` \u2014 ${sp.description}`) : "";
34761
- const parent = sp.parent_id ? chalk2.dim(` (child of ${sp.parent_id})`) : "";
34762
- const archived = sp.archived_at ? chalk2.yellow(" [archived]") : "";
34763
- console.log(`${chalk2.magenta(`#${sp.name}`)}${desc}${parent}${archived} ${sp.member_count} members, ${sp.message_count} messages`);
35038
+ const desc = sp.description ? chalk3.dim(` \u2014 ${sp.description}`) : "";
35039
+ const parent = sp.parent_id ? chalk3.dim(` (child of ${sp.parent_id})`) : "";
35040
+ const archived = sp.archived_at ? chalk3.yellow(" [archived]") : "";
35041
+ console.log(`${chalk3.magenta(`#${sp.name}`)}${desc}${parent}${archived} ${sp.member_count} members, ${sp.message_count} messages`);
34764
35042
  }
34765
35043
  }
34766
35044
  }
@@ -34769,7 +35047,7 @@ space.command("list").description("List all spaces").option("--project <id>", "F
34769
35047
  space.command("update").description("Update a space").argument("<name>", "Space name").option("--description <text>", "New description").option("--parent <name>", "New parent space name").option("--project <id>", "New project ID").option("--json", "Output as JSON").action((name, opts) => {
34770
35048
  const spaceName = typeof name === "string" ? name.trim() : "";
34771
35049
  if (!spaceName) {
34772
- console.error(chalk2.red("Space name cannot be empty."));
35050
+ console.error(chalk3.red("Space name cannot be empty."));
34773
35051
  process.exit(1);
34774
35052
  }
34775
35053
  const updates = {};
@@ -34784,10 +35062,10 @@ space.command("update").description("Update a space").argument("<name>", "Space
34784
35062
  if (opts.json) {
34785
35063
  console.log(JSON.stringify(sp, null, 2));
34786
35064
  } else {
34787
- console.log(chalk2.green(`Space #${sp.name} updated.`));
35065
+ console.log(chalk3.green(`Space #${sp.name} updated.`));
34788
35066
  }
34789
35067
  } catch (e) {
34790
- console.error(chalk2.red(e.message));
35068
+ console.error(chalk3.red(e.message));
34791
35069
  process.exit(1);
34792
35070
  }
34793
35071
  closeDb();
@@ -34795,7 +35073,7 @@ space.command("update").description("Update a space").argument("<name>", "Space
34795
35073
  space.command("archive").description("Archive a space").argument("<name>", "Space name").option("--json", "Output as JSON").action((name, opts) => {
34796
35074
  const spaceName = typeof name === "string" ? name.trim() : "";
34797
35075
  if (!spaceName) {
34798
- console.error(chalk2.red("Space name cannot be empty."));
35076
+ console.error(chalk3.red("Space name cannot be empty."));
34799
35077
  process.exit(1);
34800
35078
  }
34801
35079
  try {
@@ -34803,10 +35081,10 @@ space.command("archive").description("Archive a space").argument("<name>", "Spac
34803
35081
  if (opts.json) {
34804
35082
  console.log(JSON.stringify(sp, null, 2));
34805
35083
  } else {
34806
- console.log(chalk2.green(`Space #${sp.name} archived.`));
35084
+ console.log(chalk3.green(`Space #${sp.name} archived.`));
34807
35085
  }
34808
35086
  } catch (e) {
34809
- console.error(chalk2.red(e.message));
35087
+ console.error(chalk3.red(e.message));
34810
35088
  process.exit(1);
34811
35089
  }
34812
35090
  closeDb();
@@ -34814,7 +35092,7 @@ space.command("archive").description("Archive a space").argument("<name>", "Spac
34814
35092
  space.command("unarchive").description("Unarchive a space").argument("<name>", "Space name").option("--json", "Output as JSON").action((name, opts) => {
34815
35093
  const spaceName = typeof name === "string" ? name.trim() : "";
34816
35094
  if (!spaceName) {
34817
- console.error(chalk2.red("Space name cannot be empty."));
35095
+ console.error(chalk3.red("Space name cannot be empty."));
34818
35096
  process.exit(1);
34819
35097
  }
34820
35098
  try {
@@ -34822,10 +35100,10 @@ space.command("unarchive").description("Unarchive a space").argument("<name>", "
34822
35100
  if (opts.json) {
34823
35101
  console.log(JSON.stringify(sp, null, 2));
34824
35102
  } else {
34825
- console.log(chalk2.green(`Space #${sp.name} unarchived.`));
35103
+ console.log(chalk3.green(`Space #${sp.name} unarchived.`));
34826
35104
  }
34827
35105
  } catch (e) {
34828
- console.error(chalk2.red(e.message));
35106
+ console.error(chalk3.red(e.message));
34829
35107
  process.exit(1);
34830
35108
  }
34831
35109
  closeDb();
@@ -34835,20 +35113,20 @@ space.command("send").description("Send a message to a space").argument("<space>
34835
35113
  const spaceArg = typeof spaceName === "string" ? spaceName.trim() : "";
34836
35114
  const content = typeof message === "string" ? message : "";
34837
35115
  if (!from) {
34838
- console.error(chalk2.red("Sender identity is required."));
35116
+ console.error(chalk3.red("Sender identity is required."));
34839
35117
  process.exit(1);
34840
35118
  }
34841
35119
  if (!spaceArg) {
34842
- console.error(chalk2.red("Space name cannot be empty."));
35120
+ console.error(chalk3.red("Space name cannot be empty."));
34843
35121
  process.exit(1);
34844
35122
  }
34845
35123
  if (!content.trim()) {
34846
- console.error(chalk2.red("Message content cannot be empty."));
35124
+ console.error(chalk3.red("Message content cannot be empty."));
34847
35125
  process.exit(1);
34848
35126
  }
34849
35127
  const sp = getSpace(spaceArg);
34850
35128
  if (!sp) {
34851
- console.error(chalk2.red(`Space #${spaceArg} not found.`));
35129
+ console.error(chalk3.red(`Space #${spaceArg} not found.`));
34852
35130
  process.exit(1);
34853
35131
  }
34854
35132
  const msg = sendMessage({
@@ -34862,14 +35140,14 @@ space.command("send").description("Send a message to a space").argument("<space>
34862
35140
  if (opts.json) {
34863
35141
  console.log(JSON.stringify(msg, null, 2));
34864
35142
  } else {
34865
- console.log(chalk2.green(`Message sent to #${spaceArg}`) + chalk2.dim(` (id: ${msg.id})`));
35143
+ console.log(chalk3.green(`Message sent to #${spaceArg}`) + chalk3.dim(` (id: ${msg.id})`));
34866
35144
  }
34867
35145
  closeDb();
34868
35146
  });
34869
35147
  space.command("read").description("Read messages from a space").argument("<space>", "Space name").option("--since <timestamp>", "Messages after this ISO timestamp").option("--limit <n>", "Max messages to return", parseInt).option("--json", "Output as JSON").action((spaceName, opts) => {
34870
35148
  const spaceArg = typeof spaceName === "string" ? spaceName.trim() : "";
34871
35149
  if (!spaceArg) {
34872
- console.error(chalk2.red("Space name cannot be empty."));
35150
+ console.error(chalk3.red("Space name cannot be empty."));
34873
35151
  process.exit(1);
34874
35152
  }
34875
35153
  const messages = readMessages({
@@ -34881,13 +35159,18 @@ space.command("read").description("Read messages from a space").argument("<space
34881
35159
  console.log(JSON.stringify(messages, null, 2));
34882
35160
  } else {
34883
35161
  if (messages.length === 0) {
34884
- console.log(chalk2.dim(`No messages in #${spaceArg}.`));
35162
+ console.log(chalk3.dim(`No messages in #${spaceArg}.`));
34885
35163
  } else {
34886
35164
  for (const msg of messages) {
34887
- const time3 = chalk2.dim(msg.created_at.slice(11, 19));
34888
- const from = chalk2.cyan(msg.from_agent);
34889
- const priority = msg.priority !== "normal" ? chalk2.red(` [${msg.priority}]`) : "";
34890
- console.log(`${time3} ${from} \u2192 ${chalk2.magenta(`#${spaceArg}`)}${priority}: ${msg.content}`);
35165
+ const time3 = chalk3.dim(msg.created_at.slice(11, 19));
35166
+ const from = chalk3.cyan(msg.from_agent);
35167
+ const priority = msg.priority !== "normal" ? chalk3.red(` [${msg.priority}]`) : "";
35168
+ console.log(`${time3} ${from} \u2192 ${chalk3.magenta(`#${spaceArg}`)}${priority}`);
35169
+ const rendered = renderContent(msg.content);
35170
+ const indented = rendered.split(`
35171
+ `).map((l) => " " + l).join(`
35172
+ `);
35173
+ console.log(indented);
34891
35174
  }
34892
35175
  }
34893
35176
  }
@@ -34897,22 +35180,22 @@ space.command("join").description("Join a space").argument("<space>", "Space nam
34897
35180
  const agent = resolveIdentity(opts.from).trim();
34898
35181
  const spaceArg = typeof spaceName === "string" ? spaceName.trim() : "";
34899
35182
  if (!agent) {
34900
- console.error(chalk2.red("Agent identity is required."));
35183
+ console.error(chalk3.red("Agent identity is required."));
34901
35184
  process.exit(1);
34902
35185
  }
34903
35186
  if (!spaceArg) {
34904
- console.error(chalk2.red("Space name cannot be empty."));
35187
+ console.error(chalk3.red("Space name cannot be empty."));
34905
35188
  process.exit(1);
34906
35189
  }
34907
35190
  const ok = joinSpace(spaceArg, agent);
34908
35191
  if (!ok) {
34909
- console.error(chalk2.red(`Space #${spaceArg} not found.`));
35192
+ console.error(chalk3.red(`Space #${spaceArg} not found.`));
34910
35193
  process.exit(1);
34911
35194
  }
34912
35195
  if (opts.json) {
34913
35196
  console.log(JSON.stringify({ space: spaceArg, agent, joined: true }));
34914
35197
  } else {
34915
- console.log(chalk2.green(`${agent} joined #${spaceArg}`));
35198
+ console.log(chalk3.green(`${agent} joined #${spaceArg}`));
34916
35199
  }
34917
35200
  closeDb();
34918
35201
  });
@@ -34920,11 +35203,11 @@ space.command("leave").description("Leave a space").argument("<space>", "Space n
34920
35203
  const agent = resolveIdentity(opts.from).trim();
34921
35204
  const spaceArg = typeof spaceName === "string" ? spaceName.trim() : "";
34922
35205
  if (!agent) {
34923
- console.error(chalk2.red("Agent identity is required."));
35206
+ console.error(chalk3.red("Agent identity is required."));
34924
35207
  process.exit(1);
34925
35208
  }
34926
35209
  if (!spaceArg) {
34927
- console.error(chalk2.red("Space name cannot be empty."));
35210
+ console.error(chalk3.red("Space name cannot be empty."));
34928
35211
  process.exit(1);
34929
35212
  }
34930
35213
  const ok = leaveSpace(spaceArg, agent);
@@ -34932,9 +35215,9 @@ space.command("leave").description("Leave a space").argument("<space>", "Space n
34932
35215
  console.log(JSON.stringify({ space: spaceArg, agent, left: ok }));
34933
35216
  } else {
34934
35217
  if (ok) {
34935
- console.log(chalk2.green(`${agent} left #${spaceArg}`));
35218
+ console.log(chalk3.green(`${agent} left #${spaceArg}`));
34936
35219
  } else {
34937
- console.log(chalk2.dim(`${agent} was not a member of #${spaceArg}`));
35220
+ console.log(chalk3.dim(`${agent} was not a member of #${spaceArg}`));
34938
35221
  }
34939
35222
  }
34940
35223
  closeDb();
@@ -34942,7 +35225,7 @@ space.command("leave").description("Leave a space").argument("<space>", "Space n
34942
35225
  space.command("members").description("List space members").argument("<space>", "Space name").option("--json", "Output as JSON").action((spaceName, opts) => {
34943
35226
  const spaceArg = typeof spaceName === "string" ? spaceName.trim() : "";
34944
35227
  if (!spaceArg) {
34945
- console.error(chalk2.red("Space name cannot be empty."));
35228
+ console.error(chalk3.red("Space name cannot be empty."));
34946
35229
  process.exit(1);
34947
35230
  }
34948
35231
  const members = getSpaceMembers(spaceArg);
@@ -34950,11 +35233,11 @@ space.command("members").description("List space members").argument("<space>", "
34950
35233
  console.log(JSON.stringify(members, null, 2));
34951
35234
  } else {
34952
35235
  if (members.length === 0) {
34953
- console.log(chalk2.dim(`No members in #${spaceArg}.`));
35236
+ console.log(chalk3.dim(`No members in #${spaceArg}.`));
34954
35237
  } else {
34955
- console.log(chalk2.magenta(`#${spaceArg}`) + chalk2.dim(` \u2014 ${members.length} member(s)`));
35238
+ console.log(chalk3.magenta(`#${spaceArg}`) + chalk3.dim(` \u2014 ${members.length} member(s)`));
34956
35239
  for (const m of members) {
34957
- console.log(` ${chalk2.cyan(m.agent)} ${chalk2.dim(`joined ${m.joined_at.slice(0, 10)}`)}`);
35240
+ console.log(` ${chalk3.cyan(m.agent)} ${chalk3.dim(`joined ${m.joined_at.slice(0, 10)}`)}`);
34958
35241
  }
34959
35242
  }
34960
35243
  }
@@ -34965,11 +35248,11 @@ project.command("create").description("Create a new project").argument("<name>",
34965
35248
  const agent = resolveIdentity(opts.from).trim();
34966
35249
  const projectName = typeof name === "string" ? name.trim() : "";
34967
35250
  if (!agent) {
34968
- console.error(chalk2.red("Creator identity is required."));
35251
+ console.error(chalk3.red("Creator identity is required."));
34969
35252
  process.exit(1);
34970
35253
  }
34971
35254
  if (!projectName) {
34972
- console.error(chalk2.red("Project name cannot be empty."));
35255
+ console.error(chalk3.red("Project name cannot be empty."));
34973
35256
  process.exit(1);
34974
35257
  }
34975
35258
  let tags;
@@ -34977,7 +35260,7 @@ project.command("create").description("Create a new project").argument("<name>",
34977
35260
  try {
34978
35261
  tags = JSON.parse(opts.tags);
34979
35262
  } catch {
34980
- console.error(chalk2.red("Invalid --tags JSON. Expected array of strings."));
35263
+ console.error(chalk3.red("Invalid --tags JSON. Expected array of strings."));
34981
35264
  process.exit(1);
34982
35265
  }
34983
35266
  }
@@ -34993,14 +35276,14 @@ project.command("create").description("Create a new project").argument("<name>",
34993
35276
  if (opts.json) {
34994
35277
  console.log(JSON.stringify(p, null, 2));
34995
35278
  } else {
34996
- console.log(chalk2.green(`Project "${p.name}" created`) + chalk2.dim(` (id: ${p.id})`));
35279
+ console.log(chalk3.green(`Project "${p.name}" created`) + chalk3.dim(` (id: ${p.id})`));
34997
35280
  }
34998
35281
  } catch (e) {
34999
35282
  if (e.message?.includes("UNIQUE constraint")) {
35000
- console.error(chalk2.red(`Project "${projectName}" already exists.`));
35283
+ console.error(chalk3.red(`Project "${projectName}" already exists.`));
35001
35284
  process.exit(1);
35002
35285
  }
35003
- console.error(chalk2.red(e.message));
35286
+ console.error(chalk3.red(e.message));
35004
35287
  process.exit(1);
35005
35288
  }
35006
35289
  closeDb();
@@ -35012,12 +35295,12 @@ project.command("list").description("List all projects").option("--status <statu
35012
35295
  console.log(JSON.stringify(projects, null, 2));
35013
35296
  } else {
35014
35297
  if (projects.length === 0) {
35015
- console.log(chalk2.dim("No projects found."));
35298
+ console.log(chalk3.dim("No projects found."));
35016
35299
  } else {
35017
35300
  for (const p of projects) {
35018
- const desc = p.description ? chalk2.dim(` \u2014 ${p.description}`) : "";
35019
- const statusBadge = p.status === "archived" ? chalk2.yellow(" [archived]") : "";
35020
- console.log(`${chalk2.bold(p.name)}${desc}${statusBadge} ${p.space_count} spaces`);
35301
+ const desc = p.description ? chalk3.dim(` \u2014 ${p.description}`) : "";
35302
+ const statusBadge = p.status === "archived" ? chalk3.yellow(" [archived]") : "";
35303
+ console.log(`${chalk3.bold(p.name)}${desc}${statusBadge} ${p.space_count} spaces`);
35021
35304
  }
35022
35305
  }
35023
35306
  }
@@ -35028,13 +35311,13 @@ project.command("get").description("Get project details").argument("<id-or-name>
35028
35311
  if (!p)
35029
35312
  p = getProjectByName(idOrName);
35030
35313
  if (!p) {
35031
- console.error(chalk2.red(`Project "${idOrName}" not found.`));
35314
+ console.error(chalk3.red(`Project "${idOrName}" not found.`));
35032
35315
  process.exit(1);
35033
35316
  }
35034
35317
  if (opts.json) {
35035
35318
  console.log(JSON.stringify(p, null, 2));
35036
35319
  } else {
35037
- console.log(chalk2.bold(p.name));
35320
+ console.log(chalk3.bold(p.name));
35038
35321
  if (p.description)
35039
35322
  console.log(` Description: ${p.description}`);
35040
35323
  if (p.path)
@@ -35065,7 +35348,7 @@ project.command("update").description("Update a project").argument("<id>", "Proj
35065
35348
  try {
35066
35349
  updates.tags = JSON.parse(opts.tags);
35067
35350
  } catch {
35068
- console.error(chalk2.red("Invalid --tags JSON."));
35351
+ console.error(chalk3.red("Invalid --tags JSON."));
35069
35352
  process.exit(1);
35070
35353
  }
35071
35354
  }
@@ -35074,10 +35357,10 @@ project.command("update").description("Update a project").argument("<id>", "Proj
35074
35357
  if (opts.json) {
35075
35358
  console.log(JSON.stringify(p, null, 2));
35076
35359
  } else {
35077
- console.log(chalk2.green(`Project "${p.name}" updated.`));
35360
+ console.log(chalk3.green(`Project "${p.name}" updated.`));
35078
35361
  }
35079
35362
  } catch (e) {
35080
- console.error(chalk2.red(e.message));
35363
+ console.error(chalk3.red(e.message));
35081
35364
  process.exit(1);
35082
35365
  }
35083
35366
  closeDb();
@@ -35086,16 +35369,16 @@ project.command("delete").description("Delete a project").argument("<id>", "Proj
35086
35369
  try {
35087
35370
  const deleted = deleteProject(id);
35088
35371
  if (!deleted) {
35089
- console.error(chalk2.red(`Project "${id}" not found.`));
35372
+ console.error(chalk3.red(`Project "${id}" not found.`));
35090
35373
  process.exit(1);
35091
35374
  }
35092
35375
  if (opts.json) {
35093
35376
  console.log(JSON.stringify({ id, deleted: true }));
35094
35377
  } else {
35095
- console.log(chalk2.green(`Project deleted.`));
35378
+ console.log(chalk3.green(`Project deleted.`));
35096
35379
  }
35097
35380
  } catch (e) {
35098
- console.error(chalk2.red(e.message));
35381
+ console.error(chalk3.red(e.message));
35099
35382
  process.exit(1);
35100
35383
  }
35101
35384
  closeDb();
@@ -35103,7 +35386,7 @@ project.command("delete").description("Delete a project").argument("<id>", "Proj
35103
35386
  program2.command("delete").description("Delete a message (only sender can delete)").argument("<id>", "Message ID", parseInt).option("--from <agent>", "Sender agent ID").option("--json", "Output as JSON").action((id, opts) => {
35104
35387
  const agent = resolveIdentity(opts.from).trim();
35105
35388
  if (!agent) {
35106
- console.error(chalk2.red("Agent identity is required."));
35389
+ console.error(chalk3.red("Agent identity is required."));
35107
35390
  process.exit(1);
35108
35391
  }
35109
35392
  const result = deleteMessage(id, agent);
@@ -35111,9 +35394,9 @@ program2.command("delete").description("Delete a message (only sender can delete
35111
35394
  console.log(JSON.stringify({ id, deleted: result }));
35112
35395
  } else {
35113
35396
  if (result) {
35114
- console.log(chalk2.green(`Message #${id} deleted.`));
35397
+ console.log(chalk3.green(`Message #${id} deleted.`));
35115
35398
  } else {
35116
- console.error(chalk2.red(`Message #${id} not found or not your message.`));
35399
+ console.error(chalk3.red(`Message #${id} not found or not your message.`));
35117
35400
  process.exit(1);
35118
35401
  }
35119
35402
  }
@@ -35123,11 +35406,11 @@ program2.command("edit").description("Edit a message (only sender can edit)").ar
35123
35406
  const agent = resolveIdentity(opts.from).trim();
35124
35407
  const content = typeof newContent === "string" ? newContent : "";
35125
35408
  if (!agent) {
35126
- console.error(chalk2.red("Agent identity is required."));
35409
+ console.error(chalk3.red("Agent identity is required."));
35127
35410
  process.exit(1);
35128
35411
  }
35129
35412
  if (!content.trim()) {
35130
- console.error(chalk2.red("New content cannot be empty."));
35413
+ console.error(chalk3.red("New content cannot be empty."));
35131
35414
  process.exit(1);
35132
35415
  }
35133
35416
  const msg = editMessage(id, agent, content);
@@ -35135,9 +35418,9 @@ program2.command("edit").description("Edit a message (only sender can edit)").ar
35135
35418
  console.log(JSON.stringify(msg, null, 2));
35136
35419
  } else {
35137
35420
  if (msg) {
35138
- console.log(chalk2.green(`Message #${id} edited.`));
35421
+ console.log(chalk3.green(`Message #${id} edited.`));
35139
35422
  } else {
35140
- console.error(chalk2.red(`Message #${id} not found or not your message.`));
35423
+ console.error(chalk3.red(`Message #${id} not found or not your message.`));
35141
35424
  process.exit(1);
35142
35425
  }
35143
35426
  }
@@ -35149,9 +35432,9 @@ program2.command("pin").description("Pin a message").argument("<id>", "Message I
35149
35432
  console.log(JSON.stringify(msg, null, 2));
35150
35433
  } else {
35151
35434
  if (msg) {
35152
- console.log(chalk2.green(`Message #${id} pinned.`));
35435
+ console.log(chalk3.green(`Message #${id} pinned.`));
35153
35436
  } else {
35154
- console.error(chalk2.red(`Message #${id} not found.`));
35437
+ console.error(chalk3.red(`Message #${id} not found.`));
35155
35438
  process.exit(1);
35156
35439
  }
35157
35440
  }
@@ -35163,9 +35446,9 @@ program2.command("unpin").description("Unpin a message").argument("<id>", "Messa
35163
35446
  console.log(JSON.stringify(msg, null, 2));
35164
35447
  } else {
35165
35448
  if (msg) {
35166
- console.log(chalk2.green(`Message #${id} unpinned.`));
35449
+ console.log(chalk3.green(`Message #${id} unpinned.`));
35167
35450
  } else {
35168
- console.error(chalk2.red(`Message #${id} not found.`));
35451
+ console.error(chalk3.red(`Message #${id} not found.`));
35169
35452
  process.exit(1);
35170
35453
  }
35171
35454
  }
@@ -35180,13 +35463,13 @@ agents.command("list").description("List all agents with their presence status")
35180
35463
  console.log(JSON.stringify(agentsList, null, 2));
35181
35464
  } else {
35182
35465
  if (agentsList.length === 0) {
35183
- console.log(chalk2.dim("No agents found."));
35466
+ console.log(chalk3.dim("No agents found."));
35184
35467
  } else {
35185
35468
  for (const a of agentsList) {
35186
- const status = a.online ? chalk2.green("online") : chalk2.dim("offline");
35187
- const lastSeen = chalk2.dim(a.last_seen_at.slice(0, 19));
35188
- const agentName = a.agent === agent ? chalk2.cyan(`${a.agent} (you)`) : chalk2.cyan(a.agent);
35189
- console.log(` ${agentName} ${status} ${chalk2.dim(a.status)} ${lastSeen}`);
35469
+ const status = a.online ? chalk3.green("online") : chalk3.dim("offline");
35470
+ const lastSeen = chalk3.dim(a.last_seen_at.slice(0, 19));
35471
+ const agentName = a.agent === agent ? chalk3.cyan(`${a.agent} (you)`) : chalk3.cyan(a.agent);
35472
+ console.log(` ${agentName} ${status} ${chalk3.dim(a.status)} ${lastSeen}`);
35190
35473
  }
35191
35474
  }
35192
35475
  }
@@ -35195,7 +35478,7 @@ agents.command("list").description("List all agents with their presence status")
35195
35478
  agents.command("remove").description("Remove an agent from the presence list").argument("<name>", "Agent name to remove").option("--json", "Output as JSON").action((name, opts) => {
35196
35479
  const agentName = typeof name === "string" ? name.trim() : "";
35197
35480
  if (!agentName) {
35198
- console.error(chalk2.red("Agent name cannot be empty."));
35481
+ console.error(chalk3.red("Agent name cannot be empty."));
35199
35482
  process.exit(1);
35200
35483
  }
35201
35484
  const removed = removePresence(agentName);
@@ -35203,9 +35486,9 @@ agents.command("remove").description("Remove an agent from the presence list").a
35203
35486
  console.log(JSON.stringify({ agent: agentName, removed }));
35204
35487
  } else {
35205
35488
  if (removed) {
35206
- console.log(chalk2.green(`Agent "${agentName}" removed.`));
35489
+ console.log(chalk3.green(`Agent "${agentName}" removed.`));
35207
35490
  } else {
35208
- console.error(chalk2.red(`Agent "${agentName}" not found.`));
35491
+ console.error(chalk3.red(`Agent "${agentName}" not found.`));
35209
35492
  process.exit(1);
35210
35493
  }
35211
35494
  }
@@ -35215,26 +35498,58 @@ agents.command("rename").description("Rename an agent in the presence list").arg
35215
35498
  const old = typeof oldName === "string" ? oldName.trim() : "";
35216
35499
  const renamed = typeof newName === "string" ? newName.trim() : "";
35217
35500
  if (!old || !renamed) {
35218
- console.error(chalk2.red("Both old and new names are required."));
35501
+ console.error(chalk3.red("Both old and new names are required."));
35219
35502
  process.exit(1);
35220
35503
  }
35221
35504
  try {
35222
35505
  const ok = renameAgent(old, renamed);
35223
35506
  if (!ok) {
35224
- console.error(chalk2.red(`Agent "${old}" not found.`));
35507
+ console.error(chalk3.red(`Agent "${old}" not found.`));
35225
35508
  process.exit(1);
35226
35509
  }
35227
35510
  if (opts.json) {
35228
35511
  console.log(JSON.stringify({ old_name: old, new_name: renamed, renamed: true }));
35229
35512
  } else {
35230
- console.log(chalk2.green(`Agent "${old}" renamed to "${renamed}".`));
35513
+ console.log(chalk3.green(`Agent "${old}" renamed to "${renamed}".`));
35231
35514
  }
35232
35515
  } catch (e) {
35233
- console.error(chalk2.red(e.message));
35516
+ console.error(chalk3.red(e.message));
35234
35517
  process.exit(1);
35235
35518
  }
35236
35519
  closeDb();
35237
35520
  });
35521
+ program2.command("whoami").description("Show current agent identity and online status").option("--from <agent>", "Explicit agent identity").action((opts) => {
35522
+ const envValue = process.env.CONVERSATIONS_AGENT_ID?.trim();
35523
+ const agent = resolveIdentity(opts.from);
35524
+ let source;
35525
+ if (opts.from) {
35526
+ source = "explicit (--from flag)";
35527
+ } else if (envValue) {
35528
+ source = "env var (CONVERSATIONS_AGENT_ID)";
35529
+ } else {
35530
+ const { join: join6 } = __require("path");
35531
+ const { homedir: homedir5 } = __require("os");
35532
+ const agentIdFile = join6(homedir5(), ".conversations", "agent-id");
35533
+ source = `auto-generated (${agentIdFile})`;
35534
+ }
35535
+ const presence = getPresence(agent);
35536
+ let onlineStatus;
35537
+ if (presence && presence.online) {
35538
+ const lastSeenMs = new Date(presence.last_seen_at + "Z").getTime();
35539
+ const agoMs = Date.now() - lastSeenMs;
35540
+ const agoSec = Math.floor(agoMs / 1000);
35541
+ const agoStr = agoSec < 60 ? `${agoSec}s ago` : `${Math.floor(agoSec / 60)}m ago`;
35542
+ onlineStatus = chalk3.green(`yes`) + chalk3.dim(` (last seen ${agoStr})`);
35543
+ } else if (presence) {
35544
+ onlineStatus = chalk3.red("no") + chalk3.dim(` (last seen ${presence.last_seen_at})`);
35545
+ } else {
35546
+ onlineStatus = chalk3.red("no") + chalk3.dim(" (no presence record)");
35547
+ }
35548
+ console.log(` ${chalk3.bold("Agent:")} ${chalk3.cyan(agent)}`);
35549
+ console.log(` ${chalk3.bold("Source:")} ${source}`);
35550
+ console.log(` ${chalk3.bold("Online:")} ${onlineStatus}`);
35551
+ closeDb();
35552
+ });
35238
35553
  program2.command("blockers").description("Check for unread blocking messages").option("--from <agent>", "Agent to check blockers for").option("--json", "Output as JSON").action((opts) => {
35239
35554
  const agent = resolveIdentity(opts.from);
35240
35555
  const blockers = getUnreadBlockers(agent);
@@ -35242,70 +35557,40 @@ program2.command("blockers").description("Check for unread blocking messages").o
35242
35557
  console.log(JSON.stringify(blockers, null, 2));
35243
35558
  } else {
35244
35559
  if (blockers.length === 0) {
35245
- console.log(chalk2.dim("No blocking messages."));
35560
+ console.log(chalk3.dim("No blocking messages."));
35246
35561
  } else {
35247
- console.log(chalk2.red.bold(`${blockers.length} blocking message(s):
35562
+ console.log(chalk3.red.bold(`${blockers.length} blocking message(s):
35248
35563
  `));
35249
35564
  for (const b of blockers) {
35250
- const where = b.space ? chalk2.magenta(`#${b.space}`) : chalk2.yellow("DM");
35251
- const time3 = chalk2.dim(b.created_at.slice(11, 19));
35252
- console.log(` ${chalk2.red(`[#${b.id}]`)} ${time3} ${chalk2.cyan(b.from_agent)} ${where}: ${b.content}`);
35565
+ const where = b.space ? chalk3.magenta(`#${b.space}`) : chalk3.yellow("DM");
35566
+ const time3 = chalk3.dim(b.created_at.slice(11, 19));
35567
+ console.log(` ${chalk3.red(`[#${b.id}]`)} ${time3} ${chalk3.cyan(b.from_agent)} ${where}: ${b.content}`);
35253
35568
  }
35254
- console.log(chalk2.dim(`
35569
+ console.log(chalk3.dim(`
35255
35570
  Acknowledge with: conversations mark-read ${blockers.map((b) => b.id).join(" ")}`));
35256
35571
  }
35257
35572
  }
35258
35573
  closeDb();
35259
35574
  });
35260
- program2.command("watch").description("Watch for new messages with desktop notifications").option("--from <agent>", "Your agent identity").option("--space <name>", "Watch a specific space").option("--interval <ms>", "Poll interval in milliseconds", parseInt).action((opts) => {
35575
+ program2.command("watch").description("Watch for new messages with desktop notifications").option("--from <agent>", "Your agent identity").option("--space <name>", "Watch a specific space").option("--all", "Watch DMs and all subscribed spaces").option("--interval <ms>", "Poll interval in milliseconds", parseInt).action((opts) => {
35261
35576
  const agent = resolveIdentity(opts.from);
35262
35577
  heartbeat(agent);
35263
35578
  const interval = Number.isFinite(opts.interval) && opts.interval > 0 ? opts.interval : 1000;
35264
35579
  const cols = Math.min(process.stdout.columns || 80, 100);
35580
+ let agentSpaces = [];
35581
+ if (opts.all) {
35582
+ const db2 = getDb();
35583
+ const rows = db2.prepare("SELECT space FROM space_members WHERE agent = ?").all(agent);
35584
+ agentSpaces = rows.map((r) => r.space);
35585
+ }
35586
+ const modeLabel = opts.all ? `DMs + ${agentSpaces.length} space(s)` : opts.space ? `Space: #${opts.space}` : "All DMs";
35265
35587
  console.log("");
35266
- console.log(chalk2.bold(` Conversations`) + chalk2.dim(` \u2014 watching as ${chalk2.cyan(agent)}`));
35267
- console.log(chalk2.dim(` ${opts.space ? `Space: #${opts.space}` : "All DMs"} \xB7 Poll: ${interval}ms \xB7 Ctrl+C to stop`));
35268
- console.log(chalk2.dim(" " + "\u2500".repeat(cols - 4)));
35588
+ console.log(chalk3.bold(` Conversations`) + chalk3.dim(` \u2014 watching as ${chalk3.cyan(agent)}`));
35589
+ console.log(chalk3.dim(` ${modeLabel} \xB7 Poll: ${interval}ms \xB7 Ctrl+C to stop`));
35590
+ console.log(chalk3.dim(" " + "\u2500".repeat(cols - 4)));
35269
35591
  console.log("");
35270
35592
  const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
35271
- const renderContent = (content) => {
35272
- const lines = content.split(`
35273
- `);
35274
- const rendered = [];
35275
- for (const line of lines) {
35276
- let l = line;
35277
- const h = l.match(/^(#{1,3})\s+(.+)/);
35278
- if (h) {
35279
- rendered.push(chalk2.bold(h[2]));
35280
- continue;
35281
- }
35282
- if (/^\s*[-*+]\s/.test(l)) {
35283
- rendered.push(" " + chalk2.dim("\u2022") + " " + renderInline(l.replace(/^\s*[-*+]\s/, "")));
35284
- continue;
35285
- }
35286
- const ol = l.match(/^\s*(\d+)[.)]\s(.*)/);
35287
- if (ol) {
35288
- rendered.push(" " + chalk2.dim(ol[1] + ".") + " " + renderInline(ol[2]));
35289
- continue;
35290
- }
35291
- if (l.startsWith(">")) {
35292
- rendered.push(chalk2.dim(" \u2502 ") + chalk2.italic(renderInline(l.replace(/^>\s?/, ""))));
35293
- continue;
35294
- }
35295
- if (l.trimStart().startsWith("```"))
35296
- continue;
35297
- if (l.trim() === "") {
35298
- rendered.push("");
35299
- continue;
35300
- }
35301
- rendered.push(renderInline(l));
35302
- }
35303
- return rendered.join(`
35304
- `);
35305
- };
35306
- const renderInline = (text) => {
35307
- return text.replace(/`([^`]+)`/g, (_, code) => chalk2.bgGray.white(` ${code} `)).replace(/\*\*\*(.+?)\*\*\*/g, (_, t) => chalk2.bold.italic(t)).replace(/\*\*(.+?)\*\*/g, (_, t) => chalk2.bold(t)).replace(/\*(.+?)\*/g, (_, t) => chalk2.italic(t)).replace(/~~(.+?)~~/g, (_, t) => chalk2.strikethrough(t));
35308
- };
35593
+ const { renderContent: renderContent2 } = (init_terminal_markdown(), __toCommonJS(exports_terminal_markdown));
35309
35594
  const desktopNotify = (title, body) => {
35310
35595
  if (process.platform === "darwin") {
35311
35596
  try {
@@ -35317,52 +35602,78 @@ program2.command("watch").description("Watch for new messages with desktop notif
35317
35602
  }
35318
35603
  };
35319
35604
  const renderMessage = (msg) => {
35320
- const time3 = chalk2.dim(msg.created_at.slice(11, 19));
35321
- const where = msg.space ? chalk2.magenta(`#${msg.space}`) : chalk2.yellow("DM");
35322
- const priority = msg.priority !== "normal" ? msg.priority === "urgent" ? chalk2.red.bold(` [${msg.priority}]`) : msg.priority === "high" ? chalk2.yellow(` [${msg.priority}]`) : chalk2.dim(` [${msg.priority}]`) : "";
35323
- const blocking = msg.blocking ? chalk2.red.bold(" \u26A0 BLOCKER") : "";
35324
- const sender = chalk2.cyan.bold(msg.from_agent);
35605
+ const time3 = chalk3.dim(msg.created_at.slice(11, 19));
35606
+ const where = msg.space ? chalk3.magenta(`#${msg.space}`) : chalk3.yellow("DM");
35607
+ const priority = msg.priority !== "normal" ? msg.priority === "urgent" ? chalk3.red.bold(` [${msg.priority}]`) : msg.priority === "high" ? chalk3.yellow(` [${msg.priority}]`) : chalk3.dim(` [${msg.priority}]`) : "";
35608
+ const blocking = msg.blocking ? chalk3.red.bold(" \u26A0 BLOCKER") : "";
35609
+ const sender = chalk3.cyan.bold(msg.from_agent);
35325
35610
  console.log(` ${sender} ${where} ${time3}${priority}${blocking}`);
35326
- const content = renderContent(msg.content);
35611
+ const content = renderContent2(msg.content);
35327
35612
  const indented = content.split(`
35328
35613
  `).map((l) => " " + l).join(`
35329
35614
  `);
35330
35615
  console.log(indented);
35331
- console.log(chalk2.dim(" " + "\xB7".repeat(Math.min(cols - 8, 60))));
35616
+ console.log(chalk3.dim(" " + "\xB7".repeat(Math.min(cols - 8, 60))));
35332
35617
  console.log("");
35333
35618
  };
35334
- const recent = readMessages({
35335
- to: opts.space ? undefined : agent,
35336
- space: opts.space,
35337
- limit: 20,
35338
- order: "asc"
35339
- });
35340
- if (recent.length > 0) {
35341
- console.log(chalk2.dim(` \u2500\u2500 Recent messages (${recent.length}) \u2500\u2500
35619
+ if (opts.all) {
35620
+ const dmRecent = readMessages({ to: agent, limit: 20, order: "asc" });
35621
+ const spaceRecent = [];
35622
+ for (const sp of agentSpaces) {
35623
+ spaceRecent.push(...readMessages({ space: sp, limit: 10, order: "asc" }));
35624
+ }
35625
+ const recent = [...dmRecent, ...spaceRecent].sort((a, b) => a.created_at.localeCompare(b.created_at)).slice(-20);
35626
+ if (recent.length > 0) {
35627
+ console.log(chalk3.dim(` \u2500\u2500 Recent messages (${recent.length}) \u2500\u2500
35628
+ `));
35629
+ for (const msg of recent) {
35630
+ renderMessage(msg);
35631
+ }
35632
+ console.log(chalk3.dim(` \u2500\u2500 Live \u2500\u2500
35342
35633
  `));
35343
- for (const msg of recent) {
35344
- renderMessage(msg);
35345
35634
  }
35346
- console.log(chalk2.dim(` \u2500\u2500 Live \u2500\u2500
35635
+ } else {
35636
+ const recent = readMessages({
35637
+ to: opts.space ? undefined : agent,
35638
+ space: opts.space,
35639
+ limit: 20,
35640
+ order: "asc"
35641
+ });
35642
+ if (recent.length > 0) {
35643
+ console.log(chalk3.dim(` \u2500\u2500 Recent messages (${recent.length}) \u2500\u2500
35347
35644
  `));
35348
- }
35349
- startPolling2({
35350
- to_agent: opts.space ? undefined : agent,
35351
- space: opts.space,
35352
- interval_ms: interval,
35353
- on_messages: (messages) => {
35354
- for (const msg of messages) {
35355
- if (msg.from_agent === agent)
35356
- continue;
35645
+ for (const msg of recent) {
35357
35646
  renderMessage(msg);
35358
- const where = msg.space ? `#${msg.space}` : "DM";
35359
- const preview = msg.content.replace(/[*#`~_>\-]/g, "").slice(0, 150);
35360
- desktopNotify(`${msg.from_agent} (${where})`, preview);
35361
35647
  }
35648
+ console.log(chalk3.dim(` \u2500\u2500 Live \u2500\u2500
35649
+ `));
35362
35650
  }
35363
- });
35651
+ }
35652
+ const onNewMessages = (messages) => {
35653
+ for (const msg of messages) {
35654
+ if (msg.from_agent === agent)
35655
+ continue;
35656
+ renderMessage(msg);
35657
+ const where = msg.space ? `#${msg.space}` : "DM";
35658
+ const preview = msg.content.replace(/[*#`~_>\-]/g, "").slice(0, 150);
35659
+ desktopNotify(`${msg.from_agent} (${where})`, preview);
35660
+ }
35661
+ };
35662
+ if (opts.all) {
35663
+ startPolling2({ to_agent: agent, interval_ms: interval, on_messages: onNewMessages });
35664
+ for (const sp of agentSpaces) {
35665
+ startPolling2({ space: sp, interval_ms: interval, on_messages: onNewMessages });
35666
+ }
35667
+ } else {
35668
+ startPolling2({
35669
+ to_agent: opts.space ? undefined : agent,
35670
+ space: opts.space,
35671
+ interval_ms: interval,
35672
+ on_messages: onNewMessages
35673
+ });
35674
+ }
35364
35675
  process.on("SIGINT", () => {
35365
- console.log(chalk2.dim(`
35676
+ console.log(chalk3.dim(`
35366
35677
  Stopped watching.`));
35367
35678
  closeDb();
35368
35679
  process.exit(0);
@@ -35372,15 +35683,19 @@ program2.command("mcp").description("Start MCP server").action(async () => {
35372
35683
  const { startMcpServer: startMcpServer2 } = await Promise.resolve().then(() => (init_mcp2(), exports_mcp));
35373
35684
  await startMcpServer2();
35374
35685
  });
35375
- program2.command("dashboard").description("Start web dashboard").option("--port <port>", "Port to listen on", parseInt).option("--host <host>", "Host to bind (default: 127.0.0.1)").action(async (opts) => {
35686
+ program2.command("dashboard").description("Start web dashboard").option("--port <port>", "Port to listen on", parseInt).option("--host <host>", "Host to bind (default: 127.0.0.1)").option("--open", "Auto-open dashboard in browser").action(async (opts) => {
35376
35687
  const { startDashboardServer: startDashboardServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
35377
35688
  const port = Number.isFinite(opts.port) && opts.port >= 0 && opts.port <= 65535 ? opts.port : 0;
35378
- startDashboardServer2(port, opts.host);
35689
+ const server2 = startDashboardServer2(port, opts.host);
35690
+ if (opts.open) {
35691
+ const { exec } = __require("child_process");
35692
+ exec(`open http://localhost:${server2.port}`);
35693
+ }
35379
35694
  });
35380
35695
  program2.action(() => {
35381
35696
  if (!process.stdin.isTTY) {
35382
- console.error(chalk2.red("Interactive mode requires a TTY terminal."));
35383
- console.error(chalk2.dim("Use subcommands (send, read, sessions, etc.) for non-interactive use."));
35697
+ console.error(chalk3.red("Interactive mode requires a TTY terminal."));
35698
+ console.error(chalk3.dim("Use subcommands (send, read, sessions, etc.) for non-interactive use."));
35384
35699
  process.exit(1);
35385
35700
  }
35386
35701
  const agent = resolveIdentity();