ummaya 0.2.3 → 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 +17 -3
- package/bin/ummaya +10 -1
- package/npm-shrinkwrap.json +253 -2
- package/package.json +5 -1
- package/prompts/manifest.yaml +2 -2
- package/prompts/session_guidance_v1.md +3 -1
- package/prompts/system_v1.md +9 -7
- package/pyproject.toml +26 -7
- package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
- package/src/ummaya/_canonical/__init__.py +2 -0
- package/src/ummaya/context/builder.py +17 -11
- package/src/ummaya/engine/engine.py +30 -113
- package/src/ummaya/engine/query.py +20 -0
- package/src/ummaya/evidence/__init__.py +44 -0
- package/src/ummaya/evidence/__main__.py +7 -0
- 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 +145 -0
- 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 +177 -0
- package/src/ummaya/evidence/source_provenance.py +246 -0
- package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
- package/src/ummaya/evidence/task_registry.py +264 -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 +52 -5
- package/src/ummaya/ipc/route_diagnostics.py +73 -0
- package/src/ummaya/ipc/stdio.py +2282 -417
- package/src/ummaya/llm/client.py +234 -59
- package/src/ummaya/llm/config.py +8 -3
- package/src/ummaya/llm/reasoning.py +84 -0
- 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 +34 -2
- 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 +61 -12
- package/src/ummaya/tools/geocoding/kakao_client.py +1 -2
- package/src/ummaya/tools/kma/apihub_catalog.py +984 -1
- package/src/ummaya/tools/kma/apihub_structured_adapter.py +86 -6
- package/src/ummaya/tools/kma/apihub_url_adapter.py +593 -0
- package/src/ummaya/tools/kma/apihub_url_catalog.py +296 -0
- package/src/ummaya/tools/live_proxy.py +0 -3
- package/src/ummaya/tools/location_adapters.py +8 -6
- package/src/ummaya/tools/manifest_metadata.py +16 -3
- package/src/ummaya/tools/models.py +5 -1
- package/src/ummaya/tools/mvp_surface.py +2 -2
- package/src/ummaya/tools/nmc/emergency_search.py +8 -6
- package/src/ummaya/tools/register_all.py +17 -0
- package/src/ummaya/tools/registry.py +10 -1
- package/src/ummaya/tools/resolve_location.py +4 -4
- 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 +40 -106
- package/src/ummaya/tools/verified_data_go_kr/_manifest.py +115 -25
- package/src/ummaya/tools/verified_data_go_kr/airkorea_air_quality.py +109 -4
- package/src/ummaya/tools/verified_data_go_kr/nmc_aed_site.py +108 -2
- package/src/ummaya/tools/verified_data_go_kr/pps_bid_public_info.py +174 -9
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_arrival.py +66 -3
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_location.py +12 -2
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_route.py +8 -2
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_route_station.py +114 -0
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_station.py +14 -3
- package/src/ummaya/tools/verify_canonical_map.py +21 -0
- package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
- package/tui/package.json +1 -2
- package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
- package/tui/src/QueryEngine.ts +12 -4
- package/tui/src/bridge/inboundAttachments.ts +3 -3
- package/tui/src/cli/handlers/auth.ts +4 -13
- package/tui/src/cli/handlers/mcp.tsx +3 -3
- package/tui/src/cli/print.ts +69 -18
- package/tui/src/cli/update.ts +13 -13
- package/tui/src/commands/copy/index.ts +1 -1
- package/tui/src/commands/cost/cost.ts +2 -2
- package/tui/src/commands/init-verifiers.ts +5 -5
- package/tui/src/commands/init.ts +30 -30
- package/tui/src/commands/insights.ts +44 -44
- package/tui/src/commands/install-github-app/install-github-app.tsx +2 -2
- package/tui/src/commands/install-github-app/setupGitHubActions.ts +3 -3
- package/tui/src/commands/install-github-app/types.ts +8 -30
- package/tui/src/commands/install.tsx +5 -5
- package/tui/src/commands/mcp/addCommand.ts +5 -5
- package/tui/src/commands/mcp/xaaIdpCommand.ts +2 -2
- package/tui/src/commands/plugin/ManageMarketplaces.tsx +2 -2
- package/tui/src/commands/plugin/types.ts +6 -28
- package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
- package/tui/src/commands/reasoning/index.ts +13 -0
- package/tui/src/commands/reasoning/reasoning.tsx +177 -0
- package/tui/src/commands/rename/generateSessionName.ts +1 -1
- package/tui/src/commands/thinkback/thinkback.tsx +3 -3
- package/tui/src/commands.ts +2 -0
- 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/Messages.tsx +2 -1
- package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
- package/tui/src/components/Spinner/types.ts +6 -28
- package/tui/src/components/Spinner.tsx +2 -2
- 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/design-system/LoadingState.tsx +2 -2
- 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 +29 -3
- package/tui/src/ipc/frames.generated.ts +407 -312
- package/tui/src/ipc/llmClient.ts +279 -76
- package/tui/src/ipc/llmTypes.ts +16 -1
- package/tui/src/ipc/schema/frame.schema.json +1 -3475
- package/tui/src/keybindings/defaultBindings.ts +4 -0
- package/tui/src/main.tsx +32 -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 -1721
- package/tui/src/screens/REPL.tsx +42 -31
- 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 +98 -14
- 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 -364
- 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/skills/bundled/stuck.ts +12 -12
- package/tui/src/state/AppStateStore.ts +7 -0
- 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 +1239 -163
- 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 +48 -29
- package/tui/src/tools/LookupPrimitive/prompt.ts +6 -7
- 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 +30 -19
- 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 +51 -18
- 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 +27 -10
- 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/citizenUserText.ts +49 -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/locationInputRepair.ts +112 -0
- package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
- package/tui/src/tools/_shared/rootPrimitiveInput.ts +68 -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 +61 -0
- 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/attachments.ts +1 -1
- 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/kExaoneReasoning.ts +138 -0
- package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
- package/tui/src/utils/messages.ts +19 -0
- package/tui/src/utils/migrateSessions.ts +3 -3
- package/tui/src/utils/model/model.ts +6 -6
- package/tui/src/utils/multiToolLayout.ts +13 -0
- 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/processUserInput/processSlashCommand.tsx +2 -2
- package/tui/src/utils/processUserInput/processUserInput.ts +26 -0
- 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/applySettingsChange.ts +4 -0
- package/tui/src/utils/settings/permissionValidation.ts +14 -2
- package/tui/src/utils/settings/types.ts +9 -3
- package/tui/src/utils/shell/prefix.ts +1 -1
- package/tui/src/utils/sideQuery.ts +1 -1
- package/tui/src/utils/stats.ts +1 -1
- package/tui/src/utils/systemThemeWatcher.ts +13 -3
- package/tui/src/utils/teleport.tsx +1 -1
- package/uv.lock +394 -22
- package/assets/copilot-gate-logo.svg +0 -58
- package/assets/govon-logo.svg +0 -40
- package/src/ummaya/eval/__init__.py +0 -5
- package/src/ummaya/eval/retrieval.py +0 -713
- package/tui/src/services/api/claude.ts +0 -3510
- package/tui/src/utils/messageStream.ts +0 -186
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Promoted archive/container document engines."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import gzip
|
|
7
|
+
import html
|
|
8
|
+
import io
|
|
9
|
+
import re
|
|
10
|
+
import shutil
|
|
11
|
+
import subprocess
|
|
12
|
+
import tarfile
|
|
13
|
+
import tempfile
|
|
14
|
+
import zipfile
|
|
15
|
+
from decimal import Decimal
|
|
16
|
+
from pathlib import Path, PurePosixPath
|
|
17
|
+
from typing import NoReturn
|
|
18
|
+
|
|
19
|
+
from ummaya.tools.documents.engines import DocumentMutationBlockedError
|
|
20
|
+
from ummaya.tools.documents.models import (
|
|
21
|
+
BlockedReason,
|
|
22
|
+
DocumentExtraction,
|
|
23
|
+
DocumentFormat,
|
|
24
|
+
DocumentPatch,
|
|
25
|
+
DocumentPatchOperation,
|
|
26
|
+
FormField,
|
|
27
|
+
MetadataValue,
|
|
28
|
+
OperationType,
|
|
29
|
+
ParagraphBlock,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
_ARCHIVE_FORMATS = (
|
|
33
|
+
DocumentFormat.epub,
|
|
34
|
+
DocumentFormat.zip,
|
|
35
|
+
DocumentFormat.seven_z,
|
|
36
|
+
DocumentFormat.tar,
|
|
37
|
+
DocumentFormat.gz,
|
|
38
|
+
)
|
|
39
|
+
_ARCHIVE_BODY_RE = re.compile(r"^/archive/(?P<member>.+)/body$")
|
|
40
|
+
_GZIP_BODY_TARGET = "/gzip/payload"
|
|
41
|
+
_MAX_INLINE_TEXT_BYTES = 128 * 1024
|
|
42
|
+
_BSDTAR_TIMEOUT_SECONDS = 15
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ArchiveContainerDocumentEngine:
|
|
46
|
+
"""Bounded writer for archive containers with child-payload replacement."""
|
|
47
|
+
|
|
48
|
+
render_engine_id = "archive-container-structural-svg"
|
|
49
|
+
render_artifact_extension = "svg"
|
|
50
|
+
render_mime_type = "image/svg+xml"
|
|
51
|
+
|
|
52
|
+
def __init__(self, document_format: DocumentFormat) -> None:
|
|
53
|
+
if document_format not in _ARCHIVE_FORMATS:
|
|
54
|
+
raise ValueError(f"unsupported archive document format: {document_format.value}")
|
|
55
|
+
self.document_format = document_format
|
|
56
|
+
self.engine_id = f"archive-container-{document_format.value}"
|
|
57
|
+
|
|
58
|
+
def inspect(self, path: Path, *, artifact_id: str) -> DocumentExtraction:
|
|
59
|
+
"""Inspect archive members without extracting to the workspace."""
|
|
60
|
+
entries = _read_entries(path, document_format=self.document_format)
|
|
61
|
+
paragraphs = [
|
|
62
|
+
ParagraphBlock(
|
|
63
|
+
block_id=f"archive-member-{index}",
|
|
64
|
+
text=entry.preview,
|
|
65
|
+
source_path=entry.target_path,
|
|
66
|
+
)
|
|
67
|
+
for index, entry in enumerate(entries, start=1)
|
|
68
|
+
]
|
|
69
|
+
fields = [
|
|
70
|
+
FormField(
|
|
71
|
+
field_id=f"archive-member-{index}",
|
|
72
|
+
label=entry.name,
|
|
73
|
+
path=entry.target_path,
|
|
74
|
+
field_type="text",
|
|
75
|
+
required=False,
|
|
76
|
+
current_value=entry.text,
|
|
77
|
+
source_confidence=Decimal("0.85"),
|
|
78
|
+
)
|
|
79
|
+
for index, entry in enumerate(entries, start=1)
|
|
80
|
+
if entry.editable
|
|
81
|
+
]
|
|
82
|
+
return DocumentExtraction(
|
|
83
|
+
artifact_id=artifact_id,
|
|
84
|
+
paragraphs=paragraphs,
|
|
85
|
+
fields=fields,
|
|
86
|
+
metadata=_metadata(
|
|
87
|
+
path,
|
|
88
|
+
document_format=self.document_format,
|
|
89
|
+
entry_count=len(entries),
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def apply_patch(self, path: Path, patch: DocumentPatch) -> bytes:
|
|
94
|
+
"""Return a repacked archive derivative after replacing child payloads."""
|
|
95
|
+
replacements: dict[str, bytes] = {}
|
|
96
|
+
gzip_replacement: bytes | None = None
|
|
97
|
+
for operation in patch.operations:
|
|
98
|
+
target_name = _operation_target_name(operation)
|
|
99
|
+
replacement = _operation_replacement(operation)
|
|
100
|
+
if self.document_format is DocumentFormat.gz:
|
|
101
|
+
if target_name != _GZIP_BODY_TARGET:
|
|
102
|
+
_raise_unsupported_operation(operation)
|
|
103
|
+
gzip_replacement = replacement
|
|
104
|
+
continue
|
|
105
|
+
member_name = _member_name_from_target(target_name)
|
|
106
|
+
replacements[member_name] = replacement
|
|
107
|
+
|
|
108
|
+
if self.document_format is DocumentFormat.gz:
|
|
109
|
+
if gzip_replacement is None:
|
|
110
|
+
raise DocumentMutationBlockedError(
|
|
111
|
+
BlockedReason.validation_failed,
|
|
112
|
+
"Gzip archive patch did not include /gzip/payload.",
|
|
113
|
+
)
|
|
114
|
+
return _write_gzip_payload(gzip_replacement)
|
|
115
|
+
if not replacements:
|
|
116
|
+
raise DocumentMutationBlockedError(
|
|
117
|
+
BlockedReason.validation_failed,
|
|
118
|
+
"Archive patch did not include a child member replacement.",
|
|
119
|
+
)
|
|
120
|
+
if self.document_format in {DocumentFormat.zip, DocumentFormat.epub}:
|
|
121
|
+
return _replace_zip_members(
|
|
122
|
+
path,
|
|
123
|
+
replacements,
|
|
124
|
+
preserve_epub_mimetype=self.document_format is DocumentFormat.epub,
|
|
125
|
+
)
|
|
126
|
+
if self.document_format is DocumentFormat.seven_z:
|
|
127
|
+
return _replace_7z_members(path, replacements)
|
|
128
|
+
if self.document_format is DocumentFormat.tar:
|
|
129
|
+
return _replace_tar_members(path, replacements)
|
|
130
|
+
raise DocumentMutationBlockedError(
|
|
131
|
+
BlockedReason.unsupported_format,
|
|
132
|
+
f"Unsupported archive format: {self.document_format.value}",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def render(self, path: Path, *, artifact_id: str, output_dir: Path) -> tuple[bytes, ...]:
|
|
136
|
+
"""Render archive member previews as structural SVG evidence."""
|
|
137
|
+
_ = output_dir
|
|
138
|
+
extraction = self.inspect(path, artifact_id=artifact_id)
|
|
139
|
+
lines = [paragraph.text for paragraph in extraction.paragraphs]
|
|
140
|
+
return (_render_lines_svg(lines, title=f"{self.document_format.value.upper()} archive"),)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class _ArchiveEntry:
|
|
144
|
+
def __init__(self, *, name: str, text: str, editable: bool) -> None:
|
|
145
|
+
self.name = name
|
|
146
|
+
self.text = text
|
|
147
|
+
self.editable = editable
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def target_path(self) -> str:
|
|
151
|
+
if self.name == "gzip-payload":
|
|
152
|
+
return _GZIP_BODY_TARGET
|
|
153
|
+
return f"/archive/{self.name}/body"
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def preview(self) -> str:
|
|
157
|
+
if self.text:
|
|
158
|
+
return f"{self.name}: {self.text}"
|
|
159
|
+
return self.name
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _read_entries(path: Path, *, document_format: DocumentFormat) -> list[_ArchiveEntry]:
|
|
163
|
+
if document_format in {DocumentFormat.zip, DocumentFormat.epub}:
|
|
164
|
+
return _read_zip_entries(path)
|
|
165
|
+
if document_format is DocumentFormat.seven_z:
|
|
166
|
+
return _read_7z_entries(path)
|
|
167
|
+
if document_format is DocumentFormat.tar:
|
|
168
|
+
return _read_tar_entries(path)
|
|
169
|
+
if document_format is DocumentFormat.gz:
|
|
170
|
+
return [_ArchiveEntry(name="gzip-payload", text=_read_gzip_text(path), editable=True)]
|
|
171
|
+
raise DocumentMutationBlockedError(
|
|
172
|
+
BlockedReason.unsupported_format,
|
|
173
|
+
f"Unsupported archive format: {document_format.value}",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _read_zip_entries(path: Path) -> list[_ArchiveEntry]:
|
|
178
|
+
entries: list[_ArchiveEntry] = []
|
|
179
|
+
try:
|
|
180
|
+
with zipfile.ZipFile(path) as archive:
|
|
181
|
+
for info in archive.infolist():
|
|
182
|
+
if info.is_dir() or not _safe_member_name(info.filename):
|
|
183
|
+
continue
|
|
184
|
+
text = _read_zip_member_text(archive, info)
|
|
185
|
+
entries.append(_ArchiveEntry(name=info.filename, text=text, editable=bool(text)))
|
|
186
|
+
except zipfile.BadZipFile as exc:
|
|
187
|
+
raise DocumentMutationBlockedError(BlockedReason.corrupt, "Invalid ZIP container.") from exc
|
|
188
|
+
return entries
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _read_tar_entries(path: Path) -> list[_ArchiveEntry]:
|
|
192
|
+
entries: list[_ArchiveEntry] = []
|
|
193
|
+
try:
|
|
194
|
+
with tarfile.open(path, mode="r:*") as archive:
|
|
195
|
+
for member in archive.getmembers():
|
|
196
|
+
if not member.isfile() or not _safe_member_name(member.name):
|
|
197
|
+
continue
|
|
198
|
+
payload = archive.extractfile(member)
|
|
199
|
+
if payload is None:
|
|
200
|
+
continue
|
|
201
|
+
text = _decode_inline_text(payload.read(_MAX_INLINE_TEXT_BYTES))
|
|
202
|
+
entries.append(_ArchiveEntry(name=member.name, text=text, editable=bool(text)))
|
|
203
|
+
except tarfile.TarError as exc:
|
|
204
|
+
raise DocumentMutationBlockedError(BlockedReason.corrupt, "Invalid TAR container.") from exc
|
|
205
|
+
return entries
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _read_7z_entries(path: Path) -> list[_ArchiveEntry]:
|
|
209
|
+
with tempfile.TemporaryDirectory(prefix="ummaya-7z-read-") as temp_root:
|
|
210
|
+
root = Path(temp_root)
|
|
211
|
+
member_names = _extract_7z_archive(path, root)
|
|
212
|
+
entries: list[_ArchiveEntry] = []
|
|
213
|
+
for member_name in member_names:
|
|
214
|
+
member_path = _safe_extracted_member_path(root, member_name)
|
|
215
|
+
if member_path is None or not member_path.is_file() or member_path.is_symlink():
|
|
216
|
+
continue
|
|
217
|
+
payload = member_path.read_bytes()[:_MAX_INLINE_TEXT_BYTES]
|
|
218
|
+
text = _decode_inline_text(payload)
|
|
219
|
+
entries.append(_ArchiveEntry(name=member_name, text=text, editable=bool(text)))
|
|
220
|
+
return entries
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _read_zip_member_text(archive: zipfile.ZipFile, info: zipfile.ZipInfo) -> str:
|
|
224
|
+
if info.file_size > _MAX_INLINE_TEXT_BYTES:
|
|
225
|
+
return ""
|
|
226
|
+
try:
|
|
227
|
+
return _decode_inline_text(archive.read(info.filename))
|
|
228
|
+
except (KeyError, RuntimeError, NotImplementedError, zipfile.BadZipFile):
|
|
229
|
+
return ""
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _read_gzip_text(path: Path) -> str:
|
|
233
|
+
try:
|
|
234
|
+
with gzip.open(path, "rb") as archive:
|
|
235
|
+
return _decode_inline_text(archive.read(_MAX_INLINE_TEXT_BYTES))
|
|
236
|
+
except OSError as exc:
|
|
237
|
+
raise DocumentMutationBlockedError(BlockedReason.corrupt, "Invalid GZIP payload.") from exc
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _decode_inline_text(payload: bytes) -> str:
|
|
241
|
+
if b"\x00" in payload:
|
|
242
|
+
return ""
|
|
243
|
+
try:
|
|
244
|
+
return payload.decode("utf-8").strip()
|
|
245
|
+
except UnicodeDecodeError:
|
|
246
|
+
return ""
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _operation_target_name(operation: DocumentPatchOperation) -> str:
|
|
250
|
+
if operation.operation_type not in {OperationType.replace_text, OperationType.set_field_value}:
|
|
251
|
+
_raise_unsupported_operation(operation)
|
|
252
|
+
return operation.target_path
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _operation_replacement(operation: DocumentPatchOperation) -> bytes:
|
|
256
|
+
if operation.value is None:
|
|
257
|
+
_raise_unsupported_operation(operation)
|
|
258
|
+
return str(operation.value).encode("utf-8")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _member_name_from_target(target_path: str) -> str:
|
|
262
|
+
match = _ARCHIVE_BODY_RE.fullmatch(target_path)
|
|
263
|
+
if match is None:
|
|
264
|
+
raise DocumentMutationBlockedError(
|
|
265
|
+
BlockedReason.unsupported_operation,
|
|
266
|
+
f"Archive target must match /archive/<member>/body: {target_path}",
|
|
267
|
+
)
|
|
268
|
+
member_name = match.group("member")
|
|
269
|
+
if not _safe_member_name(member_name):
|
|
270
|
+
raise DocumentMutationBlockedError(
|
|
271
|
+
BlockedReason.path_traversal_detected,
|
|
272
|
+
f"Archive member target is unsafe: {member_name}",
|
|
273
|
+
)
|
|
274
|
+
return member_name
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _replace_zip_members(
|
|
278
|
+
path: Path,
|
|
279
|
+
replacements: dict[str, bytes],
|
|
280
|
+
*,
|
|
281
|
+
preserve_epub_mimetype: bool,
|
|
282
|
+
) -> bytes:
|
|
283
|
+
buffer = io.BytesIO()
|
|
284
|
+
found: set[str] = set()
|
|
285
|
+
with (
|
|
286
|
+
zipfile.ZipFile(path) as source,
|
|
287
|
+
zipfile.ZipFile(buffer, "w", compression=zipfile.ZIP_DEFLATED) as target,
|
|
288
|
+
):
|
|
289
|
+
if preserve_epub_mimetype and "mimetype" in source.namelist():
|
|
290
|
+
target.writestr(
|
|
291
|
+
"mimetype",
|
|
292
|
+
source.read("mimetype"),
|
|
293
|
+
compress_type=zipfile.ZIP_STORED,
|
|
294
|
+
)
|
|
295
|
+
for info in source.infolist():
|
|
296
|
+
if info.is_dir():
|
|
297
|
+
continue
|
|
298
|
+
if preserve_epub_mimetype and info.filename == "mimetype":
|
|
299
|
+
continue
|
|
300
|
+
if not _safe_member_name(info.filename):
|
|
301
|
+
raise DocumentMutationBlockedError(
|
|
302
|
+
BlockedReason.path_traversal_detected,
|
|
303
|
+
f"Archive member path is unsafe: {info.filename}",
|
|
304
|
+
)
|
|
305
|
+
payload = replacements.get(info.filename)
|
|
306
|
+
if payload is None:
|
|
307
|
+
payload = source.read(info.filename)
|
|
308
|
+
else:
|
|
309
|
+
found.add(info.filename)
|
|
310
|
+
target.writestr(info.filename, payload)
|
|
311
|
+
_ensure_all_replacements_found(replacements, found)
|
|
312
|
+
return buffer.getvalue()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _replace_tar_members(path: Path, replacements: dict[str, bytes]) -> bytes:
|
|
316
|
+
buffer = io.BytesIO()
|
|
317
|
+
found: set[str] = set()
|
|
318
|
+
with tarfile.open(path, mode="r:*") as source, tarfile.open(fileobj=buffer, mode="w") as target:
|
|
319
|
+
for member in source.getmembers():
|
|
320
|
+
if not _safe_member_name(member.name):
|
|
321
|
+
raise DocumentMutationBlockedError(
|
|
322
|
+
BlockedReason.path_traversal_detected,
|
|
323
|
+
f"Archive member path is unsafe: {member.name}",
|
|
324
|
+
)
|
|
325
|
+
if not member.isfile():
|
|
326
|
+
target.addfile(member)
|
|
327
|
+
continue
|
|
328
|
+
payload = replacements.get(member.name)
|
|
329
|
+
if payload is None:
|
|
330
|
+
source_payload = source.extractfile(member)
|
|
331
|
+
payload = b"" if source_payload is None else source_payload.read()
|
|
332
|
+
else:
|
|
333
|
+
found.add(member.name)
|
|
334
|
+
info = tarfile.TarInfo(member.name)
|
|
335
|
+
info.size = len(payload)
|
|
336
|
+
info.mode = member.mode
|
|
337
|
+
info.mtime = member.mtime
|
|
338
|
+
target.addfile(info, io.BytesIO(payload))
|
|
339
|
+
_ensure_all_replacements_found(replacements, found)
|
|
340
|
+
return buffer.getvalue()
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def _replace_7z_members(path: Path, replacements: dict[str, bytes]) -> bytes:
|
|
344
|
+
with tempfile.TemporaryDirectory(prefix="ummaya-7z-write-") as temp_root:
|
|
345
|
+
root = Path(temp_root) / "src"
|
|
346
|
+
root.mkdir()
|
|
347
|
+
member_names = _extract_7z_archive(path, root)
|
|
348
|
+
found: set[str] = set()
|
|
349
|
+
for member_name, payload in replacements.items():
|
|
350
|
+
member_path = _safe_extracted_member_path(root, member_name)
|
|
351
|
+
if member_path is None or not member_path.is_file() or member_path.is_symlink():
|
|
352
|
+
continue
|
|
353
|
+
member_path.write_bytes(payload)
|
|
354
|
+
found.add(member_name)
|
|
355
|
+
_ensure_all_replacements_found(replacements, found)
|
|
356
|
+
|
|
357
|
+
output_path = Path(temp_root) / "updated.7z"
|
|
358
|
+
_run_bsdtar(
|
|
359
|
+
[
|
|
360
|
+
"-cf",
|
|
361
|
+
str(output_path),
|
|
362
|
+
"--format=7zip",
|
|
363
|
+
"-C",
|
|
364
|
+
str(root),
|
|
365
|
+
*member_names,
|
|
366
|
+
]
|
|
367
|
+
)
|
|
368
|
+
return output_path.read_bytes()
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _write_gzip_payload(payload: bytes) -> bytes:
|
|
372
|
+
buffer = io.BytesIO()
|
|
373
|
+
with gzip.GzipFile(fileobj=buffer, mode="wb") as archive:
|
|
374
|
+
archive.write(payload)
|
|
375
|
+
return buffer.getvalue()
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _ensure_all_replacements_found(replacements: dict[str, bytes], found: set[str]) -> None:
|
|
379
|
+
missing = sorted(set(replacements) - found)
|
|
380
|
+
if missing:
|
|
381
|
+
raise DocumentMutationBlockedError(
|
|
382
|
+
BlockedReason.validation_failed,
|
|
383
|
+
f"Archive member does not exist: {missing[0]}",
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _safe_member_name(name: str) -> bool:
|
|
388
|
+
if not name or "\x00" in name or "\\" in name:
|
|
389
|
+
return False
|
|
390
|
+
path = PurePosixPath(name)
|
|
391
|
+
return not path.is_absolute() and not any(part in {"", ".", ".."} for part in path.parts)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _extract_7z_archive(path: Path, destination: Path) -> list[str]:
|
|
395
|
+
member_names = _list_7z_member_names(path)
|
|
396
|
+
_run_bsdtar(
|
|
397
|
+
[
|
|
398
|
+
"-xf",
|
|
399
|
+
str(path),
|
|
400
|
+
"-C",
|
|
401
|
+
str(destination),
|
|
402
|
+
"--no-same-owner",
|
|
403
|
+
"--no-same-permissions",
|
|
404
|
+
]
|
|
405
|
+
)
|
|
406
|
+
_validate_extracted_7z_tree(destination, member_names)
|
|
407
|
+
return member_names
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _list_7z_member_names(path: Path) -> list[str]:
|
|
411
|
+
result = _run_bsdtar(["-tf", str(path)], capture_output=True)
|
|
412
|
+
names = [line.strip() for line in result.stdout.decode("utf-8").splitlines() if line.strip()]
|
|
413
|
+
unsafe = [name for name in names if not _safe_member_name(name)]
|
|
414
|
+
if unsafe:
|
|
415
|
+
raise DocumentMutationBlockedError(
|
|
416
|
+
BlockedReason.path_traversal_detected,
|
|
417
|
+
f"7z archive member path is unsafe: {unsafe[0]}",
|
|
418
|
+
)
|
|
419
|
+
return names
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _validate_extracted_7z_tree(root: Path, member_names: list[str]) -> None:
|
|
423
|
+
expected = {_normalize_member_name(name) for name in member_names}
|
|
424
|
+
actual: set[str] = set()
|
|
425
|
+
for item in root.rglob("*"):
|
|
426
|
+
relative = item.relative_to(root).as_posix()
|
|
427
|
+
if item.is_symlink():
|
|
428
|
+
raise DocumentMutationBlockedError(
|
|
429
|
+
BlockedReason.unsupported_operation,
|
|
430
|
+
f"7z archive symlink entries are not promoted: {relative}",
|
|
431
|
+
)
|
|
432
|
+
if item.is_file():
|
|
433
|
+
actual.add(relative)
|
|
434
|
+
unexpected = sorted(actual - expected)
|
|
435
|
+
if unexpected:
|
|
436
|
+
raise DocumentMutationBlockedError(
|
|
437
|
+
BlockedReason.path_traversal_detected,
|
|
438
|
+
f"7z archive extracted an unexpected member: {unexpected[0]}",
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _safe_extracted_member_path(root: Path, member_name: str) -> Path | None:
|
|
443
|
+
normalized = _normalize_member_name(member_name)
|
|
444
|
+
if not _safe_member_name(normalized):
|
|
445
|
+
return None
|
|
446
|
+
candidate = root.joinpath(*PurePosixPath(normalized).parts)
|
|
447
|
+
try:
|
|
448
|
+
candidate.resolve().relative_to(root.resolve())
|
|
449
|
+
except ValueError:
|
|
450
|
+
return None
|
|
451
|
+
return candidate
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _normalize_member_name(member_name: str) -> str:
|
|
455
|
+
return member_name.rstrip("/")
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _run_bsdtar(
|
|
459
|
+
args: list[str],
|
|
460
|
+
*,
|
|
461
|
+
capture_output: bool = False,
|
|
462
|
+
) -> subprocess.CompletedProcess[bytes]:
|
|
463
|
+
executable = shutil.which("bsdtar")
|
|
464
|
+
if executable is None:
|
|
465
|
+
raise DocumentMutationBlockedError(
|
|
466
|
+
BlockedReason.unsupported_operation,
|
|
467
|
+
"7z archive handling requires a local bsdtar/libarchive runtime.",
|
|
468
|
+
)
|
|
469
|
+
try:
|
|
470
|
+
return subprocess.run( # noqa: S603 - static executable lookup plus static argv.
|
|
471
|
+
[executable, *args],
|
|
472
|
+
check=True,
|
|
473
|
+
capture_output=capture_output,
|
|
474
|
+
timeout=_BSDTAR_TIMEOUT_SECONDS,
|
|
475
|
+
)
|
|
476
|
+
except subprocess.CalledProcessError as exc:
|
|
477
|
+
raise DocumentMutationBlockedError(
|
|
478
|
+
BlockedReason.corrupt,
|
|
479
|
+
f"7z archive operation failed through bsdtar: {exc.stderr.decode('utf-8', 'replace')}",
|
|
480
|
+
) from exc
|
|
481
|
+
except subprocess.TimeoutExpired as exc:
|
|
482
|
+
raise DocumentMutationBlockedError(
|
|
483
|
+
BlockedReason.oversized_expanded_bytes,
|
|
484
|
+
"7z archive operation exceeded the local timeout budget.",
|
|
485
|
+
) from exc
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def _metadata(
|
|
489
|
+
path: Path,
|
|
490
|
+
*,
|
|
491
|
+
document_format: DocumentFormat,
|
|
492
|
+
entry_count: int,
|
|
493
|
+
) -> dict[str, MetadataValue]:
|
|
494
|
+
return {
|
|
495
|
+
"adapter_id": f"archive-container-{document_format.value}-adapter",
|
|
496
|
+
"engine_id": f"archive-container-{document_format.value}",
|
|
497
|
+
"format": document_format.value,
|
|
498
|
+
"source_name": path.name,
|
|
499
|
+
"entry_count": entry_count,
|
|
500
|
+
"mutation_policy": "archive_child_derivative_write_render_save",
|
|
501
|
+
"render_oracle": "archive-container-structural-svg",
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _render_lines_svg(lines: list[str], *, title: str) -> bytes:
|
|
506
|
+
escaped_title = html.escape(title)
|
|
507
|
+
safe_lines = [html.escape(line) for line in lines if line]
|
|
508
|
+
height = max(160, 72 + len(safe_lines) * 28)
|
|
509
|
+
text_nodes = [
|
|
510
|
+
f'<text x="32" y="{84 + index * 28}" font-size="15" font-family="monospace">{line}</text>'
|
|
511
|
+
for index, line in enumerate(safe_lines[:80])
|
|
512
|
+
]
|
|
513
|
+
svg = (
|
|
514
|
+
f'<svg xmlns="http://www.w3.org/2000/svg" width="1040" height="{height}" '
|
|
515
|
+
f'viewBox="0 0 1040 {height}">'
|
|
516
|
+
'<rect width="100%" height="100%" fill="#ffffff"/>'
|
|
517
|
+
f'<text x="32" y="40" font-size="22" font-family="sans-serif" '
|
|
518
|
+
f'font-weight="700">{escaped_title}</text>' + "".join(text_nodes) + "</svg>"
|
|
519
|
+
)
|
|
520
|
+
return svg.encode("utf-8")
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def _raise_unsupported_operation(operation: DocumentPatchOperation) -> NoReturn:
|
|
524
|
+
raise DocumentMutationBlockedError(
|
|
525
|
+
BlockedReason.unsupported_operation,
|
|
526
|
+
f"Archive operation is not supported: "
|
|
527
|
+
f"{operation.operation_type.value} {operation.target_path}",
|
|
528
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Shared engine-backed adapter protocol for document formats."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import TYPE_CHECKING, Protocol
|
|
8
|
+
|
|
9
|
+
from ummaya.tools.documents.models import DocumentExtraction, DocumentFormat, KnownDocumentFormat
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from ummaya.tools.documents.tool_defs import DocumentFieldPatch
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DocumentFormatAdapter(Protocol):
|
|
16
|
+
"""Format-scoped adapter boundary below the single document primitive."""
|
|
17
|
+
|
|
18
|
+
adapter_id: str
|
|
19
|
+
known_formats: tuple[KnownDocumentFormat, ...]
|
|
20
|
+
promoted_formats: tuple[DocumentFormat, ...]
|
|
21
|
+
|
|
22
|
+
def inspect(self, path: Path, *, artifact_id: str) -> DocumentExtraction:
|
|
23
|
+
"""Return LLM-readable document IR for a local artifact."""
|
|
24
|
+
|
|
25
|
+
def normalize_fill_patches(
|
|
26
|
+
self,
|
|
27
|
+
patches: tuple[DocumentFieldPatch, ...],
|
|
28
|
+
*,
|
|
29
|
+
extraction: DocumentExtraction | None,
|
|
30
|
+
) -> tuple[DocumentFieldPatch, ...]:
|
|
31
|
+
"""Normalize adapter-specific model-facing fill targets before mutation."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EngineBackedFormatAdapter(Protocol):
|
|
35
|
+
"""Thin wrapper around a promoted external or optional document engine."""
|
|
36
|
+
|
|
37
|
+
document_format: DocumentFormat
|
|
38
|
+
engine_id: str
|
|
39
|
+
|
|
40
|
+
def inspect(self, path: Path, *, artifact_id: str) -> DocumentExtraction:
|
|
41
|
+
"""Delegate inspection to the selected engine."""
|