@ouro.bot/cli 0.1.0-alpha.696 → 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 (43) hide show
  1. package/changelog.json +9 -0
  2. package/dist/heart/autonomy-budget.js +434 -0
  3. package/dist/heart/core.js +11 -0
  4. package/dist/heart/daemon/cadence.js +110 -0
  5. package/dist/heart/daemon/cli-exec.js +20 -0
  6. package/dist/heart/daemon/cli-help.js +74 -0
  7. package/dist/heart/daemon/cli-parse.js +466 -0
  8. package/dist/heart/daemon/daemon.js +24 -3
  9. package/dist/heart/daemon/doctor.js +151 -0
  10. package/dist/heart/daemon/stale-bundle-prune.js +23 -6
  11. package/dist/heart/habits/habit-parser.js +4 -1
  12. package/dist/heart/habits/habit-scheduler.js +46 -38
  13. package/dist/heart/habits/habit-session.js +2 -3
  14. package/dist/heart/run-ledger.js +247 -0
  15. package/dist/heart/runtime-credentials.js +12 -3
  16. package/dist/mind/prompt-budget.js +380 -0
  17. package/dist/repertoire/tools-base.js +2 -0
  18. package/dist/repertoire/tools-rsvp.js +139 -0
  19. package/dist/rsvp/aisleplanner-client.js +354 -0
  20. package/dist/rsvp/cli.js +679 -0
  21. package/dist/rsvp/config.js +327 -0
  22. package/dist/rsvp/cutover.js +431 -0
  23. package/dist/rsvp/diagnostics.js +222 -0
  24. package/dist/rsvp/diff-renderer.js +159 -0
  25. package/dist/rsvp/habit-policy.js +85 -0
  26. package/dist/rsvp/habit-stage.js +93 -0
  27. package/dist/rsvp/incident-bundle.js +97 -0
  28. package/dist/rsvp/migration.js +204 -0
  29. package/dist/rsvp/outbound-state.js +254 -0
  30. package/dist/rsvp/query.js +118 -0
  31. package/dist/rsvp/replay.js +233 -0
  32. package/dist/rsvp/snapshot.js +232 -0
  33. package/dist/senses/bluebubbles/client.js +13 -7
  34. package/dist/senses/bluebubbles/index.js +227 -22
  35. package/dist/senses/bluebubbles/outbound-state.js +304 -0
  36. package/dist/senses/bluebubbles/replay.js +147 -0
  37. package/dist/senses/context-packet-ledger.js +245 -0
  38. package/dist/senses/context-packets.js +154 -0
  39. package/dist/senses/pipeline.js +91 -1
  40. package/dist/senses/private-runtime-worker.js +170 -31
  41. package/dist/senses/private-runtime.js +13 -1
  42. package/dist/senses/voice/twilio-phone.js +9 -5
  43. package/package.json +1 -1
