@ouro.bot/cli 0.1.0-alpha.723 → 0.1.0-alpha.725

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 (55) hide show
  1. package/changelog.json +295 -275
  2. package/dist/heart/active-work.js +10 -3
  3. package/dist/heart/core.js +343 -77
  4. package/dist/heart/daemon/cli-exec.js +132 -15
  5. package/dist/heart/daemon/cli-help.js +13 -2
  6. package/dist/heart/daemon/cli-parse.js +82 -1
  7. package/dist/heart/daemon/daemon.js +286 -33
  8. package/dist/heart/daemon/doctor.js +82 -6
  9. package/dist/heart/daemon/habit-private-wake.js +6 -1
  10. package/dist/heart/habits/habit-cancel.js +810 -0
  11. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  12. package/dist/heart/habits/habit-migration.js +28 -23
  13. package/dist/heart/habits/habit-parser.js +72 -8
  14. package/dist/heart/habits/habit-runtime-state.js +3 -24
  15. package/dist/heart/habits/habit-scheduler.js +268 -50
  16. package/dist/heart/habits/habit-session.js +16 -0
  17. package/dist/heart/hatch/hatch-flow.js +6 -4
  18. package/dist/heart/mailbox/readers/runtime-readers.js +26 -27
  19. package/dist/heart/orientation-frame.js +58 -1
  20. package/dist/heart/provider-attempt.js +4 -1
  21. package/dist/heart/providers/anthropic.js +23 -2
  22. package/dist/heart/start-of-turn-packet.js +14 -11
  23. package/dist/heart/streaming.js +565 -185
  24. package/dist/mind/obligation-steering.js +15 -9
  25. package/dist/mind/prompt.js +58 -26
  26. package/dist/nerves/coverage/run-artifacts.js +55 -35
  27. package/dist/nerves/review/cli.js +2 -2
  28. package/dist/nerves/review/core.js +14 -7
  29. package/dist/repertoire/tools-base.js +2 -0
  30. package/dist/repertoire/tools-habits.js +103 -0
  31. package/dist/repertoire/tools.js +18 -3
  32. package/dist/rsvp/cli.js +126 -20
  33. package/dist/rsvp/diagnostics.js +12 -2
  34. package/dist/rsvp/habit-policy.js +7 -0
  35. package/dist/rsvp/habit-stage.js +19 -4
  36. package/dist/rsvp/native-habit-runner.js +151 -11
  37. package/dist/rsvp/outbound-state.js +360 -0
  38. package/dist/senses/bluebubbles/client.js +90 -17
  39. package/dist/senses/bluebubbles/inbound-log.js +1 -0
  40. package/dist/senses/bluebubbles/index.js +662 -425
  41. package/dist/senses/bluebubbles/model.js +57 -13
  42. package/dist/senses/bluebubbles/mutation-log.js +21 -10
  43. package/dist/senses/bluebubbles/reaction-policy.js +55 -0
  44. package/dist/senses/bluebubbles/semantic-receipts.js +1818 -0
  45. package/dist/senses/cli/tui-store.js +1 -0
  46. package/dist/senses/cli.js +1 -0
  47. package/dist/senses/habit-lifecycle-guard.js +24 -0
  48. package/dist/senses/ingress-evidence.js +2 -0
  49. package/dist/senses/mail.js +129 -0
  50. package/dist/senses/pipeline.js +90 -24
  51. package/dist/senses/private-runtime-worker.js +21 -20
  52. package/dist/senses/private-runtime.js +79 -29
  53. package/dist/senses/shared-turn.js +1 -0
  54. package/dist/senses/teams.js +1 -0
  55. package/package.json +6 -1
@@ -76,6 +76,7 @@ const await_parser_1 = require("../heart/awaiting/await-parser");
76
76
  const await_runtime_state_1 = require("../heart/awaiting/await-runtime-state");
77
77
  const habit_parser_1 = require("../heart/habits/habit-parser");
78
78
  const habit_runtime_state_1 = require("../heart/habits/habit-runtime-state");
79
+ const habit_lifecycle_guard_1 = require("./habit-lifecycle-guard");
79
80
  const cadence_1 = require("../heart/daemon/cadence");
80
81
  const habit_policy_1 = require("../rsvp/habit-policy");
81
82
  const daemon_health_1 = require("../heart/daemon/daemon-health");
@@ -344,6 +345,14 @@ function assertPayloadBinding(decision, kind, id) {
344
345
  return;
345
346
  throw new Error(`private-runtime decision payload mismatch: missing ${kind} origin ref ${id}`);
346
347
  }
