@ouro.bot/cli 0.1.0-alpha.724 → 0.1.0-alpha.725

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.
Files changed (54) hide show
  1. package/changelog.json +12 -0
  2. package/dist/heart/active-work.js +10 -3
  3. package/dist/heart/core.js +343 -77
  4. package/dist/heart/daemon/cli-exec.js +72 -14
  5. package/dist/heart/daemon/cli-help.js +13 -2
  6. package/dist/heart/daemon/cli-parse.js +82 -1
  7. package/dist/heart/daemon/daemon.js +286 -33
  8. package/dist/heart/daemon/doctor.js +21 -6
  9. package/dist/heart/daemon/habit-private-wake.js +6 -1
  10. package/dist/heart/habits/habit-cancel.js +810 -0
  11. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  12. package/dist/heart/habits/habit-migration.js +28 -23
  13. package/dist/heart/habits/habit-parser.js +72 -8
  14. package/dist/heart/habits/habit-runtime-state.js +3 -24
  15. package/dist/heart/habits/habit-scheduler.js +268 -50
  16. package/dist/heart/habits/habit-session.js +16 -0
  17. package/dist/heart/hatch/hatch-flow.js +6 -4
  18. package/dist/heart/mailbox/readers/runtime-readers.js +26 -27
  19. package/dist/heart/orientation-frame.js +58 -1
  20. package/dist/heart/provider-attempt.js +4 -1
  21. package/dist/heart/providers/anthropic.js +23 -2
  22. package/dist/heart/start-of-turn-packet.js +14 -11
  23. package/dist/heart/streaming.js +565 -185
  24. package/dist/mind/obligation-steering.js +15 -9
  25. package/dist/mind/prompt.js +58 -26
  26. package/dist/nerves/coverage/run-artifacts.js +55 -35
  27. package/dist/nerves/review/cli.js +2 -2
  28. package/dist/nerves/review/core.js +14 -7
  29. package/dist/repertoire/tools-base.js +2 -0
  30. package/dist/repertoire/tools-habits.js +103 -0
  31. package/dist/repertoire/tools.js +18 -3
  32. package/dist/rsvp/cli.js +126 -20
  33. package/dist/rsvp/diagnostics.js +12 -2
  34. package/dist/rsvp/habit-policy.js +7 -0
  35. package/dist/rsvp/habit-stage.js +19 -4
  36. package/dist/rsvp/native-habit-runner.js +151 -11
  37. package/dist/rsvp/outbound-state.js +360 -0
  38. package/dist/senses/bluebubbles/client.js +90 -17
  39. package/dist/senses/bluebubbles/inbound-log.js +1 -0
  40. package/dist/senses/bluebubbles/index.js +662 -425
  41. package/dist/senses/bluebubbles/model.js +57 -13
  42. package/dist/senses/bluebubbles/mutation-log.js +21 -10
  43. package/dist/senses/bluebubbles/reaction-policy.js +55 -0
  44. package/dist/senses/bluebubbles/semantic-receipts.js +1818 -0
  45. package/dist/senses/cli/tui-store.js +1 -0
  46. package/dist/senses/cli.js +1 -0
  47. package/dist/senses/habit-lifecycle-guard.js +24 -0
  48. package/dist/senses/ingress-evidence.js +2 -0
  49. package/dist/senses/pipeline.js +90 -24
  50. package/dist/senses/private-runtime-worker.js +21 -20
  51. package/dist/senses/private-runtime.js +79 -29
  52. package/dist/senses/shared-turn.js +1 -0
  53. package/dist/senses/teams.js +1 -0
  54. package/package.json +6 -1
@@ -92,9 +92,11 @@ function buildChatRef(data, threadOriginatorGuid) {
92
92
  }
93
93
  function extractSender(data, chat) {
94
94
  const handle = asRecord(data.handle) ?? asRecord(data.sender) ?? null;
95
- const rawId = readString(handle, "address") ??
95
+ const observedRawId = readString(handle, "address") ??
96
96
  readString(handle, "id") ??
97
- readString(data, "senderId") ??
97
+ readString(data, "senderId");
98
+ const observed = typeof observedRawId === "string" && observedRawId.trim().length > 0;
99
+ const rawId = observedRawId ??
98
100
  chat.chatIdentifier ??
99
101
  chat.chatGuid ??
100
102
  "unknown";
@@ -105,6 +107,7 @@ function extractSender(data, chat) {
105
107
  externalId,
106
108
  rawId,
107
109
  displayName,
110
+ observed,
108
111
  };
109
112
  }
