ummaya 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (534) hide show
  1. package/README.md +17 -3
  2. package/bin/ummaya +10 -1
  3. package/npm-shrinkwrap.json +253 -2
  4. package/package.json +5 -1
  5. package/prompts/manifest.yaml +2 -2
  6. package/prompts/session_guidance_v1.md +3 -1
  7. package/prompts/system_v1.md +9 -7
  8. package/pyproject.toml +26 -7
  9. package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
  10. package/src/ummaya/_canonical/__init__.py +2 -0
  11. package/src/ummaya/context/builder.py +17 -11
  12. package/src/ummaya/engine/engine.py +30 -113
  13. package/src/ummaya/engine/query.py +20 -0
  14. package/src/ummaya/evidence/__init__.py +44 -0
  15. package/src/ummaya/evidence/__main__.py +7 -0
  16. package/src/ummaya/evidence/dataset_contract.py +193 -0
  17. package/src/ummaya/evidence/document_authoring_cases.py +33 -0
  18. package/src/ummaya/evidence/document_harness.py +313 -0
  19. package/src/ummaya/evidence/document_viewer_ux.py +391 -0
  20. package/src/ummaya/evidence/gates.py +70 -0
  21. package/src/ummaya/evidence/json_types.py +20 -0
  22. package/src/ummaya/evidence/models.py +145 -0
  23. package/src/ummaya/evidence/output_payload.py +89 -0
  24. package/src/ummaya/evidence/payload_documents.py +233 -0
  25. package/src/ummaya/evidence/route_contracts.py +224 -0
  26. package/src/ummaya/evidence/route_helpers.py +150 -0
  27. package/src/ummaya/evidence/runner.py +177 -0
  28. package/src/ummaya/evidence/source_provenance.py +246 -0
  29. package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
  30. package/src/ummaya/evidence/task_registry.py +264 -0
  31. package/src/ummaya/evidence/tool_layer.py +39 -0
  32. package/src/ummaya/evidence/tool_layer_models.py +151 -0
  33. package/src/ummaya/ipc/adapter_manifest_emitter.py +26 -10
  34. package/src/ummaya/ipc/document_intent_normalization.py +185 -0
  35. package/src/ummaya/ipc/frame_schema.py +52 -5
  36. package/src/ummaya/ipc/route_diagnostics.py +73 -0
  37. package/src/ummaya/ipc/stdio.py +2282 -417
  38. package/src/ummaya/llm/client.py +234 -59
  39. package/src/ummaya/llm/config.py +8 -3
  40. package/src/ummaya/llm/reasoning.py +84 -0
  41. package/src/ummaya/primitives/__init__.py +6 -2
  42. package/src/ummaya/primitives/delegation.py +1 -1
  43. package/src/ummaya/primitives/document.py +28 -0
  44. package/src/ummaya/settings.py +0 -3
  45. package/src/ummaya/tools/discovery_bridge.py +34 -2
  46. package/src/ummaya/tools/documents/__init__.py +297 -0
  47. package/src/ummaya/tools/documents/adapter_registry.py +487 -0
  48. package/src/ummaya/tools/documents/archive_container_probe.py +167 -0
  49. package/src/ummaya/tools/documents/artifact_store.py +454 -0
  50. package/src/ummaya/tools/documents/authoring.py +283 -0
  51. package/src/ummaya/tools/documents/baselines.py +114 -0
  52. package/src/ummaya/tools/documents/capability.py +331 -0
  53. package/src/ummaya/tools/documents/contracts.py +112 -0
  54. package/src/ummaya/tools/documents/conversion.py +521 -0
  55. package/src/ummaya/tools/documents/diff.py +275 -0
  56. package/src/ummaya/tools/documents/engines.py +163 -0
  57. package/src/ummaya/tools/documents/evaluation.py +291 -0
  58. package/src/ummaya/tools/documents/explicit_values.py +108 -0
  59. package/src/ummaya/tools/documents/fixtures.py +174 -0
  60. package/src/ummaya/tools/documents/format_completion_audit.py +471 -0
  61. package/src/ummaya/tools/documents/formats/__init__.py +2 -0
  62. package/src/ummaya/tools/documents/formats/archive.py +528 -0
  63. package/src/ummaya/tools/documents/formats/base.py +41 -0
  64. package/src/ummaya/tools/documents/formats/code_file.py +211 -0
  65. package/src/ummaya/tools/documents/formats/data_file.py +272 -0
  66. package/src/ummaya/tools/documents/formats/hwp.py +284 -0
  67. package/src/ummaya/tools/documents/formats/hwpx.py +1837 -0
  68. package/src/ummaya/tools/documents/formats/odf.py +435 -0
  69. package/src/ummaya/tools/documents/formats/ooxml.py +1030 -0
  70. package/src/ummaya/tools/documents/formats/passive.py +766 -0
  71. package/src/ummaya/tools/documents/formats/pdf.py +702 -0
  72. package/src/ummaya/tools/documents/formats/text_web.py +268 -0
  73. package/src/ummaya/tools/documents/hwp_conversion_probe.py +178 -0
  74. package/src/ummaya/tools/documents/hwp_direct_candidate.py +141 -0
  75. package/src/ummaya/tools/documents/inspection.py +289 -0
  76. package/src/ummaya/tools/documents/intake.py +1079 -0
  77. package/src/ummaya/tools/documents/legacy_office_promotion_probe.py +366 -0
  78. package/src/ummaya/tools/documents/models.py +1598 -0
  79. package/src/ummaya/tools/documents/odf_promotion_probe.py +167 -0
  80. package/src/ummaya/tools/documents/orchestrator.py +96 -0
  81. package/src/ummaya/tools/documents/passive_capability_probe.py +251 -0
  82. package/src/ummaya/tools/documents/patch.py +170 -0
  83. package/src/ummaya/tools/documents/pdfa_conformance.py +284 -0
  84. package/src/ummaya/tools/documents/pdfa_promotion_probe.py +198 -0
  85. package/src/ummaya/tools/documents/permissions.py +110 -0
  86. package/src/ummaya/tools/documents/planner.py +616 -0
  87. package/src/ummaya/tools/documents/registry.py +2733 -0
  88. package/src/ummaya/tools/documents/render.py +978 -0
  89. package/src/ummaya/tools/documents/render_comparison.py +113 -0
  90. package/src/ummaya/tools/documents/render_comparison_models.py +74 -0
  91. package/src/ummaya/tools/documents/render_comparison_regions.py +73 -0
  92. package/src/ummaya/tools/documents/render_comparison_style.py +161 -0
  93. package/src/ummaya/tools/documents/reread.py +157 -0
  94. package/src/ummaya/tools/documents/runtime_authoring.py +244 -0
  95. package/src/ummaya/tools/documents/runtime_authoring_bundle.py +76 -0
  96. package/src/ummaya/tools/documents/scorecard.py +184 -0
  97. package/src/ummaya/tools/documents/socratic_planner.py +193 -0
  98. package/src/ummaya/tools/documents/style.py +48 -0
  99. package/src/ummaya/tools/documents/tool_defs.py +523 -0
  100. package/src/ummaya/tools/documents/validate.py +347 -0
  101. package/src/ummaya/tools/executor.py +61 -12
  102. package/src/ummaya/tools/geocoding/kakao_client.py +1 -2
  103. package/src/ummaya/tools/kma/apihub_catalog.py +984 -1
  104. package/src/ummaya/tools/kma/apihub_structured_adapter.py +86 -6
  105. package/src/ummaya/tools/kma/apihub_url_adapter.py +593 -0
  106. package/src/ummaya/tools/kma/apihub_url_catalog.py +296 -0
  107. package/src/ummaya/tools/live_proxy.py +0 -3
  108. package/src/ummaya/tools/location_adapters.py +8 -6
  109. package/src/ummaya/tools/manifest_metadata.py +16 -3
  110. package/src/ummaya/tools/models.py +5 -1
  111. package/src/ummaya/tools/mvp_surface.py +2 -2
  112. package/src/ummaya/tools/nmc/emergency_search.py +8 -6
  113. package/src/ummaya/tools/register_all.py +17 -0
  114. package/src/ummaya/tools/registry.py +10 -1
  115. package/src/ummaya/tools/resolve_location.py +4 -4
  116. package/src/ummaya/tools/routing/__init__.py +59 -0
  117. package/src/ummaya/tools/routing/builder.py +105 -0
  118. package/src/ummaya/tools/routing/cards.py +29 -0
  119. package/src/ummaya/tools/routing/decision_service.py +534 -0
  120. package/src/ummaya/tools/routing/decision_types.py +74 -0
  121. package/src/ummaya/tools/routing/feasibility.py +122 -0
  122. package/src/ummaya/tools/routing/intent.py +17 -0
  123. package/src/ummaya/tools/routing/intent_extractor.py +207 -0
  124. package/src/ummaya/tools/routing/intent_patterns.py +160 -0
  125. package/src/ummaya/tools/routing/intent_public_data.py +150 -0
  126. package/src/ummaya/tools/routing/intent_types.py +48 -0
  127. package/src/ummaya/tools/routing/lint.py +78 -0
  128. package/src/ummaya/tools/routing/metadata.py +174 -0
  129. package/src/ummaya/tools/routing/projection.py +340 -0
  130. package/src/ummaya/tools/routing/retrieval_policy.py +629 -0
  131. package/src/ummaya/tools/routing/schema.py +81 -0
  132. package/src/ummaya/tools/routing/types.py +96 -0
  133. package/src/ummaya/tools/routing_index.py +2 -2
  134. package/src/ummaya/tools/search.py +40 -106
  135. package/src/ummaya/tools/verified_data_go_kr/_manifest.py +115 -25
  136. package/src/ummaya/tools/verified_data_go_kr/airkorea_air_quality.py +109 -4
  137. package/src/ummaya/tools/verified_data_go_kr/nmc_aed_site.py +108 -2
  138. package/src/ummaya/tools/verified_data_go_kr/pps_bid_public_info.py +174 -9
  139. package/src/ummaya/tools/verified_data_go_kr/tago_bus_arrival.py +66 -3
  140. package/src/ummaya/tools/verified_data_go_kr/tago_bus_location.py +12 -2
  141. package/src/ummaya/tools/verified_data_go_kr/tago_bus_route.py +8 -2
  142. package/src/ummaya/tools/verified_data_go_kr/tago_bus_route_station.py +114 -0
  143. package/src/ummaya/tools/verified_data_go_kr/tago_bus_station.py +14 -3
  144. package/src/ummaya/tools/verify_canonical_map.py +21 -0
  145. package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
  146. package/tui/package.json +1 -2
  147. package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
  148. package/tui/src/QueryEngine.ts +12 -4
  149. package/tui/src/bridge/inboundAttachments.ts +3 -3
  150. package/tui/src/cli/handlers/auth.ts +4 -13
  151. package/tui/src/cli/handlers/mcp.tsx +3 -3
  152. package/tui/src/cli/print.ts +69 -18
  153. package/tui/src/cli/update.ts +13 -13
  154. package/tui/src/commands/copy/index.ts +1 -1
  155. package/tui/src/commands/cost/cost.ts +2 -2
  156. package/tui/src/commands/init-verifiers.ts +5 -5
  157. package/tui/src/commands/init.ts +30 -30
  158. package/tui/src/commands/insights.ts +44 -44
  159. package/tui/src/commands/install-github-app/install-github-app.tsx +2 -2
  160. package/tui/src/commands/install-github-app/setupGitHubActions.ts +3 -3
  161. package/tui/src/commands/install-github-app/types.ts +8 -30
  162. package/tui/src/commands/install.tsx +5 -5
  163. package/tui/src/commands/mcp/addCommand.ts +5 -5
  164. package/tui/src/commands/mcp/xaaIdpCommand.ts +2 -2
  165. package/tui/src/commands/plugin/ManageMarketplaces.tsx +2 -2
  166. package/tui/src/commands/plugin/types.ts +6 -28
  167. package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
  168. package/tui/src/commands/reasoning/index.ts +13 -0
  169. package/tui/src/commands/reasoning/reasoning.tsx +177 -0
  170. package/tui/src/commands/rename/generateSessionName.ts +1 -1
  171. package/tui/src/commands/thinkback/thinkback.tsx +3 -3
  172. package/tui/src/commands.ts +2 -0
  173. package/tui/src/components/Feedback.tsx +1 -1
  174. package/tui/src/components/LogoV2/EmergencyTip.tsx +11 -2
  175. package/tui/src/components/LogoV2/WelcomeV2.tsx +1 -3
  176. package/tui/src/components/Messages.tsx +2 -1
  177. package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
  178. package/tui/src/components/Spinner/types.ts +6 -28
  179. package/tui/src/components/Spinner.tsx +2 -2
  180. package/tui/src/components/agents/generateAgent.ts +1 -1
  181. package/tui/src/components/agents/new-agent-creation/types.ts +4 -26
  182. package/tui/src/components/config/EnvSecretIsolatedEditor.tsx +1 -1
  183. package/tui/src/components/design-system/LoadingState.tsx +2 -2
  184. package/tui/src/components/mcp/types.ts +16 -38
  185. package/tui/src/components/messages/AssistantToolUseMessage.tsx +3 -2
  186. package/tui/src/components/messages/UserCrossSessionMessage.ts +16 -4
  187. package/tui/src/components/messages/UserForkBoilerplateMessage.ts +16 -4
  188. package/tui/src/components/messages/UserGitHubWebhookMessage.ts +16 -4
  189. package/tui/src/components/messages/UserToolResultMessage/utils.tsx +3 -2
  190. package/tui/src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.ts +9 -4
  191. package/tui/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.ts +9 -4
  192. package/tui/src/components/primitive/DocumentSocraticReviewBlock.tsx +129 -0
  193. package/tui/src/components/primitive/DocumentToolResultCard.tsx +224 -0
  194. package/tui/src/components/primitive/documentSocraticReview.ts +215 -0
  195. package/tui/src/components/primitive/index.tsx +43 -1
  196. package/tui/src/components/primitive/types.ts +137 -0
  197. package/tui/src/components/ui/option.ts +4 -26
  198. package/tui/src/constants/common.ts +0 -2
  199. package/tui/src/constants/prompts.ts +4 -3
  200. package/tui/src/constants/querySource.ts +4 -26
  201. package/tui/src/entrypoints/sdk/controlTypes.ts +26 -48
  202. package/tui/src/entrypoints/sdk/coreTypes.generated.ts +3 -25
  203. package/tui/src/entrypoints/sdk/runtimeTypes.ts +38 -60
  204. package/tui/src/entrypoints/sdk/sdkUtilityTypes.ts +4 -26
  205. package/tui/src/entrypoints/sdk/settingsTypes.generated.ts +3 -25
  206. package/tui/src/entrypoints/sdk/toolTypes.ts +3 -25
  207. package/tui/src/hooks/toolPermission/handlers/interactiveHandler.ts +10 -0
  208. package/tui/src/hooks/useApiKeyVerification.ts +1 -1
  209. package/tui/src/hooks/useVirtualScroll.ts +1 -1
  210. package/tui/src/ink/ink.tsx +33 -14
  211. package/tui/src/ink/reconciler.ts +2 -3
  212. package/tui/src/ink/render-to-screen.ts +30 -10
  213. package/tui/src/ipc/bridge.ts +62 -15
  214. package/tui/src/ipc/bridgeSingleton.ts +5 -1
  215. package/tui/src/ipc/codec.ts +29 -3
  216. package/tui/src/ipc/frames.generated.ts +407 -312
  217. package/tui/src/ipc/llmClient.ts +279 -76
  218. package/tui/src/ipc/llmTypes.ts +16 -1
  219. package/tui/src/ipc/schema/frame.schema.json +1 -3475
  220. package/tui/src/keybindings/defaultBindings.ts +4 -0
  221. package/tui/src/main.tsx +32 -11
  222. package/tui/src/native-ts/file-index/index.ts +33 -3
  223. package/tui/src/observability/surface.ts +2 -2
  224. package/tui/src/probes/toolRegistryProbe.tsx +3 -1
  225. package/tui/src/projectOnboardingState.ts +7 -6
  226. package/tui/src/query/chatMessageTypes.ts +18 -0
  227. package/tui/src/query/chatMessagesBuilder.ts +1 -1
  228. package/tui/src/query/deps.ts +1 -1
  229. package/tui/src/query/messageGuards.ts +106 -0
  230. package/tui/src/query/publicDataTerminalRepair.ts +384 -0
  231. package/tui/src/query/run.ts +1075 -0
  232. package/tui/src/query/supportBoundary.ts +168 -0
  233. package/tui/src/query/toolResultErrors.ts +103 -0
  234. package/tui/src/query/toolRunner.ts +687 -0
  235. package/tui/src/query/unavailableToolRepair.ts +118 -0
  236. package/tui/src/query.ts +9 -1721
  237. package/tui/src/screens/REPL.tsx +42 -31
  238. package/tui/src/services/api/adapterManifest.ts +4 -0
  239. package/tui/src/services/api/backendChat/events.ts +117 -0
  240. package/tui/src/services/api/backendChat/finalMessage.ts +40 -0
  241. package/tui/src/services/api/backendChat/frame.ts +9 -0
  242. package/tui/src/services/api/backendChat/streaming.ts +430 -0
  243. package/tui/src/services/api/backendChat/types.ts +62 -0
  244. package/tui/src/services/api/backendChat.ts +1 -0
  245. package/tui/src/services/api/client.ts +98 -14
  246. package/tui/src/services/api/errorUtils.ts +5 -5
  247. package/tui/src/services/api/errors.ts +1 -1
  248. package/tui/src/services/api/logging.ts +1 -1
  249. package/tui/src/services/api/ummaya/evidence.ts +194 -0
  250. package/tui/src/services/api/ummaya/messages.ts +255 -0
  251. package/tui/src/services/api/ummaya/nonStreaming.ts +66 -0
  252. package/tui/src/services/api/ummaya/provider.ts +200 -0
  253. package/tui/src/services/api/ummaya/reasoning.ts +24 -0
  254. package/tui/src/services/api/ummaya/request.ts +200 -0
  255. package/tui/src/services/api/ummaya/selectionContext.ts +240 -0
  256. package/tui/src/services/api/ummaya/streaming.ts +365 -0
  257. package/tui/src/services/api/ummaya/streamingPayload.ts +129 -0
  258. package/tui/src/services/api/ummaya/streamingReader.ts +40 -0
  259. package/tui/src/services/api/ummaya/toolSelection.ts +217 -0
  260. package/tui/src/services/api/ummaya/types.ts +110 -0
  261. package/tui/src/services/api/ummaya/usage.ts +30 -0
  262. package/tui/src/services/api/ummaya.ts +26 -364
  263. package/tui/src/services/api/withRetry.ts +1 -1
  264. package/tui/src/services/awaySummary.ts +2 -2
  265. package/tui/src/services/claudeAiLimits.ts +1 -1
  266. package/tui/src/services/compact/autoCompact.ts +1 -1
  267. package/tui/src/services/compact/compact.ts +1 -1
  268. package/tui/src/services/lsp/types.ts +8 -30
  269. package/tui/src/services/tips/types.ts +6 -28
  270. package/tui/src/services/tokenEstimation.ts +1 -1
  271. package/tui/src/services/toolRegistry/bootGuard.ts +5 -5
  272. package/tui/src/services/toolUseSummary/toolUseSummaryGenerator.ts +1 -1
  273. package/tui/src/services/tools/toolExecution.ts +94 -1
  274. package/tui/src/skills/bundled/stuck.ts +12 -12
  275. package/tui/src/state/AppStateStore.ts +7 -0
  276. package/tui/src/store/pendingPermissionSlot.ts +1 -1
  277. package/tui/src/store/session-store.ts +10 -36
  278. package/tui/src/stubs/any-stub.ts +15 -10
  279. package/tui/src/stubs/color-diff-napi.ts +37 -23
  280. package/tui/src/stubs/globals.d.ts +3 -3
  281. package/tui/src/stubs/macro-preload.ts +23 -12
  282. package/tui/src/tools/AdapterTool/AdapterTool.ts +1239 -163
  283. package/tui/src/tools/AdapterTool/routeDiagnostics.ts +75 -0
  284. package/tui/src/tools/AgentTool/AgentTool.tsx +84 -1371
  285. package/tui/src/tools/AgentTool/agentToolHandoff.ts +114 -0
  286. package/tui/src/tools/AgentTool/agentToolPartialResult.ts +16 -0
  287. package/tui/src/tools/AgentTool/agentToolProgress.ts +32 -0
  288. package/tui/src/tools/AgentTool/agentToolResolver.ts +161 -0
  289. package/tui/src/tools/AgentTool/agentToolResult.ts +163 -0
  290. package/tui/src/tools/AgentTool/agentToolUtils.ts +14 -686
  291. package/tui/src/tools/AgentTool/asyncAgentLifecycle.ts +208 -0
  292. package/tui/src/tools/AgentTool/asyncLifecycle.ts +153 -0
  293. package/tui/src/tools/AgentTool/backgroundedCompletion.ts +126 -0
  294. package/tui/src/tools/AgentTool/backgroundedLifecycle.ts +174 -0
  295. package/tui/src/tools/AgentTool/foregroundBackground.ts +83 -0
  296. package/tui/src/tools/AgentTool/foregroundDrain.tsx +133 -0
  297. package/tui/src/tools/AgentTool/foregroundFinalize.ts +98 -0
  298. package/tui/src/tools/AgentTool/foregroundLifecycle.tsx +237 -0
  299. package/tui/src/tools/AgentTool/foregroundProgress.tsx +169 -0
  300. package/tui/src/tools/AgentTool/foregroundTask.ts +89 -0
  301. package/tui/src/tools/AgentTool/forkSubagent.ts +1 -12
  302. package/tui/src/tools/AgentTool/forkSubagentGate.ts +34 -0
  303. package/tui/src/tools/AgentTool/launchRouting.ts +203 -0
  304. package/tui/src/tools/AgentTool/lifecycle.ts +244 -0
  305. package/tui/src/tools/AgentTool/mcpRouting.ts +73 -0
  306. package/tui/src/tools/AgentTool/orchestrationSupport.ts +70 -0
  307. package/tui/src/tools/AgentTool/permissions.ts +39 -0
  308. package/tui/src/tools/AgentTool/promptSetup.ts +181 -0
  309. package/tui/src/tools/AgentTool/remoteRouting.ts +62 -0
  310. package/tui/src/tools/AgentTool/resultMapping.ts +116 -0
  311. package/tui/src/tools/AgentTool/resumeAgent.ts +39 -107
  312. package/tui/src/tools/AgentTool/resumeAgentHelpers.ts +140 -0
  313. package/tui/src/tools/AgentTool/runAgent.ts +1 -1
  314. package/tui/src/tools/AgentTool/runtimeConfig.ts +57 -0
  315. package/tui/src/tools/AgentTool/schemas.ts +196 -0
  316. package/tui/src/tools/AgentTool/sourceVerificationPropagation.ts +263 -0
  317. package/tui/src/tools/AgentTool/worktreeLifecycle.ts +105 -0
  318. package/tui/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +174 -202
  319. package/tui/src/tools/BashTool/BashTool.tsx +71 -1072
  320. package/tui/src/tools/BashTool/bashCommandHelpers.ts +12 -12
  321. package/tui/src/tools/BashTool/bashPermissions/astPreflight.ts +173 -0
  322. package/tui/src/tools/BashTool/bashPermissions/classifierChecks.ts +199 -0
  323. package/tui/src/tools/BashTool/bashPermissions/compoundGuards.ts +53 -0
  324. package/tui/src/tools/BashTool/bashPermissions/constants.ts +99 -0
  325. package/tui/src/tools/BashTool/bashPermissions/index.ts +38 -0
  326. package/tui/src/tools/BashTool/bashPermissions/legacyMisparsing.ts +62 -0
  327. package/tui/src/tools/BashTool/bashPermissions/main.ts +135 -0
  328. package/tui/src/tools/BashTool/bashPermissions/normalizedCommands.ts +33 -0
  329. package/tui/src/tools/BashTool/bashPermissions/operatorFlow.ts +98 -0
  330. package/tui/src/tools/BashTool/bashPermissions/permissionChecks.ts +200 -0
  331. package/tui/src/tools/BashTool/bashPermissions/prefixSuggestions.ts +88 -0
  332. package/tui/src/tools/BashTool/bashPermissions/promptClassifierRules.ts +125 -0
  333. package/tui/src/tools/BashTool/bashPermissions/ruleDelegates.ts +19 -0
  334. package/tui/src/tools/BashTool/bashPermissions/ruleMatching.ts +145 -0
  335. package/tui/src/tools/BashTool/bashPermissions/sandboxAutoAllow.ts +75 -0
  336. package/tui/src/tools/BashTool/bashPermissions/subcommandFlow.ts +205 -0
  337. package/tui/src/tools/BashTool/bashPermissions/subcommandGuards.ts +73 -0
  338. package/tui/src/tools/BashTool/bashPermissions/subcommandResultHelpers.ts +116 -0
  339. package/tui/src/tools/BashTool/bashPermissions/types.ts +26 -0
  340. package/tui/src/tools/BashTool/bashPermissions/wrapperStripping.ts +139 -0
  341. package/tui/src/tools/BashTool/bashPermissions.ts +26 -2621
  342. package/tui/src/tools/BashTool/call.ts +202 -0
  343. package/tui/src/tools/BashTool/callLoader.ts +35 -0
  344. package/tui/src/tools/BashTool/commandClassification.ts +151 -0
  345. package/tui/src/tools/BashTool/commandClassificationLoader.ts +40 -0
  346. package/tui/src/tools/BashTool/cwdReset.ts +33 -0
  347. package/tui/src/tools/BashTool/lineTruncation.ts +11 -0
  348. package/tui/src/tools/BashTool/modeValidation.ts +13 -1
  349. package/tui/src/tools/BashTool/outputPersistence.ts +42 -0
  350. package/tui/src/tools/BashTool/permissionClassification.ts +66 -0
  351. package/tui/src/tools/BashTool/permissionLoader.ts +44 -0
  352. package/tui/src/tools/BashTool/resultLoader.ts +29 -0
  353. package/tui/src/tools/BashTool/resultMapping.ts +83 -0
  354. package/tui/src/tools/BashTool/sandboxPolicy.ts +79 -0
  355. package/tui/src/tools/BashTool/schemas.ts +65 -0
  356. package/tui/src/tools/BashTool/sedEditExecution.ts +59 -0
  357. package/tui/src/tools/BashTool/shellExecution.tsx +245 -0
  358. package/tui/src/tools/BashTool/shellOutputUtils.ts +85 -0
  359. package/tui/src/tools/BashTool/shellPermissionGauntlet.ts +97 -0
  360. package/tui/src/tools/BashTool/uiLoader.ts +37 -0
  361. package/tui/src/tools/BriefTool/upload.ts +1 -1
  362. package/tui/src/tools/CalculatorTool/parser.ts +2 -2
  363. package/tui/src/tools/DocumentPrimitive/DocumentPrimitive.ts +262 -0
  364. package/tui/src/tools/DocumentPrimitive/dispatchNormalization.ts +270 -0
  365. package/tui/src/tools/DocumentPrimitive/documentDestinationPath.ts +18 -0
  366. package/tui/src/tools/DocumentPrimitive/documentMutationGuard.ts +22 -0
  367. package/tui/src/tools/DocumentPrimitive/documentPatchNormalization.ts +248 -0
  368. package/tui/src/tools/DocumentPrimitive/documentSourceVerification.ts +245 -0
  369. package/tui/src/tools/DocumentPrimitive/documentSourceVerificationFields.ts +103 -0
  370. package/tui/src/tools/DocumentPrimitive/modelVisibleOutput.ts +40 -0
  371. package/tui/src/tools/DocumentPrimitive/prompt.ts +35 -0
  372. package/tui/src/tools/FileEditTool/FileEditTool.ts +9 -507
  373. package/tui/src/tools/FileEditTool/call.ts +228 -0
  374. package/tui/src/tools/FileEditTool/validateInput.ts +196 -0
  375. package/tui/src/tools/FileReadTool/imageProcessor.ts +13 -0
  376. package/tui/src/tools/FileWriteTool/FileWriteTool.ts +7 -300
  377. package/tui/src/tools/FileWriteTool/call.ts +223 -0
  378. package/tui/src/tools/FileWriteTool/validateInput.ts +80 -0
  379. package/tui/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +19 -3
  380. package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +48 -29
  381. package/tui/src/tools/LookupPrimitive/prompt.ts +6 -7
  382. package/tui/src/tools/MCPTool/trustPolicy.ts +118 -0
  383. package/tui/src/tools/McpAuthTool/McpAuthTool.ts +21 -3
  384. package/tui/src/tools/NotebookEditTool/NotebookEditTool.ts +7 -326
  385. package/tui/src/tools/NotebookEditTool/call.ts +254 -0
  386. package/tui/src/tools/NotebookEditTool/notebookModel.ts +51 -0
  387. package/tui/src/tools/NotebookEditTool/validateInput.ts +142 -0
  388. package/tui/src/tools/PowerShellTool/PowerShellTool.tsx +46 -937
  389. package/tui/src/tools/PowerShellTool/acceptEditsCommandValidation.ts +162 -0
  390. package/tui/src/tools/PowerShellTool/call.ts +179 -0
  391. package/tui/src/tools/PowerShellTool/callLoader.ts +37 -0
  392. package/tui/src/tools/PowerShellTool/commandClassification.ts +86 -0
  393. package/tui/src/tools/PowerShellTool/modeValidation.ts +25 -332
  394. package/tui/src/tools/PowerShellTool/outputPersistence.ts +42 -0
  395. package/tui/src/tools/PowerShellTool/permissionClassification.ts +28 -0
  396. package/tui/src/tools/PowerShellTool/resultLoader.ts +31 -0
  397. package/tui/src/tools/PowerShellTool/resultMapping.ts +75 -0
  398. package/tui/src/tools/PowerShellTool/schemas.ts +40 -0
  399. package/tui/src/tools/PowerShellTool/shellExecution.tsx +258 -0
  400. package/tui/src/tools/PowerShellTool/symlinkModeValidation.ts +44 -0
  401. package/tui/src/tools/PowerShellTool/uiLoader.ts +37 -0
  402. package/tui/src/tools/PowerShellTool/validation.ts +39 -0
  403. package/tui/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +19 -3
  404. package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +30 -19
  405. package/tui/src/tools/ResolveLocationPrimitive/prompt.ts +2 -6
  406. package/tui/src/tools/SkillTool/SkillTool.ts +2 -2
  407. package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +51 -18
  408. package/tui/src/tools/TaskCreateTool/TaskCreateTool.ts +16 -2
  409. package/tui/src/tools/TaskGetTool/TaskGetTool.ts +23 -3
  410. package/tui/src/tools/TaskListTool/TaskListTool.ts +22 -4
  411. package/tui/src/tools/TaskOutputTool/TaskOutputTool.tsx +46 -547
  412. package/tui/src/tools/TaskOutputTool/lookup.ts +216 -0
  413. package/tui/src/tools/TaskOutputTool/render.tsx +257 -0
  414. package/tui/src/tools/TaskOutputTool/schemas.ts +55 -0
  415. package/tui/src/tools/TaskOutputTool/serialization.ts +36 -0
  416. package/tui/src/tools/TaskStopTool/TaskStopTool.ts +10 -0
  417. package/tui/src/tools/TaskUpdateTool/TaskUpdateTool.ts +14 -364
  418. package/tui/src/tools/TaskUpdateTool/completion.ts +62 -0
  419. package/tui/src/tools/TaskUpdateTool/schemas.ts +62 -0
  420. package/tui/src/tools/TaskUpdateTool/serialization.ts +46 -0
  421. package/tui/src/tools/TaskUpdateTool/statusUpdate.ts +247 -0
  422. package/tui/src/tools/TodoWriteTool/TodoWriteTool.ts +21 -2
  423. package/tui/src/tools/ToolSearchTool/ToolSearchTool.ts +21 -302
  424. package/tui/src/tools/ToolSearchTool/ccSupportTools.ts +223 -0
  425. package/tui/src/tools/ToolSearchTool/descriptionCache.ts +50 -0
  426. package/tui/src/tools/ToolSearchTool/keywordSearch.ts +216 -0
  427. package/tui/src/tools/ToolSearchTool/prompt.ts +10 -4
  428. package/tui/src/tools/ToolSearchTool/resultMapping.ts +30 -0
  429. package/tui/src/tools/ToolSearchTool/schemas.ts +30 -0
  430. package/tui/src/tools/ToolSearchTool/searchPool.ts +47 -0
  431. package/tui/src/tools/ToolSearchTool/supportIntentHints.ts +140 -0
  432. package/tui/src/tools/TranslateTool/TranslateTool.ts +1 -1
  433. package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +27 -10
  434. package/tui/src/tools/WebFetchTool/WebFetchTool.ts +43 -138
  435. package/tui/src/tools/WebFetchTool/call.ts +227 -0
  436. package/tui/src/tools/WebFetchTool/resolvedAddressSafety.ts +78 -0
  437. package/tui/src/tools/WebFetchTool/sourceVerification.ts +204 -0
  438. package/tui/src/tools/WebFetchTool/types.ts +23 -0
  439. package/tui/src/tools/WebFetchTool/urlSafety.ts +181 -0
  440. package/tui/src/tools/WebFetchTool/utils.ts +1 -1
  441. package/tui/src/tools/WebSearchTool/UI.tsx +0 -1
  442. package/tui/src/tools/WebSearchTool/WebSearchTool.ts +9 -313
  443. package/tui/src/tools/WebSearchTool/call.ts +33 -0
  444. package/tui/src/tools/WebSearchTool/responseMapping.ts +190 -0
  445. package/tui/src/tools/WebSearchTool/resultBlock.ts +47 -0
  446. package/tui/src/tools/WebSearchTool/schemas.ts +47 -0
  447. package/tui/src/tools/WebSearchTool/toolSchema.ts +12 -0
  448. package/tui/src/tools/WorkspaceToolAdapter/WorkspaceToolAdapter.ts +79 -0
  449. package/tui/src/tools/WorkspaceToolAdapter/allowedRootPolicy.ts +85 -0
  450. package/tui/src/tools/WorkspaceToolAdapter/documentFormatGuards.ts +73 -0
  451. package/tui/src/tools/WorkspaceToolAdapter/inputNormalization.ts +105 -0
  452. package/tui/src/tools/WorkspaceToolAdapter/mcpExposurePolicy.ts +64 -0
  453. package/tui/src/tools/WorkspaceToolAdapter/toolDefFactory.ts +215 -0
  454. package/tui/src/tools/WorkspaceToolAdapter/toolNames.ts +6 -0
  455. package/tui/src/tools/WorkspaceToolAdapter/workspacePolicy.ts +15 -0
  456. package/tui/src/tools/_shared/citizenUserText.ts +49 -0
  457. package/tui/src/tools/_shared/dispatchPrimitive.ts +6 -6
  458. package/tui/src/tools/_shared/documentChangeToPatch.ts +125 -0
  459. package/tui/src/tools/_shared/documentDispatchArguments.ts +87 -0
  460. package/tui/src/tools/_shared/documentPrimitiveTimeout.ts +13 -0
  461. package/tui/src/tools/_shared/documentToolResultRender.ts +98 -0
  462. package/tui/src/tools/_shared/locationInputRepair.ts +112 -0
  463. package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
  464. package/tui/src/tools/_shared/rootPrimitiveInput.ts +68 -0
  465. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPatterns.ts +58 -0
  466. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPrompt.ts +271 -0
  467. package/tui/src/tools/_shared/toolChoiceRepair/documentRepair.ts +452 -0
  468. package/tui/src/tools/_shared/toolChoiceRepair/messageAccess.ts +80 -0
  469. package/tui/src/tools/_shared/toolChoiceRepair/publicDataRepair.ts +92 -0
  470. package/tui/src/tools/_shared/toolChoiceRepair/supportRepair.ts +135 -0
  471. package/tui/src/tools/_shared/toolChoiceRepair.ts +61 -0
  472. package/tui/src/tools/shared/mockDisclaimer.ts +1 -1
  473. package/tui/src/tools.ts +39 -190
  474. package/tui/src/types/fileSuggestion.ts +4 -26
  475. package/tui/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +186 -148
  476. package/tui/src/types/generated/events_mono/common/v1/auth.ts +25 -11
  477. package/tui/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +47 -30
  478. package/tui/src/types/generated/google/protobuf/timestamp.ts +21 -7
  479. package/tui/src/types/message.ts +80 -102
  480. package/tui/src/types/messageQueueTypes.ts +6 -28
  481. package/tui/src/types/notebook.ts +16 -38
  482. package/tui/src/types/statusLine.ts +4 -26
  483. package/tui/src/types/tools.ts +24 -46
  484. package/tui/src/types/utils.ts +6 -28
  485. package/tui/src/upstreamproxy/relay.ts +7 -3
  486. package/tui/src/upstreamproxy/upstreamproxy.ts +1 -1
  487. package/tui/src/utils/assistantMessageFactories.ts +9 -3
  488. package/tui/src/utils/attachments.ts +1 -1
  489. package/tui/src/utils/auth.ts +129 -139
  490. package/tui/src/utils/bash/ast.ts +23 -23
  491. package/tui/src/utils/bash/bashParser.ts +5 -5
  492. package/tui/src/utils/billing.ts +1 -1
  493. package/tui/src/utils/collapseReadSearch.ts +3 -3
  494. package/tui/src/utils/cronTasks.ts +1 -1
  495. package/tui/src/utils/execFileNoThrow.ts +1 -1
  496. package/tui/src/utils/filePersistence/types.ts +16 -38
  497. package/tui/src/utils/forkedAgent.ts +1 -1
  498. package/tui/src/utils/gracefulShutdown.ts +4 -4
  499. package/tui/src/utils/heapDumpService.ts +12 -8
  500. package/tui/src/utils/hooks/apiQueryHookHelper.ts +1 -1
  501. package/tui/src/utils/hooks/execPromptHook.ts +1 -1
  502. package/tui/src/utils/hooks/skillImprovement.ts +1 -1
  503. package/tui/src/utils/kExaoneReasoning.ts +138 -0
  504. package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
  505. package/tui/src/utils/messages.ts +19 -0
  506. package/tui/src/utils/migrateSessions.ts +3 -3
  507. package/tui/src/utils/model/model.ts +6 -6
  508. package/tui/src/utils/multiToolLayout.ts +13 -0
  509. package/tui/src/utils/permissions/yoloClassifier.ts +1 -1
  510. package/tui/src/utils/plugins/headlessPluginInstall.ts +1 -1
  511. package/tui/src/utils/plugins/mcpPluginIntegration.ts +1 -1
  512. package/tui/src/utils/plugins/mcpbHandler.ts +1 -1
  513. package/tui/src/utils/plugins/pluginLoader.ts +8 -8
  514. package/tui/src/utils/processUserInput/processSlashCommand.tsx +2 -2
  515. package/tui/src/utils/processUserInput/processUserInput.ts +26 -0
  516. package/tui/src/utils/protectedNamespace.ts +5 -3
  517. package/tui/src/utils/rawJsonToolCall.ts +242 -0
  518. package/tui/src/utils/ripgrep.ts +16 -7
  519. package/tui/src/utils/sessionTitle.ts +1 -1
  520. package/tui/src/utils/settings/applySettingsChange.ts +4 -0
  521. package/tui/src/utils/settings/permissionValidation.ts +14 -2
  522. package/tui/src/utils/settings/types.ts +9 -3
  523. package/tui/src/utils/shell/prefix.ts +1 -1
  524. package/tui/src/utils/sideQuery.ts +1 -1
  525. package/tui/src/utils/stats.ts +1 -1
  526. package/tui/src/utils/systemThemeWatcher.ts +13 -3
  527. package/tui/src/utils/teleport.tsx +1 -1
  528. package/uv.lock +394 -22
  529. package/assets/copilot-gate-logo.svg +0 -58
  530. package/assets/govon-logo.svg +0 -40
  531. package/src/ummaya/eval/__init__.py +0 -5
  532. package/src/ummaya/eval/retrieval.py +0 -713
  533. package/tui/src/services/api/claude.ts +0 -3510
  534. package/tui/src/utils/messageStream.ts +0 -186
