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,29 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
|
|
3
|
+
import type { Out } from './schemas.js'
|
|
4
|
+
|
|
5
|
+
type BashResultRuntime = {
|
|
6
|
+
readonly mapBashToolResultToBlock: (
|
|
7
|
+
output: Out,
|
|
8
|
+
toolUseID: string,
|
|
9
|
+
) => ToolResultBlockParam
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const requireModule = createRequire(import.meta.url)
|
|
13
|
+
let cachedRuntime: BashResultRuntime | undefined
|
|
14
|
+
|
|
15
|
+
function isBashResultRuntime(value: unknown): value is BashResultRuntime {
|
|
16
|
+
if (typeof value !== 'object' || value === null) return false
|
|
17
|
+
const module = value as Partial<Record<keyof BashResultRuntime, unknown>>
|
|
18
|
+
return typeof module.mapBashToolResultToBlock === 'function'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadBashResultRuntime(): BashResultRuntime {
|
|
22
|
+
if (cachedRuntime !== undefined) return cachedRuntime
|
|
23
|
+
const loaded: unknown = requireModule('./resultMapping.js')
|
|
24
|
+
if (!isBashResultRuntime(loaded)) {
|
|
25
|
+
throw new Error('Bash result module did not expose expected mapper')
|
|
26
|
+
}
|
|
27
|
+
cachedRuntime = loaded
|
|
28
|
+
return loaded
|
|
29
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs';
|
|
2
|
+
import { getTaskOutputPath } from '../../utils/task/diskOutput.js';
|
|
3
|
+
import { buildLargeToolResultMessage, generatePreview, PREVIEW_SIZE_BYTES } from '../../utils/toolResultStorage.js';
|
|
4
|
+
import { buildImageToolResult } from './shellOutputUtils.js';
|
|
5
|
+
import type { Out } from './schemas.js';
|
|
6
|
+
|
|
7
|
+
const EOL = '\n';
|
|
8
|
+
const ASSISTANT_BLOCKING_BUDGET_MS = 15_000;
|
|
9
|
+
|
|
10
|
+
export function mapBashToolResultToBlock({
|
|
11
|
+
interrupted,
|
|
12
|
+
stdout,
|
|
13
|
+
stderr,
|
|
14
|
+
isImage,
|
|
15
|
+
backgroundTaskId,
|
|
16
|
+
backgroundedByUser,
|
|
17
|
+
assistantAutoBackgrounded,
|
|
18
|
+
structuredContent,
|
|
19
|
+
persistedOutputPath,
|
|
20
|
+
persistedOutputSize
|
|
21
|
+
}: Out, toolUseID: string): ToolResultBlockParam {
|
|
22
|
+
if (structuredContent && structuredContent.length > 0) {
|
|
23
|
+
return {
|
|
24
|
+
tool_use_id: toolUseID,
|
|
25
|
+
type: 'tool_result',
|
|
26
|
+
content: structuredContent
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (isImage) {
|
|
30
|
+
const block = buildImageToolResult(stdout, toolUseID);
|
|
31
|
+
if (block) return block;
|
|
32
|
+
}
|
|
33
|
+
let processedStdout = stdout;
|
|
34
|
+
if (stdout) {
|
|
35
|
+
processedStdout = stdout.replace(/^(\s*\n)+/, '').trimEnd();
|
|
36
|
+
}
|
|
37
|
+
if (persistedOutputPath) {
|
|
38
|
+
const preview = generatePreview(processedStdout, PREVIEW_SIZE_BYTES);
|
|
39
|
+
processedStdout = buildLargeToolResultMessage({
|
|
40
|
+
filepath: persistedOutputPath,
|
|
41
|
+
originalSize: persistedOutputSize ?? 0,
|
|
42
|
+
isJson: false,
|
|
43
|
+
preview: preview.preview,
|
|
44
|
+
hasMore: preview.hasMore
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
let errorMessage = stderr.trim();
|
|
48
|
+
if (interrupted) {
|
|
49
|
+
if (stderr) errorMessage += EOL;
|
|
50
|
+
errorMessage += '<error>Command was aborted before completion</error>';
|
|
51
|
+
}
|
|
52
|
+
const backgroundInfo = formatBackgroundInfo({
|
|
53
|
+
backgroundTaskId,
|
|
54
|
+
backgroundedByUser,
|
|
55
|
+
assistantAutoBackgrounded
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
tool_use_id: toolUseID,
|
|
59
|
+
type: 'tool_result',
|
|
60
|
+
content: [processedStdout, errorMessage, backgroundInfo].filter(Boolean).join('\n'),
|
|
61
|
+
is_error: interrupted
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type BackgroundInfoInput = Pick<Out, 'backgroundTaskId' | 'backgroundedByUser' | 'assistantAutoBackgrounded'>;
|
|
66
|
+
|
|
67
|
+
function formatBackgroundInfo({
|
|
68
|
+
backgroundTaskId,
|
|
69
|
+
backgroundedByUser,
|
|
70
|
+
assistantAutoBackgrounded
|
|
71
|
+
}: BackgroundInfoInput): string {
|
|
72
|
+
if (!backgroundTaskId) {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
const outputPath = getTaskOutputPath(backgroundTaskId);
|
|
76
|
+
if (assistantAutoBackgrounded) {
|
|
77
|
+
return `Command exceeded the assistant-mode blocking budget (${ASSISTANT_BLOCKING_BUDGET_MS / 1000}s) and was moved to the background with ID: ${backgroundTaskId}. It is still running — you will be notified when it completes. Output is being written to: ${outputPath}. In assistant mode, delegate long-running work to a subagent or use run_in_background to keep this conversation responsive.`;
|
|
78
|
+
}
|
|
79
|
+
if (backgroundedByUser) {
|
|
80
|
+
return `Command was manually backgrounded by user with ID: ${backgroundTaskId}. Output is being written to: ${outputPath}`;
|
|
81
|
+
}
|
|
82
|
+
return `Command running in background with ID: ${backgroundTaskId}. Output is being written to: ${outputPath}`;
|
|
83
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
|
|
3
|
+
type SandboxInput = {
|
|
4
|
+
readonly command?: string
|
|
5
|
+
readonly dangerouslyDisableSandbox?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type ShouldUseSandbox = (input: SandboxInput) => boolean
|
|
9
|
+
type ShellSandboxManager = {
|
|
10
|
+
readonly annotateStderrWithSandboxFailures: (
|
|
11
|
+
command: string,
|
|
12
|
+
stderr: string,
|
|
13
|
+
) => string
|
|
14
|
+
readonly isSandboxEnabledInSettings: () => boolean
|
|
15
|
+
readonly areUnsandboxedCommandsAllowed: () => boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const requireModule = createRequire(import.meta.url)
|
|
19
|
+
let cachedShouldUseSandbox: ShouldUseSandbox | undefined
|
|
20
|
+
let cachedSandboxManager: ShellSandboxManager | undefined
|
|
21
|
+
|
|
22
|
+
function isSandboxModule(
|
|
23
|
+
value: unknown,
|
|
24
|
+
): value is { readonly shouldUseSandbox: ShouldUseSandbox } {
|
|
25
|
+
if (typeof value !== 'object' || value === null) return false
|
|
26
|
+
const module = value as { readonly shouldUseSandbox?: unknown }
|
|
27
|
+
return typeof module.shouldUseSandbox === 'function'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function shouldUseSandboxForShell(input: SandboxInput): boolean {
|
|
31
|
+
if (cachedShouldUseSandbox !== undefined) return cachedShouldUseSandbox(input)
|
|
32
|
+
const loaded: unknown = requireModule('./shouldUseSandbox.js')
|
|
33
|
+
if (!isSandboxModule(loaded)) {
|
|
34
|
+
throw new Error('Bash sandbox policy module did not expose shouldUseSandbox')
|
|
35
|
+
}
|
|
36
|
+
cachedShouldUseSandbox = loaded.shouldUseSandbox
|
|
37
|
+
return loaded.shouldUseSandbox(input)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isSandboxAdapterModule(
|
|
41
|
+
value: unknown,
|
|
42
|
+
): value is { readonly SandboxManager: ShellSandboxManager } {
|
|
43
|
+
if (typeof value !== 'object' || value === null) return false
|
|
44
|
+
const module = value as { readonly SandboxManager?: unknown }
|
|
45
|
+
if (typeof module.SandboxManager !== 'object' || module.SandboxManager === null) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
const manager = module.SandboxManager as Partial<ShellSandboxManager>
|
|
49
|
+
return (
|
|
50
|
+
typeof manager.annotateStderrWithSandboxFailures === 'function' &&
|
|
51
|
+
typeof manager.isSandboxEnabledInSettings === 'function' &&
|
|
52
|
+
typeof manager.areUnsandboxedCommandsAllowed === 'function'
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function loadSandboxManager(): ShellSandboxManager {
|
|
57
|
+
if (cachedSandboxManager !== undefined) return cachedSandboxManager
|
|
58
|
+
const loaded: unknown = requireModule('../../utils/sandbox/sandbox-adapter.js')
|
|
59
|
+
if (!isSandboxAdapterModule(loaded)) {
|
|
60
|
+
throw new Error('Sandbox adapter module did not expose SandboxManager')
|
|
61
|
+
}
|
|
62
|
+
cachedSandboxManager = loaded.SandboxManager
|
|
63
|
+
return loaded.SandboxManager
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function annotateShellStderrWithSandboxFailures(
|
|
67
|
+
command: string,
|
|
68
|
+
stderr: string,
|
|
69
|
+
): string {
|
|
70
|
+
return loadSandboxManager().annotateStderrWithSandboxFailures(command, stderr)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isShellSandboxEnabledInSettings(): boolean {
|
|
74
|
+
return loadSandboxManager().isSandboxEnabledInSettings()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function areUnsandboxedShellCommandsAllowed(): boolean {
|
|
78
|
+
return loadSandboxManager().areUnsandboxedCommandsAllowed()
|
|
79
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
import { isEnvTruthy } from '../../utils/envUtils.js';
|
|
4
|
+
import { lazySchema } from '../../utils/lazySchema.js';
|
|
5
|
+
import { semanticBoolean } from '../../utils/semanticBoolean.js';
|
|
6
|
+
import { semanticNumber } from '../../utils/semanticNumber.js';
|
|
7
|
+
import { getMaxBashTimeoutMs } from '../../utils/timeouts.js';
|
|
8
|
+
|
|
9
|
+
export const isBackgroundTasksDisabled =
|
|
10
|
+
// eslint-disable-next-line custom-rules/no-process-env-top-level -- Intentional: schema must be defined at module load
|
|
11
|
+
isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS);
|
|
12
|
+
|
|
13
|
+
export const fullInputSchema = lazySchema(() => z.strictObject({
|
|
14
|
+
command: z.string().describe('The command to execute'),
|
|
15
|
+
timeout: semanticNumber(z.number().optional()).describe(`Optional timeout in milliseconds (max ${getMaxBashTimeoutMs()})`),
|
|
16
|
+
description: z.string().optional().describe(`Clear, concise description of what this command does in active voice. Never use words like "complex" or "risk" in the description - just describe what it does.
|
|
17
|
+
|
|
18
|
+
For simple commands (git, npm, standard CLI tools), keep it brief (5-10 words):
|
|
19
|
+
- ls -> "List files in current directory"
|
|
20
|
+
- git status -> "Show working tree status"
|
|
21
|
+
- npm install -> "Install package dependencies"
|
|
22
|
+
|
|
23
|
+
For commands that are harder to parse at a glance (piped commands, obscure flags, etc.), add enough context to clarify what it does:
|
|
24
|
+
- find . -name "*.tmp" -exec rm {} \\; -> "Find and delete all .tmp files recursively"
|
|
25
|
+
- git reset --hard origin/main -> "Discard all local changes and match remote main"
|
|
26
|
+
- curl -s url | jq '.data[]' -> "Fetch JSON from URL and extract data array elements"`),
|
|
27
|
+
run_in_background: semanticBoolean(z.boolean().optional()).describe(`Set to true to run this command in the background. Use Read to read the output later.`),
|
|
28
|
+
dangerouslyDisableSandbox: semanticBoolean(z.boolean().optional()).describe('Set this to true to dangerously override sandbox mode and run commands without sandboxing.'),
|
|
29
|
+
_simulatedSedEdit: z.object({
|
|
30
|
+
filePath: z.string(),
|
|
31
|
+
newContent: z.string()
|
|
32
|
+
}).optional().describe('Internal: pre-computed sed edit result from preview')
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
export const inputSchema = lazySchema(() => isBackgroundTasksDisabled ? fullInputSchema().omit({
|
|
36
|
+
run_in_background: true,
|
|
37
|
+
_simulatedSedEdit: true
|
|
38
|
+
}) : fullInputSchema().omit({
|
|
39
|
+
_simulatedSedEdit: true
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
type StructuredContent = Exclude<ToolResultBlockParam['content'], string>;
|
|
43
|
+
const structuredContentSchema = z.custom<StructuredContent>((value): value is StructuredContent => Array.isArray(value));
|
|
44
|
+
|
|
45
|
+
export const outputSchema = lazySchema(() => z.object({
|
|
46
|
+
stdout: z.string().describe('The standard output of the command'),
|
|
47
|
+
stderr: z.string().describe('The standard error output of the command'),
|
|
48
|
+
rawOutputPath: z.string().optional().describe('Path to raw output file for large MCP tool outputs'),
|
|
49
|
+
interrupted: z.boolean().describe('Whether the command was interrupted'),
|
|
50
|
+
isImage: z.boolean().optional().describe('Flag to indicate if stdout contains image data'),
|
|
51
|
+
backgroundTaskId: z.string().optional().describe('ID of the background task if command is running in background'),
|
|
52
|
+
backgroundedByUser: z.boolean().optional().describe('True if the user manually backgrounded the command with Ctrl+B'),
|
|
53
|
+
assistantAutoBackgrounded: z.boolean().optional().describe('True if assistant-mode auto-backgrounded a long-running blocking command'),
|
|
54
|
+
dangerouslyDisableSandbox: z.boolean().optional().describe('Flag to indicate if sandbox mode was overridden'),
|
|
55
|
+
returnCodeInterpretation: z.string().optional().describe('Semantic interpretation for non-error exit codes with special meaning'),
|
|
56
|
+
noOutputExpected: z.boolean().optional().describe('Whether the command is expected to produce no output on success'),
|
|
57
|
+
structuredContent: structuredContentSchema.optional().describe('Structured content blocks'),
|
|
58
|
+
persistedOutputPath: z.string().optional().describe('Path to the persisted full output in tool-results dir (set when output is too large for inline)'),
|
|
59
|
+
persistedOutputSize: z.number().optional().describe('Total size of the output in bytes (set when output is too large for inline)')
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
export type InputSchema = ReturnType<typeof inputSchema>;
|
|
63
|
+
export type BashToolInput = z.infer<ReturnType<typeof fullInputSchema>>;
|
|
64
|
+
export type OutputSchema = ReturnType<typeof outputSchema>;
|
|
65
|
+
export type Out = z.infer<OutputSchema>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { notifyVscodeFileUpdated } from '../../services/mcp/vscodeSdkMcp.js';
|
|
2
|
+
import type { ToolUseContext } from '../../Tool.js';
|
|
3
|
+
import type { AssistantMessage } from '../../types/message.js';
|
|
4
|
+
import { isENOENT } from '../../utils/errors.js';
|
|
5
|
+
import { detectFileEncoding, detectLineEndings, getFileModificationTime, writeTextContent } from '../../utils/file.js';
|
|
6
|
+
import { fileHistoryEnabled, fileHistoryTrackEdit } from '../../utils/fileHistory.js';
|
|
7
|
+
import { getFsImplementation } from '../../utils/fsOperations.js';
|
|
8
|
+
import { expandPath } from '../../utils/path.js';
|
|
9
|
+
import type { Out } from './schemas.js';
|
|
10
|
+
|
|
11
|
+
type SimulatedSedEditResult = {
|
|
12
|
+
readonly data: Out;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type SimulatedSedEditContext = Pick<ToolUseContext, 'readFileState' | 'updateFileHistoryState'>;
|
|
16
|
+
|
|
17
|
+
export async function applySedEdit(simulatedEdit: {
|
|
18
|
+
readonly filePath: string;
|
|
19
|
+
readonly newContent: string;
|
|
20
|
+
}, toolUseContext: SimulatedSedEditContext, parentMessage?: AssistantMessage): Promise<SimulatedSedEditResult> {
|
|
21
|
+
const { filePath, newContent } = simulatedEdit;
|
|
22
|
+
const absoluteFilePath = expandPath(filePath);
|
|
23
|
+
const fs = getFsImplementation();
|
|
24
|
+
const encoding = detectFileEncoding(absoluteFilePath);
|
|
25
|
+
let originalContent: string;
|
|
26
|
+
try {
|
|
27
|
+
originalContent = await fs.readFile(absoluteFilePath, { encoding });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (isENOENT(error)) {
|
|
30
|
+
return {
|
|
31
|
+
data: {
|
|
32
|
+
stdout: '',
|
|
33
|
+
stderr: `sed: ${filePath}: No such file or directory\nExit code 1`,
|
|
34
|
+
interrupted: false
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
if (fileHistoryEnabled() && parentMessage) {
|
|
41
|
+
await fileHistoryTrackEdit(toolUseContext.updateFileHistoryState, absoluteFilePath, parentMessage.uuid);
|
|
42
|
+
}
|
|
43
|
+
const endings = detectLineEndings(absoluteFilePath);
|
|
44
|
+
writeTextContent(absoluteFilePath, newContent, encoding, endings);
|
|
45
|
+
notifyVscodeFileUpdated(absoluteFilePath, originalContent, newContent);
|
|
46
|
+
toolUseContext.readFileState.set(absoluteFilePath, {
|
|
47
|
+
content: newContent,
|
|
48
|
+
timestamp: getFileModificationTime(absoluteFilePath),
|
|
49
|
+
offset: undefined,
|
|
50
|
+
limit: undefined
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
data: {
|
|
54
|
+
stdout: '',
|
|
55
|
+
stderr: '',
|
|
56
|
+
interrupted: false
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { feature } from 'bun:bundle';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import type { SetToolJSXFn } from '../../Tool.js';
|
|
4
|
+
import { backgroundExistingForegroundTask, markTaskNotified, registerForeground, spawnShellTask, unregisterForeground } from '../../tasks/LocalShellTask/LocalShellTask.js';
|
|
5
|
+
import type { AgentId } from '../../types/ids.js';
|
|
6
|
+
import type { AppState } from '../../state/AppState.js';
|
|
7
|
+
import { getKairosActive } from '../../bootstrap/state.js';
|
|
8
|
+
import { logEvent } from '../../services/analytics/index.js';
|
|
9
|
+
import { exec } from '../../utils/Shell.js';
|
|
10
|
+
import type { ExecResult } from '../../utils/ShellCommand.js';
|
|
11
|
+
import { TaskOutput } from '../../utils/task/TaskOutput.js';
|
|
12
|
+
import { getDefaultTimeoutMs } from './prompt.js';
|
|
13
|
+
import { shouldUseSandboxForShell } from './sandboxPolicy.js';
|
|
14
|
+
import type { BashToolInput } from './schemas.js';
|
|
15
|
+
import { isBackgroundTasksDisabled } from './schemas.js';
|
|
16
|
+
import { getCommandTypeForLogging, isAutobackgroundingAllowed } from './commandClassification.js';
|
|
17
|
+
import { loadBashUI } from './uiLoader.js';
|
|
18
|
+
|
|
19
|
+
const PROGRESS_THRESHOLD_MS = 2000;
|
|
20
|
+
const ASSISTANT_BLOCKING_BUDGET_MS = 15_000;
|
|
21
|
+
|
|
22
|
+
export type BashExecutionProgress = {
|
|
23
|
+
readonly type: 'progress'; readonly output: string; readonly fullOutput: string;
|
|
24
|
+
readonly elapsedTimeSeconds: number; readonly totalLines: number;
|
|
25
|
+
readonly totalBytes?: number; readonly taskId?: string; readonly timeoutMs?: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type RunShellCommandInput = {
|
|
29
|
+
readonly input: BashToolInput; readonly abortController: AbortController;
|
|
30
|
+
readonly setAppState: (f: (prev: AppState) => AppState) => void;
|
|
31
|
+
readonly setToolJSX?: SetToolJSXFn; readonly preventCwdChanges?: boolean;
|
|
32
|
+
readonly isMainThread?: boolean; readonly toolUseId?: string; readonly agentId?: AgentId;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export async function* runShellCommand({
|
|
36
|
+
input,
|
|
37
|
+
abortController,
|
|
38
|
+
setAppState,
|
|
39
|
+
setToolJSX,
|
|
40
|
+
preventCwdChanges,
|
|
41
|
+
isMainThread,
|
|
42
|
+
toolUseId,
|
|
43
|
+
agentId
|
|
44
|
+
}: RunShellCommandInput): AsyncGenerator<BashExecutionProgress, ExecResult, void> {
|
|
45
|
+
const { command, description, timeout, run_in_background } = input;
|
|
46
|
+
const timeoutMs = timeout || getDefaultTimeoutMs();
|
|
47
|
+
let fullOutput = '';
|
|
48
|
+
let lastProgressOutput = '';
|
|
49
|
+
let lastTotalLines = 0;
|
|
50
|
+
let lastTotalBytes = 0;
|
|
51
|
+
let backgroundShellId: string | undefined = undefined;
|
|
52
|
+
let assistantAutoBackgrounded = false;
|
|
53
|
+
let resolveProgress: (() => void) | null = null;
|
|
54
|
+
function createProgressSignal(): Promise<null> {
|
|
55
|
+
return new Promise<null>(resolve => {
|
|
56
|
+
resolveProgress = () => resolve(null);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const shouldAutoBackground = !isBackgroundTasksDisabled && isAutobackgroundingAllowed(command);
|
|
60
|
+
const shellCommand = await exec(command, abortController.signal, 'bash', {
|
|
61
|
+
timeout: timeoutMs,
|
|
62
|
+
onProgress(lastLines, allLines, totalLines, totalBytes, isIncomplete) {
|
|
63
|
+
lastProgressOutput = lastLines;
|
|
64
|
+
fullOutput = allLines;
|
|
65
|
+
lastTotalLines = totalLines;
|
|
66
|
+
lastTotalBytes = isIncomplete ? totalBytes : 0;
|
|
67
|
+
const resolve = resolveProgress;
|
|
68
|
+
if (resolve) {
|
|
69
|
+
resolveProgress = null;
|
|
70
|
+
resolve();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
preventCwdChanges,
|
|
74
|
+
shouldUseSandbox: shouldUseSandboxForShell(input),
|
|
75
|
+
shouldAutoBackground
|
|
76
|
+
});
|
|
77
|
+
const resultPromise = shellCommand.result;
|
|
78
|
+
async function spawnBackgroundTask(): Promise<string> {
|
|
79
|
+
const handle = await spawnShellTask({
|
|
80
|
+
command,
|
|
81
|
+
description: description || command,
|
|
82
|
+
shellCommand,
|
|
83
|
+
toolUseId,
|
|
84
|
+
agentId
|
|
85
|
+
}, {
|
|
86
|
+
abortController,
|
|
87
|
+
getAppState: () => {
|
|
88
|
+
throw new Error('getAppState not available in runShellCommand context');
|
|
89
|
+
},
|
|
90
|
+
setAppState
|
|
91
|
+
});
|
|
92
|
+
return handle.taskId;
|
|
93
|
+
}
|
|
94
|
+
function startBackgrounding(eventName: string, backgroundFn?: (shellId: string) => void): void {
|
|
95
|
+
if (foregroundTaskId) {
|
|
96
|
+
if (!backgroundExistingForegroundTask(foregroundTaskId, shellCommand, description || command, setAppState, toolUseId)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
backgroundShellId = foregroundTaskId;
|
|
100
|
+
logEvent(eventName, {
|
|
101
|
+
command_type: getCommandTypeForLogging(command)
|
|
102
|
+
});
|
|
103
|
+
backgroundFn?.(foregroundTaskId);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
void spawnBackgroundTask().then(shellId => {
|
|
107
|
+
backgroundShellId = shellId;
|
|
108
|
+
const resolve = resolveProgress;
|
|
109
|
+
if (resolve) {
|
|
110
|
+
resolveProgress = null;
|
|
111
|
+
resolve();
|
|
112
|
+
}
|
|
113
|
+
logEvent(eventName, {
|
|
114
|
+
command_type: getCommandTypeForLogging(command)
|
|
115
|
+
});
|
|
116
|
+
if (backgroundFn) {
|
|
117
|
+
backgroundFn(shellId);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (shellCommand.onTimeout && shouldAutoBackground) {
|
|
122
|
+
shellCommand.onTimeout(backgroundFn => {
|
|
123
|
+
startBackgrounding('tengu_bash_command_timeout_backgrounded', backgroundFn);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (feature('KAIROS') && getKairosActive() && isMainThread && !isBackgroundTasksDisabled && run_in_background !== true) {
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
if (shellCommand.status === 'running' && backgroundShellId === undefined) {
|
|
129
|
+
assistantAutoBackgrounded = true;
|
|
130
|
+
startBackgrounding('tengu_bash_command_assistant_auto_backgrounded');
|
|
131
|
+
}
|
|
132
|
+
}, ASSISTANT_BLOCKING_BUDGET_MS).unref();
|
|
133
|
+
}
|
|
134
|
+
if (run_in_background === true && !isBackgroundTasksDisabled) {
|
|
135
|
+
const shellId = await spawnBackgroundTask();
|
|
136
|
+
logEvent('tengu_bash_command_explicitly_backgrounded', {
|
|
137
|
+
command_type: getCommandTypeForLogging(command)
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
stdout: '',
|
|
141
|
+
stderr: '',
|
|
142
|
+
code: 0,
|
|
143
|
+
interrupted: false,
|
|
144
|
+
backgroundTaskId: shellId
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const startTime = Date.now();
|
|
148
|
+
let foregroundTaskId: string | undefined = undefined;
|
|
149
|
+
{
|
|
150
|
+
const initialResult = await Promise.race([resultPromise, new Promise<null>(resolve => {
|
|
151
|
+
const timer = setTimeout((done: (value: null) => void) => done(null), PROGRESS_THRESHOLD_MS, resolve);
|
|
152
|
+
timer.unref();
|
|
153
|
+
})]);
|
|
154
|
+
if (initialResult !== null) {
|
|
155
|
+
shellCommand.cleanup();
|
|
156
|
+
return initialResult;
|
|
157
|
+
}
|
|
158
|
+
if (backgroundShellId) {
|
|
159
|
+
return {
|
|
160
|
+
stdout: '',
|
|
161
|
+
stderr: '',
|
|
162
|
+
code: 0,
|
|
163
|
+
interrupted: false,
|
|
164
|
+
backgroundTaskId: backgroundShellId,
|
|
165
|
+
assistantAutoBackgrounded
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
TaskOutput.startPolling(shellCommand.taskOutput.taskId);
|
|
170
|
+
try {
|
|
171
|
+
while (true) {
|
|
172
|
+
const progressSignal = createProgressSignal();
|
|
173
|
+
const result = await Promise.race([resultPromise, progressSignal]);
|
|
174
|
+
if (result !== null) {
|
|
175
|
+
if (result.backgroundTaskId !== undefined) {
|
|
176
|
+
markTaskNotified(result.backgroundTaskId, setAppState);
|
|
177
|
+
const fixedResult: ExecResult = { ...result, backgroundTaskId: undefined };
|
|
178
|
+
const { taskOutput } = shellCommand;
|
|
179
|
+
if (taskOutput.stdoutToFile && !taskOutput.outputFileRedundant) {
|
|
180
|
+
fixedResult.outputFilePath = taskOutput.path;
|
|
181
|
+
fixedResult.outputFileSize = taskOutput.outputFileSize;
|
|
182
|
+
fixedResult.outputTaskId = taskOutput.taskId;
|
|
183
|
+
}
|
|
184
|
+
shellCommand.cleanup();
|
|
185
|
+
return fixedResult;
|
|
186
|
+
}
|
|
187
|
+
if (foregroundTaskId) {
|
|
188
|
+
unregisterForeground(foregroundTaskId, setAppState);
|
|
189
|
+
}
|
|
190
|
+
shellCommand.cleanup();
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
if (backgroundShellId) {
|
|
194
|
+
return {
|
|
195
|
+
stdout: '',
|
|
196
|
+
stderr: '',
|
|
197
|
+
code: 0,
|
|
198
|
+
interrupted: false,
|
|
199
|
+
backgroundTaskId: backgroundShellId,
|
|
200
|
+
assistantAutoBackgrounded
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (foregroundTaskId && shellCommand.status === 'backgrounded') {
|
|
204
|
+
return {
|
|
205
|
+
stdout: '',
|
|
206
|
+
stderr: '',
|
|
207
|
+
code: 0,
|
|
208
|
+
interrupted: false,
|
|
209
|
+
backgroundTaskId: foregroundTaskId,
|
|
210
|
+
backgroundedByUser: true
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
|
|
214
|
+
if (!isBackgroundTasksDisabled && backgroundShellId === undefined && elapsedSeconds >= PROGRESS_THRESHOLD_MS / 1000 && setToolJSX) {
|
|
215
|
+
if (!foregroundTaskId) {
|
|
216
|
+
foregroundTaskId = registerForeground({
|
|
217
|
+
command,
|
|
218
|
+
description: description || command,
|
|
219
|
+
shellCommand,
|
|
220
|
+
agentId
|
|
221
|
+
}, setAppState, toolUseId);
|
|
222
|
+
}
|
|
223
|
+
const { BackgroundHint } = loadBashUI();
|
|
224
|
+
setToolJSX({
|
|
225
|
+
jsx: <BackgroundHint />,
|
|
226
|
+
shouldHidePromptInput: false,
|
|
227
|
+
shouldContinueAnimation: true,
|
|
228
|
+
showSpinner: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
yield {
|
|
232
|
+
type: 'progress',
|
|
233
|
+
fullOutput,
|
|
234
|
+
output: lastProgressOutput,
|
|
235
|
+
elapsedTimeSeconds: elapsedSeconds,
|
|
236
|
+
totalLines: lastTotalLines,
|
|
237
|
+
totalBytes: lastTotalBytes,
|
|
238
|
+
taskId: shellCommand.taskOutput.taskId,
|
|
239
|
+
...(timeout ? { timeoutMs } : undefined)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
} finally {
|
|
243
|
+
TaskOutput.stopPolling(shellCommand.taskOutput.taskId);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Base64ImageSource,
|
|
3
|
+
ToolResultBlockParam,
|
|
4
|
+
} from '@anthropic-ai/sdk/resources/index.mjs'
|
|
5
|
+
import { readFile, stat } from 'fs/promises'
|
|
6
|
+
import { getOriginalCwd } from '../../bootstrap/state.js'
|
|
7
|
+
import { maybeResizeAndDownsampleImageBuffer } from '../../utils/imageResizer.js'
|
|
8
|
+
|
|
9
|
+
export function stripEmptyLines(content: string): string {
|
|
10
|
+
const lines = content.split('\n')
|
|
11
|
+
let startIndex = 0
|
|
12
|
+
while (startIndex < lines.length && lines[startIndex]?.trim() === '') {
|
|
13
|
+
startIndex++
|
|
14
|
+
}
|
|
15
|
+
let endIndex = lines.length - 1
|
|
16
|
+
while (endIndex >= 0 && lines[endIndex]?.trim() === '') {
|
|
17
|
+
endIndex--
|
|
18
|
+
}
|
|
19
|
+
if (startIndex > endIndex) return ''
|
|
20
|
+
return lines.slice(startIndex, endIndex + 1).join('\n')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isImageOutput(content: string): boolean {
|
|
24
|
+
return /^data:image\/[a-z0-9.+_-]+;base64,/i.test(content)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const DATA_URI_RE = /^data:([^;]+);base64,(.+)$/
|
|
28
|
+
|
|
29
|
+
function parseDataUri(
|
|
30
|
+
value: string,
|
|
31
|
+
): { readonly mediaType: string; readonly data: string } | null {
|
|
32
|
+
const match = value.trim().match(DATA_URI_RE)
|
|
33
|
+
if (!match || !match[1] || !match[2]) return null
|
|
34
|
+
return { mediaType: match[1], data: match[2] }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function buildImageToolResult(
|
|
38
|
+
stdout: string,
|
|
39
|
+
toolUseID: string,
|
|
40
|
+
): ToolResultBlockParam | null {
|
|
41
|
+
const parsed = parseDataUri(stdout)
|
|
42
|
+
if (!parsed) return null
|
|
43
|
+
return {
|
|
44
|
+
tool_use_id: toolUseID,
|
|
45
|
+
type: 'tool_result',
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'image',
|
|
49
|
+
source: {
|
|
50
|
+
type: 'base64',
|
|
51
|
+
media_type: parsed.mediaType as Base64ImageSource['media_type'],
|
|
52
|
+
data: parsed.data,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024
|
|
60
|
+
|
|
61
|
+
export async function resizeShellImageOutput(
|
|
62
|
+
stdout: string,
|
|
63
|
+
outputFilePath: string | undefined,
|
|
64
|
+
outputFileSize: number | undefined,
|
|
65
|
+
): Promise<string | null> {
|
|
66
|
+
let source = stdout
|
|
67
|
+
if (outputFilePath) {
|
|
68
|
+
const size = outputFileSize ?? (await stat(outputFilePath)).size
|
|
69
|
+
if (size > MAX_IMAGE_FILE_SIZE) return null
|
|
70
|
+
source = await readFile(outputFilePath, 'utf8')
|
|
71
|
+
}
|
|
72
|
+
const parsed = parseDataUri(source)
|
|
73
|
+
if (!parsed) return null
|
|
74
|
+
const buffer = Buffer.from(parsed.data, 'base64')
|
|
75
|
+
const extension = parsed.mediaType.split('/')[1] || 'png'
|
|
76
|
+
const resized = await maybeResizeAndDownsampleImageBuffer(
|
|
77
|
+
buffer,
|
|
78
|
+
buffer.length,
|
|
79
|
+
extension,
|
|
80
|
+
)
|
|
81
|
+
return `data:image/${resized.mediaType};base64,${resized.buffer.toString('base64')}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const stdErrAppendShellResetMessage = (stderr: string): string =>
|
|
85
|
+
`${stderr.trim()}\nShell cwd was reset to ${getOriginalCwd()}`
|