@ouro.bot/cli 0.1.0-alpha.127 → 0.1.0-alpha.129

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,21 @@
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.129",
6
+ "changes": [
7
+ "Fixed final_answer truth-check false positive: agents with intent=complete can now deliver answers even when delegation suggests going inward.",
8
+ "Fixed BlueBubbles message deduplication: messages are recorded immediately to prevent duplicate processing on webhook retries.",
9
+ "Fixed session key normalization: listSessionActivity now compares sanitized keys so the current session is correctly excluded from other-session lists.",
10
+ "ouro auth with no args now shows auth-specific help instead of full CLI help."
11
+ ]
12
+ },
13
+ {
14
+ "version": "0.1.0-alpha.128",
15
+ "changes": [
16
+ "Error message sanitizer now strips HTML responses (Cloudflare challenge pages, error pages) in addition to JSON."
17
+ ]
18
+ },
4
19
  {
5
20
  "version": "0.1.0-alpha.127",
6
21
  "changes": [
@@ -238,8 +238,11 @@ function isExternalStateQuery(toolName, args) {
238
238
  return /\bgh\s+(pr|run|api|issue)\b/.test(cmd) || /\bnpm\s+(view|info|show)\b/.test(cmd);
239
239
  }
240
240
  function getFinalAnswerRetryError(mustResolveBeforeHandoff, intent, sawSteeringFollowUp, delegationDecision, sawSendMessageSelf, sawGoInward, sawQuerySession, currentObligation, innerJob, sawExternalStateQuery) {
241
- // 1. Delegation adherence: delegate-inward without evidence of inward action
242
- if (delegationDecision?.target === "delegate-inward" && !sawSendMessageSelf && !sawGoInward && !sawQuerySession) {
241
+ // 1. Delegation adherence: delegate-inward without evidence of inward action.
242
+ // Only enforce on the FIRST final_answer attempt if the agent persists with
243
+ // intent=complete, respect it. The delegation is a suggestion, not a hard gate.
244
+ // Without this escape, the agent gets stuck in a loop unable to respond.
245
+ if (delegationDecision?.target === "delegate-inward" && !sawSendMessageSelf && !sawGoInward && !sawQuerySession && intent !== "complete" && intent !== "blocked") {
243
246
  (0, runtime_1.emitNervesEvent)({
244
247
  event: "engine.delegation_adherence_rejected",
245
248
  component: "engine",
@@ -688,8 +688,14 @@ function parseAuthCommand(args) {
688
688
  continue;
689
689
  }
690
690
  }
691
- if (!agent)
692
- throw new Error(`Usage\n${usage()}`);
691
+ if (!agent) {
692
+ throw new Error([
693
+ "Usage:",
694
+ " ouro auth --agent <name> [--provider <provider>] Set up credentials",
695
+ " ouro auth verify --agent <name> [--provider <p>] Verify credentials work",
696
+ " ouro auth switch --agent <name> --provider <p> Switch active provider",
697
+ ].join("\n"));
698
+ }
693
699
  return provider ? { kind: "auth.run", agent, provider } : { kind: "auth.run", agent };
694
700
  }
695
701
  function parseReminderCommand(args) {
@@ -12,25 +12,33 @@ const auth_flow_1 = require("./daemon/auth-flow");
12
12
  const runtime_1 = require("../nerves/runtime");
13
13
  const PING_TIMEOUT_MS = 10_000;
14
14
  /**
15
- * Strip raw JSON API response bodies from error messages.
16
- * SDK errors often include the full response body: "400 {"type":"error","error":...}".
17
- * Extract just the HTTP status prefix or a short summary.
15
+ * Strip raw JSON/HTML API response bodies from error messages.
16
+ * SDK errors often include the full response: "400 {"type":"error",...}" or "403 <html>...".
17
+ * Extract just the HTTP status and a short human-readable summary.
18
18
  */
19
19
  function sanitizeErrorMessage(message) {
20
- // Match "NNN {json...}" pattern — keep the status code, drop the JSON
21
- const match = message.match(/^(\d{3})\s*\{/);
22
- if (match) {
23
- // Try to extract the inner message from the JSON
20
+ const statusMatch = message.match(/^(\d{3})\s/);
21
+ if (!statusMatch)
22
+ return message;
23
+ const status = statusMatch[1];
24
+ const body = message.slice(status.length).trim();
25
+ // HTML response (Cloudflare challenge, error pages, etc.)
26
+ if (body.startsWith("<") || body.includes("<!DOCTYPE") || body.includes("<html")) {
27
+ return `HTTP ${status}`;
28
+ }
29
+ // JSON response
30
+ if (body.startsWith("{")) {
24
31
  try {
25
- const json = JSON.parse(message.slice(match[1].length).trim());
32
+ const json = JSON.parse(body);
26
33
  const inner = json?.error?.message;
27
34
  if (typeof inner === "string" && inner && inner !== "Error") {
28
- return `${match[1]} ${inner}`;
35
+ return `${status} ${inner}`;
29
36
  }
30
37
  }
31
- catch { /* not valid JSON, fall through */ }
32
- return `HTTP ${match[1]}`;
38
+ catch { /* not valid JSON */ }
39
+ return `HTTP ${status}`;
33
40
  }
41
+ // Already clean (e.g., "401 Provided authentication token is expired.")
34
42
  return message;
35
43
  }
36
44
  function hasEmptyCredentials(provider, config) {
@@ -38,6 +38,7 @@ exports.findFreshestFriendSession = findFreshestFriendSession;
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const runtime_1 = require("../nerves/runtime");
41
+ const config_1 = require("./config");
41
42
  const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
42
43
  function activityPriority(source) {
43
44
  return source === "friend-facing" ? 0 : 1;
@@ -130,7 +131,10 @@ function listSessionActivity(query) {
130
131
  if (!keyFile.endsWith(".json"))
131
132
  continue;
132
133
  const key = keyFile.replace(/\.json$/, "");
133
- if (currentSession && friendId === currentSession.friendId && channel === currentSession.channel && key === currentSession.key) {
134
+ // Compare with sanitizeKey on both sides session keys from the filesystem
135
+ // are already sanitized (colons → underscores), but the canonical key from
136
+ // the pipeline may still have colons (e.g. "chat:any" vs "chat_any").
137
+ if (currentSession && friendId === currentSession.friendId && channel === currentSession.channel && (0, config_1.sanitizeKey)(key) === (0, config_1.sanitizeKey)(currentSession.key)) {
134
138
  continue;
135
139
  }
136
140
  const sessionPath = path.join(channelPath, keyFile);
@@ -579,6 +579,9 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
579
579
  });
580
580
  return { handled: true, notifiedAgent: false, kind: event.kind, reason: "already_processed" };
581
581
  }
582
+ // Record EARLY to prevent duplicate processing. BB webhooks can retry
583
+ // before the first turn completes — recording after the turn is too late.
584
+ (0, bluebubbles_inbound_log_1.recordBlueBubblesInbound)(agentName, event, source);
582
585
  if (source !== "webhook" && sessionLikelyContainsMessage(event, existing?.messages ?? sessionMessages)) {
583
586
  (0, bluebubbles_inbound_log_1.recordBlueBubblesInbound)(agentName, event, "recovery-bootstrap");
584
587
  (0, runtime_1.emitNervesEvent)({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.127",
3
+ "version": "0.1.0-alpha.129",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",