@spendguard/sdk 0.5.0

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/dist/client.js ADDED
@@ -0,0 +1,4815 @@
1
+ import { createHash, createHmac } from 'crypto';
2
+ import { credentials } from '@grpc/grpc-js';
3
+ import { GrpcTransport } from '@protobuf-ts/grpc-transport';
4
+ import { ServiceType, RpcError, stackIntercept } from '@protobuf-ts/runtime-rpc';
5
+ import { MessageType, PbLong, typeofJsonValue, isJsonObject } from '@protobuf-ts/runtime';
6
+ import { AsyncLocalStorage } from 'async_hooks';
7
+
8
+ // src/client.ts
9
+ var Timestamp$Type = class extends MessageType {
10
+ constructor() {
11
+ super("google.protobuf.Timestamp", [
12
+ {
13
+ no: 1,
14
+ name: "seconds",
15
+ kind: "scalar",
16
+ T: 3
17
+ /*ScalarType.INT64*/
18
+ },
19
+ {
20
+ no: 2,
21
+ name: "nanos",
22
+ kind: "scalar",
23
+ T: 5
24
+ /*ScalarType.INT32*/
25
+ }
26
+ ]);
27
+ }
28
+ /**
29
+ * Creates a new `Timestamp` for the current time.
30
+ */
31
+ now() {
32
+ const msg = this.create();
33
+ const ms = Date.now();
34
+ msg.seconds = PbLong.from(Math.floor(ms / 1e3)).toString();
35
+ msg.nanos = ms % 1e3 * 1e6;
36
+ return msg;
37
+ }
38
+ /**
39
+ * Converts a `Timestamp` to a JavaScript Date.
40
+ */
41
+ toDate(message) {
42
+ return new Date(PbLong.from(message.seconds).toNumber() * 1e3 + Math.ceil(message.nanos / 1e6));
43
+ }
44
+ /**
45
+ * Converts a JavaScript Date to a `Timestamp`.
46
+ */
47
+ fromDate(date) {
48
+ const msg = this.create();
49
+ const ms = date.getTime();
50
+ msg.seconds = PbLong.from(Math.floor(ms / 1e3)).toString();
51
+ msg.nanos = (ms % 1e3 + (ms < 0 && ms % 1e3 !== 0 ? 1e3 : 0)) * 1e6;
52
+ return msg;
53
+ }
54
+ /**
55
+ * In JSON format, the `Timestamp` type is encoded as a string
56
+ * in the RFC 3339 format.
57
+ */
58
+ internalJsonWrite(message, options) {
59
+ let ms = PbLong.from(message.seconds).toNumber() * 1e3;
60
+ if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z"))
61
+ throw new Error("Unable to encode Timestamp to JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
62
+ if (message.nanos < 0)
63
+ throw new Error("Unable to encode invalid Timestamp to JSON. Nanos must not be negative.");
64
+ let z = "Z";
65
+ if (message.nanos > 0) {
66
+ let nanosStr = (message.nanos + 1e9).toString().substring(1);
67
+ if (nanosStr.substring(3) === "000000")
68
+ z = "." + nanosStr.substring(0, 3) + "Z";
69
+ else if (nanosStr.substring(6) === "000")
70
+ z = "." + nanosStr.substring(0, 6) + "Z";
71
+ else
72
+ z = "." + nanosStr + "Z";
73
+ }
74
+ return new Date(ms).toISOString().replace(".000Z", z);
75
+ }
76
+ /**
77
+ * In JSON format, the `Timestamp` type is encoded as a string
78
+ * in the RFC 3339 format.
79
+ */
80
+ internalJsonRead(json, options, target) {
81
+ if (typeof json !== "string")
82
+ throw new Error("Unable to parse Timestamp from JSON " + typeofJsonValue(json) + ".");
83
+ let matches = json.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:Z|\.([0-9]{3,9})Z|([+-][0-9][0-9]:[0-9][0-9]))$/);
84
+ if (!matches)
85
+ throw new Error("Unable to parse Timestamp from JSON. Invalid format.");
86
+ let ms = Date.parse(matches[1] + "-" + matches[2] + "-" + matches[3] + "T" + matches[4] + ":" + matches[5] + ":" + matches[6] + (matches[8] ? matches[8] : "Z"));
87
+ if (Number.isNaN(ms))
88
+ throw new Error("Unable to parse Timestamp from JSON. Invalid value.");
89
+ if (ms < Date.parse("0001-01-01T00:00:00Z") || ms > Date.parse("9999-12-31T23:59:59Z"))
90
+ throw new globalThis.Error("Unable to parse Timestamp from JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
91
+ if (!target)
92
+ target = this.create();
93
+ target.seconds = PbLong.from(ms / 1e3).toString();
94
+ target.nanos = 0;
95
+ if (matches[7])
96
+ target.nanos = parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) - 1e9;
97
+ return target;
98
+ }
99
+ };
100
+ var Timestamp = new Timestamp$Type();
101
+
102
+ // src/_proto/spendguard/common/v1/common.ts
103
+ var UnitRef_Kind = /* @__PURE__ */ ((UnitRef_Kind2) => {
104
+ UnitRef_Kind2[UnitRef_Kind2["KIND_UNSPECIFIED"] = 0] = "KIND_UNSPECIFIED";
105
+ UnitRef_Kind2[UnitRef_Kind2["MONETARY"] = 1] = "MONETARY";
106
+ UnitRef_Kind2[UnitRef_Kind2["TOKEN"] = 2] = "TOKEN";
107
+ UnitRef_Kind2[UnitRef_Kind2["CREDIT"] = 3] = "CREDIT";
108
+ UnitRef_Kind2[UnitRef_Kind2["NON_MONETARY"] = 4] = "NON_MONETARY";
109
+ return UnitRef_Kind2;
110
+ })(UnitRef_Kind || {});
111
+ var Replay_StatusCode = /* @__PURE__ */ ((Replay_StatusCode2) => {
112
+ Replay_StatusCode2[Replay_StatusCode2["STATUS_CODE_UNSPECIFIED"] = 0] = "STATUS_CODE_UNSPECIFIED";
113
+ Replay_StatusCode2[Replay_StatusCode2["POSTED"] = 1] = "POSTED";
114
+ Replay_StatusCode2[Replay_StatusCode2["VOIDED"] = 2] = "VOIDED";
115
+ Replay_StatusCode2[Replay_StatusCode2["PENDING"] = 3] = "PENDING";
116
+ return Replay_StatusCode2;
117
+ })(Replay_StatusCode || {});
118
+ var Error_Code = /* @__PURE__ */ ((Error_Code2) => {
119
+ Error_Code2[Error_Code2["CODE_UNSPECIFIED"] = 0] = "CODE_UNSPECIFIED";
120
+ Error_Code2[Error_Code2["FENCING_EPOCH_STALE"] = 1] = "FENCING_EPOCH_STALE";
121
+ Error_Code2[Error_Code2["LOCK_ORDER_TOKEN_MISMATCH"] = 2] = "LOCK_ORDER_TOKEN_MISMATCH";
122
+ Error_Code2[Error_Code2["PRICING_VERSION_UNKNOWN"] = 3] = "PRICING_VERSION_UNKNOWN";
123
+ Error_Code2[Error_Code2["UNIT_NORMALIZATION_REQUIRED"] = 4] = "UNIT_NORMALIZATION_REQUIRED";
124
+ Error_Code2[Error_Code2["BUDGET_EXHAUSTED"] = 5] = "BUDGET_EXHAUSTED";
125
+ Error_Code2[Error_Code2["DEADLOCK_TIMEOUT"] = 6] = "DEADLOCK_TIMEOUT";
126
+ Error_Code2[Error_Code2["SYNC_REPLICA_UNAVAILABLE"] = 7] = "SYNC_REPLICA_UNAVAILABLE";
127
+ Error_Code2[Error_Code2["TENANT_DISABLED"] = 8] = "TENANT_DISABLED";
128
+ Error_Code2[Error_Code2["SCHEMA_BUNDLE_UNKNOWN"] = 9] = "SCHEMA_BUNDLE_UNKNOWN";
129
+ Error_Code2[Error_Code2["SIGNATURE_INVALID"] = 10] = "SIGNATURE_INVALID";
130
+ Error_Code2[Error_Code2["AUDIT_INVARIANT_VIOLATED"] = 11] = "AUDIT_INVARIANT_VIOLATED";
131
+ Error_Code2[Error_Code2["DUPLICATE_DECISION_EVENT"] = 12] = "DUPLICATE_DECISION_EVENT";
132
+ Error_Code2[Error_Code2["RESERVATION_STATE_CONFLICT"] = 13] = "RESERVATION_STATE_CONFLICT";
133
+ Error_Code2[Error_Code2["PRICING_FREEZE_MISMATCH"] = 14] = "PRICING_FREEZE_MISMATCH";
134
+ Error_Code2[Error_Code2["OVERRUN_RESERVATION"] = 15] = "OVERRUN_RESERVATION";
135
+ Error_Code2[Error_Code2["RESERVATION_TTL_EXPIRED"] = 16] = "RESERVATION_TTL_EXPIRED";
136
+ Error_Code2[Error_Code2["MULTI_RESERVATION_COMMIT_DEFERRED"] = 17] = "MULTI_RESERVATION_COMMIT_DEFERRED";
137
+ Error_Code2[Error_Code2["IDEMPOTENCY_CONFLICT"] = 18] = "IDEMPOTENCY_CONFLICT";
138
+ return Error_Code2;
139
+ })(Error_Code || {});
140
+ var BudgetClaim_Direction = /* @__PURE__ */ ((BudgetClaim_Direction2) => {
141
+ BudgetClaim_Direction2[BudgetClaim_Direction2["DIRECTION_UNSPECIFIED"] = 0] = "DIRECTION_UNSPECIFIED";
142
+ BudgetClaim_Direction2[BudgetClaim_Direction2["DEBIT"] = 1] = "DEBIT";
143
+ BudgetClaim_Direction2[BudgetClaim_Direction2["CREDIT"] = 2] = "CREDIT";
144
+ return BudgetClaim_Direction2;
145
+ })(BudgetClaim_Direction || {});
146
+ var SubscriptionMeter_CapDecision = /* @__PURE__ */ ((SubscriptionMeter_CapDecision2) => {
147
+ SubscriptionMeter_CapDecision2[SubscriptionMeter_CapDecision2["CAP_DECISION_UNSPECIFIED"] = 0] = "CAP_DECISION_UNSPECIFIED";
148
+ SubscriptionMeter_CapDecision2[SubscriptionMeter_CapDecision2["CAP_PASS"] = 1] = "CAP_PASS";
149
+ SubscriptionMeter_CapDecision2[SubscriptionMeter_CapDecision2["CAP_SOFT_ALERT"] = 2] = "CAP_SOFT_ALERT";
150
+ SubscriptionMeter_CapDecision2[SubscriptionMeter_CapDecision2["CAP_HARD_BLOCK"] = 3] = "CAP_HARD_BLOCK";
151
+ return SubscriptionMeter_CapDecision2;
152
+ })(SubscriptionMeter_CapDecision || {});
153
+ var ReservationSource = /* @__PURE__ */ ((ReservationSource2) => {
154
+ ReservationSource2[ReservationSource2["UNSPECIFIED"] = 0] = "UNSPECIFIED";
155
+ ReservationSource2[ReservationSource2["BYOK"] = 1] = "BYOK";
156
+ ReservationSource2[ReservationSource2["SUBSCRIPTION_METER"] = 2] = "SUBSCRIPTION_METER";
157
+ return ReservationSource2;
158
+ })(ReservationSource || {});
159
+ var TraceContext$Type = class extends MessageType {
160
+ constructor() {
161
+ super("spendguard.common.v1.TraceContext", [
162
+ {
163
+ no: 1,
164
+ name: "trace_id",
165
+ kind: "scalar",
166
+ T: 9
167
+ /*ScalarType.STRING*/
168
+ },
169
+ {
170
+ no: 2,
171
+ name: "span_id",
172
+ kind: "scalar",
173
+ T: 9
174
+ /*ScalarType.STRING*/
175
+ },
176
+ {
177
+ no: 3,
178
+ name: "parent_span_id",
179
+ kind: "scalar",
180
+ T: 9
181
+ /*ScalarType.STRING*/
182
+ },
183
+ {
184
+ no: 4,
185
+ name: "trace_state",
186
+ kind: "scalar",
187
+ T: 9
188
+ /*ScalarType.STRING*/
189
+ }
190
+ ]);
191
+ }
192
+ };
193
+ var TraceContext = new TraceContext$Type();
194
+ var SpendGuardIds$Type = class extends MessageType {
195
+ constructor() {
196
+ super("spendguard.common.v1.SpendGuardIds", [
197
+ {
198
+ no: 1,
199
+ name: "run_id",
200
+ kind: "scalar",
201
+ T: 9
202
+ /*ScalarType.STRING*/
203
+ },
204
+ {
205
+ no: 2,
206
+ name: "step_id",
207
+ kind: "scalar",
208
+ T: 9
209
+ /*ScalarType.STRING*/
210
+ },
211
+ {
212
+ no: 3,
213
+ name: "llm_call_id",
214
+ kind: "scalar",
215
+ T: 9
216
+ /*ScalarType.STRING*/
217
+ },
218
+ {
219
+ no: 4,
220
+ name: "tool_call_id",
221
+ kind: "scalar",
222
+ T: 9
223
+ /*ScalarType.STRING*/
224
+ },
225
+ {
226
+ no: 5,
227
+ name: "decision_id",
228
+ kind: "scalar",
229
+ T: 9
230
+ /*ScalarType.STRING*/
231
+ },
232
+ {
233
+ no: 6,
234
+ name: "snapshot_id",
235
+ kind: "scalar",
236
+ T: 9
237
+ /*ScalarType.STRING*/
238
+ }
239
+ ]);
240
+ }
241
+ };
242
+ var SpendGuardIds = new SpendGuardIds$Type();
243
+ var UnitRef$Type = class extends MessageType {
244
+ constructor() {
245
+ super("spendguard.common.v1.UnitRef", [
246
+ {
247
+ no: 1,
248
+ name: "unit_id",
249
+ kind: "scalar",
250
+ T: 9
251
+ /*ScalarType.STRING*/
252
+ },
253
+ { no: 2, name: "kind", kind: "enum", T: () => ["spendguard.common.v1.UnitRef.Kind", UnitRef_Kind] },
254
+ {
255
+ no: 3,
256
+ name: "currency",
257
+ kind: "scalar",
258
+ T: 9
259
+ /*ScalarType.STRING*/
260
+ },
261
+ {
262
+ no: 4,
263
+ name: "unit_name",
264
+ kind: "scalar",
265
+ T: 9
266
+ /*ScalarType.STRING*/
267
+ },
268
+ {
269
+ no: 5,
270
+ name: "token_kind",
271
+ kind: "scalar",
272
+ T: 9
273
+ /*ScalarType.STRING*/
274
+ },
275
+ {
276
+ no: 6,
277
+ name: "model_family",
278
+ kind: "scalar",
279
+ T: 9
280
+ /*ScalarType.STRING*/
281
+ },
282
+ {
283
+ no: 7,
284
+ name: "credit_program",
285
+ kind: "scalar",
286
+ T: 9
287
+ /*ScalarType.STRING*/
288
+ }
289
+ ]);
290
+ }
291
+ };
292
+ var UnitRef = new UnitRef$Type();
293
+ var Amount$Type = class extends MessageType {
294
+ constructor() {
295
+ super("spendguard.common.v1.Amount", [
296
+ {
297
+ no: 1,
298
+ name: "atomic",
299
+ kind: "scalar",
300
+ T: 9
301
+ /*ScalarType.STRING*/
302
+ },
303
+ { no: 2, name: "unit", kind: "message", T: () => UnitRef }
304
+ ]);
305
+ }
306
+ };
307
+ new Amount$Type();
308
+ var PricingFreeze$Type = class extends MessageType {
309
+ constructor() {
310
+ super("spendguard.common.v1.PricingFreeze", [
311
+ {
312
+ no: 1,
313
+ name: "pricing_version",
314
+ kind: "scalar",
315
+ T: 9
316
+ /*ScalarType.STRING*/
317
+ },
318
+ {
319
+ no: 2,
320
+ name: "price_snapshot_hash",
321
+ kind: "scalar",
322
+ T: 12
323
+ /*ScalarType.BYTES*/
324
+ },
325
+ {
326
+ no: 3,
327
+ name: "fx_rate_version",
328
+ kind: "scalar",
329
+ T: 9
330
+ /*ScalarType.STRING*/
331
+ },
332
+ {
333
+ no: 4,
334
+ name: "unit_conversion_version",
335
+ kind: "scalar",
336
+ T: 9
337
+ /*ScalarType.STRING*/
338
+ }
339
+ ]);
340
+ }
341
+ };
342
+ var PricingFreeze = new PricingFreeze$Type();
343
+ var Fencing$Type = class extends MessageType {
344
+ constructor() {
345
+ super("spendguard.common.v1.Fencing", [
346
+ {
347
+ no: 1,
348
+ name: "epoch",
349
+ kind: "scalar",
350
+ T: 4
351
+ /*ScalarType.UINT64*/
352
+ },
353
+ {
354
+ no: 2,
355
+ name: "scope_id",
356
+ kind: "scalar",
357
+ T: 9
358
+ /*ScalarType.STRING*/
359
+ },
360
+ {
361
+ no: 3,
362
+ name: "workload_instance_id",
363
+ kind: "scalar",
364
+ T: 9
365
+ /*ScalarType.STRING*/
366
+ }
367
+ ]);
368
+ }
369
+ };
370
+ new Fencing$Type();
371
+ var Replay$Type = class extends MessageType {
372
+ constructor() {
373
+ super("spendguard.common.v1.Replay", [
374
+ {
375
+ no: 1,
376
+ name: "ledger_transaction_id",
377
+ kind: "scalar",
378
+ T: 9
379
+ /*ScalarType.STRING*/
380
+ },
381
+ {
382
+ no: 2,
383
+ name: "operation_kind",
384
+ kind: "scalar",
385
+ T: 9
386
+ /*ScalarType.STRING*/
387
+ },
388
+ {
389
+ no: 3,
390
+ name: "audit_decision_event_id",
391
+ kind: "scalar",
392
+ T: 9
393
+ /*ScalarType.STRING*/
394
+ },
395
+ { no: 4, name: "recorded_at", kind: "message", T: () => Timestamp },
396
+ {
397
+ no: 5,
398
+ name: "operation_id",
399
+ kind: "scalar",
400
+ T: 9
401
+ /*ScalarType.STRING*/
402
+ },
403
+ { no: 6, name: "status_code", kind: "enum", T: () => ["spendguard.common.v1.Replay.StatusCode", Replay_StatusCode] },
404
+ {
405
+ no: 7,
406
+ name: "decision_id",
407
+ kind: "scalar",
408
+ T: 9
409
+ /*ScalarType.STRING*/
410
+ },
411
+ {
412
+ no: 8,
413
+ name: "projection_ids",
414
+ kind: "scalar",
415
+ repeat: 2,
416
+ T: 9
417
+ /*ScalarType.STRING*/
418
+ },
419
+ { no: 9, name: "ttl_expires_at", kind: "message", T: () => Timestamp }
420
+ ]);
421
+ }
422
+ };
423
+ new Replay$Type();
424
+ var FullResponsePayload$Type = class extends MessageType {
425
+ constructor() {
426
+ super("spendguard.common.v1.FullResponsePayload", [
427
+ {
428
+ no: 1,
429
+ name: "payload",
430
+ kind: "scalar",
431
+ T: 12
432
+ /*ScalarType.BYTES*/
433
+ },
434
+ { no: 2, name: "expires_at", kind: "message", T: () => Timestamp }
435
+ ]);
436
+ }
437
+ };
438
+ new FullResponsePayload$Type();
439
+ var Error$Type = class extends MessageType {
440
+ constructor() {
441
+ super("spendguard.common.v1.Error", [
442
+ { no: 1, name: "code", kind: "enum", T: () => ["spendguard.common.v1.Error.Code", Error_Code] },
443
+ {
444
+ no: 2,
445
+ name: "message",
446
+ kind: "scalar",
447
+ T: 9
448
+ /*ScalarType.STRING*/
449
+ },
450
+ { no: 3, name: "details", kind: "map", K: 9, V: {
451
+ kind: "scalar",
452
+ T: 9
453
+ /*ScalarType.STRING*/
454
+ } }
455
+ ]);
456
+ }
457
+ };
458
+ var Error2 = new Error$Type();
459
+ var BudgetClaim$Type = class extends MessageType {
460
+ constructor() {
461
+ super("spendguard.common.v1.BudgetClaim", [
462
+ {
463
+ no: 1,
464
+ name: "budget_id",
465
+ kind: "scalar",
466
+ T: 9
467
+ /*ScalarType.STRING*/
468
+ },
469
+ { no: 2, name: "unit", kind: "message", T: () => UnitRef },
470
+ {
471
+ no: 3,
472
+ name: "amount_atomic",
473
+ kind: "scalar",
474
+ T: 9
475
+ /*ScalarType.STRING*/
476
+ },
477
+ { no: 4, name: "direction", kind: "enum", T: () => ["spendguard.common.v1.BudgetClaim.Direction", BudgetClaim_Direction] },
478
+ {
479
+ no: 5,
480
+ name: "window_instance_id",
481
+ kind: "scalar",
482
+ T: 9
483
+ /*ScalarType.STRING*/
484
+ }
485
+ ]);
486
+ }
487
+ };
488
+ var BudgetClaim = new BudgetClaim$Type();
489
+ var LockOrderToken$Type = class extends MessageType {
490
+ constructor() {
491
+ super("spendguard.common.v1.LockOrderToken", [
492
+ {
493
+ no: 1,
494
+ name: "value",
495
+ kind: "scalar",
496
+ T: 9
497
+ /*ScalarType.STRING*/
498
+ }
499
+ ]);
500
+ }
501
+ };
502
+ new LockOrderToken$Type();
503
+ var CloudEvent$Type = class extends MessageType {
504
+ constructor() {
505
+ super("spendguard.common.v1.CloudEvent", [
506
+ {
507
+ no: 1,
508
+ name: "specversion",
509
+ kind: "scalar",
510
+ T: 9
511
+ /*ScalarType.STRING*/
512
+ },
513
+ {
514
+ no: 2,
515
+ name: "type",
516
+ kind: "scalar",
517
+ T: 9
518
+ /*ScalarType.STRING*/
519
+ },
520
+ {
521
+ no: 3,
522
+ name: "source",
523
+ kind: "scalar",
524
+ T: 9
525
+ /*ScalarType.STRING*/
526
+ },
527
+ {
528
+ no: 4,
529
+ name: "id",
530
+ kind: "scalar",
531
+ T: 9
532
+ /*ScalarType.STRING*/
533
+ },
534
+ { no: 5, name: "time", kind: "message", T: () => Timestamp },
535
+ {
536
+ no: 6,
537
+ name: "datacontenttype",
538
+ kind: "scalar",
539
+ T: 9
540
+ /*ScalarType.STRING*/
541
+ },
542
+ {
543
+ no: 7,
544
+ name: "data",
545
+ kind: "scalar",
546
+ T: 12
547
+ /*ScalarType.BYTES*/
548
+ },
549
+ {
550
+ no: 100,
551
+ name: "tenant_id",
552
+ kind: "scalar",
553
+ T: 9
554
+ /*ScalarType.STRING*/
555
+ },
556
+ {
557
+ no: 101,
558
+ name: "run_id",
559
+ kind: "scalar",
560
+ T: 9
561
+ /*ScalarType.STRING*/
562
+ },
563
+ {
564
+ no: 102,
565
+ name: "decision_id",
566
+ kind: "scalar",
567
+ T: 9
568
+ /*ScalarType.STRING*/
569
+ },
570
+ {
571
+ no: 103,
572
+ name: "schema_bundle_id",
573
+ kind: "scalar",
574
+ T: 9
575
+ /*ScalarType.STRING*/
576
+ },
577
+ {
578
+ no: 200,
579
+ name: "producer_id",
580
+ kind: "scalar",
581
+ T: 9
582
+ /*ScalarType.STRING*/
583
+ },
584
+ {
585
+ no: 201,
586
+ name: "producer_sequence",
587
+ kind: "scalar",
588
+ T: 4
589
+ /*ScalarType.UINT64*/
590
+ },
591
+ {
592
+ no: 202,
593
+ name: "producer_signature",
594
+ kind: "scalar",
595
+ T: 12
596
+ /*ScalarType.BYTES*/
597
+ },
598
+ {
599
+ no: 203,
600
+ name: "signing_key_id",
601
+ kind: "scalar",
602
+ T: 9
603
+ /*ScalarType.STRING*/
604
+ },
605
+ {
606
+ no: 300,
607
+ name: "predicted_a_tokens",
608
+ kind: "scalar",
609
+ T: 3
610
+ /*ScalarType.INT64*/
611
+ },
612
+ {
613
+ no: 301,
614
+ name: "predicted_b_tokens",
615
+ kind: "scalar",
616
+ T: 3
617
+ /*ScalarType.INT64*/
618
+ },
619
+ {
620
+ no: 302,
621
+ name: "predicted_c_tokens",
622
+ kind: "scalar",
623
+ T: 3
624
+ /*ScalarType.INT64*/
625
+ },
626
+ {
627
+ no: 303,
628
+ name: "reserved_strategy",
629
+ kind: "scalar",
630
+ T: 9
631
+ /*ScalarType.STRING*/
632
+ },
633
+ {
634
+ no: 304,
635
+ name: "prediction_strategy_used",
636
+ kind: "scalar",
637
+ T: 9
638
+ /*ScalarType.STRING*/
639
+ },
640
+ {
641
+ no: 305,
642
+ name: "prediction_policy_used",
643
+ kind: "scalar",
644
+ T: 9
645
+ /*ScalarType.STRING*/
646
+ },
647
+ {
648
+ no: 306,
649
+ name: "tokenizer_tier",
650
+ kind: "scalar",
651
+ T: 9
652
+ /*ScalarType.STRING*/
653
+ },
654
+ {
655
+ no: 307,
656
+ name: "tokenizer_version_id",
657
+ kind: "scalar",
658
+ T: 9
659
+ /*ScalarType.STRING*/
660
+ },
661
+ {
662
+ no: 308,
663
+ name: "prediction_confidence",
664
+ kind: "scalar",
665
+ T: 2
666
+ /*ScalarType.FLOAT*/
667
+ },
668
+ {
669
+ no: 309,
670
+ name: "prediction_sample_size",
671
+ kind: "scalar",
672
+ T: 3
673
+ /*ScalarType.INT64*/
674
+ },
675
+ {
676
+ no: 310,
677
+ name: "cold_start_layer_used",
678
+ kind: "scalar",
679
+ T: 9
680
+ /*ScalarType.STRING*/
681
+ },
682
+ {
683
+ no: 311,
684
+ name: "run_projection_at_decision_atomic",
685
+ kind: "scalar",
686
+ T: 3
687
+ /*ScalarType.INT64*/
688
+ },
689
+ {
690
+ no: 312,
691
+ name: "run_predicted_remaining_steps",
692
+ kind: "scalar",
693
+ T: 5
694
+ /*ScalarType.INT32*/
695
+ },
696
+ {
697
+ no: 313,
698
+ name: "run_steps_completed_so_far",
699
+ kind: "scalar",
700
+ T: 3
701
+ /*ScalarType.INT64*/
702
+ },
703
+ {
704
+ no: 314,
705
+ name: "actual_input_tokens",
706
+ kind: "scalar",
707
+ T: 3
708
+ /*ScalarType.INT64*/
709
+ },
710
+ {
711
+ no: 315,
712
+ name: "actual_output_tokens",
713
+ kind: "scalar",
714
+ T: 3
715
+ /*ScalarType.INT64*/
716
+ },
717
+ {
718
+ no: 316,
719
+ name: "delta_b_ratio",
720
+ kind: "scalar",
721
+ T: 2
722
+ /*ScalarType.FLOAT*/
723
+ },
724
+ {
725
+ no: 317,
726
+ name: "delta_c_ratio",
727
+ kind: "scalar",
728
+ T: 2
729
+ /*ScalarType.FLOAT*/
730
+ }
731
+ ]);
732
+ }
733
+ };
734
+ new CloudEvent$Type();
735
+ var SchemaBundleRef$Type = class extends MessageType {
736
+ constructor() {
737
+ super("spendguard.common.v1.SchemaBundleRef", [
738
+ {
739
+ no: 1,
740
+ name: "schema_bundle_id",
741
+ kind: "scalar",
742
+ T: 9
743
+ /*ScalarType.STRING*/
744
+ },
745
+ {
746
+ no: 2,
747
+ name: "schema_bundle_hash",
748
+ kind: "scalar",
749
+ T: 12
750
+ /*ScalarType.BYTES*/
751
+ },
752
+ {
753
+ no: 3,
754
+ name: "canonical_schema_version",
755
+ kind: "scalar",
756
+ T: 9
757
+ /*ScalarType.STRING*/
758
+ }
759
+ ]);
760
+ }
761
+ };
762
+ var SchemaBundleRef = new SchemaBundleRef$Type();
763
+ var ContractBundleRef$Type = class extends MessageType {
764
+ constructor() {
765
+ super("spendguard.common.v1.ContractBundleRef", [
766
+ {
767
+ no: 1,
768
+ name: "bundle_id",
769
+ kind: "scalar",
770
+ T: 9
771
+ /*ScalarType.STRING*/
772
+ },
773
+ {
774
+ no: 2,
775
+ name: "bundle_hash",
776
+ kind: "scalar",
777
+ T: 12
778
+ /*ScalarType.BYTES*/
779
+ },
780
+ {
781
+ no: 3,
782
+ name: "bundle_signature",
783
+ kind: "scalar",
784
+ T: 12
785
+ /*ScalarType.BYTES*/
786
+ },
787
+ {
788
+ no: 4,
789
+ name: "signing_key_id",
790
+ kind: "scalar",
791
+ T: 9
792
+ /*ScalarType.STRING*/
793
+ }
794
+ ]);
795
+ }
796
+ };
797
+ var ContractBundleRef = new ContractBundleRef$Type();
798
+ var Idempotency$Type = class extends MessageType {
799
+ constructor() {
800
+ super("spendguard.common.v1.Idempotency", [
801
+ {
802
+ no: 1,
803
+ name: "key",
804
+ kind: "scalar",
805
+ T: 9
806
+ /*ScalarType.STRING*/
807
+ },
808
+ {
809
+ no: 2,
810
+ name: "request_hash",
811
+ kind: "scalar",
812
+ T: 12
813
+ /*ScalarType.BYTES*/
814
+ }
815
+ ]);
816
+ }
817
+ };
818
+ var Idempotency = new Idempotency$Type();
819
+ var SubscriptionMeter$Type = class extends MessageType {
820
+ constructor() {
821
+ super("spendguard.common.v1.SubscriptionMeter", [
822
+ {
823
+ no: 1,
824
+ name: "plan",
825
+ kind: "scalar",
826
+ T: 9
827
+ /*ScalarType.STRING*/
828
+ },
829
+ { no: 2, name: "period_start", kind: "message", T: () => Timestamp },
830
+ { no: 3, name: "period_end", kind: "message", T: () => Timestamp },
831
+ {
832
+ no: 4,
833
+ name: "consumed_atomic",
834
+ kind: "scalar",
835
+ T: 3
836
+ /*ScalarType.INT64*/
837
+ },
838
+ {
839
+ no: 5,
840
+ name: "monthly_cap_atomic",
841
+ kind: "scalar",
842
+ T: 3
843
+ /*ScalarType.INT64*/
844
+ },
845
+ {
846
+ no: 6,
847
+ name: "alert_at_atomic",
848
+ kind: "scalar",
849
+ T: 3
850
+ /*ScalarType.INT64*/
851
+ },
852
+ {
853
+ no: 7,
854
+ name: "hard_cap_at_atomic",
855
+ kind: "scalar",
856
+ T: 3
857
+ /*ScalarType.INT64*/
858
+ },
859
+ { no: 8, name: "cap_decision", kind: "enum", T: () => ["spendguard.common.v1.SubscriptionMeter.CapDecision", SubscriptionMeter_CapDecision] },
860
+ {
861
+ no: 9,
862
+ name: "retry_after_seconds",
863
+ kind: "scalar",
864
+ T: 3
865
+ /*ScalarType.INT64*/
866
+ }
867
+ ]);
868
+ }
869
+ };
870
+ var SubscriptionMeter = new SubscriptionMeter$Type();
871
+ var NullValue = /* @__PURE__ */ ((NullValue2) => {
872
+ NullValue2[NullValue2["NULL_VALUE"] = 0] = "NULL_VALUE";
873
+ return NullValue2;
874
+ })(NullValue || {});
875
+ var Struct$Type = class extends MessageType {
876
+ constructor() {
877
+ super("google.protobuf.Struct", [
878
+ { no: 1, name: "fields", kind: "map", K: 9, V: { kind: "message", T: () => Value } }
879
+ ]);
880
+ }
881
+ /**
882
+ * Encode `Struct` to JSON object.
883
+ */
884
+ internalJsonWrite(message, options) {
885
+ let json = {};
886
+ for (let [k, v] of Object.entries(message.fields)) {
887
+ json[k] = Value.toJson(v);
888
+ }
889
+ return json;
890
+ }
891
+ /**
892
+ * Decode `Struct` from JSON object.
893
+ */
894
+ internalJsonRead(json, options, target) {
895
+ if (!isJsonObject(json))
896
+ throw new globalThis.Error("Unable to parse message " + this.typeName + " from JSON " + typeofJsonValue(json) + ".");
897
+ if (!target)
898
+ target = this.create();
899
+ for (let [k, v] of globalThis.Object.entries(json)) {
900
+ target.fields[k] = Value.fromJson(v);
901
+ }
902
+ return target;
903
+ }
904
+ };
905
+ var Struct = new Struct$Type();
906
+ var Value$Type = class extends MessageType {
907
+ constructor() {
908
+ super("google.protobuf.Value", [
909
+ { no: 1, name: "null_value", kind: "enum", oneof: "kind", T: () => ["google.protobuf.NullValue", NullValue] },
910
+ {
911
+ no: 2,
912
+ name: "number_value",
913
+ kind: "scalar",
914
+ oneof: "kind",
915
+ T: 1
916
+ /*ScalarType.DOUBLE*/
917
+ },
918
+ {
919
+ no: 3,
920
+ name: "string_value",
921
+ kind: "scalar",
922
+ oneof: "kind",
923
+ T: 9
924
+ /*ScalarType.STRING*/
925
+ },
926
+ {
927
+ no: 4,
928
+ name: "bool_value",
929
+ kind: "scalar",
930
+ oneof: "kind",
931
+ T: 8
932
+ /*ScalarType.BOOL*/
933
+ },
934
+ { no: 5, name: "struct_value", kind: "message", oneof: "kind", T: () => Struct },
935
+ { no: 6, name: "list_value", kind: "message", oneof: "kind", T: () => ListValue }
936
+ ]);
937
+ }
938
+ /**
939
+ * Encode `Value` to JSON value.
940
+ */
941
+ internalJsonWrite(message, options) {
942
+ if (message.kind.oneofKind === void 0)
943
+ throw new globalThis.Error();
944
+ switch (message.kind.oneofKind) {
945
+ case void 0:
946
+ throw new globalThis.Error();
947
+ case "boolValue":
948
+ return message.kind.boolValue;
949
+ case "nullValue":
950
+ return null;
951
+ case "numberValue":
952
+ let numberValue = message.kind.numberValue;
953
+ if (typeof numberValue == "number" && !Number.isFinite(numberValue))
954
+ throw new globalThis.Error();
955
+ return numberValue;
956
+ case "stringValue":
957
+ return message.kind.stringValue;
958
+ case "listValue":
959
+ let listValueField = this.fields.find((f) => f.no === 6);
960
+ if (listValueField?.kind !== "message")
961
+ throw new globalThis.Error();
962
+ return listValueField.T().toJson(message.kind.listValue);
963
+ case "structValue":
964
+ let structValueField = this.fields.find((f) => f.no === 5);
965
+ if (structValueField?.kind !== "message")
966
+ throw new globalThis.Error();
967
+ return structValueField.T().toJson(message.kind.structValue);
968
+ }
969
+ }
970
+ /**
971
+ * Decode `Value` from JSON value.
972
+ */
973
+ internalJsonRead(json, options, target) {
974
+ if (!target)
975
+ target = this.create();
976
+ switch (typeof json) {
977
+ case "number":
978
+ target.kind = { oneofKind: "numberValue", numberValue: json };
979
+ break;
980
+ case "string":
981
+ target.kind = { oneofKind: "stringValue", stringValue: json };
982
+ break;
983
+ case "boolean":
984
+ target.kind = { oneofKind: "boolValue", boolValue: json };
985
+ break;
986
+ case "object":
987
+ if (json === null) {
988
+ target.kind = { oneofKind: "nullValue", nullValue: 0 /* NULL_VALUE */ };
989
+ } else if (globalThis.Array.isArray(json)) {
990
+ target.kind = { oneofKind: "listValue", listValue: ListValue.fromJson(json) };
991
+ } else {
992
+ target.kind = { oneofKind: "structValue", structValue: Struct.fromJson(json) };
993
+ }
994
+ break;
995
+ default:
996
+ throw new globalThis.Error("Unable to parse " + this.typeName + " from JSON " + typeofJsonValue(json));
997
+ }
998
+ return target;
999
+ }
1000
+ };
1001
+ var Value = new Value$Type();
1002
+ var ListValue$Type = class extends MessageType {
1003
+ constructor() {
1004
+ super("google.protobuf.ListValue", [
1005
+ { no: 1, name: "values", kind: "message", repeat: 2, T: () => Value }
1006
+ ]);
1007
+ }
1008
+ /**
1009
+ * Encode `ListValue` to JSON array.
1010
+ */
1011
+ internalJsonWrite(message, options) {
1012
+ return message.values.map((v) => Value.toJson(v));
1013
+ }
1014
+ /**
1015
+ * Decode `ListValue` from JSON array.
1016
+ */
1017
+ internalJsonRead(json, options, target) {
1018
+ if (!globalThis.Array.isArray(json))
1019
+ throw new globalThis.Error("Unable to parse " + this.typeName + " from JSON " + typeofJsonValue(json));
1020
+ if (!target)
1021
+ target = this.create();
1022
+ let values = json.map((v) => Value.fromJson(v));
1023
+ target.values.push(...values);
1024
+ return target;
1025
+ }
1026
+ };
1027
+ var ListValue = new ListValue$Type();
1028
+
1029
+ // src/_proto/spendguard/sidecar_adapter/v1/adapter.ts
1030
+ var HandshakeRequest_CapabilityLevel = /* @__PURE__ */ ((HandshakeRequest_CapabilityLevel2) => {
1031
+ HandshakeRequest_CapabilityLevel2[HandshakeRequest_CapabilityLevel2["CAPABILITY_LEVEL_UNSPECIFIED"] = 0] = "CAPABILITY_LEVEL_UNSPECIFIED";
1032
+ HandshakeRequest_CapabilityLevel2[HandshakeRequest_CapabilityLevel2["L0_INGEST"] = 16] = "L0_INGEST";
1033
+ HandshakeRequest_CapabilityLevel2[HandshakeRequest_CapabilityLevel2["L1_LLM_CALL"] = 32] = "L1_LLM_CALL";
1034
+ HandshakeRequest_CapabilityLevel2[HandshakeRequest_CapabilityLevel2["L2_STEP"] = 48] = "L2_STEP";
1035
+ HandshakeRequest_CapabilityLevel2[HandshakeRequest_CapabilityLevel2["L3_POLICY_HOOK"] = 64] = "L3_POLICY_HOOK";
1036
+ HandshakeRequest_CapabilityLevel2[HandshakeRequest_CapabilityLevel2["L4_RUNTIME_NATIVE"] = 80] = "L4_RUNTIME_NATIVE";
1037
+ return HandshakeRequest_CapabilityLevel2;
1038
+ })(HandshakeRequest_CapabilityLevel || {});
1039
+ var DecisionRequest_Trigger = /* @__PURE__ */ ((DecisionRequest_Trigger2) => {
1040
+ DecisionRequest_Trigger2[DecisionRequest_Trigger2["TRIGGER_UNSPECIFIED"] = 0] = "TRIGGER_UNSPECIFIED";
1041
+ DecisionRequest_Trigger2[DecisionRequest_Trigger2["RUN_PRE"] = 1] = "RUN_PRE";
1042
+ DecisionRequest_Trigger2[DecisionRequest_Trigger2["AGENT_STEP_PRE"] = 2] = "AGENT_STEP_PRE";
1043
+ DecisionRequest_Trigger2[DecisionRequest_Trigger2["LLM_CALL_PRE"] = 3] = "LLM_CALL_PRE";
1044
+ DecisionRequest_Trigger2[DecisionRequest_Trigger2["TOOL_CALL_PRE"] = 4] = "TOOL_CALL_PRE";
1045
+ return DecisionRequest_Trigger2;
1046
+ })(DecisionRequest_Trigger || {});
1047
+ var DecisionResponse_Decision = /* @__PURE__ */ ((DecisionResponse_Decision2) => {
1048
+ DecisionResponse_Decision2[DecisionResponse_Decision2["DECISION_UNSPECIFIED"] = 0] = "DECISION_UNSPECIFIED";
1049
+ DecisionResponse_Decision2[DecisionResponse_Decision2["CONTINUE"] = 1] = "CONTINUE";
1050
+ DecisionResponse_Decision2[DecisionResponse_Decision2["DEGRADE"] = 2] = "DEGRADE";
1051
+ DecisionResponse_Decision2[DecisionResponse_Decision2["SKIP"] = 3] = "SKIP";
1052
+ DecisionResponse_Decision2[DecisionResponse_Decision2["STOP"] = 4] = "STOP";
1053
+ DecisionResponse_Decision2[DecisionResponse_Decision2["REQUIRE_APPROVAL"] = 5] = "REQUIRE_APPROVAL";
1054
+ DecisionResponse_Decision2[DecisionResponse_Decision2["STOP_RUN_PROJECTION"] = 6] = "STOP_RUN_PROJECTION";
1055
+ return DecisionResponse_Decision2;
1056
+ })(DecisionResponse_Decision || {});
1057
+ var PublishOutcomeRequest_Outcome = /* @__PURE__ */ ((PublishOutcomeRequest_Outcome2) => {
1058
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["OUTCOME_UNSPECIFIED"] = 0] = "OUTCOME_UNSPECIFIED";
1059
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["APPLIED"] = 1] = "APPLIED";
1060
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["APPLIED_NOOP"] = 2] = "APPLIED_NOOP";
1061
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["APPLY_FAILED"] = 3] = "APPLY_FAILED";
1062
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["APPROVAL_GRANTED"] = 4] = "APPROVAL_GRANTED";
1063
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["APPROVAL_DENIED"] = 5] = "APPROVAL_DENIED";
1064
+ PublishOutcomeRequest_Outcome2[PublishOutcomeRequest_Outcome2["APPROVAL_TIMED_OUT"] = 6] = "APPROVAL_TIMED_OUT";
1065
+ return PublishOutcomeRequest_Outcome2;
1066
+ })(PublishOutcomeRequest_Outcome || {});
1067
+ var TraceEvent_EventKind = /* @__PURE__ */ ((TraceEvent_EventKind2) => {
1068
+ TraceEvent_EventKind2[TraceEvent_EventKind2["EVENT_KIND_UNSPECIFIED"] = 0] = "EVENT_KIND_UNSPECIFIED";
1069
+ TraceEvent_EventKind2[TraceEvent_EventKind2["RUN_START"] = 1] = "RUN_START";
1070
+ TraceEvent_EventKind2[TraceEvent_EventKind2["RUN_END"] = 2] = "RUN_END";
1071
+ TraceEvent_EventKind2[TraceEvent_EventKind2["AGENT_STEP_POST"] = 3] = "AGENT_STEP_POST";
1072
+ TraceEvent_EventKind2[TraceEvent_EventKind2["LLM_CALL_POST"] = 4] = "LLM_CALL_POST";
1073
+ TraceEvent_EventKind2[TraceEvent_EventKind2["TOOL_CALL_POST"] = 5] = "TOOL_CALL_POST";
1074
+ TraceEvent_EventKind2[TraceEvent_EventKind2["SPAN_DELTA"] = 6] = "SPAN_DELTA";
1075
+ TraceEvent_EventKind2[TraceEvent_EventKind2["APPROVAL_LIFECYCLE"] = 7] = "APPROVAL_LIFECYCLE";
1076
+ TraceEvent_EventKind2[TraceEvent_EventKind2["ROLLBACK"] = 8] = "ROLLBACK";
1077
+ return TraceEvent_EventKind2;
1078
+ })(TraceEvent_EventKind || {});
1079
+ var LlmCallPostPayload_Outcome = /* @__PURE__ */ ((LlmCallPostPayload_Outcome2) => {
1080
+ LlmCallPostPayload_Outcome2[LlmCallPostPayload_Outcome2["OUTCOME_UNSPECIFIED"] = 0] = "OUTCOME_UNSPECIFIED";
1081
+ LlmCallPostPayload_Outcome2[LlmCallPostPayload_Outcome2["SUCCESS"] = 1] = "SUCCESS";
1082
+ LlmCallPostPayload_Outcome2[LlmCallPostPayload_Outcome2["PROVIDER_ERROR"] = 2] = "PROVIDER_ERROR";
1083
+ LlmCallPostPayload_Outcome2[LlmCallPostPayload_Outcome2["CLIENT_TIMEOUT"] = 3] = "CLIENT_TIMEOUT";
1084
+ LlmCallPostPayload_Outcome2[LlmCallPostPayload_Outcome2["RUN_ABORTED"] = 4] = "RUN_ABORTED";
1085
+ return LlmCallPostPayload_Outcome2;
1086
+ })(LlmCallPostPayload_Outcome || {});
1087
+ var ApprovalLifecyclePayload_State = /* @__PURE__ */ ((ApprovalLifecyclePayload_State2) => {
1088
+ ApprovalLifecyclePayload_State2[ApprovalLifecyclePayload_State2["STATE_UNSPECIFIED"] = 0] = "STATE_UNSPECIFIED";
1089
+ ApprovalLifecyclePayload_State2[ApprovalLifecyclePayload_State2["REQUESTED"] = 1] = "REQUESTED";
1090
+ ApprovalLifecyclePayload_State2[ApprovalLifecyclePayload_State2["GRANTED"] = 2] = "GRANTED";
1091
+ ApprovalLifecyclePayload_State2[ApprovalLifecyclePayload_State2["DENIED"] = 3] = "DENIED";
1092
+ ApprovalLifecyclePayload_State2[ApprovalLifecyclePayload_State2["EXPIRED"] = 4] = "EXPIRED";
1093
+ return ApprovalLifecyclePayload_State2;
1094
+ })(ApprovalLifecyclePayload_State || {});
1095
+ var TraceEventAck_Status = /* @__PURE__ */ ((TraceEventAck_Status2) => {
1096
+ TraceEventAck_Status2[TraceEventAck_Status2["STATUS_UNSPECIFIED"] = 0] = "STATUS_UNSPECIFIED";
1097
+ TraceEventAck_Status2[TraceEventAck_Status2["ACCEPTED"] = 1] = "ACCEPTED";
1098
+ TraceEventAck_Status2[TraceEventAck_Status2["QUARANTINED"] = 2] = "QUARANTINED";
1099
+ TraceEventAck_Status2[TraceEventAck_Status2["REJECTED"] = 3] = "REJECTED";
1100
+ return TraceEventAck_Status2;
1101
+ })(TraceEventAck_Status || {});
1102
+ var DrainSignal_Phase = /* @__PURE__ */ ((DrainSignal_Phase2) => {
1103
+ DrainSignal_Phase2[DrainSignal_Phase2["PHASE_UNSPECIFIED"] = 0] = "PHASE_UNSPECIFIED";
1104
+ DrainSignal_Phase2[DrainSignal_Phase2["DRAIN_INITIATED"] = 1] = "DRAIN_INITIATED";
1105
+ DrainSignal_Phase2[DrainSignal_Phase2["DRAIN_TIMEOUT_APPROACHING"] = 2] = "DRAIN_TIMEOUT_APPROACHING";
1106
+ DrainSignal_Phase2[DrainSignal_Phase2["DRAIN_COMPLETE"] = 3] = "DRAIN_COMPLETE";
1107
+ return DrainSignal_Phase2;
1108
+ })(DrainSignal_Phase || {});
1109
+ var CommitSessionDeltaRequest_Outcome = /* @__PURE__ */ ((CommitSessionDeltaRequest_Outcome2) => {
1110
+ CommitSessionDeltaRequest_Outcome2[CommitSessionDeltaRequest_Outcome2["OUTCOME_UNSPECIFIED"] = 0] = "OUTCOME_UNSPECIFIED";
1111
+ CommitSessionDeltaRequest_Outcome2[CommitSessionDeltaRequest_Outcome2["SUCCESS"] = 1] = "SUCCESS";
1112
+ CommitSessionDeltaRequest_Outcome2[CommitSessionDeltaRequest_Outcome2["PROVIDER_ERROR"] = 2] = "PROVIDER_ERROR";
1113
+ CommitSessionDeltaRequest_Outcome2[CommitSessionDeltaRequest_Outcome2["CLIENT_TIMEOUT"] = 3] = "CLIENT_TIMEOUT";
1114
+ CommitSessionDeltaRequest_Outcome2[CommitSessionDeltaRequest_Outcome2["RUN_ABORTED"] = 4] = "RUN_ABORTED";
1115
+ return CommitSessionDeltaRequest_Outcome2;
1116
+ })(CommitSessionDeltaRequest_Outcome || {});
1117
+ var ResumeAfterApprovalRequest$Type = class extends MessageType {
1118
+ constructor() {
1119
+ super("spendguard.sidecar_adapter.v1.ResumeAfterApprovalRequest", [
1120
+ {
1121
+ no: 1,
1122
+ name: "tenant_id",
1123
+ kind: "scalar",
1124
+ T: 9
1125
+ /*ScalarType.STRING*/
1126
+ },
1127
+ {
1128
+ no: 2,
1129
+ name: "decision_id",
1130
+ kind: "scalar",
1131
+ T: 9
1132
+ /*ScalarType.STRING*/
1133
+ },
1134
+ {
1135
+ no: 3,
1136
+ name: "approval_id",
1137
+ kind: "scalar",
1138
+ T: 9
1139
+ /*ScalarType.STRING*/
1140
+ },
1141
+ {
1142
+ no: 4,
1143
+ name: "workload_instance_id",
1144
+ kind: "scalar",
1145
+ T: 9
1146
+ /*ScalarType.STRING*/
1147
+ },
1148
+ {
1149
+ no: 5,
1150
+ name: "session_id",
1151
+ kind: "scalar",
1152
+ T: 9
1153
+ /*ScalarType.STRING*/
1154
+ }
1155
+ ]);
1156
+ }
1157
+ };
1158
+ var ResumeAfterApprovalRequest = new ResumeAfterApprovalRequest$Type();
1159
+ var ResumeAfterApprovalResponse$Type = class extends MessageType {
1160
+ constructor() {
1161
+ super("spendguard.sidecar_adapter.v1.ResumeAfterApprovalResponse", [
1162
+ { no: 1, name: "decision", kind: "message", oneof: "outcome", T: () => DecisionResponse },
1163
+ { no: 2, name: "denied", kind: "message", oneof: "outcome", T: () => ResumeAfterApprovalDenied },
1164
+ { no: 3, name: "error", kind: "message", oneof: "outcome", T: () => Error2 }
1165
+ ]);
1166
+ }
1167
+ };
1168
+ var ResumeAfterApprovalResponse = new ResumeAfterApprovalResponse$Type();
1169
+ var ResumeAfterApprovalDenied$Type = class extends MessageType {
1170
+ constructor() {
1171
+ super("spendguard.sidecar_adapter.v1.ResumeAfterApprovalDenied", [
1172
+ {
1173
+ no: 1,
1174
+ name: "audit_decision_event_id",
1175
+ kind: "scalar",
1176
+ T: 9
1177
+ /*ScalarType.STRING*/
1178
+ },
1179
+ {
1180
+ no: 2,
1181
+ name: "approver_reason",
1182
+ kind: "scalar",
1183
+ T: 9
1184
+ /*ScalarType.STRING*/
1185
+ },
1186
+ {
1187
+ no: 3,
1188
+ name: "approver_subject",
1189
+ kind: "scalar",
1190
+ T: 9
1191
+ /*ScalarType.STRING*/
1192
+ },
1193
+ {
1194
+ no: 4,
1195
+ name: "matched_rule_ids",
1196
+ kind: "scalar",
1197
+ repeat: 2,
1198
+ T: 9
1199
+ /*ScalarType.STRING*/
1200
+ }
1201
+ ]);
1202
+ }
1203
+ };
1204
+ var ResumeAfterApprovalDenied = new ResumeAfterApprovalDenied$Type();
1205
+ var HandshakeRequest$Type = class extends MessageType {
1206
+ constructor() {
1207
+ super("spendguard.sidecar_adapter.v1.HandshakeRequest", [
1208
+ {
1209
+ no: 1,
1210
+ name: "sdk_version",
1211
+ kind: "scalar",
1212
+ T: 9
1213
+ /*ScalarType.STRING*/
1214
+ },
1215
+ {
1216
+ no: 2,
1217
+ name: "runtime_kind",
1218
+ kind: "scalar",
1219
+ T: 9
1220
+ /*ScalarType.STRING*/
1221
+ },
1222
+ {
1223
+ no: 3,
1224
+ name: "runtime_version",
1225
+ kind: "scalar",
1226
+ T: 9
1227
+ /*ScalarType.STRING*/
1228
+ },
1229
+ { no: 4, name: "capability_level", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.HandshakeRequest.CapabilityLevel", HandshakeRequest_CapabilityLevel] },
1230
+ {
1231
+ no: 5,
1232
+ name: "tenant_id_assertion",
1233
+ kind: "scalar",
1234
+ T: 9
1235
+ /*ScalarType.STRING*/
1236
+ },
1237
+ {
1238
+ no: 6,
1239
+ name: "workload_instance_id",
1240
+ kind: "scalar",
1241
+ T: 9
1242
+ /*ScalarType.STRING*/
1243
+ },
1244
+ {
1245
+ no: 7,
1246
+ name: "protocol_version",
1247
+ kind: "scalar",
1248
+ T: 13
1249
+ /*ScalarType.UINT32*/
1250
+ }
1251
+ ]);
1252
+ }
1253
+ };
1254
+ var HandshakeRequest = new HandshakeRequest$Type();
1255
+ var HandshakeResponse$Type = class extends MessageType {
1256
+ constructor() {
1257
+ super("spendguard.sidecar_adapter.v1.HandshakeResponse", [
1258
+ {
1259
+ no: 1,
1260
+ name: "sidecar_version",
1261
+ kind: "scalar",
1262
+ T: 9
1263
+ /*ScalarType.STRING*/
1264
+ },
1265
+ { no: 2, name: "schema_bundle", kind: "message", T: () => SchemaBundleRef },
1266
+ { no: 3, name: "contract_bundle", kind: "message", T: () => ContractBundleRef },
1267
+ { no: 4, name: "capability_required", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.HandshakeRequest.CapabilityLevel", HandshakeRequest_CapabilityLevel] },
1268
+ { no: 5, name: "active_key_epochs", kind: "message", T: () => HandshakeResponse_KeyEpochs },
1269
+ {
1270
+ no: 6,
1271
+ name: "protocol_version",
1272
+ kind: "scalar",
1273
+ T: 13
1274
+ /*ScalarType.UINT32*/
1275
+ },
1276
+ {
1277
+ no: 7,
1278
+ name: "session_id",
1279
+ kind: "scalar",
1280
+ T: 9
1281
+ /*ScalarType.STRING*/
1282
+ },
1283
+ {
1284
+ no: 8,
1285
+ name: "signing_key_id",
1286
+ kind: "scalar",
1287
+ T: 9
1288
+ /*ScalarType.STRING*/
1289
+ },
1290
+ {
1291
+ no: 9,
1292
+ name: "announcement_signature",
1293
+ kind: "scalar",
1294
+ T: 12
1295
+ /*ScalarType.BYTES*/
1296
+ }
1297
+ ]);
1298
+ }
1299
+ };
1300
+ var HandshakeResponse = new HandshakeResponse$Type();
1301
+ var HandshakeResponse_KeyEpochs$Type = class extends MessageType {
1302
+ constructor() {
1303
+ super("spendguard.sidecar_adapter.v1.HandshakeResponse.KeyEpochs", [
1304
+ {
1305
+ no: 1,
1306
+ name: "producer_signing_key_epochs",
1307
+ kind: "scalar",
1308
+ repeat: 2,
1309
+ T: 9
1310
+ /*ScalarType.STRING*/
1311
+ },
1312
+ {
1313
+ no: 2,
1314
+ name: "hmac_tenant_salt_epochs",
1315
+ kind: "scalar",
1316
+ repeat: 2,
1317
+ T: 9
1318
+ /*ScalarType.STRING*/
1319
+ }
1320
+ ]);
1321
+ }
1322
+ };
1323
+ var HandshakeResponse_KeyEpochs = new HandshakeResponse_KeyEpochs$Type();
1324
+ var ClaimEstimate$Type = class extends MessageType {
1325
+ constructor() {
1326
+ super("spendguard.sidecar_adapter.v1.ClaimEstimate", [
1327
+ {
1328
+ no: 1,
1329
+ name: "tokenizer_tier",
1330
+ kind: "scalar",
1331
+ T: 9
1332
+ /*ScalarType.STRING*/
1333
+ },
1334
+ {
1335
+ no: 2,
1336
+ name: "tokenizer_version_id",
1337
+ kind: "scalar",
1338
+ T: 9
1339
+ /*ScalarType.STRING*/
1340
+ },
1341
+ {
1342
+ no: 3,
1343
+ name: "input_tokens",
1344
+ kind: "scalar",
1345
+ T: 3
1346
+ /*ScalarType.INT64*/
1347
+ },
1348
+ {
1349
+ no: 4,
1350
+ name: "predicted_a_tokens",
1351
+ kind: "scalar",
1352
+ T: 3
1353
+ /*ScalarType.INT64*/
1354
+ },
1355
+ {
1356
+ no: 5,
1357
+ name: "predicted_b_tokens",
1358
+ kind: "scalar",
1359
+ T: 3
1360
+ /*ScalarType.INT64*/
1361
+ },
1362
+ {
1363
+ no: 6,
1364
+ name: "predicted_c_tokens",
1365
+ kind: "scalar",
1366
+ T: 3
1367
+ /*ScalarType.INT64*/
1368
+ },
1369
+ {
1370
+ no: 7,
1371
+ name: "reserved_strategy",
1372
+ kind: "scalar",
1373
+ T: 9
1374
+ /*ScalarType.STRING*/
1375
+ },
1376
+ {
1377
+ no: 8,
1378
+ name: "prediction_strategy_used",
1379
+ kind: "scalar",
1380
+ T: 9
1381
+ /*ScalarType.STRING*/
1382
+ },
1383
+ {
1384
+ no: 9,
1385
+ name: "prediction_policy_used",
1386
+ kind: "scalar",
1387
+ T: 9
1388
+ /*ScalarType.STRING*/
1389
+ },
1390
+ {
1391
+ no: 10,
1392
+ name: "prediction_confidence",
1393
+ kind: "scalar",
1394
+ T: 2
1395
+ /*ScalarType.FLOAT*/
1396
+ },
1397
+ {
1398
+ no: 11,
1399
+ name: "prediction_sample_size",
1400
+ kind: "scalar",
1401
+ T: 3
1402
+ /*ScalarType.INT64*/
1403
+ },
1404
+ {
1405
+ no: 12,
1406
+ name: "cold_start_layer_used",
1407
+ kind: "scalar",
1408
+ T: 9
1409
+ /*ScalarType.STRING*/
1410
+ },
1411
+ {
1412
+ no: 13,
1413
+ name: "classifier_version",
1414
+ kind: "scalar",
1415
+ T: 9
1416
+ /*ScalarType.STRING*/
1417
+ },
1418
+ {
1419
+ no: 14,
1420
+ name: "fingerprint_version",
1421
+ kind: "scalar",
1422
+ T: 9
1423
+ /*ScalarType.STRING*/
1424
+ },
1425
+ {
1426
+ no: 15,
1427
+ name: "prompt_class_fingerprint",
1428
+ kind: "scalar",
1429
+ T: 9
1430
+ /*ScalarType.STRING*/
1431
+ },
1432
+ {
1433
+ no: 16,
1434
+ name: "run_projection_at_decision_atomic",
1435
+ kind: "scalar",
1436
+ T: 3
1437
+ /*ScalarType.INT64*/
1438
+ },
1439
+ {
1440
+ no: 17,
1441
+ name: "run_predicted_remaining_steps",
1442
+ kind: "scalar",
1443
+ T: 5
1444
+ /*ScalarType.INT32*/
1445
+ },
1446
+ {
1447
+ no: 18,
1448
+ name: "run_steps_completed_so_far",
1449
+ kind: "scalar",
1450
+ T: 3
1451
+ /*ScalarType.INT64*/
1452
+ },
1453
+ {
1454
+ no: 19,
1455
+ name: "run_code_triggered",
1456
+ kind: "scalar",
1457
+ T: 9
1458
+ /*ScalarType.STRING*/
1459
+ },
1460
+ {
1461
+ no: 20,
1462
+ name: "model",
1463
+ kind: "scalar",
1464
+ T: 9
1465
+ /*ScalarType.STRING*/
1466
+ },
1467
+ {
1468
+ no: 21,
1469
+ name: "prompt_class",
1470
+ kind: "scalar",
1471
+ T: 9
1472
+ /*ScalarType.STRING*/
1473
+ }
1474
+ ]);
1475
+ }
1476
+ };
1477
+ var ClaimEstimate = new ClaimEstimate$Type();
1478
+ var DecisionRequest$Type = class extends MessageType {
1479
+ constructor() {
1480
+ super("spendguard.sidecar_adapter.v1.DecisionRequest", [
1481
+ {
1482
+ no: 1,
1483
+ name: "session_id",
1484
+ kind: "scalar",
1485
+ T: 9
1486
+ /*ScalarType.STRING*/
1487
+ },
1488
+ { no: 2, name: "trigger", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.DecisionRequest.Trigger", DecisionRequest_Trigger] },
1489
+ { no: 3, name: "trace", kind: "message", T: () => TraceContext },
1490
+ { no: 4, name: "ids", kind: "message", T: () => SpendGuardIds },
1491
+ {
1492
+ no: 5,
1493
+ name: "route",
1494
+ kind: "scalar",
1495
+ T: 9
1496
+ /*ScalarType.STRING*/
1497
+ },
1498
+ { no: 6, name: "inputs", kind: "message", T: () => DecisionRequest_Inputs },
1499
+ {
1500
+ no: 7,
1501
+ name: "parent_run_id",
1502
+ kind: "scalar",
1503
+ T: 9
1504
+ /*ScalarType.STRING*/
1505
+ },
1506
+ {
1507
+ no: 8,
1508
+ name: "budget_grant_jti",
1509
+ kind: "scalar",
1510
+ T: 9
1511
+ /*ScalarType.STRING*/
1512
+ },
1513
+ { no: 9, name: "idempotency", kind: "message", T: () => Idempotency },
1514
+ {
1515
+ no: 17,
1516
+ name: "planned_steps_hint",
1517
+ kind: "scalar",
1518
+ T: 5
1519
+ /*ScalarType.INT32*/
1520
+ },
1521
+ { no: 18, name: "reservation_source", kind: "enum", T: () => ["spendguard.common.v1.ReservationSource", ReservationSource, "RESERVATION_SOURCE_"] },
1522
+ {
1523
+ no: 19,
1524
+ name: "meter_only_estimate",
1525
+ kind: "scalar",
1526
+ T: 8
1527
+ /*ScalarType.BOOL*/
1528
+ }
1529
+ ]);
1530
+ }
1531
+ };
1532
+ var DecisionRequest = new DecisionRequest$Type();
1533
+ var DecisionRequest_Inputs$Type = class extends MessageType {
1534
+ constructor() {
1535
+ super("spendguard.sidecar_adapter.v1.DecisionRequest.Inputs", [
1536
+ { no: 1, name: "projected_claims", kind: "message", repeat: 2, T: () => BudgetClaim },
1537
+ {
1538
+ no: 2,
1539
+ name: "projected_p50_atomic",
1540
+ kind: "scalar",
1541
+ T: 9
1542
+ /*ScalarType.STRING*/
1543
+ },
1544
+ {
1545
+ no: 3,
1546
+ name: "projected_p90_atomic",
1547
+ kind: "scalar",
1548
+ T: 9
1549
+ /*ScalarType.STRING*/
1550
+ },
1551
+ {
1552
+ no: 4,
1553
+ name: "projected_p95_atomic",
1554
+ kind: "scalar",
1555
+ T: 9
1556
+ /*ScalarType.STRING*/
1557
+ },
1558
+ {
1559
+ no: 5,
1560
+ name: "projected_p99_atomic",
1561
+ kind: "scalar",
1562
+ T: 9
1563
+ /*ScalarType.STRING*/
1564
+ },
1565
+ { no: 6, name: "projected_unit", kind: "message", T: () => UnitRef },
1566
+ { no: 7, name: "runtime_metadata", kind: "message", T: () => Struct },
1567
+ { no: 8, name: "claim_estimate", kind: "message", T: () => ClaimEstimate }
1568
+ ]);
1569
+ }
1570
+ };
1571
+ var DecisionRequest_Inputs = new DecisionRequest_Inputs$Type();
1572
+ var DecisionResponse$Type = class extends MessageType {
1573
+ constructor() {
1574
+ super("spendguard.sidecar_adapter.v1.DecisionResponse", [
1575
+ {
1576
+ no: 1,
1577
+ name: "decision_id",
1578
+ kind: "scalar",
1579
+ T: 9
1580
+ /*ScalarType.STRING*/
1581
+ },
1582
+ {
1583
+ no: 2,
1584
+ name: "audit_decision_event_id",
1585
+ kind: "scalar",
1586
+ T: 9
1587
+ /*ScalarType.STRING*/
1588
+ },
1589
+ { no: 3, name: "decision", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.DecisionResponse.Decision", DecisionResponse_Decision] },
1590
+ {
1591
+ no: 4,
1592
+ name: "reason_codes",
1593
+ kind: "scalar",
1594
+ repeat: 2,
1595
+ T: 9
1596
+ /*ScalarType.STRING*/
1597
+ },
1598
+ {
1599
+ no: 5,
1600
+ name: "matched_rule_ids",
1601
+ kind: "scalar",
1602
+ repeat: 2,
1603
+ T: 9
1604
+ /*ScalarType.STRING*/
1605
+ },
1606
+ {
1607
+ no: 6,
1608
+ name: "mutation_patch_json",
1609
+ kind: "scalar",
1610
+ T: 9
1611
+ /*ScalarType.STRING*/
1612
+ },
1613
+ {
1614
+ no: 7,
1615
+ name: "effect_hash",
1616
+ kind: "scalar",
1617
+ T: 12
1618
+ /*ScalarType.BYTES*/
1619
+ },
1620
+ {
1621
+ no: 8,
1622
+ name: "ledger_transaction_id",
1623
+ kind: "scalar",
1624
+ T: 9
1625
+ /*ScalarType.STRING*/
1626
+ },
1627
+ {
1628
+ no: 9,
1629
+ name: "reservation_ids",
1630
+ kind: "scalar",
1631
+ repeat: 2,
1632
+ T: 9
1633
+ /*ScalarType.STRING*/
1634
+ },
1635
+ { no: 10, name: "ttl_expires_at", kind: "message", T: () => Timestamp },
1636
+ {
1637
+ no: 11,
1638
+ name: "approval_request_id",
1639
+ kind: "scalar",
1640
+ T: 9
1641
+ /*ScalarType.STRING*/
1642
+ },
1643
+ { no: 12, name: "approval_ttl", kind: "message", T: () => Timestamp },
1644
+ {
1645
+ no: 13,
1646
+ name: "approver_role",
1647
+ kind: "scalar",
1648
+ T: 9
1649
+ /*ScalarType.STRING*/
1650
+ },
1651
+ {
1652
+ no: 14,
1653
+ name: "terminal",
1654
+ kind: "scalar",
1655
+ T: 8
1656
+ /*ScalarType.BOOL*/
1657
+ },
1658
+ { no: 15, name: "error", kind: "message", T: () => Error2 },
1659
+ {
1660
+ no: 16,
1661
+ name: "run_code_triggered",
1662
+ kind: "scalar",
1663
+ T: 9
1664
+ /*ScalarType.STRING*/
1665
+ },
1666
+ { no: 17, name: "subscription_meter", kind: "message", T: () => SubscriptionMeter }
1667
+ ]);
1668
+ }
1669
+ };
1670
+ var DecisionResponse = new DecisionResponse$Type();
1671
+ var PublishOutcomeRequest$Type = class extends MessageType {
1672
+ constructor() {
1673
+ super("spendguard.sidecar_adapter.v1.PublishOutcomeRequest", [
1674
+ {
1675
+ no: 1,
1676
+ name: "session_id",
1677
+ kind: "scalar",
1678
+ T: 9
1679
+ /*ScalarType.STRING*/
1680
+ },
1681
+ {
1682
+ no: 2,
1683
+ name: "decision_id",
1684
+ kind: "scalar",
1685
+ T: 9
1686
+ /*ScalarType.STRING*/
1687
+ },
1688
+ {
1689
+ no: 3,
1690
+ name: "effect_hash",
1691
+ kind: "scalar",
1692
+ T: 12
1693
+ /*ScalarType.BYTES*/
1694
+ },
1695
+ { no: 4, name: "outcome", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.PublishOutcomeRequest.Outcome", PublishOutcomeRequest_Outcome] },
1696
+ {
1697
+ no: 5,
1698
+ name: "adapter_error",
1699
+ kind: "scalar",
1700
+ T: 9
1701
+ /*ScalarType.STRING*/
1702
+ }
1703
+ ]);
1704
+ }
1705
+ };
1706
+ var PublishOutcomeRequest = new PublishOutcomeRequest$Type();
1707
+ var PublishOutcomeResponse$Type = class extends MessageType {
1708
+ constructor() {
1709
+ super("spendguard.sidecar_adapter.v1.PublishOutcomeResponse", [
1710
+ {
1711
+ no: 1,
1712
+ name: "audit_outcome_event_id",
1713
+ kind: "scalar",
1714
+ T: 9
1715
+ /*ScalarType.STRING*/
1716
+ },
1717
+ { no: 2, name: "recorded_at", kind: "message", T: () => Timestamp },
1718
+ { no: 3, name: "error", kind: "message", T: () => Error2 }
1719
+ ]);
1720
+ }
1721
+ };
1722
+ var PublishOutcomeResponse = new PublishOutcomeResponse$Type();
1723
+ var TraceEvent$Type = class extends MessageType {
1724
+ constructor() {
1725
+ super("spendguard.sidecar_adapter.v1.TraceEvent", [
1726
+ {
1727
+ no: 1,
1728
+ name: "session_id",
1729
+ kind: "scalar",
1730
+ T: 9
1731
+ /*ScalarType.STRING*/
1732
+ },
1733
+ { no: 2, name: "trace", kind: "message", T: () => TraceContext },
1734
+ { no: 3, name: "ids", kind: "message", T: () => SpendGuardIds },
1735
+ { no: 4, name: "kind", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.TraceEvent.EventKind", TraceEvent_EventKind] },
1736
+ { no: 5, name: "event_time", kind: "message", T: () => Timestamp },
1737
+ { no: 6, name: "generic", kind: "message", oneof: "payload", T: () => Struct },
1738
+ { no: 8, name: "llm_call_post", kind: "message", oneof: "payload", T: () => LlmCallPostPayload },
1739
+ { no: 9, name: "approval", kind: "message", oneof: "payload", T: () => ApprovalLifecyclePayload },
1740
+ { no: 10, name: "rollback", kind: "message", oneof: "payload", T: () => RollbackPayload },
1741
+ {
1742
+ no: 7,
1743
+ name: "provider_response_metadata",
1744
+ kind: "scalar",
1745
+ T: 9
1746
+ /*ScalarType.STRING*/
1747
+ }
1748
+ ]);
1749
+ }
1750
+ };
1751
+ var TraceEvent = new TraceEvent$Type();
1752
+ var LlmCallPostPayload$Type = class extends MessageType {
1753
+ constructor() {
1754
+ super("spendguard.sidecar_adapter.v1.LlmCallPostPayload", [
1755
+ {
1756
+ no: 1,
1757
+ name: "reservation_id",
1758
+ kind: "scalar",
1759
+ T: 9
1760
+ /*ScalarType.STRING*/
1761
+ },
1762
+ {
1763
+ no: 2,
1764
+ name: "provider_reported_amount_atomic",
1765
+ kind: "scalar",
1766
+ T: 9
1767
+ /*ScalarType.STRING*/
1768
+ },
1769
+ { no: 3, name: "unit", kind: "message", T: () => UnitRef },
1770
+ { no: 4, name: "pricing", kind: "message", T: () => PricingFreeze },
1771
+ {
1772
+ no: 5,
1773
+ name: "provider_event_id",
1774
+ kind: "scalar",
1775
+ T: 9
1776
+ /*ScalarType.STRING*/
1777
+ },
1778
+ { no: 6, name: "outcome", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.LlmCallPostPayload.Outcome", LlmCallPostPayload_Outcome] },
1779
+ {
1780
+ no: 7,
1781
+ name: "estimated_amount_atomic",
1782
+ kind: "scalar",
1783
+ T: 9
1784
+ /*ScalarType.STRING*/
1785
+ },
1786
+ {
1787
+ no: 8,
1788
+ name: "actual_input_tokens",
1789
+ kind: "scalar",
1790
+ opt: true,
1791
+ T: 3
1792
+ /*ScalarType.INT64*/
1793
+ },
1794
+ {
1795
+ no: 9,
1796
+ name: "actual_output_tokens",
1797
+ kind: "scalar",
1798
+ opt: true,
1799
+ T: 3
1800
+ /*ScalarType.INT64*/
1801
+ },
1802
+ {
1803
+ no: 10,
1804
+ name: "delta_b_ratio",
1805
+ kind: "scalar",
1806
+ opt: true,
1807
+ T: 2
1808
+ /*ScalarType.FLOAT*/
1809
+ },
1810
+ {
1811
+ no: 11,
1812
+ name: "delta_c_ratio",
1813
+ kind: "scalar",
1814
+ opt: true,
1815
+ T: 2
1816
+ /*ScalarType.FLOAT*/
1817
+ }
1818
+ ]);
1819
+ }
1820
+ };
1821
+ var LlmCallPostPayload = new LlmCallPostPayload$Type();
1822
+ var ApprovalLifecyclePayload$Type = class extends MessageType {
1823
+ constructor() {
1824
+ super("spendguard.sidecar_adapter.v1.ApprovalLifecyclePayload", [
1825
+ {
1826
+ no: 1,
1827
+ name: "approval_request_id",
1828
+ kind: "scalar",
1829
+ T: 9
1830
+ /*ScalarType.STRING*/
1831
+ },
1832
+ { no: 2, name: "state", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.ApprovalLifecyclePayload.State", ApprovalLifecyclePayload_State] },
1833
+ {
1834
+ no: 3,
1835
+ name: "approver",
1836
+ kind: "scalar",
1837
+ T: 9
1838
+ /*ScalarType.STRING*/
1839
+ },
1840
+ { no: 4, name: "deadline", kind: "message", T: () => Timestamp }
1841
+ ]);
1842
+ }
1843
+ };
1844
+ var ApprovalLifecyclePayload = new ApprovalLifecyclePayload$Type();
1845
+ var RollbackPayload$Type = class extends MessageType {
1846
+ constructor() {
1847
+ super("spendguard.sidecar_adapter.v1.RollbackPayload", [
1848
+ {
1849
+ no: 1,
1850
+ name: "ledger_transaction_id",
1851
+ kind: "scalar",
1852
+ T: 9
1853
+ /*ScalarType.STRING*/
1854
+ },
1855
+ {
1856
+ no: 2,
1857
+ name: "reason_code",
1858
+ kind: "scalar",
1859
+ T: 9
1860
+ /*ScalarType.STRING*/
1861
+ },
1862
+ {
1863
+ no: 3,
1864
+ name: "compensating_ledger_transaction_id",
1865
+ kind: "scalar",
1866
+ T: 9
1867
+ /*ScalarType.STRING*/
1868
+ }
1869
+ ]);
1870
+ }
1871
+ };
1872
+ var RollbackPayload = new RollbackPayload$Type();
1873
+ var TraceEventAck$Type = class extends MessageType {
1874
+ constructor() {
1875
+ super("spendguard.sidecar_adapter.v1.TraceEventAck", [
1876
+ {
1877
+ no: 1,
1878
+ name: "event_id",
1879
+ kind: "scalar",
1880
+ T: 9
1881
+ /*ScalarType.STRING*/
1882
+ },
1883
+ { no: 2, name: "status", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.TraceEventAck.Status", TraceEventAck_Status] },
1884
+ { no: 3, name: "error", kind: "message", T: () => Error2 }
1885
+ ]);
1886
+ }
1887
+ };
1888
+ var TraceEventAck = new TraceEventAck$Type();
1889
+ var IssueBudgetGrantRequest$Type = class extends MessageType {
1890
+ constructor() {
1891
+ super("spendguard.sidecar_adapter.v1.IssueBudgetGrantRequest", [
1892
+ {
1893
+ no: 1,
1894
+ name: "session_id",
1895
+ kind: "scalar",
1896
+ T: 9
1897
+ /*ScalarType.STRING*/
1898
+ },
1899
+ {
1900
+ no: 2,
1901
+ name: "parent_run_id",
1902
+ kind: "scalar",
1903
+ T: 9
1904
+ /*ScalarType.STRING*/
1905
+ },
1906
+ {
1907
+ no: 3,
1908
+ name: "child_workload_kind",
1909
+ kind: "scalar",
1910
+ T: 9
1911
+ /*ScalarType.STRING*/
1912
+ },
1913
+ { no: 4, name: "scope", kind: "message", T: () => IssueBudgetGrantRequest_Scope }
1914
+ ]);
1915
+ }
1916
+ };
1917
+ var IssueBudgetGrantRequest = new IssueBudgetGrantRequest$Type();
1918
+ var IssueBudgetGrantRequest_Scope$Type = class extends MessageType {
1919
+ constructor() {
1920
+ super("spendguard.sidecar_adapter.v1.IssueBudgetGrantRequest.Scope", [
1921
+ {
1922
+ no: 1,
1923
+ name: "allowed_routes",
1924
+ kind: "scalar",
1925
+ repeat: 2,
1926
+ T: 9
1927
+ /*ScalarType.STRING*/
1928
+ },
1929
+ {
1930
+ no: 2,
1931
+ name: "allowed_runtimes",
1932
+ kind: "scalar",
1933
+ repeat: 2,
1934
+ T: 9
1935
+ /*ScalarType.STRING*/
1936
+ },
1937
+ {
1938
+ no: 3,
1939
+ name: "max_amount_atomic",
1940
+ kind: "scalar",
1941
+ T: 9
1942
+ /*ScalarType.STRING*/
1943
+ },
1944
+ { no: 4, name: "max_amount_unit", kind: "message", T: () => UnitRef },
1945
+ { no: 5, name: "expires_at", kind: "message", T: () => Timestamp },
1946
+ {
1947
+ no: 6,
1948
+ name: "reservation_id",
1949
+ kind: "scalar",
1950
+ T: 9
1951
+ /*ScalarType.STRING*/
1952
+ }
1953
+ ]);
1954
+ }
1955
+ };
1956
+ var IssueBudgetGrantRequest_Scope = new IssueBudgetGrantRequest_Scope$Type();
1957
+ var IssueBudgetGrantResponse$Type = class extends MessageType {
1958
+ constructor() {
1959
+ super("spendguard.sidecar_adapter.v1.IssueBudgetGrantResponse", [
1960
+ {
1961
+ no: 1,
1962
+ name: "jwt_grant",
1963
+ kind: "scalar",
1964
+ T: 9
1965
+ /*ScalarType.STRING*/
1966
+ },
1967
+ {
1968
+ no: 2,
1969
+ name: "grant_jti",
1970
+ kind: "scalar",
1971
+ T: 9
1972
+ /*ScalarType.STRING*/
1973
+ },
1974
+ { no: 3, name: "issued_at", kind: "message", T: () => Timestamp },
1975
+ { no: 4, name: "expires_at", kind: "message", T: () => Timestamp }
1976
+ ]);
1977
+ }
1978
+ };
1979
+ var IssueBudgetGrantResponse = new IssueBudgetGrantResponse$Type();
1980
+ var RevokeBudgetGrantRequest$Type = class extends MessageType {
1981
+ constructor() {
1982
+ super("spendguard.sidecar_adapter.v1.RevokeBudgetGrantRequest", [
1983
+ {
1984
+ no: 1,
1985
+ name: "session_id",
1986
+ kind: "scalar",
1987
+ T: 9
1988
+ /*ScalarType.STRING*/
1989
+ },
1990
+ {
1991
+ no: 2,
1992
+ name: "grant_jti",
1993
+ kind: "scalar",
1994
+ T: 9
1995
+ /*ScalarType.STRING*/
1996
+ },
1997
+ {
1998
+ no: 3,
1999
+ name: "reason",
2000
+ kind: "scalar",
2001
+ T: 9
2002
+ /*ScalarType.STRING*/
2003
+ }
2004
+ ]);
2005
+ }
2006
+ };
2007
+ var RevokeBudgetGrantRequest = new RevokeBudgetGrantRequest$Type();
2008
+ var RevokeBudgetGrantResponse$Type = class extends MessageType {
2009
+ constructor() {
2010
+ super("spendguard.sidecar_adapter.v1.RevokeBudgetGrantResponse", [
2011
+ {
2012
+ no: 1,
2013
+ name: "revoked",
2014
+ kind: "scalar",
2015
+ T: 8
2016
+ /*ScalarType.BOOL*/
2017
+ },
2018
+ { no: 2, name: "revoked_at", kind: "message", T: () => Timestamp }
2019
+ ]);
2020
+ }
2021
+ };
2022
+ var RevokeBudgetGrantResponse = new RevokeBudgetGrantResponse$Type();
2023
+ var ConsumeBudgetGrantRequest$Type = class extends MessageType {
2024
+ constructor() {
2025
+ super("spendguard.sidecar_adapter.v1.ConsumeBudgetGrantRequest", [
2026
+ {
2027
+ no: 1,
2028
+ name: "session_id",
2029
+ kind: "scalar",
2030
+ T: 9
2031
+ /*ScalarType.STRING*/
2032
+ },
2033
+ {
2034
+ no: 2,
2035
+ name: "jwt_grant",
2036
+ kind: "scalar",
2037
+ T: 9
2038
+ /*ScalarType.STRING*/
2039
+ }
2040
+ ]);
2041
+ }
2042
+ };
2043
+ var ConsumeBudgetGrantRequest = new ConsumeBudgetGrantRequest$Type();
2044
+ var ConsumeBudgetGrantResponse$Type = class extends MessageType {
2045
+ constructor() {
2046
+ super("spendguard.sidecar_adapter.v1.ConsumeBudgetGrantResponse", [
2047
+ {
2048
+ no: 1,
2049
+ name: "accepted",
2050
+ kind: "scalar",
2051
+ T: 8
2052
+ /*ScalarType.BOOL*/
2053
+ },
2054
+ {
2055
+ no: 2,
2056
+ name: "parent_run_id",
2057
+ kind: "scalar",
2058
+ T: 9
2059
+ /*ScalarType.STRING*/
2060
+ },
2061
+ {
2062
+ no: 3,
2063
+ name: "parent_tenant_id",
2064
+ kind: "scalar",
2065
+ T: 9
2066
+ /*ScalarType.STRING*/
2067
+ },
2068
+ { no: 4, name: "expires_at", kind: "message", T: () => Timestamp },
2069
+ { no: 5, name: "error", kind: "message", T: () => Error2 }
2070
+ ]);
2071
+ }
2072
+ };
2073
+ var ConsumeBudgetGrantResponse = new ConsumeBudgetGrantResponse$Type();
2074
+ var DrainSubscribeRequest$Type = class extends MessageType {
2075
+ constructor() {
2076
+ super("spendguard.sidecar_adapter.v1.DrainSubscribeRequest", [
2077
+ {
2078
+ no: 1,
2079
+ name: "session_id",
2080
+ kind: "scalar",
2081
+ T: 9
2082
+ /*ScalarType.STRING*/
2083
+ }
2084
+ ]);
2085
+ }
2086
+ };
2087
+ var DrainSubscribeRequest = new DrainSubscribeRequest$Type();
2088
+ var DrainSignal$Type = class extends MessageType {
2089
+ constructor() {
2090
+ super("spendguard.sidecar_adapter.v1.DrainSignal", [
2091
+ { no: 1, name: "phase", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.DrainSignal.Phase", DrainSignal_Phase] },
2092
+ { no: 2, name: "deadline", kind: "message", T: () => Timestamp },
2093
+ {
2094
+ no: 3,
2095
+ name: "drain_trigger",
2096
+ kind: "scalar",
2097
+ T: 9
2098
+ /*ScalarType.STRING*/
2099
+ }
2100
+ ]);
2101
+ }
2102
+ };
2103
+ var DrainSignal = new DrainSignal$Type();
2104
+ var ReleaseReservationRequest$Type = class extends MessageType {
2105
+ constructor() {
2106
+ super("spendguard.sidecar_adapter.v1.ReleaseReservationRequest", [
2107
+ {
2108
+ no: 1,
2109
+ name: "reservation_id",
2110
+ kind: "scalar",
2111
+ T: 9
2112
+ /*ScalarType.STRING*/
2113
+ },
2114
+ {
2115
+ no: 2,
2116
+ name: "idempotency_key",
2117
+ kind: "scalar",
2118
+ T: 9
2119
+ /*ScalarType.STRING*/
2120
+ },
2121
+ {
2122
+ no: 3,
2123
+ name: "reason_codes",
2124
+ kind: "scalar",
2125
+ repeat: 2,
2126
+ T: 9
2127
+ /*ScalarType.STRING*/
2128
+ },
2129
+ {
2130
+ no: 100,
2131
+ name: "tenant_id",
2132
+ kind: "scalar",
2133
+ T: 9
2134
+ /*ScalarType.STRING*/
2135
+ },
2136
+ {
2137
+ no: 101,
2138
+ name: "workload_instance_id",
2139
+ kind: "scalar",
2140
+ T: 9
2141
+ /*ScalarType.STRING*/
2142
+ },
2143
+ {
2144
+ no: 102,
2145
+ name: "session_id",
2146
+ kind: "scalar",
2147
+ T: 9
2148
+ /*ScalarType.STRING*/
2149
+ }
2150
+ ]);
2151
+ }
2152
+ };
2153
+ var ReleaseReservationRequest = new ReleaseReservationRequest$Type();
2154
+ var ReleaseReservationResponse$Type = class extends MessageType {
2155
+ constructor() {
2156
+ super("spendguard.sidecar_adapter.v1.ReleaseReservationResponse", [
2157
+ {
2158
+ no: 1,
2159
+ name: "audit_event_signature",
2160
+ kind: "scalar",
2161
+ T: 12
2162
+ /*ScalarType.BYTES*/
2163
+ },
2164
+ {
2165
+ no: 100,
2166
+ name: "ledger_transaction_id",
2167
+ kind: "scalar",
2168
+ T: 9
2169
+ /*ScalarType.STRING*/
2170
+ },
2171
+ {
2172
+ no: 101,
2173
+ name: "released_reservation_ids",
2174
+ kind: "scalar",
2175
+ repeat: 2,
2176
+ T: 9
2177
+ /*ScalarType.STRING*/
2178
+ }
2179
+ ]);
2180
+ }
2181
+ };
2182
+ var ReleaseReservationResponse = new ReleaseReservationResponse$Type();
2183
+ var ReserveSessionRequest$Type = class extends MessageType {
2184
+ constructor() {
2185
+ super("spendguard.sidecar_adapter.v1.ReserveSessionRequest", [
2186
+ {
2187
+ no: 1,
2188
+ name: "tenant_id",
2189
+ kind: "scalar",
2190
+ T: 9
2191
+ /*ScalarType.STRING*/
2192
+ },
2193
+ {
2194
+ no: 2,
2195
+ name: "budget_id",
2196
+ kind: "scalar",
2197
+ T: 9
2198
+ /*ScalarType.STRING*/
2199
+ },
2200
+ {
2201
+ no: 3,
2202
+ name: "window_instance_id",
2203
+ kind: "scalar",
2204
+ T: 9
2205
+ /*ScalarType.STRING*/
2206
+ },
2207
+ { no: 4, name: "unit", kind: "message", T: () => UnitRef },
2208
+ { no: 5, name: "pricing", kind: "message", T: () => PricingFreeze },
2209
+ {
2210
+ no: 6,
2211
+ name: "session_id",
2212
+ kind: "scalar",
2213
+ T: 9
2214
+ /*ScalarType.STRING*/
2215
+ },
2216
+ {
2217
+ no: 7,
2218
+ name: "route",
2219
+ kind: "scalar",
2220
+ T: 9
2221
+ /*ScalarType.STRING*/
2222
+ },
2223
+ {
2224
+ no: 8,
2225
+ name: "estimated_amount_atomic",
2226
+ kind: "scalar",
2227
+ T: 9
2228
+ /*ScalarType.STRING*/
2229
+ },
2230
+ {
2231
+ no: 9,
2232
+ name: "ttl_seconds",
2233
+ kind: "scalar",
2234
+ T: 13
2235
+ /*ScalarType.UINT32*/
2236
+ },
2237
+ {
2238
+ no: 10,
2239
+ name: "idempotency_key",
2240
+ kind: "scalar",
2241
+ T: 9
2242
+ /*ScalarType.STRING*/
2243
+ }
2244
+ ]);
2245
+ }
2246
+ };
2247
+ var ReserveSessionRequest = new ReserveSessionRequest$Type();
2248
+ var ReserveSessionOutcome$Type = class extends MessageType {
2249
+ constructor() {
2250
+ super("spendguard.sidecar_adapter.v1.ReserveSessionOutcome", [
2251
+ { no: 1, name: "accepted", kind: "message", oneof: "outcome", T: () => ReserveSessionAccepted },
2252
+ { no: 2, name: "denied", kind: "message", oneof: "outcome", T: () => ReserveSessionDenied },
2253
+ { no: 3, name: "error", kind: "message", oneof: "outcome", T: () => Error2 }
2254
+ ]);
2255
+ }
2256
+ };
2257
+ var ReserveSessionOutcome = new ReserveSessionOutcome$Type();
2258
+ var ReserveSessionAccepted$Type = class extends MessageType {
2259
+ constructor() {
2260
+ super("spendguard.sidecar_adapter.v1.ReserveSessionAccepted", [
2261
+ {
2262
+ no: 1,
2263
+ name: "session_reservation_id",
2264
+ kind: "scalar",
2265
+ T: 9
2266
+ /*ScalarType.STRING*/
2267
+ },
2268
+ {
2269
+ no: 2,
2270
+ name: "ledger_transaction_id",
2271
+ kind: "scalar",
2272
+ T: 9
2273
+ /*ScalarType.STRING*/
2274
+ },
2275
+ {
2276
+ no: 3,
2277
+ name: "audit_session_event_id",
2278
+ kind: "scalar",
2279
+ T: 9
2280
+ /*ScalarType.STRING*/
2281
+ },
2282
+ { no: 4, name: "ttl_expires_at", kind: "message", T: () => Timestamp },
2283
+ {
2284
+ no: 5,
2285
+ name: "reserved_amount_atomic",
2286
+ kind: "scalar",
2287
+ T: 9
2288
+ /*ScalarType.STRING*/
2289
+ },
2290
+ {
2291
+ no: 6,
2292
+ name: "remaining_amount_atomic",
2293
+ kind: "scalar",
2294
+ T: 9
2295
+ /*ScalarType.STRING*/
2296
+ }
2297
+ ]);
2298
+ }
2299
+ };
2300
+ var ReserveSessionAccepted = new ReserveSessionAccepted$Type();
2301
+ var ReserveSessionDenied$Type = class extends MessageType {
2302
+ constructor() {
2303
+ super("spendguard.sidecar_adapter.v1.ReserveSessionDenied", [
2304
+ {
2305
+ no: 1,
2306
+ name: "audit_session_event_id",
2307
+ kind: "scalar",
2308
+ T: 9
2309
+ /*ScalarType.STRING*/
2310
+ },
2311
+ {
2312
+ no: 2,
2313
+ name: "reason_codes",
2314
+ kind: "scalar",
2315
+ repeat: 2,
2316
+ T: 9
2317
+ /*ScalarType.STRING*/
2318
+ },
2319
+ {
2320
+ no: 3,
2321
+ name: "matched_rule_ids",
2322
+ kind: "scalar",
2323
+ repeat: 2,
2324
+ T: 9
2325
+ /*ScalarType.STRING*/
2326
+ },
2327
+ { no: 4, name: "error", kind: "message", T: () => Error2 }
2328
+ ]);
2329
+ }
2330
+ };
2331
+ var ReserveSessionDenied = new ReserveSessionDenied$Type();
2332
+ var CommitSessionDeltaRequest$Type = class extends MessageType {
2333
+ constructor() {
2334
+ super("spendguard.sidecar_adapter.v1.CommitSessionDeltaRequest", [
2335
+ {
2336
+ no: 1,
2337
+ name: "session_reservation_id",
2338
+ kind: "scalar",
2339
+ T: 9
2340
+ /*ScalarType.STRING*/
2341
+ },
2342
+ {
2343
+ no: 2,
2344
+ name: "streaming_commit_id",
2345
+ kind: "scalar",
2346
+ T: 9
2347
+ /*ScalarType.STRING*/
2348
+ },
2349
+ {
2350
+ no: 3,
2351
+ name: "amount_atomic_delta",
2352
+ kind: "scalar",
2353
+ T: 9
2354
+ /*ScalarType.STRING*/
2355
+ },
2356
+ { no: 4, name: "outcome", kind: "enum", T: () => ["spendguard.sidecar_adapter.v1.CommitSessionDeltaRequest.Outcome", CommitSessionDeltaRequest_Outcome] },
2357
+ { no: 5, name: "event_time", kind: "message", T: () => Timestamp },
2358
+ {
2359
+ no: 6,
2360
+ name: "idempotency_key",
2361
+ kind: "scalar",
2362
+ T: 9
2363
+ /*ScalarType.STRING*/
2364
+ }
2365
+ ]);
2366
+ }
2367
+ };
2368
+ var CommitSessionDeltaRequest = new CommitSessionDeltaRequest$Type();
2369
+ var CommitSessionDeltaOutcome$Type = class extends MessageType {
2370
+ constructor() {
2371
+ super("spendguard.sidecar_adapter.v1.CommitSessionDeltaOutcome", [
2372
+ { no: 1, name: "accepted", kind: "message", oneof: "outcome", T: () => CommitSessionDeltaAccepted },
2373
+ { no: 2, name: "error", kind: "message", oneof: "outcome", T: () => Error2 }
2374
+ ]);
2375
+ }
2376
+ };
2377
+ var CommitSessionDeltaOutcome = new CommitSessionDeltaOutcome$Type();
2378
+ var CommitSessionDeltaAccepted$Type = class extends MessageType {
2379
+ constructor() {
2380
+ super("spendguard.sidecar_adapter.v1.CommitSessionDeltaAccepted", [
2381
+ {
2382
+ no: 1,
2383
+ name: "session_reservation_id",
2384
+ kind: "scalar",
2385
+ T: 9
2386
+ /*ScalarType.STRING*/
2387
+ },
2388
+ {
2389
+ no: 2,
2390
+ name: "streaming_commit_id",
2391
+ kind: "scalar",
2392
+ T: 9
2393
+ /*ScalarType.STRING*/
2394
+ },
2395
+ {
2396
+ no: 3,
2397
+ name: "ledger_transaction_id",
2398
+ kind: "scalar",
2399
+ T: 9
2400
+ /*ScalarType.STRING*/
2401
+ },
2402
+ {
2403
+ no: 4,
2404
+ name: "audit_session_event_id",
2405
+ kind: "scalar",
2406
+ T: 9
2407
+ /*ScalarType.STRING*/
2408
+ },
2409
+ {
2410
+ no: 5,
2411
+ name: "committed_delta_atomic",
2412
+ kind: "scalar",
2413
+ T: 9
2414
+ /*ScalarType.STRING*/
2415
+ },
2416
+ {
2417
+ no: 6,
2418
+ name: "cumulative_committed_atomic",
2419
+ kind: "scalar",
2420
+ T: 9
2421
+ /*ScalarType.STRING*/
2422
+ },
2423
+ {
2424
+ no: 7,
2425
+ name: "remaining_amount_atomic",
2426
+ kind: "scalar",
2427
+ T: 9
2428
+ /*ScalarType.STRING*/
2429
+ },
2430
+ { no: 8, name: "recorded_at", kind: "message", T: () => Timestamp }
2431
+ ]);
2432
+ }
2433
+ };
2434
+ var CommitSessionDeltaAccepted = new CommitSessionDeltaAccepted$Type();
2435
+ var ReleaseSessionRequest$Type = class extends MessageType {
2436
+ constructor() {
2437
+ super("spendguard.sidecar_adapter.v1.ReleaseSessionRequest", [
2438
+ {
2439
+ no: 1,
2440
+ name: "session_reservation_id",
2441
+ kind: "scalar",
2442
+ T: 9
2443
+ /*ScalarType.STRING*/
2444
+ },
2445
+ {
2446
+ no: 2,
2447
+ name: "reason_code",
2448
+ kind: "scalar",
2449
+ T: 9
2450
+ /*ScalarType.STRING*/
2451
+ },
2452
+ { no: 3, name: "event_time", kind: "message", T: () => Timestamp },
2453
+ {
2454
+ no: 4,
2455
+ name: "idempotency_key",
2456
+ kind: "scalar",
2457
+ T: 9
2458
+ /*ScalarType.STRING*/
2459
+ }
2460
+ ]);
2461
+ }
2462
+ };
2463
+ var ReleaseSessionRequest = new ReleaseSessionRequest$Type();
2464
+ var ReleaseSessionOutcome$Type = class extends MessageType {
2465
+ constructor() {
2466
+ super("spendguard.sidecar_adapter.v1.ReleaseSessionOutcome", [
2467
+ { no: 1, name: "accepted", kind: "message", oneof: "outcome", T: () => ReleaseSessionAccepted },
2468
+ { no: 2, name: "error", kind: "message", oneof: "outcome", T: () => Error2 }
2469
+ ]);
2470
+ }
2471
+ };
2472
+ var ReleaseSessionOutcome = new ReleaseSessionOutcome$Type();
2473
+ var ReleaseSessionAccepted$Type = class extends MessageType {
2474
+ constructor() {
2475
+ super("spendguard.sidecar_adapter.v1.ReleaseSessionAccepted", [
2476
+ {
2477
+ no: 1,
2478
+ name: "session_reservation_id",
2479
+ kind: "scalar",
2480
+ T: 9
2481
+ /*ScalarType.STRING*/
2482
+ },
2483
+ {
2484
+ no: 2,
2485
+ name: "ledger_transaction_id",
2486
+ kind: "scalar",
2487
+ T: 9
2488
+ /*ScalarType.STRING*/
2489
+ },
2490
+ {
2491
+ no: 3,
2492
+ name: "audit_session_event_id",
2493
+ kind: "scalar",
2494
+ T: 9
2495
+ /*ScalarType.STRING*/
2496
+ },
2497
+ {
2498
+ no: 4,
2499
+ name: "released_amount_atomic",
2500
+ kind: "scalar",
2501
+ T: 9
2502
+ /*ScalarType.STRING*/
2503
+ },
2504
+ {
2505
+ no: 5,
2506
+ name: "committed_amount_atomic",
2507
+ kind: "scalar",
2508
+ T: 9
2509
+ /*ScalarType.STRING*/
2510
+ },
2511
+ { no: 6, name: "recorded_at", kind: "message", T: () => Timestamp }
2512
+ ]);
2513
+ }
2514
+ };
2515
+ var ReleaseSessionAccepted = new ReleaseSessionAccepted$Type();
2516
+ var SidecarAdapter = new ServiceType("spendguard.sidecar_adapter.v1.SidecarAdapter", [
2517
+ { name: "Handshake", options: {}, I: HandshakeRequest, O: HandshakeResponse },
2518
+ { name: "RequestDecision", options: {}, I: DecisionRequest, O: DecisionResponse },
2519
+ { name: "ConfirmPublishOutcome", options: {}, I: PublishOutcomeRequest, O: PublishOutcomeResponse },
2520
+ { name: "EmitTraceEvents", serverStreaming: true, clientStreaming: true, options: {}, I: TraceEvent, O: TraceEventAck },
2521
+ { name: "IssueBudgetGrant", options: {}, I: IssueBudgetGrantRequest, O: IssueBudgetGrantResponse },
2522
+ { name: "RevokeBudgetGrant", options: {}, I: RevokeBudgetGrantRequest, O: RevokeBudgetGrantResponse },
2523
+ { name: "ConsumeBudgetGrant", options: {}, I: ConsumeBudgetGrantRequest, O: ConsumeBudgetGrantResponse },
2524
+ { name: "StreamDrainSignal", serverStreaming: true, options: {}, I: DrainSubscribeRequest, O: DrainSignal },
2525
+ { name: "ResumeAfterApproval", options: {}, I: ResumeAfterApprovalRequest, O: ResumeAfterApprovalResponse },
2526
+ { name: "ReleaseReservation", options: {}, I: ReleaseReservationRequest, O: ReleaseReservationResponse },
2527
+ { name: "ReserveSession", options: {}, I: ReserveSessionRequest, O: ReserveSessionOutcome },
2528
+ { name: "CommitSessionDelta", options: {}, I: CommitSessionDeltaRequest, O: CommitSessionDeltaOutcome },
2529
+ { name: "ReleaseSession", options: {}, I: ReleaseSessionRequest, O: ReleaseSessionOutcome }
2530
+ ]);
2531
+ var SidecarAdapterClient = class {
2532
+ constructor(_transport) {
2533
+ this._transport = _transport;
2534
+ }
2535
+ _transport;
2536
+ typeName = SidecarAdapter.typeName;
2537
+ methods = SidecarAdapter.methods;
2538
+ options = SidecarAdapter.options;
2539
+ /**
2540
+ * Initial handshake; mandatory before any other RPC. Negotiates SDK version,
2541
+ * runtime kind, capability level, tenant_id assertion, key epochs.
2542
+ *
2543
+ * @generated from protobuf rpc: Handshake
2544
+ */
2545
+ handshake(input, options) {
2546
+ const method = this.methods[0], opt = this._transport.mergeOptions(options);
2547
+ return stackIntercept("unary", this._transport, method, opt, input);
2548
+ }
2549
+ // -- Decision boundary (per Contract §15 trigger points) -----------------
2550
+ /**
2551
+ * Adapter requests a decision at a *.pre trigger point.
2552
+ * Sidecar runs Contract §6 stages 1-5 and returns DecisionResult.
2553
+ * Sidecar may take up to Contract §14 latency budget (50ms p99 warm).
2554
+ *
2555
+ * @generated from protobuf rpc: RequestDecision
2556
+ */
2557
+ requestDecision(input, options) {
2558
+ const method = this.methods[1], opt = this._transport.mergeOptions(options);
2559
+ return stackIntercept("unary", this._transport, method, opt, input);
2560
+ }
2561
+ /**
2562
+ * Adapter confirms publish_effect outcome (per Contract §6 stage 7).
2563
+ * Idempotent via effect_hash (Stage 2 §4.6).
2564
+ *
2565
+ * @generated from protobuf rpc: ConfirmPublishOutcome
2566
+ */
2567
+ confirmPublishOutcome(input, options) {
2568
+ const method = this.methods[2], opt = this._transport.mergeOptions(options);
2569
+ return stackIntercept("unary", this._transport, method, opt, input);
2570
+ }
2571
+ // -- Trace events (per Contract §15 *.post triggers; observation only) ---
2572
+ /**
2573
+ * Adapter emits canonical/lifecycle events (agent.run.start, agent.step.post
2574
+ * tool.call.post, llm.call.post for commit, etc.). Server-streamed because
2575
+ * sidecar may emit follow-up acks or correction events.
2576
+ *
2577
+ * @generated from protobuf rpc: EmitTraceEvents
2578
+ */
2579
+ emitTraceEvents(options) {
2580
+ const method = this.methods[3], opt = this._transport.mergeOptions(options);
2581
+ return stackIntercept("duplex", this._transport, method, opt);
2582
+ }
2583
+ // -- Sub-agent budget grant (per Contract §8) ----------------------------
2584
+ /**
2585
+ * Parent agent requests budget_grant JWT to delegate to child agent.
2586
+ *
2587
+ * @generated from protobuf rpc: IssueBudgetGrant
2588
+ */
2589
+ issueBudgetGrant(input, options) {
2590
+ const method = this.methods[4], opt = this._transport.mergeOptions(options);
2591
+ return stackIntercept("unary", this._transport, method, opt, input);
2592
+ }
2593
+ /**
2594
+ * Parent revokes a previously issued grant.
2595
+ *
2596
+ * @generated from protobuf rpc: RevokeBudgetGrant
2597
+ */
2598
+ revokeBudgetGrant(input, options) {
2599
+ const method = this.methods[5], opt = this._transport.mergeOptions(options);
2600
+ return stackIntercept("unary", this._transport, method, opt, input);
2601
+ }
2602
+ /**
2603
+ * Child agent uses grant to bootstrap budget context.
2604
+ *
2605
+ * @generated from protobuf rpc: ConsumeBudgetGrant
2606
+ */
2607
+ consumeBudgetGrant(input, options) {
2608
+ const method = this.methods[6], opt = this._transport.mergeOptions(options);
2609
+ return stackIntercept("unary", this._transport, method, opt, input);
2610
+ }
2611
+ // -- Lifecycle drain (per Sidecar §11) -----------------------------------
2612
+ /**
2613
+ * Sidecar signals adapter that drain has begun; adapter should stop
2614
+ * initiating new decision boundaries and let in-flight finish.
2615
+ *
2616
+ * @generated from protobuf rpc: StreamDrainSignal
2617
+ */
2618
+ streamDrainSignal(input, options) {
2619
+ const method = this.methods[7], opt = this._transport.mergeOptions(options);
2620
+ return stackIntercept("serverStreaming", this._transport, method, opt, input);
2621
+ }
2622
+ // -- Approval resume (Phase 5 GA hardening S16) --------------------------
2623
+ /**
2624
+ * Adapter calls this AFTER the human approver has approved/denied the
2625
+ * pending approval (REQUIRE_APPROVAL outcome from RequestDecision).
2626
+ * Sidecar:
2627
+ * 1. Looks up the approval by (tenant_id, decision_id, approval_id).
2628
+ * 2. Reads the current state (must be `approved` or `denied`).
2629
+ * 3. For `approved`: re-runs Contract evaluation + Ledger.ReserveSet
2630
+ * with a NEW idempotency key derived from approval_id (so a
2631
+ * replay can never double-publish the effect).
2632
+ * 4. For `denied`: emits the deny audit row + returns Decision::STOP.
2633
+ * 5. For other states (pending/expired/cancelled): returns the
2634
+ * typed Error code so the adapter raises the right exception.
2635
+ *
2636
+ * Idempotent: repeated ResumeAfterApproval calls with the same
2637
+ * (decision_id, approval_id) produce the same DecisionResponse
2638
+ * because the idempotency key includes both.
2639
+ *
2640
+ * @generated from protobuf rpc: ResumeAfterApproval
2641
+ */
2642
+ resumeAfterApproval(input, options) {
2643
+ const method = this.methods[8], opt = this._transport.mergeOptions(options);
2644
+ return stackIntercept("unary", this._transport, method, opt, input);
2645
+ }
2646
+ // -- Explicit Release (Draft-01 §4 of Agent Spend Protocol) ---------------
2647
+ /**
2648
+ * Adapter-initiated release of a held reservation before commit.
2649
+ * Use when the provider call is aborted, the client times out, or the
2650
+ * agent run is cancelled — and the adapter wants to surface that
2651
+ * explicitly rather than waiting for an outcome-driven (APPLY_FAILED
2652
+ * or trace-event-driven) implicit release.
2653
+ *
2654
+ * Coexists with the implicit release paths in ConfirmPublishOutcome and
2655
+ * EmitTraceEvents; those remain unchanged. Implicit paths are still the
2656
+ * default for adapters that report outcomes naturally; the explicit RPC
2657
+ * is for adapters that want to match the Agent Spend Protocol Draft-01
2658
+ * wire shape (docs/specs/agent-spend-protocol/draft-01.md §4).
2659
+ *
2660
+ * Idempotent: repeated ReleaseReservation calls with the same
2661
+ * (reservation_id, idempotency_key) pair return the original outcome.
2662
+ *
2663
+ * @generated from protobuf rpc: ReleaseReservation
2664
+ */
2665
+ releaseReservation(input, options) {
2666
+ const method = this.methods[9], opt = this._transport.mergeOptions(options);
2667
+ return stackIntercept("unary", this._transport, method, opt, input);
2668
+ }
2669
+ // -- Session reservation (D41 voice substrate SR-V1) ---------------------
2670
+ /**
2671
+ * Reserve a session-scoped ledger hold before a realtime voice session
2672
+ * connects to paid model providers. Idempotent by
2673
+ * (tenant_id, session_id, route, idempotency_key).
2674
+ *
2675
+ * @generated from protobuf rpc: ReserveSession
2676
+ */
2677
+ reserveSession(input, options) {
2678
+ const method = this.methods[10], opt = this._transport.mergeOptions(options);
2679
+ return stackIntercept("unary", this._transport, method, opt, input);
2680
+ }
2681
+ /**
2682
+ * Commit one positive streaming spend delta against an existing session
2683
+ * reservation. Idempotent by (session_reservation_id,
2684
+ * streaming_commit_id). amount_atomic_delta MUST be > 0.
2685
+ *
2686
+ * @generated from protobuf rpc: CommitSessionDelta
2687
+ */
2688
+ commitSessionDelta(input, options) {
2689
+ const method = this.methods[11], opt = this._transport.mergeOptions(options);
2690
+ return stackIntercept("unary", this._transport, method, opt, input);
2691
+ }
2692
+ /**
2693
+ * Release the uncommitted remainder of a session-scoped reservation.
2694
+ * Idempotent by (session_reservation_id, idempotency_key).
2695
+ *
2696
+ * @generated from protobuf rpc: ReleaseSession
2697
+ */
2698
+ releaseSession(input, options) {
2699
+ const method = this.methods[12], opt = this._transport.mergeOptions(options);
2700
+ return stackIntercept("unary", this._transport, method, opt, input);
2701
+ }
2702
+ };
2703
+
2704
+ // src/errors.ts
2705
+ var SpendGuardError = class extends Error {
2706
+ name = "SpendGuardError";
2707
+ constructor(message, opts) {
2708
+ super(message);
2709
+ if (opts?.cause !== void 0) {
2710
+ this.cause = opts.cause;
2711
+ }
2712
+ Object.defineProperty(this, "name", {
2713
+ value: this.name,
2714
+ enumerable: true,
2715
+ configurable: true,
2716
+ writable: true
2717
+ });
2718
+ }
2719
+ };
2720
+ var SpendGuardConfigError = class extends SpendGuardError {
2721
+ name = "SpendGuardConfigError";
2722
+ };
2723
+ var SidecarUnavailable = class extends SpendGuardError {
2724
+ name = "SidecarUnavailable";
2725
+ statusCode = 503;
2726
+ };
2727
+ var SpendGuardConnectionError = class extends SpendGuardError {
2728
+ name = "SpendGuardConnectionError";
2729
+ };
2730
+ var HandshakeError = class extends SpendGuardError {
2731
+ name = "HandshakeError";
2732
+ };
2733
+ var DecisionDenied = class extends SpendGuardError {
2734
+ name = "DecisionDenied";
2735
+ statusCode = 403;
2736
+ decisionId;
2737
+ reasonCodes;
2738
+ auditDecisionEventId;
2739
+ matchedRuleIds;
2740
+ constructor(message, init, opts) {
2741
+ super(message, opts);
2742
+ this.decisionId = init.decisionId;
2743
+ this.reasonCodes = init.reasonCodes ?? [];
2744
+ if (init.auditDecisionEventId !== void 0) {
2745
+ this.auditDecisionEventId = init.auditDecisionEventId;
2746
+ }
2747
+ this.matchedRuleIds = init.matchedRuleIds ?? [];
2748
+ }
2749
+ };
2750
+ var DecisionStopped = class extends DecisionDenied {
2751
+ name = "DecisionStopped";
2752
+ };
2753
+ var DecisionSkipped = class extends DecisionDenied {
2754
+ name = "DecisionSkipped";
2755
+ };
2756
+ var ApprovalRequired = class extends DecisionDenied {
2757
+ name = "ApprovalRequired";
2758
+ approvalRequestId;
2759
+ approverRole;
2760
+ tenantId;
2761
+ constructor(message, init, opts) {
2762
+ super(message, init, opts);
2763
+ this.approvalRequestId = init.approvalRequestId;
2764
+ if (init.approverRole !== void 0) {
2765
+ this.approverRole = init.approverRole;
2766
+ }
2767
+ if (init.tenantId !== void 0) {
2768
+ this.tenantId = init.tenantId;
2769
+ }
2770
+ }
2771
+ /**
2772
+ * Resume the decision after a human operator has acted. Delegates to
2773
+ * `client.resumeAfterApproval(...)`.
2774
+ *
2775
+ * @throws ApprovalDeniedError when the operator rejected.
2776
+ * @throws ApprovalLapsedError when the approval expired / was cancelled.
2777
+ * @throws ApprovalBundleHotReloadedError when bundle rotated mid-approval.
2778
+ */
2779
+ async resume(client) {
2780
+ return client.resumeAfterApproval({
2781
+ approvalId: this.approvalRequestId,
2782
+ tenantId: this.tenantId ?? "",
2783
+ decisionId: this.decisionId
2784
+ });
2785
+ }
2786
+ };
2787
+ var ApprovalBundleHotReloadedError = class extends SpendGuardError {
2788
+ name = "ApprovalBundleHotReloadedError";
2789
+ originalBundleHash;
2790
+ currentBundleHash;
2791
+ constructor(message, init, opts) {
2792
+ super(message, opts);
2793
+ this.originalBundleHash = init.originalBundleHash;
2794
+ this.currentBundleHash = init.currentBundleHash;
2795
+ }
2796
+ };
2797
+ var MutationApplyFailed = class extends SpendGuardError {
2798
+ name = "MutationApplyFailed";
2799
+ };
2800
+
2801
+ // src/config.ts
2802
+ var DEFAULT_DECISION_TIMEOUT_MS = 250;
2803
+ var DEFAULT_HANDSHAKE_TIMEOUT_MS = 2e3;
2804
+ var DEFAULT_PUBLISH_TIMEOUT_MS = 150;
2805
+ var DEFAULT_TRACE_TIMEOUT_MS = 500;
2806
+ var DEFAULT_CAPABILITY_LEVEL = 64;
2807
+ var DEFAULT_PROTOCOL_VERSION = 1;
2808
+ function validateConfig(cfg) {
2809
+ if (cfg.socketPath.length === 0) {
2810
+ throw new SpendGuardConfigError(
2811
+ "socketPath is required (or set SPENDGUARD_SOCKET_PATH / SPENDGUARD_SIDECAR_UDS)"
2812
+ );
2813
+ }
2814
+ if (cfg.tenantId.length === 0) {
2815
+ throw new SpendGuardConfigError("tenantId is required (or set SPENDGUARD_TENANT_ID)");
2816
+ }
2817
+ if (cfg.otelTracer !== void 0 && cfg.onSpan !== void 0) {
2818
+ throw new SpendGuardConfigError("otelTracer and onSpan are mutually exclusive");
2819
+ }
2820
+ if (cfg.runtime !== "uds-grpc") {
2821
+ throw new SpendGuardConfigError(
2822
+ `runtime=${JSON.stringify(cfg.runtime)} is not supported in v0.1.x; only "uds-grpc" is wired`
2823
+ );
2824
+ }
2825
+ if (cfg.protocolVersion !== 1) {
2826
+ throw new SpendGuardConfigError(
2827
+ `protocolVersion=${cfg.protocolVersion} is not supported in v0.1.x; only 1 is wired`
2828
+ );
2829
+ }
2830
+ assertPositiveIntegerField(cfg.capabilityLevel, "capabilityLevel");
2831
+ assertPositiveIntegerField(cfg.decisionTimeoutMs, "decisionTimeoutMs");
2832
+ assertPositiveIntegerField(cfg.handshakeTimeoutMs, "handshakeTimeoutMs");
2833
+ assertPositiveIntegerField(cfg.publishTimeoutMs, "publishTimeoutMs");
2834
+ assertPositiveIntegerField(cfg.traceTimeoutMs, "traceTimeoutMs");
2835
+ }
2836
+ function assertPositiveIntegerField(value, field) {
2837
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
2838
+ throw new SpendGuardConfigError(`${field}=${value} must be a finite non-negative integer`);
2839
+ }
2840
+ }
2841
+
2842
+ // src/env.ts
2843
+ var DEFAULT_SOCKET_PATH = "/var/run/spendguard/adapter.sock";
2844
+ function resolveEnvConfig(env = process.env) {
2845
+ const out = {};
2846
+ const socketPath = env.SPENDGUARD_SOCKET_PATH ?? env.SPENDGUARD_SIDECAR_UDS;
2847
+ if (socketPath !== void 0 && socketPath.length > 0) {
2848
+ out.socketPath = socketPath;
2849
+ }
2850
+ const tenantId = env.SPENDGUARD_TENANT_ID;
2851
+ if (tenantId !== void 0 && tenantId.length > 0) {
2852
+ out.tenantId = tenantId;
2853
+ }
2854
+ const workloadInstanceId = env.SPENDGUARD_WORKLOAD_INSTANCE_ID;
2855
+ if (workloadInstanceId !== void 0 && workloadInstanceId.length > 0) {
2856
+ out.workloadInstanceId = workloadInstanceId;
2857
+ }
2858
+ const decisionTimeoutMs = parsePositiveIntegerEnv(
2859
+ env.SPENDGUARD_DECISION_TIMEOUT_MS,
2860
+ "SPENDGUARD_DECISION_TIMEOUT_MS"
2861
+ );
2862
+ if (decisionTimeoutMs !== void 0) {
2863
+ out.decisionTimeoutMs = decisionTimeoutMs;
2864
+ }
2865
+ const handshakeTimeoutMs = parsePositiveIntegerEnv(
2866
+ env.SPENDGUARD_HANDSHAKE_TIMEOUT_MS,
2867
+ "SPENDGUARD_HANDSHAKE_TIMEOUT_MS"
2868
+ );
2869
+ if (handshakeTimeoutMs !== void 0) {
2870
+ out.handshakeTimeoutMs = handshakeTimeoutMs;
2871
+ }
2872
+ const runProjectionDefault = env.SPENDGUARD_RUN_PROJECTION_DEFAULT;
2873
+ if (runProjectionDefault !== void 0 && runProjectionDefault.length > 0) {
2874
+ out.runProjectionDefault = runProjectionDefault;
2875
+ }
2876
+ const profile = env.SPENDGUARD_PROFILE;
2877
+ if (profile !== void 0 && profile.length > 0) {
2878
+ out.profile = profile.toLowerCase();
2879
+ }
2880
+ const disabled = env.SPENDGUARD_DISABLE;
2881
+ if (disabled !== void 0) {
2882
+ const norm = disabled.toLowerCase();
2883
+ if (norm === "1" || norm === "true" || norm === "yes" || norm === "on") {
2884
+ out.disabled = true;
2885
+ }
2886
+ }
2887
+ return out;
2888
+ }
2889
+ function parsePositiveIntegerEnv(raw, name) {
2890
+ if (raw === void 0 || raw.length === 0) return void 0;
2891
+ const n = Number(raw);
2892
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n < 0) {
2893
+ throw new EnvParseError(
2894
+ `env var ${name}=${JSON.stringify(raw)} is not a finite non-negative integer`,
2895
+ name
2896
+ );
2897
+ }
2898
+ return n;
2899
+ }
2900
+ var EnvParseError = class extends Error {
2901
+ name = "EnvParseError";
2902
+ varName;
2903
+ constructor(message, varName) {
2904
+ super(message);
2905
+ this.varName = varName;
2906
+ }
2907
+ };
2908
+
2909
+ // src/otel.ts
2910
+ var SPENDGUARD_OTEL_ATTR = {
2911
+ TENANT_ID: "spendguard.tenant_id",
2912
+ DECISION_ID: "spendguard.decision_id",
2913
+ TRIGGER: "spendguard.trigger",
2914
+ SDK_VERSION: "spendguard.sdk.version",
2915
+ RESERVATION_ID: "spendguard.reservation_id",
2916
+ SCOPE_ID: "spendguard.scope_id"
2917
+ };
2918
+ var SPAN_STATUS_ERROR = 2;
2919
+ async function withOtelSpan(tracer, rpcName, attributes, fn, onSpan) {
2920
+ if (tracer === void 0 && onSpan === void 0) return await fn();
2921
+ const spanName = `spendguard.${rpcName}`;
2922
+ const filtered = {};
2923
+ for (const [k, v] of Object.entries(attributes)) {
2924
+ if (v !== void 0) filtered[k] = v;
2925
+ }
2926
+ if (tracer === void 0) {
2927
+ const startTimeMs2 = Date.now();
2928
+ let error;
2929
+ try {
2930
+ return await fn();
2931
+ } catch (err) {
2932
+ error = err instanceof Error ? err : new Error(String(err));
2933
+ throw err;
2934
+ } finally {
2935
+ emitSpanRecord(onSpan, spanName, startTimeMs2, filtered, error);
2936
+ }
2937
+ }
2938
+ const startTimeMs = Date.now();
2939
+ let observerError;
2940
+ const span = tracer.startSpan(spanName, { attributes: filtered });
2941
+ try {
2942
+ const result = await fn();
2943
+ return result;
2944
+ } catch (err) {
2945
+ if (err instanceof Error) {
2946
+ observerError = err;
2947
+ span.recordException(err);
2948
+ span.setStatus({ code: SPAN_STATUS_ERROR, message: err.message });
2949
+ } else {
2950
+ const message = typeof err === "string" ? err : String(err);
2951
+ observerError = new Error(message);
2952
+ span.recordException({ name: "SpendGuardError", message });
2953
+ span.setStatus({ code: SPAN_STATUS_ERROR, message });
2954
+ }
2955
+ throw err;
2956
+ } finally {
2957
+ span.end();
2958
+ emitSpanRecord(onSpan, spanName, startTimeMs, filtered, observerError);
2959
+ }
2960
+ }
2961
+ function emitSpanRecord(onSpan, name, startTimeMs, filtered, error) {
2962
+ if (onSpan === void 0) return;
2963
+ const attributes = {};
2964
+ for (const [k, v] of Object.entries(filtered)) {
2965
+ if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
2966
+ attributes[k] = v;
2967
+ }
2968
+ }
2969
+ const record = {
2970
+ name,
2971
+ startTimeMs,
2972
+ durationMs: Date.now() - startTimeMs,
2973
+ attributes,
2974
+ ...error !== void 0 ? { error } : {}
2975
+ };
2976
+ try {
2977
+ onSpan(record);
2978
+ } catch {
2979
+ }
2980
+ }
2981
+ var ASCII_WHITESPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\f", "\r"]);
2982
+ var UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
2983
+ function canonicalizeTenant(tenantId) {
2984
+ if (UUID_RE.test(tenantId)) return tenantId.toLowerCase();
2985
+ return tenantId;
2986
+ }
2987
+ function stripAscii(s) {
2988
+ let i = 0;
2989
+ while (i < s.length && ASCII_WHITESPACE.has(s.charAt(i))) i++;
2990
+ let j = s.length;
2991
+ while (j > i && ASCII_WHITESPACE.has(s.charAt(j - 1))) j--;
2992
+ return s.slice(i, j);
2993
+ }
2994
+ function computePromptHash(promptText, tenantId) {
2995
+ const key = canonicalizeTenant(tenantId);
2996
+ const trimmed = stripAscii(promptText);
2997
+ return createHmac("sha256", key).update(trimmed, "utf8").digest("hex");
2998
+ }
2999
+
3000
+ // src/retry.ts
3001
+ var TRANSIENT_STATUS_CODES = /* @__PURE__ */ new Set([
3002
+ "UNAVAILABLE",
3003
+ "DEADLINE_EXCEEDED",
3004
+ "CANCELLED"
3005
+ ]);
3006
+ function classifyRpcError(err) {
3007
+ if (err instanceof SidecarUnavailable) return "transient";
3008
+ if (err !== null && typeof err === "object" && "code" in err) {
3009
+ const code = err.code;
3010
+ if (typeof code === "string" && TRANSIENT_STATUS_CODES.has(code)) {
3011
+ return "transient";
3012
+ }
3013
+ }
3014
+ return "permanent";
3015
+ }
3016
+ async function runWithRetry(fn, opts = {}) {
3017
+ const idempotencyKey = opts.idempotencyKey;
3018
+ const maxAttempts = clampMaxAttempts(opts.maxAttempts ?? 2);
3019
+ const baseBackoffMs = opts.baseBackoffMs ?? 25;
3020
+ const jitterMs = opts.jitterMs ?? 25;
3021
+ const sleep = opts.sleep ?? defaultSleep;
3022
+ let lastErr;
3023
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3024
+ try {
3025
+ return await fn();
3026
+ } catch (err) {
3027
+ const classification = classifyRpcError(err);
3028
+ if (classification === "permanent") {
3029
+ throw err;
3030
+ }
3031
+ if (idempotencyKey === void 0 || idempotencyKey.length === 0) {
3032
+ if (err instanceof SidecarUnavailable) {
3033
+ throw err;
3034
+ }
3035
+ throw new SidecarUnavailable(
3036
+ `transient RPC failure with no idempotency key; refusing to retry: ${errorMessage(err)}`,
3037
+ { cause: err }
3038
+ );
3039
+ }
3040
+ lastErr = err;
3041
+ if (attempt < maxAttempts) {
3042
+ const delay = baseBackoffMs + Math.floor(Math.random() * (jitterMs + 1));
3043
+ await sleep(delay);
3044
+ continue;
3045
+ }
3046
+ throw err;
3047
+ }
3048
+ }
3049
+ throw lastErr ?? new SpendGuardError("runWithRetry: unreachable");
3050
+ }
3051
+ function clampMaxAttempts(value) {
3052
+ if (!Number.isFinite(value) || !Number.isInteger(value)) return 2;
3053
+ if (value < 1) return 1;
3054
+ if (value > 5) return 5;
3055
+ return value;
3056
+ }
3057
+ function defaultSleep(ms) {
3058
+ return new Promise((resolve) => {
3059
+ setTimeout(resolve, ms);
3060
+ });
3061
+ }
3062
+ function errorMessage(err) {
3063
+ if (err instanceof Error) return err.message;
3064
+ if (typeof err === "string") return err;
3065
+ return String(err);
3066
+ }
3067
+ var storage = new AsyncLocalStorage();
3068
+ function currentRunPlan() {
3069
+ return storage.getStore() ?? null;
3070
+ }
3071
+
3072
+ // src/session.ts
3073
+ function buildReserveSessionRequest(req) {
3074
+ assertPositiveDecimal(req.estimatedAmountAtomic, "estimatedAmountAtomic");
3075
+ assertPositiveInteger(req.ttlSeconds, "ttlSeconds");
3076
+ return {
3077
+ tenantId: req.tenantId,
3078
+ budgetId: req.budgetId,
3079
+ windowInstanceId: req.windowInstanceId,
3080
+ unit: mapUnitRef(req.unit),
3081
+ pricing: {
3082
+ pricingVersion: req.pricing.pricingVersion,
3083
+ priceSnapshotHash: req.pricing.pricingHash,
3084
+ fxRateVersion: req.pricing.fxRateVersion ?? "",
3085
+ unitConversionVersion: req.pricing.unitConversionVersion ?? ""
3086
+ },
3087
+ sessionId: req.sessionId,
3088
+ route: req.route,
3089
+ estimatedAmountAtomic: req.estimatedAmountAtomic,
3090
+ ttlSeconds: req.ttlSeconds,
3091
+ idempotencyKey: req.idempotencyKey
3092
+ };
3093
+ }
3094
+ function buildCommitSessionDeltaRequest(req) {
3095
+ assertPositiveDecimal(req.amountAtomicDelta, "amountAtomicDelta");
3096
+ return {
3097
+ sessionReservationId: req.sessionReservationId,
3098
+ streamingCommitId: req.streamingCommitId,
3099
+ amountAtomicDelta: req.amountAtomicDelta,
3100
+ outcome: commitOutcomeEnumOf(req.outcome),
3101
+ eventTime: toTimestamp(req.eventTime),
3102
+ idempotencyKey: req.idempotencyKey
3103
+ };
3104
+ }
3105
+ function buildReleaseSessionRequest(req) {
3106
+ return {
3107
+ sessionReservationId: req.sessionReservationId,
3108
+ reasonCode: req.reasonCode,
3109
+ eventTime: toTimestamp(req.eventTime),
3110
+ idempotencyKey: req.idempotencyKey
3111
+ };
3112
+ }
3113
+ function mapUnitRef(unit) {
3114
+ return {
3115
+ unitId: unit.unitId ?? "",
3116
+ kind: 0,
3117
+ currency: "",
3118
+ unitName: unit.unit,
3119
+ tokenKind: "",
3120
+ modelFamily: "",
3121
+ creditProgram: ""
3122
+ };
3123
+ }
3124
+ function commitOutcomeEnumOf(outcome) {
3125
+ switch (outcome) {
3126
+ case "SUCCESS":
3127
+ return 1 /* SUCCESS */;
3128
+ case "PROVIDER_ERROR":
3129
+ return 2 /* PROVIDER_ERROR */;
3130
+ case "CLIENT_TIMEOUT":
3131
+ return 3 /* CLIENT_TIMEOUT */;
3132
+ case "RUN_ABORTED":
3133
+ return 4 /* RUN_ABORTED */;
3134
+ }
3135
+ }
3136
+ function assertPositiveDecimal(value, field) {
3137
+ if (!/^[0-9]+$/.test(value)) {
3138
+ throw new RangeError(`${field} must be a positive decimal string`);
3139
+ }
3140
+ if (BigInt(value) <= 0n) {
3141
+ throw new RangeError(`${field} must be greater than zero`);
3142
+ }
3143
+ }
3144
+ function assertPositiveInteger(value, field) {
3145
+ if (!Number.isInteger(value) || value <= 0) {
3146
+ throw new RangeError(`${field} must be a positive integer`);
3147
+ }
3148
+ }
3149
+ function toTimestamp(value) {
3150
+ if (value instanceof Date) return epochMsToTimestamp(value.getTime());
3151
+ if (typeof value === "number") return epochMsToTimestamp(value);
3152
+ return value;
3153
+ }
3154
+ function epochMsToTimestamp(epochMs) {
3155
+ if (!Number.isFinite(epochMs)) {
3156
+ throw new RangeError("eventTime must be finite");
3157
+ }
3158
+ const seconds = Math.floor(epochMs / 1e3);
3159
+ const nanos = (epochMs - seconds * 1e3) * 1e6;
3160
+ return { seconds: seconds.toString(), nanos };
3161
+ }
3162
+ function timestampToDate(value) {
3163
+ if (value === void 0) return null;
3164
+ const seconds = typeof value.seconds === "bigint" ? Number(value.seconds) : Number.parseInt(value.seconds, 10);
3165
+ if (!Number.isFinite(seconds)) return null;
3166
+ return new Date(seconds * 1e3 + Math.floor(value.nanos / 1e6));
3167
+ }
3168
+
3169
+ // src/version.ts
3170
+ var VERSION = "0.5.0";
3171
+
3172
+ // src/client.ts
3173
+ var SLICE_7_NOT_WIRED = "not yet wired \u2014 SLICE 7 (see docs/internal/slices/COV_S05_05_d05_release_query.md anti-scope)";
3174
+ var QUERY_BUDGET_NOT_YET_WIRED = "query_budget not yet wired in sidecar; tracked at https://github.com/m24927605/agentic-spendguard/issues/TBD-queryBudget";
3175
+ function resolveDisabled(explicitDisabled, envSnapshot) {
3176
+ if (explicitDisabled === true) {
3177
+ emitDisabledWarning(
3178
+ "SpendGuard ENFORCEMENT DISABLED via the explicit `disabled: true` option \u2014 all decisions return CONTINUE with no reservation and no audit record. This is for tests only and must never be set in production."
3179
+ );
3180
+ return true;
3181
+ }
3182
+ if (envSnapshot.disabled === true) {
3183
+ if (envSnapshot.profile === "demo") {
3184
+ emitDisabledWarning(
3185
+ "SpendGuard ENFORCEMENT DISABLED via SPENDGUARD_DISABLE under SPENDGUARD_PROFILE=demo \u2014 all decisions return CONTINUE with no reservation and no audit record. Never set this in production."
3186
+ );
3187
+ return true;
3188
+ }
3189
+ emitDisabledWarning(
3190
+ `SPENDGUARD_DISABLE is set but SPENDGUARD_PROFILE is not 'demo' (got ${JSON.stringify(envSnapshot.profile ?? "")}); IGNORING the disable request and keeping enforcement ON. To intentionally disable enforcement (tests/demos only) set SPENDGUARD_PROFILE=demo or pass \`disabled: true\` explicitly.`
3191
+ );
3192
+ return false;
3193
+ }
3194
+ return false;
3195
+ }
3196
+ function emitDisabledWarning(message) {
3197
+ const proc = globalThis.process;
3198
+ if (proc?.emitWarning !== void 0) {
3199
+ proc.emitWarning(message, "SpendGuardEnforcementWarning");
3200
+ }
3201
+ }
3202
+ var SpendGuardClient = class _SpendGuardClient {
3203
+ /** Frozen, merged + validated configuration. */
3204
+ cfg;
3205
+ /** Active gRPC transport, or `null` before `connect()` / after `close()`. */
3206
+ transport = null;
3207
+ /** Active SidecarAdapter gRPC client; mirrors `transport` lifetime. */
3208
+ adapterClient = null;
3209
+ /** Cached handshake outcome; first `handshake()` populates, subsequent reads reuse. */
3210
+ handshakeResult = null;
3211
+ /**
3212
+ * Coalesces concurrent `handshake()` callers into a single in-flight RPC.
3213
+ * Mirrors Python `self._handshake_lock` in `client.py`. Stays non-null only
3214
+ * while a handshake RPC is pending; cleared after success or failure so a
3215
+ * post-failure retry can re-enter.
3216
+ */
3217
+ handshakeInFlight = null;
3218
+ /**
3219
+ * Construct a client. Per design.md §5.2: explicit options win over env
3220
+ * fallback; required fields without either throw `SpendGuardConfigError`
3221
+ * immediately.
3222
+ */
3223
+ constructor(rawOpts = {}) {
3224
+ let envSnapshot;
3225
+ try {
3226
+ envSnapshot = resolveEnvConfig();
3227
+ } catch (err) {
3228
+ if (err instanceof EnvParseError) {
3229
+ throw new SpendGuardConfigError(err.message);
3230
+ }
3231
+ throw err;
3232
+ }
3233
+ const socketPath = rawOpts.socketPath ?? envSnapshot.socketPath ?? "";
3234
+ const tenantId = rawOpts.tenantId ?? envSnapshot.tenantId ?? "";
3235
+ const disabled = resolveDisabled(rawOpts.disabled, envSnapshot);
3236
+ const cfg = {
3237
+ socketPath,
3238
+ tenantId,
3239
+ runtimeKind: rawOpts.runtimeKind ?? "",
3240
+ runtimeVersion: rawOpts.runtimeVersion ?? "",
3241
+ sdkVersion: rawOpts.sdkVersion ?? VERSION,
3242
+ protocolVersion: rawOpts.protocolVersion ?? DEFAULT_PROTOCOL_VERSION,
3243
+ capabilityLevel: rawOpts.capabilityLevel ?? DEFAULT_CAPABILITY_LEVEL,
3244
+ workloadInstanceId: rawOpts.workloadInstanceId ?? envSnapshot.workloadInstanceId ?? "",
3245
+ decisionTimeoutMs: rawOpts.decisionTimeoutMs ?? envSnapshot.decisionTimeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS,
3246
+ handshakeTimeoutMs: rawOpts.handshakeTimeoutMs ?? envSnapshot.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS,
3247
+ publishTimeoutMs: rawOpts.publishTimeoutMs ?? DEFAULT_PUBLISH_TIMEOUT_MS,
3248
+ traceTimeoutMs: rawOpts.traceTimeoutMs ?? DEFAULT_TRACE_TIMEOUT_MS,
3249
+ runtime: rawOpts.runtime ?? "uds-grpc",
3250
+ disabled,
3251
+ runProjectionDefault: rawOpts.runProjectionDefault ?? envSnapshot.runProjectionDefault ?? ""
3252
+ };
3253
+ if (rawOpts.onSpan !== void 0) {
3254
+ cfg.onSpan = rawOpts.onSpan;
3255
+ }
3256
+ if (rawOpts.otelTracer !== void 0) {
3257
+ cfg.otelTracer = rawOpts.otelTracer;
3258
+ }
3259
+ if (rawOpts.idempotencyCache !== void 0) {
3260
+ cfg.idempotencyCache = rawOpts.idempotencyCache;
3261
+ }
3262
+ validateConfig(cfg);
3263
+ this.cfg = Object.freeze(cfg);
3264
+ }
3265
+ /**
3266
+ * Wrap an RPC body in the configured observability hook(s). Threads BOTH
3267
+ * `cfg.otelTracer` and `cfg.onSpan` into `withOtelSpan` so every RPC site
3268
+ * gets span emission uniformly — the documented `onSpan` observer (the
3269
+ * no-OTel-dep path) is invoked once per RPC, fixing the prior drift where
3270
+ * `onSpan` was stored but never called. The two are mutually exclusive at
3271
+ * config-validation time, so at most one fires.
3272
+ */
3273
+ withSpan(rpcName, attributes, fn) {
3274
+ return withOtelSpan(this.cfg.otelTracer, rpcName, attributes, fn, this.cfg.onSpan);
3275
+ }
3276
+ /**
3277
+ * Convenience factory that reads required config from env vars and falls
3278
+ * back to `/var/run/spendguard/adapter.sock` when `SPENDGUARD_SOCKET_PATH`
3279
+ * is unset.
3280
+ *
3281
+ * Env vars consumed:
3282
+ * - `SPENDGUARD_SOCKET_PATH` (slice-doc alias) / `SPENDGUARD_SIDECAR_UDS`
3283
+ * (design §5.1) — UDS path; defaults to `/var/run/spendguard/adapter.sock`.
3284
+ * - `SPENDGUARD_TENANT_ID` — **required**; throws `SpendGuardConfigError`
3285
+ * when unset.
3286
+ * - `SPENDGUARD_RUN_PROJECTION_DEFAULT` — optional default
3287
+ * `run_projection` policy name; SLICE 4 wires consumption.
3288
+ * - `SPENDGUARD_WORKLOAD_INSTANCE_ID` / `SPENDGUARD_DECISION_TIMEOUT_MS`
3289
+ * / `SPENDGUARD_HANDSHAKE_TIMEOUT_MS` / `SPENDGUARD_DISABLE` — optional
3290
+ * per design §5.1.
3291
+ *
3292
+ * Extra options provided as the `overrides` argument win over env per
3293
+ * design.md §5.2.
3294
+ *
3295
+ * @throws SpendGuardConfigError when `SPENDGUARD_TENANT_ID` is missing.
3296
+ */
3297
+ static fromEnv(overrides = {}) {
3298
+ let envSnapshot;
3299
+ try {
3300
+ envSnapshot = resolveEnvConfig();
3301
+ } catch (err) {
3302
+ if (err instanceof EnvParseError) {
3303
+ throw new SpendGuardConfigError(err.message);
3304
+ }
3305
+ throw err;
3306
+ }
3307
+ const resolvedTenant = overrides.tenantId ?? envSnapshot.tenantId;
3308
+ const merged = {
3309
+ socketPath: overrides.socketPath ?? envSnapshot.socketPath ?? DEFAULT_SOCKET_PATH,
3310
+ ...resolvedTenant !== void 0 ? { tenantId: resolvedTenant } : {},
3311
+ ...envSnapshot.workloadInstanceId !== void 0 ? { workloadInstanceId: envSnapshot.workloadInstanceId } : {},
3312
+ ...envSnapshot.runProjectionDefault !== void 0 ? { runProjectionDefault: envSnapshot.runProjectionDefault } : {},
3313
+ ...envSnapshot.decisionTimeoutMs !== void 0 ? { decisionTimeoutMs: envSnapshot.decisionTimeoutMs } : {},
3314
+ ...envSnapshot.handshakeTimeoutMs !== void 0 ? { handshakeTimeoutMs: envSnapshot.handshakeTimeoutMs } : {},
3315
+ // NOTE: we deliberately do NOT promote `envSnapshot.disabled` into an
3316
+ // explicit `disabled: true` option here. Doing so would bypass the
3317
+ // fail-closed profile gate in `resolveDisabled` (an explicit option is
3318
+ // honored unconditionally). The constructor re-reads the env itself and
3319
+ // applies the `SPENDGUARD_PROFILE=demo` gate to the env-var disable
3320
+ // path. An explicit `overrides.disabled` (deliberate code-level opt-in)
3321
+ // still wins via the spread below.
3322
+ ...overrides
3323
+ };
3324
+ return new _SpendGuardClient(merged);
3325
+ }
3326
+ // ── Lifecycle ────────────────────────────────────────────────────────────
3327
+ /**
3328
+ * Open the UDS gRPC channel. Idempotent — a second call when already
3329
+ * connected is a no-op.
3330
+ *
3331
+ * Per design.md §6.3 / Python `client.py:240-251`, the `unix:` URI scheme
3332
+ * is used and `grpc.default_authority=localhost` is set so the tonic-based
3333
+ * sidecar accepts the HTTP/2 `:authority` pseudo-header. Without this
3334
+ * channel option, tonic resets every stream with `PROTOCOL_ERROR`.
3335
+ *
3336
+ * In disabled mode (`SPENDGUARD_DISABLE=1` or `disabled: true`), no
3337
+ * transport is opened — the call returns immediately. Subsequent RPCs
3338
+ * short-circuit to no-op outcomes in SLICE 4.
3339
+ *
3340
+ * @throws SpendGuardConnectionError when the underlying transport could
3341
+ * not be opened (e.g. malformed socket path).
3342
+ */
3343
+ async connect() {
3344
+ if (this.cfg.disabled) return;
3345
+ if (this.transport !== null) return;
3346
+ const target = `unix:${this.cfg.socketPath}`;
3347
+ try {
3348
+ this.transport = new GrpcTransport({
3349
+ host: target,
3350
+ channelCredentials: this.buildChannelCredentials(),
3351
+ clientOptions: {
3352
+ // tonic-compat: see design §6.3 + Python `client.py:240-251`.
3353
+ "grpc.default_authority": "localhost",
3354
+ // v1 message ceiling per review-standards §6.3 (≥ 4 MiB).
3355
+ "grpc.max_receive_message_length": 4 * 1024 * 1024,
3356
+ "grpc.max_send_message_length": 4 * 1024 * 1024
3357
+ }
3358
+ });
3359
+ this.adapterClient = new SidecarAdapterClient(this.transport);
3360
+ } catch (err) {
3361
+ this.transport = null;
3362
+ this.adapterClient = null;
3363
+ throw new SpendGuardConnectionError(
3364
+ `failed to open UDS transport to ${target}: ${errorMessage2(err)}`,
3365
+ { cause: err }
3366
+ );
3367
+ }
3368
+ }
3369
+ /**
3370
+ * Graceful close. Idempotent — calling `close()` twice (or `close()` before
3371
+ * `connect()`) does not throw.
3372
+ *
3373
+ * On success the transport is dropped; the next `connect()` allocates a new
3374
+ * channel. In-flight RPCs may complete with a `CANCELLED` status; SLICE 8
3375
+ * adds the grace-period drain semantics that mirror the Python SDK's
3376
+ * `await ch.close(grace=0.5)` path.
3377
+ */
3378
+ async close() {
3379
+ const t = this.transport;
3380
+ this.transport = null;
3381
+ this.adapterClient = null;
3382
+ if (t === null) return;
3383
+ try {
3384
+ t.close();
3385
+ } catch {
3386
+ }
3387
+ }
3388
+ /**
3389
+ * ESM 2024 `await using` hook. Equivalent to `await this.close()`.
3390
+ *
3391
+ * Usage:
3392
+ *
3393
+ * await using client = new SpendGuardClient({ ... });
3394
+ * // ... use client ...
3395
+ * // [Symbol.asyncDispose] runs here automatically.
3396
+ */
3397
+ async [Symbol.asyncDispose]() {
3398
+ await this.close();
3399
+ }
3400
+ // ── Read-only state ──────────────────────────────────────────────────────
3401
+ /** The tenant id this client asserted at construction. Stable for the client's lifetime. */
3402
+ get tenantId() {
3403
+ return this.cfg.tenantId;
3404
+ }
3405
+ /**
3406
+ * The negotiated session id. Throws `HandshakeError` until `handshake()`
3407
+ * has completed (SLICE 4 wires the handshake; until then the getter is
3408
+ * effectively unusable, which is intentional — adapters should call
3409
+ * `handshake()` before reading state).
3410
+ *
3411
+ * @throws HandshakeError before `handshake()` completes.
3412
+ */
3413
+ get sessionId() {
3414
+ if (this.handshakeResult === null) {
3415
+ throw new HandshakeError("handshake() has not completed; sessionId is not yet known");
3416
+ }
3417
+ return this.handshakeResult.sessionId;
3418
+ }
3419
+ /** Full handshake outcome. Throws `HandshakeError` until `handshake()` completes. */
3420
+ get handshakeOutcome() {
3421
+ if (this.handshakeResult === null) {
3422
+ throw new HandshakeError("handshake() has not completed");
3423
+ }
3424
+ return this.handshakeResult;
3425
+ }
3426
+ /** Whether the client is currently connected to the sidecar. */
3427
+ get isConnected() {
3428
+ return this.transport !== null;
3429
+ }
3430
+ /** Frozen view of the resolved configuration. Useful for tests + debugging. */
3431
+ get config() {
3432
+ return this.cfg;
3433
+ }
3434
+ // ── Handshake (design.md §4.5 lifecycle) ────────────────────────────────
3435
+ /**
3436
+ * Mandatory initial handshake. Idempotent — a second call returns the cached
3437
+ * outcome without re-issuing the RPC (design.md §4.5). Concurrent callers
3438
+ * are coalesced into the same in-flight RPC via `handshakeInFlight`.
3439
+ *
3440
+ * Disabled mode (`SPENDGUARD_DISABLE=1` / `disabled: true`) short-circuits
3441
+ * to a synthetic `HandshakeOutcome` so unit tests can run without a real
3442
+ * sidecar (`makeDisabledHandshake`).
3443
+ *
3444
+ * @throws HandshakeError on protocol-version mismatch or insufficient
3445
+ * capability advertisement.
3446
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3447
+ * @throws SpendGuardError on any other gRPC failure surface.
3448
+ */
3449
+ async handshake(opts = {}) {
3450
+ if (this.cfg.disabled) {
3451
+ if (this.handshakeResult === null) {
3452
+ this.handshakeResult = makeDisabledHandshake();
3453
+ }
3454
+ return this.handshakeResult;
3455
+ }
3456
+ if (this.handshakeResult !== null) return this.handshakeResult;
3457
+ if (this.handshakeInFlight !== null) return this.handshakeInFlight;
3458
+ const promise = this.doHandshake(opts).finally(() => {
3459
+ this.handshakeInFlight = null;
3460
+ });
3461
+ this.handshakeInFlight = promise;
3462
+ return promise;
3463
+ }
3464
+ /**
3465
+ * Internal: issue the real Handshake RPC and map the response.
3466
+ *
3467
+ * Splits out from `handshake()` so the idempotency guard there stays
3468
+ * obviously correct: `doHandshake` never reads `handshakeResult` itself;
3469
+ * it only writes it on success.
3470
+ */
3471
+ async doHandshake(opts) {
3472
+ if (this.adapterClient === null) {
3473
+ await this.connect();
3474
+ }
3475
+ const adapter = this.adapterClient;
3476
+ if (adapter === null) {
3477
+ throw new SidecarUnavailable("transport not established for handshake");
3478
+ }
3479
+ const workloadInstanceId = opts.workloadInstanceId ?? this.cfg.workloadInstanceId ?? "";
3480
+ const req = {
3481
+ sdkVersion: this.cfg.sdkVersion,
3482
+ runtimeKind: this.cfg.runtimeKind,
3483
+ runtimeVersion: this.cfg.runtimeVersion,
3484
+ // Wire-level enum uses the same numeric value as cfg.capabilityLevel
3485
+ // (DEFAULT_CAPABILITY_LEVEL = 0x40 = L3_POLICY_HOOK = 64).
3486
+ capabilityLevel: this.cfg.capabilityLevel,
3487
+ tenantIdAssertion: this.cfg.tenantId,
3488
+ workloadInstanceId,
3489
+ protocolVersion: this.cfg.protocolVersion
3490
+ };
3491
+ const resp = await this.withSpan(
3492
+ "handshake",
3493
+ {
3494
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3495
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3496
+ },
3497
+ async () => {
3498
+ try {
3499
+ return await adapter.handshake(req, {
3500
+ timeout: this.cfg.handshakeTimeoutMs
3501
+ }).response;
3502
+ } catch (err) {
3503
+ throw mapGrpcStatusToError(err, { rpc: "handshake" });
3504
+ }
3505
+ }
3506
+ );
3507
+ if (resp.protocolVersion !== this.cfg.protocolVersion) {
3508
+ throw new HandshakeError(
3509
+ `protocol version mismatch: adapter=${this.cfg.protocolVersion} sidecar=${resp.protocolVersion}`
3510
+ );
3511
+ }
3512
+ const outcome = {
3513
+ sessionId: resp.sessionId,
3514
+ sidecarVersion: resp.sidecarVersion,
3515
+ schemaBundleId: resp.schemaBundle?.schemaBundleId ?? "",
3516
+ schemaBundleHash: resp.schemaBundle?.schemaBundleHash ?? new Uint8Array(),
3517
+ contractBundleId: resp.contractBundle?.bundleId ?? "",
3518
+ contractBundleHash: resp.contractBundle?.bundleHash ?? new Uint8Array(),
3519
+ capabilityRequired: Number(resp.capabilityRequired ?? 0),
3520
+ signingKeyId: resp.signingKeyId,
3521
+ announcementSignature: resp.announcementSignature ?? new Uint8Array()
3522
+ };
3523
+ if (outcome.capabilityRequired > this.cfg.capabilityLevel) {
3524
+ throw new HandshakeError(
3525
+ `sidecar requires capability ${toHex(outcome.capabilityRequired)} but adapter advertised ${toHex(this.cfg.capabilityLevel)}; refusing`
3526
+ );
3527
+ }
3528
+ this.handshakeResult = outcome;
3529
+ return outcome;
3530
+ }
3531
+ // ── Core RPC surface (handshake / reserve / commitEstimated wired) ───────
3532
+ /**
3533
+ * Run a `*.pre` decision boundary through the sidecar. Equivalent to the
3534
+ * Python SDK's `request_decision` (design.md §4.7).
3535
+ *
3536
+ * The wire shape is built in `buildDecisionRequest()` and consumes:
3537
+ * - the cached handshake `sessionId` (auto-handshakes on first use),
3538
+ * - the caller-supplied `idempotencyKey` (REQUIRED — see design §6.5),
3539
+ * - `runProjectionDefault` from config when the caller did not pass
3540
+ * one in `decisionContextJson.run_projection_policy` (closes MJ-1).
3541
+ *
3542
+ * The response is mapped through `mapDecisionResponse()`: CONTINUE / DEGRADE
3543
+ * return a `DecisionOutcome`; STOP / STOP_RUN_PROJECTION / SKIP /
3544
+ * REQUIRE_APPROVAL raise the matching typed exception so adapters can route
3545
+ * on `instanceof DecisionDenied` (and its subclasses) per review-standards §5.
3546
+ *
3547
+ * @throws DecisionStopped on STOP / STOP_RUN_PROJECTION.
3548
+ * @throws DecisionSkipped on SKIP.
3549
+ * @throws ApprovalRequired on REQUIRE_APPROVAL — `await err.resume(client)`
3550
+ * surfaces the operator decision.
3551
+ * @throws DecisionDenied on an unknown decision enum.
3552
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3553
+ * @throws SpendGuardError on any other gRPC failure surface.
3554
+ */
3555
+ async reserve(req) {
3556
+ if (this.cfg.disabled) return makeDisabledDecision(req);
3557
+ if (this.handshakeResult === null) {
3558
+ throw new HandshakeError(
3559
+ "reserve() requires handshake(); call await client.handshake() before reserve()"
3560
+ );
3561
+ }
3562
+ const cache = this.cfg.idempotencyCache;
3563
+ const bodyHash = cache !== void 0 && req.idempotencyKey.length > 0 ? computeReserveBodyHash(req) : void 0;
3564
+ if (cache !== void 0 && req.idempotencyKey.length > 0) {
3565
+ const cached = cache.get(req.idempotencyKey, bodyHash);
3566
+ if (cached !== void 0) return cached;
3567
+ }
3568
+ if (this.adapterClient === null) {
3569
+ await this.connect();
3570
+ }
3571
+ const adapter = this.adapterClient;
3572
+ if (adapter === null) {
3573
+ throw new SidecarUnavailable("transport not established for reserve");
3574
+ }
3575
+ const grpcReq = this.buildDecisionRequest(req);
3576
+ return await this.withSpan(
3577
+ "reserve",
3578
+ {
3579
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3580
+ [SPENDGUARD_OTEL_ATTR.DECISION_ID]: req.decisionId,
3581
+ [SPENDGUARD_OTEL_ATTR.TRIGGER]: req.trigger,
3582
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3583
+ },
3584
+ async () => {
3585
+ let resp;
3586
+ try {
3587
+ resp = await runWithRetry(
3588
+ async () => {
3589
+ try {
3590
+ return await adapter.requestDecision(grpcReq, {
3591
+ timeout: this.cfg.decisionTimeoutMs
3592
+ }).response;
3593
+ } catch (err) {
3594
+ throw mapGrpcStatusToError(err, { rpc: "reserve" });
3595
+ }
3596
+ },
3597
+ { idempotencyKey: req.idempotencyKey }
3598
+ );
3599
+ } catch (err) {
3600
+ if (err instanceof SpendGuardError) throw err;
3601
+ throw mapGrpcStatusToError(err, { rpc: "reserve" });
3602
+ }
3603
+ if (resp.error && resp.error.code !== 0) {
3604
+ throw new SpendGuardError(
3605
+ `sidecar error code=${resp.error.code} message=${resp.error.message}`
3606
+ );
3607
+ }
3608
+ const outcome = mapDecisionResponse(resp, this.cfg.tenantId);
3609
+ if (cache !== void 0 && req.idempotencyKey.length > 0) {
3610
+ cache.set(req.idempotencyKey, outcome, void 0, bodyHash);
3611
+ }
3612
+ return outcome;
3613
+ }
3614
+ );
3615
+ }
3616
+ /**
3617
+ * Alias for `reserve()` — identical function reference (review-standards §1.5
3618
+ * P0 BLOCKER). The Python SDK exposes the symbol as `request_decision`; the
3619
+ * TS surface keeps `reserve` as the canonical name AND exposes
3620
+ * `requestDecision` so cross-language docs work without surprise.
3621
+ *
3622
+ * Implemented as an instance-field initializer that reads `this.reserve`
3623
+ * during construction. The dot-lookup on `this.reserve` (inside the field
3624
+ * initializer, before any instance shadow exists) resolves to the prototype
3625
+ * method `SpendGuardClient.prototype.reserve`. Assigning it to the field
3626
+ * makes `client.requestDecision === client.reserve` Boolean-true at runtime
3627
+ * (both resolve to the same prototype function reference).
3628
+ *
3629
+ * NOTE on `.bind(this)`: implementation.md §4 line 581 sketches the field
3630
+ * as `this.reserve.bind(this)`. The literal `bind` would produce a NEW
3631
+ * function object and break the §1.5 identity gate; the constraint cited
3632
+ * by the slice doc (review-standards §1.5 P0 BLOCKER) wins, so we drop
3633
+ * `.bind(this)`. Callers always invoke as `client.requestDecision(req)`
3634
+ * (method-call form) which preserves `this` via JS dispatch semantics —
3635
+ * the bind was over-specification for the Pythonic detached-method
3636
+ * pattern, which the TS SDK does not advertise.
3637
+ *
3638
+ * NOTE: do NOT add a JSDoc `@throws` block here — TypeScript erases JSDoc
3639
+ * from runtime fields and the identity invariant is the primary contract
3640
+ * this declaration enforces.
3641
+ */
3642
+ requestDecision = this.reserve;
3643
+ /**
3644
+ * Commit an estimated LLM-call outcome. Equivalent to the Python SDK's
3645
+ * `emit_llm_call_post` with `estimated_amount_atomic` (design.md §4.8).
3646
+ *
3647
+ * Single-event LlmCallPostPayload over the EmitTraceEvents duplex stream:
3648
+ * the client opens a fresh stream per commit, sends one event, awaits one
3649
+ * ack, and closes (Python parity — `emit_llm_call_post` at client.py:818).
3650
+ * SLICE 5+ may switch to a long-lived stream for production latency, but
3651
+ * the per-event setup cost is acceptable in v0.1.x.
3652
+ *
3653
+ * Ack semantics: the sidecar emits exactly one `TraceEventAck` per inbound
3654
+ * event in this POC. Status != ACCEPTED surfaces as `SpendGuardError`
3655
+ * (Codex round-2 P1.1 from Python parity — silent failure here would mask
3656
+ * a commit-lifecycle bug).
3657
+ *
3658
+ * Mutually exclusive with the deferred provider-report path: this method
3659
+ * always sends `estimated_amount_atomic`; the `provider_reported_amount_atomic`
3660
+ * wire field stays empty. Adapters needing the provider-report path use the
3661
+ * lower-level `emitLlmCallPost` (SLICE 7+).
3662
+ *
3663
+ * **SLICE 5 multi-event extension.** When `req.outcomeKind` is set, the
3664
+ * client emits TWO events on the same bidi stream — the original
3665
+ * LLM_CALL_POST event first, then a second LLM_CALL_POST-kind event whose
3666
+ * `outcome` field reflects `outcomeKind` (SUCCESS → SUCCESS,
3667
+ * FAILURE → PROVIDER_ERROR) and whose `providerResponseMetadata` carries a
3668
+ * `{"error_message": ...}` envelope when `actualErrorMessage` is supplied.
3669
+ * Both events are acked individually; if either ack is non-ACCEPTED the
3670
+ * method raises `SpendGuardError`. See the JSDoc on
3671
+ * `CommitEstimatedRequest.outcomeKind` for the LLM_CALL_OUTCOME proto-kind
3672
+ * deviation note (Declared Deviation #1 in SLICE 5).
3673
+ *
3674
+ * When `req.outcomeKind` is ABSENT, behaviour is identical to SLICE 4 —
3675
+ * a single event is sent, a single ack is drained.
3676
+ *
3677
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3678
+ * @throws SpendGuardError on rejected ack or any other gRPC failure surface.
3679
+ */
3680
+ async commitEstimated(req) {
3681
+ if (this.cfg.disabled) return;
3682
+ if (this.handshakeResult === null) {
3683
+ throw new HandshakeError(
3684
+ "commitEstimated() requires handshake(); call await client.handshake() before commitEstimated()"
3685
+ );
3686
+ }
3687
+ if (this.adapterClient === null) {
3688
+ await this.connect();
3689
+ }
3690
+ const adapter = this.adapterClient;
3691
+ if (adapter === null) {
3692
+ throw new SidecarUnavailable("transport not established for commitEstimated");
3693
+ }
3694
+ const events = [this.buildLlmCallPostEvent(req)];
3695
+ if (req.outcomeKind !== void 0) {
3696
+ events.push(this.buildLlmCallOutcomeEvent(req));
3697
+ }
3698
+ await this.withSpan(
3699
+ "commitEstimated",
3700
+ {
3701
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3702
+ [SPENDGUARD_OTEL_ATTR.DECISION_ID]: req.decisionId,
3703
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3704
+ },
3705
+ async () => {
3706
+ let call;
3707
+ try {
3708
+ call = adapter.emitTraceEvents({
3709
+ timeout: this.cfg.traceTimeoutMs
3710
+ });
3711
+ for (const event of events) {
3712
+ await call.requests.send(event);
3713
+ }
3714
+ await call.requests.complete();
3715
+ } catch (err) {
3716
+ throw mapGrpcStatusToError(err, { rpc: "commitEstimated" });
3717
+ }
3718
+ try {
3719
+ let acked = 0;
3720
+ for await (const ack of call.responses) {
3721
+ acked += 1;
3722
+ if (ack.status !== 1 /* ACCEPTED */) {
3723
+ throw new SpendGuardError(buildAckRejectMessage(ack));
3724
+ }
3725
+ }
3726
+ await call.status;
3727
+ await call.trailers;
3728
+ if (acked === 0) {
3729
+ throw new SpendGuardError("EmitTraceEvents closed without an ack from sidecar");
3730
+ }
3731
+ if (acked < events.length) {
3732
+ throw new SpendGuardError(
3733
+ `EmitTraceEvents acked ${acked} of ${events.length} events before closing`
3734
+ );
3735
+ }
3736
+ } catch (err) {
3737
+ if (err instanceof SpendGuardError) throw err;
3738
+ throw mapGrpcStatusToError(err, { rpc: "commitEstimated" });
3739
+ }
3740
+ }
3741
+ );
3742
+ }
3743
+ /**
3744
+ * Explicit release of a held reservation. Matches Agent Spend Protocol
3745
+ * Draft-01 §4 one-to-one (the proto wire's
3746
+ * `ReleaseReservationRequest` carries the canonical ASP fields at tags 1-3
3747
+ * and SpendGuard extensions at tag 100+).
3748
+ *
3749
+ * Behaviour:
3750
+ * - Disabled-mode short-circuit returns a synthetic
3751
+ * `makeDisabledReleaseOutcome(req)` — no UDS contact.
3752
+ * - Pre-handshake call throws `HandshakeError` via the `sessionId` getter
3753
+ * gate (the request envelope requires the negotiated session id).
3754
+ * - Wire envelope built by `buildReleaseRequest(req, sessionId)`.
3755
+ * - Response mapped by `mapReleaseResponse(res, decisionIdHint)`.
3756
+ * - Errors mapped centrally through `mapGrpcStatusToError`, with the
3757
+ * `release`-specific NOT_FOUND override (reservation lookup misses
3758
+ * surface as a plain `SpendGuardError("reservation not found")` so
3759
+ * adapters can distinguish "no such reservation" from the rich
3760
+ * FAILED_PRECONDITION cluster).
3761
+ *
3762
+ * @throws HandshakeError before `handshake()` completes.
3763
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3764
+ * @throws MutationApplyFailed on FAILED_PRECONDITION + IDEMPOTENCY_CONFLICT
3765
+ * or BUDGET_EXCEEDED — or an unknown FAILED_PRECONDITION reason (the
3766
+ * conservative default; never bare `SpendGuardError` for this cluster).
3767
+ * @throws ApprovalBundleHotReloadedError on FAILED_PRECONDITION +
3768
+ * BUNDLE_HOT_RELOADED.
3769
+ * @throws SpendGuardError on NOT_FOUND ("reservation not found") + any
3770
+ * other unmapped gRPC failure.
3771
+ */
3772
+ async release(req) {
3773
+ if (this.cfg.disabled) return makeDisabledReleaseOutcome(req);
3774
+ if (this.handshakeResult === null) {
3775
+ throw new HandshakeError(
3776
+ "release() requires handshake(); call await client.handshake() before release()"
3777
+ );
3778
+ }
3779
+ if (this.adapterClient === null) {
3780
+ await this.connect();
3781
+ }
3782
+ const adapter = this.adapterClient;
3783
+ if (adapter === null) {
3784
+ throw new SidecarUnavailable("transport not established for release");
3785
+ }
3786
+ const grpcReq = buildReleaseRequest(req, this.handshakeResult.sessionId);
3787
+ return await this.withSpan(
3788
+ "release",
3789
+ {
3790
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3791
+ [SPENDGUARD_OTEL_ATTR.RESERVATION_ID]: req.reservationId,
3792
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3793
+ },
3794
+ async () => {
3795
+ let resp;
3796
+ try {
3797
+ resp = await adapter.releaseReservation(grpcReq, {
3798
+ timeout: this.cfg.publishTimeoutMs
3799
+ }).response;
3800
+ } catch (err) {
3801
+ throw mapGrpcStatusToError(err, { rpc: "release", releaseNotFoundAsPlain: true });
3802
+ }
3803
+ return mapReleaseResponse(resp);
3804
+ }
3805
+ );
3806
+ }
3807
+ /**
3808
+ * Reserve a session-scoped hold for a realtime voice session (D41 SR-V3).
3809
+ *
3810
+ * The public request shape mirrors `buildReserveSessionRequest`. When
3811
+ * `req.sessionId` is empty, the SDK fills it from the completed sidecar
3812
+ * handshake so adapter code can bind the session reservation to the active
3813
+ * UDS session without duplicating handshake plumbing.
3814
+ *
3815
+ * @throws HandshakeError before `handshake()` completes.
3816
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3817
+ * @throws SpendGuardError on proto error outcome or unmapped gRPC failure.
3818
+ */
3819
+ async reserveSession(req) {
3820
+ if (this.cfg.disabled) {
3821
+ buildReserveSessionRequest(req);
3822
+ return makeDisabledReserveSessionOutcome(req);
3823
+ }
3824
+ if (this.handshakeResult === null) {
3825
+ throw new HandshakeError(
3826
+ "reserveSession() requires handshake(); call await client.handshake() before reserveSession()"
3827
+ );
3828
+ }
3829
+ if (this.adapterClient === null) {
3830
+ await this.connect();
3831
+ }
3832
+ const adapter = this.adapterClient;
3833
+ if (adapter === null) {
3834
+ throw new SidecarUnavailable("transport not established for reserveSession");
3835
+ }
3836
+ const grpcReq = buildReserveSessionRequest({
3837
+ ...req,
3838
+ sessionId: req.sessionId || this.handshakeResult.sessionId
3839
+ });
3840
+ return await this.withSpan(
3841
+ "reserveSession",
3842
+ {
3843
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3844
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3845
+ },
3846
+ async () => {
3847
+ try {
3848
+ const resp = await adapter.reserveSession(grpcReq, {
3849
+ timeout: this.cfg.decisionTimeoutMs
3850
+ }).response;
3851
+ return mapReserveSessionOutcome(resp);
3852
+ } catch (err) {
3853
+ throw mapGrpcStatusToError(err, { rpc: "reserveSession" });
3854
+ }
3855
+ }
3856
+ );
3857
+ }
3858
+ /**
3859
+ * Commit one positive streaming spend delta against a session reservation.
3860
+ *
3861
+ * @throws HandshakeError before `handshake()` completes.
3862
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3863
+ * @throws SpendGuardError on proto error outcome or unmapped gRPC failure.
3864
+ */
3865
+ async commitSessionDelta(req) {
3866
+ if (this.cfg.disabled) {
3867
+ buildCommitSessionDeltaRequest(req);
3868
+ return makeDisabledCommitSessionDeltaOutcome(req);
3869
+ }
3870
+ if (this.handshakeResult === null) {
3871
+ throw new HandshakeError(
3872
+ "commitSessionDelta() requires handshake(); call await client.handshake() before commitSessionDelta()"
3873
+ );
3874
+ }
3875
+ if (this.adapterClient === null) {
3876
+ await this.connect();
3877
+ }
3878
+ const adapter = this.adapterClient;
3879
+ if (adapter === null) {
3880
+ throw new SidecarUnavailable("transport not established for commitSessionDelta");
3881
+ }
3882
+ const grpcReq = buildCommitSessionDeltaRequest(req);
3883
+ return await this.withSpan(
3884
+ "commitSessionDelta",
3885
+ {
3886
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3887
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3888
+ },
3889
+ async () => {
3890
+ try {
3891
+ const resp = await adapter.commitSessionDelta(grpcReq, {
3892
+ timeout: this.cfg.traceTimeoutMs
3893
+ }).response;
3894
+ return mapCommitSessionDeltaOutcome(resp);
3895
+ } catch (err) {
3896
+ throw mapGrpcStatusToError(err, { rpc: "commitSessionDelta" });
3897
+ }
3898
+ }
3899
+ );
3900
+ }
3901
+ /**
3902
+ * Release the uncommitted remainder of a session reservation.
3903
+ *
3904
+ * @throws HandshakeError before `handshake()` completes.
3905
+ * @throws SidecarUnavailable on UNAVAILABLE / DEADLINE_EXCEEDED / CANCELLED.
3906
+ * @throws SpendGuardError on proto error outcome or unmapped gRPC failure.
3907
+ */
3908
+ async releaseSession(req) {
3909
+ if (this.cfg.disabled) {
3910
+ buildReleaseSessionRequest(req);
3911
+ return makeDisabledReleaseSessionOutcome(req);
3912
+ }
3913
+ if (this.handshakeResult === null) {
3914
+ throw new HandshakeError(
3915
+ "releaseSession() requires handshake(); call await client.handshake() before releaseSession()"
3916
+ );
3917
+ }
3918
+ if (this.adapterClient === null) {
3919
+ await this.connect();
3920
+ }
3921
+ const adapter = this.adapterClient;
3922
+ if (adapter === null) {
3923
+ throw new SidecarUnavailable("transport not established for releaseSession");
3924
+ }
3925
+ const grpcReq = buildReleaseSessionRequest(req);
3926
+ return await this.withSpan(
3927
+ "releaseSession",
3928
+ {
3929
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3930
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3931
+ },
3932
+ async () => {
3933
+ try {
3934
+ const resp = await adapter.releaseSession(grpcReq, {
3935
+ timeout: this.cfg.publishTimeoutMs
3936
+ }).response;
3937
+ return mapReleaseSessionOutcome(resp);
3938
+ } catch (err) {
3939
+ throw mapGrpcStatusToError(err, { rpc: "releaseSession" });
3940
+ }
3941
+ }
3942
+ );
3943
+ }
3944
+ /**
3945
+ * Read-only budget snapshot. Locked decision #4 of design.md §9: in v0.1.x
3946
+ * the substrate ships the method signature but the sidecar wire is NOT yet
3947
+ * implemented. SLICE 5 wires the §9.4 placeholder body — adapters call this
3948
+ * method, catch the explicit `SpendGuardError`, and surface a clear "feature
3949
+ * not yet available" upstream rather than a stray NOT_FOUND from a missing
3950
+ * RPC route.
3951
+ *
3952
+ * Disabled-mode short-circuit returns a synthetic
3953
+ * `makeDisabledQueryBudgetResult(req)` so unit tests can program against
3954
+ * the method without a sidecar.
3955
+ *
3956
+ * @throws HandshakeError before `handshake()` completes.
3957
+ * @throws SpendGuardError carrying the tracking-issue URL otherwise.
3958
+ */
3959
+ async queryBudget(req) {
3960
+ if (this.cfg.disabled) return makeDisabledQueryBudgetResult(req);
3961
+ if (this.handshakeResult === null) {
3962
+ throw new HandshakeError(
3963
+ "queryBudget() requires handshake(); call await client.handshake() before queryBudget()"
3964
+ );
3965
+ }
3966
+ return await this.withSpan(
3967
+ "queryBudget",
3968
+ {
3969
+ [SPENDGUARD_OTEL_ATTR.TENANT_ID]: this.cfg.tenantId,
3970
+ [SPENDGUARD_OTEL_ATTR.SCOPE_ID]: req.scopeId,
3971
+ [SPENDGUARD_OTEL_ATTR.SDK_VERSION]: this.cfg.sdkVersion
3972
+ },
3973
+ async () => {
3974
+ throw new SpendGuardError(QUERY_BUDGET_NOT_YET_WIRED);
3975
+ }
3976
+ );
3977
+ }
3978
+ // ── Lower-level surface (deferred to SLICE 7+) ──────────────────────────
3979
+ /**
3980
+ * Confirm `publish_effect` outcome. SLICE 7 wires body.
3981
+ * @throws SpendGuardError until SLICE 7 wires the body.
3982
+ */
3983
+ async confirmPublishOutcome(req) {
3984
+ throw new SpendGuardError(`confirmPublishOutcome() ${SLICE_7_NOT_WIRED}`);
3985
+ }
3986
+ /**
3987
+ * Resume after a human approver acted on a `REQUIRE_APPROVAL` decision.
3988
+ * SLICE 7 wires the body; references this method from `ApprovalRequired.resume`.
3989
+ * @throws SpendGuardError until SLICE 7 wires the body.
3990
+ */
3991
+ async resumeAfterApproval(req) {
3992
+ throw new SpendGuardError(`resumeAfterApproval() ${SLICE_7_NOT_WIRED}`);
3993
+ }
3994
+ /**
3995
+ * Safe-ack the `APPLY_FAILED` publish outcome — swallows transport errors
3996
+ * so the caller's original exception is never shadowed. SLICE 7 wires body.
3997
+ * @throws SpendGuardError until SLICE 7 wires the body.
3998
+ */
3999
+ async safeConfirmApplyFailed(req) {
4000
+ throw new SpendGuardError(`safeConfirmApplyFailed() ${SLICE_7_NOT_WIRED}`);
4001
+ }
4002
+ /**
4003
+ * Lower-level entry point that `commitEstimated()` wraps. Provided so
4004
+ * adapters that need the raw trace-event surface have access. SLICE 7
4005
+ * wires body (the provider-report path).
4006
+ * @throws SpendGuardError until SLICE 7 wires the body.
4007
+ */
4008
+ async emitLlmCallPost(req) {
4009
+ throw new SpendGuardError(`emitLlmCallPost() ${SLICE_7_NOT_WIRED}`);
4010
+ }
4011
+ // ── Internals ────────────────────────────────────────────────────────────
4012
+ /**
4013
+ * Build the gRPC channel credentials. Always insecure over UDS — the kernel
4014
+ * `SO_PEERCRED` check on the sidecar side is the trust anchor (Sidecar
4015
+ * Architecture §5). TLS over a Unix socket adds overhead with no security
4016
+ * benefit when the connection is implicitly local.
4017
+ *
4018
+ * Carved into its own method so a future slice can override under a
4019
+ * `runtime` flag once HTTP-gateway transport is added — `runtime: "fetch"`
4020
+ * would override to use TLS credentials. v0.1.x only supports `"uds-grpc"`.
4021
+ */
4022
+ buildChannelCredentials() {
4023
+ return credentials.createInsecure();
4024
+ }
4025
+ /**
4026
+ * Translate the public `ReserveRequest` (camelCase, TS-idiomatic) into the
4027
+ * snake_case-on-wire `DecisionRequest` proto. Per implementation.md §4:
4028
+ *
4029
+ * 1. SessionId from the cached handshake (caller already gated above).
4030
+ * 2. Trigger enum mapping via `triggerEnumOf()`.
4031
+ * 3. W3C `traceparent` → `TraceContext` via `buildTraceContext()` (matches
4032
+ * Python `_build_trace_context`).
4033
+ * 4. `runtimeMetadata` carries the prompt hash (when caller supplied
4034
+ * `promptText`) and any `decisionContextJson` keys. The
4035
+ * `run_projection_policy` slot is filled from the caller's
4036
+ * `decisionContextJson.run_projection_policy` if present, otherwise
4037
+ * from `cfg.runProjectionDefault` when non-empty. **This is the
4038
+ * SLICE 4 consumption of MJ-1** — SLICE 3 stored the field on the
4039
+ * config; this method wires it onto the wire.
4040
+ * 5. `plannedStepsHint` is `plan.plannedCalls + plan.plannedTools` when
4041
+ * a `withRunPlan` scope is active (SLICE 7 R2), otherwise the proto3
4042
+ * default `0`.
4043
+ *
4044
+ * `runtime_metadata` is encoded as a hand-built `google.protobuf.Struct`
4045
+ * payload because the SDK does not yet ship `computePromptHash` (SLICE 6).
4046
+ * Until then this method ALWAYS sends an empty Struct body when no caller
4047
+ * decoration is requested — matching Python `runtime_metadata = None` which
4048
+ * is wire-equivalent to "field absent" under proto3 message optionality.
4049
+ */
4050
+ buildDecisionRequest(req) {
4051
+ if (this.handshakeResult === null) {
4052
+ throw new HandshakeError("internal: buildDecisionRequest without handshake");
4053
+ }
4054
+ const plan = currentRunPlan();
4055
+ const plannedStepsHint = plan !== null ? plan.plannedCalls + plan.plannedTools : 0;
4056
+ const trigger = triggerEnumOf(req.trigger);
4057
+ const trace = buildTraceContext(req.traceparent ?? "", req.tracestate ?? "");
4058
+ const ids = {
4059
+ runId: req.runId,
4060
+ stepId: req.stepId,
4061
+ llmCallId: req.llmCallId,
4062
+ toolCallId: req.toolCallId ?? "",
4063
+ decisionId: req.decisionId,
4064
+ snapshotId: ""
4065
+ };
4066
+ const projectedClaims = req.projectedClaims.map((claim) => ({
4067
+ budgetId: claim.scopeId,
4068
+ unit: mapUnitRef2(claim.unit),
4069
+ amountAtomic: claim.amountAtomic,
4070
+ // direction: DEBIT (1) — SDK callers only project debits; credits are
4071
+ // generated server-side as compensating ledger entries (Stage 2 §4.6).
4072
+ direction: 1,
4073
+ // HARDEN_D05_WI — thread caller-supplied windowInstanceId onto the
4074
+ // wire claim. Omitted keeps the pre-HARDEN wire shape ("").
4075
+ windowInstanceId: claim.windowInstanceId ?? ""
4076
+ }));
4077
+ const runtimeMetadata = this.buildRuntimeMetadataStruct(req);
4078
+ const inputs = {
4079
+ projectedClaims,
4080
+ projectedP50Atomic: req.projectedP50Atomic ?? "",
4081
+ projectedP90Atomic: req.projectedP90Atomic ?? "",
4082
+ projectedP95Atomic: req.projectedP95Atomic ?? "",
4083
+ projectedP99Atomic: req.projectedP99Atomic ?? "",
4084
+ ...req.projectedUnit !== void 0 ? { projectedUnit: mapUnitRef2(req.projectedUnit) } : {},
4085
+ ...runtimeMetadata !== void 0 ? { runtimeMetadata } : {},
4086
+ ...req.claimEstimate !== void 0 ? { claimEstimate: mapClaimEstimate(req.claimEstimate) } : {}
4087
+ };
4088
+ const idempotency = {
4089
+ key: req.idempotencyKey,
4090
+ // The sidecar/ledger own the canonical request hash; SDK leaves it empty
4091
+ // (matches Python parity at client.py:494).
4092
+ requestHash: new Uint8Array()
4093
+ };
4094
+ return {
4095
+ sessionId: this.handshakeResult.sessionId,
4096
+ trigger,
4097
+ trace,
4098
+ ids,
4099
+ route: req.route,
4100
+ inputs,
4101
+ parentRunId: req.parentRunId ?? "",
4102
+ budgetGrantJti: req.budgetGrantJti ?? "",
4103
+ idempotency,
4104
+ // SLICE 7 R2: Signal 3 — `plannedCalls + plannedTools` when an active
4105
+ // `withRunPlan` scope is in flight, otherwise proto3 default `0`. The
4106
+ // sidecar enforces the upper bound `[0, MAX_PLANNED_STEPS]` server-side
4107
+ // (`services/run_cost_projector/src/server.rs`) so the SDK doesn't gate
4108
+ // on a value the server may bump independently.
4109
+ plannedStepsHint,
4110
+ // D13 additive proto fields. These defaults preserve the pre-D13 BYOK
4111
+ // request-scoped path until an adapter explicitly opts into meter-only.
4112
+ reservationSource: 0 /* UNSPECIFIED */,
4113
+ meterOnlyEstimate: false
4114
+ };
4115
+ }
4116
+ /**
4117
+ * Build the `google.protobuf.Struct` payload that lands in
4118
+ * `DecisionRequest.inputs.runtime_metadata`. Returns `undefined` when there
4119
+ * is nothing to send (proto3 message optionality — wire equivalent to
4120
+ * "field absent").
4121
+ *
4122
+ * Two slots are populated here:
4123
+ * - `decision_context_json.*` keys from the caller (verbatim).
4124
+ * - `run_projection_policy` from the caller (if present in
4125
+ * `decisionContextJson`) OR `cfg.runProjectionDefault` (when set and
4126
+ * non-empty). The caller's value wins; the default only fills in when
4127
+ * the caller did not provide one — matches design.md §4.2 R2 semantics.
4128
+ *
4129
+ * SLICE 6 R1 closure of SLICE 4 M-3: when `req.promptText` is set,
4130
+ * `computePromptHash(req.promptText, this.cfg.tenantId)` populates
4131
+ * `runtime_metadata.prompt_hash` as a stringValue. Mirrors Python parity
4132
+ * at `sdk/python/.../client.py` (the `prompt_hash` field is the rules
4133
+ * dedup key per Cost Advisor P0.5 §5.1). The caller may pre-set
4134
+ * `decisionContextJson.prompt_hash` to override (e.g. when the prompt is
4135
+ * tokenised upstream and the hash is computed there); the caller-supplied
4136
+ * value wins.
4137
+ */
4138
+ buildRuntimeMetadataStruct(req) {
4139
+ const fields = {};
4140
+ let hasField = false;
4141
+ if (req.decisionContextJson !== void 0) {
4142
+ for (const [k, v] of Object.entries(req.decisionContextJson)) {
4143
+ fields[k] = jsonValueToStructValue(v);
4144
+ hasField = true;
4145
+ }
4146
+ }
4147
+ if (this.cfg.runProjectionDefault !== "" && fields.run_projection_policy === void 0) {
4148
+ fields.run_projection_policy = jsonValueToStructValue(this.cfg.runProjectionDefault);
4149
+ hasField = true;
4150
+ }
4151
+ if (req.promptText !== void 0 && fields.prompt_hash === void 0) {
4152
+ const hash = computePromptHash(req.promptText, this.cfg.tenantId);
4153
+ fields.prompt_hash = jsonValueToStructValue(hash);
4154
+ hasField = true;
4155
+ }
4156
+ return hasField ? { fields } : void 0;
4157
+ }
4158
+ /**
4159
+ * Build the single LLM_CALL_POST trace event for `commitEstimated()`.
4160
+ * Mirrors Python `emit_llm_call_post` at client.py:818 with the difference
4161
+ * that `provider_reported_amount_atomic` is always empty here (the
4162
+ * provider-report path lives in SLICE 5+'s `emitLlmCallPost`).
4163
+ */
4164
+ buildLlmCallPostEvent(req) {
4165
+ if (this.handshakeResult === null) {
4166
+ throw new HandshakeError("internal: buildLlmCallPostEvent without handshake");
4167
+ }
4168
+ const ts = wallClockToTimestamp(Date.now());
4169
+ return {
4170
+ sessionId: this.handshakeResult.sessionId,
4171
+ trace: buildTraceContext(req.traceparent ?? "", req.tracestate ?? ""),
4172
+ ids: {
4173
+ runId: req.runId,
4174
+ stepId: req.stepId,
4175
+ llmCallId: req.llmCallId,
4176
+ toolCallId: "",
4177
+ decisionId: req.decisionId,
4178
+ snapshotId: ""
4179
+ },
4180
+ kind: 4 /* LLM_CALL_POST */,
4181
+ eventTime: ts,
4182
+ payload: {
4183
+ oneofKind: "llmCallPost",
4184
+ llmCallPost: {
4185
+ reservationId: req.reservationId,
4186
+ providerReportedAmountAtomic: "",
4187
+ unit: mapUnitRef2(req.unit),
4188
+ pricing: {
4189
+ pricingVersion: req.pricing.pricingVersion,
4190
+ priceSnapshotHash: req.pricing.pricingHash,
4191
+ // HARDEN_D05_WI — thread the full freeze tuple (omitted → "").
4192
+ fxRateVersion: req.pricing.fxRateVersion ?? "",
4193
+ unitConversionVersion: req.pricing.unitConversionVersion ?? ""
4194
+ },
4195
+ providerEventId: req.providerEventId,
4196
+ outcome: llmOutcomeEnumOf(req.outcome),
4197
+ estimatedAmountAtomic: req.estimatedAmountAtomic,
4198
+ ...req.actualInputTokens !== void 0 ? { actualInputTokens: String(req.actualInputTokens) } : {},
4199
+ ...req.actualOutputTokens !== void 0 ? { actualOutputTokens: String(req.actualOutputTokens) } : {},
4200
+ ...req.deltaBRatio !== void 0 ? { deltaBRatio: req.deltaBRatio } : {},
4201
+ ...req.deltaCRatio !== void 0 ? { deltaCRatio: req.deltaCRatio } : {}
4202
+ }
4203
+ },
4204
+ providerResponseMetadata: req.providerResponseMetadata ?? ""
4205
+ };
4206
+ }
4207
+ /**
4208
+ * Build the SLICE 5 multi-event "outcome" companion event for
4209
+ * `commitEstimated()` when `req.outcomeKind` is set.
4210
+ *
4211
+ * The event reuses `TraceEvent_EventKind.LLM_CALL_POST` (per Declared
4212
+ * Deviation #1 — `LLM_CALL_OUTCOME` does not exist as a proto enum value
4213
+ * in `sidecar_adapter/v1/adapter.proto` yet).
4214
+ *
4215
+ * TODO(GH-issue-TBD): proto bump for `LLM_CALL_OUTCOME` +
4216
+ * sidecar `x-spendguard-reason-code` trailer extension. Both deferred to
4217
+ * the cross-component slice that touches `proto/` and
4218
+ * `services/sidecar/`. Track at
4219
+ * https://github.com/m24927605/agentic-spendguard/issues/TBD-proto-bump-llm-call-outcome
4220
+ * (R2 follow-up; not in scope for D05 SLICE 5).
4221
+ *
4222
+ * The `outcome` field on the inner `LlmCallPostPayload` carries the
4223
+ * semantic:
4224
+ *
4225
+ * - `outcomeKind === "SUCCESS"` → `LlmCallPostPayload_Outcome.SUCCESS`
4226
+ * - `outcomeKind === "FAILURE"` → `LlmCallPostPayload_Outcome.PROVIDER_ERROR`
4227
+ *
4228
+ * Actuals (`actualInputTokens` / `actualOutputTokens`) prefer the SLICE 5
4229
+ * `*Wire` fields when supplied (int64-as-string form), falling back to
4230
+ * the SLICE 4 numeric `actualInputTokens` / `actualOutputTokens` shape so
4231
+ * adapters do not need to double-specify. `actualErrorMessage` is threaded
4232
+ * onto `TraceEvent.providerResponseMetadata` as a JSON envelope
4233
+ * `{"error_message": "..."}` only when `outcomeKind === "FAILURE"`.
4234
+ *
4235
+ * `estimatedAmountAtomic` is intentionally set to an empty string on the
4236
+ * outcome event — the first event already booked the commit; the outcome
4237
+ * event only carries observation, not commit semantics. The sidecar will
4238
+ * surface a rejection ack if it requires the field (`mock-sidecar`'s tests
4239
+ * lock this in).
4240
+ *
4241
+ * @private
4242
+ */
4243
+ buildLlmCallOutcomeEvent(req) {
4244
+ if (this.handshakeResult === null) {
4245
+ throw new HandshakeError("internal: buildLlmCallOutcomeEvent without handshake");
4246
+ }
4247
+ if (req.outcomeKind === void 0) {
4248
+ throw new SpendGuardError(
4249
+ "internal: buildLlmCallOutcomeEvent called without outcomeKind set"
4250
+ );
4251
+ }
4252
+ const ts = wallClockToTimestamp(Date.now());
4253
+ const inputWire = req.actualInputTokensWire ?? (req.actualInputTokens !== void 0 ? String(req.actualInputTokens) : void 0);
4254
+ const outputWire = req.actualOutputTokensWire ?? (req.actualOutputTokens !== void 0 ? String(req.actualOutputTokens) : void 0);
4255
+ const outcomeEnum = req.outcomeKind === "SUCCESS" ? 1 /* SUCCESS */ : 2 /* PROVIDER_ERROR */;
4256
+ const errorMessageEnvelope = req.outcomeKind === "FAILURE" && req.actualErrorMessage !== void 0 ? JSON.stringify({ error_message: req.actualErrorMessage }) : req.providerResponseMetadata ?? "";
4257
+ return {
4258
+ sessionId: this.handshakeResult.sessionId,
4259
+ trace: buildTraceContext(req.traceparent ?? "", req.tracestate ?? ""),
4260
+ ids: {
4261
+ runId: req.runId,
4262
+ stepId: req.stepId,
4263
+ llmCallId: req.llmCallId,
4264
+ toolCallId: "",
4265
+ decisionId: req.decisionId,
4266
+ snapshotId: ""
4267
+ },
4268
+ kind: 4 /* LLM_CALL_POST */,
4269
+ eventTime: ts,
4270
+ payload: {
4271
+ oneofKind: "llmCallPost",
4272
+ llmCallPost: {
4273
+ reservationId: req.reservationId,
4274
+ providerReportedAmountAtomic: "",
4275
+ unit: mapUnitRef2(req.unit),
4276
+ pricing: {
4277
+ pricingVersion: req.pricing.pricingVersion,
4278
+ priceSnapshotHash: req.pricing.pricingHash,
4279
+ // HARDEN_D05_WI — thread the full freeze tuple (omitted → "").
4280
+ fxRateVersion: req.pricing.fxRateVersion ?? "",
4281
+ unitConversionVersion: req.pricing.unitConversionVersion ?? ""
4282
+ },
4283
+ providerEventId: req.providerEventId,
4284
+ outcome: outcomeEnum,
4285
+ // The outcome companion event carries observation; the booking
4286
+ // amount stayed on the first event. Empty string is wire-equivalent
4287
+ // to "absent" for the proto3 string field.
4288
+ estimatedAmountAtomic: "",
4289
+ ...inputWire !== void 0 ? { actualInputTokens: inputWire } : {},
4290
+ ...outputWire !== void 0 ? { actualOutputTokens: outputWire } : {},
4291
+ ...req.deltaBRatio !== void 0 ? { deltaBRatio: req.deltaBRatio } : {},
4292
+ ...req.deltaCRatio !== void 0 ? { deltaCRatio: req.deltaCRatio } : {}
4293
+ }
4294
+ },
4295
+ providerResponseMetadata: errorMessageEnvelope
4296
+ };
4297
+ }
4298
+ };
4299
+ function errorMessage2(err) {
4300
+ if (err instanceof Error) return err.message;
4301
+ if (typeof err === "string") return err;
4302
+ try {
4303
+ return JSON.stringify(err);
4304
+ } catch {
4305
+ return String(err);
4306
+ }
4307
+ }
4308
+ function toHex(n) {
4309
+ return `0x${n.toString(16)}`;
4310
+ }
4311
+ function triggerEnumOf(name) {
4312
+ switch (name) {
4313
+ case "RUN_PRE":
4314
+ return 1 /* RUN_PRE */;
4315
+ case "AGENT_STEP_PRE":
4316
+ return 2 /* AGENT_STEP_PRE */;
4317
+ case "LLM_CALL_PRE":
4318
+ return 3 /* LLM_CALL_PRE */;
4319
+ case "TOOL_CALL_PRE":
4320
+ return 4 /* TOOL_CALL_PRE */;
4321
+ default: {
4322
+ throw new SpendGuardError(`unknown trigger: ${String(name)}`);
4323
+ }
4324
+ }
4325
+ }
4326
+ function llmOutcomeEnumOf(name) {
4327
+ switch (name) {
4328
+ case "SUCCESS":
4329
+ return 1 /* SUCCESS */;
4330
+ case "PROVIDER_ERROR":
4331
+ return 2 /* PROVIDER_ERROR */;
4332
+ case "CLIENT_TIMEOUT":
4333
+ return 3 /* CLIENT_TIMEOUT */;
4334
+ case "RUN_ABORTED":
4335
+ return 4 /* RUN_ABORTED */;
4336
+ default: {
4337
+ throw new SpendGuardError(`unknown llm outcome: ${String(name)}`);
4338
+ }
4339
+ }
4340
+ }
4341
+ function decisionEnumName(value) {
4342
+ switch (value) {
4343
+ case 1 /* CONTINUE */:
4344
+ return "CONTINUE";
4345
+ case 2 /* DEGRADE */:
4346
+ return "DEGRADE";
4347
+ case 3 /* SKIP */:
4348
+ return "SKIP";
4349
+ case 4 /* STOP */:
4350
+ return "STOP";
4351
+ case 5 /* REQUIRE_APPROVAL */:
4352
+ return "REQUIRE_APPROVAL";
4353
+ case 6 /* STOP_RUN_PROJECTION */:
4354
+ return "STOP_RUN_PROJECTION";
4355
+ default:
4356
+ return "UNKNOWN";
4357
+ }
4358
+ }
4359
+ function buildTraceContext(traceparent, tracestate) {
4360
+ if (traceparent === "") {
4361
+ return {
4362
+ traceId: "",
4363
+ spanId: "",
4364
+ parentSpanId: "",
4365
+ traceState: tracestate
4366
+ };
4367
+ }
4368
+ const parts = traceparent.split("-");
4369
+ if (parts.length !== 4 || parts[1]?.length !== 32 || parts[2]?.length !== 16) {
4370
+ return {
4371
+ traceId: "",
4372
+ spanId: "",
4373
+ parentSpanId: "",
4374
+ traceState: tracestate
4375
+ };
4376
+ }
4377
+ return {
4378
+ traceId: parts[1],
4379
+ spanId: parts[2],
4380
+ // Per Python parity, reuse upstream span_id as parent_span_id — the next
4381
+ // event is a child span from the sidecar's perspective.
4382
+ parentSpanId: parts[2],
4383
+ traceState: tracestate
4384
+ };
4385
+ }
4386
+ function mapUnitRef2(unit) {
4387
+ return {
4388
+ unitId: unit.unitId ?? "",
4389
+ kind: 0,
4390
+ currency: "",
4391
+ unitName: unit.unit,
4392
+ tokenKind: "",
4393
+ modelFamily: "",
4394
+ creditProgram: ""
4395
+ };
4396
+ }
4397
+ function mapClaimEstimate(estimate) {
4398
+ return {
4399
+ tokenizerTier: estimate.tokenizerTier ?? "",
4400
+ tokenizerVersionId: estimate.tokenizerVersionId ?? "",
4401
+ inputTokens: int64Field(estimate.inputTokens),
4402
+ predictedATokens: int64Field(estimate.predictedATokens),
4403
+ predictedBTokens: int64Field(estimate.predictedBTokens),
4404
+ predictedCTokens: int64Field(estimate.predictedCTokens),
4405
+ reservedStrategy: estimate.reservedStrategy ?? "",
4406
+ predictionStrategyUsed: estimate.predictionStrategyUsed ?? "",
4407
+ predictionPolicyUsed: estimate.predictionPolicyUsed ?? "",
4408
+ predictionConfidence: estimate.predictionConfidence ?? 0,
4409
+ predictionSampleSize: int64Field(estimate.predictionSampleSize),
4410
+ coldStartLayerUsed: estimate.coldStartLayerUsed ?? "",
4411
+ classifierVersion: estimate.classifierVersion ?? "",
4412
+ fingerprintVersion: estimate.fingerprintVersion ?? "",
4413
+ promptClassFingerprint: estimate.promptClassFingerprint ?? "",
4414
+ runProjectionAtDecisionAtomic: int64Field(estimate.runProjectionAtDecisionAtomic),
4415
+ runPredictedRemainingSteps: estimate.runPredictedRemainingSteps ?? 0,
4416
+ runStepsCompletedSoFar: int64Field(estimate.runStepsCompletedSoFar),
4417
+ runCodeTriggered: estimate.runCodeTriggered ?? "",
4418
+ model: estimate.model ?? "",
4419
+ promptClass: estimate.promptClass ?? ""
4420
+ };
4421
+ }
4422
+ function int64Field(v) {
4423
+ if (v === void 0) return "0";
4424
+ return typeof v === "bigint" ? v.toString() : Math.trunc(v).toString();
4425
+ }
4426
+ function wallClockToTimestamp(epochMs) {
4427
+ const seconds = Math.floor(epochMs / 1e3);
4428
+ const nanos = epochMs % 1e3 * 1e6;
4429
+ return { seconds: seconds.toString(), nanos };
4430
+ }
4431
+ function jsonValueToStructValue(value) {
4432
+ if (value === null || value === void 0) {
4433
+ return { kind: { oneofKind: "nullValue", nullValue: 0 } };
4434
+ }
4435
+ if (typeof value === "boolean") {
4436
+ return { kind: { oneofKind: "boolValue", boolValue: value } };
4437
+ }
4438
+ if (typeof value === "number") {
4439
+ return { kind: { oneofKind: "numberValue", numberValue: value } };
4440
+ }
4441
+ if (typeof value === "bigint") {
4442
+ return { kind: { oneofKind: "stringValue", stringValue: value.toString() } };
4443
+ }
4444
+ if (typeof value === "string") {
4445
+ return { kind: { oneofKind: "stringValue", stringValue: value } };
4446
+ }
4447
+ if (Array.isArray(value)) {
4448
+ return {
4449
+ kind: {
4450
+ oneofKind: "listValue",
4451
+ listValue: { values: value.map(jsonValueToStructValue) }
4452
+ }
4453
+ };
4454
+ }
4455
+ if (value instanceof Date) {
4456
+ return { kind: { oneofKind: "stringValue", stringValue: value.toISOString() } };
4457
+ }
4458
+ if (typeof value === "object") {
4459
+ const fields = {};
4460
+ for (const [k, v] of Object.entries(value)) {
4461
+ fields[k] = jsonValueToStructValue(v);
4462
+ }
4463
+ return {
4464
+ kind: { oneofKind: "structValue", structValue: { fields } }
4465
+ };
4466
+ }
4467
+ return { kind: { oneofKind: "stringValue", stringValue: String(value) } };
4468
+ }
4469
+ function mapDecisionResponse(resp, tenantId) {
4470
+ const name = decisionEnumName(resp.decision);
4471
+ if (name === "CONTINUE" || name === "DEGRADE") {
4472
+ return {
4473
+ decisionId: resp.decisionId,
4474
+ auditDecisionEventId: resp.auditDecisionEventId,
4475
+ decision: name,
4476
+ mutationPatchJson: resp.mutationPatchJson,
4477
+ effectHash: resp.effectHash ?? new Uint8Array(),
4478
+ ledgerTransactionId: resp.ledgerTransactionId,
4479
+ reservationIds: Object.freeze([...resp.reservationIds]),
4480
+ ttlExpiresAtSeconds: Number(resp.ttlExpiresAt?.seconds ?? 0),
4481
+ reasonCodes: Object.freeze([...resp.reasonCodes]),
4482
+ matchedRuleIds: Object.freeze([...resp.matchedRuleIds])
4483
+ };
4484
+ }
4485
+ if (name === "STOP" || name === "STOP_RUN_PROJECTION") {
4486
+ throw new DecisionStopped(
4487
+ `sidecar ${name} terminal=${resp.terminal} reasons=${JSON.stringify(resp.reasonCodes)}`,
4488
+ {
4489
+ decisionId: resp.decisionId,
4490
+ reasonCodes: [...resp.reasonCodes],
4491
+ ...resp.auditDecisionEventId ? { auditDecisionEventId: resp.auditDecisionEventId } : {},
4492
+ matchedRuleIds: [...resp.matchedRuleIds]
4493
+ }
4494
+ );
4495
+ }
4496
+ if (name === "SKIP") {
4497
+ throw new DecisionSkipped(`sidecar SKIP reasons=${JSON.stringify(resp.reasonCodes)}`, {
4498
+ decisionId: resp.decisionId,
4499
+ reasonCodes: [...resp.reasonCodes],
4500
+ ...resp.auditDecisionEventId ? { auditDecisionEventId: resp.auditDecisionEventId } : {},
4501
+ matchedRuleIds: [...resp.matchedRuleIds]
4502
+ });
4503
+ }
4504
+ if (name === "REQUIRE_APPROVAL") {
4505
+ throw new ApprovalRequired(
4506
+ `sidecar REQUIRE_APPROVAL approval_request_id=${resp.approvalRequestId}`,
4507
+ {
4508
+ decisionId: resp.decisionId,
4509
+ approvalRequestId: resp.approvalRequestId,
4510
+ ...resp.approverRole ? { approverRole: resp.approverRole } : {},
4511
+ reasonCodes: [...resp.reasonCodes],
4512
+ ...resp.auditDecisionEventId ? { auditDecisionEventId: resp.auditDecisionEventId } : {},
4513
+ matchedRuleIds: [...resp.matchedRuleIds],
4514
+ tenantId
4515
+ }
4516
+ );
4517
+ }
4518
+ throw new DecisionDenied(`sidecar returned unknown decision=${resp.decision}`, {
4519
+ decisionId: resp.decisionId,
4520
+ reasonCodes: [...resp.reasonCodes],
4521
+ ...resp.auditDecisionEventId ? { auditDecisionEventId: resp.auditDecisionEventId } : {},
4522
+ matchedRuleIds: [...resp.matchedRuleIds]
4523
+ });
4524
+ }
4525
+ function computeReserveBodyHash(req) {
4526
+ const { idempotencyKey: _omit, ...identity } = req;
4527
+ return createHash("sha256").update(canonicalStableJson(identity), "utf8").digest("hex");
4528
+ }
4529
+ function canonicalStableJson(value) {
4530
+ if (value === null) return "null";
4531
+ if (typeof value === "bigint") return `"bigint:${value.toString()}"`;
4532
+ if (value instanceof Uint8Array) return `"bytes:${Buffer.from(value).toString("base64")}"`;
4533
+ if (Array.isArray(value)) {
4534
+ return `[${value.map((v) => canonicalStableJson(v)).join(",")}]`;
4535
+ }
4536
+ if (typeof value === "object") {
4537
+ const obj = value;
4538
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
4539
+ const parts = keys.map((k) => `${JSON.stringify(k)}:${canonicalStableJson(obj[k])}`);
4540
+ return `{${parts.join(",")}}`;
4541
+ }
4542
+ return JSON.stringify(value);
4543
+ }
4544
+ var REASON_CODE_HEADER = "x-spendguard-reason-code";
4545
+ var REASON_CODE_PREFIXES = [
4546
+ // Forward-compat: bracket-tagged emission from the resume path.
4547
+ ["[bundle_hot_reloaded]", "BUNDLE_HOT_RELOADED"],
4548
+ // Defensive: matches the test fixture wording ("bundle hot-reloaded ...")
4549
+ // and any future un-bracketed sidecar emission without coupling to exact
4550
+ // detail text.
4551
+ ["bundle hot-reload", "BUNDLE_HOT_RELOADED"],
4552
+ // DomainError::IdempotencyConflict (error.rs:44-45).
4553
+ ["idempotency conflict", "IDEMPOTENCY_CONFLICT"],
4554
+ // FAILED_PRECONDITION cluster mapped to BUDGET_EXCEEDED — the five
4555
+ // variants from error.rs (lines 26-39) that to_status() routes to
4556
+ // Status::failed_precondition (excluding IdempotencyConflict above).
4557
+ ["reservation state conflict", "BUDGET_EXCEEDED"],
4558
+ ["reservation ttl expired", "BUDGET_EXCEEDED"],
4559
+ ["pricing freeze mismatch", "BUDGET_EXCEEDED"],
4560
+ ["overrun reservation", "BUDGET_EXCEEDED"],
4561
+ ["multi-reservation commit deferred", "BUDGET_EXCEEDED"]
4562
+ ];
4563
+ function mapGrpcStatusToError(err, ctx) {
4564
+ if (err instanceof SpendGuardError) return err;
4565
+ if (!(err instanceof RpcError)) {
4566
+ return new SpendGuardError(`${ctx.rpc} failed: ${errorMessage2(err)}`, { cause: err });
4567
+ }
4568
+ const code = err.code;
4569
+ const cause = err;
4570
+ if (code === "UNAVAILABLE" || code === "DEADLINE_EXCEEDED" || code === "CANCELLED") {
4571
+ return new SidecarUnavailable(
4572
+ `${ctx.rpc} failed: code=${code} detail=${JSON.stringify(err.message)}`,
4573
+ { cause }
4574
+ );
4575
+ }
4576
+ if (code === "FAILED_PRECONDITION") {
4577
+ const reason = readReasonCode(err);
4578
+ if (reason === "BUNDLE_HOT_RELOADED") {
4579
+ return new ApprovalBundleHotReloadedError(
4580
+ `${ctx.rpc} failed: code=FAILED_PRECONDITION reason=BUNDLE_HOT_RELOADED detail=${JSON.stringify(err.message)}`,
4581
+ { originalBundleHash: "", currentBundleHash: "" },
4582
+ { cause }
4583
+ );
4584
+ }
4585
+ const reasonSuffix = reason !== void 0 ? ` reason=${reason}` : "";
4586
+ return new MutationApplyFailed(
4587
+ `${ctx.rpc} failed: code=FAILED_PRECONDITION${reasonSuffix} detail=${JSON.stringify(err.message)}`,
4588
+ { cause }
4589
+ );
4590
+ }
4591
+ if (code === "NOT_FOUND") {
4592
+ if (ctx.releaseNotFoundAsPlain === true) {
4593
+ return new SpendGuardError("reservation not found", { cause });
4594
+ }
4595
+ return new SpendGuardError(
4596
+ `${ctx.rpc} failed: code=NOT_FOUND detail=${JSON.stringify(err.message)}`,
4597
+ { cause }
4598
+ );
4599
+ }
4600
+ if (code === "ABORTED") {
4601
+ return new SpendGuardError(
4602
+ `${ctx.rpc} failed: code=ABORTED detail=${JSON.stringify(err.message)}`,
4603
+ { cause }
4604
+ );
4605
+ }
4606
+ return new SpendGuardError(
4607
+ `${ctx.rpc} failed: code=${code} detail=${JSON.stringify(err.message)}`,
4608
+ { cause }
4609
+ );
4610
+ }
4611
+ function readReasonCode(err) {
4612
+ const msg = err.message;
4613
+ if (typeof msg === "string" && msg.length > 0) {
4614
+ const lower = msg.toLowerCase();
4615
+ for (const [prefix, code] of REASON_CODE_PREFIXES) {
4616
+ if (lower.startsWith(prefix)) return code;
4617
+ }
4618
+ }
4619
+ const raw = err.meta?.[REASON_CODE_HEADER];
4620
+ if (raw === void 0) return void 0;
4621
+ if (typeof raw === "string") return raw;
4622
+ if (Array.isArray(raw) && raw.length > 0 && typeof raw[0] === "string") return raw[0];
4623
+ return void 0;
4624
+ }
4625
+ function buildAckRejectMessage(ack) {
4626
+ const statusName = ackStatusName(ack.status);
4627
+ const code = ack.error?.code ?? 0;
4628
+ const message = ack.error?.message ?? "";
4629
+ return `EmitTraceEvents rejected: status=${statusName} code=${code} message=${JSON.stringify(message)}`;
4630
+ }
4631
+ function ackStatusName(value) {
4632
+ switch (value) {
4633
+ case 1 /* ACCEPTED */:
4634
+ return "ACCEPTED";
4635
+ case 2 /* QUARANTINED */:
4636
+ return "QUARANTINED";
4637
+ case 3 /* REJECTED */:
4638
+ return "REJECTED";
4639
+ default:
4640
+ return `STATUS_${value}`;
4641
+ }
4642
+ }
4643
+ function makeDisabledHandshake() {
4644
+ return {
4645
+ sessionId: "disabled-noop-session",
4646
+ sidecarVersion: "disabled",
4647
+ schemaBundleId: "",
4648
+ schemaBundleHash: new Uint8Array(),
4649
+ contractBundleId: "",
4650
+ contractBundleHash: new Uint8Array(),
4651
+ capabilityRequired: 0,
4652
+ signingKeyId: "",
4653
+ announcementSignature: new Uint8Array()
4654
+ };
4655
+ }
4656
+ function makeDisabledDecision(req) {
4657
+ return {
4658
+ decisionId: req.decisionId,
4659
+ auditDecisionEventId: "",
4660
+ decision: "CONTINUE",
4661
+ mutationPatchJson: "",
4662
+ effectHash: new Uint8Array(),
4663
+ ledgerTransactionId: "",
4664
+ reservationIds: Object.freeze([]),
4665
+ ttlExpiresAtSeconds: 0,
4666
+ reasonCodes: Object.freeze(["disabled_mode"]),
4667
+ matchedRuleIds: Object.freeze([])
4668
+ };
4669
+ }
4670
+ function makeDisabledReleaseOutcome(req) {
4671
+ return {
4672
+ auditEventSignature: new Uint8Array(),
4673
+ ledgerTransactionId: "",
4674
+ releasedReservationIds: Object.freeze([req.reservationId])
4675
+ };
4676
+ }
4677
+ function makeDisabledReserveSessionOutcome(req) {
4678
+ return {
4679
+ kind: "accepted",
4680
+ sessionReservationId: `disabled:${req.sessionId || "session"}`,
4681
+ ledgerTransactionId: "",
4682
+ auditSessionEventId: "",
4683
+ ttlExpiresAt: null,
4684
+ reservedAmountAtomic: req.estimatedAmountAtomic,
4685
+ remainingAmountAtomic: req.estimatedAmountAtomic
4686
+ };
4687
+ }
4688
+ function makeDisabledCommitSessionDeltaOutcome(req) {
4689
+ return {
4690
+ sessionReservationId: req.sessionReservationId,
4691
+ streamingCommitId: req.streamingCommitId,
4692
+ ledgerTransactionId: "",
4693
+ auditSessionEventId: "",
4694
+ committedDeltaAtomic: req.amountAtomicDelta,
4695
+ cumulativeCommittedAtomic: req.amountAtomicDelta,
4696
+ remainingAmountAtomic: "0",
4697
+ recordedAt: null
4698
+ };
4699
+ }
4700
+ function makeDisabledReleaseSessionOutcome(req) {
4701
+ return {
4702
+ sessionReservationId: req.sessionReservationId,
4703
+ ledgerTransactionId: "",
4704
+ auditSessionEventId: "",
4705
+ releasedAmountAtomic: "0",
4706
+ committedAmountAtomic: "0",
4707
+ recordedAt: null
4708
+ };
4709
+ }
4710
+ function makeDisabledQueryBudgetResult(req) {
4711
+ return {
4712
+ availableAtomic: "0",
4713
+ reservedAtomic: "0",
4714
+ committedAtomic: "0",
4715
+ unit: { unit: "USD_MICROS", denomination: 1 },
4716
+ asOfSeconds: req.asOfSeconds ?? 0
4717
+ };
4718
+ }
4719
+ function buildReleaseRequest(req, sessionId) {
4720
+ return {
4721
+ reservationId: req.reservationId,
4722
+ idempotencyKey: req.idempotencyKey,
4723
+ reasonCodes: [...req.reasonCodes ?? []],
4724
+ tenantId: req.tenantId ?? "",
4725
+ workloadInstanceId: req.workloadInstanceId ?? "",
4726
+ sessionId
4727
+ };
4728
+ }
4729
+ function mapReserveSessionOutcome(resp) {
4730
+ switch (resp.outcome.oneofKind) {
4731
+ case "accepted": {
4732
+ const accepted = resp.outcome.accepted;
4733
+ return {
4734
+ kind: "accepted",
4735
+ sessionReservationId: accepted.sessionReservationId,
4736
+ ledgerTransactionId: accepted.ledgerTransactionId,
4737
+ auditSessionEventId: accepted.auditSessionEventId,
4738
+ ttlExpiresAt: timestampToDate(accepted.ttlExpiresAt),
4739
+ reservedAmountAtomic: accepted.reservedAmountAtomic,
4740
+ remainingAmountAtomic: accepted.remainingAmountAtomic
4741
+ };
4742
+ }
4743
+ case "denied": {
4744
+ const denied = resp.outcome.denied;
4745
+ return {
4746
+ kind: "denied",
4747
+ auditSessionEventId: denied.auditSessionEventId,
4748
+ reasonCodes: Object.freeze([...denied.reasonCodes]),
4749
+ matchedRuleIds: Object.freeze([...denied.matchedRuleIds]),
4750
+ ...denied.error !== void 0 ? { error: denied.error } : {}
4751
+ };
4752
+ }
4753
+ case "error":
4754
+ throw new SpendGuardError(
4755
+ `sidecar reserveSession error code=${resp.outcome.error.code} message=${resp.outcome.error.message}`
4756
+ );
4757
+ default:
4758
+ throw new SpendGuardError("sidecar reserveSession returned empty outcome");
4759
+ }
4760
+ }
4761
+ function mapCommitSessionDeltaOutcome(resp) {
4762
+ switch (resp.outcome.oneofKind) {
4763
+ case "accepted": {
4764
+ const accepted = resp.outcome.accepted;
4765
+ return {
4766
+ sessionReservationId: accepted.sessionReservationId,
4767
+ streamingCommitId: accepted.streamingCommitId,
4768
+ ledgerTransactionId: accepted.ledgerTransactionId,
4769
+ auditSessionEventId: accepted.auditSessionEventId,
4770
+ committedDeltaAtomic: accepted.committedDeltaAtomic,
4771
+ cumulativeCommittedAtomic: accepted.cumulativeCommittedAtomic,
4772
+ remainingAmountAtomic: accepted.remainingAmountAtomic,
4773
+ recordedAt: timestampToDate(accepted.recordedAt)
4774
+ };
4775
+ }
4776
+ case "error":
4777
+ throw new SpendGuardError(
4778
+ `sidecar commitSessionDelta error code=${resp.outcome.error.code} message=${resp.outcome.error.message}`
4779
+ );
4780
+ default:
4781
+ throw new SpendGuardError("sidecar commitSessionDelta returned empty outcome");
4782
+ }
4783
+ }
4784
+ function mapReleaseSessionOutcome(resp) {
4785
+ switch (resp.outcome.oneofKind) {
4786
+ case "accepted": {
4787
+ const accepted = resp.outcome.accepted;
4788
+ return {
4789
+ sessionReservationId: accepted.sessionReservationId,
4790
+ ledgerTransactionId: accepted.ledgerTransactionId,
4791
+ auditSessionEventId: accepted.auditSessionEventId,
4792
+ releasedAmountAtomic: accepted.releasedAmountAtomic,
4793
+ committedAmountAtomic: accepted.committedAmountAtomic,
4794
+ recordedAt: timestampToDate(accepted.recordedAt)
4795
+ };
4796
+ }
4797
+ case "error":
4798
+ throw new SpendGuardError(
4799
+ `sidecar releaseSession error code=${resp.outcome.error.code} message=${resp.outcome.error.message}`
4800
+ );
4801
+ default:
4802
+ throw new SpendGuardError("sidecar releaseSession returned empty outcome");
4803
+ }
4804
+ }
4805
+ function mapReleaseResponse(resp) {
4806
+ return {
4807
+ auditEventSignature: resp.auditEventSignature ?? new Uint8Array(),
4808
+ ledgerTransactionId: resp.ledgerTransactionId,
4809
+ releasedReservationIds: Object.freeze([...resp.releasedReservationIds])
4810
+ };
4811
+ }
4812
+
4813
+ export { DEFAULT_CAPABILITY_LEVEL, DEFAULT_DECISION_TIMEOUT_MS, DEFAULT_HANDSHAKE_TIMEOUT_MS, DEFAULT_PROTOCOL_VERSION, DEFAULT_PUBLISH_TIMEOUT_MS, DEFAULT_TRACE_TIMEOUT_MS, REASON_CODE_PREFIXES, SpendGuardClient, computeReserveBodyHash };
4814
+ //# sourceMappingURL=client.js.map
4815
+ //# sourceMappingURL=client.js.map