ummaya 0.2.4 → 0.2.5

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 (477) hide show
  1. package/README.md +15 -2
  2. package/bin/ummaya +10 -1
  3. package/npm-shrinkwrap.json +253 -2
  4. package/package.json +5 -1
  5. package/prompts/manifest.yaml +1 -1
  6. package/prompts/system_v1.md +1 -0
  7. package/pyproject.toml +26 -2
  8. package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
  9. package/src/ummaya/_canonical/__init__.py +2 -0
  10. package/src/ummaya/engine/engine.py +29 -132
  11. package/src/ummaya/evidence/__init__.py +21 -2
  12. package/src/ummaya/evidence/dataset_contract.py +193 -0
  13. package/src/ummaya/evidence/document_authoring_cases.py +33 -0
  14. package/src/ummaya/evidence/document_harness.py +313 -0
  15. package/src/ummaya/evidence/document_viewer_ux.py +391 -0
  16. package/src/ummaya/evidence/gates.py +70 -0
  17. package/src/ummaya/evidence/json_types.py +20 -0
  18. package/src/ummaya/evidence/models.py +88 -1
  19. package/src/ummaya/evidence/output_payload.py +89 -0
  20. package/src/ummaya/evidence/payload_documents.py +233 -0
  21. package/src/ummaya/evidence/route_contracts.py +224 -0
  22. package/src/ummaya/evidence/route_helpers.py +150 -0
  23. package/src/ummaya/evidence/runner.py +81 -212
  24. package/src/ummaya/evidence/source_provenance.py +246 -0
  25. package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
  26. package/src/ummaya/evidence/tool_layer.py +39 -0
  27. package/src/ummaya/evidence/tool_layer_models.py +151 -0
  28. package/src/ummaya/ipc/adapter_manifest_emitter.py +26 -10
  29. package/src/ummaya/ipc/document_intent_normalization.py +185 -0
  30. package/src/ummaya/ipc/frame_schema.py +5 -5
  31. package/src/ummaya/ipc/route_diagnostics.py +73 -0
  32. package/src/ummaya/ipc/stdio.py +1109 -477
  33. package/src/ummaya/llm/client.py +102 -3
  34. package/src/ummaya/llm/config.py +8 -3
  35. package/src/ummaya/primitives/__init__.py +6 -2
  36. package/src/ummaya/primitives/delegation.py +1 -1
  37. package/src/ummaya/primitives/document.py +28 -0
  38. package/src/ummaya/settings.py +0 -3
  39. package/src/ummaya/tools/discovery_bridge.py +17 -1
  40. package/src/ummaya/tools/documents/__init__.py +297 -0
  41. package/src/ummaya/tools/documents/adapter_registry.py +487 -0
  42. package/src/ummaya/tools/documents/archive_container_probe.py +167 -0
  43. package/src/ummaya/tools/documents/artifact_store.py +454 -0
  44. package/src/ummaya/tools/documents/authoring.py +283 -0
  45. package/src/ummaya/tools/documents/baselines.py +114 -0
  46. package/src/ummaya/tools/documents/capability.py +331 -0
  47. package/src/ummaya/tools/documents/contracts.py +112 -0
  48. package/src/ummaya/tools/documents/conversion.py +521 -0
  49. package/src/ummaya/tools/documents/diff.py +275 -0
  50. package/src/ummaya/tools/documents/engines.py +163 -0
  51. package/src/ummaya/tools/documents/evaluation.py +291 -0
  52. package/src/ummaya/tools/documents/explicit_values.py +108 -0
  53. package/src/ummaya/tools/documents/fixtures.py +174 -0
  54. package/src/ummaya/tools/documents/format_completion_audit.py +471 -0
  55. package/src/ummaya/tools/documents/formats/__init__.py +2 -0
  56. package/src/ummaya/tools/documents/formats/archive.py +528 -0
  57. package/src/ummaya/tools/documents/formats/base.py +41 -0
  58. package/src/ummaya/tools/documents/formats/code_file.py +211 -0
  59. package/src/ummaya/tools/documents/formats/data_file.py +272 -0
  60. package/src/ummaya/tools/documents/formats/hwp.py +284 -0
  61. package/src/ummaya/tools/documents/formats/hwpx.py +1837 -0
  62. package/src/ummaya/tools/documents/formats/odf.py +435 -0
  63. package/src/ummaya/tools/documents/formats/ooxml.py +1030 -0
  64. package/src/ummaya/tools/documents/formats/passive.py +766 -0
  65. package/src/ummaya/tools/documents/formats/pdf.py +702 -0
  66. package/src/ummaya/tools/documents/formats/text_web.py +268 -0
  67. package/src/ummaya/tools/documents/hwp_conversion_probe.py +178 -0
  68. package/src/ummaya/tools/documents/hwp_direct_candidate.py +141 -0
  69. package/src/ummaya/tools/documents/inspection.py +289 -0
  70. package/src/ummaya/tools/documents/intake.py +1079 -0
  71. package/src/ummaya/tools/documents/legacy_office_promotion_probe.py +366 -0
  72. package/src/ummaya/tools/documents/models.py +1598 -0
  73. package/src/ummaya/tools/documents/odf_promotion_probe.py +167 -0
  74. package/src/ummaya/tools/documents/orchestrator.py +96 -0
  75. package/src/ummaya/tools/documents/passive_capability_probe.py +251 -0
  76. package/src/ummaya/tools/documents/patch.py +170 -0
  77. package/src/ummaya/tools/documents/pdfa_conformance.py +284 -0
  78. package/src/ummaya/tools/documents/pdfa_promotion_probe.py +198 -0
  79. package/src/ummaya/tools/documents/permissions.py +110 -0
  80. package/src/ummaya/tools/documents/planner.py +616 -0
  81. package/src/ummaya/tools/documents/registry.py +2733 -0
  82. package/src/ummaya/tools/documents/render.py +978 -0
  83. package/src/ummaya/tools/documents/render_comparison.py +113 -0
  84. package/src/ummaya/tools/documents/render_comparison_models.py +74 -0
  85. package/src/ummaya/tools/documents/render_comparison_regions.py +73 -0
  86. package/src/ummaya/tools/documents/render_comparison_style.py +161 -0
  87. package/src/ummaya/tools/documents/reread.py +157 -0
  88. package/src/ummaya/tools/documents/runtime_authoring.py +244 -0
  89. package/src/ummaya/tools/documents/runtime_authoring_bundle.py +76 -0
  90. package/src/ummaya/tools/documents/scorecard.py +184 -0
  91. package/src/ummaya/tools/documents/socratic_planner.py +193 -0
  92. package/src/ummaya/tools/documents/style.py +48 -0
  93. package/src/ummaya/tools/documents/tool_defs.py +523 -0
  94. package/src/ummaya/tools/documents/validate.py +347 -0
  95. package/src/ummaya/tools/executor.py +29 -0
  96. package/src/ummaya/tools/live_proxy.py +0 -3
  97. package/src/ummaya/tools/models.py +5 -1
  98. package/src/ummaya/tools/register_all.py +8 -0
  99. package/src/ummaya/tools/registry.py +10 -1
  100. package/src/ummaya/tools/routing/__init__.py +59 -0
  101. package/src/ummaya/tools/routing/builder.py +105 -0
  102. package/src/ummaya/tools/routing/cards.py +29 -0
  103. package/src/ummaya/tools/routing/decision_service.py +534 -0
  104. package/src/ummaya/tools/routing/decision_types.py +74 -0
  105. package/src/ummaya/tools/routing/feasibility.py +122 -0
  106. package/src/ummaya/tools/routing/intent.py +17 -0
  107. package/src/ummaya/tools/routing/intent_extractor.py +207 -0
  108. package/src/ummaya/tools/routing/intent_patterns.py +160 -0
  109. package/src/ummaya/tools/routing/intent_public_data.py +150 -0
  110. package/src/ummaya/tools/routing/intent_types.py +48 -0
  111. package/src/ummaya/tools/routing/lint.py +78 -0
  112. package/src/ummaya/tools/routing/metadata.py +174 -0
  113. package/src/ummaya/tools/routing/projection.py +340 -0
  114. package/src/ummaya/tools/routing/retrieval_policy.py +629 -0
  115. package/src/ummaya/tools/routing/schema.py +81 -0
  116. package/src/ummaya/tools/routing/types.py +96 -0
  117. package/src/ummaya/tools/routing_index.py +2 -2
  118. package/src/ummaya/tools/search.py +34 -746
  119. package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
  120. package/tui/package.json +1 -1
  121. package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
  122. package/tui/src/QueryEngine.ts +12 -8
  123. package/tui/src/bridge/inboundAttachments.ts +3 -3
  124. package/tui/src/cli/handlers/auth.ts +3 -12
  125. package/tui/src/cli/print.ts +7 -7
  126. package/tui/src/commands/insights.ts +1 -1
  127. package/tui/src/commands/install-github-app/types.ts +8 -30
  128. package/tui/src/commands/plugin/types.ts +6 -28
  129. package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
  130. package/tui/src/commands/rename/generateSessionName.ts +1 -1
  131. package/tui/src/components/Feedback.tsx +1 -1
  132. package/tui/src/components/LogoV2/EmergencyTip.tsx +11 -2
  133. package/tui/src/components/LogoV2/WelcomeV2.tsx +1 -3
  134. package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
  135. package/tui/src/components/Spinner/types.ts +6 -28
  136. package/tui/src/components/agents/generateAgent.ts +1 -1
  137. package/tui/src/components/agents/new-agent-creation/types.ts +4 -26
  138. package/tui/src/components/config/EnvSecretIsolatedEditor.tsx +1 -1
  139. package/tui/src/components/mcp/types.ts +16 -38
  140. package/tui/src/components/messages/AssistantToolUseMessage.tsx +3 -2
  141. package/tui/src/components/messages/UserCrossSessionMessage.ts +16 -4
  142. package/tui/src/components/messages/UserForkBoilerplateMessage.ts +16 -4
  143. package/tui/src/components/messages/UserGitHubWebhookMessage.ts +16 -4
  144. package/tui/src/components/messages/UserToolResultMessage/utils.tsx +3 -2
  145. package/tui/src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.ts +9 -4
  146. package/tui/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.ts +9 -4
  147. package/tui/src/components/primitive/DocumentSocraticReviewBlock.tsx +129 -0
  148. package/tui/src/components/primitive/DocumentToolResultCard.tsx +224 -0
  149. package/tui/src/components/primitive/documentSocraticReview.ts +215 -0
  150. package/tui/src/components/primitive/index.tsx +43 -1
  151. package/tui/src/components/primitive/types.ts +137 -0
  152. package/tui/src/components/ui/option.ts +4 -26
  153. package/tui/src/constants/common.ts +0 -2
  154. package/tui/src/constants/prompts.ts +4 -3
  155. package/tui/src/constants/querySource.ts +4 -26
  156. package/tui/src/entrypoints/sdk/controlTypes.ts +26 -48
  157. package/tui/src/entrypoints/sdk/coreTypes.generated.ts +3 -25
  158. package/tui/src/entrypoints/sdk/runtimeTypes.ts +38 -60
  159. package/tui/src/entrypoints/sdk/sdkUtilityTypes.ts +4 -26
  160. package/tui/src/entrypoints/sdk/settingsTypes.generated.ts +3 -25
  161. package/tui/src/entrypoints/sdk/toolTypes.ts +3 -25
  162. package/tui/src/hooks/toolPermission/handlers/interactiveHandler.ts +10 -0
  163. package/tui/src/hooks/useApiKeyVerification.ts +1 -1
  164. package/tui/src/hooks/useVirtualScroll.ts +1 -1
  165. package/tui/src/ink/ink.tsx +33 -14
  166. package/tui/src/ink/reconciler.ts +2 -3
  167. package/tui/src/ink/render-to-screen.ts +30 -10
  168. package/tui/src/ipc/bridge.ts +62 -15
  169. package/tui/src/ipc/bridgeSingleton.ts +5 -1
  170. package/tui/src/ipc/codec.ts +3 -3
  171. package/tui/src/ipc/frames.generated.ts +12 -12
  172. package/tui/src/ipc/llmClient.ts +151 -27
  173. package/tui/src/ipc/schema/frame.schema.json +1 -1
  174. package/tui/src/keybindings/defaultBindings.ts +4 -0
  175. package/tui/src/main.tsx +29 -11
  176. package/tui/src/native-ts/file-index/index.ts +33 -3
  177. package/tui/src/observability/surface.ts +2 -2
  178. package/tui/src/probes/toolRegistryProbe.tsx +3 -1
  179. package/tui/src/projectOnboardingState.ts +7 -6
  180. package/tui/src/query/chatMessageTypes.ts +18 -0
  181. package/tui/src/query/chatMessagesBuilder.ts +1 -1
  182. package/tui/src/query/deps.ts +1 -1
  183. package/tui/src/query/messageGuards.ts +106 -0
  184. package/tui/src/query/publicDataTerminalRepair.ts +384 -0
  185. package/tui/src/query/run.ts +1075 -0
  186. package/tui/src/query/supportBoundary.ts +168 -0
  187. package/tui/src/query/toolResultErrors.ts +103 -0
  188. package/tui/src/query/toolRunner.ts +687 -0
  189. package/tui/src/query/unavailableToolRepair.ts +118 -0
  190. package/tui/src/query.ts +9 -2186
  191. package/tui/src/screens/REPL.tsx +40 -29
  192. package/tui/src/services/api/adapterManifest.ts +4 -0
  193. package/tui/src/services/api/backendChat/events.ts +117 -0
  194. package/tui/src/services/api/backendChat/finalMessage.ts +40 -0
  195. package/tui/src/services/api/backendChat/frame.ts +9 -0
  196. package/tui/src/services/api/backendChat/streaming.ts +430 -0
  197. package/tui/src/services/api/backendChat/types.ts +62 -0
  198. package/tui/src/services/api/backendChat.ts +1 -0
  199. package/tui/src/services/api/client.ts +65 -2
  200. package/tui/src/services/api/errorUtils.ts +5 -5
  201. package/tui/src/services/api/errors.ts +1 -1
  202. package/tui/src/services/api/logging.ts +1 -1
  203. package/tui/src/services/api/ummaya/evidence.ts +194 -0
  204. package/tui/src/services/api/ummaya/messages.ts +255 -0
  205. package/tui/src/services/api/ummaya/nonStreaming.ts +66 -0
  206. package/tui/src/services/api/ummaya/provider.ts +200 -0
  207. package/tui/src/services/api/ummaya/reasoning.ts +24 -0
  208. package/tui/src/services/api/ummaya/request.ts +200 -0
  209. package/tui/src/services/api/ummaya/selectionContext.ts +240 -0
  210. package/tui/src/services/api/ummaya/streaming.ts +365 -0
  211. package/tui/src/services/api/ummaya/streamingPayload.ts +129 -0
  212. package/tui/src/services/api/ummaya/streamingReader.ts +40 -0
  213. package/tui/src/services/api/ummaya/toolSelection.ts +217 -0
  214. package/tui/src/services/api/ummaya/types.ts +110 -0
  215. package/tui/src/services/api/ummaya/usage.ts +30 -0
  216. package/tui/src/services/api/ummaya.ts +26 -418
  217. package/tui/src/services/api/withRetry.ts +1 -1
  218. package/tui/src/services/awaySummary.ts +2 -2
  219. package/tui/src/services/claudeAiLimits.ts +1 -1
  220. package/tui/src/services/compact/autoCompact.ts +1 -1
  221. package/tui/src/services/compact/compact.ts +1 -1
  222. package/tui/src/services/lsp/types.ts +8 -30
  223. package/tui/src/services/tips/types.ts +6 -28
  224. package/tui/src/services/tokenEstimation.ts +1 -1
  225. package/tui/src/services/toolRegistry/bootGuard.ts +5 -5
  226. package/tui/src/services/toolUseSummary/toolUseSummaryGenerator.ts +1 -1
  227. package/tui/src/services/tools/toolExecution.ts +94 -1
  228. package/tui/src/store/pendingPermissionSlot.ts +1 -1
  229. package/tui/src/store/session-store.ts +10 -36
  230. package/tui/src/stubs/any-stub.ts +15 -10
  231. package/tui/src/stubs/color-diff-napi.ts +37 -23
  232. package/tui/src/stubs/globals.d.ts +3 -3
  233. package/tui/src/stubs/macro-preload.ts +23 -12
  234. package/tui/src/tools/AdapterTool/AdapterTool.ts +1207 -714
  235. package/tui/src/tools/AdapterTool/routeDiagnostics.ts +75 -0
  236. package/tui/src/tools/AgentTool/AgentTool.tsx +84 -1371
  237. package/tui/src/tools/AgentTool/agentToolHandoff.ts +114 -0
  238. package/tui/src/tools/AgentTool/agentToolPartialResult.ts +16 -0
  239. package/tui/src/tools/AgentTool/agentToolProgress.ts +32 -0
  240. package/tui/src/tools/AgentTool/agentToolResolver.ts +161 -0
  241. package/tui/src/tools/AgentTool/agentToolResult.ts +163 -0
  242. package/tui/src/tools/AgentTool/agentToolUtils.ts +14 -686
  243. package/tui/src/tools/AgentTool/asyncAgentLifecycle.ts +208 -0
  244. package/tui/src/tools/AgentTool/asyncLifecycle.ts +153 -0
  245. package/tui/src/tools/AgentTool/backgroundedCompletion.ts +126 -0
  246. package/tui/src/tools/AgentTool/backgroundedLifecycle.ts +174 -0
  247. package/tui/src/tools/AgentTool/foregroundBackground.ts +83 -0
  248. package/tui/src/tools/AgentTool/foregroundDrain.tsx +133 -0
  249. package/tui/src/tools/AgentTool/foregroundFinalize.ts +98 -0
  250. package/tui/src/tools/AgentTool/foregroundLifecycle.tsx +237 -0
  251. package/tui/src/tools/AgentTool/foregroundProgress.tsx +169 -0
  252. package/tui/src/tools/AgentTool/foregroundTask.ts +89 -0
  253. package/tui/src/tools/AgentTool/forkSubagent.ts +1 -12
  254. package/tui/src/tools/AgentTool/forkSubagentGate.ts +34 -0
  255. package/tui/src/tools/AgentTool/launchRouting.ts +203 -0
  256. package/tui/src/tools/AgentTool/lifecycle.ts +244 -0
  257. package/tui/src/tools/AgentTool/mcpRouting.ts +73 -0
  258. package/tui/src/tools/AgentTool/orchestrationSupport.ts +70 -0
  259. package/tui/src/tools/AgentTool/permissions.ts +39 -0
  260. package/tui/src/tools/AgentTool/promptSetup.ts +181 -0
  261. package/tui/src/tools/AgentTool/remoteRouting.ts +62 -0
  262. package/tui/src/tools/AgentTool/resultMapping.ts +116 -0
  263. package/tui/src/tools/AgentTool/resumeAgent.ts +39 -107
  264. package/tui/src/tools/AgentTool/resumeAgentHelpers.ts +140 -0
  265. package/tui/src/tools/AgentTool/runAgent.ts +1 -1
  266. package/tui/src/tools/AgentTool/runtimeConfig.ts +57 -0
  267. package/tui/src/tools/AgentTool/schemas.ts +196 -0
  268. package/tui/src/tools/AgentTool/sourceVerificationPropagation.ts +263 -0
  269. package/tui/src/tools/AgentTool/worktreeLifecycle.ts +105 -0
  270. package/tui/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +174 -202
  271. package/tui/src/tools/BashTool/BashTool.tsx +71 -1072
  272. package/tui/src/tools/BashTool/bashCommandHelpers.ts +12 -12
  273. package/tui/src/tools/BashTool/bashPermissions/astPreflight.ts +173 -0
  274. package/tui/src/tools/BashTool/bashPermissions/classifierChecks.ts +199 -0
  275. package/tui/src/tools/BashTool/bashPermissions/compoundGuards.ts +53 -0
  276. package/tui/src/tools/BashTool/bashPermissions/constants.ts +99 -0
  277. package/tui/src/tools/BashTool/bashPermissions/index.ts +38 -0
  278. package/tui/src/tools/BashTool/bashPermissions/legacyMisparsing.ts +62 -0
  279. package/tui/src/tools/BashTool/bashPermissions/main.ts +135 -0
  280. package/tui/src/tools/BashTool/bashPermissions/normalizedCommands.ts +33 -0
  281. package/tui/src/tools/BashTool/bashPermissions/operatorFlow.ts +98 -0
  282. package/tui/src/tools/BashTool/bashPermissions/permissionChecks.ts +200 -0
  283. package/tui/src/tools/BashTool/bashPermissions/prefixSuggestions.ts +88 -0
  284. package/tui/src/tools/BashTool/bashPermissions/promptClassifierRules.ts +125 -0
  285. package/tui/src/tools/BashTool/bashPermissions/ruleDelegates.ts +19 -0
  286. package/tui/src/tools/BashTool/bashPermissions/ruleMatching.ts +145 -0
  287. package/tui/src/tools/BashTool/bashPermissions/sandboxAutoAllow.ts +75 -0
  288. package/tui/src/tools/BashTool/bashPermissions/subcommandFlow.ts +205 -0
  289. package/tui/src/tools/BashTool/bashPermissions/subcommandGuards.ts +73 -0
  290. package/tui/src/tools/BashTool/bashPermissions/subcommandResultHelpers.ts +116 -0
  291. package/tui/src/tools/BashTool/bashPermissions/types.ts +26 -0
  292. package/tui/src/tools/BashTool/bashPermissions/wrapperStripping.ts +139 -0
  293. package/tui/src/tools/BashTool/bashPermissions.ts +26 -2621
  294. package/tui/src/tools/BashTool/call.ts +202 -0
  295. package/tui/src/tools/BashTool/callLoader.ts +35 -0
  296. package/tui/src/tools/BashTool/commandClassification.ts +151 -0
  297. package/tui/src/tools/BashTool/commandClassificationLoader.ts +40 -0
  298. package/tui/src/tools/BashTool/cwdReset.ts +33 -0
  299. package/tui/src/tools/BashTool/lineTruncation.ts +11 -0
  300. package/tui/src/tools/BashTool/modeValidation.ts +13 -1
  301. package/tui/src/tools/BashTool/outputPersistence.ts +42 -0
  302. package/tui/src/tools/BashTool/permissionClassification.ts +66 -0
  303. package/tui/src/tools/BashTool/permissionLoader.ts +44 -0
  304. package/tui/src/tools/BashTool/resultLoader.ts +29 -0
  305. package/tui/src/tools/BashTool/resultMapping.ts +83 -0
  306. package/tui/src/tools/BashTool/sandboxPolicy.ts +79 -0
  307. package/tui/src/tools/BashTool/schemas.ts +65 -0
  308. package/tui/src/tools/BashTool/sedEditExecution.ts +59 -0
  309. package/tui/src/tools/BashTool/shellExecution.tsx +245 -0
  310. package/tui/src/tools/BashTool/shellOutputUtils.ts +85 -0
  311. package/tui/src/tools/BashTool/shellPermissionGauntlet.ts +97 -0
  312. package/tui/src/tools/BashTool/uiLoader.ts +37 -0
  313. package/tui/src/tools/BriefTool/upload.ts +1 -1
  314. package/tui/src/tools/CalculatorTool/parser.ts +2 -2
  315. package/tui/src/tools/DocumentPrimitive/DocumentPrimitive.ts +262 -0
  316. package/tui/src/tools/DocumentPrimitive/dispatchNormalization.ts +270 -0
  317. package/tui/src/tools/DocumentPrimitive/documentDestinationPath.ts +18 -0
  318. package/tui/src/tools/DocumentPrimitive/documentMutationGuard.ts +22 -0
  319. package/tui/src/tools/DocumentPrimitive/documentPatchNormalization.ts +248 -0
  320. package/tui/src/tools/DocumentPrimitive/documentSourceVerification.ts +245 -0
  321. package/tui/src/tools/DocumentPrimitive/documentSourceVerificationFields.ts +103 -0
  322. package/tui/src/tools/DocumentPrimitive/modelVisibleOutput.ts +40 -0
  323. package/tui/src/tools/DocumentPrimitive/prompt.ts +35 -0
  324. package/tui/src/tools/FileEditTool/FileEditTool.ts +9 -507
  325. package/tui/src/tools/FileEditTool/call.ts +228 -0
  326. package/tui/src/tools/FileEditTool/validateInput.ts +196 -0
  327. package/tui/src/tools/FileReadTool/imageProcessor.ts +13 -0
  328. package/tui/src/tools/FileWriteTool/FileWriteTool.ts +7 -300
  329. package/tui/src/tools/FileWriteTool/call.ts +223 -0
  330. package/tui/src/tools/FileWriteTool/validateInput.ts +80 -0
  331. package/tui/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +19 -3
  332. package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +25 -32
  333. package/tui/src/tools/LookupPrimitive/prompt.ts +0 -2
  334. package/tui/src/tools/MCPTool/trustPolicy.ts +118 -0
  335. package/tui/src/tools/McpAuthTool/McpAuthTool.ts +21 -3
  336. package/tui/src/tools/NotebookEditTool/NotebookEditTool.ts +7 -326
  337. package/tui/src/tools/NotebookEditTool/call.ts +254 -0
  338. package/tui/src/tools/NotebookEditTool/notebookModel.ts +51 -0
  339. package/tui/src/tools/NotebookEditTool/validateInput.ts +142 -0
  340. package/tui/src/tools/PowerShellTool/PowerShellTool.tsx +46 -937
  341. package/tui/src/tools/PowerShellTool/acceptEditsCommandValidation.ts +162 -0
  342. package/tui/src/tools/PowerShellTool/call.ts +179 -0
  343. package/tui/src/tools/PowerShellTool/callLoader.ts +37 -0
  344. package/tui/src/tools/PowerShellTool/commandClassification.ts +86 -0
  345. package/tui/src/tools/PowerShellTool/modeValidation.ts +25 -332
  346. package/tui/src/tools/PowerShellTool/outputPersistence.ts +42 -0
  347. package/tui/src/tools/PowerShellTool/permissionClassification.ts +28 -0
  348. package/tui/src/tools/PowerShellTool/resultLoader.ts +31 -0
  349. package/tui/src/tools/PowerShellTool/resultMapping.ts +75 -0
  350. package/tui/src/tools/PowerShellTool/schemas.ts +40 -0
  351. package/tui/src/tools/PowerShellTool/shellExecution.tsx +258 -0
  352. package/tui/src/tools/PowerShellTool/symlinkModeValidation.ts +44 -0
  353. package/tui/src/tools/PowerShellTool/uiLoader.ts +37 -0
  354. package/tui/src/tools/PowerShellTool/validation.ts +39 -0
  355. package/tui/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +19 -3
  356. package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +1 -11
  357. package/tui/src/tools/ResolveLocationPrimitive/prompt.ts +2 -6
  358. package/tui/src/tools/SkillTool/SkillTool.ts +2 -2
  359. package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +27 -10
  360. package/tui/src/tools/TaskCreateTool/TaskCreateTool.ts +16 -2
  361. package/tui/src/tools/TaskGetTool/TaskGetTool.ts +23 -3
  362. package/tui/src/tools/TaskListTool/TaskListTool.ts +22 -4
  363. package/tui/src/tools/TaskOutputTool/TaskOutputTool.tsx +46 -547
  364. package/tui/src/tools/TaskOutputTool/lookup.ts +216 -0
  365. package/tui/src/tools/TaskOutputTool/render.tsx +257 -0
  366. package/tui/src/tools/TaskOutputTool/schemas.ts +55 -0
  367. package/tui/src/tools/TaskOutputTool/serialization.ts +36 -0
  368. package/tui/src/tools/TaskStopTool/TaskStopTool.ts +10 -0
  369. package/tui/src/tools/TaskUpdateTool/TaskUpdateTool.ts +14 -364
  370. package/tui/src/tools/TaskUpdateTool/completion.ts +62 -0
  371. package/tui/src/tools/TaskUpdateTool/schemas.ts +62 -0
  372. package/tui/src/tools/TaskUpdateTool/serialization.ts +46 -0
  373. package/tui/src/tools/TaskUpdateTool/statusUpdate.ts +247 -0
  374. package/tui/src/tools/TodoWriteTool/TodoWriteTool.ts +21 -2
  375. package/tui/src/tools/ToolSearchTool/ToolSearchTool.ts +21 -302
  376. package/tui/src/tools/ToolSearchTool/ccSupportTools.ts +223 -0
  377. package/tui/src/tools/ToolSearchTool/descriptionCache.ts +50 -0
  378. package/tui/src/tools/ToolSearchTool/keywordSearch.ts +216 -0
  379. package/tui/src/tools/ToolSearchTool/prompt.ts +10 -4
  380. package/tui/src/tools/ToolSearchTool/resultMapping.ts +30 -0
  381. package/tui/src/tools/ToolSearchTool/schemas.ts +30 -0
  382. package/tui/src/tools/ToolSearchTool/searchPool.ts +47 -0
  383. package/tui/src/tools/ToolSearchTool/supportIntentHints.ts +140 -0
  384. package/tui/src/tools/TranslateTool/TranslateTool.ts +1 -1
  385. package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +2 -1
  386. package/tui/src/tools/WebFetchTool/WebFetchTool.ts +43 -138
  387. package/tui/src/tools/WebFetchTool/call.ts +227 -0
  388. package/tui/src/tools/WebFetchTool/resolvedAddressSafety.ts +78 -0
  389. package/tui/src/tools/WebFetchTool/sourceVerification.ts +204 -0
  390. package/tui/src/tools/WebFetchTool/types.ts +23 -0
  391. package/tui/src/tools/WebFetchTool/urlSafety.ts +181 -0
  392. package/tui/src/tools/WebFetchTool/utils.ts +1 -1
  393. package/tui/src/tools/WebSearchTool/UI.tsx +0 -1
  394. package/tui/src/tools/WebSearchTool/WebSearchTool.ts +9 -313
  395. package/tui/src/tools/WebSearchTool/call.ts +33 -0
  396. package/tui/src/tools/WebSearchTool/responseMapping.ts +190 -0
  397. package/tui/src/tools/WebSearchTool/resultBlock.ts +47 -0
  398. package/tui/src/tools/WebSearchTool/schemas.ts +47 -0
  399. package/tui/src/tools/WebSearchTool/toolSchema.ts +12 -0
  400. package/tui/src/tools/WorkspaceToolAdapter/WorkspaceToolAdapter.ts +79 -0
  401. package/tui/src/tools/WorkspaceToolAdapter/allowedRootPolicy.ts +85 -0
  402. package/tui/src/tools/WorkspaceToolAdapter/documentFormatGuards.ts +73 -0
  403. package/tui/src/tools/WorkspaceToolAdapter/inputNormalization.ts +105 -0
  404. package/tui/src/tools/WorkspaceToolAdapter/mcpExposurePolicy.ts +64 -0
  405. package/tui/src/tools/WorkspaceToolAdapter/toolDefFactory.ts +215 -0
  406. package/tui/src/tools/WorkspaceToolAdapter/toolNames.ts +6 -0
  407. package/tui/src/tools/WorkspaceToolAdapter/workspacePolicy.ts +15 -0
  408. package/tui/src/tools/_shared/dispatchPrimitive.ts +6 -6
  409. package/tui/src/tools/_shared/documentChangeToPatch.ts +125 -0
  410. package/tui/src/tools/_shared/documentDispatchArguments.ts +87 -0
  411. package/tui/src/tools/_shared/documentPrimitiveTimeout.ts +13 -0
  412. package/tui/src/tools/_shared/documentToolResultRender.ts +98 -0
  413. package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
  414. package/tui/src/tools/_shared/rootPrimitiveInput.ts +1 -0
  415. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPatterns.ts +58 -0
  416. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPrompt.ts +271 -0
  417. package/tui/src/tools/_shared/toolChoiceRepair/documentRepair.ts +452 -0
  418. package/tui/src/tools/_shared/toolChoiceRepair/messageAccess.ts +80 -0
  419. package/tui/src/tools/_shared/toolChoiceRepair/publicDataRepair.ts +92 -0
  420. package/tui/src/tools/_shared/toolChoiceRepair/supportRepair.ts +135 -0
  421. package/tui/src/tools/_shared/toolChoiceRepair.ts +55 -860
  422. package/tui/src/tools/shared/mockDisclaimer.ts +1 -1
  423. package/tui/src/tools.ts +39 -190
  424. package/tui/src/types/fileSuggestion.ts +4 -26
  425. package/tui/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +186 -148
  426. package/tui/src/types/generated/events_mono/common/v1/auth.ts +25 -11
  427. package/tui/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +47 -30
  428. package/tui/src/types/generated/google/protobuf/timestamp.ts +21 -7
  429. package/tui/src/types/message.ts +80 -102
  430. package/tui/src/types/messageQueueTypes.ts +6 -28
  431. package/tui/src/types/notebook.ts +16 -38
  432. package/tui/src/types/statusLine.ts +4 -26
  433. package/tui/src/types/tools.ts +24 -46
  434. package/tui/src/types/utils.ts +6 -28
  435. package/tui/src/upstreamproxy/relay.ts +7 -3
  436. package/tui/src/upstreamproxy/upstreamproxy.ts +1 -1
  437. package/tui/src/utils/assistantMessageFactories.ts +9 -3
  438. package/tui/src/utils/auth.ts +129 -139
  439. package/tui/src/utils/bash/ast.ts +23 -23
  440. package/tui/src/utils/bash/bashParser.ts +5 -5
  441. package/tui/src/utils/billing.ts +1 -1
  442. package/tui/src/utils/collapseReadSearch.ts +3 -3
  443. package/tui/src/utils/cronTasks.ts +1 -1
  444. package/tui/src/utils/execFileNoThrow.ts +1 -1
  445. package/tui/src/utils/filePersistence/types.ts +16 -38
  446. package/tui/src/utils/forkedAgent.ts +1 -1
  447. package/tui/src/utils/gracefulShutdown.ts +4 -4
  448. package/tui/src/utils/heapDumpService.ts +12 -8
  449. package/tui/src/utils/hooks/apiQueryHookHelper.ts +1 -1
  450. package/tui/src/utils/hooks/execPromptHook.ts +1 -1
  451. package/tui/src/utils/hooks/skillImprovement.ts +1 -1
  452. package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
  453. package/tui/src/utils/messages.ts +18 -0
  454. package/tui/src/utils/migrateSessions.ts +3 -3
  455. package/tui/src/utils/model/model.ts +6 -6
  456. package/tui/src/utils/permissions/yoloClassifier.ts +1 -1
  457. package/tui/src/utils/plugins/headlessPluginInstall.ts +1 -1
  458. package/tui/src/utils/plugins/mcpPluginIntegration.ts +1 -1
  459. package/tui/src/utils/plugins/mcpbHandler.ts +1 -1
  460. package/tui/src/utils/plugins/pluginLoader.ts +8 -8
  461. package/tui/src/utils/protectedNamespace.ts +5 -3
  462. package/tui/src/utils/rawJsonToolCall.ts +242 -0
  463. package/tui/src/utils/ripgrep.ts +16 -7
  464. package/tui/src/utils/sessionTitle.ts +1 -1
  465. package/tui/src/utils/settings/permissionValidation.ts +14 -2
  466. package/tui/src/utils/shell/prefix.ts +1 -1
  467. package/tui/src/utils/sideQuery.ts +1 -1
  468. package/tui/src/utils/systemThemeWatcher.ts +13 -3
  469. package/tui/src/utils/teleport.tsx +1 -1
  470. package/uv.lock +400 -14
  471. package/tui/src/services/api/claude.ts +0 -3540
  472. package/tui/src/tools/_shared/directPublicDataGuard.ts +0 -362
  473. package/tui/src/tools/_shared/kmaAnalysisGuard.ts +0 -197
  474. package/tui/src/tools/_shared/kmaAviationGuard.ts +0 -70
  475. package/tui/src/tools/_shared/nmcAedGuard.ts +0 -234
  476. package/tui/src/tools/_shared/protectedCheckGuard.ts +0 -207
  477. package/tui/src/tools/_shared/textToolCallGuard.ts +0 -91
