@ouro.bot/cli 0.1.0-alpha.695 → 0.1.0-alpha.697

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 (48) hide show
  1. package/changelog.json +15 -0
  2. package/dist/arc/flight-recorder.js +139 -21
  3. package/dist/arc/obligations.js +28 -6
  4. package/dist/arc/packets.js +48 -0
  5. package/dist/heart/autonomy-budget.js +434 -0
  6. package/dist/heart/core.js +11 -0
  7. package/dist/heart/daemon/cadence.js +110 -0
  8. package/dist/heart/daemon/cli-exec.js +20 -0
  9. package/dist/heart/daemon/cli-help.js +74 -0
  10. package/dist/heart/daemon/cli-parse.js +466 -0
  11. package/dist/heart/daemon/daemon.js +24 -3
  12. package/dist/heart/daemon/doctor.js +151 -0
  13. package/dist/heart/daemon/stale-bundle-prune.js +23 -6
  14. package/dist/heart/habits/habit-parser.js +4 -1
  15. package/dist/heart/habits/habit-scheduler.js +46 -38
  16. package/dist/heart/habits/habit-session.js +2 -3
  17. package/dist/heart/run-ledger.js +247 -0
  18. package/dist/heart/runtime-credentials.js +12 -3
  19. package/dist/heart/work-card.js +1 -11
  20. package/dist/mind/prompt-budget.js +380 -0
  21. package/dist/repertoire/tools-base.js +2 -0
  22. package/dist/repertoire/tools-obligations.js +39 -0
  23. package/dist/repertoire/tools-rsvp.js +139 -0
  24. package/dist/rsvp/aisleplanner-client.js +354 -0
  25. package/dist/rsvp/cli.js +679 -0
  26. package/dist/rsvp/config.js +327 -0
  27. package/dist/rsvp/cutover.js +431 -0
  28. package/dist/rsvp/diagnostics.js +222 -0
  29. package/dist/rsvp/diff-renderer.js +159 -0
  30. package/dist/rsvp/habit-policy.js +85 -0
  31. package/dist/rsvp/habit-stage.js +93 -0
  32. package/dist/rsvp/incident-bundle.js +97 -0
  33. package/dist/rsvp/migration.js +204 -0
  34. package/dist/rsvp/outbound-state.js +254 -0
  35. package/dist/rsvp/query.js +118 -0
  36. package/dist/rsvp/replay.js +233 -0
  37. package/dist/rsvp/snapshot.js +232 -0
  38. package/dist/senses/bluebubbles/client.js +13 -7
  39. package/dist/senses/bluebubbles/index.js +227 -22
  40. package/dist/senses/bluebubbles/outbound-state.js +304 -0
  41. package/dist/senses/bluebubbles/replay.js +147 -0
  42. package/dist/senses/context-packet-ledger.js +245 -0
  43. package/dist/senses/context-packets.js +154 -0
  44. package/dist/senses/pipeline.js +92 -12
  45. package/dist/senses/private-runtime-worker.js +170 -31
  46. package/dist/senses/private-runtime.js +13 -1
  47. package/dist/senses/voice/twilio-phone.js +9 -5
  48. package/package.json +1 -1
