@ouro.bot/cli 0.1.0-alpha.696 → 0.1.0-alpha.698

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 (46) hide show
  1. package/changelog.json +18 -0
  2. package/dist/heart/agent-entry.js +32 -2
  3. package/dist/heart/autonomy-budget.js +434 -0
  4. package/dist/heart/core.js +11 -0
  5. package/dist/heart/daemon/cadence.js +110 -0
  6. package/dist/heart/daemon/cli-exec.js +20 -0
  7. package/dist/heart/daemon/cli-help.js +74 -0
  8. package/dist/heart/daemon/cli-parse.js +466 -0
  9. package/dist/heart/daemon/daemon-entry.js +104 -3
  10. package/dist/heart/daemon/daemon.js +24 -3
  11. package/dist/heart/daemon/doctor.js +151 -0
  12. package/dist/heart/daemon/stale-bundle-prune.js +23 -6
  13. package/dist/heart/habits/habit-parser.js +4 -1
  14. package/dist/heart/habits/habit-scheduler.js +46 -38
  15. package/dist/heart/habits/habit-session.js +2 -3
  16. package/dist/heart/provider-credentials.js +16 -0
  17. package/dist/heart/run-ledger.js +247 -0
  18. package/dist/heart/runtime-credentials.js +12 -3
  19. package/dist/mind/prompt-budget.js +380 -0
  20. package/dist/repertoire/tools-base.js +2 -0
  21. package/dist/repertoire/tools-rsvp.js +139 -0
  22. package/dist/rsvp/aisleplanner-client.js +354 -0
  23. package/dist/rsvp/cli.js +704 -0
  24. package/dist/rsvp/config.js +327 -0
  25. package/dist/rsvp/cutover.js +431 -0
  26. package/dist/rsvp/diagnostics.js +222 -0
  27. package/dist/rsvp/diff-renderer.js +161 -0
  28. package/dist/rsvp/habit-policy.js +85 -0
  29. package/dist/rsvp/habit-stage.js +93 -0
  30. package/dist/rsvp/incident-bundle.js +97 -0
  31. package/dist/rsvp/migration.js +204 -0
  32. package/dist/rsvp/outbound-state.js +254 -0
  33. package/dist/rsvp/query.js +118 -0
  34. package/dist/rsvp/replay.js +233 -0
  35. package/dist/rsvp/snapshot.js +232 -0
  36. package/dist/senses/bluebubbles/client.js +13 -7
  37. package/dist/senses/bluebubbles/index.js +227 -22
  38. package/dist/senses/bluebubbles/outbound-state.js +304 -0
  39. package/dist/senses/bluebubbles/replay.js +147 -0
  40. package/dist/senses/context-packet-ledger.js +245 -0
  41. package/dist/senses/context-packets.js +154 -0
  42. package/dist/senses/pipeline.js +91 -1
  43. package/dist/senses/private-runtime-worker.js +170 -31
  44. package/dist/senses/private-runtime.js +13 -1
  45. package/dist/senses/voice/twilio-phone.js +9 -5
  46. package/package.json +1 -1
@@ -126,10 +126,26 @@ const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
126
126
  const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
127
127
  const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
128
128
  const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
129
+ const RSVP_CLI_KINDS = new Set([
130
+ "rsvp.doctor",
131
+ "rsvp.incident",
132
+ "rsvp.cutover",
133
+ "rsvp.legacy-render",
134
+ "rsvp.replay",
135
+ "rsvp.config.import-legacy",
136
+ "rsvp.habit.stage",
137
+ "rsvp.import-legacy",
138
+ "rsvp.refresh",
139
+ "rsvp.compare",
140
+ "rsvp.smoke",
141
+ ]);
129
142
  const CONNECT_PROVIDER_ORIENTATION_PING_OPTIONS = {
130
143
  attemptPolicy: { maxAttempts: 1, baseDelayMs: 0, backoffMultiplier: 2 },
131
144
  timeoutMs: 5_000,
132
145
  };
