ummaya 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -2
- package/bin/ummaya +10 -1
- package/bun.lock +180 -244
- package/npm-shrinkwrap.json +760 -1760
- package/package.json +39 -22
- package/prompts/manifest.yaml +1 -1
- package/prompts/system_v1.md +1 -0
- package/pyproject.toml +27 -2
- package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
- package/src/ummaya/_canonical/__init__.py +2 -0
- package/src/ummaya/_canonical/baselines.yaml +113 -0
- package/src/ummaya/engine/engine.py +29 -132
- package/src/ummaya/evidence/__init__.py +21 -2
- package/src/ummaya/evidence/dataset_contract.py +193 -0
- package/src/ummaya/evidence/document_authoring_cases.py +33 -0
- package/src/ummaya/evidence/document_harness.py +313 -0
- package/src/ummaya/evidence/document_viewer_ux.py +391 -0
- package/src/ummaya/evidence/gates.py +70 -0
- package/src/ummaya/evidence/json_types.py +20 -0
- package/src/ummaya/evidence/models.py +88 -1
- package/src/ummaya/evidence/output_payload.py +89 -0
- package/src/ummaya/evidence/payload_documents.py +233 -0
- package/src/ummaya/evidence/route_contracts.py +224 -0
- package/src/ummaya/evidence/route_helpers.py +150 -0
- package/src/ummaya/evidence/runner.py +81 -212
- package/src/ummaya/evidence/source_provenance.py +246 -0
- package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
- package/src/ummaya/evidence/tool_layer.py +39 -0
- package/src/ummaya/evidence/tool_layer_models.py +151 -0
- package/src/ummaya/ipc/adapter_manifest_emitter.py +26 -10
- package/src/ummaya/ipc/document_intent_normalization.py +185 -0
- package/src/ummaya/ipc/frame_schema.py +5 -5
- package/src/ummaya/ipc/route_diagnostics.py +73 -0
- package/src/ummaya/ipc/stdio.py +1109 -477
- package/src/ummaya/llm/client.py +102 -3
- package/src/ummaya/llm/config.py +8 -3
- package/src/ummaya/primitives/__init__.py +6 -2
- package/src/ummaya/primitives/delegation.py +1 -1
- package/src/ummaya/primitives/document.py +28 -0
- package/src/ummaya/settings.py +0 -3
- package/src/ummaya/tools/discovery_bridge.py +17 -1
- package/src/ummaya/tools/documents/__init__.py +297 -0
- package/src/ummaya/tools/documents/adapter_registry.py +487 -0
- package/src/ummaya/tools/documents/archive_container_probe.py +167 -0
- package/src/ummaya/tools/documents/artifact_store.py +454 -0
- package/src/ummaya/tools/documents/authoring.py +283 -0
- package/src/ummaya/tools/documents/baselines.py +132 -0
- package/src/ummaya/tools/documents/capability.py +331 -0
- package/src/ummaya/tools/documents/contracts.py +112 -0
- package/src/ummaya/tools/documents/conversion.py +521 -0
- package/src/ummaya/tools/documents/diff.py +275 -0
- package/src/ummaya/tools/documents/engines.py +163 -0
- package/src/ummaya/tools/documents/evaluation.py +291 -0
- package/src/ummaya/tools/documents/explicit_values.py +108 -0
- package/src/ummaya/tools/documents/fixtures.py +174 -0
- package/src/ummaya/tools/documents/format_completion_audit.py +471 -0
- package/src/ummaya/tools/documents/formats/__init__.py +2 -0
- package/src/ummaya/tools/documents/formats/archive.py +528 -0
- package/src/ummaya/tools/documents/formats/base.py +41 -0
- package/src/ummaya/tools/documents/formats/code_file.py +211 -0
- package/src/ummaya/tools/documents/formats/data_file.py +272 -0
- package/src/ummaya/tools/documents/formats/hwp.py +284 -0
- package/src/ummaya/tools/documents/formats/hwpx.py +1837 -0
- package/src/ummaya/tools/documents/formats/odf.py +435 -0
- package/src/ummaya/tools/documents/formats/ooxml.py +1030 -0
- package/src/ummaya/tools/documents/formats/passive.py +766 -0
- package/src/ummaya/tools/documents/formats/pdf.py +702 -0
- package/src/ummaya/tools/documents/formats/text_web.py +268 -0
- package/src/ummaya/tools/documents/hwp_conversion_probe.py +178 -0
- package/src/ummaya/tools/documents/hwp_direct_candidate.py +141 -0
- package/src/ummaya/tools/documents/inspection.py +289 -0
- package/src/ummaya/tools/documents/intake.py +1079 -0
- package/src/ummaya/tools/documents/legacy_office_promotion_probe.py +366 -0
- package/src/ummaya/tools/documents/models.py +1598 -0
- package/src/ummaya/tools/documents/odf_promotion_probe.py +167 -0
- package/src/ummaya/tools/documents/orchestrator.py +96 -0
- package/src/ummaya/tools/documents/passive_capability_probe.py +251 -0
- package/src/ummaya/tools/documents/patch.py +170 -0
- package/src/ummaya/tools/documents/pdfa_conformance.py +284 -0
- package/src/ummaya/tools/documents/pdfa_promotion_probe.py +198 -0
- package/src/ummaya/tools/documents/permissions.py +110 -0
- package/src/ummaya/tools/documents/planner.py +616 -0
- package/src/ummaya/tools/documents/registry.py +2733 -0
- package/src/ummaya/tools/documents/render.py +978 -0
- package/src/ummaya/tools/documents/render_comparison.py +113 -0
- package/src/ummaya/tools/documents/render_comparison_models.py +74 -0
- package/src/ummaya/tools/documents/render_comparison_regions.py +73 -0
- package/src/ummaya/tools/documents/render_comparison_style.py +161 -0
- package/src/ummaya/tools/documents/reread.py +157 -0
- package/src/ummaya/tools/documents/runtime_authoring.py +244 -0
- package/src/ummaya/tools/documents/runtime_authoring_bundle.py +76 -0
- package/src/ummaya/tools/documents/scorecard.py +184 -0
- package/src/ummaya/tools/documents/socratic_planner.py +193 -0
- package/src/ummaya/tools/documents/style.py +48 -0
- package/src/ummaya/tools/documents/tool_defs.py +523 -0
- package/src/ummaya/tools/documents/validate.py +347 -0
- package/src/ummaya/tools/executor.py +29 -0
- package/src/ummaya/tools/live_proxy.py +0 -3
- package/src/ummaya/tools/models.py +5 -1
- package/src/ummaya/tools/register_all.py +8 -0
- package/src/ummaya/tools/registry.py +10 -1
- package/src/ummaya/tools/routing/__init__.py +59 -0
- package/src/ummaya/tools/routing/builder.py +105 -0
- package/src/ummaya/tools/routing/cards.py +29 -0
- package/src/ummaya/tools/routing/decision_service.py +534 -0
- package/src/ummaya/tools/routing/decision_types.py +74 -0
- package/src/ummaya/tools/routing/feasibility.py +122 -0
- package/src/ummaya/tools/routing/intent.py +17 -0
- package/src/ummaya/tools/routing/intent_extractor.py +207 -0
- package/src/ummaya/tools/routing/intent_patterns.py +160 -0
- package/src/ummaya/tools/routing/intent_public_data.py +150 -0
- package/src/ummaya/tools/routing/intent_types.py +48 -0
- package/src/ummaya/tools/routing/lint.py +78 -0
- package/src/ummaya/tools/routing/metadata.py +174 -0
- package/src/ummaya/tools/routing/projection.py +340 -0
- package/src/ummaya/tools/routing/retrieval_policy.py +629 -0
- package/src/ummaya/tools/routing/schema.py +81 -0
- package/src/ummaya/tools/routing/types.py +96 -0
- package/src/ummaya/tools/routing_index.py +2 -2
- package/src/ummaya/tools/search.py +34 -746
- package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
- package/tui/bun.lock +126 -305
- package/tui/package.json +35 -22
- package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
- package/tui/src/QueryEngine.ts +12 -8
- package/tui/src/bridge/inboundAttachments.ts +3 -3
- package/tui/src/cli/handlers/auth.ts +3 -12
- package/tui/src/cli/handlers/mcp.tsx +0 -1
- package/tui/src/cli/print.ts +8 -9
- package/tui/src/commands/insights.ts +1 -1
- package/tui/src/commands/install-github-app/types.ts +8 -30
- package/tui/src/commands/plugin/types.ts +6 -28
- package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
- package/tui/src/commands/rename/generateSessionName.ts +1 -1
- package/tui/src/components/Feedback.tsx +1 -1
- package/tui/src/components/LogoV2/EmergencyTip.tsx +11 -2
- package/tui/src/components/LogoV2/WelcomeV2.tsx +1 -3
- package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
- package/tui/src/components/Spinner/types.ts +6 -28
- package/tui/src/components/agents/generateAgent.ts +1 -1
- package/tui/src/components/agents/new-agent-creation/types.ts +4 -26
- package/tui/src/components/config/EnvSecretIsolatedEditor.tsx +1 -1
- package/tui/src/components/mcp/types.ts +16 -38
- package/tui/src/components/messages/AssistantToolUseMessage.tsx +3 -2
- package/tui/src/components/messages/UserCrossSessionMessage.ts +16 -4
- package/tui/src/components/messages/UserForkBoilerplateMessage.ts +16 -4
- package/tui/src/components/messages/UserGitHubWebhookMessage.ts +16 -4
- package/tui/src/components/messages/UserToolResultMessage/utils.tsx +3 -2
- package/tui/src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.ts +9 -4
- package/tui/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.ts +9 -4
- package/tui/src/components/primitive/DocumentSocraticReviewBlock.tsx +129 -0
- package/tui/src/components/primitive/DocumentToolResultCard.tsx +224 -0
- package/tui/src/components/primitive/documentSocraticReview.ts +215 -0
- package/tui/src/components/primitive/index.tsx +43 -1
- package/tui/src/components/primitive/types.ts +137 -0
- package/tui/src/components/ui/option.ts +4 -26
- package/tui/src/constants/common.ts +0 -2
- package/tui/src/constants/prompts.ts +4 -3
- package/tui/src/constants/querySource.ts +4 -26
- package/tui/src/entrypoints/sdk/controlTypes.ts +26 -48
- package/tui/src/entrypoints/sdk/coreTypes.generated.ts +3 -25
- package/tui/src/entrypoints/sdk/runtimeTypes.ts +38 -60
- package/tui/src/entrypoints/sdk/sdkUtilityTypes.ts +4 -26
- package/tui/src/entrypoints/sdk/settingsTypes.generated.ts +3 -25
- package/tui/src/entrypoints/sdk/toolTypes.ts +3 -25
- package/tui/src/hooks/toolPermission/handlers/interactiveHandler.ts +10 -0
- package/tui/src/hooks/useApiKeyVerification.ts +1 -1
- package/tui/src/hooks/useVirtualScroll.ts +1 -1
- package/tui/src/ink/ink.tsx +33 -14
- package/tui/src/ink/reconciler.ts +2 -3
- package/tui/src/ink/render-to-screen.ts +30 -10
- package/tui/src/ipc/bridge.ts +62 -15
- package/tui/src/ipc/bridgeSingleton.ts +5 -1
- package/tui/src/ipc/codec.ts +3 -3
- package/tui/src/ipc/frames.generated.ts +12 -12
- package/tui/src/ipc/llmClient.ts +151 -27
- package/tui/src/ipc/schema/frame.schema.json +1 -1
- package/tui/src/keybindings/defaultBindings.ts +4 -0
- package/tui/src/main.tsx +32 -15
- package/tui/src/native-ts/file-index/index.ts +33 -3
- package/tui/src/observability/surface.ts +2 -2
- package/tui/src/probes/toolRegistryProbe.tsx +3 -1
- package/tui/src/projectOnboardingState.ts +7 -6
- package/tui/src/query/chatMessageTypes.ts +18 -0
- package/tui/src/query/chatMessagesBuilder.ts +1 -1
- package/tui/src/query/deps.ts +1 -1
- package/tui/src/query/messageGuards.ts +106 -0
- package/tui/src/query/publicDataTerminalRepair.ts +384 -0
- package/tui/src/query/run.ts +1075 -0
- package/tui/src/query/supportBoundary.ts +168 -0
- package/tui/src/query/toolResultErrors.ts +103 -0
- package/tui/src/query/toolRunner.ts +687 -0
- package/tui/src/query/unavailableToolRepair.ts +118 -0
- package/tui/src/query.ts +9 -2186
- package/tui/src/screens/REPL.tsx +40 -29
- package/tui/src/services/api/adapterManifest.ts +4 -0
- package/tui/src/services/api/backendChat/events.ts +117 -0
- package/tui/src/services/api/backendChat/finalMessage.ts +40 -0
- package/tui/src/services/api/backendChat/frame.ts +9 -0
- package/tui/src/services/api/backendChat/streaming.ts +430 -0
- package/tui/src/services/api/backendChat/types.ts +62 -0
- package/tui/src/services/api/backendChat.ts +1 -0
- package/tui/src/services/api/client.ts +65 -2
- package/tui/src/services/api/errorUtils.ts +5 -5
- package/tui/src/services/api/errors.ts +1 -1
- package/tui/src/services/api/logging.ts +1 -1
- package/tui/src/services/api/ummaya/evidence.ts +194 -0
- package/tui/src/services/api/ummaya/messages.ts +255 -0
- package/tui/src/services/api/ummaya/nonStreaming.ts +66 -0
- package/tui/src/services/api/ummaya/provider.ts +200 -0
- package/tui/src/services/api/ummaya/reasoning.ts +24 -0
- package/tui/src/services/api/ummaya/request.ts +200 -0
- package/tui/src/services/api/ummaya/selectionContext.ts +240 -0
- package/tui/src/services/api/ummaya/streaming.ts +365 -0
- package/tui/src/services/api/ummaya/streamingPayload.ts +129 -0
- package/tui/src/services/api/ummaya/streamingReader.ts +40 -0
- package/tui/src/services/api/ummaya/toolSelection.ts +217 -0
- package/tui/src/services/api/ummaya/types.ts +110 -0
- package/tui/src/services/api/ummaya/usage.ts +30 -0
- package/tui/src/services/api/ummaya.ts +26 -418
- package/tui/src/services/api/withRetry.ts +1 -1
- package/tui/src/services/awaySummary.ts +2 -2
- package/tui/src/services/claudeAiLimits.ts +1 -1
- package/tui/src/services/compact/autoCompact.ts +1 -1
- package/tui/src/services/compact/compact.ts +1 -1
- package/tui/src/services/lsp/types.ts +8 -30
- package/tui/src/services/tips/types.ts +6 -28
- package/tui/src/services/tokenEstimation.ts +1 -1
- package/tui/src/services/toolRegistry/bootGuard.ts +5 -5
- package/tui/src/services/toolUseSummary/toolUseSummaryGenerator.ts +1 -1
- package/tui/src/services/tools/toolExecution.ts +94 -1
- package/tui/src/store/pendingPermissionSlot.ts +1 -1
- package/tui/src/store/session-store.ts +10 -36
- package/tui/src/stubs/any-stub.ts +15 -10
- package/tui/src/stubs/color-diff-napi.ts +37 -23
- package/tui/src/stubs/globals.d.ts +3 -3
- package/tui/src/stubs/macro-preload.ts +23 -12
- package/tui/src/tools/AdapterTool/AdapterTool.ts +1207 -714
- package/tui/src/tools/AdapterTool/routeDiagnostics.ts +75 -0
- package/tui/src/tools/AgentTool/AgentTool.tsx +84 -1371
- package/tui/src/tools/AgentTool/agentToolHandoff.ts +114 -0
- package/tui/src/tools/AgentTool/agentToolPartialResult.ts +16 -0
- package/tui/src/tools/AgentTool/agentToolProgress.ts +32 -0
- package/tui/src/tools/AgentTool/agentToolResolver.ts +161 -0
- package/tui/src/tools/AgentTool/agentToolResult.ts +163 -0
- package/tui/src/tools/AgentTool/agentToolUtils.ts +14 -686
- package/tui/src/tools/AgentTool/asyncAgentLifecycle.ts +208 -0
- package/tui/src/tools/AgentTool/asyncLifecycle.ts +153 -0
- package/tui/src/tools/AgentTool/backgroundedCompletion.ts +126 -0
- package/tui/src/tools/AgentTool/backgroundedLifecycle.ts +174 -0
- package/tui/src/tools/AgentTool/foregroundBackground.ts +83 -0
- package/tui/src/tools/AgentTool/foregroundDrain.tsx +133 -0
- package/tui/src/tools/AgentTool/foregroundFinalize.ts +98 -0
- package/tui/src/tools/AgentTool/foregroundLifecycle.tsx +237 -0
- package/tui/src/tools/AgentTool/foregroundProgress.tsx +169 -0
- package/tui/src/tools/AgentTool/foregroundTask.ts +89 -0
- package/tui/src/tools/AgentTool/forkSubagent.ts +1 -12
- package/tui/src/tools/AgentTool/forkSubagentGate.ts +34 -0
- package/tui/src/tools/AgentTool/launchRouting.ts +203 -0
- package/tui/src/tools/AgentTool/lifecycle.ts +244 -0
- package/tui/src/tools/AgentTool/mcpRouting.ts +73 -0
- package/tui/src/tools/AgentTool/orchestrationSupport.ts +70 -0
- package/tui/src/tools/AgentTool/permissions.ts +39 -0
- package/tui/src/tools/AgentTool/promptSetup.ts +181 -0
- package/tui/src/tools/AgentTool/remoteRouting.ts +62 -0
- package/tui/src/tools/AgentTool/resultMapping.ts +116 -0
- package/tui/src/tools/AgentTool/resumeAgent.ts +39 -107
- package/tui/src/tools/AgentTool/resumeAgentHelpers.ts +140 -0
- package/tui/src/tools/AgentTool/runAgent.ts +1 -1
- package/tui/src/tools/AgentTool/runtimeConfig.ts +57 -0
- package/tui/src/tools/AgentTool/schemas.ts +196 -0
- package/tui/src/tools/AgentTool/sourceVerificationPropagation.ts +263 -0
- package/tui/src/tools/AgentTool/worktreeLifecycle.ts +105 -0
- package/tui/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +174 -202
- package/tui/src/tools/BashTool/BashTool.tsx +71 -1072
- package/tui/src/tools/BashTool/bashCommandHelpers.ts +12 -12
- package/tui/src/tools/BashTool/bashPermissions/astPreflight.ts +173 -0
- package/tui/src/tools/BashTool/bashPermissions/classifierChecks.ts +199 -0
- package/tui/src/tools/BashTool/bashPermissions/compoundGuards.ts +53 -0
- package/tui/src/tools/BashTool/bashPermissions/constants.ts +99 -0
- package/tui/src/tools/BashTool/bashPermissions/index.ts +38 -0
- package/tui/src/tools/BashTool/bashPermissions/legacyMisparsing.ts +62 -0
- package/tui/src/tools/BashTool/bashPermissions/main.ts +135 -0
- package/tui/src/tools/BashTool/bashPermissions/normalizedCommands.ts +33 -0
- package/tui/src/tools/BashTool/bashPermissions/operatorFlow.ts +98 -0
- package/tui/src/tools/BashTool/bashPermissions/permissionChecks.ts +200 -0
- package/tui/src/tools/BashTool/bashPermissions/prefixSuggestions.ts +88 -0
- package/tui/src/tools/BashTool/bashPermissions/promptClassifierRules.ts +125 -0
- package/tui/src/tools/BashTool/bashPermissions/ruleDelegates.ts +19 -0
- package/tui/src/tools/BashTool/bashPermissions/ruleMatching.ts +145 -0
- package/tui/src/tools/BashTool/bashPermissions/sandboxAutoAllow.ts +75 -0
- package/tui/src/tools/BashTool/bashPermissions/subcommandFlow.ts +205 -0
- package/tui/src/tools/BashTool/bashPermissions/subcommandGuards.ts +73 -0
- package/tui/src/tools/BashTool/bashPermissions/subcommandResultHelpers.ts +116 -0
- package/tui/src/tools/BashTool/bashPermissions/types.ts +26 -0
- package/tui/src/tools/BashTool/bashPermissions/wrapperStripping.ts +139 -0
- package/tui/src/tools/BashTool/bashPermissions.ts +26 -2621
- package/tui/src/tools/BashTool/call.ts +202 -0
- package/tui/src/tools/BashTool/callLoader.ts +35 -0
- package/tui/src/tools/BashTool/commandClassification.ts +151 -0
- package/tui/src/tools/BashTool/commandClassificationLoader.ts +40 -0
- package/tui/src/tools/BashTool/cwdReset.ts +33 -0
- package/tui/src/tools/BashTool/lineTruncation.ts +11 -0
- package/tui/src/tools/BashTool/modeValidation.ts +13 -1
- package/tui/src/tools/BashTool/outputPersistence.ts +42 -0
- package/tui/src/tools/BashTool/permissionClassification.ts +66 -0
- package/tui/src/tools/BashTool/permissionLoader.ts +44 -0
- package/tui/src/tools/BashTool/resultLoader.ts +29 -0
- package/tui/src/tools/BashTool/resultMapping.ts +83 -0
- package/tui/src/tools/BashTool/sandboxPolicy.ts +79 -0
- package/tui/src/tools/BashTool/schemas.ts +65 -0
- package/tui/src/tools/BashTool/sedEditExecution.ts +59 -0
- package/tui/src/tools/BashTool/shellExecution.tsx +245 -0
- package/tui/src/tools/BashTool/shellOutputUtils.ts +85 -0
- package/tui/src/tools/BashTool/shellPermissionGauntlet.ts +97 -0
- package/tui/src/tools/BashTool/uiLoader.ts +37 -0
- package/tui/src/tools/BriefTool/upload.ts +1 -1
- package/tui/src/tools/CalculatorTool/parser.ts +2 -2
- package/tui/src/tools/DocumentPrimitive/DocumentPrimitive.ts +262 -0
- package/tui/src/tools/DocumentPrimitive/dispatchNormalization.ts +270 -0
- package/tui/src/tools/DocumentPrimitive/documentDestinationPath.ts +18 -0
- package/tui/src/tools/DocumentPrimitive/documentMutationGuard.ts +22 -0
- package/tui/src/tools/DocumentPrimitive/documentPatchNormalization.ts +248 -0
- package/tui/src/tools/DocumentPrimitive/documentSourceVerification.ts +245 -0
- package/tui/src/tools/DocumentPrimitive/documentSourceVerificationFields.ts +103 -0
- package/tui/src/tools/DocumentPrimitive/modelVisibleOutput.ts +40 -0
- package/tui/src/tools/DocumentPrimitive/prompt.ts +35 -0
- package/tui/src/tools/FileEditTool/FileEditTool.ts +9 -507
- package/tui/src/tools/FileEditTool/call.ts +228 -0
- package/tui/src/tools/FileEditTool/validateInput.ts +196 -0
- package/tui/src/tools/FileReadTool/imageProcessor.ts +13 -0
- package/tui/src/tools/FileWriteTool/FileWriteTool.ts +7 -300
- package/tui/src/tools/FileWriteTool/call.ts +223 -0
- package/tui/src/tools/FileWriteTool/validateInput.ts +80 -0
- package/tui/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +19 -3
- package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +25 -32
- package/tui/src/tools/LookupPrimitive/prompt.ts +0 -2
- package/tui/src/tools/MCPTool/trustPolicy.ts +118 -0
- package/tui/src/tools/McpAuthTool/McpAuthTool.ts +21 -3
- package/tui/src/tools/NotebookEditTool/NotebookEditTool.ts +7 -326
- package/tui/src/tools/NotebookEditTool/call.ts +254 -0
- package/tui/src/tools/NotebookEditTool/notebookModel.ts +51 -0
- package/tui/src/tools/NotebookEditTool/validateInput.ts +142 -0
- package/tui/src/tools/PowerShellTool/PowerShellTool.tsx +46 -937
- package/tui/src/tools/PowerShellTool/acceptEditsCommandValidation.ts +162 -0
- package/tui/src/tools/PowerShellTool/call.ts +179 -0
- package/tui/src/tools/PowerShellTool/callLoader.ts +37 -0
- package/tui/src/tools/PowerShellTool/commandClassification.ts +86 -0
- package/tui/src/tools/PowerShellTool/modeValidation.ts +25 -332
- package/tui/src/tools/PowerShellTool/outputPersistence.ts +42 -0
- package/tui/src/tools/PowerShellTool/permissionClassification.ts +28 -0
- package/tui/src/tools/PowerShellTool/resultLoader.ts +31 -0
- package/tui/src/tools/PowerShellTool/resultMapping.ts +75 -0
- package/tui/src/tools/PowerShellTool/schemas.ts +40 -0
- package/tui/src/tools/PowerShellTool/shellExecution.tsx +258 -0
- package/tui/src/tools/PowerShellTool/symlinkModeValidation.ts +44 -0
- package/tui/src/tools/PowerShellTool/uiLoader.ts +37 -0
- package/tui/src/tools/PowerShellTool/validation.ts +39 -0
- package/tui/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +19 -3
- package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +1 -11
- package/tui/src/tools/ResolveLocationPrimitive/prompt.ts +2 -6
- package/tui/src/tools/SkillTool/SkillTool.ts +2 -2
- package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +27 -10
- package/tui/src/tools/TaskCreateTool/TaskCreateTool.ts +16 -2
- package/tui/src/tools/TaskGetTool/TaskGetTool.ts +23 -3
- package/tui/src/tools/TaskListTool/TaskListTool.ts +22 -4
- package/tui/src/tools/TaskOutputTool/TaskOutputTool.tsx +46 -547
- package/tui/src/tools/TaskOutputTool/lookup.ts +216 -0
- package/tui/src/tools/TaskOutputTool/render.tsx +257 -0
- package/tui/src/tools/TaskOutputTool/schemas.ts +55 -0
- package/tui/src/tools/TaskOutputTool/serialization.ts +36 -0
- package/tui/src/tools/TaskStopTool/TaskStopTool.ts +10 -0
- package/tui/src/tools/TaskUpdateTool/TaskUpdateTool.ts +14 -364
- package/tui/src/tools/TaskUpdateTool/completion.ts +62 -0
- package/tui/src/tools/TaskUpdateTool/schemas.ts +62 -0
- package/tui/src/tools/TaskUpdateTool/serialization.ts +46 -0
- package/tui/src/tools/TaskUpdateTool/statusUpdate.ts +247 -0
- package/tui/src/tools/TodoWriteTool/TodoWriteTool.ts +21 -2
- package/tui/src/tools/ToolSearchTool/ToolSearchTool.ts +21 -302
- package/tui/src/tools/ToolSearchTool/ccSupportTools.ts +223 -0
- package/tui/src/tools/ToolSearchTool/descriptionCache.ts +50 -0
- package/tui/src/tools/ToolSearchTool/keywordSearch.ts +216 -0
- package/tui/src/tools/ToolSearchTool/prompt.ts +10 -4
- package/tui/src/tools/ToolSearchTool/resultMapping.ts +30 -0
- package/tui/src/tools/ToolSearchTool/schemas.ts +30 -0
- package/tui/src/tools/ToolSearchTool/searchPool.ts +47 -0
- package/tui/src/tools/ToolSearchTool/supportIntentHints.ts +140 -0
- package/tui/src/tools/TranslateTool/TranslateTool.ts +1 -1
- package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +2 -1
- package/tui/src/tools/WebFetchTool/WebFetchTool.ts +43 -138
- package/tui/src/tools/WebFetchTool/call.ts +227 -0
- package/tui/src/tools/WebFetchTool/resolvedAddressSafety.ts +78 -0
- package/tui/src/tools/WebFetchTool/sourceVerification.ts +204 -0
- package/tui/src/tools/WebFetchTool/types.ts +23 -0
- package/tui/src/tools/WebFetchTool/urlSafety.ts +181 -0
- package/tui/src/tools/WebFetchTool/utils.ts +1 -1
- package/tui/src/tools/WebSearchTool/UI.tsx +0 -1
- package/tui/src/tools/WebSearchTool/WebSearchTool.ts +9 -313
- package/tui/src/tools/WebSearchTool/call.ts +33 -0
- package/tui/src/tools/WebSearchTool/responseMapping.ts +190 -0
- package/tui/src/tools/WebSearchTool/resultBlock.ts +47 -0
- package/tui/src/tools/WebSearchTool/schemas.ts +47 -0
- package/tui/src/tools/WebSearchTool/toolSchema.ts +12 -0
- package/tui/src/tools/WorkspaceToolAdapter/WorkspaceToolAdapter.ts +79 -0
- package/tui/src/tools/WorkspaceToolAdapter/allowedRootPolicy.ts +85 -0
- package/tui/src/tools/WorkspaceToolAdapter/documentFormatGuards.ts +73 -0
- package/tui/src/tools/WorkspaceToolAdapter/inputNormalization.ts +105 -0
- package/tui/src/tools/WorkspaceToolAdapter/mcpExposurePolicy.ts +64 -0
- package/tui/src/tools/WorkspaceToolAdapter/toolDefFactory.ts +215 -0
- package/tui/src/tools/WorkspaceToolAdapter/toolNames.ts +6 -0
- package/tui/src/tools/WorkspaceToolAdapter/workspacePolicy.ts +15 -0
- package/tui/src/tools/_shared/dispatchPrimitive.ts +6 -6
- package/tui/src/tools/_shared/documentChangeToPatch.ts +125 -0
- package/tui/src/tools/_shared/documentDispatchArguments.ts +87 -0
- package/tui/src/tools/_shared/documentPrimitiveTimeout.ts +13 -0
- package/tui/src/tools/_shared/documentToolResultRender.ts +98 -0
- package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
- package/tui/src/tools/_shared/rootPrimitiveInput.ts +1 -0
- package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPatterns.ts +58 -0
- package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPrompt.ts +271 -0
- package/tui/src/tools/_shared/toolChoiceRepair/documentRepair.ts +452 -0
- package/tui/src/tools/_shared/toolChoiceRepair/messageAccess.ts +80 -0
- package/tui/src/tools/_shared/toolChoiceRepair/publicDataRepair.ts +92 -0
- package/tui/src/tools/_shared/toolChoiceRepair/supportRepair.ts +135 -0
- package/tui/src/tools/_shared/toolChoiceRepair.ts +55 -860
- package/tui/src/tools/shared/mockDisclaimer.ts +1 -1
- package/tui/src/tools.ts +39 -190
- package/tui/src/types/fileSuggestion.ts +4 -26
- package/tui/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +186 -148
- package/tui/src/types/generated/events_mono/common/v1/auth.ts +25 -11
- package/tui/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +47 -30
- package/tui/src/types/generated/google/protobuf/timestamp.ts +21 -7
- package/tui/src/types/message.ts +80 -102
- package/tui/src/types/messageQueueTypes.ts +6 -28
- package/tui/src/types/notebook.ts +16 -38
- package/tui/src/types/statusLine.ts +4 -26
- package/tui/src/types/tools.ts +24 -46
- package/tui/src/types/utils.ts +6 -28
- package/tui/src/upstreamproxy/relay.ts +7 -3
- package/tui/src/upstreamproxy/upstreamproxy.ts +1 -1
- package/tui/src/utils/assistantMessageFactories.ts +9 -3
- package/tui/src/utils/auth.ts +129 -139
- package/tui/src/utils/bash/ast.ts +23 -23
- package/tui/src/utils/bash/bashParser.ts +5 -5
- package/tui/src/utils/billing.ts +1 -1
- package/tui/src/utils/claudeDesktop.ts +4 -4
- package/tui/src/utils/collapseReadSearch.ts +3 -3
- package/tui/src/utils/cronTasks.ts +1 -1
- package/tui/src/utils/execFileNoThrow.ts +1 -1
- package/tui/src/utils/filePersistence/types.ts +16 -38
- package/tui/src/utils/forkedAgent.ts +1 -1
- package/tui/src/utils/gracefulShutdown.ts +4 -4
- package/tui/src/utils/heapDumpService.ts +12 -8
- package/tui/src/utils/hooks/apiQueryHookHelper.ts +1 -1
- package/tui/src/utils/hooks/execPromptHook.ts +1 -1
- package/tui/src/utils/hooks/skillImprovement.ts +1 -1
- package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
- package/tui/src/utils/messages.ts +18 -0
- package/tui/src/utils/migrateSessions.ts +3 -3
- package/tui/src/utils/model/model.ts +6 -6
- package/tui/src/utils/permissions/yoloClassifier.ts +1 -1
- package/tui/src/utils/plugins/headlessPluginInstall.ts +1 -1
- package/tui/src/utils/plugins/mcpPluginIntegration.ts +1 -1
- package/tui/src/utils/plugins/mcpbHandler.ts +1 -1
- package/tui/src/utils/plugins/pluginLoader.ts +8 -8
- package/tui/src/utils/protectedNamespace.ts +5 -3
- package/tui/src/utils/rawJsonToolCall.ts +242 -0
- package/tui/src/utils/ripgrep.ts +16 -7
- package/tui/src/utils/sessionTitle.ts +1 -1
- package/tui/src/utils/settings/permissionValidation.ts +14 -2
- package/tui/src/utils/shell/prefix.ts +1 -1
- package/tui/src/utils/sideQuery.ts +1 -1
- package/tui/src/utils/systemThemeWatcher.ts +13 -3
- package/tui/src/utils/teleport.tsx +1 -1
- package/uv.lock +426 -45
- package/tui/src/services/api/claude.ts +0 -3540
- package/tui/src/tools/_shared/directPublicDataGuard.ts +0 -362
- package/tui/src/tools/_shared/kmaAnalysisGuard.ts +0 -197
- package/tui/src/tools/_shared/kmaAviationGuard.ts +0 -70
- package/tui/src/tools/_shared/nmcAedGuard.ts +0 -234
- package/tui/src/tools/_shared/protectedCheckGuard.ts +0 -207
- package/tui/src/tools/_shared/textToolCallGuard.ts +0 -91
|
@@ -0,0 +1,1075 @@
|
|
|
1
|
+
import type { QueryParams, QueryGenerator } from '../query.js'
|
|
2
|
+
import type { AssistantMessage, Message } from '../types/message.js'
|
|
3
|
+
import { productionDeps } from './deps.js'
|
|
4
|
+
import { Terminal } from './transitions.js'
|
|
5
|
+
import { enforceSupportToolBoundary } from './supportBoundary.js'
|
|
6
|
+
import {
|
|
7
|
+
buildPublicDataTerminalRepairPrompt,
|
|
8
|
+
buildStalePriorToolResultFinalAnswerBlockedText,
|
|
9
|
+
buildUnsupportedRouteFinalAnswerBlockedText,
|
|
10
|
+
buildUnavailableToolFinalAnswerBlockedText,
|
|
11
|
+
shouldBlockStalePriorToolResultAnswer,
|
|
12
|
+
shouldBlockFinalAnswerAfterUnsupportedRouteRepair,
|
|
13
|
+
shouldBlockFinalAnswerAfterUnavailableToolRepair,
|
|
14
|
+
shouldBlockUnsupportedRouteDetailAnswer,
|
|
15
|
+
} from './publicDataTerminalRepair.js'
|
|
16
|
+
import {
|
|
17
|
+
contentBlocks,
|
|
18
|
+
isAssistantMessage,
|
|
19
|
+
isUserMessage,
|
|
20
|
+
latestTextUserMessageIndex,
|
|
21
|
+
messageText,
|
|
22
|
+
toolUseBlocks,
|
|
23
|
+
type ToolUseBlock,
|
|
24
|
+
} from './messageGuards.js'
|
|
25
|
+
import { runToolUseBlocks } from './toolRunner.js'
|
|
26
|
+
import {
|
|
27
|
+
buildDocumentCompletionPromptIfNeeded,
|
|
28
|
+
selectRecoveredDocumentToolChoiceNameForMessages,
|
|
29
|
+
selectRecoveredSupportToolChoiceNameForMessages,
|
|
30
|
+
} from '../tools/_shared/toolChoiceRepair.js'
|
|
31
|
+
import {
|
|
32
|
+
extractTextualToolCallProposals,
|
|
33
|
+
parseTrailingRawJsonToolCallProposal,
|
|
34
|
+
textContainsToolCallProposal,
|
|
35
|
+
textContainsMalformedToolCallProposal,
|
|
36
|
+
} from '../utils/rawJsonToolCall.js'
|
|
37
|
+
import {
|
|
38
|
+
buildGenericPendingFinalAnswerToolUseBlockedText,
|
|
39
|
+
shouldBlockToolUseAfterGenericPendingFinalAnswerRepair,
|
|
40
|
+
} from '../tools/_shared/toolChoiceRepair/publicDataRepair.js'
|
|
41
|
+
import {
|
|
42
|
+
parseToolUnavailableError,
|
|
43
|
+
} from './toolResultErrors.js'
|
|
44
|
+
import {
|
|
45
|
+
appendRouteDiagnostic,
|
|
46
|
+
hashRouteDiagnosticText,
|
|
47
|
+
} from '../tools/AdapterTool/routeDiagnostics.js'
|
|
48
|
+
import { createAssistantMessage, createUserMessage } from '../utils/messages.js'
|
|
49
|
+
|
|
50
|
+
const ROOT_FIND_TOOL_NAME = 'find'
|
|
51
|
+
const ROOT_PRIMITIVE_TOOL_NAMES = new Set([
|
|
52
|
+
'find',
|
|
53
|
+
'locate',
|
|
54
|
+
'check',
|
|
55
|
+
'send',
|
|
56
|
+
'document',
|
|
57
|
+
])
|
|
58
|
+
const KAKAO_LOCATION_TOOL_NAMES = new Set([
|
|
59
|
+
'kakao_address_search',
|
|
60
|
+
'kakao_keyword_search',
|
|
61
|
+
])
|
|
62
|
+
const KMA_METAR_TOOL_NAME = 'kma_apihub_url_air_metar_decoded'
|
|
63
|
+
const KMA_ORDINARY_WEATHER_TOOL_NAMES = new Set([
|
|
64
|
+
'kma_apihub_upp_mtly_info_service_get_max_wind',
|
|
65
|
+
'kma_current_observation',
|
|
66
|
+
'kma_forecast_fetch',
|
|
67
|
+
'kma_short_term_forecast',
|
|
68
|
+
'kma_ultra_short_term_forecast',
|
|
69
|
+
])
|
|
70
|
+
const REGISTERED_EMERGENCY_RESULT_TOOL_NAMES = new Set([
|
|
71
|
+
'nmc_emergency_search',
|
|
72
|
+
'hira_hospital_search',
|
|
73
|
+
])
|
|
74
|
+
let recoveredRawJsonToolUseSequence = 0
|
|
75
|
+
const PERMISSION_DENIED_TEXT_RE =
|
|
76
|
+
/(permission_denied|Authentication rejected|permission denied|인증이 거부)/iu
|
|
77
|
+
const ADAPTERLESS_FIND_TEXT_RE =
|
|
78
|
+
/find\(mode='fetch'\) requires a concrete adapter tool_id|No concrete adapter was selected|requires a concrete adapter tool_id|Missing or invalid fields:\s*tool_id/iu
|
|
79
|
+
const PROMPT_INJECTION_USER_RE =
|
|
80
|
+
/(이전\s*지시.*무시|모든\s*지시.*무시|ignore\s+(?:all\s+)?(?:previous|prior|system)\s+instructions|시스템\s*프롬프트|system\s+prompt|토큰|token|secret|credential|api[_\s-]?key|도구를\s*(?:이렇게\s*)?(?:직접\s*)?실행|execute\s+(?:this\s+)?tool|run\s+(?:this\s+)?tool|call\s+(?:this\s+)?tool)/iu
|
|
81
|
+
const SENSITIVE_DISCLOSURE_ACK_RE =
|
|
82
|
+
/(시스템\s*프롬프트.*(출력|공개)|토큰.*(출력|공개)|system\s+prompt.*(?:print|show|reveal)|token.*(?:print|show|reveal)|secret.*(?:print|show|reveal)|도구를.*(?:직접\s*)?실행|execute\s+(?:the\s+)?tool|run\s+(?:the\s+)?tool)/iu
|
|
83
|
+
const EMERGENCY_RESULT_REQUEST_RE =
|
|
84
|
+
/(응급|응급실|야간\s*진료|야간진료|병원|의료|emergency|hospital|\bER\b)/iu
|
|
85
|
+
const EMERGENCY_FACILITY_CLAIM_RE =
|
|
86
|
+
/[가-힣A-Za-z0-9·()]{1,24}(?:병원|의료원|응급의료센터|응급센터|응급실)/u
|
|
87
|
+
const EMERGENCY_STATUS_OR_DISTANCE_CLAIM_RE =
|
|
88
|
+
/(?:\d+(?:\.\d+)?\s*(?:km|킬로미터|m|미터)|병상|가용|진료\s*가능|운영\s*중|24\s*시간|대기\s*시간)/iu
|
|
89
|
+
const EMERGENCY_SEARCH_SUCCESS_CLAIM_RE =
|
|
90
|
+
/(?:응급실|응급의료|병원)[^\n.。]*(?:검색|조회|확인|찾았|추천|결과)|(?:검색|조회)\s*결과[^\n.。]*(?:응급실|응급의료|병원)/iu
|
|
91
|
+
const EMERGENCY_SAFE_LIMITATION_RE =
|
|
92
|
+
/(결과\s*없이|없이는|단정하지|조회하지\s*못|확인하지\s*못|연결된\s*뒤|adapter|handoff|공식\s*(?:채널|응급의료)|119)/iu
|
|
93
|
+
type ToolResultBlock = {
|
|
94
|
+
readonly type: 'tool_result'
|
|
95
|
+
readonly tool_use_id: string
|
|
96
|
+
readonly content?: unknown
|
|
97
|
+
readonly is_error?: boolean
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function toolResultBlocks(message: Message): readonly ToolResultBlock[] {
|
|
101
|
+
return contentBlocks(message).filter(
|
|
102
|
+
(block): block is ToolResultBlock =>
|
|
103
|
+
block.type === 'tool_result' &&
|
|
104
|
+
'tool_use_id' in block &&
|
|
105
|
+
typeof block.tool_use_id === 'string',
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function ccStyleUnavailableToolResultContent(content: unknown): string | undefined {
|
|
110
|
+
const unavailable = parseToolUnavailableError(content)
|
|
111
|
+
if (unavailable === undefined) return undefined
|
|
112
|
+
const toolName = unavailable.error.tool_name
|
|
113
|
+
return JSON.stringify({
|
|
114
|
+
...unavailable,
|
|
115
|
+
error: {
|
|
116
|
+
...unavailable.error,
|
|
117
|
+
message: `Tool ${toolName} is unavailable.`,
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function upgradeUnavailableToolResultMessage(message: Message): Message {
|
|
123
|
+
if (!isUserMessage(message) || !Array.isArray(message.message.content)) {
|
|
124
|
+
return message
|
|
125
|
+
}
|
|
126
|
+
let changed = false
|
|
127
|
+
const content = message.message.content.map(block => {
|
|
128
|
+
if (
|
|
129
|
+
typeof block !== 'object' ||
|
|
130
|
+
block === null ||
|
|
131
|
+
!('type' in block) ||
|
|
132
|
+
block.type !== 'tool_result' ||
|
|
133
|
+
!('content' in block)
|
|
134
|
+
) {
|
|
135
|
+
return block
|
|
136
|
+
}
|
|
137
|
+
const upgradedContent = ccStyleUnavailableToolResultContent(block.content)
|
|
138
|
+
if (upgradedContent === undefined || upgradedContent === block.content) {
|
|
139
|
+
return block
|
|
140
|
+
}
|
|
141
|
+
changed = true
|
|
142
|
+
return { ...block, content: upgradedContent }
|
|
143
|
+
})
|
|
144
|
+
return changed
|
|
145
|
+
? { ...message, message: { ...message.message, content } }
|
|
146
|
+
: message
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function disabledProviderToolNamesForTurn(
|
|
150
|
+
messages: readonly Message[],
|
|
151
|
+
): readonly string[] {
|
|
152
|
+
const disabled: string[] = []
|
|
153
|
+
disabled.push(...permissionDeniedToolNames(messages))
|
|
154
|
+
if (hasAdapterlessFindFailure(messages)) {
|
|
155
|
+
disabled.push(ROOT_FIND_TOOL_NAME)
|
|
156
|
+
}
|
|
157
|
+
return [...new Set(disabled)]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function contentText(value: unknown): string {
|
|
161
|
+
if (typeof value === 'string') return value
|
|
162
|
+
if (value === undefined || value === null) return ''
|
|
163
|
+
try {
|
|
164
|
+
return JSON.stringify(value)
|
|
165
|
+
} catch {
|
|
166
|
+
return String(value)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function parseJsonRecord(value: unknown): Record<string, unknown> | undefined {
|
|
171
|
+
if (isRecord(value)) return value
|
|
172
|
+
if (typeof value !== 'string') return undefined
|
|
173
|
+
try {
|
|
174
|
+
const parsed: unknown = JSON.parse(value)
|
|
175
|
+
return isRecord(parsed) ? parsed : undefined
|
|
176
|
+
} catch {
|
|
177
|
+
return undefined
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isStructuredFailureToolResult(value: unknown): boolean {
|
|
182
|
+
return parseJsonRecord(value)?.ok === false
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function stripLeadingTemplateControlTokens(text: string): string {
|
|
186
|
+
return text.replace(/^\s*(?:<%+|%>+)\s*/u, '')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function sanitizeVisibleAssistantControlTokens(
|
|
190
|
+
message: AssistantMessage,
|
|
191
|
+
): AssistantMessage {
|
|
192
|
+
const content = message.message.content
|
|
193
|
+
if (typeof content === 'string') {
|
|
194
|
+
const sanitized = stripLeadingTemplateControlTokens(content)
|
|
195
|
+
return sanitized === content
|
|
196
|
+
? message
|
|
197
|
+
: { ...message, message: { ...message.message, content: sanitized } }
|
|
198
|
+
}
|
|
199
|
+
if (!Array.isArray(content)) return message
|
|
200
|
+
let changed = false
|
|
201
|
+
const sanitizedContent = content.map(block => {
|
|
202
|
+
if (!isRecord(block) || block.type !== 'text' || typeof block.text !== 'string') {
|
|
203
|
+
return block
|
|
204
|
+
}
|
|
205
|
+
const sanitizedText = stripLeadingTemplateControlTokens(block.text)
|
|
206
|
+
if (sanitizedText === block.text) return block
|
|
207
|
+
changed = true
|
|
208
|
+
return { ...block, text: sanitizedText }
|
|
209
|
+
})
|
|
210
|
+
return changed
|
|
211
|
+
? { ...message, message: { ...message.message, content: sanitizedContent } }
|
|
212
|
+
: message
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
type PriorToolResult = {
|
|
216
|
+
readonly toolName: string
|
|
217
|
+
readonly content: unknown
|
|
218
|
+
readonly isError: boolean
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function toolResultsSinceLatestPrompt(
|
|
222
|
+
messages: readonly Message[],
|
|
223
|
+
): readonly PriorToolResult[] {
|
|
224
|
+
const latestUserIndex = latestTextUserMessageIndex(messages)
|
|
225
|
+
if (latestUserIndex < 0) return []
|
|
226
|
+
const toolNamesByUseId = new Map<string, string>()
|
|
227
|
+
const results: PriorToolResult[] = []
|
|
228
|
+
for (const message of messages.slice(latestUserIndex + 1)) {
|
|
229
|
+
if (isAssistantMessage(message)) {
|
|
230
|
+
for (const block of toolUseBlocks(message)) {
|
|
231
|
+
toolNamesByUseId.set(block.id, block.name)
|
|
232
|
+
}
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
if (!isUserMessage(message)) continue
|
|
236
|
+
for (const block of toolResultBlocks(message)) {
|
|
237
|
+
const toolName = toolNamesByUseId.get(block.tool_use_id)
|
|
238
|
+
if (toolName === undefined) continue
|
|
239
|
+
results.push({
|
|
240
|
+
toolName,
|
|
241
|
+
content: block.content,
|
|
242
|
+
isError: block.is_error === true || isStructuredFailureToolResult(block.content),
|
|
243
|
+
})
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return results
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function systemPromptForTurn(
|
|
250
|
+
basePrompt: readonly string[],
|
|
251
|
+
messages: readonly Message[],
|
|
252
|
+
): readonly string[] {
|
|
253
|
+
const priorResults = toolResultsSinceLatestPrompt(messages)
|
|
254
|
+
if (!priorResults.some(result => result.toolName === KMA_METAR_TOOL_NAME)) {
|
|
255
|
+
return basePrompt
|
|
256
|
+
}
|
|
257
|
+
return [
|
|
258
|
+
...basePrompt,
|
|
259
|
+
[
|
|
260
|
+
'KMA aviation weather boundary: prior decoded METAR evidence exists for this airport-flight request.',
|
|
261
|
+
'Do not ask the user for KMA nx/ny grid coordinates.',
|
|
262
|
+
'Do not call ordinary KMA current or forecast tools as a fallback for airport aviation weather.',
|
|
263
|
+
'Answer from the aviation evidence or hand off to official airline/airport status channels.',
|
|
264
|
+
].join(' '),
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function hasConcreteFindToolId(input: Record<string, unknown>): boolean {
|
|
269
|
+
const toolId = input.tool_id
|
|
270
|
+
return typeof toolId === 'string' &&
|
|
271
|
+
toolId.trim().length > 0 &&
|
|
272
|
+
!ROOT_PRIMITIVE_TOOL_NAMES.has(toolId)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function isAdapterlessFindToolUse(block: ToolUseBlock): boolean {
|
|
276
|
+
return block.name === ROOT_FIND_TOOL_NAME && !hasConcreteFindToolId(block.input)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
280
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function createRecoveredRawJsonToolUseId(baseUuid: string): string {
|
|
284
|
+
recoveredRawJsonToolUseSequence += 1
|
|
285
|
+
return `toolu-raw-json-${baseUuid}-${recoveredRawJsonToolUseSequence}`
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function upgradeRawJsonToolCallAssistant(params: {
|
|
289
|
+
readonly assistantMessage: AssistantMessage
|
|
290
|
+
readonly createToolUseId: () => string
|
|
291
|
+
}): AssistantMessage {
|
|
292
|
+
if (toolUseBlocks(params.assistantMessage).length > 0) {
|
|
293
|
+
return params.assistantMessage
|
|
294
|
+
}
|
|
295
|
+
const blocks = contentBlocks(params.assistantMessage)
|
|
296
|
+
const textualExtraction = extractTextualToolCallProposals({
|
|
297
|
+
text: messageText(params.assistantMessage),
|
|
298
|
+
})
|
|
299
|
+
if (textualExtraction !== undefined) {
|
|
300
|
+
const preservedBlocks = blocks.filter(block => block.type !== 'text')
|
|
301
|
+
const content = textualExtraction.text.length > 0
|
|
302
|
+
? [
|
|
303
|
+
...preservedBlocks,
|
|
304
|
+
{ type: 'text' as const, text: textualExtraction.text },
|
|
305
|
+
]
|
|
306
|
+
: preservedBlocks
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
...params.assistantMessage,
|
|
310
|
+
message: {
|
|
311
|
+
...params.assistantMessage.message,
|
|
312
|
+
content: [
|
|
313
|
+
...content,
|
|
314
|
+
...textualExtraction.proposals.map(proposal => ({
|
|
315
|
+
type: 'tool_use' as const,
|
|
316
|
+
id: params.createToolUseId(),
|
|
317
|
+
name: proposal.name,
|
|
318
|
+
input: proposal.input,
|
|
319
|
+
})),
|
|
320
|
+
],
|
|
321
|
+
},
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const trailingProposal = parseTrailingRawJsonToolCallProposal({
|
|
326
|
+
text: messageText(params.assistantMessage),
|
|
327
|
+
})
|
|
328
|
+
if (trailingProposal === undefined) return params.assistantMessage
|
|
329
|
+
|
|
330
|
+
const preservedBlocks = blocks.filter(block => block.type !== 'text')
|
|
331
|
+
const content = trailingProposal.prelude.length > 0
|
|
332
|
+
? [
|
|
333
|
+
...preservedBlocks,
|
|
334
|
+
{ type: 'text' as const, text: trailingProposal.prelude },
|
|
335
|
+
]
|
|
336
|
+
: preservedBlocks
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
...params.assistantMessage,
|
|
340
|
+
message: {
|
|
341
|
+
...params.assistantMessage.message,
|
|
342
|
+
content: [
|
|
343
|
+
...content,
|
|
344
|
+
{
|
|
345
|
+
type: 'tool_use' as const,
|
|
346
|
+
id: params.createToolUseId(),
|
|
347
|
+
name: trailingProposal.proposal.name,
|
|
348
|
+
input: trailingProposal.proposal.input,
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function hasAdapterlessFindFailure(messages: readonly Message[]): boolean {
|
|
356
|
+
const latestUserIndex = latestTextUserMessageIndex(messages)
|
|
357
|
+
if (latestUserIndex < 0) return false
|
|
358
|
+
const toolNamesByUseId = new Map<string, string>()
|
|
359
|
+
for (let index = latestUserIndex + 1; index < messages.length; index += 1) {
|
|
360
|
+
const message = messages[index]
|
|
361
|
+
if (!message) continue
|
|
362
|
+
if (isAssistantMessage(message)) {
|
|
363
|
+
for (const block of toolUseBlocks(message)) {
|
|
364
|
+
toolNamesByUseId.set(block.id, block.name)
|
|
365
|
+
}
|
|
366
|
+
continue
|
|
367
|
+
}
|
|
368
|
+
if (!isUserMessage(message)) continue
|
|
369
|
+
for (const block of toolResultBlocks(message)) {
|
|
370
|
+
if (
|
|
371
|
+
toolNamesByUseId.get(block.tool_use_id) === ROOT_FIND_TOOL_NAME &&
|
|
372
|
+
ADAPTERLESS_FIND_TEXT_RE.test(contentText(block.content))
|
|
373
|
+
) {
|
|
374
|
+
return true
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return false
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function shouldBlockRepeatedAdapterlessFind(
|
|
382
|
+
block: ToolUseBlock,
|
|
383
|
+
messages: readonly Message[],
|
|
384
|
+
): boolean {
|
|
385
|
+
return isAdapterlessFindToolUse(block) && hasAdapterlessFindFailure(messages)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function createAdapterlessFindContinuationGuardResult(
|
|
389
|
+
block: ToolUseBlock,
|
|
390
|
+
assistantMessage: AssistantMessage,
|
|
391
|
+
): Message {
|
|
392
|
+
const message = [
|
|
393
|
+
'Root find wrapper already failed because no concrete adapter tool_id was selected.',
|
|
394
|
+
'Do not call find again without tool_id in this citizen turn.',
|
|
395
|
+
'Call a concrete loaded adapter directly when one is available; otherwise write a stable Korean final, needs-input, or handoff answer that names the missing adapter/credential limitation.',
|
|
396
|
+
].join(' ')
|
|
397
|
+
return createUserMessage({
|
|
398
|
+
content: [
|
|
399
|
+
{
|
|
400
|
+
type: 'tool_result',
|
|
401
|
+
tool_use_id: block.id,
|
|
402
|
+
content: message,
|
|
403
|
+
is_error: true,
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
toolUseResult: message,
|
|
407
|
+
sourceToolAssistantUUID: assistantMessage.uuid,
|
|
408
|
+
})
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function createAdapterlessFindHardGuardFinalMessage(): AssistantMessage {
|
|
412
|
+
return createAssistantMessage({
|
|
413
|
+
content: [
|
|
414
|
+
'공공서비스 adapter가 선택되지 않아 이 시민 업무를 계속 진행할 수 없습니다.',
|
|
415
|
+
'로컬 파일/워크스페이스 결과는 이 시민 업무의 근거로 사용하지 않습니다.',
|
|
416
|
+
'요청하신 신청/조회/등록 업무는 공식 공공서비스 adapter와 필요한 인증/위임 credential이 연결된 뒤 이어서 처리해야 합니다. 지금은 공식 신청 채널 확인으로 handoff합니다.',
|
|
417
|
+
].join('\n\n'),
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function createPromptInjectionToolRecoveryBlockedMessage(): AssistantMessage {
|
|
422
|
+
return createAssistantMessage({
|
|
423
|
+
content: [
|
|
424
|
+
'시스템 프롬프트나 토큰, 비밀값은 공개할 수 없습니다.',
|
|
425
|
+
'사용자 입력에 포함된 JSON/도구 실행 지시는 신뢰된 도구 호출이 아니므로 실행하지 않습니다.',
|
|
426
|
+
'필요한 업무를 자연어로 다시 요청하면 등록된 도구와 권한 경계 안에서 처리하겠습니다.',
|
|
427
|
+
].join('\n\n'),
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function createMalformedToolProposalBlockedMessage(): AssistantMessage {
|
|
432
|
+
return createAssistantMessage({
|
|
433
|
+
content: [
|
|
434
|
+
'유효하지 않은 도구 호출 형식이 감지되어 실행하지 않았습니다.',
|
|
435
|
+
'등록된 도구의 정상 tool_use 경계와 검증된 tool_result 없이 서류 목록이나 처리 결과를 단정하지 않겠습니다.',
|
|
436
|
+
'필요한 업무를 자연어로 다시 요청하면 현재 등록된 도구 표면 안에서 처리하겠습니다.',
|
|
437
|
+
].join('\n\n'),
|
|
438
|
+
})
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function createGenericPendingFinalAnswerToolUseBlockedMessage(): AssistantMessage {
|
|
442
|
+
return createAssistantMessage({
|
|
443
|
+
content: buildGenericPendingFinalAnswerToolUseBlockedText(),
|
|
444
|
+
})
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function createUnavailableToolFinalAnswerBlockedMessage(
|
|
448
|
+
messages: readonly Message[],
|
|
449
|
+
): AssistantMessage {
|
|
450
|
+
void messages
|
|
451
|
+
return createAssistantMessage({
|
|
452
|
+
content: buildUnavailableToolFinalAnswerBlockedText(),
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function createUnsupportedRouteFinalAnswerBlockedMessage(): AssistantMessage {
|
|
457
|
+
return createAssistantMessage({
|
|
458
|
+
content: buildUnsupportedRouteFinalAnswerBlockedText(),
|
|
459
|
+
})
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function createStalePriorToolResultFinalAnswerBlockedMessage(): AssistantMessage {
|
|
463
|
+
return createAssistantMessage({
|
|
464
|
+
content: buildStalePriorToolResultFinalAnswerBlockedText(),
|
|
465
|
+
})
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function createEmergencyNoClaimBlockedMessage(): AssistantMessage {
|
|
469
|
+
return createAssistantMessage({
|
|
470
|
+
content: [
|
|
471
|
+
'등록된 응급의료 adapter 결과 없이 응급실 이름, 거리, 운영 상태, 병상 여부를 단정하지 않습니다.',
|
|
472
|
+
'최신 사용자 요청 이후 nmc_emergency_search 또는 hira_hospital_search tool_result가 없으므로 공식 검색 결과처럼 안내할 수 없습니다.',
|
|
473
|
+
'긴급하면 119 또는 응급의료포털/병원 공식 채널에서 즉시 확인해야 하며, 해당 adapter가 연결되면 그 결과에 근거해 다시 안내하겠습니다.',
|
|
474
|
+
].join('\n\n'),
|
|
475
|
+
})
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function hasSuccessfulRegisteredEmergencyResult(
|
|
479
|
+
messages: readonly Message[],
|
|
480
|
+
): boolean {
|
|
481
|
+
return toolResultsSinceLatestPrompt(messages).some(result =>
|
|
482
|
+
REGISTERED_EMERGENCY_RESULT_TOOL_NAMES.has(result.toolName) &&
|
|
483
|
+
!result.isError,
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function isEmergencyResultClaim(text: string): boolean {
|
|
488
|
+
const hasFacilityClaim = EMERGENCY_FACILITY_CLAIM_RE.test(text)
|
|
489
|
+
const hasStatusOrDistanceClaim =
|
|
490
|
+
EMERGENCY_STATUS_OR_DISTANCE_CLAIM_RE.test(text)
|
|
491
|
+
if (
|
|
492
|
+
EMERGENCY_SAFE_LIMITATION_RE.test(text) &&
|
|
493
|
+
!hasFacilityClaim &&
|
|
494
|
+
!hasStatusOrDistanceClaim
|
|
495
|
+
) {
|
|
496
|
+
return false
|
|
497
|
+
}
|
|
498
|
+
return hasFacilityClaim ||
|
|
499
|
+
hasStatusOrDistanceClaim ||
|
|
500
|
+
EMERGENCY_SEARCH_SUCCESS_CLAIM_RE.test(text)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function shouldBlockEmergencyResultClaim(params: {
|
|
504
|
+
readonly messages: readonly Message[]
|
|
505
|
+
readonly candidate: AssistantMessage
|
|
506
|
+
}): boolean {
|
|
507
|
+
if (toolUseBlocks(params.candidate).length > 0) return false
|
|
508
|
+
if (!EMERGENCY_RESULT_REQUEST_RE.test(latestUserText(params.messages))) {
|
|
509
|
+
return false
|
|
510
|
+
}
|
|
511
|
+
if (hasSuccessfulRegisteredEmergencyResult(params.messages)) return false
|
|
512
|
+
return isEmergencyResultClaim(messageText(params.candidate))
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function domainGuardFinalAnswerForToolResults(
|
|
516
|
+
results: readonly Message[],
|
|
517
|
+
): AssistantMessage | undefined {
|
|
518
|
+
const text = results
|
|
519
|
+
.flatMap(message =>
|
|
520
|
+
contentBlocks(message).map(block => contentText(block)),
|
|
521
|
+
)
|
|
522
|
+
.join('\n')
|
|
523
|
+
if (text.includes('Hometax lookup already returned')) {
|
|
524
|
+
return createAssistantMessage({
|
|
525
|
+
content:
|
|
526
|
+
'홈택스 조회는 이미 완료되었습니다. 다음 단계는 본인확인, 위임, 동의 확인입니다. verify 도구로 인증/위임 승인을 먼저 진행하거나, 승인 없이는 공식 채널 확인으로 handoff합니다.',
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
if (text.includes('KMA aviation lookup already returned')) {
|
|
530
|
+
return createAssistantMessage({
|
|
531
|
+
content:
|
|
532
|
+
'항공기상 확인 결과는 이미 확보했거나 credential 한계가 확인되었습니다. 같은 항공기상 도구를 반복 호출하지 않고, 확보된 근거와 공식 항공사/공항 상태 확인 한계를 기준으로 안내합니다.',
|
|
533
|
+
})
|
|
534
|
+
}
|
|
535
|
+
if (text.includes('as a fallback for airport aviation weather')) {
|
|
536
|
+
return createAssistantMessage({
|
|
537
|
+
content:
|
|
538
|
+
'항공기상 근거를 바탕으로 지연 위험과 일반 날씨 fallback 한계를 안내합니다. 공항/항공편 상태는 항공사와 공항 공식 채널에서 최종 확인해야 합니다.',
|
|
539
|
+
})
|
|
540
|
+
}
|
|
541
|
+
if (text.includes('Do not repeat ordinary KMA current or forecast tools')) {
|
|
542
|
+
return createAssistantMessage({
|
|
543
|
+
content:
|
|
544
|
+
'KMA 일반 날씨 도구를 반복 호출하지 않습니다. 기존 위치 근거와 격자값 누락 한계를 바탕으로 안전한 이동 판단은 공식 날씨/교통 채널 확인으로 handoff합니다.',
|
|
545
|
+
})
|
|
546
|
+
}
|
|
547
|
+
return undefined
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function isPermissionDeniedToolResultBlock(block: ToolResultBlock): boolean {
|
|
551
|
+
return PERMISSION_DENIED_TEXT_RE.test(contentText(block.content))
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function permissionDeniedToolNames(messages: readonly Message[]): readonly string[] {
|
|
555
|
+
const latestUserIndex = latestTextUserMessageIndex(messages)
|
|
556
|
+
if (latestUserIndex < 0) return []
|
|
557
|
+
const toolNamesByUseId = new Map<string, string>()
|
|
558
|
+
const denied = new Set<string>()
|
|
559
|
+
for (let index = latestUserIndex + 1; index < messages.length; index += 1) {
|
|
560
|
+
const message = messages[index]
|
|
561
|
+
if (!message) continue
|
|
562
|
+
if (isAssistantMessage(message)) {
|
|
563
|
+
for (const block of toolUseBlocks(message)) {
|
|
564
|
+
toolNamesByUseId.set(block.id, block.name)
|
|
565
|
+
}
|
|
566
|
+
continue
|
|
567
|
+
}
|
|
568
|
+
if (!isUserMessage(message)) continue
|
|
569
|
+
for (const block of toolResultBlocks(message)) {
|
|
570
|
+
const toolName = toolNamesByUseId.get(block.tool_use_id)
|
|
571
|
+
if (toolName !== undefined && isPermissionDeniedToolResultBlock(block)) {
|
|
572
|
+
denied.add(toolName)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return [...denied]
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function shouldBlockRepeatedPermissionDeniedTool(
|
|
580
|
+
block: ToolUseBlock,
|
|
581
|
+
messages: readonly Message[],
|
|
582
|
+
): boolean {
|
|
583
|
+
return permissionDeniedToolNames(messages).includes(block.name)
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function createPermissionDeniedContinuationGuardResult(
|
|
587
|
+
block: ToolUseBlock,
|
|
588
|
+
assistantMessage: AssistantMessage,
|
|
589
|
+
): Message {
|
|
590
|
+
const message = [
|
|
591
|
+
`Permission boundary blocked: ${block.name} was already rejected with permission_denied in this request.`,
|
|
592
|
+
'Do not retry the same protected check until the user explicitly grants or restarts authentication.',
|
|
593
|
+
'Write a stable Korean needs-input or handoff answer explaining that authentication approval is required before continuing.',
|
|
594
|
+
].join(' ')
|
|
595
|
+
return createUserMessage({
|
|
596
|
+
content: [
|
|
597
|
+
{
|
|
598
|
+
type: 'tool_result',
|
|
599
|
+
tool_use_id: block.id,
|
|
600
|
+
content: message,
|
|
601
|
+
is_error: true,
|
|
602
|
+
},
|
|
603
|
+
],
|
|
604
|
+
toolUseResult: message,
|
|
605
|
+
sourceToolAssistantUUID: assistantMessage.uuid,
|
|
606
|
+
})
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function latestQueryHash(messages: readonly Message[]): string {
|
|
610
|
+
const latestUserIndex = latestTextUserMessageIndex(messages)
|
|
611
|
+
const latestUserMessage = latestUserIndex >= 0 ? messages[latestUserIndex] : undefined
|
|
612
|
+
return hashRouteDiagnosticText(
|
|
613
|
+
latestUserMessage ? messageText(latestUserMessage) : '',
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function latestUserText(messages: readonly Message[]): string {
|
|
618
|
+
const latestUserIndex = latestTextUserMessageIndex(messages)
|
|
619
|
+
const latestUserMessage = latestUserIndex >= 0 ? messages[latestUserIndex] : undefined
|
|
620
|
+
return latestUserMessage ? messageText(latestUserMessage) : ''
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function shouldBlockPromptInjectionToolRecovery(params: {
|
|
624
|
+
readonly messages: readonly Message[]
|
|
625
|
+
readonly candidate: AssistantMessage
|
|
626
|
+
readonly activeToolChoiceName: string | undefined
|
|
627
|
+
}): boolean {
|
|
628
|
+
const userText = latestUserText(params.messages)
|
|
629
|
+
if (!PROMPT_INJECTION_USER_RE.test(userText)) return false
|
|
630
|
+
const candidateToolUses = toolUseBlocks(params.candidate)
|
|
631
|
+
if (
|
|
632
|
+
params.activeToolChoiceName !== undefined &&
|
|
633
|
+
candidateToolUses.some(block => block.name === params.activeToolChoiceName)
|
|
634
|
+
) {
|
|
635
|
+
return false
|
|
636
|
+
}
|
|
637
|
+
if (candidateToolUses.length > 0) return true
|
|
638
|
+
const candidateText = messageText(params.candidate)
|
|
639
|
+
return textContainsToolCallProposal(candidateText) ||
|
|
640
|
+
SENSITIVE_DISCLOSURE_ACK_RE.test(candidateText)
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function shouldBlockMalformedUserToolProposal(
|
|
644
|
+
messages: readonly Message[],
|
|
645
|
+
): boolean {
|
|
646
|
+
return textContainsMalformedToolCallProposal(latestUserText(messages))
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function appendQueryAssistantDiagnostic(params: {
|
|
650
|
+
readonly event: string
|
|
651
|
+
readonly querySource: string
|
|
652
|
+
readonly messages: readonly Message[]
|
|
653
|
+
readonly assistantMessage: AssistantMessage
|
|
654
|
+
readonly turnCount: number
|
|
655
|
+
readonly boundaryKind: string
|
|
656
|
+
readonly repairPromptChars: number
|
|
657
|
+
readonly continueAfterRepair: boolean
|
|
658
|
+
}): void {
|
|
659
|
+
appendRouteDiagnostic(params.event, {
|
|
660
|
+
query_hash: latestQueryHash(params.messages),
|
|
661
|
+
query_source: params.querySource,
|
|
662
|
+
turn_count: params.turnCount,
|
|
663
|
+
message_count: params.messages.length,
|
|
664
|
+
assistant_text_chars: messageText(params.assistantMessage).length,
|
|
665
|
+
assistant_tool_use_count: toolUseBlocks(params.assistantMessage).length,
|
|
666
|
+
assistant_content_block_count: contentBlocks(params.assistantMessage).length,
|
|
667
|
+
boundary_kind: params.boundaryKind,
|
|
668
|
+
repair_prompt_chars: params.repairPromptChars,
|
|
669
|
+
continue_after_repair: params.continueAfterRepair,
|
|
670
|
+
})
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function appendQueryCompletedWithoutAssistantDiagnostic(params: {
|
|
674
|
+
readonly querySource: string
|
|
675
|
+
readonly messages: readonly Message[]
|
|
676
|
+
readonly turnCount: number
|
|
677
|
+
}): void {
|
|
678
|
+
appendRouteDiagnostic('query_completed_without_assistant', {
|
|
679
|
+
query_hash: latestQueryHash(params.messages),
|
|
680
|
+
query_source: params.querySource,
|
|
681
|
+
turn_count: params.turnCount,
|
|
682
|
+
message_count: params.messages.length,
|
|
683
|
+
})
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export async function* query(params: QueryParams): QueryGenerator {
|
|
687
|
+
const deps = params.deps ?? productionDeps()
|
|
688
|
+
const messages: Message[] = [...params.messages]
|
|
689
|
+
let turnCount = 0
|
|
690
|
+
|
|
691
|
+
while (params.maxTurns === undefined || turnCount < params.maxTurns) {
|
|
692
|
+
turnCount += 1
|
|
693
|
+
if (params.toolUseContext.abortController.signal.aborted) {
|
|
694
|
+
return Terminal.aborted_streaming()
|
|
695
|
+
}
|
|
696
|
+
if (shouldBlockMalformedUserToolProposal(messages)) {
|
|
697
|
+
const blockedMessage = createMalformedToolProposalBlockedMessage()
|
|
698
|
+
appendQueryAssistantDiagnostic({
|
|
699
|
+
event: 'query_user_blocked_malformed_tool_proposal',
|
|
700
|
+
querySource: String(params.querySource),
|
|
701
|
+
messages,
|
|
702
|
+
assistantMessage: blockedMessage,
|
|
703
|
+
turnCount,
|
|
704
|
+
boundaryKind: 'block',
|
|
705
|
+
repairPromptChars: 0,
|
|
706
|
+
continueAfterRepair: false,
|
|
707
|
+
})
|
|
708
|
+
yield blockedMessage
|
|
709
|
+
messages.push(blockedMessage)
|
|
710
|
+
return Terminal.completed()
|
|
711
|
+
}
|
|
712
|
+
yield { type: 'stream_request_start' }
|
|
713
|
+
|
|
714
|
+
let assistantMessage: AssistantMessage | undefined
|
|
715
|
+
let shouldContinueAfterRepairPrompt = false
|
|
716
|
+
const disabledProviderToolNames = disabledProviderToolNamesForTurn(
|
|
717
|
+
messages,
|
|
718
|
+
)
|
|
719
|
+
const activeToolChoiceName =
|
|
720
|
+
selectRecoveredSupportToolChoiceNameForMessages(messages) ??
|
|
721
|
+
selectRecoveredDocumentToolChoiceNameForMessages({
|
|
722
|
+
messages,
|
|
723
|
+
tools: params.toolUseContext.options.tools,
|
|
724
|
+
})
|
|
725
|
+
const activeToolChoiceAvailable = activeToolChoiceName
|
|
726
|
+
? params.toolUseContext.options.tools.some(
|
|
727
|
+
tool => tool.name === activeToolChoiceName,
|
|
728
|
+
)
|
|
729
|
+
: false
|
|
730
|
+
|
|
731
|
+
for await (const event of deps.callModel({
|
|
732
|
+
messages,
|
|
733
|
+
systemPrompt: systemPromptForTurn(params.systemPrompt, messages),
|
|
734
|
+
thinkingConfig: params.toolUseContext.options.thinkingConfig,
|
|
735
|
+
tools: params.toolUseContext.options.tools,
|
|
736
|
+
signal: params.toolUseContext.abortController.signal,
|
|
737
|
+
options: {
|
|
738
|
+
getToolPermissionContext: async () =>
|
|
739
|
+
params.toolUseContext.getAppState().toolPermissionContext,
|
|
740
|
+
model: params.toolUseContext.options.mainLoopModel,
|
|
741
|
+
isNonInteractiveSession:
|
|
742
|
+
params.toolUseContext.options.isNonInteractiveSession,
|
|
743
|
+
querySource: params.querySource,
|
|
744
|
+
agents: params.toolUseContext.options.agentDefinitions.activeAgents,
|
|
745
|
+
allowedAgentTypes:
|
|
746
|
+
params.toolUseContext.options.agentDefinitions.allowedAgentTypes,
|
|
747
|
+
mcpTools: params.toolUseContext.options.mcpClients,
|
|
748
|
+
maxOutputTokensOverride: params.maxOutputTokensOverride,
|
|
749
|
+
taskBudget: params.taskBudget,
|
|
750
|
+
skipCacheWrite: params.skipCacheWrite,
|
|
751
|
+
disabledProviderToolNames,
|
|
752
|
+
},
|
|
753
|
+
})) {
|
|
754
|
+
if (params.toolUseContext.abortController.signal.aborted) {
|
|
755
|
+
return Terminal.aborted_streaming()
|
|
756
|
+
}
|
|
757
|
+
if (!isAssistantMessage(event)) {
|
|
758
|
+
yield event
|
|
759
|
+
continue
|
|
760
|
+
}
|
|
761
|
+
const sanitizedCandidate = sanitizeVisibleAssistantControlTokens(event)
|
|
762
|
+
if (
|
|
763
|
+
shouldBlockPromptInjectionToolRecovery({
|
|
764
|
+
messages,
|
|
765
|
+
candidate: sanitizedCandidate,
|
|
766
|
+
activeToolChoiceName,
|
|
767
|
+
})
|
|
768
|
+
) {
|
|
769
|
+
appendQueryAssistantDiagnostic({
|
|
770
|
+
event: 'query_assistant_blocked_prompt_injection_tool_recovery',
|
|
771
|
+
querySource: String(params.querySource),
|
|
772
|
+
messages,
|
|
773
|
+
assistantMessage: sanitizedCandidate,
|
|
774
|
+
turnCount,
|
|
775
|
+
boundaryKind: 'block',
|
|
776
|
+
repairPromptChars: 0,
|
|
777
|
+
continueAfterRepair: false,
|
|
778
|
+
})
|
|
779
|
+
const blockedMessage = createPromptInjectionToolRecoveryBlockedMessage()
|
|
780
|
+
yield blockedMessage
|
|
781
|
+
messages.push(blockedMessage)
|
|
782
|
+
return Terminal.completed()
|
|
783
|
+
}
|
|
784
|
+
const assistantCandidate = upgradeRawJsonToolCallAssistant({
|
|
785
|
+
assistantMessage: sanitizedCandidate,
|
|
786
|
+
createToolUseId: () => createRecoveredRawJsonToolUseId(deps.uuid()),
|
|
787
|
+
})
|
|
788
|
+
if (
|
|
789
|
+
toolUseBlocks(assistantCandidate).length === 0 &&
|
|
790
|
+
textContainsMalformedToolCallProposal(messageText(assistantCandidate))
|
|
791
|
+
) {
|
|
792
|
+
appendQueryAssistantDiagnostic({
|
|
793
|
+
event: 'query_assistant_blocked_malformed_tool_proposal',
|
|
794
|
+
querySource: String(params.querySource),
|
|
795
|
+
messages,
|
|
796
|
+
assistantMessage: assistantCandidate,
|
|
797
|
+
turnCount,
|
|
798
|
+
boundaryKind: 'block',
|
|
799
|
+
repairPromptChars: 0,
|
|
800
|
+
continueAfterRepair: false,
|
|
801
|
+
})
|
|
802
|
+
const blockedMessage = createMalformedToolProposalBlockedMessage()
|
|
803
|
+
yield blockedMessage
|
|
804
|
+
messages.push(blockedMessage)
|
|
805
|
+
return Terminal.completed()
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const boundary = enforceSupportToolBoundary({
|
|
809
|
+
activeToolChoiceName,
|
|
810
|
+
activeToolChoiceAvailable,
|
|
811
|
+
messagesForQuery: messages,
|
|
812
|
+
assistantMessage: assistantCandidate,
|
|
813
|
+
})
|
|
814
|
+
if (
|
|
815
|
+
boundary.kind === 'pass' &&
|
|
816
|
+
shouldBlockUnsupportedRouteDetailAnswer({
|
|
817
|
+
messages,
|
|
818
|
+
candidate: boundary.message,
|
|
819
|
+
})
|
|
820
|
+
) {
|
|
821
|
+
appendQueryAssistantDiagnostic({
|
|
822
|
+
event: 'query_assistant_blocked_unsupported_route_detail',
|
|
823
|
+
querySource: String(params.querySource),
|
|
824
|
+
messages,
|
|
825
|
+
assistantMessage: boundary.message,
|
|
826
|
+
turnCount,
|
|
827
|
+
boundaryKind: 'block',
|
|
828
|
+
repairPromptChars: 0,
|
|
829
|
+
continueAfterRepair: false,
|
|
830
|
+
})
|
|
831
|
+
const blockedMessage = createUnsupportedRouteFinalAnswerBlockedMessage()
|
|
832
|
+
yield blockedMessage
|
|
833
|
+
messages.push(blockedMessage)
|
|
834
|
+
return Terminal.completed()
|
|
835
|
+
}
|
|
836
|
+
if (
|
|
837
|
+
boundary.kind === 'pass' &&
|
|
838
|
+
shouldBlockStalePriorToolResultAnswer({
|
|
839
|
+
messages,
|
|
840
|
+
candidate: boundary.message,
|
|
841
|
+
})
|
|
842
|
+
) {
|
|
843
|
+
appendQueryAssistantDiagnostic({
|
|
844
|
+
event: 'query_assistant_blocked_stale_prior_tool_result',
|
|
845
|
+
querySource: String(params.querySource),
|
|
846
|
+
messages,
|
|
847
|
+
assistantMessage: boundary.message,
|
|
848
|
+
turnCount,
|
|
849
|
+
boundaryKind: 'block',
|
|
850
|
+
repairPromptChars: 0,
|
|
851
|
+
continueAfterRepair: false,
|
|
852
|
+
})
|
|
853
|
+
const blockedMessage = createStalePriorToolResultFinalAnswerBlockedMessage()
|
|
854
|
+
yield blockedMessage
|
|
855
|
+
messages.push(blockedMessage)
|
|
856
|
+
return Terminal.completed()
|
|
857
|
+
}
|
|
858
|
+
const publicDataRepairPrompt = boundary.kind === 'pass'
|
|
859
|
+
? buildPublicDataTerminalRepairPrompt({
|
|
860
|
+
messages,
|
|
861
|
+
candidate: boundary.message,
|
|
862
|
+
})
|
|
863
|
+
: undefined
|
|
864
|
+
if (publicDataRepairPrompt !== undefined) {
|
|
865
|
+
appendQueryAssistantDiagnostic({
|
|
866
|
+
event: 'query_assistant_withheld_for_repair',
|
|
867
|
+
querySource: String(params.querySource),
|
|
868
|
+
messages,
|
|
869
|
+
assistantMessage: boundary.message,
|
|
870
|
+
turnCount,
|
|
871
|
+
boundaryKind: boundary.kind,
|
|
872
|
+
repairPromptChars: publicDataRepairPrompt.length,
|
|
873
|
+
continueAfterRepair: true,
|
|
874
|
+
})
|
|
875
|
+
messages.push(boundary.message)
|
|
876
|
+
messages.push(
|
|
877
|
+
createUserMessage({
|
|
878
|
+
content: publicDataRepairPrompt,
|
|
879
|
+
isMeta: true,
|
|
880
|
+
}),
|
|
881
|
+
)
|
|
882
|
+
shouldContinueAfterRepairPrompt = true
|
|
883
|
+
break
|
|
884
|
+
}
|
|
885
|
+
if (
|
|
886
|
+
boundary.kind === 'pass' &&
|
|
887
|
+
shouldBlockEmergencyResultClaim({
|
|
888
|
+
messages,
|
|
889
|
+
candidate: boundary.message,
|
|
890
|
+
})
|
|
891
|
+
) {
|
|
892
|
+
appendQueryAssistantDiagnostic({
|
|
893
|
+
event: 'query_assistant_blocked_emergency_no_claim',
|
|
894
|
+
querySource: String(params.querySource),
|
|
895
|
+
messages,
|
|
896
|
+
assistantMessage: boundary.message,
|
|
897
|
+
turnCount,
|
|
898
|
+
boundaryKind: 'block',
|
|
899
|
+
repairPromptChars: 0,
|
|
900
|
+
continueAfterRepair: false,
|
|
901
|
+
})
|
|
902
|
+
const blockedMessage = createEmergencyNoClaimBlockedMessage()
|
|
903
|
+
yield blockedMessage
|
|
904
|
+
messages.push(blockedMessage)
|
|
905
|
+
return Terminal.completed()
|
|
906
|
+
}
|
|
907
|
+
if (
|
|
908
|
+
boundary.kind === 'pass' &&
|
|
909
|
+
shouldBlockFinalAnswerAfterUnavailableToolRepair({
|
|
910
|
+
messages,
|
|
911
|
+
candidate: boundary.message,
|
|
912
|
+
})
|
|
913
|
+
) {
|
|
914
|
+
appendQueryAssistantDiagnostic({
|
|
915
|
+
event: 'query_assistant_blocked_after_unavailable_tool_repair',
|
|
916
|
+
querySource: String(params.querySource),
|
|
917
|
+
messages,
|
|
918
|
+
assistantMessage: boundary.message,
|
|
919
|
+
turnCount,
|
|
920
|
+
boundaryKind: 'block',
|
|
921
|
+
repairPromptChars: 0,
|
|
922
|
+
continueAfterRepair: false,
|
|
923
|
+
})
|
|
924
|
+
const blockedMessage = createUnavailableToolFinalAnswerBlockedMessage(messages)
|
|
925
|
+
yield blockedMessage
|
|
926
|
+
messages.push(blockedMessage)
|
|
927
|
+
return Terminal.completed()
|
|
928
|
+
}
|
|
929
|
+
if (
|
|
930
|
+
boundary.kind === 'pass' &&
|
|
931
|
+
shouldBlockFinalAnswerAfterUnsupportedRouteRepair({
|
|
932
|
+
messages,
|
|
933
|
+
candidate: boundary.message,
|
|
934
|
+
})
|
|
935
|
+
) {
|
|
936
|
+
appendQueryAssistantDiagnostic({
|
|
937
|
+
event: 'query_assistant_blocked_after_unsupported_route_repair',
|
|
938
|
+
querySource: String(params.querySource),
|
|
939
|
+
messages,
|
|
940
|
+
assistantMessage: boundary.message,
|
|
941
|
+
turnCount,
|
|
942
|
+
boundaryKind: 'block',
|
|
943
|
+
repairPromptChars: 0,
|
|
944
|
+
continueAfterRepair: false,
|
|
945
|
+
})
|
|
946
|
+
const blockedMessage = createUnsupportedRouteFinalAnswerBlockedMessage()
|
|
947
|
+
yield blockedMessage
|
|
948
|
+
messages.push(blockedMessage)
|
|
949
|
+
return Terminal.completed()
|
|
950
|
+
}
|
|
951
|
+
if (
|
|
952
|
+
boundary.kind === 'pass' &&
|
|
953
|
+
shouldBlockToolUseAfterGenericPendingFinalAnswerRepair({
|
|
954
|
+
messages,
|
|
955
|
+
candidate: boundary.message,
|
|
956
|
+
})
|
|
957
|
+
) {
|
|
958
|
+
appendQueryAssistantDiagnostic({
|
|
959
|
+
event: 'query_assistant_blocked_after_final_repair',
|
|
960
|
+
querySource: String(params.querySource),
|
|
961
|
+
messages,
|
|
962
|
+
assistantMessage: boundary.message,
|
|
963
|
+
turnCount,
|
|
964
|
+
boundaryKind: 'block',
|
|
965
|
+
repairPromptChars: 0,
|
|
966
|
+
continueAfterRepair: false,
|
|
967
|
+
})
|
|
968
|
+
const blockedMessage = createGenericPendingFinalAnswerToolUseBlockedMessage()
|
|
969
|
+
yield blockedMessage
|
|
970
|
+
messages.push(blockedMessage)
|
|
971
|
+
return Terminal.completed()
|
|
972
|
+
}
|
|
973
|
+
appendQueryAssistantDiagnostic({
|
|
974
|
+
event: 'query_assistant_yield',
|
|
975
|
+
querySource: String(params.querySource),
|
|
976
|
+
messages,
|
|
977
|
+
assistantMessage: boundary.message,
|
|
978
|
+
turnCount,
|
|
979
|
+
boundaryKind: boundary.kind,
|
|
980
|
+
repairPromptChars: 0,
|
|
981
|
+
continueAfterRepair: false,
|
|
982
|
+
})
|
|
983
|
+
yield boundary.message
|
|
984
|
+
assistantMessage = boundary.message
|
|
985
|
+
messages.push(boundary.message)
|
|
986
|
+
if (boundary.kind === 'block') return Terminal.completed()
|
|
987
|
+
break
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (shouldContinueAfterRepairPrompt) continue
|
|
991
|
+
if (!assistantMessage) {
|
|
992
|
+
appendQueryCompletedWithoutAssistantDiagnostic({
|
|
993
|
+
querySource: String(params.querySource),
|
|
994
|
+
messages,
|
|
995
|
+
turnCount,
|
|
996
|
+
})
|
|
997
|
+
return Terminal.completed()
|
|
998
|
+
}
|
|
999
|
+
const toolUses = toolUseBlocks(assistantMessage)
|
|
1000
|
+
if (toolUses.length === 0) return Terminal.completed()
|
|
1001
|
+
if (params.toolUseContext.abortController.signal.aborted) {
|
|
1002
|
+
return Terminal.aborted_tools()
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
const toolResults: Message[] = []
|
|
1006
|
+
let shouldCompleteAfterHardGuard = false
|
|
1007
|
+
for (let blockIndex = 0; blockIndex < toolUses.length; blockIndex += 1) {
|
|
1008
|
+
const block = toolUses[blockIndex]
|
|
1009
|
+
if (!block) continue
|
|
1010
|
+
if (shouldBlockRepeatedPermissionDeniedTool(block, messages)) {
|
|
1011
|
+
toolResults.push(
|
|
1012
|
+
createPermissionDeniedContinuationGuardResult(block, assistantMessage),
|
|
1013
|
+
)
|
|
1014
|
+
} else if (shouldBlockRepeatedAdapterlessFind(block, messages)) {
|
|
1015
|
+
toolResults.push(
|
|
1016
|
+
createAdapterlessFindContinuationGuardResult(block, assistantMessage),
|
|
1017
|
+
)
|
|
1018
|
+
toolResults.push(createAdapterlessFindHardGuardFinalMessage())
|
|
1019
|
+
shouldCompleteAfterHardGuard = true
|
|
1020
|
+
} else {
|
|
1021
|
+
const blockResults = await runToolUseBlocks({
|
|
1022
|
+
blocks: [block],
|
|
1023
|
+
assistantMessage,
|
|
1024
|
+
messages: [...messages, ...toolResults],
|
|
1025
|
+
toolUseContext: params.toolUseContext,
|
|
1026
|
+
canUseTool: params.canUseTool,
|
|
1027
|
+
})
|
|
1028
|
+
toolResults.push(...blockResults.map(upgradeUnavailableToolResultMessage))
|
|
1029
|
+
const nextAdapterlessFind = toolUses
|
|
1030
|
+
.slice(blockIndex + 1)
|
|
1031
|
+
.find(isAdapterlessFindToolUse)
|
|
1032
|
+
if (
|
|
1033
|
+
isAdapterlessFindToolUse(block) &&
|
|
1034
|
+
nextAdapterlessFind !== undefined &&
|
|
1035
|
+
hasAdapterlessFindFailure([...messages, ...toolResults])
|
|
1036
|
+
) {
|
|
1037
|
+
toolResults.push(
|
|
1038
|
+
createAdapterlessFindContinuationGuardResult(nextAdapterlessFind, assistantMessage),
|
|
1039
|
+
)
|
|
1040
|
+
toolResults.push(createAdapterlessFindHardGuardFinalMessage())
|
|
1041
|
+
shouldCompleteAfterHardGuard = true
|
|
1042
|
+
break
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
for (const result of toolResults) {
|
|
1048
|
+
if (params.toolUseContext.abortController.signal.aborted) {
|
|
1049
|
+
return Terminal.aborted_tools()
|
|
1050
|
+
}
|
|
1051
|
+
messages.push(result)
|
|
1052
|
+
yield result
|
|
1053
|
+
}
|
|
1054
|
+
const domainGuardFinalAnswer = domainGuardFinalAnswerForToolResults(toolResults)
|
|
1055
|
+
if (domainGuardFinalAnswer !== undefined) {
|
|
1056
|
+
messages.push(domainGuardFinalAnswer)
|
|
1057
|
+
yield domainGuardFinalAnswer
|
|
1058
|
+
return Terminal.completed()
|
|
1059
|
+
}
|
|
1060
|
+
if (shouldCompleteAfterHardGuard) return Terminal.completed()
|
|
1061
|
+
const documentCompletionPrompt = buildDocumentCompletionPromptIfNeeded({
|
|
1062
|
+
messages,
|
|
1063
|
+
})
|
|
1064
|
+
if (documentCompletionPrompt !== undefined) {
|
|
1065
|
+
messages.push(
|
|
1066
|
+
createUserMessage({
|
|
1067
|
+
content: documentCompletionPrompt,
|
|
1068
|
+
isMeta: true,
|
|
1069
|
+
}),
|
|
1070
|
+
)
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
return Terminal.max_turns(turnCount)
|
|
1075
|
+
}
|