@@ -0,0 +1,679 @@
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.runRsvpCliCommand = runRsvpCliCommand;
37
+ const fs = __importStar(require("node:fs"));
38
+ const os = __importStar(require("node:os"));
39
+ const path = __importStar(require("node:path"));
40
+ const doctor_1 = require("../heart/daemon/doctor");
41
+ const identity_1 = require("../heart/identity");
42
+ const machine_identity_1 = require("../heart/machine-identity");
43
+ const runtime_credentials_1 = require("../heart/runtime-credentials");
44
+ const runtime_1 = require("../nerves/runtime");
45
+ const client_1 = require("../senses/bluebubbles/client");
46
+ const aisleplanner_client_1 = require("./aisleplanner-client");
47
+ const config_1 = require("./config");
48
+ const cutover_1 = require("./cutover");
49
+ const diff_renderer_1 = require("./diff-renderer");
50
+ const habit_stage_1 = require("./habit-stage");
51
+ const incident_bundle_1 = require("./incident-bundle");
52
+ const migration_1 = require("./migration");
53
+ const outbound_state_1 = require("./outbound-state");
54
+ const query_1 = require("./query");
55
+ const replay_1 = require("./replay");
56
+ const snapshot_1 = require("./snapshot");
57
+ function commandJson(command) {
58
+ return "json" in command && command.json === true;
59
+ }
60
+ function commandOutputPath(command) {
61
+ return "outputPath" in command ? command.outputPath : undefined;
62
+ }
63
+ function commandAgent(command) {
64
+ return "agent" in command ? command.agent : undefined;
65
+ }
66
+ function commandMode(command) {
67
+ return "mode" in command ? command.mode : undefined;
68
+ }
69
+ function maybeAgentRoot(command, deps) {
70
+ const agent = commandAgent(command);
71
+ if (!agent)
72
+ return undefined;
73
+ return deps.agentBundleRoot ?? path.join(deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(), `${agent}.ouro`);
74
+ }
75
+ function writeJsonFile(filePath, payload) {
76
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
77
+ fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
78
+ }
79
+ function readJsonFile(filePath) {
80
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
81
+ }
82
+ function fallbackReadRsvpConfig(agentRoot) {
83
+ const configPath = path.join(agentRoot, "rsvp", "config.json");
84
+ if (!fs.existsSync(configPath)) {
85
+ return { ok: false, reason: "missing", path: configPath, message: "missing native RSVP config" };
86
+ }
87
+ try {
88
+ return { ok: true, config: readJsonFile(configPath), path: configPath };
89
+ }
90
+ catch {
91
+ return { ok: false, reason: "malformed", path: configPath, message: "native RSVP config is not valid JSON" };
92
+ }
93
+ }
94
+ function resolveRsvpConfig(agentRoot) {
95
+ return (0, config_1.readRsvpConfig)(agentRoot) ?? fallbackReadRsvpConfig(agentRoot);
96
+ }
97
+ function localText(value) {
98
+ return typeof value === "string" ? value.trim() : "";
99
+ }
100
+ function defaultLegacyRoot(deps) {
101
+ return path.join(deps.homeDir ?? os.homedir(), "Projects", "rsvp-tracker");
102
+ }
103
+ function resolveCutoverLegacyRoot(config, deps) {
104
+ const configured = localText(config.cutover?.legacyRoot);
105
+ if (configured)
106
+ return configured;
107
+ const fallback = defaultLegacyRoot(deps);
108
+ return fs.existsSync(path.join(fallback, "config.json")) ? fallback : null;
109
+ }
110
+ async function checkLiveSendCutover(input) {
111
+ const legacyRoot = resolveCutoverLegacyRoot(input.config, input.deps);
112
+ if (!legacyRoot)
113
+ return null;
114
+ return (0, cutover_1.checkRsvpCutover)({
115
+ agent: input.agent,
116
+ legacyRoot,
117
+ ...(input.deps.rsvpCutoverDeps ? { deps: input.deps.rsvpCutoverDeps } : {}),
118
+ });
119
+ }
120
+ function blockedLiveSendPayload(command, message, extra = {}) {
121
+ return basePayload(command, false, message, {
122
+ ok: false,
123
+ allowSend: command.allowSend === true,
124
+ sendAllowed: false,
125
+ requires: "passing RSVP cutover check",
126
+ ...extra,
127
+ });
128
+ }
129
+ function cutoverPayload(cutover) {
130
+ if (!cutover) {
131
+ return {
132
+ cutover: {
133
+ ok: false,
134
+ sendAllowed: false,
135
+ denialReasons: ["no RSVP legacy root found for live-send cutover proof"],
136
+ },
137
+ };
138
+ }
139
+ return {
140
+ legacyRoot: cutover.legacyRoot,
141
+ cutover: {
142
+ ok: cutover.ok,
143
+ sendAllowed: cutover.sendAllowed,
144
+ checks: cutover.checks,
145
+ denialReasons: cutover.denialReasons,
146
+ },
147
+ };
148
+ }
149
+ function rsvpStateRoot(agentRoot) {
150
+ return path.join(agentRoot, "state", "rsvp");
151
+ }
152
+ function latestSnapshotPath(agentRoot) {
153
+ return path.join(rsvpStateRoot(agentRoot), "snapshots", "latest.json");
154
+ }
155
+ function snapshotFilePath(agentRoot, snapshotId) {
156
+ return path.join(rsvpStateRoot(agentRoot), "snapshots", `${snapshotId}.json`);
157
+ }
158
+ function writeSnapshotFiles(agentRoot, snapshot) {
159
+ fs.mkdirSync(path.join(rsvpStateRoot(agentRoot), "snapshots"), { recursive: true });
160
+ fs.writeFileSync(snapshotFilePath(agentRoot, snapshot.snapshotId), `${JSON.stringify(snapshot, null, 2)}\n`, "utf-8");
161
+ fs.writeFileSync(latestSnapshotPath(agentRoot), `${JSON.stringify(snapshot, null, 2)}\n`, "utf-8");
162
+ }
163
+ function parseSnapshotFromFile(filePath) {
164
+ const result = (0, snapshot_1.parseRsvpSnapshot)(readJsonFile(filePath));
165
+ if (!result.ok)
166
+ throw new Error(`invalid RSVP snapshot at ${filePath}: ${result.reason}`);
167
+ return result.snapshot;
168
+ }
169
+ function readBaselineSnapshot(agentRoot) {
170
+ const baselinePath = path.join(rsvpStateRoot(agentRoot), "baseline.json");
171
+ if (!fs.existsSync(baselinePath))
172
+ return null;
173
+ const baseline = readJsonFile(baselinePath);
174
+ const snapshotId = baseline && typeof baseline === "object" && !Array.isArray(baseline)
175
+ ? baseline.nativeSnapshotId
176
+ : null;
177
+ return typeof snapshotId === "string" && fs.existsSync(snapshotFilePath(agentRoot, snapshotId))
178
+ ? parseSnapshotFromFile(snapshotFilePath(agentRoot, snapshotId))
179
+ : null;
180
+ }
181
+ function readLatestSnapshot(agentRoot) {
182
+ return parseSnapshotFromFile(latestSnapshotPath(agentRoot));
183
+ }
184
+ function machineIdForCli() {
185
+ return (0, machine_identity_1.loadOrCreateMachineIdentity)().machineId;
186
+ }
187
+ function blueBubblesChatFor(config) {
188
+ const sendTarget = config.bluebubblesRoute.chatIdentifier
189
+ ? { kind: "chat_identifier", value: config.bluebubblesRoute.chatIdentifier }
190
+ : { kind: "chat_guid", value: config.bluebubblesRoute.chatGuid };
191
+ return {
192
+ chatGuid: config.bluebubblesRoute.chatGuid,
193
+ ...(config.bluebubblesRoute.chatIdentifier ? { chatIdentifier: config.bluebubblesRoute.chatIdentifier } : {}),
194
+ displayName: "RSVP",
195
+ isGroup: true,
196
+ sessionKey: `bluebubbles:rsvp:${config.bluebubblesRoute.chatIdentifier ?? config.bluebubblesRoute.chatGuid}`,
197
+ sendTarget,
198
+ participantHandles: [],
199
+ };
200
+ }
201
+ function normalizeDelivery(value) {
202
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
203
+ return {
204
+ ...(typeof record.guid === "string" ? { guid: record.guid } : {}),
205
+ ...(typeof record.messageGuid === "string" ? { guid: record.messageGuid, messageGuid: record.messageGuid } : {}),
206
+ };
207
+ }
208
+ function textSummary(payload) {
209
+ const effect = payload.sideEffect ? "explicit side effects enabled" : "dry run";
210
+ const agent = payload.agent ? ` agent=${payload.agent}` : "";
211
+ const requires = payload.requires ? ` requires=${payload.requires}` : "";
212
+ return `${payload.command}: ${effect}${agent}${requires}`;
213
+ }
214
+ function writePayload(command, deps, payload) {
215
+ const outputPath = commandOutputPath(command);
216
+ if (outputPath)
217
+ writeJsonFile(outputPath, payload);
218
+ const text = commandJson(command) ? JSON.stringify(payload, null, 2) : textSummary(payload);
219
+ deps.writeStdout(text);
220
+ (0, runtime_1.emitNervesEvent)({
221
+ component: "rsvp",
222
+ event: "rsvp.cli_command_executed",
223
+ message: "executed RSVP CLI command",
224
+ meta: {
225
+ command: payload.command,
226
+ sideEffect: payload.sideEffect,
227
+ outputPath,
228
+ agent: payload.agent,
229
+ },
230
+ });
231
+ return text;
232
+ }
233
+ function writeIncidentText(deps, command, agent, bundle, outputPath) {
234
+ const text = command.json === true
235
+ ? JSON.stringify({
236
+ ok: bundle.doctor.summary.failed === 0,
237
+ command: command.kind,
238
+ agent,
239
+ sideEffect: false,
240
+ incidentBundle: bundle,
241
+ }, null, 2)
242
+ : `rsvp.incident: wrote side-effect-free bundle agent=${agent}${outputPath ? ` output=${outputPath}` : ""}`;
243
+ deps.writeStdout(text);
244
+ (0, runtime_1.emitNervesEvent)({
245
+ component: "rsvp",
246
+ event: "rsvp.cli_command_executed",
247
+ message: "executed RSVP CLI command",
248
+ meta: {
249
+ command: command.kind,
250
+ sideEffect: false,
251
+ outputPath,
252
+ agent,
253
+ },
254
+ });
255
+ return text;
256
+ }
257
+ function basePayload(command, sideEffect, message, extra = {}) {
258
+ return {
259
+ ok: true,
260
+ command: command.kind,
261
+ sideEffect,
262
+ ...(commandAgent(command) ? { agent: commandAgent(command) } : {}),
263
+ ...(commandMode(command) ? { mode: commandMode(command) } : {}),
264
+ message,
265
+ ...extra,
266
+ };
267
+ }
268
+ async function executeImportLegacy(command, deps) {
269
+ if (!command.yes) {
270
+ return basePayload(command, false, "legacy RSVP import preview only; pass --yes to write native config and runtime/config", {
271
+ requires: "--yes",
272
+ inputs: {
273
+ legacyRoot: command.legacyRoot,
274
+ mode: command.mode,
275
+ },
276
+ });
277
+ }
278
+ const agentRoot = maybeAgentRoot(command, deps);
279
+ if (!command.agent || !agentRoot) {
280
+ return {
281
+ ok: false,
282
+ command: command.kind,
283
+ sideEffect: false,
284
+ message: "legacy RSVP import requires --agent <name>",
285
+ requires: "--agent",
286
+ };
287
+ }
288
+ if (command.kind === "rsvp.import-legacy") {
289
+ const configResult = resolveRsvpConfig(agentRoot);
290
+ if (!configResult.ok) {
291
+ return {
292
+ ok: false,
293
+ command: command.kind,
294
+ sideEffect: false,
295
+ agent: command.agent,
296
+ message: configResult.message,
297
+ requires: "native RSVP config",
298
+ };
299
+ }
300
+ const result = (0, migration_1.importLegacyRsvpState)({
301
+ agent: command.agent,
302
+ agentRoot,
303
+ legacyRoot: command.legacyRoot,
304
+ weddingId: configResult.config.source.weddingId,
305
+ eventId: configResult.config.source.eventId,
306
+ importedAt: new Date().toISOString(),
307
+ });
308
+ return basePayload(command, true, result.ok ? "legacy RSVP state imported" : result.message, {
309
+ migration: result,
310
+ });
311
+ }
312
+ const result = await (0, config_1.importLegacyRsvpConfig)({
313
+ agent: command.agent,
314
+ agentRoot,
315
+ legacyRoot: command.legacyRoot,
316
+ mode: command.mode,
317
+ confirm: true,
318
+ });
319
+ return basePayload(command, true, result.ok ? "legacy RSVP config imported" : result.message, {
320
+ result: result,
321
+ });
322
+ }
323
+ async function executeCutover(command, deps) {
324
+ const result = await (0, cutover_1.runRsvpCutover)({
325
+ ...(command.agent ? { agent: command.agent } : {}),
326
+ legacyRoot: command.legacyRoot,
327
+ action: command.action,
328
+ yes: command.yes === true,
329
+ ...(deps.rsvpCutoverDeps ? { deps: deps.rsvpCutoverDeps } : {}),
330
+ });
331
+ return {
332
+ ok: result.ok,
333
+ command: command.kind,
334
+ sideEffect: result.sideEffect,
335
+ ...(command.agent ? { agent: command.agent } : {}),
336
+ action: result.action,
337
+ legacyRoot: result.legacyRoot,
338
+ message: result.message,
339
+ ...(result.requires ? { requires: result.requires } : {}),
340
+ checks: result.checks,
341
+ sendAllowed: result.sendAllowed,
342
+ denialReasons: result.denialReasons,
343
+ rollback: result.rollback,
344
+ };
345
+ }
346
+ function doctorDepsFor(deps) {
347
+ const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
348
+ return {
349
+ existsSync: (filePath) => fs.existsSync(filePath),
350
+ readFileSync: (filePath) => fs.readFileSync(filePath, "utf-8"),
351
+ readdirSync: (dirPath) => fs.readdirSync(dirPath),
352
+ statSync: fs.statSync,
353
+ checkSocketAlive: deps.checkSocketAlive,
354
+ fetchImpl: deps.fetchImpl ?? fetch,
355
+ socketPath: deps.socketPath,
356
+ bundlesRoot,
357
+ daemonLogsDir: path.join(deps.homeDir ?? os.homedir(), ".ouro-cli", "daemon", "logs"),
358
+ homedir: deps.homeDir ?? os.homedir(),
359
+ envPath: process.env.PATH ?? "",
360
+ platform: process.platform,
361
+ ...(deps.rsvpCutoverDeps ? { rsvpCutoverDeps: deps.rsvpCutoverDeps } : {}),
362
+ };
363
+ }
364
+ async function executeDoctor(command, deps) {
365
+ const doctor = await (0, doctor_1.runDoctorChecks)(doctorDepsFor(deps), { category: "RSVP" });
366
+ const ok = doctor.summary.warnings === 0 && doctor.summary.failed === 0;
367
+ if (command.strict && !ok)
368
+ deps.setExitCode?.(1);
369
+ return basePayload(command, false, ok ? "RSVP doctor checks passed" : "RSVP doctor checks found issues", {
370
+ ok,
371
+ inputs: { strict: command.strict === true },
372
+ doctor: doctor,
373
+ strict: command.strict === true,
374
+ });
375
+ }
376
+ async function executeIncident(command, deps) {
377
+ const agentRoot = maybeAgentRoot(command, deps);
378
+ if (!command.agent || !agentRoot) {
379
+ return writePayload(command, deps, {
380
+ ok: false,
381
+ command: command.kind,
382
+ sideEffect: false,
383
+ message: "RSVP incident bundle requires --agent <name>",
384
+ requires: "--agent",
385
+ });
386
+ }
387
+ const diagnosticsDeps = {
388
+ existsSync: (filePath) => fs.existsSync(filePath),
389
+ readFileSync: (filePath) => fs.readFileSync(filePath, "utf-8"),
390
+ readdirSync: (dirPath) => fs.readdirSync(dirPath),
391
+ statSync: fs.statSync,
392
+ checkSocketAlive: deps.checkSocketAlive,
393
+ runDoctorChecks: () => (0, doctor_1.runDoctorChecks)(doctorDepsFor(deps), { category: "RSVP" }),
394
+ };
395
+ const outputPath = commandOutputPath(command);
396
+ const bundle = outputPath
397
+ ? (await (0, incident_bundle_1.writeRsvpIncidentBundle)({ agent: command.agent, agentRoot, outputPath, deps: diagnosticsDeps })).bundle
398
+ : await (0, incident_bundle_1.buildRsvpIncidentBundle)({ agent: command.agent, agentRoot, deps: diagnosticsDeps });
399
+ return writeIncidentText(deps, command, command.agent, bundle, outputPath);
400
+ }
401
+ async function executeLegacyRender(command, deps) {
402
+ const outputPath = command.outputPath ?? path.join(os.tmpdir(), `ouro-rsvp-legacy-render-${process.pid}-${Date.now()}.json`);
403
+ const result = await (0, replay_1.renderLegacyRsvpSnapshotOffline)({ legacyRoot: command.legacyRoot, outputPath });
404
+ const text = command.json
405
+ ? JSON.stringify({ ok: true, command: command.kind, sideEffect: false, result }, null, 2)
406
+ : `rsvp.legacy-render: wrote side-effect-free render output=${outputPath}`;
407
+ deps.writeStdout(text);
408
+ (0, runtime_1.emitNervesEvent)({
409
+ component: "rsvp",
410
+ event: "rsvp.cli_command_executed",
411
+ message: "executed RSVP CLI command",
412
+ meta: { command: command.kind, sideEffect: false, outputPath, agent: command.agent },
413
+ });
414
+ return text;
415
+ }
416
+ async function executeReplay(command) {
417
+ const replay = await (0, replay_1.replayRsvpFixture)({ fixturePath: command.fixturePath });
418
+ return basePayload(command, false, "RSVP replay fixture executed offline", {
419
+ replay: replay,
420
+ });
421
+ }
422
+ async function executeHabitStage(command, deps) {
423
+ const agentRoot = maybeAgentRoot(command, deps);
424
+ if (!command.agent || !agentRoot) {
425
+ return {
426
+ ok: false,
427
+ command: command.kind,
428
+ sideEffect: false,
429
+ message: "RSVP habit staging requires --agent <name>",
430
+ requires: "--agent",
431
+ };
432
+ }
433
+ const result = (0, habit_stage_1.stageRsvpHabit)({
434
+ agent: command.agent,
435
+ agentRoot,
436
+ mode: command.mode,
437
+ cadence: command.cadence,
438
+ });
439
+ return basePayload(command, true, "native RSVP habit staged", {
440
+ habit: result,
441
+ });
442
+ }
443
+ async function executeRefresh(command, deps) {
444
+ const agentRoot = maybeAgentRoot(command, deps);
445
+ if (!command.agent || !agentRoot) {
446
+ return {
447
+ ok: false,
448
+ command: command.kind,
449
+ sideEffect: false,
450
+ message: "RSVP refresh requires --agent <name>",
451
+ requires: "--agent",
452
+ };
453
+ }
454
+ const configResult = resolveRsvpConfig(agentRoot);
455
+ if (!configResult.ok) {
456
+ return basePayload(command, false, "RSVP refresh requires native RSVP config before live work can run", {
457
+ requires: "native RSVP config",
458
+ result: configResult,
459
+ });
460
+ }
461
+ const runtimeConfig = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(command.agent, { preserveCachedOnFailure: true });
462
+ const machineRuntimeConfig = await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(command.agent, machineIdForCli(), { preserveCachedOnFailure: true });
463
+ const readiness = (0, config_1.validateRsvpReadiness)({
464
+ agent: command.agent,
465
+ agentRoot,
466
+ config: configResult,
467
+ runtimeConfig,
468
+ machineRuntimeConfig,
469
+ });
470
+ const readinessOk = readiness.ok === true || readiness.status === "ready";
471
+ if (!readinessOk) {
472
+ return {
473
+ ok: false,
474
+ command: command.kind,
475
+ sideEffect: false,
476
+ agent: command.agent,
477
+ mode: command.mode,
478
+ message: "RSVP refresh blocked by readiness checks",
479
+ checks: { readiness: readiness },
480
+ };
481
+ }
482
+ const credentials = "credentials" in readiness
483
+ ? readiness.credentials
484
+ : { username: "", password: "" };
485
+ const fetched = await (0, aisleplanner_client_1.fetchAislePlannerRsvps)({
486
+ agent: command.agent,
487
+ weddingId: configResult.config.source.weddingId,
488
+ eventId: configResult.config.source.eventId,
489
+ credentials,
490
+ ...(deps.fetchImpl ? { fetchFn: deps.fetchImpl } : {}),
491
+ });
492
+ if (!fetched.ok) {
493
+ return {
494
+ ok: false,
495
+ command: command.kind,
496
+ sideEffect: false,
497
+ agent: command.agent,
498
+ mode: command.mode,
499
+ message: fetched.message,
500
+ result: fetched,
501
+ };
502
+ }
503
+ const previousSnapshot = readBaselineSnapshot(agentRoot);
504
+ const currentSnapshot = (0, snapshot_1.buildRsvpSnapshot)({
505
+ agent: command.agent,
506
+ fetchedAt: fetched.fetchedAt,
507
+ source: {
508
+ kind: "aisleplanner",
509
+ weddingId: configResult.config.source.weddingId,
510
+ eventId: configResult.config.source.eventId,
511
+ adapter: "aisleplanner-api-v1",
512
+ },
513
+ guests: fetched.guests,
514
+ allGuests: fetched.allGuests,
515
+ provenance: { kind: "live-fetch", fetchedBy: "ouro rsvp refresh" },
516
+ });
517
+ writeSnapshotFiles(agentRoot, currentSnapshot);
518
+ const delta = (0, diff_renderer_1.computeRsvpDelta)(previousSnapshot, currentSnapshot);
519
+ const reportText = (0, diff_renderer_1.renderRsvpReport)(delta);
520
+ const outboundDecision = (0, outbound_state_1.decideRsvpOutboundReport)({ agentRoot, currentSnapshot, reportText });
521
+ const wantsLiveSend = command.mode === "live" && command.allowSend === true && outboundDecision.action === "send";
522
+ if (wantsLiveSend) {
523
+ const cutover = await checkLiveSendCutover({
524
+ agent: command.agent,
525
+ config: configResult.config,
526
+ deps,
527
+ });
528
+ if (cutover?.sendAllowed !== true) {
529
+ return blockedLiveSendPayload(command, "RSVP live refresh send blocked by cutover gates", {
530
+ ...cutoverPayload(cutover),
531
+ refresh: {
532
+ snapshotId: currentSnapshot.snapshotId,
533
+ reportText,
534
+ outboundDecision: outboundDecision,
535
+ },
536
+ });
537
+ }
538
+ }
539
+ const sendAllowed = wantsLiveSend;
540
+ let delivery;
541
+ if (sendAllowed) {
542
+ const sent = await (0, client_1.createBlueBubblesClient)().sendText({
543
+ chat: blueBubblesChatFor(configResult.config),
544
+ text: reportText,
545
+ tempGuid: outboundDecision.idempotencyKey,
546
+ });
547
+ delivery = normalizeDelivery(sent);
548
+ (0, outbound_state_1.recordRsvpOutboundAttempt)({
549
+ agentRoot,
550
+ currentSnapshot,
551
+ reportText,
552
+ bluebubblesRecord: {
553
+ recordId: outboundDecision.idempotencyKey,
554
+ status: "accepted",
555
+ tempGuid: outboundDecision.idempotencyKey,
556
+ ...(delivery.guid ? { messageGuid: String(delivery.guid) } : {}),
557
+ },
558
+ recordedAt: new Date().toISOString(),
559
+ });
560
+ }
561
+ return basePayload(command, sendAllowed, "RSVP refresh completed", {
562
+ allowSend: sendAllowed,
563
+ sendAllowed,
564
+ refresh: {
565
+ snapshotId: currentSnapshot.snapshotId,
566
+ reportText,
567
+ outboundDecision: outboundDecision,
568
+ ...(delivery ? { delivery } : {}),
569
+ },
570
+ });
571
+ }
572
+ async function executeCompare(command) {
573
+ if (!fs.existsSync(command.nativePath) || !fs.existsSync(command.legacyPath)) {
574
+ return basePayload(command, false, "RSVP compare requires readable native and legacy snapshot files", {
575
+ requires: "readable snapshots",
576
+ inputs: { nativePath: command.nativePath, legacyPath: command.legacyPath },
577
+ });
578
+ }
579
+ const nativeSnapshot = parseSnapshotFromFile(command.nativePath);
580
+ const legacySnapshot = parseSnapshotFromFile(command.legacyPath);
581
+ const delta = (0, diff_renderer_1.computeRsvpDelta)(legacySnapshot, nativeSnapshot);
582
+ const reportText = (0, diff_renderer_1.renderRsvpReport)(delta);
583
+ return basePayload(command, false, "RSVP native/legacy comparison completed", {
584
+ compare: {
585
+ nativeSnapshotId: nativeSnapshot.snapshotId,
586
+ legacySnapshotId: legacySnapshot.snapshotId,
587
+ reportText,
588
+ delta: delta,
589
+ },
590
+ });
591
+ }
592
+ async function executeSmoke(command, deps) {
593
+ const agentRoot = maybeAgentRoot(command, deps);
594
+ if (!command.agent || !agentRoot) {
595
+ return {
596
+ ok: false,
597
+ command: command.kind,
598
+ sideEffect: false,
599
+ message: "RSVP smoke requires --agent <name>",
600
+ requires: "--agent",
601
+ };
602
+ }
603
+ const configResult = resolveRsvpConfig(agentRoot);
604
+ if (!configResult.ok) {
605
+ return basePayload(command, command.allowSend === true, "RSVP smoke requires native RSVP config before follow-up can run", {
606
+ allowSend: command.allowSend === true,
607
+ sendAllowed: false,
608
+ requires: "native RSVP config",
609
+ result: configResult,
610
+ });
611
+ }
612
+ const snapshot = readLatestSnapshot(agentRoot);
613
+ const question = command.question ?? "who is pending?";
614
+ const answer = (0, query_1.queryRsvpSnapshot)(snapshot, { query: question });
615
+ const wantsLiveSend = command.mode === "live" && command.allowSend === true;
616
+ if (wantsLiveSend) {
617
+ const cutover = await checkLiveSendCutover({
618
+ agent: command.agent,
619
+ config: configResult.config,
620
+ deps,
621
+ });
622
+ if (cutover?.sendAllowed !== true) {
623
+ const blocked = blockedLiveSendPayload(command, "RSVP live smoke send blocked by cutover gates", {
624
+ ...cutoverPayload(cutover),
625
+ answer: answer.text,
626
+ result: answer,
627
+ });
628
+ if (command.replayOutputPath)
629
+ writeJsonFile(command.replayOutputPath, blocked);
630
+ return blocked;
631
+ }
632
+ }
633
+ const sendAllowed = wantsLiveSend;
634
+ let delivery;
635
+ if (sendAllowed) {
636
+ const sent = await (0, client_1.createBlueBubblesClient)().sendText({
637
+ chat: blueBubblesChatFor(configResult.config),
638
+ text: answer.text,
639
+ });
640
+ delivery = normalizeDelivery(sent);
641
+ }
642
+ const payload = basePayload(command, sendAllowed, sendAllowed ? "RSVP live smoke completed" : "RSVP smoke preflight completed", {
643
+ allowSend: sendAllowed,
644
+ sendAllowed,
645
+ answer: answer.text,
646
+ result: answer,
647
+ ...(delivery ? { delivery } : {}),
648
+ });
649
+ if (command.replayOutputPath)
650
+ writeJsonFile(command.replayOutputPath, payload);
651
+ return payload;
652
+ }
653
+ function plannedPayload(command) {
654
+ throw new Error(`unsupported RSVP CLI command: ${String(command.kind)}`);
655
+ }
656
+ async function runRsvpCliCommand(command, deps) {
657
+ if (command.kind === "rsvp.incident")
658
+ return executeIncident(command, deps);
659
+ if (command.kind === "rsvp.legacy-render")
660
+ return executeLegacyRender(command, deps);
661
+ const payload = command.kind === "rsvp.config.import-legacy" || command.kind === "rsvp.import-legacy"
662
+ ? await executeImportLegacy(command, deps)
663
+ : command.kind === "rsvp.cutover"
664
+ ? await executeCutover(command, deps)
665
+ : command.kind === "rsvp.doctor"
666
+ ? await executeDoctor(command, deps)
667
+ : command.kind === "rsvp.replay"
668
+ ? await executeReplay(command)
669
+ : command.kind === "rsvp.habit.stage"
670
+ ? await executeHabitStage(command, deps)
671
+ : command.kind === "rsvp.refresh"
672
+ ? await executeRefresh(command, deps)
673
+ : command.kind === "rsvp.compare"
674
+ ? await executeCompare(command)
675
+ : command.kind === "rsvp.smoke"
676
+ ? await executeSmoke(command, deps)
677
+ : plannedPayload(command);
678
+ return writePayload(command, deps, payload);
679
+ }