claude-ide-bridge 2.42.2 → 2.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +26 -20
  2. package/dist/activityLog.js +3 -2
  3. package/dist/activityLog.js.map +1 -1
  4. package/dist/automation.d.ts +73 -7
  5. package/dist/automation.js +147 -3
  6. package/dist/automation.js.map +1 -1
  7. package/dist/bridge.d.ts +8 -0
  8. package/dist/bridge.js +29 -6
  9. package/dist/bridge.js.map +1 -1
  10. package/dist/claudeOrchestrator.d.ts +15 -1
  11. package/dist/claudeOrchestrator.js +42 -2
  12. package/dist/claudeOrchestrator.js.map +1 -1
  13. package/dist/config.d.ts +9 -0
  14. package/dist/config.js +44 -11
  15. package/dist/config.js.map +1 -1
  16. package/dist/errors.d.ts +1 -0
  17. package/dist/errors.js +1 -0
  18. package/dist/errors.js.map +1 -1
  19. package/dist/extensionClient.d.ts +4 -0
  20. package/dist/extensionClient.js +234 -91
  21. package/dist/extensionClient.js.map +1 -1
  22. package/dist/fp/activityAnalytics.js +2 -13
  23. package/dist/fp/activityAnalytics.js.map +1 -1
  24. package/dist/fp/automationUtils.d.ts +7 -3
  25. package/dist/fp/automationUtils.js +20 -7
  26. package/dist/fp/automationUtils.js.map +1 -1
  27. package/dist/index.js +185 -12
  28. package/dist/index.js.map +1 -1
  29. package/dist/tools/batchLsp.js +3 -3
  30. package/dist/tools/batchLsp.js.map +1 -1
  31. package/dist/tools/bridgeDoctor.d.ts +11 -1
  32. package/dist/tools/bridgeDoctor.js +163 -2
  33. package/dist/tools/bridgeDoctor.js.map +1 -1
  34. package/dist/tools/bridgeStatus.d.ts +6 -0
  35. package/dist/tools/bridgeStatus.js +5 -1
  36. package/dist/tools/bridgeStatus.js.map +1 -1
  37. package/dist/tools/cancelClaudeTask.js +8 -3
  38. package/dist/tools/cancelClaudeTask.js.map +1 -1
  39. package/dist/tools/codeLens.js +1 -1
  40. package/dist/tools/codeLens.js.map +1 -1
  41. package/dist/tools/documentLinks.js +1 -1
  42. package/dist/tools/documentLinks.js.map +1 -1
  43. package/dist/tools/editText.js +1 -1
  44. package/dist/tools/editText.js.map +1 -1
  45. package/dist/tools/explainDiagnostic.js +1 -1
  46. package/dist/tools/explainDiagnostic.js.map +1 -1
  47. package/dist/tools/explainSymbol.js +1 -1
  48. package/dist/tools/explainSymbol.js.map +1 -1
  49. package/dist/tools/fileOperations.js +2 -2
  50. package/dist/tools/fileOperations.js.map +1 -1
  51. package/dist/tools/foldingRanges.js +1 -1
  52. package/dist/tools/foldingRanges.js.map +1 -1
  53. package/dist/tools/generateTests.js +10 -1
  54. package/dist/tools/generateTests.js.map +1 -1
  55. package/dist/tools/getBufferContent.js +1 -1
  56. package/dist/tools/getBufferContent.js.map +1 -1
  57. package/dist/tools/getChangeImpact.js +1 -1
  58. package/dist/tools/getChangeImpact.js.map +1 -1
  59. package/dist/tools/getClaudeTaskStatus.d.ts +7 -0
  60. package/dist/tools/getClaudeTaskStatus.js +11 -2
  61. package/dist/tools/getClaudeTaskStatus.js.map +1 -1
  62. package/dist/tools/getDocumentSymbols.js +1 -1
  63. package/dist/tools/getDocumentSymbols.js.map +1 -1
  64. package/dist/tools/getImportedSignatures.js +1 -1
  65. package/dist/tools/getImportedSignatures.js.map +1 -1
  66. package/dist/tools/getSymbolHistory.js +1 -1
  67. package/dist/tools/getSymbolHistory.js.map +1 -1
  68. package/dist/tools/gitWrite.js +4 -4
  69. package/dist/tools/gitWrite.js.map +1 -1
  70. package/dist/tools/index.d.ts +6 -5
  71. package/dist/tools/index.js +7 -6
  72. package/dist/tools/index.js.map +1 -1
  73. package/dist/tools/inlayHints.js +1 -1
  74. package/dist/tools/inlayHints.js.map +1 -1
  75. package/dist/tools/listClaudeTasks.d.ts +7 -0
  76. package/dist/tools/listClaudeTasks.js +5 -1
  77. package/dist/tools/listClaudeTasks.js.map +1 -1
  78. package/dist/tools/lsp.d.ts +25 -0
  79. package/dist/tools/lsp.js +28 -13
  80. package/dist/tools/lsp.js.map +1 -1
  81. package/dist/tools/openFile.js +1 -1
  82. package/dist/tools/openFile.js.map +1 -1
  83. package/dist/tools/refactorAnalyze.js +1 -1
  84. package/dist/tools/refactorAnalyze.js.map +1 -1
  85. package/dist/tools/refactorPreview.js +1 -1
  86. package/dist/tools/refactorPreview.js.map +1 -1
  87. package/dist/tools/replaceBlock.js +1 -1
  88. package/dist/tools/replaceBlock.js.map +1 -1
  89. package/dist/tools/resumeClaudeTask.js +6 -1
  90. package/dist/tools/resumeClaudeTask.js.map +1 -1
  91. package/dist/tools/selectionRanges.js +1 -1
  92. package/dist/tools/selectionRanges.js.map +1 -1
  93. package/dist/tools/semanticTokens.js +1 -1
  94. package/dist/tools/semanticTokens.js.map +1 -1
  95. package/dist/tools/signatureHelp.js +1 -1
  96. package/dist/tools/signatureHelp.js.map +1 -1
  97. package/dist/tools/typeHierarchy.js +1 -1
  98. package/dist/tools/typeHierarchy.js.map +1 -1
  99. package/dist/tools/watchDiagnostics.js +7 -0
  100. package/dist/tools/watchDiagnostics.js.map +1 -1
  101. package/package.json +2 -1
  102. package/templates/automation-policies/security-first.json +2 -1
  103. package/templates/automation-policies/strict-lint.json +2 -1
  104. package/templates/automation-policies/test-driven.json +2 -1
  105. package/templates/automation-policy.example.json +2 -1
