1use std::sync::OnceLock;
12
13use harn_vm::{VmDictExt, VmValue};
14
15use crate::error::HostlibError;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum SchemaKind {
20 Request,
22 Response,
24}
25
26pub const SCHEMAS: &[(&str, &str, SchemaKind, &str)] = &[
33 (
35 "ast",
36 "parse_file",
37 SchemaKind::Request,
38 include_str!("../schemas/ast/parse_file.request.json"),
39 ),
40 (
41 "ast",
42 "parse_file",
43 SchemaKind::Response,
44 include_str!("../schemas/ast/parse_file.response.json"),
45 ),
46 (
47 "ast",
48 "symbols",
49 SchemaKind::Request,
50 include_str!("../schemas/ast/symbols.request.json"),
51 ),
52 (
53 "ast",
54 "symbols",
55 SchemaKind::Response,
56 include_str!("../schemas/ast/symbols.response.json"),
57 ),
58 (
59 "ast",
60 "outline",
61 SchemaKind::Request,
62 include_str!("../schemas/ast/outline.request.json"),
63 ),
64 (
65 "ast",
66 "outline",
67 SchemaKind::Response,
68 include_str!("../schemas/ast/outline.response.json"),
69 ),
70 (
71 "ast",
72 "parse_errors",
73 SchemaKind::Request,
74 include_str!("../schemas/ast/parse_errors.request.json"),
75 ),
76 (
77 "ast",
78 "parse_errors",
79 SchemaKind::Response,
80 include_str!("../schemas/ast/parse_errors.response.json"),
81 ),
82 (
83 "ast",
84 "undefined_names",
85 SchemaKind::Request,
86 include_str!("../schemas/ast/undefined_names.request.json"),
87 ),
88 (
89 "ast",
90 "undefined_names",
91 SchemaKind::Response,
92 include_str!("../schemas/ast/undefined_names.response.json"),
93 ),
94 (
95 "ast",
96 "function_body",
97 SchemaKind::Request,
98 include_str!("../schemas/ast/function_body.request.json"),
99 ),
100 (
101 "ast",
102 "function_body",
103 SchemaKind::Response,
104 include_str!("../schemas/ast/function_body.response.json"),
105 ),
106 (
107 "ast",
108 "function_bodies",
109 SchemaKind::Request,
110 include_str!("../schemas/ast/function_bodies.request.json"),
111 ),
112 (
113 "ast",
114 "function_bodies",
115 SchemaKind::Response,
116 include_str!("../schemas/ast/function_bodies.response.json"),
117 ),
118 (
119 "ast",
120 "extract_imports",
121 SchemaKind::Request,
122 include_str!("../schemas/ast/extract_imports.request.json"),
123 ),
124 (
125 "ast",
126 "extract_imports",
127 SchemaKind::Response,
128 include_str!("../schemas/ast/extract_imports.response.json"),
129 ),
130 (
131 "ast",
132 "symbol_extract",
133 SchemaKind::Request,
134 include_str!("../schemas/ast/symbol_extract.request.json"),
135 ),
136 (
137 "ast",
138 "symbol_extract",
139 SchemaKind::Response,
140 include_str!("../schemas/ast/symbol_extract.response.json"),
141 ),
142 (
143 "ast",
144 "symbol_delete",
145 SchemaKind::Request,
146 include_str!("../schemas/ast/symbol_delete.request.json"),
147 ),
148 (
149 "ast",
150 "symbol_delete",
151 SchemaKind::Response,
152 include_str!("../schemas/ast/symbol_delete.response.json"),
153 ),
154 (
155 "ast",
156 "symbol_replace",
157 SchemaKind::Request,
158 include_str!("../schemas/ast/symbol_replace.request.json"),
159 ),
160 (
161 "ast",
162 "symbol_replace",
163 SchemaKind::Response,
164 include_str!("../schemas/ast/symbol_replace.response.json"),
165 ),
166 (
167 "ast",
168 "bracket_balance",
169 SchemaKind::Request,
170 include_str!("../schemas/ast/bracket_balance.request.json"),
171 ),
172 (
173 "ast",
174 "bracket_balance",
175 SchemaKind::Response,
176 include_str!("../schemas/ast/bracket_balance.response.json"),
177 ),
178 (
179 "ast",
180 "apply_node",
181 SchemaKind::Request,
182 include_str!("../schemas/ast/apply_node.request.json"),
183 ),
184 (
185 "ast",
186 "apply_node",
187 SchemaKind::Response,
188 include_str!("../schemas/ast/apply_node.response.json"),
189 ),
190 (
191 "ast",
192 "insert_at_anchor",
193 SchemaKind::Request,
194 include_str!("../schemas/ast/insert_at_anchor.request.json"),
195 ),
196 (
197 "ast",
198 "insert_at_anchor",
199 SchemaKind::Response,
200 include_str!("../schemas/ast/insert_at_anchor.response.json"),
201 ),
202 (
203 "ast",
204 "batch_apply",
205 SchemaKind::Request,
206 include_str!("../schemas/ast/batch_apply.request.json"),
207 ),
208 (
209 "ast",
210 "batch_apply",
211 SchemaKind::Response,
212 include_str!("../schemas/ast/batch_apply.response.json"),
213 ),
214 (
215 "ast",
216 "dry_run",
217 SchemaKind::Request,
218 include_str!("../schemas/ast/dry_run.request.json"),
219 ),
220 (
221 "ast",
222 "dry_run",
223 SchemaKind::Response,
224 include_str!("../schemas/ast/dry_run.response.json"),
225 ),
226 (
227 "ast",
228 "search",
229 SchemaKind::Request,
230 include_str!("../schemas/ast/search.request.json"),
231 ),
232 (
233 "ast",
234 "search",
235 SchemaKind::Response,
236 include_str!("../schemas/ast/search.response.json"),
237 ),
238 (
239 "ast",
240 "structural_diff",
241 SchemaKind::Request,
242 include_str!("../schemas/ast/structural_diff.request.json"),
243 ),
244 (
245 "ast",
246 "structural_diff",
247 SchemaKind::Response,
248 include_str!("../schemas/ast/structural_diff.response.json"),
249 ),
250 (
251 "ast",
252 "capabilities",
253 SchemaKind::Request,
254 include_str!("../schemas/ast/capabilities.request.json"),
255 ),
256 (
257 "ast",
258 "capabilities",
259 SchemaKind::Response,
260 include_str!("../schemas/ast/capabilities.response.json"),
261 ),
262 (
264 "code_index",
265 "query",
266 SchemaKind::Request,
267 include_str!("../schemas/code_index/query.request.json"),
268 ),
269 (
270 "code_index",
271 "query",
272 SchemaKind::Response,
273 include_str!("../schemas/code_index/query.response.json"),
274 ),
275 (
276 "code_index",
277 "rebuild",
278 SchemaKind::Request,
279 include_str!("../schemas/code_index/rebuild.request.json"),
280 ),
281 (
282 "code_index",
283 "rebuild",
284 SchemaKind::Response,
285 include_str!("../schemas/code_index/rebuild.response.json"),
286 ),
287 (
288 "code_index",
289 "stats",
290 SchemaKind::Request,
291 include_str!("../schemas/code_index/stats.request.json"),
292 ),
293 (
294 "code_index",
295 "stats",
296 SchemaKind::Response,
297 include_str!("../schemas/code_index/stats.response.json"),
298 ),
299 (
300 "code_index",
301 "imports_for",
302 SchemaKind::Request,
303 include_str!("../schemas/code_index/imports_for.request.json"),
304 ),
305 (
306 "code_index",
307 "imports_for",
308 SchemaKind::Response,
309 include_str!("../schemas/code_index/imports_for.response.json"),
310 ),
311 (
312 "code_index",
313 "importers_of",
314 SchemaKind::Request,
315 include_str!("../schemas/code_index/importers_of.request.json"),
316 ),
317 (
318 "code_index",
319 "importers_of",
320 SchemaKind::Response,
321 include_str!("../schemas/code_index/importers_of.response.json"),
322 ),
323 (
325 "code_index",
326 "add_readonly_roots",
327 SchemaKind::Request,
328 include_str!("../schemas/code_index/add_readonly_roots.request.json"),
329 ),
330 (
331 "code_index",
332 "add_readonly_roots",
333 SchemaKind::Response,
334 include_str!("../schemas/code_index/add_readonly_roots.response.json"),
335 ),
336 (
338 "code_index",
339 "path_to_id",
340 SchemaKind::Request,
341 include_str!("../schemas/code_index/path_to_id.request.json"),
342 ),
343 (
344 "code_index",
345 "path_to_id",
346 SchemaKind::Response,
347 include_str!("../schemas/code_index/path_to_id.response.json"),
348 ),
349 (
350 "code_index",
351 "id_to_path",
352 SchemaKind::Request,
353 include_str!("../schemas/code_index/id_to_path.request.json"),
354 ),
355 (
356 "code_index",
357 "id_to_path",
358 SchemaKind::Response,
359 include_str!("../schemas/code_index/id_to_path.response.json"),
360 ),
361 (
362 "code_index",
363 "file_ids",
364 SchemaKind::Request,
365 include_str!("../schemas/code_index/file_ids.request.json"),
366 ),
367 (
368 "code_index",
369 "file_ids",
370 SchemaKind::Response,
371 include_str!("../schemas/code_index/file_ids.response.json"),
372 ),
373 (
374 "code_index",
375 "file_meta",
376 SchemaKind::Request,
377 include_str!("../schemas/code_index/file_meta.request.json"),
378 ),
379 (
380 "code_index",
381 "file_meta",
382 SchemaKind::Response,
383 include_str!("../schemas/code_index/file_meta.response.json"),
384 ),
385 (
386 "code_index",
387 "file_hash",
388 SchemaKind::Request,
389 include_str!("../schemas/code_index/file_hash.request.json"),
390 ),
391 (
392 "code_index",
393 "file_hash",
394 SchemaKind::Response,
395 include_str!("../schemas/code_index/file_hash.response.json"),
396 ),
397 (
398 "code_index",
399 "file_hash_snapshot",
400 SchemaKind::Request,
401 include_str!("../schemas/code_index/file_hash_snapshot.request.json"),
402 ),
403 (
404 "code_index",
405 "file_hash_snapshot",
406 SchemaKind::Response,
407 include_str!("../schemas/code_index/file_hash_snapshot.response.json"),
408 ),
409 (
411 "code_index",
412 "read_range",
413 SchemaKind::Request,
414 include_str!("../schemas/code_index/read_range.request.json"),
415 ),
416 (
417 "code_index",
418 "read_range",
419 SchemaKind::Response,
420 include_str!("../schemas/code_index/read_range.response.json"),
421 ),
422 (
423 "code_index",
424 "reindex_file",
425 SchemaKind::Request,
426 include_str!("../schemas/code_index/reindex_file.request.json"),
427 ),
428 (
429 "code_index",
430 "reindex_file",
431 SchemaKind::Response,
432 include_str!("../schemas/code_index/reindex_file.response.json"),
433 ),
434 (
435 "code_index",
436 "trigram_query",
437 SchemaKind::Request,
438 include_str!("../schemas/code_index/trigram_query.request.json"),
439 ),
440 (
441 "code_index",
442 "trigram_query",
443 SchemaKind::Response,
444 include_str!("../schemas/code_index/trigram_query.response.json"),
445 ),
446 (
447 "code_index",
448 "extract_trigrams",
449 SchemaKind::Request,
450 include_str!("../schemas/code_index/extract_trigrams.request.json"),
451 ),
452 (
453 "code_index",
454 "extract_trigrams",
455 SchemaKind::Response,
456 include_str!("../schemas/code_index/extract_trigrams.response.json"),
457 ),
458 (
459 "code_index",
460 "word_get",
461 SchemaKind::Request,
462 include_str!("../schemas/code_index/word_get.request.json"),
463 ),
464 (
465 "code_index",
466 "word_get",
467 SchemaKind::Response,
468 include_str!("../schemas/code_index/word_get.response.json"),
469 ),
470 (
471 "code_index",
472 "deps_get",
473 SchemaKind::Request,
474 include_str!("../schemas/code_index/deps_get.request.json"),
475 ),
476 (
477 "code_index",
478 "deps_get",
479 SchemaKind::Response,
480 include_str!("../schemas/code_index/deps_get.response.json"),
481 ),
482 (
483 "code_index",
484 "outline_get",
485 SchemaKind::Request,
486 include_str!("../schemas/code_index/outline_get.request.json"),
487 ),
488 (
489 "code_index",
490 "outline_get",
491 SchemaKind::Response,
492 include_str!("../schemas/code_index/outline_get.response.json"),
493 ),
494 (
496 "code_index",
497 "current_seq",
498 SchemaKind::Request,
499 include_str!("../schemas/code_index/current_seq.request.json"),
500 ),
501 (
502 "code_index",
503 "current_seq",
504 SchemaKind::Response,
505 include_str!("../schemas/code_index/current_seq.response.json"),
506 ),
507 (
508 "code_index",
509 "changes_since",
510 SchemaKind::Request,
511 include_str!("../schemas/code_index/changes_since.request.json"),
512 ),
513 (
514 "code_index",
515 "changes_since",
516 SchemaKind::Response,
517 include_str!("../schemas/code_index/changes_since.response.json"),
518 ),
519 (
520 "code_index",
521 "version_record",
522 SchemaKind::Request,
523 include_str!("../schemas/code_index/version_record.request.json"),
524 ),
525 (
526 "code_index",
527 "version_record",
528 SchemaKind::Response,
529 include_str!("../schemas/code_index/version_record.response.json"),
530 ),
531 (
533 "code_index",
534 "agent_register",
535 SchemaKind::Request,
536 include_str!("../schemas/code_index/agent_register.request.json"),
537 ),
538 (
539 "code_index",
540 "agent_register",
541 SchemaKind::Response,
542 include_str!("../schemas/code_index/agent_register.response.json"),
543 ),
544 (
545 "code_index",
546 "agent_heartbeat",
547 SchemaKind::Request,
548 include_str!("../schemas/code_index/agent_heartbeat.request.json"),
549 ),
550 (
551 "code_index",
552 "agent_heartbeat",
553 SchemaKind::Response,
554 include_str!("../schemas/code_index/agent_heartbeat.response.json"),
555 ),
556 (
557 "code_index",
558 "agent_unregister",
559 SchemaKind::Request,
560 include_str!("../schemas/code_index/agent_unregister.request.json"),
561 ),
562 (
563 "code_index",
564 "agent_unregister",
565 SchemaKind::Response,
566 include_str!("../schemas/code_index/agent_unregister.response.json"),
567 ),
568 (
569 "code_index",
570 "lock_try",
571 SchemaKind::Request,
572 include_str!("../schemas/code_index/lock_try.request.json"),
573 ),
574 (
575 "code_index",
576 "lock_try",
577 SchemaKind::Response,
578 include_str!("../schemas/code_index/lock_try.response.json"),
579 ),
580 (
581 "code_index",
582 "lock_release",
583 SchemaKind::Request,
584 include_str!("../schemas/code_index/lock_release.request.json"),
585 ),
586 (
587 "code_index",
588 "lock_release",
589 SchemaKind::Response,
590 include_str!("../schemas/code_index/lock_release.response.json"),
591 ),
592 (
593 "code_index",
594 "status",
595 SchemaKind::Request,
596 include_str!("../schemas/code_index/status.request.json"),
597 ),
598 (
599 "code_index",
600 "status",
601 SchemaKind::Response,
602 include_str!("../schemas/code_index/status.response.json"),
603 ),
604 (
605 "code_index",
606 "current_agent_id",
607 SchemaKind::Request,
608 include_str!("../schemas/code_index/current_agent_id.request.json"),
609 ),
610 (
611 "code_index",
612 "current_agent_id",
613 SchemaKind::Response,
614 include_str!("../schemas/code_index/current_agent_id.response.json"),
615 ),
616 (
617 "code_index",
618 "cypher",
619 SchemaKind::Request,
620 include_str!("../schemas/code_index/cypher.request.json"),
621 ),
622 (
623 "code_index",
624 "cypher",
625 SchemaKind::Response,
626 include_str!("../schemas/code_index/cypher.response.json"),
627 ),
628 (
629 "code_index",
630 "repo_map",
631 SchemaKind::Request,
632 include_str!("../schemas/code_index/repo_map.request.json"),
633 ),
634 (
635 "code_index",
636 "repo_map",
637 SchemaKind::Response,
638 include_str!("../schemas/code_index/repo_map.response.json"),
639 ),
640 (
641 "code_index",
642 "branch_overlay",
643 SchemaKind::Request,
644 include_str!("../schemas/code_index/branch_overlay.request.json"),
645 ),
646 (
647 "code_index",
648 "branch_overlay",
649 SchemaKind::Response,
650 include_str!("../schemas/code_index/branch_overlay.response.json"),
651 ),
652 (
653 "code_index",
654 "freshness",
655 SchemaKind::Request,
656 include_str!("../schemas/code_index/freshness.request.json"),
657 ),
658 (
659 "code_index",
660 "freshness",
661 SchemaKind::Response,
662 include_str!("../schemas/code_index/freshness.response.json"),
663 ),
664 (
665 "code_index",
666 "rename_symbol",
667 SchemaKind::Request,
668 include_str!("../schemas/code_index/rename_symbol.request.json"),
669 ),
670 (
671 "code_index",
672 "rename_symbol",
673 SchemaKind::Response,
674 include_str!("../schemas/code_index/rename_symbol.response.json"),
675 ),
676 (
678 "scanner",
679 "scan_project",
680 SchemaKind::Request,
681 include_str!("../schemas/scanner/scan_project.request.json"),
682 ),
683 (
684 "scanner",
685 "scan_project",
686 SchemaKind::Response,
687 include_str!("../schemas/scanner/scan_project.response.json"),
688 ),
689 (
690 "scanner",
691 "scan_incremental",
692 SchemaKind::Request,
693 include_str!("../schemas/scanner/scan_incremental.request.json"),
694 ),
695 (
696 "scanner",
697 "scan_incremental",
698 SchemaKind::Response,
699 include_str!("../schemas/scanner/scan_incremental.response.json"),
700 ),
701 (
703 "fs",
704 "set_mode",
705 SchemaKind::Request,
706 include_str!("../schemas/fs/set_mode.request.json"),
707 ),
708 (
709 "fs",
710 "set_mode",
711 SchemaKind::Response,
712 include_str!("../schemas/fs/set_mode.response.json"),
713 ),
714 (
715 "fs",
716 "staged_status",
717 SchemaKind::Request,
718 include_str!("../schemas/fs/staged_status.request.json"),
719 ),
720 (
721 "fs",
722 "staged_status",
723 SchemaKind::Response,
724 include_str!("../schemas/fs/staged_status.response.json"),
725 ),
726 (
727 "fs",
728 "commit_staged",
729 SchemaKind::Request,
730 include_str!("../schemas/fs/commit_staged.request.json"),
731 ),
732 (
733 "fs",
734 "commit_staged",
735 SchemaKind::Response,
736 include_str!("../schemas/fs/commit_staged.response.json"),
737 ),
738 (
739 "fs",
740 "discard_staged",
741 SchemaKind::Request,
742 include_str!("../schemas/fs/discard_staged.request.json"),
743 ),
744 (
745 "fs",
746 "discard_staged",
747 SchemaKind::Response,
748 include_str!("../schemas/fs/discard_staged.response.json"),
749 ),
750 (
751 "fs",
752 "safe_text_patch",
753 SchemaKind::Request,
754 include_str!("../schemas/fs/safe_text_patch.request.json"),
755 ),
756 (
757 "fs",
758 "safe_text_patch",
759 SchemaKind::Response,
760 include_str!("../schemas/fs/safe_text_patch.response.json"),
761 ),
762 (
763 "fs",
764 "read_text",
765 SchemaKind::Request,
766 include_str!("../schemas/fs/read_text.request.json"),
767 ),
768 (
769 "fs",
770 "read_text",
771 SchemaKind::Response,
772 include_str!("../schemas/fs/read_text.response.json"),
773 ),
774 (
775 "fs",
776 "emit_safe_text_patch_result",
777 SchemaKind::Request,
778 include_str!("../schemas/fs/emit_safe_text_patch_result.request.json"),
779 ),
780 (
781 "fs",
782 "emit_safe_text_patch_result",
783 SchemaKind::Response,
784 include_str!("../schemas/fs/emit_safe_text_patch_result.response.json"),
785 ),
786 (
787 "fs",
788 "snapshot",
789 SchemaKind::Request,
790 include_str!("../schemas/fs/snapshot.request.json"),
791 ),
792 (
793 "fs",
794 "snapshot",
795 SchemaKind::Response,
796 include_str!("../schemas/fs/snapshot.response.json"),
797 ),
798 (
799 "fs",
800 "restore",
801 SchemaKind::Request,
802 include_str!("../schemas/fs/restore.request.json"),
803 ),
804 (
805 "fs",
806 "restore",
807 SchemaKind::Response,
808 include_str!("../schemas/fs/restore.response.json"),
809 ),
810 (
811 "fs",
812 "list_snapshots",
813 SchemaKind::Request,
814 include_str!("../schemas/fs/list_snapshots.request.json"),
815 ),
816 (
817 "fs",
818 "list_snapshots",
819 SchemaKind::Response,
820 include_str!("../schemas/fs/list_snapshots.response.json"),
821 ),
822 (
823 "fs",
824 "drop_snapshot",
825 SchemaKind::Request,
826 include_str!("../schemas/fs/drop_snapshot.request.json"),
827 ),
828 (
829 "fs",
830 "drop_snapshot",
831 SchemaKind::Response,
832 include_str!("../schemas/fs/drop_snapshot.response.json"),
833 ),
834 (
836 "fs_watch",
837 "subscribe",
838 SchemaKind::Request,
839 include_str!("../schemas/fs_watch/subscribe.request.json"),
840 ),
841 (
842 "fs_watch",
843 "subscribe",
844 SchemaKind::Response,
845 include_str!("../schemas/fs_watch/subscribe.response.json"),
846 ),
847 (
848 "fs_watch",
849 "unsubscribe",
850 SchemaKind::Request,
851 include_str!("../schemas/fs_watch/unsubscribe.request.json"),
852 ),
853 (
854 "fs_watch",
855 "unsubscribe",
856 SchemaKind::Response,
857 include_str!("../schemas/fs_watch/unsubscribe.response.json"),
858 ),
859 (
861 "tools",
862 "search",
863 SchemaKind::Request,
864 include_str!("../schemas/tools/search.request.json"),
865 ),
866 (
867 "tools",
868 "search",
869 SchemaKind::Response,
870 include_str!("../schemas/tools/search.response.json"),
871 ),
872 (
873 "tools",
874 "read_file",
875 SchemaKind::Request,
876 include_str!("../schemas/tools/read_file.request.json"),
877 ),
878 (
879 "tools",
880 "read_file",
881 SchemaKind::Response,
882 include_str!("../schemas/tools/read_file.response.json"),
883 ),
884 (
885 "tools",
886 "write_file",
887 SchemaKind::Request,
888 include_str!("../schemas/tools/write_file.request.json"),
889 ),
890 (
891 "tools",
892 "write_file",
893 SchemaKind::Response,
894 include_str!("../schemas/tools/write_file.response.json"),
895 ),
896 (
897 "tools",
898 "delete_file",
899 SchemaKind::Request,
900 include_str!("../schemas/tools/delete_file.request.json"),
901 ),
902 (
903 "tools",
904 "delete_file",
905 SchemaKind::Response,
906 include_str!("../schemas/tools/delete_file.response.json"),
907 ),
908 (
909 "tools",
910 "list_directory",
911 SchemaKind::Request,
912 include_str!("../schemas/tools/list_directory.request.json"),
913 ),
914 (
915 "tools",
916 "list_directory",
917 SchemaKind::Response,
918 include_str!("../schemas/tools/list_directory.response.json"),
919 ),
920 (
921 "tools",
922 "get_file_outline",
923 SchemaKind::Request,
924 include_str!("../schemas/tools/get_file_outline.request.json"),
925 ),
926 (
927 "tools",
928 "get_file_outline",
929 SchemaKind::Response,
930 include_str!("../schemas/tools/get_file_outline.response.json"),
931 ),
932 (
933 "tools",
934 "git",
935 SchemaKind::Request,
936 include_str!("../schemas/tools/git.request.json"),
937 ),
938 (
939 "tools",
940 "git",
941 SchemaKind::Response,
942 include_str!("../schemas/tools/git.response.json"),
943 ),
944 (
945 "tools",
946 "run_command",
947 SchemaKind::Request,
948 include_str!("../schemas/tools/run_command.request.json"),
949 ),
950 (
951 "tools",
952 "run_command",
953 SchemaKind::Response,
954 include_str!("../schemas/tools/run_command.response.json"),
955 ),
956 (
957 "tools",
958 "read_command_output",
959 SchemaKind::Request,
960 include_str!("../schemas/tools/read_command_output.request.json"),
961 ),
962 (
963 "tools",
964 "read_command_output",
965 SchemaKind::Response,
966 include_str!("../schemas/tools/read_command_output.response.json"),
967 ),
968 (
969 "tools",
970 "wait_command",
971 SchemaKind::Request,
972 include_str!("../schemas/tools/wait_command.request.json"),
973 ),
974 (
975 "tools",
976 "wait_command",
977 SchemaKind::Response,
978 include_str!("../schemas/tools/wait_command.response.json"),
979 ),
980 (
981 "tools",
982 "run_test",
983 SchemaKind::Request,
984 include_str!("../schemas/tools/run_test.request.json"),
985 ),
986 (
987 "tools",
988 "run_test",
989 SchemaKind::Response,
990 include_str!("../schemas/tools/run_test.response.json"),
991 ),
992 (
993 "tools",
994 "run_build_command",
995 SchemaKind::Request,
996 include_str!("../schemas/tools/run_build_command.request.json"),
997 ),
998 (
999 "tools",
1000 "run_build_command",
1001 SchemaKind::Response,
1002 include_str!("../schemas/tools/run_build_command.response.json"),
1003 ),
1004 (
1005 "tools",
1006 "inspect_test_results",
1007 SchemaKind::Request,
1008 include_str!("../schemas/tools/inspect_test_results.request.json"),
1009 ),
1010 (
1011 "tools",
1012 "inspect_test_results",
1013 SchemaKind::Response,
1014 include_str!("../schemas/tools/inspect_test_results.response.json"),
1015 ),
1016 (
1017 "tools",
1018 "manage_packages",
1019 SchemaKind::Request,
1020 include_str!("../schemas/tools/manage_packages.request.json"),
1021 ),
1022 (
1023 "tools",
1024 "manage_packages",
1025 SchemaKind::Response,
1026 include_str!("../schemas/tools/manage_packages.response.json"),
1027 ),
1028 (
1029 "tools",
1030 "cancel_handle",
1031 SchemaKind::Request,
1032 include_str!("../schemas/tools/cancel_handle.request.json"),
1033 ),
1034 (
1035 "tools",
1036 "cancel_handle",
1037 SchemaKind::Response,
1038 include_str!("../schemas/tools/cancel_handle.response.json"),
1039 ),
1040 (
1041 "tools",
1042 "toolchain_facts",
1043 SchemaKind::Request,
1044 include_str!("../schemas/tools/toolchain_facts.request.json"),
1045 ),
1046 (
1047 "tools",
1048 "toolchain_facts",
1049 SchemaKind::Response,
1050 include_str!("../schemas/tools/toolchain_facts.response.json"),
1051 ),
1052 (
1053 "tools",
1054 "enable",
1055 SchemaKind::Request,
1056 include_str!("../schemas/tools/enable.request.json"),
1057 ),
1058 (
1059 "tools",
1060 "enable",
1061 SchemaKind::Response,
1062 include_str!("../schemas/tools/enable.response.json"),
1063 ),
1064 (
1066 "secret_store",
1067 "get",
1068 SchemaKind::Request,
1069 include_str!("../schemas/secret_store/get.request.json"),
1070 ),
1071 (
1072 "secret_store",
1073 "get",
1074 SchemaKind::Response,
1075 include_str!("../schemas/secret_store/get.response.json"),
1076 ),
1077 (
1078 "secret_store",
1079 "set",
1080 SchemaKind::Request,
1081 include_str!("../schemas/secret_store/set.request.json"),
1082 ),
1083 (
1084 "secret_store",
1085 "set",
1086 SchemaKind::Response,
1087 include_str!("../schemas/secret_store/set.response.json"),
1088 ),
1089 (
1090 "secret_store",
1091 "delete",
1092 SchemaKind::Request,
1093 include_str!("../schemas/secret_store/delete.request.json"),
1094 ),
1095 (
1096 "secret_store",
1097 "delete",
1098 SchemaKind::Response,
1099 include_str!("../schemas/secret_store/delete.response.json"),
1100 ),
1101 (
1102 "secret_store",
1103 "list",
1104 SchemaKind::Request,
1105 include_str!("../schemas/secret_store/list.request.json"),
1106 ),
1107 (
1108 "secret_store",
1109 "list",
1110 SchemaKind::Response,
1111 include_str!("../schemas/secret_store/list.response.json"),
1112 ),
1113 (
1114 "embed",
1115 "similarity",
1116 SchemaKind::Request,
1117 include_str!("../schemas/embed/similarity.request.json"),
1118 ),
1119 (
1120 "embed",
1121 "similarity",
1122 SchemaKind::Response,
1123 include_str!("../schemas/embed/similarity.response.json"),
1124 ),
1125 (
1126 "embed",
1127 "top_k",
1128 SchemaKind::Request,
1129 include_str!("../schemas/embed/top_k.request.json"),
1130 ),
1131 (
1132 "embed",
1133 "top_k",
1134 SchemaKind::Response,
1135 include_str!("../schemas/embed/top_k.response.json"),
1136 ),
1137 (
1138 "embed",
1139 "vector",
1140 SchemaKind::Request,
1141 include_str!("../schemas/embed/vector.request.json"),
1142 ),
1143 (
1144 "embed",
1145 "vector",
1146 SchemaKind::Response,
1147 include_str!("../schemas/embed/vector.response.json"),
1148 ),
1149 (
1150 "embed",
1151 "info",
1152 SchemaKind::Request,
1153 include_str!("../schemas/embed/info.request.json"),
1154 ),
1155 (
1156 "embed",
1157 "info",
1158 SchemaKind::Response,
1159 include_str!("../schemas/embed/info.response.json"),
1160 ),
1161 (
1163 "rules",
1164 "search",
1165 SchemaKind::Request,
1166 include_str!("../schemas/rules/search.request.json"),
1167 ),
1168 (
1169 "rules",
1170 "search",
1171 SchemaKind::Response,
1172 include_str!("../schemas/rules/search.response.json"),
1173 ),
1174 (
1175 "rules",
1176 "report",
1177 SchemaKind::Request,
1178 include_str!("../schemas/rules/report.request.json"),
1179 ),
1180 (
1181 "rules",
1182 "report",
1183 SchemaKind::Response,
1184 include_str!("../schemas/rules/report.response.json"),
1185 ),
1186 (
1187 "rules",
1188 "diagnostics",
1189 SchemaKind::Request,
1190 include_str!("../schemas/rules/diagnostics.request.json"),
1191 ),
1192 (
1193 "rules",
1194 "diagnostics",
1195 SchemaKind::Response,
1196 include_str!("../schemas/rules/diagnostics.response.json"),
1197 ),
1198 (
1199 "rules",
1200 "apply",
1201 SchemaKind::Request,
1202 include_str!("../schemas/rules/apply.request.json"),
1203 ),
1204 (
1205 "rules",
1206 "apply",
1207 SchemaKind::Response,
1208 include_str!("../schemas/rules/apply.response.json"),
1209 ),
1210 (
1211 "rules",
1212 "fold",
1213 SchemaKind::Request,
1214 include_str!("../schemas/rules/fold.request.json"),
1215 ),
1216 (
1217 "rules",
1218 "fold",
1219 SchemaKind::Response,
1220 include_str!("../schemas/rules/fold.response.json"),
1221 ),
1222 (
1223 "lint",
1224 "run",
1225 SchemaKind::Request,
1226 include_str!("../schemas/lint/run.request.json"),
1227 ),
1228 (
1229 "lint",
1230 "run",
1231 SchemaKind::Response,
1232 include_str!("../schemas/lint/run.response.json"),
1233 ),
1234];
1235
1236pub fn lookup(module: &str, method: &str, kind: SchemaKind) -> Option<&'static str> {
1238 SCHEMAS
1239 .iter()
1240 .find(|(m, mt, k, _)| *m == module && *mt == method && *k == kind)
1241 .map(|(_, _, _, body)| *body)
1242}
1243
1244struct CompiledSchema {
1245 module: &'static str,
1246 method: &'static str,
1247 kind: SchemaKind,
1248 body: Result<VmValue, String>,
1249}
1250
1251fn compiled_schemas() -> &'static [CompiledSchema] {
1252 static COMPILED: OnceLock<Vec<CompiledSchema>> = OnceLock::new();
1253 COMPILED.get_or_init(|| {
1254 SCHEMAS
1255 .iter()
1256 .filter(|(_, _, kind, _)| *kind == SchemaKind::Request)
1257 .map(|(module, method, kind, body)| {
1258 let body = serde_json::from_str::<serde_json::Value>(body)
1259 .map_err(|err| format!("schema is not valid JSON: {err}"))
1260 .map(|json| harn_vm::json_to_vm_value(&json))
1261 .and_then(|schema| harn_vm::schema::canonicalize_json_schema(&schema));
1262 CompiledSchema {
1263 module,
1264 method,
1265 kind: *kind,
1266 body,
1267 }
1268 })
1269 .collect()
1270 })
1271}
1272
1273fn compiled_schema(
1274 module: &str,
1275 method: &str,
1276 kind: SchemaKind,
1277) -> Option<&'static CompiledSchema> {
1278 compiled_schemas()
1279 .iter()
1280 .find(|schema| schema.module == module && schema.method == method && schema.kind == kind)
1281}
1282
1283pub(crate) fn validate_request_args(
1284 builtin: &'static str,
1285 module: &'static str,
1286 method: &'static str,
1287 args: &[VmValue],
1288) -> Result<VmValue, HostlibError> {
1289 let request = normalize_request_arg(builtin, module, method, args)?;
1290 let schema = compiled_schema(module, method, SchemaKind::Request).ok_or_else(|| {
1291 HostlibError::Backend {
1292 builtin,
1293 message: format!("missing request schema for {module}.{method}"),
1294 }
1295 })?;
1296 let schema = schema
1297 .body
1298 .as_ref()
1299 .map_err(|message| HostlibError::Backend {
1300 builtin,
1301 message: format!("invalid request schema for {module}.{method}: {message}"),
1302 })?;
1303 harn_vm::schema::validate_value_against_canonical_schema(&request, schema, true).map_err(
1304 |message| HostlibError::InvalidParameter {
1305 builtin,
1306 param: "request",
1307 message,
1308 },
1309 )
1310}
1311
1312fn normalize_request_arg(
1313 builtin: &'static str,
1314 module: &'static str,
1315 method: &'static str,
1316 args: &[VmValue],
1317) -> Result<VmValue, HostlibError> {
1318 if args.len() > 1 {
1319 return Err(HostlibError::InvalidParameter {
1320 builtin,
1321 param: "request",
1322 message: format!("expected exactly one request argument, got {}", args.len()),
1323 });
1324 }
1325
1326 let first = args.first().ok_or(HostlibError::MissingParameter {
1327 builtin,
1328 param: "request",
1329 })?;
1330 match first {
1331 VmValue::Dict(map) => Ok(prune_top_level_nil_dict_fields(map)),
1332 VmValue::String(feature) if (module, method) == ("tools", "enable") => {
1333 let mut normalized = harn_vm::value::DictMap::new();
1334 normalized.put_str("feature", feature.to_string());
1335 Ok(VmValue::dict(normalized))
1336 }
1337 other => Err(HostlibError::InvalidParameter {
1338 builtin,
1339 param: "request",
1340 message: format!("expected a dict request body, got {}", other.type_name()),
1341 }),
1342 }
1343}
1344
1345fn prune_top_level_nil_dict_fields(map: &harn_vm::value::DictMap) -> VmValue {
1346 let mut pruned = harn_vm::value::DictMap::new();
1347 for (key, child) in map.iter() {
1348 if matches!(child, VmValue::Nil) {
1349 continue;
1350 }
1351 pruned.insert(key.clone(), child.clone());
1352 }
1353 VmValue::dict_map(pruned)
1354}
1355
1356#[cfg(test)]
1357mod tests {
1358 use std::sync::Arc;
1359
1360 use harn_vm::VmValue;
1361
1362 use super::*;
1363
1364 #[test]
1365 fn request_validation_prunes_nil_optional_fields() {
1366 let request = VmValue::dict([
1367 ("session_id", VmValue::string("session-1")),
1368 ("scope_id", VmValue::string("scope-1")),
1369 ("root", VmValue::Nil),
1370 ]);
1371
1372 let validated = validate_request_args("hostlib_fs_snapshot", "fs", "snapshot", &[request])
1373 .expect("nil optional root should be omitted before schema validation");
1374 let fields = validated.as_dict().expect("validated request is a dict");
1375 assert!(fields.get("root").is_none());
1376 assert_eq!(
1377 fields.get("session_id").map(VmValue::display),
1378 Some("session-1".to_string())
1379 );
1380 }
1381
1382 #[test]
1383 fn run_command_request_schema_accepts_env_remove() {
1384 let request = VmValue::dict([
1390 (
1391 "argv",
1392 VmValue::List(Arc::new(vec![VmValue::string("env")])),
1393 ),
1394 (
1395 "env_remove",
1396 VmValue::List(Arc::new(vec![VmValue::string("HARN_EVENT_LOG_DIR")])),
1397 ),
1398 ]);
1399
1400 validate_request_args(
1401 "hostlib_tools_run_command",
1402 "tools",
1403 "run_command",
1404 &[request],
1405 )
1406 .expect("env_remove must be a valid run_command request field");
1407 }
1408
1409 #[test]
1410 fn request_validation_does_not_prune_nested_nil_fields() {
1411 let request = VmValue::dict([
1412 (
1413 "argv",
1414 VmValue::List(Arc::new(vec![VmValue::string("env")])),
1415 ),
1416 ("env", VmValue::dict([("FOO", VmValue::Nil)])),
1417 ]);
1418
1419 let err = validate_request_args(
1420 "hostlib_tools_run_command",
1421 "tools",
1422 "run_command",
1423 &[request],
1424 )
1425 .expect_err("nested nil map values must remain visible to schema validation");
1426 match err {
1427 HostlibError::InvalidParameter { message, .. } => {
1428 assert!(
1429 message.contains("env") && message.contains("string"),
1430 "nested env nil should fail as a non-string value, got: {message}"
1431 );
1432 }
1433 other => panic!("expected request validation error, got {other:?}"),
1434 }
1435 }
1436
1437 #[test]
1438 fn dry_run_request_schema_allows_handler_rejected_plan_ops() {
1439 let unknown = VmValue::dict([
1440 ("op", VmValue::string("blow_up_the_world")),
1441 ("path", VmValue::string("multi.txt")),
1442 ]);
1443 let missing_op = VmValue::dict_map(harn_vm::value::DictMap::new());
1444 let non_string_op = VmValue::dict([("op", VmValue::Int(1))]);
1445 let request = VmValue::dict([(
1446 "plan",
1447 VmValue::List(Arc::new(vec![unknown, missing_op, non_string_op])),
1448 )]);
1449
1450 validate_request_args("hostlib_ast_dry_run", "ast", "dry_run", &[request]).expect(
1451 "dry_run unknown/missing/non-string ops must reach the handler for structured rejection",
1452 );
1453 }
1454
1455 #[test]
1456 fn extension_hostlib_rules_search_has_request_schema() {
1457 let request = VmValue::dict([
1458 (
1459 "rule",
1460 VmValue::string(
1461 "id = \"noop\"\nlanguage = \"typescript\"\n[rule]\npattern = \"$X\"",
1462 ),
1463 ),
1464 ("source", VmValue::string("foo();")),
1465 ("language", VmValue::string("typescript")),
1466 ]);
1467
1468 validate_request_args("hostlib_rules_search", "rules", "search", &[request])
1469 .expect("rules.search should be covered by the shared hostlib schema catalog");
1470 }
1471}