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.
- package/README.md +15 -2
- package/bin/ummaya +10 -1
- package/npm-shrinkwrap.json +253 -2
- package/package.json +5 -1
- package/prompts/manifest.yaml +1 -1
- package/prompts/system_v1.md +1 -0
- package/pyproject.toml +26 -2
- package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
- package/src/ummaya/_canonical/__init__.py +2 -0
- package/src/ummaya/engine/engine.py +29 -132
- package/src/ummaya/evidence/__init__.py +21 -2
- package/src/ummaya/evidence/dataset_contract.py +193 -0
- package/src/ummaya/evidence/document_authoring_cases.py +33 -0
- package/src/ummaya/evidence/document_harness.py +313 -0
- package/src/ummaya/evidence/document_viewer_ux.py +391 -0
- package/src/ummaya/evidence/gates.py +70 -0
- package/src/ummaya/evidence/json_types.py +20 -0
- package/src/ummaya/evidence/models.py +88 -1
- package/src/ummaya/evidence/output_payload.py +89 -0
- package/src/ummaya/evidence/payload_documents.py +233 -0
- package/src/ummaya/evidence/route_contracts.py +224 -0
- package/src/ummaya/evidence/route_helpers.py +150 -0
- package/src/ummaya/evidence/runner.py +81 -212
- package/src/ummaya/evidence/source_provenance.py +246 -0
- package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
- package/src/ummaya/evidence/tool_layer.py +39 -0
- package/src/ummaya/evidence/tool_layer_models.py +151 -0
- package/src/ummaya/ipc/adapter_manifest_emitter.py +26 -10
- package/src/ummaya/ipc/document_intent_normalization.py +185 -0
- package/src/ummaya/ipc/frame_schema.py +5 -5
- package/src/ummaya/ipc/route_diagnostics.py +73 -0
- package/src/ummaya/ipc/stdio.py +1109 -477
- package/src/ummaya/llm/client.py +102 -3
- package/src/ummaya/llm/config.py +8 -3
- package/src/ummaya/primitives/__init__.py +6 -2
- package/src/ummaya/primitives/delegation.py +1 -1
- package/src/ummaya/primitives/document.py +28 -0
- package/src/ummaya/settings.py +0 -3
- package/src/ummaya/tools/discovery_bridge.py +17 -1
- package/src/ummaya/tools/documents/__init__.py +297 -0
- package/src/ummaya/tools/documents/adapter_registry.py +487 -0
- package/src/ummaya/tools/documents/archive_container_probe.py +167 -0
- package/src/ummaya/tools/documents/artifact_store.py +454 -0
- package/src/ummaya/tools/documents/authoring.py +283 -0
- package/src/ummaya/tools/documents/baselines.py +114 -0
- package/src/ummaya/tools/documents/capability.py +331 -0
- package/src/ummaya/tools/documents/contracts.py +112 -0
- package/src/ummaya/tools/documents/conversion.py +521 -0
- package/src/ummaya/tools/documents/diff.py +275 -0
- package/src/ummaya/tools/documents/engines.py +163 -0
- package/src/ummaya/tools/documents/evaluation.py +291 -0
- package/src/ummaya/tools/documents/explicit_values.py +108 -0
- package/src/ummaya/tools/documents/fixtures.py +174 -0
- package/src/ummaya/tools/documents/format_completion_audit.py +471 -0
- package/src/ummaya/tools/documents/formats/__init__.py +2 -0
- package/src/ummaya/tools/documents/formats/archive.py +528 -0
- package/src/ummaya/tools/documents/formats/base.py +41 -0
- package/src/ummaya/tools/documents/formats/code_file.py +211 -0
- package/src/ummaya/tools/documents/formats/data_file.py +272 -0
- package/src/ummaya/tools/documents/formats/hwp.py +284 -0
- package/src/ummaya/tools/documents/formats/hwpx.py +1837 -0
- package/src/ummaya/tools/documents/formats/odf.py +435 -0
- package/src/ummaya/tools/documents/formats/ooxml.py +1030 -0
- package/src/ummaya/tools/documents/formats/passive.py +766 -0
- package/src/ummaya/tools/documents/formats/pdf.py +702 -0
- package/src/ummaya/tools/documents/formats/text_web.py +268 -0
- package/src/ummaya/tools/documents/hwp_conversion_probe.py +178 -0
- package/src/ummaya/tools/documents/hwp_direct_candidate.py +141 -0
- package/src/ummaya/tools/documents/inspection.py +289 -0
- package/src/ummaya/tools/documents/intake.py +1079 -0
- package/src/ummaya/tools/documents/legacy_office_promotion_probe.py +366 -0
- package/src/ummaya/tools/documents/models.py +1598 -0
- package/src/ummaya/tools/documents/odf_promotion_probe.py +167 -0
- package/src/ummaya/tools/documents/orchestrator.py +96 -0
- package/src/ummaya/tools/documents/passive_capability_probe.py +251 -0
- package/src/ummaya/tools/documents/patch.py +170 -0
- package/src/ummaya/tools/documents/pdfa_conformance.py +284 -0
- package/src/ummaya/tools/documents/pdfa_promotion_probe.py +198 -0
- package/src/ummaya/tools/documents/permissions.py +110 -0
- package/src/ummaya/tools/documents/planner.py +616 -0
- package/src/ummaya/tools/documents/registry.py +2733 -0
- package/src/ummaya/tools/documents/render.py +978 -0
- package/src/ummaya/tools/documents/render_comparison.py +113 -0
- package/src/ummaya/tools/documents/render_comparison_models.py +74 -0
- package/src/ummaya/tools/documents/render_comparison_regions.py +73 -0
- package/src/ummaya/tools/documents/render_comparison_style.py +161 -0
- package/src/ummaya/tools/documents/reread.py +157 -0
- package/src/ummaya/tools/documents/runtime_authoring.py +244 -0
- package/src/ummaya/tools/documents/runtime_authoring_bundle.py +76 -0
- package/src/ummaya/tools/documents/scorecard.py +184 -0
- package/src/ummaya/tools/documents/socratic_planner.py +193 -0
- package/src/ummaya/tools/documents/style.py +48 -0
- package/src/ummaya/tools/documents/tool_defs.py +523 -0
- package/src/ummaya/tools/documents/validate.py +347 -0
- package/src/ummaya/tools/executor.py +29 -0
- package/src/ummaya/tools/live_proxy.py +0 -3
- package/src/ummaya/tools/models.py +5 -1
- package/src/ummaya/tools/register_all.py +8 -0
- package/src/ummaya/tools/registry.py +10 -1
- package/src/ummaya/tools/routing/__init__.py +59 -0
- package/src/ummaya/tools/routing/builder.py +105 -0
- package/src/ummaya/tools/routing/cards.py +29 -0
- package/src/ummaya/tools/routing/decision_service.py +534 -0
- package/src/ummaya/tools/routing/decision_types.py +74 -0
- package/src/ummaya/tools/routing/feasibility.py +122 -0
- package/src/ummaya/tools/routing/intent.py +17 -0
- package/src/ummaya/tools/routing/intent_extractor.py +207 -0
- package/src/ummaya/tools/routing/intent_patterns.py +160 -0
- package/src/ummaya/tools/routing/intent_public_data.py +150 -0
- package/src/ummaya/tools/routing/intent_types.py +48 -0
- package/src/ummaya/tools/routing/lint.py +78 -0
- package/src/ummaya/tools/routing/metadata.py +174 -0
- package/src/ummaya/tools/routing/projection.py +340 -0
- package/src/ummaya/tools/routing/retrieval_policy.py +629 -0
- package/src/ummaya/tools/routing/schema.py +81 -0
- package/src/ummaya/tools/routing/types.py +96 -0
- package/src/ummaya/tools/routing_index.py +2 -2
- package/src/ummaya/tools/search.py +34 -746
- package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
- package/tui/package.json +1 -1
- package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
- package/tui/src/QueryEngine.ts +12 -8
- package/tui/src/bridge/inboundAttachments.ts +3 -3
- package/tui/src/cli/handlers/auth.ts +3 -12
- package/tui/src/cli/print.ts +7 -7
- package/tui/src/commands/insights.ts +1 -1
- package/tui/src/commands/install-github-app/types.ts +8 -30
- package/tui/src/commands/plugin/types.ts +6 -28
- package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
- package/tui/src/commands/rename/generateSessionName.ts +1 -1
- package/tui/src/components/Feedback.tsx +1 -1
- package/tui/src/components/LogoV2/EmergencyTip.tsx +11 -2
- package/tui/src/components/LogoV2/WelcomeV2.tsx +1 -3
- package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
- package/tui/src/components/Spinner/types.ts +6 -28
- package/tui/src/components/agents/generateAgent.ts +1 -1
- package/tui/src/components/agents/new-agent-creation/types.ts +4 -26
- package/tui/src/components/config/EnvSecretIsolatedEditor.tsx +1 -1
- package/tui/src/components/mcp/types.ts +16 -38
- package/tui/src/components/messages/AssistantToolUseMessage.tsx +3 -2
- package/tui/src/components/messages/UserCrossSessionMessage.ts +16 -4
- package/tui/src/components/messages/UserForkBoilerplateMessage.ts +16 -4
- package/tui/src/components/messages/UserGitHubWebhookMessage.ts +16 -4
- package/tui/src/components/messages/UserToolResultMessage/utils.tsx +3 -2
- package/tui/src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.ts +9 -4
- package/tui/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.ts +9 -4
- package/tui/src/components/primitive/DocumentSocraticReviewBlock.tsx +129 -0
- package/tui/src/components/primitive/DocumentToolResultCard.tsx +224 -0
- package/tui/src/components/primitive/documentSocraticReview.ts +215 -0
- package/tui/src/components/primitive/index.tsx +43 -1
- package/tui/src/components/primitive/types.ts +137 -0
- package/tui/src/components/ui/option.ts +4 -26
- package/tui/src/constants/common.ts +0 -2
- package/tui/src/constants/prompts.ts +4 -3
- package/tui/src/constants/querySource.ts +4 -26
- package/tui/src/entrypoints/sdk/controlTypes.ts +26 -48
- package/tui/src/entrypoints/sdk/coreTypes.generated.ts +3 -25
- package/tui/src/entrypoints/sdk/runtimeTypes.ts +38 -60
- package/tui/src/entrypoints/sdk/sdkUtilityTypes.ts +4 -26
- package/tui/src/entrypoints/sdk/settingsTypes.generated.ts +3 -25
- package/tui/src/entrypoints/sdk/toolTypes.ts +3 -25
- package/tui/src/hooks/toolPermission/handlers/interactiveHandler.ts +10 -0
- package/tui/src/hooks/useApiKeyVerification.ts +1 -1
- package/tui/src/hooks/useVirtualScroll.ts +1 -1
- package/tui/src/ink/ink.tsx +33 -14
- package/tui/src/ink/reconciler.ts +2 -3
- package/tui/src/ink/render-to-screen.ts +30 -10
- package/tui/src/ipc/bridge.ts +62 -15
- package/tui/src/ipc/bridgeSingleton.ts +5 -1
- package/tui/src/ipc/codec.ts +3 -3
- package/tui/src/ipc/frames.generated.ts +12 -12
- package/tui/src/ipc/llmClient.ts +151 -27
- package/tui/src/ipc/schema/frame.schema.json +1 -1
- package/tui/src/keybindings/defaultBindings.ts +4 -0
- package/tui/src/main.tsx +29 -11
- package/tui/src/native-ts/file-index/index.ts +33 -3
- package/tui/src/observability/surface.ts +2 -2
- package/tui/src/probes/toolRegistryProbe.tsx +3 -1
- package/tui/src/projectOnboardingState.ts +7 -6
- package/tui/src/query/chatMessageTypes.ts +18 -0
- package/tui/src/query/chatMessagesBuilder.ts +1 -1
- package/tui/src/query/deps.ts +1 -1
- package/tui/src/query/messageGuards.ts +106 -0
- package/tui/src/query/publicDataTerminalRepair.ts +384 -0
- package/tui/src/query/run.ts +1075 -0
- package/tui/src/query/supportBoundary.ts +168 -0
- package/tui/src/query/toolResultErrors.ts +103 -0
- package/tui/src/query/toolRunner.ts +687 -0
- package/tui/src/query/unavailableToolRepair.ts +118 -0
- package/tui/src/query.ts +9 -2186
- package/tui/src/screens/REPL.tsx +40 -29
- package/tui/src/services/api/adapterManifest.ts +4 -0
- package/tui/src/services/api/backendChat/events.ts +117 -0
- package/tui/src/services/api/backendChat/finalMessage.ts +40 -0
- package/tui/src/services/api/backendChat/frame.ts +9 -0
- package/tui/src/services/api/backendChat/streaming.ts +430 -0
- package/tui/src/services/api/backendChat/types.ts +62 -0
- package/tui/src/services/api/backendChat.ts +1 -0
- package/tui/src/services/api/client.ts +65 -2
- package/tui/src/services/api/errorUtils.ts +5 -5
- package/tui/src/services/api/errors.ts +1 -1
- package/tui/src/services/api/logging.ts +1 -1
- package/tui/src/services/api/ummaya/evidence.ts +194 -0
- package/tui/src/services/api/ummaya/messages.ts +255 -0
- package/tui/src/services/api/ummaya/nonStreaming.ts +66 -0
- package/tui/src/services/api/ummaya/provider.ts +200 -0
- package/tui/src/services/api/ummaya/reasoning.ts +24 -0
- package/tui/src/services/api/ummaya/request.ts +200 -0
- package/tui/src/services/api/ummaya/selectionContext.ts +240 -0
- package/tui/src/services/api/ummaya/streaming.ts +365 -0
- package/tui/src/services/api/ummaya/streamingPayload.ts +129 -0
- package/tui/src/services/api/ummaya/streamingReader.ts +40 -0
- package/tui/src/services/api/ummaya/toolSelection.ts +217 -0
- package/tui/src/services/api/ummaya/types.ts +110 -0
- package/tui/src/services/api/ummaya/usage.ts +30 -0
- package/tui/src/services/api/ummaya.ts +26 -418
- package/tui/src/services/api/withRetry.ts +1 -1
- package/tui/src/services/awaySummary.ts +2 -2
- package/tui/src/services/claudeAiLimits.ts +1 -1
- package/tui/src/services/compact/autoCompact.ts +1 -1
- package/tui/src/services/compact/compact.ts +1 -1
- package/tui/src/services/lsp/types.ts +8 -30
- package/tui/src/services/tips/types.ts +6 -28
- package/tui/src/services/tokenEstimation.ts +1 -1
- package/tui/src/services/toolRegistry/bootGuard.ts +5 -5
- package/tui/src/services/toolUseSummary/toolUseSummaryGenerator.ts +1 -1
- package/tui/src/services/tools/toolExecution.ts +94 -1
- package/tui/src/store/pendingPermissionSlot.ts +1 -1
- package/tui/src/store/session-store.ts +10 -36
- package/tui/src/stubs/any-stub.ts +15 -10
- package/tui/src/stubs/color-diff-napi.ts +37 -23
- package/tui/src/stubs/globals.d.ts +3 -3
- package/tui/src/stubs/macro-preload.ts +23 -12
- package/tui/src/tools/AdapterTool/AdapterTool.ts +1207 -714
- package/tui/src/tools/AdapterTool/routeDiagnostics.ts +75 -0
- package/tui/src/tools/AgentTool/AgentTool.tsx +84 -1371
- package/tui/src/tools/AgentTool/agentToolHandoff.ts +114 -0
- package/tui/src/tools/AgentTool/agentToolPartialResult.ts +16 -0
- package/tui/src/tools/AgentTool/agentToolProgress.ts +32 -0
- package/tui/src/tools/AgentTool/agentToolResolver.ts +161 -0
- package/tui/src/tools/AgentTool/agentToolResult.ts +163 -0
- package/tui/src/tools/AgentTool/agentToolUtils.ts +14 -686
- package/tui/src/tools/AgentTool/asyncAgentLifecycle.ts +208 -0
- package/tui/src/tools/AgentTool/asyncLifecycle.ts +153 -0
- package/tui/src/tools/AgentTool/backgroundedCompletion.ts +126 -0
- package/tui/src/tools/AgentTool/backgroundedLifecycle.ts +174 -0
- package/tui/src/tools/AgentTool/foregroundBackground.ts +83 -0
- package/tui/src/tools/AgentTool/foregroundDrain.tsx +133 -0
- package/tui/src/tools/AgentTool/foregroundFinalize.ts +98 -0
- package/tui/src/tools/AgentTool/foregroundLifecycle.tsx +237 -0
- package/tui/src/tools/AgentTool/foregroundProgress.tsx +169 -0
- package/tui/src/tools/AgentTool/foregroundTask.ts +89 -0
- package/tui/src/tools/AgentTool/forkSubagent.ts +1 -12
- package/tui/src/tools/AgentTool/forkSubagentGate.ts +34 -0
- package/tui/src/tools/AgentTool/launchRouting.ts +203 -0
- package/tui/src/tools/AgentTool/lifecycle.ts +244 -0
- package/tui/src/tools/AgentTool/mcpRouting.ts +73 -0
- package/tui/src/tools/AgentTool/orchestrationSupport.ts +70 -0
- package/tui/src/tools/AgentTool/permissions.ts +39 -0
- package/tui/src/tools/AgentTool/promptSetup.ts +181 -0
- package/tui/src/tools/AgentTool/remoteRouting.ts +62 -0
- package/tui/src/tools/AgentTool/resultMapping.ts +116 -0
- package/tui/src/tools/AgentTool/resumeAgent.ts +39 -107
- package/tui/src/tools/AgentTool/resumeAgentHelpers.ts +140 -0
- package/tui/src/tools/AgentTool/runAgent.ts +1 -1
- package/tui/src/tools/AgentTool/runtimeConfig.ts +57 -0
- package/tui/src/tools/AgentTool/schemas.ts +196 -0
- package/tui/src/tools/AgentTool/sourceVerificationPropagation.ts +263 -0
- package/tui/src/tools/AgentTool/worktreeLifecycle.ts +105 -0
- package/tui/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +174 -202
- package/tui/src/tools/BashTool/BashTool.tsx +71 -1072
- package/tui/src/tools/BashTool/bashCommandHelpers.ts +12 -12
- package/tui/src/tools/BashTool/bashPermissions/astPreflight.ts +173 -0
- package/tui/src/tools/BashTool/bashPermissions/classifierChecks.ts +199 -0
- package/tui/src/tools/BashTool/bashPermissions/compoundGuards.ts +53 -0
- package/tui/src/tools/BashTool/bashPermissions/constants.ts +99 -0
- package/tui/src/tools/BashTool/bashPermissions/index.ts +38 -0
- package/tui/src/tools/BashTool/bashPermissions/legacyMisparsing.ts +62 -0
- package/tui/src/tools/BashTool/bashPermissions/main.ts +135 -0
- package/tui/src/tools/BashTool/bashPermissions/normalizedCommands.ts +33 -0
- package/tui/src/tools/BashTool/bashPermissions/operatorFlow.ts +98 -0
- package/tui/src/tools/BashTool/bashPermissions/permissionChecks.ts +200 -0
- package/tui/src/tools/BashTool/bashPermissions/prefixSuggestions.ts +88 -0
- package/tui/src/tools/BashTool/bashPermissions/promptClassifierRules.ts +125 -0
- package/tui/src/tools/BashTool/bashPermissions/ruleDelegates.ts +19 -0
- package/tui/src/tools/BashTool/bashPermissions/ruleMatching.ts +145 -0
- package/tui/src/tools/BashTool/bashPermissions/sandboxAutoAllow.ts +75 -0
- package/tui/src/tools/BashTool/bashPermissions/subcommandFlow.ts +205 -0
- package/tui/src/tools/BashTool/bashPermissions/subcommandGuards.ts +73 -0
- package/tui/src/tools/BashTool/bashPermissions/subcommandResultHelpers.ts +116 -0
- package/tui/src/tools/BashTool/bashPermissions/types.ts +26 -0
- package/tui/src/tools/BashTool/bashPermissions/wrapperStripping.ts +139 -0
- package/tui/src/tools/BashTool/bashPermissions.ts +26 -2621
- package/tui/src/tools/BashTool/call.ts +202 -0
- package/tui/src/tools/BashTool/callLoader.ts +35 -0
- package/tui/src/tools/BashTool/commandClassification.ts +151 -0
- package/tui/src/tools/BashTool/commandClassificationLoader.ts +40 -0
- package/tui/src/tools/BashTool/cwdReset.ts +33 -0
- package/tui/src/tools/BashTool/lineTruncation.ts +11 -0
- package/tui/src/tools/BashTool/modeValidation.ts +13 -1
- package/tui/src/tools/BashTool/outputPersistence.ts +42 -0
- package/tui/src/tools/BashTool/permissionClassification.ts +66 -0
- package/tui/src/tools/BashTool/permissionLoader.ts +44 -0
- package/tui/src/tools/BashTool/resultLoader.ts +29 -0
- package/tui/src/tools/BashTool/resultMapping.ts +83 -0
- package/tui/src/tools/BashTool/sandboxPolicy.ts +79 -0
- package/tui/src/tools/BashTool/schemas.ts +65 -0
- package/tui/src/tools/BashTool/sedEditExecution.ts +59 -0
- package/tui/src/tools/BashTool/shellExecution.tsx +245 -0
- package/tui/src/tools/BashTool/shellOutputUtils.ts +85 -0
- package/tui/src/tools/BashTool/shellPermissionGauntlet.ts +97 -0
- package/tui/src/tools/BashTool/uiLoader.ts +37 -0
- package/tui/src/tools/BriefTool/upload.ts +1 -1
- package/tui/src/tools/CalculatorTool/parser.ts +2 -2
- package/tui/src/tools/DocumentPrimitive/DocumentPrimitive.ts +262 -0
- package/tui/src/tools/DocumentPrimitive/dispatchNormalization.ts +270 -0
- package/tui/src/tools/DocumentPrimitive/documentDestinationPath.ts +18 -0
- package/tui/src/tools/DocumentPrimitive/documentMutationGuard.ts +22 -0
- package/tui/src/tools/DocumentPrimitive/documentPatchNormalization.ts +248 -0
- package/tui/src/tools/DocumentPrimitive/documentSourceVerification.ts +245 -0
- package/tui/src/tools/DocumentPrimitive/documentSourceVerificationFields.ts +103 -0
- package/tui/src/tools/DocumentPrimitive/modelVisibleOutput.ts +40 -0
- package/tui/src/tools/DocumentPrimitive/prompt.ts +35 -0
- package/tui/src/tools/FileEditTool/FileEditTool.ts +9 -507
- package/tui/src/tools/FileEditTool/call.ts +228 -0
- package/tui/src/tools/FileEditTool/validateInput.ts +196 -0
- package/tui/src/tools/FileReadTool/imageProcessor.ts +13 -0
- package/tui/src/tools/FileWriteTool/FileWriteTool.ts +7 -300
- package/tui/src/tools/FileWriteTool/call.ts +223 -0
- package/tui/src/tools/FileWriteTool/validateInput.ts +80 -0
- package/tui/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +19 -3
- package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +25 -32
- package/tui/src/tools/LookupPrimitive/prompt.ts +0 -2
- package/tui/src/tools/MCPTool/trustPolicy.ts +118 -0
- package/tui/src/tools/McpAuthTool/McpAuthTool.ts +21 -3
- package/tui/src/tools/NotebookEditTool/NotebookEditTool.ts +7 -326
- package/tui/src/tools/NotebookEditTool/call.ts +254 -0
- package/tui/src/tools/NotebookEditTool/notebookModel.ts +51 -0
- package/tui/src/tools/NotebookEditTool/validateInput.ts +142 -0
- package/tui/src/tools/PowerShellTool/PowerShellTool.tsx +46 -937
- package/tui/src/tools/PowerShellTool/acceptEditsCommandValidation.ts +162 -0
- package/tui/src/tools/PowerShellTool/call.ts +179 -0
- package/tui/src/tools/PowerShellTool/callLoader.ts +37 -0
- package/tui/src/tools/PowerShellTool/commandClassification.ts +86 -0
- package/tui/src/tools/PowerShellTool/modeValidation.ts +25 -332
- package/tui/src/tools/PowerShellTool/outputPersistence.ts +42 -0
- package/tui/src/tools/PowerShellTool/permissionClassification.ts +28 -0
- package/tui/src/tools/PowerShellTool/resultLoader.ts +31 -0
- package/tui/src/tools/PowerShellTool/resultMapping.ts +75 -0
- package/tui/src/tools/PowerShellTool/schemas.ts +40 -0
- package/tui/src/tools/PowerShellTool/shellExecution.tsx +258 -0
- package/tui/src/tools/PowerShellTool/symlinkModeValidation.ts +44 -0
- package/tui/src/tools/PowerShellTool/uiLoader.ts +37 -0
- package/tui/src/tools/PowerShellTool/validation.ts +39 -0
- package/tui/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +19 -3
- package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +1 -11
- package/tui/src/tools/ResolveLocationPrimitive/prompt.ts +2 -6
- package/tui/src/tools/SkillTool/SkillTool.ts +2 -2
- package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +27 -10
- package/tui/src/tools/TaskCreateTool/TaskCreateTool.ts +16 -2
- package/tui/src/tools/TaskGetTool/TaskGetTool.ts +23 -3
- package/tui/src/tools/TaskListTool/TaskListTool.ts +22 -4
- package/tui/src/tools/TaskOutputTool/TaskOutputTool.tsx +46 -547
- package/tui/src/tools/TaskOutputTool/lookup.ts +216 -0
- package/tui/src/tools/TaskOutputTool/render.tsx +257 -0
- package/tui/src/tools/TaskOutputTool/schemas.ts +55 -0
- package/tui/src/tools/TaskOutputTool/serialization.ts +36 -0
- package/tui/src/tools/TaskStopTool/TaskStopTool.ts +10 -0
- package/tui/src/tools/TaskUpdateTool/TaskUpdateTool.ts +14 -364
- package/tui/src/tools/TaskUpdateTool/completion.ts +62 -0
- package/tui/src/tools/TaskUpdateTool/schemas.ts +62 -0
- package/tui/src/tools/TaskUpdateTool/serialization.ts +46 -0
- package/tui/src/tools/TaskUpdateTool/statusUpdate.ts +247 -0
- package/tui/src/tools/TodoWriteTool/TodoWriteTool.ts +21 -2
- package/tui/src/tools/ToolSearchTool/ToolSearchTool.ts +21 -302
- package/tui/src/tools/ToolSearchTool/ccSupportTools.ts +223 -0
- package/tui/src/tools/ToolSearchTool/descriptionCache.ts +50 -0
- package/tui/src/tools/ToolSearchTool/keywordSearch.ts +216 -0
- package/tui/src/tools/ToolSearchTool/prompt.ts +10 -4
- package/tui/src/tools/ToolSearchTool/resultMapping.ts +30 -0
- package/tui/src/tools/ToolSearchTool/schemas.ts +30 -0
- package/tui/src/tools/ToolSearchTool/searchPool.ts +47 -0
- package/tui/src/tools/ToolSearchTool/supportIntentHints.ts +140 -0
- package/tui/src/tools/TranslateTool/TranslateTool.ts +1 -1
- package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +2 -1
- package/tui/src/tools/WebFetchTool/WebFetchTool.ts +43 -138
- package/tui/src/tools/WebFetchTool/call.ts +227 -0
- package/tui/src/tools/WebFetchTool/resolvedAddressSafety.ts +78 -0
- package/tui/src/tools/WebFetchTool/sourceVerification.ts +204 -0
- package/tui/src/tools/WebFetchTool/types.ts +23 -0
- package/tui/src/tools/WebFetchTool/urlSafety.ts +181 -0
- package/tui/src/tools/WebFetchTool/utils.ts +1 -1
- package/tui/src/tools/WebSearchTool/UI.tsx +0 -1
- package/tui/src/tools/WebSearchTool/WebSearchTool.ts +9 -313
- package/tui/src/tools/WebSearchTool/call.ts +33 -0
- package/tui/src/tools/WebSearchTool/responseMapping.ts +190 -0
- package/tui/src/tools/WebSearchTool/resultBlock.ts +47 -0
- package/tui/src/tools/WebSearchTool/schemas.ts +47 -0
- package/tui/src/tools/WebSearchTool/toolSchema.ts +12 -0
- package/tui/src/tools/WorkspaceToolAdapter/WorkspaceToolAdapter.ts +79 -0
- package/tui/src/tools/WorkspaceToolAdapter/allowedRootPolicy.ts +85 -0
- package/tui/src/tools/WorkspaceToolAdapter/documentFormatGuards.ts +73 -0
- package/tui/src/tools/WorkspaceToolAdapter/inputNormalization.ts +105 -0
- package/tui/src/tools/WorkspaceToolAdapter/mcpExposurePolicy.ts +64 -0
- package/tui/src/tools/WorkspaceToolAdapter/toolDefFactory.ts +215 -0
- package/tui/src/tools/WorkspaceToolAdapter/toolNames.ts +6 -0
- package/tui/src/tools/WorkspaceToolAdapter/workspacePolicy.ts +15 -0
- package/tui/src/tools/_shared/dispatchPrimitive.ts +6 -6
- package/tui/src/tools/_shared/documentChangeToPatch.ts +125 -0
- package/tui/src/tools/_shared/documentDispatchArguments.ts +87 -0
- package/tui/src/tools/_shared/documentPrimitiveTimeout.ts +13 -0
- package/tui/src/tools/_shared/documentToolResultRender.ts +98 -0
- package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
- package/tui/src/tools/_shared/rootPrimitiveInput.ts +1 -0
- package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPatterns.ts +58 -0
- package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPrompt.ts +271 -0
- package/tui/src/tools/_shared/toolChoiceRepair/documentRepair.ts +452 -0
- package/tui/src/tools/_shared/toolChoiceRepair/messageAccess.ts +80 -0
- package/tui/src/tools/_shared/toolChoiceRepair/publicDataRepair.ts +92 -0
- package/tui/src/tools/_shared/toolChoiceRepair/supportRepair.ts +135 -0
- package/tui/src/tools/_shared/toolChoiceRepair.ts +55 -860
- package/tui/src/tools/shared/mockDisclaimer.ts +1 -1
- package/tui/src/tools.ts +39 -190
- package/tui/src/types/fileSuggestion.ts +4 -26
- package/tui/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +186 -148
- package/tui/src/types/generated/events_mono/common/v1/auth.ts +25 -11
- package/tui/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +47 -30
- package/tui/src/types/generated/google/protobuf/timestamp.ts +21 -7
- package/tui/src/types/message.ts +80 -102
- package/tui/src/types/messageQueueTypes.ts +6 -28
- package/tui/src/types/notebook.ts +16 -38
- package/tui/src/types/statusLine.ts +4 -26
- package/tui/src/types/tools.ts +24 -46
- package/tui/src/types/utils.ts +6 -28
- package/tui/src/upstreamproxy/relay.ts +7 -3
- package/tui/src/upstreamproxy/upstreamproxy.ts +1 -1
- package/tui/src/utils/assistantMessageFactories.ts +9 -3
- package/tui/src/utils/auth.ts +129 -139
- package/tui/src/utils/bash/ast.ts +23 -23
- package/tui/src/utils/bash/bashParser.ts +5 -5
- package/tui/src/utils/billing.ts +1 -1
- package/tui/src/utils/collapseReadSearch.ts +3 -3
- package/tui/src/utils/cronTasks.ts +1 -1
- package/tui/src/utils/execFileNoThrow.ts +1 -1
- package/tui/src/utils/filePersistence/types.ts +16 -38
- package/tui/src/utils/forkedAgent.ts +1 -1
- package/tui/src/utils/gracefulShutdown.ts +4 -4
- package/tui/src/utils/heapDumpService.ts +12 -8
- package/tui/src/utils/hooks/apiQueryHookHelper.ts +1 -1
- package/tui/src/utils/hooks/execPromptHook.ts +1 -1
- package/tui/src/utils/hooks/skillImprovement.ts +1 -1
- package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
- package/tui/src/utils/messages.ts +18 -0
- package/tui/src/utils/migrateSessions.ts +3 -3
- package/tui/src/utils/model/model.ts +6 -6
- package/tui/src/utils/permissions/yoloClassifier.ts +1 -1
- package/tui/src/utils/plugins/headlessPluginInstall.ts +1 -1
- package/tui/src/utils/plugins/mcpPluginIntegration.ts +1 -1
- package/tui/src/utils/plugins/mcpbHandler.ts +1 -1
- package/tui/src/utils/plugins/pluginLoader.ts +8 -8
- package/tui/src/utils/protectedNamespace.ts +5 -3
- package/tui/src/utils/rawJsonToolCall.ts +242 -0
- package/tui/src/utils/ripgrep.ts +16 -7
- package/tui/src/utils/sessionTitle.ts +1 -1
- package/tui/src/utils/settings/permissionValidation.ts +14 -2
- package/tui/src/utils/shell/prefix.ts +1 -1
- package/tui/src/utils/sideQuery.ts +1 -1
- package/tui/src/utils/systemThemeWatcher.ts +13 -3
- package/tui/src/utils/teleport.tsx +1 -1
- package/uv.lock +400 -14
- package/tui/src/services/api/claude.ts +0 -3540
- package/tui/src/tools/_shared/directPublicDataGuard.ts +0 -362
- package/tui/src/tools/_shared/kmaAnalysisGuard.ts +0 -197
- package/tui/src/tools/_shared/kmaAviationGuard.ts +0 -70
- package/tui/src/tools/_shared/nmcAedGuard.ts +0 -234
- package/tui/src/tools/_shared/protectedCheckGuard.ts +0 -207
- 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
|