@@ -3,7 +3,7 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
- from pydantic import BaseModel, ConfigDict, Field
6
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
7
7
 
8
8
  from ummaya.tools.executor import ToolExecutor
9
9
  from ummaya.tools.models import GovAPITool
@@ -21,11 +21,29 @@ class AirKoreaAirQualityInput(BaseModel):
21
21
 
22
22
  model_config = ConfigDict(extra="forbid")
23
23
 
24
- sido_name: str = Field(..., min_length=1, description="Korean province/city name.")
24
+ sido_name: str = Field(
25
+ ...,
26
+ min_length=1,
27
+ description="AirKorea short province/city name, e.g. 서울, 부산, 경기.",
28
+ )
25
29
  page_no: int = Field(default=1, ge=1, description="Page number.")
26
- num_of_rows: int = Field(default=10, ge=1, le=100, description="Rows per page.")
30
+ num_of_rows: int = Field(
31
+ default=100,
32
+ ge=1,
33
+ le=100,
34
+ description="Rows per page; 100 captures all city/province stations in normal use.",
35
+ )
27
36
  ver: str = Field(default="1.0", description="AirKorea response version.")
28
37
 
38
+ @field_validator("sido_name", mode="before")
39
+ @classmethod
40
+ def normalize_sido_name(cls, value: object) -> object:
41
+ """AirKorea returns rows for short 시도 names, not full 행정명 names."""
42
+
43
+ if not isinstance(value, str):
44
+ return value
45
+ return _normalize_airkorea_sido_name(value)
46
+
29
47
 
