@ouro.bot/cli 0.1.0-alpha.489 → 0.1.0-alpha.490
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/README.md +4 -3
- package/changelog.json +6 -0
- package/dist/heart/config.js +9 -1
- package/dist/heart/core.js +0 -2
- package/dist/heart/session-events.js +4 -1
- package/dist/senses/bluebubbles/index.js +47 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,9 @@ Current first-class senses:
|
|
|
22
22
|
- `cli`
|
|
23
23
|
- `teams`
|
|
24
24
|
- `bluebubbles`
|
|
25
|
-
- `
|
|
25
|
+
- `mail`
|
|
26
|
+
|
|
27
|
+
(MCP is a bridge for developer tools — a separate channel, not a sense. See `src/heart/mcp/` for the implementation.)
|
|
26
28
|
|
|
27
29
|
Current provider ids:
|
|
28
30
|
|
|
@@ -45,7 +47,7 @@ The shared harness lives in `src/`:
|
|
|
45
47
|
- `src/repertoire/`
|
|
46
48
|
Tool registry (split into category modules: files, shell, notes, bridge, session, continuity, flow, surface, config, and sense-specific tools), coding orchestration, task tools, shared API client, and integration clients (Graph, ADO, GitHub).
|
|
47
49
|
- `src/senses/`
|
|
48
|
-
CLI (with TUI in senses/cli/), Teams, BlueBubbles (in senses/bluebubbles/),
|
|
50
|
+
CLI (with TUI in senses/cli/), Teams, BlueBubbles (in senses/bluebubbles/), Mail (in senses/mail.ts), activity transport, inner-dialog orchestration, and contextual heartbeat. The MCP bridge is at `src/heart/mcp/`, not here.
|
|
49
51
|
- `src/nerves/`
|
|
50
52
|
Structured runtime logging and coverage-audit infrastructure.
|
|
51
53
|
- `src/__tests__/`
|
|
@@ -267,4 +269,3 @@ See `skills/configure-dev-tools.md` for the full tool inventory and troubleshoot
|
|
|
267
269
|
## A Note To Future Maintainers
|
|
268
270
|
|
|
269
271
|
If you discover a doc that lies, fix it or remove it. Accuracy is a kindness. A future agent should not have to untangle a fossil record just to understand where their hands are.
|
|
270
|
-
# Production SPA serving
|
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.490",
|
|
6
|
+
"changes": [
|
|
7
|
+
"BlueBubbles group echo self-talk fix. The BB ingest path previously relied solely on the payload's `isFromMe` flag to detect the agent's own outbound messages — but in groups, BlueBubbles sometimes broadcasts the echo back through the webhook with that flag missing or false. Without a fallback, the agent would ingest its own message and reply to it (the user reported this in a group with their friend Rach: 'Slugger talking to himself'). New `bluebubbles.ownHandles` config field accepts the agent's known iMessage handles (phone numbers in any formatting, or email addresses); a fallback guard at the head of `handleBlueBubblesNormalizedEvent` filters any event whose `sender.externalId` matches a configured handle (case-insensitive, with phone-number normalization across +/space/paren/dash differences) and emits a `senses.bluebubbles_self_handle_filtered` warn-level nerve event so the case is observable. Direct chats are unaffected (their echoes already carry `isFromMe: true` reliably). Also folds in three trivial cleanups surfaced by the full-system audit: removed a stray `# Production SPA serving` heading from the README, removed a vestigial `// getPhrases removed` comment in bluebubbles/index.ts, and removed two unreachable `throw new Error('unreachable')` statements after `process.exit(1)` in heart/core.ts (process.exit returns `never` so TS already knows control doesn't continue)."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
4
10
|
{
|
|
5
11
|
"version": "0.1.0-alpha.489",
|
|
6
12
|
"changes": [
|
package/dist/heart/config.js
CHANGED
|
@@ -92,6 +92,7 @@ const DEFAULT_LOCAL_RUNTIME_CONFIG = {
|
|
|
92
92
|
serverUrl: "",
|
|
93
93
|
password: "",
|
|
94
94
|
accountId: "default",
|
|
95
|
+
ownHandles: [],
|
|
95
96
|
},
|
|
96
97
|
bluebubblesChannel: {
|
|
97
98
|
port: 18790,
|
|
@@ -284,17 +285,24 @@ function getTeamsChannelConfig() {
|
|
|
284
285
|
}
|
|
285
286
|
function getBlueBubblesConfig() {
|
|
286
287
|
const config = loadConfig();
|
|
287
|
-
const { serverUrl, password, accountId } = config.bluebubbles;
|
|
288
|
+
const { serverUrl, password, accountId, ownHandles } = config.bluebubbles;
|
|
288
289
|
if (!serverUrl.trim()) {
|
|
289
290
|
throw new Error("bluebubbles.serverUrl is required in this machine's agent-vault runtime config. Run `ouro connect bluebubbles --agent <agent>`.");
|
|
290
291
|
}
|
|
291
292
|
if (!password.trim()) {
|
|
292
293
|
throw new Error("bluebubbles.password is required in this machine's agent-vault runtime config. Run `ouro connect bluebubbles --agent <agent>`.");
|
|
293
294
|
}
|
|
295
|
+
const normalizedHandles = [];
|
|
296
|
+
for (const handle of ownHandles) {
|
|
297
|
+
const trimmed = handle.trim();
|
|
298
|
+
if (trimmed.length > 0)
|
|
299
|
+
normalizedHandles.push(trimmed);
|
|
300
|
+
}
|
|
294
301
|
return {
|
|
295
302
|
serverUrl: serverUrl.trim(),
|
|
296
303
|
password: password.trim(),
|
|
297
304
|
accountId: accountId.trim() || "default",
|
|
305
|
+
ownHandles: normalizedHandles,
|
|
298
306
|
};
|
|
299
307
|
}
|
|
300
308
|
function getBlueBubblesChannelConfig() {
|
package/dist/heart/core.js
CHANGED
|
@@ -121,7 +121,6 @@ async function getProviderRuntime(facing = "human") {
|
|
|
121
121
|
// eslint-disable-next-line no-console -- pre-boot guard: provider init failure
|
|
122
122
|
console.error(`\n[fatal] ${msg}\n`);
|
|
123
123
|
process.exit(1);
|
|
124
|
-
throw new Error("unreachable");
|
|
125
124
|
}
|
|
126
125
|
if (!_providerRuntimes[facing]) {
|
|
127
126
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -132,7 +131,6 @@ async function getProviderRuntime(facing = "human") {
|
|
|
132
131
|
meta: {},
|
|
133
132
|
});
|
|
134
133
|
process.exit(1);
|
|
135
|
-
throw new Error("unreachable");
|
|
136
134
|
}
|
|
137
135
|
return _providerRuntimes[facing].runtime;
|
|
138
136
|
}
|
|
@@ -963,7 +963,10 @@ function appendEvictedToArchive(sessPath, evictedEvents) {
|
|
|
963
963
|
}
|
|
964
964
|
}
|
|
965
965
|
function appendSyntheticAssistantEvent(envelope, content, recordedAt) {
|
|
966
|
-
|
|
966
|
+
// Use nextEventSequence(events) instead of `events.length + 1` so any gap
|
|
967
|
+
// (from pruning, archive replay, or self-heal dedup) cannot collide with
|
|
968
|
+
// an existing event id. Same fix pattern as line 1046.
|
|
969
|
+
const sequence = nextEventSequence(envelope.events);
|
|
967
970
|
const event = buildEventFromMessage({ role: "assistant", content }, sequence, recordedAt, "synthetic", null, null);
|
|
968
971
|
return {
|
|
969
972
|
...envelope,
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.enrichReactionText = enrichReactionText;
|
|
37
37
|
exports.createStatusBatcher = createStatusBatcher;
|
|
38
|
+
exports.isAgentSelfHandle = isAgentSelfHandle;
|
|
38
39
|
exports.handleBlueBubblesEvent = handleBlueBubblesEvent;
|
|
39
40
|
exports.catchUpMissedBlueBubblesMessages = catchUpMissedBlueBubblesMessages;
|
|
40
41
|
exports.recoverCapturedBlueBubblesInboundMessages = recoverCapturedBlueBubblesInboundMessages;
|
|
@@ -60,7 +61,6 @@ const channel_1 = require("../../mind/friends/channel");
|
|
|
60
61
|
const pending_1 = require("../../mind/pending");
|
|
61
62
|
const prompt_1 = require("../../mind/prompt");
|
|
62
63
|
const mcp_manager_1 = require("../../repertoire/mcp-manager");
|
|
63
|
-
// getPhrases removed — no longer needed after debug-activity cleanup
|
|
64
64
|
const runtime_1 = require("../../nerves/runtime");
|
|
65
65
|
const proactive_content_guard_1 = require("../proactive-content-guard");
|
|
66
66
|
const model_1 = require("./model");
|
|
@@ -159,6 +159,7 @@ const defaultDeps = {
|
|
|
159
159
|
createFriendStore: () => new store_file_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends")),
|
|
160
160
|
createFriendResolver: (store, params) => new resolver_1.FriendResolver(store, params),
|
|
161
161
|
createServer: http.createServer,
|
|
162
|
+
getOwnHandles: () => (0, config_1.getBlueBubblesConfig)().ownHandles,
|
|
162
163
|
};
|
|
163
164
|
const BLUEBUBBLES_RUNTIME_SYNC_INTERVAL_MS = 30_000;
|
|
164
165
|
const BLUEBUBBLES_CATCHUP_PAGE_SIZE = 50;
|
|
@@ -607,6 +608,32 @@ function isWebhookPasswordValid(url, expectedPassword) {
|
|
|
607
608
|
const provided = url.searchParams.get("password");
|
|
608
609
|
return !provided || provided === expectedPassword;
|
|
609
610
|
}
|
|
611
|
+
function normalizeHandleForSelfMatch(handle) {
|
|
612
|
+
const trimmed = handle.trim().toLowerCase();
|
|
613
|
+
if (!trimmed)
|
|
614
|
+
return "";
|
|
615
|
+
// Phone-shaped: strip everything but digits so +1 (415) 555-... matches 14155550000.
|
|
616
|
+
if (/[+\d]/.test(trimmed) && !trimmed.includes("@")) {
|
|
617
|
+
const digits = trimmed.replace(/\D/g, "");
|
|
618
|
+
if (digits.length >= 7)
|
|
619
|
+
return digits;
|
|
620
|
+
}
|
|
621
|
+
return trimmed;
|
|
622
|
+
}
|
|
623
|
+
function isAgentSelfHandle(senderExternalId, ownHandles) {
|
|
624
|
+
if (!senderExternalId || !senderExternalId.trim())
|
|
625
|
+
return false;
|
|
626
|
+
const target = normalizeHandleForSelfMatch(senderExternalId);
|
|
627
|
+
/* v8 ignore start -- target is non-empty by construction since senderExternalId was just verified non-whitespace */
|
|
628
|
+
if (!target)
|
|
629
|
+
return false;
|
|
630
|
+
/* v8 ignore stop */
|
|
631
|
+
for (const own of ownHandles) {
|
|
632
|
+
if (normalizeHandleForSelfMatch(own) === target)
|
|
633
|
+
return true;
|
|
634
|
+
}
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
610
637
|
async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
|
|
611
638
|
const client = resolvedDeps.createClient();
|
|
612
639
|
const agentName = resolvedDeps.getAgentName();
|
|
@@ -622,6 +649,25 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
|
|
|
622
649
|
});
|
|
623
650
|
return { handled: true, notifiedAgent: false, kind: event.kind, reason: "from_me" };
|
|
624
651
|
}
|
|
652
|
+
// Fallback self-detection: BlueBubbles sometimes broadcasts a group-chat
|
|
653
|
+
// outbound message back through the webhook with `isFromMe` missing/false.
|
|
654
|
+
// Without this guard the agent ingests its own message and replies to it
|
|
655
|
+
// ("Slugger talking to himself"). Compare the sender's externalId against
|
|
656
|
+
// the agent's known iMessage handles.
|
|
657
|
+
if (isAgentSelfHandle(event.sender.externalId, resolvedDeps.getOwnHandles())) {
|
|
658
|
+
(0, runtime_1.emitNervesEvent)({
|
|
659
|
+
level: "warn",
|
|
660
|
+
component: "senses",
|
|
661
|
+
event: "senses.bluebubbles_self_handle_filtered",
|
|
662
|
+
message: "filtered bluebubbles event whose sender matched an agent-owned handle (isFromMe was missing/false)",
|
|
663
|
+
meta: {
|
|
664
|
+
messageGuid: event.messageGuid,
|
|
665
|
+
kind: event.kind,
|
|
666
|
+
senderExternalId: event.sender.externalId,
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
return { handled: true, notifiedAgent: false, kind: event.kind, reason: "from_me" };
|
|
670
|
+
}
|
|
625
671
|
if (event.kind === "mutation") {
|
|
626
672
|
try {
|
|
627
673
|
resolvedDeps.recordMutation(resolvedDeps.getAgentName(), event);
|