@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.
- package/changelog.json +15 -0
- package/dist/arc/flight-recorder.js +139 -21
- package/dist/arc/obligations.js +28 -6
- package/dist/arc/packets.js +48 -0
- package/dist/heart/autonomy-budget.js +434 -0
- package/dist/heart/core.js +11 -0
- package/dist/heart/daemon/cadence.js +110 -0
- package/dist/heart/daemon/cli-exec.js +20 -0
- package/dist/heart/daemon/cli-help.js +74 -0
- package/dist/heart/daemon/cli-parse.js +466 -0
- package/dist/heart/daemon/daemon.js +24 -3
- package/dist/heart/daemon/doctor.js +151 -0
- package/dist/heart/daemon/stale-bundle-prune.js +23 -6
- package/dist/heart/habits/habit-parser.js +4 -1
- package/dist/heart/habits/habit-scheduler.js +46 -38
- package/dist/heart/habits/habit-session.js +2 -3
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-credentials.js +12 -3
- package/dist/heart/work-card.js +1 -11
- package/dist/mind/prompt-budget.js +380 -0
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-obligations.js +39 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +679 -0
- package/dist/rsvp/config.js +327 -0
- package/dist/rsvp/cutover.js +431 -0
- package/dist/rsvp/diagnostics.js +222 -0
- package/dist/rsvp/diff-renderer.js +159 -0
- package/dist/rsvp/habit-policy.js +85 -0
- package/dist/rsvp/habit-stage.js +93 -0
- package/dist/rsvp/incident-bundle.js +97 -0
- package/dist/rsvp/migration.js +204 -0
- package/dist/rsvp/outbound-state.js +254 -0
- package/dist/rsvp/query.js +118 -0
- package/dist/rsvp/replay.js +233 -0
- package/dist/rsvp/snapshot.js +232 -0
- package/dist/senses/bluebubbles/client.js +13 -7
- package/dist/senses/bluebubbles/index.js +227 -22
- package/dist/senses/bluebubbles/outbound-state.js +304 -0
- package/dist/senses/bluebubbles/replay.js +147 -0
- package/dist/senses/context-packet-ledger.js +245 -0
- package/dist/senses/context-packets.js +154 -0
- package/dist/senses/pipeline.js +92 -12
- package/dist/senses/private-runtime-worker.js +170 -31
- package/dist/senses/private-runtime.js +13 -1
- package/dist/senses/voice/twilio-phone.js +9 -5
- package/package.json +1 -1
|
@@ -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
|
+
}
|
package/dist/heart/core.js
CHANGED
|
@@ -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
|
}
|