30
48
  SPEC = require_spec("airkorea_ctprvn_air_quality")
31
49
  INPUT_SCHEMA = AirKoreaAirQualityInput
@@ -39,7 +57,94 @@ async def handle(
39
57
  ) -> dict[str, object]:
40
58
  """Fetch or replay AirKorea air quality rows."""
41
59
 
42
- return await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
60
+ output = await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
61
+ return _enrich_air_quality_grades(output)
62
+
63
+
64
+ _AIRKOREA_GRADE_LABELS: dict[str, str] = {
65
+ "1": "좋음",
66
+ "2": "보통",
67
+ "3": "나쁨",
68
+ "4": "매우나쁨",
69
+ }
70
+
71
+ _AIRKOREA_ITEM_NAMES: dict[str, str] = {
72
+ "khai": "통합대기환경지수(CAI)",
73
+ "pm10": "미세먼지(PM10)",
74
+ "pm25": "초미세먼지(PM2.5)",
75
+ "o3": "오존(O3)",
76
+ "no2": "이산화질소(NO2)",
77
+ "co": "일산화탄소(CO)",
78
+ "so2": "아황산가스(SO2)",
79
+ }
80
+
81
+ _AIRKOREA_SIDO_ALIASES: dict[str, str] = {
82
+ "서울특별시": "서울",
83
+ "부산광역시": "부산",
84
+ "대구광역시": "대구",
85
+ "인천광역시": "인천",
86
+ "광주광역시": "광주",
87
+ "대전광역시": "대전",
88
+ "울산광역시": "울산",
89
+ "세종특별자치시": "세종",
90
+ "경기도": "경기",
91
+ "강원특별자치도": "강원",
92
+ "강원도": "강원",
93
+ "충청북도": "충북",
94
+ "충청남도": "충남",
95
+ "전북특별자치도": "전북",
96
+ "전라북도": "전북",
97
+ "전라남도": "전남",
98
+ "경상북도": "경북",
99
+ "경상남도": "경남",
100
+ "제주특별자치도": "제주",
101
+ "제주도": "제주",
102
+ }
103
+
104
+
105
+ def _normalize_airkorea_sido_name(value: str) -> str:
106
+ normalized = value.strip()
107
+ return _AIRKOREA_SIDO_ALIASES.get(normalized, normalized)
108
+
109
+
110
+ def _enrich_air_quality_grades(output: dict[str, object]) -> dict[str, object]:
111
+ items = output.get("items")
112
+ if not isinstance(items, list):
113
+ return output
114
+ enriched_items: list[dict[str, object]] = []
115
+ changed = False
116
+ for item in items:
117
+ if not isinstance(item, dict):
118
+ continue
119
+ record_raw = item.get("record")
120
+ if not isinstance(record_raw, dict):
121
+ enriched_items.append(item)
122
+ continue
123
+ record = dict(record_raw)
124
+ for prefix, name_ko in _AIRKOREA_ITEM_NAMES.items():
125
+ name_key = f"{prefix}NameKo"
126
+ if name_key not in record:
127
+ record[name_key] = name_ko
128
+ changed = True
129
+ grade_value = record.get(f"{prefix}Grade")
130
+ label = _airkorea_grade_label(grade_value)
131
+ if label is not None:
132
+ record[f"{prefix}GradeLabelKo"] = label
133
+ changed = True
134
+ next_item = dict(item)
135
+ next_item["record"] = record
136
+ enriched_items.append(next_item)
137
+ if not changed:
138
+ return output
139
+ enriched_output = dict(output)
140
+ enriched_output["items"] = enriched_items
141
+ return enriched_output
142
+
143
+
144
+ def _airkorea_grade_label(value: object) -> str | None:
145
+ if value is None:
146
+ return None
147
+ return _AIRKOREA_GRADE_LABELS.get(str(value).strip())
43
148
 
