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 "terminal_session",
1164 "start",
1165 SchemaKind::Request,
1166 include_str!("../schemas/terminal_session/start.request.json"),
1167 ),
1168 (
1169 "terminal_session",
1170 "start",
1171 SchemaKind::Response,
1172 include_str!("../schemas/terminal_session/start.response.json"),
1173 ),
1174 (
1175 "terminal_session",
1176 "send_keys",
1177 SchemaKind::Request,
1178 include_str!("../schemas/terminal_session/send_keys.request.json"),
1179 ),
1180 (
1181 "terminal_session",
1182 "send_keys",
1183 SchemaKind::Response,
1184 include_str!("../schemas/terminal_session/send_keys.response.json"),
1185 ),
1186 (
1187 "terminal_session",
1188 "capture",
1189 SchemaKind::Request,
1190 include_str!("../schemas/terminal_session/capture.request.json"),
1191 ),
1192 (
1193 "terminal_session",
1194 "capture",
1195 SchemaKind::Response,
1196 include_str!("../schemas/terminal_session/capture.response.json"),
1197 ),
1198 (
1199 "terminal_session",
1200 "resize",
1201 SchemaKind::Request,
1202 include_str!("../schemas/terminal_session/resize.request.json"),
1203 ),
1204 (
1205 "terminal_session",
1206 "resize",
1207 SchemaKind::Response,
1208 include_str!("../schemas/terminal_session/resize.response.json"),
1209 ),
1210 (
1211 "terminal_session",
1212 "wait_idle",
1213 SchemaKind::Request,
1214 include_str!("../schemas/terminal_session/wait_idle.request.json"),
1215 ),
1216 (
1217 "terminal_session",
1218 "wait_idle",
1219 SchemaKind::Response,
1220 include_str!("../schemas/terminal_session/wait_idle.response.json"),
1221 ),
1222 (
1223 "terminal_session",
1224 "end",
1225 SchemaKind::Request,
1226 include_str!("../schemas/terminal_session/end.request.json"),
1227 ),
1228 (
1229 "terminal_session",
1230 "end",
1231 SchemaKind::Response,
1232 include_str!("../schemas/terminal_session/end.response.json"),
1233 ),
1234 (
1236 "rules",
1237 "search",
1238 SchemaKind::Request,
1239 include_str!("../schemas/rules/search.request.json"),
1240 ),
1241 (
1242 "rules",
1243 "search",
1244 SchemaKind::Response,
1245 include_str!("../schemas/rules/search.response.json"),
1246 ),
1247 (
1248 "rules",
1249 "report",
1250 SchemaKind::Request,
1251 include_str!("../schemas/rules/report.request.json"),
1252 ),
1253 (
1254 "rules",
1255 "report",
1256 SchemaKind::Response,
1257 include_str!("../schemas/rules/report.response.json"),
1258 ),
1259 (
1260 "rules",
1261 "diagnostics",
1262 SchemaKind::Request,
1263 include_str!("../schemas/rules/diagnostics.request.json"),
1264 ),
1265 (
1266 "rules",
1267 "diagnostics",
1268 SchemaKind::Response,
1269 include_str!("../schemas/rules/diagnostics.response.json"),
1270 ),
1271 (
1272 "rules",
1273 "apply",
1274 SchemaKind::Request,
1275 include_str!("../schemas/rules/apply.request.json"),
1276 ),
1277 (
1278 "rules",
1279 "apply",
1280 SchemaKind::Response,
1281 include_str!("../schemas/rules/apply.response.json"),
1282 ),
1283 (
1284 "rules",
1285 "fold",
1286 SchemaKind::Request,
1287 include_str!("../schemas/rules/fold.request.json"),
1288 ),
1289 (
1290 "rules",
1291 "fold",
1292 SchemaKind::Response,
1293 include_str!("../schemas/rules/fold.response.json"),
1294 ),
1295 (
1296 "lint",
1297 "run",
1298 SchemaKind::Request,
1299 include_str!("../schemas/lint/run.request.json"),
1300 ),
1301 (
1302 "lint",
1303 "run",
1304 SchemaKind::Response,
1305 include_str!("../schemas/lint/run.response.json"),
1306 ),
1307];
1308
1309pub fn lookup(module: &str, method: &str, kind: SchemaKind) -> Option<&'static str> {
1311 SCHEMAS
1312 .iter()
1313 .find(|(m, mt, k, _)| *m == module && *mt == method && *k == kind)
1314 .map(|(_, _, _, body)| *body)
1315}
1316
1317struct CompiledSchema {
1318 module: &'static str,
1319 method: &'static str,
1320 kind: SchemaKind,
1321 body: Result<VmValue, String>,
1322}
1323
1324fn compiled_schemas() -> &'static [CompiledSchema] {
1325 static COMPILED: OnceLock<Vec<CompiledSchema>> = OnceLock::new();
1326 COMPILED.get_or_init(|| {
1327 SCHEMAS
1328 .iter()
1329 .filter(|(_, _, kind, _)| *kind == SchemaKind::Request)
1330 .map(|(module, method, kind, body)| {
1331 let body = serde_json::from_str::<serde_json::Value>(body)
1332 .map_err(|err| format!("schema is not valid JSON: {err}"))
1333 .map(|json| harn_vm::json_to_vm_value(&json))
1334 .and_then(|schema| harn_vm::schema::canonicalize_json_schema(&schema));
1335 CompiledSchema {
1336 module,
1337 method,
1338 kind: *kind,
1339 body,
1340 }
1341 })
1342 .collect()
1343 })
1344}
1345
1346fn compiled_schema(
1347 module: &str,
1348 method: &str,
1349 kind: SchemaKind,
1350) -> Option<&'static CompiledSchema> {
1351 compiled_schemas()
1352 .iter()
1353 .find(|schema| schema.module == module && schema.method == method && schema.kind == kind)
1354}
1355
1356pub(crate) fn validate_request_args(
1357 builtin: &'static str,
1358 module: &'static str,
1359 method: &'static str,
1360 args: &[VmValue],
1361) -> Result<VmValue, HostlibError> {
1362 let request = normalize_request_arg(builtin, module, method, args)?;
1363 let schema = compiled_schema(module, method, SchemaKind::Request).ok_or_else(|| {
1364 HostlibError::Backend {
1365 builtin,
1366 message: format!("missing request schema for {module}.{method}"),
1367 }
1368 })?;
1369 let schema = schema
1370 .body
1371 .as_ref()
1372 .map_err(|message| HostlibError::Backend {
1373 builtin,
1374 message: format!("invalid request schema for {module}.{method}: {message}"),
1375 })?;
1376 harn_vm::schema::validate_value_against_canonical_schema(&request, schema, true).map_err(
1377 |message| HostlibError::InvalidParameter {
1378 builtin,
1379 param: "request",
1380 message,
1381 },
1382 )
1383}
1384
1385fn normalize_request_arg(
1386 builtin: &'static str,
1387 module: &'static str,
1388 method: &'static str,
1389 args: &[VmValue],
1390) -> Result<VmValue, HostlibError> {
1391 if args.len() > 1 {
1392 return Err(HostlibError::InvalidParameter {
1393 builtin,
1394 param: "request",
1395 message: format!("expected exactly one request argument, got {}", args.len()),
1396 });
1397 }
1398
1399 let first = args.first().ok_or(HostlibError::MissingParameter {
1400 builtin,
1401 param: "request",
1402 })?;
1403 match first {
1404 VmValue::Dict(map) => Ok(prune_top_level_nil_dict_fields(map)),
1405 VmValue::String(feature) if (module, method) == ("tools", "enable") => {
1406 let mut normalized = harn_vm::value::DictMap::new();
1407 normalized.put_str("feature", feature.to_string());
1408 Ok(VmValue::dict(normalized))
1409 }
1410 other => Err(HostlibError::InvalidParameter {
1411 builtin,
1412 param: "request",
1413 message: format!("expected a dict request body, got {}", other.type_name()),
1414 }),
1415 }
1416}
1417
1418fn prune_top_level_nil_dict_fields(map: &harn_vm::value::DictMap) -> VmValue {
1419 let mut pruned = harn_vm::value::DictMap::new();
1420 for (key, child) in map.iter() {
1421 if matches!(child, VmValue::Nil) {
1422 continue;
1423 }
1424 pruned.insert(key.clone(), child.clone());
1425 }
1426 VmValue::dict_map(pruned)
1427}
1428
1429#[cfg(test)]
1430mod tests {
1431 use std::sync::Arc;
1432
1433 use harn_vm::VmValue;
1434
1435 use super::*;
1436
1437 #[test]
1438 fn request_validation_prunes_nil_optional_fields() {
1439 let request = VmValue::dict([
1440 ("session_id", VmValue::string("session-1")),
1441 ("scope_id", VmValue::string("scope-1")),
1442 ("root", VmValue::Nil),
1443 ]);
1444
1445 let validated = validate_request_args("hostlib_fs_snapshot", "fs", "snapshot", &[request])
1446 .expect("nil optional root should be omitted before schema validation");
1447 let fields = validated.as_dict().expect("validated request is a dict");
1448 assert!(fields.get("root").is_none());
1449 assert_eq!(
1450 fields.get("session_id").map(VmValue::display),
1451 Some("session-1".to_string())
1452 );
1453 }
1454
1455 #[test]
1456 fn run_command_request_schema_accepts_env_remove() {
1457 let request = VmValue::dict([
1463 (
1464 "argv",
1465 VmValue::List(Arc::new(vec![VmValue::string("env")])),
1466 ),
1467 (
1468 "env_remove",
1469 VmValue::List(Arc::new(vec![VmValue::string("HARN_EVENT_LOG_DIR")])),
1470 ),
1471 ]);
1472
1473 validate_request_args(
1474 "hostlib_tools_run_command",
1475 "tools",
1476 "run_command",
1477 &[request],
1478 )
1479 .expect("env_remove must be a valid run_command request field");
1480 }
1481
1482 #[test]
1483 fn request_validation_does_not_prune_nested_nil_fields() {
1484 let request = VmValue::dict([
1485 (
1486 "argv",
1487 VmValue::List(Arc::new(vec![VmValue::string("env")])),
1488 ),
1489 ("env", VmValue::dict([("FOO", VmValue::Nil)])),
1490 ]);
1491
1492 let err = validate_request_args(
1493 "hostlib_tools_run_command",
1494 "tools",
1495 "run_command",
1496 &[request],
1497 )
1498 .expect_err("nested nil map values must remain visible to schema validation");
1499 match err {
1500 HostlibError::InvalidParameter { message, .. } => {
1501 assert!(
1502 message.contains("env") && message.contains("string"),
1503 "nested env nil should fail as a non-string value, got: {message}"
1504 );
1505 }
1506 other => panic!("expected request validation error, got {other:?}"),
1507 }
1508 }
1509
1510 #[test]
1511 fn dry_run_request_schema_allows_handler_rejected_plan_ops() {
1512 let unknown = VmValue::dict([
1513 ("op", VmValue::string("blow_up_the_world")),
1514 ("path", VmValue::string("multi.txt")),
1515 ]);
1516 let missing_op = VmValue::dict_map(harn_vm::value::DictMap::new());
1517 let non_string_op = VmValue::dict([("op", VmValue::Int(1))]);
1518 let request = VmValue::dict([(
1519 "plan",
1520 VmValue::List(Arc::new(vec![unknown, missing_op, non_string_op])),
1521 )]);
1522
1523 validate_request_args("hostlib_ast_dry_run", "ast", "dry_run", &[request]).expect(
1524 "dry_run unknown/missing/non-string ops must reach the handler for structured rejection",
1525 );
1526 }
1527
1528 #[test]
1529 fn extension_hostlib_rules_search_has_request_schema() {
1530 let request = VmValue::dict([
1531 (
1532 "rule",
1533 VmValue::string(
1534 "id = \"noop\"\nlanguage = \"typescript\"\n[rule]\npattern = \"$X\"",
1535 ),
1536 ),
1537 ("source", VmValue::string("foo();")),
1538 ("language", VmValue::string("typescript")),
1539 ]);
1540
1541 validate_request_args("hostlib_rules_search", "rules", "search", &[request])
1542 .expect("rules.search should be covered by the shared hostlib schema catalog");
1543 }
1544}