@ouro.bot/cli 0.1.0-alpha.611 → 0.1.0-alpha.612

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 (34) hide show
  1. package/changelog.json +10 -0
  2. package/dist/arc/cares.js +7 -3
  3. package/dist/arc/episodes.js +4 -3
  4. package/dist/arc/intentions.js +2 -1
  5. package/dist/arc/obligations.js +18 -6
  6. package/dist/arc/packets.js +9 -8
  7. package/dist/heart/awaiting/await-runtime-state.js +4 -1
  8. package/dist/heart/bridges/store.js +14 -2
  9. package/dist/heart/daemon/daemon.js +47 -0
  10. package/dist/heart/daemon/process-manager.js +13 -0
  11. package/dist/heart/daemon/sense-manager.js +12 -0
  12. package/dist/heart/mailbox/readers/runtime-readers.js +107 -2
  13. package/dist/heart/mailbox/readers/sessions.js +2 -2
  14. package/dist/heart/session-events.js +73 -66
  15. package/dist/heart/session-transcript.js +6 -116
  16. package/dist/mailbox-ui/assets/{index-CtUWEo-S.js → index-9-AxCxuB.js} +3 -3
  17. package/dist/mailbox-ui/assets/index-CWzt267f.css +1 -0
  18. package/dist/mailbox-ui/index.html +2 -2
  19. package/dist/mind/diary.js +6 -1
  20. package/dist/mind/friends/store-file.js +9 -1
  21. package/dist/mind/journal-index.js +2 -1
  22. package/dist/mind/pending.js +2 -1
  23. package/dist/mind/prompt.js +6 -5
  24. package/dist/repertoire/coding/context-pack.js +3 -2
  25. package/dist/repertoire/coding/manager.js +6 -2
  26. package/dist/repertoire/tools-awaiting.js +11 -6
  27. package/dist/repertoire/tools-base.js +2 -0
  28. package/dist/repertoire/tools-bridge.js +0 -1
  29. package/dist/repertoire/tools-record.js +463 -0
  30. package/dist/repertoire/tools-runtime.js +87 -0
  31. package/dist/repertoire/tools-session.js +9 -37
  32. package/dist/senses/bluebubbles/index.js +1 -1
  33. package/package.json +1 -1
  34. package/dist/mailbox-ui/assets/index-BPr5vNuM.css +0 -1
@@ -33,6 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.EVENT_CONTENT_MAX_CHARS = void 0;
37
+ exports.truncateLargeEventContent = truncateLargeEventContent;
38
+ exports.capStructuredRecordString = capStructuredRecordString;
39
+ exports.capStructuredRecordStringArray = capStructuredRecordStringArray;
40
+ exports.capStructuredRecordStringLeaves = capStructuredRecordStringLeaves;
36
41
  exports.normalizeContinuityState = normalizeContinuityState;
37
42
  exports.validateSessionMessages = validateSessionMessages;
38
43
  exports.detectDuplicateToolCallIds = detectDuplicateToolCallIds;
@@ -52,11 +57,51 @@ exports.migrateLegacySessionEnvelope = migrateLegacySessionEnvelope;
52
57
  exports.parseSessionEnvelope = parseSessionEnvelope;
53
58
  exports.loadSessionEnvelopeFile = loadSessionEnvelopeFile;
54
59
  exports.buildCanonicalSessionEnvelope = buildCanonicalSessionEnvelope;
55
- exports.loadFullEventHistory = loadFullEventHistory;
56
60
  exports.appendEvictedToArchive = appendEvictedToArchive;
57
61
  exports.appendSyntheticAssistantEvent = appendSyntheticAssistantEvent;
58
62
  const fs = __importStar(require("fs"));
59
63
  const runtime_1 = require("../nerves/runtime");
