The lq-ai contract & upstream loop
Donna implements no legal-AI logic — it consumes a contract. This is what happens when the contract isn't enough: file an ask, relay it, bump the pin.
src/lib/api/backend.d.ts10892 lines
Outline 5 symbols
- paths interface export
- webhooks type export
- components interface export
- $defs type export
- operations type export
1/**
2 * This file was auto-generated by openapi-typescript.
3 * Do not make direct changes to the file.
4 */
5
6export interface paths {
7 "/api/v1/auth/login": {
8 parameters: {
9 query?: never;
10 header?: never;
11 path?: never;
12 cookie?: never;
13 };
14 get?: never;
15 put?: never;
16 /** Authenticate with email and password */
17 post: {
18 parameters: {
19 query?: never;
20 header?: never;
21 path?: never;
22 cookie?: never;
23 };
24 requestBody: {
25 content: {
26 "application/json": components["schemas"]["LoginRequest"];
27 };
28 };
29 responses: {
30 /** @description Authentication successful */
31 200: {
32 headers: {
33 [name: string]: unknown;
34 };
35 content: {
36 "application/json": components["schemas"]["LoginResponse"];
37 };
38 };
39 /** @description Invalid credentials */
40 401: {
41 headers: {
42 [name: string]: unknown;
43 };
44 content?: never;
45 };
46 /** @description MFA required */
47 423: {
48 headers: {
49 [name: string]: unknown;
50 };
51 content: {
52 "application/json": components["schemas"]["MfaChallenge"];
53 };
54 };
55 };
56 };
57 delete?: never;
58 options?: never;
59 head?: never;
60 patch?: never;
61 trace?: never;
62 };
63 "/api/v1/auth/mfa/verify": {
64 parameters: {
65 query?: never;
66 header?: never;
67 path?: never;
68 cookie?: never;
69 };
70 get?: never;
71 put?: never;
72 /** Complete MFA challenge with TOTP code */
73 post: {
74 parameters: {
75 query?: never;
76 header?: never;
77 path?: never;
78 cookie?: never;
79 };
80 requestBody: {
81 content: {
82 "application/json": {
83 /** @description Token from the MFA challenge response */
84 mfa_token: string;
85 code: string;
86 };
87 };
88 };
89 responses: {
90 /** @description MFA verification successful */
91 200: {
92 headers: {
93 [name: string]: unknown;
94 };
95 content: {
96 "application/json": components["schemas"]["LoginResponse"];
97 };
98 };
99 /** @description Invalid MFA code */
100 401: {
101 headers: {
102 [name: string]: unknown;
103 };
104 content?: never;
105 };
106 };
107 };
108 delete?: never;
109 options?: never;
110 head?: never;
111 patch?: never;
112 trace?: never;
113 };
114 "/api/v1/auth/refresh": {
115 parameters: {
116 query?: never;
117 header?: never;
118 path?: never;
119 cookie?: never;
120 };
121 get?: never;
122 put?: never;
123 /** Refresh access token using refresh token */
124 post: {
125 parameters: {
126 query?: never;
127 header?: never;
128 path?: never;
129 cookie?: never;
130 };
131 requestBody: {
132 content: {
133 "application/json": {
134 refresh_token: string;
135 };
136 };
137 };
138 responses: {
139 /** @description New access token issued */
140 200: {
141 headers: {
142 [name: string]: unknown;
143 };
144 content: {
145 "application/json": components["schemas"]["TokenResponse"];
146 };
147 };
148 };
149 };
150 delete?: never;
151 options?: never;
152 head?: never;
153 patch?: never;
154 trace?: never;
155 };
156 "/api/v1/auth/logout": {
157 parameters: {
158 query?: never;
159 header?: never;
160 path?: never;
161 cookie?: never;
162 };
163 get?: never;
164 put?: never;
165 /** Invalidate the current refresh token */
166 post: {
167 parameters: {
168 query?: never;
169 header?: never;
170 path?: never;
171 cookie?: never;
172 };
173 requestBody?: never;
174 responses: {
175 /** @description Logout successful */
176 204: {
177 headers: {
178 [name: string]: unknown;
179 };
180 content?: never;
181 };
182 };
183 };
184 delete?: never;
185 options?: never;
186 head?: never;
187 patch?: never;
188 trace?: never;
189 };
190 "/api/v1/auth/mfa/setup": {
191 parameters: {
192 query?: never;
193 header?: never;
194 path?: never;
195 cookie?: never;
196 };
197 get?: never;
198 put?: never;
199 /**
200 * Enroll in TOTP-based MFA
201 * @description Bearer-authenticated. Issues a fresh TOTP secret, provisioning
202 * URI, and 10 single-use recovery codes. Persists the secret +
203 * bcrypt-hashed codes on the user, but does NOT enable MFA — the
204 * user must subsequently prove possession by submitting a TOTP
205 * code to `/auth/mfa/enable`. Re-running setup before enable
206 * overwrites the pending secret + codes; re-running after MFA is
207 * already enabled is a 409 (`mfa_already_enabled`).
208 */
209 post: {
210 parameters: {
211 query?: never;
212 header?: never;
213 path?: never;
214 cookie?: never;
215 };
216 requestBody?: never;
217 responses: {
218 /** @description TOTP enrollment data (provisioning URI + recovery codes) */
219 200: {
220 headers: {
221 [name: string]: unknown;
222 };
223 content: {
224 "application/json": {
225 secret: string;
226 /** @description otpauth:// URI for QR code */
227 provisioning_uri: string;
228 recovery_codes: string[];
229 };
230 };
231 };
232 /** @description MFA is already enabled (call `/auth/mfa/disable` first) */
233 409: {
234 headers: {
235 [name: string]: unknown;
236 };
237 content?: never;
238 };
239 };
240 };
241 delete?: never;
242 options?: never;
243 head?: never;
244 patch?: never;
245 trace?: never;
246 };
247 "/api/v1/auth/mfa/enable": {
248 parameters: {
249 query?: never;
250 header?: never;
251 path?: never;
252 cookie?: never;
253 };
254 get?: never;
255 put?: never;
256 /**
257 * Confirm TOTP enrollment with a verification code
258 * @description Bearer-authenticated. Verifies a 6-digit TOTP code against the
259 * pending secret stored by `/auth/mfa/setup`. On success flips
260 * `users.mfa_enabled` to TRUE; subsequent logins for this user
261 * return 423 + `MfaChallenge`.
262 */
263 post: {
264 parameters: {
265 query?: never;
266 header?: never;
267 path?: never;
268 cookie?: never;
269 };
270 requestBody: {
271 content: {
272 "application/json": {
273 code: string;
274 };
275 };
276 };
277 responses: {
278 /** @description MFA enabled */
279 204: {
280 headers: {
281 [name: string]: unknown;
282 };
283 content?: never;
284 };
285 /** @description Setup not started or code invalid */
286 400: {
287 headers: {
288 [name: string]: unknown;
289 };
290 content?: never;
291 };
292 /** @description MFA is already enabled */
293 409: {
294 headers: {
295 [name: string]: unknown;
296 };
297 content?: never;
298 };
299 };
300 };
301 delete?: never;
302 options?: never;
303 head?: never;
304 patch?: never;
305 trace?: never;
306 };
307 "/api/v1/auth/mfa/disable": {
308 parameters: {
309 query?: never;
310 header?: never;
311 path?: never;
312 cookie?: never;
313 };
314 get?: never;
315 put?: never;
316 /**
317 * Disable MFA after re-proving password and a current MFA code
318 * @description Bearer-authenticated. Requires both the user's current password
319 * AND a current TOTP code (or a single-use recovery code). On
320 * success clears the TOTP secret, recovery codes, and the
321 * `mfa_enabled` flag. Wrong-password and wrong-code branches both
322 * return 401 — the response shape does not reveal which leg
323 * failed.
324 */
325 post: {
326 parameters: {
327 query?: never;
328 header?: never;
329 path?: never;
330 cookie?: never;
331 };
332 requestBody: {
333 content: {
334 "application/json": {
335 password: string;
336 code: string;
337 };
338 };
339 };
340 responses: {
341 /** @description MFA disabled */
342 204: {
343 headers: {
344 [name: string]: unknown;
345 };
346 content?: never;
347 };
348 /** @description MFA is not enabled */
349 400: {
350 headers: {
351 [name: string]: unknown;
352 };
353 content?: never;
354 };
355 /** @description Invalid credentials or MFA code */
356 401: {
357 headers: {
358 [name: string]: unknown;
359 };
360 content?: never;
361 };
362 };
363 };
364 delete?: never;
365 options?: never;
366 head?: never;
367 patch?: never;
368 trace?: never;
369 };
370 "/api/v1/auth/change-password": {
371 parameters: {
372 query?: never;
373 header?: never;
374 path?: never;
375 cookie?: never;
376 };
377 get?: never;
378 put?: never;
379 /**
380 * Change the calling user's password
381 * @description Bearer-authenticated. Verifies the current password before
382 * accepting a new one (defense against a stolen access token).
383 * Clears `must_change_password` if it was set (used by the
384 * first-run admin flow per Task B2). Revokes all active sessions
385 * for the user; the caller must re-authenticate.
386 */
387 post: {
388 parameters: {
389 query?: never;
390 header?: never;
391 path?: never;
392 cookie?: never;
393 };
394 requestBody: {
395 content: {
396 "application/json": components["schemas"]["ChangePasswordRequest"];
397 };
398 };
399 responses: {
400 /** @description Password changed successfully */
401 204: {
402 headers: {
403 [name: string]: unknown;
404 };
405 content?: never;
406 };
407 /** @description New password fails policy (too short */
408 400: {
409 headers: {
410 [name: string]: unknown;
411 };
412 content?: never;
413 };
414 /** @description Bearer token invalid or current_password is wrong */
415 401: {
416 headers: {
417 [name: string]: unknown;
418 };
419 content?: never;
420 };
421 /**
422 * @description Reserved by the must-change-password gate elsewhere; the
423 * change-password endpoint itself is reachable while the gate
424 * is active.
425 */
426 403: {
427 headers: {
428 [name: string]: unknown;
429 };
430 content?: never;
431 };
432 };
433 };
434 delete?: never;
435 options?: never;
436 head?: never;
437 patch?: never;
438 trace?: never;
439 };
440 "/api/v1/users/me": {
441 parameters: {
442 query?: never;
443 header?: never;
444 path?: never;
445 cookie?: never;
446 };
447 /** Get current user */
448 get: {
449 parameters: {
450 query?: never;
451 header?: never;
452 path?: never;
453 cookie?: never;
454 };
455 requestBody?: never;
456 responses: {
457 /** @description Current user */
458 200: {
459 headers: {
460 [name: string]: unknown;
461 };
462 content: {
463 "application/json": components["schemas"]["User"];
464 };
465 };
466 };
467 };
468 put?: never;
469 post?: never;
470 delete?: never;
471 options?: never;
472 head?: never;
473 /**
474 * Update the caller's own profile (display_name)
475 * @description Bearer-authenticated. Caller-scoped — updates only the
476 * authenticated user's own row; no user id is accepted from the
477 * path or body. Currently edits `display_name` only: the value is
478 * trimmed, must be non-empty after trimming, and is length-capped
479 * (200 chars). Omitting `display_name` (or sending an empty body)
480 * is a 422 — there is nothing to update. Writes a
481 * `user.profile_updated` audit row listing the changed fields and
482 * returns the updated `User` (same shape as `GET /users/me`).
483 *
484 * Email self-service editing is intentionally out of scope on this
485 * surface — changing the login email pulls in re-verification, MFA,
486 * and uniqueness concerns and warrants its own dedicated flow (a
487 * future enhancement).
488 */
489 patch: {
490 parameters: {
491 query?: never;
492 header?: never;
493 path?: never;
494 cookie?: never;
495 };
496 requestBody: {
497 content: {
498 "application/json": components["schemas"]["UserProfileUpdate"];
499 };
500 };
501 responses: {
502 /** @description Updated user */
503 200: {
504 headers: {
505 [name: string]: unknown;
506 };
507 content: {
508 "application/json": components["schemas"]["User"];
509 };
510 };
511 /** @description Validation error (empty/whitespace/over-length display_name */
512 422: {
513 headers: {
514 [name: string]: unknown;
515 };
516 content?: never;
517 };
518 };
519 };
520 trace?: never;
521 };
522 "/api/v1/users/me/export": {
523 parameters: {
524 query?: never;
525 header?: never;
526 path?: never;
527 cookie?: never;
528 };
529 get?: never;
530 put?: never;
531 /**
532 * Request a per-user data export (GDPR Article 20)
533 * @description Bearer-authenticated. Inserts a `user_export_jobs` row with
534 * status `queued` and enqueues the worker job that builds +
535 * uploads the ZIP. Returns 202 immediately — clients poll the
536 * per-job endpoint below for completion.
537 */
538 post: {
539 parameters: {
540 query?: never;
541 header?: never;
542 path?: never;
543 cookie?: never;
544 };
545 requestBody?: never;
546 responses: {
547 /** @description Export job queued */
548 202: {
549 headers: {
550 [name: string]: unknown;
551 };
552 content: {
553 "application/json": {
554 /** Format: uuid */
555 job_id: string;
556 /** @enum {string} */
557 status: "queued" | "processing" | "completed" | "failed";
558 download_url?: string | null;
559 };
560 };
561 };
562 };
563 };
564 delete?: never;
565 options?: never;
566 head?: never;
567 patch?: never;
568 trace?: never;
569 };
570 "/api/v1/users/me/export/{job_id}": {
571 parameters: {
572 query?: never;
573 header?: never;
574 path?: never;
575 cookie?: never;
576 };
577 /**
578 * Poll a user-export job's status
579 * @description Bearer-authenticated. Returns the current status of the export
580 * job. When `status=completed` and the bundle has not yet expired,
581 * `download_url` is a presigned MinIO URL valid for 24 hours. A
582 * 404 covers both "no such job" and "job belongs to another user"
583 * (does not leak existence cross-user).
584 */
585 get: {
586 parameters: {
587 query?: never;
588 header?: never;
589 path: {
590 job_id: string;
591 };
592 cookie?: never;
593 };
594 requestBody?: never;
595 responses: {
596 /** @description Job status */
597 200: {
598 headers: {
599 [name: string]: unknown;
600 };
601 content: {
602 "application/json": {
603 /** Format: uuid */
604 job_id: string;
605 /** @enum {string} */
606 status: "queued" | "processing" | "completed" | "failed";
607 download_url?: string | null;
608 };
609 };
610 };
611 /** @description Job not found */
612 404: {
613 headers: {
614 [name: string]: unknown;
615 };
616 content?: never;
617 };
618 };
619 };
620 put?: never;
621 post?: never;
622 delete?: never;
623 options?: never;
624 head?: never;
625 patch?: never;
626 trace?: never;
627 };
628 "/api/v1/users/me/delete": {
629 parameters: {
630 query?: never;
631 header?: never;
632 path?: never;
633 cookie?: never;
634 };
635 get?: never;
636 put?: never;
637 /**
638 * Request account deletion (GDPR Article 17)
639 * @description Bearer-authenticated. Soft-schedules deletion: sets
640 * `users.deletion_scheduled_at = now() + gdpr_grace_period_days`,
641 * revokes all active sessions, and audit-logs the request. Login
642 * during the grace period is preserved so the user can call the
643 * cancel endpoint. Hard delete itself runs daily via a worker
644 * cron. Idempotent: re-calling returns the existing schedule
645 * rather than pushing it back.
646 */
647 post: {
648 parameters: {
649 query?: never;
650 header?: never;
651 path?: never;
652 cookie?: never;
653 };
654 requestBody?: never;
655 responses: {
656 /** @description Deletion queued */
657 202: {
658 headers: {
659 [name: string]: unknown;
660 };
661 content: {
662 "application/json": {
663 /** Format: date-time */
664 scheduled_deletion_at: string;
665 grace_period_days: number;
666 };
667 };
668 };
669 };
670 };
671 delete?: never;
672 options?: never;
673 head?: never;
674 patch?: never;
675 trace?: never;
676 };
677 "/api/v1/users/me/delete/cancel": {
678 parameters: {
679 query?: never;
680 header?: never;
681 path?: never;
682 cookie?: never;
683 };
684 get?: never;
685 put?: never;
686 /**
687 * Cancel a pending account deletion
688 * @description Bearer-authenticated. Clears `deletion_scheduled_at` if a
689 * pending deletion exists; returns 400 if there is no pending
690 * deletion to cancel. Only valid during the grace-period window —
691 * once the hard-delete worker has run, there is no user row to
692 * recover.
693 */
694 post: {
695 parameters: {
696 query?: never;
697 header?: never;
698 path?: never;
699 cookie?: never;
700 };
701 requestBody?: never;
702 responses: {
703 /** @description Pending deletion cancelled */
704 204: {
705 headers: {
706 [name: string]: unknown;
707 };
708 content?: never;
709 };
710 /** @description No pending deletion to cancel */
711 400: {
712 headers: {
713 [name: string]: unknown;
714 };
715 content?: never;
716 };
717 };
718 };
719 delete?: never;
720 options?: never;
721 head?: never;
722 patch?: never;
723 trace?: never;
724 };
725 "/api/v1/users/me/preferences": {
726 parameters: {
727 query?: never;
728 header?: never;
729 path?: never;
730 cookie?: never;
731 };
732 /**
733 * Read the caller's preferences (Wave A — PRD §3.2)
734 * @description Returns the preferences slice of the user profile. The same
735 * fields appear on ``GET /users/me``; this dedicated endpoint
736 * exists so frontends can subscribe to preferences changes
737 * without re-fetching the whole user object.
738 */
739 get: {
740 parameters: {
741 query?: never;
742 header?: never;
743 path?: never;
744 cookie?: never;
745 };
746 requestBody?: never;
747 responses: {
748 /** @description Preferences snapshot */
749 200: {
750 headers: {
751 [name: string]: unknown;
752 };
753 content: {
754 "application/json": components["schemas"]["UserPreferences"];
755 };
756 };
757 };
758 };
759 put?: never;
760 post?: never;
761 delete?: never;
762 options?: never;
763 head?: never;
764 /**
765 * Update the caller's preferences (partial)
766 * @description Only the fields the user supplies move. An idempotent PATCH
767 * (same value re-supplied) returns 200 without writing an audit
768 * row, matching the user_skills / saved_prompts pattern. Real
769 * changes write a ``user.preferences_updated`` audit row with
770 * before/after values in details.
771 */
772 patch: {
773 parameters: {
774 query?: never;
775 header?: never;
776 path?: never;
777 cookie?: never;
778 };
779 requestBody: {
780 content: {
781 "application/json": components["schemas"]["UserPreferencesUpdate"];
782 };
783 };
784 responses: {
785 /** @description Updated preferences */
786 200: {
787 headers: {
788 [name: string]: unknown;
789 };
790 content: {
791 "application/json": components["schemas"]["UserPreferences"];
792 };
793 };
794 };
795 };
796 trace?: never;
797 };
798 "/api/v1/enhance-prompt": {
799 parameters: {
800 query?: never;
801 header?: never;
802 path?: never;
803 cookie?: never;
804 };
805 get?: never;
806 put?: never;
807 /**
808 * Expand a short prompt into a structured legal prompt
809 * @description Invokes the ``enhance-prompt`` skill against the gateway (with
810 * ``lq_ai_skills=['enhance-prompt']`` so user/team-scope
811 * shadows of the skill are honored per the D8.1b resolver).
812 * Returns the structured expansion the SKILL.md output schema
813 * produces — ``expansion_applied`` + ``expanded_prompt`` +
814 * ``reasoning`` + ``skip_reason`` + ``preview_to_user`` — plus
815 * an ``interaction_id`` the frontend uses to PATCH ``used`` /
816 * ``edited_before_use`` after the user acts on the preview.
817 *
818 * Defaults to the ``fast`` model alias (cheaper/smaller than
819 * ``smart`` per the spec's "smaller/cheaper model" guidance);
820 * ``model`` on the body overrides per-request.
821 *
822 * When the model produces malformed structured output, the
823 * response carries ``expansion_applied=false`` +
824 * ``skip_reason='parse_error'`` rather than 500 — the frontend
825 * falls back to submitting the original prompt.
826 */
827 post: {
828 parameters: {
829 query?: never;
830 header?: never;
831 path?: never;
832 cookie?: never;
833 };
834 requestBody: {
835 content: {
836 "application/json": components["schemas"]["EnhancePromptRequest"];
837 };
838 };
839 responses: {
840 /** @description Expansion (or a structured skip decision) */
841 200: {
842 headers: {
843 [name: string]: unknown;
844 };
845 content: {
846 "application/json": components["schemas"]["EnhancePromptResponse"];
847 };
848 };
849 /** @description Gateway / provider failure during enhancement */
850 502: {
851 headers: {
852 [name: string]: unknown;
853 };
854 content: {
855 "application/json": components["schemas"]["Error"];
856 };
857 };
858 };
859 };
860 delete?: never;
861 options?: never;
862 head?: never;
863 patch?: never;
864 trace?: never;
865 };
866 "/api/v1/enhance-prompt/{interaction_id}": {
867 parameters: {
868 query?: never;
869 header?: never;
870 path: {
871 interaction_id: string;
872 };
873 cookie?: never;
874 };
875 get?: never;
876 put?: never;
877 post?: never;
878 delete?: never;
879 options?: never;
880 head?: never;
881 /**
882 * Record what the user did with the enhancement preview
883 * @description Updates ``used`` and ``edited_before_use`` for telemetry.
884 * Owner-only (404 if the row belongs to another user — same
885 * id-probing posture as user_skills + chats). Idempotent: a
886 * no-op PATCH returns 200 without writing.
887 */
888 patch: {
889 parameters: {
890 query?: never;
891 header?: never;
892 path: {
893 interaction_id: string;
894 };
895 cookie?: never;
896 };
897 requestBody: {
898 content: {
899 "application/json": components["schemas"]["EnhancePromptOutcomeUpdate"];
900 };
901 };
902 responses: {
903 /** @description Updated interaction */
904 200: {
905 headers: {
906 [name: string]: unknown;
907 };
908 content: {
909 "application/json": components["schemas"]["EnhancePromptResponse"];
910 };
911 };
912 /** @description Interaction not found OR not owned by the caller. */
913 404: {
914 headers: {
915 [name: string]: unknown;
916 };
917 content: {
918 "application/json": components["schemas"]["Error"];
919 };
920 };
921 };
922 };
923 trace?: never;
924 };
925 "/api/v1/projects": {
926 parameters: {
927 query?: never;
928 header?: never;
929 path?: never;
930 cookie?: never;
931 };
932 /**
933 * List the caller's projects
934 * @description Returns the caller's active projects by default. ``archived=true``
935 * returns archived projects only; ``archived=false`` is equivalent
936 * to omitting the parameter (active only). Per-user isolation:
937 * only the caller's own projects are returned.
938 */
939 get: {
940 parameters: {
941 query?: {
942 /** @description When true, return only archived projects. */
943 archived?: boolean;
944 };
945 header?: never;
946 path?: never;
947 cookie?: never;
948 };
949 requestBody?: never;
950 responses: {
951 /** @description List of projects (active or archived per the filter). */
952 200: {
953 headers: {
954 [name: string]: unknown;
955 };
956 content: {
957 "application/json": components["schemas"]["Project"][];
958 };
959 };
960 };
961 };
962 put?: never;
963 /**
964 * Create a project
965 * @description Creates a project owned by the caller. ``slug`` is generated
966 * from ``name`` if omitted; collisions with the caller's existing
967 * active projects resolve with a numeric suffix (``-2``, ``-3``,
968 * ...). Setting ``privileged=true`` requires
969 * ``minimum_inference_tier`` to be set; missing-tier returns 422.
970 */
971 post: {
972 parameters: {
973 query?: never;
974 header?: never;
975 path?: never;
976 cookie?: never;
977 };
978 requestBody: {
979 content: {
980 "application/json": components["schemas"]["ProjectCreate"];
981 };
982 };
983 responses: {
984 /** @description Project created */
985 201: {
986 headers: {
987 [name: string]: unknown;
988 };
989 content: {
990 "application/json": components["schemas"]["Project"];
991 };
992 };
993 /** @description Slug collision under concurrent create */
994 409: {
995 headers: {
996 [name: string]: unknown;
997 };
998 content: {
999 "application/json": components["schemas"]["Error"];
1000 };
1001 };
1002 /** @description Schema validation failure (e.g., privileged-without-tier) */
1003 422: {
1004 headers: {
1005 [name: string]: unknown;
1006 };
1007 content: {
1008 "application/json": components["schemas"]["Error"];
1009 };
1010 };
1011 };
1012 };
1013 delete?: never;
1014 options?: never;
1015 head?: never;
1016 patch?: never;
1017 trace?: never;
1018 };
1019 "/api/v1/projects/{project_id}": {
1020 parameters: {
1021 query?: never;
1022 header?: never;
1023 path: {
1024 project_id: string;
1025 };
1026 cookie?: never;
1027 };
1028 /**
1029 * Fetch a single project
1030 * @description Returns the canonical ``Project`` shape with ``attached_file_ids``
1031 * and ``attached_skill_names`` populated from the join tables.
1032 * Archived projects are visible via direct GET so the client can
1033 * render an archived-detail page; the list endpoint excludes them
1034 * by default.
1035 */
1036 get: {
1037 parameters: {
1038 query?: never;
1039 header?: never;
1040 path: {
1041 project_id: string;
1042 };
1043 cookie?: never;
1044 };
1045 requestBody?: never;
1046 responses: {
1047 /** @description Project */
1048 200: {
1049 headers: {
1050 [name: string]: unknown;
1051 };
1052 content: {
1053 "application/json": components["schemas"]["Project"];
1054 };
1055 };
1056 /** @description Not found (or owned by another user) */
1057 404: {
1058 headers: {
1059 [name: string]: unknown;
1060 };
1061 content: {
1062 "application/json": components["schemas"]["Error"];
1063 };
1064 };
1065 };
1066 };
1067 put?: never;
1068 post?: never;
1069 /**
1070 * Soft-delete (archive) a project
1071 * @description Sets ``archived_at`` on the row. Hard-delete is owned by D6.
1072 * Idempotent: a second DELETE on an already-archived project
1073 * returns 404.
1074 */
1075 delete: {
1076 parameters: {
1077 query?: never;
1078 header?: never;
1079 path: {
1080 project_id: string;
1081 };
1082 cookie?: never;
1083 };
1084 requestBody?: never;
1085 responses: {
1086 /** @description Project archived */
1087 204: {
1088 headers: {
1089 [name: string]: unknown;
1090 };
1091 content?: never;
1092 };
1093 /** @description Not found, or already archived */
1094 404: {
1095 headers: {
1096 [name: string]: unknown;
1097 };
1098 content: {
1099 "application/json": components["schemas"]["Error"];
1100 };
1101 };
1102 };
1103 };
1104 options?: never;
1105 head?: never;
1106 /**
1107 * Partial update of a project
1108 * @description Applies only the fields the caller sets. The
1109 * ``privileged``↔``minimum_inference_tier`` rule is re-checked
1110 * against the *merged* state — setting either alone in a way that
1111 * would leave the project privileged-but-tierless returns 400.
1112 * ``archived: true`` archives the project (equivalent to DELETE);
1113 * ``archived: false`` unarchives it.
1114 */
1115 patch: {
1116 parameters: {
1117 query?: never;
1118 header?: never;
1119 path: {
1120 project_id: string;
1121 };
1122 cookie?: never;
1123 };
1124 requestBody: {
1125 content: {
1126 "application/json": components["schemas"]["ProjectUpdate"];
1127 };
1128 };
1129 responses: {
1130 /** @description Project updated */
1131 200: {
1132 headers: {
1133 [name: string]: unknown;
1134 };
1135 content: {
1136 "application/json": components["schemas"]["Project"];
1137 };
1138 };
1139 /** @description Cross-field rule violation (e.g., privileged-without-tier) */
1140 400: {
1141 headers: {
1142 [name: string]: unknown;
1143 };
1144 content: {
1145 "application/json": components["schemas"]["Error"];
1146 };
1147 };
1148 /** @description Not found (or owned by another user) */
1149 404: {
1150 headers: {
1151 [name: string]: unknown;
1152 };
1153 content: {
1154 "application/json": components["schemas"]["Error"];
1155 };
1156 };
1157 };
1158 };
1159 trace?: never;
1160 };
1161 "/api/v1/projects/{project_id}/skills": {
1162 parameters: {
1163 query?: never;
1164 header?: never;
1165 path: {
1166 project_id: string;
1167 };
1168 cookie?: never;
1169 };
1170 get?: never;
1171 put?: never;
1172 /**
1173 * Attach a skill to the project
1174 * @description Attaches a registry-known skill to the project by name. The
1175 * skill must exist in the in-memory skill registry (C1); unknown
1176 * names return 404. Re-attaching an already-attached skill
1177 * returns 409.
1178 */
1179 post: {
1180 parameters: {
1181 query?: never;
1182 header?: never;
1183 path: {
1184 project_id: string;
1185 };
1186 cookie?: never;
1187 };
1188 requestBody: {
1189 content: {
1190 "application/json": {
1191 skill_name: string;
1192 };
1193 };
1194 };
1195 responses: {
1196 /** @description Attached */
1197 204: {
1198 headers: {
1199 [name: string]: unknown;
1200 };
1201 content?: never;
1202 };
1203 /** @description Project or skill not found */
1204 404: {
1205 headers: {
1206 [name: string]: unknown;
1207 };
1208 content: {
1209 "application/json": components["schemas"]["Error"];
1210 };
1211 };
1212 /** @description Skill already attached */
1213 409: {
1214 headers: {
1215 [name: string]: unknown;
1216 };
1217 content: {
1218 "application/json": components["schemas"]["Error"];
1219 };
1220 };
1221 };
1222 };
1223 delete?: never;
1224 options?: never;
1225 head?: never;
1226 patch?: never;
1227 trace?: never;
1228 };
1229 "/api/v1/projects/{project_id}/skills/{skill_name}": {
1230 parameters: {
1231 query?: never;
1232 header?: never;
1233 path: {
1234 project_id: string;
1235 skill_name: string;
1236 };
1237 cookie?: never;
1238 };
1239 get?: never;
1240 put?: never;
1241 post?: never;
1242 /**
1243 * Detach a skill from the project
1244 * @description Removes the project↔skill join row. The skill itself is
1245 * registry-canonical and untouched. Detaching a skill that is
1246 * not attached returns 404 (idempotency-violating; clients
1247 * should treat 204 and 404 as terminal success states).
1248 */
1249 delete: {
1250 parameters: {
1251 query?: never;
1252 header?: never;
1253 path: {
1254 project_id: string;
1255 skill_name: string;
1256 };
1257 cookie?: never;
1258 };
1259 requestBody?: never;
1260 responses: {
1261 /** @description Detached */
1262 204: {
1263 headers: {
1264 [name: string]: unknown;
1265 };
1266 content?: never;
1267 };
1268 /** @description Project or attachment not found */
1269 404: {
1270 headers: {
1271 [name: string]: unknown;
1272 };
1273 content: {
1274 "application/json": components["schemas"]["Error"];
1275 };
1276 };
1277 };
1278 };
1279 options?: never;
1280 head?: never;
1281 patch?: never;
1282 trace?: never;
1283 };
1284 "/api/v1/projects/{project_id}/files": {
1285 parameters: {
1286 query?: never;
1287 header?: never;
1288 path: {
1289 project_id: string;
1290 };
1291 cookie?: never;
1292 };
1293 get?: never;
1294 put?: never;
1295 /**
1296 * Attach an uploaded file to the project
1297 * @description Attaches an uploaded file to the project. The caller must own
1298 * both the project and the file (cross-user → 404 on either).
1299 * Re-attaching an already-attached file returns 409.
1300 */
1301 post: {
1302 parameters: {
1303 query?: never;
1304 header?: never;
1305 path: {
1306 project_id: string;
1307 };
1308 cookie?: never;
1309 };
1310 requestBody: {
1311 content: {
1312 "application/json": {
1313 /** Format: uuid */
1314 file_id: string;
1315 };
1316 };
1317 };
1318 responses: {
1319 /** @description Attached */
1320 204: {
1321 headers: {
1322 [name: string]: unknown;
1323 };
1324 content?: never;
1325 };
1326 /** @description Project or file not found */
1327 404: {
1328 headers: {
1329 [name: string]: unknown;
1330 };
1331 content: {
1332 "application/json": components["schemas"]["Error"];
1333 };
1334 };
1335 /** @description File already attached */
1336 409: {
1337 headers: {
1338 [name: string]: unknown;
1339 };
1340 content: {
1341 "application/json": components["schemas"]["Error"];
1342 };
1343 };
1344 };
1345 };
1346 delete?: never;
1347 options?: never;
1348 head?: never;
1349 patch?: never;
1350 trace?: never;
1351 };
1352 "/api/v1/projects/{project_id}/files/{file_id}": {
1353 parameters: {
1354 query?: never;
1355 header?: never;
1356 path: {
1357 project_id: string;
1358 file_id: string;
1359 };
1360 cookie?: never;
1361 };
1362 get?: never;
1363 put?: never;
1364 post?: never;
1365 /**
1366 * Detach a file from the project
1367 * @description Removes the project↔file join row. The file row itself is
1368 * untouched (use ``DELETE /api/v1/files/{file_id}`` to soft-
1369 * delete the file). Detaching a file that is not attached
1370 * returns 404.
1371 */
1372 delete: {
1373 parameters: {
1374 query?: never;
1375 header?: never;
1376 path: {
1377 project_id: string;
1378 file_id: string;
1379 };
1380 cookie?: never;
1381 };
1382 requestBody?: never;
1383 responses: {
1384 /** @description Detached */
1385 204: {
1386 headers: {
1387 [name: string]: unknown;
1388 };
1389 content?: never;
1390 };
1391 /** @description Project or attachment not found */
1392 404: {
1393 headers: {
1394 [name: string]: unknown;
1395 };
1396 content: {
1397 "application/json": components["schemas"]["Error"];
1398 };
1399 };
1400 };
1401 };
1402 options?: never;
1403 head?: never;
1404 patch?: never;
1405 trace?: never;
1406 };
1407 "/api/v1/projects/sandbox/ensure": {
1408 parameters: {
1409 query?: never;
1410 header?: never;
1411 path?: never;
1412 cookie?: never;
1413 };
1414 get?: never;
1415 put?: never;
1416 /**
1417 * Find or create the caller's try-it sandbox matter (Wave D.2)
1418 * @description Idempotent find-or-create for the per-user *try-it sandbox*
1419 * project. The sandbox is a system-managed matter (slug
1420 * ``__sandbox__``, ``is_sandbox=true``) used to scope skill try-it
1421 * conversations that should not count toward billable matter
1422 * activity.
1423 *
1424 * * First call returns 201 with a fresh row.
1425 * * Subsequent calls return 200 with the same row.
1426 * * If the sandbox was previously soft-deleted, the next ensure
1427 * call recreates it (returns 201).
1428 *
1429 * Concurrent callers are safe: the ``ON CONFLICT DO NOTHING`` path
1430 * ensures only one row is ever created per owner.
1431 */
1432 post: {
1433 parameters: {
1434 query?: never;
1435 header?: never;
1436 path?: never;
1437 cookie?: never;
1438 };
1439 requestBody?: never;
1440 responses: {
1441 /** @description Existing sandbox (already existed) */
1442 200: {
1443 headers: {
1444 [name: string]: unknown;
1445 };
1446 content: {
1447 "application/json": components["schemas"]["Project"];
1448 };
1449 };
1450 /** @description Sandbox created */
1451 201: {
1452 headers: {
1453 [name: string]: unknown;
1454 };
1455 content: {
1456 "application/json": components["schemas"]["Project"];
1457 };
1458 };
1459 };
1460 };
1461 delete?: never;
1462 options?: never;
1463 head?: never;
1464 patch?: never;
1465 trace?: never;
1466 };
1467 "/api/v1/chats/search": {
1468 parameters: {
1469 query?: never;
1470 header?: never;
1471 path?: never;
1472 cookie?: never;
1473 };
1474 /**
1475 * Full-text search across chats + messages (PRD §1.7)
1476 * @description Wave B. Postgres ``websearch_to_tsquery`` against the
1477 * ``chats.title_tsv`` + ``messages.content_tsv`` generated
1478 * columns (migration 0016). Owner-scoped; archived chats are
1479 * excluded. Returns ranked hits with a ``ts_headline`` snippet
1480 * of the matching message (or the title itself for title hits).
1481 */
1482 get: {
1483 parameters: {
1484 query: {
1485 q: string;
1486 limit?: number;
1487 };
1488 header?: never;
1489 path?: never;
1490 cookie?: never;
1491 };
1492 requestBody?: never;
1493 responses: {
1494 /** @description Ranked search hits */
1495 200: {
1496 headers: {
1497 [name: string]: unknown;
1498 };
1499 content: {
1500 "application/json": components["schemas"]["ChatSearchResponse"];
1501 };
1502 };
1503 };
1504 };
1505 put?: never;
1506 post?: never;
1507 delete?: never;
1508 options?: never;
1509 head?: never;
1510 patch?: never;
1511 trace?: never;
1512 };
1513 "/api/v1/chats": {
1514 parameters: {
1515 query?: never;
1516 header?: never;
1517 path?: never;
1518 cookie?: never;
1519 };
1520 /** List the caller's chats (cursor-paginated) */
1521 get: {
1522 parameters: {
1523 query?: {
1524 /** @description Filter to chats inside a specific project. */
1525 project_id?: string;
1526 /** @description When true, return archived chats only. Default excludes archived. */
1527 archived?: boolean;
1528 /** @description Opaque cursor from a previous page's `next_cursor`. */
1529 cursor?: string;
1530 limit?: number;
1531 };
1532 header?: never;
1533 path?: never;
1534 cookie?: never;
1535 };
1536 requestBody?: never;
1537 responses: {
1538 /** @description Paginated chats */
1539 200: {
1540 headers: {
1541 [name: string]: unknown;
1542 };
1543 content: {
1544 "application/json": {
1545 items: components["schemas"]["Chat"][];
1546 next_cursor: string | null;
1547 };
1548 };
1549 };
1550 };
1551 };
1552 put?: never;
1553 /** Create a new chat */
1554 post: {
1555 parameters: {
1556 query?: never;
1557 header?: never;
1558 path?: never;
1559 cookie?: never;
1560 };
1561 requestBody: {
1562 content: {
1563 "application/json": components["schemas"]["ChatCreate"];
1564 };
1565 };
1566 responses: {
1567 /** @description Chat created */
1568 201: {
1569 headers: {
1570 [name: string]: unknown;
1571 };
1572 content: {
1573 "application/json": components["schemas"]["Chat"];
1574 };
1575 };
1576 };
1577 };
1578 delete?: never;
1579 options?: never;
1580 head?: never;
1581 patch?: never;
1582 trace?: never;
1583 };
1584 "/api/v1/chats/{chat_id}": {
1585 parameters: {
1586 query?: never;
1587 header?: never;
1588 path: {
1589 chat_id: string;
1590 };
1591 cookie?: never;
1592 };
1593 /** Fetch a single chat (archived rows are visible) */
1594 get: {
1595 parameters: {
1596 query?: never;
1597 header?: never;
1598 path: {
1599 chat_id: string;
1600 };
1601 cookie?: never;
1602 };
1603 requestBody?: never;
1604 responses: {
1605 /** @description Chat */
1606 200: {
1607 headers: {
1608 [name: string]: unknown;
1609 };
1610 content: {
1611 "application/json": components["schemas"]["Chat"];
1612 };
1613 };
1614 /** @description Chat does not exist or is owned by a different user */
1615 404: {
1616 headers: {
1617 [name: string]: unknown;
1618 };
1619 content: {
1620 "application/json": components["schemas"]["Error"];
1621 };
1622 };
1623 };
1624 };
1625 put?: never;
1626 post?: never;
1627 /** Soft-delete the chat (sets `archived_at`) */
1628 delete: {
1629 parameters: {
1630 query?: never;
1631 header?: never;
1632 path: {
1633 chat_id: string;
1634 };
1635 cookie?: never;
1636 };
1637 requestBody?: never;
1638 responses: {
1639 /** @description Chat archived */
1640 204: {
1641 headers: {
1642 [name: string]: unknown;
1643 };
1644 content?: never;
1645 };
1646 /** @description Chat already archived or does not exist */
1647 404: {
1648 headers: {
1649 [name: string]: unknown;
1650 };
1651 content: {
1652 "application/json": components["schemas"]["Error"];
1653 };
1654 };
1655 };
1656 };
1657 options?: never;
1658 head?: never;
1659 /** Partial update of a chat (title or archived flag) */
1660 patch: {
1661 parameters: {
1662 query?: never;
1663 header?: never;
1664 path: {
1665 chat_id: string;
1666 };
1667 cookie?: never;
1668 };
1669 requestBody: {
1670 content: {
1671 "application/json": components["schemas"]["ChatUpdate"];
1672 };
1673 };
1674 responses: {
1675 /** @description Chat updated */
1676 200: {
1677 headers: {
1678 [name: string]: unknown;
1679 };
1680 content: {
1681 "application/json": components["schemas"]["Chat"];
1682 };
1683 };
1684 /** @description Chat does not exist or is owned by a different user */
1685 404: {
1686 headers: {
1687 [name: string]: unknown;
1688 };
1689 content: {
1690 "application/json": components["schemas"]["Error"];
1691 };
1692 };
1693 };
1694 };
1695 trace?: never;
1696 };
1697 "/api/v1/chats/{chat_id}/messages": {
1698 parameters: {
1699 query?: never;
1700 header?: never;
1701 path: {
1702 chat_id: string;
1703 };
1704 cookie?: never;
1705 };
1706 /** List messages in chat (cursor-paginated, oldest-first) */
1707 get: {
1708 parameters: {
1709 query?: {
1710 /** @description Opaque cursor from a previous page's `next_cursor`. */
1711 cursor?: string;
1712 limit?: number;
1713 };
1714 header?: never;
1715 path: {
1716 chat_id: string;
1717 };
1718 cookie?: never;
1719 };
1720 requestBody?: never;
1721 responses: {
1722 /** @description Paginated messages */
1723 200: {
1724 headers: {
1725 [name: string]: unknown;
1726 };
1727 content: {
1728 "application/json": {
1729 items: components["schemas"]["Message"][];
1730 next_cursor: string | null;
1731 };
1732 };
1733 };
1734 };
1735 };
1736 put?: never;
1737 /**
1738 * Post a user message and get the model response
1739 * @description Posts a user message and returns the assistant response. The
1740 * request's `stream` field selects between two response shapes:
1741 *
1742 * * `stream: false` (default) → JSON response with the persisted
1743 * assistant message, the routed Inference Tier, and routing
1744 * metadata.
1745 * * `stream: true` → Server-Sent Events stream:
1746 * 1. The opening `start` frame carries `lq_ai_message_id`
1747 * (the persisted assistant message id) so clients can
1748 * poll the row later.
1749 * 2. Each `delta` frame carries `lq_ai_message_id`,
1750 * `routed_inference_tier`, and `applied_skills` per
1751 * ADR 0007.
1752 * 3. The terminal `complete` frame (success) or `Error`
1753 * envelope (mid-stream failure) precedes the OpenAI-style
1754 * `data: [DONE]` line.
1755 *
1756 * **C3 persistence.** The backend writes the user message row
1757 * first (unconditionally), generates the assistant message UUID,
1758 * forwards it to the gateway as `lq_ai_message_id` so the
1759 * gateway's `inference_routing_log` row carries the same id, and
1760 * persists the assistant row at end-of-stream (or end of the
1761 * non-streaming response). On streaming failure the assistant
1762 * row is persisted with whatever content was received and
1763 * `error_code` populated for audit.
1764 *
1765 * Citations are an empty array until M2's citation engine ships.
1766 *
1767 * The response also surfaces the routed Inference Tier in the
1768 * `X-LQ-AI-Routed-Inference-Tier` HTTP header (header-only
1769 * consumers don't need to parse the body).
1770 */
1771 post: {
1772 parameters: {
1773 query?: never;
1774 header?: never;
1775 path: {
1776 chat_id: string;
1777 };
1778 cookie?: never;
1779 };
1780 requestBody: {
1781 content: {
1782 "application/json": components["schemas"]["MessageCreate"];
1783 };
1784 };
1785 responses: {
1786 /** @description Either a JSON response or an SSE stream depending on `stream` */
1787 200: {
1788 headers: {
1789 /** @description Routed Inference Tier — duplicates the body field for header-only consumers */
1790 "X-LQ-AI-Routed-Inference-Tier"?: 1 | 2 | 3 | 4 | 5;
1791 /** @description Provider that handled the request (set by gateway) */
1792 "X-LQ-AI-Routed-Provider"?: string;
1793 [name: string]: unknown;
1794 };
1795 content: {
1796 "application/json": components["schemas"]["MessagePostResponse"];
1797 "text/event-stream": components["schemas"]["MessageStreamEvent"];
1798 };
1799 };
1800 /** @description Invalid request (bad chat_id, empty content, malformed body) */
1801 400: {
1802 headers: {
1803 [name: string]: unknown;
1804 };
1805 content: {
1806 "application/json": components["schemas"]["Error"];
1807 };
1808 };
1809 /** @description Missing or invalid bearer token */
1810 401: {
1811 headers: {
1812 [name: string]: unknown;
1813 };
1814 content: {
1815 "application/json": components["schemas"]["Error"];
1816 };
1817 };
1818 /** @description Forced password change required (B2 gate) or tier-floor refusal (D1) */
1819 403: {
1820 headers: {
1821 [name: string]: unknown;
1822 };
1823 content: {
1824 "application/json": components["schemas"]["Error"];
1825 };
1826 };
1827 /** @description Gateway returned an upstream error (provider_unavailable, etc.) */
1828 502: {
1829 headers: {
1830 [name: string]: unknown;
1831 };
1832 content: {
1833 "application/json": components["schemas"]["Error"];
1834 };
1835 };
1836 /** @description Gateway unreachable (network failure or 5xx) */
1837 503: {
1838 headers: {
1839 [name: string]: unknown;
1840 };
1841 content: {
1842 "application/json": components["schemas"]["Error"];
1843 };
1844 };
1845 /** @description Gateway request timed out */
1846 504: {
1847 headers: {
1848 [name: string]: unknown;
1849 };
1850 content: {
1851 "application/json": components["schemas"]["Error"];
1852 };
1853 };
1854 };
1855 };
1856 delete?: never;
1857 options?: never;
1858 head?: never;
1859 patch?: never;
1860 trace?: never;
1861 };
1862 "/api/v1/chats/{chat_id}/messages/{message_id}/citations": {
1863 parameters: {
1864 query?: never;
1865 header?: never;
1866 path: {
1867 chat_id: string;
1868 message_id: string;
1869 };
1870 cookie?: never;
1871 };
1872 /** Get citations for a message (resolves to source documents) */
1873 get: {
1874 parameters: {
1875 query?: never;
1876 header?: never;
1877 path: {
1878 chat_id: string;
1879 message_id: string;
1880 };
1881 cookie?: never;
1882 };
1883 requestBody?: never;
1884 responses: {
1885 /** @description Citations */
1886 200: {
1887 headers: {
1888 [name: string]: unknown;
1889 };
1890 content: {
1891 "application/json": components["schemas"]["Citation"][];
1892 };
1893 };
1894 };
1895 };
1896 put?: never;
1897 post?: never;
1898 delete?: never;
1899 options?: never;
1900 head?: never;
1901 patch?: never;
1902 trace?: never;
1903 };
1904 "/api/v1/skills": {
1905 parameters: {
1906 query?: never;
1907 header?: never;
1908 path?: never;
1909 cookie?: never;
1910 };
1911 /** List available skills */
1912 get: {
1913 parameters: {
1914 query?: {
1915 scope?: "builtin" | "user" | "team" | "all";
1916 tag?: string;
1917 };
1918 header?: never;
1919 path?: never;
1920 cookie?: never;
1921 };
1922 requestBody?: never;
1923 responses: {
1924 /** @description Skills */
1925 200: {
1926 headers: {
1927 [name: string]: unknown;
1928 };
1929 content: {
1930 "application/json": components["schemas"]["SkillSummary"][];
1931 };
1932 };
1933 };
1934 };
1935 put?: never;
1936 post?: never;
1937 delete?: never;
1938 options?: never;
1939 head?: never;
1940 patch?: never;
1941 trace?: never;
1942 };
1943 "/api/v1/skills/{skill_name}": {
1944 parameters: {
1945 query?: never;
1946 header?: never;
1947 path: {
1948 skill_name: string;
1949 };
1950 cookie?: never;
1951 };
1952 /** Get skill details (frontmatter + body + reference files) */
1953 get: {
1954 parameters: {
1955 query?: never;
1956 header?: never;
1957 path: {
1958 skill_name: string;
1959 };
1960 cookie?: never;
1961 };
1962 requestBody?: never;
1963 responses: {
1964 /** @description Skill */
1965 200: {
1966 headers: {
1967 [name: string]: unknown;
1968 };
1969 content: {
1970 "application/json": components["schemas"]["Skill"];
1971 };
1972 };
1973 /** @description No skill with that name is in the registry. */
1974 404: {
1975 headers: {
1976 [name: string]: unknown;
1977 };
1978 content: {
1979 "application/json": components["schemas"]["Error"];
1980 };
1981 };
1982 };
1983 };
1984 put?: never;
1985 post?: never;
1986 delete?: never;
1987 options?: never;
1988 head?: never;
1989 patch?: never;
1990 trace?: never;
1991 };
1992 "/api/v1/skills/{skill_name}/contents": {
1993 parameters: {
1994 query?: never;
1995 header?: never;
1996 path: {
1997 skill_name: string;
1998 };
1999 cookie?: never;
2000 };
2001 /**
2002 * Full skill contents for the skill inspector (PRD §3.4 — Wave A)
2003 * @description Returns the same payload shape as ``GET /skills/{skill_name}``
2004 * (SKILL.md frontmatter + body + reference + example files). The
2005 * dedicated URL exists because PRD §3.4 names it explicitly as
2006 * the contract behind the "view this skill" affordance + the
2007 * skill inspector side panel. Applies the D8.1b resolution stack
2008 * (user > team > built-in).
2009 */
2010 get: {
2011 parameters: {
2012 query?: never;
2013 header?: never;
2014 path: {
2015 skill_name: string;
2016 };
2017 cookie?: never;
2018 };
2019 requestBody?: never;
2020 responses: {
2021 /** @description Skill (same shape as the base GET) */
2022 200: {
2023 headers: {
2024 [name: string]: unknown;
2025 };
2026 content: {
2027 "application/json": components["schemas"]["Skill"];
2028 };
2029 };
2030 /** @description No skill with that name resolves for the caller. */
2031 404: {
2032 headers: {
2033 [name: string]: unknown;
2034 };
2035 content: {
2036 "application/json": components["schemas"]["Error"];
2037 };
2038 };
2039 };
2040 };
2041 put?: never;
2042 post?: never;
2043 delete?: never;
2044 options?: never;
2045 head?: never;
2046 patch?: never;
2047 trace?: never;
2048 };
2049 "/api/v1/skills/{skill_name}/inputs": {
2050 parameters: {
2051 query?: never;
2052 header?: never;
2053 path: {
2054 skill_name: string;
2055 };
2056 cookie?: never;
2057 };
2058 /**
2059 * Declared inputs (form schema) for the skill (PRD §3.4 — Wave A)
2060 * @description The PRD §3.4 skill-input-form pattern: skills declare inputs
2061 * in their frontmatter so the UI can render a structured form
2062 * rather than letting the model ask for missing context
2063 * conversationally. The endpoint resolves user > team > built-in
2064 * and looks for the inputs block at either the top of the
2065 * frontmatter (formal-guide shape) or under ``lq_ai.inputs``
2066 * (corpus reality for skills like enhance-prompt). Returns a
2067 * name-only stub when the skill declares no inputs.
2068 */
2069 get: {
2070 parameters: {
2071 query?: never;
2072 header?: never;
2073 path: {
2074 skill_name: string;
2075 };
2076 cookie?: never;
2077 };
2078 requestBody?: never;
2079 responses: {
2080 /** @description Inputs (required + optional) */
2081 200: {
2082 headers: {
2083 [name: string]: unknown;
2084 };
2085 content: {
2086 "application/json": components["schemas"]["SkillInputs"];
2087 };
2088 };
2089 /** @description No skill with that name resolves for the caller. */
2090 404: {
2091 headers: {
2092 [name: string]: unknown;
2093 };
2094 content: {
2095 "application/json": components["schemas"]["Error"];
2096 };
2097 };
2098 };
2099 };
2100 put?: never;
2101 post?: never;
2102 delete?: never;
2103 options?: never;
2104 head?: never;
2105 patch?: never;
2106 trace?: never;
2107 };
2108 "/api/v1/skills/{skill_name}/fork": {
2109 parameters: {
2110 query?: never;
2111 header?: never;
2112 path: {
2113 skill_name: string;
2114 };
2115 cookie?: never;
2116 };
2117 get?: never;
2118 put?: never;
2119 /**
2120 * Fork a built-in skill into the caller's user scope (D8)
2121 * @description Copies the built-in's resolved frontmatter and body into a new
2122 * ``user_skills`` row owned by the caller (ADR 0012). The request
2123 * body's ``new_name`` becomes the new row's slug (omitted →
2124 * defaults to the source slug, which produces a same-slug shadow);
2125 * ``scope`` MUST be ``user`` in D8 — ``team`` is reserved for
2126 * D8.1 and returns 400.
2127 */
2128 post: {
2129 parameters: {
2130 query?: never;
2131 header?: never;
2132 path: {
2133 skill_name: string;
2134 };
2135 cookie?: never;
2136 };
2137 requestBody?: {
2138 content: {
2139 "application/json": {
2140 /** @description New slug for the user-scope row */
2141 new_name?: string;
2142 /**
2143 * @default user
2144 * @enum {string}
2145 */
2146 scope?: "user" | "team";
2147 };
2148 };
2149 };
2150 responses: {
2151 /** @description Fork created */
2152 201: {
2153 headers: {
2154 [name: string]: unknown;
2155 };
2156 content: {
2157 "application/json": components["schemas"]["Skill"];
2158 };
2159 };
2160 /** @description ``scope=team`` requested (deferred to D8.1). */
2161 400: {
2162 headers: {
2163 [name: string]: unknown;
2164 };
2165 content: {
2166 "application/json": components["schemas"]["Error"];
2167 };
2168 };
2169 /** @description A user-scope skill with that slug already exists for this user. */
2170 409: {
2171 headers: {
2172 [name: string]: unknown;
2173 };
2174 content: {
2175 "application/json": components["schemas"]["Error"];
2176 };
2177 };
2178 };
2179 };
2180 delete?: never;
2181 options?: never;
2182 head?: never;
2183 patch?: never;
2184 trace?: never;
2185 };
2186 "/api/v1/skills/autocomplete": {
2187 parameters: {
2188 query?: never;
2189 header?: never;
2190 path?: never;
2191 cookie?: never;
2192 };
2193 /**
2194 * Typeahead for the chat composer's skill picker (Wave D.2)
2195 * @description Two modes based on the ``q`` query parameter:
2196 *
2197 * * **Empty ``q``** — returns the caller's most-recently-used skills
2198 * (by ``messages.applied_skills``), filled out alphabetically to
2199 * ``limit`` when recents are fewer than ``limit``.
2200 * * **Non-empty ``q``** — ranks the merged catalog by three signals
2201 * (slash-alias prefix > slug prefix > title substring) and returns
2202 * up to ``limit`` non-zero matches.
2203 *
2204 * Shadowing: a user-scope row at the same slug as a built-in hides
2205 * the built-in (ADR 0012). ``scope`` on each result distinguishes
2206 * user shadows from built-ins.
2207 *
2208 * ``limit`` is hard-clamped at 25 — a request for ``limit=50``
2209 * returns 422 rather than silently truncating.
2210 */
2211 get: {
2212 parameters: {
2213 query?: {
2214 /** @description Substring to match. Empty returns recents. */
2215 q?: string;
2216 limit?: number;
2217 };
2218 header?: never;
2219 path?: never;
2220 cookie?: never;
2221 };
2222 requestBody?: never;
2223 responses: {
2224 /** @description Autocomplete results (may be empty) */
2225 200: {
2226 headers: {
2227 [name: string]: unknown;
2228 };
2229 content: {
2230 "application/json": {
2231 results: {
2232 slug: string;
2233 slash_alias?: string | null;
2234 title: string;
2235 description?: string | null;
2236 /** @enum {string} */
2237 scope: "builtin" | "user" | "team";
2238 icon?: string | null;
2239 }[];
2240 };
2241 };
2242 };
2243 /** @description limit out of range (must be 1–25). */
2244 422: {
2245 headers: {
2246 [name: string]: unknown;
2247 };
2248 content?: never;
2249 };
2250 };
2251 };
2252 put?: never;
2253 post?: never;
2254 delete?: never;
2255 options?: never;
2256 head?: never;
2257 patch?: never;
2258 trace?: never;
2259 };
2260 "/api/v1/user-skills": {
2261 parameters: {
2262 query?: never;
2263 header?: never;
2264 path?: never;
2265 cookie?: never;
2266 };
2267 /**
2268 * List the caller's editable skills (user-scope by default; ?scope=team|all)
2269 * @description Returns the rich ``UserSkill`` view (body + frontmatter_extra
2270 * included) for the management UI. The picker-side merge view
2271 * lives at ``GET /api/v1/skills?scope=all`` (summary shape).
2272 *
2273 * Scope filter (D8.1c — default ``user`` for back-compat):
2274 *
2275 * * ``user`` — caller's user-scope rows only.
2276 * * ``team`` — team-scope rows from teams where the caller is a
2277 * team-admin. (Non-admin members read team skills in the picker,
2278 * not here, since they can't mutate.)
2279 * * ``all`` — both layers merged + sorted by ``updated_at DESC``.
2280 */
2281 get: {
2282 parameters: {
2283 query?: {
2284 scope?: "user" | "team" | "all";
2285 };
2286 header?: never;
2287 path?: never;
2288 cookie?: never;
2289 };
2290 requestBody?: never;
2291 responses: {
2292 /** @description List of editable skill rows for the caller */
2293 200: {
2294 headers: {
2295 [name: string]: unknown;
2296 };
2297 content: {
2298 "application/json": components["schemas"]["UserSkill"][];
2299 };
2300 };
2301 };
2302 };
2303 put?: never;
2304 /**
2305 * Create a user- or team-scope skill (D8 + D8.1b)
2306 * @description Creates a new skill row. Scope branches (per ADR 0012 + D8.1b):
2307 *
2308 * * ``scope='user'`` (default) — owned by the caller. Slug
2309 * collision with the caller's existing non-archived rows
2310 * returns 409. Collision with a filesystem built-in is
2311 * **allowed** — the deliberate shadow case; the user's row
2312 * wins for their chats.
2313 * * ``scope='team'`` — owned by ``owner_team_id``. Caller must
2314 * be a team-admin of that team or 404 (id-probing-safe).
2315 * Slug collision within the team's non-archived rows returns
2316 * 409. The D8.1b resolver picks user > team > built-in.
2317 *
2318 * 422 fires when ``scope`` and ``owner_team_id`` are inconsistent.
2319 */
2320 post: {
2321 parameters: {
2322 query?: never;
2323 header?: never;
2324 path?: never;
2325 cookie?: never;
2326 };
2327 requestBody: {
2328 content: {
2329 "application/json": components["schemas"]["UserSkillCreate"];
2330 };
2331 };
2332 responses: {
2333 /** @description Created */
2334 201: {
2335 headers: {
2336 [name: string]: unknown;
2337 };
2338 content: {
2339 "application/json": components["schemas"]["UserSkill"];
2340 };
2341 };
2342 /** @description Team referenced by owner_team_id does not exist OR caller is not a team-admin (id-probing-safe). */
2343 404: {
2344 headers: {
2345 [name: string]: unknown;
2346 };
2347 content: {
2348 "application/json": components["schemas"]["Error"];
2349 };
2350 };
2351 /** @description Slug collides with the caller's user-scope rows OR the named team's team-scope rows. */
2352 409: {
2353 headers: {
2354 [name: string]: unknown;
2355 };
2356 content: {
2357 "application/json": components["schemas"]["Error"];
2358 };
2359 };
2360 /** @description scope/owner_team_id combination is invalid (team missing team id, or user carrying team id). */
2361 422: {
2362 headers: {
2363 [name: string]: unknown;
2364 };
2365 content: {
2366 "application/json": components["schemas"]["Error"];
2367 };
2368 };
2369 };
2370 };
2371 delete?: never;
2372 options?: never;
2373 head?: never;
2374 patch?: never;
2375 trace?: never;
2376 };
2377 "/api/v1/teams": {
2378 parameters: {
2379 query?: never;
2380 header?: never;
2381 path?: never;
2382 cookie?: never;
2383 };
2384 /**
2385 * List teams the caller belongs to (newest first)
2386 * @description Each row carries ``caller_role`` (admin | member) so the UI can
2387 * render mutate affordances without a second round-trip. The
2388 * optional ``?role=`` filter (D8.1c) restricts to a specific
2389 * role — the team-scope skill creation picker uses
2390 * ``?role=admin``.
2391 */
2392 get: {
2393 parameters: {
2394 query?: {
2395 role?: "admin" | "member";
2396 };
2397 header?: never;
2398 path?: never;
2399 cookie?: never;
2400 };
2401 requestBody?: never;
2402 responses: {
2403 /** @description Caller's teams (with caller_role populated) */
2404 200: {
2405 headers: {
2406 [name: string]: unknown;
2407 };
2408 content: {
2409 "application/json": components["schemas"]["TeamSummary"][];
2410 };
2411 };
2412 };
2413 };
2414 put?: never;
2415 post?: never;
2416 delete?: never;
2417 options?: never;
2418 head?: never;
2419 patch?: never;
2420 trace?: never;
2421 };
2422 "/api/v1/teams/{team_id}": {
2423 parameters: {
2424 query?: never;
2425 header?: never;
2426 path: {
2427 team_id: string;
2428 };
2429 cookie?: never;
2430 };
2431 /** Fetch a team (member-only; 404 conflates "no such team" and "not a member") */
2432 get: {
2433 parameters: {
2434 query?: never;
2435 header?: never;
2436 path: {
2437 team_id: string;
2438 };
2439 cookie?: never;
2440 };
2441 requestBody?: never;
2442 responses: {
2443 /** @description Team payload with member roster */
2444 200: {
2445 headers: {
2446 [name: string]: unknown;
2447 };
2448 content: {
2449 "application/json": components["schemas"]["Team"];
2450 };
2451 };
2452 /** @description Team does not exist OR caller is not a member. */
2453 404: {
2454 headers: {
2455 [name: string]: unknown;
2456 };
2457 content: {
2458 "application/json": components["schemas"]["Error"];
2459 };
2460 };
2461 };
2462 };
2463 put?: never;
2464 post?: never;
2465 delete?: never;
2466 options?: never;
2467 head?: never;
2468 patch?: never;
2469 trace?: never;
2470 };
2471 "/api/v1/admin/teams": {
2472 parameters: {
2473 query?: never;
2474 header?: never;
2475 path?: never;
2476 cookie?: never;
2477 };
2478 /** List every team in the deployment (admin) */
2479 get: {
2480 parameters: {
2481 query?: never;
2482 header?: never;
2483 path?: never;
2484 cookie?: never;
2485 };
2486 requestBody?: never;
2487 responses: {
2488 /** @description All teams */
2489 200: {
2490 headers: {
2491 [name: string]: unknown;
2492 };
2493 content: {
2494 "application/json": components["schemas"]["TeamSummary"][];
2495 };
2496 };
2497 };
2498 };
2499 put?: never;
2500 /** Create a new team; admin is auto-added as the first admin member */
2501 post: {
2502 parameters: {
2503 query?: never;
2504 header?: never;
2505 path?: never;
2506 cookie?: never;
2507 };
2508 requestBody: {
2509 content: {
2510 "application/json": components["schemas"]["TeamCreate"];
2511 };
2512 };
2513 responses: {
2514 /** @description Created */
2515 201: {
2516 headers: {
2517 [name: string]: unknown;
2518 };
2519 content: {
2520 "application/json": components["schemas"]["Team"];
2521 };
2522 };
2523 /** @description A team with that slug already exists. */
2524 409: {
2525 headers: {
2526 [name: string]: unknown;
2527 };
2528 content: {
2529 "application/json": components["schemas"]["Error"];
2530 };
2531 };
2532 };
2533 };
2534 delete?: never;
2535 options?: never;
2536 head?: never;
2537 patch?: never;
2538 trace?: never;
2539 };
2540 "/api/v1/admin/teams/{team_id}": {
2541 parameters: {
2542 query?: never;
2543 header?: never;
2544 path: {
2545 team_id: string;
2546 };
2547 cookie?: never;
2548 };
2549 /** Fetch a team with its member roster (admin) */
2550 get: {
2551 parameters: {
2552 query?: never;
2553 header?: never;
2554 path: {
2555 team_id: string;
2556 };
2557 cookie?: never;
2558 };
2559 requestBody?: never;
2560 responses: {
2561 /** @description Team payload */
2562 200: {
2563 headers: {
2564 [name: string]: unknown;
2565 };
2566 content: {
2567 "application/json": components["schemas"]["Team"];
2568 };
2569 };
2570 };
2571 };
2572 put?: never;
2573 post?: never;
2574 /** Delete a team; CASCADES to team_members and team-scope user_skills */
2575 delete: {
2576 parameters: {
2577 query?: never;
2578 header?: never;
2579 path: {
2580 team_id: string;
2581 };
2582 cookie?: never;
2583 };
2584 requestBody?: never;
2585 responses: {
2586 /** @description Deleted */
2587 204: {
2588 headers: {
2589 [name: string]: unknown;
2590 };
2591 content?: never;
2592 };
2593 };
2594 };
2595 options?: never;
2596 head?: never;
2597 /** Update name / description (admin) */
2598 patch: {
2599 parameters: {
2600 query?: never;
2601 header?: never;
2602 path: {
2603 team_id: string;
2604 };
2605 cookie?: never;
2606 };
2607 requestBody: {
2608 content: {
2609 "application/json": components["schemas"]["TeamUpdate"];
2610 };
2611 };
2612 responses: {
2613 /** @description Updated team */
2614 200: {
2615 headers: {
2616 [name: string]: unknown;
2617 };
2618 content: {
2619 "application/json": components["schemas"]["Team"];
2620 };
2621 };
2622 };
2623 };
2624 trace?: never;
2625 };
2626 "/api/v1/admin/teams/{team_id}/members": {
2627 parameters: {
2628 query?: never;
2629 header?: never;
2630 path: {
2631 team_id: string;
2632 };
2633 cookie?: never;
2634 };
2635 get?: never;
2636 put?: never;
2637 /** Add a user to a team (admin) */
2638 post: {
2639 parameters: {
2640 query?: never;
2641 header?: never;
2642 path: {
2643 team_id: string;
2644 };
2645 cookie?: never;
2646 };
2647 requestBody: {
2648 content: {
2649 "application/json": {
2650 /** Format: uuid */
2651 user_id: string;
2652 /**
2653 * @default member
2654 * @enum {string}
2655 */
2656 role?: "admin" | "member";
2657 };
2658 };
2659 };
2660 responses: {
2661 /** @description Member added */
2662 201: {
2663 headers: {
2664 [name: string]: unknown;
2665 };
2666 content: {
2667 "application/json": components["schemas"]["TeamMember"];
2668 };
2669 };
2670 /** @description User is already a member of this team. */
2671 409: {
2672 headers: {
2673 [name: string]: unknown;
2674 };
2675 content: {
2676 "application/json": components["schemas"]["Error"];
2677 };
2678 };
2679 };
2680 };
2681 delete?: never;
2682 options?: never;
2683 head?: never;
2684 patch?: never;
2685 trace?: never;
2686 };
2687 "/api/v1/admin/teams/{team_id}/members/{user_id}": {
2688 parameters: {
2689 query?: never;
2690 header?: never;
2691 path: {
2692 team_id: string;
2693 user_id: string;
2694 };
2695 cookie?: never;
2696 };
2697 get?: never;
2698 put?: never;
2699 post?: never;
2700 /** Remove a user from a team (admin) */
2701 delete: {
2702 parameters: {
2703 query?: never;
2704 header?: never;
2705 path: {
2706 team_id: string;
2707 user_id: string;
2708 };
2709 cookie?: never;
2710 };
2711 requestBody?: never;
2712 responses: {
2713 /** @description Member removed */
2714 204: {
2715 headers: {
2716 [name: string]: unknown;
2717 };
2718 content?: never;
2719 };
2720 };
2721 };
2722 options?: never;
2723 head?: never;
2724 /** Change a member's role (admin) */
2725 patch: {
2726 parameters: {
2727 query?: never;
2728 header?: never;
2729 path: {
2730 team_id: string;
2731 user_id: string;
2732 };
2733 cookie?: never;
2734 };
2735 requestBody: {
2736 content: {
2737 "application/json": {
2738 /** @enum {string} */
2739 role: "admin" | "member";
2740 };
2741 };
2742 };
2743 responses: {
2744 /** @description Updated membership */
2745 200: {
2746 headers: {
2747 [name: string]: unknown;
2748 };
2749 content: {
2750 "application/json": components["schemas"]["TeamMember"];
2751 };
2752 };
2753 };
2754 };
2755 trace?: never;
2756 };
2757 "/api/v1/user-skills/{skill_id}": {
2758 parameters: {
2759 query?: never;
2760 header?: never;
2761 path: {
2762 skill_id: string;
2763 };
2764 cookie?: never;
2765 };
2766 /**
2767 * Fetch a single user- or team-scope skill (owner or team-admin)
2768 * @description Owner-only for ``scope='user'`` rows; team-admin-only for
2769 * ``scope='team'`` rows (D8.1b). Non-admin team members read team
2770 * skills through the merged picker at ``GET /api/v1/skills/{slug}``,
2771 * not through this management endpoint.
2772 */
2773 get: {
2774 parameters: {
2775 query?: never;
2776 header?: never;
2777 path: {
2778 skill_id: string;
2779 };
2780 cookie?: never;
2781 };
2782 requestBody?: never;
2783 responses: {
2784 /** @description Skill row */
2785 200: {
2786 headers: {
2787 [name: string]: unknown;
2788 };
2789 content: {
2790 "application/json": components["schemas"]["UserSkill"];
2791 };
2792 };
2793 /** @description Skill not found OR caller is not the owner / not a team-admin (id-probing-safe). */
2794 404: {
2795 headers: {
2796 [name: string]: unknown;
2797 };
2798 content: {
2799 "application/json": components["schemas"]["Error"];
2800 };
2801 };
2802 };
2803 };
2804 put?: never;
2805 post?: never;
2806 /**
2807 * Soft-delete (owner or team-admin)
2808 * @description Owner-only for ``scope='user'`` rows; team-admin-only for
2809 * ``scope='team'`` rows (D8.1b). Sets ``archived_at = now()``.
2810 * The slug is freed for a new skill at the same slug after
2811 * archive. Audit log records the deletion with the row's
2812 * identity; team-scope rows carry ``team_id`` in the details bag.
2813 */
2814 delete: {
2815 parameters: {
2816 query?: never;
2817 header?: never;
2818 path: {
2819 skill_id: string;
2820 };
2821 cookie?: never;
2822 };
2823 requestBody?: never;
2824 responses: {
2825 /** @description Deleted */
2826 204: {
2827 headers: {
2828 [name: string]: unknown;
2829 };
2830 content?: never;
2831 };
2832 /** @description Skill not found OR caller is not the owner / not a team-admin. */
2833 404: {
2834 headers: {
2835 [name: string]: unknown;
2836 };
2837 content: {
2838 "application/json": components["schemas"]["Error"];
2839 };
2840 };
2841 /** @description Skill is already archived. */
2842 410: {
2843 headers: {
2844 [name: string]: unknown;
2845 };
2846 content: {
2847 "application/json": components["schemas"]["Error"];
2848 };
2849 };
2850 };
2851 };
2852 options?: never;
2853 head?: never;
2854 /**
2855 * Partial update (owner or team-admin)
2856 * @description Owner-only for ``scope='user'`` rows; team-admin-only for
2857 * ``scope='team'`` rows (D8.1b). Audit details on team-scope
2858 * rows carry ``team_id`` alongside slug + changed fields.
2859 */
2860 patch: {
2861 parameters: {
2862 query?: never;
2863 header?: never;
2864 path: {
2865 skill_id: string;
2866 };
2867 cookie?: never;
2868 };
2869 requestBody: {
2870 content: {
2871 "application/json": components["schemas"]["UserSkillUpdate"];
2872 };
2873 };
2874 responses: {
2875 /** @description Updated row */
2876 200: {
2877 headers: {
2878 [name: string]: unknown;
2879 };
2880 content: {
2881 "application/json": components["schemas"]["UserSkill"];
2882 };
2883 };
2884 /** @description Skill not found OR caller is not the owner / not a team-admin. */
2885 404: {
2886 headers: {
2887 [name: string]: unknown;
2888 };
2889 content: {
2890 "application/json": components["schemas"]["Error"];
2891 };
2892 };
2893 };
2894 };
2895 trace?: never;
2896 };
2897 "/api/v1/user-skills/{skill_id}/versions": {
2898 parameters: {
2899 query?: never;
2900 header?: never;
2901 path: {
2902 skill_id: string;
2903 };
2904 cookie?: never;
2905 };
2906 /**
2907 * Audit-log view of edits for this user-skill (Wave D.2)
2908 * @description Returns ``audit_log`` rows for ``resource_type='user_skill'``
2909 * keyed by this skill id, ordered ``timestamp DESC`` (newest first).
2910 * The timeline UI renders top-down so the most recent edit appears
2911 * at the top; ``user_skill.created`` lands last.
2912 *
2913 * Access gate mirrors the mutable surface: owner-only for
2914 * ``scope='user'`` rows; team-admin-only for ``scope='team'`` rows.
2915 * 404 conflates "no such id" and "not your skill" (id-probing-safe).
2916 * Active AND archived rows are accessible so a freshly-archived
2917 * skill's timeline can still be rendered.
2918 */
2919 get: {
2920 parameters: {
2921 query?: {
2922 limit?: number;
2923 };
2924 header?: never;
2925 path: {
2926 skill_id: string;
2927 };
2928 cookie?: never;
2929 };
2930 requestBody?: never;
2931 responses: {
2932 /** @description Version history items, newest first */
2933 200: {
2934 headers: {
2935 [name: string]: unknown;
2936 };
2937 content: {
2938 "application/json": {
2939 items: {
2940 /** Format: date-time */
2941 timestamp: string;
2942 /** Format: uuid */
2943 actor_user_id?: string | null;
2944 actor_email?: string | null;
2945 /** @description e.g. 'user_skill.created' or 'user_skill.updated' */
2946 action: string;
2947 version?: string | null;
2948 details?: {
2949 [key: string]: unknown;
2950 } | null;
2951 }[];
2952 };
2953 };
2954 };
2955 /** @description Skill not found OR caller is not the owner / not a team-admin. */
2956 404: {
2957 headers: {
2958 [name: string]: unknown;
2959 };
2960 content: {
2961 "application/json": components["schemas"]["Error"];
2962 };
2963 };
2964 };
2965 };
2966 put?: never;
2967 post?: never;
2968 delete?: never;
2969 options?: never;
2970 head?: never;
2971 patch?: never;
2972 trace?: never;
2973 };
2974 "/api/v1/models": {
2975 parameters: {
2976 query?: never;
2977 header?: never;
2978 path?: never;
2979 cookie?: never;
2980 };
2981 /**
2982 * List available models — proxy to gateway /v1/models (D0)
2983 * @description Authenticated proxy that forwards the gateway's merged
2984 * ``GET /v1/models`` payload (aliases + live-discovered Ollama
2985 * tags + live-discovered Anthropic catalog rows). The LQ.AI shell's
2986 * model picker consumes this endpoint; the response shape is
2987 * identical to the gateway's per
2988 * ``docs/api/gateway-openapi.yaml`` so the contract stays single-
2989 * sourced.
2990 *
2991 * Auth: bearer token + cleared ``must_change_password``. The
2992 * picker contents reveal which providers the operator has
2993 * configured, so anonymous reads are intentionally not allowed.
2994 *
2995 * Errors:
2996 * - Gateway timeout → 504 ``gateway_timeout``.
2997 * - Gateway unreachable / 5xx / 401 → 503 ``gateway_unreachable``.
2998 * A 401 from the gateway is operator-side misconfiguration; the
2999 * user must not see the underlying detail.
3000 */
3001 get: {
3002 parameters: {
3003 query?: never;
3004 header?: never;
3005 path?: never;
3006 cookie?: never;
3007 };
3008 requestBody?: never;
3009 responses: {
3010 /** @description Model list */
3011 200: {
3012 headers: {
3013 [name: string]: unknown;
3014 };
3015 content: {
3016 "application/json": {
3017 /** @enum {string} */
3018 object: "list";
3019 data: {
3020 id: string;
3021 /** @enum {string} */
3022 object: "model";
3023 created: number;
3024 owned_by: string;
3025 /** @enum {string} */
3026 lq_ai_kind: "alias" | "provider_native";
3027 /** @enum {integer} */
3028 routed_inference_tier?: 1 | 2 | 3 | 4 | 5;
3029 /** @enum {string} */
3030 provider_type?: "anthropic" | "openai" | "vertex" | "cohere" | "azure_openai" | "bedrock" | "ollama" | "vllm" | "openai_compatible";
3031 lq_ai_resolves_to?: string;
3032 lq_ai_fallback_count?: number;
3033 }[];
3034 };
3035 };
3036 };
3037 /** @description Missing or invalid bearer token */
3038 401: {
3039 headers: {
3040 [name: string]: unknown;
3041 };
3042 content: {
3043 "application/json": components["schemas"]["Error"];
3044 };
3045 };
3046 /** @description Gateway unreachable */
3047 503: {
3048 headers: {
3049 [name: string]: unknown;
3050 };
3051 content: {
3052 "application/json": components["schemas"]["Error"];
3053 };
3054 };
3055 /** @description Gateway timed out */
3056 504: {
3057 headers: {
3058 [name: string]: unknown;
3059 };
3060 content: {
3061 "application/json": components["schemas"]["Error"];
3062 };
3063 };
3064 };
3065 };
3066 put?: never;
3067 post?: never;
3068 delete?: never;
3069 options?: never;
3070 head?: never;
3071 patch?: never;
3072 trace?: never;
3073 };
3074 "/api/v1/internal/skills/{skill_name}": {
3075 parameters: {
3076 query?: {
3077 /**
3078 * @description When supplied, the resolver applies the D8.1b stack (per
3079 * ADR 0012): user shadow → team shadow → filesystem built-in.
3080 * A non-archived ``user_skills`` row for that user at this slug
3081 * wins outright; otherwise the newest non-archived team-scope
3082 * row owned by any team the user is a member of wins;
3083 * otherwise the filesystem-canonical built-in is returned.
3084 * When omitted, the registry view (built-in only) is returned.
3085 */
3086 user_id?: string;
3087 };
3088 header?: never;
3089 path: {
3090 skill_name: string;
3091 };
3092 cookie?: never;
3093 };
3094 /**
3095 * Internal — fetch a skill (gateway → backend; user > team > built-in)
3096 * @description Service-to-service endpoint used by the Inference Gateway during
3097 * prompt assembly (Task C2 / ADR 0007). The user-facing
3098 * `GET /api/v1/skills/{name}` endpoint is gated by user-token auth
3099 * (`get_active_user`) and is inappropriate for the gateway's calls
3100 * — the gateway has no user. This route is gated by the shared
3101 * `X-LQ-AI-Gateway-Key` secret instead.
3102 *
3103 * Trust-domain separation: user-facing routes stay under user-token
3104 * auth; internal routes stay under shared-secret auth. The two
3105 * never mix on a single route.
3106 *
3107 * The optional ``user_id`` query param activates the D8.1b
3108 * resolution stack (user > team > built-in). Multi-team conflicts
3109 * resolve to the row with the most recent ``updated_at``. The
3110 * gateway's skill cache stays keyed on ``(name, user_id)``;
3111 * team-membership changes propagate via the existing TTL.
3112 *
3113 * The response shape matches `GET /api/v1/skills/{name}` (the
3114 * `Skill` schema) so the gateway-side client reuses the same
3115 * response model.
3116 */
3117 get: {
3118 parameters: {
3119 query?: {
3120 /**
3121 * @description When supplied, the resolver applies the D8.1b stack (per
3122 * ADR 0012): user shadow → team shadow → filesystem built-in.
3123 * A non-archived ``user_skills`` row for that user at this slug
3124 * wins outright; otherwise the newest non-archived team-scope
3125 * row owned by any team the user is a member of wins;
3126 * otherwise the filesystem-canonical built-in is returned.
3127 * When omitted, the registry view (built-in only) is returned.
3128 */
3129 user_id?: string;
3130 };
3131 header?: never;
3132 path: {
3133 skill_name: string;
3134 };
3135 cookie?: never;
3136 };
3137 requestBody?: never;
3138 responses: {
3139 /** @description Skill detail */
3140 200: {
3141 headers: {
3142 [name: string]: unknown;
3143 };
3144 content: {
3145 "application/json": components["schemas"]["Skill"];
3146 };
3147 };
3148 /** @description Missing or invalid `X-LQ-AI-Gateway-Key`. */
3149 401: {
3150 headers: {
3151 [name: string]: unknown;
3152 };
3153 content: {
3154 "application/json": components["schemas"]["Error"];
3155 };
3156 };
3157 /** @description No skill with that name is in the registry. */
3158 404: {
3159 headers: {
3160 [name: string]: unknown;
3161 };
3162 content: {
3163 "application/json": components["schemas"]["Error"];
3164 };
3165 };
3166 /**
3167 * @description Operator has not configured `LQ_AI_GATEWAY_KEY` on the
3168 * backend; service-to-service traffic is refused rather than
3169 * silently accepted.
3170 */
3171 500: {
3172 headers: {
3173 [name: string]: unknown;
3174 };
3175 content: {
3176 "application/json": components["schemas"]["Error"];
3177 };
3178 };
3179 };
3180 };
3181 put?: never;
3182 post?: never;
3183 delete?: never;
3184 options?: never;
3185 head?: never;
3186 patch?: never;
3187 trace?: never;
3188 };
3189 "/api/v1/internal/organization-profile": {
3190 parameters: {
3191 query?: never;
3192 header?: never;
3193 path?: never;
3194 cookie?: never;
3195 };
3196 /**
3197 * Internal — fetch the deployment's Organization Profile (gateway → backend)
3198 * @description Service-to-service endpoint used by the Inference Gateway during
3199 * prompt assembly (Task D4-coverage). Returns the Organization
3200 * Profile (PRD §3.12) as a `Skill`-shaped JSON body so the
3201 * gateway can plug it into the same prompt-assembler that
3202 * consumes user-attached skills. The synthesized frontmatter
3203 * carries `is_organization_profile: true` and
3204 * `use_organization_profile: false` so consumers identify the
3205 * Profile by metadata and the assembler never recursively
3206 * re-prepends the Profile to itself.
3207 *
3208 * Same trust posture as `/internal/skills/{name}`:
3209 * `X-LQ-AI-Gateway-Key` shared-secret auth (constant-time
3210 * compare); user-facing reads of the Profile use the bearer-
3211 * authed `/api/v1/organization-profile` route instead.
3212 */
3213 get: {
3214 parameters: {
3215 query?: never;
3216 header?: never;
3217 path?: never;
3218 cookie?: never;
3219 };
3220 requestBody?: never;
3221 responses: {
3222 /** @description Profile present; body is the `Skill`-shaped synthesized payload. */
3223 200: {
3224 headers: {
3225 [name: string]: unknown;
3226 };
3227 content: {
3228 "application/json": components["schemas"]["Skill"];
3229 };
3230 };
3231 /** @description Missing or invalid `X-LQ-AI-Gateway-Key`. */
3232 401: {
3233 headers: {
3234 [name: string]: unknown;
3235 };
3236 content: {
3237 "application/json": components["schemas"]["Error"];
3238 };
3239 };
3240 /**
3241 * @description No Profile is set on this deployment, or the Profile's body
3242 * is empty. The gateway treats this as "no Profile to
3243 * prepend" and proceeds with skill-only prompt assembly.
3244 */
3245 404: {
3246 headers: {
3247 [name: string]: unknown;
3248 };
3249 content: {
3250 "application/json": components["schemas"]["Error"];
3251 };
3252 };
3253 /**
3254 * @description Operator has not configured `LQ_AI_GATEWAY_KEY` on the
3255 * backend; service-to-service traffic is refused rather than
3256 * silently accepted.
3257 */
3258 500: {
3259 headers: {
3260 [name: string]: unknown;
3261 };
3262 content: {
3263 "application/json": components["schemas"]["Error"];
3264 };
3265 };
3266 };
3267 };
3268 put?: never;
3269 post?: never;
3270 delete?: never;
3271 options?: never;
3272 head?: never;
3273 patch?: never;
3274 trace?: never;
3275 };
3276 "/api/v1/files": {
3277 parameters: {
3278 query?: never;
3279 header?: never;
3280 path?: never;
3281 cookie?: never;
3282 };
3283 get?: never;
3284 put?: never;
3285 /**
3286 * Upload a file
3287 * @description Streaming multipart upload. The body is streamed to MinIO without
3288 * loading the whole file into memory; a SHA-256 is computed as bytes
3289 * flow past, and metadata is persisted with `ingestion_status='pending'`.
3290 * The document pipeline (Task C5) picks the row up asynchronously
3291 * and flips status through `processing` → `ready` (or `failed`).
3292 *
3293 * Per-request size limit: `LQ_AI_MAX_UPLOAD_SIZE_MB` (default 100 MB
3294 * per Task C4). Exceeding it returns 413 with `code=payload_too_large`.
3295 *
3296 * `project_id` is accepted for forward compatibility (it appears in
3297 * the multipart body) but is currently ignored; project attachment
3298 * lands in C7. Files always start unattached and are linked to a
3299 * project via `POST /api/v1/projects/{project_id}/files` (C7).
3300 */
3301 post: {
3302 parameters: {
3303 query?: never;
3304 header?: never;
3305 path?: never;
3306 cookie?: never;
3307 };
3308 requestBody: {
3309 content: {
3310 "multipart/form-data": {
3311 /** Format: binary */
3312 file: string;
3313 /** Format: uuid */
3314 project_id?: string | null;
3315 };
3316 };
3317 };
3318 responses: {
3319 /** @description File uploaded; ingestion may be async. */
3320 201: {
3321 headers: {
3322 [name: string]: unknown;
3323 };
3324 content: {
3325 "application/json": components["schemas"]["File"];
3326 };
3327 };
3328 /** @description Validation error (e.g., missing filename on multipart part). */
3329 400: {
3330 headers: {
3331 [name: string]: unknown;
3332 };
3333 content: {
3334 "application/json": components["schemas"]["Error"];
3335 };
3336 };
3337 /** @description Missing or invalid bearer token. */
3338 401: {
3339 headers: {
3340 [name: string]: unknown;
3341 };
3342 content: {
3343 "application/json": components["schemas"]["Error"];
3344 };
3345 };
3346 /** @description User must change their password before uploading (B2 gate). */
3347 403: {
3348 headers: {
3349 [name: string]: unknown;
3350 };
3351 content: {
3352 "application/json": components["schemas"]["Error"];
3353 };
3354 };
3355 /**
3356 * @description Uploaded body exceeds the per-request size cap
3357 * (`LQ_AI_MAX_UPLOAD_SIZE_MB`, default 100 MB). The response
3358 * body's `details` carries `limit_bytes` and `received_bytes`.
3359 */
3360 413: {
3361 headers: {
3362 [name: string]: unknown;
3363 };
3364 content: {
3365 "application/json": components["schemas"]["Error"];
3366 };
3367 };
3368 };
3369 };
3370 delete?: never;
3371 options?: never;
3372 head?: never;
3373 patch?: never;
3374 trace?: never;
3375 };
3376 "/api/v1/files/{file_id}": {
3377 parameters: {
3378 query?: never;
3379 header?: never;
3380 path: {
3381 file_id: string;
3382 };
3383 cookie?: never;
3384 };
3385 /** Get file metadata */
3386 get: {
3387 parameters: {
3388 query?: never;
3389 header?: never;
3390 path: {
3391 file_id: string;
3392 };
3393 cookie?: never;
3394 };
3395 requestBody?: never;
3396 responses: {
3397 /** @description File metadata. */
3398 200: {
3399 headers: {
3400 [name: string]: unknown;
3401 };
3402 content: {
3403 "application/json": components["schemas"]["File"];
3404 };
3405 };
3406 /** @description `file_id` is not a valid UUID. */
3407 400: {
3408 headers: {
3409 [name: string]: unknown;
3410 };
3411 content: {
3412 "application/json": components["schemas"]["Error"];
3413 };
3414 };
3415 /** @description Missing or invalid bearer token. */
3416 401: {
3417 headers: {
3418 [name: string]: unknown;
3419 };
3420 content: {
3421 "application/json": components["schemas"]["Error"];
3422 };
3423 };
3424 /**
3425 * @description File does not exist, has been soft-deleted, or is owned by a
3426 * different user. The cross-user case returns 404 (not 403) to
3427 * avoid leaking existence information.
3428 */
3429 404: {
3430 headers: {
3431 [name: string]: unknown;
3432 };
3433 content: {
3434 "application/json": components["schemas"]["Error"];
3435 };
3436 };
3437 };
3438 };
3439 put?: never;
3440 post?: never;
3441 /**
3442 * Soft-delete a file
3443 * @description Sets `deleted_at` on the row; the MinIO object is left in place
3444 * and reaped later by D6 (per-user export+delete) or a future GC
3445 * sweep, per `docs/adr/0005-file-storage-soft-delete-and-key-scheme.md`.
3446 * Idempotent: deleting an already-soft-deleted or missing file
3447 * returns 404.
3448 */
3449 delete: {
3450 parameters: {
3451 query?: never;
3452 header?: never;
3453 path: {
3454 file_id: string;
3455 };
3456 cookie?: never;
3457 };
3458 requestBody?: never;
3459 responses: {
3460 /** @description File soft-deleted (no body). */
3461 204: {
3462 headers: {
3463 [name: string]: unknown;
3464 };
3465 content?: never;
3466 };
3467 /** @description `file_id` is not a valid UUID. */
3468 400: {
3469 headers: {
3470 [name: string]: unknown;
3471 };
3472 content: {
3473 "application/json": components["schemas"]["Error"];
3474 };
3475 };
3476 /** @description Missing or invalid bearer token. */
3477 401: {
3478 headers: {
3479 [name: string]: unknown;
3480 };
3481 content: {
3482 "application/json": components["schemas"]["Error"];
3483 };
3484 };
3485 /** @description File missing, soft-deleted, or owned by a different user. */
3486 404: {
3487 headers: {
3488 [name: string]: unknown;
3489 };
3490 content: {
3491 "application/json": components["schemas"]["Error"];
3492 };
3493 };
3494 };
3495 };
3496 options?: never;
3497 head?: never;
3498 patch?: never;
3499 trace?: never;
3500 };
3501 "/api/v1/files/{file_id}/content": {
3502 parameters: {
3503 query?: never;
3504 header?: never;
3505 path: {
3506 file_id: string;
3507 };
3508 cookie?: never;
3509 };
3510 /**
3511 * Download original file content
3512 * @description Streams the original bytes back as the upload arrived. The
3513 * `Content-Type` is the stored MIME type, `Content-Length` is the
3514 * stored size in bytes, and `Content-Disposition` is
3515 * `attachment; filename="..."` with an RFC 5987 `filename*=UTF-8''...`
3516 * for non-ASCII filenames.
3517 */
3518 get: {
3519 parameters: {
3520 query?: never;
3521 header?: never;
3522 path: {
3523 file_id: string;
3524 };
3525 cookie?: never;
3526 };
3527 requestBody?: never;
3528 responses: {
3529 /** @description File bytes (streaming). */
3530 200: {
3531 headers: {
3532 /**
3533 * @description `attachment; filename="..."` (RFC 6266); for non-ASCII
3534 * filenames an additional RFC 5987 `filename*=UTF-8''...`
3535 * parameter is included.
3536 */
3537 "Content-Disposition"?: string;
3538 /** @description Always `nosniff`. */
3539 "X-Content-Type-Options"?: string;
3540 [name: string]: unknown;
3541 };
3542 content: {
3543 "application/octet-stream": unknown;
3544 };
3545 };
3546 /** @description `file_id` is not a valid UUID. */
3547 400: {
3548 headers: {
3549 [name: string]: unknown;
3550 };
3551 content: {
3552 "application/json": components["schemas"]["Error"];
3553 };
3554 };
3555 /** @description Missing or invalid bearer token. */
3556 401: {
3557 headers: {
3558 [name: string]: unknown;
3559 };
3560 content: {
3561 "application/json": components["schemas"]["Error"];
3562 };
3563 };
3564 /** @description File missing, soft-deleted, or owned by a different user. */
3565 404: {
3566 headers: {
3567 [name: string]: unknown;
3568 };
3569 content: {
3570 "application/json": components["schemas"]["Error"];
3571 };
3572 };
3573 };
3574 };
3575 put?: never;
3576 post?: never;
3577 delete?: never;
3578 options?: never;
3579 head?: never;
3580 patch?: never;
3581 trace?: never;
3582 };
3583 "/api/v1/knowledge-bases": {
3584 parameters: {
3585 query?: never;
3586 header?: never;
3587 path?: never;
3588 cookie?: never;
3589 };
3590 /** List the caller's knowledge bases */
3591 get: {
3592 parameters: {
3593 query?: {
3594 /** @description When true, list archived KBs. Default (omit) lists active only. */
3595 archived?: boolean;
3596 /** @description Filter to KBs associated with this project. */
3597 project_id?: string;
3598 };
3599 header?: never;
3600 path?: never;
3601 cookie?: never;
3602 };
3603 requestBody?: never;
3604 responses: {
3605 /** @description KBs accessible to the user */
3606 200: {
3607 headers: {
3608 [name: string]: unknown;
3609 };
3610 content: {
3611 "application/json": components["schemas"]["KnowledgeBase"][];
3612 };
3613 };
3614 };
3615 };
3616 put?: never;
3617 /** Create a knowledge base */
3618 post: {
3619 parameters: {
3620 query?: never;
3621 header?: never;
3622 path?: never;
3623 cookie?: never;
3624 };
3625 requestBody: {
3626 content: {
3627 "application/json": components["schemas"]["KnowledgeBaseCreate"];
3628 };
3629 };
3630 responses: {
3631 /** @description KB created */
3632 201: {
3633 headers: {
3634 [name: string]: unknown;
3635 };
3636 content: {
3637 "application/json": components["schemas"]["KnowledgeBase"];
3638 };
3639 };
3640 /** @description Project (if `project_id` was provided) not found. */
3641 404: {
3642 headers: {
3643 [name: string]: unknown;
3644 };
3645 content: {
3646 "application/json": components["schemas"]["Error"];
3647 };
3648 };
3649 };
3650 };
3651 delete?: never;
3652 options?: never;
3653 head?: never;
3654 patch?: never;
3655 trace?: never;
3656 };
3657 "/api/v1/knowledge-bases/{kb_id}": {
3658 parameters: {
3659 query?: never;
3660 header?: never;
3661 path: {
3662 kb_id: string;
3663 };
3664 cookie?: never;
3665 };
3666 /** Fetch a single knowledge base with metadata + file count */
3667 get: {
3668 parameters: {
3669 query?: never;
3670 header?: never;
3671 path: {
3672 kb_id: string;
3673 };
3674 cookie?: never;
3675 };
3676 requestBody?: never;
3677 responses: {
3678 /** @description Knowledge base */
3679 200: {
3680 headers: {
3681 [name: string]: unknown;
3682 };
3683 content: {
3684 "application/json": components["schemas"]["KnowledgeBase"];
3685 };
3686 };
3687 /** @description Not found */
3688 404: {
3689 headers: {
3690 [name: string]: unknown;
3691 };
3692 content: {
3693 "application/json": components["schemas"]["Error"];
3694 };
3695 };
3696 };
3697 };
3698 put?: never;
3699 post?: never;
3700 /** Soft-delete a knowledge base */
3701 delete: {
3702 parameters: {
3703 query?: never;
3704 header?: never;
3705 path: {
3706 kb_id: string;
3707 };
3708 cookie?: never;
3709 };
3710 requestBody?: never;
3711 responses: {
3712 /** @description Soft-deleted */
3713 204: {
3714 headers: {
3715 [name: string]: unknown;
3716 };
3717 content?: never;
3718 };
3719 /** @description Not found */
3720 404: {
3721 headers: {
3722 [name: string]: unknown;
3723 };
3724 content: {
3725 "application/json": components["schemas"]["Error"];
3726 };
3727 };
3728 };
3729 };
3730 options?: never;
3731 head?: never;
3732 /** Partial update — name, description, project association, alpha, or archived */
3733 patch: {
3734 parameters: {
3735 query?: never;
3736 header?: never;
3737 path: {
3738 kb_id: string;
3739 };
3740 cookie?: never;
3741 };
3742 requestBody: {
3743 content: {
3744 "application/json": components["schemas"]["KnowledgeBaseUpdate"];
3745 };
3746 };
3747 responses: {
3748 /** @description Updated */
3749 200: {
3750 headers: {
3751 [name: string]: unknown;
3752 };
3753 content: {
3754 "application/json": components["schemas"]["KnowledgeBase"];
3755 };
3756 };
3757 /** @description Not found */
3758 404: {
3759 headers: {
3760 [name: string]: unknown;
3761 };
3762 content: {
3763 "application/json": components["schemas"]["Error"];
3764 };
3765 };
3766 };
3767 };
3768 trace?: never;
3769 };
3770 "/api/v1/knowledge-bases/{kb_id}/files": {
3771 parameters: {
3772 query?: never;
3773 header?: never;
3774 path: {
3775 kb_id: string;
3776 };
3777 cookie?: never;
3778 };
3779 /**
3780 * List the files attached to a knowledge base
3781 * @description Returns the files currently attached to the KB (the `File`
3782 * shape from `GET /files/{id}` plus the `attached_at` timestamp
3783 * from the join). Owner-scoped — cross-user / unknown KB id
3784 * returns 404. Soft-deleted files are excluded. Sorted by
3785 * `attached_at DESC` so the most recent uploads surface first.
3786 *
3787 * Drives the Knowledge surface's detail-page document list per
3788 * the M1 frontend design spec (Wave C).
3789 */
3790 get: {
3791 parameters: {
3792 query?: never;
3793 header?: never;
3794 path: {
3795 kb_id: string;
3796 };
3797 cookie?: never;
3798 };
3799 requestBody?: never;
3800 responses: {
3801 /** @description List of attached files */
3802 200: {
3803 headers: {
3804 [name: string]: unknown;
3805 };
3806 content: {
3807 "application/json": components["schemas"]["KBFile"][];
3808 };
3809 };
3810 /** @description KB not found / cross-user */
3811 404: {
3812 headers: {
3813 [name: string]: unknown;
3814 };
3815 content: {
3816 "application/json": components["schemas"]["Error"];
3817 };
3818 };
3819 };
3820 };
3821 put?: never;
3822 /**
3823 * Attach a file to a knowledge base
3824 * @description The file must be owned by the caller and have completed the C5
3825 * ingest pipeline (`ingestion_status='ready'`). After successful
3826 * attach the backend enqueues an embed job; the query path also
3827 * covers any unembedded chunks lazily (embed-on-read).
3828 */
3829 post: {
3830 parameters: {
3831 query?: never;
3832 header?: never;
3833 path: {
3834 kb_id: string;
3835 };
3836 cookie?: never;
3837 };
3838 requestBody: {
3839 content: {
3840 "application/json": {
3841 /** Format: uuid */
3842 file_id: string;
3843 };
3844 };
3845 };
3846 responses: {
3847 /** @description Attached */
3848 204: {
3849 headers: {
3850 [name: string]: unknown;
3851 };
3852 content?: never;
3853 };
3854 /** @description file_id is malformed */
3855 400: {
3856 headers: {
3857 [name: string]: unknown;
3858 };
3859 content: {
3860 "application/json": components["schemas"]["Error"];
3861 };
3862 };
3863 /** @description KB or file not found / cross-user */
3864 404: {
3865 headers: {
3866 [name: string]: unknown;
3867 };
3868 content: {
3869 "application/json": components["schemas"]["Error"];
3870 };
3871 };
3872 /** @description File already attached to this KB (idempotency-violating POST) */
3873 409: {
3874 headers: {
3875 [name: string]: unknown;
3876 };
3877 content: {
3878 "application/json": components["schemas"]["Error"];
3879 };
3880 };
3881 /** @description File is not in `ingestion_status='ready'` */
3882 422: {
3883 headers: {
3884 [name: string]: unknown;
3885 };
3886 content: {
3887 "application/json": components["schemas"]["Error"];
3888 };
3889 };
3890 };
3891 };
3892 delete?: never;
3893 options?: never;
3894 head?: never;
3895 patch?: never;
3896 trace?: never;
3897 };
3898 "/api/v1/knowledge-bases/{kb_id}/files/{file_id}": {
3899 parameters: {
3900 query?: never;
3901 header?: never;
3902 path: {
3903 kb_id: string;
3904 file_id: string;
3905 };
3906 cookie?: never;
3907 };
3908 get?: never;
3909 put?: never;
3910 post?: never;
3911 /** Detach a file from a knowledge base */
3912 delete: {
3913 parameters: {
3914 query?: never;
3915 header?: never;
3916 path: {
3917 kb_id: string;
3918 file_id: string;
3919 };
3920 cookie?: never;
3921 };
3922 requestBody?: never;
3923 responses: {
3924 /** @description Detached */
3925 204: {
3926 headers: {
3927 [name: string]: unknown;
3928 };
3929 content?: never;
3930 };
3931 /** @description KB not found, or the file is not attached */
3932 404: {
3933 headers: {
3934 [name: string]: unknown;
3935 };
3936 content: {
3937 "application/json": components["schemas"]["Error"];
3938 };
3939 };
3940 };
3941 };
3942 options?: never;
3943 head?: never;
3944 patch?: never;
3945 trace?: never;
3946 };
3947 "/api/v1/knowledge-bases/{kb_id}/query": {
3948 parameters: {
3949 query?: never;
3950 header?: never;
3951 path: {
3952 kb_id: string;
3953 };
3954 cookie?: never;
3955 };
3956 get?: never;
3957 put?: never;
3958 /**
3959 * Hybrid (vector + FTS) search over the knowledge base
3960 * @description Per ADR 0008 the score is `(1 - alpha) * vector + alpha * fts`,
3961 * where each side is min-max normalized across the union of the
3962 * per-side candidate sets (each side returns up to `top_k * 4`
3963 * candidates). `hybrid_alpha` defaults to the KB's stored value
3964 * (which itself defaults to 0.5).
3965 *
3966 * Embed-on-read: if the gateway's `/v1/embeddings` is unavailable
3967 * the query degrades gracefully to FTS-only ranking; the response
3968 * carries `score_components.vector = 0` for every result in that
3969 * case so the client can detect the degraded mode.
3970 */
3971 post: {
3972 parameters: {
3973 query?: never;
3974 header?: never;
3975 path: {
3976 kb_id: string;
3977 };
3978 cookie?: never;
3979 };
3980 requestBody: {
3981 content: {
3982 "application/json": components["schemas"]["KBQueryRequest"];
3983 };
3984 };
3985 responses: {
3986 /** @description Search results */
3987 200: {
3988 headers: {
3989 [name: string]: unknown;
3990 };
3991 content: {
3992 "application/json": components["schemas"]["KBQueryResponse"];
3993 };
3994 };
3995 /** @description KB not found / cross-user */
3996 404: {
3997 headers: {
3998 [name: string]: unknown;
3999 };
4000 content: {
4001 "application/json": components["schemas"]["Error"];
4002 };
4003 };
4004 };
4005 };
4006 delete?: never;
4007 options?: never;
4008 head?: never;
4009 patch?: never;
4010 trace?: never;
4011 };
4012 "/api/v1/organization-profile": {
4013 parameters: {
4014 query?: never;
4015 header?: never;
4016 path?: never;
4017 cookie?: never;
4018 };
4019 /**
4020 * Get the deployment's Organization Profile (singleton)
4021 * @description Bearer-authenticated. Readable by every authenticated user
4022 * (PRD §1.3 transparency: users are entitled to see what's
4023 * shaping their output). Returns `content_md=""` plus null
4024 * timestamps when no admin has set a Profile yet — the read
4025 * path never 404s.
4026 */
4027 get: {
4028 parameters: {
4029 query?: never;
4030 header?: never;
4031 path?: never;
4032 cookie?: never;
4033 };
4034 requestBody?: never;
4035 responses: {
4036 /** @description Organization Profile (or empty placeholder) */
4037 200: {
4038 headers: {
4039 [name: string]: unknown;
4040 };
4041 content: {
4042 "application/json": {
4043 content_md: string;
4044 /** Format: date-time */
4045 updated_at?: string | null;
4046 /** Format: uuid */
4047 updated_by?: string | null;
4048 };
4049 };
4050 };
4051 };
4052 };
4053 /**
4054 * Update the Organization Profile (admin only)
4055 * @description Admin-only upsert: idempotent against the singleton row. Empty
4056 * `content_md` is allowed (operators may want to clear without
4057 * deleting). Audit-logged as `organization_profile.updated`.
4058 */
4059 put: {
4060 parameters: {
4061 query?: never;
4062 header?: never;
4063 path?: never;
4064 cookie?: never;
4065 };
4066 requestBody: {
4067 content: {
4068 "application/json": {
4069 content_md: string;
4070 };
4071 };
4072 };
4073 responses: {
4074 /** @description Updated */
4075 200: {
4076 headers: {
4077 [name: string]: unknown;
4078 };
4079 content: {
4080 "application/json": {
4081 content_md: string;
4082 /** Format: date-time */
4083 updated_at?: string | null;
4084 /** Format: uuid */
4085 updated_by?: string | null;
4086 };
4087 };
4088 };
4089 /** @description Bearer token missing or invalid */
4090 401: {
4091 headers: {
4092 [name: string]: unknown;
4093 };
4094 content?: never;
4095 };
4096 /** @description Caller is not an admin */
4097 403: {
4098 headers: {
4099 [name: string]: unknown;
4100 };
4101 content?: never;
4102 };
4103 };
4104 };
4105 post?: never;
4106 delete?: never;
4107 options?: never;
4108 head?: never;
4109 patch?: never;
4110 trace?: never;
4111 };
4112 "/api/v1/organization-profile/raw": {
4113 parameters: {
4114 query?: never;
4115 header?: never;
4116 path?: never;
4117 cookie?: never;
4118 };
4119 /**
4120 * Get the Profile body as raw Markdown
4121 * @description Bearer-authenticated. Convenience endpoint for the Skill
4122 * Inspector (§3.4) and any UI surface that wants to render the
4123 * Profile as Markdown without JSON-decoding the body. Returns
4124 * an empty 200 when no Profile is set.
4125 */
4126 get: {
4127 parameters: {
4128 query?: never;
4129 header?: never;
4130 path?: never;
4131 cookie?: never;
4132 };
4133 requestBody?: never;
4134 responses: {
4135 /** @description Profile body */
4136 200: {
4137 headers: {
4138 [name: string]: unknown;
4139 };
4140 content: {
4141 "text/markdown": unknown;
4142 };
4143 };
4144 };
4145 };
4146 put?: never;
4147 post?: never;
4148 delete?: never;
4149 options?: never;
4150 head?: never;
4151 patch?: never;
4152 trace?: never;
4153 };
4154 "/api/v1/saved-prompts": {
4155 parameters: {
4156 query?: never;
4157 header?: never;
4158 path?: never;
4159 cookie?: never;
4160 };
4161 get: {
4162 parameters: {
4163 query?: never;
4164 header?: never;
4165 path?: never;
4166 cookie?: never;
4167 };
4168 requestBody?: never;
4169 responses: {
4170 /** @description User's saved prompts */
4171 200: {
4172 headers: {
4173 [name: string]: unknown;
4174 };
4175 content: {
4176 "application/json": components["schemas"]["SavedPrompt"][];
4177 };
4178 };
4179 };
4180 };
4181 put?: never;
4182 post: {
4183 parameters: {
4184 query?: never;
4185 header?: never;
4186 path?: never;
4187 cookie?: never;
4188 };
4189 requestBody: {
4190 content: {
4191 "application/json": {
4192 name: string;
4193 prompt_text: string;
4194 tags?: string[];
4195 };
4196 };
4197 };
4198 responses: {
4199 /** @description Created */
4200 201: {
4201 headers: {
4202 [name: string]: unknown;
4203 };
4204 content: {
4205 "application/json": components["schemas"]["SavedPrompt"];
4206 };
4207 };
4208 };
4209 };
4210 delete?: never;
4211 options?: never;
4212 head?: never;
4213 patch?: never;
4214 trace?: never;
4215 };
4216 "/api/v1/saved-prompts/{prompt_id}": {
4217 parameters: {
4218 query?: never;
4219 header?: never;
4220 path: {
4221 prompt_id: string;
4222 };
4223 cookie?: never;
4224 };
4225 /** Fetch a single saved prompt */
4226 get: {
4227 parameters: {
4228 query?: never;
4229 header?: never;
4230 path: {
4231 prompt_id: string;
4232 };
4233 cookie?: never;
4234 };
4235 requestBody?: never;
4236 responses: {
4237 /** @description Saved prompt */
4238 200: {
4239 headers: {
4240 [name: string]: unknown;
4241 };
4242 content: {
4243 "application/json": components["schemas"]["SavedPrompt"];
4244 };
4245 };
4246 /** @description Not found */
4247 404: {
4248 headers: {
4249 [name: string]: unknown;
4250 };
4251 content?: never;
4252 };
4253 };
4254 };
4255 put?: never;
4256 post?: never;
4257 delete: {
4258 parameters: {
4259 query?: never;
4260 header?: never;
4261 path: {
4262 prompt_id: string;
4263 };
4264 cookie?: never;
4265 };
4266 requestBody?: never;
4267 responses: {
4268 /** @description Deleted */
4269 204: {
4270 headers: {
4271 [name: string]: unknown;
4272 };
4273 content?: never;
4274 };
4275 };
4276 };
4277 options?: never;
4278 head?: never;
4279 patch: {
4280 parameters: {
4281 query?: never;
4282 header?: never;
4283 path: {
4284 prompt_id: string;
4285 };
4286 cookie?: never;
4287 };
4288 requestBody: {
4289 content: {
4290 "application/json": {
4291 name?: string;
4292 prompt_text?: string;
4293 tags?: string[];
4294 };
4295 };
4296 };
4297 responses: {
4298 /** @description Updated */
4299 200: {
4300 headers: {
4301 [name: string]: unknown;
4302 };
4303 content: {
4304 "application/json": components["schemas"]["SavedPrompt"];
4305 };
4306 };
4307 };
4308 };
4309 trace?: never;
4310 };
4311 "/api/v1/admin/audit-log": {
4312 parameters: {
4313 query?: never;
4314 header?: never;
4315 path?: never;
4316 cookie?: never;
4317 };
4318 /** Query audit log (admin only) */
4319 get: {
4320 parameters: {
4321 query?: {
4322 user_id?: string;
4323 action?: string;
4324 resource_type?: string;
4325 privilege_marked?: boolean;
4326 routed_inference_tier?: 1 | 2 | 3 | 4 | 5;
4327 from?: string;
4328 to?: string;
4329 cursor?: string;
4330 };
4331 header?: never;
4332 path?: never;
4333 cookie?: never;
4334 };
4335 requestBody?: never;
4336 responses: {
4337 /** @description Audit log entries */
4338 200: {
4339 headers: {
4340 [name: string]: unknown;
4341 };
4342 content: {
4343 "application/json": {
4344 items: components["schemas"]["AuditLogEntry"][];
4345 next_cursor: string | null;
4346 };
4347 };
4348 };
4349 };
4350 };
4351 put?: never;
4352 post?: never;
4353 delete?: never;
4354 options?: never;
4355 head?: never;
4356 patch?: never;
4357 trace?: never;
4358 };
4359 "/api/v1/playbooks": {
4360 parameters: {
4361 query?: never;
4362 header?: never;
4363 path?: never;
4364 cookie?: never;
4365 };
4366 /**
4367 * List playbooks visible to the caller (M3-A4)
4368 * @description Returns playbooks the caller can see: admins see all;
4369 * non-admins see playbooks they authored or built-in playbooks
4370 * (``created_by IS NULL``). Soft-deleted rows (``deleted_at IS NOT
4371 * NULL``) are excluded for everyone, including admins. Positions
4372 * are NOT inlined; clients call ``GET /api/v1/playbooks/{id}`` to
4373 * fetch them.
4374 */
4375 get: {
4376 parameters: {
4377 query?: never;
4378 header?: never;
4379 path?: never;
4380 cookie?: never;
4381 };
4382 requestBody?: never;
4383 responses: {
4384 /** @description List of playbooks (positions empty). */
4385 200: {
4386 headers: {
4387 [name: string]: unknown;
4388 };
4389 content: {
4390 "application/json": components["schemas"]["Playbook"][];
4391 };
4392 };
4393 /** @description Not authenticated. */
4394 401: {
4395 headers: {
4396 [name: string]: unknown;
4397 };
4398 content?: never;
4399 };
4400 };
4401 };
4402 put?: never;
4403 /**
4404 * Create a new playbook (M3-A6)
4405 * @description Creates a playbook owned by the caller (``created_by =
4406 * caller.id``). Admins do not get to mint built-ins through this
4407 * endpoint — built-ins (``created_by IS NULL``) ship via seed
4408 * migration only. The Inference-Tier and audit posture matches
4409 * other write endpoints on the API.
4410 */
4411 post: {
4412 parameters: {
4413 query?: never;
4414 header?: never;
4415 path?: never;
4416 cookie?: never;
4417 };
4418 requestBody: {
4419 content: {
4420 "application/json": components["schemas"]["PlaybookCreate"];
4421 };
4422 };
4423 responses: {
4424 /** @description Playbook created. Body includes the assembled positions. */
4425 201: {
4426 headers: {
4427 [name: string]: unknown;
4428 };
4429 content: {
4430 "application/json": components["schemas"]["Playbook"];
4431 };
4432 };
4433 /** @description Not authenticated. */
4434 401: {
4435 headers: {
4436 [name: string]: unknown;
4437 };
4438 content?: never;
4439 };
4440 /** @description Validation error (e.g., unknown field, invalid severity enum). */
4441 422: {
4442 headers: {
4443 [name: string]: unknown;
4444 };
4445 content?: never;
4446 };
4447 };
4448 };
4449 delete?: never;
4450 options?: never;
4451 head?: never;
4452 patch?: never;
4453 trace?: never;
4454 };
4455 "/api/v1/playbooks/{playbook_id}": {
4456 parameters: {
4457 query?: never;
4458 header?: never;
4459 path: {
4460 playbook_id: string;
4461 };
4462 cookie?: never;
4463 };
4464 /**
4465 * Get a playbook with its full position list (M3-A4)
4466 * @description Returns the playbook header + positions + fallback tiers.
4467 * Visibility matches ``GET /api/v1/playbooks``. 404 (not 403)
4468 * on unauthorized access, including for soft-deleted rows.
4469 */
4470 get: {
4471 parameters: {
4472 query?: never;
4473 header?: never;
4474 path: {
4475 playbook_id: string;
4476 };
4477 cookie?: never;
4478 };
4479 requestBody?: never;
4480 responses: {
4481 /** @description Full playbook with positions. */
4482 200: {
4483 headers: {
4484 [name: string]: unknown;
4485 };
4486 content: {
4487 "application/json": components["schemas"]["Playbook"];
4488 };
4489 };
4490 /** @description Not authenticated. */
4491 401: {
4492 headers: {
4493 [name: string]: unknown;
4494 };
4495 content?: never;
4496 };
4497 /** @description Playbook not found, or caller is not authorized to see it. */
4498 404: {
4499 headers: {
4500 [name: string]: unknown;
4501 };
4502 content?: never;
4503 };
4504 };
4505 };
4506 put?: never;
4507 post?: never;
4508 /**
4509 * Soft-delete a playbook (M3-A6)
4510 * @description Sets ``deleted_at`` on the row. The ``playbook_positions`` rows
4511 * remain so historical executions still resolve their position
4512 * references. Authorization mirrors PATCH: built-ins are 403;
4513 * non-built-ins require admin OR ownership; cross-user / already-
4514 * deleted is 404. Subsequent reads (GET, list) treat the row as
4515 * absent.
4516 */
4517 delete: {
4518 parameters: {
4519 query?: never;
4520 header?: never;
4521 path: {
4522 playbook_id: string;
4523 };
4524 cookie?: never;
4525 };
4526 requestBody?: never;
4527 responses: {
4528 /** @description Playbook soft-deleted. */
4529 204: {
4530 headers: {
4531 [name: string]: unknown;
4532 };
4533 content?: never;
4534 };
4535 /** @description Not authenticated. */
4536 401: {
4537 headers: {
4538 [name: string]: unknown;
4539 };
4540 content?: never;
4541 };
4542 /** @description Built-in playbooks cannot be deleted through this endpoint. */
4543 403: {
4544 headers: {
4545 [name: string]: unknown;
4546 };
4547 content?: never;
4548 };
4549 /** @description Playbook not found, already soft-deleted, or not authorized. */
4550 404: {
4551 headers: {
4552 [name: string]: unknown;
4553 };
4554 content?: never;
4555 };
4556 };
4557 };
4558 options?: never;
4559 head?: never;
4560 /**
4561 * Update a playbook header; optionally replace positions (M3-A6)
4562 * @description Patches the playbook. Authorization:
4563 *
4564 * * Built-in playbooks (``created_by IS NULL``) are 403 to
4565 * everyone, including admins — operators fork-then-edit. This
4566 * keeps shared deployment-level content immutable to a single
4567 * operator's mistake.
4568 * * Non-built-in playbooks can be patched by the playbook's author
4569 * OR by an admin. Cross-user / soft-deleted cases return 404.
4570 *
4571 * Position replacement is atomic: when ``positions`` is supplied,
4572 * every existing position row for this playbook is deleted and
4573 * the new list is inserted in a single transaction.
4574 */
4575 patch: {
4576 parameters: {
4577 query?: never;
4578 header?: never;
4579 path: {
4580 playbook_id: string;
4581 };
4582 cookie?: never;
4583 };
4584 requestBody: {
4585 content: {
4586 "application/json": components["schemas"]["PlaybookUpdate"];
4587 };
4588 };
4589 responses: {
4590 /** @description Playbook updated; response is the fresh full shape. */
4591 200: {
4592 headers: {
4593 [name: string]: unknown;
4594 };
4595 content: {
4596 "application/json": components["schemas"]["Playbook"];
4597 };
4598 };
4599 /** @description Not authenticated. */
4600 401: {
4601 headers: {
4602 [name: string]: unknown;
4603 };
4604 content?: never;
4605 };
4606 /** @description Built-in playbooks cannot be edited through this endpoint. */
4607 403: {
4608 headers: {
4609 [name: string]: unknown;
4610 };
4611 content?: never;
4612 };
4613 /** @description Playbook not found, soft-deleted, or not authorized. */
4614 404: {
4615 headers: {
4616 [name: string]: unknown;
4617 };
4618 content?: never;
4619 };
4620 /** @description Validation error. */
4621 422: {
4622 headers: {
4623 [name: string]: unknown;
4624 };
4625 content?: never;
4626 };
4627 };
4628 };
4629 trace?: never;
4630 };
4631 "/api/v1/playbooks/easy": {
4632 parameters: {
4633 query?: never;
4634 header?: never;
4635 path?: never;
4636 cookie?: never;
4637 };
4638 get?: never;
4639 put?: never;
4640 /**
4641 * Start an Easy Playbook generation run (M3-A6)
4642 * @description Kicks off the Easy Playbook auto-generation pipeline against
4643 * the supplied document corpus. Creates an
4644 * ``EasyPlaybookGeneration`` row at ``status='pending'`` and
4645 * enqueues the ARQ worker job on the ``arq:m3a6`` queue (the
4646 * dedicated M3-A6 worker — see ``app/workers/arq_setup.py``).
4647 * Returns 202 immediately; the wizard's Step 2 polls
4648 * ``GET /api/v1/playbooks/easy/{generation_id}`` until status
4649 * reaches a terminal value.
4650 *
4651 * Authorization: caller must own every document in
4652 * ``document_ids`` (admins bypass). Cross-user / missing /
4653 * soft-deleted documents collapse into 404.
4654 *
4655 * Generation completion does NOT mean the playbook is fit for
4656 * use; the wizard's Step 3 inline editor is where the user-
4657 * attorney validates and edits before the final save (which
4658 * POSTs to ``/api/v1/playbooks`` like any other playbook).
4659 */
4660 post: {
4661 parameters: {
4662 query?: never;
4663 header?: never;
4664 path?: never;
4665 cookie?: never;
4666 };
4667 requestBody: {
4668 content: {
4669 "application/json": components["schemas"]["EasyPlaybookGenerationCreate"];
4670 };
4671 };
4672 responses: {
4673 /** @description Generation scheduled; row created at status `pending`. */
4674 202: {
4675 headers: {
4676 [name: string]: unknown;
4677 };
4678 content: {
4679 "application/json": components["schemas"]["EasyPlaybookGeneration"];
4680 };
4681 };
4682 /** @description Not authenticated. */
4683 401: {
4684 headers: {
4685 [name: string]: unknown;
4686 };
4687 content?: never;
4688 };
4689 /** @description One or more documents not found / not authorized. */
4690 404: {
4691 headers: {
4692 [name: string]: unknown;
4693 };
4694 content?: never;
4695 };
4696 /** @description Validation error (empty ``document_ids``, unknown field, etc.). */
4697 422: {
4698 headers: {
4699 [name: string]: unknown;
4700 };
4701 content?: never;
4702 };
4703 };
4704 };
4705 delete?: never;
4706 options?: never;
4707 head?: never;
4708 patch?: never;
4709 trace?: never;
4710 };
4711 "/api/v1/playbooks/easy/{generation_id}": {
4712 parameters: {
4713 query?: never;
4714 header?: never;
4715 path: {
4716 generation_id: string;
4717 };
4718 cookie?: never;
4719 };
4720 /**
4721 * Poll an Easy Playbook generation row (M3-A6)
4722 * @description Returns the current state of one Easy Playbook generation.
4723 * Caller must be the row's user OR an admin; cross-user / missing
4724 * rows return 404.
4725 *
4726 * When ``status='completed'``, ``draft_playbook`` carries the
4727 * assembled ``PlaybookCreate`` shape — the wizard's Step 3 inline
4728 * editor binds to this. When ``status='error'``, ``error_message``
4729 * is populated. The wizard's Step 2 polls every few seconds until
4730 * a terminal status arrives.
4731 */
4732 get: {
4733 parameters: {
4734 query?: never;
4735 header?: never;
4736 path: {
4737 generation_id: string;
4738 };
4739 cookie?: never;
4740 };
4741 requestBody?: never;
4742 responses: {
4743 /** @description Current generation row. */
4744 200: {
4745 headers: {
4746 [name: string]: unknown;
4747 };
4748 content: {
4749 "application/json": components["schemas"]["EasyPlaybookGeneration"];
4750 };
4751 };
4752 /** @description Not authenticated. */
4753 401: {
4754 headers: {
4755 [name: string]: unknown;
4756 };
4757 content?: never;
4758 };
4759 /** @description Generation not found or not authorized. */
4760 404: {
4761 headers: {
4762 [name: string]: unknown;
4763 };
4764 content?: never;
4765 };
4766 };
4767 };
4768 put?: never;
4769 post?: never;
4770 delete?: never;
4771 options?: never;
4772 head?: never;
4773 patch?: never;
4774 trace?: never;
4775 };
4776 "/api/v1/tabular/preview-cost": {
4777 parameters: {
4778 query?: never;
4779 header?: never;
4780 path?: never;
4781 cookie?: never;
4782 };
4783 get?: never;
4784 put?: never;
4785 /**
4786 * Preview the cost of a proposed tabular execution (M3-C2)
4787 * @description Synchronous cost preview — no execution row is created. The UI
4788 * calls this before showing the confirmation modal so the
4789 * operator sees the cell-count + estimated cost + per-tier
4790 * breakdown (Phase C prep doc Decision C-5).
4791 *
4792 * Either ``skill_name`` or ``columns`` is required (not both).
4793 * The estimator uses a rolling-average over recent
4794 * ``purpose='tabular_extraction'`` routing-log rows; cold-start
4795 * deployments see the conservative default per-cell cost until
4796 * enough calibration data accumulates.
4797 */
4798 post: {
4799 parameters: {
4800 query?: never;
4801 header?: never;
4802 path?: never;
4803 cookie?: never;
4804 };
4805 requestBody: {
4806 content: {
4807 "application/json": components["schemas"]["TabularPreviewCostRequest"];
4808 };
4809 };
4810 responses: {
4811 /** @description Cost preview. */
4812 200: {
4813 headers: {
4814 [name: string]: unknown;
4815 };
4816 content: {
4817 "application/json": components["schemas"]["TabularPreviewCostResponse"];
4818 };
4819 };
4820 /** @description Both ``skill_name`` and ``columns`` provided, or skill has no columns. */
4821 400: {
4822 headers: {
4823 [name: string]: unknown;
4824 };
4825 content?: never;
4826 };
4827 /** @description Not authenticated. */
4828 401: {
4829 headers: {
4830 [name: string]: unknown;
4831 };
4832 content?: never;
4833 };
4834 /** @description ``skill_name`` not found in the live skill registry. */
4835 404: {
4836 headers: {
4837 [name: string]: unknown;
4838 };
4839 content?: never;
4840 };
4841 /** @description Validation error (empty ``document_ids``, malformed column spec, etc.). */
4842 422: {
4843 headers: {
4844 [name: string]: unknown;
4845 };
4846 content?: never;
4847 };
4848 };
4849 };
4850 delete?: never;
4851 options?: never;
4852 head?: never;
4853 patch?: never;
4854 trace?: never;
4855 };
4856 "/api/v1/tabular/execute": {
4857 parameters: {
4858 query?: never;
4859 header?: never;
4860 path?: never;
4861 cookie?: never;
4862 };
4863 get?: never;
4864 put?: never;
4865 /**
4866 * Start a tabular execution (M3-C2)
4867 * @description Kicks off a tabular execution against the supplied document
4868 * corpus. Creates a ``TabularExecution`` row at
4869 * ``status='pending'`` and enqueues the ARQ worker job on the
4870 * shared playbook queue (per Decision C-3 — same queue as Easy
4871 * Playbook). Returns 202 immediately; the result view polls
4872 * ``GET /api/v1/tabular/executions/{id}`` until status reaches
4873 * a terminal value.
4874 *
4875 * Authorization: caller must own every document in
4876 * ``document_ids`` (admins bypass). Cross-user / missing /
4877 * soft-deleted documents collapse into 404.
4878 *
4879 * Execution completion does NOT mean every cell is correct —
4880 * the result view's per-cell citation drawer is where the
4881 * user-attorney validates extractions before relying on them.
4882 */
4883 post: {
4884 parameters: {
4885 query?: never;
4886 header?: never;
4887 path?: never;
4888 cookie?: never;
4889 };
4890 requestBody: {
4891 content: {
4892 "application/json": components["schemas"]["TabularExecutionCreate"];
4893 };
4894 };
4895 responses: {
4896 /** @description Execution scheduled; row created at status `pending`. */
4897 202: {
4898 headers: {
4899 [name: string]: unknown;
4900 };
4901 content: {
4902 "application/json": components["schemas"]["TabularExecution"];
4903 };
4904 };
4905 /** @description Both ``skill_name`` and ``columns`` provided, or skill has no columns. */
4906 400: {
4907 headers: {
4908 [name: string]: unknown;
4909 };
4910 content?: never;
4911 };
4912 /** @description Not authenticated. */
4913 401: {
4914 headers: {
4915 [name: string]: unknown;
4916 };
4917 content?: never;
4918 };
4919 /** @description One or more documents not found / not authorized, or skill not found. */
4920 404: {
4921 headers: {
4922 [name: string]: unknown;
4923 };
4924 content?: never;
4925 };
4926 /** @description Validation error. */
4927 422: {
4928 headers: {
4929 [name: string]: unknown;
4930 };
4931 content?: never;
4932 };
4933 };
4934 };
4935 delete?: never;
4936 options?: never;
4937 head?: never;
4938 patch?: never;
4939 trace?: never;
4940 };
4941 "/api/v1/tabular/executions": {
4942 parameters: {
4943 query?: never;
4944 header?: never;
4945 path?: never;
4946 cookie?: never;
4947 };
4948 /**
4949 * List the caller's tabular executions (M3-C2)
4950 * @description Returns the caller's tabular executions in recent-first order.
4951 * Soft-deleted rows are excluded. Admins see all rows; non-admins
4952 * see only their own.
4953 */
4954 get: {
4955 parameters: {
4956 query?: {
4957 /** @description Maximum rows to return; default 50. */
4958 limit?: number;
4959 };
4960 header?: never;
4961 path?: never;
4962 cookie?: never;
4963 };
4964 requestBody?: never;
4965 responses: {
4966 /** @description List of tabular execution summaries. */
4967 200: {
4968 headers: {
4969 [name: string]: unknown;
4970 };
4971 content: {
4972 "application/json": components["schemas"]["TabularExecutionSummary"][];
4973 };
4974 };
4975 /** @description Not authenticated. */
4976 401: {
4977 headers: {
4978 [name: string]: unknown;
4979 };
4980 content?: never;
4981 };
4982 };
4983 };
4984 put?: never;
4985 post?: never;
4986 delete?: never;
4987 options?: never;
4988 head?: never;
4989 patch?: never;
4990 trace?: never;
4991 };
4992 "/api/v1/tabular/executions/{execution_id}": {
4993 parameters: {
4994 query?: never;
4995 header?: never;
4996 path: {
4997 execution_id: string;
4998 };
4999 cookie?: never;
5000 };
5001 /**
5002 * Get a tabular execution by id (M3-C2)
5003 * @description Returns the full execution — status, results grid, costs,
5004 * timing. Caller must be the row's user OR an admin; cross-user
5005 * / missing / soft-deleted rows return 404.
5006 */
5007 get: {
5008 parameters: {
5009 query?: never;
5010 header?: never;
5011 path: {
5012 execution_id: string;
5013 };
5014 cookie?: never;
5015 };
5016 requestBody?: never;
5017 responses: {
5018 /** @description Current execution row. */
5019 200: {
5020 headers: {
5021 [name: string]: unknown;
5022 };
5023 content: {
5024 "application/json": components["schemas"]["TabularExecution"];
5025 };
5026 };
5027 /** @description Not authenticated. */
5028 401: {
5029 headers: {
5030 [name: string]: unknown;
5031 };
5032 content?: never;
5033 };
5034 /** @description Execution not found or not authorized. */
5035 404: {
5036 headers: {
5037 [name: string]: unknown;
5038 };
5039 content?: never;
5040 };
5041 };
5042 };
5043 put?: never;
5044 post?: never;
5045 /**
5046 * Soft-delete a tabular execution (M3-C2)
5047 * @description Sets ``deleted_at`` to now. Soft-deleted rows are invisible
5048 * to list / get / cancel endpoints; subsequent operations
5049 * return 404.
5050 */
5051 delete: {
5052 parameters: {
5053 query?: never;
5054 header?: never;
5055 path: {
5056 execution_id: string;
5057 };
5058 cookie?: never;
5059 };
5060 requestBody?: never;
5061 responses: {
5062 /** @description Soft-deleted. */
5063 204: {
5064 headers: {
5065 [name: string]: unknown;
5066 };
5067 content?: never;
5068 };
5069 /** @description Not authenticated. */
5070 401: {
5071 headers: {
5072 [name: string]: unknown;
5073 };
5074 content?: never;
5075 };
5076 /** @description Execution not found or already deleted. */
5077 404: {
5078 headers: {
5079 [name: string]: unknown;
5080 };
5081 content?: never;
5082 };
5083 };
5084 };
5085 options?: never;
5086 head?: never;
5087 patch?: never;
5088 trace?: never;
5089 };
5090 "/api/v1/tabular/executions/{execution_id}/cancel": {
5091 parameters: {
5092 query?: never;
5093 header?: never;
5094 path: {
5095 execution_id: string;
5096 };
5097 cookie?: never;
5098 };
5099 get?: never;
5100 put?: never;
5101 /**
5102 * Cancel a pending or running tabular execution (M3-C2)
5103 * @description Sets status to ``cancelled`` and ``completed_at`` to now.
5104 * The worker's per-cell loop checks the row status at each
5105 * cell-iteration boundary; on cancellation it stops accepting
5106 * new cells and the aggregate node writes whatever partial
5107 * results landed. Terminal rows (``completed`` / ``failed`` /
5108 * ``cancelled``) return 409 — operators wanting a clean re-run
5109 * start a new execution via ``POST /tabular/execute``.
5110 */
5111 post: {
5112 parameters: {
5113 query?: never;
5114 header?: never;
5115 path: {
5116 execution_id: string;
5117 };
5118 cookie?: never;
5119 };
5120 requestBody?: never;
5121 responses: {
5122 /** @description Cancellation accepted; row updated. */
5123 200: {
5124 headers: {
5125 [name: string]: unknown;
5126 };
5127 content: {
5128 "application/json": components["schemas"]["TabularExecution"];
5129 };
5130 };
5131 /** @description Not authenticated. */
5132 401: {
5133 headers: {
5134 [name: string]: unknown;
5135 };
5136 content?: never;
5137 };
5138 /** @description Execution not found or not authorized. */
5139 404: {
5140 headers: {
5141 [name: string]: unknown;
5142 };
5143 content?: never;
5144 };
5145 /** @description Execution already in a terminal status. */
5146 409: {
5147 headers: {
5148 [name: string]: unknown;
5149 };
5150 content?: never;
5151 };
5152 };
5153 };
5154 delete?: never;
5155 options?: never;
5156 head?: never;
5157 patch?: never;
5158 trace?: never;
5159 };
5160 "/api/v1/tabular/executions/{execution_id}/export": {
5161 parameters: {
5162 query?: never;
5163 header?: never;
5164 path: {
5165 execution_id: string;
5166 };
5167 cookie?: never;
5168 };
5169 /**
5170 * Export a completed tabular grid as XLSX or CSV (M3-C4a)
5171 * @description Streams the tabular grid in the requested format. Both formats
5172 * carry the document column (leftmost) plus one column per declared
5173 * column spec, in spec order. Cells with ``confidence='failed'``
5174 * render as ``"(failed)"`` so operators can spot gaps.
5175 *
5176 * Citation surfacing differs by format:
5177 *
5178 * * **XLSX** — each grid cell with at least one citation carries an
5179 * openpyxl cell ``Comment`` listing the citation ids + confidences
5180 * (up to 5 per comment; the cell retains the full count). Operators
5181 * hover any cell in Excel / Numbers / Google Sheets to see sources.
5182 * * **CSV** — a trailing ``citation_links`` column per row carries a
5183 * semicolon-separated list of ``"<column_name>:<citation_id>"``
5184 * pairs across the row's cells; empty when no cell had citations.
5185 *
5186 * The execution must be in ``status='completed'`` — pending /
5187 * running / cancelled / failed rows return 409 (partial grids would
5188 * mislead downstream consumers).
5189 */
5190 get: {
5191 parameters: {
5192 query?: {
5193 /** @description Export format. `xlsx` carries citations as cell comments; `csv` flattens them into a `citation_links` column. */
5194 format?: "xlsx" | "csv";
5195 };
5196 header?: never;
5197 path: {
5198 execution_id: string;
5199 };
5200 cookie?: never;
5201 };
5202 requestBody?: never;
5203 responses: {
5204 /** @description Streaming export; `Content-Type` matches the format. */
5205 200: {
5206 headers: {
5207 [name: string]: unknown;
5208 };
5209 content: {
5210 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": string;
5211 "text/csv": string;
5212 };
5213 };
5214 /** @description Not authenticated. */
5215 401: {
5216 headers: {
5217 [name: string]: unknown;
5218 };
5219 content?: never;
5220 };
5221 /** @description Execution not found or not authorized. */
5222 404: {
5223 headers: {
5224 [name: string]: unknown;
5225 };
5226 content?: never;
5227 };
5228 /** @description Execution not in a terminal `completed` status — cannot export. */
5229 409: {
5230 headers: {
5231 [name: string]: unknown;
5232 };
5233 content?: never;
5234 };
5235 };
5236 };
5237 put?: never;
5238 post?: never;
5239 delete?: never;
5240 options?: never;
5241 head?: never;
5242 patch?: never;
5243 trace?: never;
5244 };
5245 "/api/v1/playbooks/{playbook_id}/execute": {
5246 parameters: {
5247 query?: never;
5248 header?: never;
5249 path: {
5250 playbook_id: string;
5251 };
5252 cookie?: never;
5253 };
5254 get?: never;
5255 put?: never;
5256 /**
5257 * Kick off a playbook execution against a target document (M3-A2)
5258 * @description Creates a new ``playbook_executions`` row at status ``'pending'``
5259 * and schedules the four-node LangGraph workflow
5260 * (retrieve → classify → redline → compile) as a FastAPI
5261 * BackgroundTask. The response returns immediately (202) with the
5262 * new row; clients poll
5263 * ``GET /api/v1/playbook-executions/{execution_id}`` until they
5264 * see a terminal status (``completed`` or ``error``).
5265 *
5266 * Authorization: caller must be an admin or the playbook's
5267 * author, and own the target document's parent file. If
5268 * ``project_id`` is supplied, caller must also own the project.
5269 */
5270 post: {
5271 parameters: {
5272 query?: never;
5273 header?: never;
5274 path: {
5275 playbook_id: string;
5276 };
5277 cookie?: never;
5278 };
5279 requestBody: {
5280 content: {
5281 "application/json": {
5282 /** Format: uuid */
5283 target_document_id: string;
5284 /** Format: uuid */
5285 project_id?: string | null;
5286 };
5287 };
5288 };
5289 responses: {
5290 /** @description Execution scheduled; row created at status `pending`. */
5291 202: {
5292 headers: {
5293 [name: string]: unknown;
5294 };
5295 content: {
5296 "application/json": components["schemas"]["PlaybookExecution"];
5297 };
5298 };
5299 /** @description Not authenticated. */
5300 401: {
5301 headers: {
5302 [name: string]: unknown;
5303 };
5304 content: {
5305 "application/json": components["schemas"]["Error"];
5306 };
5307 };
5308 /** @description Playbook, target document, or project not found / not owned. */
5309 404: {
5310 headers: {
5311 [name: string]: unknown;
5312 };
5313 content: {
5314 "application/json": components["schemas"]["Error"];
5315 };
5316 };
5317 /** @description Request body validation error (e.g., missing or malformed `target_document_id`, unknown field). */
5318 422: {
5319 headers: {
5320 [name: string]: unknown;
5321 };
5322 content: {
5323 "application/json": components["schemas"]["Error"];
5324 };
5325 };
5326 };
5327 };
5328 delete?: never;
5329 options?: never;
5330 head?: never;
5331 patch?: never;
5332 trace?: never;
5333 };
5334 "/api/v1/playbook-executions/{execution_id}": {
5335 parameters: {
5336 query?: never;
5337 header?: never;
5338 path: {
5339 execution_id: string;
5340 };
5341 cookie?: never;
5342 };
5343 /**
5344 * Poll the current state of a playbook execution (M3-A2)
5345 * @description Returns the row regardless of completion state. Callers poll
5346 * until they see a terminal ``completed`` or ``error`` status.
5347 * Authorization: caller must own the execution (``user_id``
5348 * match) or be an admin.
5349 */
5350 get: {
5351 parameters: {
5352 query?: never;
5353 header?: never;
5354 path: {
5355 execution_id: string;
5356 };
5357 cookie?: never;
5358 };
5359 requestBody?: never;
5360 responses: {
5361 /** @description Current execution state. */
5362 200: {
5363 headers: {
5364 [name: string]: unknown;
5365 };
5366 content: {
5367 "application/json": components["schemas"]["PlaybookExecution"];
5368 };
5369 };
5370 /** @description Not authenticated. */
5371 401: {
5372 headers: {
5373 [name: string]: unknown;
5374 };
5375 content: {
5376 "application/json": components["schemas"]["Error"];
5377 };
5378 };
5379 /** @description Execution not found or caller does not own it. */
5380 404: {
5381 headers: {
5382 [name: string]: unknown;
5383 };
5384 content: {
5385 "application/json": components["schemas"]["Error"];
5386 };
5387 };
5388 };
5389 };
5390 put?: never;
5391 post?: never;
5392 delete?: never;
5393 options?: never;
5394 head?: never;
5395 patch?: never;
5396 trace?: never;
5397 };
5398 "/api/v1/admin/ingest-health": {
5399 parameters: {
5400 query?: never;
5401 header?: never;
5402 path?: never;
5403 cookie?: never;
5404 };
5405 /**
5406 * Aggregate ingest-status counts across the deployment (admin-only)
5407 * @description Reports document-level + file-level ingest health in a single
5408 * response: ``ok`` (chunks embedded), ``embed_failed`` /
5409 * ``partial`` (document-level post-parse failures introduced by
5410 * M3-0.3 / DE-276), and ``parse_failed`` (file-level parse
5411 * failures from the M1 pipeline). Soft-deleted rows are excluded.
5412 *
5413 * A non-zero ``embed_failed`` or ``partial`` count is the
5414 * operator's canonical signal that hybrid retrieval has silently
5415 * degraded for some documents — the exact failure mode DE-276
5416 * was filed to surface. A non-zero ``parse_failed`` count means
5417 * operators should inspect the file-list UI for the specific
5418 * failures.
5419 */
5420 get: {
5421 parameters: {
5422 query?: never;
5423 header?: never;
5424 path?: never;
5425 cookie?: never;
5426 };
5427 requestBody?: never;
5428 responses: {
5429 /** @description Aggregate ingest-health summary */
5430 200: {
5431 headers: {
5432 [name: string]: unknown;
5433 };
5434 content: {
5435 "application/json": {
5436 /** @description Documents with all chunks successfully embedded. */
5437 ok: number;
5438 /**
5439 * @description Documents whose embed worker raised before any chunk
5440 * was embedded — hybrid retrieval falls back to FTS-only.
5441 */
5442 embed_failed: number;
5443 /**
5444 * @description Documents whose embed worker raised mid-batch — some
5445 * chunks have vectors, others are NULL.
5446 */
5447 partial: number;
5448 /**
5449 * @description Files that failed at parse time and never produced a
5450 * ``documents`` row (``files.ingestion_status='failed'``).
5451 */
5452 parse_failed: number;
5453 /** @description Sum of ok + embed_failed + partial. */
5454 total_documents: number;
5455 };
5456 };
5457 };
5458 /** @description Caller is not an admin */
5459 403: {
5460 headers: {
5461 [name: string]: unknown;
5462 };
5463 content: {
5464 "application/json": components["schemas"]["Error"];
5465 };
5466 };
5467 };
5468 };
5469 put?: never;
5470 post?: never;
5471 delete?: never;
5472 options?: never;
5473 head?: never;
5474 patch?: never;
5475 trace?: never;
5476 };
5477 "/api/v1/admin/bootstrap-status": {
5478 parameters: {
5479 query?: never;
5480 header?: never;
5481 path?: never;
5482 cookie?: never;
5483 };
5484 /**
5485 * Report whether the first-run bootstrap admin password is still active
5486 * @description **Unauthenticated.** Consulted by the login UI before the operator
5487 * has credentials, so it can render a fresh-install hint pointing at
5488 * the API container logs when the bootstrap password has not yet
5489 * been rotated (M3-0.1 / DE-283).
5490 *
5491 * ``default_password_active`` is True when at least one non-deleted
5492 * admin user still has ``must_change_password=True``. The flag flips
5493 * to False once the operator hits ``POST /api/v1/auth/change-password``,
5494 * at which point the login UI stops surfacing the hint.
5495 *
5496 * The signal exposed by this endpoint is low-sensitivity: the
5497 * bootstrap password is 24 characters of CSPRNG output, so knowing
5498 * a deployment is in fresh-install state does not measurably help
5499 * an attacker who lacks the log line.
5500 */
5501 get: {
5502 parameters: {
5503 query?: never;
5504 header?: never;
5505 path?: never;
5506 cookie?: never;
5507 };
5508 requestBody?: never;
5509 responses: {
5510 /** @description Bootstrap state */
5511 200: {
5512 headers: {
5513 [name: string]: unknown;
5514 };
5515 content: {
5516 "application/json": {
5517 /**
5518 * @description True when at least one admin user is still in the
5519 * ``must_change_password=True`` state, indicating the
5520 * first-run bootstrap password has not been rotated.
5521 */
5522 default_password_active: boolean;
5523 /**
5524 * @description Shell command an operator can run to retrieve the
5525 * bootstrap password from the API container's logs.
5526 */
5527 logs_hint: string;
5528 };
5529 };
5530 };
5531 };
5532 };
5533 put?: never;
5534 post?: never;
5535 delete?: never;
5536 options?: never;
5537 head?: never;
5538 patch?: never;
5539 trace?: never;
5540 };
5541 "/api/v1/admin/aliases": {
5542 parameters: {
5543 query?: never;
5544 header?: never;
5545 path?: never;
5546 cookie?: never;
5547 };
5548 /**
5549 * List configured model aliases (admin only)
5550 * @description Proxies the gateway's ``/admin/v1/aliases`` surface. The user's
5551 * bearer token must have ``is_admin = true`` or the endpoint
5552 * returns 403 ``forbidden``.
5553 */
5554 get: {
5555 parameters: {
5556 query?: never;
5557 header?: never;
5558 path?: never;
5559 cookie?: never;
5560 };
5561 requestBody?: never;
5562 responses: {
5563 /** @description Alias list */
5564 200: {
5565 headers: {
5566 [name: string]: unknown;
5567 };
5568 content: {
5569 "application/json": {
5570 /** @enum {string} */
5571 object: "list";
5572 data: components["schemas"]["AdminAliasEntry"][];
5573 };
5574 };
5575 };
5576 /** @description Caller is not an admin */
5577 403: {
5578 headers: {
5579 [name: string]: unknown;
5580 };
5581 content: {
5582 "application/json": components["schemas"]["Error"];
5583 };
5584 };
5585 };
5586 };
5587 put?: never;
5588 /** Create a new model alias (admin only) */
5589 post: {
5590 parameters: {
5591 query?: never;
5592 header?: never;
5593 path?: never;
5594 cookie?: never;
5595 };
5596 requestBody: {
5597 content: {
5598 "application/json": components["schemas"]["AdminAliasCreate"];
5599 };
5600 };
5601 responses: {
5602 /** @description Alias created */
5603 201: {
5604 headers: {
5605 [name: string]: unknown;
5606 };
5607 content: {
5608 "application/json": components["schemas"]["AdminAliasEntry"];
5609 };
5610 };
5611 /** @description Alias name already configured */
5612 409: {
5613 headers: {
5614 [name: string]: unknown;
5615 };
5616 content: {
5617 "application/json": components["schemas"]["Error"];
5618 };
5619 };
5620 };
5621 };
5622 delete?: never;
5623 options?: never;
5624 head?: never;
5625 patch?: never;
5626 trace?: never;
5627 };
5628 "/api/v1/admin/aliases/{name}": {
5629 parameters: {
5630 query?: never;
5631 header?: never;
5632 path: {
5633 name: string;
5634 };
5635 cookie?: never;
5636 };
5637 /** Get a single alias (admin only) */
5638 get: {
5639 parameters: {
5640 query?: never;
5641 header?: never;
5642 path: {
5643 name: string;
5644 };
5645 cookie?: never;
5646 };
5647 requestBody?: never;
5648 responses: {
5649 /** @description Alias detail */
5650 200: {
5651 headers: {
5652 [name: string]: unknown;
5653 };
5654 content: {
5655 "application/json": components["schemas"]["AdminAliasEntry"];
5656 };
5657 };
5658 /** @description Alias not configured */
5659 404: {
5660 headers: {
5661 [name: string]: unknown;
5662 };
5663 content: {
5664 "application/json": components["schemas"]["Error"];
5665 };
5666 };
5667 };
5668 };
5669 put?: never;
5670 post?: never;
5671 /** Remove an alias (admin only) */
5672 delete: {
5673 parameters: {
5674 query?: never;
5675 header?: never;
5676 path: {
5677 name: string;
5678 };
5679 cookie?: never;
5680 };
5681 requestBody?: never;
5682 responses: {
5683 /** @description Alias removed */
5684 204: {
5685 headers: {
5686 [name: string]: unknown;
5687 };
5688 content?: never;
5689 };
5690 /** @description Alias not configured */
5691 404: {
5692 headers: {
5693 [name: string]: unknown;
5694 };
5695 content: {
5696 "application/json": components["schemas"]["Error"];
5697 };
5698 };
5699 };
5700 };
5701 options?: never;
5702 head?: never;
5703 /** Update an alias (admin only) */
5704 patch: {
5705 parameters: {
5706 query?: never;
5707 header?: never;
5708 path: {
5709 name: string;
5710 };
5711 cookie?: never;
5712 };
5713 requestBody: {
5714 content: {
5715 "application/json": components["schemas"]["AdminAliasUpdate"];
5716 };
5717 };
5718 responses: {
5719 /** @description Alias updated */
5720 200: {
5721 headers: {
5722 [name: string]: unknown;
5723 };
5724 content: {
5725 "application/json": components["schemas"]["AdminAliasEntry"];
5726 };
5727 };
5728 /** @description Alias not configured */
5729 404: {
5730 headers: {
5731 [name: string]: unknown;
5732 };
5733 content: {
5734 "application/json": components["schemas"]["Error"];
5735 };
5736 };
5737 };
5738 };
5739 trace?: never;
5740 };
5741 "/api/v1/admin/provider-keys": {
5742 parameters: {
5743 query?: never;
5744 header?: never;
5745 path?: never;
5746 cookie?: never;
5747 };
5748 /**
5749 * List provider-key status (admin only)
5750 * @description Donna #7 (runtime BYOK). Proxies the gateway's
5751 * ``/admin/v1/provider-keys`` surface. The backend holds the
5752 * gateway-key; the frontend never does. Returns a secret-safe status
5753 * row per configured provider — the response NEVER contains a full
5754 * key, only the last 4 characters of a resolved key. The caller's
5755 * bearer token must have ``is_admin = true`` or the endpoint returns
5756 * 403 ``forbidden``.
5757 */
5758 get: {
5759 parameters: {
5760 query?: never;
5761 header?: never;
5762 path?: never;
5763 cookie?: never;
5764 };
5765 requestBody?: never;
5766 responses: {
5767 /** @description Provider-key status list */
5768 200: {
5769 headers: {
5770 [name: string]: unknown;
5771 };
5772 content: {
5773 "application/json": components["schemas"]["ProviderKeyList"];
5774 };
5775 };
5776 /** @description Caller is not an admin */
5777 403: {
5778 headers: {
5779 [name: string]: unknown;
5780 };
5781 content: {
5782 "application/json": components["schemas"]["Error"];
5783 };
5784 };
5785 };
5786 };
5787 put?: never;
5788 /**
5789 * Set/replace a provider's runtime key (admin only)
5790 * @description Proxies the gateway's set-key path. The gateway encrypts the
5791 * plaintext key, persists it to gateway.yaml, and hot-applies the
5792 * rebuilt adapter — no restart. The backend forwards the JSON and
5793 * never returns the secret. 400 when the gateway master key is unset
5794 * (surfaced as ``validation_error``); 404 when the provider isn't a
5795 * configured entry.
5796 */
5797 post: {
5798 parameters: {
5799 query?: never;
5800 header?: never;
5801 path?: never;
5802 cookie?: never;
5803 };
5804 requestBody: {
5805 content: {
5806 "application/json": components["schemas"]["ProviderKeySetRequest"];
5807 };
5808 };
5809 responses: {
5810 /** @description Key applied; provider status returned (no secret) */
5811 200: {
5812 headers: {
5813 [name: string]: unknown;
5814 };
5815 content: {
5816 "application/json": components["schemas"]["ProviderKeyStatus"];
5817 };
5818 };
5819 /** @description Gateway master key is not set */
5820 400: {
5821 headers: {
5822 [name: string]: unknown;
5823 };
5824 content: {
5825 "application/json": components["schemas"]["Error"];
5826 };
5827 };
5828 /** @description Caller is not an admin */
5829 403: {
5830 headers: {
5831 [name: string]: unknown;
5832 };
5833 content: {
5834 "application/json": components["schemas"]["Error"];
5835 };
5836 };
5837 /** @description Provider not configured */
5838 404: {
5839 headers: {
5840 [name: string]: unknown;
5841 };
5842 content: {
5843 "application/json": components["schemas"]["Error"];
5844 };
5845 };
5846 };
5847 };
5848 delete?: never;
5849 options?: never;
5850 head?: never;
5851 patch?: never;
5852 trace?: never;
5853 };
5854 "/api/v1/admin/provider-keys/{provider}": {
5855 parameters: {
5856 query?: never;
5857 header?: never;
5858 path: {
5859 provider: string;
5860 };
5861 cookie?: never;
5862 };
5863 get?: never;
5864 put?: never;
5865 post?: never;
5866 /**
5867 * Revoke a provider's runtime key (admin only)
5868 * @description Proxies the gateway's revoke path. Only runtime
5869 * (encrypted-at-rest) keys can be revoked; an env-sourced key is
5870 * owned by the operator's environment and returns 409.
5871 */
5872 delete: {
5873 parameters: {
5874 query?: never;
5875 header?: never;
5876 path: {
5877 provider: string;
5878 };
5879 cookie?: never;
5880 };
5881 requestBody?: never;
5882 responses: {
5883 /** @description Runtime key revoked */
5884 204: {
5885 headers: {
5886 [name: string]: unknown;
5887 };
5888 content?: never;
5889 };
5890 /** @description Caller is not an admin */
5891 403: {
5892 headers: {
5893 [name: string]: unknown;
5894 };
5895 content: {
5896 "application/json": components["schemas"]["Error"];
5897 };
5898 };
5899 /** @description Provider not configured */
5900 404: {
5901 headers: {
5902 [name: string]: unknown;
5903 };
5904 content: {
5905 "application/json": components["schemas"]["Error"];
5906 };
5907 };
5908 /** @description Provider has no runtime key to revoke (e.g., env-sourced) */
5909 409: {
5910 headers: {
5911 [name: string]: unknown;
5912 };
5913 content: {
5914 "application/json": components["schemas"]["Error"];
5915 };
5916 };
5917 };
5918 };
5919 options?: never;
5920 head?: never;
5921 /**
5922 * Rotate a provider's runtime key (admin only)
5923 * @description Same mechanics as POST /api/v1/admin/provider-keys; the provider
5924 * comes from the path. Proxies the gateway, which retires the
5925 * displaced adapter and swaps the rebuilt one in. The backend never
5926 * returns the secret. 400 when the gateway master key is unset; 404
5927 * when the provider isn't configured.
5928 */
5929 patch: {
5930 parameters: {
5931 query?: never;
5932 header?: never;
5933 path: {
5934 provider: string;
5935 };
5936 cookie?: never;
5937 };
5938 requestBody: {
5939 content: {
5940 "application/json": components["schemas"]["ProviderKeyRotateRequest"];
5941 };
5942 };
5943 responses: {
5944 /** @description Key rotated; provider status returned (no secret) */
5945 200: {
5946 headers: {
5947 [name: string]: unknown;
5948 };
5949 content: {
5950 "application/json": components["schemas"]["ProviderKeyStatus"];
5951 };
5952 };
5953 /** @description Gateway master key is not set */
5954 400: {
5955 headers: {
5956 [name: string]: unknown;
5957 };
5958 content: {
5959 "application/json": components["schemas"]["Error"];
5960 };
5961 };
5962 /** @description Caller is not an admin */
5963 403: {
5964 headers: {
5965 [name: string]: unknown;
5966 };
5967 content: {
5968 "application/json": components["schemas"]["Error"];
5969 };
5970 };
5971 /** @description Provider not configured */
5972 404: {
5973 headers: {
5974 [name: string]: unknown;
5975 };
5976 content: {
5977 "application/json": components["schemas"]["Error"];
5978 };
5979 };
5980 };
5981 };
5982 trace?: never;
5983 };
5984 "/api/v1/admin/config": {
5985 parameters: {
5986 query?: never;
5987 header?: never;
5988 path?: never;
5989 cookie?: never;
5990 };
5991 /**
5992 * Sanitized live gateway config (admin only)
5993 * @description Proxies the gateway's ``/admin/v1/config`` endpoint. Used by
5994 * the web UI to populate the provider dropdown when editing
5995 * aliases. Secrets are not in the schema; the payload contains
5996 * env-var *names* only.
5997 */
5998 get: {
5999 parameters: {
6000 query?: never;
6001 header?: never;
6002 path?: never;
6003 cookie?: never;
6004 };
6005 requestBody?: never;
6006 responses: {
6007 /** @description Sanitized config payload */
6008 200: {
6009 headers: {
6010 [name: string]: unknown;
6011 };
6012 content: {
6013 "application/json": {
6014 [key: string]: unknown;
6015 };
6016 };
6017 };
6018 };
6019 };
6020 put?: never;
6021 post?: never;
6022 delete?: never;
6023 options?: never;
6024 head?: never;
6025 patch?: never;
6026 trace?: never;
6027 };
6028 "/api/v1/admin/users": {
6029 parameters: {
6030 query?: never;
6031 header?: never;
6032 path?: never;
6033 cookie?: never;
6034 };
6035 /**
6036 * List users for RBAC administration (Wave B v2 — PRD §5.2)
6037 * @description Admin-only. Returns the full list of non-deleted users with
6038 * their role + auth state. Supports filtering by role, email
6039 * substring search, and pagination. Default sort is email ASC.
6040 * Used by /lq-ai/admin/developer's role-management card.
6041 */
6042 get: {
6043 parameters: {
6044 query?: {
6045 role?: "admin" | "member" | "viewer";
6046 email_q?: string;
6047 limit?: number;
6048 offset?: number;
6049 };
6050 header?: never;
6051 path?: never;
6052 cookie?: never;
6053 };
6054 requestBody?: never;
6055 responses: {
6056 /** @description User list (paginated) */
6057 200: {
6058 headers: {
6059 [name: string]: unknown;
6060 };
6061 content: {
6062 "application/json": components["schemas"]["AdminUserListResponse"];
6063 };
6064 };
6065 /** @description Invalid role filter */
6066 400: {
6067 headers: {
6068 [name: string]: unknown;
6069 };
6070 content?: never;
6071 };
6072 /** @description Caller is not admin */
6073 403: {
6074 headers: {
6075 [name: string]: unknown;
6076 };
6077 content?: never;
6078 };
6079 };
6080 };
6081 put?: never;
6082 post?: never;
6083 delete?: never;
6084 options?: never;
6085 head?: never;
6086 patch?: never;
6087 trace?: never;
6088 };
6089 "/api/v1/admin/users/{user_id}/role": {
6090 parameters: {
6091 query?: never;
6092 header?: never;
6093 path: {
6094 user_id: string;
6095 };
6096 cookie?: never;
6097 };
6098 get?: never;
6099 put?: never;
6100 post?: never;
6101 delete?: never;
6102 options?: never;
6103 head?: never;
6104 /**
6105 * Set a user's RBAC role (admin | member | viewer)
6106 * @description Wave C. Updates ``users.role`` and keeps ``is_admin`` in sync
6107 * (True iff role='admin'). Idempotent: re-applying the same role
6108 * returns 200 without an audit row. Real changes write a
6109 * ``user.role_updated`` audit row with before/after values.
6110 *
6111 * Lockout protection: refuses to demote the last admin in the
6112 * deployment (409 — promote someone else first).
6113 */
6114 patch: {
6115 parameters: {
6116 query?: never;
6117 header?: never;
6118 path: {
6119 user_id: string;
6120 };
6121 cookie?: never;
6122 };
6123 requestBody: {
6124 content: {
6125 "application/json": {
6126 /** @enum {string} */
6127 role: "admin" | "member" | "viewer";
6128 };
6129 };
6130 };
6131 responses: {
6132 /** @description Updated role */
6133 200: {
6134 headers: {
6135 [name: string]: unknown;
6136 };
6137 content: {
6138 "application/json": {
6139 /** Format: uuid */
6140 user_id: string;
6141 /** Format: email */
6142 email: string;
6143 /** @enum {string} */
6144 role: "admin" | "member" | "viewer";
6145 is_admin: boolean;
6146 };
6147 };
6148 };
6149 /** @description Last-admin demotion lockout. */
6150 403: {
6151 headers: {
6152 [name: string]: unknown;
6153 };
6154 content: {
6155 "application/json": components["schemas"]["Error"];
6156 };
6157 };
6158 /** @description Target user not found. */
6159 404: {
6160 headers: {
6161 [name: string]: unknown;
6162 };
6163 content: {
6164 "application/json": components["schemas"]["Error"];
6165 };
6166 };
6167 /** @description Invalid role value. */
6168 422: {
6169 headers: {
6170 [name: string]: unknown;
6171 };
6172 content: {
6173 "application/json": components["schemas"]["Error"];
6174 };
6175 };
6176 };
6177 };
6178 trace?: never;
6179 };
6180 "/api/v1/admin/tier-policy": {
6181 parameters: {
6182 query?: never;
6183 header?: never;
6184 path?: never;
6185 cookie?: never;
6186 };
6187 /** Get current Inference Tier policy */
6188 get: {
6189 parameters: {
6190 query?: never;
6191 header?: never;
6192 path?: never;
6193 cookie?: never;
6194 };
6195 requestBody?: never;
6196 responses: {
6197 /** @description Policy */
6198 200: {
6199 headers: {
6200 [name: string]: unknown;
6201 };
6202 content: {
6203 "application/json": components["schemas"]["TierPolicy"];
6204 };
6205 };
6206 };
6207 };
6208 put?: never;
6209 post?: never;
6210 delete?: never;
6211 options?: never;
6212 head?: never;
6213 /**
6214 * Partial update to Inference Tier policy
6215 * @description Admin-only. Audited. Partial-update semantics — fields omitted from
6216 * the request body are left unchanged.
6217 */
6218 patch: {
6219 parameters: {
6220 query?: never;
6221 header?: never;
6222 path?: never;
6223 cookie?: never;
6224 };
6225 requestBody: {
6226 content: {
6227 "application/json": components["schemas"]["TierPolicy"];
6228 };
6229 };
6230 responses: {
6231 /** @description Updated */
6232 200: {
6233 headers: {
6234 [name: string]: unknown;
6235 };
6236 content: {
6237 "application/json": components["schemas"]["TierPolicy"];
6238 };
6239 };
6240 };
6241 };
6242 trace?: never;
6243 };
6244 "/api/v1/admin/usage": {
6245 parameters: {
6246 query?: never;
6247 header?: never;
6248 path?: never;
6249 cookie?: never;
6250 };
6251 /**
6252 * Cost + tokens aggregation across inference_routing_log
6253 * @description Admin-only. Aggregates ``inference_routing_log`` rows by one of
6254 * user | provider | model | tier | day. Refusals are excluded by
6255 * default (they didn't consume upstream tokens). Filters: date
6256 * range + the same grouping dimensions. Returns per-group rows
6257 * plus deployment-wide totals so the admin UI can render
6258 * percentages without a second query.
6259 */
6260 get: {
6261 parameters: {
6262 query?: {
6263 group_by?: "user" | "provider" | "model" | "tier" | "day";
6264 date_from?: string;
6265 date_to?: string;
6266 user_id?: string;
6267 provider?: string;
6268 model?: string;
6269 tier?: number;
6270 };
6271 header?: never;
6272 path?: never;
6273 cookie?: never;
6274 };
6275 requestBody?: never;
6276 responses: {
6277 /** @description Aggregated usage rows + totals */
6278 200: {
6279 headers: {
6280 [name: string]: unknown;
6281 };
6282 content: {
6283 "application/json": components["schemas"]["UsageResponse"];
6284 };
6285 };
6286 };
6287 };
6288 put?: never;
6289 post?: never;
6290 delete?: never;
6291 options?: never;
6292 head?: never;
6293 patch?: never;
6294 trace?: never;
6295 };
6296 "/api/v1/admin/word-addin/manifest": {
6297 parameters: {
6298 query?: never;
6299 header?: never;
6300 path?: never;
6301 cookie?: never;
6302 };
6303 /**
6304 * Render the Word add-in manifest XML for sideload (M3-B1)
6305 * @description Admin-only. Renders the Office Add-in XML manifest with the
6306 * operator's deployment URL + a freshly generated GUID substituted
6307 * into the template. Operators use the rendered file to sideload
6308 * the add-in via Microsoft 365 Admin Center.
6309 *
6310 * Origin resolution order:
6311 * 1. ``deployment_origin`` query param when supplied
6312 * 2. ``X-Forwarded-Proto`` + ``X-Forwarded-Host`` headers (the
6313 * operator's reverse-proxy reports these)
6314 * 3. The request URL's scheme + host (single-process dev fallback)
6315 *
6316 * Per PRD §9 DE-287, the Word add-in's user-facing feature tabs
6317 * (chat / skills / playbook execution / Inference Tier badge) are
6318 * descoped to M4 / community contribution; this M3-B1 endpoint
6319 * ships the install-and-authenticate plumbing.
6320 */
6321 get: {
6322 parameters: {
6323 query?: {
6324 /**
6325 * @description Override the deployment origin embedded in the manifest.
6326 * Defaults to the request's effective origin (reverse-proxy
6327 * aware). No trailing slash.
6328 */
6329 deployment_origin?: string;
6330 /**
6331 * @description Branded name surfaced inside Word's ribbon and the task
6332 * pane GetStarted message.
6333 */
6334 display_name?: string;
6335 /**
6336 * @description ProviderName value the manifest surfaces to Microsoft 365
6337 * Admin Center; typically the operator org's name.
6338 */
6339 provider_name?: string;
6340 };
6341 header?: never;
6342 path?: never;
6343 cookie?: never;
6344 };
6345 requestBody?: never;
6346 responses: {
6347 /** @description Rendered Office Add-in XML manifest */
6348 200: {
6349 headers: {
6350 /** @description Tells the browser to download the manifest as a file. */
6351 "Content-Disposition"?: string;
6352 [name: string]: unknown;
6353 };
6354 content: {
6355 "application/xml": string;
6356 };
6357 };
6358 /** @description Unauthenticated */
6359 401: {
6360 headers: {
6361 [name: string]: unknown;
6362 };
6363 content: {
6364 "application/json": components["schemas"]["Error"];
6365 };
6366 };
6367 /** @description Caller is not an admin user */
6368 403: {
6369 headers: {
6370 [name: string]: unknown;
6371 };
6372 content: {
6373 "application/json": components["schemas"]["Error"];
6374 };
6375 };
6376 };
6377 };
6378 put?: never;
6379 post?: never;
6380 delete?: never;
6381 options?: never;
6382 head?: never;
6383 patch?: never;
6384 trace?: never;
6385 };
6386 "/api/v1/word-addin/version": {
6387 parameters: {
6388 query?: never;
6389 header?: never;
6390 path?: never;
6391 cookie?: never;
6392 };
6393 /**
6394 * Word add-in version handshake (M3-B8)
6395 * @description **Unauthenticated.** The Word add-in's task pane calls this on
6396 * mount, BEFORE the user has signed in, so an out-of-date add-in
6397 * can surface an "Update needed" overlay rather than getting
6398 * stuck at a broken OAuth handshake or a breaking-change API
6399 * call.
6400 *
6401 * Response payload:
6402 * - `deployment_version` — the LQ.AI api package version (the
6403 * deployment's release number). Informational; the add-in
6404 * surfaces it in the "Update needed" overlay so a user can
6405 * quote it to support.
6406 * - `addin_min_compatible_version` / `addin_max_compatible_version`
6407 * — the semver range of add-in bundles this deployment can
6408 * talk to. The task pane refuses to render features outside
6409 * the range.
6410 * - `taskpane_bundle_url` — canonical URL of the task pane HTML
6411 * entry. The task pane already knows this from `window.location`
6412 * today; the field exists in the handshake so a future
6413 * deployment can serve the bundle from a CDN or operator-chosen
6414 * path without changing the manifest.
6415 * - `taskpane_bundle_hash` — optional SHA-256 of the deployed
6416 * bundle JS. M3-B8 ships this nullable; M3-B7 signing CI
6417 * populates the value from the build manifest. Null means
6418 * "don't enforce" — not an error.
6419 */
6420 get: {
6421 parameters: {
6422 query?: never;
6423 header?: never;
6424 path?: never;
6425 cookie?: never;
6426 };
6427 requestBody?: never;
6428 responses: {
6429 /** @description Version handshake payload */
6430 200: {
6431 headers: {
6432 [name: string]: unknown;
6433 };
6434 content: {
6435 "application/json": components["schemas"]["WordAddinVersionResponse"];
6436 };
6437 };
6438 };
6439 };
6440 put?: never;
6441 post?: never;
6442 delete?: never;
6443 options?: never;
6444 head?: never;
6445 patch?: never;
6446 trace?: never;
6447 };
6448 "/api/v1/inference/current-tier": {
6449 parameters: {
6450 query?: never;
6451 header?: never;
6452 path?: never;
6453 cookie?: never;
6454 };
6455 /**
6456 * Derive routed tier for a (provider, model) pair
6457 * @description The Tier Awareness UI's click-for-details panel calls this so
6458 * the user sees the human-readable tier label + the provider type
6459 * the Provider Compliance Matrix renders against. 404 when the
6460 * pair isn't in the gateway's model list.
6461 */
6462 get: {
6463 parameters: {
6464 query: {
6465 provider: string;
6466 model: string;
6467 };
6468 header?: never;
6469 path?: never;
6470 cookie?: never;
6471 };
6472 requestBody?: never;
6473 responses: {
6474 /** @description Current tier + explanation */
6475 200: {
6476 headers: {
6477 [name: string]: unknown;
6478 };
6479 content: {
6480 "application/json": components["schemas"]["CurrentTierResponse"];
6481 };
6482 };
6483 /** @description No (provider, model) entry in the gateway's model list. */
6484 404: {
6485 headers: {
6486 [name: string]: unknown;
6487 };
6488 content: {
6489 "application/json": components["schemas"]["Error"];
6490 };
6491 };
6492 };
6493 };
6494 put?: never;
6495 post?: never;
6496 delete?: never;
6497 options?: never;
6498 head?: never;
6499 patch?: never;
6500 trace?: never;
6501 };
6502 "/api/v1/inference/tier-config": {
6503 parameters: {
6504 query?: never;
6505 header?: never;
6506 path?: never;
6507 cookie?: never;
6508 };
6509 /**
6510 * Operator's tier policy (deployment-level disclosure)
6511 * @description User-accessible read of allowed_tiers_global + the default /
6512 * privileged minima. The admin write surface lives at
6513 * ``PATCH /api/v1/admin/tier-policy``.
6514 */
6515 get: {
6516 parameters: {
6517 query?: never;
6518 header?: never;
6519 path?: never;
6520 cookie?: never;
6521 };
6522 requestBody?: never;
6523 responses: {
6524 /** @description Tier policy snapshot */
6525 200: {
6526 headers: {
6527 [name: string]: unknown;
6528 };
6529 content: {
6530 "application/json": components["schemas"]["TierConfigResponse"];
6531 };
6532 };
6533 };
6534 };
6535 put?: never;
6536 post?: never;
6537 delete?: never;
6538 options?: never;
6539 head?: never;
6540 patch?: never;
6541 trace?: never;
6542 };
6543 "/api/v1/inference/override-tier-floor": {
6544 parameters: {
6545 query?: never;
6546 header?: never;
6547 path?: never;
6548 cookie?: never;
6549 };
6550 get?: never;
6551 put?: never;
6552 /**
6553 * Re-run a refused inference with the tier floor lifted (admin only)
6554 * @description Wave D.1 T4. Admin-only override that re-runs the original user
6555 * prompt with ``tier_floor=None`` for this turn. Looks up the
6556 * ``kind='refusal'`` message at ``message_id``, finds the
6557 * immediately preceding ``kind='user'`` message in the same chat,
6558 * and forwards both to the gateway with no project floor and no
6559 * per-call minimum. Persists a new ``kind='ai'`` ``Message`` row
6560 * and writes an ``audit_log`` row with the operator-supplied
6561 * ``reason``. Returns the new message + the routing-log row id
6562 * the gateway wrote.
6563 *
6564 * M1 binds to the ``is_admin`` role. Per-user
6565 * ``override_tier_floor`` capability is deferred to v1.1+.
6566 */
6567 post: {
6568 parameters: {
6569 query?: never;
6570 header?: never;
6571 path?: never;
6572 cookie?: never;
6573 };
6574 requestBody: {
6575 content: {
6576 "application/json": {
6577 /** Format: uuid */
6578 message_id: string;
6579 reason: string;
6580 };
6581 };
6582 };
6583 responses: {
6584 /** @description Re-run completed; new ``kind='ai'`` message persisted. */
6585 200: {
6586 headers: {
6587 [name: string]: unknown;
6588 };
6589 content: {
6590 "application/json": {
6591 ai_message: components["schemas"]["Message"];
6592 /** Format: uuid */
6593 routing_log_id?: string | null;
6594 };
6595 };
6596 };
6597 /** @description Caller is not an admin. */
6598 403: {
6599 headers: {
6600 [name: string]: unknown;
6601 };
6602 content: {
6603 "application/json": components["schemas"]["Error"];
6604 };
6605 };
6606 /** @description ``message_id`` does not resolve to a refusal row. */
6607 404: {
6608 headers: {
6609 [name: string]: unknown;
6610 };
6611 content: {
6612 "application/json": components["schemas"]["Error"];
6613 };
6614 };
6615 };
6616 };
6617 delete?: never;
6618 options?: never;
6619 head?: never;
6620 patch?: never;
6621 trace?: never;
6622 };
6623 "/api/v1/chats/{chat_id}/receipts": {
6624 parameters: {
6625 query?: never;
6626 header?: never;
6627 path?: never;
6628 cookie?: never;
6629 };
6630 /**
6631 * Chronological event log for a chat (replay-at-read)
6632 * @description Wave D.1 T5 (spec §7.6). Merges chronological events from four
6633 * source tables into a single timestamp-ordered stream:
6634 *
6635 * * ``messages`` (``kind='message'``)
6636 * * ``messages.applied_skills`` (one event per skill name; ``kind='skill'``)
6637 * * ``inference_routing_log`` (``kind='inference'`` or ``kind='error'`` if ``refused=True``)
6638 * * ``audit_log`` (``kind='audit'`` or ``kind='retrieval'`` if ``action='inference.kb_chunks_retrieved'``)
6639 *
6640 * Owner-of-chat or admin only. Replay-at-read for M1 — chats are
6641 * bounded (<100 events typical). A materialized ``chat_receipts``
6642 * projection is a v1.1+ candidate if latency degrades.
6643 */
6644 get: {
6645 parameters: {
6646 query?: {
6647 /**
6648 * @description Comma-separated subset of: ``message``, ``inference``,
6649 * ``audit``, ``skill``, ``retrieval``, ``error``. Omit for
6650 * all kinds. Unknown tokens are silently ignored.
6651 */
6652 event_kinds?: string;
6653 };
6654 header?: never;
6655 path: {
6656 chat_id: string;
6657 };
6658 cookie?: never;
6659 };
6660 requestBody?: never;
6661 responses: {
6662 /** @description Chronological list of receipt events, oldest first. */
6663 200: {
6664 headers: {
6665 [name: string]: unknown;
6666 };
6667 content: {
6668 "application/json": {
6669 /** Format: date-time */
6670 ts: string;
6671 /** @enum {string} */
6672 kind: "message" | "inference" | "audit" | "skill" | "retrieval" | "error";
6673 detail: {
6674 [key: string]: unknown;
6675 };
6676 }[];
6677 };
6678 };
6679 /** @description Caller is not the chat owner or an admin. */
6680 403: {
6681 headers: {
6682 [name: string]: unknown;
6683 };
6684 content: {
6685 "application/json": components["schemas"]["Error"];
6686 };
6687 };
6688 /** @description Chat not found. */
6689 404: {
6690 headers: {
6691 [name: string]: unknown;
6692 };
6693 content: {
6694 "application/json": components["schemas"]["Error"];
6695 };
6696 };
6697 };
6698 };
6699 put?: never;
6700 post?: never;
6701 delete?: never;
6702 options?: never;
6703 head?: never;
6704 patch?: never;
6705 trace?: never;
6706 };
6707 "/api/v1/chats/{chat_id}/receipts/export.jsonl": {
6708 parameters: {
6709 query?: never;
6710 header?: never;
6711 path?: never;
6712 cookie?: never;
6713 };
6714 /** Export receipts as JSONL (one event per line) */
6715 get: {
6716 parameters: {
6717 query?: {
6718 /** @description Comma-separated subset (same as the JSON endpoint) */
6719 event_kinds?: string;
6720 };
6721 header?: never;
6722 path: {
6723 chat_id: string;
6724 };
6725 cookie?: never;
6726 };
6727 requestBody?: never;
6728 responses: {
6729 /** @description JSONL stream — one event per line */
6730 200: {
6731 headers: {
6732 [name: string]: unknown;
6733 };
6734 content: {
6735 "application/jsonl": string;
6736 };
6737 };
6738 /** @description Forbidden — not the owner or admin */
6739 403: {
6740 headers: {
6741 [name: string]: unknown;
6742 };
6743 content?: never;
6744 };
6745 /** @description Chat not found */
6746 404: {
6747 headers: {
6748 [name: string]: unknown;
6749 };
6750 content?: never;
6751 };
6752 };
6753 };
6754 put?: never;
6755 post?: never;
6756 delete?: never;
6757 options?: never;
6758 head?: never;
6759 patch?: never;
6760 trace?: never;
6761 };
6762 "/api/v1/integrations/slack/workspaces": {
6763 parameters: {
6764 query?: never;
6765 header?: never;
6766 path?: never;
6767 cookie?: never;
6768 };
6769 get?: never;
6770 put?: never;
6771 /**
6772 * Persist a Slack workspace from the slack-bridge OAuth flow (M3-D1)
6773 * @description Service-to-service endpoint. Authenticated by a shared
6774 * `Authorization: Bearer ${LQ_AI_BRIDGE_TOKEN}` token (NOT a user
6775 * JWT) — the slack-bridge is the only intended caller.
6776 *
6777 * The bridge POSTs the workspace tuple after the operator
6778 * completes the Slack OAuth install flow at
6779 * `${LQ_AI_BRIDGE_PUBLIC_URL}/slack/oauth/install`. The api
6780 * encrypts the bot token at rest under `LQ_AI_BRIDGE_MASTER_KEY`
6781 * (a separate Fernet master key from the gateway's
6782 * `LQ_AI_GATEWAY_MASTER_KEY` — different threat models) and
6783 * upserts on `team_id` so a re-install (Slack rotates bot tokens
6784 * on re-install) replaces the old ciphertext + scope + installer
6785 * in place. Soft-deleted rows revive on re-install.
6786 *
6787 * 500 (not 401) is the response when the operator hasn't set
6788 * `LQ_AI_BRIDGE_TOKEN` on the api — accepting bridge traffic
6789 * with no enforced secret would silently break the trust
6790 * contract.
6791 */
6792 post: {
6793 parameters: {
6794 query?: never;
6795 header?: never;
6796 path?: never;
6797 cookie?: never;
6798 };
6799 requestBody: {
6800 content: {
6801 "application/json": components["schemas"]["SlackWorkspaceCreate"];
6802 };
6803 };
6804 responses: {
6805 /** @description Persisted (or upserted) the workspace record */
6806 201: {
6807 headers: {
6808 [name: string]: unknown;
6809 };
6810 content: {
6811 "application/json": components["schemas"]["SlackWorkspaceResponse"];
6812 };
6813 };
6814 /** @description Missing, malformed, or non-matching bridge bearer token */
6815 401: {
6816 headers: {
6817 [name: string]: unknown;
6818 };
6819 content?: never;
6820 };
6821 /** @description Operator has not configured `LQ_AI_BRIDGE_TOKEN` on the api */
6822 500: {
6823 headers: {
6824 [name: string]: unknown;
6825 };
6826 content?: never;
6827 };
6828 };
6829 };
6830 delete?: never;
6831 options?: never;
6832 head?: never;
6833 patch?: never;
6834 trace?: never;
6835 };
6836 "/api/v1/integrations/teams/tenants": {
6837 parameters: {
6838 query?: never;
6839 header?: never;
6840 path?: never;
6841 cookie?: never;
6842 };
6843 get?: never;
6844 put?: never;
6845 /**
6846 * Persist a Microsoft 365 tenant from the teams-bridge OAuth flow (M3-D3)
6847 * @description Service-to-service endpoint. Authenticated by the **same**
6848 * `Authorization: Bearer ${LQ_AI_BRIDGE_TOKEN}` bearer as the
6849 * slack-bridge endpoint per M3-D3 decision #2 — one shared
6850 * secret authenticates every bridge → api call.
6851 *
6852 * Unlike the Slack equivalent, no bot token is persisted here:
6853 * Microsoft Teams uses operator-supplied APP-LEVEL bot
6854 * credentials (one `MICROSOFT_APP_ID` per deployment) not
6855 * per-tenant tokens. The persisted record carries only the
6856 * tenant id + display name + installer object id.
6857 *
6858 * Upserts on `tenant_id`. Soft-deleted rows revive on
6859 * re-install. `installed_at` is NOT moved by upsert (operators
6860 * can infer re-install activity from other field changes).
6861 */
6862 post: {
6863 parameters: {
6864 query?: never;
6865 header?: never;
6866 path?: never;
6867 cookie?: never;
6868 };
6869 requestBody: {
6870 content: {
6871 "application/json": components["schemas"]["TeamsTenantCreate"];
6872 };
6873 };
6874 responses: {
6875 /** @description Persisted (or upserted) the tenant record */
6876 201: {
6877 headers: {
6878 [name: string]: unknown;
6879 };
6880 content: {
6881 "application/json": components["schemas"]["TeamsTenantResponse"];
6882 };
6883 };
6884 /** @description Missing, malformed, or non-matching bridge bearer token */
6885 401: {
6886 headers: {
6887 [name: string]: unknown;
6888 };
6889 content?: never;
6890 };
6891 /** @description Operator has not configured `LQ_AI_BRIDGE_TOKEN` on the api */
6892 500: {
6893 headers: {
6894 [name: string]: unknown;
6895 };
6896 content?: never;
6897 };
6898 };
6899 };
6900 delete?: never;
6901 options?: never;
6902 head?: never;
6903 patch?: never;
6904 trace?: never;
6905 };
6906 "/api/v1/admin/intake-bridges": {
6907 parameters: {
6908 query?: never;
6909 header?: never;
6910 path?: never;
6911 cookie?: never;
6912 };
6913 /**
6914 * List live Slack + Teams installs for the admin UI (M3-D4)
6915 * @description Returns non-soft-deleted Slack workspaces and Teams tenants,
6916 * sorted by `installed_at DESC` within each section. Admin-only.
6917 * Backs the SvelteKit admin page at `/lq-ai/admin/intake-bridges`.
6918 */
6919 get: {
6920 parameters: {
6921 query?: never;
6922 header?: never;
6923 path?: never;
6924 cookie?: never;
6925 };
6926 requestBody?: never;
6927 responses: {
6928 /** @description Live install list */
6929 200: {
6930 headers: {
6931 [name: string]: unknown;
6932 };
6933 content: {
6934 "application/json": components["schemas"]["IntakeBridgesList"];
6935 };
6936 };
6937 /** @description Not authenticated */
6938 401: {
6939 headers: {
6940 [name: string]: unknown;
6941 };
6942 content?: never;
6943 };
6944 /** @description Authenticated but not an admin */
6945 403: {
6946 headers: {
6947 [name: string]: unknown;
6948 };
6949 content?: never;
6950 };
6951 };
6952 };
6953 put?: never;
6954 post?: never;
6955 delete?: never;
6956 options?: never;
6957 head?: never;
6958 patch?: never;
6959 trace?: never;
6960 };
6961 "/api/v1/admin/intake-bridges/slack/{workspace_id}": {
6962 parameters: {
6963 query?: never;
6964 header?: never;
6965 path?: never;
6966 cookie?: never;
6967 };
6968 get?: never;
6969 put?: never;
6970 post?: never;
6971 /**
6972 * Soft-delete a Slack workspace install (M3-D4)
6973 * @description Sets `deleted_at` to now. The row stays in the DB so a
6974 * re-install via the slack-bridge OAuth flow revives it in
6975 * place per the M3-D1 upsert semantics; the install history
6976 * is preserved across disconnect/reconnect cycles.
6977 */
6978 delete: {
6979 parameters: {
6980 query?: never;
6981 header?: never;
6982 path: {
6983 workspace_id: string;
6984 };
6985 cookie?: never;
6986 };
6987 requestBody?: never;
6988 responses: {
6989 /** @description Soft-deleted */
6990 204: {
6991 headers: {
6992 [name: string]: unknown;
6993 };
6994 content?: never;
6995 };
6996 /** @description Not authenticated */
6997 401: {
6998 headers: {
6999 [name: string]: unknown;
7000 };
7001 content?: never;
7002 };
7003 /** @description Authenticated but not an admin */
7004 403: {
7005 headers: {
7006 [name: string]: unknown;
7007 };
7008 content?: never;
7009 };
7010 /** @description Workspace not found or already disconnected */
7011 404: {
7012 headers: {
7013 [name: string]: unknown;
7014 };
7015 content?: never;
7016 };
7017 };
7018 };
7019 options?: never;
7020 head?: never;
7021 patch?: never;
7022 trace?: never;
7023 };
7024 "/api/v1/admin/intake-bridges/teams/{tenant_id}": {
7025 parameters: {
7026 query?: never;
7027 header?: never;
7028 path?: never;
7029 cookie?: never;
7030 };
7031 get?: never;
7032 put?: never;
7033 post?: never;
7034 /**
7035 * Soft-delete a Microsoft 365 tenant install (M3-D4)
7036 * @description Same posture as the Slack endpoint above — soft-delete with
7037 * revivable row.
7038 */
7039 delete: {
7040 parameters: {
7041 query?: never;
7042 header?: never;
7043 path: {
7044 tenant_id: string;
7045 };
7046 cookie?: never;
7047 };
7048 requestBody?: never;
7049 responses: {
7050 /** @description Soft-deleted */
7051 204: {
7052 headers: {
7053 [name: string]: unknown;
7054 };
7055 content?: never;
7056 };
7057 /** @description Not authenticated */
7058 401: {
7059 headers: {
7060 [name: string]: unknown;
7061 };
7062 content?: never;
7063 };
7064 /** @description Authenticated but not an admin */
7065 403: {
7066 headers: {
7067 [name: string]: unknown;
7068 };
7069 content?: never;
7070 };
7071 /** @description Tenant not found or already disconnected */
7072 404: {
7073 headers: {
7074 [name: string]: unknown;
7075 };
7076 content?: never;
7077 };
7078 };
7079 };
7080 options?: never;
7081 head?: never;
7082 patch?: never;
7083 trace?: never;
7084 };
7085 "/api/v1/autonomous/sessions": {
7086 parameters: {
7087 query?: never;
7088 header?: never;
7089 path?: never;
7090 cookie?: never;
7091 };
7092 /**
7093 * List the calling user's autonomous sessions (newest first, paginated)
7094 * @description Returns the caller's sessions ordered by ``created_at DESC``.
7095 * ``limit`` is clamped to [1, 200]; ``offset`` to [0, ∞).
7096 * Only the caller's own sessions are returned — cross-user isolation
7097 * is enforced at the query level.
7098 */
7099 get: {
7100 parameters: {
7101 query?: {
7102 /** @description Maximum number of sessions to return (clamped to [1, 200]). */
7103 limit?: number;
7104 /** @description Zero-based index of the first result to return. */
7105 offset?: number;
7106 };
7107 header?: never;
7108 path?: never;
7109 cookie?: never;
7110 };
7111 requestBody?: never;
7112 responses: {
7113 /** @description Paginated list of autonomous sessions */
7114 200: {
7115 headers: {
7116 [name: string]: unknown;
7117 };
7118 content: {
7119 "application/json": components["schemas"]["AutonomousSessionListResponse"];
7120 };
7121 };
7122 /** @description Not authenticated */
7123 401: {
7124 headers: {
7125 [name: string]: unknown;
7126 };
7127 content?: never;
7128 };
7129 };
7130 };
7131 put?: never;
7132 post?: never;
7133 delete?: never;
7134 options?: never;
7135 head?: never;
7136 patch?: never;
7137 trace?: never;
7138 };
7139 "/api/v1/autonomous/sessions/{session_id}": {
7140 parameters: {
7141 query?: never;
7142 header?: never;
7143 path?: never;
7144 cookie?: never;
7145 };
7146 /**
7147 * Fetch a single autonomous session with its full receipt
7148 * @description Returns the session plus a live-reconstructed receipt built from
7149 * audit rows (works for running and completed sessions). A completed
7150 * session also has the receipt persisted in ``result``.
7151 *
7152 * Another user's ``session_id`` returns 404 (not 403) to avoid
7153 * existence disclosure.
7154 */
7155 get: {
7156 parameters: {
7157 query?: never;
7158 header?: never;
7159 path: {
7160 session_id: string;
7161 };
7162 cookie?: never;
7163 };
7164 requestBody?: never;
7165 responses: {
7166 /** @description Session detail with receipt */
7167 200: {
7168 headers: {
7169 [name: string]: unknown;
7170 };
7171 content: {
7172 "application/json": components["schemas"]["AutonomousSessionDetailResponse"];
7173 };
7174 };
7175 /** @description Not authenticated */
7176 401: {
7177 headers: {
7178 [name: string]: unknown;
7179 };
7180 content?: never;
7181 };
7182 /** @description Session not found */
7183 404: {
7184 headers: {
7185 [name: string]: unknown;
7186 };
7187 content?: never;
7188 };
7189 };
7190 };
7191 put?: never;
7192 post?: never;
7193 delete?: never;
7194 options?: never;
7195 head?: never;
7196 patch?: never;
7197 trace?: never;
7198 };
7199 "/api/v1/autonomous/sessions/{session_id}/findings": {
7200 parameters: {
7201 query?: never;
7202 header?: never;
7203 path?: never;
7204 cookie?: never;
7205 };
7206 /**
7207 * List a session's persisted findings (work-product, stable order)
7208 * @description Returns the run's persisted findings (the ``emit_finding`` chokepoint
7209 * work-product) ordered by ``created_at ASC, id ASC``. Rows a run
7210 * emits in its single executor commit typically share one
7211 * ``created_at`` (transaction-stable ``now()``), so ``id`` is the
7212 * deterministic tiebreaker that keeps pagination stable — a
7213 * repeatable order, not a guaranteed emission sequence. This differs
7214 * intentionally from the newest-first autonomous lists: these are one
7215 * run's output.
7216 *
7217 * Owner-gated by loading the owned session first (the findings table
7218 * has no ``user_id`` — authz is via the parent session). Another
7219 * user's ``session_id`` — or a missing one — returns 404 (not 403) to
7220 * avoid existence disclosure. ``limit`` is clamped to [1, 200];
7221 * ``offset`` to [0, ∞).
7222 */
7223 get: {
7224 parameters: {
7225 query?: {
7226 /** @description Maximum number of findings to return (clamped to [1, 200]). */
7227 limit?: number;
7228 /** @description Zero-based index of the first result to return. */
7229 offset?: number;
7230 };
7231 header?: never;
7232 path: {
7233 session_id: string;
7234 };
7235 cookie?: never;
7236 };
7237 requestBody?: never;
7238 responses: {
7239 /** @description Paginated list of the session's findings (stable created_at, id order) */
7240 200: {
7241 headers: {
7242 [name: string]: unknown;
7243 };
7244 content: {
7245 "application/json": components["schemas"]["AutonomousFindingListResponse"];
7246 };
7247 };
7248 /** @description Not authenticated */
7249 401: {
7250 headers: {
7251 [name: string]: unknown;
7252 };
7253 content?: never;
7254 };
7255 /** @description Session not found */
7256 404: {
7257 headers: {
7258 [name: string]: unknown;
7259 };
7260 content?: never;
7261 };
7262 };
7263 };
7264 put?: never;
7265 post?: never;
7266 delete?: never;
7267 options?: never;
7268 head?: never;
7269 patch?: never;
7270 trace?: never;
7271 };
7272 "/api/v1/autonomous/sessions/{session_id}/artifacts": {
7273 parameters: {
7274 query?: never;
7275 header?: never;
7276 path?: never;
7277 cookie?: never;
7278 };
7279 /**
7280 * List a session's persisted document-grade artifacts (work-product, stable order)
7281 * @description Returns the run's persisted artifact references (the
7282 * ``emit_artifact`` chokepoint work-product — markdown memos an
7283 * opted-in run saved into its target knowledge base as real
7284 * documents) ordered by ``created_at ASC, id ASC`` — one run's rows
7285 * typically share ``created_at`` (transaction-stable ``now()``), so
7286 * ``id`` is the deterministic tiebreaker that keeps pagination
7287 * stable; a repeatable order, not a guaranteed emission sequence.
7288 * Mirrors the findings read above.
7289 *
7290 * Owner-gated by loading the owned session first (the artifacts
7291 * table has no ``user_id`` — authz is via the parent session).
7292 * Another user's ``session_id`` — or a missing one — returns 404
7293 * (not 403) to avoid existence disclosure. ``limit`` is clamped to
7294 * [1, 200]; ``offset`` to [0, ∞).
7295 *
7296 * ``document_id`` is enriched at read time via the unique
7297 * ``documents.file_id``. Deletion semantics: a hard file-delete
7298 * SET-NULLs ``file_id`` (name/size metadata survives; both refs
7299 * return null); deleting the session removes these reference rows
7300 * but never the KB document — the document outlives the session.
7301 */
7302 get: {
7303 parameters: {
7304 query?: {
7305 /** @description Maximum number of artifacts to return (clamped to [1, 200]). */
7306 limit?: number;
7307 /** @description Zero-based index of the first result to return. */
7308 offset?: number;
7309 };
7310 header?: never;
7311 path: {
7312 session_id: string;
7313 };
7314 cookie?: never;
7315 };
7316 requestBody?: never;
7317 responses: {
7318 /** @description Paginated list of the session's artifact references (stable created_at, id order) */
7319 200: {
7320 headers: {
7321 [name: string]: unknown;
7322 };
7323 content: {
7324 "application/json": components["schemas"]["AutonomousArtifactListResponse"];
7325 };
7326 };
7327 /** @description Not authenticated */
7328 401: {
7329 headers: {
7330 [name: string]: unknown;
7331 };
7332 content?: never;
7333 };
7334 /** @description Session not found */
7335 404: {
7336 headers: {
7337 [name: string]: unknown;
7338 };
7339 content?: never;
7340 };
7341 };
7342 };
7343 put?: never;
7344 post?: never;
7345 delete?: never;
7346 options?: never;
7347 head?: never;
7348 patch?: never;
7349 trace?: never;
7350 };
7351 "/api/v1/autonomous/sessions/{session_id}/halt": {
7352 parameters: {
7353 query?: never;
7354 header?: never;
7355 path?: never;
7356 cookie?: never;
7357 };
7358 get?: never;
7359 put?: never;
7360 /**
7361 * Request an immediate halt for an autonomous session (idempotent)
7362 * @description Sets ``halt_state = 'halt_requested'`` so the executor's R5 temporal
7363 * brake trips on the next tool call and transitions the session to
7364 * ``halted``.
7365 *
7366 * **Idempotent:** if ``halt_state`` is already ``halt_requested`` or
7367 * ``halted``, the endpoint returns the current session state with 200
7368 * and writes no duplicate audit row.
7369 *
7370 * Another user's ``session_id`` returns 404 (not 403).
7371 */
7372 post: {
7373 parameters: {
7374 query?: never;
7375 header?: never;
7376 path: {
7377 session_id: string;
7378 };
7379 cookie?: never;
7380 };
7381 requestBody?: {
7382 content: {
7383 "*/*"?: never;
7384 };
7385 };
7386 responses: {
7387 /** @description Updated session (halt requested or already halted) */
7388 200: {
7389 headers: {
7390 [name: string]: unknown;
7391 };
7392 content: {
7393 "application/json": components["schemas"]["AutonomousSessionRead"];
7394 };
7395 };
7396 /** @description Not authenticated */
7397 401: {
7398 headers: {
7399 [name: string]: unknown;
7400 };
7401 content?: never;
7402 };
7403 /** @description Session not found */
7404 404: {
7405 headers: {
7406 [name: string]: unknown;
7407 };
7408 content?: never;
7409 };
7410 };
7411 };
7412 delete?: never;
7413 options?: never;
7414 head?: never;
7415 patch?: never;
7416 trace?: never;
7417 };
7418 "/api/v1/autonomous/memory": {
7419 parameters: {
7420 query?: never;
7421 header?: never;
7422 path?: never;
7423 cookie?: never;
7424 };
7425 /**
7426 * List the calling user's autonomous memory entries (non-deleted, newest first)
7427 * @description Returns the caller's non-deleted memory entries ordered by
7428 * ``created_at DESC``. Pass ``?state=proposed|kept|dismissed`` to
7429 * filter by review state; omitting ``state`` returns all non-deleted
7430 * entries. ``limit`` is clamped to [1, 200]; ``offset`` to [0, ∞).
7431 */
7432 get: {
7433 parameters: {
7434 query?: {
7435 /** @description Filter by review state; omit to return all non-deleted entries. */
7436 state?: "proposed" | "kept" | "dismissed";
7437 /** @description Narrow to the memories a specific run proposed; omit for all. */
7438 source_session_id?: string;
7439 /** @description Maximum number of entries to return (clamped to [1, 200]). */
7440 limit?: number;
7441 /** @description Zero-based index of the first result to return. */
7442 offset?: number;
7443 };
7444 header?: never;
7445 path?: never;
7446 cookie?: never;
7447 };
7448 requestBody?: never;
7449 responses: {
7450 /** @description Paginated list of autonomous memory entries */
7451 200: {
7452 headers: {
7453 [name: string]: unknown;
7454 };
7455 content: {
7456 "application/json": components["schemas"]["AutonomousMemoryListResponse"];
7457 };
7458 };
7459 /** @description Not authenticated */
7460 401: {
7461 headers: {
7462 [name: string]: unknown;
7463 };
7464 content?: never;
7465 };
7466 };
7467 };
7468 put?: never;
7469 post?: never;
7470 delete?: never;
7471 options?: never;
7472 head?: never;
7473 patch?: never;
7474 trace?: never;
7475 };
7476 "/api/v1/autonomous/memory/{memory_id}/keep": {
7477 parameters: {
7478 query?: never;
7479 header?: never;
7480 path?: never;
7481 cookie?: never;
7482 };
7483 get?: never;
7484 put?: never;
7485 /**
7486 * Keep (approve) an autonomous memory entry; optional edit-on-keep
7487 * @description Transitions ``proposed`` or ``dismissed`` → ``kept``. If the
7488 * optional body supplies ``content``, the entry's text is overwritten
7489 * (edit-on-keep).
7490 *
7491 * **Re-keep semantics:** if the entry is already ``kept``, the action
7492 * is allowed — content is updated if provided; ``kept_at`` is
7493 * preserved.
7494 *
7495 * Another user's ``memory_id`` returns 404. Audited.
7496 */
7497 post: {
7498 parameters: {
7499 query?: never;
7500 header?: never;
7501 path: {
7502 memory_id: string;
7503 };
7504 cookie?: never;
7505 };
7506 requestBody?: {
7507 content: {
7508 "application/json": components["schemas"]["MemoryKeepRequest"];
7509 };
7510 };
7511 responses: {
7512 /** @description Updated memory entry (now kept) */
7513 200: {
7514 headers: {
7515 [name: string]: unknown;
7516 };
7517 content: {
7518 "application/json": components["schemas"]["AutonomousMemoryRead"];
7519 };
7520 };
7521 /** @description Not authenticated */
7522 401: {
7523 headers: {
7524 [name: string]: unknown;
7525 };
7526 content?: never;
7527 };
7528 /** @description Memory entry not found */
7529 404: {
7530 headers: {
7531 [name: string]: unknown;
7532 };
7533 content?: never;
7534 };
7535 };
7536 };
7537 delete?: never;
7538 options?: never;
7539 head?: never;
7540 patch?: never;
7541 trace?: never;
7542 };
7543 "/api/v1/autonomous/memory/{memory_id}/dismiss": {
7544 parameters: {
7545 query?: never;
7546 header?: never;
7547 path?: never;
7548 cookie?: never;
7549 };
7550 get?: never;
7551 put?: never;
7552 /**
7553 * Dismiss an autonomous memory entry
7554 * @description Transitions ``proposed`` or ``kept`` → ``dismissed``.
7555 *
7556 * Another user's ``memory_id`` returns 404. Audited.
7557 */
7558 post: {
7559 parameters: {
7560 query?: never;
7561 header?: never;
7562 path: {
7563 memory_id: string;
7564 };
7565 cookie?: never;
7566 };
7567 requestBody?: never;
7568 responses: {
7569 /** @description Updated memory entry (now dismissed) */
7570 200: {
7571 headers: {
7572 [name: string]: unknown;
7573 };
7574 content: {
7575 "application/json": components["schemas"]["AutonomousMemoryRead"];
7576 };
7577 };
7578 /** @description Not authenticated */
7579 401: {
7580 headers: {
7581 [name: string]: unknown;
7582 };
7583 content?: never;
7584 };
7585 /** @description Memory entry not found */
7586 404: {
7587 headers: {
7588 [name: string]: unknown;
7589 };
7590 content?: never;
7591 };
7592 };
7593 };
7594 delete?: never;
7595 options?: never;
7596 head?: never;
7597 patch?: never;
7598 trace?: never;
7599 };
7600 "/api/v1/autonomous/memory/{memory_id}": {
7601 parameters: {
7602 query?: never;
7603 header?: never;
7604 path?: never;
7605 cookie?: never;
7606 };
7607 get?: never;
7608 put?: never;
7609 post?: never;
7610 /**
7611 * Soft-delete an autonomous memory entry (returns 200 with updated entry)
7612 * @description Soft-deletes the entry by setting ``deleted_at=now(UTC)``.
7613 * Returns **200** with the updated (deleted) entry — not 204 — to
7614 * avoid the FastAPI JSONResponse/204 assertion pitfall.
7615 *
7616 * A subsequent GET excludes the entry; keep/dismiss/delete on a
7617 * deleted entry return 404.
7618 *
7619 * Another user's ``memory_id`` returns 404. Audited.
7620 */
7621 delete: {
7622 parameters: {
7623 query?: never;
7624 header?: never;
7625 path: {
7626 memory_id: string;
7627 };
7628 cookie?: never;
7629 };
7630 requestBody?: never;
7631 responses: {
7632 /** @description Soft-deleted memory entry */
7633 200: {
7634 headers: {
7635 [name: string]: unknown;
7636 };
7637 content: {
7638 "application/json": components["schemas"]["AutonomousMemoryRead"];
7639 };
7640 };
7641 /** @description Not authenticated */
7642 401: {
7643 headers: {
7644 [name: string]: unknown;
7645 };
7646 content?: never;
7647 };
7648 /** @description Memory entry not found */
7649 404: {
7650 headers: {
7651 [name: string]: unknown;
7652 };
7653 content?: never;
7654 };
7655 };
7656 };
7657 options?: never;
7658 head?: never;
7659 patch?: never;
7660 trace?: never;
7661 };
7662 "/api/v1/autonomous/precedents": {
7663 parameters: {
7664 query?: never;
7665 header?: never;
7666 path?: never;
7667 cookie?: never;
7668 };
7669 /**
7670 * List the calling user's precedent entries (non-dismissed, newest first)
7671 * @description Returns the caller's non-dismissed precedent entries
7672 * (``dismissed_at IS NULL``) ordered by ``created_at DESC``. Pass
7673 * ``?pattern_kind=`` to filter to one classifier; omitting it returns
7674 * all non-dismissed entries. ``limit`` is clamped to [1, 200];
7675 * ``offset`` to [0, ∞).
7676 */
7677 get: {
7678 parameters: {
7679 query?: {
7680 /** @description Filter by precedent classifier; omit to return all non-dismissed entries. */
7681 pattern_kind?: string;
7682 /** @description Maximum number of entries to return (clamped to [1, 200]). */
7683 limit?: number;
7684 /** @description Zero-based index of the first result to return. */
7685 offset?: number;
7686 };
7687 header?: never;
7688 path?: never;
7689 cookie?: never;
7690 };
7691 requestBody?: never;
7692 responses: {
7693 /** @description Paginated list of precedent entries */
7694 200: {
7695 headers: {
7696 [name: string]: unknown;
7697 };
7698 content: {
7699 "application/json": components["schemas"]["PrecedentEntryListResponse"];
7700 };
7701 };
7702 /** @description Not authenticated */
7703 401: {
7704 headers: {
7705 [name: string]: unknown;
7706 };
7707 content?: never;
7708 };
7709 };
7710 };
7711 put?: never;
7712 post?: never;
7713 delete?: never;
7714 options?: never;
7715 head?: never;
7716 patch?: never;
7717 trace?: never;
7718 };
7719 "/api/v1/autonomous/precedents/{precedent_id}/dismiss": {
7720 parameters: {
7721 query?: never;
7722 header?: never;
7723 path?: never;
7724 cookie?: never;
7725 };
7726 get?: never;
7727 put?: never;
7728 /**
7729 * Dismiss a precedent entry (idempotent)
7730 * @description Sets ``dismissed_at=now(UTC)`` so the entry drops out of the board.
7731 * Re-dismissing leaves the original ``dismissed_at`` untouched.
7732 *
7733 * Another user's ``precedent_id`` returns 404. Audited.
7734 */
7735 post: {
7736 parameters: {
7737 query?: never;
7738 header?: never;
7739 path: {
7740 precedent_id: string;
7741 };
7742 cookie?: never;
7743 };
7744 requestBody?: never;
7745 responses: {
7746 /** @description Updated precedent entry (now dismissed) */
7747 200: {
7748 headers: {
7749 [name: string]: unknown;
7750 };
7751 content: {
7752 "application/json": components["schemas"]["PrecedentEntryRead"];
7753 };
7754 };
7755 /** @description Not authenticated */
7756 401: {
7757 headers: {
7758 [name: string]: unknown;
7759 };
7760 content?: never;
7761 };
7762 /** @description Precedent entry not found */
7763 404: {
7764 headers: {
7765 [name: string]: unknown;
7766 };
7767 content?: never;
7768 };
7769 };
7770 };
7771 delete?: never;
7772 options?: never;
7773 head?: never;
7774 patch?: never;
7775 trace?: never;
7776 };
7777 "/api/v1/autonomous/precedents/{precedent_id}/promote": {
7778 parameters: {
7779 query?: never;
7780 header?: never;
7781 path?: never;
7782 cookie?: never;
7783 };
7784 get?: never;
7785 put?: never;
7786 /**
7787 * Propose promoting a precedent into a Project's context (proposal only)
7788 * @description Creates a ``proposed`` project-context proposal linking the
7789 * precedent to ``project_id``. The ``suggested_md`` snippet is
7790 * derived server-side from the precedent's ``summary``.
7791 *
7792 * This endpoint does **NOT** mutate ``projects.context_md`` —
7793 * promotion is a proposal only; the user accepting it performs the
7794 * authorized write (ADR 0013 D5).
7795 *
7796 * Another user's ``precedent_id`` — or a ``project_id`` the caller
7797 * does not own — returns 404. Audited.
7798 */
7799 post: {
7800 parameters: {
7801 query?: never;
7802 header?: never;
7803 path: {
7804 precedent_id: string;
7805 };
7806 cookie?: never;
7807 };
7808 requestBody: {
7809 content: {
7810 "application/json": components["schemas"]["PromotePrecedentRequest"];
7811 };
7812 };
7813 responses: {
7814 /** @description Proposal created */
7815 201: {
7816 headers: {
7817 [name: string]: unknown;
7818 };
7819 content: {
7820 "application/json": components["schemas"]["ProjectContextProposalRead"];
7821 };
7822 };
7823 /** @description Not authenticated */
7824 401: {
7825 headers: {
7826 [name: string]: unknown;
7827 };
7828 content?: never;
7829 };
7830 /** @description Precedent or target project not found */
7831 404: {
7832 headers: {
7833 [name: string]: unknown;
7834 };
7835 content?: never;
7836 };
7837 };
7838 };
7839 delete?: never;
7840 options?: never;
7841 head?: never;
7842 patch?: never;
7843 trace?: never;
7844 };
7845 "/api/v1/autonomous/project-context-proposals": {
7846 parameters: {
7847 query?: never;
7848 header?: never;
7849 path?: never;
7850 cookie?: never;
7851 };
7852 /**
7853 * List the calling user's project-context proposals (newest first)
7854 * @description Returns the caller's project-context proposals ordered by
7855 * ``created_at DESC``. Pass ``?state=proposed|accepted|rejected``
7856 * and/or ``?project_id=`` to filter. ``limit`` is clamped to
7857 * [1, 200]; ``offset`` to [0, ∞).
7858 */
7859 get: {
7860 parameters: {
7861 query?: {
7862 /** @description Filter by proposal state. */
7863 state?: "proposed" | "accepted" | "rejected";
7864 /** @description Filter to proposals targeting one Project. */
7865 project_id?: string;
7866 /** @description Maximum number of proposals to return (clamped to [1, 200]). */
7867 limit?: number;
7868 /** @description Zero-based index of the first result to return. */
7869 offset?: number;
7870 };
7871 header?: never;
7872 path?: never;
7873 cookie?: never;
7874 };
7875 requestBody?: never;
7876 responses: {
7877 /** @description Paginated list of project-context proposals */
7878 200: {
7879 headers: {
7880 [name: string]: unknown;
7881 };
7882 content: {
7883 "application/json": components["schemas"]["ProjectContextProposalListResponse"];
7884 };
7885 };
7886 /** @description Not authenticated */
7887 401: {
7888 headers: {
7889 [name: string]: unknown;
7890 };
7891 content?: never;
7892 };
7893 };
7894 };
7895 put?: never;
7896 post?: never;
7897 delete?: never;
7898 options?: never;
7899 head?: never;
7900 patch?: never;
7901 trace?: never;
7902 };
7903 "/api/v1/autonomous/project-context-proposals/{proposal_id}/accept": {
7904 parameters: {
7905 query?: never;
7906 header?: never;
7907 path?: never;
7908 cookie?: never;
7909 };
7910 get?: never;
7911 put?: never;
7912 /**
7913 * Accept a proposal — append the suggested context to the Project (user-authorized write)
7914 * @description The user-authorized write (ADR 0013 D5): appends the proposal's
7915 * ``suggested_md`` to the target Project's ``context_md``
7916 * (initializing it if NULL), sets ``state='accepted'`` and
7917 * ``accepted_at``.
7918 *
7919 * Idempotent on re-accept: if already ``accepted``, returns the
7920 * current state without re-appending. A ``rejected`` proposal may be
7921 * accepted (rejected→accepted) and the append occurs.
7922 *
7923 * Another user's ``proposal_id`` returns 404. Audited.
7924 */
7925 post: {
7926 parameters: {
7927 query?: never;
7928 header?: never;
7929 path: {
7930 proposal_id: string;
7931 };
7932 cookie?: never;
7933 };
7934 requestBody?: never;
7935 responses: {
7936 /** @description Updated proposal (now accepted) */
7937 200: {
7938 headers: {
7939 [name: string]: unknown;
7940 };
7941 content: {
7942 "application/json": components["schemas"]["ProjectContextProposalRead"];
7943 };
7944 };
7945 /** @description Not authenticated */
7946 401: {
7947 headers: {
7948 [name: string]: unknown;
7949 };
7950 content?: never;
7951 };
7952 /** @description Proposal not found */
7953 404: {
7954 headers: {
7955 [name: string]: unknown;
7956 };
7957 content?: never;
7958 };
7959 };
7960 };
7961 delete?: never;
7962 options?: never;
7963 head?: never;
7964 patch?: never;
7965 trace?: never;
7966 };
7967 "/api/v1/autonomous/project-context-proposals/{proposal_id}/reject": {
7968 parameters: {
7969 query?: never;
7970 header?: never;
7971 path?: never;
7972 cookie?: never;
7973 };
7974 get?: never;
7975 put?: never;
7976 /**
7977 * Reject a proposal (does not touch Project context)
7978 * @description Sets ``state='rejected'`` and ``rejected_at``. Does NOT touch
7979 * ``projects.context_md``.
7980 *
7981 * Another user's ``proposal_id`` returns 404. Audited.
7982 */
7983 post: {
7984 parameters: {
7985 query?: never;
7986 header?: never;
7987 path: {
7988 proposal_id: string;
7989 };
7990 cookie?: never;
7991 };
7992 requestBody?: never;
7993 responses: {
7994 /** @description Updated proposal (now rejected) */
7995 200: {
7996 headers: {
7997 [name: string]: unknown;
7998 };
7999 content: {
8000 "application/json": components["schemas"]["ProjectContextProposalRead"];
8001 };
8002 };
8003 /** @description Not authenticated */
8004 401: {
8005 headers: {
8006 [name: string]: unknown;
8007 };
8008 content?: never;
8009 };
8010 /** @description Proposal not found */
8011 404: {
8012 headers: {
8013 [name: string]: unknown;
8014 };
8015 content?: never;
8016 };
8017 };
8018 };
8019 delete?: never;
8020 options?: never;
8021 head?: never;
8022 patch?: never;
8023 trace?: never;
8024 };
8025 "/api/v1/autonomous/schedules": {
8026 parameters: {
8027 query?: never;
8028 header?: never;
8029 path?: never;
8030 cookie?: never;
8031 };
8032 /**
8033 * List the calling user's autonomous schedules (non-deleted, newest first)
8034 * @description Returns the caller's non-deleted schedules ordered by
8035 * ``created_at DESC``. Optional ``?enabled=`` filter.
8036 */
8037 get: {
8038 parameters: {
8039 query?: {
8040 enabled?: boolean;
8041 limit?: number;
8042 offset?: number;
8043 };
8044 header?: never;
8045 path?: never;
8046 cookie?: never;
8047 };
8048 requestBody?: never;
8049 responses: {
8050 /** @description Paginated list of schedules */
8051 200: {
8052 headers: {
8053 [name: string]: unknown;
8054 };
8055 content: {
8056 "application/json": components["schemas"]["AutonomousScheduleListResponse"];
8057 };
8058 };
8059 /** @description Not authenticated */
8060 401: {
8061 headers: {
8062 [name: string]: unknown;
8063 };
8064 content?: never;
8065 };
8066 };
8067 };
8068 put?: never;
8069 /**
8070 * Create an autonomous schedule (cron-triggered run definition)
8071 * @description Validates ``cron_expr`` (a five-field cron string); invalid
8072 * expressions return 422. Seeds ``next_run_at`` from the cron
8073 * expression so the dispatcher can pick it up. Per-user isolated.
8074 */
8075 post: {
8076 parameters: {
8077 query?: never;
8078 header?: never;
8079 path?: never;
8080 cookie?: never;
8081 };
8082 requestBody: {
8083 content: {
8084 "application/json": components["schemas"]["AutonomousScheduleCreate"];
8085 };
8086 };
8087 responses: {
8088 /** @description Schedule created */
8089 201: {
8090 headers: {
8091 [name: string]: unknown;
8092 };
8093 content: {
8094 "application/json": components["schemas"]["AutonomousScheduleRead"];
8095 };
8096 };
8097 /** @description Not authenticated */
8098 401: {
8099 headers: {
8100 [name: string]: unknown;
8101 };
8102 content?: never;
8103 };
8104 /** @description Referenced project not found */
8105 404: {
8106 headers: {
8107 [name: string]: unknown;
8108 };
8109 content?: never;
8110 };
8111 /** @description Invalid cron expression */
8112 422: {
8113 headers: {
8114 [name: string]: unknown;
8115 };
8116 content?: never;
8117 };
8118 };
8119 };
8120 delete?: never;
8121 options?: never;
8122 head?: never;
8123 patch?: never;
8124 trace?: never;
8125 };
8126 "/api/v1/autonomous/schedules/{schedule_id}": {
8127 parameters: {
8128 query?: never;
8129 header?: never;
8130 path?: never;
8131 cookie?: never;
8132 };
8133 get?: never;
8134 put?: never;
8135 post?: never;
8136 /**
8137 * Soft-delete an autonomous schedule (returns 200 with updated entity)
8138 * @description Soft-deletes by setting ``deleted_at``. Returns 200 with the
8139 * updated entity (NOT 204 — FastAPI JSONResponse/204 pitfall).
8140 * A deleted schedule is excluded from list and the dispatcher;
8141 * re-delete returns 404. Another user's ``schedule_id`` returns 404.
8142 * Audited.
8143 */
8144 delete: {
8145 parameters: {
8146 query?: never;
8147 header?: never;
8148 path: {
8149 schedule_id: string;
8150 };
8151 cookie?: never;
8152 };
8153 requestBody?: never;
8154 responses: {
8155 /** @description Updated schedule (now soft-deleted) */
8156 200: {
8157 headers: {
8158 [name: string]: unknown;
8159 };
8160 content: {
8161 "application/json": components["schemas"]["AutonomousScheduleRead"];
8162 };
8163 };
8164 /** @description Not authenticated */
8165 401: {
8166 headers: {
8167 [name: string]: unknown;
8168 };
8169 content?: never;
8170 };
8171 /** @description Schedule not found */
8172 404: {
8173 headers: {
8174 [name: string]: unknown;
8175 };
8176 content?: never;
8177 };
8178 };
8179 };
8180 options?: never;
8181 head?: never;
8182 /**
8183 * Partially update an autonomous schedule (edit / enable / disable)
8184 * @description Partial update. If ``cron_expr`` changes it is re-validated (422
8185 * on invalid) and ``next_run_at`` is recomputed. Another user's
8186 * ``schedule_id`` returns 404. Audited.
8187 */
8188 patch: {
8189 parameters: {
8190 query?: never;
8191 header?: never;
8192 path: {
8193 schedule_id: string;
8194 };
8195 cookie?: never;
8196 };
8197 requestBody: {
8198 content: {
8199 "application/json": components["schemas"]["AutonomousScheduleUpdate"];
8200 };
8201 };
8202 responses: {
8203 /** @description Updated schedule */
8204 200: {
8205 headers: {
8206 [name: string]: unknown;
8207 };
8208 content: {
8209 "application/json": components["schemas"]["AutonomousScheduleRead"];
8210 };
8211 };
8212 /** @description Not authenticated */
8213 401: {
8214 headers: {
8215 [name: string]: unknown;
8216 };
8217 content?: never;
8218 };
8219 /** @description Schedule or referenced project not found */
8220 404: {
8221 headers: {
8222 [name: string]: unknown;
8223 };
8224 content?: never;
8225 };
8226 /** @description Invalid cron expression */
8227 422: {
8228 headers: {
8229 [name: string]: unknown;
8230 };
8231 content?: never;
8232 };
8233 };
8234 };
8235 trace?: never;
8236 };
8237 "/api/v1/autonomous/watches": {
8238 parameters: {
8239 query?: never;
8240 header?: never;
8241 path?: never;
8242 cookie?: never;
8243 };
8244 /**
8245 * List the calling user's autonomous watches (non-deleted, newest first)
8246 * @description Returns the caller's non-deleted watches ordered by
8247 * ``created_at DESC``. Optional ``?enabled=`` and
8248 * ``?knowledge_base_id=`` filters.
8249 */
8250 get: {
8251 parameters: {
8252 query?: {
8253 enabled?: boolean;
8254 knowledge_base_id?: string;
8255 limit?: number;
8256 offset?: number;
8257 };
8258 header?: never;
8259 path?: never;
8260 cookie?: never;
8261 };
8262 requestBody?: never;
8263 responses: {
8264 /** @description Paginated list of watches */
8265 200: {
8266 headers: {
8267 [name: string]: unknown;
8268 };
8269 content: {
8270 "application/json": components["schemas"]["AutonomousWatchListResponse"];
8271 };
8272 };
8273 /** @description Not authenticated */
8274 401: {
8275 headers: {
8276 [name: string]: unknown;
8277 };
8278 content?: never;
8279 };
8280 };
8281 };
8282 put?: never;
8283 /**
8284 * Create an autonomous watch (KB-arrival-triggered run definition)
8285 * @description Creates a watch on a knowledge base the caller owns; document
8286 * arrivals (KB attach) then spawn an autonomous session. The caller
8287 * must own the target ``knowledge_base_id`` — a KB they cannot see
8288 * returns 404 (KB-sharing is out of scope). Per-user isolated.
8289 */
8290 post: {
8291 parameters: {
8292 query?: never;
8293 header?: never;
8294 path?: never;
8295 cookie?: never;
8296 };
8297 requestBody: {
8298 content: {
8299 "application/json": components["schemas"]["AutonomousWatchCreate"];
8300 };
8301 };
8302 responses: {
8303 /** @description Watch created */
8304 201: {
8305 headers: {
8306 [name: string]: unknown;
8307 };
8308 content: {
8309 "application/json": components["schemas"]["AutonomousWatchRead"];
8310 };
8311 };
8312 /** @description Not authenticated */
8313 401: {
8314 headers: {
8315 [name: string]: unknown;
8316 };
8317 content?: never;
8318 };
8319 /** @description Target knowledge base or referenced project not found */
8320 404: {
8321 headers: {
8322 [name: string]: unknown;
8323 };
8324 content?: never;
8325 };
8326 };
8327 };
8328 delete?: never;
8329 options?: never;
8330 head?: never;
8331 patch?: never;
8332 trace?: never;
8333 };
8334 "/api/v1/autonomous/watches/{watch_id}": {
8335 parameters: {
8336 query?: never;
8337 header?: never;
8338 path?: never;
8339 cookie?: never;
8340 };
8341 get?: never;
8342 put?: never;
8343 post?: never;
8344 /**
8345 * Soft-delete an autonomous watch (returns 200 with updated entity)
8346 * @description Soft-deletes by setting ``deleted_at``. Returns 200 with the
8347 * updated entity (NOT 204 — FastAPI JSONResponse/204 pitfall).
8348 * A deleted watch is excluded from list and the KB-arrival trigger;
8349 * re-delete returns 404. Another user's ``watch_id`` returns 404.
8350 * Audited.
8351 */
8352 delete: {
8353 parameters: {
8354 query?: never;
8355 header?: never;
8356 path: {
8357 watch_id: string;
8358 };
8359 cookie?: never;
8360 };
8361 requestBody?: never;
8362 responses: {
8363 /** @description Updated watch (now soft-deleted) */
8364 200: {
8365 headers: {
8366 [name: string]: unknown;
8367 };
8368 content: {
8369 "application/json": components["schemas"]["AutonomousWatchRead"];
8370 };
8371 };
8372 /** @description Not authenticated */
8373 401: {
8374 headers: {
8375 [name: string]: unknown;
8376 };
8377 content?: never;
8378 };
8379 /** @description Watch not found */
8380 404: {
8381 headers: {
8382 [name: string]: unknown;
8383 };
8384 content?: never;
8385 };
8386 };
8387 };
8388 options?: never;
8389 head?: never;
8390 /**
8391 * Partially update an autonomous watch (enable / disable / retarget)
8392 * @description Partial update of ``enabled`` / ``playbook_id`` / ``skill_ref``.
8393 * The watch's ``knowledge_base_id`` is immutable. Another user's
8394 * ``watch_id`` returns 404. Audited.
8395 */
8396 patch: {
8397 parameters: {
8398 query?: never;
8399 header?: never;
8400 path: {
8401 watch_id: string;
8402 };
8403 cookie?: never;
8404 };
8405 requestBody: {
8406 content: {
8407 "application/json": components["schemas"]["AutonomousWatchUpdate"];
8408 };
8409 };
8410 responses: {
8411 /** @description Updated watch */
8412 200: {
8413 headers: {
8414 [name: string]: unknown;
8415 };
8416 content: {
8417 "application/json": components["schemas"]["AutonomousWatchRead"];
8418 };
8419 };
8420 /** @description Not authenticated */
8421 401: {
8422 headers: {
8423 [name: string]: unknown;
8424 };
8425 content?: never;
8426 };
8427 /** @description Watch or referenced project not found */
8428 404: {
8429 headers: {
8430 [name: string]: unknown;
8431 };
8432 content?: never;
8433 };
8434 };
8435 };
8436 trace?: never;
8437 };
8438 "/api/v1/autonomous/notifications": {
8439 parameters: {
8440 query?: never;
8441 header?: never;
8442 path?: never;
8443 cookie?: never;
8444 };
8445 /**
8446 * List the calling user's autonomous notifications (newest first)
8447 * @description Returns the caller's notifications ordered by ``created_at DESC``.
8448 * Pass ``?unread=true`` to narrow to unread rows (``read_at IS
8449 * NULL``). Per-user isolated.
8450 */
8451 get: {
8452 parameters: {
8453 query?: {
8454 unread?: boolean;
8455 limit?: number;
8456 offset?: number;
8457 };
8458 header?: never;
8459 path?: never;
8460 cookie?: never;
8461 };
8462 requestBody?: never;
8463 responses: {
8464 /** @description Paginated list of notifications */
8465 200: {
8466 headers: {
8467 [name: string]: unknown;
8468 };
8469 content: {
8470 "application/json": components["schemas"]["AutonomousNotificationListResponse"];
8471 };
8472 };
8473 /** @description Not authenticated */
8474 401: {
8475 headers: {
8476 [name: string]: unknown;
8477 };
8478 content?: never;
8479 };
8480 };
8481 };
8482 put?: never;
8483 post?: never;
8484 delete?: never;
8485 options?: never;
8486 head?: never;
8487 patch?: never;
8488 trace?: never;
8489 };
8490 "/api/v1/autonomous/notifications/{notification_id}/read": {
8491 parameters: {
8492 query?: never;
8493 header?: never;
8494 path?: never;
8495 cookie?: never;
8496 };
8497 get?: never;
8498 put?: never;
8499 /**
8500 * Mark an autonomous notification read (the dismiss action; idempotent)
8501 * @description Sets ``read_at`` if currently NULL; idempotent (re-read preserves
8502 * the original timestamp). "Read" IS the dismiss action — a read
8503 * notification drops out of ``?unread=true``. Another user's
8504 * ``notification_id`` returns 404. Audited.
8505 */
8506 post: {
8507 parameters: {
8508 query?: never;
8509 header?: never;
8510 path: {
8511 notification_id: string;
8512 };
8513 cookie?: never;
8514 };
8515 requestBody?: never;
8516 responses: {
8517 /** @description Updated notification (now read) */
8518 200: {
8519 headers: {
8520 [name: string]: unknown;
8521 };
8522 content: {
8523 "application/json": components["schemas"]["AutonomousNotificationRead"];
8524 };
8525 };
8526 /** @description Not authenticated */
8527 401: {
8528 headers: {
8529 [name: string]: unknown;
8530 };
8531 content?: never;
8532 };
8533 /** @description Notification not found */
8534 404: {
8535 headers: {
8536 [name: string]: unknown;
8537 };
8538 content?: never;
8539 };
8540 };
8541 };
8542 delete?: never;
8543 options?: never;
8544 head?: never;
8545 patch?: never;
8546 trace?: never;
8547 };
8548 "/api/v1/autonomous/run-now": {
8549 parameters: {
8550 query?: never;
8551 header?: never;
8552 path?: never;
8553 cookie?: never;
8554 };
8555 get?: never;
8556 put?: never;
8557 /**
8558 * Run a skill or playbook once now (one-off manual autonomous session)
8559 * @description Spawns a single ``trigger_kind='manual'`` autonomous session so a
8560 * user can test what a skill/playbook does — and inspect the
8561 * resulting receipt — before arming it as a schedule or watch.
8562 * Exactly one of ``playbook_id`` / ``skill_ref`` must be set (zero
8563 * or both → 422). ``target_kb_id`` / ``project_id`` are optional
8564 * scope; ``max_cost_usd`` is the per-run cap (NULL falls back to the
8565 * config default so R4 always arms). Gated by opt-in
8566 * (``autonomous_enabled``); the spawned session runs under the same
8567 * R4/R5/R6 brakes as every other session. Per-user isolated. Audited.
8568 */
8569 post: {
8570 parameters: {
8571 query?: never;
8572 header?: never;
8573 path?: never;
8574 cookie?: never;
8575 };
8576 requestBody: {
8577 content: {
8578 "application/json": components["schemas"]["AutonomousManualRunRequest"];
8579 };
8580 };
8581 responses: {
8582 /** @description Session spawned */
8583 201: {
8584 headers: {
8585 [name: string]: unknown;
8586 };
8587 content: {
8588 "application/json": components["schemas"]["AutonomousSessionRead"];
8589 };
8590 };
8591 /** @description Not authenticated */
8592 401: {
8593 headers: {
8594 [name: string]: unknown;
8595 };
8596 content?: never;
8597 };
8598 /** @description Autonomous layer not enabled for this user */
8599 403: {
8600 headers: {
8601 [name: string]: unknown;
8602 };
8603 content?: never;
8604 };
8605 /** @description Referenced project not found */
8606 404: {
8607 headers: {
8608 [name: string]: unknown;
8609 };
8610 content?: never;
8611 };
8612 /** @description Invalid target (need exactly one of playbook_id/skill_ref) */
8613 422: {
8614 headers: {
8615 [name: string]: unknown;
8616 };
8617 content?: never;
8618 };
8619 };
8620 };
8621 delete?: never;
8622 options?: never;
8623 head?: never;
8624 patch?: never;
8625 trace?: never;
8626 };
8627}
8628export type webhooks = Record<string, never>;
8629export interface components {
8630 schemas: {
8631 /**
8632 * @description Request body for `POST /api/v1/integrations/slack/workspaces`
8633 * (M3-D1). Shape matches what `slack-bridge/app/oauth.py`
8634 * constructs from Slack's `oauth.v2.access` response after the
8635 * operator completes consent. The bridge → api contract is
8636 * position-coupled to this shape; renames here would break it.
8637 */
8638 SlackWorkspaceCreate: {
8639 /**
8640 * @description Slack workspace id (T0...).
8641 * @example T01234567
8642 */
8643 team_id: string;
8644 /**
8645 * @description Workspace display name at install time.
8646 * @example Acme Legal
8647 */
8648 team_name: string;
8649 /**
8650 * @description The xoxb- bot user OAuth token Slack returned. Plaintext
8651 * on the wire (the request travels over the trusted
8652 * in-cluster network with a bridge bearer token); encrypted
8653 * under `LQ_AI_BRIDGE_MASTER_KEY` before persistence.
8654 * @example xoxb-...
8655 */
8656 bot_token: string;
8657 /**
8658 * @description Slack user id of the bot user.
8659 * @example U99BOT
8660 */
8661 bot_user_id: string;
8662 /**
8663 * @description Slack user id of the operator who completed install (audit only).
8664 * @example U11INSTALLER
8665 */
8666 installer_slack_user_id: string;
8667 /**
8668 * @description Comma-separated scope list Slack returned.
8669 * @example commands,chat:write
8670 */
8671 scope: string;
8672 };
8673 /**
8674 * @description Request body for `POST /api/v1/integrations/teams/tenants`
8675 * (M3-D3). Shape matches what `teams-bridge/app/oauth.py`
8676 * constructs from the Microsoft id_token claims (`tid`, `oid`)
8677 * + a best-effort Microsoft Graph `/organization` display-name
8678 * lookup.
8679 */
8680 TeamsTenantCreate: {
8681 /**
8682 * @description Microsoft tenant id (`tid` claim — the M365 directory GUID).
8683 * @example 00000000-0000-0000-0000-aaaaaaaaaaaa
8684 */
8685 tenant_id: string;
8686 /**
8687 * @description Tenant display name at install time. Best-effort from
8688 * Microsoft Graph `/organization`; falls back to the
8689 * tenant_id if Graph errors.
8690 * @example Acme Legal LLP
8691 */
8692 tenant_name: string;
8693 /**
8694 * @description M365 `oid` claim of the admin who completed consent.
8695 * Audit only — does not grant LQ.AI permissions.
8696 * @example 00000000-0000-0000-0000-111111111111
8697 */
8698 installer_oid: string;
8699 };
8700 /**
8701 * @description Persisted-tenant response for `POST
8702 * /api/v1/integrations/teams/tenants`. Mirrors
8703 * `SlackWorkspaceResponse` minus the per-tenant bot fields
8704 * (Teams uses app-level credentials).
8705 */
8706 TeamsTenantResponse: {
8707 /**
8708 * Format: uuid
8709 * @description api-side tenant row id.
8710 */
8711 id: string;
8712 /** @example 00000000-0000-0000-0000-aaaaaaaaaaaa */
8713 tenant_id: string;
8714 /** @example Acme Legal LLP */
8715 tenant_name: string;
8716 /** @example 00000000-0000-0000-0000-111111111111 */
8717 installer_oid: string;
8718 /**
8719 * Format: date-time
8720 * @description Original install timestamp. NOT moved by upsert.
8721 */
8722 installed_at: string;
8723 };
8724 /**
8725 * @description One live Slack workspace row in the admin intake-bridges list
8726 * (M3-D4). The wire shape mirrors the model row minus the bot
8727 * token ciphertext (which the admin UI has no business seeing).
8728 */
8729 SlackWorkspaceSummary: {
8730 /** Format: uuid */
8731 id: string;
8732 team_id: string;
8733 team_name: string;
8734 installer_slack_user_id: string;
8735 /** Format: date-time */
8736 installed_at: string;
8737 };
8738 /**
8739 * @description One live Microsoft 365 tenant row in the admin intake-bridges
8740 * list (M3-D4). Mirrors the model row.
8741 */
8742 TeamsTenantSummary: {
8743 /** Format: uuid */
8744 id: string;
8745 tenant_id: string;
8746 tenant_name: string;
8747 installer_oid: string;
8748 /** Format: date-time */
8749 installed_at: string;
8750 };
8751 /**
8752 * @description Section-split response for `GET /api/v1/admin/intake-bridges`.
8753 * Separate sections (rather than a single polymorphic list) so
8754 * the SvelteKit admin page renders two distinct sections without
8755 * client-side discriminator-key gymnastics.
8756 */
8757 IntakeBridgesList: {
8758 slack_workspaces: components["schemas"]["SlackWorkspaceSummary"][];
8759 teams_tenants: components["schemas"]["TeamsTenantSummary"][];
8760 };
8761 /**
8762 * @description Persisted-workspace response for `POST
8763 * /api/v1/integrations/slack/workspaces`. Bot token is
8764 * deliberately omitted — neither the plaintext nor the
8765 * ciphertext is echoed back to the bridge.
8766 */
8767 SlackWorkspaceResponse: {
8768 /**
8769 * Format: uuid
8770 * @description api-side workspace row id.
8771 */
8772 id: string;
8773 /** @example T01234567 */
8774 team_id: string;
8775 /** @example Acme Legal */
8776 team_name: string;
8777 /** @example U99BOT */
8778 bot_user_id: string;
8779 /** @example U11INSTALLER */
8780 installer_slack_user_id: string;
8781 /** @example commands,chat:write */
8782 scope: string;
8783 /**
8784 * Format: date-time
8785 * @description Original install timestamp. NOT moved by upsert.
8786 */
8787 installed_at: string;
8788 };
8789 /**
8790 * @description Response payload for `GET /api/v1/word-addin/version` (M3-B8).
8791 * Consumed by the task pane on mount to decide whether the
8792 * installed add-in version is compatible with this deployment.
8793 */
8794 WordAddinVersionResponse: {
8795 /**
8796 * @description LQ.AI deployment version (the api package `__version__`).
8797 * Informational — the add-in surfaces it in the "Update
8798 * needed" overlay so users can quote it to support.
8799 * @example 0.3.0
8800 */
8801 deployment_version: string;
8802 /**
8803 * @description Lowest add-in version (semver string) this deployment
8804 * accepts. The task pane refuses to render features when
8805 * its bundled version is lower.
8806 * @example 0.3.0
8807 */
8808 addin_min_compatible_version: string;
8809 /**
8810 * @description Highest add-in version this deployment recognizes. Bundles
8811 * newer than this still load (forward compatibility is
8812 * best-effort) but the add-in surfaces a soft warning so the
8813 * operator knows to update the deployment.
8814 * @example 0.3.99
8815 */
8816 addin_max_compatible_version: string;
8817 /**
8818 * Format: uri
8819 * @description Canonical URL of the task pane bundle's HTML entry point.
8820 * @example https://lq.acme.example/word-addin/taskpane.html
8821 */
8822 taskpane_bundle_url: string;
8823 /**
8824 * @description Optional SHA-256 hash of the deployed task pane bundle JS.
8825 * M3-B8 ships nullable; M3-B7 signing CI populates the value.
8826 * Null means "don't enforce" — not an error.
8827 * @example null
8828 */
8829 taskpane_bundle_hash?: string | null;
8830 };
8831 /**
8832 * @description One acceptable alternative to a position's standard language.
8833 * Ranked 1-based (1 = preferred fallback). Stored as JSONB on
8834 * ``playbook_positions.fallback_tiers`` (M3-A1).
8835 */
8836 FallbackTier: {
8837 rank: number;
8838 description: string;
8839 language: string;
8840 };
8841 /**
8842 * @description One issue in a playbook — the org's standard + fallbacks for a
8843 * clause. Mirrors the ``Position`` shape in PRD §3.7.
8844 */
8845 Position: {
8846 /** Format: uuid */
8847 id: string;
8848 issue: string;
8849 description?: string;
8850 standard_language: string;
8851 fallback_tiers?: components["schemas"]["FallbackTier"][];
8852 redline_strategy?: string;
8853 /** @enum {string} */
8854 severity_if_missing: "critical" | "high" | "medium" | "low";
8855 detection_keywords?: string[];
8856 detection_examples?: string[];
8857 position_order?: number;
8858 };
8859 /**
8860 * @description Full playbook — header plus ordered positions. Mirrors PRD §3.7.
8861 * ``GET /api/v1/playbooks`` returns this shape with ``positions``
8862 * empty; ``GET /api/v1/playbooks/{id}`` returns it with the full
8863 * position list inlined.
8864 */
8865 Playbook: {
8866 /** Format: uuid */
8867 id: string;
8868 name: string;
8869 contract_type: string;
8870 description?: string;
8871 version?: string;
8872 /** Format: uuid */
8873 created_by?: string | null;
8874 /** Format: date-time */
8875 created_at: string;
8876 /** Format: date-time */
8877 updated_at: string;
8878 positions?: components["schemas"]["Position"][];
8879 };
8880 /**
8881 * @description Request shape for a single position when creating or updating a
8882 * playbook (M3-A6). Identical to ``Position`` minus the server-
8883 * assigned ``id`` (the server mints UUIDs at insert time).
8884 */
8885 PositionCreate: {
8886 issue: string;
8887 description?: string;
8888 standard_language: string;
8889 fallback_tiers?: components["schemas"]["FallbackTier"][];
8890 redline_strategy?: string;
8891 /** @enum {string} */
8892 severity_if_missing: "critical" | "high" | "medium" | "low";
8893 detection_keywords?: string[];
8894 detection_examples?: string[];
8895 position_order?: number;
8896 };
8897 /**
8898 * @description Request shape for ``POST /api/v1/playbooks`` (M3-A6). The server
8899 * sets ``created_by`` to the caller's id unconditionally; there is
8900 * no path to mint a built-in (``created_by IS NULL``) via the HTTP
8901 * surface (built-ins ship via seed migration only).
8902 */
8903 PlaybookCreate: {
8904 name: string;
8905 contract_type: string;
8906 description?: string;
8907 version?: string;
8908 positions?: components["schemas"]["PositionCreate"][];
8909 };
8910 /**
8911 * @description Request shape for ``PATCH /api/v1/playbooks/{id}`` (M3-A6). All
8912 * fields optional; a missing field is "leave alone." If
8913 * ``positions`` is supplied, the server **atomically replaces**
8914 * the entire positions list. To leave positions alone, omit the
8915 * field; to clear them, send ``positions: []``.
8916 */
8917 PlaybookUpdate: {
8918 name?: string;
8919 contract_type?: string;
8920 description?: string;
8921 version?: string;
8922 positions?: components["schemas"]["PositionCreate"][] | null;
8923 };
8924 /**
8925 * @description Request shape for ``POST /api/v1/playbooks/easy`` (M3-A6). The
8926 * document corpus the wizard's Step 1 collected, plus the
8927 * contract family and an optional caller-supplied name.
8928 */
8929 EasyPlaybookGenerationCreate: {
8930 document_ids: string[];
8931 /**
8932 * @description The contract family the playbook targets ("NDA",
8933 * "MSA-SaaS", "DPA", etc.). Free-form; passed to the
8934 * extractor + assembly LLM calls as a hint.
8935 */
8936 contract_type: string;
8937 /**
8938 * @description Playbook name. Falls back to "Generated {contract_type}
8939 * Playbook" if omitted.
8940 */
8941 name?: string | null;
8942 /**
8943 * @description Reserved for a future ephemeral-upload mode. Currently
8944 * always true; uploaded documents persist to the user's
8945 * library per M3-A6 §3.3.
8946 * @default true
8947 */
8948 persist_documents_after_generation: boolean;
8949 };
8950 /**
8951 * @description One row from the ``easy_playbook_generations`` table. Returned
8952 * by ``POST /api/v1/playbooks/easy`` (at status ``pending``) and
8953 * by ``GET /api/v1/playbooks/easy/{id}`` (the wizard's poll
8954 * target). ``draft_playbook`` is populated only on
8955 * ``status='completed'`` and carries the assembled
8956 * ``PlaybookCreate`` shape for the Step 3 inline editor.
8957 */
8958 EasyPlaybookGeneration: {
8959 /** Format: uuid */
8960 id: string;
8961 /** Format: uuid */
8962 user_id?: string | null;
8963 contract_type: string;
8964 /** @enum {string} */
8965 status: "pending" | "running" | "completed" | "error";
8966 document_ids: string[];
8967 /**
8968 * @description The assembled ``PlaybookCreate`` shape. ``null`` until the
8969 * worker reaches ``status='completed'``.
8970 */
8971 draft_playbook?: {
8972 [key: string]: unknown;
8973 } | null;
8974 error_message?: string | null;
8975 /** Format: date-time */
8976 created_at: string;
8977 /** Format: date-time */
8978 started_at?: string | null;
8979 /** Format: date-time */
8980 completed_at?: string | null;
8981 };
8982 /**
8983 * @description One execution of a playbook against a target document.
8984 * Status lifecycle: ``pending → running → completed | error``.
8985 * ``results`` is populated on ``completed``; ``error`` is populated
8986 * on ``error``. ``completed_at`` is non-null in either terminal state.
8987 */
8988 PlaybookExecution: {
8989 /** Format: uuid */
8990 id: string;
8991 /** Format: uuid */
8992 playbook_id: string;
8993 /** Format: uuid */
8994 target_document_id: string;
8995 /** Format: uuid */
8996 user_id?: string | null;
8997 /** Format: uuid */
8998 project_id?: string | null;
8999 /** @enum {string} */
9000 status: "pending" | "running" | "completed" | "error";
9001 /**
9002 * @description Per-position outcomes + summary. Shape is versioned via
9003 * ``schema_version`` (currently ``m3-a2-v1``); see the
9004 * M3-A2 executor module for the canonical structure.
9005 */
9006 results?: {
9007 [key: string]: unknown;
9008 } | null;
9009 error?: string | null;
9010 /** Format: date-time */
9011 created_at: string;
9012 /** Format: date-time */
9013 completed_at?: string | null;
9014 };
9015 /**
9016 * @description Request body for ``POST /api/v1/tabular/preview-cost``. Either
9017 * ``skill_name`` or ``columns`` must be supplied (not both).
9018 */
9019 TabularPreviewCostRequest: {
9020 document_ids: string[];
9021 /** @description Name of a registered ``output_format: table`` skill. */
9022 skill_name?: string | null;
9023 /** @description Ad-hoc column spec (alternative to ``skill_name``). */
9024 columns?: components["schemas"]["ColumnSpec"][] | null;
9025 };
9026 TabularPreviewCostResponse: {
9027 /** @description ``len(document_ids) * len(columns)``. */
9028 cells_count: number;
9029 /** @description Per-cell token average x cells_count. */
9030 estimated_tokens: number;
9031 /**
9032 * @description Per-cell rolling-average cost x cells_count, serialized as
9033 * a JSON string to preserve decimal precision.
9034 */
9035 estimated_cost_usd: string;
9036 /**
9037 * @description Map of tier label (``tier_1`` / ``tier_2`` / ... or
9038 * ``default``) to cell count routed at that tier. Columns
9039 * without an explicit ``minimum_inference_tier`` count in
9040 * the ``default`` bucket.
9041 */
9042 per_tier_breakdown: {
9043 [key: string]: number;
9044 };
9045 /**
9046 * @description Number of cells that will run ensemble Citation-Engine
9047 * verification (``len(document_ids) * ensemble-column count``).
9048 * ``0`` when no column is ensemble-verified.
9049 */
9050 ensemble_cells_count?: number;
9051 /**
9052 * @description The ensemble judge-call cost included in
9053 * ``estimated_cost_usd`` (= ``n_judges * per-judge-cost *
9054 * ensemble_cells_count``), serialized as a JSON string to
9055 * preserve decimal precision. ``"0"`` when no column is
9056 * ensemble-verified. Note that ``estimated_cost_usd`` is the
9057 * TOTAL (base extraction + this premium); ``estimated_tokens``
9058 * is extraction-only and excludes judge tokens.
9059 */
9060 ensemble_premium_usd?: string;
9061 };
9062 /**
9063 * @description Request body for ``POST /api/v1/tabular/execute``. Either
9064 * ``skill_name`` or ``columns`` must be supplied (not both).
9065 * ``confirmed_cost_usd`` echoes the preview value so the row
9066 * carries an audit trail of the operator confirming a specific
9067 * cost ceiling.
9068 */
9069 TabularExecutionCreate: {
9070 document_ids: string[];
9071 skill_name?: string | null;
9072 columns?: components["schemas"]["ColumnSpec"][] | null;
9073 /**
9074 * @description Operator-confirmed cost from the preview endpoint. Persisted
9075 * to ``tabular_executions.cost_estimate_usd``.
9076 */
9077 confirmed_cost_usd?: string | null;
9078 };
9079 /**
9080 * @description One row from the ``tabular_executions`` table. Lifecycle:
9081 * ``pending → running → completed | failed | cancelled``.
9082 * ``results`` is populated by the aggregate node on ``completed``;
9083 * ``error_text`` is populated on ``failed``. Soft-deleted rows
9084 * (``deleted_at`` non-null) are invisible to list / detail
9085 * endpoints.
9086 */
9087 TabularExecution: {
9088 /** Format: uuid */
9089 id: string;
9090 /** Format: uuid */
9091 user_id?: string | null;
9092 /**
9093 * Format: uuid
9094 * @description Non-null on bulk-op sibling rows (Phase C prep doc Decision
9095 * C-9). Bulk ops spawn siblings rather than mutating the
9096 * original grid, preserving auditability.
9097 */
9098 parent_execution_id?: string | null;
9099 /**
9100 * @description Source skill name; ``null`` for ad-hoc executions where the
9101 * operator typed columns directly in the wizard.
9102 */
9103 skill_name?: string | null;
9104 /** @enum {string} */
9105 status: "pending" | "running" | "completed" | "failed" | "cancelled";
9106 document_ids: string[];
9107 /**
9108 * @description Per-document display names (operator-uploaded filenames),
9109 * parallel to ``document_ids`` in selection order. Assembled on
9110 * the response by ``_to_response``; not a stored column.
9111 */
9112 document_names: string[];
9113 columns: components["schemas"]["ColumnSpec"][];
9114 /**
9115 * @description Assembled grid shape with rows + per-cell results. Shape is
9116 * versioned via ``schema_version`` (currently ``m3-c2-v1``).
9117 *
9118 * Each cell result also carries ``verification_method``
9119 * (string, nullable) — the per-cell ensemble Citation-Engine
9120 * verification outcome (``ensemble_strict`` /
9121 * ``ensemble_majority``), or ``null`` when the cell's column is
9122 * not ensemble-verified or verification did not confirm support.
9123 *
9124 * Each cell carries a ``citations`` array. A tabular cell
9125 * ``Citation`` is distinct from the chat ``Citation`` schema; it
9126 * has: ``citation_id`` (uuid; deterministic display-only id —
9127 * DE-309), ``document_id`` (uuid; the source ``documents.id``),
9128 * ``chunk_id`` (uuid, nullable; the cited ``document_chunks.id``),
9129 * and ``confidence`` (one of ``high|medium|low|failed``). Each
9130 * citation additionally carries ``verification_method`` (string,
9131 * nullable) — the Citation-Engine verification method
9132 * (``ensemble_strict`` / ``ensemble_majority``), or ``null`` when
9133 * the cell's column is not ensemble-verified or verification did
9134 * not confirm support. It is mirrored from the cell-level value.
9135 *
9136 * On ``GET /api/v1/tabular/executions/{id}`` (only) each citation
9137 * is additionally enriched at read time so the frontend can open
9138 * the cited source in its doc panel (same UX as chat citations):
9139 *
9140 * - ``source_file_id`` (uuid, nullable): the source document's
9141 * ``documents.file_id`` — the ``files.id`` the doc panel keys
9142 * off. ``null`` when the backing chunk row is missing/stale.
9143 * - ``source_page`` (integer, nullable): the cited chunk's
9144 * ``document_chunks.page_start`` (nullable — best-effort page
9145 * assignment).
9146 * - ``source_text`` (string, nullable): the cited chunk's full
9147 * ``document_chunks.content`` (the frontend locates/highlights
9148 * the cited span within it).
9149 *
9150 * These three fields are resolved via two batched ``IN`` queries
9151 * (chunks, then documents) — no N+1 — and are ``null`` on the
9152 * list/export paths, which do not enrich.
9153 */
9154 results?: {
9155 [key: string]: unknown;
9156 } | null;
9157 cost_estimate_usd?: string | null;
9158 cost_actual_usd?: string | null;
9159 error_text?: string | null;
9160 /** Format: date-time */
9161 created_at: string;
9162 /** Format: date-time */
9163 started_at?: string | null;
9164 /** Format: date-time */
9165 completed_at?: string | null;
9166 };
9167 /**
9168 * @description Compact projection used by the list endpoint. Drops the
9169 * (potentially large) ``results`` payload — operators fetch the
9170 * full execution row when they open one.
9171 */
9172 TabularExecutionSummary: {
9173 /** Format: uuid */
9174 id: string;
9175 /** Format: uuid */
9176 user_id?: string | null;
9177 /** Format: uuid */
9178 parent_execution_id?: string | null;
9179 skill_name?: string | null;
9180 /** @enum {string} */
9181 status: "pending" | "running" | "completed" | "failed" | "cancelled";
9182 document_count: number;
9183 column_count: number;
9184 cost_estimate_usd?: string | null;
9185 cost_actual_usd?: string | null;
9186 /** Format: date-time */
9187 created_at: string;
9188 /** Format: date-time */
9189 completed_at?: string | null;
9190 };
9191 AdminUserRow: {
9192 /** Format: uuid */
9193 id: string;
9194 /** Format: email */
9195 email: string;
9196 display_name?: string | null;
9197 /** @enum {string} */
9198 role: "admin" | "member" | "viewer";
9199 is_admin: boolean;
9200 mfa_enabled: boolean;
9201 must_change_password: boolean;
9202 /** Format: date-time */
9203 created_at: string;
9204 /** Format: date-time */
9205 last_login_at?: string | null;
9206 /** Format: date-time */
9207 deletion_scheduled_at?: string | null;
9208 };
9209 /**
9210 * @description Read-only view of an ``autonomous_findings`` row — one analysis
9211 * finding emitted by a run via the ``emit_finding`` chokepoint.
9212 * ``content`` is the finding body (the LLM's ``summary``).
9213 * ``severity`` is LLM-emitted free text (no CHECK; ``info`` |
9214 * ``warn`` | ``critical`` are the intended values).
9215 */
9216 AutonomousFindingRead: {
9217 /** Format: uuid */
9218 id: string;
9219 /** Format: uuid */
9220 session_id: string;
9221 severity: string;
9222 title: string;
9223 content: string;
9224 /** Format: date-time */
9225 created_at: string;
9226 };
9227 /**
9228 * @description Paginated list of a session's findings, in stable
9229 * ``created_at ASC, id ASC`` order — one run's rows typically
9230 * share ``created_at`` (transaction-stable ``now()``); ``id`` is
9231 * the pagination tiebreaker. Repeatable, not a guaranteed
9232 * emission sequence.
9233 */
9234 AutonomousFindingListResponse: {
9235 findings: components["schemas"]["AutonomousFindingRead"][];
9236 total_count: number;
9237 limit: number;
9238 offset: number;
9239 };
9240 /**
9241 * @description Read-only view of an ``autonomous_artifacts`` row — a reference to
9242 * one document-grade artifact (a markdown memo) an opted-in run
9243 * persisted into its target knowledge base via the ``emit_artifact``
9244 * chokepoint. ``name`` and ``mime`` are LLM-emitted free text (no
9245 * CHECK; the ``severity`` precedent).
9246 */
9247 AutonomousArtifactRead: {
9248 /** Format: uuid */
9249 id: string;
9250 name: string;
9251 mime: string;
9252 size_bytes: number;
9253 /**
9254 * Format: uuid
9255 * @description The backing ``files`` row. The FK is ``ON DELETE SET NULL`` —
9256 * null means the file was later hard-deleted; the name/size
9257 * metadata here survives. The KB document itself outlives the
9258 * session (deleting the session removes only this reference).
9259 */
9260 file_id?: string | null;
9261 /**
9262 * Format: uuid
9263 * @description NOT a column on ``autonomous_artifacts`` — enriched at read
9264 * time via the unique ``documents.file_id`` (1:1) so a client
9265 * can deep-link the KB document. Null when ``file_id`` is null
9266 * or no ``documents`` row exists for the file.
9267 */
9268 document_id?: string | null;
9269 /** Format: date-time */
9270 created_at: string;
9271 };
9272 /**
9273 * @description Paginated list of a session's artifact references, in the same
9274 * stable ``created_at ASC, id ASC`` order as the findings list —
9275 * repeatable, not a guaranteed emission sequence.
9276 */
9277 AutonomousArtifactListResponse: {
9278 artifacts: components["schemas"]["AutonomousArtifactRead"][];
9279 total_count: number;
9280 limit: number;
9281 offset: number;
9282 };
9283 /**
9284 * @description Read-only view of an ``autonomous_memory`` row. Returned by the
9285 * list, keep, dismiss, and delete endpoints.
9286 */
9287 AutonomousMemoryRead: {
9288 /** Format: uuid */
9289 id: string;
9290 /** Format: uuid */
9291 user_id: string;
9292 /** @enum {string} */
9293 state: "proposed" | "kept" | "dismissed";
9294 category: string;
9295 content: string;
9296 /** Format: uuid */
9297 source_session_id?: string | null;
9298 /** Format: date-time */
9299 kept_at?: string | null;
9300 /** Format: date-time */
9301 deleted_at?: string | null;
9302 /** Format: date-time */
9303 created_at: string;
9304 /** Format: date-time */
9305 updated_at: string;
9306 };
9307 /** @description Paginated list of autonomous memory entries (non-deleted, newest first). */
9308 AutonomousMemoryListResponse: {
9309 entries: components["schemas"]["AutonomousMemoryRead"][];
9310 total_count: number;
9311 limit: number;
9312 offset: number;
9313 };
9314 /**
9315 * @description Optional request body for ``POST /autonomous/memory/{id}/keep``.
9316 * If ``content`` is provided, the memory entry's text is overwritten
9317 * on keep (edit-on-keep).
9318 */
9319 MemoryKeepRequest: {
9320 content?: string | null;
9321 };
9322 /**
9323 * @description Read-only view of a ``precedent_entries`` row. Returned by the
9324 * list, dismiss endpoints.
9325 */
9326 PrecedentEntryRead: {
9327 /** Format: uuid */
9328 id: string;
9329 /** Format: uuid */
9330 user_id: string;
9331 pattern_kind: string;
9332 summary: string;
9333 observed_count: number;
9334 /** Format: uuid */
9335 source_session_id?: string | null;
9336 /** Format: date-time */
9337 dismissed_at?: string | null;
9338 /** Format: date-time */
9339 created_at: string;
9340 /** Format: date-time */
9341 updated_at: string;
9342 };
9343 /** @description Paginated list of precedent entries (non-dismissed, newest first). */
9344 PrecedentEntryListResponse: {
9345 entries: components["schemas"]["PrecedentEntryRead"][];
9346 total_count: number;
9347 limit: number;
9348 offset: number;
9349 };
9350 /**
9351 * @description Request body for ``POST /autonomous/precedents/{id}/promote``.
9352 * ``project_id`` is the target Project; the caller must own it.
9353 */
9354 PromotePrecedentRequest: {
9355 /** Format: uuid */
9356 project_id: string;
9357 };
9358 /**
9359 * @description Read-only view of a ``project_context_proposals`` row — a proposal
9360 * to promote a precedent into a Project's context document. Only the
9361 * user accepting a proposal writes ``projects.context_md`` (ADR 0013 D5).
9362 */
9363 ProjectContextProposalRead: {
9364 /** Format: uuid */
9365 id: string;
9366 /** Format: uuid */
9367 user_id: string;
9368 /** Format: uuid */
9369 precedent_id: string;
9370 /** Format: uuid */
9371 project_id: string;
9372 suggested_md: string;
9373 /** @enum {string} */
9374 state: "proposed" | "accepted" | "rejected";
9375 /** Format: date-time */
9376 accepted_at?: string | null;
9377 /** Format: date-time */
9378 rejected_at?: string | null;
9379 /** Format: date-time */
9380 created_at: string;
9381 /** Format: date-time */
9382 updated_at: string;
9383 };
9384 /** @description Paginated list of project-context proposals (newest first). */
9385 ProjectContextProposalListResponse: {
9386 proposals: components["schemas"]["ProjectContextProposalRead"][];
9387 total_count: number;
9388 limit: number;
9389 offset: number;
9390 };
9391 /**
9392 * @description Read-only view of an ``autonomous_schedules`` row — a cron-triggered
9393 * run definition.
9394 */
9395 AutonomousScheduleRead: {
9396 /** Format: uuid */
9397 id: string;
9398 /** Format: uuid */
9399 user_id: string;
9400 /** Format: uuid */
9401 project_id?: string | null;
9402 name?: string | null;
9403 cron_expr: string;
9404 /** Format: uuid */
9405 playbook_id?: string | null;
9406 skill_ref?: string | null;
9407 /** Format: uuid */
9408 target_kb_id?: string | null;
9409 enabled: boolean;
9410 /**
9411 * @description Opt-in document-grade artifact emission (Donna #8) for the
9412 * schedule's sessions; default off so existing automations are
9413 * unchanged.
9414 */
9415 emit_artifacts: boolean;
9416 max_cost_usd?: string | null;
9417 /** Format: date-time */
9418 last_run_at?: string | null;
9419 /** Format: date-time */
9420 next_run_at?: string | null;
9421 /** Format: date-time */
9422 deleted_at?: string | null;
9423 /** Format: date-time */
9424 created_at: string;
9425 /** Format: date-time */
9426 updated_at: string;
9427 };
9428 /**
9429 * @description Request body for ``POST /autonomous/schedules``. ``cron_expr`` is a
9430 * five-field cron string (minute hour day-of-month month day-of-week)
9431 * supporting ``*``, lists, ranges, and steps; invalid expressions
9432 * return 422.
9433 */
9434 AutonomousScheduleCreate: {
9435 cron_expr: string;
9436 name?: string | null;
9437 /** Format: uuid */
9438 playbook_id?: string | null;
9439 skill_ref?: string | null;
9440 /** Format: uuid */
9441 target_kb_id?: string | null;
9442 /** Format: uuid */
9443 project_id?: string | null;
9444 /** @default true */
9445 enabled: boolean;
9446 /**
9447 * @description Opt-in document-grade artifact emission (Donna #8) for the
9448 * schedule's sessions; default off so existing automations are
9449 * unchanged.
9450 * @default false
9451 */
9452 emit_artifacts: boolean;
9453 max_cost_usd?: string | null;
9454 };
9455 /**
9456 * @description Request body for ``PATCH /autonomous/schedules/{id}``. All fields
9457 * optional. Changing ``cron_expr`` re-validates (422) and recomputes
9458 * ``next_run_at``. The matter (``project_id``) may be reassigned; an
9459 * explicit ``null`` unassigns it, and a non-null ``project_id`` the
9460 * caller does not own returns 404 (id-probing-safe).
9461 */
9462 AutonomousScheduleUpdate: {
9463 name?: string | null;
9464 cron_expr?: string | null;
9465 enabled?: boolean | null;
9466 /**
9467 * @description Toggle opt-in document-grade artifact emission (Donna #8)
9468 * for the schedule's future sessions; omitted/null = unchanged.
9469 */
9470 emit_artifacts?: boolean | null;
9471 /** Format: uuid */
9472 playbook_id?: string | null;
9473 skill_ref?: string | null;
9474 /** Format: uuid */
9475 target_kb_id?: string | null;
9476 /** Format: uuid */
9477 project_id?: string | null;
9478 max_cost_usd?: string | null;
9479 };
9480 /**
9481 * @description Request body for ``POST /autonomous/run-now``. Spawns one
9482 * ``trigger_kind='manual'`` session. Exactly one of ``playbook_id``
9483 * / ``skill_ref`` must be set (zero or both → 422). ``target_kb_id``
9484 * / ``project_id`` are optional scope. ``max_cost_usd`` is the
9485 * per-run cap (NULL → fall back to the config default). A manual
9486 * run has no schedule/watch row to inherit ``emit_artifacts`` from,
9487 * so this body IS the opt-in source.
9488 */
9489 AutonomousManualRunRequest: {
9490 /** Format: uuid */
9491 playbook_id?: string | null;
9492 skill_ref?: string | null;
9493 /** Format: uuid */
9494 target_kb_id?: string | null;
9495 /** Format: uuid */
9496 project_id?: string | null;
9497 max_cost_usd?: string | null;
9498 /**
9499 * @description Opt-in document-grade artifact emission (Donna #8) for this
9500 * one-off run; default off so existing callers are unchanged.
9501 * @default false
9502 */
9503 emit_artifacts: boolean;
9504 };
9505 /** @description Paginated list of autonomous schedules (newest first). */
9506 AutonomousScheduleListResponse: {
9507 schedules: components["schemas"]["AutonomousScheduleRead"][];
9508 total_count: number;
9509 limit: number;
9510 offset: number;
9511 };
9512 /**
9513 * @description Read-only view of an ``autonomous_watches`` row — a KB-arrival-
9514 * triggered run definition. When a file is attached to
9515 * ``knowledge_base_id``, an autonomous session spawns.
9516 */
9517 AutonomousWatchRead: {
9518 /** Format: uuid */
9519 id: string;
9520 /** Format: uuid */
9521 user_id: string;
9522 /** Format: uuid */
9523 project_id?: string | null;
9524 /** Format: uuid */
9525 knowledge_base_id: string;
9526 /** Format: uuid */
9527 playbook_id?: string | null;
9528 skill_ref?: string | null;
9529 enabled: boolean;
9530 /**
9531 * @description Opt-in document-grade artifact emission (Donna #8) for the
9532 * watch's sessions; default off so existing automations are
9533 * unchanged.
9534 */
9535 emit_artifacts: boolean;
9536 max_cost_usd?: string | null;
9537 /** Format: date-time */
9538 deleted_at?: string | null;
9539 /** Format: date-time */
9540 created_at: string;
9541 /** Format: date-time */
9542 updated_at: string;
9543 };
9544 /**
9545 * @description Request body for ``POST /autonomous/watches``. ``knowledge_base_id``
9546 * is required and must be owned by the caller (404 otherwise; KB-sharing
9547 * is out of scope). The target (``playbook_id`` / ``skill_ref``) and
9548 * ``project_id`` are optional; ``enabled`` defaults to true.
9549 */
9550 AutonomousWatchCreate: {
9551 /** Format: uuid */
9552 knowledge_base_id: string;
9553 /** Format: uuid */
9554 playbook_id?: string | null;
9555 skill_ref?: string | null;
9556 /** Format: uuid */
9557 project_id?: string | null;
9558 /** @default true */
9559 enabled: boolean;
9560 /**
9561 * @description Opt-in document-grade artifact emission (Donna #8) for the
9562 * watch's sessions; default off so existing automations are
9563 * unchanged.
9564 * @default false
9565 */
9566 emit_artifacts: boolean;
9567 max_cost_usd?: string | null;
9568 };
9569 /**
9570 * @description Request body for ``PATCH /autonomous/watches/{id}``. All fields
9571 * optional. The watch's ``knowledge_base_id`` is immutable (not
9572 * present here) — a watch is bound to its KB. The matter
9573 * (``project_id``) may be reassigned; an explicit ``null`` unassigns
9574 * it, and a non-null ``project_id`` the caller does not own returns
9575 * 404 (id-probing-safe).
9576 */
9577 AutonomousWatchUpdate: {
9578 enabled?: boolean | null;
9579 /**
9580 * @description Toggle opt-in document-grade artifact emission (Donna #8)
9581 * for the watch's future sessions; omitted/null = unchanged.
9582 */
9583 emit_artifacts?: boolean | null;
9584 /** Format: uuid */
9585 playbook_id?: string | null;
9586 skill_ref?: string | null;
9587 /** Format: uuid */
9588 project_id?: string | null;
9589 max_cost_usd?: string | null;
9590 };
9591 /** @description Paginated list of autonomous watches (newest first). */
9592 AutonomousWatchListResponse: {
9593 watches: components["schemas"]["AutonomousWatchRead"][];
9594 total_count: number;
9595 limit: number;
9596 offset: number;
9597 };
9598 /**
9599 * @description Read-only view of an ``autonomous_notifications`` row. Written by
9600 * the ``notify`` chokepoint handler. ``read_at`` IS NULL = unread;
9601 * marking read is the dismiss action. ``channel`` is one of
9602 * ``in_app`` / ``email`` / ``webhook`` (``webhook`` reserved).
9603 * ``body``/``payload`` carry counts/IDs + a receipt link — never raw
9604 * entity values. A run-completion notification's ``payload`` carries
9605 * ``finding_count`` and ``artifact_count`` (``artifact_count`` is 0
9606 * unless the run opted in via ``emit_artifacts``).
9607 */
9608 AutonomousNotificationRead: {
9609 /** Format: uuid */
9610 id: string;
9611 /** Format: uuid */
9612 user_id: string;
9613 /** Format: uuid */
9614 session_id: string;
9615 /** @enum {string} */
9616 channel: "in_app" | "email" | "webhook";
9617 title: string;
9618 body: string;
9619 payload?: {
9620 [key: string]: unknown;
9621 } | null;
9622 /** Format: date-time */
9623 read_at?: string | null;
9624 /** Format: date-time */
9625 created_at: string;
9626 /** Format: date-time */
9627 updated_at: string;
9628 };
9629 /** @description Paginated list of autonomous notifications (newest first). */
9630 AutonomousNotificationListResponse: {
9631 notifications: components["schemas"]["AutonomousNotificationRead"][];
9632 total_count: number;
9633 limit: number;
9634 offset: number;
9635 };
9636 /**
9637 * @description Read-only view of an ``autonomous_sessions`` row. Returned by the
9638 * halt, list, and detail endpoints.
9639 */
9640 AutonomousSessionRead: {
9641 /** Format: uuid */
9642 id: string;
9643 /** Format: uuid */
9644 user_id: string;
9645 /** Format: uuid */
9646 project_id?: string | null;
9647 /** @enum {string} */
9648 trigger_kind: "watch" | "schedule" | "suggestion" | "manual";
9649 /** Format: uuid */
9650 trigger_ref?: string | null;
9651 /** @enum {string} */
9652 current_phase: "intake" | "analysis" | "drafting" | "ethics_review" | "delivery";
9653 /** @enum {string} */
9654 halt_state: "running" | "halt_requested" | "halted" | "paused";
9655 max_cost_usd?: string | null;
9656 cost_total_usd: string;
9657 cost_cap_reached: boolean;
9658 idle_halt_minutes: number;
9659 /** Format: date-time */
9660 last_activity_at: string;
9661 /** @enum {string} */
9662 status: "running" | "completed" | "halted" | "failed";
9663 params?: {
9664 [key: string]: unknown;
9665 };
9666 result?: {
9667 [key: string]: unknown;
9668 } | null;
9669 error?: string | null;
9670 /** Format: date-time */
9671 created_at: string;
9672 /** Format: date-time */
9673 updated_at: string;
9674 /** Format: date-time */
9675 completed_at?: string | null;
9676 };
9677 /** @description Paginated list of autonomous sessions (newest first). */
9678 AutonomousSessionListResponse: {
9679 sessions: components["schemas"]["AutonomousSessionRead"][];
9680 total_count: number;
9681 limit: number;
9682 offset: number;
9683 };
9684 /**
9685 * @description Session detail view with live-reconstructed receipt.
9686 * The receipt is built from audit rows on every request and contains
9687 * phase transitions, tool calls, terminal reason, and session metadata.
9688 * No raw document text or entity values are included.
9689 */
9690 AutonomousSessionDetailResponse: {
9691 session: components["schemas"]["AutonomousSessionRead"];
9692 /**
9693 * @description Audit-derived receipt: session_id, trigger_kind, status,
9694 * halt_state, current_phase, cost_total_usd, max_cost_usd,
9695 * cost_cap_reached, created_at, completed_at,
9696 * phase_transitions (list), tool_calls (list),
9697 * terminal_reason (string | null).
9698 */
9699 receipt: {
9700 [key: string]: unknown;
9701 };
9702 };
9703 AdminUserListResponse: {
9704 users: components["schemas"]["AdminUserRow"][];
9705 total_count: number;
9706 limit: number;
9707 offset: number;
9708 };
9709 LoginRequest: {
9710 /** Format: email */
9711 email: string;
9712 /** Format: password */
9713 password: string;
9714 };
9715 LoginResponse: {
9716 access_token: string;
9717 /** @enum {string} */
9718 token_type: "Bearer";
9719 /** @description Lifetime in seconds */
9720 expires_in: number;
9721 refresh_token?: string;
9722 user: components["schemas"]["User"];
9723 };
9724 TokenResponse: {
9725 /** @description Short-lived JWT access token (~15 min). */
9726 access_token: string;
9727 /**
9728 * @description Refresh token returned on /auth/login and rotated on every
9729 * /auth/refresh per PRD §5.1. The bearer of the most recent
9730 * refresh_token can mint new access tokens; older refresh
9731 * tokens are revoked when rotation happens.
9732 */
9733 refresh_token: string;
9734 /** @enum {string} */
9735 token_type: "Bearer";
9736 /** @description Seconds until the access token expires. */
9737 expires_in: number;
9738 };
9739 MfaChallenge: {
9740 mfa_token: string;
9741 methods: ("totp" | "recovery_code")[];
9742 };
9743 ChangePasswordRequest: {
9744 /**
9745 * Format: password
9746 * @description The user's current password. Required even when the
9747 * `must_change_password` flag is set — a stolen access token
9748 * alone must not be sufficient to rotate credentials.
9749 */
9750 current_password: string;
9751 /**
9752 * Format: password
9753 * @description The replacement password. Must satisfy the deployment's
9754 * password policy (minimum length, etc.) and must differ from
9755 * `current_password`. The first-run admin flow accepts any
9756 * password meeting policy; subsequent rotations may add
9757 * complexity rules.
9758 */
9759 new_password: string;
9760 };
9761 User: {
9762 /** Format: uuid */
9763 id: string;
9764 /** Format: email */
9765 email: string;
9766 display_name?: string | null;
9767 is_admin: boolean;
9768 /**
9769 * @description Wave C — PRD §5.2 RBAC three-role system. ``admin`` has
9770 * full access; ``member`` can mutate owned resources; ``viewer``
9771 * is read-only. Mutating endpoints reject ``viewer`` via the
9772 * ``MutatingUser`` dependency. Kept in sync with ``is_admin``
9773 * (``role='admin'`` iff ``is_admin=true``).
9774 * @default member
9775 * @enum {string}
9776 */
9777 role: "admin" | "member" | "viewer";
9778 mfa_enabled: boolean;
9779 /**
9780 * @description When true, the user must call POST /api/v1/auth/change-password
9781 * before they can use any other authenticated endpoint. Set on
9782 * the auto-created first-run admin (Task B2) and on accounts
9783 * after `reset-admin-password`. Other authenticated endpoints
9784 * return 403 with `error.code = "password_change_required"`
9785 * until this clears.
9786 */
9787 must_change_password: boolean;
9788 /**
9789 * @description Wave A — PRD §3.2 Enhance Prompt reasoning visibility:
9790 * ``always_show`` (reasoning visible by default),
9791 * ``disclosure`` (collapsed behind a toggle; spec default),
9792 * ``on_request`` (hidden until the user opens the skill
9793 * inspector). Modifiable via ``PATCH /users/me/preferences``.
9794 * @default disclosure
9795 * @enum {string}
9796 */
9797 reasoning_visibility: "always_show" | "disclosure" | "on_request";
9798 /**
9799 * @description Wave B v2 — PRD §3.2.1 / frontend spec §4.3. Where Enhance
9800 * Prompt, Skill Creator, KB, and Apply Skill surface:
9801 * ``prominent`` (dashboard cards; default) vs. ``inline``
9802 * (toolbar only for veterans).
9803 * @default prominent
9804 * @enum {string}
9805 */
9806 featured_tools: "prominent" | "inline";
9807 /**
9808 * @description Wave B v2 — PRD §3.2.1 / frontend spec §4.3. Matter workspace
9809 * pane composition (Wave C surfaces): ``three_pane`` (default),
9810 * ``two_pane``, or ``one_pane``.
9811 * @default three_pane
9812 * @enum {string}
9813 */
9814 workspace_layout: "three_pane" | "two_pane" | "one_pane";
9815 /**
9816 * @description Wave B v2 — PRD §3.2.1 / frontend spec §4.3. Ambient trust
9817 * pill format: ``labels`` (full text, e.g. "● self-hosted";
9818 * default) vs. ``dots`` (minimal dot only).
9819 * @default labels
9820 * @enum {string}
9821 */
9822 trust_pills: "labels" | "dots";
9823 /**
9824 * @description Wave B v2 — PRD §3.2.1 / frontend spec §4.3. Per-message
9825 * skill/tier/provider pill row visibility: ``always`` (visible
9826 * by default) vs. ``collapsed`` (hidden until expanded).
9827 * @default always
9828 * @enum {string}
9829 */
9830 provenance_pills: "always" | "collapsed";
9831 /** Format: date-time */
9832 created_at: string;
9833 /** Format: date-time */
9834 last_login_at?: string | null;
9835 /**
9836 * Format: date-time
9837 * @description Non-null while a GDPR Article 17 grace-period deletion is pending (set by POST /users/me/delete, cleared by .../delete/cancel).
9838 */
9839 deletion_scheduled_at?: string | null;
9840 };
9841 /**
9842 * @description All 6 personalization preference fields. Wave A (``reasoning_visibility``)
9843 * per PRD §3.2; Wave B v2 fields per PRD §3.2.1 and frontend spec §4.3;
9844 * ``autonomous_enabled`` per PRD §5 / M4-C2.
9845 */
9846 UserPreferences: {
9847 /**
9848 * @default disclosure
9849 * @enum {string}
9850 */
9851 reasoning_visibility: "always_show" | "disclosure" | "on_request";
9852 /**
9853 * @default prominent
9854 * @enum {string}
9855 */
9856 featured_tools: "prominent" | "inline";
9857 /**
9858 * @default three_pane
9859 * @enum {string}
9860 */
9861 workspace_layout: "three_pane" | "two_pane" | "one_pane";
9862 /**
9863 * @default labels
9864 * @enum {string}
9865 */
9866 trust_pills: "labels" | "dots";
9867 /**
9868 * @default always
9869 * @enum {string}
9870 */
9871 provenance_pills: "always" | "collapsed";
9872 autonomous_enabled: boolean;
9873 };
9874 /**
9875 * @description All fields optional; only supplied keys are updated. Wave B v2 adds
9876 * four personalization fields per PRD §3.2.1 and frontend spec §4.3;
9877 * ``autonomous_enabled`` per PRD §5 / M4-C2.
9878 */
9879 UserPreferencesUpdate: {
9880 /** @enum {string} */
9881 reasoning_visibility?: "always_show" | "disclosure" | "on_request";
9882 /** @enum {string} */
9883 featured_tools?: "prominent" | "inline";
9884 /** @enum {string} */
9885 workspace_layout?: "three_pane" | "two_pane" | "one_pane";
9886 /** @enum {string} */
9887 trust_pills?: "labels" | "dots";
9888 /** @enum {string} */
9889 provenance_pills?: "always" | "collapsed";
9890 autonomous_enabled?: boolean;
9891 };
9892 /**
9893 * @description PATCH body for `/api/v1/users/me`. Caller-scoped self-edit of the
9894 * user's own profile. Currently `display_name` only: when supplied
9895 * it is trimmed and must be non-empty after trimming. At least one
9896 * updatable field must be present (an all-omitted body is a 422).
9897 *
9898 * Email self-service editing is intentionally out of scope here —
9899 * changing the login email pulls in re-verification, MFA, and
9900 * uniqueness concerns and is deferred to a dedicated flow.
9901 */
9902 UserProfileUpdate: {
9903 /**
9904 * @description New display name. Trimmed server-side; must be non-empty after
9905 * trimming. Omit to leave unchanged (but at least one field must
9906 * be present, so an empty body is rejected).
9907 */
9908 display_name?: string;
9909 };
9910 Project: {
9911 /** Format: uuid */
9912 id: string;
9913 name: string;
9914 /**
9915 * @description URL-friendly identifier; unique-per-owner within the active set
9916 * (an archived project's slug can be reused). Generated from
9917 * `name` on create when not supplied; collisions resolve with a
9918 * numeric suffix (`-2`, `-3`, ...).
9919 */
9920 slug: string;
9921 description?: string | null;
9922 /**
9923 * @description Free-form Markdown context document for the matter. Capped at
9924 * 100 KiB (UTF-8 bytes); exceeding the cap returns 422.
9925 */
9926 context_md?: string | null;
9927 /** Format: uuid */
9928 owner_id: string;
9929 /**
9930 * @description When true, every chat in this project is marked privileged in the audit log
9931 * and the routed inference tier is forced to be at least minimum_inference_tier.
9932 * Setting `privileged=true` requires `minimum_inference_tier` to be set;
9933 * attempts to set one without the other return 422 (POST) or 400 (PATCH).
9934 */
9935 privileged: boolean;
9936 /**
9937 * @description When set, requests below this tier are refused
9938 * @enum {integer|null}
9939 */
9940 minimum_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
9941 /** @description Skills attached to this project, ordered by attachment time */
9942 attached_skill_names?: string[];
9943 /** @description Files attached to this project, ordered by attachment time */
9944 attached_file_ids?: string[];
9945 /**
9946 * @description System-managed flag added by migration 0022. When true, this
9947 * project is the per-user try-it sandbox (slug ``__sandbox__``).
9948 * Sandbox projects are excluded from the default ``GET /projects``
9949 * list; pass ``include_sandbox=true`` or ``only_sandbox=true`` to
9950 * surface them. Created by ``POST /projects/sandbox/ensure``.
9951 * @default false
9952 */
9953 is_sandbox: boolean;
9954 /**
9955 * Format: date-time
9956 * @description When set, the project is soft-deleted (archived); excluded from default list
9957 */
9958 archived_at?: string | null;
9959 /** Format: date-time */
9960 created_at: string;
9961 /** Format: date-time */
9962 updated_at: string;
9963 };
9964 ProjectCreate: {
9965 name: string;
9966 /** @description Optional; defaults to slugified `name` with collision suffixes. */
9967 slug?: string;
9968 description?: string;
9969 /** @description Free-form Markdown context document; max 100 KiB. */
9970 context_md?: string;
9971 /** @default false */
9972 privileged: boolean;
9973 /** @enum {integer} */
9974 minimum_inference_tier?: 1 | 2 | 3 | 4 | 5;
9975 };
9976 ProjectUpdate: {
9977 name?: string;
9978 slug?: string;
9979 description?: string;
9980 /** @description Free-form Markdown context document; max 100 KiB. */
9981 context_md?: string;
9982 privileged?: boolean;
9983 /** @enum {integer|null} */
9984 minimum_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
9985 /**
9986 * @description When true, archive (soft-delete) the project; when false,
9987 * unarchive a previously-archived project. The dedicated
9988 * `DELETE /api/v1/projects/{id}` endpoint is the destructive
9989 * equivalent of `PATCH {archived: true}`.
9990 */
9991 archived?: boolean;
9992 };
9993 Chat: {
9994 /** Format: uuid */
9995 id: string;
9996 /**
9997 * @description Chat title. Defaults to "New chat"; auto-renamed to the
9998 * first 80 chars of the first user message on first POST
9999 * /messages. Never overwrites a user-set title (one-shot).
10000 */
10001 title: string;
10002 /** Format: uuid */
10003 owner_id: string;
10004 /** Format: uuid */
10005 project_id?: string | null;
10006 /**
10007 * Format: date-time
10008 * @description Soft-delete timestamp. NULL means active.
10009 */
10010 archived_at?: string | null;
10011 /** @description Count of persisted messages in this chat. */
10012 message_count?: number;
10013 /** Format: date-time */
10014 created_at: string;
10015 /** Format: date-time */
10016 updated_at: string;
10017 };
10018 ChatCreate: {
10019 /**
10020 * @description Defaults to "New chat" when omitted. The chat will be
10021 * auto-renamed from the first user message; supplying a
10022 * non-default title here suppresses auto-rename.
10023 */
10024 title?: string;
10025 /** Format: uuid */
10026 project_id?: string | null;
10027 };
10028 /** @description PATCH body for /api/v1/chats/{chat_id}; partial update. */
10029 ChatUpdate: {
10030 title?: string;
10031 /** @description Set true to archive (soft-delete); false to unarchive. */
10032 archived?: boolean;
10033 };
10034 Message: {
10035 /** Format: uuid */
10036 id: string;
10037 /** Format: uuid */
10038 chat_id: string;
10039 /** @enum {string} */
10040 role: "user" | "assistant" | "system" | "tool";
10041 content: string;
10042 /**
10043 * @description C2 / ADR 0007: skills the gateway applied for this message
10044 * exchange. Captured on user messages from the request body
10045 * and on assistant messages from the gateway response.
10046 */
10047 applied_skills?: string[];
10048 /**
10049 * @description Tier the request was routed against (set by gateway). NULL on user messages.
10050 * @enum {integer|null}
10051 */
10052 routed_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
10053 /** @description Provider that handled the request (assistant only). */
10054 routed_provider?: string | null;
10055 /** @description Native provider model name (assistant only). */
10056 routed_model?: string | null;
10057 prompt_tokens?: number | null;
10058 completion_tokens?: number | null;
10059 /**
10060 * @description USD cost estimate. The DB stores integer USD micros
10061 * (1e-6 USD); the wire field is the floating-point USD value.
10062 */
10063 cost_estimate?: number | null;
10064 /**
10065 * @description Populated when the assistant message failed mid-stream or
10066 * the gateway raised. Carries the canonical Error code
10067 * (e.g., `provider_unavailable`, `gateway_timeout`).
10068 */
10069 error_code?: string | null;
10070 /** @description M2 populates this; M1 stores []. */
10071 citations?: components["schemas"]["Citation"][];
10072 /** Format: date-time */
10073 created_at: string;
10074 };
10075 MessageCreate: {
10076 content: string;
10077 /**
10078 * @description Model alias (e.g., 'smart', 'fast') or provider-native model name
10079 * @default smart
10080 */
10081 model: string;
10082 /**
10083 * @description When true, response is `text/event-stream` per OpenAI streaming
10084 * convention. When false, response is a single JSON body with the
10085 * assistant message and routing metadata (B5 default).
10086 * @default false
10087 */
10088 stream: boolean;
10089 /**
10090 * @description C2: skill names to attach. The backend forwards these as
10091 * `lq_ai_skills` to the gateway, which fetches each from the
10092 * backend's internal-skills endpoint and assembles the prompt.
10093 */
10094 skills?: string[];
10095 /**
10096 * @description C2: per-skill input bindings, keyed by skill name. Inner
10097 * dict maps input variable names to values. Required inputs
10098 * that the skill's frontmatter declares but the bindings
10099 * don't provide cause a 400 with `code=skill_input_missing`.
10100 *
10101 * Values are plain scalars interpolated into the skill body via
10102 * `{{name}}` substitution (ADR 0006). There is NO `type:"file"`
10103 * binding: passing a file UUID here interpolates the literal
10104 * UUID string, not the file's document text. For per-turn
10105 * document context use the separate `file_ids` channel below —
10106 * `skill_inputs` and `file_ids` are distinct; a `file_id` is
10107 * NOT bindable to a skill file-input via `skill_inputs`.
10108 */
10109 skill_inputs?: {
10110 [key: string]: {
10111 [key: string]: unknown;
10112 };
10113 };
10114 /**
10115 * @description Donna: caller-owned file UUIDs supplying ephemeral,
10116 * per-message document context for this one chat turn.
10117 * Distinct from KB attach (project-scoped, persistent) and
10118 * from `skill_inputs` (scalar skill params — see the note
10119 * above; no `type:"file"` binding is wired). Each id is
10120 * validated server-side to exist and be owned by the caller;
10121 * a foreign, unknown, or soft-deleted id returns 404
10122 * (id-probing-safe — indistinguishable from "not found").
10123 * Validated ids are forwarded to the gateway as `lq_ai_file_ids`
10124 * alongside `lq_ai_skills` and echoed back as `applied_file_ids`
10125 * on the response / SSE `complete` frame. Capped at 16 entries.
10126 * Omitted / empty is back-compatible.
10127 */
10128 file_ids?: string[];
10129 };
10130 /**
10131 * @description Non-streaming response for `POST /api/v1/chats/{chat_id}/messages`.
10132 * The `routed_inference_tier`, `routed_provider`, and `cost_estimate`
10133 * come from the gateway (B4). `applied_skills` lists the skills
10134 * that were assembled into the prompt (C2). After C3 the backend
10135 * also persists both the user and assistant message rows; the
10136 * `message.id` carried here resolves to the persisted assistant
10137 * row.
10138 */
10139 MessagePostResponse: {
10140 message: components["schemas"]["Message"];
10141 /** @description Empty until M2 (citation engine). */
10142 citations: components["schemas"]["Citation"][];
10143 /**
10144 * @description Routed Inference Tier as derived by the gateway (B4)
10145 * @enum {integer|null}
10146 */
10147 routed_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
10148 /** @description Provider name that handled the request (B4) */
10149 routed_provider?: string | null;
10150 /** @description USD cost estimate (gateway-side, when rate is configured) */
10151 cost_estimate?: number | null;
10152 /**
10153 * @description C2: skills that were successfully assembled into the prompt
10154 * for this request. Empty when no skills were attached.
10155 */
10156 applied_skills?: string[];
10157 /**
10158 * @description Donna: caller-owned file ids that were validated and
10159 * forwarded to the gateway as `lq_ai_file_ids` for this turn —
10160 * the echo of `MessageCreate.file_ids` (mirrors
10161 * `applied_skills`). Turn-scoped: there is no
10162 * `messages.file_ids` column, so this surfaces only on the send
10163 * response (and the SSE `complete` frame), not on rows read
10164 * back via `GET /chats/{id}/messages`. Empty when none attached.
10165 */
10166 applied_file_ids?: string[];
10167 };
10168 /**
10169 * @description Frames emitted on the SSE stream. The first frame is a `start`
10170 * event carrying the persisted assistant message id (so clients
10171 * can poll the row later). Subsequent frames are `delta` events,
10172 * each tagged with the `lq_ai_message_id`, the
10173 * `routed_inference_tier`, and `applied_skills`. The final frame
10174 * is either a `complete` event (success) or an `Error` envelope
10175 * (the stream ended in failure; the assistant row is persisted
10176 * with `error_code` populated for audit). Termination is
10177 * signalled by the OpenAI-style `data: [DONE]` line.
10178 */
10179 MessageStreamEvent: components["schemas"]["MessageStart"] | components["schemas"]["MessageDelta"] | components["schemas"]["MessageComplete"] | components["schemas"]["Error"];
10180 /**
10181 * @description Opening frame on the SSE stream. Carries the assistant message
10182 * id the backend generated *before* dispatch; the persisted row
10183 * will eventually carry this same id, so the client can fetch the
10184 * row later via `GET /chats/{id}/messages`.
10185 */
10186 MessageStart: {
10187 /** @enum {string} */
10188 type: "start";
10189 /** Format: uuid */
10190 lq_ai_message_id: string;
10191 /** Format: uuid */
10192 chat_id: string;
10193 };
10194 MessageDelta: {
10195 /** @enum {string} */
10196 type: "delta";
10197 delta: string;
10198 /**
10199 * Format: uuid
10200 * @description C3: the persisted assistant message id (stable across all frames).
10201 */
10202 lq_ai_message_id: string;
10203 /**
10204 * @description B4: routed tier surfaced on every chunk.
10205 * @enum {integer|null}
10206 */
10207 routed_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
10208 /** @description C2: skills assembled into the prompt. */
10209 applied_skills?: string[];
10210 };
10211 MessageComplete: {
10212 /** @enum {string} */
10213 type: "complete";
10214 /** Format: uuid */
10215 lq_ai_message_id: string;
10216 message: components["schemas"]["Message"];
10217 citations?: components["schemas"]["Citation"][];
10218 /**
10219 * @description C2: skills that were successfully assembled into the prompt
10220 * for this request. Empty when no skills were attached.
10221 */
10222 applied_skills?: string[];
10223 /**
10224 * @description Donna: caller-owned file ids forwarded to the gateway as
10225 * `lq_ai_file_ids` for this turn — the echo of
10226 * `MessageCreate.file_ids` (mirrors `applied_skills`).
10227 * Turn-scoped; not persisted. Empty when none attached.
10228 */
10229 applied_file_ids?: string[];
10230 /** @enum {integer|null} */
10231 routed_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
10232 routed_provider?: string | null;
10233 };
10234 Citation: {
10235 /** Format: uuid */
10236 id: string;
10237 /** Format: uuid */
10238 source_file_id: string;
10239 source_offset_start?: number;
10240 source_offset_end?: number;
10241 source_page?: number | null;
10242 /** @description Verbatim text from source — verified to appear in source */
10243 source_text: string;
10244 /** @description Citation engine verified the quote */
10245 verified?: boolean;
10246 /**
10247 * @description True when the paraphrase judge (M2-C1) returned ``partial`` — the source supports the claim but only partially. Drives the M2-C2 UI's "verified with caveats" rendering (verified-paraphrase state).
10248 * @default false
10249 */
10250 partial: boolean;
10251 };
10252 SkillSummary: {
10253 name: string;
10254 /** @description Skill version. Free-form to accommodate skills authored before the formal authoring guide; the loader defaults to ``"unversioned"`` when the source frontmatter omits this field. */
10255 version: string;
10256 /** @enum {string} */
10257 scope: "builtin" | "user" | "team";
10258 title: string;
10259 description?: string;
10260 /** @description Attribution string from the skill's ``lq_ai.author`` frontmatter (DE-316). Surfaced verbatim; absent when the frontmatter omits it. Read by the gateway's ``skill.execute`` OTel span as ``skill.author``. */
10261 author?: string;
10262 tags?: string[];
10263 /** @description Free-form jurisdiction tag (e.g., ``us``, ``regime-aware``, ``agnostic``). Surfaced verbatim from the skill's frontmatter. */
10264 jurisdiction?: string;
10265 /** @description Inference-tier floor declared by the skill, if any. The gateway's tier-floor enforcement (Task D1) consults this; C1 only surfaces the value. */
10266 minimum_inference_tier?: number;
10267 /** @description Skill's documented output format (e.g., ``report``, ``markdown``, ``structured_checklist``, ``redline``). Free-form per the corpus. */
10268 output_format?: string;
10269 };
10270 /** @description One column in an ``output_format: table`` skill (M3-C1). The Tabular Review workflow (M3-C2) runs each ``(document, column)`` cell as a Citation-Engine-grounded extraction. */
10271 ColumnSpec: {
10272 /** @description Grid column header. */
10273 name: string;
10274 /** @description Per-row extraction prompt instantiated against each document. */
10275 query: string;
10276 /** @description Per-column override of the skill-level ``ensemble_verification`` field. ``true`` routes this column's cells through Stage 4 of the Citation Engine cascade. */
10277 ensemble_verification?: boolean | null;
10278 /** @description Per-column override of the skill-level tier floor. High-stakes columns can demand Tier 4+ while routine columns route Tier 1. */
10279 minimum_inference_tier?: number | null;
10280 };
10281 Skill: components["schemas"]["SkillSummary"] & {
10282 /**
10283 * Format: uuid
10284 * @description Wave D.2 — underlying ``user_skills.id`` row UUID when the resolved scope is ``user`` or ``team``. ``null`` for built-in (filesystem-canonical) skills, which have no DB row. The skill detail page's Versions tab needs this id to call the audit-history endpoint; surfacing it on the detail GET avoids a second round-trip to resolve slug → id.
10285 */
10286 id?: string | null;
10287 /** @description Frontmatter YAML */
10288 content_yaml: string;
10289 /** @description Body markdown */
10290 content_md: string;
10291 reference_files?: {
10292 path: string;
10293 content: string;
10294 }[];
10295 example_files?: {
10296 path: string;
10297 content: string;
10298 }[];
10299 /** @description M3-C1 — column definitions for ``output_format: table`` skills. ``null`` for non-table skills. The validator rejects ``output_format: table`` skills that lack a non-empty ``columns`` list at load time. */
10300 columns?: components["schemas"]["ColumnSpec"][] | null;
10301 };
10302 SkillInputDef: {
10303 name: string;
10304 /** @description Free-form per corpus reality (text, enum, boolean, integer, structured, file, ...). */
10305 type?: string | null;
10306 /** @default false */
10307 required: boolean;
10308 description?: string | null;
10309 enum?: string[] | null;
10310 default?: unknown;
10311 };
10312 SkillInputs: {
10313 name: string;
10314 required: components["schemas"]["SkillInputDef"][];
10315 optional: components["schemas"]["SkillInputDef"][];
10316 };
10317 EnhancePromptAttachedSkill: {
10318 name: string;
10319 description?: string | null;
10320 };
10321 EnhancePromptAttachedFile: {
10322 /** Format: uuid */
10323 file_id?: string | null;
10324 filename: string;
10325 mime_type?: string | null;
10326 description?: string | null;
10327 };
10328 EnhancePromptRequest: {
10329 raw_input: string;
10330 /** Format: uuid */
10331 chat_id?: string | null;
10332 attached_skills?: components["schemas"]["EnhancePromptAttachedSkill"][];
10333 attached_files?: components["schemas"]["EnhancePromptAttachedFile"][];
10334 jurisdiction?: string | null;
10335 /** @description Optional model alias override. Defaults to ``fast``. */
10336 model?: string | null;
10337 };
10338 EnhancePromptResponse: {
10339 /** Format: uuid */
10340 interaction_id: string;
10341 expansion_applied: boolean;
10342 /**
10343 * @description The expansion when ``expansion_applied=true``, or the original
10344 * ``raw_input`` (echoed back for client convenience) when
10345 * ``expansion_applied=false``.
10346 */
10347 expanded_prompt: string;
10348 reasoning: string[];
10349 skip_reason?: string | null;
10350 /** @description Human-readable render of the expansion + reasoning the application can show in the review screen. */
10351 preview_to_user?: string;
10352 routed_inference_tier?: number | null;
10353 routed_provider?: string | null;
10354 routed_model?: string | null;
10355 };
10356 /** @description All fields optional; only supplied keys move. */
10357 EnhancePromptOutcomeUpdate: {
10358 used?: boolean | null;
10359 edited_before_use?: boolean | null;
10360 };
10361 CurrentTierResponse: {
10362 provider: string;
10363 model: string;
10364 routed_inference_tier?: number | null;
10365 routed_provider_type?: string | null;
10366 explanation?: string;
10367 };
10368 TierConfigResponse: {
10369 allowed_tiers_global: number[];
10370 default_minimum_tier: number;
10371 privileged_minimum_tier: number;
10372 warn_on_tiers: number[];
10373 };
10374 UsageRow: {
10375 group_key: string;
10376 request_count: number;
10377 tokens_in_sum: number;
10378 tokens_out_sum: number;
10379 cost_estimate_sum: number;
10380 };
10381 UsageResponse: {
10382 rows: components["schemas"]["UsageRow"][];
10383 /** @enum {string} */
10384 group_by: "user" | "provider" | "model" | "tier" | "day";
10385 total_request_count: number;
10386 total_tokens_in: number;
10387 total_tokens_out: number;
10388 total_cost_estimate: number;
10389 };
10390 ChatSearchHit: {
10391 /** Format: uuid */
10392 chat_id: string;
10393 title: string;
10394 snippet: string;
10395 /** @enum {string} */
10396 match_source: "title" | "message";
10397 rank: number;
10398 /** Format: date-time */
10399 created_at: string;
10400 /** Format: date-time */
10401 updated_at: string;
10402 };
10403 ChatSearchResponse: {
10404 items: components["schemas"]["ChatSearchHit"][];
10405 query: string;
10406 };
10407 File: {
10408 /** Format: uuid */
10409 id: string;
10410 /** Format: uuid */
10411 owner_id: string;
10412 /** Format: uuid */
10413 project_id?: string | null;
10414 filename: string;
10415 mime_type: string;
10416 size_bytes: number;
10417 hash_sha256?: string;
10418 /**
10419 * @description Lifecycle: `pending` (just uploaded; C4 sets this on insert)
10420 * → `processing` (C5 worker has picked it up) → `ready`
10421 * (parsed, chunks persisted) OR `failed` (with `ingestion_error`
10422 * set; e.g., `unsupported_type` for non-PDF MIMEs in M1, or
10423 * `parse_failed` for corrupt PDFs).
10424 * @enum {string}
10425 */
10426 ingestion_status?: "pending" | "processing" | "ready" | "failed";
10427 /**
10428 * @description Set when `ingestion_status='failed'`. M1 values include
10429 * `unsupported_type`, `unsupported_content`, `parse_failed`.
10430 */
10431 ingestion_error?: string | null;
10432 /**
10433 * @description Populated by the C5 document pipeline once
10434 * `ingestion_status='ready'`; `null` while pending /
10435 * processing / failed.
10436 */
10437 page_count?: number | null;
10438 /**
10439 * @description Populated by the C5 document pipeline once
10440 * `ingestion_status='ready'`; counts characters of the
10441 * canonical PyMuPDF-extracted text, not of the original
10442 * PDF byte stream.
10443 */
10444 character_count?: number | null;
10445 /**
10446 * Format: uuid
10447 * @description UUID of the parsed-content `documents` row (M3-A6 Phase 6).
10448 * Distinct from `id` (the File UUID). `null` until the C5
10449 * parse pipeline produces the row; the Easy Playbook wizard
10450 * polls `GET /files/{id}` until this flips non-null, then
10451 * passes the value to `POST /playbooks/easy` in `document_ids`.
10452 */
10453 document_id?: string | null;
10454 /** Format: date-time */
10455 created_at: string;
10456 };
10457 KnowledgeBase: {
10458 /** Format: uuid */
10459 id: string;
10460 name: string;
10461 description?: string | null;
10462 /** Format: uuid */
10463 owner_id: string;
10464 /** Format: uuid */
10465 project_id?: string | null;
10466 /**
10467 * @description Per-KB default for the hybrid score combine. 0 = vector-only;
10468 * 1 = FTS-only; 0.5 = balanced (per ADR 0008).
10469 */
10470 hybrid_alpha: number;
10471 file_count: number;
10472 chunk_count: number;
10473 /** Format: date-time */
10474 archived_at?: string | null;
10475 /** Format: date-time */
10476 created_at: string;
10477 /** Format: date-time */
10478 updated_at: string;
10479 };
10480 KnowledgeBaseCreate: {
10481 name: string;
10482 description?: string | null;
10483 /** Format: uuid */
10484 project_id?: string | null;
10485 /** @default 0.5 */
10486 hybrid_alpha: number;
10487 };
10488 KnowledgeBaseUpdate: {
10489 name?: string;
10490 description?: string | null;
10491 /** Format: uuid */
10492 project_id?: string | null;
10493 hybrid_alpha?: number;
10494 /** @description true to archive; false to unarchive */
10495 archived?: boolean;
10496 };
10497 /**
10498 * @description One row of `GET /api/v1/knowledge-bases/{kb_id}/files`. Mirrors
10499 * `File` (the wire shape returned by `POST /files` /
10500 * `GET /files/{id}`) and adds `attached_at` from the join row so
10501 * the Knowledge surface can sort by attach time.
10502 */
10503 KBFile: {
10504 /** Format: uuid */
10505 id: string;
10506 /** Format: uuid */
10507 owner_id: string;
10508 /** Format: uuid */
10509 project_id?: string | null;
10510 filename: string;
10511 mime_type: string;
10512 size_bytes: number;
10513 hash_sha256: string;
10514 /** @enum {string} */
10515 ingestion_status: "pending" | "processing" | "ready" | "failed";
10516 ingestion_error?: string | null;
10517 page_count?: number | null;
10518 character_count?: number | null;
10519 /**
10520 * Format: uuid
10521 * @description The parsed-content `documents` row UUID, distinct from `id`
10522 * (the File UUID). Null until the C5 parse pipeline produces
10523 * a documents row. Added in M3-A4 so playbook-execute callers
10524 * can resolve File → Document client-side without a second
10525 * fetch.
10526 */
10527 document_id?: string | null;
10528 /** Format: date-time */
10529 created_at: string;
10530 /**
10531 * Format: date-time
10532 * @description When the file was attached to this KB (from
10533 * `knowledge_base_files.attached_at`).
10534 */
10535 attached_at: string;
10536 };
10537 KBQueryRequest: {
10538 query: string;
10539 /** @default 10 */
10540 top_k: number;
10541 /**
10542 * @description Override the KB's stored alpha for this query. Omit to use
10543 * the KB default.
10544 */
10545 hybrid_alpha?: number;
10546 };
10547 KBQueryResponse: {
10548 results: components["schemas"]["SearchResult"][];
10549 /** @description The alpha actually used (per-query override or KB default). */
10550 hybrid_alpha: number;
10551 };
10552 SearchResult: {
10553 chunk: {
10554 /** Format: uuid */
10555 id: string;
10556 /** Format: uuid */
10557 document_id: string;
10558 /** Format: uuid */
10559 file_id: string;
10560 file_name: string;
10561 content: string;
10562 page_start?: number | null;
10563 page_end?: number | null;
10564 char_offset_start: number;
10565 char_offset_end: number;
10566 };
10567 /** @description Combined hybrid score: (1 - alpha) * vector + alpha * fts. Min-max normalized; in [0, 1]. */
10568 score: number;
10569 score_components: {
10570 vector: number;
10571 fts: number;
10572 };
10573 };
10574 SavedPrompt: {
10575 /** Format: uuid */
10576 id: string;
10577 /** Format: uuid */
10578 user_id: string;
10579 name: string;
10580 prompt_text: string;
10581 tags?: string[];
10582 /** Format: date-time */
10583 created_at: string;
10584 /** Format: date-time */
10585 updated_at?: string;
10586 };
10587 UserSkill: {
10588 /** Format: uuid */
10589 id: string;
10590 /**
10591 * @description 'user' is owned by ``owner_user_id``; 'team' is owned by ``owner_team_id`` (D8.1b).
10592 * @enum {string}
10593 */
10594 scope: "user" | "team";
10595 /**
10596 * Format: uuid
10597 * @description Set when scope='user'; null when scope='team'. Exactly one of owner_user_id / owner_team_id is non-null (enforced by ck_user_skills_scope_owner_consistency).
10598 */
10599 owner_user_id?: string | null;
10600 /**
10601 * Format: uuid
10602 * @description Set when scope='team' (D8.1b). null when scope='user'.
10603 */
10604 owner_team_id?: string | null;
10605 /** @description Stable identifier; matches filesystem skill folder-name conventions. */
10606 slug: string;
10607 display_name: string;
10608 description: string;
10609 /** @description User-set free-form semver (e.g. "1.0.0"). */
10610 version: string;
10611 tags?: string[];
10612 /** @description Arbitrary ``lq_ai:`` extension keys (jurisdiction, output_format, minimum_inference_tier, etc.). */
10613 frontmatter_extra?: {
10614 [key: string]: unknown;
10615 };
10616 body: string;
10617 /** @description Chat-composer trigger alias (e.g. ``/nda``). Added by migration 0023. */
10618 slash_alias?: string | null;
10619 /** @description Slug of the source skill when this row was forked. Added by migration 0023. */
10620 forked_from?: string | null;
10621 /**
10622 * Format: date-time
10623 * @description Soft-delete timestamp; null when active.
10624 */
10625 archived_at?: string | null;
10626 /** Format: date-time */
10627 created_at: string;
10628 /** Format: date-time */
10629 updated_at: string;
10630 };
10631 UserSkillCreate: {
10632 slug: string;
10633 display_name: string;
10634 description: string;
10635 body: string;
10636 /** @default 1.0.0 */
10637 version: string;
10638 tags?: string[];
10639 frontmatter_extra?: {
10640 [key: string]: unknown;
10641 };
10642 /**
10643 * @description Defaults to 'user'. 'team' (D8.1b) requires owner_team_id and gates on team-admin role.
10644 * @default user
10645 * @enum {string}
10646 */
10647 scope: "user" | "team";
10648 /**
10649 * Format: uuid
10650 * @description Required when scope='team'; must be null when scope='user'. 422 if the combination is inconsistent. 404 if the caller is not a team-admin of the named team (id-probing-safe).
10651 */
10652 owner_team_id?: string | null;
10653 /**
10654 * @description Optional chat-composer trigger alias (e.g. ``/nda``). Must be
10655 * ``/`` followed by 1–32 lowercase alphanumeric-or-dash chars.
10656 * Unique per owner within the active (non-archived) set; 422 on
10657 * collision with ``"slash_alias '...' is already used by another
10658 * of your skills."``.
10659 */
10660 slash_alias?: string | null;
10661 /**
10662 * @description Slug of the source skill when this row was created by forking a
10663 * built-in or another skill via the detail-page fork button. Set
10664 * on create; read-only afterward. Stored as plain text (no FK —
10665 * the source may be a filesystem-canonical built-in with no DB row).
10666 */
10667 forked_from?: string | null;
10668 };
10669 /** @description All fields optional; only supplied keys are updated. */
10670 UserSkillUpdate: {
10671 display_name?: string;
10672 description?: string;
10673 body?: string;
10674 version?: string;
10675 tags?: string[];
10676 frontmatter_extra?: {
10677 [key: string]: unknown;
10678 };
10679 /**
10680 * @description Update the chat-composer trigger alias. Same validation and
10681 * uniqueness rules as on create. 422 on collision.
10682 */
10683 slash_alias?: string | null;
10684 };
10685 TeamSummary: {
10686 /** Format: uuid */
10687 id: string;
10688 slug: string;
10689 name: string;
10690 description?: string | null;
10691 /** Format: uuid */
10692 created_by_user_id: string;
10693 /** Format: date-time */
10694 created_at: string;
10695 /** Format: date-time */
10696 updated_at: string;
10697 member_count?: number;
10698 /**
10699 * @description The caller's role on this team (D8.1c). Populated by the
10700 * user-facing ``GET /api/v1/teams`` and
10701 * ``GET /api/v1/teams/{id}`` endpoints. Null on operator-admin
10702 * views (``/admin/teams``) when the admin isn't a member.
10703 * @enum {string|null}
10704 */
10705 caller_role?: "admin" | "member" | null;
10706 };
10707 TeamMember: {
10708 /** Format: uuid */
10709 user_id: string;
10710 email: string;
10711 display_name?: string | null;
10712 /** @enum {string} */
10713 role: "admin" | "member";
10714 /** Format: uuid */
10715 added_by_user_id: string;
10716 /** Format: date-time */
10717 created_at: string;
10718 };
10719 Team: components["schemas"]["TeamSummary"] & {
10720 members?: components["schemas"]["TeamMember"][];
10721 };
10722 TeamCreate: {
10723 slug: string;
10724 name: string;
10725 description?: string | null;
10726 };
10727 TeamUpdate: {
10728 name?: string;
10729 description?: string | null;
10730 };
10731 AuditLogEntry: {
10732 /** Format: uuid */
10733 id: string;
10734 /** Format: date-time */
10735 timestamp: string;
10736 /** Format: uuid */
10737 user_id?: string | null;
10738 /** @description e.g. chat.create, message.send, skill.fork */
10739 action: string;
10740 /** @description e.g. chat, project, skill */
10741 resource_type: string;
10742 resource_id?: string | null;
10743 privilege_marked?: boolean;
10744 /** @description Why this entry was marked privileged */
10745 privilege_basis?: string | null;
10746 /** @enum {integer|null} */
10747 routed_inference_tier?: 1 | 2 | 3 | 4 | 5 | null;
10748 ip_address?: string | null;
10749 user_agent?: string | null;
10750 details?: {
10751 [key: string]: unknown;
10752 };
10753 };
10754 TierPolicy: {
10755 /** @description Tiers globally allowed; requests at other tiers refused */
10756 allowed_tiers_global: (1 | 2 | 3 | 4 | 5)[];
10757 /**
10758 * @description Default minimum tier for non-privileged work
10759 * @enum {integer}
10760 */
10761 default_minimum_tier: 1 | 2 | 3 | 4 | 5;
10762 /**
10763 * @description Minimum tier when privileged: true on Project
10764 * @enum {integer}
10765 */
10766 privileged_minimum_tier?: 1 | 2 | 3 | 4 | 5;
10767 /** @description Tiers that surface a warning in the chat UI */
10768 warn_on_tiers?: (1 | 2 | 3 | 4 | 5)[];
10769 };
10770 AdminAliasFallback: {
10771 provider: string;
10772 model: string;
10773 };
10774 AdminAliasEntry: {
10775 name: string;
10776 provider: string;
10777 model: string;
10778 fallback: components["schemas"]["AdminAliasFallback"][];
10779 /** @enum {integer} */
10780 primary_inference_tier?: 1 | 2 | 3 | 4 | 5;
10781 };
10782 AdminAliasCreate: {
10783 name: string;
10784 provider: string;
10785 model: string;
10786 fallback?: components["schemas"]["AdminAliasFallback"][];
10787 };
10788 AdminAliasUpdate: {
10789 provider: string;
10790 model: string;
10791 fallback?: components["schemas"]["AdminAliasFallback"][];
10792 };
10793 ProviderKeySetRequest: {
10794 /** @description Name of an already-configured provider entry. */
10795 provider: string;
10796 /**
10797 * @description Plaintext provider key. The gateway encrypts it with the
10798 * gateway master key before it touches disk; it is never echoed
10799 * back in any response.
10800 */
10801 api_key: string;
10802 };
10803 ProviderKeyRotateRequest: {
10804 /**
10805 * @description Replacement plaintext provider key. Same handling as
10806 * ProviderKeySetRequest.api_key.
10807 */
10808 api_key: string;
10809 };
10810 /**
10811 * @description Secret-safe status for one provider. Carries at most the last 4
10812 * characters of the resolved key — never the full key or token.
10813 */
10814 ProviderKeyStatus: {
10815 provider: string;
10816 /**
10817 * @description The provider's adapter type. Null only in the rare race where a
10818 * provider is removed between the write and the status read-back on
10819 * a single-provider (POST/PATCH) response; never null on the list.
10820 */
10821 type: string | null;
10822 /**
10823 * @description True when the provider has a live, routable adapter (its key
10824 * resolved). A keyless or unresolvable provider is false.
10825 */
10826 configured: boolean;
10827 /**
10828 * @description Last 4 characters of the resolved key, or null when the key is
10829 * absent, unresolvable, or shorter than 4 characters.
10830 */
10831 last4: string | null;
10832 /**
10833 * @description Key source: `runtime` (encrypted-at-rest in gateway.yaml),
10834 * `env` (api_key_env), or null (no key configured).
10835 * @enum {string|null}
10836 */
10837 source: "env" | "runtime" | null;
10838 };
10839 ProviderKeyList: {
10840 provider_keys: components["schemas"]["ProviderKeyStatus"][];
10841 };
10842 /**
10843 * @description Canonical structured error envelope returned by every backend
10844 * endpoint that raises a typed error (`LQAIError` subclass).
10845 * Implemented by the FastAPI exception handler in
10846 * `api/app/main.py`; per ADR 0003 the wrapper key is `detail`
10847 * (matching FastAPI's native `HTTPException` response and the
10848 * existing B2 forced-password-change pattern). The inner shape
10849 * — `code` + `message` + `details` — is the load-bearing
10850 * contract and matches the shape the gateway uses on its side
10851 * under the `error` wrapper (see gateway-openapi.yaml's
10852 * `GatewayError`).
10853 *
10854 * The pre-existing 501 stub envelope (returned by un-implemented
10855 * endpoints during M1 buildout, A4 scaffold) uses the legacy
10856 * `{"error": {...}}` wrapper instead. Each stub is replaced by a
10857 * real handler as its M1 task lands; new typed errors all use
10858 * the `detail` wrapper above.
10859 */
10860 Error: {
10861 detail: {
10862 /**
10863 * @description Stable error code. Backend-only codes:
10864 * `password_change_required`, `payload_too_large`,
10865 * `conflict`, `unauthorized`, `forbidden`, `not_found`,
10866 * `validation_error`, `rate_limited`, `internal_error`.
10867 * Backend↔gateway crossing codes (propagated from gateway
10868 * responses):
10869 * `gateway_unreachable`, `gateway_timeout`,
10870 * `gateway_invalid_response`, `provider_unavailable`,
10871 * `tier_below_minimum`, `invalid_model`,
10872 * `skill_not_found`, `skill_fetch_failed`,
10873 * `skill_input_missing` (C2).
10874 * @enum {string}
10875 */
10876 code: "password_change_required" | "payload_too_large" | "conflict" | "unauthorized" | "forbidden" | "not_found" | "validation_error" | "rate_limited" | "internal_error" | "gateway_unreachable" | "gateway_timeout" | "gateway_invalid_response" | "provider_unavailable" | "tier_below_minimum" | "invalid_model" | "skill_not_found" | "skill_fetch_failed" | "skill_input_missing";
10877 message: string;
10878 details?: {
10879 [key: string]: unknown;
10880 };
10881 };
10882 };
10883 };
10884 responses: never;
10885 parameters: never;
10886 requestBodies: never;
10887 headers: never;
10888 pathItems: never;
10889}
10890export type $defs = Record<string, never>;
10891export type operations = Record<string, never>;
10892