qati-sdk 1.0.5 → 1.1.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/README.md CHANGED
@@ -12,6 +12,7 @@ The main feature groups on the client are:
12
12
  - `client.trustState` — read risk and trust scores for users or other entities.
13
13
  - `client.advisory` — list persisted advisories for the tenant (with filters and pagination).
14
14
  - `client.explain` — fetch a composite explain / attribution payload for a single entity (by entity key).
15
+ - `client.mccEvaluation` — list, retrieve, and manually trigger MCC evaluation traces (keyset pagination).
15
16
  - `client.events` — send telemetry events to the ingestion pipeline.
16
17
  - `client.webhooks` — manage webhook endpoints for a tenant (register, list, update, delete, trigger test delivery).
17
18
  - `client.quotas` — read and configure numeric caps (entities, monthly events, rate) for a tenant.
@@ -168,6 +169,134 @@ try {
168
169
  }
169
170
  ```
170
171
 
172
+ ## Query API: MCC evaluation traces
173
+
174
+ MCC (Multi-Candidate Commit) evaluation traces record how the scoring engine evaluated candidate actions for an entity — branch scores, predicted risk tiers, constraint failures, and the selected commit. Use `client.mccEvaluation` to query persisted traces or run a manual evaluation against an entity's latest trust state via the Query API.
175
+
176
+ Endpoints:
177
+
178
+ - `GET /v1/mcc/evaluations` — list with optional filters and **keyset pagination**
179
+ - `GET /v1/mcc/evaluations/{evaluation_id}` — fetch one trace by id
180
+ - `POST /v1/mcc/evaluate` — run MCC-Lite against the entity's latest trust state and persist the trace
181
+
182
+ Pagination uses a cursor, not an offset. Pass `after_id` from the previous page's `next_cursor`; when `next_cursor` is `null`, you have reached the last page.
183
+
184
+ ### List evaluations for an entity
185
+
186
+ ```ts
187
+ import { Session } from 'qati-sdk';
188
+
189
+ const session = new Session();
190
+ const client = session.createClient();
191
+ try {
192
+ const page = await client.mccEvaluation.list({
193
+ entityKey: 'SESSION:conv-abc',
194
+ limit: 50,
195
+ });
196
+ console.log(page.total_count, page.evaluations.length);
197
+ for (const ev of page.evaluations) {
198
+ console.log(ev.evaluation_id, ev.commit_type, ev.degraded);
199
+ }
200
+ } finally {
201
+ await client.close();
202
+ }
203
+ ```
204
+
205
+ Optional filters: `entity_key`, `since`, `until` (inclusive bounds on `created_at` as `Date` or ISO-8601 string), and `limit` (server caps at 100).
206
+
207
+ ### Paginate through all evaluations
208
+
209
+ ```ts
210
+ import { Session } from 'qati-sdk';
211
+
212
+ const session = new Session();
213
+ const client = session.createClient();
214
+ try {
215
+ let page = await client.mccEvaluation.list({
216
+ entity_key: 'USER:user-123',
217
+ limit: 50,
218
+ });
219
+ const allIds: string[] = [];
220
+
221
+ while (true) {
222
+ for (const ev of page.evaluations) {
223
+ allIds.push(ev.evaluation_id);
224
+ }
225
+ if (!page.next_cursor) break;
226
+ page = await client.mccEvaluation.list({
227
+ entity_key: 'USER:user-123',
228
+ limit: 50,
229
+ after_id: page.next_cursor,
230
+ });
231
+ }
232
+
233
+ console.log(`Fetched ${allIds.length} evaluation ids`);
234
+ } finally {
235
+ await client.close();
236
+ }
237
+ ```
238
+
239
+ ### Get one evaluation by id
240
+
241
+ Returns the full trace including `evaluated_branches`, `selected_branch`, residuals, and version metadata. A missing id raises `QatiNotFoundError`.
242
+
243
+ ```ts
244
+ import { Session, QatiNotFoundError } from 'qati-sdk';
245
+
246
+ const session = new Session();
247
+ const client = session.createClient();
248
+ try {
249
+ const evaluationId = '550e8400-e29b-41d4-a716-446655440000';
250
+ try {
251
+ const trace = await client.mccEvaluation.get(evaluationId);
252
+ const branch = trace.selected_branch ?? trace.evaluated_branches[0];
253
+ if (branch) {
254
+ console.log(
255
+ trace.entity_key,
256
+ branch.action_type,
257
+ branch.predicted_risk_tier,
258
+ branch.branch_score,
259
+ );
260
+ }
261
+ } catch (e) {
262
+ if (e instanceof QatiNotFoundError) {
263
+ console.log('Evaluation not found');
264
+ } else {
265
+ throw e;
266
+ }
267
+ }
268
+ } finally {
269
+ await client.close();
270
+ }
271
+ ```
272
+
273
+ ### Trigger a manual evaluation
274
+
275
+ Runs MCC-Lite against the entity's current trust state, persists the trace, and returns the full `MCCEvaluation`. Requires an existing trust state for the entity; otherwise raises `QatiNotFoundError`.
276
+
277
+ ```ts
278
+ import { Session, QatiNotFoundError } from 'qati-sdk';
279
+
280
+ const session = new Session();
281
+ const client = session.createClient();
282
+ try {
283
+ try {
284
+ const trace = await client.mccEvaluation.evaluate('USER:alice');
285
+ console.log(trace.evaluation_id, trace.commit_type, trace.degraded);
286
+ } catch (e) {
287
+ if (e instanceof QatiNotFoundError) {
288
+ console.log('Trust state not found for entity');
289
+ } else {
290
+ throw e;
291
+ }
292
+ }
293
+ } finally {
294
+ await client.close();
295
+ }
296
+ ```
297
+
298
+ Types `MCCEvaluation`, `MCCEvaluationListResponse`, `MCCBranchEvaluation`, and `ListMccEvaluationsOptions` are exported from `qati-sdk`.
299
+
171
300
  ## Webhooks
172
301
 
173
302
  `client.webhooks` manages webhook endpoints for a specific tenant. All methods require `tenantId` as their first argument — resolve it once with `client.tenant.verifyCredentials()` and reuse the value.
@@ -279,7 +408,11 @@ const client = session.createClient();
279
408
  try {
280
409
  const creds = await client.tenant.verifyCredentials();
281
410
  const quota = await client.quotas.get(creds.tenant_id);
282
- console.log(quota.max_tracked_entities, quota.max_monthly_events, quota.max_events_per_second);
411
+ console.log(
412
+ quota.max_tracked_entities,
413
+ quota.max_monthly_events,
414
+ quota.max_events_per_second,
415
+ );
283
416
  } finally {
284
417
  await client.close();
285
418
  }
@@ -367,15 +500,65 @@ const v1Event = (signalPayload: object, extra: object = {}) => {
367
500
  };
368
501
  ```
