ummaya 0.2.4 → 0.2.6

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 (482) hide show
  1. package/README.md +15 -2
  2. package/bin/ummaya +10 -1
  3. package/bun.lock +180 -244
  4. package/npm-shrinkwrap.json +760 -1760
  5. package/package.json +39 -22
  6. package/prompts/manifest.yaml +1 -1
  7. package/prompts/system_v1.md +1 -0
  8. package/pyproject.toml +27 -2
  9. package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
  10. package/src/ummaya/_canonical/__init__.py +2 -0
  11. package/src/ummaya/_canonical/baselines.yaml +113 -0
  12. package/src/ummaya/engine/engine.py +29 -132
  13. package/src/ummaya/evidence/__init__.py +21 -2
  14. package/src/ummaya/evidence/dataset_contract.py +193 -0
  15. package/src/ummaya/evidence/document_authoring_cases.py +33 -0
  16. package/src/ummaya/evidence/document_harness.py +313 -0
  17. package/src/ummaya/evidence/document_viewer_ux.py +391 -0
  18. package/src/ummaya/evidence/gates.py +70 -0
  19. package/src/ummaya/evidence/json_types.py +20 -0
  20. package/src/ummaya/evidence/models.py +88 -1
  21. package/src/ummaya/evidence/output_payload.py +89 -0
  22. package/src/ummaya/evidence/payload_documents.py +233 -0
  23. package/src/ummaya/evidence/route_contracts.py +224 -0
  24. package/src/ummaya/evidence/route_helpers.py +150 -0
  25. package/src/ummaya/evidence/runner.py +81 -212
  26. package/src/ummaya/evidence/source_provenance.py +246 -0
  27. package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
  28. package/src/ummaya/evidence/tool_layer.py +39 -0
  29. package/src/ummaya/evidence/tool_layer_models.py +151 -0
  30. package/src/ummaya/ipc/adapter_manifest_emitter.py +26 -10
  31. package/src/ummaya/ipc/document_intent_normalization.py +185 -0
  32. package/src/ummaya/ipc/frame_schema.py +5 -5
  33. package/src/ummaya/ipc/route_diagnostics.py +73 -0
  34. package/src/ummaya/ipc/stdio.py +1109 -477
  35. package/src/ummaya/llm/client.py +102 -3
  36. package/src/ummaya/llm/config.py +8 -3
  37. package/src/ummaya/primitives/__init__.py +6 -2
  38. package/src/ummaya/primitives/delegation.py +1 -1
  39. package/src/ummaya/primitives/document.py +28 -0
  40. package/src/ummaya/settings.py +0 -3
  41. package/src/ummaya/tools/discovery_bridge.py +17 -1
  42. package/src/ummaya/tools/documents/__init__.py +297 -0
  43. package/src/ummaya/tools/documents/adapter_registry.py +487 -0
  44. package/src/ummaya/tools/documents/archive_container_probe.py +167 -0
  45. package/src/ummaya/tools/documents/artifact_store.py +454 -0
  46. package/src/ummaya/tools/documents/authoring.py +283 -0
  47. package/src/ummaya/tools/documents/baselines.py +132 -0
  48. package/src/ummaya/tools/documents/capability.py +331 -0
  49. package/src/ummaya/tools/documents/contracts.py +112 -0
  50. package/src/ummaya/tools/documents/conversion.py +521 -0
  51. package/src/ummaya/tools/documents/diff.py +275 -0
  52. package/src/ummaya/tools/documents/engines.py +163 -0
  53. package/src/ummaya/tools/documents/evaluation.py +291 -0
  54. package/src/ummaya/tools/documents/explicit_values.py +108 -0
  55. package/src/ummaya/tools/documents/fixtures.py +174 -0
  56. package/src/ummaya/tools/documents/format_completion_audit.py +471 -0
  57. package/src/ummaya/tools/documents/formats/__init__.py +2 -0
  58. package/src/ummaya/tools/documents/formats/archive.py +528 -0
  59. package/src/ummaya/tools/documents/formats/base.py +41 -0
  60. package/src/ummaya/tools/documents/formats/code_file.py +211 -0
  61. package/src/ummaya/tools/documents/formats/data_file.py +272 -0
  62. package/src/ummaya/tools/documents/formats/hwp.py +284 -0
  63. package/src/ummaya/tools/documents/formats/hwpx.py +1837 -0
  64. package/src/ummaya/tools/documents/formats/odf.py +435 -0
  65. package/src/ummaya/tools/documents/formats/ooxml.py +1030 -0
  66. package/src/ummaya/tools/documents/formats/passive.py +766 -0
  67. package/src/ummaya/tools/documents/formats/pdf.py +702 -0
  68. package/src/ummaya/tools/documents/formats/text_web.py +268 -0
  69. package/src/ummaya/tools/documents/hwp_conversion_probe.py +178 -0
  70. package/src/ummaya/tools/documents/hwp_direct_candidate.py +141 -0
  71. package/src/ummaya/tools/documents/inspection.py +289 -0
  72. package/src/ummaya/tools/documents/intake.py +1079 -0
  73. package/src/ummaya/tools/documents/legacy_office_promotion_probe.py +366 -0
  74. package/src/ummaya/tools/documents/models.py +1598 -0
  75. package/src/ummaya/tools/documents/odf_promotion_probe.py +167 -0
  76. package/src/ummaya/tools/documents/orchestrator.py +96 -0
  77. package/src/ummaya/tools/documents/passive_capability_probe.py +251 -0
  78. package/src/ummaya/tools/documents/patch.py +170 -0
  79. package/src/ummaya/tools/documents/pdfa_conformance.py +284 -0
  80. package/src/ummaya/tools/documents/pdfa_promotion_probe.py +198 -0
  81. package/src/ummaya/tools/documents/permissions.py +110 -0
  82. package/src/ummaya/tools/documents/planner.py +616 -0
  83. package/src/ummaya/tools/documents/registry.py +2733 -0
  84. package/src/ummaya/tools/documents/render.py +978 -0
  85. package/src/ummaya/tools/documents/render_comparison.py +113 -0
  86. package/src/ummaya/tools/documents/render_comparison_models.py +74 -0
  87. package/src/ummaya/tools/documents/render_comparison_regions.py +73 -0
  88. package/src/ummaya/tools/documents/render_comparison_style.py +161 -0
  89. package/src/ummaya/tools/documents/reread.py +157 -0
  90. package/src/ummaya/tools/documents/runtime_authoring.py +244 -0
  91. package/src/ummaya/tools/documents/runtime_authoring_bundle.py +76 -0
  92. package/src/ummaya/tools/documents/scorecard.py +184 -0
  93. package/src/ummaya/tools/documents/socratic_planner.py +193 -0
  94. package/src/ummaya/tools/documents/style.py +48 -0
  95. package/src/ummaya/tools/documents/tool_defs.py +523 -0
  96. package/src/ummaya/tools/documents/validate.py +347 -0
  97. package/src/ummaya/tools/executor.py +29 -0
  98. package/src/ummaya/tools/live_proxy.py +0 -3
  99. package/src/ummaya/tools/models.py +5 -1
  100. package/src/ummaya/tools/register_all.py +8 -0
  101. package/src/ummaya/tools/registry.py +10 -1
  102. package/src/ummaya/tools/routing/__init__.py +59 -0
  103. package/src/ummaya/tools/routing/builder.py +105 -0
  104. package/src/ummaya/tools/routing/cards.py +29 -0
  105. package/src/ummaya/tools/routing/decision_service.py +534 -0
  106. package/src/ummaya/tools/routing/decision_types.py +74 -0
  107. package/src/ummaya/tools/routing/feasibility.py +122 -0
  108. package/src/ummaya/tools/routing/intent.py +17 -0
  109. package/src/ummaya/tools/routing/intent_extractor.py +207 -0
  110. package/src/ummaya/tools/routing/intent_patterns.py +160 -0
  111. package/src/ummaya/tools/routing/intent_public_data.py +150 -0
  112. package/src/ummaya/tools/routing/intent_types.py +48 -0
  113. package/src/ummaya/tools/routing/lint.py +78 -0
  114. package/src/ummaya/tools/routing/metadata.py +174 -0
  115. package/src/ummaya/tools/routing/projection.py +340 -0
  116. package/src/ummaya/tools/routing/retrieval_policy.py +629 -0
  117. package/src/ummaya/tools/routing/schema.py +81 -0
  118. package/src/ummaya/tools/routing/types.py +96 -0
  119. package/src/ummaya/tools/routing_index.py +2 -2
  120. package/src/ummaya/tools/search.py +34 -746
  121. package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
  122. package/tui/bun.lock +126 -305
  123. package/tui/package.json +35 -22
  124. package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
  125. package/tui/src/QueryEngine.ts +12 -8
  126. package/tui/src/bridge/inboundAttachments.ts +3 -3
  127. package/tui/src/cli/handlers/auth.ts +3 -12
  128. package/tui/src/cli/handlers/mcp.tsx +0 -1
  129. package/tui/src/cli/print.ts +8 -9
  130. package/tui/src/commands/insights.ts +1 -1
  131. package/tui/src/commands/install-github-app/types.ts +8 -30
  132. package/tui/src/commands/plugin/types.ts +6 -28
  133. package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
  134. package/tui/src/commands/rename/generateSessionName.ts +1 -1
  135. package/tui/src/components/Feedback.tsx +1 -1
  136. package/tui/src/components/LogoV2/EmergencyTip.tsx +11 -2
  137. package/tui/src/components/LogoV2/WelcomeV2.tsx +1 -3
  138. package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
  139. package/tui/src/components/Spinner/types.ts +6 -28
  140. package/tui/src/components/agents/generateAgent.ts +1 -1
  141. package/tui/src/components/agents/new-agent-creation/types.ts +4 -26
  142. package/tui/src/components/config/EnvSecretIsolatedEditor.tsx +1 -1
  143. package/tui/src/components/mcp/types.ts +16 -38
  144. package/tui/src/components/messages/AssistantToolUseMessage.tsx +3 -2
  145. package/tui/src/components/messages/UserCrossSessionMessage.ts +16 -4
  146. package/tui/src/components/messages/UserForkBoilerplateMessage.ts +16 -4
  147. package/tui/src/components/messages/UserGitHubWebhookMessage.ts +16 -4
  148. package/tui/src/components/messages/UserToolResultMessage/utils.tsx +3 -2
  149. package/tui/src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.ts +9 -4
  150. package/tui/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.ts +9 -4
  151. package/tui/src/components/primitive/DocumentSocraticReviewBlock.tsx +129 -0
  152. package/tui/src/components/primitive/DocumentToolResultCard.tsx +224 -0
  153. package/tui/src/components/primitive/documentSocraticReview.ts +215 -0
  154. package/tui/src/components/primitive/index.tsx +43 -1
  155. package/tui/src/components/primitive/types.ts +137 -0
  156. package/tui/src/components/ui/option.ts +4 -26
  157. package/tui/src/constants/common.ts +0 -2
  158. package/tui/src/constants/prompts.ts +4 -3
  159. package/tui/src/constants/querySource.ts +4 -26
  160. package/tui/src/entrypoints/sdk/controlTypes.ts +26 -48
  161. package/tui/src/entrypoints/sdk/coreTypes.generated.ts +3 -25
  162. package/tui/src/entrypoints/sdk/runtimeTypes.ts +38 -60
  163. package/tui/src/entrypoints/sdk/sdkUtilityTypes.ts +4 -26
  164. package/tui/src/entrypoints/sdk/settingsTypes.generated.ts +3 -25
  165. package/tui/src/entrypoints/sdk/toolTypes.ts +3 -25
  166. package/tui/src/hooks/toolPermission/handlers/interactiveHandler.ts +10 -0
  167. package/tui/src/hooks/useApiKeyVerification.ts +1 -1
  168. package/tui/src/hooks/useVirtualScroll.ts +1 -1
  169. package/tui/src/ink/ink.tsx +33 -14
  170. package/tui/src/ink/reconciler.ts +2 -3
  171. package/tui/src/ink/render-to-screen.ts +30 -10
  172. package/tui/src/ipc/bridge.ts +62 -15
  173. package/tui/src/ipc/bridgeSingleton.ts +5 -1
  174. package/tui/src/ipc/codec.ts +3 -3
  175. package/tui/src/ipc/frames.generated.ts +12 -12
  176. package/tui/src/ipc/llmClient.ts +151 -27
  177. package/tui/src/ipc/schema/frame.schema.json +1 -1
  178. package/tui/src/keybindings/defaultBindings.ts +4 -0
  179. package/tui/src/main.tsx +32 -15
  180. package/tui/src/native-ts/file-index/index.ts +33 -3
  181. package/tui/src/observability/surface.ts +2 -2
  182. package/tui/src/probes/toolRegistryProbe.tsx +3 -1
  183. package/tui/src/projectOnboardingState.ts +7 -6
  184. package/tui/src/query/chatMessageTypes.ts +18 -0
  185. package/tui/src/query/chatMessagesBuilder.ts +1 -1
  186. package/tui/src/query/deps.ts +1 -1
  187. package/tui/src/query/messageGuards.ts +106 -0
  188. package/tui/src/query/publicDataTerminalRepair.ts +384 -0
  189. package/tui/src/query/run.ts +1075 -0
  190. package/tui/src/query/supportBoundary.ts +168 -0
  191. package/tui/src/query/toolResultErrors.ts +103 -0
  192. package/tui/src/query/toolRunner.ts +687 -0
  193. package/tui/src/query/unavailableToolRepair.ts +118 -0
  194. package/tui/src/query.ts +9 -2186
  195. package/tui/src/screens/REPL.tsx +40 -29
  196. package/tui/src/services/api/adapterManifest.ts +4 -0
  197. package/tui/src/services/api/backendChat/events.ts +117 -0
  198. package/tui/src/services/api/backendChat/finalMessage.ts +40 -0
  199. package/tui/src/services/api/backendChat/frame.ts +9 -0
  200. package/tui/src/services/api/backendChat/streaming.ts +430 -0
  201. package/tui/src/services/api/backendChat/types.ts +62 -0
  202. package/tui/src/services/api/backendChat.ts +1 -0
  203. package/tui/src/services/api/client.ts +65 -2
  204. package/tui/src/services/api/errorUtils.ts +5 -5
  205. package/tui/src/services/api/errors.ts +1 -1
  206. package/tui/src/services/api/logging.ts +1 -1
  207. package/tui/src/services/api/ummaya/evidence.ts +194 -0
  208. package/tui/src/services/api/ummaya/messages.ts +255 -0
  209. package/tui/src/services/api/ummaya/nonStreaming.ts +66 -0
  210. package/tui/src/services/api/ummaya/provider.ts +200 -0
  211. package/tui/src/services/api/ummaya/reasoning.ts +24 -0
  212. package/tui/src/services/api/ummaya/request.ts +200 -0
  213. package/tui/src/services/api/ummaya/selectionContext.ts +240 -0
  214. package/tui/src/services/api/ummaya/streaming.ts +365 -0
  215. package/tui/src/services/api/ummaya/streamingPayload.ts +129 -0
  216. package/tui/src/services/api/ummaya/streamingReader.ts +40 -0
  217. package/tui/src/services/api/ummaya/toolSelection.ts +217 -0
  218. package/tui/src/services/api/ummaya/types.ts +110 -0
  219. package/tui/src/services/api/ummaya/usage.ts +30 -0
  220. package/tui/src/services/api/ummaya.ts +26 -418
  221. package/tui/src/services/api/withRetry.ts +1 -1
  222. package/tui/src/services/awaySummary.ts +2 -2
  223. package/tui/src/services/claudeAiLimits.ts +1 -1
  224. package/tui/src/services/compact/autoCompact.ts +1 -1
  225. package/tui/src/services/compact/compact.ts +1 -1
  226. package/tui/src/services/lsp/types.ts +8 -30
  227. package/tui/src/services/tips/types.ts +6 -28
  228. package/tui/src/services/tokenEstimation.ts +1 -1
  229. package/tui/src/services/toolRegistry/bootGuard.ts +5 -5
  230. package/tui/src/services/toolUseSummary/toolUseSummaryGenerator.ts +1 -1
  231. package/tui/src/services/tools/toolExecution.ts +94 -1
  232. package/tui/src/store/pendingPermissionSlot.ts +1 -1
  233. package/tui/src/store/session-store.ts +10 -36
  234. package/tui/src/stubs/any-stub.ts +15 -10
  235. package/tui/src/stubs/color-diff-napi.ts +37 -23
  236. package/tui/src/stubs/globals.d.ts +3 -3
  237. package/tui/src/stubs/macro-preload.ts +23 -12
  238. package/tui/src/tools/AdapterTool/AdapterTool.ts +1207 -714
  239. package/tui/src/tools/AdapterTool/routeDiagnostics.ts +75 -0
  240. package/tui/src/tools/AgentTool/AgentTool.tsx +84 -1371
  241. package/tui/src/tools/AgentTool/agentToolHandoff.ts +114 -0
  242. package/tui/src/tools/AgentTool/agentToolPartialResult.ts +16 -0
  243. package/tui/src/tools/AgentTool/agentToolProgress.ts +32 -0
  244. package/tui/src/tools/AgentTool/agentToolResolver.ts +161 -0
  245. package/tui/src/tools/AgentTool/agentToolResult.ts +163 -0
  246. package/tui/src/tools/AgentTool/agentToolUtils.ts +14 -686
  247. package/tui/src/tools/AgentTool/asyncAgentLifecycle.ts +208 -0
  248. package/tui/src/tools/AgentTool/asyncLifecycle.ts +153 -0
  249. package/tui/src/tools/AgentTool/backgroundedCompletion.ts +126 -0
  250. package/tui/src/tools/AgentTool/backgroundedLifecycle.ts +174 -0
  251. package/tui/src/tools/AgentTool/foregroundBackground.ts +83 -0
  252. package/tui/src/tools/AgentTool/foregroundDrain.tsx +133 -0
  253. package/tui/src/tools/AgentTool/foregroundFinalize.ts +98 -0
  254. package/tui/src/tools/AgentTool/foregroundLifecycle.tsx +237 -0
  255. package/tui/src/tools/AgentTool/foregroundProgress.tsx +169 -0
  256. package/tui/src/tools/AgentTool/foregroundTask.ts +89 -0
  257. package/tui/src/tools/AgentTool/forkSubagent.ts +1 -12
  258. package/tui/src/tools/AgentTool/forkSubagentGate.ts +34 -0
  259. package/tui/src/tools/AgentTool/launchRouting.ts +203 -0
  260. package/tui/src/tools/AgentTool/lifecycle.ts +244 -0
  261. package/tui/src/tools/AgentTool/mcpRouting.ts +73 -0
  262. package/tui/src/tools/AgentTool/orchestrationSupport.ts +70 -0
  263. package/tui/src/tools/AgentTool/permissions.ts +39 -0
  264. package/tui/src/tools/AgentTool/promptSetup.ts +181 -0
  265. package/tui/src/tools/AgentTool/remoteRouting.ts +62 -0
  266. package/tui/src/tools/AgentTool/resultMapping.ts +116 -0
  267. package/tui/src/tools/AgentTool/resumeAgent.ts +39 -107
  268. package/tui/src/tools/AgentTool/resumeAgentHelpers.ts +140 -0
  269. package/tui/src/tools/AgentTool/runAgent.ts +1 -1
  270. package/tui/src/tools/AgentTool/runtimeConfig.ts +57 -0
  271. package/tui/src/tools/AgentTool/schemas.ts +196 -0
  272. package/tui/src/tools/AgentTool/sourceVerificationPropagation.ts +263 -0
  273. package/tui/src/tools/AgentTool/worktreeLifecycle.ts +105 -0
  274. package/tui/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +174 -202
  275. package/tui/src/tools/BashTool/BashTool.tsx +71 -1072
  276. package/tui/src/tools/BashTool/bashCommandHelpers.ts +12 -12
  277. package/tui/src/tools/BashTool/bashPermissions/astPreflight.ts +173 -0
  278. package/tui/src/tools/BashTool/bashPermissions/classifierChecks.ts +199 -0
  279. package/tui/src/tools/BashTool/bashPermissions/compoundGuards.ts +53 -0
  280. package/tui/src/tools/BashTool/bashPermissions/constants.ts +99 -0
  281. package/tui/src/tools/BashTool/bashPermissions/index.ts +38 -0
  282. package/tui/src/tools/BashTool/bashPermissions/legacyMisparsing.ts +62 -0
  283. package/tui/src/tools/BashTool/bashPermissions/main.ts +135 -0
  284. package/tui/src/tools/BashTool/bashPermissions/normalizedCommands.ts +33 -0
  285. package/tui/src/tools/BashTool/bashPermissions/operatorFlow.ts +98 -0
  286. package/tui/src/tools/BashTool/bashPermissions/permissionChecks.ts +200 -0
  287. package/tui/src/tools/BashTool/bashPermissions/prefixSuggestions.ts +88 -0
  288. package/tui/src/tools/BashTool/bashPermissions/promptClassifierRules.ts +125 -0
  289. package/tui/src/tools/BashTool/bashPermissions/ruleDelegates.ts +19 -0
  290. package/tui/src/tools/BashTool/bashPermissions/ruleMatching.ts +145 -0
  291. package/tui/src/tools/BashTool/bashPermissions/sandboxAutoAllow.ts +75 -0
  292. package/tui/src/tools/BashTool/bashPermissions/subcommandFlow.ts +205 -0
  293. package/tui/src/tools/BashTool/bashPermissions/subcommandGuards.ts +73 -0
  294. package/tui/src/tools/BashTool/bashPermissions/subcommandResultHelpers.ts +116 -0
  295. package/tui/src/tools/BashTool/bashPermissions/types.ts +26 -0
  296. package/tui/src/tools/BashTool/bashPermissions/wrapperStripping.ts +139 -0
  297. package/tui/src/tools/BashTool/bashPermissions.ts +26 -2621
  298. package/tui/src/tools/BashTool/call.ts +202 -0
  299. package/tui/src/tools/BashTool/callLoader.ts +35 -0
  300. package/tui/src/tools/BashTool/commandClassification.ts +151 -0
  301. package/tui/src/tools/BashTool/commandClassificationLoader.ts +40 -0
  302. package/tui/src/tools/BashTool/cwdReset.ts +33 -0
  303. package/tui/src/tools/BashTool/lineTruncation.ts +11 -0
  304. package/tui/src/tools/BashTool/modeValidation.ts +13 -1
  305. package/tui/src/tools/BashTool/outputPersistence.ts +42 -0
  306. package/tui/src/tools/BashTool/permissionClassification.ts +66 -0
  307. package/tui/src/tools/BashTool/permissionLoader.ts +44 -0
  308. package/tui/src/tools/BashTool/resultLoader.ts +29 -0
  309. package/tui/src/tools/BashTool/resultMapping.ts +83 -0
  310. package/tui/src/tools/BashTool/sandboxPolicy.ts +79 -0
  311. package/tui/src/tools/BashTool/schemas.ts +65 -0
  312. package/tui/src/tools/BashTool/sedEditExecution.ts +59 -0
  313. package/tui/src/tools/BashTool/shellExecution.tsx +245 -0
  314. package/tui/src/tools/BashTool/shellOutputUtils.ts +85 -0
  315. package/tui/src/tools/BashTool/shellPermissionGauntlet.ts +97 -0
  316. package/tui/src/tools/BashTool/uiLoader.ts +37 -0
  317. package/tui/src/tools/BriefTool/upload.ts +1 -1
  318. package/tui/src/tools/CalculatorTool/parser.ts +2 -2
  319. package/tui/src/tools/DocumentPrimitive/DocumentPrimitive.ts +262 -0
  320. package/tui/src/tools/DocumentPrimitive/dispatchNormalization.ts +270 -0
  321. package/tui/src/tools/DocumentPrimitive/documentDestinationPath.ts +18 -0
  322. package/tui/src/tools/DocumentPrimitive/documentMutationGuard.ts +22 -0
  323. package/tui/src/tools/DocumentPrimitive/documentPatchNormalization.ts +248 -0
  324. package/tui/src/tools/DocumentPrimitive/documentSourceVerification.ts +245 -0
  325. package/tui/src/tools/DocumentPrimitive/documentSourceVerificationFields.ts +103 -0
  326. package/tui/src/tools/DocumentPrimitive/modelVisibleOutput.ts +40 -0
  327. package/tui/src/tools/DocumentPrimitive/prompt.ts +35 -0
  328. package/tui/src/tools/FileEditTool/FileEditTool.ts +9 -507
  329. package/tui/src/tools/FileEditTool/call.ts +228 -0
  330. package/tui/src/tools/FileEditTool/validateInput.ts +196 -0
  331. package/tui/src/tools/FileReadTool/imageProcessor.ts +13 -0
  332. package/tui/src/tools/FileWriteTool/FileWriteTool.ts +7 -300
  333. package/tui/src/tools/FileWriteTool/call.ts +223 -0
  334. package/tui/src/tools/FileWriteTool/validateInput.ts +80 -0
  335. package/tui/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +19 -3
  336. package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +25 -32
  337. package/tui/src/tools/LookupPrimitive/prompt.ts +0 -2
  338. package/tui/src/tools/MCPTool/trustPolicy.ts +118 -0
  339. package/tui/src/tools/McpAuthTool/McpAuthTool.ts +21 -3
  340. package/tui/src/tools/NotebookEditTool/NotebookEditTool.ts +7 -326
  341. package/tui/src/tools/NotebookEditTool/call.ts +254 -0
  342. package/tui/src/tools/NotebookEditTool/notebookModel.ts +51 -0
  343. package/tui/src/tools/NotebookEditTool/validateInput.ts +142 -0
  344. package/tui/src/tools/PowerShellTool/PowerShellTool.tsx +46 -937
  345. package/tui/src/tools/PowerShellTool/acceptEditsCommandValidation.ts +162 -0
  346. package/tui/src/tools/PowerShellTool/call.ts +179 -0
  347. package/tui/src/tools/PowerShellTool/callLoader.ts +37 -0
  348. package/tui/src/tools/PowerShellTool/commandClassification.ts +86 -0
  349. package/tui/src/tools/PowerShellTool/modeValidation.ts +25 -332
  350. package/tui/src/tools/PowerShellTool/outputPersistence.ts +42 -0
  351. package/tui/src/tools/PowerShellTool/permissionClassification.ts +28 -0
  352. package/tui/src/tools/PowerShellTool/resultLoader.ts +31 -0
  353. package/tui/src/tools/PowerShellTool/resultMapping.ts +75 -0
  354. package/tui/src/tools/PowerShellTool/schemas.ts +40 -0
  355. package/tui/src/tools/PowerShellTool/shellExecution.tsx +258 -0
  356. package/tui/src/tools/PowerShellTool/symlinkModeValidation.ts +44 -0
  357. package/tui/src/tools/PowerShellTool/uiLoader.ts +37 -0
  358. package/tui/src/tools/PowerShellTool/validation.ts +39 -0
  359. package/tui/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +19 -3
  360. package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +1 -11
  361. package/tui/src/tools/ResolveLocationPrimitive/prompt.ts +2 -6
  362. package/tui/src/tools/SkillTool/SkillTool.ts +2 -2
  363. package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +27 -10
  364. package/tui/src/tools/TaskCreateTool/TaskCreateTool.ts +16 -2
  365. package/tui/src/tools/TaskGetTool/TaskGetTool.ts +23 -3
  366. package/tui/src/tools/TaskListTool/TaskListTool.ts +22 -4
  367. package/tui/src/tools/TaskOutputTool/TaskOutputTool.tsx +46 -547
  368. package/tui/src/tools/TaskOutputTool/lookup.ts +216 -0
  369. package/tui/src/tools/TaskOutputTool/render.tsx +257 -0
  370. package/tui/src/tools/TaskOutputTool/schemas.ts +55 -0
  371. package/tui/src/tools/TaskOutputTool/serialization.ts +36 -0
  372. package/tui/src/tools/TaskStopTool/TaskStopTool.ts +10 -0
  373. package/tui/src/tools/TaskUpdateTool/TaskUpdateTool.ts +14 -364
  374. package/tui/src/tools/TaskUpdateTool/completion.ts +62 -0
  375. package/tui/src/tools/TaskUpdateTool/schemas.ts +62 -0
  376. package/tui/src/tools/TaskUpdateTool/serialization.ts +46 -0
  377. package/tui/src/tools/TaskUpdateTool/statusUpdate.ts +247 -0
  378. package/tui/src/tools/TodoWriteTool/TodoWriteTool.ts +21 -2
  379. package/tui/src/tools/ToolSearchTool/ToolSearchTool.ts +21 -302
  380. package/tui/src/tools/ToolSearchTool/ccSupportTools.ts +223 -0
  381. package/tui/src/tools/ToolSearchTool/descriptionCache.ts +50 -0
  382. package/tui/src/tools/ToolSearchTool/keywordSearch.ts +216 -0
  383. package/tui/src/tools/ToolSearchTool/prompt.ts +10 -4
  384. package/tui/src/tools/ToolSearchTool/resultMapping.ts +30 -0
  385. package/tui/src/tools/ToolSearchTool/schemas.ts +30 -0
  386. package/tui/src/tools/ToolSearchTool/searchPool.ts +47 -0
  387. package/tui/src/tools/ToolSearchTool/supportIntentHints.ts +140 -0
  388. package/tui/src/tools/TranslateTool/TranslateTool.ts +1 -1
  389. package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +2 -1
  390. package/tui/src/tools/WebFetchTool/WebFetchTool.ts +43 -138
  391. package/tui/src/tools/WebFetchTool/call.ts +227 -0
  392. package/tui/src/tools/WebFetchTool/resolvedAddressSafety.ts +78 -0
  393. package/tui/src/tools/WebFetchTool/sourceVerification.ts +204 -0
  394. package/tui/src/tools/WebFetchTool/types.ts +23 -0
  395. package/tui/src/tools/WebFetchTool/urlSafety.ts +181 -0
  396. package/tui/src/tools/WebFetchTool/utils.ts +1 -1
  397. package/tui/src/tools/WebSearchTool/UI.tsx +0 -1
  398. package/tui/src/tools/WebSearchTool/WebSearchTool.ts +9 -313
  399. package/tui/src/tools/WebSearchTool/call.ts +33 -0
  400. package/tui/src/tools/WebSearchTool/responseMapping.ts +190 -0
  401. package/tui/src/tools/WebSearchTool/resultBlock.ts +47 -0
  402. package/tui/src/tools/WebSearchTool/schemas.ts +47 -0
  403. package/tui/src/tools/WebSearchTool/toolSchema.ts +12 -0
  404. package/tui/src/tools/WorkspaceToolAdapter/WorkspaceToolAdapter.ts +79 -0
  405. package/tui/src/tools/WorkspaceToolAdapter/allowedRootPolicy.ts +85 -0
  406. package/tui/src/tools/WorkspaceToolAdapter/documentFormatGuards.ts +73 -0
  407. package/tui/src/tools/WorkspaceToolAdapter/inputNormalization.ts +105 -0
  408. package/tui/src/tools/WorkspaceToolAdapter/mcpExposurePolicy.ts +64 -0
  409. package/tui/src/tools/WorkspaceToolAdapter/toolDefFactory.ts +215 -0
  410. package/tui/src/tools/WorkspaceToolAdapter/toolNames.ts +6 -0
  411. package/tui/src/tools/WorkspaceToolAdapter/workspacePolicy.ts +15 -0
  412. package/tui/src/tools/_shared/dispatchPrimitive.ts +6 -6
  413. package/tui/src/tools/_shared/documentChangeToPatch.ts +125 -0
  414. package/tui/src/tools/_shared/documentDispatchArguments.ts +87 -0
  415. package/tui/src/tools/_shared/documentPrimitiveTimeout.ts +13 -0
  416. package/tui/src/tools/_shared/documentToolResultRender.ts +98 -0
  417. package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
  418. package/tui/src/tools/_shared/rootPrimitiveInput.ts +1 -0
  419. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPatterns.ts +58 -0
  420. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPrompt.ts +271 -0
  421. package/tui/src/tools/_shared/toolChoiceRepair/documentRepair.ts +452 -0
  422. package/tui/src/tools/_shared/toolChoiceRepair/messageAccess.ts +80 -0
  423. package/tui/src/tools/_shared/toolChoiceRepair/publicDataRepair.ts +92 -0
  424. package/tui/src/tools/_shared/toolChoiceRepair/supportRepair.ts +135 -0
  425. package/tui/src/tools/_shared/toolChoiceRepair.ts +55 -860
  426. package/tui/src/tools/shared/mockDisclaimer.ts +1 -1
  427. package/tui/src/tools.ts +39 -190
  428. package/tui/src/types/fileSuggestion.ts +4 -26
  429. package/tui/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +186 -148
  430. package/tui/src/types/generated/events_mono/common/v1/auth.ts +25 -11
  431. package/tui/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +47 -30
  432. package/tui/src/types/generated/google/protobuf/timestamp.ts +21 -7
  433. package/tui/src/types/message.ts +80 -102
  434. package/tui/src/types/messageQueueTypes.ts +6 -28
  435. package/tui/src/types/notebook.ts +16 -38
  436. package/tui/src/types/statusLine.ts +4 -26
  437. package/tui/src/types/tools.ts +24 -46
  438. package/tui/src/types/utils.ts +6 -28
  439. package/tui/src/upstreamproxy/relay.ts +7 -3
  440. package/tui/src/upstreamproxy/upstreamproxy.ts +1 -1
  441. package/tui/src/utils/assistantMessageFactories.ts +9 -3
  442. package/tui/src/utils/auth.ts +129 -139
  443. package/tui/src/utils/bash/ast.ts +23 -23
  444. package/tui/src/utils/bash/bashParser.ts +5 -5
  445. package/tui/src/utils/billing.ts +1 -1
  446. package/tui/src/utils/claudeDesktop.ts +4 -4
  447. package/tui/src/utils/collapseReadSearch.ts +3 -3
  448. package/tui/src/utils/cronTasks.ts +1 -1
  449. package/tui/src/utils/execFileNoThrow.ts +1 -1
  450. package/tui/src/utils/filePersistence/types.ts +16 -38
  451. package/tui/src/utils/forkedAgent.ts +1 -1
  452. package/tui/src/utils/gracefulShutdown.ts +4 -4
  453. package/tui/src/utils/heapDumpService.ts +12 -8
  454. package/tui/src/utils/hooks/apiQueryHookHelper.ts +1 -1
  455. package/tui/src/utils/hooks/execPromptHook.ts +1 -1
  456. package/tui/src/utils/hooks/skillImprovement.ts +1 -1
  457. package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
  458. package/tui/src/utils/messages.ts +18 -0
  459. package/tui/src/utils/migrateSessions.ts +3 -3
  460. package/tui/src/utils/model/model.ts +6 -6
  461. package/tui/src/utils/permissions/yoloClassifier.ts +1 -1
  462. package/tui/src/utils/plugins/headlessPluginInstall.ts +1 -1
  463. package/tui/src/utils/plugins/mcpPluginIntegration.ts +1 -1
  464. package/tui/src/utils/plugins/mcpbHandler.ts +1 -1
  465. package/tui/src/utils/plugins/pluginLoader.ts +8 -8
  466. package/tui/src/utils/protectedNamespace.ts +5 -3
  467. package/tui/src/utils/rawJsonToolCall.ts +242 -0
  468. package/tui/src/utils/ripgrep.ts +16 -7
  469. package/tui/src/utils/sessionTitle.ts +1 -1
  470. package/tui/src/utils/settings/permissionValidation.ts +14 -2
  471. package/tui/src/utils/shell/prefix.ts +1 -1
  472. package/tui/src/utils/sideQuery.ts +1 -1
  473. package/tui/src/utils/systemThemeWatcher.ts +13 -3
  474. package/tui/src/utils/teleport.tsx +1 -1
  475. package/uv.lock +426 -45
  476. package/tui/src/services/api/claude.ts +0 -3540
  477. package/tui/src/tools/_shared/directPublicDataGuard.ts +0 -362
  478. package/tui/src/tools/_shared/kmaAnalysisGuard.ts +0 -197
  479. package/tui/src/tools/_shared/kmaAviationGuard.ts +0 -70
  480. package/tui/src/tools/_shared/nmcAedGuard.ts +0 -234
  481. package/tui/src/tools/_shared/protectedCheckGuard.ts +0 -207
  482. package/tui/src/tools/_shared/textToolCallGuard.ts +0 -91