146
+ function isRsvpCliCommand(command) {
147
+ return RSVP_CLI_KINDS.has(command.kind);
148
+ }
133
149
  function summarizeCliUpdateCheckStatus(error, timedOut = false) {
134
150
  const normalized = error.trim().toLowerCase();
135
151
  if (timedOut || normalized.includes("timed out") || normalized.includes("abort")) {
@@ -6027,6 +6043,10 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
6027
6043
  deps.writeStdout(text);
6028
6044
  return text;
6029
6045
  }
6046
+ if (isRsvpCliCommand(command)) {
6047
+ const { runRsvpCliCommand } = await Promise.resolve().then(() => __importStar(require("../../rsvp/cli")));
6048
+ return runRsvpCliCommand(command, deps);
6049
+ }
6030
6050
  if (command.kind === "connect") {
6031
6051
  return executeConnect(command, deps);
6032
6052
  }
@@ -144,6 +144,25 @@ exports.COMMAND_REGISTRY = {
144
144
  example: "ouro habit list",
145
145
  subcommands: ["list", "create", "runs", "inspect", "summary", "poke"],
146
146
  },
147
+ rsvp: {
148
+ category: "Habits",
149
+ description: "AislePlanner-backed RSVP habit operations, triage, receipts, ledgers, migration checks, refreshes, and smoke tests",
150
+ usage: "ouro rsvp <doctor|incident|cutover|legacy-render|replay|config|habit|import-legacy|refresh|compare|smoke> ...",
151
+ example: "ouro rsvp doctor --agent slugger --json",
152
+ subcommands: [
153
+ "doctor",
154
+ "incident",
155
+ "cutover",
156
+ "legacy-render",
157
+ "replay",
158
+ "config import-legacy",
159
+ "habit stage",
160
+ "import-legacy",
161
+ "refresh",
162
+ "compare",
163
+ "smoke",
164
+ ],
165
+ },
147
166
  desk: {
148
167
  category: "Tasks",
149
168
  description: "Manage the agent's desk — tasks, tracks, friction, lessons, search, and recall (routes through the desk MCP server).",
@@ -431,6 +450,61 @@ const SUBCOMMAND_HELP = {
431
450
  usage: "ouro habit summary [--agent <name>] (--run-id <id>|--habit <name>|--operation-id <id>) [--which latest|previous|latest-success|latest-failure] [--json]",
432
451
  example: "ouro habit summary --agent slugger --operation-id habit:standup --which latest --json",
433
452
  },
453
+ "rsvp doctor": {
454
+ description: "Check native RSVP configuration, credentials, BlueBubbles attachment, ledgers, receipts, and migration readiness without sending messages; use --json --strict in triage",
455
+ usage: "ouro rsvp doctor [--agent <name>] [--strict] [--output <path>] [--json]",
456
+ example: "ouro rsvp doctor --agent slugger --strict --json",
457
+ },
458
+ "rsvp incident": {
459
+ description: "Collect a redacted local RSVP diagnostic incident bundle with doctor status, receipts, and ledger pointers without contacting the daemon",
460
+ usage: "ouro rsvp incident [--agent <name>] [--output <path>] [--json]",
461
+ example: "ouro rsvp incident --agent slugger --output /tmp/rsvp-incident.json",
462
+ },
463
+ "rsvp cutover": {
464
+ description: "Inspect or retire legacy RSVP sender state before native live send",
465
+ usage: "ouro rsvp cutover [--agent <name>] --legacy-root <path> --action check|quarantine-launchd|retire-legacy-send-config [--yes] [--output <path>] [--json]",
466
+ example: "ouro rsvp cutover --agent slugger --legacy-root ~/Projects/rsvp-tracker --action check --json",
467
+ },
468
+ "rsvp legacy-render": {
469
+ description: "Render the legacy RSVP script's local snapshot/report shape for comparison without mutating native state",
470
+ usage: "ouro rsvp legacy-render --legacy-root <path> [--agent <name>] [--output <path>] [--json]",
471
+ example: "ouro rsvp legacy-render --legacy-root ~/Projects/rsvp-tracker --output /tmp/legacy.json",
472
+ },
473
+ "rsvp replay": {
474
+ description: "Replay an offline fixture through deterministic native RSVP report/query logic without live BlueBubbles or AislePlanner access",
475
+ usage: "ouro rsvp replay [--agent <name>] --fixture <path> [--output <path>] [--json]",
476
+ example: "ouro rsvp replay --agent slugger --fixture /tmp/rsvp-replay.json --json",
477
+ },
478
+ "rsvp config import-legacy": {
479
+ description: "Import legacy RSVP coordinates into native config and vault-backed runtime/config; mutates only with --yes",
480
+ usage: "ouro rsvp config import-legacy [--agent <name>] --legacy-root <path> --mode shadow|live [--yes] [--output <path>] [--json]",
481
+ example: "ouro rsvp config import-legacy --agent slugger --legacy-root ~/Projects/rsvp-tracker --mode shadow --yes",
482
+ },
483
+ "rsvp habit stage": {
484
+ description: "Stage the daily RSVP refresh habit with a fixed cron cadence, usually the 10 AM dogfood run",
485
+ usage: "ouro rsvp habit stage [--agent <name>] --mode shadow|live --cadence '<cron>' [--output <path>] [--json]",
486
+ example: "ouro rsvp habit stage --agent slugger --mode shadow --cadence '0 10 * * *'",
487
+ },
488
+ "rsvp import-legacy": {
489
+ description: "Compatibility alias for `ouro rsvp config import-legacy`; mutates only with --yes",
490
+ usage: "ouro rsvp import-legacy [--agent <name>] --legacy-root <path> --mode shadow|live [--yes] [--output <path>] [--json]",
491
+ example: "ouro rsvp import-legacy --agent slugger --legacy-root ~/Projects/rsvp-tracker --mode shadow --yes",
492
+ },
493
+ "rsvp refresh": {
494
+ description: "Run the native RSVP refresh path and write receipts; defaults to dry-run behavior unless --allow-send is explicit",
495
+ usage: "ouro rsvp refresh [--agent <name>] --mode shadow|live [--no-send|--allow-send] [--output <path>] [--json]",
496
+ example: "ouro rsvp refresh --agent slugger --mode shadow --no-send --json",
497
+ },
498
+ "rsvp compare": {
499
+ description: "Compare native and legacy RSVP render outputs for migration verification",
500
+ usage: "ouro rsvp compare [--agent <name>] --native <path> --legacy <path> [--output <path>] [--json]",
501
+ example: "ouro rsvp compare --agent slugger --native /tmp/native.json --legacy /tmp/legacy.json",
502
+ },
503
+ "rsvp smoke": {
504
+ description: "Smoke-test RSVP follow-up behavior through a target sense; preflight is no-send and live mode requires --allow-send",
505
+ usage: "ouro rsvp smoke [--agent <name>] --mode preflight|live --surface bluebubbles [--question <text>] [--allow-send] [--output <path>] [--replay-output <path>] [--json]",
506
+ example: "ouro rsvp smoke --agent slugger --mode preflight --surface bluebubbles --question 'who is pending?'",
507
+ },
434
508
  "mail import-mbox": {
435
509
  description: "Import a HEY or other MBOX export into an existing delegated Mailroom source grant",
436
510
  usage: "ouro mail import-mbox (--file <path>|--discover) [--owner-email <email>] [--source <label>] [--agent <name>] [--foreground]",
@@ -114,6 +114,7 @@ function usage() {
114
114
  " ouro habit summary [--agent <name>] (--run-id <id>|--habit <name>|--operation-id <id>) [--which latest|previous|latest-success|latest-failure] [--json]",
115
115
  " ouro link <agent> --friend <id> --provider <provider> --external-id <external-id>",
116
116
  " ouro bluebubbles replay [--agent <name>] --message-guid <guid> [--event-type new-message|updated-message] [--json]",
117
+ " ouro rsvp <doctor|incident|cutover|legacy-render|replay|config|habit|import-legacy|refresh|compare|smoke> ...",
117
118
  " ouro friend list [--agent <name>]",
118
119
  " ouro friend show <id> [--agent <name>]",
119
120
  " ouro friend create --name <name> [--trust <level>] [--agent <name>]",
@@ -1681,6 +1682,469 @@ function parseBlueBubblesCommand(args) {
1681
1682
  ...(json ? { json: true } : {}),
1682
1683
  };
1683
1684
  }
1685
+ const RSVP_USAGE = "Usage: ouro rsvp <doctor|incident|cutover|legacy-render|replay|config|habit|import-legacy|refresh|compare|smoke> ...";
1686
+ function isRsvpMode(value) {
1687
+ return value === "shadow" || value === "live";
1688
+ }
1689
+ function isRsvpCutoverAction(value) {
1690
+ return value === "check" || value === "quarantine-launchd" || value === "retire-legacy-send-config";
1691
+ }
1692
+ function isRsvpSmokeMode(value) {
1693
+ return value === "preflight" || value === "live";
1694
+ }
1695
+ function isRsvpSmokeSurface(value) {
1696
+ return value === "bluebubbles";
1697
+ }
1698
+ function rsvpPath(value, label) {
1699
+ const trimmed = value.trim();
1700
+ if (!trimmed || /[\r\n\t]/.test(trimmed))
1701
+ throw new Error(`rsvp ${label} requires a non-empty path`);
1702
+ return trimmed;
1703
+ }
1704
+ function validRsvpCronCadence(value) {
1705
+ const fields = value.trim().split(/\s+/);
1706
+ return fields.length === 5 && fields.every((field) => field.length > 0);
1707
+ }
1708
+ function parseRsvpCommand(args) {
1709
+ const subcommand = args[0];
1710
+ if (!subcommand)
1711
+ throw new Error(RSVP_USAGE);
1712
+ if (subcommand === "config") {
1713
+ const nested = args[1];
1714
+ if (nested !== "import-legacy")
1715
+ throw new Error("Usage: ouro rsvp config import-legacy [--agent <name>] --legacy-root <path> --mode shadow|live [--yes] [--output <path>] [--json]");
1716
+ return parseRsvpImportLegacyCommand(args.slice(2), "rsvp.config.import-legacy");
1717
+ }
1718
+ if (subcommand === "habit") {
1719
+ const nested = args[1];
1720
+ if (nested !== "stage")
1721
+ throw new Error("Usage: ouro rsvp habit stage [--agent <name>] --mode shadow|live --cadence '<cron>' [--output <path>] [--json]");
1722
+ return parseRsvpHabitStageCommand(args.slice(2));
1723
+ }
1724
+ if (subcommand === "doctor")
1725
+ return parseRsvpDoctorCommand(args.slice(1));
1726
+ if (subcommand === "incident")
1727
+ return parseRsvpIncidentCommand(args.slice(1));
1728
+ if (subcommand === "cutover")
1729
+ return parseRsvpCutoverCommand(args.slice(1));
1730
+ if (subcommand === "legacy-render")
1731
+ return parseRsvpLegacyRenderCommand(args.slice(1));
1732
+ if (subcommand === "replay")
1733
+ return parseRsvpReplayCommand(args.slice(1));
1734
+ if (subcommand === "import-legacy")
1735
+ return parseRsvpImportLegacyCommand(args.slice(1), "rsvp.import-legacy");
1736
+ if (subcommand === "refresh")
1737
+ return parseRsvpRefreshCommand(args.slice(1));
1738
+ if (subcommand === "compare")
1739
+ return parseRsvpCompareCommand(args.slice(1));
1740
+ if (subcommand === "smoke")
1741
+ return parseRsvpSmokeCommand(args.slice(1));
1742
+ throw new Error(RSVP_USAGE);
1743
+ }
1744
+ function parseRsvpDoctorCommand(args) {
1745
+ const { agent, rest } = extractAgentFlag(args);
1746
+ let json = false;
1747
+ let strict = false;
1748
+ let outputPath;
1749
+ for (let i = 0; i < rest.length; i += 1) {
1750
+ const token = rest[i];
1751
+ if (token === "--json") {
1752
+ json = true;
1753
+ continue;
1754
+ }
1755
+ if (token === "--strict") {
1756
+ strict = true;
1757
+ continue;
1758
+ }
1759
+ if (token === "--output" && rest[i + 1]) {
1760
+ outputPath = rsvpPath(rest[++i], "--output");
1761
+ continue;
1762
+ }
1763
+ throw new Error("Usage: ouro rsvp doctor [--agent <name>] [--strict] [--output <path>] [--json]");
1764
+ }
1765
+ return {
1766
+ kind: "rsvp.doctor",
1767
+ ...(agent ? { agent } : {}),
1768
+ ...(json ? { json: true } : {}),
1769
+ ...(strict ? { strict: true } : {}),
1770
+ ...(outputPath ? { outputPath } : {}),
1771
+ };
1772
+ }
1773
+ function parseRsvpIncidentCommand(args) {
1774
+ const { agent, rest } = extractAgentFlag(args);
1775
+ let json = false;
1776
+ let outputPath;
1777
+ for (let i = 0; i < rest.length; i += 1) {
1778
+ const token = rest[i];
1779
+ if (token === "--json") {
1780
+ json = true;
1781
+ continue;
1782
+ }
1783
+ if (token === "--output" && rest[i + 1]) {
1784
+ outputPath = rsvpPath(rest[++i], "--output");
1785
+ continue;
1786
+ }
1787
+ throw new Error("Usage: ouro rsvp incident [--agent <name>] [--output <path>] [--json]");
1788
+ }
1789
+ return {
1790
+ kind: "rsvp.incident",
1791
+ ...(agent ? { agent } : {}),
1792
+ ...(json ? { json: true } : {}),
1793
+ ...(outputPath ? { outputPath } : {}),
1794
+ };
1795
+ }
1796
+ function parseRsvpCutoverCommand(args) {
1797
+ const { agent, rest } = extractAgentFlag(args);
1798
+ let legacyRoot;
1799
+ let action;
1800
+ let yes = false;
1801
+ let json = false;
1802
+ let outputPath;
1803
+ for (let i = 0; i < rest.length; i += 1) {
1804
+ const token = rest[i];
1805
+ if (token === "--legacy-root" && rest[i + 1]) {
1806
+ legacyRoot = rsvpPath(rest[++i], "--legacy-root");
1807
+ continue;
1808
+ }
1809
+ if (token === "--action" && rest[i + 1]) {
1810
+ const value = rest[++i];
1811
+ if (!isRsvpCutoverAction(value))
1812
+ throw new Error("rsvp cutover --action must be check, quarantine-launchd, or retire-legacy-send-config");
1813
+ action = value;
1814
+ continue;
1815
+ }
1816
+ if (token === "--yes") {
1817
+ yes = true;
1818
+ continue;
1819
+ }
1820
+ if (token === "--json") {
1821
+ json = true;
1822
+ continue;
1823
+ }
1824
+ if (token === "--output" && rest[i + 1]) {
1825
+ outputPath = rsvpPath(rest[++i], "--output");
1826
+ continue;
1827
+ }
1828
+ throw new Error("Usage: ouro rsvp cutover [--agent <name>] --legacy-root <path> --action check|quarantine-launchd|retire-legacy-send-config [--yes] [--output <path>] [--json]");
1829
+ }
1830
+ if (!legacyRoot)
1831
+ throw new Error("rsvp cutover requires --legacy-root <path>");
1832
+ if (!action)
1833
+ throw new Error("rsvp cutover requires --action check|quarantine-launchd|retire-legacy-send-config");
1834
+ return {
1835
+ kind: "rsvp.cutover",
1836
+ ...(agent ? { agent } : {}),
1837
+ legacyRoot,
1838
+ action,
1839
+ ...(yes ? { yes: true } : {}),
1840
+ ...(json ? { json: true } : {}),
1841
+ ...(outputPath ? { outputPath } : {}),
1842
+ };
1843
+ }
1844
+ function parseRsvpLegacyRenderCommand(args) {
1845
+ const { agent, rest } = extractAgentFlag(args);
1846
+ let legacyRoot;
1847
+ let json = false;
1848
+ let outputPath;
1849
+ for (let i = 0; i < rest.length; i += 1) {
1850
+ const token = rest[i];
1851
+ if (token === "--legacy-root" && rest[i + 1]) {
1852
+ legacyRoot = rsvpPath(rest[++i], "--legacy-root");
1853
+ continue;
1854
+ }
1855
+ if (token === "--json") {
1856
+ json = true;
1857
+ continue;
1858
+ }
1859
+ if (token === "--output" && rest[i + 1]) {
1860
+ outputPath = rsvpPath(rest[++i], "--output");
1861
+ continue;
1862
+ }
1863
+ throw new Error("Usage: ouro rsvp legacy-render --legacy-root <path> [--agent <name>] [--output <path>] [--json]");
1864
+ }
1865
+ if (!legacyRoot)
1866
+ throw new Error("rsvp legacy-render requires --legacy-root <path>");
1867
+ return {
1868
+ kind: "rsvp.legacy-render",
1869
+ ...(agent ? { agent } : {}),
1870
+ legacyRoot,
1871
+ ...(json ? { json: true } : {}),
1872
+ ...(outputPath ? { outputPath } : {}),
1873
+ };
1874
+ }
1875
+ function parseRsvpReplayCommand(args) {
1876
+ const { agent, rest } = extractAgentFlag(args);
1877
+ let fixturePath;
1878
+ let json = false;
1879
+ let outputPath;
1880
+ for (let i = 0; i < rest.length; i += 1) {
1881
+ const token = rest[i];
1882
+ if (token === "--fixture" && rest[i + 1]) {
1883
+ fixturePath = rsvpPath(rest[++i], "--fixture");
1884
+ continue;
1885
+ }
1886
+ if (token === "--json") {
1887
+ json = true;
1888
+ continue;
1889
+ }
1890
+ if (token === "--output" && rest[i + 1]) {
1891
+ outputPath = rsvpPath(rest[++i], "--output");
1892
+ continue;
1893
+ }
1894
+ throw new Error("Usage: ouro rsvp replay [--agent <name>] --fixture <path> [--output <path>] [--json]");
1895
+ }
1896
+ if (!fixturePath)
1897
+ throw new Error("rsvp replay requires --fixture <path>");
1898
+ return {
1899
+ kind: "rsvp.replay",
1900
+ ...(agent ? { agent } : {}),
1901
+ fixturePath,
1902
+ ...(json ? { json: true } : {}),
1903
+ ...(outputPath ? { outputPath } : {}),
1904
+ };
1905
+ }
1906
+ function parseRsvpImportLegacyCommand(args, kind) {
1907
+ const { agent, rest } = extractAgentFlag(args);
1908
+ let legacyRoot;
1909
+ let mode;
1910
+ let yes = false;
1911
+ let json = false;
1912
+ let outputPath;
1913
+ for (let i = 0; i < rest.length; i += 1) {
1914
+ const token = rest[i];
1915
+ if (token === "--legacy-root" && rest[i + 1]) {
1916
+ legacyRoot = rsvpPath(rest[++i], "--legacy-root");
1917
+ continue;
1918
+ }
1919
+ if (token === "--mode" && rest[i + 1]) {
1920
+ const value = rest[++i];
1921
+ if (!isRsvpMode(value))
1922
+ throw new Error("rsvp import-legacy --mode must be shadow or live");
1923
+ mode = value;
1924
+ continue;
1925
+ }
1926
+ if (token === "--yes") {
1927
+ yes = true;
1928
+ continue;
1929
+ }
1930
+ if (token === "--json") {
1931
+ json = true;
1932
+ continue;
1933
+ }
1934
+ if (token === "--output" && rest[i + 1]) {
1935
+ outputPath = rsvpPath(rest[++i], "--output");
1936
+ continue;
1937
+ }
1938
+ throw new Error("Usage: ouro rsvp config import-legacy [--agent <name>] --legacy-root <path> --mode shadow|live [--yes] [--output <path>] [--json]");
1939
+ }
1940
+ if (!legacyRoot)
1941
+ throw new Error("rsvp import-legacy requires --legacy-root <path>");
1942
+ if (!mode)
1943
+ throw new Error("rsvp import-legacy requires --mode shadow|live");
1944
+ return {
1945
+ kind,
1946
+ ...(agent ? { agent } : {}),
1947
+ legacyRoot,
1948
+ mode,
1949
+ ...(yes ? { yes: true } : {}),
1950
+ ...(json ? { json: true } : {}),
1951
+ ...(outputPath ? { outputPath } : {}),
1952
+ };
1953
+ }
1954
+ function parseRsvpHabitStageCommand(args) {
1955
+ const { agent, rest } = extractAgentFlag(args);
1956
+ let mode;
1957
+ let cadence;
1958
+ let json = false;
1959
+ let outputPath;
1960
+ for (let i = 0; i < rest.length; i += 1) {
1961
+ const token = rest[i];
1962
+ if (token === "--mode" && rest[i + 1]) {
1963
+ const value = rest[++i];
1964
+ if (!isRsvpMode(value))
1965
+ throw new Error("rsvp habit stage --mode must be shadow or live");
1966
+ mode = value;
1967
+ continue;
1968
+ }
1969
+ if (token === "--cadence" && rest[i + 1]) {
1970
+ cadence = rest[++i];
1971
+ continue;
1972
+ }
1973
+ if (token === "--json") {
1974
+ json = true;
1975
+ continue;
1976
+ }
1977
+ if (token === "--output" && rest[i + 1]) {
1978
+ outputPath = rsvpPath(rest[++i], "--output");
1979
+ continue;
1980
+ }
1981
+ throw new Error("Usage: ouro rsvp habit stage [--agent <name>] --mode shadow|live --cadence '<cron>' [--output <path>] [--json]");
1982
+ }
1983
+ if (!mode)
1984
+ throw new Error("rsvp habit stage requires --mode shadow|live");
1985
+ if (!cadence || !validRsvpCronCadence(cadence))
1986
+ throw new Error("rsvp habit stage --cadence must be a five-field cron cadence");
1987
+ return {
1988
+ kind: "rsvp.habit.stage",
1989
+ ...(agent ? { agent } : {}),
1990
+ mode,
1991
+ cadence,
1992
+ ...(json ? { json: true } : {}),
1993
+ ...(outputPath ? { outputPath } : {}),
1994
+ };
1995
+ }
1996
+ function parseRsvpRefreshCommand(args) {
1997
+ const { agent, rest } = extractAgentFlag(args);
1998
+ let mode = "shadow";
1999
+ let noSend = false;
2000
+ let allowSend = false;
2001
+ let json = false;
2002
+ let outputPath;
2003
+ for (let i = 0; i < rest.length; i += 1) {
2004
+ const token = rest[i];
2005
+ if (token === "--mode" && rest[i + 1]) {
2006
+ const value = rest[++i];
2007
+ if (!isRsvpMode(value))
2008
+ throw new Error("rsvp refresh --mode must be shadow or live");
2009
+ mode = value;
2010
+ continue;
2011
+ }
2012
+ if (token === "--no-send") {
2013
+ noSend = true;
2014
+ continue;
2015
+ }
2016
+ if (token === "--allow-send") {
2017
+ allowSend = true;
2018
+ continue;
2019
+ }
2020
+ if (token === "--json") {
2021
+ json = true;
2022
+ continue;
2023
+ }
2024
+ if (token === "--output" && rest[i + 1]) {
2025
+ outputPath = rsvpPath(rest[++i], "--output");
2026
+ continue;
2027
+ }
2028
+ throw new Error("Usage: ouro rsvp refresh [--agent <name>] --mode shadow|live [--no-send|--allow-send] [--output <path>] [--json]");
2029
+ }
2030
+ if (noSend && allowSend)
2031
+ throw new Error("rsvp refresh cannot combine --allow-send and --no-send");
2032
+ if (mode === "shadow" && allowSend)
2033
+ throw new Error("rsvp refresh --mode shadow cannot use --allow-send; use --no-send or omit send flags");
2034
+ if (mode === "live" && !allowSend)
2035
+ throw new Error("rsvp refresh --mode live requires --allow-send, or use --no-send for a dry run");
2036
+ return {
2037
+ kind: "rsvp.refresh",
2038
+ ...(agent ? { agent } : {}),
2039
+ mode,
2040
+ ...(noSend ? { noSend: true } : {}),
2041
+ ...(allowSend ? { allowSend: true } : {}),
2042
+ ...(json ? { json: true } : {}),
2043
+ ...(outputPath ? { outputPath } : {}),
2044
+ };
2045
+ }
2046
+ function parseRsvpCompareCommand(args) {
2047
+ const { agent, rest } = extractAgentFlag(args);
2048
+ let nativePath;
2049
+ let legacyPath;
2050
+ let json = false;
2051
+ let outputPath;
2052
+ for (let i = 0; i < rest.length; i += 1) {
2053
+ const token = rest[i];
2054
+ if (token === "--native" && rest[i + 1]) {
2055
+ nativePath = rsvpPath(rest[++i], "--native");
2056
+ continue;
2057
+ }
2058
+ if (token === "--legacy" && rest[i + 1]) {
2059
+ legacyPath = rsvpPath(rest[++i], "--legacy");
2060
+ continue;
2061
+ }
2062
+ if (token === "--json") {
2063
+ json = true;
2064
+ continue;
2065
+ }
2066
+ if (token === "--output" && rest[i + 1]) {
2067
+ outputPath = rsvpPath(rest[++i], "--output");
2068
+ continue;
2069
+ }
2070
+ throw new Error("Usage: ouro rsvp compare [--agent <name>] --native <path> --legacy <path> [--output <path>] [--json]");
2071
+ }
2072
+ if (!nativePath)
2073
+ throw new Error("rsvp compare requires --native <path>");
2074
+ if (!legacyPath)
2075
+ throw new Error("rsvp compare requires --legacy <path>");
2076
+ return {
2077
+ kind: "rsvp.compare",
2078
+ ...(agent ? { agent } : {}),
2079
+ nativePath,
2080
+ legacyPath,
2081
+ ...(json ? { json: true } : {}),
2082
+ ...(outputPath ? { outputPath } : {}),
2083
+ };
2084
+ }
2085
+ function parseRsvpSmokeCommand(args) {
2086
+ const { agent, rest } = extractAgentFlag(args);
2087
+ let mode = "preflight";
2088
+ let surface;
2089
+ let question;
2090
+ let allowSend = false;
2091
+ let json = false;
2092
+ let outputPath;
2093
+ let replayOutputPath;
2094
+ for (let i = 0; i < rest.length; i += 1) {
2095
+ const token = rest[i];
2096
+ if (token === "--mode" && rest[i + 1]) {
2097
+ const value = rest[++i];
2098
+ if (!isRsvpSmokeMode(value))
2099
+ throw new Error("rsvp smoke --mode must be preflight or live");
2100
+ mode = value;
2101
+ continue;
2102
+ }
2103
+ if (token === "--surface" && rest[i + 1]) {
2104
+ const value = rest[++i];
2105
+ if (!isRsvpSmokeSurface(value))
2106
+ throw new Error("rsvp smoke --surface must be bluebubbles");
2107
+ surface = value;
2108
+ continue;
2109
+ }
2110
+ if (token === "--question" && rest[i + 1]) {
2111
+ question = rest[++i];
2112
+ continue;
2113
+ }
2114
+ if (token === "--allow-send") {
2115
+ allowSend = true;
2116
+ continue;
2117
+ }
2118
+ if (token === "--json") {
2119
+ json = true;
2120
+ continue;
2121
+ }
2122
+ if (token === "--output" && rest[i + 1]) {
2123
+ outputPath = rsvpPath(rest[++i], "--output");
2124
+ continue;
2125
+ }
2126
+ if (token === "--replay-output" && rest[i + 1]) {
2127
+ replayOutputPath = rsvpPath(rest[++i], "--replay-output");
2128
+ continue;
2129
+ }
2130
+ throw new Error("Usage: ouro rsvp smoke [--agent <name>] --mode preflight|live --surface bluebubbles [--question <text>] [--allow-send] [--output <path>] [--replay-output <path>] [--json]");
2131
+ }
2132
+ if (!surface)
2133
+ throw new Error("rsvp smoke requires --surface bluebubbles");
2134
+ if (mode === "live" && !allowSend)
2135
+ throw new Error("rsvp smoke --mode live requires --allow-send");
2136
+ return {
2137
+ kind: "rsvp.smoke",
2138
+ ...(agent ? { agent } : {}),
2139
+ mode,
2140
+ surface,
2141
+ ...(question ? { question } : {}),
2142
+ ...(allowSend ? { allowSend: true } : {}),
2143
+ ...(json ? { json: true } : {}),
2144
+ ...(outputPath ? { outputPath } : {}),
2145
+ ...(replayOutputPath ? { replayOutputPath } : {}),
2146
+ };
2147
+ }
1684
2148
  function parseNervesReviewCommand(args) {
1685
2149
  const { agent, rest } = extractAgentFlag(args);
1686
2150
  let processName = "daemon";
@@ -1989,6 +2453,8 @@ function parseOuroCommand(args) {
1989
2453
  }
1990
2454
  if (head === "bluebubbles")
1991
2455
  return parseBlueBubblesCommand(args.slice(1));
2456
+ if (head === "rsvp")
2457
+ return parseRsvpCommand(args.slice(1));
1992
2458
  const suggestion = (0, cli_help_1.suggestCommand)(head);
1993
2459
  const hint = suggestion ? ` Did you mean '${suggestion}'?` : "";
1994
2460
  throw new Error(`Unknown command '${args.join(" ")}'.${hint}\n${usage()}`);