369
502
 
503
+ ### Canonical scalar hashing (`stableHash`)
504
+
505
+ Several payload fields store a hash of sensitive text (`action_hash`, `device_fingerprint_hash`, `query_hash`, `retrieval_hash`, `retrieved_context_hash`, `user_comment_hash`). Use the exported **`stableHash`** helper so your client matches the server canonical format:
506
+
507
+ 1. Unicode **NFC** normalization
508
+ 2. Strip outer whitespace, **lowercase**
509
+ 3. SHA-256 over UTF-8 bytes
510
+ 4. Prefix with `sha256:` (for example `sha256:2c26b46b…`)
511
+
512
+ Empty, whitespace-only, or null/undefined input returns `undefined` — omit the field from the payload rather than hashing empty strings.
513
+
514
+ ```ts
515
+ import { stableHash } from 'qati-sdk';
516
+
517
+ const action = 'SELECT id FROM users LIMIT 10';
518
+ const actionHash = stableHash(action); // sha256:…
519
+ ```
520
+
521
+ On the Python SDK, use the same algorithm via `qati_lib.utils.crypto.stable_hash` (the server reference implementation).
522
+
370
523
  ### Available signal types
371
524
 
525
+ All **17** v1 signal types have a matching `create*Event` builder exported from `qati-sdk`. Each builder validates `signal_payload` with **Zod** and returns a `RawEventRequest`. Required fields must be supplied; optional fields may be omitted (defaults apply where defined).
526
+
527
+ | Signal type | Builder | Notes |
528
+ | -------------------- | ----------------------------- | ------------------------------------------- |
529
+ | `TRANSACTION` | `createTransactionEvent` | Financial / data-access transactions |
530
+ | `AUTH` | `createAuthEvent` | Login and MFA outcomes |
531
+ | `BEHAVIOR` | `createBehaviorEvent` | Requires `behavior_type`, `deviation_score` |
532
+ | `ANOMALY_FLAG` | `createAnomalyFlagEvent` | Requires `anomaly_type`, `severity` |
533
+ | `NETWORK_EVENT` | `createNetworkEvent` | IP/ASN reputation and threat scores |
534
+ | `MODEL_OUTPUT` | `createModelOutputEvent` | LLM output quality metrics |
535
+ | `SYSTEM_TELEMETRY` | `createSystemTelemetryEvent` | Infra and OT telemetry |
536
+ | `API_CALL` | `createApiCallEvent` | HTTP API invocation audit |
537
+ | `WORKFLOW_ACTION` | `createWorkflowActionEvent` | Multi-step workflow governance |
538
+ | `TOOL_CALL` | `createToolCallEvent` | Post-execution tool invocation |
539
+ | `TOOL_CALL_PROPOSED` | `createToolCallProposedEvent` | **Pre-execution** tool proposal gate |
540
+ | `SESSION` | `createSessionEvent` | Conversation/session lifecycle |
541
+ | `PROMPT_INPUT` | `createPromptInputEvent` | User prompt risk signals |
542
+ | `RAG_RETRIEVAL` | `createRagRetrievalEvent` | Retrieval quality and trust |
543
+ | `CONTEXT_INTEGRITY` | `createContextIntegrityEvent` | Prompt/context conflict detection |
544
+ | `POLICY_EVENT` | `createPolicyEvent` | Policy check outcomes |
545
+ | `USER_FEEDBACK` | `createUserFeedbackEvent` | End-user feedback on responses |
546
+
372
547
  #### Transaction