64
+ let archiveDisabledEmitted = false;
65
+ exports.EVENT_CONTENT_MAX_CHARS = 256 * 1024;
66
+ function truncateLargeEventContent(content, maxChars) {
67
+ if (typeof content !== "string") {
68
+ return { content, truncated: false, originalLength: 0 };
69
+ }
70
+ if (content.length <= maxChars) {
71
+ return { content, truncated: false, originalLength: content.length };
72
+ }
73
+ const marker = `[truncated — event content exceeded ${maxChars} chars; original length ${content.length} chars]`;
74
+ const remainingBudget = Math.max(0, maxChars - marker.length);
75
+ const headLength = Math.ceil(remainingBudget * 0.75);
76
+ const tailLength = Math.max(0, remainingBudget - headLength);
77
+ return {
78
+ content: `${content.slice(0, headLength)}${marker}${tailLength > 0 ? content.slice(-tailLength) : ""}`,
79
+ truncated: true,
80
+ originalLength: content.length,
81
+ };
82
+ }
83
+ function capStructuredRecordString(value) {
84
+ return truncateLargeEventContent(value, exports.EVENT_CONTENT_MAX_CHARS).content;
85
+ }
86
+ function capStructuredRecordStringArray(values) {
87
+ return values.map((value) => capStructuredRecordString(value));
88
+ }
89
+ function capStructuredRecordStringLeaves(value) {
90
+ if (typeof value === "string") {
91
+ return capStructuredRecordString(value);
92
+ }
93
+ if (Array.isArray(value)) {
94
+ return value.map((item) => capStructuredRecordStringLeaves(item));
95
+ }
96
+ if (!value || typeof value !== "object") {
97
+ return value;
98
+ }
99
+ const capped = {};
100
+ for (const [key, child] of Object.entries(value)) {
101
+ capped[key] = capStructuredRecordStringLeaves(child);
102
+ }
103
+ return capped;
104
+ }
60
105
  function formatElapsed(ms) {
61
106
  const minutes = Math.max(0, Math.floor(ms / 60000));
62
107
  if (minutes < 60)
@@ -684,11 +729,12 @@ function createEventTime(role, recordedAt, captureKind, ingressAt) {
684
729
  function buildEventFromMessage(message, sequence, recordedAt, captureKind, sourceMessageIndex, legacyVersion, ingressAt) {
685
730
  const normalized = normalizeMessage(message);
686
731
  const role = normalized.role;
732
+ const cappedContent = truncateLargeEventContent(normalized.content, exports.EVENT_CONTENT_MAX_CHARS).content;
687
733
  return {
688
734
  id: makeEventId(sequence),
689
735
  sequence,
690
736
  role,
691
- content: normalized.content,
737
+ content: cappedContent,
692
738
  name: normalized.name,
693
739
  toolCallId: role === "tool" ? normalized.toolCallId : null,
694
740
  toolCalls: role === "assistant" ? normalized.toolCalls : [],
@@ -969,14 +1015,20 @@ function findCommonPrefixLength(a, b) {
969
1015
  function selectProjectedEventIds(currentMessages, currentEventIds, trimmedMessages) {
970
1016
  if (trimmedMessages.length === 0)
971
1017
  return [];
972
- const trimmedFingerprints = trimmedMessages.map(messageFingerprint);
973
1018
  const result = [];
974
1019
  let needle = 0;
975
- for (let i = 0; i < currentMessages.length && needle < trimmedFingerprints.length; i++) {
976
- if (messageFingerprint(currentMessages[i]) !== trimmedFingerprints[needle])
977
- continue;
1020
+ let trimmedFingerprint = null;
1021
+ for (let i = 0; i < currentMessages.length && needle < trimmedMessages.length; i++) {
1022
+ const currentMessage = currentMessages[i];
1023
+ const trimmedMessage = trimmedMessages[needle];
1024
+ if (currentMessage !== trimmedMessage) {
1025
+ trimmedFingerprint ??= messageFingerprint(trimmedMessage);
1026
+ if (messageFingerprint(currentMessage) !== trimmedFingerprint)
1027
+ continue;
1028
+ }
978
1029
  result.push(currentEventIds[i]);
979
1030
  needle++;
1031
+ trimmedFingerprint = null;
980
1032
  }
981
1033
  return result;
982
1034
  }
@@ -1057,75 +1109,30 @@ function buildCanonicalSessionEnvelope(options) {
1057
1109
  evictedEvents,
1058
1110
  };
1059
1111
  }
1060
- /**
1061
- * Load full event history from both the pruned envelope and the NDJSON archive.
1062
- * Returns all events deduplicated by id and sorted by sequence.
1063
- * Corrupted archive lines are silently skipped.
1064
- */
1065
- function loadFullEventHistory(sessPath) {
1066
- const envelope = loadSessionEnvelopeFile(sessPath);
1067
- if (!envelope)
1068
- return [];
1069
- const envelopeEvents = envelope.events;
1070
- const archivePath = sessPath.replace(/\.json$/, ".archive.ndjson");
1071
- let archiveEvents = [];
1072
- try {
1073
- const raw = fs.readFileSync(archivePath, "utf-8");
1074
- const lines = raw.split("\n");
1075
- for (const line of lines) {
1076
- const trimmed = line.trim();
1077
- if (trimmed.length === 0)
1078
- continue;
1079
- try {
1080
- const event = JSON.parse(trimmed);
1081
- if (event && typeof event.id === "string" && typeof event.sequence === "number") {
1082
- archiveEvents.push(event);
1083
- }
1084
- }
1085
- catch {
1086
- // Skip corrupted lines
1087
- }
1088
- }
1089
- }
1090
- catch {
1091
- // Archive file doesn't exist or can't be read -- that's fine
1092
- }
1093
- // Merge, deduplicate by id, sort by sequence. The live envelope is the
1094
- // current projection, so it wins if an older archive line has a colliding id.
1095
- const mergedById = new Map();
1096
- for (const event of archiveEvents)
1097
- mergedById.set(event.id, event);
1098
- for (const event of envelopeEvents)
1099
- mergedById.set(event.id, event);
1100
- const merged = [...mergedById.values()];
1101
- merged.sort((a, b) => a.sequence - b.sequence);
1102
- return merged;
1112
+ function agentFromSessionPath(sessPath) {
1113
+ const match = sessPath.match(/(?:^|[/\\])AgentBundles[/\\]([^/\\]+)\.ouro(?:[/\\]|$)/);
1114
+ return match?.[1] ?? "unknown";
1103
1115
  }
1104
1116
  /**
1105
- * Append evicted events to an NDJSON archive file.
1106
- * The archive path is derived from the session path by replacing .json with .archive.ndjson.
1107
- * Each event is written as a single JSON line. The file is appended to, not overwritten.
1108
- * Failures are logged and swallowed -- archive write must never crash the persist path.
1117
+ * Archive writes are intentionally disabled. The session envelope remains the
1118
+ * bounded working-memory record; evicted events are no longer persisted to an
1119
+ * unbounded sidecar.
1109
1120
  */
1110
1121
  function appendEvictedToArchive(sessPath, evictedEvents) {
1111
1122
  if (evictedEvents.length === 0)
1112
1123
  return;
1113
- const archivePath = sessPath.replace(/\.json$/, ".archive.ndjson");
1114
- try {
1115
- const ndjson = evictedEvents.map((event) => JSON.stringify(event)).join("\n") + "\n";
1116
- fs.appendFileSync(archivePath, ndjson);
1117
- }
1118
- catch (err) {
1124
+ if (!archiveDisabledEmitted) {
1125
+ archiveDisabledEmitted = true;
1119
1126
  (0, runtime_1.emitNervesEvent)({
1120
- level: "warn",
1121
1127
  component: "heart",
1122
- event: "heart.archive_write_error",
1123
- message: "failed to write evicted events to archive",
1128
+ event: "heart.session_archive_disabled",
1129
+ message: "session archive append disabled",
1124
1130
  meta: {
1125
- archivePath,
1126
- eventCount: evictedEvents.length,
1127
- /* v8 ignore next -- defensive: Node fs always throws Error instances @preserve */
1128
- error: err instanceof Error ? err.message : String(err),
1131
+ type: "session_archive_disabled",
1132
+ agent: agentFromSessionPath(sessPath),
1133
+ sessionPath: sessPath,
1134
+ evictedCount: evictedEvents.length,
1135
+ ts: new Date().toISOString(),
1129
1136
  },
1130
1137
  });
1131
1138
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.summarizeSessionTail = summarizeSessionTail;
4
- exports.searchSessionTranscript = searchSessionTranscript;
5
4
  const runtime_1 = require("../nerves/runtime");
6
5
  const session_events_1 = require("./session-events");
7
6
  function shouldIncludeToolMessages(friendId, channel) {
@@ -73,11 +72,9 @@ function clip(text, limit = 160) {
73
72
  }
74
73
  function buildSnapshot(summary, tailMessages) {
75
74
  const lines = [`recent focus: ${clip(summary, 240)}`];
76
- const latestUser = [...tailMessages].reverse().find((message) => message.role === "user")?.content;
75
+ const latestUser = [...tailMessages].reverse().find((message) => message.role === "user").content;
77
76
  const latestAssistant = [...tailMessages].reverse().find((message) => message.role === "assistant")?.content;
78
- if (latestUser) {
79
- lines.push(`latest user: ${clip(latestUser)}`);
80
- }
77
+ lines.push(`latest user: ${clip(latestUser)}`);
81
78
  if (latestAssistant) {
82
79
  lines.push(`latest assistant: ${clip(latestAssistant)}`);
83
80
  }
@@ -89,67 +86,11 @@ function selectSessionTailMessages(messages, messageCount) {
89
86
  const selectedIds = new Set(tail.map((message) => message.id));
90
87
  const latestUser = [...messages].reverse().find((message) => message.role === "user");
91
88
  const latestAssistant = [...messages].reverse().find((message) => message.role === "assistant");
92
- if (latestUser)
93
- selectedIds.add(latestUser.id);
89
+ selectedIds.add(latestUser.id);
94
90
  if (latestAssistant)
95
91
  selectedIds.add(latestAssistant.id);
96
92
  return messages.filter((message) => selectedIds.has(message.id));
97
93
  }
98
- function buildSearchSnapshot(query, messages, includeLatestTurn = true) {
99
- const lines = [`history query: "${clip(query, 120)}"`];
100
- if (!includeLatestTurn) {
101
- return lines.join("\n");
102
- }
103
- const latestUser = [...messages].reverse().find((message) => message.role === "user")?.content;
104
- const latestAssistant = [...messages].reverse().find((message) => message.role === "assistant")?.content;
105
- if (latestUser) {
106
- lines.push(`latest user: ${clip(latestUser)}`);
107
- }
108
- if (latestAssistant) {
109
- lines.push(`latest assistant: ${clip(latestAssistant)}`);
110
- }
111
- return lines.join("\n");
112
- }
113
- function buildSearchExcerpts(messages, query, maxMatches) {
114
- const normalizedQuery = query.trim().toLowerCase();
115
- if (!normalizedQuery)
116
- return [];
117
- const candidates = [];
118
- let lastMatchIndex = -2;
119
- for (let i = 0; i < messages.length; i++) {
120
- if (!messages[i].content.toLowerCase().includes(normalizedQuery))
121
- continue;
122
- if (i <= lastMatchIndex + 1)
123
- continue;
124
- lastMatchIndex = i;
125
- const start = Math.max(0, i - 1);
126
- const end = Math.min(messages.length, i + 2);
127
- const excerpt = messages
128
- .slice(start, end)
129
- .map((message) => `[${message.timestamp} | ${message.role} | ${message.id}] ${clip(message.content, 200)}`)
130
- .join("\n");
131
- const signature = messages
132
- .slice(start, end)
133
- .map((message) => `[${message.role}] ${clip(message.content, 200)}`)
134
- .join("\n");
135
- const score = messages
136
- .slice(start, end)
137
- .filter((message) => message.content.toLowerCase().includes(normalizedQuery))
138
- .length;
139
- candidates.push({ excerpt, signature, score, index: i });
140
- }
141
- const seen = new Set();
142
- return candidates
143
- .sort((a, b) => b.score - a.score || a.index - b.index)
144
- .filter((candidate) => {
145
- if (seen.has(candidate.signature))
146
- return false;
147
- seen.add(candidate.signature);
148
- return true;
149
- })
150
- .slice(0, maxMatches)
151
- .map((candidate) => candidate.excerpt);
152
- }
153
94
  async function summarizeSessionTail(options) {
154
95
  (0, runtime_1.emitNervesEvent)({
155
96
  component: "daemon",
@@ -171,16 +112,11 @@ async function summarizeSessionTail(options) {
171
112
  key: options.key,
172
113
  includeToolMessages: shouldIncludeToolMessages(options.friendId, options.channel),
173
114
  };
174
- let visibleMessages = normalizeSessionMessages(envelope.events, transcriptContext);
175
- if (options.archiveFallback && !visibleMessages.some((message) => message.role === "user")) {
176
- const fullHistoryMessages = normalizeSessionMessages((0, session_events_1.loadFullEventHistory)(options.sessionPath), transcriptContext);
177
- if (fullHistoryMessages.length > 0)
178
- visibleMessages = fullHistoryMessages;
115
+ const visibleMessages = normalizeSessionMessages(envelope.events, transcriptContext);
116
+ if (!visibleMessages.some((message) => message.role === "user")) {
117
+ return { kind: "empty", reason: "envelope_trimmed" };
179
118
  }
180
119
  const tailMessages = selectSessionTailMessages(visibleMessages, options.messageCount);
181
- if (tailMessages.length === 0) {
182
- return { kind: "empty" };
183
- }
184
120
  const transcript = tailMessages
185
121
  .map((message) => `[${message.timestamp} | ${message.role} | ${message.id}] ${message.content}`)
186
122
  .join("\n");
@@ -195,49 +131,3 @@ async function summarizeSessionTail(options) {
195
131
  tailMessages,
196
132
  };
197
133
  }
198
- async function searchSessionTranscript(options) {
199
- (0, runtime_1.emitNervesEvent)({
200
- component: "daemon",
201
- event: "daemon.session_search",
202
- message: "searching session transcript",
203
- meta: {
204
- friendId: options.friendId,
205
- channel: options.channel,
206
- key: options.key,
207
- query: options.query,
208
- maxMatches: options.maxMatches ?? 5,
209
- },
210
- });
211
- // Use full event history (envelope + archive) for search to find older messages
212
- const allEvents = (0, session_events_1.loadFullEventHistory)(options.sessionPath);
213
- if (allEvents.length === 0) {
214
- const envelope = (0, session_events_1.loadSessionEnvelopeFile)(options.sessionPath);
215
- if (!envelope)
216
- return { kind: "missing" };
217
- return { kind: "empty" };
218
- }
219
- const messages = normalizeSessionMessages(allEvents, {
220
- friendId: options.friendId,
221
- channel: options.channel,
222
- key: options.key,
223
- includeToolMessages: shouldIncludeToolMessages(options.friendId, options.channel),
224
- });
225
- if (messages.length === 0) {
226
- return { kind: "empty" };
227
- }
228
- const query = options.query.trim();
229
- const matches = buildSearchExcerpts(messages, query, options.maxMatches ?? 5);
230
- if (matches.length === 0) {
231
- return {
232
- kind: "no_match",
233
- query,
234
- snapshot: buildSearchSnapshot(query, messages),
235
- };
236
- }
237
- return {
238
- kind: "ok",
239
- query,
240
- snapshot: buildSearchSnapshot(query, messages, false),
241
- matches,
242
- };
243
- }