@@ -0,0 +1,521 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Format conversion engine registry for document editable derivatives."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import io
8
+ import json
9
+ import os
10
+ import shutil
11
+ import subprocess
12
+ import tempfile
13
+ import zipfile
14
+ from collections.abc import Mapping, Sequence
15
+ from pathlib import Path
16
+ from shutil import which
17
+ from typing import Protocol
18
+
19
+ from ummaya.tools.documents.models import DocumentArtifact, DocumentFormat
20
+
21
+ _HWP_TO_HWPX_CONVERTER_ENV = "UMMAYA_HWP_TO_HWPX_CONVERTER"
22
+ _HWP_TO_HWPX_CONVERTER_ARGS_ENV = "UMMAYA_HWP_TO_HWPX_CONVERTER_ARGS_JSON"
23
+ _HWP_TO_HWPX_CONVERTER_ENGINE_ID_ENV = "UMMAYA_HWP_TO_HWPX_CONVERTER_ENGINE_ID"
24
+ _HWP_TO_HWPX_CONVERTER_TIMEOUT_ENV = "UMMAYA_HWP_TO_HWPX_CONVERTER_TIMEOUT_SECONDS"
25
+ _HWPXJS_ENGINE_ID = "hwpxjs-cli-convert-hwp"
26
+ _HWPXJS_HWP_TO_HWPX_ARGS = ("convert:hwp", "{source}", "{output}")
27
+ _HWP_CONVERTER_DEFAULT_TIMEOUT_SECONDS = 120
28
+ _LEGACY_OFFICE_ENGINE_ID = "libreoffice-legacy-office-to-ooxml-bridge"
29
+ _TEXTUTIL_DOC_ENGINE_ID = "macos-textutil-doc-to-docx-bridge"
30
+ _MICROSOFT_EXCEL_APP_ENV = "UMMAYA_MICROSOFT_EXCEL_APP"
31
+ _MICROSOFT_EXCEL_ENGINE_ID = "microsoft-excel-applescript-xls-to-xlsx-bridge"
32
+ _LEGACY_OFFICE_TIMEOUT_SECONDS = 120
33
+ _TEXTUTIL_TIMEOUT_SECONDS = 60
34
+ _MICROSOFT_EXCEL_TIMEOUT_SECONDS = 120
35
+ _LEGACY_OFFICE_CONVERSIONS: tuple[
36
+ tuple[DocumentFormat, DocumentFormat, tuple[str, ...]],
37
+ ...,
38
+ ] = (
39
+ (
40
+ DocumentFormat.doc,
41
+ DocumentFormat.docx,
42
+ (
43
+ "--headless",
44
+ "--convert-to",
45
+ "docx:MS Word 2007 XML",
46
+ "--outdir",
47
+ "{outdir}",
48
+ "{source}",
49
+ ),
50
+ ),
51
+ (
52
+ DocumentFormat.xls,
53
+ DocumentFormat.xlsx,
54
+ (
55
+ "--headless",
56
+ "--convert-to",
57
+ "xlsx:Calc MS Excel 2007 XML",
58
+ "--outdir",
59
+ "{outdir}",
60
+ "{source}",
61
+ ),
62
+ ),
63
+ (
64
+ DocumentFormat.ppt,
65
+ DocumentFormat.pptx,
66
+ (
67
+ "--headless",
68
+ "--convert-to",
69
+ "pptx:Impress MS PowerPoint 2007 XML",
70
+ "--outdir",
71
+ "{outdir}",
72
+ "{source}",
73
+ ),
74
+ ),
75
+ )
76
+ _TEXTUTIL_DOC_TO_DOCX_ARGS = ("-convert", "docx", "-output", "{output}", "{source}")
77
+ _MICROSOFT_EXCEL_XLS_TO_XLSX_ARGS = (
78
+ "-e",
79
+ "on run argv",
80
+ "-e",
81
+ "set sourcePath to POSIX file (item 1 of argv)",
82
+ "-e",
83
+ "set outputPath to POSIX file (item 2 of argv)",
84
+ "-e",
85
+ 'tell application "Microsoft Excel"',
86
+ "-e",
87
+ "set display alerts to false",
88
+ "-e",
89
+ "open workbook workbook file name sourcePath",
90
+ "-e",
91
+ "save workbook as active workbook filename outputPath file format workbook default",
92
+ "-e",
93
+ "close active workbook saving no",
94
+ "-e",
95
+ "end tell",
96
+ "-e",
97
+ "end run",
98
+ "{source}",
99
+ "{output}",
100
+ )
101
+
102
+
103
+ class DocumentConversionEngine(Protocol):
104
+ """Promoted engine that converts one source format into one output format."""
105
+
106
+ source_format: DocumentFormat
107
+ output_format: DocumentFormat
108
+ engine_id: str
109
+
110
+ def convert_for_edit(self, source: DocumentArtifact) -> bytes:
111
+ """Return editable derivative bytes without mutating the source artifact."""
112
+
113
+
114
+ class UnsupportedDocumentConversionError(LookupError):
115
+ """Raised when no promoted conversion engine is registered for a format pair."""
116
+
117
+ def __init__(self, source_format: DocumentFormat, output_format: DocumentFormat) -> None:
118
+ super().__init__(
119
+ "No promoted document conversion registered for "
120
+ f"{source_format.value} -> {output_format.value}"
121
+ )
122
+ self.source_format = source_format
123
+ self.output_format = output_format
124
+
125
+
126
+ class DocumentConversionEngineError(ValueError):
127
+ """Raised when a promoted conversion engine fails its execution contract."""
128
+
129
+
130
+ class LocalCliDocumentConversionEngine:
131
+ """Fail-closed conversion bridge for pinned local CLI candidates."""
132
+
133
+ def __init__(
134
+ self,
135
+ *,
136
+ source_format: DocumentFormat,
137
+ output_format: DocumentFormat,
138
+ engine_id: str,
139
+ executable: Path,
140
+ args: Sequence[str],
141
+ timeout_seconds: int = 30,
142
+ ) -> None:
143
+ executable_path = executable.expanduser()
144
+ if not executable_path.is_absolute():
145
+ raise ValueError("document conversion executable must be an absolute path")
146
+ resolved_executable = executable_path.resolve(strict=False)
147
+ if not resolved_executable.exists():
148
+ raise ValueError(
149
+ f"document conversion executable does not exist: {resolved_executable}"
150
+ )
151
+ if not resolved_executable.is_file():
152
+ raise ValueError(f"document conversion executable is not a file: {resolved_executable}")
153
+ if not os.access(resolved_executable, os.X_OK):
154
+ raise ValueError(
155
+ f"document conversion executable is not executable: {resolved_executable}"
156
+ )
157
+ if not engine_id:
158
+ raise ValueError("document conversion engine_id is required")
159
+ if timeout_seconds < 1 or timeout_seconds > 300:
160
+ raise ValueError("document conversion timeout_seconds must be between 1 and 300")
161
+ if not args:
162
+ raise ValueError("document conversion CLI args are required")
163
+ if not any("{source}" in arg for arg in args):
164
+ raise ValueError("document conversion CLI args must include {source}")
165
+ if not any("{output}" in arg for arg in args) and not any(
166
+ "{outdir}" in arg for arg in args
167
+ ):
168
+ raise ValueError("document conversion CLI args must include {output} or {outdir}")
169
+
170
+ self.source_format = source_format
171
+ self.output_format = output_format
172
+ self.engine_id = engine_id
173
+ self.executable = resolved_executable
174
+ self.args = tuple(args)
175
+ self.timeout_seconds = timeout_seconds
176
+
177
+ def convert_for_edit(self, source: DocumentArtifact) -> bytes:
178
+ """Run the pinned CLI and return validated editable derivative bytes."""
179
+ if source.format is not self.source_format:
180
+ raise DocumentConversionEngineError(
181
+ "document conversion source format mismatch: "
182
+ f"expected {self.source_format.value}, got {source.format.value}"
183
+ )
184
+ source_path = source.source_path.expanduser().resolve()
185
+ if not source_path.exists() or not source_path.is_file():
186
+ raise DocumentConversionEngineError(
187
+ f"document conversion source file does not exist: {source_path}"
188
+ )
189
+ source_hash_before = _file_sha256(source_path)
190
+
191
+ with tempfile.TemporaryDirectory(prefix="ummaya-document-conversion-") as temp_root:
192
+ temp_source_path = Path(temp_root) / source_path.name
193
+ shutil.copy2(source_path, temp_source_path)
194
+ output_path = Path(temp_root) / f"output.{self.output_format.value}"
195
+ command = [str(self.executable), *self._expanded_args(temp_source_path, output_path)]
196
+ try:
197
+ completed = subprocess.run( # noqa: S603 - executable is absolute and prevalidated.
198
+ command,
199
+ cwd=temp_root,
200
+ stdin=subprocess.DEVNULL,
201
+ capture_output=True,
202
+ timeout=self.timeout_seconds,
203
+ check=False,
204
+ )
205
+ except subprocess.TimeoutExpired as exc:
206
+ raise DocumentConversionEngineError(
207
+ f"document conversion CLI timed out after {self.timeout_seconds}s"
208
+ ) from exc
209
+ if completed.returncode != 0:
210
+ raise DocumentConversionEngineError(
211
+ "document conversion CLI exited non-zero "
212
+ f"({completed.returncode}): {_process_output_summary(completed)}"
213
+ )
214
+ source_hash_after = _file_sha256(source_path)
215
+ if source_hash_after != source_hash_before:
216
+ raise DocumentConversionEngineError(
217
+ "document conversion CLI modified the source artifact"
218
+ )
219
+ if not output_path.exists():
220
+ legacy_output = output_path.with_name(
221
+ f"{temp_source_path.stem}.{self.output_format.value}"
222
+ )
223
+ if legacy_output.exists():
224
+ output_path = legacy_output
225
+ if not output_path.is_file():
226
+ raise DocumentConversionEngineError(
227
+ "document conversion CLI did not create the declared output artifact"
228
+ )
229
+ payload = output_path.read_bytes()
230
+ if not payload:
231
+ raise DocumentConversionEngineError(
232
+ "document conversion CLI produced an empty output artifact"
233
+ )
234
+ _validate_output_payload(payload, self.output_format)
235
+ return payload
236
+
237
+ def _expanded_args(self, source_path: Path, output_path: Path) -> tuple[str, ...]:
238
+ return tuple(
239
+ arg.replace("{source}", str(source_path))
240
+ .replace("{output}", str(output_path))
241
+ .replace("{outdir}", str(output_path.parent))
242
+ for arg in self.args
243
+ )
244
+
245
+
246
+ class DocumentConversionRegistry:
247
+ """Session-local registry of promoted document conversion engines."""
248
+
249
+ def __init__(self) -> None:
250
+ self._engines: dict[tuple[DocumentFormat, DocumentFormat], DocumentConversionEngine] = {}
251
+
252
+ def register(self, engine: DocumentConversionEngine) -> None:
253
+ """Register one promoted engine for one source/output format pair."""
254
+ key = (engine.source_format, engine.output_format)
255
+ if key in self._engines:
256
+ raise ValueError(
257
+ "document conversion engine already registered for "
258
+ f"{engine.source_format.value} -> {engine.output_format.value}"
259
+ )
260
+ self._engines[key] = engine
261
+
262
+ def get(
263
+ self,
264
+ source_format: DocumentFormat,
265
+ output_format: DocumentFormat,
266
+ ) -> DocumentConversionEngine | None:
267
+ """Return the promoted conversion engine for a format pair, if present."""
268
+ return self._engines.get((source_format, output_format))
269
+
270
+ def require(
271
+ self,
272
+ source_format: DocumentFormat,
273
+ output_format: DocumentFormat,
274
+ ) -> DocumentConversionEngine:
275
+ """Return the promoted conversion engine or fail closed."""
276
+ engine = self.get(source_format, output_format)
277
+ if engine is None:
278
+ raise UnsupportedDocumentConversionError(source_format, output_format)
279
+ return engine
280
+
281
+
282
+ def build_default_document_conversion_registry(
283
+ *,
284
+ env: Mapping[str, str] | None = None,
285
+ ) -> DocumentConversionRegistry:
286
+ """Build promoted local conversion engines from explicit UMMAYA env config."""
287
+ active_env = os.environ if env is None else env
288
+ registry = DocumentConversionRegistry()
289
+ executable = active_env.get(_HWP_TO_HWPX_CONVERTER_ENV)
290
+ if not executable:
291
+ hwpxjs_executable = _find_default_hwpxjs_executable(active_env)
292
+ if hwpxjs_executable is not None:
293
+ registry.register(
294
+ LocalCliDocumentConversionEngine(
295
+ source_format=DocumentFormat.hwp,
296
+ output_format=DocumentFormat.hwpx,
297
+ engine_id=_HWPXJS_ENGINE_ID,
298
+ executable=hwpxjs_executable,
299
+ args=_HWPXJS_HWP_TO_HWPX_ARGS,
300
+ timeout_seconds=_HWP_CONVERTER_DEFAULT_TIMEOUT_SECONDS,
301
+ )
302
+ )
303
+ legacy_office_executable = _find_default_libreoffice_executable(active_env)
304
+ if legacy_office_executable is not None:
305
+ for source_format, output_format, args in _LEGACY_OFFICE_CONVERSIONS:
306
+ registry.register(
307
+ LocalCliDocumentConversionEngine(
308
+ source_format=source_format,
309
+ output_format=output_format,
310
+ engine_id=_LEGACY_OFFICE_ENGINE_ID,
311
+ executable=legacy_office_executable,
312
+ args=args,
313
+ timeout_seconds=_LEGACY_OFFICE_TIMEOUT_SECONDS,
314
+ )
315
+ )
316
+ elif (textutil_executable := _find_default_textutil_executable(active_env)) is not None:
317
+ registry.register(
318
+ LocalCliDocumentConversionEngine(
319
+ source_format=DocumentFormat.doc,
320
+ output_format=DocumentFormat.docx,
321
+ engine_id=_TEXTUTIL_DOC_ENGINE_ID,
322
+ executable=textutil_executable,
323
+ args=_TEXTUTIL_DOC_TO_DOCX_ARGS,
324
+ timeout_seconds=_TEXTUTIL_TIMEOUT_SECONDS,
325
+ )
326
+ )
327
+ if legacy_office_executable is None:
328
+ excel_executable = _find_default_excel_osascript_executable(active_env)
329
+ if excel_executable is not None:
330
+ registry.register(
331
+ LocalCliDocumentConversionEngine(
332
+ source_format=DocumentFormat.xls,
333
+ output_format=DocumentFormat.xlsx,
334
+ engine_id=_MICROSOFT_EXCEL_ENGINE_ID,
335
+ executable=excel_executable,
336
+ args=_MICROSOFT_EXCEL_XLS_TO_XLSX_ARGS,
337
+ timeout_seconds=_MICROSOFT_EXCEL_TIMEOUT_SECONDS,
338
+ )
339
+ )
340
+ return registry
341
+
342
+ args_raw = active_env.get(_HWP_TO_HWPX_CONVERTER_ARGS_ENV)
343
+ if not args_raw:
344
+ raise ValueError(
345
+ f"{_HWP_TO_HWPX_CONVERTER_ARGS_ENV} is required when "
346
+ f"{_HWP_TO_HWPX_CONVERTER_ENV} is set"
347
+ )
348
+
349
+ registry.register(
350
+ LocalCliDocumentConversionEngine(
351
+ source_format=DocumentFormat.hwp,
352
+ output_format=DocumentFormat.hwpx,
353
+ engine_id=active_env.get(
354
+ _HWP_TO_HWPX_CONVERTER_ENGINE_ID_ENV,
355
+ "local-hwp-to-hwpx",
356
+ ),
357
+ executable=Path(executable),
358
+ args=_converter_args_from_json(args_raw),
359
+ timeout_seconds=_converter_timeout_from_env(
360
+ active_env.get(_HWP_TO_HWPX_CONVERTER_TIMEOUT_ENV)
361
+ ),
362
+ )
363
+ )
364
+ return registry
365
+
366
+
367
+ def _find_executable(name: str, *, active_env: Mapping[str, str]) -> Path | None:
368
+ path_env = active_env.get("PATH")
369
+ if not path_env:
370
+ return None
371
+ found = which(name, path=path_env)
372
+ if found is None:
373
+ return None
374
+ candidate = Path(found).expanduser().resolve(strict=False)
375
+ if not candidate.exists() or not candidate.is_file() or not os.access(candidate, os.X_OK):
376
+ return None
377
+ return candidate
378
+
379
+
380
+ def _find_default_hwpxjs_executable(active_env: Mapping[str, str]) -> Path | None:
381
+ discovered = _find_executable("hwpxjs", active_env=active_env)
382
+ if discovered is not None:
383
+ return discovered
384
+ if active_env is not os.environ:
385
+ return None
386
+ for root in (Path.cwd(), Path(__file__).resolve().parents[4]):
387
+ candidate = root / "node_modules" / ".bin" / "hwpxjs"
388
+ if candidate.is_file() and os.access(candidate, os.X_OK):
389
+ return candidate.resolve(strict=False)
390
+ return None
391
+
392
+
393
+ def _find_default_libreoffice_executable(active_env: Mapping[str, str]) -> Path | None:
394
+ for executable_name in ("soffice", "libreoffice"):
395
+ discovered = _find_executable(executable_name, active_env=active_env)
396
+ if discovered is not None:
397
+ return discovered
398
+ return None
399
+
400
+
401
+ def _find_default_textutil_executable(active_env: Mapping[str, str]) -> Path | None:
402
+ return _find_executable("textutil", active_env=active_env)
403
+
404
+
405
+ def _find_default_excel_osascript_executable(active_env: Mapping[str, str]) -> Path | None:
406
+ excel_app = _find_microsoft_excel_app(active_env)
407
+ if excel_app is None:
408
+ return None
409
+ return _find_executable("osascript", active_env=active_env)
410
+
411
+
412
+ def _find_microsoft_excel_app(active_env: Mapping[str, str]) -> Path | None:
413
+ configured = active_env.get(_MICROSOFT_EXCEL_APP_ENV)
414
+ if configured:
415
+ candidate = Path(configured).expanduser().resolve(strict=False)
416
+ if candidate.exists() and candidate.is_dir():
417
+ return candidate
418
+ return None
419
+ if active_env is not os.environ:
420
+ return None
421
+ candidate = Path("/Applications/Microsoft Excel.app")
422
+ if candidate.exists() and candidate.is_dir():
423
+ return candidate
424
+ return None
425
+
426
+
427
+ def _converter_args_from_json(raw: str) -> tuple[str, ...]:
428
+ try:
429
+ decoded = json.loads(raw)
430
+ except json.JSONDecodeError as exc:
431
+ raise ValueError(f"{_HWP_TO_HWPX_CONVERTER_ARGS_ENV} must be a JSON string list") from exc
432
+ if not isinstance(decoded, list) or not decoded:
433
+ raise ValueError(f"{_HWP_TO_HWPX_CONVERTER_ARGS_ENV} must be a non-empty JSON list")
434
+ if not all(isinstance(item, str) and item for item in decoded):
435
+ raise ValueError(f"{_HWP_TO_HWPX_CONVERTER_ARGS_ENV} entries must be non-empty strings")
436
+ return tuple(decoded)
437
+
438
+
439
+ def _converter_timeout_from_env(raw: str | None) -> int:
440
+ if raw is None or raw == "":
441
+ return 30
442
+ try:
443
+ return int(raw)
444
+ except ValueError as exc:
445
+ raise ValueError(f"{_HWP_TO_HWPX_CONVERTER_TIMEOUT_ENV} must be an integer") from exc
446
+
447
+
448
+ def _file_sha256(path: Path) -> str:
449
+ digest = hashlib.sha256()
450
+ with path.open("rb") as stream:
451
+ for chunk in iter(lambda: stream.read(1024 * 1024), b""):
452
+ digest.update(chunk)
453
+ return digest.hexdigest()
454
+
455
+
456
+ def _process_output_summary(completed: subprocess.CompletedProcess[bytes]) -> str:
457
+ stdout = _truncate_process_bytes(completed.stdout)
458
+ stderr = _truncate_process_bytes(completed.stderr)
459
+ if stdout and stderr:
460
+ return f"stdout={stdout!r}; stderr={stderr!r}"
461
+ if stdout:
462
+ return f"stdout={stdout!r}"
463
+ if stderr:
464
+ return f"stderr={stderr!r}"
465
+ return "no process output"
466
+
467
+
468
+ def _truncate_process_bytes(payload: bytes, limit: int = 500) -> str:
469
+ text = payload.decode("utf-8", errors="replace").strip()
470
+ if len(text) <= limit:
471
+ return text
472
+ return f"{text[:limit]}..."
473
+
474
+
475
+ def _validate_output_payload(payload: bytes, output_format: DocumentFormat) -> None:
476
+ if output_format is DocumentFormat.hwpx:
477
+ _validate_hwpx_payload(payload)
478
+ return
479
+ if output_format in {DocumentFormat.docx, DocumentFormat.xlsx, DocumentFormat.pptx}:
480
+ _validate_ooxml_payload(payload, output_format)
481
+
482
+
483
+ def _validate_hwpx_payload(payload: bytes) -> None:
484
+ try:
485
+ with zipfile.ZipFile(io.BytesIO(payload)) as archive:
486
+ members = set(archive.namelist())
487
+ if "mimetype" not in members:
488
+ raise DocumentConversionEngineError("CLI output is not a valid HWPX package")
489
+ mimetype = archive.read("mimetype").decode("utf-8", errors="replace").strip()
490
+ if mimetype != "application/owpml":
491
+ raise DocumentConversionEngineError("CLI output is not a valid HWPX package")
492
+ if "Contents/header.xml" not in members:
493
+ raise DocumentConversionEngineError("CLI output is not a valid HWPX package")
494
+ has_section = any(
495
+ member.startswith("Contents/section") and member.endswith(".xml")
496
+ for member in members
497
+ )
498
+ if not has_section:
499
+ raise DocumentConversionEngineError("CLI output is not a valid HWPX package")
500
+ except zipfile.BadZipFile as exc:
501
+ raise DocumentConversionEngineError("CLI output is not a valid HWPX package") from exc
502
+
503
+
504
+ def _validate_ooxml_payload(payload: bytes, output_format: DocumentFormat) -> None:
505
+ marker_by_format = {
506
+ DocumentFormat.docx: "word/document.xml",
507
+ DocumentFormat.xlsx: "xl/workbook.xml",
508
+ DocumentFormat.pptx: "ppt/presentation.xml",
509
+ }
510
+ marker = marker_by_format[output_format]
511
+ try:
512
+ with zipfile.ZipFile(io.BytesIO(payload)) as archive:
513
+ members = set(archive.namelist())
514
+ if "[Content_Types].xml" not in members or marker not in members:
515
+ raise DocumentConversionEngineError(
516
+ f"CLI output is not a valid {output_format.value.upper()} package"
517
+ )
518
+ except zipfile.BadZipFile as exc:
519
+ raise DocumentConversionEngineError(
520
+ f"CLI output is not a valid {output_format.value.upper()} package"
521
+ ) from exc