@inetafrica/open-claudia 2.2.11 → 2.2.13

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.2.13
4
+ - **Security fix**: `intro-flow.handleInbound` no longer auto-claims ownership on *any* first inbound message. Previously, if `people.json` had no owner record (fresh pod / fresh install), the very first inbound — including a `/start` tap from a t.me deep-link — would register the sender as the bot owner. In AgentSpace pods this meant anyone who guessed or was sent the bot username could take over a pod simply by clicking Start. The bootstrap path now requires either (a) pod mode (`AGENTSPACE_POD_TOKEN` set) with the inbound chat id pre-seeded in `TELEGRAM_CHAT_ID` env, or (b) local mode with the inbound message literally starting with `/auth`. Anything else gets a "no owner configured" reply and an `intro.bootstrap-refused` audit entry. Existing owner records are unaffected. Follow-up work needed in the AgentSpace backend provisioner to seed `TELEGRAM_CHAT_ID` at pod creation time from the provisioning user's Telegram chat id; until that lands, new pods will refuse all inbound until an operator manually seeds the env.
5
+
6
+ ## v2.2.12
7
+ - Cross-channel relay (`open-claudia send-to`, and by extension cron/wakeup-fired messages and any other caller of `relay.send`) now passes `parseMode: "Markdown"` when the resolved adapter is Telegram. Previously `relay.send` called `adapter.send(channelId, text)` with no opts, so the Telegram adapter never set `parse_mode` and every relayed message went out as plain text — `*bold*`, backticks, and `_italic_` rendered as literal characters. The main reply path already injected this via `core/runner.js:25-28`; relay was the missing twin. Non-telegram adapters are unaffected.
8
+
3
9
  ## v2.2.11
4
10
  - Telegram replies now get channel-specific formatting guidance in the system prompt: short mobile-friendly sections, single-asterisk Telegram Markdown labels, no tables/headings/Markdown links, and no noisy quotes/backticks around normal business terms.
5
11
  - Telegram final/progress messages now request Markdown parse mode, and edited messages support the same parse mode with a plain-text fallback if Telegram rejects the markup.
@@ -112,11 +112,32 @@ async function handleInbound(envelope, sendFn) {
112
112
  people.seedOwnerFromLegacy();
113
113
  }
114
114
  if (!people.hasOwnerRecord()) {
115
+ // No owner record yet. Two failure modes we are guarding against:
116
+ // (a) AgentSpace pods: random Telegram users finding the bot username
117
+ // and auto-claiming ownership just by sending /start.
118
+ // (b) Local installs: any inbound (button taps, joins, automated
119
+ // crawls) claiming ownership before the actual operator messages.
120
+ // Pod mode: only chat ids pre-seeded into TELEGRAM_CHAT_ID may claim.
121
+ // Local mode: only an explicit "/auth" message may claim.
122
+ const podMode = !!process.env.AGENTSPACE_POD_TOKEN;
123
+ const allowedChatIds = String(process.env.TELEGRAM_CHAT_ID || "")
124
+ .split(",").map((s) => s.trim()).filter(Boolean);
125
+ if (podMode) {
126
+ if (allowedChatIds.length === 0 || !allowedChatIds.includes(String(channelId))) {
127
+ await sendFn("This bot has no owner configured yet. The AgentSpace operator needs to seed the owner chat id before the bot can be used.");
128
+ audit.log("intro.bootstrap-refused", { reason: "pod-mode-not-preseeded", adapter, channelId });
129
+ return true;
130
+ }
131
+ } else if (!/^\/auth\b/i.test(text)) {
132
+ await sendFn("This bot has no owner yet. Send /auth from the operator's chat to claim ownership.");
133
+ audit.log("intro.bootstrap-refused", { reason: "local-mode-non-auth-message", adapter, channelId });
134
+ return true;
135
+ }
115
136
  const displayName = displayNameFromEnvelope(envelope) || "Owner";
116
- const person = people.add({ name: displayName, isOwner: true, bio: "Auto-registered on first message" });
137
+ const person = people.add({ name: displayName, isOwner: true, bio: "Auto-registered as bootstrap owner" });
117
138
  try { people.linkHandle(person.id, { adapter, channelId, displayName, approvedBy: "bootstrap" }); } catch (e) {}
118
139
  try { bootstrapOwner({ chatId: channelId, name: displayName, username: usernameFromEnvelope(envelope) }); } catch (e) {}
119
- audit.log("people.bootstrap-owner", { personId: person.id, adapter, channelId });
140
+ audit.log("people.bootstrap-owner", { personId: person.id, adapter, channelId, mode: podMode ? "pod" : "local" });
120
141
  await sendFn(`Welcome, ${displayName}. You're registered as the bot owner. Send /start to begin.`);
121
142
  return true;
122
143
  }
package/core/relay.js CHANGED
@@ -35,7 +35,8 @@ async function send({ text, target, from = null, kind = "relay" }) {
35
35
  let messageId = null;
36
36
  let errorMsg = null;
37
37
  try {
38
- const result = await adapter.send(resolved.channelId, String(text));
38
+ const sendOpts = adapter.type === "telegram" ? { parseMode: "Markdown" } : {};
39
+ const result = await adapter.send(resolved.channelId, String(text), sendOpts);
39
40
  if (typeof result === "object" && result && "messageId" in result) { messageId = result.messageId; ok = !!messageId; }
40
41
  else { messageId = result; ok = !!result; }
41
42
  } catch (e) { errorMsg = e.message; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "2.2.11",
3
+ "version": "2.2.13",
4
4
  "description": "Your always-on AI coding assistant — Claude Code, Cursor Agent, and OpenAI Codex via Telegram or Kazee Chat",
5
5
  "main": "bot.js",
6
6
  "bin": {