@michael-joseph-miller/ant-bot 0.2.0 → 0.2.1

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/dist/server.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  SettingsSchema,
19
19
  UpdateBotRequest,
20
20
  UpdateConnectorRequest
21
- } from "./chunk-RMDMUCVS.js";
21
+ } from "./chunk-F3D2K4WZ.js";
22
22
  import {
23
23
  findWebDist,
24
24
  nodeLocateDeps,
@@ -498,6 +498,28 @@ var Store = class {
498
498
  this.db.prepare(`DELETE FROM routines WHERE bot_id=?`).run(id);
499
499
  if (bot.threadId) this.db.prepare(`DELETE FROM threads WHERE id=?`).run(bot.threadId);
500
500
  }
501
+ /**
502
+ * Clear a bot's conversation and its SDK session, keeping everything that defines the bot.
503
+ *
504
+ * Deliberately narrow. Memory, skills, connectors, routines and the bot's files all survive —
505
+ * those are the bot. What goes is the accumulated conversation: the messages in its thread and
506
+ * the `session_id` the SDK resumes from, which is what makes a turn carry prior context.
507
+ *
508
+ * Messages are deleted rather than the thread, so the thread id every other row points at
509
+ * stays valid; the FTS triggers keep the search index in step.
510
+ */
511
+ resetBotSession(id) {
512
+ const bot = this.getBot(id);
513
+ if (!bot) return null;
514
+ let messagesDeleted = 0;
515
+ if (bot.threadId) {
516
+ const info = this.db.prepare(`DELETE FROM messages WHERE thread_id=?`).run(bot.threadId);
517
+ messagesDeleted = info.changes;
518
+ this.db.prepare(`UPDATE threads SET last_read_at=0 WHERE id=?`).run(bot.threadId);
519
+ }
520
+ this.db.prepare(`UPDATE bots SET session_id=NULL, attention='none' WHERE id=?`).run(id);
521
+ return { messagesDeleted };
522
+ }
501
523
  duplicateBot(id) {
502
524
  const src = this.getBot(id);
503
525
  if (!src) return null;
@@ -1319,6 +1341,9 @@ async function* runTurn(req) {
1319
1341
  switch (m.type) {
1320
1342
  case "system":
1321
1343
  if (m.subtype === "init" && m.session_id) yield { kind: "session", sessionId: m.session_id };
1344
+ if (m.subtype === "init" && Array.isArray(m.mcp_servers)) {
1345
+ yield { kind: "mcp_status", mcpStatus: m.mcp_servers };
1346
+ }
1322
1347
  break;
1323
1348
  case "stream_event": {
1324
1349
  const ev = m.event;
@@ -1897,6 +1922,25 @@ Pick the one you need and install just it, e.g. ${args.source.replace(/^https?:\
1897
1922
  case "session":
1898
1923
  if (ev.sessionId) store.updateBot(bot.id, { sessionId: ev.sessionId });
1899
1924
  break;
1925
+ // A connector that does not come up gives the bot no tools and no way to say why — it
1926
+ // simply behaves as though the connector were never assigned. Surfacing the SDK's own
1927
+ // verdict is the difference between "my bot ignores my connector" and a stated reason.
1928
+ case "mcp_status": {
1929
+ const bad = (ev.mcpStatus ?? []).filter((m) => m.status !== "connected");
1930
+ if (!bad.length) break;
1931
+ for (const m of bad) {
1932
+ log6.warn(`connector "${m.name}" did not connect for ${bot.slug}: ${m.status}${m.error ? ` \u2014 ${m.error}` : ""}`);
1933
+ }
1934
+ bus.publish({
1935
+ type: "notify",
1936
+ botId: bot.id,
1937
+ threadId: job.threadId,
1938
+ title: "Connector unavailable",
1939
+ body: bad.map((m) => `${m.name}: ${describeMcpStatus(m.status)}`).join("; "),
1940
+ level: "warn"
1941
+ });
1942
+ break;
1943
+ }
1900
1944
  case "text":
1901
1945
  if (ev.text) {
1902
1946
  const cur = store.getMessage(msgId);
@@ -1960,6 +2004,20 @@ Pick the one you need and install just it, e.g. ${args.source.replace(/^https?:\
1960
2004
  }
1961
2005
  }
1962
2006
  };
2007
+ function describeMcpStatus(status) {
2008
+ switch (status) {
2009
+ case "needs-auth":
2010
+ return "needs authentication \u2014 the server rejected the credentials it was given (or was given none)";
2011
+ case "failed":
2012
+ return "failed to start \u2014 check the command or URL with `antbot connector test`";
2013
+ case "pending":
2014
+ return "did not finish connecting in time";
2015
+ case "disabled":
2016
+ return "is disabled";
2017
+ default:
2018
+ return status;
2019
+ }
2020
+ }
1963
2021
  function summarizeTool(name, input) {
1964
2022
  const o = input ?? {};
1965
2023
  const s = (k) => typeof o[k] === "string" ? o[k] : "";
@@ -2660,6 +2718,17 @@ function registerCoreRoutes(f, app) {
2660
2718
  store.deleteBot(req.params.id);
2661
2719
  return { ok: true };
2662
2720
  });
2721
+ f.post("/api/bots/:id/reset", async (req, reply) => {
2722
+ const bot = store.getBot(req.params.id);
2723
+ if (!bot) return reply.code(404).send({ error: "No such bot" });
2724
+ if (bot.state === "running" || bot.state === "queued") {
2725
+ return reply.code(409).send({ error: "This bot is working. Stop it first, then start fresh." });
2726
+ }
2727
+ const result = store.resetBotSession(bot.id);
2728
+ if (!result) return reply.code(404).send({ error: "No such bot" });
2729
+ if (bot.threadId) bus.publish({ type: "thread.updated", threadId: bot.threadId, botId: bot.id, threadId2: bot.threadId });
2730
+ return { ok: true, ...result };
2731
+ });
2663
2732
  f.post("/api/bots/:id/duplicate", async (req, reply) => {
2664
2733
  try {
2665
2734
  const copy = store.duplicateBot(req.params.id);
@@ -2951,7 +3020,13 @@ async function probeConnector(config, opts = {}) {
2951
3020
  return await probeStdio(config, timeoutMs);
2952
3021
  }
2953
3022
  if (type === "http") {
2954
- return await probeHttp(config, timeoutMs);
3023
+ const result = await probeHttp(config, timeoutMs);
3024
+ const headers = config.headers ?? {};
3025
+ const authed = Object.keys(headers).some((h) => /^(authorization|x-api-key|api-key)$/i.test(h));
3026
+ if (result.ok && !authed) {
3027
+ result.authHint = "This server was reached without any credential. If it needs one, the tools will list here but a bot will still see none \u2014 add an Authorization header, e.g. {{secret:NAME}}.";
3028
+ }
3029
+ return result;
2955
3030
  }
2956
3031
  if (type === "sse") return failed("testing is not supported for sse connectors \u2014 assign it to a bot and run a turn");
2957
3032
  return failed(`unknown transport: ${String(type)}`);