@@ -24,6 +24,8 @@ export class ExtensionClient {
24
24
  extensionSuspendedUntil = 0;
25
25
  extensionFailureTimes = []; // timestamps of recent timeouts
26
26
  extensionHalfOpen = false;
27
+ _circuitOpenCount = 0;
28
+ _lastCircuitOpenedAt = null;
27
29
  // State pushed by extension via notifications
28
30
  latestDiagnostics = new Map();
29
31
  /** Parallel timestamp map — tracks when each file's diagnostics were last updated. */
@@ -566,7 +568,9 @@ export class ExtensionClient {
566
568
  const result = await inner;
567
569
  // Success — reset circuit breaker and half-open state
568
570
  if (this.extensionFailureTimes.length > 0) {
569
- this.logger.warn("Extension backoff reset — connection recovered");
571
+ this.logger.info("Extension backoff reset — connection recovered", {
572
+ openCount: this._circuitOpenCount,
573
+ });
570
574
  }
571
575
  this.extensionFailureTimes = [];
572
576
  this.extensionSuspendedUntil = 0;
@@ -590,7 +594,9 @@ export class ExtensionClient {
590
594
  const capMs = Math.min(1_000 * 2 ** (failures - 1), 60_000);
591
595
  const backoffMs = Math.floor(Math.random() * capMs) + 1;
592
596
  this.extensionSuspendedUntil = now + backoffMs;
593
- this.logger.warn(`Extension circuit open (${failures} failures) — suspending for ${Math.round(backoffMs / 100) / 10}s`);
597
+ this._circuitOpenCount++;
598
+ this._lastCircuitOpenedAt = new Date();
599
+ this.logger.warn(`Extension circuit open (${failures} failures) — suspending for ${Math.round(backoffMs / 100) / 10}s`, { openCount: this._circuitOpenCount });
594
600
  // Fast-fail all other in-flight requests immediately when the circuit
595
601
  // opens. Without this, each queued request waits its own REQUEST_TIMEOUT
596
602
  // (10s) independently, so a tool handler chaining N extension calls
@@ -685,9 +691,14 @@ export class ExtensionClient {
685
691
  async isDirty(file) {
686
692
  return this.tryRequest("extension/isDirty", { file });
687
693
  }
694
+ // handler returns { content, isDirty, languageId, lineCount, version, source } | { success:false, error } — validatedRequest
688
695
  async getFileContent(file) {
689
- return this.requestOrNull("extension/getFileContent", { file });
696
+ return this.validatedRequest("extension/getFileContent", { file }, (r) => {
697
+ const o = r;
698
+ return typeof o.content === "string" ? r : null;
699
+ });
690
700
  }
701
+ // handler returns true (boolean) on success — bare requestOrNull, caller checks === true
691
702
  async openFile(file, line) {
692
703
  const result = await this.requestOrNull("extension/openFile", {
693
704
  file,
@@ -733,60 +744,113 @@ export class ExtensionClient {
733
744
  return this.tryRequest("extension/getAIComments");
734
745
  }
735
746
  // --- LSP Semantic Features ---
747
+ // handler returns array of { file, line, column, endLine, endColumn } | null — tryRequest
736
748
  async goToDefinition(file, line, column, signal) {
737
- return this.requestOrNull("extension/goToDefinition", { file, line, column }, undefined, signal);
749
+ return this.tryRequest("extension/goToDefinition", { file, line, column }, undefined, signal);
738
750
  }
751
+ // handler returns { references: [...], count } — validated
739
752
  async findReferences(file, line, column, signal) {
740
- return this.requestOrNull("extension/findReferences", { file, line, column }, undefined, signal);
753
+ return this.validatedRequest("extension/findReferences", { file, line, column }, (r) => {
754
+ const o = r;
755
+ return Array.isArray(o.references) ? r : null;
756
+ }, undefined, signal);
741
757
  }
758
+ // handler returns { found: true, implementations, count } | null — validatedRequest
742
759
  async findImplementations(file, line, column, signal) {
743
- return this.requestOrNull("extension/findImplementations", { file, line, column }, undefined, signal);
760
+ return this.validatedRequest("extension/findImplementations", { file, line, column }, (r) => {
761
+ const o = r;
762
+ return typeof o.found === "boolean" ? r : null;
763
+ }, undefined, signal);
744
764
  }
765
+ // handler returns { found: true, locations } | null — validatedRequest
745
766
  async goToTypeDefinition(file, line, column, signal) {
746
- return this.requestOrNull("extension/goToTypeDefinition", { file, line, column }, undefined, signal);
767
+ return this.validatedRequest("extension/goToTypeDefinition", { file, line, column }, (r) => {
768
+ const o = r;
769
+ return typeof o.found === "boolean" ? r : null;
770
+ }, undefined, signal);
747
771
  }
772
+ // handler returns { found: true, locations } | null — validatedRequest
748
773
  async goToDeclaration(file, line, column, signal) {
749
- return this.requestOrNull("extension/goToDeclaration", { file, line, column }, undefined, signal);
774
+ return this.validatedRequest("extension/goToDeclaration", { file, line, column }, (r) => {
775
+ const o = r;
776
+ return typeof o.found === "boolean" ? r : null;
777
+ }, undefined, signal);
750
778
  }
779
+ // handler returns { contents: string[], range? } | null — validated
751
780
  async getHover(file, line, column, signal) {
752
- return this.requestOrNull("extension/getHover", { file, line, column }, undefined, signal);
781
+ return this.validatedRequest("extension/getHover", { file, line, column }, (r) => {
782
+ const o = r;
783
+ return Array.isArray(o.contents) ? r : null;
784
+ }, undefined, signal);
753
785
  }
786
+ // handler returns { actions: [{title, kind?, isPreferred}] } — validated
754
787
  async getCodeActions(file, startLine, startColumn, endLine, endColumn, signal) {
755
- return this.requestOrNull("extension/getCodeActions", { file, startLine, startColumn, endLine, endColumn }, undefined, signal);
788
+ return this.validatedRequest("extension/getCodeActions", { file, startLine, startColumn, endLine, endColumn }, (r) => {
789
+ const o = r;
790
+ return Array.isArray(o.actions) ? r : null;
791
+ }, undefined, signal);
756
792
  }
793
+ // handler returns { applied: boolean, title?, command?, error?, available? } — validatedRequest
757
794
  async applyCodeAction(file, startLine, startColumn, endLine, endColumn, actionTitle, signal) {
758
- return this.requestOrNull("extension/applyCodeAction", { file, startLine, startColumn, endLine, endColumn, actionTitle }, undefined, signal);
795
+ return this.validatedRequest("extension/applyCodeAction", { file, startLine, startColumn, endLine, endColumn, actionTitle }, (r) => {
796
+ const o = r;
797
+ return typeof o.applied === "boolean" ? r : null;
798
+ }, undefined, signal);
759
799
  }
800
+ // handler returns { title, changes, ... } | { error } — tryRequest (error-obj on failure)
760
801
  async previewCodeAction(file, startLine, startColumn, endLine, endColumn, actionTitle, signal) {
761
- return this.requestOrNull("extension/previewCodeAction", { file, startLine, startColumn, endLine, endColumn, actionTitle }, 15_000, signal);
802
+ return this.tryRequest("extension/previewCodeAction", { file, startLine, startColumn, endLine, endColumn, actionTitle }, 15_000, signal);
762
803
  }
804
+ // handler returns { success, newName?, affectedFiles?, totalEdits?, error? } — rich contract, caller needs success field
763
805
  async renameSymbol(file, line, column, newName, signal) {
764
806
  // Rename can be slow on large projects
765
- return this.requestOrNull("extension/renameSymbol", { file, line, column, newName }, 15_000, signal);
807
+ return this.validatedRequest("extension/renameSymbol", { file, line, column, newName }, (r) => {
808
+ const o = r;
809
+ return typeof o.success === "boolean" ? r : null;
810
+ }, 15_000, signal);
766
811
  }
767
812
  async searchSymbols(query, maxResults, signal) {
768
813
  return this.requestOrNull("extension/searchSymbols", { query, maxResults }, undefined, signal);
769
814
  }
815
+ // handler returns { canRename: boolean, range?, placeholder?, reason? } — validatedRequest
770
816
  async prepareRename(file, line, column, signal) {
771
- return this.requestOrNull("extension/prepareRename", { file, line, column }, undefined, signal);
817
+ return this.validatedRequest("extension/prepareRename", { file, line, column }, (r) => {
818
+ const o = r;
819
+ return typeof o.canRename === "boolean" ? r : null;
820
+ }, undefined, signal);
772
821
  }
822
+ // handler returns { formatted: boolean, editCount? } or { formatted: false, reason? } — validated
773
823
  async formatRange(file, startLine, endLine, signal) {
774
- return this.requestOrNull("extension/formatRange", { file, startLine, endLine }, undefined, signal);
824
+ return this.validatedRequest("extension/formatRange", { file, startLine, endLine }, (r) => {
825
+ const o = r;
826
+ return typeof o.formatted === "boolean" ? r : null;
827
+ }, undefined, signal);
775
828
  }
829
+ // handler returns { activeSignature, activeParameter, signatures } | null — tryRequest
776
830
  async signatureHelp(file, line, column, signal) {
777
- return this.requestOrNull("extension/signatureHelp", { file, line, column }, undefined, signal);
831
+ return this.tryRequest("extension/signatureHelp", { file, line, column }, undefined, signal);
778
832
  }
833
+ // handler returns { ranges: [...] } — validatedRequest
779
834
  async foldingRanges(file, signal) {
780
- return this.requestOrNull("extension/foldingRanges", { file }, undefined, signal);
835
+ return this.validatedRequest("extension/foldingRanges", { file }, (r) => {
836
+ const o = r;
837
+ return Array.isArray(o.ranges) ? r : null;
838
+ }, undefined, signal);
781
839
  }
840
+ // handler returns { ranges: [...] } — validatedRequest
782
841
  async selectionRanges(file, line, column, signal) {
783
- return this.requestOrNull("extension/selectionRanges", { file, line, column }, undefined, signal);
842
+ return this.validatedRequest("extension/selectionRanges", { file, line, column }, (r) => {
843
+ const o = r;
844
+ return Array.isArray(o.ranges) ? r : null;
845
+ }, undefined, signal);
784
846
  }
847
+ // handler returns { watching: true, id, pattern } — tryRequest
785
848
  async watchFiles(id, pattern) {
786
- return this.requestOrNull("extension/watchFiles", { id, pattern });
849
+ return this.tryRequest("extension/watchFiles", { id, pattern });
787
850
  }
851
+ // handler returns { unwatched: true, id } — tryRequest
788
852
  async unwatchFiles(id) {
789
- return this.requestOrNull("extension/unwatchFiles", { id });
853
+ return this.tryRequest("extension/unwatchFiles", { id });
790
854
  }
791
855
  async captureScreenshot() {
792
856
  return this.validatedRequest("extension/captureScreenshot", {}, (r) => {
@@ -797,60 +861,77 @@ export class ExtensionClient {
797
861
  });
798
862
  }
799
863
  // --- Terminal Features ---
864
+ // handler returns { terminals, count, outputCaptureAvailable } — validatedRequest
800
865
  async listTerminals() {
801
- return this.requestOrNull("extension/listTerminals");
866
+ return this.validatedRequest("extension/listTerminals", undefined, (r) => {
867
+ const o = r;
868
+ return Array.isArray(o.terminals) ? r : null;
869
+ });
802
870
  }
871
+ // handler returns { available: boolean, ... } — validatedRequest
803
872
  async getTerminalOutput(name, index, lines) {
804
- return this.requestOrNull("extension/getTerminalOutput", {
805
- name,
806
- index,
807
- lines,
873
+ return this.validatedRequest("extension/getTerminalOutput", { name, index, lines }, (r) => {
874
+ const o = r;
875
+ return typeof o.available === "boolean" ? r : null;
808
876
  });
809
877
  }
878
+ // handler returns { success: boolean, terminalName? } — validatedRequest
810
879
  async disposeTerminal(name, index) {
811
- return this.requestOrNull("extension/disposeTerminal", { name, index });
880
+ return this.validatedRequest("extension/disposeTerminal", { name, index }, (r) => {
881
+ const o = r;
882
+ return typeof o.success === "boolean" ? r : null;
883
+ });
812
884
  }
813
885
  // --- File Operations ---
886
+ // handler returns { success: boolean, filePath, isDirectory, created } | { success: false, error } — validatedRequest
814
887
  async createFile(filePath, content, isDirectory, overwrite, openAfterCreate) {
815
- return this.requestOrNull("extension/createFile", {
816
- filePath,
817
- content,
818
- isDirectory,
819
- overwrite,
820
- openAfterCreate,
888
+ return this.validatedRequest("extension/createFile", { filePath, content, isDirectory, overwrite, openAfterCreate }, (r) => {
889
+ const o = r;
890
+ return typeof o.success === "boolean" ? r : null;
821
891
  });
822
892
  }
893
+ // handler returns { success: boolean, filePath, deleted } | { success: false, error } — validatedRequest
823
894
  async deleteFile(filePath, recursive, useTrash) {
824
- return this.requestOrNull("extension/deleteFile", {
825
- filePath,
826
- recursive,
827
- useTrash,
895
+ return this.validatedRequest("extension/deleteFile", { filePath, recursive, useTrash }, (r) => {
896
+ const o = r;
897
+ return typeof o.success === "boolean" ? r : null;
828
898
  });
829
899
  }
900
+ // handler returns { success: boolean, oldPath, newPath, renamed } | { success: false, error } — validatedRequest
830
901
  async renameFile(oldPath, newPath, overwrite) {
831
- return this.requestOrNull("extension/renameFile", {
832
- oldPath,
833
- newPath,
834
- overwrite,
902
+ return this.validatedRequest("extension/renameFile", { oldPath, newPath, overwrite }, (r) => {
903
+ const o = r;
904
+ return typeof o.success === "boolean" ? r : null;
835
905
  });
836
906
  }
837
907
  // --- Text Editing ---
908
+ // handler returns { success: boolean, editCount, saved } | { success: false, error } — validatedRequest
838
909
  async editText(filePath, edits, save) {
839
- return this.requestOrNull("extension/editText", { filePath, edits, save });
910
+ return this.validatedRequest("extension/editText", { filePath, edits, save }, (r) => {
911
+ const o = r;
912
+ return typeof o.success === "boolean" ? r : null;
913
+ });
840
914
  }
915
+ // handler returns { success: boolean, saved, source } | { success: false, error } — validatedRequest
841
916
  async replaceBlock(filePath, oldContent, newContent, save) {
842
- return this.requestOrNull("extension/replaceBlock", {
843
- filePath,
844
- oldContent,
845
- newContent,
846
- save,
917
+ return this.validatedRequest("extension/replaceBlock", { filePath, oldContent, newContent, save }, (r) => {
918
+ const o = r;
919
+ return typeof o.success === "boolean" ? r : null;
847
920
  });
848
921
  }
922
+ // handler returns { symbols: FlatSymbol[], count } — validated
849
923
  async getDocumentSymbols(file, signal) {
850
- return this.requestOrNull("extension/getDocumentSymbols", { file }, undefined, signal);
924
+ return this.validatedRequest("extension/getDocumentSymbols", { file }, (r) => {
925
+ const o = r;
926
+ return Array.isArray(o.symbols) ? r : null;
927
+ }, undefined, signal);
851
928
  }
929
+ // handler returns { symbol, incoming?, outgoing? } | null — validated
852
930
  async getCallHierarchy(file, line, column, direction, maxResults, signal) {
853
- return this.requestOrNull("extension/getCallHierarchy", { file, line, column, direction, maxResults }, 15_000, signal);
931
+ return this.validatedRequest("extension/getCallHierarchy", { file, line, column, direction, maxResults }, (r) => {
932
+ const o = r;
933
+ return typeof o.symbol === "object" && o.symbol !== null ? r : null;
934
+ }, 15_000, signal);
854
935
  }
855
936
  // --- Code Actions (format, fix, organize) ---
856
937
  async formatDocument(file) {
@@ -864,34 +945,41 @@ export class ExtensionClient {
864
945
  // unwraps to null so consumers fall through to their CLI fallback.
865
946
  return this.tryRequest("extension/fixAllLintErrors", { file }, 15_000);
866
947
  }
948
+ // handler returns { success: true, actionsApplied } | { error } — tryRequest (error-obj on failure)
867
949
  async organizeImports(file) {
868
- return this.requestOrNull("extension/organizeImports", { file }, 15_000);
950
+ return this.tryRequest("extension/organizeImports", { file }, 15_000);
869
951
  }
870
952
  // --- Terminal Control ---
953
+ // handler returns { success: true, name, index } — validatedRequest
871
954
  async createTerminal(name, cwd, env, show) {
872
- return this.requestOrNull("extension/createTerminal", {
873
- name,
874
- cwd,
875
- env,
876
- show,
955
+ return this.validatedRequest("extension/createTerminal", { name, cwd, env, show }, (r) => {
956
+ const o = r;
957
+ return typeof o.success === "boolean" ? r : null;
877
958
  });
878
959
  }
960
+ // handler returns { success: boolean, terminalName? } | { success: false, error, availableTerminals } — validatedRequest
879
961
  async sendTerminalCommand(text, name, index, addNewline) {
880
- return this.requestOrNull("extension/sendTerminalCommand", {
881
- text,
882
- name,
883
- index,
884
- addNewline,
962
+ return this.validatedRequest("extension/sendTerminalCommand", { text, name, index, addNewline }, (r) => {
963
+ const o = r;
964
+ return typeof o.success === "boolean" ? r : null;
885
965
  });
886
966
  }
967
+ // handler returns { matched: boolean, matchedLine?, elapsed?, terminalName, timedOut?, error? } — validatedRequest
887
968
  async waitForTerminalOutput(pattern, name, index, timeoutMs) {
888
969
  const requestTimeout = (timeoutMs ?? 30_000) + 5_000;
889
- return this.requestOrNull("extension/waitForTerminalOutput", { pattern, name, index, timeoutMs }, requestTimeout);
970
+ return this.validatedRequest("extension/waitForTerminalOutput", { pattern, name, index, timeoutMs }, (r) => {
971
+ const o = r;
972
+ return typeof o.matched === "boolean" ? r : null;
973
+ }, requestTimeout);
890
974
  }
975
+ // handler returns { success: boolean, exitCode?, output, terminalName, timedOut?, error? } — validatedRequest
891
976
  async executeInTerminal(command, name, index, timeoutMs, show) {
892
977
  // Add 5s overhead beyond the command timeout so the bridge doesn't cut the extension off early
893
978
  const requestTimeout = (timeoutMs ?? 30_000) + 5_000;
894
- return this.requestOrNull("extension/executeInTerminal", { command, name, index, timeoutMs, show }, requestTimeout);
979
+ return this.validatedRequest("extension/executeInTerminal", { command, name, index, timeoutMs, show }, (r) => {
980
+ const o = r;
981
+ return typeof o.success === "boolean" ? r : null;
982
+ }, requestTimeout);
895
983
  }
896
984
  // --- Debug ---
897
985
  async getDebugState() {
@@ -899,34 +987,55 @@ export class ExtensionClient {
899
987
  ? r
900
988
  : null);
901
989
  }
990
+ // handler returns { result, type?, variablesReference? } — validatedRequest
902
991
  async evaluateInDebugger(expression, frameId, context, signal) {
903
- return this.requestOrNull("extension/evaluateInDebugger", { expression, frameId, context }, 15_000, signal);
992
+ return this.validatedRequest("extension/evaluateInDebugger", { expression, frameId, context }, (r) => {
993
+ const o = r;
994
+ return "result" in o ? r : null;
995
+ }, 15_000, signal);
904
996
  }
997
+ // handler returns { set: number, file } — validatedRequest
905
998
  async setDebugBreakpoints(file, breakpoints, signal) {
906
- return this.requestOrNull("extension/setDebugBreakpoints", { file, breakpoints }, undefined, signal);
999
+ return this.validatedRequest("extension/setDebugBreakpoints", { file, breakpoints }, (r) => {
1000
+ const o = r;
1001
+ return typeof o.set === "number" ? r : null;
1002
+ }, undefined, signal);
907
1003
  }
1004
+ // handler returns { started: boolean } — validatedRequest
908
1005
  async startDebugging(configName, signal) {
909
- return this.requestOrNull("extension/startDebugging", { configName }, 15_000, signal);
1006
+ return this.validatedRequest("extension/startDebugging", { configName }, (r) => {
1007
+ const o = r;
1008
+ return typeof o.started === "boolean" ? r : null;
1009
+ }, 15_000, signal);
910
1010
  }
1011
+ // handler returns { stopped: boolean, message? } | { stopped: false, error } — validatedRequest
911
1012
  async stopDebugging(signal) {
912
- return this.requestOrNull("extension/stopDebugging", undefined, undefined, signal);
1013
+ return this.validatedRequest("extension/stopDebugging", undefined, (r) => {
1014
+ const o = r;
1015
+ return typeof o.stopped === "boolean" ? r : null;
1016
+ }, undefined, signal);
913
1017
  }
914
1018
  // --- Decorations ---
1019
+ // handler returns { applied: number, editorsUpdated: number } — validatedRequest
915
1020
  async setDecorations(id, file, decorations) {
916
- return this.requestOrNull("extension/setDecorations", {
917
- id,
918
- file,
919
- decorations,
1021
+ return this.validatedRequest("extension/setDecorations", { id, file, decorations }, (r) => {
1022
+ const o = r;
1023
+ return typeof o.applied === "number" ? r : null;
920
1024
  });
921
1025
  }
1026
+ // handler returns { cleared: number } — validatedRequest
922
1027
  async clearDecorations(id) {
923
- return this.requestOrNull("extension/clearDecorations", { id });
1028
+ return this.validatedRequest("extension/clearDecorations", { id }, (r) => {
1029
+ const o = r;
1030
+ return typeof o.cleared === "number" ? r : null;
1031
+ });
924
1032
  }
925
1033
  // --- VS Code Commands ---
1034
+ // handler returns { result, _warning? } — validatedRequest
926
1035
  async executeVSCodeCommand(command, args) {
927
- return this.requestOrNull("extension/executeVSCodeCommand", {
928
- command,
929
- args,
1036
+ return this.validatedRequest("extension/executeVSCodeCommand", { command, args }, (r) => {
1037
+ const o = r;
1038
+ return "result" in o ? r : null;
930
1039
  });
931
1040
  }
932
1041
  async listVSCodeCommands(filter) {
@@ -938,57 +1047,86 @@ export class ExtensionClient {
938
1047
  });
939
1048
  }
940
1049
  // --- Workspace Settings ---
1050
+ // handler returns { section, settings } — validatedRequest
941
1051
  async getWorkspaceSettings(section, target) {
942
- return this.requestOrNull("extension/getWorkspaceSettings", {
943
- section,
944
- target,
1052
+ return this.validatedRequest("extension/getWorkspaceSettings", { section, target }, (r) => {
1053
+ const o = r;
1054
+ return typeof o.settings === "object" && o.settings !== null ? r : null;
945
1055
  });
946
1056
  }
1057
+ // handler returns { set: true, key, target } — validatedRequest
947
1058
  async setWorkspaceSetting(key, value, target) {
948
- return this.requestOrNull("extension/setWorkspaceSetting", {
949
- key,
950
- value,
951
- target,
1059
+ return this.validatedRequest("extension/setWorkspaceSetting", { key, value, target }, (r) => {
1060
+ const o = r;
1061
+ return typeof o.set === "boolean" ? r : null;
952
1062
  });
953
1063
  }
954
1064
  // --- Clipboard ---
1065
+ // handler returns { text, byteLength, truncated } — validatedRequest
955
1066
  async readClipboard() {
956
- return this.requestOrNull("extension/readClipboard");
1067
+ return this.validatedRequest("extension/readClipboard", undefined, (r) => {
1068
+ const o = r;
1069
+ return typeof o.text === "string" ? r : null;
1070
+ });
957
1071
  }
1072
+ // handler returns { written: boolean, byteLength? } | { written: false, error } — validatedRequest
958
1073
  async writeClipboard(text) {
959
- return this.requestOrNull("extension/writeClipboard", { text });
1074
+ return this.validatedRequest("extension/writeClipboard", { text }, (r) => {
1075
+ const o = r;
1076
+ return typeof o.written === "boolean" ? r : null;
1077
+ });
960
1078
  }
961
1079
  // --- Inlay Hints ---
1080
+ // handler returns { hints: [...], count, capped? } — validated
962
1081
  async getInlayHints(file, startLine, endLine) {
963
- return this.requestOrNull("extension/getInlayHints", {
964
- file,
965
- startLine,
966
- endLine,
1082
+ return this.validatedRequest("extension/getInlayHints", { file, startLine, endLine }, (r) => {
1083
+ const o = r;
1084
+ return Array.isArray(o.hints) ? r : null;
967
1085
  });
968
1086
  }
969
1087
  // --- Type Hierarchy ---
1088
+ // handler returns { found: boolean, root?, supertypes, subtypes, direction } — validated
970
1089
  async getTypeHierarchy(file, line, column, direction, maxResults, signal) {
971
- return this.requestOrNull("extension/getTypeHierarchy", { file, line, column, direction, maxResults }, 15_000, signal);
1090
+ return this.validatedRequest("extension/getTypeHierarchy", { file, line, column, direction, maxResults }, (r) => {
1091
+ const o = r;
1092
+ return typeof o.found === "boolean" ? r : null;
1093
+ }, 15_000, signal);
972
1094
  }
973
1095
  // --- Code Lens ---
1096
+ // handler returns { lenses: [...], count } — validated
974
1097
  async getCodeLens(file, signal) {
975
- return this.requestOrNull("extension/getCodeLens", { file }, undefined, signal);
1098
+ return this.validatedRequest("extension/getCodeLens", { file }, (r) => {
1099
+ const o = r;
1100
+ return Array.isArray(o.lenses) ? r : null;
1101
+ }, undefined, signal);
976
1102
  }
977
1103
  // --- Semantic Tokens ---
1104
+ // handler returns { tokens: [...], count, capped, legend: { tokenTypes, tokenModifiers } } — validated
978
1105
  async getSemanticTokens(file, startLine, endLine, maxTokens, signal) {
979
- return this.requestOrNull("extension/getSemanticTokens", { file, startLine, endLine, maxTokens }, 15_000, signal);
1106
+ return this.validatedRequest("extension/getSemanticTokens", { file, startLine, endLine, maxTokens }, (r) => {
1107
+ const o = r;
1108
+ return Array.isArray(o.tokens) ? r : null;
1109
+ }, 15_000, signal);
980
1110
  }
981
1111
  // --- Document Links ---
982
1112
  async getDocumentLinks(file, signal) {
983
1113
  return this.requestOrNull("extension/getDocumentLinks", { file }, undefined, signal);
984
1114
  }
985
1115
  // --- Tasks ---
1116
+ // handler returns { tasks: [...] } — validatedRequest
986
1117
  async listTasks(type) {
987
- return this.requestOrNull("extension/listTasks", type ? { type } : undefined, 15_000);
1118
+ return this.validatedRequest("extension/listTasks", type ? { type } : undefined, (r) => {
1119
+ const o = r;
1120
+ return Array.isArray(o.tasks) ? r : null;
1121
+ }, 15_000);
988
1122
  }
1123
+ // handler returns { success: boolean, name, exitCode? } | { success: false, error, timedOut? } — validatedRequest
989
1124
  async runTask(name, type, timeoutMs) {
990
1125
  const requestTimeout = (timeoutMs ?? 60_000) + 5_000;
991
- return this.requestOrNull("extension/runTask", { name, type, timeoutMs }, requestTimeout);
1126
+ return this.validatedRequest("extension/runTask", { name, type, timeoutMs }, (r) => {
1127
+ const o = r;
1128
+ return typeof o.success === "boolean" ? r : null;
1129
+ }, requestTimeout);
992
1130
  }
993
1131
  // --- Workspace Folders ---
994
1132
  async getWorkspaceFolders() {
@@ -1003,13 +1141,16 @@ export class ExtensionClient {
1003
1141
  });
1004
1142
  }
1005
1143
  // --- Notebook ---
1144
+ // handler not yet implemented in extension — bare requestOrNull returns null until implemented
1006
1145
  async getNotebookCells(file) {
1007
1146
  return this.requestOrNull("extension/getNotebookCells", { file });
1008
1147
  }
1148
+ // handler not yet implemented in extension — bare requestOrNull returns null until implemented
1009
1149
  async runNotebookCell(file, cellIndex, timeoutMs) {
1010
1150
  const requestTimeout = (timeoutMs ?? 30_000) + 5_000;
1011
1151
  return this.requestOrNull("extension/runNotebookCell", { file, cellIndex, timeoutMs }, requestTimeout);
1012
1152
  }
1153
+ // handler not yet implemented in extension — bare requestOrNull returns null until implemented
1013
1154
  async getNotebookOutput(file, cellIndex) {
1014
1155
  return this.requestOrNull("extension/getNotebookOutput", {
1015
1156
  file,
@@ -1081,6 +1222,8 @@ export class ExtensionClient {
1081
1222
  suspended: now < this.extensionSuspendedUntil,
1082
1223
  suspendedUntil: this.extensionSuspendedUntil,
1083
1224
  failures: activeFailures.length,
1225
+ openCount: this._circuitOpenCount,
1226
+ lastOpenedAt: this._lastCircuitOpenedAt?.toISOString() ?? null,
1084
1227
  };
1085
1228
  }
1086
1229
  isConnected() {