348
+ function assertNoSendBinding(decision, noSend) {
349
+ const refs = decision.originRefs.filter((ref) => ref.kind === "capability" && ref.id === "no-send");
350
+ if (noSend === true && refs.length === 1)
351
+ return;
352
+ if (noSend === undefined && refs.length === 0)
353
+ return;
354
+ throw new Error("private-runtime decision payload mismatch: no-send capability binding does not match the turn");
355
+ }
347
356
  function ledgerDecisionFor(decision) {
348
357
  const ledgerPath = decision.ledgerLocator?.path;
349
358
  if (!ledgerPath) {
@@ -389,6 +398,7 @@ function assertPrivateTurnDecisionAllowed(input) {
389
398
  assertPayloadBinding(decision, "task", options?.taskId);
390
399
  assertPayloadBinding(decision, "habit", options?.habitName);
391
400
  assertPayloadBinding(decision, "await", options?.awaitName);
401
+ assertNoSendBinding(decision, options?.noSend);
392
402
  const ledgerRow = ledgerDecisionFor(decision);
393
403
  const decidedAtMs = Date.parse(ledgerRow.decidedAt);
394
404
  if (!Number.isFinite(decidedAtMs)) {
@@ -489,6 +499,7 @@ function extractToolCallNames(messages) {
489
499
  }
490
500
  function createPrivateRuntimeCallbacks() {
491
501
  return {
502
+ settleOutputMode: "final_only",
492
503
  onModelStart: () => { },
493
504
  onModelStreamStart: () => { },
494
505
  onTextChunk: () => { },
@@ -795,6 +806,32 @@ function buildHabitSurfacePolicy(origin, surface) {
795
806
  lines.push("- use send_message for intentional contact; use surface only for a held return tied to an existing return obligation.");
796
807
  return lines.join("\n");
797
808
  }
809
+ function reduceHabitSessionToNoSend(habitSession) {
810
+ const deniedTools = new Set([
811
+ ...(habitSession?.permissionEnvelope.deniedTools ?? []),
812
+ ...(habitSession?.toolPolicy.grantedTools ?? []),
813
+ ...(habitSession?.toolPolicy.deniedTools ?? []),
814
+ "send_message",
815
+ "surface",
816
+ ]);
817
+ return {
818
+ ...habitSession,
819
+ noSend: true,
820
+ permissionEnvelope: {
821
+ schemaVersion: 1,
822
+ canMessageOutward: false,
823
+ returnRoutes: [],
824
+ deniedTools: [...deniedTools],
825
+ warnings: [...(habitSession?.permissionEnvelope.warnings ?? [])],
826
+ },
827
+ toolPolicy: {
828
+ requestedTools: habitSession?.toolPolicy.requestedTools ?? null,
829
+ grantedTools: [],
830
+ deniedTools: [...deniedTools],
831
+ outwardMessagingAllowed: false,
832
+ },
833
+ };
834
+ }
798
835
  async function runPrivateRuntimeTurn(options) {
799
836
  const now = options?.now ?? (() => new Date());
800
837
  const reason = options?.reason ?? "instinct";
@@ -873,37 +910,43 @@ async function runPrivateRuntimeTurn(options) {
873
910
  let habitSurface = { family: true, originator: true, extra: [] };
874
911
  if (preparedHabit) {
875
912
  if (rsvpHabit && !preparedHabit.rsvp) {
876
- throw new Error(`RSVP habit metadata is required before private runtime execution: ${habitName}`);
913
+ const detail = preparedHabit.status === "degraded"
914
+ ? preparedHabit.degradedDetail
915
+ : null;
916
+ throw new Error(`RSVP habit metadata is required before private runtime execution: ${detail ?? habitName}`);
877
917
  }
878
- habitBody = preparedHabit.body || undefined;
879
- habitTitle = preparedHabit.title || habitName;
880
- habitLastRun = preparedHabit.lastRun;
881
- habitTools = preparedHabit.tools;
882
- habitOrigin = preparedHabit.origin;
883
- habitSurface = preparedHabit.surface;
918
+ const blockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(preparedHabit, "private-runtime");
919
+ if (blockedReason)
920
+ throw new Error(blockedReason);
884
921
  }
885
- else {
886
- try {
887
- const habitContent = fs.readFileSync(habitFilePath, "utf-8");
888
- const parsed = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, (0, habit_parser_1.parseHabitFile)(habitContent, habitFilePath));
889
- if (rsvpHabit && !parsed.rsvp) {
890
- throw new Error(`RSVP habit metadata is required before private runtime execution: ${habitName}`);
891
- }
892
- habitBody = parsed.body || undefined;
893
- habitTitle = parsed.title || habitName;
894
- habitLastRun = parsed.lastRun;
895
- habitTools = parsed.tools;
896
- habitOrigin = parsed.origin;
897
- habitSurface = parsed.surface;
898
- }
899
- catch (error) {
900
- if (rsvpHabit) {
901
- const reason = error instanceof Error ? error.message : String(error);
902
- throw new Error(`RSVP habit metadata is required before private runtime execution: ${reason}`);
903
- }
904
- // Habit file missing or unreadable
922
+ let currentHabit;
923
+ try {
924
+ const habitContent = fs.readFileSync(habitFilePath, "utf-8");
925
+ currentHabit = (0, habit_runtime_state_1.applyHabitRuntimeState)(agentRoot, (0, habit_parser_1.parseHabitFile)(habitContent, habitFilePath));
926
+ }
927
+ catch (error) {
928
+ const readReason = error instanceof Error ? error.message : String(error);
929
+ if (rsvpHabit) {
930
+ throw new Error(`RSVP habit metadata is required before private runtime execution: ${readReason}`);
905
931
  }
932
+ const degraded = (0, habit_parser_1.createDegradedHabitFile)(habitFilePath, "read_error", "", readReason);
933
+ const blockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(degraded, "private-runtime");
934
+ throw new Error(blockedReason);
935
+ }
936
+ if (rsvpHabit && !currentHabit.rsvp) {
937
+ const detail = currentHabit.status === "degraded" ? currentHabit.degradedDetail : null;
938
+ throw new Error(`RSVP habit metadata is required before private runtime execution: ${detail ?? habitName}`);
906
939
  }
940
+ const currentBlockedReason = (0, habit_lifecycle_guard_1.privateRuntimeHabitRejectionReason)(currentHabit, "private-runtime");
941
+ if (currentBlockedReason)
942
+ throw new Error(currentBlockedReason);
943
+ const executableHabit = preparedHabit ?? currentHabit;
944
+ habitBody = executableHabit.body || undefined;
945
+ habitTitle = executableHabit.title || habitName;
946
+ habitLastRun = executableHabit.lastRun;
947
+ habitTools = executableHabit.tools;
948
+ habitOrigin = executableHabit.origin;
949
+ habitSurface = executableHabit.surface;
907
950
  // If the habit file couldn't be read at all (no body, no title parsed), error message
908
951
  if (habitBody === undefined && habitTitle === habitName) {
909
952
  userContent = `habit "${habitName}" could not be read (file not found or unreadable). check habits/${habitName}.md exists.`;
@@ -1027,6 +1070,12 @@ async function runPrivateRuntimeTurn(options) {
1027
1070
  meta: { habitName: options.habitName },
1028
1071
  });
1029
1072
  }
1073
+ if (options?.noSend === true) {
1074
+ habitToolsResolved = [];
1075
+ }
1076
+ const effectiveHabitSession = options?.noSend === true
1077
+ ? reduceHabitSessionToNoSend(options.habitSession)
1078
+ : options?.habitSession;
1030
1079
  const sessionLoader = {
1031
1080
  loadOrCreate: async () => {
1032
1081
  if (existingMessages.length > 0) {
@@ -1111,9 +1160,10 @@ async function runPrivateRuntimeTurn(options) {
1111
1160
  toolContext: {
1112
1161
  signin: async () => undefined,
1113
1162
  delegatedOrigins: attentionQueue,
1114
- ...(options?.habitSession ? { habitSession: options.habitSession } : {}),
1163
+ ...(options?.noSend ? { noSend: true } : {}),
1164
+ ...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
1115
1165
  },
1116
- ...(options?.habitSession ? { habitSession: options.habitSession } : {}),
1166
+ ...(effectiveHabitSession ? { habitSession: effectiveHabitSession } : {}),
1117
1167
  },
1118
1168
  });
1119
1169
  // Post-turn routeDelegatedCompletion removed: delivery is now inline via surface tool.
@@ -288,6 +288,7 @@ async function runSenseTurn(options) {
288
288
  };
289
289
  /* v8 ignore start — no-op callback stubs; only onTextChunk does real work (covered via mock) */
290
290
  const callbacks = {
291
+ settleOutputMode: "retractable_buffer",
291
292
  onModelStart: () => { },
292
293
  onModelStreamStart: () => { },
293
294
  onTextChunk: (chunk) => { pendingResponseText += chunk; },
@@ -409,6 +409,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
409
409
  }
410
410
  }
411
411
  return {
412
+ settleOutputMode: "final_only",
412
413
  onModelStart: () => {
413
414
  if (hadRealOutput)
414
415
  return; // real output already shown; don't overwrite with phrases
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.723",
3
+ "version": "0.1.0-alpha.725",
4
+ "engines": {
5
+ "node": ">=22"
6
+ },
4
7
  "main": "dist/heart/daemon/ouro-entry.js",
5
8
  "bin": {
6
9
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -61,6 +64,7 @@
61
64
  "@ouro.bot/friends": "0.1.0-alpha.7",
62
65
  "@types/react": "^17.0.91",
63
66
  "@types/ws": "^8.18.1",
67
+ "better-sqlite3": "13.0.2",
64
68
  "fast-glob": "^3.3.3",
65
69
  "ink": "^3.2.0",
66
70
  "mailparser": "^3.9.8",
@@ -77,6 +81,7 @@
77
81
  },
78
82
  "devDependencies": {
79
83
  "@testing-library/react": "^16.3.2",
84
+ "@types/better-sqlite3": "7.6.13",
80
85
  "@types/mailparser": "^3.4.6",
81
86
  "@types/semver": "^7.7.1",
82
87
  "@types/smtp-server": "^3.5.13",