instar 0.28.108 → 0.28.110

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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * BurnAlertButtons — principal-bound Telegram inline-button surface for the
3
+ * burn-detection auto-heal system.
4
+ *
5
+ * Phase 5 of docs/specs/token-burn-detection-and-self-heal.md.
6
+ *
7
+ * The convergence audit identified that the original spec's "tap to throttle"
8
+ * button could be tapped by anyone in any Telegram chat the bot was in. The
9
+ * Phase 5 callback handler closes that vector with three layers:
10
+ *
11
+ * 1. **Principal verification**: every callback's `from.id` must be in the
12
+ * agent's `authorizedUserIds` list (the same list `MessagingToneGate`
13
+ * and other principal-aware code paths consult).
14
+ * 2. **HMAC signature**: every `callback_data` is signed by the agent's
15
+ * capability key over the canonical action payload. Forged callback
16
+ * data fails verification.
17
+ * 3. **Signal-id freshness**: each button carries the signal-id that
18
+ * produced it; once consumed, the same signal-id cannot be tapped
19
+ * twice (the spec's "replay reject" requirement).
20
+ *
21
+ * This module is pure logic over a tiny callback_data string. The actual
22
+ * Telegram-wire integration (sending the inline_keyboard with reply_markup,
23
+ * receiving the callback from TelegramAdapter, dispatching to handle())
24
+ * is wired in a follow-up alongside the existing CallbackRegistry pattern
25
+ * in TelegramAdapter.ts.
26
+ */
27
+ import type { LlmRateGate } from './LlmRateGate.js';
28
+ /** The four actions a user can take from a burn alert. */
29
+ export type BurnAlertAction = 'release' | 'snooze-24h' | 'extend' | 'investigate';
30
+ export interface BurnAlertCallback {
31
+ /** What the user tapped. */
32
+ action: BurnAlertAction;
33
+ /** Which attribution key this button refers to. */
34
+ attributionKey: string;
35
+ /** Signal-id from the originating burn signal (replay prevention). */
36
+ signalId: string;
37
+ }
38
+ export interface BurnAlertCallbackResult {
39
+ ok: boolean;
40
+ action: BurnAlertAction;
41
+ attributionKey: string;
42
+ reason: 'accepted' | 'principal-not-authorized' | 'invalid-signature' | 'signal-id-replayed' | 'malformed-payload' | 'snooze-recorded' | 'extend-failed-not-throttled';
43
+ /** Human-readable status to feed back to the Telegram user. */
44
+ userMessage: string;
45
+ }
46
+ export interface BurnAlertButtonsDeps {
47
+ /** HMAC key to sign + verify callback_data. */
48
+ capabilityKey: Buffer;
49
+ /** Authorized user IDs — same list MessagingToneGate consults. */
50
+ authorizedUserIds: number[];
51
+ /** The LlmRateGate; release/extend mutate throttles. */
52
+ gate: LlmRateGate;
53
+ /** Injectable clock for tests. */
54
+ now?: () => number;
55
+ /** Snooze TTL (default 24h). */
56
+ snoozeDurationMs?: number;
57
+ }
58
+ export declare class BurnAlertButtons {
59
+ private readonly capabilityKey;
60
+ private readonly authorizedUserIds;
61
+ private readonly gate;
62
+ private readonly now;
63
+ private readonly snoozeDurationMs;
64
+ /** Consumed signal-ids per button — guards against replay. */
65
+ private readonly consumed;
66
+ /** Snoozed attribution keys → snooze-expires-at (ms). Inspected by the runbook before throttling. */
67
+ private readonly snoozed;
68
+ constructor(deps: BurnAlertButtonsDeps);
69
+ /**
70
+ * Generate the signed payload for one button. The opaque callback_data
71
+ * string is what the Telegram inline button carries. The button shape
72
+ * itself (text label, button row layout) is the caller's job — they get
73
+ * the payload, they pass it to Telegram's reply_markup.
74
+ */
75
+ encodeCallbackData(payload: BurnAlertCallback): string;
76
+ /**
77
+ * Build a four-button keyboard row for a burn alert. Returns the inline
78
+ * objects the caller can pass to Telegram's reply_markup.inline_keyboard.
79
+ */
80
+ buildKeyboard(attributionKey: string, signalId: string): Array<{
81
+ text: string;
82
+ callback_data: string;
83
+ }>;
84
+ /**
85
+ * Handle a Telegram callback. Returns a structured result the caller can
86
+ * use to log the audit trail and answer the Telegram callback query.
87
+ */
88
+ handle(input: {
89
+ callbackData: string;
90
+ fromUserId: number;
91
+ }): BurnAlertCallbackResult;
92
+ /**
93
+ * Read-side: is this attribution key currently snoozed? Called by the
94
+ * runbook before installing a throttle. Auto-expires entries.
95
+ */
96
+ isSnoozed(attributionKey: string): boolean;
97
+ /** For tests + dashboard. */
98
+ listSnoozes(): Array<{
99
+ attributionKey: string;
100
+ expiresAt: string;
101
+ }>;
102
+ private canonicalize;
103
+ private parseCallbackData;
104
+ /** GC consumed signal-id-action pairs older than 24h. */
105
+ private gcConsumed;
106
+ }
107
+ //# sourceMappingURL=BurnAlertButtons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BurnAlertButtons.d.ts","sourceRoot":"","sources":["../../src/monitoring/BurnAlertButtons.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,0DAA0D;AAC1D,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,aAAa,CAAC;AAElB,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,MAAM,EAAE,eAAe,CAAC;IACxB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EACF,UAAU,GACV,0BAA0B,GAC1B,mBAAmB,GACnB,oBAAoB,GACpB,mBAAmB,GACnB,iBAAiB,GACjB,6BAA6B,CAAC;IAClC,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,wDAAwD;IACxD,IAAI,EAAE,WAAW,CAAC;IAClB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,8DAA8D;IAC9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;gBAEzC,IAAI,EAAE,oBAAoB;IAQtC;;;;;OAKG;IACH,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM;IAYtD;;;OAGG;IACH,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;IASvG;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,uBAAuB;IA4IpF;;;OAGG;IACH,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAU1C,6BAA6B;IAC7B,WAAW,IAAI,KAAK,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAYnE,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,iBAAiB;IAczB,yDAAyD;IACzD,OAAO,CAAC,UAAU;CAMnB"}
@@ -0,0 +1,270 @@
1
+ /**
2
+ * BurnAlertButtons — principal-bound Telegram inline-button surface for the
3
+ * burn-detection auto-heal system.
4
+ *
5
+ * Phase 5 of docs/specs/token-burn-detection-and-self-heal.md.
6
+ *
7
+ * The convergence audit identified that the original spec's "tap to throttle"
8
+ * button could be tapped by anyone in any Telegram chat the bot was in. The
9
+ * Phase 5 callback handler closes that vector with three layers:
10
+ *
11
+ * 1. **Principal verification**: every callback's `from.id` must be in the
12
+ * agent's `authorizedUserIds` list (the same list `MessagingToneGate`
13
+ * and other principal-aware code paths consult).
14
+ * 2. **HMAC signature**: every `callback_data` is signed by the agent's
15
+ * capability key over the canonical action payload. Forged callback
16
+ * data fails verification.
17
+ * 3. **Signal-id freshness**: each button carries the signal-id that
18
+ * produced it; once consumed, the same signal-id cannot be tapped
19
+ * twice (the spec's "replay reject" requirement).
20
+ *
21
+ * This module is pure logic over a tiny callback_data string. The actual
22
+ * Telegram-wire integration (sending the inline_keyboard with reply_markup,
23
+ * receiving the callback from TelegramAdapter, dispatching to handle())
24
+ * is wired in a follow-up alongside the existing CallbackRegistry pattern
25
+ * in TelegramAdapter.ts.
26
+ */
27
+ import crypto from 'node:crypto';
28
+ const DEFAULT_SNOOZE_MS = 24 * 60 * 60 * 1000;
29
+ export class BurnAlertButtons {
30
+ capabilityKey;
31
+ authorizedUserIds;
32
+ gate;
33
+ now;
34
+ snoozeDurationMs;
35
+ /** Consumed signal-ids per button — guards against replay. */
36
+ consumed = new Map();
37
+ /** Snoozed attribution keys → snooze-expires-at (ms). Inspected by the runbook before throttling. */
38
+ snoozed = new Map();
39
+ constructor(deps) {
40
+ this.capabilityKey = deps.capabilityKey;
41
+ this.authorizedUserIds = new Set(deps.authorizedUserIds);
42
+ this.gate = deps.gate;
43
+ this.now = deps.now ?? (() => Date.now());
44
+ this.snoozeDurationMs = deps.snoozeDurationMs ?? DEFAULT_SNOOZE_MS;
45
+ }
46
+ /**
47
+ * Generate the signed payload for one button. The opaque callback_data
48
+ * string is what the Telegram inline button carries. The button shape
49
+ * itself (text label, button row layout) is the caller's job — they get
50
+ * the payload, they pass it to Telegram's reply_markup.
51
+ */
52
+ encodeCallbackData(payload) {
53
+ const canonical = this.canonicalize(payload);
54
+ const sig = crypto
55
+ .createHmac('sha256', this.capabilityKey)
56
+ .update(canonical)
57
+ .digest('hex')
58
+ .slice(0, 16);
59
+ // Compact encoding for Telegram's 64-byte callback_data limit. Format:
60
+ // <action>|<attributionKey>|<signalId>|<sig>
61
+ return `${payload.action}|${payload.attributionKey}|${payload.signalId}|${sig}`;
62
+ }
63
+ /**
64
+ * Build a four-button keyboard row for a burn alert. Returns the inline
65
+ * objects the caller can pass to Telegram's reply_markup.inline_keyboard.
66
+ */
67
+ buildKeyboard(attributionKey, signalId) {
68
+ return [
69
+ { text: 'Release', callback_data: this.encodeCallbackData({ action: 'release', attributionKey, signalId }) },
70
+ { text: 'Snooze 24h', callback_data: this.encodeCallbackData({ action: 'snooze-24h', attributionKey, signalId }) },
71
+ { text: 'Extend +1h', callback_data: this.encodeCallbackData({ action: 'extend', attributionKey, signalId }) },
72
+ { text: 'Investigate', callback_data: this.encodeCallbackData({ action: 'investigate', attributionKey, signalId }) },
73
+ ];
74
+ }
75
+ /**
76
+ * Handle a Telegram callback. Returns a structured result the caller can
77
+ * use to log the audit trail and answer the Telegram callback query.
78
+ */
79
+ handle(input) {
80
+ const parsed = this.parseCallbackData(input.callbackData);
81
+ if (!parsed) {
82
+ return {
83
+ ok: false,
84
+ action: 'investigate',
85
+ attributionKey: '',
86
+ reason: 'malformed-payload',
87
+ userMessage: 'I could not parse that button.',
88
+ };
89
+ }
90
+ // 1. Principal check.
91
+ if (!this.authorizedUserIds.has(input.fromUserId)) {
92
+ return {
93
+ ok: false,
94
+ action: parsed.action,
95
+ attributionKey: parsed.attributionKey,
96
+ reason: 'principal-not-authorized',
97
+ userMessage: 'You are not authorized to act on this agent.',
98
+ };
99
+ }
100
+ // 2. Signature check.
101
+ const expectedSig = crypto
102
+ .createHmac('sha256', this.capabilityKey)
103
+ .update(this.canonicalize(parsed))
104
+ .digest('hex')
105
+ .slice(0, 16);
106
+ if (parsed.sig !== expectedSig) {
107
+ return {
108
+ ok: false,
109
+ action: parsed.action,
110
+ attributionKey: parsed.attributionKey,
111
+ reason: 'invalid-signature',
112
+ userMessage: 'That button signature did not match. Refusing.',
113
+ };
114
+ }
115
+ // 3. Freshness check. The same signal-id-action pair can fire at most once.
116
+ const consumptionKey = `${parsed.action}|${parsed.signalId}`;
117
+ if (this.consumed.has(consumptionKey)) {
118
+ return {
119
+ ok: false,
120
+ action: parsed.action,
121
+ attributionKey: parsed.attributionKey,
122
+ reason: 'signal-id-replayed',
123
+ userMessage: 'That button has already been used.',
124
+ };
125
+ }
126
+ this.consumed.set(consumptionKey, this.now());
127
+ this.gcConsumed();
128
+ // 4. Apply the action.
129
+ switch (parsed.action) {
130
+ case 'release': {
131
+ const released = this.gate.revokeThrottle(parsed.attributionKey);
132
+ return {
133
+ ok: true,
134
+ action: 'release',
135
+ attributionKey: parsed.attributionKey,
136
+ reason: 'accepted',
137
+ userMessage: released
138
+ ? `Released the throttle on ${parsed.attributionKey}.`
139
+ : `No throttle was active for ${parsed.attributionKey} — nothing to release.`,
140
+ };
141
+ }
142
+ case 'snooze-24h': {
143
+ this.snoozed.set(parsed.attributionKey, this.now() + this.snoozeDurationMs);
144
+ this.gate.revokeThrottle(parsed.attributionKey);
145
+ return {
146
+ ok: true,
147
+ action: 'snooze-24h',
148
+ attributionKey: parsed.attributionKey,
149
+ reason: 'snooze-recorded',
150
+ userMessage: `Snoozed ${parsed.attributionKey} for 24 hours. The runbook will not throttle this key during that window.`,
151
+ };
152
+ }
153
+ case 'extend': {
154
+ // Re-install throttle for another hour using a fresh signalId derived
155
+ // from "extend|<existing signalId>". Returns a structured error if no
156
+ // throttle is currently active.
157
+ const active = this.gate.listActiveThrottles().find((t) => t.attributionKey === parsed.attributionKey);
158
+ if (!active) {
159
+ return {
160
+ ok: false,
161
+ action: 'extend',
162
+ attributionKey: parsed.attributionKey,
163
+ reason: 'extend-failed-not-throttled',
164
+ userMessage: `Cannot extend — no active throttle on ${parsed.attributionKey}.`,
165
+ };
166
+ }
167
+ const extendSignalId = `extend:${parsed.signalId}:${this.now()}`;
168
+ const extendDurationMs = 60 * 60 * 1000;
169
+ const tok = this.gate.computeCapabilityToken({
170
+ attributionKey: parsed.attributionKey,
171
+ durationMs: extendDurationMs,
172
+ issuer: active.issuer,
173
+ signalId: extendSignalId,
174
+ }) ?? undefined;
175
+ try {
176
+ this.gate.installThrottle({
177
+ attributionKey: parsed.attributionKey,
178
+ durationMs: extendDurationMs,
179
+ reason: `User-initiated extension via Telegram button.`,
180
+ issuer: active.issuer,
181
+ signalId: extendSignalId,
182
+ capabilityToken: tok,
183
+ });
184
+ }
185
+ catch (err) {
186
+ return {
187
+ ok: false,
188
+ action: 'extend',
189
+ attributionKey: parsed.attributionKey,
190
+ reason: 'extend-failed-not-throttled',
191
+ userMessage: `Could not extend: ${err.message}`,
192
+ };
193
+ }
194
+ return {
195
+ ok: true,
196
+ action: 'extend',
197
+ attributionKey: parsed.attributionKey,
198
+ reason: 'accepted',
199
+ userMessage: `Extended the throttle on ${parsed.attributionKey} for another hour.`,
200
+ };
201
+ }
202
+ case 'investigate': {
203
+ // No state change; this is purely for audit. The user is saying "I'm
204
+ // looking at this one". The agent records it but otherwise stands by.
205
+ return {
206
+ ok: true,
207
+ action: 'investigate',
208
+ attributionKey: parsed.attributionKey,
209
+ reason: 'accepted',
210
+ userMessage: `Logged. ${parsed.attributionKey} marked for follow-up.`,
211
+ };
212
+ }
213
+ }
214
+ }
215
+ /**
216
+ * Read-side: is this attribution key currently snoozed? Called by the
217
+ * runbook before installing a throttle. Auto-expires entries.
218
+ */
219
+ isSnoozed(attributionKey) {
220
+ const expires = this.snoozed.get(attributionKey);
221
+ if (!expires)
222
+ return false;
223
+ if (this.now() >= expires) {
224
+ this.snoozed.delete(attributionKey);
225
+ return false;
226
+ }
227
+ return true;
228
+ }
229
+ /** For tests + dashboard. */
230
+ listSnoozes() {
231
+ const out = [];
232
+ for (const [key, expires] of this.snoozed.entries()) {
233
+ if (this.now() < expires) {
234
+ out.push({ attributionKey: key, expiresAt: new Date(expires).toISOString() });
235
+ }
236
+ else {
237
+ this.snoozed.delete(key);
238
+ }
239
+ }
240
+ return out;
241
+ }
242
+ canonicalize(payload) {
243
+ return `${payload.action}|${payload.attributionKey}|${payload.signalId}`;
244
+ }
245
+ parseCallbackData(s) {
246
+ const parts = s.split('|');
247
+ if (parts.length !== 4)
248
+ return null;
249
+ const action = parts[0];
250
+ if (!['release', 'snooze-24h', 'extend', 'investigate'].includes(action))
251
+ return null;
252
+ if (!parts[1] || !parts[2] || !parts[3])
253
+ return null;
254
+ return {
255
+ action,
256
+ attributionKey: parts[1],
257
+ signalId: parts[2],
258
+ sig: parts[3],
259
+ };
260
+ }
261
+ /** GC consumed signal-id-action pairs older than 24h. */
262
+ gcConsumed() {
263
+ const cutoff = this.now() - 24 * 60 * 60 * 1000;
264
+ for (const [key, ts] of this.consumed.entries()) {
265
+ if (ts < cutoff)
266
+ this.consumed.delete(key);
267
+ }
268
+ }
269
+ }
270
+ //# sourceMappingURL=BurnAlertButtons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BurnAlertButtons.js","sourceRoot":"","sources":["../../src/monitoring/BurnAlertButtons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * BurnDetectionSubscriber — wires the BurnThrottleRunbook into the
3
+ * DegradationReporter as a self-healer for feature='token-burn-detection'.
4
+ *
5
+ * Phase 5 of docs/specs/token-burn-detection-and-self-heal.md. The
6
+ * DegradationReporter already supports a `registerHealer(feature, healer)`
7
+ * surface (per Remediator V2 spec). The runbook's `handle()` is sync and
8
+ * returns RunbookOutcome; the healer signature is async + returns boolean.
9
+ * This module is the small adapter between them.
10
+ *
11
+ * Returns true to DegradationReporter when the runbook installed a
12
+ * throttle (the healer "succeeded" by acting on the signal); returns false
13
+ * when only an alert was sent (the runbook chose alert-only) so the
14
+ * reporter's own audit log records the partial-action correctly.
15
+ */
16
+ import type { DegradationEvent, DegradationReporter } from './DegradationReporter.js';
17
+ import type { BurnThrottleRunbook, RunbookOutcome } from './BurnThrottleRunbook.js';
18
+ export declare const BURN_DETECTION_FEATURE = "token-burn-detection";
19
+ export declare function registerBurnDetectionSubscriber(reporter: Pick<DegradationReporter, 'registerHealer'>, runbook: BurnThrottleRunbook,
20
+ /**
21
+ * Optional sink for outcomes — the verification step (Phase 6) will
22
+ * subscribe here to re-sample telemetry post-throttle and emit a follow-up.
23
+ */
24
+ onOutcome?: (outcome: RunbookOutcome, event: DegradationEvent) => void): void;
25
+ //# sourceMappingURL=BurnDetectionSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BurnDetectionSubscriber.d.ts","sourceRoot":"","sources":["../../src/monitoring/BurnDetectionSubscriber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACtF,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEpF,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,IAAI,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,EACrD,OAAO,EAAE,mBAAmB;AAC5B;;;GAGG;AACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,gBAAgB,KAAK,IAAI,GACrE,IAAI,CAUN"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * BurnDetectionSubscriber — wires the BurnThrottleRunbook into the
3
+ * DegradationReporter as a self-healer for feature='token-burn-detection'.
4
+ *
5
+ * Phase 5 of docs/specs/token-burn-detection-and-self-heal.md. The
6
+ * DegradationReporter already supports a `registerHealer(feature, healer)`
7
+ * surface (per Remediator V2 spec). The runbook's `handle()` is sync and
8
+ * returns RunbookOutcome; the healer signature is async + returns boolean.
9
+ * This module is the small adapter between them.
10
+ *
11
+ * Returns true to DegradationReporter when the runbook installed a
12
+ * throttle (the healer "succeeded" by acting on the signal); returns false
13
+ * when only an alert was sent (the runbook chose alert-only) so the
14
+ * reporter's own audit log records the partial-action correctly.
15
+ */
16
+ export const BURN_DETECTION_FEATURE = 'token-burn-detection';
17
+ export function registerBurnDetectionSubscriber(reporter, runbook,
18
+ /**
19
+ * Optional sink for outcomes — the verification step (Phase 6) will
20
+ * subscribe here to re-sample telemetry post-throttle and emit a follow-up.
21
+ */
22
+ onOutcome) {
23
+ reporter.registerHealer(BURN_DETECTION_FEATURE, async (event) => {
24
+ const outcome = runbook.handle(event);
25
+ try {
26
+ onOutcome?.(outcome, event);
27
+ }
28
+ catch (err) {
29
+ console.warn(`[burn-detection-subscriber] onOutcome threw: ${err.message}`);
30
+ }
31
+ return outcome.kind === 'throttle-installed';
32
+ });
33
+ }
34
+ //# sourceMappingURL=BurnDetectionSubscriber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BurnDetectionSubscriber.js","sourceRoot":"","sources":["../../src/monitoring/BurnDetectionSubscriber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAE7D,MAAM,UAAU,+BAA+B,CAC7C,QAAqD,EACrD,OAA4B;AAC5B;;;GAGG;AACH,SAAsE;IAEtE,QAAQ,CAAC,cAAc,CAAC,sBAAsB,EAAE,KAAK,EAAE,KAAuB,EAAE,EAAE;QAChF,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,SAAS,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,gDAAiD,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,OAAO,CAAC,IAAI,KAAK,oBAAoB,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -35,7 +35,7 @@ export interface BurnThrottleConfig {
35
35
  throttleDurationMs: number;
36
36
  }
