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,202 @@
|
|
|
1
|
+
import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js';
|
|
2
|
+
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../services/analytics/index.js';
|
|
3
|
+
import type { ToolCallProgress, ToolUseContext } from '../../Tool.js';
|
|
4
|
+
import type { AssistantMessage } from '../../types/message.js';
|
|
5
|
+
import type { BashProgress } from '../../types/tools.js';
|
|
6
|
+
import { extractClaudeCodeHints } from '../../utils/claudeCodeHints.js';
|
|
7
|
+
import { detectCodeIndexingFromCommand } from '../../utils/codeIndexing.js';
|
|
8
|
+
import { ShellError } from '../../utils/errors.js';
|
|
9
|
+
import { EndTruncatingAccumulator } from '../../utils/stringUtils.js';
|
|
10
|
+
import { maybeRecordPluginHint } from '../../utils/plugins/hintRecommendation.js';
|
|
11
|
+
import type { ExecResult } from '../../utils/ShellCommand.js';
|
|
12
|
+
import { resetShellCwdIfOutsideProject } from './cwdReset.js';
|
|
13
|
+
import { resizeShellImageOutput, stdErrAppendShellResetMessage, stripEmptyLines, isImageOutput } from './shellOutputUtils.js';
|
|
14
|
+
import { interpretCommandResult } from './commandSemantics.js';
|
|
15
|
+
import { trackGitOperations } from '../shared/gitOperationTracking.js';
|
|
16
|
+
import { runShellCommand } from './shellExecution.js';
|
|
17
|
+
import { persistLargeShellOutput } from './outputPersistence.js';
|
|
18
|
+
import { applySedEdit } from './sedEditExecution.js';
|
|
19
|
+
import { isSilentBashCommand } from './commandClassification.js';
|
|
20
|
+
import type { BashToolInput, Out } from './schemas.js';
|
|
21
|
+
import { annotateShellStderrWithSandboxFailures } from './sandboxPolicy.js';
|
|
22
|
+
|
|
23
|
+
const EOL = '\n';
|
|
24
|
+
|
|
25
|
+
export async function callBashTool(
|
|
26
|
+
input: BashToolInput,
|
|
27
|
+
toolUseContext: ToolUseContext,
|
|
28
|
+
_canUseTool?: CanUseToolFn,
|
|
29
|
+
parentMessage?: AssistantMessage,
|
|
30
|
+
onProgress?: ToolCallProgress<BashProgress>
|
|
31
|
+
): Promise<{ readonly data: Out }> {
|
|
32
|
+
if (input._simulatedSedEdit) {
|
|
33
|
+
return applySedEdit(input._simulatedSedEdit, toolUseContext, parentMessage);
|
|
34
|
+
}
|
|
35
|
+
const { abortController, getAppState, setAppState, setToolJSX } = toolUseContext;
|
|
36
|
+
const stdoutAccumulator = new EndTruncatingAccumulator();
|
|
37
|
+
let stderrForShellReset = '';
|
|
38
|
+
let interpretationResult: ReturnType<typeof interpretCommandResult> | undefined;
|
|
39
|
+
let wasInterrupted = false;
|
|
40
|
+
const isMainThread = !toolUseContext.agentId;
|
|
41
|
+
const preventCwdChanges = !isMainThread;
|
|
42
|
+
const { execResult } = await consumeBashCommand({
|
|
43
|
+
input,
|
|
44
|
+
toolUseContext,
|
|
45
|
+
preventCwdChanges,
|
|
46
|
+
isMainThread,
|
|
47
|
+
onProgress
|
|
48
|
+
});
|
|
49
|
+
try {
|
|
50
|
+
trackGitOperations(input.command, execResult.code, execResult.stdout);
|
|
51
|
+
const isInterrupt = execResult.interrupted && abortController.signal.reason === 'interrupt';
|
|
52
|
+
stdoutAccumulator.append((execResult.stdout || '').trimEnd() + EOL);
|
|
53
|
+
interpretationResult = interpretCommandResult(input.command, execResult.code, execResult.stdout || '', '');
|
|
54
|
+
if (execResult.stdout && execResult.stdout.includes(".git/index.lock': File exists")) {
|
|
55
|
+
logEvent('tengu_git_index_lock_error', {});
|
|
56
|
+
}
|
|
57
|
+
if (interpretationResult.isError && !isInterrupt && execResult.code !== 0) {
|
|
58
|
+
stdoutAccumulator.append(`Exit code ${execResult.code}`);
|
|
59
|
+
}
|
|
60
|
+
if (!preventCwdChanges) {
|
|
61
|
+
const appState = getAppState();
|
|
62
|
+
if (resetShellCwdIfOutsideProject(appState.toolPermissionContext)) {
|
|
63
|
+
stderrForShellReset = stdErrAppendShellResetMessage('');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const outputWithSbFailures = annotateShellStderrWithSandboxFailures(input.command, execResult.stdout || '');
|
|
67
|
+
if (execResult.preSpawnError) {
|
|
68
|
+
throw new Error(execResult.preSpawnError);
|
|
69
|
+
}
|
|
70
|
+
if (interpretationResult.isError && !isInterrupt) {
|
|
71
|
+
throw new ShellError('', outputWithSbFailures, execResult.code, execResult.interrupted);
|
|
72
|
+
}
|
|
73
|
+
wasInterrupted = execResult.interrupted;
|
|
74
|
+
} finally {
|
|
75
|
+
if (setToolJSX) setToolJSX(null);
|
|
76
|
+
}
|
|
77
|
+
const stdout = stdoutAccumulator.toString();
|
|
78
|
+
const persisted = await persistLargeShellOutput(execResult);
|
|
79
|
+
logShellExecution(input.command, stdout, execResult.code, wasInterrupted);
|
|
80
|
+
let strippedStdout = stripEmptyLines(stdout);
|
|
81
|
+
const extracted = extractClaudeCodeHints(strippedStdout, input.command);
|
|
82
|
+
strippedStdout = extracted.stripped;
|
|
83
|
+
if (isMainThread && extracted.hints.length > 0) {
|
|
84
|
+
for (const hint of extracted.hints) maybeRecordPluginHint(hint);
|
|
85
|
+
}
|
|
86
|
+
const imageResult = await normalizeImageOutput(strippedStdout, execResult.outputFilePath, persisted.size);
|
|
87
|
+
return {
|
|
88
|
+
data: {
|
|
89
|
+
stdout: imageResult.stdout,
|
|
90
|
+
stderr: stderrForShellReset,
|
|
91
|
+
interrupted: wasInterrupted,
|
|
92
|
+
isImage: imageResult.isImage,
|
|
93
|
+
returnCodeInterpretation: interpretationResult?.message,
|
|
94
|
+
noOutputExpected: isSilentBashCommand(input.command),
|
|
95
|
+
backgroundTaskId: execResult.backgroundTaskId,
|
|
96
|
+
backgroundedByUser: execResult.backgroundedByUser,
|
|
97
|
+
assistantAutoBackgrounded: execResult.assistantAutoBackgrounded,
|
|
98
|
+
dangerouslyDisableSandbox: 'dangerouslyDisableSandbox' in input ? input.dangerouslyDisableSandbox : undefined,
|
|
99
|
+
persistedOutputPath: persisted.path,
|
|
100
|
+
persistedOutputSize: persisted.size
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
type ConsumeInput = {
|
|
106
|
+
readonly input: BashToolInput;
|
|
107
|
+
readonly toolUseContext: ToolUseContext;
|
|
108
|
+
readonly preventCwdChanges: boolean;
|
|
109
|
+
readonly isMainThread: boolean;
|
|
110
|
+
readonly onProgress?: ToolCallProgress<BashProgress>;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
type ConsumeResult = {
|
|
114
|
+
readonly execResult: ExecResult;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
async function consumeBashCommand({
|
|
118
|
+
input,
|
|
119
|
+
toolUseContext,
|
|
120
|
+
preventCwdChanges,
|
|
121
|
+
isMainThread,
|
|
122
|
+
onProgress
|
|
123
|
+
}: ConsumeInput): Promise<ConsumeResult> {
|
|
124
|
+
const commandGenerator = runShellCommand({
|
|
125
|
+
input,
|
|
126
|
+
abortController: toolUseContext.abortController,
|
|
127
|
+
setAppState: toolUseContext.setAppStateForTasks ?? toolUseContext.setAppState,
|
|
128
|
+
setToolJSX: toolUseContext.setToolJSX,
|
|
129
|
+
preventCwdChanges,
|
|
130
|
+
isMainThread,
|
|
131
|
+
toolUseId: toolUseContext.toolUseId,
|
|
132
|
+
agentId: toolUseContext.agentId
|
|
133
|
+
});
|
|
134
|
+
let progressCounter = 0;
|
|
135
|
+
let generatorResult;
|
|
136
|
+
do {
|
|
137
|
+
generatorResult = await commandGenerator.next();
|
|
138
|
+
if (!generatorResult.done && onProgress) {
|
|
139
|
+
const progress = generatorResult.value;
|
|
140
|
+
onProgress({
|
|
141
|
+
toolUseID: `bash-progress-${progressCounter++}`,
|
|
142
|
+
data: {
|
|
143
|
+
type: 'bash_progress',
|
|
144
|
+
output: progress.output,
|
|
145
|
+
fullOutput: progress.fullOutput,
|
|
146
|
+
elapsedTimeSeconds: progress.elapsedTimeSeconds,
|
|
147
|
+
totalLines: progress.totalLines,
|
|
148
|
+
totalBytes: progress.totalBytes,
|
|
149
|
+
taskId: progress.taskId,
|
|
150
|
+
timeoutMs: progress.timeoutMs
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} while (!generatorResult.done);
|
|
155
|
+
return {
|
|
156
|
+
execResult: generatorResult.value
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function logShellExecution(command: string, stdout: string, code: number, interrupted: boolean): void {
|
|
161
|
+
const commandType = command.split(' ')[0];
|
|
162
|
+
logEvent('tengu_bash_tool_command_executed', {
|
|
163
|
+
command_type: commandType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
164
|
+
stdout_length: stdout.length,
|
|
165
|
+
stderr_length: 0,
|
|
166
|
+
exit_code: code,
|
|
167
|
+
interrupted
|
|
168
|
+
});
|
|
169
|
+
const codeIndexingTool = detectCodeIndexingFromCommand(command);
|
|
170
|
+
if (codeIndexingTool) {
|
|
171
|
+
logEvent('tengu_code_indexing_tool_used', {
|
|
172
|
+
tool: codeIndexingTool as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
173
|
+
source: 'cli' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
|
174
|
+
success: code === 0
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
type ImageOutput = {
|
|
180
|
+
readonly stdout: string;
|
|
181
|
+
readonly isImage: boolean;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
async function normalizeImageOutput(stdout: string, outputFilePath?: string, persistedOutputSize?: number): Promise<ImageOutput> {
|
|
185
|
+
if (!isImageOutput(stdout)) {
|
|
186
|
+
return {
|
|
187
|
+
stdout,
|
|
188
|
+
isImage: false
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const resized = await resizeShellImageOutput(stdout, outputFilePath, persistedOutputSize);
|
|
192
|
+
if (resized) {
|
|
193
|
+
return {
|
|
194
|
+
stdout: resized,
|
|
195
|
+
isImage: true
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
stdout,
|
|
200
|
+
isImage: false
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js'
|
|
3
|
+
import type { ToolCallProgress, ToolUseContext } from '../../Tool.js'
|
|
4
|
+
import type { AssistantMessage } from '../../types/message.js'
|
|
5
|
+
import type { BashProgress } from '../../types/tools.js'
|
|
6
|
+
import type { BashToolInput, Out } from './schemas.js'
|
|
7
|
+
|
|
8
|
+
type BashCallRuntime = {
|
|
9
|
+
readonly callBashTool: (
|
|
10
|
+
input: BashToolInput,
|
|
11
|
+
toolUseContext: ToolUseContext,
|
|
12
|
+
canUseTool?: CanUseToolFn,
|
|
13
|
+
parentMessage?: AssistantMessage,
|
|
14
|
+
onProgress?: ToolCallProgress<BashProgress>,
|
|
15
|
+
) => Promise<{ readonly data: Out }>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const requireModule = createRequire(import.meta.url)
|
|
19
|
+
let cachedRuntime: BashCallRuntime | undefined
|
|
20
|
+
|
|
21
|
+
function isBashCallRuntime(value: unknown): value is BashCallRuntime {
|
|
22
|
+
if (typeof value !== 'object' || value === null) return false
|
|
23
|
+
const module = value as Partial<Record<keyof BashCallRuntime, unknown>>
|
|
24
|
+
return typeof module.callBashTool === 'function'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function loadBashCallRuntime(): BashCallRuntime {
|
|
28
|
+
if (cachedRuntime !== undefined) return cachedRuntime
|
|
29
|
+
const loaded: unknown = requireModule('./call.js')
|
|
30
|
+
if (!isBashCallRuntime(loaded)) {
|
|
31
|
+
throw new Error('Bash call module did not expose expected lifecycle')
|
|
32
|
+
}
|
|
33
|
+
cachedRuntime = loaded
|
|
34
|
+
return loaded
|
|
35
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../../services/analytics/index.js';
|
|
2
|
+
import { splitCommand_DEPRECATED, splitCommandWithOperators } from '../../utils/bash/commands.js';
|
|
3
|
+
|
|
4
|
+
const BASH_SEARCH_COMMANDS = new Set(['find', 'grep', 'rg', 'ag', 'ack', 'locate', 'which', 'whereis']);
|
|
5
|
+
const BASH_READ_COMMANDS = new Set(['cat', 'head', 'tail', 'less', 'more', 'wc', 'stat', 'file', 'strings', 'jq', 'awk', 'cut', 'sort', 'uniq', 'tr']);
|
|
6
|
+
const BASH_LIST_COMMANDS = new Set(['ls', 'tree', 'du']);
|
|
7
|
+
const BASH_SEMANTIC_NEUTRAL_COMMANDS = new Set(['echo', 'printf', 'true', 'false', ':']);
|
|
8
|
+
const BASH_SILENT_COMMANDS = new Set(['mv', 'cp', 'rm', 'mkdir', 'rmdir', 'chmod', 'chown', 'chgrp', 'touch', 'ln', 'cd', 'export', 'unset', 'wait']);
|
|
9
|
+
const DISALLOWED_AUTO_BACKGROUND_COMMANDS = ['sleep'];
|
|
10
|
+
const COMMON_BACKGROUND_COMMANDS = ['npm', 'yarn', 'pnpm', 'node', 'python', 'python3', 'go', 'cargo', 'make', 'docker', 'terraform', 'webpack', 'vite', 'jest', 'pytest', 'curl', 'wget', 'build', 'test', 'serve', 'watch', 'dev'] as const;
|
|
11
|
+
|
|
12
|
+
type SearchReadClassification = {
|
|
13
|
+
readonly isSearch: boolean;
|
|
14
|
+
readonly isRead: boolean;
|
|
15
|
+
readonly isList: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const NOT_SEARCH_OR_READ: SearchReadClassification = {
|
|
19
|
+
isSearch: false,
|
|
20
|
+
isRead: false,
|
|
21
|
+
isList: false
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function isSearchOrReadBashCommand(command: string): SearchReadClassification {
|
|
25
|
+
let partsWithOperators: string[];
|
|
26
|
+
try {
|
|
27
|
+
partsWithOperators = splitCommandWithOperators(command);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
return NOT_SEARCH_OR_READ;
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
if (partsWithOperators.length === 0) {
|
|
35
|
+
return NOT_SEARCH_OR_READ;
|
|
36
|
+
}
|
|
37
|
+
let hasSearch = false;
|
|
38
|
+
let hasRead = false;
|
|
39
|
+
let hasList = false;
|
|
40
|
+
let hasNonNeutralCommand = false;
|
|
41
|
+
let skipNextAsRedirectTarget = false;
|
|
42
|
+
for (const part of partsWithOperators) {
|
|
43
|
+
if (skipNextAsRedirectTarget) {
|
|
44
|
+
skipNextAsRedirectTarget = false;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (part === '>' || part === '>>' || part === '>&') {
|
|
48
|
+
skipNextAsRedirectTarget = true;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (part === '||' || part === '&&' || part === '|' || part === ';') {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const baseCommand = part.trim().split(/\s+/)[0];
|
|
55
|
+
if (!baseCommand || BASH_SEMANTIC_NEUTRAL_COMMANDS.has(baseCommand)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
hasNonNeutralCommand = true;
|
|
59
|
+
const isPartSearch = BASH_SEARCH_COMMANDS.has(baseCommand);
|
|
60
|
+
const isPartRead = BASH_READ_COMMANDS.has(baseCommand);
|
|
61
|
+
const isPartList = BASH_LIST_COMMANDS.has(baseCommand);
|
|
62
|
+
if (!isPartSearch && !isPartRead && !isPartList) {
|
|
63
|
+
return NOT_SEARCH_OR_READ;
|
|
64
|
+
}
|
|
65
|
+
if (isPartSearch) hasSearch = true;
|
|
66
|
+
if (isPartRead) hasRead = true;
|
|
67
|
+
if (isPartList) hasList = true;
|
|
68
|
+
}
|
|
69
|
+
if (!hasNonNeutralCommand) {
|
|
70
|
+
return NOT_SEARCH_OR_READ;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
isSearch: hasSearch,
|
|
74
|
+
isRead: hasRead,
|
|
75
|
+
isList: hasList
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function isSilentBashCommand(command: string): boolean {
|
|
80
|
+
let partsWithOperators: string[];
|
|
81
|
+
try {
|
|
82
|
+
partsWithOperators = splitCommandWithOperators(command);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error instanceof Error) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
if (partsWithOperators.length === 0) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
let hasNonFallbackCommand = false;
|
|
93
|
+
let lastOperator: string | null = null;
|
|
94
|
+
let skipNextAsRedirectTarget = false;
|
|
95
|
+
for (const part of partsWithOperators) {
|
|
96
|
+
if (skipNextAsRedirectTarget) {
|
|
97
|
+
skipNextAsRedirectTarget = false;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (part === '>' || part === '>>' || part === '>&') {
|
|
101
|
+
skipNextAsRedirectTarget = true;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (part === '||' || part === '&&' || part === '|' || part === ';') {
|
|
105
|
+
lastOperator = part;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const baseCommand = part.trim().split(/\s+/)[0];
|
|
109
|
+
if (!baseCommand || (lastOperator === '||' && BASH_SEMANTIC_NEUTRAL_COMMANDS.has(baseCommand))) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
hasNonFallbackCommand = true;
|
|
113
|
+
if (!BASH_SILENT_COMMANDS.has(baseCommand)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return hasNonFallbackCommand;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function isAutobackgroundingAllowed(command: string): boolean {
|
|
121
|
+
const parts = splitCommand_DEPRECATED(command);
|
|
122
|
+
if (parts.length === 0) return true;
|
|
123
|
+
const baseCommand = parts[0]?.trim();
|
|
124
|
+
if (!baseCommand) return true;
|
|
125
|
+
return !DISALLOWED_AUTO_BACKGROUND_COMMANDS.includes(baseCommand);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function detectBlockedSleepPattern(command: string): string | null {
|
|
129
|
+
const parts = splitCommand_DEPRECATED(command);
|
|
130
|
+
if (parts.length === 0) return null;
|
|
131
|
+
const first = parts[0]?.trim() ?? '';
|
|
132
|
+
const match = /^sleep\s+(\d+)\s*$/.exec(first);
|
|
133
|
+
const secondsText = match?.[1];
|
|
134
|
+
if (!secondsText) return null;
|
|
135
|
+
const seconds = parseInt(secondsText, 10);
|
|
136
|
+
if (seconds < 2) return null;
|
|
137
|
+
const rest = parts.slice(1).join(' ').trim();
|
|
138
|
+
return rest ? `sleep ${seconds} followed by: ${rest}` : `standalone sleep ${seconds}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function getCommandTypeForLogging(command: string): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS {
|
|
142
|
+
const parts = splitCommand_DEPRECATED(command);
|
|
143
|
+
if (parts.length === 0) return 'other' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS;
|
|
144
|
+
for (const part of parts) {
|
|
145
|
+
const baseCommand = part.split(' ')[0] || '';
|
|
146
|
+
if (COMMON_BACKGROUND_COMMANDS.includes(baseCommand as (typeof COMMON_BACKGROUND_COMMANDS)[number])) {
|
|
147
|
+
return baseCommand as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return 'other' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS;
|
|
151
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
|
|
3
|
+
type SearchReadClassification = {
|
|
4
|
+
readonly isSearch: boolean
|
|
5
|
+
readonly isRead: boolean
|
|
6
|
+
readonly isList: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type BashCommandClassificationRuntime = {
|
|
10
|
+
readonly isSearchOrReadBashCommand: (
|
|
11
|
+
command: string,
|
|
12
|
+
) => SearchReadClassification
|
|
13
|
+
readonly detectBlockedSleepPattern: (command: string) => string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const requireModule = createRequire(import.meta.url)
|
|
17
|
+
let cachedRuntime: BashCommandClassificationRuntime | undefined
|
|
18
|
+
|
|
19
|
+
function isBashCommandClassificationRuntime(
|
|
20
|
+
value: unknown,
|
|
21
|
+
): value is BashCommandClassificationRuntime {
|
|
22
|
+
if (typeof value !== 'object' || value === null) return false
|
|
23
|
+
const module = value as Partial<
|
|
24
|
+
Record<keyof BashCommandClassificationRuntime, unknown>
|
|
25
|
+
>
|
|
26
|
+
return (
|
|
27
|
+
typeof module.isSearchOrReadBashCommand === 'function' &&
|
|
28
|
+
typeof module.detectBlockedSleepPattern === 'function'
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function loadBashCommandClassificationRuntime(): BashCommandClassificationRuntime {
|
|
33
|
+
if (cachedRuntime !== undefined) return cachedRuntime
|
|
34
|
+
const loaded: unknown = requireModule('./commandClassification.js')
|
|
35
|
+
if (!isBashCommandClassificationRuntime(loaded)) {
|
|
36
|
+
throw new Error('Bash command classification module shape mismatch')
|
|
37
|
+
}
|
|
38
|
+
cachedRuntime = loaded
|
|
39
|
+
return loaded
|
|
40
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import type { ToolPermissionContext } from '../../Tool.js'
|
|
3
|
+
|
|
4
|
+
type CwdResetRuntime = {
|
|
5
|
+
readonly resetCwdIfOutsideProject: (
|
|
6
|
+
toolPermissionContext: ToolPermissionContext,
|
|
7
|
+
) => boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const requireModule = createRequire(import.meta.url)
|
|
11
|
+
let cachedRuntime: CwdResetRuntime | undefined
|
|
12
|
+
|
|
13
|
+
function isCwdResetRuntime(value: unknown): value is CwdResetRuntime {
|
|
14
|
+
if (typeof value !== 'object' || value === null) return false
|
|
15
|
+
const module = value as Partial<Record<keyof CwdResetRuntime, unknown>>
|
|
16
|
+
return typeof module.resetCwdIfOutsideProject === 'function'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function loadCwdResetRuntime(): CwdResetRuntime {
|
|
20
|
+
if (cachedRuntime !== undefined) return cachedRuntime
|
|
21
|
+
const loaded: unknown = requireModule('./utils.js')
|
|
22
|
+
if (!isCwdResetRuntime(loaded)) {
|
|
23
|
+
throw new Error('Bash utility module did not expose cwd reset helper')
|
|
24
|
+
}
|
|
25
|
+
cachedRuntime = loaded
|
|
26
|
+
return loaded
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function resetShellCwdIfOutsideProject(
|
|
30
|
+
toolPermissionContext: ToolPermissionContext,
|
|
31
|
+
): boolean {
|
|
32
|
+
return loadCwdResetRuntime().resetCwdIfOutsideProject(toolPermissionContext)
|
|
33
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const MAX_LINES_TO_SHOW = 3
|
|
2
|
+
|
|
3
|
+
export function isShellOutputLineTruncated(content: string): boolean {
|
|
4
|
+
let position = 0
|
|
5
|
+
for (let line = 0; line <= MAX_LINES_TO_SHOW; line++) {
|
|
6
|
+
position = content.indexOf('\n', position)
|
|
7
|
+
if (position === -1) return false
|
|
8
|
+
position++
|
|
9
|
+
}
|
|
10
|
+
return position < content.length
|
|
11
|
+
}
|
|
@@ -3,6 +3,9 @@ import type { ToolPermissionContext } from '../../Tool.js'
|
|
|
3
3
|
import { splitCommand_DEPRECATED } from '../../utils/bash/commands.js'
|
|
4
4
|
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
|
|
5
5
|
import type { BashTool } from './BashTool.js'
|
|
6
|
+
import { getDestructiveCommandWarning } from './destructiveCommandWarning.js'
|
|
7
|
+
import { getBypassImmuneShellPermissionResult } from './shellPermissionGauntlet.js'
|
|
8
|
+
import { BASH_TOOL_NAME } from './toolName.js'
|
|
6
9
|
|
|
7
10
|
const ACCEPT_EDITS_ALLOWED_COMMANDS = [
|
|
8
11
|
'mkdir',
|
|
@@ -73,8 +76,17 @@ export function checkPermissionMode(
|
|
|
73
76
|
input: z.infer<typeof BashTool.inputSchema>,
|
|
74
77
|
toolPermissionContext: ToolPermissionContext,
|
|
75
78
|
): PermissionResult {
|
|
76
|
-
// Skip if in bypass mode (handled elsewhere)
|
|
77
79
|
if (toolPermissionContext.mode === 'bypassPermissions') {
|
|
80
|
+
const bypassImmuneResult = getBypassImmuneShellPermissionResult(
|
|
81
|
+
input.command,
|
|
82
|
+
BASH_TOOL_NAME,
|
|
83
|
+
toolPermissionContext,
|
|
84
|
+
getDestructiveCommandWarning,
|
|
85
|
+
)
|
|
86
|
+
if (bypassImmuneResult !== null) {
|
|
87
|
+
return bypassImmuneResult
|
|
88
|
+
}
|
|
89
|
+
|
|
78
90
|
return {
|
|
79
91
|
behavior: 'passthrough',
|
|
80
92
|
message: 'Bypass mode is handled in main permission flow',
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { copyFile, link, stat as fsStat, truncate as fsTruncate } from 'fs/promises';
|
|
2
|
+
import { ensureToolResultsDir, getToolResultPath } from '../../utils/toolResultStorage.js';
|
|
3
|
+
import type { ExecResult } from '../../utils/ShellCommand.js';
|
|
4
|
+
|
|
5
|
+
const MAX_PERSISTED_SIZE = 64 * 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
type PersistedOutput = {
|
|
8
|
+
readonly path?: string;
|
|
9
|
+
readonly size?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export async function persistLargeShellOutput(result: ExecResult): Promise<PersistedOutput> {
|
|
13
|
+
if (!result.outputFilePath || !result.outputTaskId) {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const fileStat = await fsStat(result.outputFilePath);
|
|
18
|
+
await ensureToolResultsDir();
|
|
19
|
+
const dest = getToolResultPath(result.outputTaskId, false);
|
|
20
|
+
if (fileStat.size > MAX_PERSISTED_SIZE) {
|
|
21
|
+
await fsTruncate(result.outputFilePath, MAX_PERSISTED_SIZE);
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await link(result.outputFilePath, dest);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error instanceof Error) {
|
|
27
|
+
await copyFile(result.outputFilePath, dest);
|
|
28
|
+
} else {
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
path: dest,
|
|
34
|
+
size: fileStat.size
|
|
35
|
+
};
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { feature } from 'bun:bundle';
|
|
2
|
+
import type { ToolUseContext, ValidationResult } from '../../Tool.js';
|
|
3
|
+
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js';
|
|
4
|
+
import { parseForSecurity } from '../../utils/bash/ast.js';
|
|
5
|
+
import { bashToolHasPermission, commandHasAnyCd, matchWildcardPattern, permissionRuleExtractPrefix } from './bashPermissions.js';
|
|
6
|
+
import { checkReadOnlyConstraints } from './readOnlyValidation.js';
|
|
7
|
+
import { getDestructiveCommandWarning } from './destructiveCommandWarning.js';
|
|
8
|
+
import { getBypassImmuneShellPermissionResult } from './shellPermissionGauntlet.js';
|
|
9
|
+
import { BASH_TOOL_NAME } from './toolName.js';
|
|
10
|
+
import { detectBlockedSleepPattern } from './commandClassification.js';
|
|
11
|
+
import { isBackgroundTasksDisabled } from './schemas.js';
|
|
12
|
+
import type { BashToolInput } from './schemas.js';
|
|
13
|
+
|
|
14
|
+
export async function validateBashInput(input: BashToolInput): Promise<ValidationResult> {
|
|
15
|
+
if (feature('MONITOR_TOOL') && !isBackgroundTasksDisabled && !input.run_in_background) {
|
|
16
|
+
const sleepPattern = detectBlockedSleepPattern(input.command);
|
|
17
|
+
if (sleepPattern !== null) {
|
|
18
|
+
return {
|
|
19
|
+
result: false,
|
|
20
|
+
message: `Blocked: ${sleepPattern}. Run blocking commands in the background with run_in_background: true — you'll get a completion notification when done. For streaming events (watching logs, polling APIs), use the Monitor tool. If you genuinely need a delay (rate limiting, deliberate pacing), keep it under 2 seconds.`,
|
|
21
|
+
errorCode: 10
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
result: true
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isBashReadOnly(input: BashToolInput): boolean {
|
|
31
|
+
const compoundCommandHasCd = commandHasAnyCd(input.command);
|
|
32
|
+
const result = checkReadOnlyConstraints(input, compoundCommandHasCd);
|
|
33
|
+
return result.behavior === 'allow';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function prepareBashPermissionMatcher({
|
|
37
|
+
command
|
|
38
|
+
}: BashToolInput): Promise<(pattern: string) => boolean> {
|
|
39
|
+
const parsed = await parseForSecurity(command);
|
|
40
|
+
if (parsed.kind !== 'simple') {
|
|
41
|
+
return () => true;
|
|
42
|
+
}
|
|
43
|
+
const subcommands = parsed.commands.map(c => c.argv.join(' '));
|
|
44
|
+
return pattern => {
|
|
45
|
+
const prefix = permissionRuleExtractPrefix(pattern);
|
|
46
|
+
return subcommands.some(cmd => {
|
|
47
|
+
if (prefix !== null) {
|
|
48
|
+
return cmd === prefix || cmd.startsWith(`${prefix} `);
|
|
49
|
+
}
|
|
50
|
+
return matchWildcardPattern(pattern, cmd);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function checkBashPermissions(input: BashToolInput, context: ToolUseContext): Promise<PermissionResult> {
|
|
56
|
+
const bypassImmuneResult = getBypassImmuneShellPermissionResult(
|
|
57
|
+
input.command,
|
|
58
|
+
BASH_TOOL_NAME,
|
|
59
|
+
context.getAppState().toolPermissionContext,
|
|
60
|
+
getDestructiveCommandWarning
|
|
61
|
+
);
|
|
62
|
+
if (bypassImmuneResult !== null) {
|
|
63
|
+
return bypassImmuneResult;
|
|
64
|
+
}
|
|
65
|
+
return bashToolHasPermission(input, context);
|
|
66
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import type { ToolUseContext, ValidationResult } from '../../Tool.js'
|
|
3
|
+
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
|
|
4
|
+
import type { BashToolInput } from './schemas.js'
|
|
5
|
+
|
|
6
|
+
type BashPermissionRuntime = {
|
|
7
|
+
readonly validateBashInput: (
|
|
8
|
+
input: BashToolInput,
|
|
9
|
+
) => Promise<ValidationResult>
|
|
10
|
+
readonly isBashReadOnly: (input: BashToolInput) => boolean
|
|
11
|
+
readonly prepareBashPermissionMatcher: (
|
|
12
|
+
input: BashToolInput,
|
|
13
|
+
) => Promise<(pattern: string) => boolean>
|
|
14
|
+
readonly checkBashPermissions: (
|
|
15
|
+
input: BashToolInput,
|
|
16
|
+
context: ToolUseContext,
|
|
17
|
+
) => Promise<PermissionResult>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const requireModule = createRequire(import.meta.url)
|
|
21
|
+
let cachedRuntime: BashPermissionRuntime | undefined
|
|
22
|
+
|
|
23
|
+
function isBashPermissionRuntime(
|
|
24
|
+
value: unknown,
|
|
25
|
+
): value is BashPermissionRuntime {
|
|
26
|
+
if (typeof value !== 'object' || value === null) return false
|
|
27
|
+
const module = value as Partial<Record<keyof BashPermissionRuntime, unknown>>
|
|
28
|
+
return (
|
|
29
|
+
typeof module.validateBashInput === 'function' &&
|
|
30
|
+
typeof module.isBashReadOnly === 'function' &&
|
|
31
|
+
typeof module.prepareBashPermissionMatcher === 'function' &&
|
|
32
|
+
typeof module.checkBashPermissions === 'function'
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function loadBashPermissionRuntime(): BashPermissionRuntime {
|
|
37
|
+
if (cachedRuntime !== undefined) return cachedRuntime
|
|
38
|
+
const loaded: unknown = requireModule('./permissionClassification.js')
|
|
39
|
+
if (!isBashPermissionRuntime(loaded)) {
|
|
40
|
+
throw new Error('Bash permission module did not expose expected functions')
|
|
41
|
+
}
|
|
42
|
+
cachedRuntime = loaded
|
|
43
|
+
return loaded
|
|
44
|
+
}
|