@noorm/marie-cli 0.1.17 → 0.1.25

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 (299) hide show
  1. package/README.md +7 -15
  2. package/SENTINEL.md +4 -7
  3. package/dist/cli-new/components/AgenticSpinner.js +28 -0
  4. package/dist/cli-new/components/AgenticSpinner.js.map +1 -0
  5. package/dist/cli-new/components/App.js +16 -63
  6. package/dist/cli-new/components/App.js.map +1 -1
  7. package/dist/cli-new/components/ApprovalDialog.js +3 -2
  8. package/dist/cli-new/components/ApprovalDialog.js.map +1 -1
  9. package/dist/cli-new/components/Banner.js +16 -5
  10. package/dist/cli-new/components/Banner.js.map +1 -1
  11. package/dist/cli-new/components/ChatArea.js +6 -7
  12. package/dist/cli-new/components/ChatArea.js.map +1 -1
  13. package/dist/cli-new/components/Header.js +14 -8
  14. package/dist/cli-new/components/Header.js.map +1 -1
  15. package/dist/cli-new/components/InputArea.js +98 -31
  16. package/dist/cli-new/components/InputArea.js.map +1 -1
  17. package/dist/cli-new/components/MessageBubble.js +28 -19
  18. package/dist/cli-new/components/MessageBubble.js.map +1 -1
  19. package/dist/cli-new/components/SessionSwitcher.js +6 -9
  20. package/dist/cli-new/components/SessionSwitcher.js.map +1 -1
  21. package/dist/cli-new/components/SetupWizard.js +80 -257
  22. package/dist/cli-new/components/SetupWizard.js.map +1 -1
  23. package/dist/cli-new/components/ToolCallDisplay.js +20 -5
  24. package/dist/cli-new/components/ToolCallDisplay.js.map +1 -1
  25. package/dist/cli-new/components/WizardSteps.js +22 -0
  26. package/dist/cli-new/components/WizardSteps.js.map +1 -0
  27. package/dist/cli-new/constants/SetupConstants.js +42 -0
  28. package/dist/cli-new/constants/SetupConstants.js.map +1 -0
  29. package/dist/cli-new/hooks/useGit.js +19 -62
  30. package/dist/cli-new/hooks/useGit.js.map +1 -1
  31. package/dist/cli-new/hooks/useMarie.js +26 -18
  32. package/dist/cli-new/hooks/useMarie.js.map +1 -1
  33. package/dist/cli-new/hooks/useSessions.js +1 -1
  34. package/dist/cli-new/hooks/useSessions.js.map +1 -1
  35. package/dist/cli-new/hooks/useSetupWizard.js +88 -0
  36. package/dist/cli-new/hooks/useSetupWizard.js.map +1 -0
  37. package/dist/cli-new/hooks/useUpdateCheck.js +4 -3
  38. package/dist/cli-new/hooks/useUpdateCheck.js.map +1 -1
  39. package/dist/cli-new/index.js +2 -4
  40. package/dist/cli-new/index.js.map +1 -1
  41. package/dist/cli-new/services/CommandService.js +104 -0
  42. package/dist/cli-new/services/CommandService.js.map +1 -0
  43. package/dist/cli-new/services/GitService.js +91 -0
  44. package/dist/cli-new/services/GitService.js.map +1 -0
  45. package/dist/cli-new/services/MarieService.js +77 -0
  46. package/dist/cli-new/services/MarieService.js.map +1 -0
  47. package/dist/cli-new/services/auth-server.js +128 -0
  48. package/dist/cli-new/services/auth-server.js.map +1 -0
  49. package/dist/cli-new/styles/theme.js +17 -17
  50. package/dist/cli-new/styles/theme.js.map +1 -1
  51. package/dist/cli-new/utils/version.js +24 -0
  52. package/dist/cli-new/utils/version.js.map +1 -0
  53. package/dist/monolith/adapters/CliMarieAdapter.js +19 -18
  54. package/dist/monolith/adapters/CliMarieAdapter.js.map +1 -1
  55. package/dist/monolith/cli/CliFileSystemPort.js +17 -3
  56. package/dist/monolith/cli/CliFileSystemPort.js.map +1 -1
  57. package/dist/monolith/cli/MarieToolDefinitionsCLI.js +44 -33
  58. package/dist/monolith/cli/MarieToolDefinitionsCLI.js.map +1 -1
  59. package/dist/monolith/cli/index.js +11 -20
  60. package/dist/monolith/cli/index.js.map +1 -1
  61. package/dist/monolith/cli/services/JoyAutomationServiceCLI.js +15 -62
  62. package/dist/monolith/cli/services/JoyAutomationServiceCLI.js.map +1 -1
  63. package/dist/monolith/cli/storage.js +154 -65
  64. package/dist/monolith/cli/storage.js.map +1 -1
  65. package/dist/monolith/domain/joy/RitualService.js +44 -46
  66. package/dist/monolith/domain/joy/RitualService.js.map +1 -1
  67. package/dist/monolith/domain/marie/MarieCortex.js +148 -0
  68. package/dist/monolith/domain/marie/MarieCortex.js.map +1 -0
  69. package/dist/monolith/domain/marie/PersonalityRenderer.js +97 -0
  70. package/dist/monolith/domain/marie/PersonalityRenderer.js.map +1 -0
  71. package/dist/monolith/infrastructure/Configuration.js +68 -0
  72. package/dist/monolith/infrastructure/Configuration.js.map +1 -0
  73. package/dist/monolith/infrastructure/CoreInfrastructure.js +204 -0
  74. package/dist/monolith/infrastructure/CoreInfrastructure.js.map +1 -0
  75. package/dist/monolith/infrastructure/ai/agents/MarieAscendant.js +3 -3
  76. package/dist/monolith/infrastructure/ai/agents/MarieAscendant.js.map +1 -1
  77. package/dist/monolith/infrastructure/ai/context/ContextArchiveService.js +51 -57
  78. package/dist/monolith/infrastructure/ai/context/ContextArchiveService.js.map +1 -1
  79. package/dist/monolith/infrastructure/ai/context/ContextManager.js +142 -98
  80. package/dist/monolith/infrastructure/ai/context/ContextManager.js.map +1 -1
  81. package/dist/monolith/infrastructure/ai/core/MarieEngine.js +104 -556
  82. package/dist/monolith/infrastructure/ai/core/MarieEngine.js.map +1 -1
  83. package/dist/monolith/infrastructure/ai/core/MarieEventDispatcher.js +1 -37
  84. package/dist/monolith/infrastructure/ai/core/MarieEventDispatcher.js.map +1 -1
  85. package/dist/monolith/infrastructure/ai/core/MarieLockManager.js +30 -5
  86. package/dist/monolith/infrastructure/ai/core/MarieLockManager.js.map +1 -1
  87. package/dist/monolith/infrastructure/ai/core/MarieProgressTracker.js +176 -196
  88. package/dist/monolith/infrastructure/ai/core/MarieProgressTracker.js.map +1 -1
  89. package/dist/monolith/infrastructure/ai/core/MariePulseService.js +41 -7
  90. package/dist/monolith/infrastructure/ai/core/MariePulseService.js.map +1 -1
  91. package/dist/monolith/infrastructure/ai/core/MarieSanitizer.js +303 -63
  92. package/dist/monolith/infrastructure/ai/core/MarieSanitizer.js.map +1 -1
  93. package/dist/monolith/infrastructure/ai/core/MarieSemaphore.js +47 -4
  94. package/dist/monolith/infrastructure/ai/core/MarieSemaphore.js.map +1 -1
  95. package/dist/monolith/infrastructure/ai/core/MarieSession.js +95 -15
  96. package/dist/monolith/infrastructure/ai/core/MarieSession.js.map +1 -1
  97. package/dist/monolith/infrastructure/ai/core/MarieStabilityMonitor.js +21 -0
  98. package/dist/monolith/infrastructure/ai/core/MarieStabilityMonitor.js.map +1 -1
  99. package/dist/monolith/infrastructure/ai/core/MarieToolMender.js +12 -0
  100. package/dist/monolith/infrastructure/ai/core/MarieToolMender.js.map +1 -1
  101. package/dist/monolith/infrastructure/ai/core/MarieToolProcessor.js +339 -481
  102. package/dist/monolith/infrastructure/ai/core/MarieToolProcessor.js.map +1 -1
  103. package/dist/monolith/infrastructure/ai/core/MarieVitality.js +238 -0
  104. package/dist/monolith/infrastructure/ai/core/MarieVitality.js.map +1 -0
  105. package/dist/monolith/infrastructure/ai/core/ReasoningBudget.js +23 -12
  106. package/dist/monolith/infrastructure/ai/core/ReasoningBudget.js.map +1 -1
  107. package/dist/monolith/infrastructure/ai/core/SessionLogService.js +9 -2
  108. package/dist/monolith/infrastructure/ai/core/SessionLogService.js.map +1 -1
  109. package/dist/monolith/infrastructure/ai/providers/AIProvider.js +402 -1
  110. package/dist/monolith/infrastructure/ai/providers/AIProvider.js.map +1 -1
  111. package/dist/monolith/infrastructure/ai/providers/DreamBeesProvider.js +114 -0
  112. package/dist/monolith/infrastructure/ai/providers/DreamBeesProvider.js.map +1 -0
  113. package/dist/monolith/infrastructure/ai/providers/OpenRouterProvider.js +426 -370
  114. package/dist/monolith/infrastructure/ai/providers/OpenRouterProvider.js.map +1 -1
  115. package/dist/monolith/infrastructure/ai/providers/OpenRouterStreamParser.js +235 -241
  116. package/dist/monolith/infrastructure/ai/providers/OpenRouterStreamParser.js.map +1 -1
  117. package/dist/monolith/infrastructure/ai/workerAi.js +185 -0
  118. package/dist/monolith/infrastructure/ai/workerAi.js.map +1 -0
  119. package/dist/monolith/infrastructure/config/ConfigService.js +216 -359
  120. package/dist/monolith/infrastructure/config/ConfigService.js.map +1 -1
  121. package/dist/monolith/infrastructure/joy/CognitiveRituals.js +8 -0
  122. package/dist/monolith/infrastructure/joy/CognitiveRituals.js.map +1 -0
  123. package/dist/monolith/infrastructure/joy/JoyTools.js +23 -43
  124. package/dist/monolith/infrastructure/joy/JoyTools.js.map +1 -1
  125. package/dist/monolith/infrastructure/persistence/MarieMindAutonomics.js +4 -0
  126. package/dist/monolith/infrastructure/persistence/MarieMindAutonomics.js.map +1 -0
  127. package/dist/monolith/infrastructure/persistence/MarieMindEngine.js +11 -0
  128. package/dist/monolith/infrastructure/persistence/MarieMindEngine.js.map +1 -0
  129. package/dist/monolith/infrastructure/persistence/NoormmeAutonomics.js +135 -0
  130. package/dist/monolith/infrastructure/persistence/NoormmeAutonomics.js.map +1 -0
  131. package/dist/monolith/infrastructure/persistence/NoormmeEngine.js +523 -0
  132. package/dist/monolith/infrastructure/persistence/NoormmeEngine.js.map +1 -0
  133. package/dist/monolith/infrastructure/persistence/NoormmeSchema.js +179 -0
  134. package/dist/monolith/infrastructure/persistence/NoormmeSchema.js.map +1 -0
  135. package/dist/monolith/infrastructure/persistence/NoormmeSeeder.js +94 -0
  136. package/dist/monolith/infrastructure/persistence/NoormmeSeeder.js.map +1 -0
  137. package/dist/monolith/infrastructure/persistence/NoormmeTools.js +371 -0
  138. package/dist/monolith/infrastructure/persistence/NoormmeTools.js.map +1 -0
  139. package/dist/monolith/infrastructure/services/MarieMemoryStore.js +133 -134
  140. package/dist/monolith/infrastructure/services/MarieMemoryStore.js.map +1 -1
  141. package/dist/monolith/infrastructure/tools/MarieToolDefinitions.js +6 -1578
  142. package/dist/monolith/infrastructure/tools/MarieToolDefinitions.js.map +1 -1
  143. package/dist/monolith/infrastructure/tools/PureStreamParser.js +68 -80
  144. package/dist/monolith/infrastructure/tools/PureStreamParser.js.map +1 -1
  145. package/dist/monolith/infrastructure/tools/SharedToolDefinitions.js +12 -11
  146. package/dist/monolith/infrastructure/tools/SharedToolDefinitions.js.map +1 -1
  147. package/dist/monolith/infrastructure/tools/SovereignTools.js +326 -0
  148. package/dist/monolith/infrastructure/tools/SovereignTools.js.map +1 -0
  149. package/dist/monolith/infrastructure/tools/ToolRegistry.js +45 -26
  150. package/dist/monolith/infrastructure/tools/ToolRegistry.js.map +1 -1
  151. package/dist/monolith/infrastructure/tools/definitions/AnalysisTools.js +232 -0
  152. package/dist/monolith/infrastructure/tools/definitions/AnalysisTools.js.map +1 -0
  153. package/dist/monolith/infrastructure/tools/definitions/AutomationTools.js +274 -0
  154. package/dist/monolith/infrastructure/tools/definitions/AutomationTools.js.map +1 -0
  155. package/dist/monolith/infrastructure/tools/definitions/ContextTools.js +71 -0
  156. package/dist/monolith/infrastructure/tools/definitions/ContextTools.js.map +1 -0
  157. package/dist/monolith/infrastructure/tools/definitions/CoreTools.js +37 -0
  158. package/dist/monolith/infrastructure/tools/definitions/CoreTools.js.map +1 -0
  159. package/dist/monolith/infrastructure/tools/definitions/DiagnosticTools.js +154 -0
  160. package/dist/monolith/infrastructure/tools/definitions/DiagnosticTools.js.map +1 -0
  161. package/dist/monolith/infrastructure/tools/definitions/NavigationTools.js +197 -0
  162. package/dist/monolith/infrastructure/tools/definitions/NavigationTools.js.map +1 -0
  163. package/dist/monolith/infrastructure/tools/definitions/PlanningTools.js +300 -0
  164. package/dist/monolith/infrastructure/tools/definitions/PlanningTools.js.map +1 -0
  165. package/dist/monolith/plumbing/Plumbing.js +238 -0
  166. package/dist/monolith/plumbing/Plumbing.js.map +1 -0
  167. package/dist/monolith/plumbing/PlumbingAnalysis.js +109 -0
  168. package/dist/monolith/plumbing/PlumbingAnalysis.js.map +1 -0
  169. package/dist/monolith/plumbing/PlumbingSystem.js +169 -0
  170. package/dist/monolith/plumbing/PlumbingSystem.js.map +1 -0
  171. package/dist/monolith/plumbing/analysis/ComplexityService.js +30 -34
  172. package/dist/monolith/plumbing/analysis/ComplexityService.js.map +1 -1
  173. package/dist/monolith/plumbing/analysis/DependencyService.js +55 -44
  174. package/dist/monolith/plumbing/analysis/DependencyService.js.map +1 -1
  175. package/dist/monolith/plumbing/analysis/DiscoveryService.js +40 -42
  176. package/dist/monolith/plumbing/analysis/DiscoveryService.js.map +1 -1
  177. package/dist/monolith/plumbing/analysis/JoyMapService.js +52 -56
  178. package/dist/monolith/plumbing/analysis/JoyMapService.js.map +1 -1
  179. package/dist/monolith/plumbing/analysis/LintService.js +118 -118
  180. package/dist/monolith/plumbing/analysis/LintService.js.map +1 -1
  181. package/dist/monolith/plumbing/analysis/MarieSentinelService.js +278 -269
  182. package/dist/monolith/plumbing/analysis/MarieSentinelService.js.map +1 -1
  183. package/dist/monolith/plumbing/analysis/QualityGuardrailService.js +116 -114
  184. package/dist/monolith/plumbing/analysis/QualityGuardrailService.js.map +1 -1
  185. package/dist/monolith/plumbing/analysis/SurgicalMender.js +57 -59
  186. package/dist/monolith/plumbing/analysis/SurgicalMender.js.map +1 -1
  187. package/dist/monolith/plumbing/analysis/TestService.js +89 -89
  188. package/dist/monolith/plumbing/analysis/TestService.js.map +1 -1
  189. package/dist/monolith/plumbing/filesystem/FileService.js +123 -195
  190. package/dist/monolith/plumbing/filesystem/FileService.js.map +1 -1
  191. package/dist/monolith/plumbing/filesystem/PathResolver.js +7 -8
  192. package/dist/monolith/plumbing/filesystem/PathResolver.js.map +1 -1
  193. package/dist/monolith/plumbing/git/GitService.js +4 -4
  194. package/dist/monolith/plumbing/git/GitService.js.map +1 -1
  195. package/dist/monolith/plumbing/lsp/SymbolService.js +5 -34
  196. package/dist/monolith/plumbing/lsp/SymbolService.js.map +1 -1
  197. package/dist/monolith/plumbing/terminal/ProcessRegistry.js +20 -22
  198. package/dist/monolith/plumbing/terminal/ProcessRegistry.js.map +1 -1
  199. package/dist/monolith/plumbing/terminal/TerminalService.js +127 -136
  200. package/dist/monolith/plumbing/terminal/TerminalService.js.map +1 -1
  201. package/dist/monolith/plumbing/utils/EnvironmentUtils.js +3 -23
  202. package/dist/monolith/plumbing/utils/EnvironmentUtils.js.map +1 -1
  203. package/dist/monolith/plumbing/utils/JsonUtils.js +252 -311
  204. package/dist/monolith/plumbing/utils/JsonUtils.js.map +1 -1
  205. package/dist/monolith/plumbing/utils/MutexUtils.js.map +1 -1
  206. package/dist/monolith/plumbing/utils/PlumbingCore.js +549 -0
  207. package/dist/monolith/plumbing/utils/PlumbingCore.js.map +1 -0
  208. package/dist/monolith/plumbing/utils/PrefixTree.js +61 -114
  209. package/dist/monolith/plumbing/utils/PrefixTree.js.map +1 -1
  210. package/dist/monolith/plumbing/utils/StreamTagDetector.js +89 -127
  211. package/dist/monolith/plumbing/utils/StreamTagDetector.js.map +1 -1
  212. package/dist/monolith/plumbing/utils/StringUtils.js +87 -89
  213. package/dist/monolith/plumbing/utils/StringUtils.js.map +1 -1
  214. package/dist/monolith/runtime/MarieRuntime.js +76 -417
  215. package/dist/monolith/runtime/MarieRuntime.js.map +1 -1
  216. package/dist/monolith/runtime/RuntimeAdapterBase.js +1 -1
  217. package/dist/monolith/runtime/RuntimeAdapterBase.js.map +1 -1
  218. package/dist/monolith/runtime/providerFactory.js +1 -7
  219. package/dist/monolith/runtime/providerFactory.js.map +1 -1
  220. package/dist/monolith/services/HealthService.js +29 -32
  221. package/dist/monolith/services/HealthService.js.map +1 -1
  222. package/dist/monolith/services/JoyAutomationService.js +58 -95
  223. package/dist/monolith/services/JoyAutomationService.js.map +1 -1
  224. package/dist/monolith/services/MarieAutomationService.js +59 -0
  225. package/dist/monolith/services/MarieAutomationService.js.map +1 -0
  226. package/dist/monolith/services/MarieGhostService.js +46 -161
  227. package/dist/monolith/services/MarieGhostService.js.map +1 -1
  228. package/dist/monolith/services/MarieServices.js +102 -0
  229. package/dist/monolith/services/MarieServices.js.map +1 -0
  230. package/dist/monolith/services/MarieTypes.js +2 -0
  231. package/dist/monolith/services/MarieTypes.js.map +1 -0
  232. package/dist/monolith/services/UpdateService.js +47 -49
  233. package/dist/monolith/services/UpdateService.js.map +1 -1
  234. package/dist/prompts.js +11 -5
  235. package/dist/prompts.js.map +1 -1
  236. package/dist/test_prefix_tree.js +9 -9
  237. package/dist/test_prefix_tree.js.map +1 -1
  238. package/package.json +18 -88
  239. package/run_test.js +5 -0
  240. package/.marie_visual_verify_1771225696548/progress_bar_check.txt +0 -1
  241. package/dist/extension.cjs +0 -635
  242. package/dist/extension.js +0 -473
  243. package/dist/extension.js.map +0 -1
  244. package/dist/monolith/adapters/VscodeMarieAdapter.js +0 -81
  245. package/dist/monolith/adapters/VscodeMarieAdapter.js.map +0 -1
  246. package/dist/monolith/domain/joy/JoyTools.js +0 -535
  247. package/dist/monolith/domain/joy/JoyTools.js.map +0 -1
  248. package/dist/monolith/infrastructure/ai/agents/MarieYOLO.js +0 -207
  249. package/dist/monolith/infrastructure/ai/agents/MarieYOLO.js.map +0 -1
  250. package/dist/monolith/infrastructure/ai/core/GhostPort.js +0 -2
  251. package/dist/monolith/infrastructure/ai/core/GhostPort.js.map +0 -1
  252. package/dist/monolith/infrastructure/ai/core/MarieYOLOTypes.js +0 -2
  253. package/dist/monolith/infrastructure/ai/core/MarieYOLOTypes.js.map +0 -1
  254. package/dist/monolith/infrastructure/ai/core/VscodeFileSystemPort.js +0 -33
  255. package/dist/monolith/infrastructure/ai/core/VscodeFileSystemPort.js.map +0 -1
  256. package/dist/monolith/infrastructure/ai/providers/AnthropicProvider.js +0 -148
  257. package/dist/monolith/infrastructure/ai/providers/AnthropicProvider.js.map +0 -1
  258. package/dist/monolith/infrastructure/ai/providers/CerebrasProvider.js +0 -208
  259. package/dist/monolith/infrastructure/ai/providers/CerebrasProvider.js.map +0 -1
  260. package/dist/monolith/plumbing/ui/DecorationService.js +0 -54
  261. package/dist/monolith/plumbing/ui/DecorationService.js.map +0 -1
  262. package/dist/monolith/services/JoyLogService.js +0 -48
  263. package/dist/monolith/services/JoyLogService.js.map +0 -1
  264. package/dist/monolith/services/JoyService.js +0 -209
  265. package/dist/monolith/services/JoyService.js.map +0 -1
  266. package/dist/monolith/services/MarieSCMProvider.js +0 -41
  267. package/dist/monolith/services/MarieSCMProvider.js.map +0 -1
  268. package/dist/test_agent_stream_control_plane.js +0 -170
  269. package/dist/test_agent_stream_control_plane.js.map +0 -1
  270. package/dist/test_strategy_integration.js +0 -114
  271. package/dist/test_strategy_integration.js.map +0 -1
  272. package/dist/test_streaming_fragility.js +0 -191
  273. package/dist/test_streaming_fragility.js.map +0 -1
  274. package/dist/webview-ui/App.js +0 -16
  275. package/dist/webview-ui/App.js.map +0 -1
  276. package/dist/webview-ui/Providers.js +0 -6
  277. package/dist/webview-ui/Providers.js.map +0 -1
  278. package/dist/webview-ui/components/ApprovalPanel.js +0 -8
  279. package/dist/webview-ui/components/ApprovalPanel.js.map +0 -1
  280. package/dist/webview-ui/components/ChatPanel.js +0 -19
  281. package/dist/webview-ui/components/ChatPanel.js.map +0 -1
  282. package/dist/webview-ui/components/Composer.js +0 -19
  283. package/dist/webview-ui/components/Composer.js.map +0 -1
  284. package/dist/webview-ui/components/HeaderBar.js +0 -5
  285. package/dist/webview-ui/components/HeaderBar.js.map +0 -1
  286. package/dist/webview-ui/components/SessionList.js +0 -14
  287. package/dist/webview-ui/components/SessionList.js.map +0 -1
  288. package/dist/webview-ui/context/WebviewStateContext.js +0 -146
  289. package/dist/webview-ui/context/WebviewStateContext.js.map +0 -1
  290. package/dist/webview-ui/main.css +0 -1
  291. package/dist/webview-ui/main.js +0 -108
  292. package/dist/webview-ui/main.js.map +0 -1
  293. package/dist/webview-ui/types.js +0 -2
  294. package/dist/webview-ui/types.js.map +0 -1
  295. package/dist/webview-ui/vscode.js +0 -4
  296. package/dist/webview-ui/vscode.js.map +0 -1
  297. package/lint_output.txt +0 -705
  298. package/lint_output_v2.txt +0 -711
  299. package/marie-coder-0.1.16.vsix +0 -0
