@ouro.bot/cli 0.1.0-alpha.720 → 0.1.0-alpha.722

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.
@@ -45,6 +45,23 @@ function messageToContextInput(event, chatGuidHash) {
45
45
  },
46
46
  };
47
47
  }
48
+ function normalizeKnownMessageText(value) {
49
+ return value.replace(/\s+/g, " ").trim().toLowerCase();
50
+ }
51
+ function hasKnownMessageText(candidate, knownTexts) {
52
+ if (knownTexts.size === 0)
53
+ return false;
54
+ const body = normalizeKnownMessageText(candidate.textForAgent || candidate.text);
55
+ if (!body)
56
+ return true;
57
+ if (knownTexts.has(body))
58
+ return true;
59
+ for (const known of knownTexts) {
60
+ if (body.length >= 12 && known.includes(body))
61
+ return true;
62
+ }
63
+ return false;
64
+ }
48
65
  async function buildBlueBubblesContextPacket(input) {
49
66
  if (!Number.isFinite(input.event.timestamp))
50
67
  return null;
@@ -53,6 +70,9 @@ async function buildBlueBubblesContextPacket(input) {
53
70
  const event = input.event;
54
71
  const chatGuidHash = blueBubblesContextChatKeyHash(event);
55
72
  const anchorTimestamp = new Date(event.timestamp).toISOString();
73
+ const knownTexts = new Set((input.knownMessageTexts ?? [])
74
+ .map(normalizeKnownMessageText)
75
+ .filter((value) => value.length > 0));
56
76
  const candidates = await input.client.listRecentMessages({
57
77
  beforeTimestamp: event.timestamp,
58
78
  limit: exports.BLUEBUBBLES_CONTEXT_PACKET_LIMIT,
@@ -65,7 +85,8 @@ async function buildBlueBubblesContextPacket(input) {
65
85
  .filter((candidate) => isSameBlueBubblesChat(candidate, event))
66
86
  .filter((candidate) => candidate.messageGuid !== event.messageGuid)
67
87
  .filter((candidate) => candidate.timestamp <= event.timestamp)
68
- .filter((candidate) => event.timestamp - candidate.timestamp <= exports.BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS);
88
+ .filter((candidate) => event.timestamp - candidate.timestamp <= exports.BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS)
89
+ .filter((candidate) => !hasKnownMessageText(candidate, knownTexts));
69
90
  if (history.length === 0)
70
91
  return null;
71
92
  const packet = (0, context_packets_1.buildSenseContextPacket)({
@@ -623,12 +623,18 @@ function insertEphemeralContextMessages(messages, contextMessages) {
623
623
  ...messages.slice(insertionIndex),
624
624
  ];
625
625
  }
626
+ function knownProviderMessageTexts(messages) {
627
+ return messages
628
+ .map((message) => extractMessageText(message.content))
629
+ .map((text) => text.trim())
630
+ .filter((text) => text.length > 0);
631
+ }
626
632
  async function buildBlueBubblesContextMessages(input) {
627
633
  if (input.event.kind !== "message")
628
- return [];
634
+ return { messages: [], contextPacketIds: [] };
629
635
  const event = input.event;
630
636
  if (!Number.isFinite(event.timestamp))
631
- return [];
637
+ return { messages: [], contextPacketIds: [] };
632
638
  const chatGuidHash = (0, context_packet_1.blueBubblesContextChatKeyHash)(event);
633
639
  const anchorTimestamp = new Date(event.timestamp).toISOString();
634
640
  try {
@@ -636,9 +642,10 @@ async function buildBlueBubblesContextMessages(input) {
636
642
  agentName: input.agentName,
637
643
  client: input.client,
638
644
  event,
645
+ knownMessageTexts: knownProviderMessageTexts(input.knownMessages ?? []),
639
646
  });
640
647
  if (!result)
641
- return [];
648
+ return { messages: [], contextPacketIds: [] };
642
649
  const { packet, rendered, historyCount } = result;
643
650
  (0, context_packet_ledger_1.writeSenseContextPacket)(input.agentRoot, packet);
644
651
  (0, runtime_1.emitNervesEvent)({
@@ -651,7 +658,10 @@ async function buildBlueBubblesContextMessages(input) {
651
658
  contextMessages: historyCount,
652
659
  },
653
660
  });
654
- return [{ role: "system", content: rendered.text }];
661
+ return {
662
+ messages: [{ role: "system", content: rendered.text }],
663
+ contextPacketIds: [packet.packetId],
664
+ };
655
665
  }
656
666
  catch (error) {
657
667
  (0, runtime_1.emitNervesEvent)({
@@ -671,9 +681,12 @@ async function buildBlueBubblesContextMessages(input) {
671
681
  maxAgeMs: 24 * 60 * 60 * 1000,
672
682
  });
673
683
  if (!fallback)
674
- return [];
684
+ return { messages: [], contextPacketIds: [] };
675
685
  const rendered = (0, context_packets_1.renderSenseContextPacketForPrompt)(fallback);
676
- return [{ role: "system", content: rendered.text }];
686
+ return {
687
+ messages: [{ role: "system", content: rendered.text }],
688
+ contextPacketIds: [fallback.packetId],
689
+ };
677
690
  }
678
691
  }
679
692
  function createReplyTargetController(event) {
@@ -1468,6 +1481,10 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source, opt
1468
1481
  if (typeof timeoutTimer.unref === "function") {
1469
1482
  timeoutTimer.unref();
1470
1483
  }
1484
+ let preparedBlueBubblesContext = {
1485
+ messages: [],
1486
+ contextPacketIds: [],
1487
+ };
1471
1488
  const turnPromise = (0, pipeline_1.handleInboundTurn)({
1472
1489
  channel: "bluebubbles",
1473
1490
  sessionKey: event.chat.sessionKey,
@@ -1494,35 +1511,44 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source, opt
1494
1511
  enforceTrustGate: trust_gate_1.enforceTrustGate,
1495
1512
  drainPending: pending_1.drainPending,
1496
1513
  drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(resolvedDeps.getAgentName(), deferredFriendId),
1497
- runAgent: async (msgs, cb, channel, sig, opts) => {
1498
- const contextMessages = await buildBlueBubblesContextMessages({
1514
+ prepareRunAgentOptions: async ({ messages, runAgentOptions }) => {
1515
+ preparedBlueBubblesContext = await buildBlueBubblesContextMessages({
1499
1516
  agentName,
1500
1517
  agentRoot: (0, identity_1.getAgentRoot)(),
1501
1518
  client,
1502
1519
  event,
1520
+ knownMessages: messages,
1503
1521
  });
1504
- return resolvedDeps.runAgent(insertEphemeralContextMessages(msgs, contextMessages), cb, channel, sig, {
1505
- ...opts,
1506
- toolContext: {
1507
- /* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
1508
- signin: async () => undefined,
1509
- ...opts?.toolContext,
1510
- summarize,
1511
- bluebubblesReplyTarget: {
1512
- setSelection: (selection) => replyTarget.setSelection(selection),
1513
- },
1514
- codingFeedback: {
1515
- send: async (message) => {
1516
- await client.sendText({
1517
- chat: event.chat,
1518
- text: message,
1519
- replyToMessageGuid: replyTarget.getReplyToMessageGuid(),
1520
- });
1521
- },
1522
+ if (preparedBlueBubblesContext.contextPacketIds.length === 0)
1523
+ return undefined;
1524
+ return {
1525
+ contextPacketIds: [
1526
+ ...(runAgentOptions.contextPacketIds ?? []),
1527
+ ...preparedBlueBubblesContext.contextPacketIds,
1528
+ ],
1529
+ };
1530
+ },
1531
+ runAgent: async (msgs, cb, channel, sig, opts) => resolvedDeps.runAgent(insertEphemeralContextMessages(msgs, preparedBlueBubblesContext.messages), cb, channel, sig, {
1532
+ ...opts,
1533
+ toolContext: {
1534
+ /* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
1535
+ signin: async () => undefined,
1536
+ ...opts?.toolContext,
1537
+ summarize,
1538
+ bluebubblesReplyTarget: {
1539
+ setSelection: (selection) => replyTarget.setSelection(selection),
1540
+ },
1541
+ codingFeedback: {
1542
+ send: async (message) => {
1543
+ await client.sendText({
1544
+ chat: event.chat,
1545
+ text: message,
1546
+ replyToMessageGuid: replyTarget.getReplyToMessageGuid(),
1547
+ });
1522
1548
  },
1523
1549
  },
1524
- });
1525
- },
1550
+ },
1551
+ }),
1526
1552
  postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
1527
1553
  const prepared = resolvedDeps.postTurnTrim(turnMessages, usage, hooks);
1528
1554
  resolvedDeps.deferPostTurnPersist(sessionPathArg, prepared, usage, state);
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.writeSenseContextPacket = writeSenseContextPacket;
37
37
  exports.readSenseContextLedger = readSenseContextLedger;
38
+ exports.listSenseContextPacketSummaries = listSenseContextPacketSummaries;
39
+ exports.readSenseContextPacketView = readSenseContextPacketView;
38
40
  exports.readLatestVisibleSenseContextPacket = readLatestVisibleSenseContextPacket;
39
41
  exports.compactSenseContextPacketReceipts = compactSenseContextPacketReceipts;
40
42
  const fs = __importStar(require("node:fs"));
@@ -44,6 +46,9 @@ const context_packets_1 = require("./context-packets");
44
46
  function contextPacketRoot(agentRoot, sense) {
45
47
  return path.join(agentRoot, "state", "senses", "context-packets", sense);
46
48
  }
49
+ function contextPacketBaseRoot(agentRoot) {
50
+ return path.join(agentRoot, "state", "senses", "context-packets");
51
+ }
47
52
  function packetPath(agentRoot, packet) {
48
53
  const month = packet.anchorTimestamp.slice(0, 7) || "unknown";
49
54
  return path.join(contextPacketRoot(agentRoot, packet.sense), month, `${packet.packetId}.json`);
@@ -181,6 +186,127 @@ function readSenseContextLedger(agentRoot, sense) {
181
186
  });
182
187
  return rows;
183
188
  }
189
+ function safeContextPacketLimit(limit) {
190
+ return Number.isInteger(limit) && typeof limit === "number" && limit >= 1 && limit <= 100 ? limit : 20;
191
+ }
192
+ function discoverContextPacketSenses(agentRoot) {
193
+ const root = contextPacketBaseRoot(agentRoot);
194
+ if (!fs.existsSync(root))
195
+ return [];
196
+ return fs.readdirSync(root)
197
+ .filter((entry) => {
198
+ try {
199
+ return fs.statSync(path.join(root, entry)).isDirectory();
200
+ }
201
+ catch {
202
+ return false;
203
+ }
204
+ })
205
+ .sort();
206
+ }
207
+ function summarizeLedgerRow(row) {
208
+ return {
209
+ packetId: row.packetId,
210
+ sense: row.sense,
211
+ agent: row.agent,
212
+ chatKeyHash: row.chatKeyHash,
213
+ anchorMessageGuid: row.anchorMessageGuid,
214
+ anchorTimestamp: row.anchorTimestamp,
215
+ createdAt: row.createdAt,
216
+ messageCount: row.messageCount,
217
+ sourceRefs: row.sourceRefs,
218
+ bodyHashes: row.bodyHashes,
219
+ rawBodyStored: row.rawBodyStored,
220
+ privacyClass: row.privacyClass,
221
+ omittedMessages: row.omittedMessages,
222
+ truncatedMessages: row.truncatedMessages,
223
+ };
224
+ }
225
+ function readAllContextPacketRows(agentRoot, sense) {
226
+ const senses = sense ? [sense] : discoverContextPacketSenses(agentRoot);
227
+ return senses.flatMap((entry) => readSenseContextLedger(agentRoot, entry));
228
+ }
229
+ function timestampSortValue(value) {
230
+ const parsed = Date.parse(value);
231
+ return Number.isFinite(parsed) ? parsed : 0;
232
+ }
233
+ function listSenseContextPacketSummaries(agentRoot, options = {}) {
234
+ const limit = safeContextPacketLimit(options.limit);
235
+ const rows = readAllContextPacketRows(agentRoot, options.sense)
236
+ .sort((left, right) => timestampSortValue(right.createdAt || right.anchorTimestamp) - timestampSortValue(left.createdAt || left.anchorTimestamp));
237
+ return {
238
+ totalCount: rows.length,
239
+ limit,
240
+ items: rows.slice(0, limit).map(summarizeLedgerRow),
241
+ };
242
+ }
243
+ function isSafeContextPacketId(packetId) {
244
+ return /^scp_[A-Za-z0-9_-]+$/.test(packetId);
245
+ }
246
+ function pathInsideContextPacketRoot(agentRoot, sense, filePath) {
247
+ const root = path.resolve(contextPacketRoot(agentRoot, sense));
248
+ const resolved = path.resolve(filePath);
249
+ return resolved === root || resolved.startsWith(`${root}${path.sep}`);
250
+ }
251
+ function redactPreview(value) {
252
+ return value
253
+ .replace(/\bpassword=[^\s]+/gi, "password=[redacted]")
254
+ .replace(/\b(token|api[_-]?key|secret)=[^\s]+/gi, "$1=[redacted]");
255
+ }
256
+ function viewMessage(message) {
257
+ const { chatGuid: _chatGuid, ...sourceRef } = message.sourceRef;
258
+ return {
259
+ timestamp: message.timestamp,
260
+ authorLabel: message.authorLabel,
261
+ bodyHash: message.bodyHash,
262
+ bodyPreview: redactPreview(message.bodyPreview),
263
+ sourceRef,
264
+ renderedSourceRef: message.renderedSourceRef,
265
+ };
266
+ }
267
+ function emitContextPacketReadWarning(packetId, reason) {
268
+ (0, runtime_1.emitNervesEvent)({
269
+ level: "warn",
270
+ component: "senses",
271
+ event: "senses.context_packet_read_error",
272
+ message: "failed to read sense context packet view",
273
+ meta: { packetId, reason },
274
+ });
275
+ }
276
+ function readSenseContextPacketView(agentRoot, packetId) {
277
+ if (!isSafeContextPacketId(packetId))
278
+ return null;
279
+ const row = readAllContextPacketRows(agentRoot).find((entry) => entry.packetId === packetId);
280
+ if (!row)
281
+ return null;
282
+ if (!pathInsideContextPacketRoot(agentRoot, row.sense, row.packetPath)) {
283
+ emitContextPacketReadWarning(packetId, "packet path escaped context-packet root");
284
+ return null;
285
+ }
286
+ if (!fs.existsSync(row.packetPath))
287
+ return null;
288
+ try {
289
+ const packet = JSON.parse(fs.readFileSync(row.packetPath, "utf-8"));
290
+ if (packet.packetId !== packetId || !Array.isArray(packet.messages))
291
+ return null;
292
+ return {
293
+ row: summarizeLedgerRow(row),
294
+ packet: {
295
+ packetId,
296
+ sense: typeof packet.sense === "string" ? packet.sense : row.sense,
297
+ agent: typeof packet.agent === "string" ? packet.agent : row.agent,
298
+ privacyClass: packet.privacyClass === "private-runtime" ? packet.privacyClass : "private-runtime",
299
+ messages: packet.messages.map((message) => viewMessage(message)),
300
+ omittedMessages: typeof packet.omittedMessages === "number" ? packet.omittedMessages : row.omittedMessages,
301
+ truncatedMessages: typeof packet.truncatedMessages === "number" ? packet.truncatedMessages : row.truncatedMessages,
302
+ },
303
+ };
304
+ }
305
+ catch (error) {
306
+ emitContextPacketReadWarning(packetId, error instanceof Error ? error.message : String(error));
307
+ return null;
308
+ }
309
+ }
184
310
  function timestampMs(value) {
185
311
  return Date.parse(value);
186
312
  }
@@ -369,6 +369,36 @@ let _lastSessionKey = null;
369
369
  function runAgentOptionContextPacketIds(options) {
370
370
  return Array.isArray(options.contextPacketIds) ? options.contextPacketIds : [];
371
371
  }
372
+ function mergeRunAgentContextPacketIds(left, right) {
373
+ const merged = [
374
+ ...(Array.isArray(left) ? left : []),
375
+ ...(Array.isArray(right) ? right : []),
376
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
377
+ return merged.length > 0 ? [...new Set(merged)] : undefined;
378
+ }
379
+ function mergePreparedRunAgentOptions(base, prepared) {
380
+ if (!prepared)
381
+ return base;
382
+ const contextPacketIds = mergeRunAgentContextPacketIds(base.contextPacketIds, prepared.contextPacketIds);
383
+ const { contextPacketIds: _baseContextPacketIds, ...baseOptions } = base;
384
+ const { contextPacketIds: _preparedContextPacketIds, ...preparedOptions } = prepared;
385
+ const merged = {
386
+ ...baseOptions,
387
+ ...preparedOptions,
388
+ ...(contextPacketIds ? { contextPacketIds } : {}),
389
+ };
390
+ if (prepared.toolContext) {
391
+ const toolContext = {
392
+ ...base.toolContext,
393
+ ...prepared.toolContext,
394
+ };
395
+ merged.toolContext = {
396
+ ...toolContext,
397
+ signin: toolContext.signin ?? (async () => undefined),
398
+ };
399
+ }
400
+ return merged;
401
+ }
372
402
  function buildInboundRunLedgerInput(input) {
373
403
  const target = {
374
404
  channel: input.channel,
@@ -822,7 +852,7 @@ async function handleInboundTurn(input) {
822
852
  const existingToolContext = input.runAgentOptions?.toolContext;
823
853
  const currentUserMessage = existingToolContext?.currentUserMessage
824
854
  ?? latestUserAuthoredText(input.messages, input.continuityIngressTexts);
825
- const runAgentOptions = {
855
+ let runAgentOptions = {
826
856
  ...input.runAgentOptions,
827
857
  ...(orientationFrame ? { orientationFrame } : {}),
828
858
  ...(liveLatencyMode ? { skipKeptNotes: true } : {}),
@@ -856,6 +886,12 @@ async function handleInboundTurn(input) {
856
886
  ...(orientationFrame ? { orientationFrame } : {}),
857
887
  },
858
888
  };
889
+ runAgentOptions = mergePreparedRunAgentOptions(runAgentOptions, await input.prepareRunAgentOptions?.({
890
+ messages: sessionMessages,
891
+ currentUserMessages,
892
+ resolvedContext,
893
+ runAgentOptions,
894
+ }));
859
895
  const runLedger = prepareInboundRunLedger({
860
896
  channel: input.channel,
861
897
  senseType: input.capabilities.senseType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.720",
3
+ "version": "0.1.0-alpha.722",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",