373
548
 
374
549
  ```ts
375
550
  import { createTransactionEvent, type TransactionSignalEvent } from 'qati-sdk';
376
551
 
377
552
  const raw = createTransactionEvent(
378
- v1Event({ amount: 99.99 }) as TransactionSignalEvent,
553
+ v1Event({
554
+ amount: 99.99,
555
+ currency: 'USD',
556
+ channel: 'ECOM',
557
+ bulk_export: false,
558
+ contains_phi: false,
559
+ safety_critical: false,
560
+ authorized: true,
561
+ }) as TransactionSignalEvent,
379
562
  );
380
563
  ```
381
564
 
@@ -386,9 +569,13 @@ import { createAuthEvent, type AuthSignalEvent } from 'qati-sdk';
386
569
 
387
570
  const raw = createAuthEvent(
388
571
  v1Event({
389
- result: 'success',
390
- auth_method: 'password',
572
+ result: 'SUCCESS',
573
+ auth_method: 'MFA_TOTP',
391
574
  mfa_used: true,
575
+ mfa_bypassed: false,
576
+ failed_attempts: 0,
577
+ unusual_location: false,
578
+ after_hours_login: false,
392
579
  }) as AuthSignalEvent,
393
580
  );
394
581
  ```
@@ -401,7 +588,14 @@ import { createModelOutputEvent, type ModelOutputSignalEvent } from 'qati-sdk';
401
588
  const raw = createModelOutputEvent(
402
589
  v1Event({
403
590
  citation_rate: 0.95,
591
+ expected_citation_rate: 0.9,
592
+ policy_violations: 0,
593
+ tool_call_inconsistency: 0.02,
404
594
  eval_window_n: 1000,
595
+ hallucination_risk_score: 0.05,
596
+ grounding_score: 0.88,
597
+ contains_unsupported_claims: false,
598
+ finish_reason: 'STOP',
405
599
  }) as ModelOutputSignalEvent,
406
600
  );
407
601
  ```
@@ -419,31 +613,55 @@ const raw = createSystemTelemetryEvent(
419
613
  metric_name: 'p99_latency_ms',
420
614
  value: 120.0,
421
615
  baseline: 80.0,
616
+ window_seconds: 60,
617
+ error_rate: 0.01,
618
+ firmware_hash_changed: false,
619
+ expected_hash_match: true,
620
+ sensor_deviation_score: 0.1,
422
621
  }) as SystemTelemetrySignalEvent,
