@ouro.bot/cli 0.1.0-alpha.539 → 0.1.0-alpha.540

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,13 @@
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.540",
6
+ "changes": [
7
+ "Runtime prompt rendering, MCP child-process startup, and BlueBubbles recovery now repair a deleted process cwd back to the runtime root instead of failing with `uv_cwd` after a transient worktree disappears.",
8
+ "BlueBubbles captured-inbound recovery now has regression coverage proving a stuck iMessage recovery item can replay successfully even when the daemon inherited a vanished working directory."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.539",
6
13
  "changes": [
@@ -0,0 +1,87 @@
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.recoverRuntimeCwd = recoverRuntimeCwd;
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ const runtime_1 = require("../nerves/runtime");
40
+ function defaultRuntimeRoot() {
41
+ return path.resolve(__dirname, "../..");
42
+ }
43
+ const defaultDeps = {
44
+ cwd: process.cwd.bind(process),
45
+ chdir: process.chdir.bind(process),
46
+ existsSync: fs.existsSync,
47
+ };
48
+ function recoverRuntimeCwd(fallback = defaultRuntimeRoot(), deps = defaultDeps) {
49
+ try {
50
+ return deps.cwd();
51
+ }
52
+ catch (error) {
53
+ const reason = error instanceof Error ? error.message : String(error);
54
+ let recovered = false;
55
+ let resolved = fallback;
56
+ let repairReason;
57
+ try {
58
+ if (deps.existsSync(fallback)) {
59
+ deps.chdir(fallback);
60
+ resolved = deps.cwd();
61
+ recovered = true;
62
+ }
63
+ else {
64
+ repairReason = "fallback cwd does not exist";
65
+ }
66
+ }
67
+ catch (repairError) {
68
+ repairReason = repairError instanceof Error ? repairError.message : String(repairError);
69
+ }
70
+ (0, runtime_1.emitNervesEvent)({
71
+ level: recovered ? "warn" : "error",
72
+ component: "heart",
73
+ event: "heart.cwd_recovery",
74
+ message: recovered
75
+ ? "recovered process cwd after the previous working directory disappeared"
76
+ : "process cwd disappeared and could not be repaired automatically",
77
+ meta: {
78
+ reason,
79
+ fallback,
80
+ resolved,
81
+ recovered,
82
+ ...(repairReason ? { repairReason } : {}),
83
+ },
84
+ });
85
+ return resolved;
86
+ }
87
+ }
@@ -229,6 +229,7 @@ function messageMatchesFilters(message, filters) {
229
229
  class AzureBlobMailroomStore {
230
230
  serviceClient;
231
231
  containerName;
232
+ mailSearchCache;
232
233
  blobOperationTimeoutMs;
233
234
  messageFetchConcurrency;
234
235
  backfillConcurrency;
@@ -236,6 +237,7 @@ class AzureBlobMailroomStore {
236
237
  constructor(options) {
237
238
  this.serviceClient = options.serviceClient;
238
239
  this.containerName = options.containerName;
240
+ this.mailSearchCache = options.mailSearchCache ?? null;
239
241
  this.blobOperationTimeoutMs = positiveInteger(options.blobOperationTimeoutMs, DEFAULT_BLOB_OPERATION_TIMEOUT_MS);
240
242
  this.messageFetchConcurrency = positiveInteger(options.messageFetchConcurrency, DEFAULT_MESSAGE_FETCH_CONCURRENCY);
241
243
  this.backfillConcurrency = positiveInteger(options.backfillConcurrency, DEFAULT_MESSAGE_INDEX_BACKFILL_CONCURRENCY);
@@ -282,6 +284,16 @@ class AzureBlobMailroomStore {
282
284
  async removeMessageIndex(message) {
283
285
  await this.messageIndexBlob(messageIndexBlobName(message)).deleteIfExists();
284
286
  }
287
+ upsertMailSearchCache(message, privateEnvelope) {
288
+ if (!this.mailSearchCache)
289
+ return;
290
+ (0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope, this.mailSearchCache);
291
+ }
292
+ syncMailSearchCache(message) {
293
+ if (!this.mailSearchCache)
294
+ return;
295
+ (0, search_cache_1.syncMailSearchCacheMetadata)(message, this.mailSearchCache);
296
+ }
285
297
  async listMessagesLegacy(filters) {
286
298
  const messageBlobNames = [];
287
299
  for await (const item of this.container.listBlobsFlat({ prefix: "messages/" })) {
@@ -413,7 +425,7 @@ class AzureBlobMailroomStore {
413
425
  throw error;
414
426
  }
415
427
  if (existing) {
416
- (0, search_cache_1.upsertMailSearchCacheDocument)(existing, privateEnvelope);
428
+ this.upsertMailSearchCache(existing, privateEnvelope);
417
429
  await this.putMessageIndex(existing);
418
430
  (0, runtime_1.emitNervesEvent)({
419
431
  component: "senses",
@@ -426,7 +438,7 @@ class AzureBlobMailroomStore {
426
438
  await this.rawBlob(message.rawObject).uploadData(blobText(rawPayload));
427
439
  await this.messageBlob(message.id).uploadData(blobText(message));
428
440
  await this.putMessageIndex(message);
429
- (0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope);
441
+ this.upsertMailSearchCache(message, privateEnvelope);
430
442
  if (candidate) {
431
443
  await this.candidateBlob(candidate.id).uploadData(blobText(candidate));
432
444
  }
@@ -493,7 +505,7 @@ class AzureBlobMailroomStore {
493
505
  await blob.uploadData(blobText(updated));
494
506
  await this.removeMessageIndex(message);
495
507
  await this.putMessageIndex(updated);
496
- (0, search_cache_1.syncMailSearchCacheMetadata)(updated);
508
+ this.syncMailSearchCache(updated);
497
509
  (0, runtime_1.emitNervesEvent)({
498
510
  component: "senses",
499
511
  event: "senses.mail_blob_store_message_placement_updated",
@@ -74,8 +74,12 @@ function sourceMatchesFilter(source, filter) {
74
74
  }
75
75
  class FileMailroomStore {
76
76
  rootDir;
77
+ mailSearchCache;
77
78
  constructor(options) {
78
79
  this.rootDir = options.rootDir;
80
+ this.mailSearchCache = options.mailSearchCache ?? {
81
+ cacheDirForAgent: () => path.resolve(this.rootDir, "..", "mail-search"),
82
+ };
79
83
  ensureDir(this.messagesDir);
80
84
  ensureDir(this.rawDir);
81
85
  ensureDir(this.logsDir);
@@ -129,7 +133,7 @@ class FileMailroomStore {
129
133
  const { message, rawPayload, privateEnvelope, candidate } = await (0, core_1.buildStoredMailMessage)(input);
130
134
  const existing = readJson(this.messagePath(message.id));
131
135
  if (existing) {
132
- (0, search_cache_1.upsertMailSearchCacheDocument)(existing, privateEnvelope);
136
+ (0, search_cache_1.upsertMailSearchCacheDocument)(existing, privateEnvelope, this.mailSearchCache);
133
137
  (0, runtime_1.emitNervesEvent)({
134
138
  component: "senses",
135
139
  event: "senses.mail_store_dedupe",
@@ -140,7 +144,7 @@ class FileMailroomStore {
140
144
  }
141
145
  writeJson(this.rawPath(message.rawObject), rawPayload);
142
146
  writeJson(this.messagePath(message.id), message);
143
- (0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope);
147
+ (0, search_cache_1.upsertMailSearchCacheDocument)(message, privateEnvelope, this.mailSearchCache);
144
148
  if (candidate) {
145
149
  writeJson(this.candidatePath(candidate.id), candidate);
146
150
  }
@@ -194,7 +198,7 @@ class FileMailroomStore {
194
198
  }
195
199
  const updated = { ...message, placement };
196
200
  writeJson(this.messagePath(id), updated);
197
- (0, search_cache_1.syncMailSearchCacheMetadata)(updated);
201
+ (0, search_cache_1.syncMailSearchCacheMetadata)(updated, this.mailSearchCache);
198
202
  (0, runtime_1.emitNervesEvent)({
199
203
  component: "senses",
200
204
  event: "senses.mail_store_message_placement_updated",
@@ -118,6 +118,9 @@ function createMailroomStore(config, agentName) {
118
118
  store: new blob_store_1.AzureBlobMailroomStore({
119
119
  serviceClient: new storage_blob_1.BlobServiceClient(config.azureAccountUrl, createBlobCredential(config)),
120
120
  containerName,
121
+ mailSearchCache: {
122
+ cacheDirForAgent: (agentId) => path.join((0, identity_2.getAgentRoot)(agentId), "state", "mail-search"),
123
+ },
121
124
  }),
122
125
  storeKind: "azure-blob",
123
126
  storeLabel: `${config.azureAccountUrl}/${containerName}`,
@@ -50,14 +50,18 @@ const search_relevance_1 = require("./search-relevance");
50
50
  const SEARCH_TEXT_EXCERPT_LIMIT = 16_384;
51
51
  exports.MAIL_SEARCH_TEXT_PROJECTION_VERSION = 2;
52
52
  const cacheStates = new Map();
53
- function cacheDir(agentId) {
53
+ function defaultCacheDir(agentId) {
54
54
  return path.join((0, identity_1.getAgentRoot)(agentId), "state", "mail-search");
55
55
  }
56
- function cachePath(agentId, messageId) {
57
- return path.join(cacheDir(agentId), `${messageId}.json`);
56
+ function cacheDir(agentId, options = {}) {
57
+ const resolve = options.cacheDirForAgent ?? defaultCacheDir;
58
+ return resolve(agentId);
58
59
  }
59
- function coverageDir(agentId) {
60
- return path.join(cacheDir(agentId), "coverage");
60
+ function cachePath(agentId, messageId, options) {
61
+ return path.join(cacheDir(agentId, options), `${messageId}.json`);
62
+ }
63
+ function coverageDir(agentId, options) {
64
+ return path.join(cacheDir(agentId, options), "coverage");
61
65
  }
62
66
  function normalizedCoverageKey(key) {
63
67
  return {
@@ -68,10 +72,10 @@ function normalizedCoverageKey(key) {
68
72
  ...(key.source ? { source: key.source.toLowerCase() } : {}),
69
73
  };
70
74
  }
71
- function coveragePath(key) {
75
+ function coveragePath(key, options) {
72
76
  const normalized = normalizedCoverageKey(key);
73
77
  const encoded = Buffer.from(JSON.stringify(normalized)).toString("base64url");
74
- return path.join(coverageDir(normalized.agentId), `${encoded}.json`);
78
+ return path.join(coverageDir(normalized.agentId, options), `${encoded}.json`);
75
79
  }
76
80
  function normalizeSearchText(privateEnvelope) {
77
81
  const readableText = (0, core_1.privateMailEnvelopeReadableText)(privateEnvelope);
@@ -90,8 +94,8 @@ function readJsonDocument(filePath) {
90
94
  return null;
91
95
  }
92
96
  }
93
- function cacheState(agentId) {
94
- const key = `${agentId}:${cacheDir(agentId)}`;
97
+ function cacheState(agentId, options) {
98
+ const key = `${agentId}:${cacheDir(agentId, options)}`;
95
99
  let state = cacheStates.get(key);
96
100
  if (state)
97
101
  return state;
@@ -99,12 +103,12 @@ function cacheState(agentId) {
99
103
  cacheStates.set(key, state);
100
104
  return state;
101
105
  }
102
- function loadCache(agentId) {
103
- const state = cacheState(agentId);
106
+ function loadCache(agentId, options) {
107
+ const state = cacheState(agentId, options);
104
108
  if (state.loaded)
105
109
  return state.docs;
106
110
  state.loaded = true;
107
- const dir = cacheDir(agentId);
111
+ const dir = cacheDir(agentId, options);
108
112
  if (!fs.existsSync(dir))
109
113
  return state.docs;
110
114
  for (const entry of fs.readdirSync(dir)) {
@@ -138,12 +142,12 @@ function buildMailSearchCacheDocument(message, privateEnvelope) {
138
142
  attachmentCount: privateEnvelope.attachments.length,
139
143
  };
140
144
  }
141
- function upsertMailSearchCacheDocument(message, privateEnvelope) {
145
+ function upsertMailSearchCacheDocument(message, privateEnvelope, options) {
142
146
  const document = buildMailSearchCacheDocument(message, privateEnvelope);
143
- const dir = cacheDir(message.agentId);
147
+ const dir = cacheDir(message.agentId, options);
144
148
  fs.mkdirSync(dir, { recursive: true });
145
- fs.writeFileSync(cachePath(message.agentId, message.id), `${JSON.stringify(document)}\n`, "utf-8");
146
- const state = cacheState(message.agentId);
149
+ fs.writeFileSync(cachePath(message.agentId, message.id, options), `${JSON.stringify(document)}\n`, "utf-8");
150
+ const state = cacheState(message.agentId, options);
147
151
  if (state.loaded)
148
152
  state.docs.set(document.messageId, document);
149
153
  (0, runtime_1.emitNervesEvent)({
@@ -159,8 +163,8 @@ function upsertMailSearchCacheDocument(message, privateEnvelope) {
159
163
  });
160
164
  return document;
161
165
  }
162
- function syncMailSearchCacheMetadata(message) {
163
- const existing = readJsonDocument(cachePath(message.agentId, message.id));
166
+ function syncMailSearchCacheMetadata(message, options) {
167
+ const existing = readJsonDocument(cachePath(message.agentId, message.id, options));
164
168
  if (!existing)
165
169
  return;
166
170
  const updated = {
@@ -171,8 +175,8 @@ function syncMailSearchCacheMetadata(message) {
171
175
  ...(message.ownerEmail ? { ownerEmail: message.ownerEmail } : {}),
172
176
  ...(message.source ? { source: message.source } : {}),
173
177
  };
174
- fs.writeFileSync(cachePath(message.agentId, message.id), `${JSON.stringify(updated)}\n`, "utf-8");
175
- const state = cacheState(message.agentId);
178
+ fs.writeFileSync(cachePath(message.agentId, message.id, options), `${JSON.stringify(updated)}\n`, "utf-8");
179
+ const state = cacheState(message.agentId, options);
176
180
  if (state.loaded)
177
181
  state.docs.set(updated.messageId, updated);
178
182
  }
@@ -183,9 +187,9 @@ function sourceMatches(source, filter) {
183
187
  return false;
184
188
  return source.toLowerCase() === filter.toLowerCase();
185
189
  }
186
- function searchMailSearchCache(filters) {
190
+ function searchMailSearchCache(filters, options) {
187
191
  const queryTerms = filters.queryTerms ?? [];
188
- const docs = [...loadCache(filters.agentId).values()]
192
+ const docs = [...loadCache(filters.agentId, options).values()]
189
193
  .filter((document) => filters.placement ? document.placement === filters.placement : true)
190
194
  .filter((document) => filters.compartmentKind ? document.compartmentKind === filters.compartmentKind : true)
191
195
  .filter((document) => sourceMatches(document.source, filters.source))
@@ -204,8 +208,8 @@ function searchMailSearchCache(filters) {
204
208
  }
205
209
  return typeof filters.limit === "number" ? ordered.slice(0, filters.limit) : ordered;
206
210
  }
207
- function readMailSearchCoverageRecord(key) {
208
- const document = readJsonDocument(coveragePath(key));
211
+ function readMailSearchCoverageRecord(key, options) {
212
+ const document = readJsonDocument(coveragePath(key, options));
209
213
  if (!document || document.schemaVersion !== 1 || document.agentId !== key.agentId)
210
214
  return null;
211
215
  const normalized = normalizedCoverageKey(key);
@@ -214,7 +218,7 @@ function readMailSearchCoverageRecord(key) {
214
218
  return null;
215
219
  return document;
216
220
  }
217
- function writeMailSearchCoverageRecord(record) {
221
+ function writeMailSearchCoverageRecord(record, options) {
218
222
  const normalized = normalizedCoverageKey(record);
219
223
  const document = {
220
224
  schemaVersion: 1,
@@ -229,8 +233,8 @@ function writeMailSearchCoverageRecord(record) {
229
233
  ...(record.oldestReceivedAt ? { oldestReceivedAt: record.oldestReceivedAt } : {}),
230
234
  ...(record.newestReceivedAt ? { newestReceivedAt: record.newestReceivedAt } : {}),
231
235
  };
232
- fs.mkdirSync(coverageDir(document.agentId), { recursive: true });
233
- fs.writeFileSync(coveragePath(document), `${JSON.stringify(document)}\n`, "utf-8");
236
+ fs.mkdirSync(coverageDir(document.agentId, options), { recursive: true });
237
+ fs.writeFileSync(coveragePath(document, options), `${JSON.stringify(document)}\n`, "utf-8");
234
238
  (0, runtime_1.emitNervesEvent)({
235
239
  component: "senses",
236
240
  event: "senses.mail_search_coverage_written",
@@ -66,6 +66,7 @@ const ouro_version_manager_1 = require("../heart/versioning/ouro-version-manager
66
66
  const tools_1 = require("../repertoire/tools");
67
67
  const skills_1 = require("../repertoire/skills");
68
68
  const identity_1 = require("../heart/identity");
69
+ const runtime_cwd_1 = require("../heart/runtime-cwd");
69
70
  const config_1 = require("../heart/config");
70
71
  const runtime_credentials_1 = require("../heart/runtime-credentials");
71
72
  const runtime_mode_1 = require("../heart/daemon/runtime-mode");
@@ -380,7 +381,7 @@ function runtimeInfoSection(channel, options) {
380
381
  const sourceRoot = (0, identity_1.getRepoRoot)();
381
382
  lines.push(`source root: ${sourceRoot}`);
382
383
  lines.push(`runtime mode: ${(0, runtime_mode_1.detectRuntimeMode)(sourceRoot)}`);
383
- lines.push(`cwd: ${process.cwd()}`);
384
+ lines.push(`cwd: ${(0, runtime_cwd_1.recoverRuntimeCwd)(sourceRoot)}`);
384
385
  lines.push(`channel: ${channel}`);
385
386
  lines.push(`current sense: ${channel}`);
386
387
  lines.push(`process type: ${processTypeLabel(channel)}`);
@@ -5,6 +5,7 @@ exports.isMcpTransportError = isMcpTransportError;
5
5
  const child_process_1 = require("child_process");
6
6
  const readline_1 = require("readline");
7
7
  const runtime_1 = require("../nerves/runtime");
8
+ const runtime_cwd_1 = require("../heart/runtime-cwd");
8
9
  const MCP_PROTOCOL_VERSION = "2024-11-05";
9
10
  const DEFAULT_REQUEST_TIMEOUT = 10_000;
10
11
  const DEFAULT_TOOL_CALL_TIMEOUT = 30_000;
@@ -43,10 +44,11 @@ class McpClient {
43
44
  meta: { command: this.config.command },
44
45
  });
45
46
  const env = { ...process.env, ...this.config.env };
47
+ const spawnCwd = this.config.cwd ?? (0, runtime_cwd_1.recoverRuntimeCwd)();
46
48
  this.process = (0, child_process_1.spawn)(this.config.command, this.config.args ?? [], {
47
49
  env,
48
50
  stdio: ["pipe", "pipe", "pipe"],
49
- cwd: this.config.cwd,
51
+ cwd: spawnCwd,
50
52
  });
51
53
  this.setupLineReader();
52
54
  this.setupProcessHandlers();
@@ -55,6 +55,7 @@ const path = __importStar(require("node:path"));
55
55
  const core_1 = require("../../heart/core");
56
56
  const config_1 = require("../../heart/config");
57
57
  const identity_1 = require("../../heart/identity");
58
+ const runtime_cwd_1 = require("../../heart/runtime-cwd");
58
59
  const turn_coordinator_1 = require("../../heart/turn-coordinator");
59
60
  const context_1 = require("../../mind/context");
60
61
  const tokens_1 = require("../../mind/friends/tokens");
@@ -759,6 +760,7 @@ async function shouldFilterAgentSelfHandle(event, resolvedDeps) {
759
760
  async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source, options = {}) {
760
761
  const client = resolvedDeps.createClient();
761
762
  const agentName = resolvedDeps.getAgentName();
763
+ (0, runtime_cwd_1.recoverRuntimeCwd)();
762
764
  if (event.fromMe) {
763
765
  (0, runtime_1.emitNervesEvent)({
764
766
  component: "senses",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.539",
3
+ "version": "0.1.0-alpha.540",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",