@ouro.bot/cli 0.1.0-alpha.703 → 0.1.0-alpha.705
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 +13 -0
- package/dist/heart/daemon/cli-exec.js +14 -0
- package/dist/heart/daemon/cli-help.js +6 -3
- package/dist/heart/daemon/cli-parse.js +17 -2
- package/dist/rsvp/diff-renderer.js +1 -1
- package/dist/rsvp/habit-stage.js +6 -1
- package/dist/rsvp/outbound-state.js +14 -0
- package/dist/rsvp/spend-ledger.js +175 -0
- package/dist/senses/bluebubbles/context-packet.js +101 -0
- package/dist/senses/bluebubbles/context-smoke.js +144 -0
- package/dist/senses/bluebubbles/index.js +10 -66
- package/dist/senses/private-runtime-worker.js +18 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
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.705",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Add a side-effect-safe BlueBubbles context smoke command for verifying same-thread packet ledgers."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.704",
|
|
12
|
+
"changes": [
|
|
13
|
+
"Restore RSVP notification copy and harden RSVP run ledgers.",
|
|
14
|
+
"Preserve loose RSVP spend-ledger history while adding cold-start state coverage."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
"version": "0.1.0-alpha.703",
|
|
6
19
|
"changes": [
|
|
@@ -485,6 +485,7 @@ function agentResolutionFailureMode(command) {
|
|
|
485
485
|
case "config.models":
|
|
486
486
|
case "setup":
|
|
487
487
|
case "bluebubbles.replay":
|
|
488
|
+
case "bluebubbles.context-smoke":
|
|
488
489
|
return "throw";
|
|
489
490
|
default:
|
|
490
491
|
return undefined;
|
|
@@ -6081,6 +6082,19 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6081
6082
|
deps.writeStdout(text);
|
|
6082
6083
|
return text;
|
|
6083
6084
|
}
|
|
6085
|
+
if (command.kind === "bluebubbles.context-smoke") {
|
|
6086
|
+
const { smokeBlueBubblesContext, formatBlueBubblesContextSmokeText } = await Promise.resolve().then(() => __importStar(require("../../senses/bluebubbles/context-smoke")));
|
|
6087
|
+
const smoke = await smokeBlueBubblesContext({
|
|
6088
|
+
agentName: command.agent,
|
|
6089
|
+
messageGuid: command.messageGuid,
|
|
6090
|
+
persist: command.persist,
|
|
6091
|
+
});
|
|
6092
|
+
const text = command.json
|
|
6093
|
+
? JSON.stringify(smoke, null, 2)
|
|
6094
|
+
: formatBlueBubblesContextSmokeText(smoke);
|
|
6095
|
+
deps.writeStdout(text);
|
|
6096
|
+
return text;
|
|
6097
|
+
}
|
|
6084
6098
|
if (isRsvpCliCommand(command)) {
|
|
6085
6099
|
const { runRsvpCliCommand } = await Promise.resolve().then(() => __importStar(require("../../rsvp/cli")));
|
|
6086
6100
|
return runRsvpCliCommand(command, deps);
|
|
@@ -359,9 +359,12 @@ exports.COMMAND_REGISTRY = {
|
|
|
359
359
|
bluebubbles: {
|
|
360
360
|
category: "System",
|
|
361
361
|
description: "BlueBubbles integration commands",
|
|
362
|
-
usage:
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
usage: [
|
|
363
|
+
"ouro bluebubbles replay [--agent <name>] --message-guid <guid> [--event-type <type>] [--json]",
|
|
364
|
+
"ouro bluebubbles context-smoke [--agent <name>] --message-guid <guid> [--persist] [--json]",
|
|
365
|
+
].join("\n"),
|
|
366
|
+
example: "ouro bluebubbles context-smoke --agent slugger --message-guid abc123 --persist --json",
|
|
367
|
+
subcommands: ["replay", "context-smoke"],
|
|
365
368
|
},
|
|
366
369
|
};
|
|
367
370
|
const SUBCOMMAND_HELP = {
|
|
@@ -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 bluebubbles context-smoke [--agent <name>] --message-guid <guid> [--persist] [--json]",
|
|
117
118
|
" ouro rsvp <doctor|incident|cutover|legacy-render|replay|config|habit|import-legacy|refresh|compare|smoke> ...",
|
|
118
119
|
" ouro friend list [--agent <name>]",
|
|
119
120
|
" ouro friend show <id> [--agent <name>]",
|
|
@@ -1643,12 +1644,13 @@ function parseMigrateToDeskCommand(args) {
|
|
|
1643
1644
|
}
|
|
1644
1645
|
function parseBlueBubblesCommand(args) {
|
|
1645
1646
|
const subcommand = args[0];
|
|
1646
|
-
if (subcommand !== "replay") {
|
|
1647
|
+
if (subcommand !== "replay" && subcommand !== "context-smoke") {
|
|
1647
1648
|
throw new Error(`Usage\n${usage()}`);
|
|
1648
1649
|
}
|
|
1649
1650
|
let agent;
|
|
1650
1651
|
let messageGuid;
|
|
1651
1652
|
let eventType = "new-message";
|
|
1653
|
+
let persist = false;
|
|
1652
1654
|
let json = false;
|
|
1653
1655
|
for (let i = 1; i < args.length; i += 1) {
|
|
1654
1656
|
if (args[i] === "--agent" && args[i + 1]) {
|
|
@@ -1667,13 +1669,26 @@ function parseBlueBubblesCommand(args) {
|
|
|
1667
1669
|
eventType = candidate;
|
|
1668
1670
|
continue;
|
|
1669
1671
|
}
|
|
1672
|
+
if (args[i] === "--persist") {
|
|
1673
|
+
persist = true;
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1670
1676
|
if (args[i] === "--json") {
|
|
1671
1677
|
json = true;
|
|
1672
1678
|
continue;
|
|
1673
1679
|
}
|
|
1674
1680
|
}
|
|
1675
1681
|
if (!messageGuid)
|
|
1676
|
-
throw new Error(
|
|
1682
|
+
throw new Error(`bluebubbles ${subcommand} requires --message-guid <guid>`);
|
|
1683
|
+
if (subcommand === "context-smoke") {
|
|
1684
|
+
return {
|
|
1685
|
+
kind: "bluebubbles.context-smoke",
|
|
1686
|
+
...(agent ? { agent } : {}),
|
|
1687
|
+
messageGuid,
|
|
1688
|
+
...(persist ? { persist: true } : {}),
|
|
1689
|
+
...(json ? { json: true } : {}),
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1677
1692
|
return {
|
|
1678
1693
|
kind: "bluebubbles.replay",
|
|
1679
1694
|
...(agent ? { agent } : {}),
|
|
@@ -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!
|
|
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
|
}
|
package/dist/rsvp/habit-stage.js
CHANGED
|
@@ -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
|
|
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
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS = exports.BLUEBUBBLES_CONTEXT_PACKET_LIMIT = void 0;
|
|
4
|
+
exports.blueBubblesContextChatKey = blueBubblesContextChatKey;
|
|
5
|
+
exports.blueBubblesContextChatKeyHash = blueBubblesContextChatKeyHash;
|
|
6
|
+
exports.buildBlueBubblesContextPacket = buildBlueBubblesContextPacket;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
9
|
+
const context_packets_1 = require("../context-packets");
|
|
10
|
+
exports.BLUEBUBBLES_CONTEXT_PACKET_LIMIT = 40;
|
|
11
|
+
exports.BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS = 48 * 60 * 60 * 1000;
|
|
12
|
+
function sha256Hex(input) {
|
|
13
|
+
return (0, node_crypto_1.createHash)("sha256").update(input).digest("hex");
|
|
14
|
+
}
|
|
15
|
+
function blueBubblesContextChatKey(event) {
|
|
16
|
+
return event.chat.chatGuid ?? event.chat.chatIdentifier ?? event.chat.sessionKey;
|
|
17
|
+
}
|
|
18
|
+
function blueBubblesContextChatKeyHash(event) {
|
|
19
|
+
return sha256Hex(blueBubblesContextChatKey(event));
|
|
20
|
+
}
|
|
21
|
+
function isSameBlueBubblesChat(candidate, anchor) {
|
|
22
|
+
return candidate.chat.sessionKey === anchor.chat.sessionKey
|
|
23
|
+
|| (!!candidate.chat.chatGuid && candidate.chat.chatGuid === anchor.chat.chatGuid)
|
|
24
|
+
|| (!!candidate.chat.chatIdentifier && candidate.chat.chatIdentifier === anchor.chat.chatIdentifier);
|
|
25
|
+
}
|
|
26
|
+
function contextAuthorLabel(event) {
|
|
27
|
+
if (event.fromMe)
|
|
28
|
+
return "Slugger";
|
|
29
|
+
return event.sender.displayName || event.sender.externalId || "Unknown";
|
|
30
|
+
}
|
|
31
|
+
function messageToContextInput(event, chatGuidHash) {
|
|
32
|
+
return {
|
|
33
|
+
timestamp: new Date(event.timestamp).toISOString(),
|
|
34
|
+
authorLabel: contextAuthorLabel(event),
|
|
35
|
+
body: event.textForAgent || event.text,
|
|
36
|
+
sourceRef: {
|
|
37
|
+
sense: "bluebubbles",
|
|
38
|
+
adapter: "bluebubbles-api-v1",
|
|
39
|
+
service: "imessage",
|
|
40
|
+
chatGuid: event.chat.chatGuid,
|
|
41
|
+
chatGuidHash,
|
|
42
|
+
messageGuid: event.messageGuid,
|
|
43
|
+
senderExternalIdHash: sha256Hex(event.sender.externalId || event.sender.rawId || "unknown"),
|
|
44
|
+
observedAt: new Date(event.timestamp).toISOString(),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async function buildBlueBubblesContextPacket(input) {
|
|
49
|
+
if (!Number.isFinite(input.event.timestamp))
|
|
50
|
+
return null;
|
|
51
|
+
if (!input.client.listRecentMessages)
|
|
52
|
+
return null;
|
|
53
|
+
const event = input.event;
|
|
54
|
+
const chatGuidHash = blueBubblesContextChatKeyHash(event);
|
|
55
|
+
const anchorTimestamp = new Date(event.timestamp).toISOString();
|
|
56
|
+
const candidates = await input.client.listRecentMessages({
|
|
57
|
+
beforeTimestamp: event.timestamp,
|
|
58
|
+
limit: exports.BLUEBUBBLES_CONTEXT_PACKET_LIMIT,
|
|
59
|
+
offset: 0,
|
|
60
|
+
...(event.chat.chatGuid ? { chatGuid: event.chat.chatGuid } : {}),
|
|
61
|
+
...(!event.chat.chatGuid && event.chat.chatIdentifier ? { chatIdentifier: event.chat.chatIdentifier } : {}),
|
|
62
|
+
});
|
|
63
|
+
const history = candidates
|
|
64
|
+
.filter((candidate) => candidate.kind === "message")
|
|
65
|
+
.filter((candidate) => isSameBlueBubblesChat(candidate, event))
|
|
66
|
+
.filter((candidate) => candidate.messageGuid !== event.messageGuid)
|
|
67
|
+
.filter((candidate) => candidate.timestamp <= event.timestamp)
|
|
68
|
+
.filter((candidate) => event.timestamp - candidate.timestamp <= exports.BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS);
|
|
69
|
+
if (history.length === 0)
|
|
70
|
+
return null;
|
|
71
|
+
const packet = (0, context_packets_1.buildSenseContextPacket)({
|
|
72
|
+
agent: input.agentName,
|
|
73
|
+
sense: "bluebubbles",
|
|
74
|
+
sessionKey: event.chat.sessionKey,
|
|
75
|
+
chatKeyHash: chatGuidHash,
|
|
76
|
+
anchorMessageGuid: event.messageGuid,
|
|
77
|
+
anchorTimestamp,
|
|
78
|
+
windowBeforeMessages: exports.BLUEBUBBLES_CONTEXT_PACKET_LIMIT,
|
|
79
|
+
windowBeforeMs: exports.BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS,
|
|
80
|
+
messages: history.map((candidate) => messageToContextInput(candidate, chatGuidHash)),
|
|
81
|
+
});
|
|
82
|
+
const rendered = (0, context_packets_1.renderSenseContextPacketForPrompt)(packet, {
|
|
83
|
+
redactionPatterns: [/password=[^\s]+/gi],
|
|
84
|
+
});
|
|
85
|
+
(0, runtime_1.emitNervesEvent)({
|
|
86
|
+
component: "senses",
|
|
87
|
+
event: "senses.bluebubbles_context_packet_built",
|
|
88
|
+
message: "built bluebubbles same-chat context packet",
|
|
89
|
+
meta: {
|
|
90
|
+
packetId: packet.packetId,
|
|
91
|
+
messageGuid: event.messageGuid,
|
|
92
|
+
contextMessages: history.length,
|
|
93
|
+
renderedMessages: rendered.stats.renderedMessages,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
packet,
|
|
98
|
+
rendered,
|
|
99
|
+
historyCount: history.length,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.smokeBlueBubblesContext = smokeBlueBubblesContext;
|
|
4
|
+
exports.formatBlueBubblesContextSmokeText = formatBlueBubblesContextSmokeText;
|
|
5
|
+
const machine_identity_1 = require("../../heart/machine-identity");
|
|
6
|
+
const identity_1 = require("../../heart/identity");
|
|
7
|
+
const runtime_credentials_1 = require("../../heart/runtime-credentials");
|
|
8
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
9
|
+
const context_packet_ledger_1 = require("../context-packet-ledger");
|
|
10
|
+
const client_1 = require("./client");
|
|
11
|
+
const context_packet_1 = require("./context-packet");
|
|
12
|
+
const model_1 = require("./model");
|
|
13
|
+
function requireMessageEvent(event, messageGuid) {
|
|
14
|
+
if (event.kind !== "message") {
|
|
15
|
+
throw new Error(`BlueBubbles context smoke requires a message event; ${messageGuid} repaired to ${event.kind}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function smokeResult(params, writeResult) {
|
|
19
|
+
return {
|
|
20
|
+
ok: true,
|
|
21
|
+
sideEffect: writeResult ? "private-runtime-ledger-write" : false,
|
|
22
|
+
...params,
|
|
23
|
+
...(writeResult ? {
|
|
24
|
+
ledgerPath: writeResult.ledgerPath,
|
|
25
|
+
packetPath: writeResult.packetPath,
|
|
26
|
+
receiptPath: writeResult.receiptPath,
|
|
27
|
+
} : {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function smokeBlueBubblesContext(params, deps = {}) {
|
|
31
|
+
const agentName = params.agentName.trim();
|
|
32
|
+
const messageGuid = params.messageGuid.trim();
|
|
33
|
+
if (!agentName)
|
|
34
|
+
throw new Error("bluebubbles context-smoke requires agentName");
|
|
35
|
+
if (!messageGuid)
|
|
36
|
+
throw new Error("bluebubbles context-smoke requires messageGuid");
|
|
37
|
+
const setSmokeAgentName = deps.setAgentName ?? identity_1.setAgentName;
|
|
38
|
+
const resetSmokeIdentity = deps.resetIdentity ?? identity_1.resetIdentity;
|
|
39
|
+
const normalizeEvent = deps.normalizeEvent ?? model_1.normalizeBlueBubblesEvent;
|
|
40
|
+
const loadMachineId = deps.loadMachineId ?? (() => (0, machine_identity_1.loadOrCreateMachineIdentity)().machineId);
|
|
41
|
+
const refreshMachineRuntimeConfig = deps.refreshMachineRuntimeConfig ?? runtime_credentials_1.refreshMachineRuntimeCredentialConfig;
|
|
42
|
+
const resolveAgentRoot = deps.getAgentRoot ?? identity_1.getAgentRoot;
|
|
43
|
+
const writePacket = deps.writePacket ?? context_packet_ledger_1.writeSenseContextPacket;
|
|
44
|
+
const createDefaultClient = deps.createDefaultClient ?? client_1.createBlueBubblesClient;
|
|
45
|
+
(0, runtime_1.emitNervesEvent)({
|
|
46
|
+
component: "senses",
|
|
47
|
+
event: "senses.bluebubbles_context_smoke_start",
|
|
48
|
+
message: "starting bluebubbles context smoke",
|
|
49
|
+
meta: {
|
|
50
|
+
agentName,
|
|
51
|
+
messageGuid,
|
|
52
|
+
persist: params.persist === true,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
setSmokeAgentName(agentName);
|
|
56
|
+
try {
|
|
57
|
+
if (!deps.createClient) {
|
|
58
|
+
const machineId = loadMachineId();
|
|
59
|
+
await refreshMachineRuntimeConfig(agentName, machineId, { preserveCachedOnFailure: true });
|
|
60
|
+
}
|
|
61
|
+
const client = deps.createClient ? deps.createClient() : createDefaultClient();
|
|
62
|
+
const probe = normalizeEvent({
|
|
63
|
+
type: "new-message",
|
|
64
|
+
data: {
|
|
65
|
+
guid: messageGuid,
|
|
66
|
+
hasPayloadData: true,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
const event = await client.repairEvent(probe);
|
|
70
|
+
requireMessageEvent(event, messageGuid);
|
|
71
|
+
const built = await (0, context_packet_1.buildBlueBubblesContextPacket)({
|
|
72
|
+
agentName,
|
|
73
|
+
client,
|
|
74
|
+
event,
|
|
75
|
+
});
|
|
76
|
+
if (!built) {
|
|
77
|
+
throw new Error(`BlueBubbles context smoke found no same-thread history before ${messageGuid}`);
|
|
78
|
+
}
|
|
79
|
+
const writeResult = params.persist
|
|
80
|
+
? writePacket(resolveAgentRoot(agentName), built.packet)
|
|
81
|
+
: undefined;
|
|
82
|
+
const result = smokeResult({
|
|
83
|
+
agentName,
|
|
84
|
+
messageGuid,
|
|
85
|
+
packetId: built.packet.packetId,
|
|
86
|
+
contextMessages: built.historyCount,
|
|
87
|
+
renderedMessages: built.rendered.stats.renderedMessages,
|
|
88
|
+
renderedCharacters: built.rendered.stats.outputCharacters,
|
|
89
|
+
omittedMessages: built.rendered.stats.omittedMessages,
|
|
90
|
+
truncatedMessages: built.rendered.stats.truncatedMessages,
|
|
91
|
+
}, writeResult);
|
|
92
|
+
(0, runtime_1.emitNervesEvent)({
|
|
93
|
+
component: "senses",
|
|
94
|
+
event: "senses.bluebubbles_context_smoke_end",
|
|
95
|
+
message: "completed bluebubbles context smoke",
|
|
96
|
+
meta: {
|
|
97
|
+
agentName,
|
|
98
|
+
messageGuid,
|
|
99
|
+
packetId: result.packetId,
|
|
100
|
+
contextMessages: result.contextMessages,
|
|
101
|
+
sideEffect: result.sideEffect,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
(0, runtime_1.emitNervesEvent)({
|
|
108
|
+
level: "warn",
|
|
109
|
+
component: "senses",
|
|
110
|
+
event: "senses.bluebubbles_context_smoke_error",
|
|
111
|
+
message: "bluebubbles context smoke failed",
|
|
112
|
+
meta: {
|
|
113
|
+
agentName,
|
|
114
|
+
messageGuid,
|
|
115
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
resetSmokeIdentity();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function formatBlueBubblesContextSmokeText(result) {
|
|
125
|
+
const lines = [
|
|
126
|
+
"bluebubbles context smoke passed",
|
|
127
|
+
`agent: ${result.agentName}`,
|
|
128
|
+
`message_guid: ${result.messageGuid}`,
|
|
129
|
+
`context_messages: ${result.contextMessages}`,
|
|
130
|
+
`packet_id: ${result.packetId}`,
|
|
131
|
+
`rendered_messages: ${result.renderedMessages}`,
|
|
132
|
+
`rendered_characters: ${result.renderedCharacters}`,
|
|
133
|
+
`truncated_messages: ${result.truncatedMessages}`,
|
|
134
|
+
`omitted_messages: ${result.omittedMessages}`,
|
|
135
|
+
`side_effect: ${result.sideEffect}`,
|
|
136
|
+
];
|
|
137
|
+
if (result.ledgerPath)
|
|
138
|
+
lines.push(`ledger_path: ${result.ledgerPath}`);
|
|
139
|
+
if (result.packetPath)
|
|
140
|
+
lines.push(`packet_path: ${result.packetPath}`);
|
|
141
|
+
if (result.receiptPath)
|
|
142
|
+
lines.push(`receipt_path: ${result.receiptPath}`);
|
|
143
|
+
return lines.join("\n");
|
|
144
|
+
}
|
|
@@ -86,6 +86,7 @@ const trust_gate_1 = require("../trust-gate");
|
|
|
86
86
|
const pipeline_1 = require("../pipeline");
|
|
87
87
|
const context_packets_1 = require("../context-packets");
|
|
88
88
|
const context_packet_ledger_1 = require("../context-packet-ledger");
|
|
89
|
+
const context_packet_1 = require("./context-packet");
|
|
89
90
|
const autonomy_budget_1 = require("../../heart/autonomy-budget");
|
|
90
91
|
const bbFailoverStates = new Map();
|
|
91
92
|
/**
|
|
@@ -263,8 +264,6 @@ const BLUEBUBBLES_LIVE_TURN_STALLED_MS = 90_000;
|
|
|
263
264
|
const BLUEBUBBLES_SILENCE_WATCHDOG_MS = 75_000;
|
|
264
265
|
const BLUEBUBBLES_CALLBACK_CLEANUP_TIMEOUT_MS = 2_000;
|
|
265
266
|
const BLUEBUBBLES_ACTIVITY_OPERATION_TIMEOUT_MS = 20_000;
|
|
266
|
-
const BLUEBUBBLES_CONTEXT_PACKET_LIMIT = 40;
|
|
267
|
-
const BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS = 48 * 60 * 60 * 1000;
|
|
268
267
|
const BLUEBUBBLES_CATCHUP_PAGE_SIZE = 50;
|
|
269
268
|
const BLUEBUBBLES_CATCHUP_MAX_PAGES = 20;
|
|
270
269
|
const BLUEBUBBLES_HEALTHY_CATCHUP_OVERLAP_MS = 90_000;
|
|
@@ -614,36 +613,6 @@ function recordBlueBubblesRecoveryFailureForBudget(agentName, event, source) {
|
|
|
614
613
|
// Autonomy failure accounting must not turn a recovery error into a broader sense outage.
|
|
615
614
|
}
|
|
616
615
|
}
|
|
617
|
-
function chatKeyForContext(event) {
|
|
618
|
-
return event.chat.chatGuid ?? event.chat.chatIdentifier ?? event.chat.sessionKey;
|
|
619
|
-
}
|
|
620
|
-
function isSameBlueBubblesChat(candidate, anchor) {
|
|
621
|
-
return candidate.chat.sessionKey === anchor.chat.sessionKey
|
|
622
|
-
|| (!!candidate.chat.chatGuid && candidate.chat.chatGuid === anchor.chat.chatGuid)
|
|
623
|
-
|| (!!candidate.chat.chatIdentifier && candidate.chat.chatIdentifier === anchor.chat.chatIdentifier);
|
|
624
|
-
}
|
|
625
|
-
function contextAuthorLabel(event) {
|
|
626
|
-
if (event.fromMe)
|
|
627
|
-
return "Slugger";
|
|
628
|
-
return event.sender.displayName || event.sender.externalId || "Unknown";
|
|
629
|
-
}
|
|
630
|
-
function messageToContextInput(event, chatGuidHash) {
|
|
631
|
-
return {
|
|
632
|
-
timestamp: new Date(event.timestamp).toISOString(),
|
|
633
|
-
authorLabel: contextAuthorLabel(event),
|
|
634
|
-
body: event.textForAgent || event.text,
|
|
635
|
-
sourceRef: {
|
|
636
|
-
sense: "bluebubbles",
|
|
637
|
-
adapter: "bluebubbles-api-v1",
|
|
638
|
-
service: "imessage",
|
|
639
|
-
chatGuid: event.chat.chatGuid,
|
|
640
|
-
chatGuidHash,
|
|
641
|
-
messageGuid: event.messageGuid,
|
|
642
|
-
senderExternalIdHash: sha256Hex(event.sender.externalId || event.sender.rawId || "unknown"),
|
|
643
|
-
observedAt: new Date(event.timestamp).toISOString(),
|
|
644
|
-
},
|
|
645
|
-
};
|
|
646
|
-
}
|
|
647
616
|
function insertEphemeralContextMessages(messages, contextMessages) {
|
|
648
617
|
if (contextMessages.length === 0)
|
|
649
618
|
return messages;
|
|
@@ -660,43 +629,18 @@ async function buildBlueBubblesContextMessages(input) {
|
|
|
660
629
|
const event = input.event;
|
|
661
630
|
if (!Number.isFinite(event.timestamp))
|
|
662
631
|
return [];
|
|
663
|
-
const
|
|
664
|
-
const chatGuidHash = sha256Hex(chatKey);
|
|
632
|
+
const chatGuidHash = (0, context_packet_1.blueBubblesContextChatKeyHash)(event);
|
|
665
633
|
const anchorTimestamp = new Date(event.timestamp).toISOString();
|
|
666
634
|
try {
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
...(event.chat.chatGuid ? { chatGuid: event.chat.chatGuid } : {}),
|
|
672
|
-
...(!event.chat.chatGuid && event.chat.chatIdentifier ? { chatIdentifier: event.chat.chatIdentifier } : {}),
|
|
673
|
-
};
|
|
674
|
-
const candidates = input.client.listRecentMessages
|
|
675
|
-
? await input.client.listRecentMessages(query)
|
|
676
|
-
: [];
|
|
677
|
-
const history = candidates
|
|
678
|
-
.filter((candidate) => candidate.kind === "message")
|
|
679
|
-
.filter((candidate) => isSameBlueBubblesChat(candidate, event))
|
|
680
|
-
.filter((candidate) => candidate.messageGuid !== event.messageGuid)
|
|
681
|
-
.filter((candidate) => candidate.timestamp <= event.timestamp)
|
|
682
|
-
.filter((candidate) => event.timestamp - candidate.timestamp <= BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS);
|
|
683
|
-
if (history.length === 0)
|
|
684
|
-
return [];
|
|
685
|
-
const packet = (0, context_packets_1.buildSenseContextPacket)({
|
|
686
|
-
agent: input.agentName,
|
|
687
|
-
sense: "bluebubbles",
|
|
688
|
-
sessionKey: event.chat.sessionKey,
|
|
689
|
-
chatKeyHash: chatGuidHash,
|
|
690
|
-
anchorMessageGuid: event.messageGuid,
|
|
691
|
-
anchorTimestamp,
|
|
692
|
-
windowBeforeMessages: BLUEBUBBLES_CONTEXT_PACKET_LIMIT,
|
|
693
|
-
windowBeforeMs: BLUEBUBBLES_CONTEXT_PACKET_MAX_AGE_MS,
|
|
694
|
-
messages: history.map((candidate) => messageToContextInput(candidate, chatGuidHash)),
|
|
635
|
+
const result = await (0, context_packet_1.buildBlueBubblesContextPacket)({
|
|
636
|
+
agentName: input.agentName,
|
|
637
|
+
client: input.client,
|
|
638
|
+
event,
|
|
695
639
|
});
|
|
640
|
+
if (!result)
|
|
641
|
+
return [];
|
|
642
|
+
const { packet, rendered, historyCount } = result;
|
|
696
643
|
(0, context_packet_ledger_1.writeSenseContextPacket)(input.agentRoot, packet);
|
|
697
|
-
const rendered = (0, context_packets_1.renderSenseContextPacketForPrompt)(packet, {
|
|
698
|
-
redactionPatterns: [/password=[^\s]+/gi],
|
|
699
|
-
});
|
|
700
644
|
(0, runtime_1.emitNervesEvent)({
|
|
701
645
|
component: "senses",
|
|
702
646
|
event: "senses.bluebubbles_context_packet_injected",
|
|
@@ -704,7 +648,7 @@ async function buildBlueBubblesContextMessages(input) {
|
|
|
704
648
|
meta: {
|
|
705
649
|
packetId: packet.packetId,
|
|
706
650
|
messageGuid: event.messageGuid,
|
|
707
|
-
contextMessages:
|
|
651
|
+
contextMessages: historyCount,
|
|
708
652
|
},
|
|
709
653
|
});
|
|
710
654
|
return [{ role: "system", content: rendered.text }];
|
|
@@ -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
|
-
|
|
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 = {
|