423
622
  );
424
623
  ```
425
624
 
426
625
  #### Anomaly flag
427
626
 
428
- `severity` must be between 0 and 1.
627
+ `severity` and `anomaly_type` are required. Optional device and volume fields support agent/session abuse patterns.
429
628
 
430
629
  ```ts
431
630
  import { createAnomalyFlagEvent, type AnomalyFlagSignalEvent } from 'qati-sdk';
631
+ import { stableHash } from 'qati-sdk';
432
632
 
433
633
  const raw = createAnomalyFlagEvent(
434
- v1Event({ severity: 0.85 }) as AnomalyFlagSignalEvent,
634
+ v1Event({
635
+ severity: 0.85,
636
+ anomaly_type: 'HIGH_VOLUME_SESSION',
637
+ metric_name: 'requests_per_minute',
638
+ metric_value: 42,
639
+ threshold: 10,
640
+ window_seconds: 60,
641
+ device_trust_state: 'UNVERIFIED',
642
+ device_fingerprint_hash: stableHash('device-fingerprint-payload'),
643
+ request_count: 42,
644
+ baseline_request_count: 10,
645
+ volume_ratio: 4.2,
646
+ }) as AnomalyFlagSignalEvent,
435
647
  );
436
648
  ```
437
649
 
438
650
  #### Behavior
439
651
 
440
- `deviation_score` must be between 0 and 1.
441
-
442
652
  ```ts
443
653
  import { createBehaviorEvent, type BehaviorSignalEvent } from 'qati-sdk';
444
654
 
445
655
  const raw = createBehaviorEvent(
446
- v1Event({ deviation_score: 0.4 }) as BehaviorSignalEvent,
656
+ v1Event({
657
+ behavior_type: 'chat_cadence',
658
+ deviation_score: 0.72,
659
+ current_value: 18,
660
+ baseline_value: 5,
661
+ unit: 'prompts_per_minute',
662
+ baseline_window_seconds: 3600,
663
+ current_window_seconds: 60,
664
+ }) as BehaviorSignalEvent,
447
665
  );
448
666
  ```
449
667
 
@@ -455,12 +673,271 @@ import { createNetworkEvent, type NetworkSignalEvent } from 'qati-sdk';
455
673
  const raw = createNetworkEvent(
456
674
  v1Event({
457
675
  ip: '198.51.100.10',
676
+ asn: 24940,
677
+ country: 'US',
458
678
  reputation_score: 0.2,
459
679
  threat_score: 0.7,
680
+ is_datacenter: true,
681
+ is_untrusted_segment: false,
682
+ asn_reputation: 0.15,
460
683
  }) as NetworkSignalEvent,
461
684
  );
462
685
  ```
463
686
 