@@ -28,11 +28,11 @@ import {
28
28
  } from '../../services/api/adapterManifest.js'
29
29
  import { FIND_TOOL_NAME, DESCRIPTION, FIND_TOOL_PROMPT } from './prompt.js'
30
30
  import { dispatchPrimitive } from '../_shared/dispatchPrimitive.js'
31
- import { validateKmaAviationToolChoice } from '../_shared/kmaAviationGuard.js'
32
- import { validateKmaAnalysisToolChoice } from '../_shared/kmaAnalysisGuard.js'
33
- import { validateNmcAedToolChoice } from '../_shared/nmcAedGuard.js'
34
- import { validateProtectedCheckToolChoice } from '../_shared/protectedCheckGuard.js'
35
- import { validateDirectPublicDataToolChoice } from '../_shared/directPublicDataGuard.js'
31
+ import {
32
+ applyDocumentVisualRenderGateToOutput,
33
+ isDocumentVisualRenderFailedOutput,
34
+ renderDocumentToolResultIfPresent,
35
+ } from '../_shared/documentToolResultRender.js'
36
36
  import {
37
37
  renderVerboseInputJson,
38
38
  renderVerboseOutputJson,
@@ -631,26 +631,27 @@ export const LookupPrimitive = buildTool({
631
631
  },
632
632
 
633
633
  mapToolResultToToolResultBlockParam(output, toolUseID) {
634
+ const gatedOutput = applyDocumentVisualRenderGateToOutput(output)
634
635
  // Spec 2521 (2026-05-02) — strip ``outbound_traces`` from the
635
636
  // LLM-facing content. The trace is UI-only (verbose render);
636
637
  // shipping raw HTTP bodies back into the next turn would bloat
637
638
  // K-EXAONE's context with KMA/data.go.kr response payloads.
638
639
  const llmContent =
639
- typeof output === 'object' && output !== null
640
+ typeof gatedOutput === 'object' && gatedOutput !== null
640
641
  ? Object.fromEntries(
641
- Object.entries(output as Record<string, unknown>).filter(
642
+ Object.entries(gatedOutput as Record<string, unknown>).filter(
642
643
  ([k]) => k !== 'outbound_traces',
643
644
  ),
644
645
  )
645
- : output
646
+ : gatedOutput
646
647
  return {
647
648
  tool_use_id: toolUseID,
648
649
  type: 'tool_result',
649
650
  content: JSON.stringify(llmContent),
651
+ ...(isDocumentVisualRenderFailedOutput(gatedOutput) ? { is_error: true } : {}),
650
652
  }
651
653
  },
652
654
 
653
- // UMMAYA hotfix #2518 follow-up — CC pattern (tools/BashTool/UI.tsx:renderToolUseMessage).
654
655
  // Return an args preview so the citizen can see which tool was dispatched.
655
656
  // Spec 2521 (2026-05-01) — fetch-only surface; legacy mode='search'
656
657
  // payloads from older sessions surface as the bare tool_id.
@@ -697,21 +698,6 @@ export const LookupPrimitive = buildTool({
697
698
  }
698
699
  }
699
700
 
700
- const protectedChoice = validateProtectedCheckToolChoice(input.tool_id, context)
701
- if (protectedChoice) return protectedChoice
702
- const directPublicDataChoice = validateDirectPublicDataToolChoice(
703
- input.tool_id,
704
- context,
705
- input.params,
706
- )
707
- if (directPublicDataChoice) return directPublicDataChoice
708
- const kmaAviationChoice = validateKmaAviationToolChoice(input.tool_id, context)
709
- if (kmaAviationChoice) return kmaAviationChoice
710
- const kmaAnalysisChoice = validateKmaAnalysisToolChoice(input.tool_id, context)
711
- if (kmaAnalysisChoice) return kmaAnalysisChoice
712
- const nmcAedChoice = validateNmcAedToolChoice(input.tool_id, context)
713
- if (nmcAedChoice) return nmcAedChoice
714
-
715
701
  // Tier 1 — synced backend manifest (FR-017).
716
702
  if (isManifestSynced()) {
717
703
  const backendEntry = resolveAdapter(input.tool_id)
@@ -787,14 +773,17 @@ export const LookupPrimitive = buildTool({
787
773
  _progress: unknown,
788
774
  options: { verbose: boolean; isTranscriptMode?: boolean } = { verbose: false },
789
775
  ): React.ReactNode {
776
+ const gatedOutput = applyDocumentVisualRenderGateToOutput(output) as Output
777
+ const documentResult = renderDocumentToolResultIfPresent(gatedOutput, options)
778
+ if (documentResult !== null) {
779
+ return documentResult
780
+ }
790
781
  // Spec 2521 (2026-05-01 evening) — Ctrl+O expand / transcript mode
791
782
  // surfaces the full envelope JSON the backend returned. Mirrors
792
783
  // CC BashTool/UI.tsx:renderToolResultMessage(verbose).
793
784
  if (options.verbose || options.isTranscriptMode) {
794
- return renderVerboseOutputJson(output)
785
+ return renderVerboseOutputJson(gatedOutput)
795
786
  }
796
- // UMMAYA hotfix #2519 (CC-original migration, 2026-04-30):
797
- //
798
787
  // After the dispatchPrimitive register-and-await rewrite, output.result
799
788
  // is the actual primitive output (the inner of the backend envelope:
800
789
  // src/ummaya/tools/find.py LookupSearchResult / LookupRecord /
@@ -802,8 +791,8 @@ export const LookupPrimitive = buildTool({
802
791
  // its own `kind` field. The CC pattern wraps each branch in
803
792
  // <MessageResponse> so the " ⎿ " gutter glyph prefixes every row
804
793
  // (tui/src/components/MessageResponse.tsx:22).
805
- if (!output.ok) {
806
- const message = extractErrorMessage(output.error.message)
794
+ if (!gatedOutput.ok) {
795
+ const message = extractErrorMessage(gatedOutput.error.message)
807
796
  return React.createElement(
808
797
  MessageResponse,
809
798
  null,
@@ -815,7 +804,7 @@ export const LookupPrimitive = buildTool({
815
804
  )
816
805
  }
817
806
 
818
- const result = output.result as Record<string, unknown>
807
+ const result = gatedOutput.result as Record<string, unknown>
819
808
 
820
809
  // search mode (LookupSearchResult, models.py:820):
821
810
  // { kind: "search", candidates: [AdapterCandidate], total_registry_size, effective_top_k, reason }
@@ -872,12 +861,16 @@ export const LookupPrimitive = buildTool({
872
861
  * validateInput has already resolved the adapter and populated ummayaCitations on the context.
873
862
  */
874
863
  async call(input, context) {
875
- return dispatchPrimitive<Output>({
864
+ const result = await dispatchPrimitive<Output>({
876
865
  primitive: 'find',
877
- args: input as Record<string, unknown>,
866
+ args: input,
878
867
  context,
879
868
  registry: getOrCreatePendingCallRegistry(),
880
869
  bridge: getOrCreateUmmayaBridge(),
881
870
  })
871
+ return {
872
+ ...result,
873
+ data: applyDocumentVisualRenderGateToOutput(result.data) as Output,
874
+ }
882
875
  },
883
876
  } satisfies ToolDef<InputSchema, Output>)
@@ -17,7 +17,6 @@ export const FIND_TOOL_PROMPT = `Discover Korean public-service lookup adapters
17
17
 
18
18
  Preferred path:
19
19
  - Call concrete adapter functions directly after their schemas are loaded.
20
- - Example: kma_current_observation({ base_date: "YYYYMMDD", base_time: "HH00", nx: 97, ny: 74 })
21
20
  - Adapter ids and schemas are progressively disclosed by ToolSearch, adapter_manifest, or backend top-K retrieval for the current citizen request.
22
21
  - Only top candidates should be loaded; do not expect every adapter schema in the prompt.
23
22
  - A concrete adapter id may be valid even when it is not listed in <available-deferred-tools>; if it appears in <available_adapters> or adapter_manifest and its function is loaded, call that function directly with its schema fields.
@@ -26,7 +25,6 @@ Legacy root wrapper:
26
25
  - If a concrete adapter function is not loaded and only the root primitive is available, find accepts { tool_id, params } for old transcripts and compatibility paths.
27
26
  - tool_id must be a concrete adapter id from <available_adapters>, never "find", "locate", "check", or "send".
28
27
  - Invalid: find({ tool_id: "find", params: {...} })
29
- - Compatibility-only: find({ tool_id: "kma_apihub_url_air_metar_decoded", params: { org: "K", help: 1 } })
30
28
 
31
29
  Rules:
32
30
  - Do not call find with mode='search' or query; discovery is handled outside the primitive call.
@@ -0,0 +1,118 @@
1
+ import { mcpInfoFromString } from '../../services/mcp/mcpStringUtils.js'
2
+ import type { ToolPermissionContext, ToolUseContext } from '../../Tool.js'
3
+ import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
4
+ import {
5
+ getAllowRules,
6
+ getDenyRuleForTool,
7
+ } from '../../utils/permissions/permissions.js'
8
+
9
+ const TRUSTED_BUILTIN_MCP_SERVERS = ['ummaya'] as const
10
+
11
+ type McpTrustInput = { readonly [key: string]: unknown }
12
+
13
+ function mcpServerRuleName(serverName: string): string {
14
+ return `mcp__${serverName}`
15
+ }
16
+
17
+ function hasExactServerAllowRule(
18
+ permissionContext: ToolPermissionContext,
19
+ serverName: string,
20
+ ): boolean {
21
+ return getAllowRules(permissionContext).some(rule => {
22
+ if (rule.ruleValue.ruleContent !== undefined) return false
23
+ const ruleInfo = mcpInfoFromString(rule.ruleValue.toolName)
24
+ if (ruleInfo === null) return false
25
+ return (
26
+ ruleInfo.serverName === serverName &&
27
+ (ruleInfo.toolName === undefined || ruleInfo.toolName === '*')
28
+ )
29
+ })
30
+ }
31
+
32
+ function hasServerDenyRule(
33
+ permissionContext: ToolPermissionContext,
34
+ serverName: string,
35
+ ): boolean {
36
+ return (
37
+ getDenyRuleForTool(permissionContext, {
38
+ name: mcpServerRuleName(serverName),
39
+ mcpInfo: { serverName, toolName: '*' },
40
+ }) !== null
41
+ )
42
+ }
43
+
44
+ export function isTrustedMcpServer(
45
+ permissionContext: ToolPermissionContext,
46
+ serverName: string,
47
+ ): boolean {
48
+ if (hasServerDenyRule(permissionContext, serverName)) return false
49
+ if (TRUSTED_BUILTIN_MCP_SERVERS.some(name => name === serverName)) return true
50
+ return hasExactServerAllowRule(permissionContext, serverName)
51
+ }
52
+
53
+ export function assertNonEmptyMcpServerName(serverName: string): void {
54
+ if (serverName.trim().length === 0) {
55
+ throw new Error('MCP server name cannot be empty')
56
+ }
57
+ }
58
+
59
+ export function assertNonEmptyMcpResourceUri(uri: string): void {
60
+ if (uri.trim().length === 0) {
61
+ throw new Error('MCP resource URI cannot be empty')
62
+ }
63
+ }
64
+
65
+ export function assertTrustedMcpServerForResourceAccess(
66
+ permissionContext: ToolPermissionContext,
67
+ serverName: string,
68
+ ): void {
69
+ assertTrustedMcpServer(
70
+ permissionContext,
71
+ serverName,
72
+ `Server "${serverName}" is not trusted for MCP resource access`,
73
+ )
74
+ }
75
+
76
+ export function assertTrustedMcpServer(
77
+ permissionContext: ToolPermissionContext,
78
+ serverName: string,
79
+ message: string,
80
+ ): void {
81
+ assertNonEmptyMcpServerName(serverName)
82
+ if (!isTrustedMcpServer(permissionContext, serverName)) {
83
+ throw new Error(message)
84
+ }
85
+ }
86
+
87
+ export function createMcpServerTrustSuggestion(serverName: string) {
88
+ return {
89
+ type: 'addRules' as const,
90
+ rules: [
91
+ {
92
+ toolName: mcpServerRuleName(serverName),
93
+ ruleContent: undefined,
94
+ },
95
+ ],
96
+ behavior: 'allow' as const,
97
+ destination: 'localSettings' as const,
98
+ }
99
+ }
100
+
101
+ export function checkMcpServerTrustPermission<Input extends McpTrustInput>(
102
+ serverName: string,
103
+ input: Input,
104
+ context: ToolUseContext,
105
+ message: string,
106
+ ): PermissionResult<Input> {
107
+ assertNonEmptyMcpServerName(serverName)
108
+ if (
109
+ isTrustedMcpServer(context.getAppState().toolPermissionContext, serverName)
110
+ ) {
111
+ return { behavior: 'allow', updatedInput: input }
112
+ }
113
+ return {
114
+ behavior: 'passthrough',
115
+ message,
116
+ suggestions: [createMcpServerTrustSuggestion(serverName)],
117
+ }
118
+ }
@@ -18,7 +18,11 @@ import type { Tool } from '../../Tool.js'
18
18
  import { errorMessage } from '../../utils/errors.js'
19
19
  import { lazySchema } from '../../utils/lazySchema.js'
20
20
  import { logMCPDebug, logMCPError } from '../../utils/log.js'
21
- import type { PermissionDecision } from '../../utils/permissions/PermissionResult.js'
21
+ import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
22
+ import {
23
+ assertTrustedMcpServer,
24
+ checkMcpServerTrustPermission,
25
+ } from '../MCPTool/trustPolicy.js'
22
26
 
23
27
  const inputSchema = lazySchema(() => z.object({}))
24
28
  type InputSchema = ReturnType<typeof inputSchema>
@@ -79,10 +83,21 @@ export function createMcpAuthTool(
79
83
  get inputSchema(): InputSchema {
80
84
  return inputSchema()
81
85
  },
82
- async checkPermissions(input): Promise<PermissionDecision> {
83
- return { behavior: 'allow', updatedInput: input }
86
+ async checkPermissions(input, context): Promise<PermissionResult> {
87
+ return checkMcpServerTrustPermission(
88
+ serverName,
89
+ input,
90
+ context,
91
+ `MCP server "${serverName}" requires trust before authentication can run.`,
92
+ )
84
93
  },
85
94
  async call(_input, context) {
95
+ assertTrustedMcpServer(
96
+ context.getAppState().toolPermissionContext,
97
+ serverName,
98
+ `MCP server "${serverName}" requires trust before authentication can run.`,
99
+ )
100
+
86
101
  // claude.ai connectors use a separate auth flow (handleClaudeAIAuth in
87
102
  // MCPRemoteServerMenu) that we don't invoke programmatically here —
88
103
  // just point the user at /mcp.
@@ -196,6 +211,9 @@ export function createMcpAuthTool(
196
211
  },
197
212
  }
198
213
  } catch (err) {
214
+ if (!(err instanceof Error)) {
215
+ throw err
216
+ }
199
217
  return {
200
218
  data: {
201
219
  status: 'error' as const,
@@ -1,22 +1,10 @@
1
1
  import { feature } from 'bun:bundle'
2
- import { extname, isAbsolute, resolve } from 'path'
3
- import {
4
- fileHistoryEnabled,
5
- fileHistoryTrackEdit,
6
- } from 'src/utils/fileHistory.js'
7
2
  import { z } from 'zod/v4'
8
- import { buildTool, type ToolDef, type ToolUseContext } from '../../Tool.js'
9
- import type { NotebookCell, NotebookContent } from '../../types/notebook.js'
10
- import { getCwd } from '../../utils/cwd.js'
11
- import { isENOENT } from '../../utils/errors.js'
12
- import { getFileModificationTime, writeTextContent } from '../../utils/file.js'
13
- import { readFileSyncWithMetadata } from '../../utils/fileRead.js'
14
- import { safeParseJSON } from '../../utils/json.js'
3
+ import { buildTool, type ToolDef } from '../../Tool.js'
15
4
  import { lazySchema } from '../../utils/lazySchema.js'
16
- import { parseCellId } from '../../utils/notebook.js'
17
5
  import { checkWritePermissionForTool } from '../../utils/permissions/filesystem.js'
18
6
  import type { PermissionDecision } from '../../utils/permissions/PermissionResult.js'
19
- import { jsonParse, jsonStringify } from '../../utils/slowOperations.js'
7
+ import { callNotebookEditTool } from './call.js'
20
8
  import { NOTEBOOK_EDIT_TOOL_NAME } from './constants.js'
21
9
  import { DESCRIPTION, PROMPT } from './prompt.js'
22
10
  import {
@@ -26,6 +14,7 @@ import {
26
14
  renderToolUseMessage,
27
15
  renderToolUseRejectedMessage,
28
16
  } from './UI.js'
17
+ import { validateNotebookEditInput } from './validateInput.js'
29
18
 
30
19
  export const inputSchema = lazySchema(() =>
31
20
  z.strictObject({
@@ -173,318 +162,10 @@ export const NotebookEditTool = buildTool({
173
162
  renderToolUseRejectedMessage,
174
163
  renderToolUseErrorMessage,
175
164
  renderToolResultMessage,
176
- async validateInput(
177
- { notebook_path, cell_type, cell_id, edit_mode = 'replace' },
178
- toolUseContext: ToolUseContext,
179
- ) {
180
- const fullPath = isAbsolute(notebook_path)
181
- ? notebook_path
182
- : resolve(getCwd(), notebook_path)
183
-
184
- // SECURITY: Skip filesystem operations for UNC paths to prevent NTLM credential leaks.
185
- if (fullPath.startsWith('\\\\') || fullPath.startsWith('//')) {
186
- return { result: true }
187
- }
188
-
189
- if (extname(fullPath) !== '.ipynb') {
190
- return {
191
- result: false,
192
- message:
193
- 'File must be a Jupyter notebook (.ipynb file). For editing other file types, use the FileEdit tool.',
194
- errorCode: 2,
195
- }
196
- }
197
-
198
- if (
199
- edit_mode !== 'replace' &&
200
- edit_mode !== 'insert' &&
201
- edit_mode !== 'delete'
202
- ) {
203
- return {
204
- result: false,
205
- message: 'Edit mode must be replace, insert, or delete.',
206
- errorCode: 4,
207
- }
208
- }
209
-
210
- if (edit_mode === 'insert' && !cell_type) {
211
- return {
212
- result: false,
213
- message: 'Cell type is required when using edit_mode=insert.',
214
- errorCode: 5,
215
- }
216
- }
217
-
218
- // Require Read-before-Edit (matches FileEditTool/FileWriteTool). Without
219
- // this, the model could edit a notebook it never saw, or edit against a
220
- // stale view after an external change — silent data loss.
221
- const readTimestamp = toolUseContext.readFileState.get(fullPath)
222
- if (!readTimestamp) {
223
- return {
224
- result: false,
225
- message:
226
- 'File has not been read yet. Read it first before writing to it.',
227
- errorCode: 9,
228
- }
229
- }
230
- if (getFileModificationTime(fullPath) > readTimestamp.timestamp) {
231
- return {
232
- result: false,
233
- message:
234
- 'File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.',
235
- errorCode: 10,
236
- }
237
- }
238
-
239
- let content: string
240
- try {
241
- content = readFileSyncWithMetadata(fullPath).content
242
- } catch (e) {
243
- if (isENOENT(e)) {
244
- return {
245
- result: false,
246
- message: 'Notebook file does not exist.',
247
- errorCode: 1,
248
- }
249
- }
250
- throw e
251
- }
252
- const notebook = safeParseJSON(content) as NotebookContent | null
253
- if (!notebook) {
254
- return {
255
- result: false,
256
- message: 'Notebook is not valid JSON.',
257
- errorCode: 6,
258
- }
259
- }
260
- if (!cell_id) {
261
- if (edit_mode !== 'insert') {
262
- return {
263
- result: false,
264
- message: 'Cell ID must be specified when not inserting a new cell.',
265
- errorCode: 7,
266
- }
267
- }
268
- } else {
269
- // First try to find the cell by its actual ID
270
- const cellIndex = notebook.cells.findIndex(cell => cell.id === cell_id)
271
-
272
- if (cellIndex === -1) {
273
- // If not found, try to parse as a numeric index (cell-N format)
274
- const parsedCellIndex = parseCellId(cell_id)
275
- if (parsedCellIndex !== undefined) {
276
- if (!notebook.cells[parsedCellIndex]) {
277
- return {
278
- result: false,
279
- message: `Cell with index ${parsedCellIndex} does not exist in notebook.`,
280
- errorCode: 7,
281
- }
282
- }
283
- } else {
284
- return {
285
- result: false,
286
- message: `Cell with ID "${cell_id}" not found in notebook.`,
287
- errorCode: 8,
288
- }
289
- }
290
- }
291
- }
292
-
293
- return { result: true }
165
+ async validateInput(input, toolUseContext) {
166
+ return validateNotebookEditInput(input, toolUseContext)
294
167
  },
295
- async call(
296
- {
297
- notebook_path,
298
- new_source,
299
- cell_id,
300
- cell_type,
301
- edit_mode: originalEditMode,
302
- },
303
- { readFileState, updateFileHistoryState },
304
- _,
305
- parentMessage,
306
- ) {
307
- const fullPath = isAbsolute(notebook_path)
308
- ? notebook_path
309
- : resolve(getCwd(), notebook_path)
310
-
311
- if (fileHistoryEnabled()) {
312
- await fileHistoryTrackEdit(
313
- updateFileHistoryState,
314
- fullPath,
315
- parentMessage.uuid,
316
- )
317
- }
318
-
319
- try {
320
- // readFileSyncWithMetadata gives content + encoding + line endings in
321
- // one safeResolvePath + readFileSync pass, replacing the previous
322
- // detectFileEncoding + readFile + detectLineEndings chain (each of
323
- // which redid safeResolvePath and/or a 4KB readSync).
324
- const { content, encoding, lineEndings } =
325
- readFileSyncWithMetadata(fullPath)
326
- // Must use non-memoized jsonParse here: safeParseJSON caches by content
327
- // string and returns a shared object reference, but we mutate the
328
- // notebook in place below (cells.splice, targetCell.source = ...).
329
- // Using the memoized version poisons the cache for validateInput() and
330
- // any subsequent call() with the same file content.
331
- let notebook: NotebookContent
332
- try {
333
- notebook = jsonParse(content) as NotebookContent
334
- } catch {
335
- return {
336
- data: {
337
- new_source,
338
- cell_type: cell_type ?? 'code',
339
- language: 'python',
340
- edit_mode: 'replace',
341
- error: 'Notebook is not valid JSON.',
342
- cell_id,
343
- notebook_path: fullPath,
344
- original_file: '',
345
- updated_file: '',
346
- },
347
- }
348
- }
349
-
350
- let cellIndex
351
- if (!cell_id) {
352
- cellIndex = 0 // Default to inserting at the beginning if no cell_id is provided
353
- } else {
354
- // First try to find the cell by its actual ID
355
- cellIndex = notebook.cells.findIndex(cell => cell.id === cell_id)
356
-
357
- // If not found, try to parse as a numeric index (cell-N format)
358
- if (cellIndex === -1) {
359
- const parsedCellIndex = parseCellId(cell_id)
360
- if (parsedCellIndex !== undefined) {
361
- cellIndex = parsedCellIndex
362
- }
363
- }
364
-
365
- if (originalEditMode === 'insert') {
366
- cellIndex += 1 // Insert after the cell with this ID
367
- }
368
- }
369
-
370
- // Convert replace to insert if trying to replace one past the end
371
- let edit_mode = originalEditMode
372
- if (edit_mode === 'replace' && cellIndex === notebook.cells.length) {
373
- edit_mode = 'insert'
374
- if (!cell_type) {
375
- cell_type = 'code' // Default to code if no cell_type specified
376
- }
377
- }
378
-
379
- const language = notebook.metadata.language_info?.name ?? 'python'
380
- let new_cell_id = undefined
381
- if (
382
- notebook.nbformat > 4 ||
383
- (notebook.nbformat === 4 && notebook.nbformat_minor >= 5)
384
- ) {
385
- if (edit_mode === 'insert') {
386
- new_cell_id = Math.random().toString(36).substring(2, 15)
387
- } else if (cell_id !== null) {
388
- new_cell_id = cell_id
389
- }
390
- }
391
-
392
- if (edit_mode === 'delete') {
393
- // Delete the specified cell
394
- notebook.cells.splice(cellIndex, 1)
395
- } else if (edit_mode === 'insert') {
396
- let new_cell: NotebookCell
397
- if (cell_type === 'markdown') {
398
- new_cell = {
399
- cell_type: 'markdown',
400
- id: new_cell_id,
401
- source: new_source,
402
- metadata: {},
403
- }
404
- } else {
405
- new_cell = {
406
- cell_type: 'code',
407
- id: new_cell_id,
408
- source: new_source,
409
- metadata: {},
410
- execution_count: null,
411
- outputs: [],
412
- }
413
- }
414
- // Insert the new cell
415
- notebook.cells.splice(cellIndex, 0, new_cell)
416
- } else {
417
- // Find the specified cell
418
- const targetCell = notebook.cells[cellIndex]! // validateInput ensures cell_number is in bounds
419
- targetCell.source = new_source
420
- if (targetCell.cell_type === 'code') {
421
- // Reset execution count and clear outputs since cell was modified
422
- targetCell.execution_count = null
423
- targetCell.outputs = []
424
- }
425
- if (cell_type && cell_type !== targetCell.cell_type) {
426
- targetCell.cell_type = cell_type
427
- }
428
- }
429
- // Write back to file
430
- const IPYNB_INDENT = 1
431
- const updatedContent = jsonStringify(notebook, null, IPYNB_INDENT)
432
- writeTextContent(fullPath, updatedContent, encoding, lineEndings)
433
- // Update readFileState with post-write mtime (matches FileEditTool/
434
- // FileWriteTool). offset:undefined breaks FileReadTool's dedup match —
435
- // without this, Read→NotebookEdit→Read in the same millisecond would
436
- // return the file_unchanged stub against stale in-context content.
437
- readFileState.set(fullPath, {
438
- content: updatedContent,
439
- timestamp: getFileModificationTime(fullPath),
440
- offset: undefined,
441
- limit: undefined,
442
- })
443
- const data = {
444
- new_source,
445
- cell_type: cell_type ?? 'code',
446
- language,
447
- edit_mode: edit_mode ?? 'replace',
448
- cell_id: new_cell_id || undefined,
449
- error: '',
450
- notebook_path: fullPath,
451
- original_file: content,
452
- updated_file: updatedContent,
453
- }
454
- return {
455
- data,
456
- }
457
- } catch (error) {
458
- if (error instanceof Error) {
459
- const data = {
460
- new_source,
461
- cell_type: cell_type ?? 'code',
462
- language: 'python',
463
- edit_mode: 'replace',
464
- error: error.message,
465
- cell_id,
466
- notebook_path: fullPath,
467
- original_file: '',
468
- updated_file: '',
469
- }
470
- return {
471
- data,
472
- }
473
- }
474
- const data = {
475
- new_source,
476
- cell_type: cell_type ?? 'code',
477
- language: 'python',
478
- edit_mode: 'replace',
479
- error: 'Unknown error occurred while editing notebook',
480
- cell_id,
481
- notebook_path: fullPath,
482
- original_file: '',
483
- updated_file: '',
484
- }
485
- return {
486
- data,
487
- }
488
- }
168
+ async call(input, context, _, parentMessage) {
169
+ return callNotebookEditTool(input, context, parentMessage)
489
170
  },
490
171
  } satisfies ToolDef<InputSchema, Output>)