110
113
  function extractAttachments(data) {
@@ -163,7 +166,7 @@ function normalizeReactionName(value) {
163
166
  return trimmed ? trimmed.toLowerCase() : undefined;
164
167
  }
165
168
  /**
166
- * iMessage tapbacks: associated_message_type 2000-2005 add a tapback, 3000-3005
169
+ * iMessage tapbacks: associated_message_type 2000-2006 add a tapback, 3000-3006
167
170
  * remove the same tapback. BlueBubbles may send either the code or the name.
168
171
  */
169
172
  const REACTION_VOCABULARY = [
@@ -173,17 +176,26 @@ const REACTION_VOCABULARY = [
173
176
  { code: 2003, name: "laugh", verb: "laughed at", noun: "laugh" },
174
177
  { code: 2004, name: "emphasize", verb: "emphasized", noun: "emphasis" },
175
178
  { code: 2005, name: "question", verb: "questioned", noun: "question" },
179
+ { code: 2006, name: "custom", verb: "reacted with custom emoji to", noun: "custom emoji" },
176
180
  ];
177
181
  const REACTION_EXCERPT_MAX_LENGTH = 80;
178
- function describeBlueBubblesReaction(raw) {
182
+ function describeBlueBubblesReaction(raw, rawTransportValue = raw) {
179
183
  const stripped = raw.startsWith("-") ? raw.slice(1) : raw;
180
184
  const code = /^\d+$/.test(stripped) ? Number(stripped) : undefined;
181
185
  const removal = raw.startsWith("-") || (code !== undefined && code >= 3000 && code < 4000);
182
186
  const action = removal ? "remove" : "add";
183
187
  const entry = REACTION_VOCABULARY.find((candidate) => candidate.name === stripped || candidate.code === code || candidate.code + 1000 === code);
188
+ const canonicalValue = entry?.name ?? "unknown";
184
189
  if (!entry)
185
- return { raw, action };
186
- return { raw, action, verb: entry.verb, noun: entry.noun };
190
+ return { raw, rawTransportValue, canonicalValue, action };
191
+ return {
192
+ raw,
193
+ rawTransportValue,
194
+ canonicalValue,
195
+ action,
196
+ verb: entry.verb,
197
+ noun: entry.noun,
198
+ };
187
199
  }
188
200
  function reactionExcerpt(text) {
189
201
  return text.length > REACTION_EXCERPT_MAX_LENGTH
@@ -243,17 +255,35 @@ function detectMutationType(eventType, data, reactionName) {
243
255
  if (reactionName)
244
256
  return "reaction";
245
257
  if (eventType === "updated-message") {
246
- if (readNumber(data, "dateRetracted"))
258
+ if (readNumber(data, "dateRetracted") !== undefined)
247
259
  return "unsend";
248
- if (readNumber(data, "dateEdited"))
260
+ if (readNumber(data, "dateEdited") !== undefined)
249
261
  return "edit";
250
- if (readNumber(data, "dateRead"))
262
+ if (readNumber(data, "dateRead") !== undefined)
251
263
  return "read";
252
- if (readBoolean(data, "isDelivered") || readNumber(data, "dateDelivered"))
264
+ if (readBoolean(data, "isDelivered") || readNumber(data, "dateDelivered") !== undefined)
253
265
  return "delivery";
254
266
  }
255
267
  return null;
256
268
  }
269
+ function readProviderRevision(data) {
270
+ const value = data.revision;
271
+ if (typeof value !== "string" && typeof value !== "number")
272
+ return undefined;
273
+ const normalized = String(value).trim();
274
+ return normalized || undefined;
275
+ }
276
+ function mutationEffectiveTimestamp(mutationType, data) {
277
+ if (mutationType === "reaction")
278
+ return readNumber(data, "dateCreated");
279
+ if (mutationType === "edit")
280
+ return readNumber(data, "dateEdited");
281
+ if (mutationType === "unsend")
282
+ return readNumber(data, "dateRetracted");
283
+ if (mutationType === "read")
284
+ return readNumber(data, "dateRead");
285
+ return readNumber(data, "dateDelivered");
286
+ }
257
287
  function normalizeBlueBubblesEvent(payload) {
258
288
  const envelope = asRecord(payload);
259
289
  const eventType = readString(envelope, "type")?.trim() ?? "";
@@ -286,14 +316,24 @@ function normalizeBlueBubblesEvent(payload) {
286
316
  const chat = buildChatRef(data, threadOriginatorGuid);
287
317
  const sender = extractSender(data, chat);
288
318
  const timestamp = readNumber(data, "dateCreated") ?? Date.now();
289
- const fromMe = readBoolean(data, "isFromMe") ?? false;
319
+ const fromMe = readBoolean(data, "isFromMe") ?? null;
290
320
  const attachments = extractAttachments(data);
291
321
  const reactionName = normalizeReactionName(data.associatedMessageType);
292
- const reaction = reactionName ? describeBlueBubblesReaction(reactionName) : undefined;
322
+ const reaction = reactionName
323
+ ? describeBlueBubblesReaction(reactionName, String(data.associatedMessageType))
324
+ : undefined;
293
325
  const targetMessageGuid = reaction
294
326
  ? stripPartPrefix(readString(data, "associatedMessageGuid"))
295
327
  : undefined;
296
328
  const mutationType = detectMutationType(eventType, data, reactionName);
329
+ const revision = mutationType ? readProviderRevision(data) : undefined;
330
+ const effectiveTimestamp = mutationType
331
+ ? mutationEffectiveTimestamp(mutationType, data)
332
+ : undefined;
333
+ const editedText = mutationType === "edit" ? readString(data, "text") : undefined;
334
+ const retractionTimestamp = mutationType === "unsend"
335
+ ? readNumber(data, "dateRetracted")
336
+ : undefined;
297
337
  const requiresRepair = (readBoolean(data, "hasPayloadData") ?? false) ||
298
338
  attachments.length > 0 ||
299
339
  eventType === "updated-message";
@@ -314,6 +354,10 @@ function normalizeBlueBubblesEvent(payload) {
314
354
  : buildMutationText(mutationType, data),
315
355
  requiresRepair,
316
356
  ...(reaction ? { reaction } : {}),
357
+ ...(editedText !== undefined ? { editedText } : {}),
358
+ ...(revision ? { revision } : {}),
359
+ ...(effectiveTimestamp !== undefined ? { effectiveTimestamp } : {}),
360
+ ...(retractionTimestamp !== undefined ? { retractionTimestamp } : {}),
317
361
  }
318
362
  : {
319
363
  kind: "message",
@@ -323,7 +367,7 @@ function normalizeBlueBubblesEvent(payload) {
323
367
  fromMe,
324
368
  sender,
325
369
  chat,
326
- text: readString(data, "text")?.trim() ?? "",
370
+ text: readString(data, "text") ?? "",
327
371
  textForAgent: formatMessageText(data, attachments),
328
372
  attachments,
329
373
  balloonBundleId: readString(data, "balloonBundleId")?.trim() || undefined,
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getBlueBubblesMutationLogPath = getBlueBubblesMutationLogPath;
37
37
  exports.recordBlueBubblesMutation = recordBlueBubblesMutation;
38
+ exports.listRecordedBlueBubblesMutations = listRecordedBlueBubblesMutations;
38
39
  exports.listBlueBubblesRecoveryCandidates = listBlueBubblesRecoveryCandidates;
39
40
  const fs = __importStar(require("node:fs"));
40
41
  const path = __importStar(require("node:path"));
@@ -73,7 +74,7 @@ function recordBlueBubblesMutation(agentName, event) {
73
74
  });
74
75
  return filePath;
75
76
  }
76
- function listBlueBubblesRecoveryCandidates(agentName) {
77
+ function readBlueBubblesMutationRows(agentName) {
77
78
  const rootDir = path.join((0, identity_1.getAgentRoot)(agentName), "state", "senses", "bluebubbles", "mutations");
78
79
  let files;
79
80
  try {
@@ -82,7 +83,7 @@ function listBlueBubblesRecoveryCandidates(agentName) {
82
83
  catch {
83
84
  return [];
84
85
  }
85
- const deduped = new Map();
86
+ const entries = [];
86
87
  for (const file of files.filter((entry) => entry.endsWith(".ndjson")).sort()) {
87
88
  const filePath = path.join(rootDir, file);
88
89
  let raw = "";
@@ -98,19 +99,29 @@ function listBlueBubblesRecoveryCandidates(agentName) {
98
99
  continue;
99
100
  try {
100
101
  const entry = JSON.parse(trimmed);
101
- if (typeof entry.messageGuid !== "string"
102
- || !entry.messageGuid.trim()
103
- || entry.fromMe
104
- || entry.shouldNotifyAgent
105
- || (entry.mutationType !== "read" && entry.mutationType !== "delivery")) {
102
+ if (typeof entry.messageGuid !== "string" || !entry.messageGuid.trim())
106
103
  continue;
107
- }
108
- deduped.set(entry.messageGuid, entry);
104
+ entries.push(entry);
109
105
  }
110
106
  catch {
111
- // ignore malformed recovery candidates
107
+ // Keep malformed legacy rows inert while retaining readable audit rows.
112
108
  }
113
109
  }
114
110
  }
111
+ return entries;
112
+ }
113
+ function listRecordedBlueBubblesMutations(agentName) {
114
+ return readBlueBubblesMutationRows(agentName).filter((entry) => (typeof entry.sessionKey === "string" && entry.sessionKey.trim().length > 0));
115
+ }
116
+ function listBlueBubblesRecoveryCandidates(agentName) {
117
+ const deduped = new Map();
118
+ for (const entry of readBlueBubblesMutationRows(agentName)) {
119
+ if (entry.fromMe !== false
120
+ || entry.shouldNotifyAgent
121
+ || (entry.mutationType !== "read" && entry.mutationType !== "delivery")) {
122
+ continue;
123
+ }
124
+ deduped.set(entry.messageGuid, entry);
125
+ }
115
126
  return [...deduped.values()].sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
116
127
  }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyBlueBubblesReaction = classifyBlueBubblesReaction;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ const POSITIVE_REACTIONS = new Set([
6
+ "love",
7
+ "like",
8
+ "laugh",
9
+ "emphasize",
10
+ ]);
11
+ function classifyBlueBubblesReaction(input) {
12
+ let decision;
13
+ if (input.fromMe) {
14
+ decision = { route: "capture_only", outcome: "ignored_self" };
15
+ }
16
+ else if (input.action === "remove") {
17
+ decision = { route: "capture_only", outcome: "capture_only_removal" };
18
+ }
19
+ else if (POSITIVE_REACTIONS.has(input.canonicalValue)) {
20
+ decision = { route: "capture_only", outcome: "capture_only_positive" };
21
+ }
22
+ else if (input.canonicalValue === "custom") {
23
+ decision = { route: "capture_only", outcome: "capture_only_custom" };
24
+ }
25
+ else if (input.canonicalValue === "unknown") {
26
+ decision = { route: "capture_only", outcome: "capture_only_unknown" };
27
+ }
28
+ else if (input.targetAuthorship !== "agent") {
29
+ decision = { route: "capture_only", outcome: "capture_only_target_not_agent" };
30
+ }
31
+ else if (input.trustedActor === undefined) {
32
+ decision = { route: "trust_required" };
33
+ }
34
+ else if (!input.trustedActor) {
35
+ decision = { route: "capture_only", outcome: "capture_only_untrusted_actor" };
36
+ }
37
+ else {
38
+ decision = { route: "restricted_feedback" };
39
+ }
40
+ (0, runtime_1.emitNervesEvent)({
41
+ component: "senses",
42
+ event: "senses.bluebubbles_reaction_policy_evaluated",
43
+ message: "evaluated bluebubbles reaction routing policy",
44
+ meta: {
45
+ action: input.action,
46
+ canonicalValue: input.canonicalValue,
47
+ fromMe: input.fromMe,
48
+ targetAuthorship: input.targetAuthorship,
49
+ trustEvaluated: input.trustedActor !== undefined,
50
+ route: decision.route,
51
+ outcome: decision.route === "capture_only" ? decision.outcome : null,
52
+ },
53
+ });
54
+ return decision;
55
+ }