44
149
 
45
150
  def register(registry: ToolRegistry, executor: ToolExecutor) -> None:
@@ -3,7 +3,9 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
- from pydantic import BaseModel, ConfigDict, Field
6
+ import math
7
+
8
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
7
9
 
8
10
  from ummaya.tools.executor import ToolExecutor
9
11
  from ummaya.tools.models import GovAPITool
@@ -25,6 +27,30 @@ class NmcAedSiteInput(BaseModel):
25
27
  q1: str = Field(..., min_length=1, description="District/county name.")
26
28
  page_no: int = Field(default=1, ge=1, description="Page number.")
27
29
  num_of_rows: int = Field(default=10, ge=1, le=100, description="Rows per page.")
30
+ origin_lat: float | None = Field(
31
+ default=None,
32
+ ge=-90,
33
+ le=90,
34
+ description=(
35
+ "Optional original query latitude for client-side distance sorting. "
36
+ "Not sent to the upstream NMC AED API."
37
+ ),
38
+ )
39
+ origin_lon: float | None = Field(
40
+ default=None,
41
+ ge=-180,
42
+ le=180,
43
+ description=(
44
+ "Optional original query longitude for client-side distance sorting. "
45
+ "Not sent to the upstream NMC AED API."
46
+ ),
47
+ )
48
+
49
+ @model_validator(mode="after")
50
+ def _origin_pair_is_complete(self) -> NmcAedSiteInput:
51
+ if (self.origin_lat is None) ^ (self.origin_lon is None):
52
+ raise ValueError("origin_lat and origin_lon must be supplied together")
53
+ return self
28
54
 
