@ouro.bot/cli 0.1.0-alpha.320 → 0.1.0-alpha.322

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.
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.readSessionInventory = readSessionInventory;
37
+ exports.readSessionTranscript = readSessionTranscript;
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../../nerves/runtime");
40
+ const identity_1 = require("../../identity");
41
+ const session_events_1 = require("../../session-events");
42
+ const shared_1 = require("./shared");
43
+ /* v8 ignore start — session envelope parsing utilities */
44
+ function parseSessionUsage(raw) {
45
+ if (!raw || typeof raw !== "object")
46
+ return null;
47
+ const record = raw;
48
+ const inputTokens = typeof record.input_tokens === "number" ? record.input_tokens : 0;
49
+ const outputTokens = typeof record.output_tokens === "number" ? record.output_tokens : 0;
50
+ const reasoningTokens = typeof record.reasoning_tokens === "number" ? record.reasoning_tokens : 0;
51
+ const totalTokens = typeof record.total_tokens === "number" ? record.total_tokens : 0;
52
+ if (inputTokens === 0 && outputTokens === 0 && totalTokens === 0)
53
+ return null;
54
+ return { input_tokens: inputTokens, output_tokens: outputTokens, reasoning_tokens: reasoningTokens, total_tokens: totalTokens };
55
+ }
56
+ function parseSessionContinuity(raw) {
57
+ if (!raw)
58
+ return null;
59
+ if (typeof raw !== "object")
60
+ return null;
61
+ const record = raw;
62
+ const continuity = {
63
+ mustResolveBeforeHandoff: record.mustResolveBeforeHandoff === true,
64
+ lastFriendActivityAt: typeof record.lastFriendActivityAt === "string" ? record.lastFriendActivityAt : null,
65
+ };
66
+ if (!continuity.mustResolveBeforeHandoff && continuity.lastFriendActivityAt === null)
67
+ return null;
68
+ return continuity;
69
+ }
70
+ function extractContent(event) {
71
+ if (!event)
72
+ return null;
73
+ const text = (0, session_events_1.extractEventText)(event);
74
+ return text.length > 0 ? text : null;
75
+ }
76
+ function extractToolCallNames(event) {
77
+ if (!event)
78
+ return [];
79
+ return event.toolCalls
80
+ .map((call) => call.function.name)
81
+ .filter((name) => typeof name === "string" && name.length > 0);
82
+ }
83
+ /* v8 ignore stop */
84
+ function estimateTokenCount(messages) {
85
+ let charCount = 0;
86
+ for (const msg of messages) {
87
+ const content = extractContent(msg);
88
+ if (content)
89
+ charCount += content.length;
90
+ if (msg.toolCalls.length > 0)
91
+ charCount += JSON.stringify(msg.toolCalls).length;
92
+ }
93
+ return Math.ceil(charCount / 4);
94
+ }
95
+ /* v8 ignore start — filesystem traversal with defensive isDirectory checks */
96
+ function resolveAllSessionPaths(sessionsDir) {
97
+ const results = [];
98
+ if (!(0, shared_1.safeIsDirectory)(sessionsDir))
99
+ return results;
100
+ for (const friendId of (0, shared_1.safeReaddir)(sessionsDir)) {
101
+ const friendDir = path.join(sessionsDir, friendId);
102
+ if (!(0, shared_1.safeIsDirectory)(friendDir))
103
+ continue;
104
+ for (const channel of (0, shared_1.safeReaddir)(friendDir)) {
105
+ const channelDir = path.join(friendDir, channel);
106
+ if (!(0, shared_1.safeIsDirectory)(channelDir))
107
+ continue;
108
+ for (const file of (0, shared_1.safeReaddir)(channelDir)) {
109
+ if (!file.endsWith(".json"))
110
+ continue;
111
+ const key = file.slice(0, -5);
112
+ results.push({
113
+ friendId,
114
+ channel,
115
+ key,
116
+ sessionPath: path.join(channelDir, file),
117
+ });
118
+ }
119
+ }
120
+ }
121
+ return results;
122
+ }
123
+ /* v8 ignore stop */
124
+ /* v8 ignore start — defensive parsing */
125
+ function readSessionInventory(agentName, options = {}) {
126
+ const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
127
+ const now = options.now?.() ?? new Date();
128
+ const agentRoot = path.join(bundlesRoot, `${agentName}.ouro`);
129
+ const sessionsDir = path.join(agentRoot, "state", "sessions");
130
+ const friendsDir = path.join(agentRoot, "friends");
131
+ const allSessions = resolveAllSessionPaths(sessionsDir);
132
+ const items = [];
133
+ for (const { friendId, channel, key, sessionPath } of allSessions) {
134
+ if (friendId === "self" && channel === "inner")
135
+ continue;
136
+ const envelope = (0, shared_1.readSessionEnvelope)(sessionPath);
137
+ const events = envelope?.events ?? [];
138
+ const chronology = (0, session_events_1.deriveSessionChronology)(events);
139
+ const lastUsage = parseSessionUsage(envelope?.lastUsage);
140
+ const continuity = parseSessionContinuity(envelope?.state);
141
+ const hasObservedEventTiming = events.some((event) => event.time.authoredAt !== null || event.time.observedAt !== null);
142
+ const lastActivityAt = hasObservedEventTiming
143
+ ? (chronology.lastActivityAt ?? continuity?.lastFriendActivityAt ?? (0, shared_1.safeFileMtime)(sessionPath) ?? now.toISOString())
144
+ : (continuity?.lastFriendActivityAt ?? (0, shared_1.safeFileMtime)(sessionPath) ?? now.toISOString());
145
+ const activitySource = hasObservedEventTiming && chronology.lastActivityAt
146
+ ? "event-timeline"
147
+ : continuity?.lastFriendActivityAt
148
+ ? "friend-facing"
149
+ : "mtime-fallback";
150
+ const userMessages = events.filter((m) => m.role === "user");
151
+ const assistantMessages = events.filter((m) => m.role === "assistant");
152
+ const lastUser = userMessages.length > 0 ? userMessages[userMessages.length - 1] : null;
153
+ const lastAssistant = assistantMessages.length > 0 ? assistantMessages[assistantMessages.length - 1] : null;
154
+ const latestToolCallNames = [];
155
+ for (let i = events.length - 1; i >= 0; i--) {
156
+ const names = extractToolCallNames(events[i]);
157
+ if (names.length > 0) {
158
+ latestToolCallNames.push(...names);
159
+ break;
160
+ }
161
+ }
162
+ const friendName = (0, shared_1.resolveFriendName)(friendsDir, friendId);
163
+ const lastMsg = events.length > 0 ? events[events.length - 1] : null;
164
+ const mustResolve = continuity?.mustResolveBeforeHandoff === true;
165
+ let replyState = "idle";
166
+ if (mustResolve) {
167
+ replyState = "on-hold";
168
+ }
169
+ else if (lastMsg?.role === "user") {
170
+ replyState = "needs-reply";
171
+ }
172
+ else if (events.length > 0) {
173
+ replyState = "monitoring";
174
+ }
175
+ items.push({
176
+ friendId,
177
+ friendName,
178
+ channel,
179
+ key,
180
+ sessionPath,
181
+ lastActivityAt,
182
+ activitySource,
183
+ replyState,
184
+ messageCount: events.length,
185
+ lastUsage,
186
+ continuity,
187
+ latestUserExcerpt: (0, shared_1.truncateExcerpt)(extractContent(lastUser)),
188
+ latestAssistantExcerpt: (0, shared_1.truncateExcerpt)(extractContent(lastAssistant)),
189
+ latestToolCallNames,
190
+ estimatedTokens: events.length > 0 ? estimateTokenCount(events) : null,
191
+ });
192
+ }
193
+ items.sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt));
194
+ const ageThreshold = now.getTime() - shared_1.STALE_THRESHOLD_MS;
195
+ const activeCount = items.filter((item) => Date.parse(item.lastActivityAt) >= ageThreshold).length;
196
+ (0, runtime_1.emitNervesEvent)({
197
+ component: "heart",
198
+ event: "heart.outlook_sessions_read",
199
+ message: "reading outlook session inventory",
200
+ meta: { agentName, totalCount: items.length, activeCount },
201
+ });
202
+ return {
203
+ totalCount: items.length,
204
+ activeCount,
205
+ staleCount: items.length - activeCount,
206
+ items,
207
+ };
208
+ }
209
+ function readSessionTranscript(agentName, friendId, channel, key, options = {}) {
210
+ const bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
211
+ const agentRoot = path.join(bundlesRoot, `${agentName}.ouro`);
212
+ const sessionPath = path.join(agentRoot, "state", "sessions", friendId, channel, `${key}.json`);
213
+ const envelope = (0, shared_1.readSessionEnvelope)(sessionPath);
214
+ if (!envelope)
215
+ return null;
216
+ const rawMessages = envelope.events;
217
+ const friendsDir = path.join(agentRoot, "friends");
218
+ const friendName = (0, shared_1.resolveFriendName)(friendsDir, friendId);
219
+ const messages = rawMessages;
220
+ return {
221
+ friendId,
222
+ friendName,
223
+ channel,
224
+ key,
225
+ sessionPath,
226
+ messageCount: messages.length,
227
+ lastUsage: parseSessionUsage(envelope.lastUsage),
228
+ continuity: parseSessionContinuity(envelope.state),
229
+ messages,
230
+ };
231
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.STALE_THRESHOLD_MS = exports.BLOCKED_CODING_STATUSES = exports.ACTIVE_CODING_STATUSES = void 0;
37
+ exports.issue = issue;
38
+ exports.safeReaddir = safeReaddir;
39
+ exports.safeIsDirectory = safeIsDirectory;
40
+ exports.resolveFriendName = resolveFriendName;
41
+ exports.safeFileMtime = safeFileMtime;
42
+ exports.truncateExcerpt = truncateExcerpt;
43
+ exports.readSessionEnvelope = readSessionEnvelope;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const runtime_1 = require("../../../nerves/runtime");
47
+ const session_events_1 = require("../../session-events");
48
+ exports.ACTIVE_CODING_STATUSES = new Set(["spawning", "running", "waiting_input", "stalled"]);
49
+ exports.BLOCKED_CODING_STATUSES = new Set(["waiting_input", "stalled"]);
50
+ exports.STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
51
+ function issue(code, detail) {
52
+ return { code, detail };
53
+ }
54
+ function safeReaddir(dir) {
55
+ try {
56
+ return fs.readdirSync(dir);
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ }
62
+ function safeIsDirectory(filePath) {
63
+ try {
64
+ return fs.statSync(filePath).isDirectory();
65
+ /* v8 ignore start */
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ /* v8 ignore stop */
71
+ }
72
+ /* v8 ignore start — defensive friend name resolution */
73
+ function resolveFriendName(friendsDir, friendId) {
74
+ try {
75
+ const raw = fs.readFileSync(path.join(friendsDir, `${friendId}.json`), "utf-8");
76
+ const parsed = JSON.parse(raw);
77
+ return typeof parsed.name === "string" ? parsed.name : friendId;
78
+ }
79
+ catch {
80
+ return friendId;
81
+ }
82
+ }
83
+ /* v8 ignore stop */
84
+ /* v8 ignore start — utility helpers with defensive branches */
85
+ function safeFileMtime(filePath) {
86
+ try {
87
+ return fs.statSync(filePath).mtime.toISOString();
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ function truncateExcerpt(content, maxLength = 200) {
94
+ if (!content)
95
+ return null;
96
+ if (content.length <= maxLength)
97
+ return content;
98
+ const truncated = content.slice(0, maxLength);
99
+ const lastSpace = truncated.lastIndexOf(" ");
100
+ return (lastSpace > maxLength * 0.6 ? truncated.slice(0, lastSpace) : truncated) + "…";
101
+ }
102
+ /* v8 ignore stop */
103
+ function readSessionEnvelope(sessionPath) {
104
+ (0, runtime_1.emitNervesEvent)({
105
+ component: "heart",
106
+ event: "heart.outlook_session_envelope_read",
107
+ message: "reading outlook session envelope",
108
+ meta: { sessionPath },
109
+ });
110
+ return (0, session_events_1.loadSessionEnvelopeFile)(sessionPath);
111
+ }
@@ -4,6 +4,7 @@ exports.createBlueBubblesClient = createBlueBubblesClient;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const config_1 = require("../../heart/config");
6
6
  const identity_1 = require("../../heart/identity");
7
+ const bluebubbles_health_diagnostics_1 = require("../../heart/daemon/bluebubbles-health-diagnostics");
7
8
  const runtime_1 = require("../../nerves/runtime");
8
9
  const minimax_1 = require("../../heart/providers/minimax");
9
10
  const minimax_vlm_1 = require("../../heart/providers/minimax-vlm");
@@ -332,19 +333,19 @@ function createBlueBubblesClient(config = (0, config_1.getBlueBubblesConfig)(),
332
333
  }
333
334
  },
334
335
  async checkHealth() {
335
- const url = buildBlueBubblesApiUrl(config.serverUrl, "/api/v1/message/count", config.password);
336
336
  (0, runtime_1.emitNervesEvent)({
337
337
  component: "senses",
338
338
  event: "senses.bluebubbles_healthcheck_start",
339
339
  message: "probing bluebubbles upstream health",
340
340
  meta: { serverUrl: config.serverUrl },
341
341
  });
342
- const response = await fetch(url, {
343
- method: "GET",
344
- signal: AbortSignal.timeout(channelConfig.requestTimeoutMs),
342
+ const result = await (0, bluebubbles_health_diagnostics_1.probeBlueBubblesHealth)({
343
+ serverUrl: config.serverUrl,
344
+ password: config.password,
345
+ requestTimeoutMs: channelConfig.requestTimeoutMs,
346
+ fetchImpl: fetch,
345
347
  });
346
- if (!response.ok) {
347
- const errorText = await response.text().catch(() => "");
348
+ if (!result.ok) {
348
349
  (0, runtime_1.emitNervesEvent)({
349
350
  level: "warn",
350
351
  component: "senses",
@@ -352,11 +353,13 @@ function createBlueBubblesClient(config = (0, config_1.getBlueBubblesConfig)(),
352
353
  message: "bluebubbles upstream health probe failed",
353
354
  meta: {
354
355
  serverUrl: config.serverUrl,
355
- status: response.status,
356
- reason: errorText || "unknown",
356
+ status: result.status,
357
+ reason: result.reason,
358
+ classification: result.classification,
359
+ detail: (0, bluebubbles_health_diagnostics_1.redactBlueBubblesHealthDetailForNerves)(result.detail),
357
360
  },
358
361
  });
359
- throw new Error(`BlueBubbles upstream health check failed (${response.status}): ${errorText || "unknown"}`);
362
+ throw new Error(result.detail);
360
363
  }
361
364
  (0, runtime_1.emitNervesEvent)({
362
365
  component: "senses",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.320",
3
+ "version": "0.1.0-alpha.322",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",