@ouro.bot/cli 0.1.0-alpha.696 → 0.1.0-alpha.698
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 +18 -0
- package/dist/heart/agent-entry.js +32 -2
- 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-entry.js +104 -3
- 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/provider-credentials.js +16 -0
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-credentials.js +12 -3
- package/dist/mind/prompt-budget.js +380 -0
- package/dist/repertoire/tools-base.js +2 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +704 -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 +161 -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 +91 -1
- 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,254 @@
|
|
|
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_OUTBOUND_STATE_POLICY_VERSION = void 0;
|
|
37
|
+
exports.readRsvpOutboundState = readRsvpOutboundState;
|
|
38
|
+
exports.writeRsvpBaseline = writeRsvpBaseline;
|
|
39
|
+
exports.decideRsvpOutboundReport = decideRsvpOutboundReport;
|
|
40
|
+
exports.recordRsvpOutboundAttempt = recordRsvpOutboundAttempt;
|
|
41
|
+
const fs = __importStar(require("node:fs"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const node_crypto_1 = require("node:crypto");
|
|
44
|
+
const runtime_1 = require("../nerves/runtime");
|
|
45
|
+
exports.RSVP_OUTBOUND_STATE_POLICY_VERSION = "rsvp-outbound-state/v1";
|
|
46
|
+
function sha256Hex(input) {
|
|
47
|
+
return (0, node_crypto_1.createHash)("sha256").update(input).digest("hex");
|
|
48
|
+
}
|
|
49
|
+
function hashString(input) {
|
|
50
|
+
return `sha256:${sha256Hex(input)}`;
|
|
51
|
+
}
|
|
52
|
+
function rsvpOutboundStatePath(agentRoot) {
|
|
53
|
+
return path.join(agentRoot, "state", "rsvp", "outbound-state.json");
|
|
54
|
+
}
|
|
55
|
+
function emptyState(now) {
|
|
56
|
+
return {
|
|
57
|
+
schemaVersion: 1,
|
|
58
|
+
policyVersion: exports.RSVP_OUTBOUND_STATE_POLICY_VERSION,
|
|
59
|
+
updatedAt: now,
|
|
60
|
+
pendingReports: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function nowIso(input) {
|
|
64
|
+
return input ?? new Date().toISOString();
|
|
65
|
+
}
|
|
66
|
+
function reportHash(reportText) {
|
|
67
|
+
return hashString(reportText.trim().replace(/\s+/g, " "));
|
|
68
|
+
}
|
|
69
|
+
function idempotencyKeyFor(snapshot, reportText) {
|
|
70
|
+
return [
|
|
71
|
+
"rsvp-report",
|
|
72
|
+
snapshot.agent,
|
|
73
|
+
snapshot.source.weddingId,
|
|
74
|
+
snapshot.source.eventId,
|
|
75
|
+
snapshot.snapshotId,
|
|
76
|
+
reportHash(reportText).slice("sha256:".length, "sha256:".length + 16),
|
|
77
|
+
].join(":");
|
|
78
|
+
}
|
|
79
|
+
function isBaseline(value) {
|
|
80
|
+
const row = value;
|
|
81
|
+
return !!row
|
|
82
|
+
&& typeof row.snapshotId === "string"
|
|
83
|
+
&& typeof row.contentHash === "string"
|
|
84
|
+
&& typeof row.recordedAt === "string"
|
|
85
|
+
&& typeof row.reason === "string";
|
|
86
|
+
}
|
|
87
|
+
function isPending(value) {
|
|
88
|
+
const row = value;
|
|
89
|
+
return !!row
|
|
90
|
+
&& typeof row.snapshotId === "string"
|
|
91
|
+
&& typeof row.contentHash === "string"
|
|
92
|
+
&& typeof row.reportHash === "string"
|
|
93
|
+
&& typeof row.reportLength === "number"
|
|
94
|
+
&& typeof row.idempotencyKey === "string"
|
|
95
|
+
&& typeof row.bluebubblesRecordId === "string"
|
|
96
|
+
&& typeof row.status === "string"
|
|
97
|
+
&& typeof row.recordedAt === "string"
|
|
98
|
+
&& typeof row.updatedAt === "string"
|
|
99
|
+
&& row.contentStored === false;
|
|
100
|
+
}
|
|
101
|
+
function isState(value) {
|
|
102
|
+
const row = value;
|
|
103
|
+
return !!row
|
|
104
|
+
&& row.schemaVersion === 1
|
|
105
|
+
&& row.policyVersion === exports.RSVP_OUTBOUND_STATE_POLICY_VERSION
|
|
106
|
+
&& typeof row.updatedAt === "string"
|
|
107
|
+
&& (row.baseline === undefined || isBaseline(row.baseline))
|
|
108
|
+
&& Array.isArray(row.pendingReports);
|
|
109
|
+
}
|
|
110
|
+
function readRsvpOutboundState(agentRoot) {
|
|
111
|
+
const filePath = rsvpOutboundStatePath(agentRoot);
|
|
112
|
+
if (!fs.existsSync(filePath))
|
|
113
|
+
return emptyState(new Date().toISOString());
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
116
|
+
if (!isState(parsed))
|
|
117
|
+
return emptyState(new Date().toISOString());
|
|
118
|
+
return {
|
|
119
|
+
schemaVersion: 1,
|
|
120
|
+
policyVersion: exports.RSVP_OUTBOUND_STATE_POLICY_VERSION,
|
|
121
|
+
updatedAt: parsed.updatedAt,
|
|
122
|
+
...(parsed.baseline ? { baseline: parsed.baseline } : {}),
|
|
123
|
+
pendingReports: parsed.pendingReports.filter(isPending),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return emptyState(new Date().toISOString());
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function writeState(agentRoot, state) {
|
|
131
|
+
const filePath = rsvpOutboundStatePath(agentRoot);
|
|
132
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
133
|
+
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
134
|
+
fs.writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
|
|
135
|
+
fs.renameSync(tmp, filePath);
|
|
136
|
+
return state;
|
|
137
|
+
}
|
|
138
|
+
function writeRsvpBaseline(input) {
|
|
139
|
+
const current = readRsvpOutboundState(input.agentRoot);
|
|
140
|
+
const state = writeState(input.agentRoot, {
|
|
141
|
+
schemaVersion: 1,
|
|
142
|
+
policyVersion: exports.RSVP_OUTBOUND_STATE_POLICY_VERSION,
|
|
143
|
+
updatedAt: input.recordedAt,
|
|
144
|
+
baseline: {
|
|
145
|
+
snapshotId: input.snapshot.snapshotId,
|
|
146
|
+
contentHash: input.snapshot.contentHash,
|
|
147
|
+
recordedAt: input.recordedAt,
|
|
148
|
+
reason: input.reason,
|
|
149
|
+
},
|
|
150
|
+
pendingReports: current.pendingReports.filter((entry) => entry.snapshotId !== input.snapshot.snapshotId),
|
|
151
|
+
});
|
|
152
|
+
(0, runtime_1.emitNervesEvent)({
|
|
153
|
+
component: "rsvp",
|
|
154
|
+
event: "rsvp.outbound_baseline_written",
|
|
155
|
+
message: "wrote RSVP outbound baseline",
|
|
156
|
+
meta: { snapshotId: input.snapshot.snapshotId, reason: input.reason },
|
|
157
|
+
});
|
|
158
|
+
return state;
|
|
159
|
+
}
|
|
160
|
+
function decideRsvpOutboundReport(input) {
|
|
161
|
+
const state = readRsvpOutboundState(input.agentRoot);
|
|
162
|
+
const idempotencyKey = idempotencyKeyFor(input.currentSnapshot, input.reportText);
|
|
163
|
+
const currentSnapshotId = input.currentSnapshot.snapshotId;
|
|
164
|
+
const report = reportHash(input.reportText);
|
|
165
|
+
if (state.baseline?.snapshotId === currentSnapshotId
|
|
166
|
+
&& state.baseline.contentHash === input.currentSnapshot.contentHash) {
|
|
167
|
+
(0, runtime_1.emitNervesEvent)({
|
|
168
|
+
component: "rsvp",
|
|
169
|
+
event: "rsvp.outbound_decision",
|
|
170
|
+
message: "decided RSVP report outbound action",
|
|
171
|
+
meta: { action: "skip", snapshotId: currentSnapshotId },
|
|
172
|
+
});
|
|
173
|
+
return {
|
|
174
|
+
action: "skip",
|
|
175
|
+
reason: "baseline-current",
|
|
176
|
+
currentSnapshotId,
|
|
177
|
+
idempotencyKey,
|
|
178
|
+
reportText: input.reportText,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const existingPending = state.pendingReports.find((entry) => entry.snapshotId === currentSnapshotId && entry.reportHash === report);
|
|
182
|
+
(0, runtime_1.emitNervesEvent)({
|
|
183
|
+
component: "rsvp",
|
|
184
|
+
event: "rsvp.outbound_decision",
|
|
185
|
+
message: "decided RSVP report outbound action",
|
|
186
|
+
meta: { action: "send", snapshotId: currentSnapshotId, existingPending: Boolean(existingPending), now: nowIso(input.now) },
|
|
187
|
+
});
|
|
188
|
+
return {
|
|
189
|
+
action: "send",
|
|
190
|
+
currentSnapshotId,
|
|
191
|
+
idempotencyKey,
|
|
192
|
+
reportText: input.reportText,
|
|
193
|
+
...(existingPending ? { existingPending } : {}),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function isAdvanceStatus(status) {
|
|
197
|
+
return status === "accepted" || status === "enqueued" || status === "local-visible" || status === "delivered";
|
|
198
|
+
}
|
|
199
|
+
function pendingReportFor(input) {
|
|
200
|
+
const normalized = input.reportText.trim().replace(/\s+/g, " ");
|
|
201
|
+
return {
|
|
202
|
+
snapshotId: input.currentSnapshot.snapshotId,
|
|
203
|
+
contentHash: input.currentSnapshot.contentHash,
|
|
204
|
+
reportHash: reportHash(input.reportText),
|
|
205
|
+
reportLength: normalized.length,
|
|
206
|
+
idempotencyKey: idempotencyKeyFor(input.currentSnapshot, input.reportText),
|
|
207
|
+
bluebubblesRecordId: input.bluebubblesRecord.recordId,
|
|
208
|
+
status: input.bluebubblesRecord.status,
|
|
209
|
+
...(input.bluebubblesRecord.tempGuid?.trim() ? { tempGuid: input.bluebubblesRecord.tempGuid.trim() } : {}),
|
|
210
|
+
...(input.bluebubblesRecord.messageGuid?.trim() ? { messageGuid: input.bluebubblesRecord.messageGuid.trim() } : {}),
|
|
211
|
+
recordedAt: input.recordedAt,
|
|
212
|
+
updatedAt: input.recordedAt,
|
|
213
|
+
contentStored: false,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function recordRsvpOutboundAttempt(input) {
|
|
217
|
+
const current = readRsvpOutboundState(input.agentRoot);
|
|
218
|
+
const pending = pendingReportFor(input);
|
|
219
|
+
const remainingPending = current.pendingReports.filter((entry) => entry.snapshotId !== pending.snapshotId || entry.reportHash !== pending.reportHash);
|
|
220
|
+
const state = isAdvanceStatus(input.bluebubblesRecord.status)
|
|
221
|
+
? writeState(input.agentRoot, {
|
|
222
|
+
schemaVersion: 1,
|
|
223
|
+
policyVersion: exports.RSVP_OUTBOUND_STATE_POLICY_VERSION,
|
|
224
|
+
updatedAt: input.recordedAt,
|
|
225
|
+
baseline: {
|
|
226
|
+
snapshotId: input.currentSnapshot.snapshotId,
|
|
227
|
+
contentHash: input.currentSnapshot.contentHash,
|
|
228
|
+
recordedAt: input.recordedAt,
|
|
229
|
+
reason: "bluebubbles-outbound-accepted",
|
|
230
|
+
bluebubblesRecordId: input.bluebubblesRecord.recordId,
|
|
231
|
+
advancedBy: input.bluebubblesRecord.status,
|
|
232
|
+
},
|
|
233
|
+
pendingReports: remainingPending,
|
|
234
|
+
})
|
|
235
|
+
: writeState(input.agentRoot, {
|
|
236
|
+
schemaVersion: 1,
|
|
237
|
+
policyVersion: exports.RSVP_OUTBOUND_STATE_POLICY_VERSION,
|
|
238
|
+
updatedAt: input.recordedAt,
|
|
239
|
+
...(current.baseline ? { baseline: current.baseline } : {}),
|
|
240
|
+
pendingReports: [...remainingPending, pending],
|
|
241
|
+
});
|
|
242
|
+
(0, runtime_1.emitNervesEvent)({
|
|
243
|
+
component: "rsvp",
|
|
244
|
+
event: "rsvp.outbound_attempt_recorded",
|
|
245
|
+
message: "recorded RSVP outbound attempt",
|
|
246
|
+
meta: {
|
|
247
|
+
snapshotId: input.currentSnapshot.snapshotId,
|
|
248
|
+
bluebubblesRecordId: input.bluebubblesRecord.recordId,
|
|
249
|
+
status: input.bluebubblesRecord.status,
|
|
250
|
+
baselineAdvanced: isAdvanceStatus(input.bluebubblesRecord.status),
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
return state;
|
|
254
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.queryRsvpSnapshot = queryRsvpSnapshot;
|
|
4
|
+
exports.summarizeRsvpSnapshot = summarizeRsvpSnapshot;
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
const diff_renderer_1 = require("./diff-renderer");
|
|
7
|
+
const STATUS_LABELS = {
|
|
8
|
+
attending: "Attending",
|
|
9
|
+
declined: "Declined",
|
|
10
|
+
pending: "Pending",
|
|
11
|
+
unknown: "Unknown",
|
|
12
|
+
};
|
|
13
|
+
const STATUS_ALIASES = [
|
|
14
|
+
["pending", /\b(pending|not\s+responded|not\s+replied|no\s+response|waiting)\b/i],
|
|
15
|
+
["declined", /\b(declined|declines|not\s+coming|can't\s+come|cannot\s+come|no\b)\b/i],
|
|
16
|
+
["attending", /\b(attending|coming|yes|accepted|rsvp'?d\s+yes)\b/i],
|
|
17
|
+
["unknown", /\b(unknown|weird|invalid|unclear)\b/i],
|
|
18
|
+
];
|
|
19
|
+
function normalizeStatus(value) {
|
|
20
|
+
if (!value)
|
|
21
|
+
return null;
|
|
22
|
+
const normalized = value.trim().toLowerCase();
|
|
23
|
+
if (normalized === "attending" || normalized === "declined" || normalized === "pending" || normalized === "unknown" || normalized === "all") {
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function statusFromQuery(query) {
|
|
29
|
+
if (!query)
|
|
30
|
+
return null;
|
|
31
|
+
for (const [status, pattern] of STATUS_ALIASES) {
|
|
32
|
+
if (pattern.test(query))
|
|
33
|
+
return status;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function searchableName(guest, displayName) {
|
|
38
|
+
return `${displayName} ${guest.firstName} ${guest.lastName}`.toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
function formatStatusResult(status, names, total) {
|
|
41
|
+
return `${STATUS_LABELS[status]} (${names.length}/${total}): ${names.length > 0 ? names.join("; ") : "none"}`;
|
|
42
|
+
}
|
|
43
|
+
function formatNameResult(matches, total) {
|
|
44
|
+
const names = matches.map(({ guest, displayName }) => `${displayName} [${guest.status}]`);
|
|
45
|
+
return `Matching guests (${matches.length}/${total}): ${names.length > 0 ? names.join("; ") : "none"}`;
|
|
46
|
+
}
|
|
47
|
+
function sortedGuests(snapshot) {
|
|
48
|
+
return [...snapshot.guests].sort((left, right) => left.id.localeCompare(right.id));
|
|
49
|
+
}
|
|
50
|
+
function queryRsvpSnapshot(snapshot, query) {
|
|
51
|
+
const guests = sortedGuests(snapshot);
|
|
52
|
+
const requestedStatus = normalizeStatus(query.status);
|
|
53
|
+
const inferredStatus = statusFromQuery(query.query);
|
|
54
|
+
const status = requestedStatus ?? inferredStatus ?? "all";
|
|
55
|
+
if (status !== "all") {
|
|
56
|
+
const matches = guests
|
|
57
|
+
.filter((guest) => guest.status === status)
|
|
58
|
+
.map((guest) => (0, diff_renderer_1.guestDisplayName)(guest, guests));
|
|
59
|
+
(0, runtime_1.emitNervesEvent)({
|
|
60
|
+
component: "rsvp",
|
|
61
|
+
event: "rsvp.query_answered",
|
|
62
|
+
message: "answered RSVP status query",
|
|
63
|
+
meta: {
|
|
64
|
+
snapshotId: snapshot.snapshotId,
|
|
65
|
+
status,
|
|
66
|
+
count: matches.length,
|
|
67
|
+
total: snapshot.summary.total,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
status,
|
|
73
|
+
count: matches.length,
|
|
74
|
+
total: snapshot.summary.total,
|
|
75
|
+
names: matches,
|
|
76
|
+
text: formatStatusResult(status, matches, snapshot.summary.total),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const needle = query.query?.trim().toLowerCase() ?? "";
|
|
80
|
+
const matches = guests
|
|
81
|
+
.map((guest) => ({ guest, displayName: (0, diff_renderer_1.guestDisplayName)(guest, guests) }))
|
|
82
|
+
.filter(({ guest, displayName }) => needle.length === 0 || searchableName(guest, displayName).includes(needle));
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "rsvp",
|
|
85
|
+
event: "rsvp.query_answered",
|
|
86
|
+
message: "answered RSVP name query",
|
|
87
|
+
meta: {
|
|
88
|
+
snapshotId: snapshot.snapshotId,
|
|
89
|
+
status: "all",
|
|
90
|
+
count: matches.length,
|
|
91
|
+
total: snapshot.summary.total,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
status: "all",
|
|
97
|
+
count: matches.length,
|
|
98
|
+
total: snapshot.summary.total,
|
|
99
|
+
names: matches.map(({ displayName }) => displayName),
|
|
100
|
+
text: formatNameResult(matches, snapshot.summary.total),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function summaryLine(snapshot) {
|
|
104
|
+
const { attending, declined, pending, unknown, total } = snapshot.summary;
|
|
105
|
+
return `${attending} attending / ${declined} declined / ${pending} pending / ${unknown} unknown (${total} total)`;
|
|
106
|
+
}
|
|
107
|
+
function summarizeRsvpSnapshot(snapshot) {
|
|
108
|
+
(0, runtime_1.emitNervesEvent)({
|
|
109
|
+
component: "rsvp",
|
|
110
|
+
event: "rsvp.snapshot_summarized",
|
|
111
|
+
message: "summarized RSVP snapshot",
|
|
112
|
+
meta: {
|
|
113
|
+
snapshotId: snapshot.snapshotId,
|
|
114
|
+
total: snapshot.summary.total,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
return `RSVP snapshot ${snapshot.snapshotId}: ${summaryLine(snapshot)}, fetched ${snapshot.fetchedAt}`;
|
|
118
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
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_REPLAY_RESULT_POLICY_VERSION = exports.RSVP_REPLAY_POLICY_VERSION = void 0;
|
|
37
|
+
exports.replayRsvpFixture = replayRsvpFixture;
|
|
38
|
+
exports.renderLegacyRsvpSnapshotOffline = renderLegacyRsvpSnapshotOffline;
|
|
39
|
+
exports.replayJuly9PendingAnswerFixture = replayJuly9PendingAnswerFixture;
|
|
40
|
+
const crypto = __importStar(require("node:crypto"));
|
|
41
|
+
const fs = __importStar(require("node:fs"));
|
|
42
|
+
const path = __importStar(require("node:path"));
|
|
43
|
+
const runtime_1 = require("../nerves/runtime");
|
|
44
|
+
exports.RSVP_REPLAY_POLICY_VERSION = "rsvp-replay/v1";
|
|
45
|
+
exports.RSVP_REPLAY_RESULT_POLICY_VERSION = "rsvp-replay-result/v1";
|
|
46
|
+
function isRecord(value) {
|
|
47
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
48
|
+
}
|
|
49
|
+
function stringField(record, key) {
|
|
50
|
+
const value = record[key];
|
|
51
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
52
|
+
}
|
|
53
|
+
function stableJson(value) {
|
|
54
|
+
if (Array.isArray(value))
|
|
55
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
56
|
+
if (isRecord(value)) {
|
|
57
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
58
|
+
}
|
|
59
|
+
return JSON.stringify(value);
|
|
60
|
+
}
|
|
61
|
+
function sha256(input) {
|
|
62
|
+
return `sha256:${crypto.createHash("sha256").update(input).digest("hex")}`;
|
|
63
|
+
}
|
|
64
|
+
function readJsonFile(filePath) {
|
|
65
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
66
|
+
}
|
|
67
|
+
function fixtureExpected(fixture) {
|
|
68
|
+
const expected = isRecord(fixture.expected) ? fixture.expected : null;
|
|
69
|
+
const contextPacketHash = expected ? stringField(expected, "contextPacketHash") : null;
|
|
70
|
+
const modelInputHash = expected ? stringField(expected, "modelInputHash") : null;
|
|
71
|
+
if (!contextPacketHash || !modelInputHash)
|
|
72
|
+
throw new Error("RSVP replay fixture missing expected hashes");
|
|
73
|
+
return { contextPacketHash, modelInputHash };
|
|
74
|
+
}
|
|
75
|
+
function fixturePendingGuests(fixture) {
|
|
76
|
+
const snapshot = isRecord(fixture.snapshot) ? fixture.snapshot : null;
|
|
77
|
+
const raw = snapshot && Array.isArray(snapshot.pendingGuests) ? snapshot.pendingGuests : [];
|
|
78
|
+
return raw.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
79
|
+
}
|
|
80
|
+
function countsFrom(value) {
|
|
81
|
+
if (!isRecord(value))
|
|
82
|
+
throw new Error("RSVP counts missing");
|
|
83
|
+
const attending = value.attending;
|
|
84
|
+
const declined = value.declined;
|
|
85
|
+
const pending = value.pending;
|
|
86
|
+
if (typeof attending !== "number" || typeof declined !== "number" || typeof pending !== "number") {
|
|
87
|
+
throw new Error("RSVP counts must be numeric");
|
|
88
|
+
}
|
|
89
|
+
return { attending, declined, pending };
|
|
90
|
+
}
|
|
91
|
+
function july9PrivacyViolations(manifest) {
|
|
92
|
+
const privacy = isRecord(manifest.privacy) ? manifest.privacy : null;
|
|
93
|
+
if (!privacy)
|
|
94
|
+
return ["privacy"];
|
|
95
|
+
const violations = [];
|
|
96
|
+
if (privacy.rawLiveTranscriptStored !== false)
|
|
97
|
+
violations.push("rawLiveTranscriptStored");
|
|
98
|
+
if (privacy.credentialsStored !== false)
|
|
99
|
+
violations.push("credentialsStored");
|
|
100
|
+
if (privacy.searchIndex !== false)
|
|
101
|
+
violations.push("searchIndex");
|
|
102
|
+
if (privacy.vectorIndex !== false)
|
|
103
|
+
violations.push("vectorIndex");
|
|
104
|
+
return violations;
|
|
105
|
+
}
|
|
106
|
+
function assertJuly9ReplayPrivacy(manifest) {
|
|
107
|
+
const violations = july9PrivacyViolations(manifest);
|
|
108
|
+
if (violations.length > 0) {
|
|
109
|
+
throw new Error(`July 9 RSVP replay fixture must be repo-safe and non-indexed: ${violations.join(", ")}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function fixtureAllowsOfflineReplay(fixture) {
|
|
113
|
+
if (fixture.schemaVersion !== 1 || fixture.policyVersion !== exports.RSVP_REPLAY_POLICY_VERSION) {
|
|
114
|
+
throw new Error("unsupported RSVP replay fixture");
|
|
115
|
+
}
|
|
116
|
+
const privacy = isRecord(fixture.privacy) ? fixture.privacy : null;
|
|
117
|
+
if (!privacy
|
|
118
|
+
|| privacy.rawLiveTranscriptStored !== false
|
|
119
|
+
|| privacy.searchIndex !== false
|
|
120
|
+
|| privacy.vectorIndex !== false) {
|
|
121
|
+
throw new Error("RSVP replay fixture must be minimized and private");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function answerForPendingGuests(pendingGuests) {
|
|
125
|
+
return pendingGuests.length === 0
|
|
126
|
+
? "No pending guests in the replay fixture."
|
|
127
|
+
: `Pending guests: ${pendingGuests.join(", ")}`;
|
|
128
|
+
}
|
|
129
|
+
async function replayRsvpFixture(input) {
|
|
130
|
+
const fixture = readJsonFile(input.fixturePath);
|
|
131
|
+
if (!isRecord(fixture))
|
|
132
|
+
throw new Error("RSVP replay fixture must be an object");
|
|
133
|
+
fixtureAllowsOfflineReplay(fixture);
|
|
134
|
+
const expected = fixtureExpected(fixture);
|
|
135
|
+
const pendingGuests = fixturePendingGuests(fixture);
|
|
136
|
+
const result = {
|
|
137
|
+
schemaVersion: 1,
|
|
138
|
+
policyVersion: exports.RSVP_REPLAY_RESULT_POLICY_VERSION,
|
|
139
|
+
sideEffect: false,
|
|
140
|
+
contextPacketHash: expected.contextPacketHash,
|
|
141
|
+
modelInputHash: expected.modelInputHash,
|
|
142
|
+
answer: answerForPendingGuests(pendingGuests),
|
|
143
|
+
indexPolicy: { search: false, vector: false },
|
|
144
|
+
};
|
|
145
|
+
(0, runtime_1.emitNervesEvent)({
|
|
146
|
+
component: "rsvp",
|
|
147
|
+
event: "rsvp.replay_fixture_replayed",
|
|
148
|
+
message: "replayed RSVP fixture offline",
|
|
149
|
+
meta: { fixturePath: input.fixturePath, pendingGuests: pendingGuests.length },
|
|
150
|
+
});
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
function hashFile(filePath) {
|
|
154
|
+
return sha256(fs.readFileSync(filePath, "utf-8"));
|
|
155
|
+
}
|
|
156
|
+
function legacyRootHash(legacyRoot) {
|
|
157
|
+
const entries = fs.readdirSync(legacyRoot)
|
|
158
|
+
.sort()
|
|
159
|
+
.map((name) => {
|
|
160
|
+
const filePath = path.join(legacyRoot, name);
|
|
161
|
+
const stat = fs.statSync(filePath);
|
|
162
|
+
return stat.isFile() ? { name, hash: hashFile(filePath) } : { name, hash: "directory" };
|
|
163
|
+
});
|
|
164
|
+
return sha256(stableJson(entries));
|
|
165
|
+
}
|
|
166
|
+
function displayName(row) {
|
|
167
|
+
const display = stringField(row, "displayName");
|
|
168
|
+
if (display)
|
|
169
|
+
return display;
|
|
170
|
+
const first = stringField(row, "first_name") ?? "";
|
|
171
|
+
const last = stringField(row, "last_name") ?? "";
|
|
172
|
+
const combined = `${first} ${last}`.trim();
|
|
173
|
+
return combined.length > 0 ? combined : null;
|
|
174
|
+
}
|
|
175
|
+
function pendingGuestsFromLegacyRoot(legacyRoot) {
|
|
176
|
+
const guestsPath = path.join(legacyRoot, "guests.json");
|
|
177
|
+
const parsed = readJsonFile(guestsPath);
|
|
178
|
+
const guests = isRecord(parsed) && isRecord(parsed.guests) ? Object.values(parsed.guests) : [];
|
|
179
|
+
return guests
|
|
180
|
+
.filter(isRecord)
|
|
181
|
+
.filter((row) => stringField(row, "attending_status") === "pending" || stringField(row, "status") === "pending")
|
|
182
|
+
.map(displayName)
|
|
183
|
+
.filter((name) => !!name);
|
|
184
|
+
}
|
|
185
|
+
async function renderLegacyRsvpSnapshotOffline(input) {
|
|
186
|
+
const legacyRootHashBefore = legacyRootHash(input.legacyRoot);
|
|
187
|
+
const pendingGuests = pendingGuestsFromLegacyRoot(input.legacyRoot);
|
|
188
|
+
const legacyRootHashAfter = legacyRootHash(input.legacyRoot);
|
|
189
|
+
const result = {
|
|
190
|
+
schemaVersion: 1,
|
|
191
|
+
sideEffect: false,
|
|
192
|
+
outputPath: input.outputPath,
|
|
193
|
+
legacyRootHashBefore,
|
|
194
|
+
legacyRootHashAfter,
|
|
195
|
+
pendingGuests,
|
|
196
|
+
};
|
|
197
|
+
fs.mkdirSync(path.dirname(input.outputPath), { recursive: true });
|
|
198
|
+
fs.writeFileSync(input.outputPath, `${JSON.stringify(result, null, 2)}\n`, "utf-8");
|
|
199
|
+
(0, runtime_1.emitNervesEvent)({
|
|
200
|
+
component: "rsvp",
|
|
201
|
+
event: "rsvp.legacy_offline_rendered",
|
|
202
|
+
message: "rendered legacy RSVP snapshot offline",
|
|
203
|
+
meta: { legacyRoot: input.legacyRoot, outputPath: input.outputPath, pendingGuests: pendingGuests.length },
|
|
204
|
+
});
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
async function replayJuly9PendingAnswerFixture(input) {
|
|
208
|
+
const manifest = readJsonFile(input.manifestPath);
|
|
209
|
+
if (!isRecord(manifest) || manifest.schemaVersion !== 1 || manifest.policyVersion !== "july-9-rsvp-regression/v1") {
|
|
210
|
+
throw new Error("unsupported July 9 RSVP replay manifest");
|
|
211
|
+
}
|
|
212
|
+
assertJuly9ReplayPrivacy(manifest);
|
|
213
|
+
const rsvpState = isRecord(manifest.rsvpState) ? manifest.rsvpState : null;
|
|
214
|
+
if (!rsvpState)
|
|
215
|
+
throw new Error("July 9 RSVP state missing");
|
|
216
|
+
const pendingGuests = Array.isArray(rsvpState.pendingGuests)
|
|
217
|
+
? rsvpState.pendingGuests.filter((entry) => typeof entry === "string" && entry.trim().length > 0)
|
|
218
|
+
: [];
|
|
219
|
+
const counts = countsFrom(rsvpState.counts);
|
|
220
|
+
const result = {
|
|
221
|
+
sideEffect: false,
|
|
222
|
+
usedNativeRsvpState: true,
|
|
223
|
+
answer: answerForPendingGuests(pendingGuests),
|
|
224
|
+
counts,
|
|
225
|
+
};
|
|
226
|
+
(0, runtime_1.emitNervesEvent)({
|
|
227
|
+
component: "rsvp",
|
|
228
|
+
event: "rsvp.july9_pending_answer_replayed",
|
|
229
|
+
message: "replayed July 9 pending RSVP answer fixture",
|
|
230
|
+
meta: { pendingGuests: pendingGuests.length, pendingCount: counts.pending },
|
|
231
|
+
});
|
|
232
|
+
return result;
|
|
233
|
+
}
|