37
37
  export declare const DEFAULT_BURN_THROTTLE_CONFIG: BurnThrottleConfig;
38
- export type RunbookOutcomeKind = 'alert-only-unknown' | 'alert-only-config-disabled' | 'alert-only-self-attribution' | 'throttle-installed' | 'throttle-failed';
38
+ export type RunbookOutcomeKind = 'alert-only-unknown' | 'alert-only-config-disabled' | 'alert-only-self-attribution' | 'alert-only-snoozed' | 'throttle-installed' | 'throttle-failed';
39
39
  export interface RunbookOutcome {
40
40
  kind: RunbookOutcomeKind;
41
41
  attributionKey: string;
@@ -57,6 +57,13 @@ export interface BurnThrottleRunbookDeps {
57
57
  alertTopicId?: number;
58
58
  /** Injectable clock for tests. */
59
59
  now?: () => number;
60
+ /**
61
+ * Phase 5 — snooze check. If provided, the runbook calls isSnoozed(key)
62
+ * before throttling. A snoozed key skips auto-throttle (the user tapped
63
+ * "Snooze 24h" on a previous alert). The alert still fires so the user
64
+ * knows the burn is recurring while snoozed.
65
+ */
66
+ isSnoozed?: (attributionKey: string) => boolean;
60
67
  }
61
68
  /** Identity tag the runbook uses when installing throttles. */
62
69
  export declare const RUNBOOK_ISSUER = "burn-throttle-runbook";
@@ -66,6 +73,7 @@ export declare class BurnThrottleRunbook {
66
73
  private readonly sendTelegram;
67
74
  private readonly alertTopicId;
68
75
  private readonly now;
76
+ private readonly isSnoozed;
69
77
  constructor(deps: BurnThrottleRunbookDeps);
70
78
  /**
71
79
  * Handle a burn-detection signal. Called from a subscriber hook on
@@ -1 +1 @@
1
- {"version":3,"file":"BurnThrottleRunbook.d.ts","sourceRoot":"","sources":["../../src/monitoring/BurnThrottleRunbook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B,yCAAyC;IACzC,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,4BAA4B,EAAE,kBAI1C,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,4BAA4B,GAC5B,6BAA6B,GAC7B,oBAAoB,GACpB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2EAA2E;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,qFAAqF;IACrF,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,+DAA+D;AAC/D,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAEtD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,EAAE,uBAAuB;IAQzC;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,cAAc;IA8F/C,OAAO,CAAC,YAAY;CAYrB;AAID;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAGrE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI9D;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAazG"}
1
+ {"version":3,"file":"BurnThrottleRunbook.d.ts","sourceRoot":"","sources":["../../src/monitoring/BurnThrottleRunbook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,qBAAqB,EAAE,OAAO,CAAC;IAC/B,yCAAyC;IACzC,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,4BAA4B,EAAE,kBAI1C,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B,oBAAoB,GACpB,4BAA4B,GAC5B,6BAA6B,GAC7B,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,2EAA2E;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,qFAAqF;IACrF,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;CACjD;AAED,+DAA+D;AAC/D,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAEtD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;gBAEzC,IAAI,EAAE,uBAAuB;IASzC;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,cAAc;IA4G/C,OAAO,CAAC,YAAY;CAYrB;AAID;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAGrE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI9D;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAazG"}
@@ -31,12 +31,14 @@ export class BurnThrottleRunbook {
31
31
  sendTelegram;
32
32
  alertTopicId;
33
33
  now;
34
+ isSnoozed;
34
35
  constructor(deps) {
35
36
  this.gate = deps.gate;
36
37
  this.config = { ...DEFAULT_BURN_THROTTLE_CONFIG, ...(deps.config ?? {}) };
37
38
  this.sendTelegram = deps.sendTelegram;
38
39
  this.alertTopicId = deps.alertTopicId ?? 8615;
39
40
  this.now = deps.now ?? (() => Date.now());
41
+ this.isSnoozed = deps.isSnoozed ?? (() => false);
40
42
  }
41
43
  /**
42
44
  * Handle a burn-detection signal. Called from a subscriber hook on
@@ -83,7 +85,20 @@ export class BurnThrottleRunbook {
83
85
  reason: 'autoThrottle is disabled in config; alert sent but no throttle installed.',
84
86
  };
85
87
  }
86
- // 3. Unknown attribution + caller has not opted in to auto-throttle on unknown.
88
+ // 3. Snoozed key (Phase 5): user already said "this is fine, leave it
89
+ // alone for 24h." We still alert because the burn is recurring — the
90
+ // user should know — but we don't throttle.
91
+ if (this.isSnoozed(attributionKey)) {
92
+ this.fireTelegram(`${alertText}\n\nThis key is currently snoozed; I will not slow it down. The snooze auto-expires at the configured time.`);
93
+ return {
94
+ kind: 'alert-only-snoozed',
95
+ attributionKey,
96
+ decidedAt,
97
+ trigger,
98
+ reason: 'Attribution key is currently snoozed; alert sent but no throttle installed.',
99
+ };
100
+ }
101
+ // 4. Unknown attribution + caller has not opted in to auto-throttle on unknown.
87
102
  if (isUnknownKey(attributionKey) && !this.config.autoThrottleOnUnknown) {
88
103
  this.fireTelegram(alertText);
89
104
  return {
@@ -94,7 +109,7 @@ export class BurnThrottleRunbook {
94
109
  reason: 'Attribution key has no known component; operator must opt in to auto-throttle on unknown.',
95
110
  };
96
111
  }
97
- // 4. Install throttle, send alert.
112
+ // 5. Install throttle, send alert.
98
113
  const token = this.gate.computeCapabilityToken({
99
114
  attributionKey,
100
115
  durationMs: this.config.throttleDurationMs,
@@ -1 +1 @@
1
- {"version":3,"file":"BurnThrottleRunbook.js","sourceRoot":"","sources":["../../src/monitoring/BurnThrottleRunbook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BurnThrottleRunbook.js","sourceRoot":"","sources":["../../src/monitoring/BurnThrottleRunbook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * BurnVerifier — post-throttle verification + follow-up alerts.
3
+ *
4
+ * Phase 6 (final phase) of docs/specs/token-burn-detection-and-self-heal.md.
5
+ * Five minutes after the runbook installs a throttle, this module re-samples
6
+ * the token-ledger telemetry for the affected attribution_key. If the
7
+ * post-throttle rate dropped materially, a structured "I caught it, I
8
+ * slowed it down, here's the before-and-after" Telegram message goes out
9
+ * (the umbrella spec's "fixed, here's the before/after" follow-up shape).
10
+ *
11
+ * If the post-throttle rate did NOT drop, the verifier escalates: the
12
+ * throttle was pointed at the wrong key, or the path doesn't honour the
13
+ * gate. Operator gets a "I tried but it did not work" message with the
14
+ * specific shape that distinguishes from "I caught it" so they know to
15
+ * intervene manually.
16
+ *
17
+ * Pure timer + telemetry-read; no LLM calls (the umbrella spec's
18
+ * §"Why it's safe — What if the watcher itself burns tokens?" guarantee).
19
+ */
20
+ import type { TokenLedger } from './TokenLedger.js';
21
+ import type { RunbookOutcome } from './BurnThrottleRunbook.js';
22
+ import type { DegradationEvent } from './DegradationReporter.js';
23
+ export interface VerificationResult {
24
+ attributionKey: string;
25
+ preThrottleRate: number;
26
+ postThrottleRate: number;
27
+ ratio: number;
28
+ successfullyThrottled: boolean;
29
+ verifiedAt: string;
30
+ }
31
+ export interface BurnVerifierDeps {
32
+ ledger: Pick<TokenLedger, 'byAttributionKey'>;
33
+ sendTelegram?: (topicId: number, text: string) => void | Promise<void>;
34
+ alertTopicId?: number;
35
+ now?: () => number;
36
+ config?: {
37
+ verifyDelayMs?: number;
38
+ sampleWindowMs?: number;
39
+ successRatio?: number;
40
+ };
41
+ schedule?: (cb: () => void, delayMs: number) => void;
42
+ }
43
+ export declare class BurnVerifier {
44
+ private readonly ledger;
45
+ private readonly sendTelegram;
46
+ private readonly alertTopicId;
47
+ private readonly now;
48
+ private readonly verifyDelayMs;
49
+ private readonly sampleWindowMs;
50
+ private readonly successRatio;
51
+ private readonly schedule;
52
+ constructor(deps: BurnVerifierDeps);
53
+ /**
54
+ * Called by the subscriber when the runbook installs a throttle. Pulls
55
+ * the pre-throttle rate from the originating event, schedules a re-sample
56
+ * after `verifyDelayMs`, and on fire sends the structured follow-up.
57
+ */
58
+ scheduleVerification(outcome: RunbookOutcome, event: DegradationEvent): void;
59
+ /**
60
+ * Run the verification re-sample. Exposed for tests + the manual-rerun
61
+ * surface (Phase 6+ dashboard control).
62
+ */
63
+ runVerification(attributionKey: string, preThrottleRate: number): VerificationResult;
64
+ private fireFollowUp;
65
+ private fireTelegram;
66
+ }
67
+ export declare function extractTokensLast1h(event: DegradationEvent): number;
68
+ //# sourceMappingURL=BurnVerifier.d.ts.map