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,487 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Format adapter registry for the single document primitive."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from ummaya.tools.documents.engines import DocumentEngineRegistry, DocumentInspectionEngine
|
|
10
|
+
from ummaya.tools.documents.formats.base import DocumentFormatAdapter
|
|
11
|
+
from ummaya.tools.documents.models import DocumentExtraction, DocumentFormat, KnownDocumentFormat
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ummaya.tools.documents.tool_defs import DocumentFieldPatch
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
_ODF_PROMOTED_FORMATS = (DocumentFormat.odt, DocumentFormat.ods, DocumentFormat.odp)
|
|
18
|
+
_TEXT_WEB_PROMOTED_FORMATS = (
|
|
19
|
+
DocumentFormat.html,
|
|
20
|
+
DocumentFormat.htm,
|
|
21
|
+
DocumentFormat.txt,
|
|
22
|
+
DocumentFormat.rtf,
|
|
23
|
+
DocumentFormat.md,
|
|
24
|
+
)
|
|
25
|
+
_DATA_PROMOTED_FORMATS = (
|
|
26
|
+
DocumentFormat.csv,
|
|
27
|
+
DocumentFormat.tsv,
|
|
28
|
+
DocumentFormat.xml,
|
|
29
|
+
DocumentFormat.rdf,
|
|
30
|
+
DocumentFormat.ttl,
|
|
31
|
+
DocumentFormat.lod,
|
|
32
|
+
DocumentFormat.json,
|
|
33
|
+
DocumentFormat.jsonl,
|
|
34
|
+
DocumentFormat.yaml,
|
|
35
|
+
DocumentFormat.yml,
|
|
36
|
+
DocumentFormat.geojson,
|
|
37
|
+
DocumentFormat.gpx,
|
|
38
|
+
DocumentFormat.kml,
|
|
39
|
+
DocumentFormat.fasta,
|
|
40
|
+
DocumentFormat.sgml,
|
|
41
|
+
DocumentFormat.dtd,
|
|
42
|
+
DocumentFormat.hml,
|
|
43
|
+
DocumentFormat.etc,
|
|
44
|
+
)
|
|
45
|
+
_ARCHIVE_PROMOTED_FORMATS = (
|
|
46
|
+
DocumentFormat.epub,
|
|
47
|
+
DocumentFormat.zip,
|
|
48
|
+
DocumentFormat.seven_z,
|
|
49
|
+
DocumentFormat.tar,
|
|
50
|
+
DocumentFormat.gz,
|
|
51
|
+
)
|
|
52
|
+
_CODE_PROMOTED_FORMATS = (DocumentFormat.python,)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class UnsupportedDocumentAdapterError(LookupError):
|
|
56
|
+
"""Raised when no document adapter is registered for a format."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, document_format: DocumentFormat | KnownDocumentFormat) -> None:
|
|
59
|
+
super().__init__(f"No document adapter registered for {document_format.value}")
|
|
60
|
+
self.document_format = document_format
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class DocumentAdapterRegistry:
|
|
64
|
+
"""Session-local registry of format-scoped document adapters."""
|
|
65
|
+
|
|
66
|
+
def __init__(self) -> None:
|
|
67
|
+
self._by_adapter_id: dict[str, DocumentFormatAdapter] = {}
|
|
68
|
+
self._by_known_format: dict[KnownDocumentFormat, DocumentFormatAdapter] = {}
|
|
69
|
+
self._by_promoted_format: dict[DocumentFormat, DocumentFormatAdapter] = {}
|
|
70
|
+
|
|
71
|
+
def register(self, adapter: DocumentFormatAdapter) -> None:
|
|
72
|
+
"""Register one adapter for its known and promoted formats."""
|
|
73
|
+
self._validate_registration(adapter)
|
|
74
|
+
|
|
75
|
+
self._by_adapter_id[adapter.adapter_id] = adapter
|
|
76
|
+
for known_format in adapter.known_formats:
|
|
77
|
+
self._by_known_format[known_format] = adapter
|
|
78
|
+
for promoted_format in adapter.promoted_formats:
|
|
79
|
+
self._by_promoted_format[promoted_format] = adapter
|
|
80
|
+
|
|
81
|
+
def _validate_registration(self, adapter: DocumentFormatAdapter) -> None:
|
|
82
|
+
"""Validate adapter metadata before mutating registry state."""
|
|
83
|
+
if not adapter.adapter_id:
|
|
84
|
+
raise ValueError("adapter_id is required")
|
|
85
|
+
if adapter.adapter_id in self._by_adapter_id:
|
|
86
|
+
raise ValueError(f"adapter_id already registered: {adapter.adapter_id}")
|
|
87
|
+
if not adapter.known_formats:
|
|
88
|
+
raise ValueError("adapter must declare at least one known format")
|
|
89
|
+
|
|
90
|
+
self._validate_format_sets(adapter)
|
|
91
|
+
self._validate_no_existing_registration(adapter)
|
|
92
|
+
|
|
93
|
+
def _validate_format_sets(self, adapter: DocumentFormatAdapter) -> None:
|
|
94
|
+
"""Validate one adapter's own format declarations."""
|
|
95
|
+
if len(set(adapter.known_formats)) != len(adapter.known_formats):
|
|
96
|
+
raise ValueError("adapter known formats must be unique")
|
|
97
|
+
if len(set(adapter.promoted_formats)) != len(adapter.promoted_formats):
|
|
98
|
+
raise ValueError("adapter promoted formats must be unique")
|
|
99
|
+
|
|
100
|
+
known_formats = set(adapter.known_formats)
|
|
101
|
+
for promoted_format in adapter.promoted_formats:
|
|
102
|
+
if KnownDocumentFormat(promoted_format.value) not in known_formats:
|
|
103
|
+
raise ValueError("adapter promoted format must also be declared as a known format")
|
|
104
|
+
|
|
105
|
+
def _validate_no_existing_registration(self, adapter: DocumentFormatAdapter) -> None:
|
|
106
|
+
"""Validate that the registry has no conflicting adapter mapping."""
|
|
107
|
+
for known_format in adapter.known_formats:
|
|
108
|
+
if known_format in self._by_known_format:
|
|
109
|
+
raise ValueError(f"known format already registered: {known_format.value}")
|
|
110
|
+
|
|
111
|
+
for promoted_format in adapter.promoted_formats:
|
|
112
|
+
if promoted_format in self._by_promoted_format:
|
|
113
|
+
raise ValueError(f"promoted format already registered: {promoted_format.value}")
|
|
114
|
+
|
|
115
|
+
def get_known(
|
|
116
|
+
self,
|
|
117
|
+
document_format: KnownDocumentFormat,
|
|
118
|
+
) -> DocumentFormatAdapter | None:
|
|
119
|
+
"""Return the adapter for a known format, if registered."""
|
|
120
|
+
return self._by_known_format.get(document_format)
|
|
121
|
+
|
|
122
|
+
def require_known(self, document_format: KnownDocumentFormat) -> DocumentFormatAdapter:
|
|
123
|
+
"""Return the adapter for a known format or fail closed."""
|
|
124
|
+
adapter = self.get_known(document_format)
|
|
125
|
+
if adapter is None:
|
|
126
|
+
raise UnsupportedDocumentAdapterError(document_format)
|
|
127
|
+
return adapter
|
|
128
|
+
|
|
129
|
+
def get_promoted(
|
|
130
|
+
self,
|
|
131
|
+
document_format: DocumentFormat,
|
|
132
|
+
) -> DocumentFormatAdapter | None:
|
|
133
|
+
"""Return the adapter for a promoted runtime format, if registered."""
|
|
134
|
+
return self._by_promoted_format.get(document_format)
|
|
135
|
+
|
|
136
|
+
def require_promoted(self, document_format: DocumentFormat) -> DocumentFormatAdapter:
|
|
137
|
+
"""Return the adapter for a promoted runtime format or fail closed."""
|
|
138
|
+
adapter = self.get_promoted(document_format)
|
|
139
|
+
if adapter is None:
|
|
140
|
+
raise UnsupportedDocumentAdapterError(document_format)
|
|
141
|
+
return adapter
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class EngineBackedDocumentAdapter:
|
|
145
|
+
"""Adapter wrapper around one promoted inspection engine."""
|
|
146
|
+
|
|
147
|
+
def __init__(
|
|
148
|
+
self,
|
|
149
|
+
*,
|
|
150
|
+
adapter_id: str,
|
|
151
|
+
known_formats: tuple[KnownDocumentFormat, ...],
|
|
152
|
+
promoted_formats: tuple[DocumentFormat, ...],
|
|
153
|
+
inspection_engine: DocumentInspectionEngine,
|
|
154
|
+
) -> None:
|
|
155
|
+
self.adapter_id = adapter_id
|
|
156
|
+
self.known_formats = known_formats
|
|
157
|
+
self.promoted_formats = promoted_formats
|
|
158
|
+
self._inspection_engine = inspection_engine
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def engine_id(self) -> str:
|
|
162
|
+
"""Return the wrapped engine id for diagnostics and result text."""
|
|
163
|
+
return self._inspection_engine.engine_id
|
|
164
|
+
|
|
165
|
+
def inspect(self, path: Path, *, artifact_id: str) -> DocumentExtraction:
|
|
166
|
+
"""Delegate inspection to the wrapped promoted engine."""
|
|
167
|
+
return self._inspection_engine.inspect(path, artifact_id=artifact_id)
|
|
168
|
+
|
|
169
|
+
def normalize_fill_patches(
|
|
170
|
+
self,
|
|
171
|
+
patches: tuple[DocumentFieldPatch, ...],
|
|
172
|
+
*,
|
|
173
|
+
extraction: DocumentExtraction | None,
|
|
174
|
+
) -> tuple[DocumentFieldPatch, ...]:
|
|
175
|
+
"""Return fill patches unchanged for adapters without target aliases."""
|
|
176
|
+
_ = extraction
|
|
177
|
+
return patches
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def build_default_document_adapter_registry() -> DocumentAdapterRegistry:
|
|
181
|
+
"""Build the default adapter registry for currently promoted engines."""
|
|
182
|
+
from ummaya.tools.documents.formats.archive import ( # noqa: PLC0415
|
|
183
|
+
ArchiveContainerDocumentEngine,
|
|
184
|
+
)
|
|
185
|
+
from ummaya.tools.documents.formats.code_file import ( # noqa: PLC0415
|
|
186
|
+
PythonSourceDocumentAdapter,
|
|
187
|
+
)
|
|
188
|
+
from ummaya.tools.documents.formats.data_file import DataFileDocumentAdapter # noqa: PLC0415
|
|
189
|
+
from ummaya.tools.documents.formats.hwp import HwpDocumentAdapter # noqa: PLC0415
|
|
190
|
+
from ummaya.tools.documents.formats.hwpx import HwpXDocumentAdapter # noqa: PLC0415
|
|
191
|
+
from ummaya.tools.documents.formats.odf import OdfdoDocumentAdapter # noqa: PLC0415
|
|
192
|
+
from ummaya.tools.documents.formats.ooxml import ( # noqa: PLC0415
|
|
193
|
+
DocxDocumentAdapter,
|
|
194
|
+
OpenPyxlDocumentEngine,
|
|
195
|
+
PptxDocumentAdapter,
|
|
196
|
+
PythonPptxDocumentEngine,
|
|
197
|
+
XlsxDocumentAdapter,
|
|
198
|
+
)
|
|
199
|
+
from ummaya.tools.documents.formats.pdf import PdfDocumentAdapter # noqa: PLC0415
|
|
200
|
+
from ummaya.tools.documents.formats.text_web import TextWebDocumentAdapter # noqa: PLC0415
|
|
201
|
+
|
|
202
|
+
registry = DocumentAdapterRegistry()
|
|
203
|
+
registry.register(HwpXDocumentAdapter())
|
|
204
|
+
registry.register(HwpDocumentAdapter())
|
|
205
|
+
registry.register(DocxDocumentAdapter())
|
|
206
|
+
registry.register(XlsxDocumentAdapter(OpenPyxlDocumentEngine()))
|
|
207
|
+
registry.register(PptxDocumentAdapter(PythonPptxDocumentEngine()))
|
|
208
|
+
registry.register(PdfDocumentAdapter())
|
|
209
|
+
registry.register(OdfdoDocumentAdapter())
|
|
210
|
+
registry.register(TextWebDocumentAdapter())
|
|
211
|
+
registry.register(DataFileDocumentAdapter())
|
|
212
|
+
registry.register(PythonSourceDocumentAdapter())
|
|
213
|
+
for document_format in _ARCHIVE_PROMOTED_FORMATS:
|
|
214
|
+
_register_engine_backed_adapter(
|
|
215
|
+
registry,
|
|
216
|
+
document_format=document_format,
|
|
217
|
+
engine=ArchiveContainerDocumentEngine(document_format),
|
|
218
|
+
)
|
|
219
|
+
_register_passive_known_only_adapters(registry)
|
|
220
|
+
return registry
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def build_document_adapter_registry_from_engine_registry(
|
|
224
|
+
engine_registry: DocumentEngineRegistry,
|
|
225
|
+
) -> DocumentAdapterRegistry:
|
|
226
|
+
"""Wrap currently registered promoted engines in document adapters."""
|
|
227
|
+
from ummaya.tools.documents.formats.hwp import HwpDocumentAdapter # noqa: PLC0415
|
|
228
|
+
from ummaya.tools.documents.formats.ooxml import ( # noqa: PLC0415
|
|
229
|
+
PptxDocumentAdapter,
|
|
230
|
+
XlsxDocumentAdapter,
|
|
231
|
+
)
|
|
232
|
+
from ummaya.tools.documents.formats.pdf import PdfDocumentAdapter # noqa: PLC0415
|
|
233
|
+
|
|
234
|
+
registry = DocumentAdapterRegistry()
|
|
235
|
+
if engine_registry.get(DocumentFormat.hwp) is None:
|
|
236
|
+
registry.register(HwpDocumentAdapter())
|
|
237
|
+
if engine_registry.get(DocumentFormat.xlsx) is None:
|
|
238
|
+
registry.register(XlsxDocumentAdapter())
|
|
239
|
+
if engine_registry.get(DocumentFormat.pptx) is None:
|
|
240
|
+
registry.register(PptxDocumentAdapter())
|
|
241
|
+
if engine_registry.get(DocumentFormat.pdf) is None:
|
|
242
|
+
registry.register(PdfDocumentAdapter(promote_default=False))
|
|
243
|
+
registered_family_formats = _register_promoted_family_adapters(registry, engine_registry)
|
|
244
|
+
_register_passive_known_only_adapters(registry)
|
|
245
|
+
for document_format in DocumentFormat:
|
|
246
|
+
engine = engine_registry.get(document_format)
|
|
247
|
+
if engine is None:
|
|
248
|
+
continue
|
|
249
|
+
if document_format in registered_family_formats:
|
|
250
|
+
continue
|
|
251
|
+
_register_engine_backed_adapter(
|
|
252
|
+
registry,
|
|
253
|
+
document_format=document_format,
|
|
254
|
+
engine=engine,
|
|
255
|
+
)
|
|
256
|
+
return registry
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _register_promoted_family_adapters(
|
|
260
|
+
registry: DocumentAdapterRegistry,
|
|
261
|
+
engine_registry: DocumentEngineRegistry,
|
|
262
|
+
) -> set[DocumentFormat]:
|
|
263
|
+
"""Register multi-format promoted adapters and return their owned formats."""
|
|
264
|
+
registered: set[DocumentFormat] = set()
|
|
265
|
+
if _has_any_engine(engine_registry, _ODF_PROMOTED_FORMATS):
|
|
266
|
+
_register_promoted_odf_adapter(registry, engine_registry)
|
|
267
|
+
registered.update(_ODF_PROMOTED_FORMATS)
|
|
268
|
+
if _has_any_engine(engine_registry, _TEXT_WEB_PROMOTED_FORMATS):
|
|
269
|
+
_register_promoted_text_web_adapter(registry, engine_registry)
|
|
270
|
+
registered.update(_TEXT_WEB_PROMOTED_FORMATS)
|
|
271
|
+
if _has_any_engine(engine_registry, _DATA_PROMOTED_FORMATS):
|
|
272
|
+
_register_promoted_data_adapter(registry, engine_registry)
|
|
273
|
+
registered.update(_DATA_PROMOTED_FORMATS)
|
|
274
|
+
if _has_any_engine(engine_registry, _CODE_PROMOTED_FORMATS):
|
|
275
|
+
_register_promoted_code_adapter(registry, engine_registry)
|
|
276
|
+
registered.update(_CODE_PROMOTED_FORMATS)
|
|
277
|
+
if _has_any_engine(engine_registry, _ARCHIVE_PROMOTED_FORMATS):
|
|
278
|
+
registered.update(_register_promoted_archive_adapters(registry, engine_registry))
|
|
279
|
+
return registered
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _has_any_engine(
|
|
283
|
+
engine_registry: DocumentEngineRegistry,
|
|
284
|
+
document_formats: tuple[DocumentFormat, ...],
|
|
285
|
+
) -> bool:
|
|
286
|
+
"""Return whether any format in a family has a promoted engine."""
|
|
287
|
+
return any(
|
|
288
|
+
engine_registry.get(document_format) is not None for document_format in document_formats
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _register_passive_known_only_adapters(registry: DocumentAdapterRegistry) -> None:
|
|
293
|
+
"""Register known-only passive adapters for non-promoted document families."""
|
|
294
|
+
from ummaya.tools.documents.formats.passive import ( # noqa: PLC0415
|
|
295
|
+
ArchiveDocumentSetAdapter,
|
|
296
|
+
CodeFileDocumentAdapter,
|
|
297
|
+
DataFileDocumentAdapter,
|
|
298
|
+
GeospatialDocumentAdapter,
|
|
299
|
+
ImageScanDocumentAdapter,
|
|
300
|
+
LegacyOfficeDocumentAdapter,
|
|
301
|
+
MediaAssetDocumentAdapter,
|
|
302
|
+
OdfDocumentAdapter,
|
|
303
|
+
TextWebExportAdapter,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
if registry.get_known(KnownDocumentFormat.odt) is None:
|
|
307
|
+
registry.register(OdfDocumentAdapter())
|
|
308
|
+
registry.register(LegacyOfficeDocumentAdapter())
|
|
309
|
+
if registry.get_known(KnownDocumentFormat.csv) is None:
|
|
310
|
+
registry.register(DataFileDocumentAdapter())
|
|
311
|
+
if registry.get_known(KnownDocumentFormat.html) is None:
|
|
312
|
+
registry.register(TextWebExportAdapter())
|
|
313
|
+
if registry.get_known(KnownDocumentFormat.python) is None:
|
|
314
|
+
registry.register(CodeFileDocumentAdapter())
|
|
315
|
+
registry.register(ImageScanDocumentAdapter())
|
|
316
|
+
registry.register(GeospatialDocumentAdapter())
|
|
317
|
+
registry.register(MediaAssetDocumentAdapter())
|
|
318
|
+
remaining_archive_formats = tuple(
|
|
319
|
+
known_format
|
|
320
|
+
for known_format in (
|
|
321
|
+
KnownDocumentFormat.epub,
|
|
322
|
+
KnownDocumentFormat.zip,
|
|
323
|
+
KnownDocumentFormat.seven_z,
|
|
324
|
+
KnownDocumentFormat.tar,
|
|
325
|
+
KnownDocumentFormat.gz,
|
|
326
|
+
)
|
|
327
|
+
if registry.get_known(known_format) is None
|
|
328
|
+
)
|
|
329
|
+
if remaining_archive_formats:
|
|
330
|
+
registry.register(ArchiveDocumentSetAdapter(known_formats=remaining_archive_formats))
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _register_promoted_archive_adapters(
|
|
334
|
+
registry: DocumentAdapterRegistry,
|
|
335
|
+
engine_registry: DocumentEngineRegistry,
|
|
336
|
+
) -> set[DocumentFormat]:
|
|
337
|
+
"""Register promoted archive engines individually."""
|
|
338
|
+
registered: set[DocumentFormat] = set()
|
|
339
|
+
for document_format in _ARCHIVE_PROMOTED_FORMATS:
|
|
340
|
+
engine = engine_registry.get(document_format)
|
|
341
|
+
if engine is None:
|
|
342
|
+
continue
|
|
343
|
+
_register_engine_backed_adapter(
|
|
344
|
+
registry,
|
|
345
|
+
document_format=document_format,
|
|
346
|
+
engine=engine,
|
|
347
|
+
)
|
|
348
|
+
registered.add(document_format)
|
|
349
|
+
return registered
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _register_promoted_odf_adapter(
|
|
353
|
+
registry: DocumentAdapterRegistry,
|
|
354
|
+
engine_registry: DocumentEngineRegistry,
|
|
355
|
+
) -> None:
|
|
356
|
+
"""Register one promoted adapter for ODT, ODS, and ODP engines."""
|
|
357
|
+
from ummaya.tools.documents.formats.odf import ( # noqa: PLC0415
|
|
358
|
+
OdfdoDocumentAdapter,
|
|
359
|
+
OdfdoPresentationDocumentEngine,
|
|
360
|
+
OdfdoSpreadsheetDocumentEngine,
|
|
361
|
+
OdfdoTextDocumentEngine,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
text_engine = engine_registry.get(DocumentFormat.odt)
|
|
365
|
+
spreadsheet_engine = engine_registry.get(DocumentFormat.ods)
|
|
366
|
+
presentation_engine = engine_registry.get(DocumentFormat.odp)
|
|
367
|
+
registry.register(
|
|
368
|
+
OdfdoDocumentAdapter(
|
|
369
|
+
text_engine=(text_engine if isinstance(text_engine, OdfdoTextDocumentEngine) else None),
|
|
370
|
+
spreadsheet_engine=(
|
|
371
|
+
spreadsheet_engine
|
|
372
|
+
if isinstance(spreadsheet_engine, OdfdoSpreadsheetDocumentEngine)
|
|
373
|
+
else None
|
|
374
|
+
),
|
|
375
|
+
presentation_engine=(
|
|
376
|
+
presentation_engine
|
|
377
|
+
if isinstance(presentation_engine, OdfdoPresentationDocumentEngine)
|
|
378
|
+
else None
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _register_promoted_text_web_adapter(
|
|
385
|
+
registry: DocumentAdapterRegistry,
|
|
386
|
+
engine_registry: DocumentEngineRegistry,
|
|
387
|
+
) -> None:
|
|
388
|
+
"""Register one promoted adapter for text and web-export engines."""
|
|
389
|
+
from ummaya.tools.documents.formats.text_web import ( # noqa: PLC0415
|
|
390
|
+
TextWebDocumentAdapter,
|
|
391
|
+
TextWebDocumentEngine,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
engines: dict[DocumentFormat, TextWebDocumentEngine] = {}
|
|
395
|
+
for document_format in _TEXT_WEB_PROMOTED_FORMATS:
|
|
396
|
+
engine = engine_registry.get(document_format)
|
|
397
|
+
if isinstance(engine, TextWebDocumentEngine):
|
|
398
|
+
engines[document_format] = engine
|
|
399
|
+
registry.register(TextWebDocumentAdapter(engines=engines or None))
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _register_promoted_data_adapter(
|
|
403
|
+
registry: DocumentAdapterRegistry,
|
|
404
|
+
engine_registry: DocumentEngineRegistry,
|
|
405
|
+
) -> None:
|
|
406
|
+
"""Register one promoted adapter for public-data engines."""
|
|
407
|
+
from ummaya.tools.documents.formats.data_file import ( # noqa: PLC0415
|
|
408
|
+
DataFileDocumentAdapter,
|
|
409
|
+
DataFileDocumentEngine,
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
engines: dict[DocumentFormat, DataFileDocumentEngine] = {}
|
|
413
|
+
for document_format in _DATA_PROMOTED_FORMATS:
|
|
414
|
+
engine = engine_registry.get(document_format)
|
|
415
|
+
if isinstance(engine, DataFileDocumentEngine):
|
|
416
|
+
engines[document_format] = engine
|
|
417
|
+
registry.register(DataFileDocumentAdapter(engines=engines or None))
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _register_promoted_code_adapter(
|
|
421
|
+
registry: DocumentAdapterRegistry,
|
|
422
|
+
engine_registry: DocumentEngineRegistry,
|
|
423
|
+
) -> None:
|
|
424
|
+
"""Register the promoted adapter for Python source attachments."""
|
|
425
|
+
from ummaya.tools.documents.formats.code_file import ( # noqa: PLC0415
|
|
426
|
+
PythonSourceDocumentAdapter,
|
|
427
|
+
PythonSourceDocumentEngine,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
engine = engine_registry.get(DocumentFormat.python)
|
|
431
|
+
registry.register(
|
|
432
|
+
PythonSourceDocumentAdapter(
|
|
433
|
+
engine=(engine if isinstance(engine, PythonSourceDocumentEngine) else None)
|
|
434
|
+
)
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _register_engine_backed_adapter(
|
|
439
|
+
registry: DocumentAdapterRegistry,
|
|
440
|
+
*,
|
|
441
|
+
document_format: DocumentFormat,
|
|
442
|
+
engine: DocumentInspectionEngine,
|
|
443
|
+
) -> None:
|
|
444
|
+
"""Register the format adapter that owns one promoted engine."""
|
|
445
|
+
if document_format in {DocumentFormat.hwpx, DocumentFormat.owpml}:
|
|
446
|
+
from ummaya.tools.documents.formats.hwpx import HwpXDocumentAdapter # noqa: PLC0415
|
|
447
|
+
|
|
448
|
+
if registry.get_known(KnownDocumentFormat.hwpx) is not None:
|
|
449
|
+
return
|
|
450
|
+
registry.register(HwpXDocumentAdapter(inspection_engine=engine))
|
|
451
|
+
return
|
|
452
|
+
if document_format is DocumentFormat.docx:
|
|
453
|
+
from ummaya.tools.documents.formats.ooxml import DocxDocumentAdapter # noqa: PLC0415
|
|
454
|
+
|
|
455
|
+
registry.register(DocxDocumentAdapter(inspection_engine=engine))
|
|
456
|
+
return
|
|
457
|
+
if document_format is DocumentFormat.xlsx:
|
|
458
|
+
from ummaya.tools.documents.formats.ooxml import XlsxDocumentAdapter # noqa: PLC0415
|
|
459
|
+
|
|
460
|
+
registry.register(XlsxDocumentAdapter(inspection_engine=engine))
|
|
461
|
+
return
|
|
462
|
+
if document_format is DocumentFormat.pptx:
|
|
463
|
+
from ummaya.tools.documents.formats.ooxml import PptxDocumentAdapter # noqa: PLC0415
|
|
464
|
+
|
|
465
|
+
registry.register(PptxDocumentAdapter(inspection_engine=engine))
|
|
466
|
+
return
|
|
467
|
+
if document_format is DocumentFormat.pdf:
|
|
468
|
+
from ummaya.tools.documents.formats.pdf import PdfDocumentAdapter # noqa: PLC0415
|
|
469
|
+
|
|
470
|
+
registry.register(PdfDocumentAdapter(inspection_engine=engine))
|
|
471
|
+
return
|
|
472
|
+
registry.register(
|
|
473
|
+
EngineBackedDocumentAdapter(
|
|
474
|
+
adapter_id=f"{engine.engine_id}-adapter",
|
|
475
|
+
known_formats=_known_formats_for_promoted_format(document_format),
|
|
476
|
+
promoted_formats=(document_format,),
|
|
477
|
+
inspection_engine=engine,
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def _known_formats_for_promoted_format(
|
|
483
|
+
document_format: DocumentFormat,
|
|
484
|
+
) -> tuple[KnownDocumentFormat, ...]:
|
|
485
|
+
if document_format in {DocumentFormat.hwpx, DocumentFormat.owpml}:
|
|
486
|
+
return (KnownDocumentFormat.hwpx, KnownDocumentFormat.owpml)
|
|
487
|
+
return (KnownDocumentFormat(document_format.value),)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Local archive/container promotion diagnostics."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from collections.abc import Mapping, Sequence
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from shutil import which
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
from ummaya.tools.documents.models import DocumentFormatFamily, KnownDocumentFormat
|
|
15
|
+
|
|
16
|
+
ARCHIVE_CANDIDATE_ID = "archive-container-child-routing"
|
|
17
|
+
ARCHIVE_READ_ADAPTER_ID = "archive-document-set-read-only-adapter"
|
|
18
|
+
ARCHIVE_SOURCE_REFS = (
|
|
19
|
+
"upstream:python-3.12-zipfile",
|
|
20
|
+
"upstream:python-3.12-tarfile-data-filter",
|
|
21
|
+
"upstream:python-3.12-gzip",
|
|
22
|
+
"upstream:libarchive-bsdtar-7zip",
|
|
23
|
+
"upstream:owasp-file-upload-archive-limits",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
ArchiveContainerProbeStatus = Literal["blocked", "candidate_available"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ArchiveContainerProbeReport(BaseModel):
|
|
30
|
+
"""Current local availability of archive child-routing candidates."""
|
|
31
|
+
|
|
32
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
33
|
+
|
|
34
|
+
candidate_id: str = Field(min_length=1)
|
|
35
|
+
known_format: KnownDocumentFormat
|
|
36
|
+
family: DocumentFormatFamily
|
|
37
|
+
status: ArchiveContainerProbeStatus
|
|
38
|
+
read_adapter_id: str = Field(min_length=1)
|
|
39
|
+
container_runtime_id: str = Field(min_length=1)
|
|
40
|
+
runtime_available: bool
|
|
41
|
+
runtime_executable: Path | None
|
|
42
|
+
child_routing_available: bool
|
|
43
|
+
direct_mutation_promoted: bool
|
|
44
|
+
reasons: tuple[str, ...]
|
|
45
|
+
required_gates: tuple[str, ...]
|
|
46
|
+
evidence_refs: tuple[str, ...]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def probe_archive_container_promotion(
|
|
50
|
+
*,
|
|
51
|
+
env: Mapping[str, str] | None = None,
|
|
52
|
+
search_path: Sequence[str] | None = None,
|
|
53
|
+
) -> tuple[ArchiveContainerProbeReport, ...]:
|
|
54
|
+
"""Report archive/container routing readiness without promoting repack writes."""
|
|
55
|
+
active_env = os.environ if env is None else env
|
|
56
|
+
bsdtar = _find_executable("bsdtar", active_env=active_env, search_path=search_path)
|
|
57
|
+
seven_z_available = bsdtar is not None
|
|
58
|
+
return (
|
|
59
|
+
_report(
|
|
60
|
+
known_format=KnownDocumentFormat.epub,
|
|
61
|
+
runtime_id="python-stdlib-zipfile-epub-container",
|
|
62
|
+
runtime_available=True,
|
|
63
|
+
runtime_executable=None,
|
|
64
|
+
child_routing_available=True,
|
|
65
|
+
reasons=(
|
|
66
|
+
"archive_child_derivative_promoted",
|
|
67
|
+
"epub_child_payload_writer_promoted",
|
|
68
|
+
"no_in_place_archive_mutation",
|
|
69
|
+
),
|
|
70
|
+
),
|
|
71
|
+
_report(
|
|
72
|
+
known_format=KnownDocumentFormat.zip,
|
|
73
|
+
runtime_id="python-stdlib-zipfile",
|
|
74
|
+
runtime_available=True,
|
|
75
|
+
runtime_executable=None,
|
|
76
|
+
child_routing_available=True,
|
|
77
|
+
reasons=(
|
|
78
|
+
"archive_child_derivative_promoted",
|
|
79
|
+
"no_in_place_archive_mutation",
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
_report(
|
|
83
|
+
known_format=KnownDocumentFormat.seven_z,
|
|
84
|
+
runtime_id="libarchive-bsdtar-7zip",
|
|
85
|
+
runtime_available=seven_z_available,
|
|
86
|
+
runtime_executable=bsdtar,
|
|
87
|
+
child_routing_available=seven_z_available,
|
|
88
|
+
reasons=(
|
|
89
|
+
(
|
|
90
|
+
"bsdtar_7zip_runtime_promoted"
|
|
91
|
+
if seven_z_available
|
|
92
|
+
else "bsdtar_7zip_runtime_not_found"
|
|
93
|
+
),
|
|
94
|
+
"archive_child_derivative_promoted" if seven_z_available else "7z_repack_blocked",
|
|
95
|
+
"no_in_place_archive_mutation",
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
_report(
|
|
99
|
+
known_format=KnownDocumentFormat.tar,
|
|
100
|
+
runtime_id="python-stdlib-tarfile",
|
|
101
|
+
runtime_available=True,
|
|
102
|
+
runtime_executable=None,
|
|
103
|
+
child_routing_available=True,
|
|
104
|
+
reasons=(
|
|
105
|
+
"archive_child_derivative_promoted",
|
|
106
|
+
"tarfile_data_filter_required",
|
|
107
|
+
"no_in_place_archive_mutation",
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
_report(
|
|
111
|
+
known_format=KnownDocumentFormat.gz,
|
|
112
|
+
runtime_id="python-stdlib-gzip",
|
|
113
|
+
runtime_available=True,
|
|
114
|
+
runtime_executable=None,
|
|
115
|
+
child_routing_available=True,
|
|
116
|
+
reasons=(
|
|
117
|
+
"archive_child_derivative_promoted",
|
|
118
|
+
"gzip_single_child_candidate",
|
|
119
|
+
"no_in_place_archive_mutation",
|
|
120
|
+
),
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _report(
|
|
126
|
+
*,
|
|
127
|
+
known_format: KnownDocumentFormat,
|
|
128
|
+
runtime_id: str,
|
|
129
|
+
runtime_available: bool,
|
|
130
|
+
runtime_executable: Path | None,
|
|
131
|
+
child_routing_available: bool,
|
|
132
|
+
reasons: tuple[str, ...],
|
|
133
|
+
) -> ArchiveContainerProbeReport:
|
|
134
|
+
return ArchiveContainerProbeReport(
|
|
135
|
+
candidate_id=ARCHIVE_CANDIDATE_ID,
|
|
136
|
+
known_format=known_format,
|
|
137
|
+
family=DocumentFormatFamily.archive,
|
|
138
|
+
status="candidate_available" if child_routing_available else "blocked",
|
|
139
|
+
read_adapter_id=ARCHIVE_READ_ADAPTER_ID,
|
|
140
|
+
container_runtime_id=runtime_id,
|
|
141
|
+
runtime_available=runtime_available,
|
|
142
|
+
runtime_executable=runtime_executable,
|
|
143
|
+
child_routing_available=child_routing_available,
|
|
144
|
+
direct_mutation_promoted=False,
|
|
145
|
+
reasons=reasons,
|
|
146
|
+
required_gates=(
|
|
147
|
+
"archive_intake_security_gate",
|
|
148
|
+
"archive_child_routing_gate",
|
|
149
|
+
"child_document_promotion_gate",
|
|
150
|
+
"archive_repack_write_gate",
|
|
151
|
+
"archive_runtime_completion_promotion",
|
|
152
|
+
),
|
|
153
|
+
evidence_refs=ARCHIVE_SOURCE_REFS,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _find_executable(
|
|
158
|
+
name: str,
|
|
159
|
+
*,
|
|
160
|
+
active_env: Mapping[str, str],
|
|
161
|
+
search_path: Sequence[str] | None,
|
|
162
|
+
) -> Path | None:
|
|
163
|
+
path_env = os.pathsep.join(search_path) if search_path is not None else active_env.get("PATH")
|
|
164
|
+
if not path_env:
|
|
165
|
+
return None
|
|
166
|
+
found = which(name, path=path_env)
|
|
167
|
+
return Path(found).resolve() if found else None
|