29
55
 
30
56
  SPEC = require_spec("nmc_aed_site_locate")
@@ -39,7 +65,87 @@ async def handle(
39
65
  ) -> dict[str, object]:
40
66
  """Fetch or replay NMC AED rows."""
41
67
 
42
- return await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
68
+ output = await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
69
+ if input_model.origin_lat is None or input_model.origin_lon is None:
70
+ return output
71
+ _sort_items_by_origin_distance(
72
+ output,
73
+ origin_lat=input_model.origin_lat,
74
+ origin_lon=input_model.origin_lon,
75
+ )
76
+ return output
77
+
78
+
79
+ def _sort_items_by_origin_distance(
80
+ output: dict[str, object],
81
+ *,
82
+ origin_lat: float,
83
+ origin_lon: float,
84
+ ) -> None:
85
+ items = output.get("items")
86
+ if not isinstance(items, list):
87
+ return
88
+ for item in items:
89
+ if not isinstance(item, dict):
90
+ continue
91
+ record = item.get("record")
92
+ if not isinstance(record, dict):
93
+ continue
94
+ lat = _as_float(record.get("wgs84Lat"))
95
+ lon = _as_float(record.get("wgs84Lon"))
96
+ if lat is None or lon is None:
97
+ continue
98
+ distance_km = round(
99
+ _haversine_km(
100
+ lat1=origin_lat,
101
+ lon1=origin_lon,
102
+ lat2=lat,
103
+ lon2=lon,
104
+ ),
105
+ 3,
106
+ )
107
+ record["distance"] = distance_km
108
+ record["distance_km"] = distance_km
109
+ record["distance_unit"] = "km"
110
+ items.sort(key=_distance_sort_key)
111
+
112
+
113
+ def _distance_sort_key(item: object) -> tuple[int, float]:
114
+ if not isinstance(item, dict):
115
+ return (1, 0.0)
116
+ record = item.get("record")
117
+ if not isinstance(record, dict):
118
+ return (1, 0.0)
119
+ distance = _as_float(record.get("distance_km"))
120
+ if distance is None:
121
+ return (1, 0.0)
122
+ return (0, distance)
123
+
124
+
125
+ def _as_float(value: object) -> float | None:
126
+ if isinstance(value, bool):
127
+ return None
128
+ if isinstance(value, int | float):
129
+ return float(value)
130
+ if not isinstance(value, str):
131
+ return None
132
+ try:
133
+ return float(value.strip())
134
+ except ValueError:
135
+ return None
136
+
137
+
138
+ def _haversine_km(*, lat1: float, lon1: float, lat2: float, lon2: float) -> float:
139
+ radius_km = 6371.0088
140
+ phi1 = math.radians(lat1)
141
+ phi2 = math.radians(lat2)
142
+ delta_phi = math.radians(lat2 - lat1)
143
+ delta_lambda = math.radians(lon2 - lon1)
144
+ a = (
145
+ math.sin(delta_phi / 2.0) ** 2
146
+ + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2.0) ** 2
147
+ )
148
+ return radius_km * 2.0 * math.atan2(math.sqrt(a), math.sqrt(1.0 - a))
43
149
 
