@ouro.bot/cli 0.1.0-alpha.263 → 0.1.0-alpha.264

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.264",
6
+ "changes": [
7
+ "fix(bluebubbles): dedup `updated-message` webhooks BEFORE running repair+hydrate+VLM. BlueBubbles routinely sends a `new-message` webhook for a fresh message, then follows up seconds later with one or more `updated-message` webhooks for delivery/read status. The BB sense's `repairEvent` path promotes updated-message events with recoverable content back to `message` kind, which re-runs the full hydration pipeline \u2014 including a second MiniMax VLM describe call on the same image. Verified live on 2026-04-08T00:58Z: two sequential VLM describes for attachment guid 317E37EB-..., 13.7s + 14.0s each, for the exact same 291KB JPEG, triggered by a `new-message` followed 3s later by an `updated-message` for the same guid. The downstream `handleBlueBubblesNormalizedEvent` dedup check was firing correctly but too late (after the expensive VLM round-trip). Fix: add a pre-repair dedup check in `handleBlueBubblesEvent` that consults the inbound sidecar by messageGuid and short-circuits before calling `client.repairEvent(...)`. New nerves event `senses.bluebubbles_repair_skipped_duplicate` at warn level for observability."
8
+ ]
9
+ },
4
10
  {
5
11
  "version": "0.1.0-alpha.263",
6
12
  "changes": [
@@ -859,7 +859,43 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
859
859
  async function handleBlueBubblesEvent(payload, deps = {}) {
860
860
  const resolvedDeps = { ...defaultDeps, ...deps };
861
861
  const client = resolvedDeps.createClient();
862
- const event = await client.repairEvent((0, model_1.normalizeBlueBubblesEvent)(payload));
862
+ const normalized = (0, model_1.normalizeBlueBubblesEvent)(payload);
863
+ // Pre-repair dedup: if we've already processed this messageGuid, skip the
864
+ // repair+hydrate path entirely. Applies to BOTH `kind: "message"` AND
865
+ // `kind: "mutation"` events — BlueBubbles often sends a `new-message`
866
+ // webhook for a fresh message AND one or more follow-up `updated-message`
867
+ // webhooks for delivery/read status. The mutation path (inside repairEvent)
868
+ // can promote an updated-message back to a message if it has recoverable
869
+ // content, which then re-runs the full VLM-describe pipeline on the same
870
+ // attachment.
871
+ //
872
+ // Without this early check, we paid DOUBLE latency and double tokens on
873
+ // every image-bearing message. Verified live on 2026-04-08T00:58Z: two
874
+ // sequential VLM describes for attachment guid 317E37EB-..., 13.7s +
875
+ // 14.0s each, for the exact same 291KB JPEG — triggered by a sequence of
876
+ // `new-message` followed ~3s later by `updated-message` for the same guid.
877
+ //
878
+ // We still route the skip through `handleBlueBubblesNormalizedEvent` so
879
+ // the downstream `already_processed` path fires its observability events
880
+ // and the caller sees a consistent return shape.
881
+ const agentName = resolvedDeps.getAgentName();
882
+ if (normalized.messageGuid
883
+ && (0, inbound_log_1.hasRecordedBlueBubblesInbound)(agentName, normalized.chat.sessionKey, normalized.messageGuid)) {
884
+ (0, runtime_1.emitNervesEvent)({
885
+ level: "warn",
886
+ component: "senses",
887
+ event: "senses.bluebubbles_repair_skipped_duplicate",
888
+ message: "skipped repair+hydrate for already-processed bluebubbles messageGuid",
889
+ meta: {
890
+ messageGuid: normalized.messageGuid,
891
+ sessionKey: normalized.chat.sessionKey,
892
+ eventType: normalized.eventType,
893
+ normalizedKind: normalized.kind,
894
+ },
895
+ });
896
+ return handleBlueBubblesNormalizedEvent(normalized, resolvedDeps, "webhook");
897
+ }
898
+ const event = await client.repairEvent(normalized);
863
899
  return handleBlueBubblesNormalizedEvent(event, resolvedDeps, "webhook");
864
900
  }
865
901
  function countPendingRecoveryCandidates(agentName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.263",
3
+ "version": "0.1.0-alpha.264",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",