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
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod/v4'
|
|
2
|
+
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { existsSync } from 'node:fs'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { basename, join } from 'node:path'
|
|
2
6
|
import {
|
|
3
7
|
buildTool,
|
|
4
8
|
type Tool,
|
|
@@ -14,89 +18,77 @@ import {
|
|
|
14
18
|
import { getOrCreateUmmayaBridge } from '../../ipc/bridgeSingleton.js'
|
|
15
19
|
import { getOrCreatePendingCallRegistry } from '../../ipc/pendingCallSingleton.js'
|
|
16
20
|
import { dispatchPrimitive } from '../_shared/dispatchPrimitive.js'
|
|
17
|
-
import { validateKmaAviationToolChoice } from '../_shared/kmaAviationGuard.js'
|
|
18
|
-
import { validateKmaAnalysisToolChoice } from '../_shared/kmaAnalysisGuard.js'
|
|
19
|
-
import { validateNmcAedToolChoice } from '../_shared/nmcAedGuard.js'
|
|
20
21
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
applyDocumentVisualRenderGateToOutput,
|
|
23
|
+
extractDocumentToolResultPayload,
|
|
24
|
+
isDocumentVisualRenderFailedOutput,
|
|
25
|
+
renderDocumentToolResultIfPresent,
|
|
26
|
+
} from '../_shared/documentToolResultRender.js'
|
|
24
27
|
import { LookupPrimitive } from '../LookupPrimitive/LookupPrimitive.js'
|
|
25
28
|
import { ResolveLocationPrimitive } from '../ResolveLocationPrimitive/ResolveLocationPrimitive.js'
|
|
26
29
|
import { SubmitPrimitive } from '../SubmitPrimitive/SubmitPrimitive.js'
|
|
27
30
|
import { VerifyPrimitive } from '../VerifyPrimitive/VerifyPrimitive.js'
|
|
31
|
+
import { DocumentPrimitive } from '../DocumentPrimitive/DocumentPrimitive.js'
|
|
32
|
+
import { resolveDocumentPrimitiveTimeoutMs } from '../_shared/documentPrimitiveTimeout.js'
|
|
28
33
|
|
|
29
|
-
type AdapterPrimitive = 'find' | 'locate' | 'send' | 'check'
|
|
34
|
+
type AdapterPrimitive = 'find' | 'locate' | 'send' | 'check' | 'document'
|
|
30
35
|
|
|
31
36
|
type InputSchema = z.ZodType<{ [key: string]: unknown }>
|
|
32
37
|
|
|
33
|
-
const ROOT_PRIMITIVE_TOOL_NAMES = new Set([
|
|
34
|
-
|
|
35
|
-
'
|
|
36
|
-
'
|
|
38
|
+
const ROOT_PRIMITIVE_TOOL_NAMES = new Set([
|
|
39
|
+
'locate',
|
|
40
|
+
'find',
|
|
41
|
+
'check',
|
|
42
|
+
'send',
|
|
43
|
+
'document',
|
|
37
44
|
])
|
|
38
|
-
const
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
45
|
+
const DOCUMENT_TOOL_NAMES = new Set([
|
|
46
|
+
'document',
|
|
47
|
+
'document_inspect',
|
|
48
|
+
'document_extract',
|
|
49
|
+
'document_form_schema',
|
|
50
|
+
'document_copy_for_edit',
|
|
51
|
+
'document_apply_fill',
|
|
52
|
+
'document_apply_style',
|
|
53
|
+
'document_render',
|
|
54
|
+
'document_validate_public_form',
|
|
55
|
+
'document_save',
|
|
42
56
|
])
|
|
43
|
-
const
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'tago_bus_route_search',
|
|
47
|
-
'tago_bus_route_station_search',
|
|
48
|
-
'tago_bus_location_search',
|
|
57
|
+
const DOCUMENT_MUTATION_TOOL_NAMES = new Set([
|
|
58
|
+
'document_apply_fill',
|
|
59
|
+
'document_apply_style',
|
|
49
60
|
])
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
/(amos|활주로|rvr|runway|시정|visibility|공항기상관측|매분)/iu
|
|
57
|
-
const KMA_ANALYSIS_DATA_RE =
|
|
58
|
-
/(분석자료|이미\s*분석|고해상도\s*격자|객관분석|aws\s*객관|지도\s*자료|일기도|분석일기도|비구름|바람\s*흐름|synoptic|weather\s*chart|objective\s*analysis|high[-\s]?resolution|grid)/iu
|
|
59
|
-
const KMA_ANALYSIS_MAP_RE =
|
|
60
|
-
/(일기도|분석일기도|지도\s*자료|비구름|바람\s*흐름|synoptic|weather\s*chart)/iu
|
|
61
|
-
const KMA_ANALYSIS_POINT_RE =
|
|
62
|
-
/(주변|근처|특정지점|좌표|위도|경도|\blat\b|\blon\b|공항\s*주변)/iu
|
|
63
|
-
const KMA_LIFESTYLE_WEATHER_RE =
|
|
64
|
-
/(날씨|현재\s*기상|실황|관측|예보|기온|습도|풍속|지금\s*비|비\s*(와|오|올|내리)|우산|강수|소나기|산책|퇴근|current\s+weather|forecast|rain|umbrella|precipitation|temperature)/iu
|
|
65
|
-
const KMA_LIFESTYLE_WEATHER_TOOL_NAMES = new Set([
|
|
66
|
-
'kma_current_observation',
|
|
67
|
-
'kma_ultra_short_term_forecast',
|
|
68
|
-
'kma_short_term_forecast',
|
|
61
|
+
const DOCUMENT_ARTIFACT_FOLLOWUP_TOOL_NAMES = new Set([
|
|
62
|
+
'document_apply_fill',
|
|
63
|
+
'document_apply_style',
|
|
64
|
+
'document_render',
|
|
65
|
+
'document_validate_public_form',
|
|
66
|
+
'document_save',
|
|
69
67
|
])
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const AED_REQUEST_RE = /(aed|자동심장|심장충격|제세동)/iu
|
|
95
|
-
const EMERGENCY_REQUEST_RE = /(응급|응급실|\ber\b|emergency)/iu
|
|
96
|
-
const MEDICAL_COLLAPSE_RE =
|
|
97
|
-
/(사람이\s*쓰러|쓰러졌|쓰러져|의식\s*잃|의식을\s*잃|심정지|호흡이\s*없|숨을\s*안|collapsed|unconscious|cardiac\s*arrest)/iu
|
|
98
|
-
const TRAFFIC_HAZARD_RE =
|
|
99
|
-
/(교통사고|사고\s*위험|사고다발|위험\s*(구간|도로|지점)|어린이보호구역|보호구역|도로\s*구간|accident|hazard|hotspot)/iu
|
|
68
|
+
// Purely mechanical pipeline steps that carry no user-meaningful change — only
|
|
69
|
+
// these are hidden on success. Substantive mutations (apply_fill / apply_style)
|
|
70
|
+
// now render their inline structural diff immediately (per-mutation trigger),
|
|
71
|
+
// the same way Claude Code shows a diff the moment an edit lands. See
|
|
72
|
+
// specs/2802-public-doc-harness/deep-research-migration-document-render.md.
|
|
73
|
+
const MECHANICAL_DOCUMENT_TOOL_NAMES = new Set(['document_copy_for_edit'])
|
|
74
|
+
const SAFE_DOCUMENT_ARTIFACT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/u
|
|
75
|
+
const EXPLICIT_DOCUMENT_ARTIFACT_MARKER_RE =
|
|
76
|
+
/(?:^|[\s"'`(])(?:artifact_id|artifact\s*id|artifact|아티팩트)\s*[:=]?\s*([A-Za-z0-9][A-Za-z0-9_.-]{0,127})(?=$|[^A-Za-z0-9_.-])/iu
|
|
77
|
+
const EXPLICIT_DOCUMENT_ARTIFACT_PREFIX_RE =
|
|
78
|
+
/(?:^|[\s"'`(])((?:source|working|derivative|render|export|viewport)-[A-Za-z0-9][A-Za-z0-9_.-]{0,127})(?=$|[^A-Za-z0-9_.-])/u
|
|
79
|
+
const DOCUMENT_FORMAT_RE = /\b(?:hwpx|hwp|docx|pdf|xlsx|pptx)\b/iu
|
|
80
|
+
const DOWNLOADS_FOLDER_PATH_RE = /(?:^|[\\/])Downloads$/u
|
|
81
|
+
const DOWNLOADS_PATH_SEGMENT_RE = /(?:^|[\\/])Downloads[\\/](?<tail>.+)$/iu
|
|
82
|
+
const DOCUMENT_EXTENSION_RE = /\.(?:hwpx|hwp|docx|pdf|xlsx|pptx)$/iu
|
|
83
|
+
const DOCUMENT_EXTENSION_TRAILING_PUNCT_RE =
|
|
84
|
+
/(\.(?:hwpx|hwp|docx|pdf|xlsx|pptx))[.。.]+$/iu
|
|
85
|
+
const EXPLICIT_LOCAL_DOCUMENT_PATH_RE =
|
|
86
|
+
/(?:^|[\s"'`(])(?<path>(?:~|\/|\.{1,2}\/|[A-Za-z]:\\)[^\n\r"'`]*?\.(?:hwpx|hwp|docx|pdf|xlsx|pptx))(?=$|[\s"'`),,。]|[가-힣])/giu
|
|
87
|
+
const HWPX_TEXT_TARGET_ALIAS_RE =
|
|
88
|
+
/^\/?hwp(?:x)?[-_/]text(?:[-_](?<indexA>\d+)|\[(?<indexB>\d+)\])(?:\/text\(\))?$/iu
|
|
89
|
+
const HWPX_TEXT_TARGET_RE = /^\/hwpx\/text\[\d+\]$/u
|
|
90
|
+
const HWPX_BLOCK_TABLE_CELL_TARGET_RE =
|
|
91
|
+
/^\/hwpx\/\[(?<tableId>hwpx-table-\d{3})\]\/cells\[(?<row>\d+)\]\[(?<column>\d+)\]$/u
|
|
100
92
|
|
|
101
93
|
const fallbackInputSchema = z.object({}).passthrough() as InputSchema
|
|
102
94
|
|
|
@@ -110,6 +102,680 @@ function asJsonObject(value: unknown): JsonObject {
|
|
|
110
102
|
return isJsonObject(value) ? value : {}
|
|
111
103
|
}
|
|
112
104
|
|
|
105
|
+
function documentToolUseAction(toolId: string): string {
|
|
106
|
+
switch (toolId) {
|
|
107
|
+
case 'document':
|
|
108
|
+
return 'Prepare document workflow'
|
|
109
|
+
case 'document_inspect':
|
|
110
|
+
return 'Inspect document form'
|
|
111
|
+
case 'document_extract':
|
|
112
|
+
return 'Read document content'
|
|
113
|
+
case 'document_form_schema':
|
|
114
|
+
return 'Map document fields'
|
|
115
|
+
case 'document_apply_fill':
|
|
116
|
+
return 'Fill document fields'
|
|
117
|
+
case 'document_apply_style':
|
|
118
|
+
return 'Apply document formatting'
|
|
119
|
+
case 'document_render':
|
|
120
|
+
return 'Render document diff'
|
|
121
|
+
case 'document_validate_public_form':
|
|
122
|
+
return 'Validate public-form rules'
|
|
123
|
+
case 'document_save':
|
|
124
|
+
return 'Save document'
|
|
125
|
+
default:
|
|
126
|
+
return 'Process document'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function documentToolUseTarget(input: Record<string, unknown>): string | undefined {
|
|
131
|
+
const document = asJsonObject(input.document)
|
|
132
|
+
const path =
|
|
133
|
+
typeof document.path === 'string'
|
|
134
|
+
? document.path
|
|
135
|
+
: typeof input.path === 'string'
|
|
136
|
+
? input.path
|
|
137
|
+
: undefined
|
|
138
|
+
if (path !== undefined && path.trim()) {
|
|
139
|
+
return basename(path)
|
|
140
|
+
}
|
|
141
|
+
if (
|
|
142
|
+
typeof document.artifact_id === 'string' ||
|
|
143
|
+
typeof input.artifact_id === 'string'
|
|
144
|
+
) {
|
|
145
|
+
return 'current document'
|
|
146
|
+
}
|
|
147
|
+
return undefined
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function renderDocumentToolUseMessage(
|
|
151
|
+
toolId: string,
|
|
152
|
+
input: Record<string, unknown>,
|
|
153
|
+
): string | null {
|
|
154
|
+
if (MECHANICAL_DOCUMENT_TOOL_NAMES.has(toolId)) return null
|
|
155
|
+
const action = documentToolUseAction(toolId)
|
|
156
|
+
const target = documentToolUseTarget(input)
|
|
157
|
+
return target === undefined ? action : `${action}: ${target}`
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function messageInnerRecord(message: unknown): JsonObject {
|
|
161
|
+
return asJsonObject(asJsonObject(message).message)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function messageRole(message: unknown): string | undefined {
|
|
165
|
+
const inner = messageInnerRecord(message)
|
|
166
|
+
const outer = asJsonObject(message)
|
|
167
|
+
if (typeof inner.role === 'string') return inner.role
|
|
168
|
+
if (typeof outer.role === 'string') return outer.role
|
|
169
|
+
return typeof outer.type === 'string' ? outer.type : undefined
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function messageContent(message: unknown): unknown {
|
|
173
|
+
const inner = messageInnerRecord(message)
|
|
174
|
+
return inner.content ?? asJsonObject(message).content
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isToolResultContent(content: unknown): boolean {
|
|
178
|
+
if (!Array.isArray(content)) return false
|
|
179
|
+
return content.some(block => asJsonObject(block).type === 'tool_result')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function textFromMessageContent(content: unknown): string {
|
|
183
|
+
if (typeof content === 'string') return content
|
|
184
|
+
if (!Array.isArray(content)) return ''
|
|
185
|
+
return content
|
|
186
|
+
.map(block => {
|
|
187
|
+
if (typeof block === 'string') return block
|
|
188
|
+
const record = asJsonObject(block)
|
|
189
|
+
if (record.type === 'tool_result') return ''
|
|
190
|
+
if (typeof record.text === 'string') return record.text
|
|
191
|
+
if (typeof record.content === 'string') return record.content
|
|
192
|
+
return ''
|
|
193
|
+
})
|
|
194
|
+
.filter(Boolean)
|
|
195
|
+
.join('\n')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function latestPlainUserText(messages: readonly unknown[]): string {
|
|
199
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
200
|
+
const content = messageContent(messages[idx])
|
|
201
|
+
if (messageRole(messages[idx]) !== 'user' || isToolResultContent(content)) {
|
|
202
|
+
continue
|
|
203
|
+
}
|
|
204
|
+
const text = textFromMessageContent(content).trim()
|
|
205
|
+
if (text) return text
|
|
206
|
+
}
|
|
207
|
+
return ''
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function safeDocumentArtifactId(value: unknown): string | undefined {
|
|
211
|
+
if (typeof value !== 'string') return undefined
|
|
212
|
+
const candidate = value.trim()
|
|
213
|
+
return SAFE_DOCUMENT_ARTIFACT_ID_RE.test(candidate) ? candidate : undefined
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function explicitDocumentArtifactIdFromText(text: string): string | undefined {
|
|
217
|
+
const marked = EXPLICIT_DOCUMENT_ARTIFACT_MARKER_RE.exec(text)?.[1]
|
|
218
|
+
const markedArtifactId = safeDocumentArtifactId(marked)
|
|
219
|
+
if (markedArtifactId) return markedArtifactId
|
|
220
|
+
const prefixed = EXPLICIT_DOCUMENT_ARTIFACT_PREFIX_RE.exec(text)?.[1]
|
|
221
|
+
return safeDocumentArtifactId(prefixed)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function parseJsonObject(value: unknown): JsonObject | undefined {
|
|
225
|
+
if (isJsonObject(value)) return value
|
|
226
|
+
if (typeof value !== 'string' || !value.trim()) return undefined
|
|
227
|
+
try {
|
|
228
|
+
return asJsonObject(JSON.parse(value))
|
|
229
|
+
} catch {
|
|
230
|
+
return undefined
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function documentToolResultPayload(message: unknown): JsonObject | undefined {
|
|
235
|
+
const directResult = asJsonObject(asJsonObject(message).toolUseResult).result
|
|
236
|
+
if (isJsonObject(directResult)) return directResult
|
|
237
|
+
|
|
238
|
+
const content = messageContent(message)
|
|
239
|
+
if (!Array.isArray(content)) return undefined
|
|
240
|
+
for (const block of content) {
|
|
241
|
+
const record = asJsonObject(block)
|
|
242
|
+
if (record.type !== 'tool_result') continue
|
|
243
|
+
const parsed = parseJsonObject(record.content)
|
|
244
|
+
const nestedResult = asJsonObject(parsed).result
|
|
245
|
+
if (isJsonObject(nestedResult)) return nestedResult
|
|
246
|
+
}
|
|
247
|
+
return undefined
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function latestDocumentArtifactRef(
|
|
251
|
+
messages: readonly unknown[],
|
|
252
|
+
options: {
|
|
253
|
+
toolIds: ReadonlySet<string>
|
|
254
|
+
artifactPrefix: string
|
|
255
|
+
},
|
|
256
|
+
): string | undefined {
|
|
257
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
258
|
+
const payload = documentToolResultPayload(messages[idx])
|
|
259
|
+
if (!payload) continue
|
|
260
|
+
if (
|
|
261
|
+
typeof payload.tool_id !== 'string' ||
|
|
262
|
+
!options.toolIds.has(payload.tool_id)
|
|
263
|
+
) continue
|
|
264
|
+
if (payload.status !== 'ok') continue
|
|
265
|
+
const refs = Array.isArray(payload.artifact_refs) ? payload.artifact_refs : []
|
|
266
|
+
for (let refIdx = refs.length - 1; refIdx >= 0; refIdx -= 1) {
|
|
267
|
+
const ref = safeDocumentArtifactId(refs[refIdx])
|
|
268
|
+
if (ref?.startsWith(options.artifactPrefix)) return ref
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return undefined
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function latestMutableDocumentArtifactRef(messages: readonly unknown[]): string | undefined {
|
|
275
|
+
return (
|
|
276
|
+
latestDocumentArtifactRef(messages, {
|
|
277
|
+
toolIds: DOCUMENT_MUTATION_TOOL_NAMES,
|
|
278
|
+
artifactPrefix: 'derivative-',
|
|
279
|
+
}) ??
|
|
280
|
+
latestDocumentArtifactRef(messages, {
|
|
281
|
+
toolIds: new Set([
|
|
282
|
+
'document_copy_for_edit',
|
|
283
|
+
'document_apply_fill',
|
|
284
|
+
'document_apply_style',
|
|
285
|
+
]),
|
|
286
|
+
artifactPrefix: 'working-',
|
|
287
|
+
})
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function shouldHideSuccessfulIntermediateDocumentResult(output: unknown): boolean {
|
|
292
|
+
const payload = extractDocumentToolResultPayload(output)
|
|
293
|
+
return (
|
|
294
|
+
payload !== null &&
|
|
295
|
+
payload.status === 'ok' &&
|
|
296
|
+
typeof payload.tool_id === 'string' &&
|
|
297
|
+
MECHANICAL_DOCUMENT_TOOL_NAMES.has(payload.tool_id)
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function documentCorrelationId(value: unknown): string {
|
|
302
|
+
return typeof value === 'string' && value.trim()
|
|
303
|
+
? value.trim()
|
|
304
|
+
: `document-render-${randomUUID()}`
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function documentFormatFromText(text: string): string | undefined {
|
|
308
|
+
return DOCUMENT_FORMAT_RE.exec(text)?.[0]?.toLowerCase()
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function documentFormatFromPath(path: string): string | undefined {
|
|
312
|
+
const match = /\.(hwpx|hwp|docx|pdf|xlsx|pptx)$/iu.exec(path.trim())
|
|
313
|
+
return match?.[1]?.toLowerCase()
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function inferredDownloadsDocumentPath(text: string): string | undefined {
|
|
317
|
+
if (!/(다운로드\s*폴더|downloads)/iu.test(text)) return undefined
|
|
318
|
+
const formatMatch = DOCUMENT_FORMAT_RE.exec(text)
|
|
319
|
+
const format = formatMatch?.[0]?.toLowerCase()
|
|
320
|
+
if (!format || formatMatch === null) return undefined
|
|
321
|
+
const beforeFormat = text.slice(0, formatMatch.index).trim()
|
|
322
|
+
const nameMatch =
|
|
323
|
+
/(?:다운로드\s*폴더|downloads)(?:에)?\s*(?:있는|의)?\s*(?<name>.+)$/iu.exec(
|
|
324
|
+
beforeFormat,
|
|
325
|
+
)
|
|
326
|
+
const rawName = nameMatch?.groups?.name?.trim()
|
|
327
|
+
if (!rawName) return undefined
|
|
328
|
+
const fileName = rawName
|
|
329
|
+
.replace(/[\\/:*?"<>|]/gu, ' ')
|
|
330
|
+
.replace(/\s+/gu, ' ')
|
|
331
|
+
.replace(/[.。.]+$/gu, '')
|
|
332
|
+
.trim()
|
|
333
|
+
if (!fileName) return undefined
|
|
334
|
+
return join(homedir(), 'Downloads', `${fileName}.${format}`)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function isDownloadsFolderLikePath(value: unknown): boolean {
|
|
338
|
+
if (typeof value !== 'string') return false
|
|
339
|
+
const normalized = value
|
|
340
|
+
.trim()
|
|
341
|
+
.replace(/^['"`]+|['"`]+$/gu, '')
|
|
342
|
+
.replace(/[\\/]+$/u, '')
|
|
343
|
+
.replace(/\.$/u, '')
|
|
344
|
+
return DOWNLOADS_FOLDER_PATH_RE.test(normalized)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function normalizeDownloadsDocumentPath(value: unknown): string | undefined {
|
|
348
|
+
if (typeof value !== 'string') return undefined
|
|
349
|
+
const cleaned = value
|
|
350
|
+
.trim()
|
|
351
|
+
.replace(/^['"`]+|['"`]+$/gu, '')
|
|
352
|
+
.replace(DOCUMENT_EXTENSION_TRAILING_PUNCT_RE, '$1')
|
|
353
|
+
if (!cleaned) return undefined
|
|
354
|
+
const downloadsPathMatch = DOWNLOADS_PATH_SEGMENT_RE.exec(cleaned)
|
|
355
|
+
if (DOCUMENT_EXTENSION_RE.test(cleaned) && downloadsPathMatch) {
|
|
356
|
+
const homeDownloads = `${homedir()}/Downloads/`
|
|
357
|
+
if (cleaned.startsWith(homeDownloads)) return cleaned
|
|
358
|
+
const tail = downloadsPathMatch.groups?.tail
|
|
359
|
+
if (!tail || tail.includes('..')) return undefined
|
|
360
|
+
const parts = tail.split(/[\\/]+/u).filter(Boolean)
|
|
361
|
+
if (parts.length === 0) return undefined
|
|
362
|
+
return join(homedir(), 'Downloads', ...parts)
|
|
363
|
+
}
|
|
364
|
+
return undefined
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function cleanUserDocumentPath(value: string): string {
|
|
368
|
+
return value
|
|
369
|
+
.trim()
|
|
370
|
+
.replace(/^['"`]+|['"`]+$/gu, '')
|
|
371
|
+
.replace(DOCUMENT_EXTENSION_TRAILING_PUNCT_RE, '$1')
|
|
372
|
+
.replace(/^~/u, homedir())
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function existingUserDocumentPathsFromText(text: string): string[] {
|
|
376
|
+
const paths: string[] = []
|
|
377
|
+
for (const match of text.matchAll(EXPLICIT_LOCAL_DOCUMENT_PATH_RE)) {
|
|
378
|
+
const rawPath = match.groups?.path
|
|
379
|
+
if (typeof rawPath !== 'string') continue
|
|
380
|
+
const candidate = cleanUserDocumentPath(rawPath)
|
|
381
|
+
if (!candidate || !DOCUMENT_EXTENSION_RE.test(candidate)) continue
|
|
382
|
+
if (!existsSync(candidate)) continue
|
|
383
|
+
if (!paths.includes(candidate)) paths.push(candidate)
|
|
384
|
+
}
|
|
385
|
+
return paths
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function normalizeExactUserDocumentPathInput(
|
|
389
|
+
input: Record<string, unknown>,
|
|
390
|
+
document: JsonObject,
|
|
391
|
+
messages: readonly unknown[],
|
|
392
|
+
): Record<string, unknown> | undefined {
|
|
393
|
+
if (document.artifact_id !== undefined) return undefined
|
|
394
|
+
const userPaths = existingUserDocumentPathsFromText(latestPlainUserText(messages))
|
|
395
|
+
if (userPaths.length !== 1) return undefined
|
|
396
|
+
const lockedPath = userPaths[0]
|
|
397
|
+
if (lockedPath === undefined) return undefined
|
|
398
|
+
|
|
399
|
+
const currentPath =
|
|
400
|
+
typeof document.path === 'string' ? cleanUserDocumentPath(document.path) : undefined
|
|
401
|
+
if (currentPath !== undefined && existsSync(currentPath)) return undefined
|
|
402
|
+
if (
|
|
403
|
+
currentPath !== undefined &&
|
|
404
|
+
basename(currentPath) !== basename(lockedPath)
|
|
405
|
+
) {
|
|
406
|
+
return undefined
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return {
|
|
410
|
+
...input,
|
|
411
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
412
|
+
document: {
|
|
413
|
+
...document,
|
|
414
|
+
path: lockedPath,
|
|
415
|
+
expected_format:
|
|
416
|
+
documentFormatFromPath(lockedPath) ??
|
|
417
|
+
document.expected_format ??
|
|
418
|
+
input.expected_format ??
|
|
419
|
+
documentFormatFromText(latestPlainUserText(messages)),
|
|
420
|
+
},
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function normalizeDocumentInspectPathInput(
|
|
425
|
+
toolId: string,
|
|
426
|
+
input: Record<string, unknown>,
|
|
427
|
+
document: JsonObject,
|
|
428
|
+
messages: readonly unknown[],
|
|
429
|
+
): Record<string, unknown> | undefined {
|
|
430
|
+
if (toolId !== 'document_inspect' && toolId !== 'document') return undefined
|
|
431
|
+
const path = document.path ?? input.path
|
|
432
|
+
const userText = latestPlainUserText(messages)
|
|
433
|
+
const cleanedPath = typeof path === 'string' ? cleanUserDocumentPath(path) : undefined
|
|
434
|
+
if (
|
|
435
|
+
cleanedPath !== undefined &&
|
|
436
|
+
DOCUMENT_EXTENSION_RE.test(cleanedPath) &&
|
|
437
|
+
existsSync(cleanedPath)
|
|
438
|
+
) {
|
|
439
|
+
return undefined
|
|
440
|
+
}
|
|
441
|
+
const normalizedDownloadsPath = normalizeDownloadsDocumentPath(path)
|
|
442
|
+
const inferredPath = isDownloadsFolderLikePath(path)
|
|
443
|
+
? inferredDownloadsDocumentPath(userText)
|
|
444
|
+
: undefined
|
|
445
|
+
const inferredDownloadsPath = inferredDownloadsDocumentPath(userText)
|
|
446
|
+
const shouldPreferUserTextDownloadsPath =
|
|
447
|
+
normalizedDownloadsPath !== undefined &&
|
|
448
|
+
inferredDownloadsPath !== undefined &&
|
|
449
|
+
normalizedDownloadsPath !== inferredDownloadsPath
|
|
450
|
+
const normalizedPath =
|
|
451
|
+
(shouldPreferUserTextDownloadsPath ? inferredDownloadsPath : undefined) ??
|
|
452
|
+
(normalizedDownloadsPath !== undefined && existsSync(normalizedDownloadsPath)
|
|
453
|
+
? normalizedDownloadsPath
|
|
454
|
+
: undefined) ??
|
|
455
|
+
(inferredPath !== undefined && existsSync(inferredPath) ? inferredPath : undefined) ??
|
|
456
|
+
(inferredDownloadsPath !== undefined && existsSync(inferredDownloadsPath)
|
|
457
|
+
? inferredDownloadsPath
|
|
458
|
+
: undefined) ??
|
|
459
|
+
inferredDownloadsPath ??
|
|
460
|
+
normalizedDownloadsPath ??
|
|
461
|
+
inferredPath
|
|
462
|
+
if (!normalizedPath) return undefined
|
|
463
|
+
return {
|
|
464
|
+
...input,
|
|
465
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
466
|
+
document: {
|
|
467
|
+
...document,
|
|
468
|
+
path: normalizedPath,
|
|
469
|
+
expected_format:
|
|
470
|
+
documentFormatFromPath(normalizedPath) ??
|
|
471
|
+
document.expected_format ??
|
|
472
|
+
input.expected_format ??
|
|
473
|
+
documentFormatFromText(userText),
|
|
474
|
+
},
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function normalizeDocumentPathExpectedFormatInput(
|
|
479
|
+
input: Record<string, unknown>,
|
|
480
|
+
document: JsonObject,
|
|
481
|
+
): Record<string, unknown> | undefined {
|
|
482
|
+
if (document.artifact_id !== undefined) return undefined
|
|
483
|
+
if (typeof document.path !== 'string') return undefined
|
|
484
|
+
const normalizedPath = cleanUserDocumentPath(document.path)
|
|
485
|
+
const pathFormat = documentFormatFromPath(normalizedPath)
|
|
486
|
+
if (pathFormat === undefined) return undefined
|
|
487
|
+
const currentFormat =
|
|
488
|
+
typeof document.expected_format === 'string'
|
|
489
|
+
? document.expected_format.toLowerCase()
|
|
490
|
+
: undefined
|
|
491
|
+
if (currentFormat === pathFormat && document.path === normalizedPath) {
|
|
492
|
+
return undefined
|
|
493
|
+
}
|
|
494
|
+
return {
|
|
495
|
+
...input,
|
|
496
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
497
|
+
document: {
|
|
498
|
+
...document,
|
|
499
|
+
path: normalizedPath,
|
|
500
|
+
expected_format: pathFormat,
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function normalizeHwpxTextTargetPath(value: unknown): string | undefined {
|
|
506
|
+
if (typeof value !== 'string') return undefined
|
|
507
|
+
const targetPath = value.trim()
|
|
508
|
+
const match = HWPX_TEXT_TARGET_ALIAS_RE.exec(targetPath)
|
|
509
|
+
const rawIndex = match?.groups?.indexA ?? match?.groups?.indexB
|
|
510
|
+
if (!rawIndex) return undefined
|
|
511
|
+
return `/hwpx/text[${Number(rawIndex)}]`
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function latestDocumentFieldPaths(messages: readonly unknown[]): Set<string> | undefined {
|
|
515
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
516
|
+
const payload = documentToolResultPayload(messages[idx])
|
|
517
|
+
const extraction = asJsonObject(payload?.extraction)
|
|
518
|
+
const fields = Array.isArray(extraction.fields) ? extraction.fields : []
|
|
519
|
+
const paths = new Set<string>()
|
|
520
|
+
for (const field of fields) {
|
|
521
|
+
const path = asJsonObject(field).path
|
|
522
|
+
if (typeof path === 'string' && path.trim()) {
|
|
523
|
+
paths.add(path.trim())
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (paths.size > 0) return paths
|
|
527
|
+
}
|
|
528
|
+
return undefined
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function latestDocumentTableCellFieldAliases(
|
|
532
|
+
messages: readonly unknown[],
|
|
533
|
+
): Map<string, string> | undefined {
|
|
534
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
535
|
+
const payload = documentToolResultPayload(messages[idx])
|
|
536
|
+
const extraction = asJsonObject(payload?.extraction)
|
|
537
|
+
const tables = Array.isArray(extraction.tables) ? extraction.tables : []
|
|
538
|
+
const aliases = new Map<string, string>()
|
|
539
|
+
for (const tableValue of tables) {
|
|
540
|
+
const table = asJsonObject(tableValue)
|
|
541
|
+
const blockId = table.block_id
|
|
542
|
+
const cells = Array.isArray(table.cells) ? table.cells : []
|
|
543
|
+
if (typeof blockId !== 'string' || !blockId.trim()) continue
|
|
544
|
+
for (const cellValue of cells) {
|
|
545
|
+
const cell = asJsonObject(cellValue)
|
|
546
|
+
const rowIndex = cell.row_index
|
|
547
|
+
const columnIndex = cell.column_index
|
|
548
|
+
const fieldPath = cell.field_path
|
|
549
|
+
if (
|
|
550
|
+
typeof rowIndex !== 'number' ||
|
|
551
|
+
typeof columnIndex !== 'number' ||
|
|
552
|
+
typeof fieldPath !== 'string' ||
|
|
553
|
+
!fieldPath.trim()
|
|
554
|
+
) {
|
|
555
|
+
continue
|
|
556
|
+
}
|
|
557
|
+
aliases.set(
|
|
558
|
+
`/hwpx/[${blockId.trim()}]/cells[${rowIndex}][${columnIndex}]`,
|
|
559
|
+
fieldPath.trim(),
|
|
560
|
+
)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
if (aliases.size > 0) return aliases
|
|
564
|
+
}
|
|
565
|
+
return undefined
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function normalizeHwpxTableCellTargetPath(
|
|
569
|
+
value: unknown,
|
|
570
|
+
aliases: ReadonlyMap<string, string> | undefined,
|
|
571
|
+
): string | undefined {
|
|
572
|
+
if (typeof value !== 'string') return undefined
|
|
573
|
+
const targetPath = value.trim()
|
|
574
|
+
if (!HWPX_BLOCK_TABLE_CELL_TARGET_RE.test(targetPath)) return undefined
|
|
575
|
+
return aliases?.get(targetPath)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function normalizeDocumentPatchTargetPaths(
|
|
579
|
+
input: Record<string, unknown>,
|
|
580
|
+
messages: readonly unknown[],
|
|
581
|
+
): Record<string, unknown> {
|
|
582
|
+
const patches = input.patches
|
|
583
|
+
if (!Array.isArray(patches)) return input
|
|
584
|
+
|
|
585
|
+
const fieldPaths = latestDocumentFieldPaths(messages)
|
|
586
|
+
const tableCellAliases = latestDocumentTableCellFieldAliases(messages)
|
|
587
|
+
let changed = false
|
|
588
|
+
const normalizedPatches = patches.flatMap(patch => {
|
|
589
|
+
if (!isJsonObject(patch)) return patch
|
|
590
|
+
const rawTargetPath =
|
|
591
|
+
typeof patch.target_path === 'string' ? patch.target_path.trim() : patch.target_path
|
|
592
|
+
const isHwpxTableCellTarget =
|
|
593
|
+
typeof rawTargetPath === 'string' && HWPX_BLOCK_TABLE_CELL_TARGET_RE.test(rawTargetPath)
|
|
594
|
+
const normalizedTargetPath =
|
|
595
|
+
normalizeHwpxTextTargetPath(patch.target_path) ??
|
|
596
|
+
normalizeHwpxTableCellTargetPath(patch.target_path, tableCellAliases)
|
|
597
|
+
const targetPath =
|
|
598
|
+
normalizedTargetPath ?? (
|
|
599
|
+
typeof rawTargetPath === 'string' ? rawTargetPath : patch.target_path
|
|
600
|
+
)
|
|
601
|
+
if (
|
|
602
|
+
isHwpxTableCellTarget &&
|
|
603
|
+
tableCellAliases !== undefined &&
|
|
604
|
+
normalizedTargetPath === undefined
|
|
605
|
+
) {
|
|
606
|
+
changed = true
|
|
607
|
+
return []
|
|
608
|
+
}
|
|
609
|
+
if (
|
|
610
|
+
typeof targetPath === 'string' &&
|
|
611
|
+
fieldPaths !== undefined &&
|
|
612
|
+
HWPX_TEXT_TARGET_RE.test(targetPath) &&
|
|
613
|
+
!fieldPaths.has(targetPath)
|
|
614
|
+
) {
|
|
615
|
+
changed = true
|
|
616
|
+
return []
|
|
617
|
+
}
|
|
618
|
+
if (
|
|
619
|
+
normalizedTargetPath === undefined ||
|
|
620
|
+
normalizedTargetPath === patch.target_path
|
|
621
|
+
) {
|
|
622
|
+
return patch
|
|
623
|
+
}
|
|
624
|
+
changed = true
|
|
625
|
+
return {
|
|
626
|
+
...patch,
|
|
627
|
+
target_path: normalizedTargetPath,
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
if (normalizedPatches.length === 0) return input
|
|
632
|
+
return changed ? { ...input, patches: normalizedPatches } : input
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function normalizeDocumentPrimitiveInstructionInput(
|
|
636
|
+
toolId: string,
|
|
637
|
+
input: Record<string, unknown>,
|
|
638
|
+
messages: readonly unknown[],
|
|
639
|
+
): Record<string, unknown> {
|
|
640
|
+
if (toolId !== 'document') return input
|
|
641
|
+
if (Array.isArray(input.patches) && input.patches.length > 0) return input
|
|
642
|
+
|
|
643
|
+
const userText = latestPlainUserText(messages)
|
|
644
|
+
if (!userText) return input
|
|
645
|
+
|
|
646
|
+
const instruction =
|
|
647
|
+
typeof input.instruction === 'string' ? input.instruction.trim() : ''
|
|
648
|
+
if (instruction.includes(userText)) return input
|
|
649
|
+
|
|
650
|
+
return {
|
|
651
|
+
...input,
|
|
652
|
+
instruction: instruction
|
|
653
|
+
? `${instruction}\n\nOriginal user request:\n${userText}`
|
|
654
|
+
: userText,
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
export function normalizeExplicitDocumentArtifactInput(
|
|
659
|
+
toolId: string,
|
|
660
|
+
input: Record<string, unknown>,
|
|
661
|
+
messages: readonly unknown[],
|
|
662
|
+
): Record<string, unknown> {
|
|
663
|
+
if (!DOCUMENT_TOOL_NAMES.has(toolId)) return input
|
|
664
|
+
|
|
665
|
+
const instructionInput = normalizeDocumentPrimitiveInstructionInput(
|
|
666
|
+
toolId,
|
|
667
|
+
input,
|
|
668
|
+
messages,
|
|
669
|
+
)
|
|
670
|
+
const normalizedPatchInput = normalizeDocumentPatchTargetPaths(
|
|
671
|
+
instructionInput,
|
|
672
|
+
messages,
|
|
673
|
+
)
|
|
674
|
+
const { artifact_id: topLevelArtifactIdRaw, ...withoutTopLevelArtifactId } =
|
|
675
|
+
normalizedPatchInput
|
|
676
|
+
const document = asJsonObject(normalizedPatchInput.document)
|
|
677
|
+
const normalizedExactUserPathInput = normalizeExactUserDocumentPathInput(
|
|
678
|
+
normalizedPatchInput,
|
|
679
|
+
document,
|
|
680
|
+
messages,
|
|
681
|
+
)
|
|
682
|
+
if (normalizedExactUserPathInput) return normalizedExactUserPathInput
|
|
683
|
+
const normalizedInspectPathInput = normalizeDocumentInspectPathInput(
|
|
684
|
+
toolId,
|
|
685
|
+
normalizedPatchInput,
|
|
686
|
+
document,
|
|
687
|
+
messages,
|
|
688
|
+
)
|
|
689
|
+
if (normalizedInspectPathInput) return normalizedInspectPathInput
|
|
690
|
+
const { path: _documentPath, ...documentWithoutPath } = document
|
|
691
|
+
const existingDocumentArtifactId = safeDocumentArtifactId(document.artifact_id)
|
|
692
|
+
const inputArtifactId =
|
|
693
|
+
existingDocumentArtifactId ?? safeDocumentArtifactId(topLevelArtifactIdRaw)
|
|
694
|
+
const hasExtractionArtifactId = inputArtifactId?.startsWith('document-intake-') === true
|
|
695
|
+
|
|
696
|
+
if (DOCUMENT_ARTIFACT_FOLLOWUP_TOOL_NAMES.has(toolId)) {
|
|
697
|
+
const artifactId = latestMutableDocumentArtifactRef(messages)
|
|
698
|
+
if (
|
|
699
|
+
artifactId &&
|
|
700
|
+
(
|
|
701
|
+
document.path !== undefined ||
|
|
702
|
+
existingDocumentArtifactId?.startsWith('source-') ||
|
|
703
|
+
hasExtractionArtifactId
|
|
704
|
+
)
|
|
705
|
+
) {
|
|
706
|
+
return {
|
|
707
|
+
...withoutTopLevelArtifactId,
|
|
708
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
709
|
+
document: {
|
|
710
|
+
...documentWithoutPath,
|
|
711
|
+
artifact_id: artifactId,
|
|
712
|
+
},
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (toolId === 'document_copy_for_edit' && hasExtractionArtifactId) {
|
|
718
|
+
const sourceArtifactId = latestDocumentArtifactRef(messages, {
|
|
719
|
+
toolIds: new Set(['document_inspect', 'document_extract', 'document_form_schema']),
|
|
720
|
+
artifactPrefix: 'source-',
|
|
721
|
+
})
|
|
722
|
+
if (sourceArtifactId) {
|
|
723
|
+
return {
|
|
724
|
+
...withoutTopLevelArtifactId,
|
|
725
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
726
|
+
document: {
|
|
727
|
+
...documentWithoutPath,
|
|
728
|
+
artifact_id: sourceArtifactId,
|
|
729
|
+
},
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (existingDocumentArtifactId) {
|
|
735
|
+
return topLevelArtifactIdRaw === undefined
|
|
736
|
+
? { ...normalizedPatchInput, document: documentWithoutPath }
|
|
737
|
+
: { ...withoutTopLevelArtifactId, document: documentWithoutPath }
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (toolId === 'document_copy_for_edit' && document.path !== undefined) {
|
|
741
|
+
const sourceArtifactId = latestDocumentArtifactRef(messages, {
|
|
742
|
+
toolIds: new Set(['document_inspect', 'document_extract', 'document_form_schema']),
|
|
743
|
+
artifactPrefix: 'source-',
|
|
744
|
+
})
|
|
745
|
+
if (sourceArtifactId) {
|
|
746
|
+
return {
|
|
747
|
+
...withoutTopLevelArtifactId,
|
|
748
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
749
|
+
document: {
|
|
750
|
+
...documentWithoutPath,
|
|
751
|
+
artifact_id: sourceArtifactId,
|
|
752
|
+
},
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const topLevelArtifactId = safeDocumentArtifactId(topLevelArtifactIdRaw)
|
|
758
|
+
const currentTextArtifactId = explicitDocumentArtifactIdFromText(
|
|
759
|
+
latestPlainUserText(messages),
|
|
760
|
+
)
|
|
761
|
+
const artifactId = topLevelArtifactId ?? currentTextArtifactId
|
|
762
|
+
if (!artifactId) {
|
|
763
|
+
return (
|
|
764
|
+
normalizeDocumentPathExpectedFormatInput(normalizedPatchInput, document) ??
|
|
765
|
+
normalizedPatchInput
|
|
766
|
+
)
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return {
|
|
770
|
+
...withoutTopLevelArtifactId,
|
|
771
|
+
correlation_id: documentCorrelationId(input.correlation_id),
|
|
772
|
+
document: {
|
|
773
|
+
...documentWithoutPath,
|
|
774
|
+
artifact_id: artifactId,
|
|
775
|
+
},
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
113
779
|
function asStringArray(value: unknown): string[] {
|
|
114
780
|
return Array.isArray(value)
|
|
115
781
|
? value.filter((item): item is string => typeof item === 'string')
|
|
@@ -270,6 +936,8 @@ function primitiveToolFor(primitive: AdapterPrimitive): Tool {
|
|
|
270
936
|
return ResolveLocationPrimitive as Tool
|
|
271
937
|
case 'send':
|
|
272
938
|
return SubmitPrimitive as Tool
|
|
939
|
+
case 'document':
|
|
940
|
+
return DocumentPrimitive as Tool
|
|
273
941
|
case 'check':
|
|
274
942
|
return VerifyPrimitive as Tool
|
|
275
943
|
case 'find':
|
|
@@ -285,21 +953,20 @@ function rootInputFor(entry: AdapterManifestEntry, input: Record<string, unknown
|
|
|
285
953
|
}
|
|
286
954
|
}
|
|
287
955
|
|
|
288
|
-
function
|
|
289
|
-
|
|
956
|
+
function concreteAdapterCallInputFor(
|
|
957
|
+
entry: AdapterManifestEntry,
|
|
290
958
|
input: Record<string, unknown>,
|
|
959
|
+
): Record<string, unknown> {
|
|
960
|
+
if (input.tool_id !== entry.tool_id) return input
|
|
961
|
+
const params = input.params
|
|
962
|
+
return isJsonObject(params) ? params : input
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function validateAdapterContractInput(
|
|
966
|
+
_toolId: string,
|
|
967
|
+
_input: Record<string, unknown>,
|
|
291
968
|
) {
|
|
292
|
-
|
|
293
|
-
const analTime = input.anal_time
|
|
294
|
-
if (typeof analTime === 'string' && /^\d{12}$/u.test(analTime)) return undefined
|
|
295
|
-
return {
|
|
296
|
-
result: false as const,
|
|
297
|
-
message:
|
|
298
|
-
'KMA analysis weather-chart schema mismatch: anal_time is required as UTC YYYYMMDDHHMM. ' +
|
|
299
|
-
"Use a 12-digit official analysis time with minutes, for example '202605281200', not a 10-digit KST hour. " +
|
|
300
|
-
'If the citizen asks for now/today, choose the latest completed official UTC analysis slot and report upstream failure directly if APIHub has no chart.',
|
|
301
|
-
errorCode: 1,
|
|
302
|
-
}
|
|
969
|
+
return undefined
|
|
303
970
|
}
|
|
304
971
|
|
|
305
972
|
export function isAdapterToolName(name: string): boolean {
|
|
@@ -323,694 +990,491 @@ function searchTokens(text: string): string[] {
|
|
|
323
990
|
return text.toLowerCase().match(/[\p{L}\p{N}_-]+/gu) ?? []
|
|
324
991
|
}
|
|
325
992
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
'simple',
|
|
509
|
-
'auth',
|
|
510
|
-
'mobile',
|
|
511
|
-
'id',
|
|
512
|
-
'mydata',
|
|
513
|
-
]) {
|
|
514
|
-
tokens.add(token)
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (isTagoBusQuery(query)) {
|
|
518
|
-
for (const token of [
|
|
519
|
-
'국토교통부',
|
|
520
|
-
'tago',
|
|
521
|
-
'버스',
|
|
522
|
-
'시내버스',
|
|
523
|
-
'버스정류소',
|
|
524
|
-
'정류장',
|
|
525
|
-
'정류소',
|
|
526
|
-
'노선',
|
|
527
|
-
'노선번호',
|
|
528
|
-
'버스도착',
|
|
529
|
-
'도착',
|
|
530
|
-
'nodeid',
|
|
531
|
-
'nodenm',
|
|
532
|
-
'nodeno',
|
|
533
|
-
'routeid',
|
|
534
|
-
'routeno',
|
|
535
|
-
'citycode',
|
|
536
|
-
'bus',
|
|
537
|
-
'station',
|
|
538
|
-
'route',
|
|
539
|
-
'arrival',
|
|
540
|
-
]) {
|
|
541
|
-
tokens.add(token)
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
if (collapseOrAedQuery) {
|
|
545
|
-
for (const token of ['aed', '자동심장충격기', '자동제세동기', '심장충격기', '위치']) {
|
|
546
|
-
tokens.add(token)
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (/(미세먼지|초미세|대기질|공기질|airquality|air quality)/u.test(compact)) {
|
|
550
|
-
for (const token of ['미세먼지', '대기질', '대기오염', 'airkorea', 'air', 'quality']) {
|
|
551
|
-
tokens.add(token)
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
if (/(법률|변호사|무료상담|상담)/u.test(compact)) {
|
|
555
|
-
for (const token of ['법률', '변호사', '마을변호사', '상담', 'legal', 'lawyer']) {
|
|
556
|
-
tokens.add(token)
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
if (/(장례|화장|봉안|장사|funeral)/u.test(compact)) {
|
|
560
|
-
for (const token of ['장례', '장례식장', '시설사용료', 'funeral', 'fee']) {
|
|
561
|
-
tokens.add(token)
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
if (/(취업|채용|공고|공무원|job|recruit)/u.test(compact)) {
|
|
565
|
-
for (const token of ['취업', '채용', '공고', '공무원', 'public', 'job']) {
|
|
566
|
-
tokens.add(token)
|
|
567
|
-
}
|
|
993
|
+
const KOREAN_TRAILING_PARTICLES = [
|
|
994
|
+
'으로부터',
|
|
995
|
+
'에서부터',
|
|
996
|
+
'에게서',
|
|
997
|
+
'한테서',
|
|
998
|
+
'까지',
|
|
999
|
+
'부터',
|
|
1000
|
+
'으로',
|
|
1001
|
+
'에서',
|
|
1002
|
+
'에게',
|
|
1003
|
+
'한테',
|
|
1004
|
+
'처럼',
|
|
1005
|
+
'보다',
|
|
1006
|
+
'하고',
|
|
1007
|
+
'이며',
|
|
1008
|
+
'이고',
|
|
1009
|
+
'로',
|
|
1010
|
+
'와',
|
|
1011
|
+
'과',
|
|
1012
|
+
'은',
|
|
1013
|
+
'는',
|
|
1014
|
+
'이',
|
|
1015
|
+
'가',
|
|
1016
|
+
'을',
|
|
1017
|
+
'를',
|
|
1018
|
+
'의',
|
|
1019
|
+
'에',
|
|
1020
|
+
'도',
|
|
1021
|
+
'만',
|
|
1022
|
+
] as const
|
|
1023
|
+
|
|
1024
|
+
const LOW_SIGNAL_DISCOVERY_TOKENS = new Set([
|
|
1025
|
+
'지금',
|
|
1026
|
+
'현재',
|
|
1027
|
+
'오늘',
|
|
1028
|
+
'내일',
|
|
1029
|
+
'모레',
|
|
1030
|
+
'이번',
|
|
1031
|
+
'근처',
|
|
1032
|
+
'주변',
|
|
1033
|
+
'어디',
|
|
1034
|
+
'어떻게',
|
|
1035
|
+
'now',
|
|
1036
|
+
'current',
|
|
1037
|
+
'today',
|
|
1038
|
+
'tomorrow',
|
|
1039
|
+
'latest',
|
|
1040
|
+
'nearby',
|
|
1041
|
+
'realtime',
|
|
1042
|
+
'real-time',
|
|
1043
|
+
])
|
|
1044
|
+
|
|
1045
|
+
const KMA_LIFESTYLE_WEATHER_RE =
|
|
1046
|
+
/(날씨|현재\s*기상|실황|관측|예보|기온|습도|풍속|지금\s*비|비\s*(?:와|오|올|내리)|우산|강수|소나기|산책|퇴근|current\s+weather|forecast|rain|umbrella|precipitation|temperature)/iu
|
|
1047
|
+
const HEALTHCARE_RE =
|
|
1048
|
+
/(응급|응급실|응급의료|야간\s*진료|야간진료|병원|의원|의료기관|진료\s*가능|\bemergency\b|\ber\b|\bhospital\b|\bclinic\b)/iu
|
|
1049
|
+
const AIR_QUALITY_RE =
|
|
1050
|
+
/(미세먼지|초미세먼지|초미세|대기질|대기오염|공기질|마스크|pm\s*2\.?5|pm\s*10|air\s*korea|airkorea|air\s*quality|airquality)/iu
|
|
1051
|
+
const KMA_ANALYSIS_RE =
|
|
1052
|
+
/(분석자료|이미\s*분석|고해상도\s*격자|객관분석|AWS\s*객관|지도\s*자료|일기도|분석일기도|비구름|바람\s*흐름|날씨\s*흐름|전국\s*날씨|synoptic|weather\s*chart|objective\s*analysis|high[-\s]?resolution|grid)/iu
|
|
1053
|
+
const AIRPORT_AVIATION_RE =
|
|
1054
|
+
/(AMOS|METAR|SPECI|RVR|항공기상|공항기상|활주로|runway|aviation|비행기|항공편|비행편|이륙|착륙|결항|지연|운항|뜰\s*만|뜨나|뜰\s*수|flight|take\s*off|landing|delay|cancel)/iu
|
|
1055
|
+
const POI_LOCATION_RE =
|
|
1056
|
+
/(근처|주변|주위|인근|가까운|우리\s*동네|여기|이\s*근처|현재\s*위치|내\s*위치|역|터미널|공항|캠퍼스|대학교|대학|해수욕장|시장|공원|랜드마크)/iu
|
|
1057
|
+
const ADMIN_LOCATION_RE =
|
|
1058
|
+
/(?:[가-힣]{2,}(?:시|군|구|동|읍|면)\b|[가-힣0-9]{2,}(?<!으)(?:로|길)\b)/iu
|
|
1059
|
+
const COORDINATE_PAIR_RE =
|
|
1060
|
+
/[+-]?\d{1,2}(?:\.\d+)?\s*,\s*[+-]?\d{2,3}(?:\.\d+)?/u
|
|
1061
|
+
const PRIOR_LOCATION_CONTEXT_RE = /\[prior_location_context\]/u
|
|
1062
|
+
const GOV24_RE = /(정부24|gov24|주민등록등본|등본|증명서|민원)/iu
|
|
1063
|
+
const GOV24_READ_ONLY_RE = /(가능\s*여부|준비물|확인|조회|안내|알려)/iu
|
|
1064
|
+
const GOV24_ACTION_RE = /(신청|진행|제출|접수|발급\s*신청|apply|submit|issue)/iu
|
|
1065
|
+
const WELFARE_RE =
|
|
1066
|
+
/(생활비|기초생활|주거급여|긴급복지|저소득|차상위|복지혜택|지원금|진료비\s*바우처|출산휴가|임신|아동수당|첫만남이용권)/iu
|
|
1067
|
+
const CIVIL_BIRTH_HANDOFF_RE =
|
|
1068
|
+
/(출생신고|아기가\s*태어|아동수당|첫만남이용권|피부양자\s*등록)/iu
|
|
1069
|
+
const UTILITY_RE = /(전기|수도|도시가스|요금|자동이체|공과금|고지서|납부)/iu
|
|
1070
|
+
const HOUSING_HANDOFF_RE =
|
|
1071
|
+
/(생애최초\s*주택구입|주택구입|대출|취득세|등기|전입)/iu
|
|
1072
|
+
const CIVIL_DEATH_RE = /(사망|돌아가|장례|유족|상속|재산|국민연금)/iu
|
|
1073
|
+
|
|
1074
|
+
const LOCATION_TOOL_IDS = new Set([
|
|
1075
|
+
'locate',
|
|
1076
|
+
'kakao_address_search',
|
|
1077
|
+
'kakao_keyword_search',
|
|
1078
|
+
'kakao_coord_to_region',
|
|
1079
|
+
'juso_adm_cd_lookup',
|
|
1080
|
+
'sgis_adm_cd_lookup',
|
|
1081
|
+
])
|
|
1082
|
+
const KMA_LIFESTYLE_WEATHER_TOOL_IDS = new Set([
|
|
1083
|
+
'kma_current_observation',
|
|
1084
|
+
'kma_ultra_short_term_forecast',
|
|
1085
|
+
'kma_short_term_forecast',
|
|
1086
|
+
'kma_forecast_fetch',
|
|
1087
|
+
])
|
|
1088
|
+
const EMERGENCY_TOOL_IDS = new Set([
|
|
1089
|
+
'nmc_emergency_search',
|
|
1090
|
+
'nmc_aed_site_locate',
|
|
1091
|
+
'hira_hospital_search',
|
|
1092
|
+
'hira_medical_institution_detail',
|
|
1093
|
+
])
|
|
1094
|
+
const GOV24_LOOKUP_TOOL_IDS = new Set(['mock_lookup_module_gov24_certificate'])
|
|
1095
|
+
const GOV24_ACTION_TOOL_IDS = new Set([
|
|
1096
|
+
'mock_lookup_module_gov24_certificate',
|
|
1097
|
+
'mock_verify_module_simple_auth',
|
|
1098
|
+
'mock_verify_ganpyeon_injeung',
|
|
1099
|
+
'mock_verify_mobile_id',
|
|
1100
|
+
'mock_submit_module_gov24_minwon',
|
|
1101
|
+
])
|
|
1102
|
+
const WELFARE_TOOL_IDS = new Set([
|
|
1103
|
+
'mohw_welfare_eligibility_search',
|
|
1104
|
+
'mock_welfare_application_submit_v1',
|
|
1105
|
+
])
|
|
1106
|
+
const UTILITY_TOOL_IDS = new Set([
|
|
1107
|
+
'kepco_contract_power_usage',
|
|
1108
|
+
'mock_kftc_opengiro_bill_send_v1',
|
|
1109
|
+
'mock_kftc_opengiro_payment_send_v1',
|
|
1110
|
+
])
|
|
1111
|
+
const CIVIL_DEATH_TOOL_IDS = new Set([
|
|
1112
|
+
'bfc_funeral_area_fee',
|
|
1113
|
+
'reb_real_estate_stat_table',
|
|
1114
|
+
'mohw_welfare_eligibility_search',
|
|
1115
|
+
])
|
|
1116
|
+
|
|
1117
|
+
type ProviderRoutingIntent = {
|
|
1118
|
+
readonly hasCoordinateLocationAnchor: boolean
|
|
1119
|
+
readonly hasAdminLocationAnchor: boolean
|
|
1120
|
+
readonly hasPriorLocationContext: boolean
|
|
1121
|
+
readonly hasLocationAnchor: boolean
|
|
1122
|
+
readonly hasLifestyleWeather: boolean
|
|
1123
|
+
readonly hasEmergencyMedical: boolean
|
|
1124
|
+
readonly hasGov24ReadOnly: boolean
|
|
1125
|
+
readonly hasGov24Action: boolean
|
|
1126
|
+
readonly hasWelfare: boolean
|
|
1127
|
+
readonly hasCivilBirthHandoff: boolean
|
|
1128
|
+
readonly hasUtility: boolean
|
|
1129
|
+
readonly hasHousingHandoff: boolean
|
|
1130
|
+
readonly hasCivilDeath: boolean
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
type AdapterSelectionOptions = {
|
|
1134
|
+
readonly hasCurrentTurnLocationContext?: boolean
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
function hasHangul(text: string): boolean {
|
|
1138
|
+
return /\p{Script=Hangul}/u.test(text)
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function extractProviderRoutingIntent(query: string): ProviderRoutingIntent {
|
|
1142
|
+
const hasEmergencyMedical = HEALTHCARE_RE.test(query)
|
|
1143
|
+
const hasGov24 = GOV24_RE.test(query)
|
|
1144
|
+
const hasGov24Action = hasGov24 && GOV24_ACTION_RE.test(query)
|
|
1145
|
+
const hasCoordinateLocationAnchor = COORDINATE_PAIR_RE.test(query)
|
|
1146
|
+
const hasAdminLocationAnchor = ADMIN_LOCATION_RE.test(query)
|
|
1147
|
+
const hasPoiLocationAnchor = POI_LOCATION_RE.test(query)
|
|
1148
|
+
const hasPriorLocationContext = PRIOR_LOCATION_CONTEXT_RE.test(query)
|
|
1149
|
+
return {
|
|
1150
|
+
hasCoordinateLocationAnchor,
|
|
1151
|
+
hasAdminLocationAnchor,
|
|
1152
|
+
hasPriorLocationContext,
|
|
1153
|
+
hasLocationAnchor:
|
|
1154
|
+
hasCoordinateLocationAnchor ||
|
|
1155
|
+
hasPoiLocationAnchor ||
|
|
1156
|
+
hasAdminLocationAnchor ||
|
|
1157
|
+
hasPriorLocationContext,
|
|
1158
|
+
hasLifestyleWeather:
|
|
1159
|
+
KMA_LIFESTYLE_WEATHER_RE.test(query) &&
|
|
1160
|
+
!hasEmergencyMedical &&
|
|
1161
|
+
!AIR_QUALITY_RE.test(query) &&
|
|
1162
|
+
!KMA_ANALYSIS_RE.test(query) &&
|
|
1163
|
+
!AIRPORT_AVIATION_RE.test(query),
|
|
1164
|
+
hasEmergencyMedical,
|
|
1165
|
+
hasGov24ReadOnly:
|
|
1166
|
+
hasGov24 &&
|
|
1167
|
+
GOV24_READ_ONLY_RE.test(query) &&
|
|
1168
|
+
!hasGov24Action,
|
|
1169
|
+
hasGov24Action,
|
|
1170
|
+
hasWelfare: WELFARE_RE.test(query),
|
|
1171
|
+
hasCivilBirthHandoff: CIVIL_BIRTH_HANDOFF_RE.test(query),
|
|
1172
|
+
hasUtility: UTILITY_RE.test(query),
|
|
1173
|
+
hasHousingHandoff: HOUSING_HANDOFF_RE.test(query),
|
|
1174
|
+
hasCivilDeath: CIVIL_DEATH_RE.test(query),
|
|
568
1175
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function addSetValues(target: Set<string>, values: ReadonlySet<string>): void {
|
|
1179
|
+
for (const value of values) target.add(value)
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function restrictiveToolIdsForIntent(
|
|
1183
|
+
intent: ProviderRoutingIntent,
|
|
1184
|
+
options: AdapterSelectionOptions = {},
|
|
1185
|
+
): Set<string> | undefined {
|
|
1186
|
+
const allowed = new Set<string>()
|
|
1187
|
+
let restrictive = false
|
|
1188
|
+
|
|
1189
|
+
if (intent.hasGov24ReadOnly) {
|
|
1190
|
+
restrictive = true
|
|
1191
|
+
addSetValues(allowed, GOV24_LOOKUP_TOOL_IDS)
|
|
1192
|
+
} else if (intent.hasGov24Action) {
|
|
1193
|
+
restrictive = true
|
|
1194
|
+
addSetValues(allowed, GOV24_ACTION_TOOL_IDS)
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
if (intent.hasLifestyleWeather) {
|
|
1198
|
+
restrictive = true
|
|
1199
|
+
if (
|
|
1200
|
+
options.hasCurrentTurnLocationContext !== true &&
|
|
1201
|
+
!intent.hasPriorLocationContext
|
|
1202
|
+
) {
|
|
1203
|
+
addSetValues(allowed, LOCATION_TOOL_IDS)
|
|
572
1204
|
}
|
|
1205
|
+
addSetValues(allowed, KMA_LIFESTYLE_WEATHER_TOOL_IDS)
|
|
573
1206
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
1207
|
+
|
|
1208
|
+
if (intent.hasEmergencyMedical) {
|
|
1209
|
+
restrictive = true
|
|
1210
|
+
if (
|
|
1211
|
+
options.hasCurrentTurnLocationContext !== true &&
|
|
1212
|
+
!intent.hasPriorLocationContext &&
|
|
1213
|
+
intent.hasLocationAnchor
|
|
1214
|
+
) {
|
|
1215
|
+
addSetValues(allowed, LOCATION_TOOL_IDS)
|
|
577
1216
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
1217
|
+
if (
|
|
1218
|
+
options.hasCurrentTurnLocationContext === true ||
|
|
1219
|
+
intent.hasPriorLocationContext ||
|
|
1220
|
+
intent.hasCoordinateLocationAnchor ||
|
|
1221
|
+
intent.hasAdminLocationAnchor
|
|
1222
|
+
) {
|
|
1223
|
+
addSetValues(allowed, EMERGENCY_TOOL_IDS)
|
|
582
1224
|
}
|
|
583
1225
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
'amos',
|
|
589
|
-
'항공기상',
|
|
590
|
-
'공항기상',
|
|
591
|
-
'항공',
|
|
592
|
-
'비행기',
|
|
593
|
-
'항공편',
|
|
594
|
-
'운항',
|
|
595
|
-
'이륙',
|
|
596
|
-
'시정',
|
|
597
|
-
'rvr',
|
|
598
|
-
'wind',
|
|
599
|
-
'visibility',
|
|
600
|
-
]) {
|
|
601
|
-
tokens.add(token)
|
|
602
|
-
}
|
|
603
|
-
if (KMA_GIMPO_AIRPORT_RE.test(query) && KMA_RUNWAY_AREA_RE.test(query)) {
|
|
604
|
-
for (const token of [
|
|
605
|
-
'amos',
|
|
606
|
-
'공항기상관측',
|
|
607
|
-
'매분자료',
|
|
608
|
-
'활주로',
|
|
609
|
-
'김포공항',
|
|
610
|
-
'stn110',
|
|
611
|
-
'runway',
|
|
612
|
-
'visibility',
|
|
613
|
-
]) {
|
|
614
|
-
tokens.add(token)
|
|
615
|
-
}
|
|
616
|
-
}
|
|
1226
|
+
|
|
1227
|
+
if (intent.hasWelfare) {
|
|
1228
|
+
restrictive = true
|
|
1229
|
+
addSetValues(allowed, WELFARE_TOOL_IDS)
|
|
617
1230
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
'고해상도',
|
|
622
|
-
'격자자료',
|
|
623
|
-
'객관분석',
|
|
624
|
-
'aws',
|
|
625
|
-
'분석일기도',
|
|
626
|
-
'지도',
|
|
627
|
-
'비구름',
|
|
628
|
-
'바람흐름',
|
|
629
|
-
'objective',
|
|
630
|
-
'analysis',
|
|
631
|
-
'grid',
|
|
632
|
-
'chart',
|
|
633
|
-
]) {
|
|
634
|
-
tokens.add(token)
|
|
635
|
-
}
|
|
1231
|
+
|
|
1232
|
+
if (intent.hasCivilBirthHandoff) {
|
|
1233
|
+
restrictive = true
|
|
636
1234
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
'위험',
|
|
642
|
-
'위험지점',
|
|
643
|
-
'사고다발',
|
|
644
|
-
'사고다발구역',
|
|
645
|
-
'어린이보호구역',
|
|
646
|
-
'행정동코드',
|
|
647
|
-
'koroad',
|
|
648
|
-
'accident',
|
|
649
|
-
'hazard',
|
|
650
|
-
'hotspot',
|
|
651
|
-
]) {
|
|
652
|
-
tokens.add(token)
|
|
653
|
-
}
|
|
1235
|
+
|
|
1236
|
+
if (intent.hasUtility) {
|
|
1237
|
+
restrictive = true
|
|
1238
|
+
addSetValues(allowed, UTILITY_TOOL_IDS)
|
|
654
1239
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
'위치',
|
|
659
|
-
'주소',
|
|
660
|
-
'좌표',
|
|
661
|
-
'행정동',
|
|
662
|
-
'법정동',
|
|
663
|
-
'geocode',
|
|
664
|
-
'address',
|
|
665
|
-
'kakao',
|
|
666
|
-
]) {
|
|
667
|
-
tokens.add(token)
|
|
668
|
-
}
|
|
1240
|
+
|
|
1241
|
+
if (intent.hasHousingHandoff) {
|
|
1242
|
+
restrictive = true
|
|
669
1243
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
for (const token of [
|
|
675
|
-
'장소',
|
|
676
|
-
'키워드',
|
|
677
|
-
'poi',
|
|
678
|
-
'랜드마크',
|
|
679
|
-
'역',
|
|
680
|
-
'keyword',
|
|
681
|
-
'station',
|
|
682
|
-
'place',
|
|
683
|
-
]) {
|
|
684
|
-
tokens.add(token)
|
|
685
|
-
}
|
|
1244
|
+
|
|
1245
|
+
if (intent.hasCivilDeath) {
|
|
1246
|
+
restrictive = true
|
|
1247
|
+
addSetValues(allowed, CIVIL_DEATH_TOOL_IDS)
|
|
686
1248
|
}
|
|
687
|
-
return tokens
|
|
688
|
-
}
|
|
689
1249
|
|
|
690
|
-
|
|
691
|
-
entry: AdapterManifestEntry
|
|
692
|
-
score: number
|
|
1250
|
+
return restrictive ? allowed : undefined
|
|
693
1251
|
}
|
|
694
1252
|
|
|
695
|
-
function
|
|
696
|
-
|
|
1253
|
+
function routingIntentBoostForTool(
|
|
1254
|
+
toolId: string,
|
|
1255
|
+
intent: ProviderRoutingIntent,
|
|
1256
|
+
): number {
|
|
1257
|
+
if (intent.hasGov24ReadOnly && GOV24_LOOKUP_TOOL_IDS.has(toolId)) return 1200
|
|
1258
|
+
if (intent.hasGov24Action && GOV24_ACTION_TOOL_IDS.has(toolId)) return 1000
|
|
1259
|
+
if (intent.hasLifestyleWeather) {
|
|
1260
|
+
if (toolId === 'kakao_keyword_search') return 1100
|
|
1261
|
+
if (toolId === 'kakao_address_search') return 1000
|
|
1262
|
+
if (toolId === 'kma_current_observation') return 900
|
|
1263
|
+
if (toolId === 'kma_ultra_short_term_forecast') return 800
|
|
1264
|
+
if (toolId === 'kma_short_term_forecast') return 650
|
|
1265
|
+
if (LOCATION_TOOL_IDS.has(toolId)) return 260
|
|
1266
|
+
}
|
|
1267
|
+
if (intent.hasEmergencyMedical && intent.hasLocationAnchor) {
|
|
1268
|
+
if (toolId === 'nmc_emergency_search') return 1200
|
|
1269
|
+
if (toolId === 'nmc_aed_site_locate') return 950
|
|
1270
|
+
if (toolId === 'kakao_keyword_search') return 900
|
|
1271
|
+
if (toolId === 'kakao_address_search') return 800
|
|
1272
|
+
if (toolId === 'kakao_coord_to_region') return 500
|
|
1273
|
+
if (toolId === 'hira_hospital_search') return 250
|
|
1274
|
+
if (toolId === 'hira_medical_institution_detail') return 200
|
|
1275
|
+
if (LOCATION_TOOL_IDS.has(toolId)) return 300
|
|
1276
|
+
}
|
|
1277
|
+
if (intent.hasWelfare && WELFARE_TOOL_IDS.has(toolId)) return 1000
|
|
1278
|
+
if (intent.hasUtility && UTILITY_TOOL_IDS.has(toolId)) return 1000
|
|
1279
|
+
if (intent.hasCivilDeath && CIVIL_DEATH_TOOL_IDS.has(toolId)) return 1000
|
|
1280
|
+
return 0
|
|
697
1281
|
}
|
|
698
1282
|
|
|
699
|
-
function
|
|
700
|
-
|
|
1283
|
+
function koreanParticleStrippedVariants(token: string): string[] {
|
|
1284
|
+
if (!hasHangul(token)) return []
|
|
1285
|
+
const variants: string[] = []
|
|
1286
|
+
let current = token
|
|
1287
|
+
for (let i = 0; i < 2; i += 1) {
|
|
1288
|
+
const nextSuffix = KOREAN_TRAILING_PARTICLES.find(
|
|
1289
|
+
suffix => current.length > suffix.length + 1 && current.endsWith(suffix),
|
|
1290
|
+
)
|
|
1291
|
+
if (!nextSuffix) break
|
|
1292
|
+
current = current.slice(0, -nextSuffix.length)
|
|
1293
|
+
variants.push(current)
|
|
1294
|
+
}
|
|
1295
|
+
return variants
|
|
701
1296
|
}
|
|
702
1297
|
|
|
703
|
-
function
|
|
704
|
-
|
|
1298
|
+
function expandedTokensForText(text: string): Set<string> {
|
|
1299
|
+
const tokens = new Set<string>()
|
|
1300
|
+
for (const token of searchTokens(text)) {
|
|
1301
|
+
tokens.add(token)
|
|
1302
|
+
for (const variant of koreanParticleStrippedVariants(token)) {
|
|
1303
|
+
tokens.add(variant)
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return tokens
|
|
705
1307
|
}
|
|
706
1308
|
|
|
707
|
-
function
|
|
708
|
-
return
|
|
1309
|
+
function expandedQueryTokens(query: string): Set<string> {
|
|
1310
|
+
return expandedTokensForText(query)
|
|
709
1311
|
}
|
|
710
1312
|
|
|
711
|
-
function
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1313
|
+
function isUsefulDiscoveryToken(token: string): boolean {
|
|
1314
|
+
const compact = token.replace(/[_-]/gu, '')
|
|
1315
|
+
if (compact.length === 0) return false
|
|
1316
|
+
if (LOW_SIGNAL_DISCOVERY_TOKENS.has(token)) return false
|
|
1317
|
+
if (hasHangul(compact)) return compact.length >= 2
|
|
1318
|
+
if (compact === 'er') return true
|
|
1319
|
+
return compact.length >= 3
|
|
718
1320
|
}
|
|
719
1321
|
|
|
720
|
-
function
|
|
1322
|
+
function isSingleHangulPlaceSuffixMatch(
|
|
1323
|
+
fieldToken: string,
|
|
1324
|
+
queryToken: string,
|
|
1325
|
+
): boolean {
|
|
721
1326
|
return (
|
|
722
|
-
(
|
|
723
|
-
|
|
1327
|
+
hasHangul(fieldToken) &&
|
|
1328
|
+
fieldToken.length === 1 &&
|
|
1329
|
+
hasHangul(queryToken) &&
|
|
1330
|
+
queryToken.length >= 3 &&
|
|
1331
|
+
queryToken.endsWith(fieldToken)
|
|
724
1332
|
)
|
|
725
1333
|
}
|
|
726
1334
|
|
|
727
|
-
function
|
|
728
|
-
|
|
1335
|
+
function fieldMatchesToken(
|
|
1336
|
+
fieldTokens: Set<string>,
|
|
1337
|
+
fieldText: string,
|
|
1338
|
+
queryToken: string,
|
|
1339
|
+
): boolean {
|
|
1340
|
+
if (fieldTokens.has(queryToken) || fieldText.includes(queryToken)) {
|
|
1341
|
+
return true
|
|
1342
|
+
}
|
|
1343
|
+
if (!isUsefulDiscoveryToken(queryToken)) return false
|
|
1344
|
+
for (const fieldToken of fieldTokens) {
|
|
1345
|
+
if (isSingleHangulPlaceSuffixMatch(fieldToken, queryToken)) {
|
|
1346
|
+
return true
|
|
1347
|
+
}
|
|
1348
|
+
if (!isUsefulDiscoveryToken(fieldToken)) continue
|
|
1349
|
+
if (fieldToken.includes(queryToken) || queryToken.includes(fieldToken)) {
|
|
1350
|
+
return true
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return false
|
|
729
1354
|
}
|
|
730
1355
|
|
|
731
|
-
function
|
|
732
|
-
return
|
|
1356
|
+
function requiredInputFieldsFor(entry: AdapterManifestEntry): string[] {
|
|
1357
|
+
return asStringArray(asJsonObject(entry.input_schema_json).required)
|
|
733
1358
|
}
|
|
734
1359
|
|
|
735
|
-
function
|
|
1360
|
+
function isOpaqueProviderIdentifierField(fieldName: string): boolean {
|
|
736
1361
|
return (
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
!isMedicalEmergencyQuery(query) &&
|
|
741
|
-
!TRAFFIC_HAZARD_RE.test(query) &&
|
|
742
|
-
!MOF_OCEAN_WATER_QUALITY_RE.test(query)
|
|
1362
|
+
fieldName === 'ykiho' ||
|
|
1363
|
+
fieldName === 'id' ||
|
|
1364
|
+
fieldName.endsWith('_id')
|
|
743
1365
|
)
|
|
744
1366
|
}
|
|
745
1367
|
|
|
746
|
-
function
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
PROTECTED_SIMPLE_AUTH_RE.test(query) ? 'mock_verify_ganpyeon_injeung' : undefined,
|
|
759
|
-
PROTECTED_MYDATA_RE.test(query) ? 'mock_verify_mydata' : undefined,
|
|
760
|
-
...PROTECTED_CHECK_TOOL_NAMES,
|
|
761
|
-
].filter((toolName): toolName is string => typeof toolName === 'string')
|
|
762
|
-
return [...new Set(preferred)]
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function isTagoBusQuery(query: string): boolean {
|
|
766
|
-
return TAGO_BUS_RE.test(query)
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
function isKmaAnalysisMapQuery(query: string): boolean {
|
|
770
|
-
return KMA_ANALYSIS_MAP_RE.test(query)
|
|
1368
|
+
function isOpaqueIdentifierOnlyInitialCandidate(
|
|
1369
|
+
entry: AdapterManifestEntry,
|
|
1370
|
+
queryTokens: Set<string>,
|
|
1371
|
+
query: string,
|
|
1372
|
+
): boolean {
|
|
1373
|
+
const requiredFields = requiredInputFieldsFor(entry)
|
|
1374
|
+
if (requiredFields.length === 0) return false
|
|
1375
|
+
if (!requiredFields.every(isOpaqueProviderIdentifierField)) return false
|
|
1376
|
+
|
|
1377
|
+
const normalizedQuery = query.toLowerCase()
|
|
1378
|
+
if (normalizedQuery.includes(entry.tool_id.toLowerCase())) return false
|
|
1379
|
+
return !requiredFields.some(fieldName => queryTokens.has(fieldName.toLowerCase()))
|
|
771
1380
|
}
|
|
772
1381
|
|
|
773
|
-
|
|
774
|
-
|
|
1382
|
+
type AdapterScore = {
|
|
1383
|
+
score: number
|
|
1384
|
+
qualifyingDiscoveryMatches: number
|
|
775
1385
|
}
|
|
776
1386
|
|
|
777
|
-
|
|
778
|
-
|
|
1387
|
+
type ScoredAdapterEntry = {
|
|
1388
|
+
entry: AdapterManifestEntry
|
|
1389
|
+
score: number
|
|
779
1390
|
}
|
|
780
1391
|
|
|
781
1392
|
function scoreAdapterEntry(
|
|
782
1393
|
entry: AdapterManifestEntry,
|
|
783
1394
|
queryTokens: Set<string>,
|
|
784
1395
|
query: string,
|
|
785
|
-
):
|
|
1396
|
+
): AdapterScore {
|
|
1397
|
+
const toolId = entry.tool_id.toLowerCase()
|
|
1398
|
+
const name = entry.name.toLowerCase()
|
|
786
1399
|
const searchHint = entry.search_hint.toLowerCase()
|
|
787
1400
|
const description = (entry.llm_description ?? '').toLowerCase()
|
|
788
1401
|
const haystack = [
|
|
789
|
-
|
|
790
|
-
|
|
1402
|
+
toolId,
|
|
1403
|
+
name,
|
|
791
1404
|
entry.primitive,
|
|
792
1405
|
searchHint,
|
|
793
1406
|
description,
|
|
794
1407
|
].join(' ').toLowerCase()
|
|
795
|
-
const
|
|
1408
|
+
const toolIdTokens = expandedTokensForText(toolId)
|
|
1409
|
+
const nameTokens = expandedTokensForText(name)
|
|
1410
|
+
const hintTokens = expandedTokensForText(searchHint)
|
|
796
1411
|
let score = 0
|
|
1412
|
+
let qualifyingDiscoveryMatches = 0
|
|
797
1413
|
for (const token of queryTokens) {
|
|
798
1414
|
if (!token) continue
|
|
799
|
-
|
|
800
|
-
if (
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
if (haystack.includes(token)) score += 1
|
|
804
|
-
}
|
|
805
|
-
if (query.toLowerCase().includes(entry.tool_id.toLowerCase())) score += 1000
|
|
806
|
-
if (
|
|
807
|
-
isReverseGeocodeAdapter(entry.tool_id) &&
|
|
808
|
-
!queryExplicitlyMentionsCoordinates(query)
|
|
809
|
-
) {
|
|
810
|
-
score = Math.max(0, score - 24)
|
|
811
|
-
}
|
|
812
|
-
if (queryTargetsKoroadHazardDataset(query)) {
|
|
813
|
-
if (entry.tool_id === 'koroad_accident_hazard_search') score += 32
|
|
814
|
-
if (entry.tool_id === 'koroad_accident_search') score = 0
|
|
815
|
-
}
|
|
816
|
-
if (isKmaAnalysisQuery(query)) {
|
|
817
|
-
if (entry.tool_id === 'kma_apihub_url_analysis_weather_chart_image') {
|
|
818
|
-
score += isKmaAnalysisMapQuery(query) ? 900 : isKmaAnalysisPointQuery(query) ? -20 : 150
|
|
819
|
-
}
|
|
820
|
-
if (entry.tool_id === 'kma_apihub_url_high_resolution_grid_point') {
|
|
821
|
-
score += isKmaAnalysisPointQuery(query) ? 900 : 450
|
|
822
|
-
}
|
|
823
|
-
if (entry.tool_id === 'kma_apihub_url_aws_objective_analysis_grid') {
|
|
824
|
-
score += isKmaAnalysisPointQuery(query) ? 800 : 400
|
|
1415
|
+
let matchedDiscovery = false
|
|
1416
|
+
if (fieldMatchesToken(toolIdTokens, toolId, token)) {
|
|
1417
|
+
score += 12
|
|
1418
|
+
matchedDiscovery = true
|
|
825
1419
|
}
|
|
826
|
-
if (
|
|
827
|
-
|
|
828
|
-
|
|
1420
|
+
if (fieldMatchesToken(hintTokens, searchHint, token)) {
|
|
1421
|
+
score += 8
|
|
1422
|
+
matchedDiscovery = true
|
|
829
1423
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
if (entry.tool_id === 'kakao_address_search') score += 1000
|
|
834
|
-
if (entry.tool_id === 'kma_current_observation') score += 900
|
|
835
|
-
if (entry.tool_id === 'kma_ultra_short_term_forecast') score += 800
|
|
836
|
-
if (entry.tool_id === 'kma_short_term_forecast') score += 650
|
|
837
|
-
if (entry.tool_id === 'kakao_coord_to_region') score += 260
|
|
838
|
-
if (entry.tool_id === 'juso_adm_cd_lookup') score += 260
|
|
839
|
-
if (entry.tool_id === 'sgis_adm_cd_lookup') score += 260
|
|
840
|
-
}
|
|
841
|
-
if (HIRA_MEDICAL_DETAIL_RE.test(query)) {
|
|
842
|
-
if (entry.tool_id === 'hira_medical_institution_detail') score += 650
|
|
843
|
-
}
|
|
844
|
-
if (MOIS_EMERGENCY_CALL_BOX_RE.test(query)) {
|
|
845
|
-
if (entry.tool_id === 'mois_emergency_call_box_lookup') score += 1000
|
|
846
|
-
}
|
|
847
|
-
if (GYERYONG_ASSISTIVE_CHARGER_RE.test(query)) {
|
|
848
|
-
if (entry.tool_id === 'gyeryong_assistive_device_charging_place_locate') {
|
|
849
|
-
score += 1000
|
|
1424
|
+
if (fieldMatchesToken(nameTokens, name, token)) {
|
|
1425
|
+
score += 4
|
|
1426
|
+
matchedDiscovery = true
|
|
850
1427
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
if (
|
|
854
|
-
|
|
855
|
-
if (isPpsBidQuery(query)) {
|
|
856
|
-
if (entry.tool_id === 'pps_bid_public_info') score += 1000
|
|
857
|
-
}
|
|
858
|
-
if (isProtectedCheckQuery(query) && entry.primitive === 'check') {
|
|
859
|
-
const preference = protectedCheckToolPreference(query)
|
|
860
|
-
const index = preference.indexOf(entry.tool_id)
|
|
861
|
-
score += index >= 0 ? 1000 - index * 20 : 500
|
|
862
|
-
}
|
|
863
|
-
if (isTagoBusQuery(query)) {
|
|
864
|
-
if (entry.tool_id === 'tago_bus_station_search') score += 1050
|
|
865
|
-
if (entry.tool_id === 'tago_bus_arrival_search') score += 1000
|
|
866
|
-
if (entry.tool_id === 'tago_bus_route_station_search') score += 950
|
|
867
|
-
if (entry.tool_id === 'tago_bus_route_search') score += 850
|
|
868
|
-
if (entry.tool_id === 'tago_bus_location_search') score += 650
|
|
869
|
-
}
|
|
870
|
-
if (isCollapseOrAedQuery(query)) {
|
|
871
|
-
if (entry.tool_id === 'nmc_aed_site_locate') score += 900
|
|
872
|
-
if (entry.tool_id === 'nmc_emergency_search') score += 700
|
|
873
|
-
if (queryPrefersPoiLocation(query) && entry.tool_id === 'kakao_keyword_search') score += 120
|
|
874
|
-
}
|
|
875
|
-
if (
|
|
876
|
-
KMA_GIMPO_AIRPORT_RE.test(query) &&
|
|
877
|
-
KMA_RUNWAY_AREA_RE.test(query) &&
|
|
878
|
-
KMA_AIRPORT_AVIATION_RE.test(query) &&
|
|
879
|
-
entry.tool_id === 'kma_apihub_url_air_amos_minute'
|
|
880
|
-
) {
|
|
881
|
-
score += 500
|
|
882
|
-
}
|
|
883
|
-
return score
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
function filterSpecialCaseRanked(
|
|
887
|
-
query: string,
|
|
888
|
-
ranked: ScoredAdapterEntry[],
|
|
889
|
-
): ScoredAdapterEntry[] {
|
|
890
|
-
let filtered = ranked
|
|
891
|
-
if (isKmaAnalysisQuery(query)) {
|
|
892
|
-
const allowLocation = isKmaAnalysisPointQuery(query)
|
|
893
|
-
const preferPoiLocation = queryPrefersPoiLocation(query)
|
|
894
|
-
filtered = filtered
|
|
895
|
-
.filter(candidate => {
|
|
896
|
-
if (KMA_ANALYSIS_TOOL_NAMES.has(candidate.entry.tool_id)) return true
|
|
897
|
-
return allowLocation && isLocationAdapter(candidate.entry)
|
|
898
|
-
})
|
|
899
|
-
.map(candidate => {
|
|
900
|
-
if (!allowLocation || !isLocationAdapter(candidate.entry)) return candidate
|
|
901
|
-
let score = Math.max(1, candidate.score - 10)
|
|
902
|
-
if (preferPoiLocation && candidate.entry.tool_id === 'kakao_keyword_search') {
|
|
903
|
-
score += 30
|
|
904
|
-
} else if (preferPoiLocation && candidate.entry.tool_id === 'kakao_address_search') {
|
|
905
|
-
score = Math.max(1, score - 15)
|
|
906
|
-
}
|
|
907
|
-
return { ...candidate, score }
|
|
908
|
-
})
|
|
909
|
-
.sort((a, b) => {
|
|
910
|
-
if (b.score !== a.score) return b.score - a.score
|
|
911
|
-
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
912
|
-
})
|
|
913
|
-
}
|
|
914
|
-
if (isLifestyleWeatherQuery(query)) {
|
|
915
|
-
const allowed = filtered.filter(
|
|
916
|
-
candidate =>
|
|
917
|
-
KMA_LIFESTYLE_WEATHER_TOOL_NAMES.has(candidate.entry.tool_id) ||
|
|
918
|
-
isLocationAdapter(candidate.entry),
|
|
919
|
-
)
|
|
920
|
-
if (allowed.length > 0) {
|
|
921
|
-
filtered = allowed.sort((a, b) => {
|
|
922
|
-
if (b.score !== a.score) return b.score - a.score
|
|
923
|
-
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
924
|
-
})
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
if (isPpsBidQuery(query)) {
|
|
928
|
-
const allowed = filtered.filter(candidate => candidate.entry.tool_id === 'pps_bid_public_info')
|
|
929
|
-
if (allowed.length > 0) {
|
|
930
|
-
filtered = allowed.sort((a, b) => {
|
|
931
|
-
if (b.score !== a.score) return b.score - a.score
|
|
932
|
-
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
933
|
-
})
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
if (isProtectedCheckQuery(query)) {
|
|
937
|
-
const preference = protectedCheckToolPreference(query)
|
|
938
|
-
const allowed = filtered.filter(candidate => candidate.entry.primitive === 'check')
|
|
939
|
-
if (allowed.length > 0) {
|
|
940
|
-
filtered = allowed.sort((a, b) => {
|
|
941
|
-
const aIndex = preference.indexOf(a.entry.tool_id)
|
|
942
|
-
const bIndex = preference.indexOf(b.entry.tool_id)
|
|
943
|
-
const aRank = aIndex >= 0 ? aIndex : Number.MAX_SAFE_INTEGER
|
|
944
|
-
const bRank = bIndex >= 0 ? bIndex : Number.MAX_SAFE_INTEGER
|
|
945
|
-
if (aRank !== bRank) return aRank - bRank
|
|
946
|
-
if (b.score !== a.score) return b.score - a.score
|
|
947
|
-
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
948
|
-
})
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
if (isTagoBusQuery(query)) {
|
|
952
|
-
const allowed = filtered.filter(candidate => TAGO_BUS_TOOL_NAMES.has(candidate.entry.tool_id))
|
|
953
|
-
if (allowed.length > 0) {
|
|
954
|
-
filtered = allowed.sort((a, b) => {
|
|
955
|
-
if (b.score !== a.score) return b.score - a.score
|
|
956
|
-
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
957
|
-
})
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
if (isCollapseOrAedQuery(query)) {
|
|
961
|
-
const allowed = filtered.filter(candidate => {
|
|
962
|
-
if (candidate.entry.tool_id === 'nmc_aed_site_locate') return true
|
|
963
|
-
if (candidate.entry.tool_id === 'nmc_emergency_search') return true
|
|
964
|
-
return isLocationAdapter(candidate.entry)
|
|
965
|
-
})
|
|
966
|
-
if (allowed.some(candidate => candidate.entry.tool_id === 'nmc_aed_site_locate')) {
|
|
967
|
-
filtered = allowed.sort((a, b) => {
|
|
968
|
-
if (b.score !== a.score) return b.score - a.score
|
|
969
|
-
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
970
|
-
})
|
|
1428
|
+
if (description.includes(token)) score += 2
|
|
1429
|
+
if (haystack.includes(token)) score += 1
|
|
1430
|
+
if (matchedDiscovery && isUsefulDiscoveryToken(token)) {
|
|
1431
|
+
qualifyingDiscoveryMatches += 1
|
|
971
1432
|
}
|
|
972
1433
|
}
|
|
973
|
-
if (
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
)
|
|
977
|
-
}
|
|
978
|
-
if (isAirportAviationQuery(query)) {
|
|
979
|
-
const hasAirUrlCandidate = filtered.some(candidate =>
|
|
980
|
-
KMA_URL_AIR_TOOL_NAMES.has(candidate.entry.tool_id),
|
|
981
|
-
)
|
|
982
|
-
if (hasAirUrlCandidate) {
|
|
983
|
-
filtered = filtered.filter(
|
|
984
|
-
candidate =>
|
|
985
|
-
!isLocationAdapter(candidate.entry) &&
|
|
986
|
-
candidate.entry.tool_id !== 'kma_current_observation',
|
|
987
|
-
)
|
|
988
|
-
}
|
|
1434
|
+
if (query.toLowerCase().includes(toolId)) {
|
|
1435
|
+
score += 1000
|
|
1436
|
+
qualifyingDiscoveryMatches += 1
|
|
989
1437
|
}
|
|
990
|
-
return
|
|
1438
|
+
return { score, qualifyingDiscoveryMatches }
|
|
991
1439
|
}
|
|
992
1440
|
|
|
993
1441
|
export function selectTopKAdapterToolNamesForQuery(
|
|
994
1442
|
query: string,
|
|
995
1443
|
maxResults = 5,
|
|
1444
|
+
options: AdapterSelectionOptions = {},
|
|
996
1445
|
): string[] {
|
|
997
1446
|
const normalizedQuery = query.trim()
|
|
998
1447
|
if (!normalizedQuery || maxResults <= 0) return []
|
|
999
1448
|
const queryTokens = expandedQueryTokens(normalizedQuery)
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1449
|
+
const routingIntent = extractProviderRoutingIntent(normalizedQuery)
|
|
1450
|
+
const restrictiveToolIds = restrictiveToolIdsForIntent(routingIntent, options)
|
|
1451
|
+
const ranked = listAdapters()
|
|
1003
1452
|
.filter(entry => !ROOT_PRIMITIVE_TOOL_NAMES.has(entry.tool_id))
|
|
1004
|
-
.map(entry =>
|
|
1005
|
-
entry,
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1453
|
+
.map(entry => {
|
|
1454
|
+
const result = scoreAdapterEntry(entry, queryTokens, normalizedQuery)
|
|
1455
|
+
const routingBoost = routingIntentBoostForTool(entry.tool_id, routingIntent)
|
|
1456
|
+
return {
|
|
1457
|
+
entry,
|
|
1458
|
+
score: result.score + routingBoost,
|
|
1459
|
+
qualifyingDiscoveryMatches:
|
|
1460
|
+
result.qualifyingDiscoveryMatches + (routingBoost > 0 ? 1 : 0),
|
|
1461
|
+
}
|
|
1462
|
+
})
|
|
1463
|
+
.filter(candidate =>
|
|
1464
|
+
(restrictiveToolIds === undefined ||
|
|
1465
|
+
restrictiveToolIds.has(candidate.entry.tool_id)) &&
|
|
1466
|
+
candidate.score > 0 &&
|
|
1467
|
+
candidate.qualifyingDiscoveryMatches > 0 &&
|
|
1468
|
+
!isOpaqueIdentifierOnlyInitialCandidate(
|
|
1469
|
+
candidate.entry,
|
|
1470
|
+
queryTokens,
|
|
1471
|
+
normalizedQuery,
|
|
1472
|
+
)
|
|
1473
|
+
)
|
|
1009
1474
|
.sort((a, b) => {
|
|
1010
1475
|
if (b.score !== a.score) return b.score - a.score
|
|
1011
1476
|
return a.entry.tool_id.localeCompare(b.entry.tool_id)
|
|
1012
|
-
})
|
|
1013
|
-
)
|
|
1477
|
+
})
|
|
1014
1478
|
|
|
1015
1479
|
return pickDiverseAdapterToolNames(ranked, maxResults)
|
|
1016
1480
|
}
|
|
@@ -1047,6 +1511,8 @@ function buildAdapterTool(entry: AdapterManifestEntry): Tool {
|
|
|
1047
1511
|
const primitiveTool = primitiveToolFor(primitive)
|
|
1048
1512
|
const adapterInputSchema = inputSchemaFor(entry)
|
|
1049
1513
|
const adapterInputJSONSchema = inputJSONSchemaFor(entry)
|
|
1514
|
+
const directCheckAdapterRequiresPermission =
|
|
1515
|
+
primitive === 'check' && !ROOT_PRIMITIVE_TOOL_NAMES.has(entry.tool_id)
|
|
1050
1516
|
|
|
1051
1517
|
return buildTool({
|
|
1052
1518
|
name: entry.tool_id,
|
|
@@ -1077,13 +1543,19 @@ function buildAdapterTool(entry: AdapterManifestEntry): Tool {
|
|
|
1077
1543
|
},
|
|
1078
1544
|
|
|
1079
1545
|
isReadOnly(input) {
|
|
1546
|
+
if (directCheckAdapterRequiresPermission) return false
|
|
1080
1547
|
return primitiveTool.isReadOnly(rootInputFor(entry, input))
|
|
1081
1548
|
},
|
|
1082
1549
|
|
|
1083
1550
|
isDestructive(input) {
|
|
1551
|
+
if (directCheckAdapterRequiresPermission) return true
|
|
1084
1552
|
return primitiveTool.isDestructive?.(rootInputFor(entry, input)) ?? false
|
|
1085
1553
|
},
|
|
1086
1554
|
|
|
1555
|
+
async checkPermissions(input, context) {
|
|
1556
|
+
return primitiveTool.checkPermissions(rootInputFor(entry, input), context)
|
|
1557
|
+
},
|
|
1558
|
+
|
|
1087
1559
|
async description() {
|
|
1088
1560
|
return entry.name
|
|
1089
1561
|
},
|
|
@@ -1098,18 +1570,6 @@ function buildAdapterTool(entry: AdapterManifestEntry): Tool {
|
|
|
1098
1570
|
},
|
|
1099
1571
|
|
|
1100
1572
|
async validateInput(input, context) {
|
|
1101
|
-
const directPublicDataChoice = validateDirectPublicDataToolChoice(
|
|
1102
|
-
entry.tool_id,
|
|
1103
|
-
context,
|
|
1104
|
-
input,
|
|
1105
|
-
)
|
|
1106
|
-
if (directPublicDataChoice) return directPublicDataChoice
|
|
1107
|
-
const kmaAviationChoice = validateKmaAviationToolChoice(entry.tool_id, context)
|
|
1108
|
-
if (kmaAviationChoice) return kmaAviationChoice
|
|
1109
|
-
const kmaAnalysisChoice = validateKmaAnalysisToolChoice(entry.tool_id, context)
|
|
1110
|
-
if (kmaAnalysisChoice) return kmaAnalysisChoice
|
|
1111
|
-
const nmcAedChoice = validateNmcAedToolChoice(entry.tool_id, context)
|
|
1112
|
-
if (nmcAedChoice) return nmcAedChoice
|
|
1113
1573
|
if (!resolveAdapter(entry.tool_id)) {
|
|
1114
1574
|
return {
|
|
1115
1575
|
result: false as const,
|
|
@@ -1133,30 +1593,55 @@ function buildAdapterTool(entry: AdapterManifestEntry): Tool {
|
|
|
1133
1593
|
},
|
|
1134
1594
|
|
|
1135
1595
|
async call(input, context) {
|
|
1136
|
-
const
|
|
1596
|
+
const normalizedDocumentInput = normalizeExplicitDocumentArtifactInput(
|
|
1137
1597
|
entry.tool_id,
|
|
1138
|
-
context,
|
|
1139
1598
|
input,
|
|
1599
|
+
context.messages,
|
|
1600
|
+
)
|
|
1601
|
+
const adapterCallInput = concreteAdapterCallInputFor(
|
|
1602
|
+
entry,
|
|
1603
|
+
normalizedDocumentInput,
|
|
1140
1604
|
)
|
|
1141
|
-
|
|
1605
|
+
const result = await dispatchPrimitive({
|
|
1142
1606
|
primitive,
|
|
1143
1607
|
toolName: entry.tool_id,
|
|
1144
|
-
args:
|
|
1608
|
+
args: adapterCallInput,
|
|
1145
1609
|
context,
|
|
1146
1610
|
registry: getOrCreatePendingCallRegistry(),
|
|
1147
1611
|
bridge: getOrCreateUmmayaBridge(),
|
|
1612
|
+
timeoutMs:
|
|
1613
|
+
primitive === 'document'
|
|
1614
|
+
? resolveDocumentPrimitiveTimeoutMs()
|
|
1615
|
+
: undefined,
|
|
1148
1616
|
})
|
|
1617
|
+
return {
|
|
1618
|
+
...result,
|
|
1619
|
+
data: applyDocumentVisualRenderGateToOutput(result.data),
|
|
1620
|
+
}
|
|
1149
1621
|
},
|
|
1150
1622
|
|
|
1151
1623
|
userFacingName(input) {
|
|
1624
|
+
if (DOCUMENT_TOOL_NAMES.has(entry.tool_id)) {
|
|
1625
|
+
return 'Document'
|
|
1626
|
+
}
|
|
1152
1627
|
return primitiveTool.userFacingName(rootInputFor(entry, input ?? {}))
|
|
1153
1628
|
},
|
|
1154
1629
|
|
|
1155
1630
|
mapToolResultToToolResultBlockParam(output, toolUseID) {
|
|
1156
|
-
|
|
1631
|
+
const gatedOutput = applyDocumentVisualRenderGateToOutput(output)
|
|
1632
|
+
const block = primitiveTool.mapToolResultToToolResultBlockParam(gatedOutput, toolUseID)
|
|
1633
|
+
return isDocumentVisualRenderFailedOutput(gatedOutput)
|
|
1634
|
+
? { ...block, is_error: true }
|
|
1635
|
+
: block
|
|
1157
1636
|
},
|
|
1158
1637
|
|
|
1159
1638
|
renderToolUseMessage(input, options) {
|
|
1639
|
+
if (DOCUMENT_TOOL_NAMES.has(entry.tool_id)) {
|
|
1640
|
+
return renderDocumentToolUseMessage(
|
|
1641
|
+
entry.tool_id,
|
|
1642
|
+
input as Record<string, unknown>,
|
|
1643
|
+
)
|
|
1644
|
+
}
|
|
1160
1645
|
const rendered = primitiveTool.renderToolUseMessage(
|
|
1161
1646
|
rootInputFor(entry, input),
|
|
1162
1647
|
options,
|
|
@@ -1165,8 +1650,16 @@ function buildAdapterTool(entry: AdapterManifestEntry): Tool {
|
|
|
1165
1650
|
},
|
|
1166
1651
|
|
|
1167
1652
|
renderToolResultMessage(output, progressMessagesForMessage, options) {
|
|
1653
|
+
const gatedOutput = applyDocumentVisualRenderGateToOutput(output)
|
|
1654
|
+
if (shouldHideSuccessfulIntermediateDocumentResult(gatedOutput)) {
|
|
1655
|
+
return null
|
|
1656
|
+
}
|
|
1657
|
+
const documentResult = renderDocumentToolResultIfPresent(gatedOutput, options)
|
|
1658
|
+
if (documentResult !== null) {
|
|
1659
|
+
return documentResult
|
|
1660
|
+
}
|
|
1168
1661
|
return primitiveTool.renderToolResultMessage?.(
|
|
1169
|
-
|
|
1662
|
+
gatedOutput,
|
|
1170
1663
|
progressMessagesForMessage,
|
|
1171
1664
|
options,
|
|
1172
1665
|
) ?? null
|