44
150
 
45
151
  def register(registry: ToolRegistry, executor: ToolExecutor) -> None:
@@ -3,9 +3,10 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ from datetime import datetime, timedelta
6
7
  from typing import Literal
7
8
 
8
- from pydantic import BaseModel, ConfigDict, Field
9
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
9
10
 
10
11
  from ummaya.tools.executor import ToolExecutor
11
12
  from ummaya.tools.models import GovAPITool
@@ -17,27 +18,113 @@ from ummaya.tools.verified_data_go_kr._factory import (
17
18
  )
18
19
  from ummaya.tools.verified_data_go_kr._manifest import require_spec
19
20
 
21
+ _PPS_DATETIME_FORMAT = "%Y%m%d%H%M"
22
+ _PPS_MAX_SEARCH_WINDOW = timedelta(days=31)
23
+
20
24
 
21
25
  class PpsBidPublicInfoInput(BaseModel):
22
26
  """Input for PPS bid public information."""
23
27
 
24
28
  model_config = ConfigDict(extra="forbid")
25
29
 
26
- inqry_div: Literal["2"] = Field(
27
- default="2",
30
+ inqry_div: Literal["1", "2"] = Field(
31
+ default="1",
32
+ description=(
33
+ "Official PPS inqryDiv. Use '1' for bid notice publication datetime "
34
+ "(pblancDate) searches such as 'this week posted notices'; use '2' "
35
+ "for bid opening datetime (opengDt) searches."
36
+ ),
37
+ )
38
+ inqry_bgn_dt: str = Field(
39
+ ...,
40
+ pattern=r"^\d{12}$",
28
41
  description=(
29
- "PPS inquiry division. This adapter wraps the verified "
30
- "inqryDiv=2 bid-notice-number lookup path."
42
+ "Official PPS inqryBgnDt search start datetime in YYYYMMDDHHMM. "
43
+ "Required when inqry_div is '1' or '2'. Keep each PPS request window "
44
+ "within 31 days."
31
45
  ),
32
46
  )
33
- bid_ntce_no: str = Field(
47
+ inqry_end_dt: str = Field(
34
48
  ...,
35
- min_length=1,
36
- description="Bid notice number required by the verified inqryDiv=2 lookup path.",
49
+ pattern=r"^\d{12}$",
50
+ description=(
51
+ "Official PPS inqryEndDt search end datetime in YYYYMMDDHHMM. "
52
+ "Required when inqry_div is '1' or '2'. Keep each PPS request window "
53
+ "within 31 days."
54
+ ),
55
+ )
56
+ bid_ntce_nm: str | None = Field(
57
+ default=None,
58
+ max_length=1000,
59
+ description=(
60
+ "Official PPS bidNtceNm notice-name keyword. Partial names are allowed; "
61
+ "use this for citizen keywords such as 전기공사."
62
+ ),
63
+ )
64
+ ntce_instt_nm: str | None = Field(
65
+ default=None,
66
+ max_length=400,
67
+ description=(
68
+ "Official PPS ntceInsttNm public notice agency name filter. "
69
+ "Partial agency names are allowed."
70
+ ),
71
+ )
72
+ dminstt_nm: str | None = Field(
73
+ default=None,
74
+ max_length=400,
75
+ description=(
76
+ "Official PPS dminsttNm demand agency name filter. Partial agency names are allowed."
77
+ ),
78
+ )
79
+ region_name: str | None = Field(
80
+ default=None,
81
+ max_length=100,
82
+ description=(
83
+ "UMMAYA client-side region relevance filter copied from citizen wording. "
84
+ "This is not sent to PPS upstream; after the official response, UMMAYA "
85
+ "keeps rows whose documented region/agency fields such as cnstrtsiteRgnNm, "
86
+ "prtcptLmtRgnNm, ntceInsttNm, or dminsttNm match this value."
87
+ ),
88
+ )
89
+ prtcpt_lmt_rgn_nm: str | None = Field(
90
+ default=None,
91
+ max_length=100,
92
+ description=(
93
+ "Official PPS prtcptLmtRgnNm participation-limit region name. "
94
+ "For Busan-region notices use 부산광역시 when the citizen says 부산시."
95
+ ),
96
+ )
97
+ indstryty_nm: str | None = Field(
98
+ default=None,
99
+ max_length=100,
100
+ description=(
101
+ "Official PPS indstrytyNm industry/license name. Use 전기공사업 "
102
+ "for electrical-construction qualification searches when requested."
103
+ ),
37
104
  )
38
105
  page_no: int = Field(default=1, ge=1, description="Page number.")
39
106
  num_of_rows: int = Field(default=10, ge=1, le=100, description="Rows per page.")
40
107
 
108
+ @model_validator(mode="after")
109
+ def validate_official_search_window(self) -> PpsBidPublicInfoInput:
110
+ """Keep PPS searches inside the observed upstream contract window."""
111
+
112
+ try:
113
+ start = datetime.strptime(self.inqry_bgn_dt, _PPS_DATETIME_FORMAT)
114
+ end = datetime.strptime(self.inqry_end_dt, _PPS_DATETIME_FORMAT)
115
+ except ValueError as exc:
116
+ raise ValueError(
117
+ "PPS inqry_bgn_dt and inqry_end_dt must be valid YYYYMMDDHHMM datetimes."
118
+ ) from exc
119
+ if end < start:
120
+ raise ValueError("PPS inqry_end_dt must be greater than or equal to inqry_bgn_dt.")
121
+ if end - start > _PPS_MAX_SEARCH_WINDOW:
122
+ raise ValueError(
123
+ "PPS Nara Market bid searches must be split into 31-day-or-smaller "
124
+ "inqry_bgn_dt/inqry_end_dt windows before calling the upstream API."
125
+ )
126
+ return self
127
+
41
128
 
42
129
  SPEC = require_spec("pps_bid_public_info")
43
130
  INPUT_SCHEMA = PpsBidPublicInfoInput
@@ -51,7 +138,85 @@ async def handle(
51
138
  ) -> dict[str, object]:
52
139
  """Fetch or replay PPS bid public information rows."""
53
140
 
54
- return await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
141
+ output = await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
142
+ return _filter_output_by_region_name(output, input_model.region_name)
143
+
144
+
145
+ _REGION_RELEVANCE_FIELDS: tuple[str, ...] = (
146
+ "cnstrtsiteRgnNm",
147
+ "prtcptLmtRgnNm",
148
+ "ntceInsttNm",
149
+ "dminsttNm",
150
+ "jntcontrctDutyRgnNm1",
151
+ "jntcontrctDutyRgnNm2",
152
+ "jntcontrctDutyRgnNm3",
153
+ )
154
+
155
+
156
+ def _filter_output_by_region_name(
157
+ output: dict[str, object],
158
+ region_name: str | None,
159
+ ) -> dict[str, object]:
160
+ """Filter PPS rows by documented region-bearing response fields."""
161
+
162
+ terms = _region_terms(region_name)
163
+ if not terms:
164
+ return output
165
+
166
+ raw_items = output.get("items")
167
+ if not isinstance(raw_items, list):
168
+ return output
169
+
170
+ filtered: list[dict[str, object]] = []
171
+ for raw_item in raw_items:
172
+ if not isinstance(raw_item, dict):
173
+ continue
174
+ if _item_matches_region(raw_item, terms):
175
+ filtered.append(raw_item)
176
+
177
+ next_output = dict(output)
178
+ next_output["items"] = filtered
179
+ next_output["total_count"] = len(filtered)
180
+ raw_meta = output.get("meta")
181
+ meta = dict(raw_meta) if isinstance(raw_meta, dict) else {}
182
+ meta["upstream_total_count"] = output.get("total_count")
183
+ meta["client_filter"] = {
184
+ "field": "region_name",
185
+ "value": region_name,
186
+ "matched_count": len(filtered),
187
+ }
188
+ next_output["meta"] = meta
189
+ return next_output
190
+
191
+
192
+ def _region_terms(region_name: str | None) -> tuple[str, ...]:
193
+ if region_name is None:
194
+ return ()
195
+ normalized = " ".join(region_name.split())
196
+ if not normalized:
197
+ return ()
198
+ terms = [normalized]
199
+ for suffix in ("특별자치시", "특별자치도", "특별시", "광역시", "도"):
200
+ if normalized.endswith(suffix):
201
+ short = normalized[: -len(suffix)]
202
+ if short:
203
+ terms.append(short)
204
+ break
205
+ return tuple(dict.fromkeys(terms))
206
+
207
+
208
+ def _item_matches_region(item: dict[str, object], terms: tuple[str, ...]) -> bool:
209
+ raw_record = item.get("record")
210
+ record = raw_record if isinstance(raw_record, dict) else item
211
+ for field_name in _REGION_RELEVANCE_FIELDS:
212
+ value = record.get(field_name)
213
+ if not isinstance(value, str):
214
+ continue
215
+ compact_value = value.replace(" ", "")
216
+ for term in terms:
217
+ if term in value or term.replace(" ", "") in compact_value:
218
+ return True
219
+ return False
55
220
 
56
221
 
57
222
  def register(registry: ToolRegistry, executor: ToolExecutor) -> None:
@@ -3,6 +3,8 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ from typing import cast
7
+
6
8
  from pydantic import BaseModel, ConfigDict, Field
7
9
 
8
10
  from ummaya.tools.executor import ToolExecutor
@@ -15,14 +17,41 @@ from ummaya.tools.verified_data_go_kr._factory import (
15
17
  )
16
18
  from ummaya.tools.verified_data_go_kr._manifest import require_spec
17
19
 
20
+ _TAGO_CITY_CODE_DESCRIPTION = (
21
+ "Official TAGO cityCode from the provider getCtyCodeList contract. "
22
+ "Common metropolitan examples: Busan=21, Daegu=22, Incheon=23, "
23
+ "Gwangju=24, Daejeon=25, Ulsan=26."
24
+ )
25
+
18
26
 
19
27
  class TagoBusArrivalInput(BaseModel):
20
28
  """Input for TAGO bus arrival search."""
21
29
 
22
30
  model_config = ConfigDict(extra="forbid")
23
31
 
24
- city_code: str = Field(..., min_length=1, description="TAGO city code.")
25
- node_id: str = Field(..., min_length=1, description="TAGO bus station ID.")
32
+ city_code: str = Field(..., min_length=1, description=_TAGO_CITY_CODE_DESCRIPTION)
33
+ node_id: str = Field(
34
+ ...,
35
+ min_length=1,
36
+ description="Official TAGO nodeId returned by tago_bus_station_search.",
37
+ )
38
+ route_no: str | None = Field(
39
+ default=None,
40
+ min_length=1,
41
+ description=(
42
+ "Optional client-side filter against the official TAGO response field routeno. "
43
+ "It is not sent upstream; use it when the citizen names a visible bus route "
44
+ "such as 1001."
45
+ ),
46
+ )
47
+ route_id: str | None = Field(
48
+ default=None,
49
+ min_length=1,
50
+ description=(
51
+ "Optional client-side filter against the official TAGO response field routeid. "
52
+ "Get it from tago_bus_route_search when route_no alone is ambiguous."
53
+ ),
54
+ )
26
55
  page_no: int = Field(default=1, ge=1, description="Page number.")
27
56
  num_of_rows: int = Field(default=10, ge=1, le=100, description="Rows per page.")
28
57
 
@@ -39,7 +68,41 @@ async def handle(
39
68
  ) -> dict[str, object]:
40
69
  """Fetch or replay TAGO bus arrival rows."""
41
70
 
42
- return await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
71
+ output = await handle_verified_input(input_model, SPEC, fixture_body=fixture_body)
72
+ if input_model.route_no is None and input_model.route_id is None:
73
+ return output
74
+ return _filter_arrivals(output, route_no=input_model.route_no, route_id=input_model.route_id)
75
+
76
+
77
+ def _filter_arrivals(
78
+ output: dict[str, object],
79
+ *,
80
+ route_no: str | None,
81
+ route_id: str | None,
82
+ ) -> dict[str, object]:
83
+ items = output.get("items")
84
+ if not isinstance(items, list):
85
+ return output
86
+
87
+ filtered: list[dict[str, object]] = []
88
+ for item in items:
89
+ if not isinstance(item, dict):
90
+ continue
91
+ record = item.get("record")
92
+ if not isinstance(record, dict):
93
+ continue
94
+ typed_record = cast(dict[str, object], record)
95
+ if route_no is not None and str(typed_record.get("routeno", "")) != route_no:
96
+ continue
97
+ if route_id is not None and str(typed_record.get("routeid", "")) != route_id:
98
+ continue
99
+ filtered.append(cast(dict[str, object], item))
100
+
101
+ filtered_output = dict(output)
102
+ filtered_output["items"] = filtered
103
+ filtered_output["total_count"] = len(filtered)
104
+ filtered_output["next_cursor"] = None
105
+ return filtered_output
43
106
 
44
107
 
45
108
  def register(registry: ToolRegistry, executor: ToolExecutor) -> None:
@@ -15,14 +15,24 @@ from ummaya.tools.verified_data_go_kr._factory import (
15
15
  )
16
16
  from ummaya.tools.verified_data_go_kr._manifest import require_spec
17
17
 
18
+ _TAGO_CITY_CODE_DESCRIPTION = (
19
+ "Official TAGO cityCode from the provider getCtyCodeList contract. "
20
+ "Common metropolitan examples: Busan=21, Daegu=22, Incheon=23, "
21
+ "Gwangju=24, Daejeon=25, Ulsan=26."
22
+ )
23
+
18
24
 
19
25
  class TagoBusLocationInput(BaseModel):
20
26
  """Input for TAGO bus location search."""
21
27
 
22
28
  model_config = ConfigDict(extra="forbid")
23
29
 
24
- city_code: str = Field(..., min_length=1, description="TAGO city code.")
25
- route_id: str = Field(..., min_length=1, description="TAGO route ID.")
30
+ city_code: str = Field(..., min_length=1, description=_TAGO_CITY_CODE_DESCRIPTION)
31
+ route_id: str = Field(
32
+ ...,
33
+ min_length=1,
34
+ description="Official TAGO routeId returned by tago_bus_route_search.",
35
+ )
26
36
  page_no: int = Field(default=1, ge=1, description="Page number.")
27
37
  num_of_rows: int = Field(default=10, ge=1, le=100, description="Rows per page.")
28
38
 
@@ -15,14 +15,20 @@ from ummaya.tools.verified_data_go_kr._factory import (
15
15
  )
16
16
  from ummaya.tools.verified_data_go_kr._manifest import require_spec
17
17
 
18
+ _TAGO_CITY_CODE_DESCRIPTION = (
19
+ "Official TAGO cityCode from the provider getCtyCodeList contract. "
20
+ "Common metropolitan examples: Busan=21, Daegu=22, Incheon=23, "
21
+ "Gwangju=24, Daejeon=25, Ulsan=26."
22
+ )
23
+
18
24
 
19
25
  class TagoBusRouteInput(BaseModel):
20
26
  """Input for TAGO bus route search."""
21
27
 
22
28
  model_config = ConfigDict(extra="forbid")
23
29
 
24
- city_code: str = Field(..., min_length=1, description="TAGO city code.")
25
- route_no: str = Field(..., min_length=1, description="Bus route number.")
30
+ city_code: str = Field(..., min_length=1, description=_TAGO_CITY_CODE_DESCRIPTION)
31
+ route_no: str = Field(..., min_length=1, description="Bus route number visible to citizens.")
26
32
  page_no: int = Field(default=1, ge=1, description="Page number.")
27
33
  num_of_rows: int = Field(default=10, ge=1, le=100, description="Rows per page.")
28
34