687
+ #### Tool call (executed)
688
+
689
+ ```ts
690
+ import { createToolCallEvent, type ToolCallSignalEvent } from 'qati-sdk';
691
+
692
+ const raw = createToolCallEvent(
693
+ v1Event({
694
+ tool_name: 'search_docs',
695
+ tool_category: 'RETRIEVAL',
696
+ action: 'query',
697
+ authorized: true,
698
+ safety_critical: false,
699
+ external_side_effect: false,
700
+ tool_call_success: true,
701
+ latency_ms: 120,
702
+ argument_risk_score: 0.1,
703
+ records_accessed: 3,
704
+ contains_sensitive_data: false,
705
+ contains_phi: false,
706
+ sensitive_data_involved: false,
707
+ sensitive_domain: false,
708
+ }) as ToolCallSignalEvent,
709
+ );
710
+ ```
711
+
712
+ #### Tool call proposed (pre-execution)
713
+
714
+ Use **`TOOL_CALL_PROPOSED`** before a tool runs to record governance metadata. **`action_hash`** is required when the action text is sensitive; compute it with `stableHash(action)`.
715
+
716
+ ```ts
717
+ import {
718
+ createToolCallProposedEvent,
719
+ stableHash,
720
+ type ToolCallProposedSignalEvent,
721
+ } from 'qati-sdk';
722
+
723
+ const action = 'SELECT id, email FROM users LIMIT 10';
724
+
725
+ const raw = createToolCallProposedEvent(
726
+ v1Event({
727
+ tool_name: 'db_query',
728
+ tool_category: 'DATABASE',
729
+ action,
730
+ action_hash: stableHash(action)!,
731
+ authorized: false,
732
+ approval_required: true,
733
+ approval_missing: true,
734
+ approval_type: 'HUMAN',
735
+ argument_risk_score: 0.85,
736
+ argument_risk_reasons: ['bulk_pii_access'],
737
+ contains_sensitive_data: true,
738
+ data_scope: 'ORGANIZATION',
739
+ action_scope: 'ORGANIZATION',
740
+ execution_pending: true,
741
+ rollback_available: false,
742
+ }) as ToolCallProposedSignalEvent,
743
+ );
744
+ ```
745
+
746
+ #### Session
747
+
748
+ ```ts
749
+ import { createSessionEvent, type SessionSignalEvent } from 'qati-sdk';
750
+
751
+ const raw = createSessionEvent(
752
+ v1Event({
753
+ session_status: 'ACTIVE',
754
+ session_age_seconds: 300,
755
+ turn_count: 12,
756
+ messages_last_minute: 8,
757
+ conversation_id: 'conv-abc',
758
+ message_count: 24,
759
+ window_seconds: 300,
760
+ cadence_deviation: 0.4,
761
+ inter_message_interval_ms: 1500,
762
+ }) as SessionSignalEvent,
763
+ );
764
+ ```
765
+
766
+ #### Prompt input
767
+
768
+ ```ts
769
+ import {
770
+ createPromptInputEvent,
771
+ stableHash,
772
+ type PromptInputSignalEvent,
773
+ } from 'qati-sdk';
774
+
775
+ const prompt = 'Summarize the last three support tickets.';
776
+ const promptHash = stableHash(prompt)?.replace(/^sha256:/, '');
777
+
778
+ const raw = createPromptInputEvent(
779
+ v1Event({
780
+ prompt_length_chars: prompt.length,
781
+ conversation_turn_index: 3,
782
+ contains_instruction_override: false,
783
+ contains_tool_request: true,
784
+ contains_secret_request: false,
785
+ prompt_injection_score: 0.05,
786
+ prompt_hash: promptHash,
787
+ rapid_repeat: false,
788
+ prompt_sequence_index: 3,
789
+ }) as PromptInputSignalEvent,
790
+ );
791
+ ```
792
+
793
+ #### RAG retrieval
794
+
795
+ ```ts
796
+ import {
797
+ createRagRetrievalEvent,
798
+ stableHash,
799
+ type RagRetrievalSignalEvent,
800
+ } from 'qati-sdk';
801
+
802
+ const bundle = 'doc-1|doc-2|doc-3';
803
+
804
+ const raw = createRagRetrievalEvent(
805
+ v1Event({
806
+ retriever_id: 'retriever-prod',
807
+ query_hash: stableHash('customer refund policy'),
808
+ documents_retrieved: 3,
809
+ top_k: 5,
810
+ average_relevance_score: 0.82,
811
+ source_trust_score: 0.9,
812
+ lowest_source_trust_score: 0.7,
813
+ untrusted_source_count: 0,
814
+ retrieved_context_tokens: 1200,
815
+ context_injection_score: 0.02,
816
+ document_ids: ['doc-1', 'doc-2', 'doc-3'],
817
+ retrieval_hash: stableHash(bundle),
818
+ contains_sensitive_data: false,
819
+ }) as RagRetrievalSignalEvent,
820
+ );
821
+ ```
822
+
823
+ #### Context integrity
824
+
825
+ ```ts
826
+ import {
827
+ createContextIntegrityEvent,
828
+ stableHash,
829
+ type ContextIntegritySignalEvent,
830
+ } from 'qati-sdk';
831
+
832
+ const raw = createContextIntegrityEvent(
833
+ v1Event({
834
+ context_source: 'RETRIEVED_DOCUMENT',
835
+ instruction_conflict_detected: true,
836
+ untrusted_instruction_detected: false,
837
+ context_priority_violation: false,
838
+ context_drift_score: 0.35,
839
+ prompt_injection_score: 0.12,
840
+ retrieved_context_hash: stableHash('assembled-context-bundle'),
841
+ untrusted_source_count: 1,
842
+ source_trust_score: 0.6,
843
+ contains_instruction_override: false,
844
+ }) as ContextIntegritySignalEvent,
845
+ );
846
+ ```
847
+
848
+ #### API call
849
+
850
+ ```ts
851
+ import { createApiCallEvent, type ApiCallSignalEvent } from 'qati-sdk';
852
+
853
+ const raw = createApiCallEvent(
854
+ v1Event({
855
+ service_id: 'payments-api',
856
+ endpoint: '/v1/transfers',
857
+ method: 'POST',
858
+ status_code: 201,
859
+ authorized: true,
860
+ authentication_present: true,
861
+ external_side_effect: true,
862
+ records_modified: 1,
863
+ contains_sensitive_data: true,
864
+ latency_ms: 85,
865
+ retry_count: 0,
866
+ }) as ApiCallSignalEvent,
867
+ );
868
+ ```
869
+
870
+ #### Workflow action
871
+
872
+ ```ts
873
+ import {
874
+ createWorkflowActionEvent,
875
+ type WorkflowActionSignalEvent,
876
+ } from 'qati-sdk';
877
+
878
+ const raw = createWorkflowActionEvent(
879
+ v1Event({
880
+ workflow_name: 'payment_approval',
881
+ workflow_id: 'wf-001',
882
+ workflow_type: 'approval',
883
+ action_name: 'submit_wire_transfer',
884
+ action_category: 'FINANCIAL',
885
+ action_stage: 'PROPOSED',
886
+ actor_type: 'AI_AGENT',
887
+ approval_required: true,
888
+ approval_missing: true,
889
+ approval_type: 'DUAL_CONTROL',
890
+ financially_material: true,
891
+ financial_amount: 50000,
892
+ financial_currency: 'USD',
893
+ data_scope: 'ORGANIZATION',
894
+ action_scope: 'EXTERNAL',
895
+ contains_sensitive_data: true,
896
+ }) as WorkflowActionSignalEvent,
897
+ );
898
+ ```
899
+
900
+ #### Policy event
901
+
902
+ ```ts
903
+ import { createPolicyEvent, type PolicyEventSignalEvent } from 'qati-sdk';
904
+
905
+ const raw = createPolicyEvent(
906
+ v1Event({
907
+ policy_check_name: 'content_safety',
908
+ policy_category: 'SAFETY',
909
+ policy_result: 'WARN',
910
+ severity: 0.4,
911
+ blocked: false,
912
+ violation_count: 1,
913
+ policy_confidence: 0.92,
914
+ redaction_applied: true,
915
+ }) as PolicyEventSignalEvent,
916
+ );
917
+ ```
918
+
919
+ #### User feedback
920
+
921
+ ```ts
922
+ import {
923
+ createUserFeedbackEvent,
924
+ stableHash,
925
+ type UserFeedbackSignalEvent,
926
+ } from 'qati-sdk';
927
+
928
+ const raw = createUserFeedbackEvent(
929
+ v1Event({
930
+ feedback_type: 'THUMBS_DOWN',
931
+ reported_issue: true,
932
+ issue_type: 'hallucination',
933
+ severity: 0.7,
934
+ response_id: 'resp-123',
935
+ user_comment_hash: stableHash('The cited regulation does not exist.'),
936
+ conversation_id: 'conv-abc',
937
+ }) as UserFeedbackSignalEvent,
938
+ );
939
+ ```
940
+
464
941
  Note: At this point `raw` is an in-memory object. Nothing has been sent over the network yet. Proceed to Step 5 to deliver it.