package/changelog.json CHANGED
@@ -1,6 +1,15 @@
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.697",
6
+ "changes": [
7
+ "Add BlueBubbles same-thread context, native RSVP habit, autonomy budgets, run ledger attribution, diagnostics, cutover, and verification guardrails.",
8
+ "Harden Twilio phone agent-root resolution so test and runtime callers use explicit bundle roots without falling back to the host AgentBundles tree.",
9
+ "Gate native RSVP live sends on a current passing legacy cutover check so refreshes and smoke tests cannot compete with the old RSVP sender.",
10
+ "Align RSVP doctor cutover preflight with the native config's legacy root so triage reports the same sender state enforced by live sends."
11
+ ]
12
+ },
4
13
  {
5
14
  "version": "0.1.0-alpha.696",
6
15
  "changes": [
@@ -0,0 +1,434 @@
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.AUTONOMY_BUDGET_DEFAULT_POLICY = void 0;
37
+ exports.autonomyBudgetStatePath = autonomyBudgetStatePath;
38
+ exports.autonomyReceiptsDir = autonomyReceiptsDir;
39
+ exports.readAutonomyBudgetState = readAutonomyBudgetState;
40
+ exports.readAutonomyStormBreakers = readAutonomyStormBreakers;
41
+ exports.reserveAutonomyBudget = reserveAutonomyBudget;
42
+ exports.recordAutonomyFailure = recordAutonomyFailure;
43
+ const fs = __importStar(require("node:fs"));
44
+ const path = __importStar(require("node:path"));
45
+ const runtime_1 = require("../nerves/runtime");
46
+ const run_ledger_1 = require("./run-ledger");
47
+ exports.AUTONOMY_BUDGET_DEFAULT_POLICY = {
48
+ agentProactivePaidTurnsPerHour: 6,
49
+ agentProactivePaidTurnsPerDay: 40,
50
+ senseRecoveryPaidTurnsPer15m: 3,
51
+ senseRecoveryPaidTurnsPerDay: 12,
52
+ habitPaidTurnsPerDay: 4,
53
+ duplicateRecoveryTtlMs: 10 * 60 * 1000,
54
+ duplicateHabitTtlMs: 24 * 60 * 60 * 1000,
55
+ stormFailureThreshold: 3,
56
+ stormFailureWindowMs: 10 * 60 * 1000,
57
+ stormBlockMs: 30 * 60 * 1000,
58
+ };
59
+ function autonomyRoot(agentRoot) {
60
+ return path.join(agentRoot, "state", "autonomy");
61
+ }
62
+ function autonomyBudgetStatePath(agentRoot) {
63
+ return path.join(autonomyRoot(agentRoot), "budgets.json");
64
+ }
65
+ function autonomyReceiptsDir(agentRoot) {
66
+ return path.join(autonomyRoot(agentRoot), "receipts");
67
+ }
68
+ function stormBreakersPath(agentRoot) {
69
+ return path.join(autonomyRoot(agentRoot), "storm-breakers.jsonl");
70
+ }
71
+ function nowIso(input) {
72
+ return input ?? new Date().toISOString();
73
+ }
74
+ function parseMs(value) {
75
+ return Date.parse(value);
76
+ }
77
+ function sameCivilDay(left, right) {
78
+ return left.slice(0, 10) === right.slice(0, 10);
79
+ }
80
+ function emptyState() {
81
+ return {
82
+ schemaVersion: 1,
83
+ updatedAt: new Date().toISOString(),
84
+ reservations: [],
85
+ failures: [],
86
+ };
87
+ }
88
+ function isReservation(value) {
89
+ const row = value;
90
+ return !!row
91
+ && row.schemaVersion === 1
92
+ && typeof row.reservedAt === "string"
93
+ && (row.status === undefined || row.status === "reserved" || row.status === "failed")
94
+ && typeof row.agent === "string"
95
+ && typeof row.triggerType === "string"
96
+ && typeof row.sourceKind === "string"
97
+ && typeof row.senseOrHabit === "string"
98
+ && typeof row.targetHash === "string"
99
+ && typeof row.idempotencyKey === "string"
100
+ && row.contentStored === false;
101
+ }
102
+ function isFailure(value) {
103
+ const row = value;
104
+ return !!row
105
+ && row.schemaVersion === 1
106
+ && typeof row.occurredAt === "string"
107
+ && typeof row.fingerprint === "string"
108
+ && typeof row.agent === "string"
109
+ && typeof row.triggerType === "string"
110
+ && typeof row.sourceKind === "string"
111
+ && typeof row.senseOrHabit === "string"
112
+ && typeof row.provider === "string"
113
+ && typeof row.targetHash === "string"
114
+ && typeof row.normalizedErrorName === "string"
115
+ && typeof row.normalizedErrorCode === "string"
116
+ && typeof row.codeLocation === "string"
117
+ && typeof row.idempotencyBucket === "string"
118
+ && row.contentStored === false;
119
+ }
120
+ function readAutonomyBudgetState(agentRoot) {
121
+ const filePath = autonomyBudgetStatePath(agentRoot);
122
+ if (!fs.existsSync(filePath))
123
+ return emptyState();
124
+ try {
125
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
126
+ return {
127
+ schemaVersion: 1,
128
+ updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : new Date().toISOString(),
129
+ reservations: Array.isArray(parsed.reservations) ? parsed.reservations.filter(isReservation) : [],
130
+ failures: Array.isArray(parsed.failures) ? parsed.failures.filter(isFailure) : [],
131
+ };
132
+ }
133
+ catch {
134
+ return emptyState();
135
+ }
136
+ }
137
+ function writeAutonomyBudgetState(agentRoot, state) {
138
+ const filePath = autonomyBudgetStatePath(agentRoot);
139
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
140
+ fs.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
141
+ }
142
+ function receiptIdFor(input) {
143
+ return `autr_${(0, run_ledger_1.runLedgerHash)(input).slice("sha256:".length, "sha256:".length + 32)}`;
144
+ }
145
+ function writeReceipt(agentRoot, decision) {
146
+ const receiptId = receiptIdFor(decision);
147
+ const receipt = {
148
+ schemaVersion: 1,
149
+ receiptId,
150
+ status: decision.status,
151
+ reason: decision.reason,
152
+ actor: decision.actor,
153
+ decidedAt: decision.decidedAt,
154
+ agent: decision.agent,
155
+ triggerType: decision.triggerType,
156
+ sourceKind: decision.sourceKind,
157
+ senseOrHabit: decision.senseOrHabit,
158
+ targetHash: decision.targetHash,
159
+ idempotencyKey: decision.idempotencyKey,
160
+ contentStored: false,
161
+ };
162
+ const filePath = path.join(autonomyReceiptsDir(agentRoot), `${receiptId}.json`);
163
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
164
+ fs.writeFileSync(filePath, `${JSON.stringify(receipt, null, 2)}\n`, "utf-8");
165
+ return receipt;
166
+ }
167
+ function duplicateTtlMs(triggerType, policy) {
168
+ if (triggerType === "habit")
169
+ return policy.duplicateHabitTtlMs;
170
+ return policy.duplicateRecoveryTtlMs;
171
+ }
172
+ function paidAutonomyTrigger(triggerType) {
173
+ return triggerType === "proactive" || triggerType === "recovery" || triggerType === "habit" || triggerType === "reload";
174
+ }
175
+ function inWindow(at, now, windowMs) {
176
+ const atMs = parseMs(at);
177
+ const nowMs = parseMs(now);
178
+ return Number.isFinite(atMs) && Number.isFinite(nowMs) && nowMs - atMs >= 0 && nowMs - atMs <= windowMs;
179
+ }
180
+ function reservationFor(request, decidedAt) {
181
+ return {
182
+ schemaVersion: 1,
183
+ reservedAt: decidedAt,
184
+ status: "reserved",
185
+ agent: request.agent,
186
+ triggerType: request.triggerType,
187
+ sourceKind: request.sourceKind,
188
+ senseOrHabit: request.senseOrHabit,
189
+ targetHash: (0, run_ledger_1.runLedgerHash)(request.target),
190
+ idempotencyKey: request.idempotencyKey,
191
+ contentStored: false,
192
+ };
193
+ }
194
+ function stormFingerprint(input) {
195
+ return (0, run_ledger_1.runLedgerHash)({
196
+ agent: input.agent,
197
+ triggerType: input.triggerType,
198
+ sourceKind: input.sourceKind,
199
+ senseOrHabit: input.senseOrHabit,
200
+ provider: input.provider,
201
+ targetHash: (0, run_ledger_1.runLedgerHash)(input.target),
202
+ normalizedErrorName: input.normalizedErrorName,
203
+ normalizedErrorCode: input.normalizedErrorCode,
204
+ codeLocation: input.codeLocation,
205
+ idempotencyBucket: input.idempotencyBucket,
206
+ });
207
+ }
208
+ function failureRecord(input, occurredAt) {
209
+ return {
210
+ schemaVersion: 1,
211
+ occurredAt,
212
+ fingerprint: stormFingerprint(input),
213
+ agent: input.agent,
214
+ triggerType: input.triggerType,
215
+ sourceKind: input.sourceKind,
216
+ senseOrHabit: input.senseOrHabit,
217
+ provider: input.provider,
218
+ targetHash: (0, run_ledger_1.runLedgerHash)(input.target),
219
+ normalizedErrorName: input.normalizedErrorName,
220
+ normalizedErrorCode: input.normalizedErrorCode,
221
+ codeLocation: input.codeLocation,
222
+ idempotencyBucket: input.idempotencyBucket,
223
+ contentStored: false,
224
+ };
225
+ }
226
+ function readAutonomyStormBreakers(agentRoot) {
227
+ const filePath = stormBreakersPath(agentRoot);
228
+ if (!fs.existsSync(filePath))
229
+ return [];
230
+ return fs.readFileSync(filePath, "utf-8")
231
+ .split("\n")
232
+ .filter((line) => line.trim().length > 0)
233
+ .flatMap((line) => {
234
+ try {
235
+ const parsed = JSON.parse(line);
236
+ return parsed.schemaVersion === 1 && parsed.contentStored === false ? [parsed] : [];
237
+ }
238
+ catch {
239
+ return [];
240
+ }
241
+ });
242
+ }
243
+ function appendStormBreaker(agentRoot, breaker) {
244
+ const filePath = stormBreakersPath(agentRoot);
245
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
246
+ fs.appendFileSync(filePath, `${JSON.stringify(breaker)}\n`, "utf-8");
247
+ }
248
+ function activeStormBreaker(agentRoot, input, now) {
249
+ const fingerprint = stormFingerprint(input);
250
+ const nowMs = parseMs(now);
251
+ return readAutonomyStormBreakers(agentRoot)
252
+ .filter((breaker) => breaker.fingerprint === fingerprint)
253
+ .filter((breaker) => parseMs(breaker.blockedUntil) > nowMs)
254
+ .sort((left, right) => right.blockedAt.localeCompare(left.blockedAt))[0] ?? null;
255
+ }
256
+ function blockDecision(agentRoot, request, decidedAt, status, reason) {
257
+ const targetHash = (0, run_ledger_1.runLedgerHash)(request.target);
258
+ const base = {
259
+ status,
260
+ actor: "agent-runnable",
261
+ reason,
262
+ decidedAt,
263
+ agent: request.agent,
264
+ triggerType: request.triggerType,
265
+ sourceKind: request.sourceKind,
266
+ senseOrHabit: request.senseOrHabit,
267
+ targetHash,
268
+ idempotencyKey: request.idempotencyKey,
269
+ };
270
+ const receipt = writeReceipt(agentRoot, base);
271
+ (0, runtime_1.emitNervesEvent)({
272
+ level: "warn",
273
+ component: "heart",
274
+ event: "heart.autonomy_budget_blocked",
275
+ message: "autonomy trigger blocked before paid work",
276
+ meta: {
277
+ agent: request.agent,
278
+ triggerType: request.triggerType,
279
+ sourceKind: request.sourceKind,
280
+ senseOrHabit: request.senseOrHabit,
281
+ status,
282
+ reason,
283
+ receiptId: receipt.receiptId,
284
+ },
285
+ });
286
+ return { allowed: false, ...base, receiptId: receipt.receiptId };
287
+ }
288
+ function budgetBlockReason(state, request, now, policy) {
289
+ if (!paidAutonomyTrigger(request.triggerType))
290
+ return null;
291
+ const agentRecentHour = state.reservations.filter((row) => row.agent === request.agent
292
+ && paidAutonomyTrigger(row.triggerType)
293
+ && inWindow(row.reservedAt, now, 60 * 60 * 1000)).length;
294
+ if (agentRecentHour >= policy.agentProactivePaidTurnsPerHour)
295
+ return "agent proactive paid turn budget exceeded for rolling hour";
296
+ const agentToday = state.reservations.filter((row) => row.agent === request.agent
297
+ && paidAutonomyTrigger(row.triggerType)
298
+ && sameCivilDay(row.reservedAt, now)).length;
299
+ if (agentToday >= policy.agentProactivePaidTurnsPerDay)
300
+ return "agent proactive paid turn budget exceeded for day";
301
+ if (request.triggerType === "recovery" && request.sourceKind === "sense") {
302
+ const recentRecovery = state.reservations.filter((row) => row.agent === request.agent
303
+ && row.triggerType === "recovery"
304
+ && row.sourceKind === request.sourceKind
305
+ && row.senseOrHabit === request.senseOrHabit
306
+ && inWindow(row.reservedAt, now, 15 * 60 * 1000)).length;
307
+ if (recentRecovery >= policy.senseRecoveryPaidTurnsPer15m)
308
+ return "sense recovery paid turn budget exceeded for rolling 15 minutes";
309
+ const recoveryToday = state.reservations.filter((row) => row.agent === request.agent
310
+ && row.triggerType === "recovery"
311
+ && row.sourceKind === request.sourceKind
312
+ && row.senseOrHabit === request.senseOrHabit
313
+ && sameCivilDay(row.reservedAt, now)).length;
314
+ if (recoveryToday >= policy.senseRecoveryPaidTurnsPerDay)
315
+ return "sense recovery paid turn budget exceeded for day";
316
+ }
317
+ if (request.triggerType === "habit") {
318
+ const habitToday = state.reservations.filter((row) => row.agent === request.agent
319
+ && row.triggerType === "habit"
320
+ && row.senseOrHabit === request.senseOrHabit
321
+ && sameCivilDay(row.reservedAt, now)).length;
322
+ if (habitToday >= policy.habitPaidTurnsPerDay)
323
+ return "habit paid turn budget exceeded for day";
324
+ }
325
+ return null;
326
+ }
327
+ function reserveAutonomyBudget(agentRoot, request, policy = exports.AUTONOMY_BUDGET_DEFAULT_POLICY) {
328
+ const decidedAt = nowIso(request.now);
329
+ const state = readAutonomyBudgetState(agentRoot);
330
+ const targetHash = (0, run_ledger_1.runLedgerHash)(request.target);
331
+ const duplicate = state.reservations.find((row) => row.idempotencyKey === request.idempotencyKey
332
+ && row.status !== "failed"
333
+ && inWindow(row.reservedAt, decidedAt, duplicateTtlMs(request.triggerType, policy)));
334
+ if (duplicate)
335
+ return blockDecision(agentRoot, request, decidedAt, "duplicate", "duplicate trigger suppressed");
336
+ if (request.storm && activeStormBreaker(agentRoot, request.storm, decidedAt)) {
337
+ return blockDecision(agentRoot, request, decidedAt, "blocked", "storm breaker active for this trigger fingerprint");
338
+ }
339
+ const reason = budgetBlockReason(state, request, decidedAt, policy);
340
+ if (reason)
341
+ return blockDecision(agentRoot, request, decidedAt, "blocked", reason);
342
+ const reservation = reservationFor(request, decidedAt);
343
+ const nextState = {
344
+ schemaVersion: 1,
345
+ updatedAt: decidedAt,
346
+ reservations: [...state.reservations, reservation],
347
+ failures: state.failures,
348
+ };
349
+ writeAutonomyBudgetState(agentRoot, nextState);
350
+ (0, runtime_1.emitNervesEvent)({
351
+ component: "heart",
352
+ event: "heart.autonomy_budget_allowed",
353
+ message: "autonomy trigger reserved budget before paid work",
354
+ meta: {
355
+ agent: request.agent,
356
+ triggerType: request.triggerType,
357
+ sourceKind: request.sourceKind,
358
+ senseOrHabit: request.senseOrHabit,
359
+ targetHash,
360
+ },
361
+ });
362
+ return {
363
+ allowed: true,
364
+ status: "allowed",
365
+ actor: "agent-runnable",
366
+ reason: "budget reserved",
367
+ decidedAt,
368
+ agent: request.agent,
369
+ triggerType: request.triggerType,
370
+ sourceKind: request.sourceKind,
371
+ senseOrHabit: request.senseOrHabit,
372
+ targetHash,
373
+ idempotencyKey: request.idempotencyKey,
374
+ };
375
+ }
376
+ function recordAutonomyFailure(agentRoot, input, policy = exports.AUTONOMY_BUDGET_DEFAULT_POLICY) {
377
+ const occurredAt = nowIso(input.occurredAt);
378
+ const record = failureRecord(input, occurredAt);
379
+ const state = readAutonomyBudgetState(agentRoot);
380
+ const targetHash = (0, run_ledger_1.runLedgerHash)(input.target);
381
+ const reservations = state.reservations.map((row) => row.agent === input.agent
382
+ && row.triggerType === input.triggerType
383
+ && row.sourceKind === input.sourceKind
384
+ && row.senseOrHabit === input.senseOrHabit
385
+ && row.targetHash === targetHash
386
+ && row.status !== "failed"
387
+ ? { ...row, status: "failed" }
388
+ : row);
389
+ const nextFailures = [...state.failures, record];
390
+ writeAutonomyBudgetState(agentRoot, {
391
+ schemaVersion: 1,
392
+ updatedAt: occurredAt,
393
+ reservations,
394
+ failures: nextFailures,
395
+ });
396
+ const recent = nextFailures.filter((row) => row.fingerprint === record.fingerprint
397
+ && inWindow(row.occurredAt, occurredAt, policy.stormFailureWindowMs));
398
+ if (recent.length >= policy.stormFailureThreshold && !activeStormBreaker(agentRoot, input, occurredAt)) {
399
+ const breaker = {
400
+ schemaVersion: 1,
401
+ fingerprint: record.fingerprint,
402
+ agent: record.agent,
403
+ triggerType: record.triggerType,
404
+ sourceKind: record.sourceKind,
405
+ senseOrHabit: record.senseOrHabit,
406
+ provider: record.provider,
407
+ targetHash: record.targetHash,
408
+ normalizedErrorName: record.normalizedErrorName,
409
+ normalizedErrorCode: record.normalizedErrorCode,
410
+ codeLocation: record.codeLocation,
411
+ idempotencyBucket: record.idempotencyBucket,
412
+ blockedAt: occurredAt,
413
+ blockedUntil: new Date(parseMs(occurredAt) + policy.stormBlockMs).toISOString(),
414
+ failureCount: recent.length,
415
+ contentStored: false,
416
+ };
417
+ appendStormBreaker(agentRoot, breaker);
418
+ (0, runtime_1.emitNervesEvent)({
419
+ level: "warn",
420
+ component: "heart",
421
+ event: "heart.autonomy_storm_breaker",
422
+ message: "autonomy storm breaker activated",
423
+ meta: {
424
+ agent: breaker.agent,
425
+ triggerType: breaker.triggerType,
426
+ sourceKind: breaker.sourceKind,
427
+ senseOrHabit: breaker.senseOrHabit,
428
+ blockedUntil: breaker.blockedUntil,
429
+ failureCount: breaker.failureCount,
430
+ },
431
+ });
432
+ }
433
+ return record;
434
+ }
@@ -21,6 +21,7 @@ const friends_1 = require("@ouro.bot/friends");
21
21
  const tools_2 = require("../repertoire/tools");
22
22
  const runtime_1 = require("../nerves/runtime");
23
23
  const context_1 = require("../mind/context");
24
+ const prompt_budget_1 = require("../mind/prompt-budget");
24
25
  const prompt_1 = require("../mind/prompt");
25
26
  const kept_notes_1 = require("./kept-notes");
26
27
  const error_classification_1 = require("./providers/error-classification");
@@ -964,6 +965,16 @@ async function runAgent(messages, callbacks, channel, signal, options) {
964
965
  callbacks.onModelStart();
965
966
  habitCallbackBufferRef.current = habitSession ? createHabitCallbackBuffer(callbacks) : null;
966
967
  try {
968
+ const promptBudget = (0, prompt_budget_1.applyPromptBudget)({
969
+ messages,
970
+ provider: providerRuntime.id,
971
+ model: providerRuntime.model,
972
+ contextWindowTokens: (0, config_1.getContextConfig)().maxTokens,
973
+ });
974
+ if (promptBudget.status !== "within_budget") {
975
+ messages.splice(0, messages.length, ...promptBudget.messages);
976
+ providerRuntime.resetTurnState(messages);
977
+ }
967
978
  return await providerRuntime.streamTurn({
968
979
  messages,
969
980
  activeTools,
@@ -3,8 +3,55 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DEFAULT_CADENCE_MS = void 0;
4
4
  exports.parseCadenceToCron = parseCadenceToCron;
5
5
  exports.parseCadenceToMs = parseCadenceToMs;
6
+ exports.nextCadenceRunAt = nextCadenceRunAt;
7
+ exports.cadenceFallbackDelayMs = cadenceFallbackDelayMs;
8
+ exports.evaluateCadenceDue = evaluateCadenceDue;
6
9
  const runtime_1 = require("../../nerves/runtime");
7
10
  exports.DEFAULT_CADENCE_MS = 30 * 60 * 1000; // 30 minutes
11
+ function parseFixedDailyCron(raw) {
12
+ if (typeof raw !== "string")
13
+ return null;
14
+ const parts = raw.trim().split(/\s+/);
15
+ if (parts.length !== 5)
16
+ return null;
17
+ const minuteRaw = parts[0];
18
+ const hourRaw = parts[1];
19
+ const day = parts[2];
20
+ const month = parts[3];
21
+ const weekday = parts[4];
22
+ if (day !== "*" || month !== "*" || weekday !== "*")
23
+ return null;
24
+ if (!/^\d+$/.test(minuteRaw) || !/^\d+$/.test(hourRaw))
25
+ return null;
26
+ const minute = Number.parseInt(minuteRaw, 10);
27
+ const hour = Number.parseInt(hourRaw, 10);
28
+ if (minute < 0 || minute > 59 || hour < 0 || hour > 23)
29
+ return null;
30
+ return { minute, hour };
31
+ }
32
+ function coerceTimeMs(value) {
33
+ const time = value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(value);
34
+ return Number.isFinite(time) ? time : null;
35
+ }
36
+ function localOccurrenceForDay(referenceMs, fixed) {
37
+ const occurrence = new Date(referenceMs);
38
+ occurrence.setHours(fixed.hour, fixed.minute, 0, 0);
39
+ return occurrence;
40
+ }
41
+ function previousFixedDailyOccurrence(fixed, nowMs) {
42
+ const occurrence = localOccurrenceForDay(nowMs, fixed);
43
+ if (occurrence.getTime() > nowMs) {
44
+ occurrence.setDate(occurrence.getDate() - 1);
45
+ }
46
+ return occurrence;
47
+ }
48
+ function nextFixedDailyOccurrence(fixed, fromMs) {
49
+ const occurrence = localOccurrenceForDay(fromMs, fixed);
50
+ if (occurrence.getTime() <= fromMs) {
51
+ occurrence.setDate(occurrence.getDate() + 1);
52
+ }
53
+ return occurrence;
54
+ }
8
55
  /**
9
56
  * Parse a cadence shorthand (e.g. "30m", "2h", "1d") into a cron string.
10
57
  * Also accepts raw cron strings (5 space-separated fields) and passes them through.
@@ -68,3 +115,66 @@ function parseCadenceToMs(raw) {
68
115
  return interval * 60 * 60 * 1000;
69
116
  return interval * 24 * 60 * 60 * 1000;
70
117
  }
118
+ function nextCadenceRunAt(raw, from) {
119
+ const fromMs = coerceTimeMs(from);
120
+ if (fromMs === null)
121
+ return null;
122
+ const intervalMs = parseCadenceToMs(raw);
123
+ if (intervalMs !== null) {
124
+ return new Date(fromMs + intervalMs).toISOString();
125
+ }
126
+ const fixed = parseFixedDailyCron(raw);
127
+ if (!fixed)
128
+ return null;
129
+ const next = nextFixedDailyOccurrence(fixed, fromMs);
130
+ (0, runtime_1.emitNervesEvent)({
131
+ event: "daemon.fixed_cadence_next_run_computed",
132
+ component: "daemon",
133
+ message: "computed fixed daily cadence next run",
134
+ meta: { raw, nextRunAt: next.toISOString() },
135
+ });
136
+ return next.toISOString();
137
+ }
138
+ function cadenceFallbackDelayMs(raw, nowMs) {
139
+ const intervalMs = parseCadenceToMs(raw);
140
+ if (intervalMs !== null)
141
+ return intervalMs;
142
+ const nextRunAt = nextCadenceRunAt(raw, nowMs);
143
+ if (!nextRunAt)
144
+ return null;
145
+ const delay = Date.parse(nextRunAt) - nowMs;
146
+ return delay;
147
+ }
148
+ function evaluateCadenceDue(raw, lastRun, nowMs) {
149
+ const intervalMs = parseCadenceToMs(raw);
150
+ if (intervalMs !== null) {
151
+ if (lastRun === null) {
152
+ return { due: true, elapsedMs: Infinity, occurrenceId: `overdue:first-run:${String(raw).trim()}` };
153
+ }
154
+ const lastRunMs = Date.parse(lastRun);
155
+ if (!Number.isFinite(lastRunMs))
156
+ return { due: false, elapsedMs: 0, occurrenceId: null };
157
+ const elapsedMs = nowMs - lastRunMs;
158
+ return {
159
+ due: elapsedMs >= intervalMs,
160
+ elapsedMs,
161
+ occurrenceId: elapsedMs >= intervalMs ? `overdue:last-run:${lastRun}:cadence:${String(raw).trim()}` : null,
162
+ };
163
+ }
164
+ const fixed = parseFixedDailyCron(raw);
165
+ if (!fixed)
166
+ return null;
167
+ const occurrence = previousFixedDailyOccurrence(fixed, nowMs);
168
+ const elapsedMs = lastRun === null ? Infinity : nowMs - occurrence.getTime();
169
+ const todayOccurrence = localOccurrenceForDay(nowMs, fixed);
170
+ const hasReachedTodayOccurrence = todayOccurrence.getTime() <= nowMs;
171
+ const lastRunMs = lastRun === null ? null : Date.parse(lastRun);
172
+ const due = lastRunMs === null
173
+ ? hasReachedTodayOccurrence
174
+ : Number.isFinite(lastRunMs) && lastRunMs < occurrence.getTime();
175
+ return {
176
+ due,
177
+ elapsedMs,
178
+ occurrenceId: due ? `fixed-daily:${occurrence.toISOString()}:cadence:${String(raw).trim()}` : null,
179
+ };
180
+ }
@@ -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
  }