@@ -1,360 +1,301 @@
1
+ import { safeGetKeys } from "../../infrastructure/ai/core/MarieSanitizer.js";
1
2
  /**
2
- * Utilities for handling shaky AI-generated JSON and non-standard tool call formats.
3
+ * Repairs truncated or unclosed JSON strings.
4
+ * Returns the repaired string and a flag indicating if any changes were made.
3
5
  */
4
- export class JsonUtils {
5
- /**
6
- * Repairs truncated or unclosed JSON strings.
7
- * Returns the repaired string and a flag indicating if any changes were made.
8
- */
9
- static repairJsonDetailed(json) {
10
- if (typeof json !== "string")
11
- return { repaired: "{}", wasFixed: false };
12
- let repaired = json.trim();
13
- if (!repaired)
14
- return { repaired: "{}", wasFixed: false };
15
- let wasFixed = false;
16
- const original = repaired;
17
- // Pre-processing: Fix unquoted keys or single quotes
18
- repaired = repaired.replace(/'/g, '"');
19
- let isInsideString = false;
20
- const stack = [];
21
- let i = 0;
22
- while (i < repaired.length) {
23
- const char = repaired[i];
24
- const prevChar = i > 0 ? repaired[i - 1] : "";
25
- const isEscaped = prevChar === "\\";
26
- if (char === '"' && !isEscaped) {
27
- isInsideString = !isInsideString;
6
+ export function repairJsonDetailed(json) {
7
+ if (typeof json !== "string")
8
+ return { repaired: "{}", wasFixed: false };
9
+ let repaired = json.trim();
10
+ if (!repaired)
11
+ return { repaired: "{}", wasFixed: false };
12
+ let wasFixed = false;
13
+ const original = repaired;
14
+ // Pre-processing: Fix unquoted keys or single quotes
15
+ repaired = repaired.replace(/'/g, '"');
16
+ let isInsideString = false;
17
+ const stack = [];
18
+ let i = 0;
19
+ while (i < repaired.length) {
20
+ const char = repaired[i];
21
+ const prevChar = i > 0 ? repaired[i - 1] : "";
22
+ const isEscaped = prevChar === "\\";
23
+ if (char === '"' && !isEscaped) {
24
+ isInsideString = !isInsideString;
25
+ }
26
+ else if (!isInsideString) {
27
+ if (char === "{" || char === "[") {
28
+ stack.push(char);
28
29
  }
29
- else if (!isInsideString) {
30
- if (char === "{" || char === "[") {
31
- stack.push(char);
32
- }
33
- else if (char === "}" || char === "]") {
34
- const expected = char === "}" ? "{" : "[";
35
- if (stack.length > 0 && stack[stack.length - 1] === expected) {
36
- stack.pop();
37
- }
30
+ else if (char === "}" || char === "]") {
31
+ const expected = char === "}" ? "{" : "[";
32
+ if (stack.length > 0 && stack[stack.length - 1] === expected) {
33
+ stack.pop();
38
34
  }
39
35
  }
40
- i++;
41
- }
42
- if (isInsideString) {
43
- repaired += '"';
44
36
  }
45
- // Close unclosed structures
46
- const hadUnclosed = stack.length > 0;
47
- while (stack.length > 0) {
48
- const last = stack.pop();
49
- if (last === "{")
50
- repaired += "}";
51
- else if (last === "[")
52
- repaired += "]";
53
- }
54
- // Extreme: Fix unquoted property names: { name: "val" } -> { "name": "val" }
55
- repaired = repaired.replace(/([{,]\s*)([a-zA-Z0-9_$]+)(\s*):/g, '$1"$2"$3:');
56
- // Trailing commas
57
- const hadTrailing = /,\s*[}\]]/.test(repaired);
58
- repaired = repaired.replace(/,\s*([}\]])/g, "$1");
59
- wasFixed =
60
- repaired !== original || hadUnclosed || hadTrailing || isInsideString;
61
- return { repaired, wasFixed };
37
+ i++;
62
38
  }
63
- static repairJson(json) {
64
- return this.repairJsonDetailed(json).repaired;
39
+ if (isInsideString) {
40
+ repaired += '"';
65
41
  }
66
- /**
67
- * LRU cache for repairJson + JSON.parse combined operations.
68
- * Prevents redundant string repair and parsing for identical inputs.
69
- */
70
- static parseCache = new Map();
71
- static PARSE_CACHE_MAX = 100;
72
- /**
73
- * Combines repairJson + JSON.parse into a single cached operation.
74
- * Returns the parsed object directly. Throws on unrecoverable parse errors.
75
- */
76
- static safeParseJson(json) {
77
- const cached = this.parseCache.get(json);
78
- if (cached)
79
- return cached.result;
80
- const { repaired, wasFixed } = this.repairJsonDetailed(json);
81
- const result = JSON.parse(repaired);
82
- // Cache the result with FIFO eviction
83
- this.parseCache.set(json, { result, wasFixed });
84
- if (this.parseCache.size > this.PARSE_CACHE_MAX) {
85
- const firstKey = this.parseCache.keys().next().value;
86
- if (firstKey)
87
- this.parseCache.delete(firstKey);
88
- }
89
- return result;
42
+ // Close unclosed structures
43
+ const hadUnclosed = stack.length > 0;
44
+ while (stack.length > 0) {
45
+ const last = stack.pop();
46
+ if (last === "{")
47
+ repaired += "}";
48
+ else if (last === "[")
49
+ repaired += "]";
50
+ }
51
+ // Extreme: Fix unquoted property names: { name: "val" } -> { "name": "val" }
52
+ repaired = repaired.replace(/([{,]\s*)([a-zA-Z0-9_$]+)(\s*):/g, '$1"$2"$3:');
53
+ // Trailing commas
54
+ const hadTrailing = /,\s*[}\]]/.test(repaired);
55
+ repaired = repaired.replace(/,\s*([}\]])/g, "$1");
56
+ wasFixed =
57
+ repaired !== original || hadUnclosed || hadTrailing || isInsideString;
58
+ return { repaired, wasFixed };
59
+ }
60
+ export function repairJson(json) {
61
+ return repairJsonDetailed(json).repaired;
62
+ }
63
+ /**
64
+ * LRU cache for repairJson + JSON.parse combined operations.
65
+ * Prevents redundant string repair and parsing for identical inputs.
66
+ */
67
+ const parseCache = new Map();
68
+ const PARSE_CACHE_MAX = 100;
69
+ /**
70
+ * Combines repairJson + JSON.parse into a single cached operation.
71
+ * Returns the parsed object directly. Returns { result: null, wasFixed: false } on error.
72
+ */
73
+ export function safeParseJson(jsonStr, cacheKey) {
74
+ if (typeof jsonStr !== "string") {
75
+ return { result: jsonStr, wasFixed: false };
90
76
  }
91
- /**
92
- * Like safeParseJson but also returns whether repair was needed.
93
- */
94
- static safeParseJsonDetailed(json) {
95
- const cached = this.parseCache.get(json);
96
- if (cached)
97
- return cached;
98
- const { repaired, wasFixed } = this.repairJsonDetailed(json);
77
+ const key = cacheKey || jsonStr;
78
+ if (parseCache.has(key)) {
79
+ return parseCache.get(key);
80
+ }
81
+ const { repaired, wasFixed } = repairJsonDetailed(jsonStr);
82
+ try {
99
83
  const result = JSON.parse(repaired);
100
- const entry = { result, wasFixed };
101
- this.parseCache.set(json, entry);
102
- if (this.parseCache.size > this.PARSE_CACHE_MAX) {
103
- const firstKey = this.parseCache.keys().next().value;
84
+ const output = { result, wasFixed };
85
+ if (parseCache.size >= PARSE_CACHE_MAX) {
86
+ const firstKey = parseCache.keys().next().value;
104
87
  if (firstKey)
105
- this.parseCache.delete(firstKey);
88
+ parseCache.delete(firstKey);
106
89
  }
107
- return entry;
90
+ parseCache.set(key, output);
91
+ return output;
108
92
  }
109
- /**
110
- * Attempts to extract a tool call from a string that might be wrapped in tags,
111
- * contains noise, or use non-standard formats.
112
- */
113
- static extractToolCall(text) {
114
- if (typeof text !== "string")
115
- return null;
116
- // Guard: Empty or whitespace-only content
117
- const trimmedText = text.trim();
118
- if (!trimmedText || trimmedText.length < 5)
119
- return null;
120
- let contentToParse = text;
121
- let isLlama = false;
122
- let toolName = "";
123
- // 0. Handle <thought> tags (some models emit reasoning before tools)
124
- // We strip them to avoid regex confusion if they contain XML-like text
125
- contentToParse = contentToParse.replace(/<thought>[\s\S]*?<\/thought>/g, "");
126
- // 0b. Guard: Check if content is only section markers with nothing between them
127
- const onlyMarkersPattern = /^\s*<\|tool_calls_section_begin\|>\s*<\|tool_calls_section_end\|>\s*$/;
128
- if (onlyMarkersPattern.test(contentToParse)) {
129
- console.warn("[Marie] JsonUtils: Empty tool section markers detected");
130
- return null;
131
- }
132
- // 0c. NEW: Check for JSON tool_calls array format (e.g., from Claude)
133
- // Format: { "tool_calls": [{ "id": "...", "type": "write_file", "arguments": {...} }] }
134
- try {
135
- const jsonStart = contentToParse.indexOf("{");
136
- const jsonEnd = contentToParse.lastIndexOf("}");
137
- if (jsonStart !== -1 && jsonEnd > jsonStart) {
138
- const jsonCandidate = contentToParse.substring(jsonStart, jsonEnd + 1);
139
- const parsed = JSON.parse(jsonCandidate);
140
- if (parsed.tool_calls &&
141
- Array.isArray(parsed.tool_calls) &&
142
- parsed.tool_calls.length > 0) {
143
- const firstCall = parsed.tool_calls[0];
144
- if (firstCall.type && firstCall.arguments) {
145
- console.log("[Marie] JsonUtils: Found JSON tool_calls format");
146
- return {
147
- name: firstCall.type,
148
- input: firstCall.arguments,
149
- id: firstCall.id,
150
- repaired: true,
151
- };
152
- }
153
- }
154
- }
155
- }
156
- catch (e) {
157
- // Not valid JSON or not the right format, continue with other parsers
158
- }
159
- // 1. Check for Llama 3 / DeepSeek style tags first (more specific)
160
- const llamaMatch = contentToParse.match(/<(?:\|tool_call_begin\|>|\|tool_calls_section_begin\|>)\s*([\w.]+)(?::\d+)?(?:\s*<\|tool_call_arguments?_begin\|>)?\s*([\s\S]*?)(?:\s*(?:<\|tool_call_end\|>|<\|tool_calls_section_end\|>)|$)/);
161
- // Guard: Check for empty content between markers
162
- if (llamaMatch) {
163
- const capturedContent = llamaMatch[2];
164
- if (!capturedContent || !capturedContent.trim()) {
165
- console.warn("[Marie] JsonUtils: Tool section markers found but no content between them");
166
- return null;
167
- }
168
- toolName = llamaMatch[1].replace(/^functions\./, "");
169
- contentToParse = capturedContent;
170
- isLlama = true;
171
- }
172
- else {
173
- // 1b. Check for compact Llama 3 / OpenRouter format: tool_name:id>JSON or tool_name>JSON
174
- // This often appears in raw streams
175
- const compactMatch = contentToParse.match(/([\w.]+)(?::(\d+))?>(\{[\s\S]*?)(?:$)/);
176
- if (compactMatch) {
177
- toolName = compactMatch[1].replace(/^functions\./, "");
178
- const toolId = compactMatch[2] ? `call_${compactMatch[2]}` : undefined;
179
- contentToParse = compactMatch[3];
180
- const { repaired, wasFixed } = this.repairJsonDetailed(contentToParse);
181
- try {
182
- const parsed = JSON.parse(repaired);
93
+ catch (e) {
94
+ return { result: null, wasFixed: false };
95
+ }
96
+ }
97
+ /**
98
+ * Extracts a tool call from unstructured mixed markdown/JSON text.
99
+ * Prioritizes custom Llama 3 markers but falls back to markdown extraction and structural analysis.
100
+ */
101
+ export function extractToolCall(text) {
102
+ if (typeof text !== "string")
103
+ return null;
104
+ const trimmedText = text.trim();
105
+ if (!trimmedText || trimmedText.length < 5)
106
+ return null;
107
+ let contentToParse = text;
108
+ let isLlama = false;
109
+ let toolName = "";
110
+ // 0. Handle <thought> tags (some models emit reasoning before tools)
111
+ // We strip them to avoid regex confusion if they contain XML-like text
112
+ contentToParse = contentToParse.replace(/<thought>[\s\S]*?<\/thought>/g, "");
113
+ // 0b. Guard: Check if content is only section markers with nothing between them
114
+ const onlyMarkersPattern = /^\s*<\|tool_calls_section_begin\|>\s*<\|tool_calls_section_end\|>\s*$/;
115
+ if (onlyMarkersPattern.test(contentToParse)) {
116
+ console.warn("[Marie] JsonUtils: Empty tool section markers detected");
117
+ return null;
118
+ }
119
+ // 0c. NEW: Check for JSON tool_calls array format (e.g., from Claude)
120
+ // Format: { "tool_calls": [{ "id": "...", "type": "write_file", "arguments": {...} }] }
121
+ try {
122
+ const jsonStart = contentToParse.indexOf("{");
123
+ const jsonEnd = contentToParse.lastIndexOf("}");
124
+ if (jsonStart !== -1 && jsonEnd > jsonStart) {
125
+ const jsonCandidate = contentToParse.substring(jsonStart, jsonEnd + 1);
126
+ const parsed = JSON.parse(jsonCandidate);
127
+ if (parsed.tool_calls &&
128
+ Array.isArray(parsed.tool_calls) &&
129
+ parsed.tool_calls.length > 0) {
130
+ const firstCall = parsed.tool_calls[0];
131
+ if (firstCall.type && firstCall.arguments) {
132
+ console.log("[Marie] JsonUtils: Found JSON tool_calls format");
183
133
  return {
184
- name: toolName,
185
- input: parsed,
186
- id: toolId,
187
- repaired: wasFixed,
134
+ name: firstCall.type,
135
+ input: firstCall.arguments,
136
+ id: firstCall.id,
137
+ repaired: true,
188
138
  };
189
139
  }
190
- catch (e) {
191
- // Fall through if not valid JSON yet
192
- }
193
- }
194
- // 2. Check for DeepSeek style <tool_code>...</tool_code>
195
- const toolCodeMatch = contentToParse.match(/<tool_code>([\s\S]*?)(?:<\/tool_code>|$)/);
196
- if (toolCodeMatch) {
197
- contentToParse = toolCodeMatch[1];
198
- }
199
- else {
200
- // 3. Check for Qwen style <tool_call>...</tool_call>
201
- const toolCallTagMatch = contentToParse.match(/<tool_call>([\s\S]*?)(?:<\/tool_call>|$)/);
202
- if (toolCallTagMatch) {
203
- contentToParse = toolCallTagMatch[1];
204
- }
205
- else {
206
- // 4. Check for <tool>...</tool>
207
- const toolTagMatch = contentToParse.match(/<tool>([\s\S]*?)(?:<\/tool>|$)/);
208
- if (toolTagMatch) {
209
- contentToParse = toolTagMatch[1];
210
- }
211
- else {
212
- // 5. Check for XML style <invoke name="...">...</invoke>
213
- const invokeStartMatch = contentToParse.match(/<invoke\s+([^>]*?name=["']?([^"'\s>]+)["']?[^>]*?)>/i);
214
- if (invokeStartMatch) {
215
- toolName = invokeStartMatch[2];
216
- const invokeStartIdx = invokeStartMatch.index;
217
- const contentAfterStart = contentToParse.substring(invokeStartIdx + invokeStartMatch[0].length);
218
- // Find closing </invoke> (handling potential nesting)
219
- const invokeEndIdx = contentAfterStart.lastIndexOf("</invoke>");
220
- const innerContent = invokeEndIdx !== -1
221
- ? contentAfterStart.substring(0, invokeEndIdx)
222
- : contentAfterStart; // Auto-close if missing
223
- // Robust Parameter Extraction (Nesting-Aware)
224
- const params = {};
225
- const paramStartRegex = /<parameter\s+[^>]*?name=["']?([^"'\s>]+)["']?[^>]*?>/gi;
226
- let pMatch;
227
- while ((pMatch = paramStartRegex.exec(innerContent)) !== null) {
228
- const pName = pMatch[1];
229
- const pStartIdx = pMatch.index + pMatch[0].length;
230
- // Find corresponding </parameter>
231
- const nextEndIdx = innerContent.indexOf("</parameter>", pStartIdx);
232
- const pValueRaw = nextEndIdx !== -1
233
- ? innerContent.substring(pStartIdx, nextEndIdx)
234
- : innerContent.substring(pStartIdx); // Auto-close
235
- const pValue = pValueRaw.trim();
236
- // Basic level-1 JSON / Type parsing
237
- if (pValue.startsWith("{") || pValue.startsWith("[")) {
238
- try {
239
- // Attempt to repair and parse any block starting with { or [
240
- params[pName] = JSON.parse(this.repairJson(pValue));
241
- }
242
- catch (e) {
243
- params[pName] = pValue;
244
- }
245
- }
246
- else if (pValue.toLowerCase() === "true") {
247
- params[pName] = true;
248
- }
249
- else if (pValue.toLowerCase() === "false") {
250
- params[pName] = false;
251
- }
252
- else if (!isNaN(Number(pValue)) && pValue !== "") {
253
- params[pName] = Number(pValue);
254
- }
255
- else {
256
- params[pName] = pValue;
257
- }
258
- }
259
- if (Object.keys(params).length > 0) {
260
- return { name: toolName, input: params, repaired: true };
261
- }
262
- contentToParse = innerContent;
263
- }
264
- }
265
- }
266
140
  }
267
141
  }
268
- // 3. Clean markdown code blocks (handle truncated blocks too)
269
- const hasMarkdown = /```/.test(contentToParse);
270
- if (hasMarkdown) {
271
- contentToParse = contentToParse
272
- .replace(/```(?:json)?/g, "")
273
- .replace(/```/g, "");
274
- }
275
- // 4. Clean preamble/postamble (text outside the first { and last })
276
- const firstBrace = contentToParse.indexOf("{");
277
- const lastBrace = contentToParse.lastIndexOf("}");
278
- // NEW: Check for bracketed tool name format before the JSON: [Tool Use: name]
279
- const bracketMatch = contentToParse.match(/\[Tool Use:\s*([\w.]+)\s*\]/i);
280
- if (bracketMatch && !toolName) {
281
- toolName = bracketMatch[1];
282
- console.log(`[Marie] JsonUtils: Found bracketed tool name: ${toolName}`);
283
- }
284
- if (firstBrace === -1) {
285
- // If we have a tool name but no JSON, return it with empty input (might be a parameterless tool)
286
- if (toolName) {
287
- return { name: toolName, input: {}, repaired: true };
288
- }
142
+ }
143
+ catch (e) {
144
+ // Not valid JSON or not the right format, continue with other parsers
145
+ }
146
+ // 1. Check for Llama 3 / DeepSeek style tags first (more specific)
147
+ const llamaMatch = contentToParse.match(/<(?:\|tool_call_begin\|>|\|tool_calls_section_begin\|>)\s*([\w.]+)(?::\d+)?(?:\s*<\|tool_call_arguments?_begin\|>)?\s*([\s\S]*?)(?:\s*(?:<\|tool_call_end\|>|<\|tool_calls_section_end\|>)|$)/);
148
+ // Guard: Check for empty content between markers
149
+ if (llamaMatch) {
150
+ const capturedContent = llamaMatch[2];
151
+ if (!capturedContent || !capturedContent.trim()) {
152
+ console.warn("[Marie] JsonUtils: Tool section markers found but no content between them");
289
153
  return null;
290
154
  }
291
- const jsonCandidate = contentToParse.substring(firstBrace);
292
- try {
293
- const { repaired, wasFixed } = this.repairJsonDetailed(jsonCandidate);
294
- const parsed = JSON.parse(repaired);
295
- if (isLlama) {
155
+ toolName = llamaMatch[1].replace(/^functions\./, "");
156
+ contentToParse = capturedContent;
157
+ isLlama = true;
158
+ }
159
+ else {
160
+ // 1b. Check for compact Llama 3 / OpenRouter format: tool_name:id>JSON or tool_name>JSON
161
+ // This often appears in raw streams
162
+ const compactMatch = contentToParse.match(/([\w.]+)(?::(\d+))?>(\{[\s\S]*?)(?:$)/);
163
+ if (compactMatch) {
164
+ toolName = compactMatch[1].replace(/^functions\./, "");
165
+ const toolId = compactMatch[2] ? `call_${compactMatch[2]}` : undefined;
166
+ contentToParse = compactMatch[3];
167
+ const { repaired, wasFixed } = repairJsonDetailed(contentToParse);
168
+ try {
169
+ const parsed = JSON.parse(repaired);
296
170
  return {
297
171
  name: toolName,
298
172
  input: parsed,
299
- repaired: wasFixed || hasMarkdown,
173
+ id: toolId,
174
+ repaired: wasFixed,
300
175
  };
301
176
  }
302
- const normalized = this.normalizeToolCall(parsed);
303
- if (normalized) {
304
- // Heuristic: if we had to strip markdown or prefix, it was "repaired"
305
- normalized.repaired = wasFixed || hasMarkdown || firstBrace > 0;
306
- return normalized;
177
+ catch (e) {
178
+ // Fall through if not valid JSON yet
307
179
  }
308
- else if (toolName) {
309
- // Use the bracketed tool name if normalization failed (JSON is just arguments)
310
- return {
311
- name: toolName,
312
- input: parsed,
313
- repaired: wasFixed || hasMarkdown || firstBrace > 0,
314
- };
180
+ }
181
+ // 2. Check for DeepSeek style <tool_code>...</tool_code>
182
+ const toolCodeMatch = contentToParse.match(/<tool_code>([\s\S]*?)(?:<\/tool_code>|$)/);
183
+ if (toolCodeMatch) {
184
+ contentToParse = toolCodeMatch[1];
185
+ }
186
+ else {
187
+ // 3. Check for Qwen style <tool_call>...</tool_call>
188
+ const toolCallTagMatch = contentToParse.match(/<tool_call>([\s\S]*?)(?:<\/tool_call>|$)/);
189
+ if (toolCallTagMatch) {
190
+ contentToParse = toolCallTagMatch[1];
315
191
  }
316
192
  }
317
- catch (e) {
318
- // console.error("Failed to parse tool content:", e);
193
+ }
194
+ // 4. Try extracting from Markdown JSON blocks (e.g. ```json ... ```)
195
+ const jsonBlockMatch = contentToParse.match(/```(?:json)?\s*([\s\S]*?)```/);
196
+ const hasMarkdown = !!jsonBlockMatch && !isLlama;
197
+ if (jsonBlockMatch && !isLlama) {
198
+ contentToParse = jsonBlockMatch[1];
199
+ }
200
+ // Attempt to parse exactly what we've isolated first
201
+ let firstBrace = contentToParse.indexOf("{");
202
+ if (!isLlama && firstBrace === -1) {
203
+ // Very fallback: Check for loose JSON at the END of the message if no block found
204
+ // Model might just start spitting JSON halfway through
205
+ const fallbackBrace = contentToParse.lastIndexOf("{");
206
+ if (fallbackBrace !== -1) {
207
+ firstBrace = fallbackBrace;
208
+ }
209
+ }
210
+ // Sometimes models prefix the JSON with the tool name without markers
211
+ if (firstBrace > 0 && !isLlama) {
212
+ const prefix = contentToParse.substring(0, firstBrace).trim();
213
+ if (prefix && prefix.length < 50 && !prefix.includes(" ")) {
214
+ console.log(`[Marie] JsonUtils: Found bracketed tool name: ${toolName}`);
215
+ toolName = prefix;
216
+ }
217
+ }
218
+ if (firstBrace === -1) {
219
+ // If we have a tool name but no JSON, return it with empty input (might be a parameterless tool)
220
+ if (toolName) {
221
+ return { name: toolName, input: {}, repaired: true };
319
222
  }
320
223
  return null;
321
224
  }
322
- static normalizeToolCall(parsed) {
323
- // Handle different common formats
324
- if (parsed.tool_name && parsed.tool_input) {
225
+ const jsonCandidate = contentToParse.substring(firstBrace);
226
+ try {
227
+ const { repaired, wasFixed } = repairJsonDetailed(jsonCandidate);
228
+ const parsed = JSON.parse(repaired);
229
+ if (isLlama) {
325
230
  return {
326
- name: parsed.tool_name,
327
- input: parsed.tool_input,
328
- id: parsed.tool_id,
231
+ name: toolName,
232
+ input: parsed,
233
+ repaired: wasFixed || hasMarkdown,
329
234
  };
330
235
  }
331
- if (parsed.name && parsed.arguments) {
332
- return {
333
- name: parsed.name,
334
- input: typeof parsed.arguments === "string"
335
- ? this.safeParseJson(parsed.arguments)
336
- : parsed.arguments,
337
- id: parsed.id,
338
- };
236
+ const normalized = normalizeToolCall(parsed);
237
+ if (normalized) {
238
+ // Heuristic: if we had to strip markdown or prefix, it was "repaired"
239
+ normalized.repaired = wasFixed || hasMarkdown || firstBrace > 0;
240
+ return normalized;
339
241
  }
340
- if (parsed.name && parsed.input) {
242
+ else if (toolName) {
243
+ // Use the bracketed tool name if normalization failed (JSON is just arguments)
341
244
  return {
342
- name: parsed.name,
343
- input: parsed.input,
344
- id: parsed.id,
245
+ name: toolName,
246
+ input: parsed,
247
+ repaired: wasFixed || hasMarkdown || firstBrace > 0,
345
248
  };
346
249
  }
347
- // Literal format: { "write_file": { ... } }
348
- const keys = Object.keys(parsed);
250
+ }
251
+ catch (e) {
252
+ // console.error("Failed to parse tool content:", e);
253
+ }
254
+ return null;
255
+ }
256
+ function normalizeToolCall(parsed) {
257
+ if (!parsed || typeof parsed !== "object")
258
+ return null;
259
+ // Handle different common formats
260
+ if (parsed.tool_name && parsed.tool_input) {
261
+ return {
262
+ name: parsed.tool_name,
263
+ input: parsed.tool_input,
264
+ id: parsed.tool_id,
265
+ };
266
+ }
267
+ if (parsed.name && parsed.arguments) {
268
+ return {
269
+ name: parsed.name,
270
+ input: typeof parsed.arguments === "string"
271
+ ? safeParseJson(parsed.arguments).result
272
+ : parsed.arguments,
273
+ id: parsed.id,
274
+ };
275
+ }
276
+ if (parsed.name && parsed.input) {
277
+ return {
278
+ name: parsed.name,
279
+ input: parsed.input,
280
+ id: parsed.id,
281
+ };
282
+ }
283
+ // Literal format: { "write_file": { ... } }
284
+ try {
285
+ const keys = safeGetKeys(parsed, 10);
349
286
  if (keys.length === 1 &&
350
287
  typeof parsed[keys[0]] === "object" &&
288
+ parsed[keys[0]] !== null &&
351
289
  !Array.isArray(parsed[keys[0]])) {
352
290
  return {
353
291
  name: keys[0],
354
292
  input: parsed[keys[0]],
355
293
  };
356
294
  }
357
- return null;
358
295
  }
296
+ catch (e) {
297
+ // Ignore enumeration errors
298
+ }
299
+ return null;
359
300
  }
360
301
  //# sourceMappingURL=JsonUtils.js.map