465
942
 
466
943
  ### Canonical wire shape (`RawEventRequest` / `BaseEvent`)
@@ -653,7 +1130,12 @@ const v1Event = (signalPayload: object, extra: object = {}) => {
653
1130
  };
654
1131
 
655
1132
  try {
656
- createAnomalyFlagEvent(v1Event({ severity: 2.0 }) as AnomalyFlagSignalEvent);
1133
+ createAnomalyFlagEvent(
1134
+ v1Event({
1135
+ severity: 2.0,
1136
+ anomaly_type: 'NEW_DEVICE',
1137
+ }) as AnomalyFlagSignalEvent,
1138
+ );
657
1139
  } catch (e) {
658
1140
  if (e instanceof ZodError) console.log(e.issues);
659
1141
  }
@@ -674,15 +1156,18 @@ Transient failures are retried automatically for **`POST /v1/events:batch`** acc
674
1156
 
675
1157
  ## Appendix: API surface (compact)
676
1158
 
677
- | Symbol | Role |
678
- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
679
- | `Session` | `new Session(config?)`, `session.config`, `session.createClient(httpClients?)`. |
680
- | `Client` | Namespaces: `tenant`, `trustState`, `advisory`, `explain`, `events`, `webhooks`, `quotas`; `await client.close()`. |
681
- | `client.events` | `enqueue`, `flush`, `shutdown`, `pendingCount`, `onIngestionFailure`. |
682
- | `client.webhooks` | `list(tenantId)`, `create(tenantId, body)`, `patch(tenantId, webhookId, body)`, `delete(tenantId, webhookId)`, `test(tenantId, webhookId)`. |
683
- | `client.quotas` | `get(tenantId)`, `upsert(tenantId, body)`. |
684
- | `create*Event` | `createTransactionEvent`, `createAuthEvent`, `createModelOutputEvent`, `createSystemTelemetryEvent`, `createAnomalyFlagEvent`, `createBehaviorEvent`, `createNetworkEvent` — all exported from `qati-sdk`. |
685
- | Config | `resolveQatiConfig`, `parseQatiConfig`, `baseUrlFor`, types `QatiConfigInput` / `QatiConfigOutput`. |
686
- | Errors | `QatiSDKError`, `QatiAPIError`, `QatiAuthError`, `QatiNotFoundError`, `QatiRateLimitError`, `QatiServerError`, `QatiConfigError`. |
1159
+ | Symbol | Role |
1160
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1161
+ | `Session` | `new Session(config?)`, `session.config`, `session.createClient(httpClients?)`. |
1162
+ | `Client` | Namespaces: `tenant`, `trustState`, `advisory`, `explain`, `mccEvaluation`, `events`, `webhooks`, `quotas`; `await client.close()`. |
1163
+ | `client.mccEvaluation` | `list(options?)`, `get(evaluationId)`, `evaluate(entityKey)`. Keyset pagination via `after_id` / `next_cursor` (no offset). |
1164
+ | MCC types | `MCCEvaluation`, `MCCEvaluationListResponse`, `MCCBranchEvaluation`, `ListMccEvaluationsOptions` exported from `qati-sdk`. |
1165
+ | `client.events` | `enqueue`, `flush`, `shutdown`, `pendingCount`, `onIngestionFailure`. |
1166
+ | `client.webhooks` | `list(tenantId)`, `create(tenantId, body)`, `patch(tenantId, webhookId, body)`, `delete(tenantId, webhookId)`, `test(tenantId, webhookId)`. |
1167
+ | `client.quotas` | `get(tenantId)`, `upsert(tenantId, body)`. |
1168
+ | `create*Event` | All 17 v1 builders — `createTransactionEvent`, `createAuthEvent`, `createBehaviorEvent`, `createAnomalyFlagEvent`, `createNetworkEvent`, `createModelOutputEvent`, `createSystemTelemetryEvent`, `createApiCallEvent`, `createWorkflowActionEvent`, `createToolCallEvent`, **`createToolCallProposedEvent`**, `createSessionEvent`, `createPromptInputEvent`, `createRagRetrievalEvent`, `createContextIntegrityEvent`, `createPolicyEvent`, `createUserFeedbackEvent` — exported from `qati-sdk`. |
1169
+ | `stableHash` | Canonical scalar string hashing (`sha256:` prefix); use for hash payload fields. |
1170
+ | Config | `resolveQatiConfig`, `parseQatiConfig`, `baseUrlFor`, types `QatiConfigInput` / `QatiConfigOutput`. |
1171
+ | Errors | `QatiSDKError`, `QatiAPIError`, `QatiAuthError`, `QatiNotFoundError`, `QatiRateLimitError`, `QatiServerError`, `QatiConfigError`. |
687
1172
 
688
1173
  `HttpClient.request(...)` exists for advanced use; prefer resource methods for application code.