@ouro.bot/cli 0.1.0-alpha.702 → 0.1.0-alpha.704

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,20 @@
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.704",
6
+ "changes": [
7
+ "Restore RSVP notification copy and harden RSVP run ledgers.",
8
+ "Preserve loose RSVP spend-ledger history while adding cold-start state coverage."
9
+ ]
10
+ },
11
+ {
12
+ "version": "0.1.0-alpha.703",
13
+ "changes": [
14
+ "Let ouro up hand off when the daemon and enabled senses are healthy but only advisory health checks, such as context-loss-sentinel warnings, remain.",
15
+ "Keep final handoff blocking on degraded enabled senses and health checks that affect daemon, cron, or sense readiness."
16
+ ]
17
+ },
4
18
  {
5
19
  "version": "0.1.0-alpha.702",
6
20
  "changes": [
@@ -715,6 +715,32 @@ function finalDaemonFailureMessage(deps, reason) {
715
715
  lines.push("Run `ouro up` again or `ouro doctor` for a deeper diagnosis.");
716
716
  return lines.join("\n");
717
717
  }
718
+ function finalHandoffBlockingHealthChecks(payload) {
719
+ return payload.healthChecks
720
+ .filter((check) => {
721
+ if (check.status === "ok")
722
+ return false;
723
+ if (check.status === "critical")
724
+ return true;
725
+ return check.name === "agent-processes"
726
+ || check.name === "cron-health"
727
+ || check.name === "sense-probes"
728
+ || check.name.startsWith("sense-probe:");
729
+ })
730
+ .map((check) => `${check.name}: ${check.status} - ${check.message}`);
731
+ }
732
+ function finalHandoffBlockingWorkers(payload) {
733
+ return payload.workers
734
+ .filter((worker) => !(worker.autoStart === false && worker.status === "stopped") && worker.status !== "running")
735
+ .map((worker) => {
736
+ const detail = [
737
+ worker.errorReason,
738
+ worker.fixHint ? `fix: ${worker.fixHint}` : null,
739
+ ].filter((part) => part !== null).join("; ");
740
+ const suffix = detail.length > 0 ? ` - ${detail}` : "";
741
+ return `${worker.agent}/${worker.worker}: ${worker.status}${suffix}`;
742
+ });
743
+ }
718
744
  async function verifyDaemonReadyForHandoff(deps, options) {
719
745
  const settleTimeoutMs = options.settleTimeoutMs;
720
746
  const settlePollIntervalMs = options.settlePollIntervalMs;
@@ -772,16 +798,25 @@ async function verifyDaemonReadyForHandoffOnce(deps, options = {}) {
772
798
  };
773
799
  }
774
800
  if (payload.overview.health !== "ok") {
801
+ const blockingWorkers = finalHandoffBlockingWorkers(payload);
775
802
  const degradedSenses = payload.senses
776
803
  .filter((sense) => sense.enabled && !["running", "interactive", "disabled", "not_attached"].includes(sense.status))
777
804
  .map((sense) => `${sense.agent}/${sense.sense}: ${sense.status} - ${sense.detail}`);
778
- const detail = degradedSenses.length > 0 ? `; ${degradedSenses.join("; ")}` : "";
805
+ const blockingHealthChecks = finalHandoffBlockingHealthChecks(payload);
806
+ const details = [...blockingWorkers, ...degradedSenses, ...blockingHealthChecks];
807
+ const detail = details.length > 0 ? `; ${details.join("; ")}` : "";
779
808
  if (options.allowDegradedHealth) {
780
809
  return {
781
810
  ok: true,
782
811
  summary: `runtime health ${payload.overview.health}`,
783
812
  };
784
813
  }
814
+ if (blockingWorkers.length === 0 && degradedSenses.length === 0 && blockingHealthChecks.length === 0) {
815
+ return {
816
+ ok: true,
817
+ summary: "daemon answered with advisory health warnings",
818
+ };
819
+ }
785
820
  const reason = `runtime health is ${payload.overview.health}${detail}`;
786
821
  return {
787
822
  ok: false,
@@ -4,7 +4,7 @@ exports.guestDisplayName = guestDisplayName;
4
4
  exports.computeRsvpDelta = computeRsvpDelta;
5
5
  exports.renderRsvpReport = renderRsvpReport;
6
6
  const runtime_1 = require("../nerves/runtime");
7
- const RSVP_REPORT_FOOTER = "🤖 Beep boop! Automated RSVP update from Slugger — no need to reply here!";
7
+ const RSVP_REPORT_FOOTER = "🤖 Beep boop! I'm a script, not Slugger — no need to reply here!";
8
8
  function byId(left, right) {
9
9
  return left.id.localeCompare(right.id);
10
10
  }
@@ -38,6 +38,8 @@ const fs = __importStar(require("node:fs"));
38
38
  const path = __importStar(require("node:path"));
39
39
  const habit_parser_1 = require("../heart/habits/habit-parser");
40
40
  const runtime_1 = require("../nerves/runtime");
41
+ const outbound_state_1 = require("./outbound-state");
42
+ const spend_ledger_1 = require("./spend-ledger");
41
43
  const habit_policy_1 = require("./habit-policy");
42
44
  function defaultRsvpHabitMetadata(mode) {
43
45
  return {
@@ -62,17 +64,20 @@ function stagedHabitBody() {
62
64
  function stageRsvpHabit(input) {
63
65
  const habitPath = path.join(input.agentRoot, "habits", `${habit_policy_1.RSVP_HABIT_NAME}.md`);
64
66
  const rsvp = defaultRsvpHabitMetadata(input.mode);
67
+ const created = (input.now ?? new Date()).toISOString();
65
68
  const content = (0, habit_parser_1.renderHabitFile)({
66
69
  title: "RSVP Ari & Rachel",
67
70
  status: "active",
68
71
  cadence: input.cadence,
69
- created: (input.now ?? new Date()).toISOString(),
72
+ created,
70
73
  tools: [...habit_policy_1.RSVP_HABIT_ALLOWED_TOOLS],
71
74
  continuity: { mode: "stateful" },
72
75
  rsvp,
73
76
  }, stagedHabitBody());
74
77
  fs.mkdirSync(path.dirname(habitPath), { recursive: true });
75
78
  fs.writeFileSync(habitPath, content, "utf-8");
79
+ (0, outbound_state_1.ensureRsvpOutboundState)(input.agentRoot, created);
80
+ (0, spend_ledger_1.ensureRsvpSpendLedger)(input.agentRoot, created);
76
81
  (0, runtime_1.emitNervesEvent)({
77
82
  component: "rsvp",
78
83
  event: "rsvp.habit_staged",
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.RSVP_OUTBOUND_STATE_POLICY_VERSION = void 0;
37
37
  exports.readRsvpOutboundState = readRsvpOutboundState;
38
+ exports.ensureRsvpOutboundState = ensureRsvpOutboundState;
38
39
  exports.writeRsvpBaseline = writeRsvpBaseline;
39
40
  exports.decideRsvpOutboundReport = decideRsvpOutboundReport;
40
41
  exports.recordRsvpOutboundAttempt = recordRsvpOutboundAttempt;
@@ -135,6 +136,19 @@ function writeState(agentRoot, state) {
135
136
  fs.renameSync(tmp, filePath);
136
137
  return state;
137
138
  }
139
+ function ensureRsvpOutboundState(agentRoot, now = new Date().toISOString()) {
140
+ const filePath = rsvpOutboundStatePath(agentRoot);
141
+ if (fs.existsSync(filePath))
142
+ return readRsvpOutboundState(agentRoot);
143
+ const state = writeState(agentRoot, emptyState(now));
144
+ (0, runtime_1.emitNervesEvent)({
145
+ component: "rsvp",
146
+ event: "rsvp.outbound_state_initialized",
147
+ message: "initialized RSVP outbound state",
148
+ meta: { updatedAt: state.updatedAt },
149
+ });
150
+ return state;
151
+ }
138
152
  function writeRsvpBaseline(input) {
139
153
  const current = readRsvpOutboundState(input.agentRoot);
140
154
  const state = writeState(input.agentRoot, {
@@ -0,0 +1,175 @@
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.RSVP_SPEND_LEDGER_POLICY_VERSION = void 0;
37
+ exports.readRsvpSpendLedger = readRsvpSpendLedger;
38
+ exports.ensureRsvpSpendLedger = ensureRsvpSpendLedger;
39
+ exports.recordRsvpSpendLedgerRun = recordRsvpSpendLedgerRun;
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
42
+ const runtime_1 = require("../nerves/runtime");
43
+ exports.RSVP_SPEND_LEDGER_POLICY_VERSION = "rsvp-spend-ledger/v1";
44
+ function ledgerPath(agentRoot) {
45
+ return path.join(agentRoot, "state", "rsvp", "spend-ledger.json");
46
+ }
47
+ function emptyLedger(now) {
48
+ return {
49
+ schemaVersion: 1,
50
+ policyVersion: exports.RSVP_SPEND_LEDGER_POLICY_VERSION,
51
+ createdAt: now,
52
+ updatedAt: now,
53
+ runs: [],
54
+ };
55
+ }
56
+ function isRecord(value) {
57
+ return !!value && typeof value === "object" && !Array.isArray(value);
58
+ }
59
+ function stringField(record, key) {
60
+ const value = record[key];
61
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
62
+ }
63
+ function isRun(value) {
64
+ const row = value;
65
+ return !!row
66
+ && row.schemaVersion === 1
67
+ && typeof row.runId === "string"
68
+ && typeof row.rootRunId === "string"
69
+ && typeof row.idempotencyKey === "string"
70
+ && typeof row.targetHash === "string"
71
+ && typeof row.habitName === "string"
72
+ && typeof row.trigger === "string"
73
+ && typeof row.lifecycle === "string"
74
+ && typeof row.startedAt === "string"
75
+ && typeof row.recordedAt === "string"
76
+ && row.contentStored === false;
77
+ }
78
+ function storedRun(value) {
79
+ if (isRun(value))
80
+ return value;
81
+ return isRecord(value) ? value : null;
82
+ }
83
+ function isLedger(value) {
84
+ const row = value;
85
+ return !!row
86
+ && row.schemaVersion === 1
87
+ && row.policyVersion === exports.RSVP_SPEND_LEDGER_POLICY_VERSION
88
+ && typeof row.createdAt === "string"
89
+ && typeof row.updatedAt === "string"
90
+ && Array.isArray(row.runs);
91
+ }
92
+ function readRsvpSpendLedger(agentRoot) {
93
+ const filePath = ledgerPath(agentRoot);
94
+ if (!fs.existsSync(filePath))
95
+ return emptyLedger(new Date().toISOString());
96
+ try {
97
+ const now = new Date().toISOString();
98
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
99
+ if (!isLedger(parsed)) {
100
+ if (!isRecord(parsed) || !Array.isArray(parsed.runs))
101
+ return emptyLedger(now);
102
+ return {
103
+ schemaVersion: 1,
104
+ policyVersion: exports.RSVP_SPEND_LEDGER_POLICY_VERSION,
105
+ createdAt: stringField(parsed, "createdAt") ?? now,
106
+ updatedAt: stringField(parsed, "updatedAt") ?? now,
107
+ runs: parsed.runs.map(storedRun).filter((run) => run !== null),
108
+ };
109
+ }
110
+ return {
111
+ schemaVersion: 1,
112
+ policyVersion: exports.RSVP_SPEND_LEDGER_POLICY_VERSION,
113
+ createdAt: parsed.createdAt,
114
+ updatedAt: parsed.updatedAt,
115
+ runs: parsed.runs.map(storedRun).filter((run) => run !== null),
116
+ };
117
+ }
118
+ catch {
119
+ return emptyLedger(new Date().toISOString());
120
+ }
121
+ }
122
+ function writeLedger(agentRoot, ledger) {
123
+ const filePath = ledgerPath(agentRoot);
124
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
125
+ const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
126
+ fs.writeFileSync(tmp, `${JSON.stringify(ledger, null, 2)}\n`, "utf-8");
127
+ fs.renameSync(tmp, filePath);
128
+ return ledger;
129
+ }
130
+ function ensureRsvpSpendLedger(agentRoot, now = new Date().toISOString()) {
131
+ const filePath = ledgerPath(agentRoot);
132
+ if (fs.existsSync(filePath))
133
+ return readRsvpSpendLedger(agentRoot);
134
+ const ledger = writeLedger(agentRoot, emptyLedger(now));
135
+ (0, runtime_1.emitNervesEvent)({
136
+ component: "rsvp",
137
+ event: "rsvp.spend_ledger_initialized",
138
+ message: "initialized RSVP spend ledger",
139
+ meta: { updatedAt: ledger.updatedAt },
140
+ });
141
+ return ledger;
142
+ }
143
+ function recordRsvpSpendLedgerRun(agentRoot, record) {
144
+ const current = readRsvpSpendLedger(agentRoot);
145
+ const run = {
146
+ schemaVersion: 1,
147
+ runId: record.runId,
148
+ rootRunId: record.rootRunId,
149
+ idempotencyKey: record.idempotencyKey,
150
+ targetHash: record.targetHash,
151
+ habitName: record.senseOrHabit,
152
+ trigger: record.triggerType,
153
+ lifecycle: record.lifecycle,
154
+ startedAt: record.startedAt,
155
+ recordedAt: record.recordedAt,
156
+ ...(record.endedAt ? { endedAt: record.endedAt } : {}),
157
+ ...(record.usage ? { usage: record.usage } : {}),
158
+ contentStored: false,
159
+ };
160
+ const withoutDuplicate = current.runs.filter((entry) => !(entry.runId === run.runId && entry.lifecycle === run.lifecycle));
161
+ const updated = writeLedger(agentRoot, {
162
+ schemaVersion: 1,
163
+ policyVersion: exports.RSVP_SPEND_LEDGER_POLICY_VERSION,
164
+ createdAt: current.createdAt,
165
+ updatedAt: run.recordedAt,
166
+ runs: [...withoutDuplicate, run],
167
+ });
168
+ (0, runtime_1.emitNervesEvent)({
169
+ component: "rsvp",
170
+ event: "rsvp.spend_ledger_recorded",
171
+ message: "recorded RSVP spend ledger row",
172
+ meta: { runId: run.runId, lifecycle: run.lifecycle, habitName: run.habitName },
173
+ });
174
+ return updated;
175
+ }
@@ -52,6 +52,7 @@ const tools_base_1 = require("../repertoire/tools-base");
52
52
  const tools_surface_1 = require("../repertoire/tools-surface");
53
53
  const tools_1 = require("../repertoire/tools");
54
54
  const habit_policy_1 = require("../rsvp/habit-policy");
55
+ const spend_ledger_1 = require("../rsvp/spend-ledger");
55
56
  const run_ledger_1 = require("../heart/run-ledger");
56
57
  const autonomy_budget_1 = require("../heart/autonomy-budget");
57
58
  function isUsageData(value) {
@@ -108,14 +109,29 @@ function habitRunLedgerBase(habitRun) {
108
109
  }
109
110
  function recordHabitRunLedger(habitRun, lifecycle, endedAt) {
110
111
  const usage = extractUsageFromHabitResults(habitRun.results);
111
- (0, run_ledger_1.appendRunLedgerRecordNonFatal)(habitRun.agentRoot, (0, run_ledger_1.createRunLedgerRecord)({
112
+ const record = (0, run_ledger_1.createRunLedgerRecord)({
112
113
  ...habitRunLedgerBase(habitRun),
113
114
  lifecycle,
114
115
  ...(endedAt ? { endedAt } : {}),
115
116
  ...(lifecycle === "completed" || lifecycle === "error"
116
117
  ? { usage: (0, run_ledger_1.usageMetadataFromUsageData)(usage, usage ? "provider" : "none") }
117
118
  : {}),
118
- }));
119
+ });
120
+ (0, run_ledger_1.appendRunLedgerRecordNonFatal)(habitRun.agentRoot, record);
121
+ if (habitRun.rsvpPolicy) {
122
+ try {
123
+ (0, spend_ledger_1.recordRsvpSpendLedgerRun)(habitRun.agentRoot, record);
124
+ }
125
+ catch (error) {
126
+ (0, runtime_1.emitNervesEvent)({
127
+ level: "error",
128
+ component: "rsvp",
129
+ event: "rsvp.spend_ledger_record_error",
130
+ message: "failed to record RSVP spend ledger row",
131
+ meta: { runId: record.runId, lifecycle, error: String(error) },
132
+ });
133
+ }
134
+ }
119
135
  }
120
136
  function reserveHabitAutonomyBudget(habitRun, nowIso) {
121
137
  const target = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.702",
3
+ "version": "0.1.0-alpha.704",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",