@ouro.bot/cli 0.1.0-alpha.230 → 0.1.0-alpha.231

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.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.231",
6
+ "changes": [
7
+ "fix: defense-in-depth group chat blocking for proactive BB delivery — sendProactiveBlueBubblesMessageToSession now rejects group chat keys (;+;) unless intent is explicit_cross_chat (bridge/delegation responses). All upper-layer paths (surface tool, send_message tool, inner-dialog delegation) now filter to DM sessions only for proactive outreach, and pass explicit_cross_chat intent for bridge/delegation returns where group responses are legitimate."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.230",
6
12
  "changes": [
@@ -592,11 +592,10 @@ exports.sessionToolDefinitions = [
592
592
  const bbDir = path.join(agentRoot, "state", "sessions", friendId, "bluebubbles");
593
593
  if (fs.existsSync(bbDir)) {
594
594
  const files = fs.readdirSync(bbDir).filter((f) => f.endsWith(".json"));
595
- // Prefer individual chats (;-;) over group chats (;+;) for proactive DMs
595
+ // Only use DM sessions (;-;) for proactive delivery — never group chats (;+;)
596
596
  const dmFile = files.find((f) => f.includes(";-;"));
597
- const chosen = dmFile ?? files[0];
598
- if (chosen) {
599
- resolvedKey = chosen.replace(/\.json$/, "");
597
+ if (dmFile) {
598
+ resolvedKey = dmFile.replace(/\.json$/, "");
600
599
  }
601
600
  }
602
601
  }
@@ -118,6 +118,7 @@ exports.surfaceToolDefinition = {
118
118
  friendId: bridgeTarget.friendId,
119
119
  sessionKey: bridgeTarget.key,
120
120
  text: content,
121
+ intent: "explicit_cross_chat",
121
122
  });
122
123
  if (proactiveResult.delivered) {
123
124
  // Inject surfaced content into the target session so it knows what was delivered
@@ -145,8 +146,8 @@ exports.surfaceToolDefinition = {
145
146
  // Priority 2: Try proactive delivery first, then queue to freshest session
146
147
  const allFriendSessions = (0, session_activity_1.listSessionActivity)({ sessionsDir, friendsDir, agentName, activeThresholdMs: Number.MAX_SAFE_INTEGER })
147
148
  .filter((s) => s.friendId === friendId && s.channel !== "inner");
148
- // 2a: Attempt proactive BB delivery on any BB session (prefer freshest)
149
- const bbSession = allFriendSessions.find((s) => s.channel === "bluebubbles");
149
+ // 2a: Attempt proactive BB delivery on a DM session (;-; = individual, never ;+; = group)
150
+ const bbSession = allFriendSessions.find((s) => s.channel === "bluebubbles" && s.key.includes(";-;"));
150
151
  if (bbSession) {
151
152
  const { sendProactiveBlueBubblesMessageToSession } = await Promise.resolve().then(() => __importStar(require("../senses/bluebubbles")));
152
153
  const proactiveResult = await sendProactiveBlueBubblesMessageToSession({
@@ -1137,6 +1137,21 @@ async function sendProactiveBlueBubblesMessageToSession(params, deps = {}) {
1137
1137
  });
1138
1138
  return { delivered: false, reason: "missing_target" };
1139
1139
  }
1140
+ // Proactive outreach to individuals must go to DMs, never group chats.
1141
+ // Explicit cross-chat responses (bridge completions, delegation returns) ARE allowed to groups
1142
+ // because the request originated from that group.
1143
+ /* v8 ignore start -- group gate: only fires when proactive send targets a group session @preserve */
1144
+ if (chat.isGroup && params.intent !== "explicit_cross_chat") {
1145
+ (0, runtime_1.emitNervesEvent)({
1146
+ level: "warn",
1147
+ component: "senses",
1148
+ event: "senses.bluebubbles_proactive_group_blocked",
1149
+ message: "proactive send blocked: would route to group chat",
1150
+ meta: { friendId: params.friendId, sessionKey: params.sessionKey, chatGuid: chat.chatGuid ?? null, intent: params.intent ?? null },
1151
+ });
1152
+ return { delivered: false, reason: "group_blocked" };
1153
+ }
1154
+ /* v8 ignore stop */
1140
1155
  try {
1141
1156
  await client.sendText({ chat, text: params.text });
1142
1157
  (0, runtime_1.emitNervesEvent)({
@@ -300,6 +300,7 @@ async function tryDeliverDelegatedCompletion(target, outboundEnvelope) {
300
300
  friendId: target.friendId,
301
301
  sessionKey: target.key,
302
302
  text: outboundEnvelope.content,
303
+ intent: "explicit_cross_chat",
303
304
  });
304
305
  return result.delivered;
305
306
  }
@@ -417,14 +418,15 @@ async function routeDelegatedCompletion(agentRoot, agentName, completion, draine
417
418
  return;
418
419
  }
419
420
  // Priority 2: Freshest active friend session.
420
- const freshest = (0, session_activity_1.findFreshestFriendSession)({
421
+ // For BB, prefer DM sessions (;-;) over group chats (;+;) — proactive outreach should never land in groups.
422
+ const allFriendSessions = (0, session_activity_1.listSessionActivity)({
421
423
  sessionsDir: path.join(agentRoot, "state", "sessions"),
422
424
  friendsDir: path.join(agentRoot, "friends"),
423
425
  agentName,
424
- friendId: delegatedFrom.friendId,
425
- activeOnly: true,
426
- });
427
- if (freshest && freshest.channel !== "inner") {
426
+ }).filter((s) => s.friendId === delegatedFrom.friendId && s.channel !== "inner");
427
+ const bbDm = allFriendSessions.find((s) => s.channel === "bluebubbles" && s.key.includes(";-;"));
428
+ const freshest = bbDm ?? allFriendSessions.find((s) => s.channel !== "bluebubbles" || s.key.includes(";-;")) ?? allFriendSessions[0];
429
+ if (freshest) {
428
430
  if (await tryDeliverDelegatedCompletion(freshest, outboundEnvelope)) {
429
431
  advanceObligationQuietly(agentName, obligationId, { status: "returned", returnedAt: timestamp, returnTarget: "freshest-session" });
430
432
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.230",
3
+ "version": "0.1.0-alpha.231",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",