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
@@ -10,8 +10,9 @@ import logging
10
10
  import os
11
11
  import random
12
12
  import time
13
- from collections.abc import AsyncIterator
13
+ from collections.abc import AsyncIterator, Iterator
14
14
  from contextvars import Token
15
+ from copy import deepcopy
15
16
  from dataclasses import dataclass
16
17
  from typing import TYPE_CHECKING, cast
17
18
 
@@ -39,6 +40,7 @@ from ummaya.llm.models import (
39
40
  ToolCall,
40
41
  ToolDefinition,
41
42
  )
43
+ from ummaya.llm.reasoning import ReasoningMode, resolve_reasoning_policy
42
44
  from ummaya.llm.usage import UsageTracker
43
45
  from ummaya.observability.semconv import (
44
46
  ERROR_TYPE,
@@ -167,6 +169,62 @@ def _compute_prompt_hash(system_text: str) -> str:
167
169
  return hashlib.sha256(hashed.encode("utf-8")).hexdigest()
168
170
 
169
171
 
172
+ def _provider_safe_parameters_schema(schema: dict[str, object]) -> dict[str, object]:
173
+ """Inline Pydantic-local JSON Schema refs before sending tool schemas."""
174
+ root = deepcopy(schema)
175
+ defs_obj = root.get("$defs")
176
+ defs: dict[str, object] = {}
177
+ if isinstance(defs_obj, dict):
178
+ defs = {str(name): value for name, value in defs_obj.items()}
179
+
180
+ inlined = _inline_local_json_schema_refs(root, defs, ())
181
+ if not isinstance(inlined, dict):
182
+ raise ValueError("OpenAI tool parameters schema must be a JSON object")
183
+ return cast(dict[str, object], inlined)
184
+
185
+
186
+ def _inline_local_json_schema_refs(
187
+ node: object,
188
+ defs: dict[str, object],
189
+ stack: tuple[str, ...],
190
+ ) -> object:
191
+ if isinstance(node, list):
192
+ return [_inline_local_json_schema_refs(item, defs, stack) for item in node]
193
+ if not isinstance(node, dict):
194
+ return node
195
+
196
+ ref_name = _local_def_ref_name(node.get("$ref"))
197
+ if ref_name is not None:
198
+ if ref_name in stack:
199
+ raise ValueError(f"Cyclic JSON Schema reference is not supported: #/$defs/{ref_name}")
200
+ if ref_name not in defs:
201
+ raise ValueError(f"Unknown JSON Schema reference: #/$defs/{ref_name}")
202
+
203
+ target = deepcopy(defs[ref_name])
204
+ expanded = _inline_local_json_schema_refs(target, defs, (*stack, ref_name))
205
+ siblings = {str(key): value for key, value in node.items() if key not in {"$ref", "$defs"}}
206
+ if not siblings:
207
+ return expanded
208
+ if not isinstance(expanded, dict):
209
+ raise ValueError(f"JSON Schema reference target must be an object: #/$defs/{ref_name}")
210
+ merged = cast(dict[str, object], expanded).copy()
211
+ for key, value in siblings.items():
212
+ merged[key] = _inline_local_json_schema_refs(value, defs, stack)
213
+ return merged
214
+
215
+ return {
216
+ str(key): _inline_local_json_schema_refs(value, defs, stack)
217
+ for key, value in node.items()
218
+ if key != "$defs"
219
+ }
220
+
221
+
222
+ def _local_def_ref_name(ref: object) -> str | None:
223
+ if not isinstance(ref, str) or not ref.startswith("#/$defs/"):
224
+ return None
225
+ return ref.removeprefix("#/$defs/").replace("~1", "/").replace("~0", "~")
226
+
227
+
170
228
  class LLMClient:
171
229
  """Async LLM client for FriendliAI Serverless endpoint."""
172
230
 
@@ -238,6 +296,7 @@ class LLMClient:
238
296
  presence_penalty: float = 0.0,
239
297
  max_tokens: int = 1024,
240
298
  stop: list[str] | None = None,
299
+ reasoning_mode: ReasoningMode | str | None = None,
241
300
  ) -> ChatCompletionResponse:
242
301
  """Send a non-streaming chat completion request.
243
302
 
@@ -271,6 +330,7 @@ class LLMClient:
271
330
  tools=tools,
272
331
  tool_choice=tool_choice,
273
332
  stream=False,
333
+ reasoning_mode=reasoning_mode,
274
334
  )
275
335
 
276
336
  logger.debug(
@@ -365,6 +425,7 @@ class LLMClient:
365
425
  presence_penalty: float = 0.0,
366
426
  max_tokens: int = 1024,
367
427
  stop: list[str] | None = None,
428
+ reasoning_mode: ReasoningMode | str | None = None,
368
429
  ) -> AsyncIterator[StreamEvent]:
369
430
  """Send a streaming chat completion request.
370
431
 
@@ -403,7 +464,9 @@ class LLMClient:
403
464
  tools=tools,
404
465
  tool_choice=tool_choice,
405
466
  stream=True,
467
+ reasoning_mode=reasoning_mode,
406
468
  )
469
+ allow_reasoning = payload.get("include_reasoning") is True
407
470
 
408
471
  logger.debug(
409
472
  "LLM stream request: model=%s messages=%d",
@@ -447,7 +510,11 @@ class LLMClient:
447
510
  _finalize: dict[str, object] = {}
448
511
 
449
512
  try:
450
- async for event in self._stream_with_retry(payload, _finalize):
513
+ async for event in self._stream_with_retry(
514
+ payload,
515
+ _finalize,
516
+ allow_reasoning=allow_reasoning,
517
+ ):
451
518
  active_span.detach()
452
519
  yield event
453
520
  active_span.attach()
@@ -490,6 +557,8 @@ class LLMClient:
490
557
  self,
491
558
  payload: dict[str, object],
492
559
  _finalize: dict[str, object],
560
+ *,
561
+ allow_reasoning: bool,
493
562
  ) -> AsyncIterator[StreamEvent]:
494
563
  """Execute stream() with Retry-After-first backoff loop (T015/T016).
495
564
 
@@ -564,7 +633,38 @@ class LLMClient:
564
633
 
565
634
  # Yield events; watch for mid-stream 429 envelopes (T016)
566
635
  rate_limited_mid_stream = False
567
- async for line in response.aiter_lines():
636
+ line_iter = response.aiter_lines()
637
+ next_data_line_deadline = (
638
+ time.monotonic() + self._config.stream_idle_timeout
639
+ )
640
+ while True:
641
+ try:
642
+ timeout_remaining = next_data_line_deadline - time.monotonic()
643
+ if timeout_remaining <= 0:
644
+ raise TimeoutError
645
+ line = await asyncio.wait_for(
646
+ anext(line_iter),
647
+ timeout=timeout_remaining,
648
+ )
649
+ except StopAsyncIteration:
650
+ break
651
+ except TimeoutError as exc:
652
+ _duration_ms = (time.monotonic() - _stream_start) * 1000
653
+ self._metrics_record_call(
654
+ success=False,
655
+ duration_ms=_duration_ms,
656
+ )
657
+ _metrics_recorded = True
658
+ raise StreamInterruptedError(
659
+ "Stream idle timeout after "
660
+ f"{self._config.stream_idle_timeout:g}s "
661
+ "without a new SSE data line"
662
+ ) from exc
663
+ if not line.startswith("data: "):
664
+ continue
665
+ next_data_line_deadline = (
666
+ time.monotonic() + self._config.stream_idle_timeout
667
+ )
568
668
  if self._is_rate_limit_envelope(line):
569
669
  rate_limited_mid_stream = True
570
670
  delay = self._compute_rate_limit_delay(response, attempt, policy)
@@ -607,7 +707,10 @@ class LLMClient:
607
707
  if chunk_info.get("model"):
608
708
  _response_model = chunk_info["model"]
609
709
 
610
- async for event in self._parse_sse_line(line):
710
+ async for event in self._parse_sse_line(
711
+ line,
712
+ allow_reasoning=allow_reasoning,
713
+ ):
611
714
  yield event
612
715
  if event.type == "done":
613
716
  _duration_ms = (time.monotonic() - _stream_start) * 1000
@@ -848,7 +951,12 @@ class LLMClient:
848
951
  if i + step < n:
849
952
  await asyncio.sleep(_LLM_STREAM_PACE_S)
850
953
 
851
- async def _parse_sse_line(self, line: str) -> AsyncIterator[StreamEvent]: # noqa: C901
954
+ async def _parse_sse_line(
955
+ self,
956
+ line: str,
957
+ *,
958
+ allow_reasoning: bool = False,
959
+ ) -> AsyncIterator[StreamEvent]:
852
960
  """Parse a single SSE line and yield corresponding StreamEvent(s)."""
853
961
  if not line or not line.startswith("data: "):
854
962
  return
@@ -859,14 +967,35 @@ class LLMClient:
859
967
  yield StreamEvent(type="done")
860
968
  return
861
969
 
970
+ chunk = self._decode_sse_payload(payload_text)
971
+ if chunk is None:
972
+ return
973
+
974
+ usage_event = self._usage_event_from_chunk(chunk)
975
+ if usage_event is not None:
976
+ yield usage_event
977
+
978
+ async for event in self._events_from_sse_choices(
979
+ chunk,
980
+ allow_reasoning=allow_reasoning,
981
+ ):
982
+ yield event
983
+
984
+ def _decode_sse_payload(self, payload_text: str) -> dict[str, object] | None:
985
+ """Decode a JSON SSE payload, returning None for malformed chunks."""
862
986
  try:
863
987
  chunk = json.loads(payload_text)
864
988
  except json.JSONDecodeError:
865
989
  logger.warning("Failed to parse SSE chunk: %r", payload_text)
866
- return
990
+ return None
991
+ return chunk if isinstance(chunk, dict) else None
867
992
 
993
+ def _usage_event_from_chunk(self, chunk: dict[str, object]) -> StreamEvent | None:
994
+ """Debit usage from a stream chunk and return the corresponding event."""
868
995
  if "usage" in chunk and chunk["usage"] is not None:
869
996
  raw_usage = chunk["usage"]
997
+ if not isinstance(raw_usage, dict):
998
+ return None
870
999
  usage = TokenUsage(
871
1000
  input_tokens=raw_usage.get("prompt_tokens", 0),
872
1001
  output_tokens=raw_usage.get("completion_tokens", 0),
@@ -877,66 +1006,104 @@ class LLMClient:
877
1006
  usage.output_tokens,
878
1007
  )
879
1008
  self._usage.debit(usage)
880
- yield StreamEvent(type="usage", usage=usage)
1009
+ return StreamEvent(type="usage", usage=usage)
1010
+ return None
881
1011
 
1012
+ async def _events_from_sse_choices(
1013
+ self,
1014
+ chunk: dict[str, object],
1015
+ *,
1016
+ allow_reasoning: bool,
1017
+ ) -> AsyncIterator[StreamEvent]:
1018
+ """Yield stream events for the first OpenAI-compatible choice delta."""
882
1019
  choices = chunk.get("choices")
883
1020
  if not choices:
884
1021
  return
1022
+ if not isinstance(choices, list):
1023
+ return
885
1024
 
886
1025
  choice = choices[0]
1026
+ if not isinstance(choice, dict):
1027
+ return
887
1028
  delta = choice.get("delta", {})
1029
+ if not isinstance(delta, dict):
1030
+ return
888
1031
 
1032
+ async for event in self._events_from_sse_delta(
1033
+ delta,
1034
+ allow_reasoning=allow_reasoning,
1035
+ ):
1036
+ yield event
1037
+
1038
+ async def _events_from_sse_delta(
1039
+ self,
1040
+ delta: dict[str, object],
1041
+ *,
1042
+ allow_reasoning: bool,
1043
+ ) -> AsyncIterator[StreamEvent]:
1044
+ """Yield content, reasoning, and tool-call events from a delta object."""
889
1045
  if "content" in delta and delta["content"] is not None:
890
1046
  # CC reference: services/api/claude.ts:2113 (text_delta content_block_delta).
891
- content = delta["content"]
1047
+ content = str(delta["content"])
892
1048
  async for sub in self._pace_text_chunk(content, "content"):
893
1049
  yield sub
894
1050
  elif "reasoning_content" in delta and delta["reasoning_content"] is not None:
895
- # CC reference: services/api/claude.ts:2148-2161 (thinking_delta
896
- # content_block_delta) — K-EXAONE emits chain-of-thought on a
897
- # separate ``delta.reasoning_content`` channel. Forwarding the
898
- # same StreamEvent shape on UMMAYA lets the TUI's
899
- # ``AssistantThinkingMessage`` component render the reasoning
900
- # inline instead of swallowing it. Log only the chunk length —
901
- # never the raw content (CoT may contain user PII or sensitive
902
- # reasoning about user input).
903
- reasoning_text = delta["reasoning_content"]
1051
+ async for sub in self._events_from_reasoning_delta(
1052
+ str(delta["reasoning_content"]),
1053
+ allow_reasoning=allow_reasoning,
1054
+ ):
1055
+ yield sub
1056
+
1057
+ if "tool_calls" in delta and delta["tool_calls"]:
1058
+ for event in self._events_from_tool_call_deltas(delta["tool_calls"]):
1059
+ yield event
1060
+
1061
+ async def _events_from_reasoning_delta(
1062
+ self,
1063
+ reasoning_text: str,
1064
+ *,
1065
+ allow_reasoning: bool,
1066
+ ) -> AsyncIterator[StreamEvent]:
1067
+ """Yield reasoning text only when the request opted into reasoning parsing."""
1068
+ if not allow_reasoning:
904
1069
  logger.debug(
905
- "Forwarding reasoning_content as thinking_delta (len=%d)",
1070
+ "Suppressed unexpected reasoning_content while include_reasoning=false (len=%d)",
906
1071
  len(reasoning_text),
907
1072
  )
908
- async for sub in self._pace_text_chunk(reasoning_text, "thinking"):
909
- yield sub
1073
+ return
1074
+ logger.debug(
1075
+ "Forwarding reasoning_content as thinking_delta (len=%d)",
1076
+ len(reasoning_text),
1077
+ )
1078
+ async for sub in self._pace_text_chunk(reasoning_text, "thinking"):
1079
+ yield sub
910
1080
 
911
- if "tool_calls" in delta and delta["tool_calls"]:
912
- # CC reference: services/api/claude.ts:1997 (tool_use content_block_start)
913
- # + services/api/claude.ts:2087 (input_json_delta content_block_delta).
914
- # FriendliAI's OpenAI-compatible streaming buffers tool_call argument
915
- # JSON across multiple deltas (matching OpenAI's incremental parser).
916
- # UMMAYA mirrors CC's pattern by emitting one StreamEvent per delta;
917
- # the IPC bridge (stdio.py) accumulates them into the final
918
- # ToolCallFrame.
919
- for tc_delta in delta["tool_calls"]:
920
- func = tc_delta.get("function", {})
921
- # Log only tool metadata (index/id/name + arg length).
922
- # Raw `arguments` often carries user-provided location strings
923
- # or other PII — never log them.
924
- _args_field = func.get("arguments")
925
- _args_len = len(_args_field) if isinstance(_args_field, str) else 0
926
- logger.debug(
927
- "tool_call_delta idx=%s id=%s name=%r args_len=%d",
928
- tc_delta.get("index"),
929
- tc_delta.get("id"),
930
- func.get("name"),
931
- _args_len,
932
- )
933
- yield StreamEvent(
934
- type="tool_call_delta",
935
- tool_call_index=tc_delta.get("index"),
936
- tool_call_id=tc_delta.get("id"),
937
- function_name=func.get("name"),
938
- function_args_delta=func.get("arguments"),
939
- )
1081
+ def _events_from_tool_call_deltas(self, tool_calls: object) -> Iterator[StreamEvent]:
1082
+ """Yield tool-call deltas without logging raw argument content."""
1083
+ if not isinstance(tool_calls, list):
1084
+ return
1085
+ for tc_delta in tool_calls:
1086
+ if not isinstance(tc_delta, dict):
1087
+ continue
1088
+ func = tc_delta.get("function", {})
1089
+ if not isinstance(func, dict):
1090
+ func = {}
1091
+ _args_field = func.get("arguments")
1092
+ _args_len = len(_args_field) if isinstance(_args_field, str) else 0
1093
+ logger.debug(
1094
+ "tool_call_delta idx=%s id=%s name=%r args_len=%d",
1095
+ tc_delta.get("index"),
1096
+ tc_delta.get("id"),
1097
+ func.get("name"),
1098
+ _args_len,
1099
+ )
1100
+ yield StreamEvent(
1101
+ type="tool_call_delta",
1102
+ tool_call_index=tc_delta.get("index"),
1103
+ tool_call_id=tc_delta.get("id"),
1104
+ function_name=func.get("name"),
1105
+ function_args_delta=func.get("arguments"),
1106
+ )
940
1107
 
941
1108
  def _build_payload(
942
1109
  self,
@@ -950,6 +1117,7 @@ class LLMClient:
950
1117
  tools: list[ToolDefinition | dict[str, object]] | None = None,
951
1118
  tool_choice: str | dict[str, object] | None = None,
952
1119
  stream: bool,
1120
+ reasoning_mode: ReasoningMode | str | None = None,
953
1121
  ) -> dict[str, object]:
954
1122
  """Construct the JSON payload for a chat completions request.
955
1123
 
@@ -979,13 +1147,7 @@ class LLMClient:
979
1147
  is not treated as normal assistant text and is never required for the
980
1148
  default CLI/TUI path.
981
1149
  """
982
- import os # noqa: PLC0415 — local import keeps top-level imports thin
983
-
984
- enable_thinking = os.environ.get("UMMAYA_K_EXAONE_THINKING", "false").lower() in (
985
- "true",
986
- "1",
987
- "yes",
988
- )
1150
+ reasoning = resolve_reasoning_policy(reasoning_mode)
989
1151
 
990
1152
  payload: dict[str, object] = {
991
1153
  "model": self._config.model,
@@ -999,7 +1161,9 @@ class LLMClient:
999
1161
  # enable_thinking=False the model emits an answer directly
1000
1162
  # without the <think>...</think> trace, dropping first-token
1001
1163
  # latency from ~60-180s to <10s for typical citizen prompts.
1002
- "chat_template_kwargs": {"enable_thinking": enable_thinking},
1164
+ "chat_template_kwargs": {"enable_thinking": reasoning.enable_thinking},
1165
+ "parse_reasoning": reasoning.parse_reasoning,
1166
+ "include_reasoning": reasoning.include_reasoning,
1003
1167
  }
1004
1168
  if stop is not None:
1005
1169
  payload["stop"] = stop
@@ -1028,8 +1192,19 @@ class LLMClient:
1028
1192
  reaches FriendliAI's strict OpenAI-compatible validator.
1029
1193
  """
1030
1194
  if isinstance(tool, ToolDefinition):
1031
- return cast(dict[str, object], tool.model_dump())
1032
- return cast(dict[str, object], ToolDefinition.model_validate(tool).model_dump())
1195
+ payload = cast(dict[str, object], tool.model_dump())
1196
+ else:
1197
+ payload = cast(dict[str, object], ToolDefinition.model_validate(tool).model_dump())
1198
+
1199
+ function_obj = payload.get("function")
1200
+ if isinstance(function_obj, dict):
1201
+ function = cast(dict[str, object], function_obj)
1202
+ parameters_obj = function.get("parameters")
1203
+ if isinstance(parameters_obj, dict):
1204
+ function["parameters"] = _provider_safe_parameters_schema(
1205
+ cast(dict[str, object], parameters_obj)
1206
+ )
1207
+ return payload
1033
1208
 
1034
1209
  # ------------------------------------------------------------------
1035
1210
  # Private retry helpers (T015)
@@ -16,6 +16,7 @@ class LLMClientConfig(BaseSettings):
16
16
  UMMAYA_FRIENDLI_MODEL — model identifier
17
17
  UMMAYA_LLM_SESSION_BUDGET — per-session token budget
18
18
  UMMAYA_LLM_TIMEOUT_SECONDS — HTTP stream timeout budget
19
+ UMMAYA_LLM_STREAM_IDLE_TIMEOUT_SECONDS — max idle gap between stream lines
19
20
  """
20
21
 
21
22
  model_config = SettingsConfigDict(
@@ -59,6 +60,11 @@ class LLMClientConfig(BaseSettings):
59
60
  ),
60
61
  description="HTTP request timeout in seconds.",
61
62
  )
63
+ stream_idle_timeout: float = Field(
64
+ default=90.0,
65
+ validation_alias="UMMAYA_LLM_STREAM_IDLE_TIMEOUT_SECONDS",
66
+ description="Maximum idle time between streaming response lines in seconds.",
67
+ )
62
68
  max_retries: int = Field(
63
69
  default=3,
64
70
  description="Maximum number of retry attempts on transient failures.",
@@ -80,12 +86,11 @@ class LLMClientConfig(BaseSettings):
80
86
  raise ValueError("session_budget must be > 0")
81
87
  return value
82
88
 
83
- @field_validator("timeout")
89
+ @field_validator("timeout", "stream_idle_timeout")
84
90
  @classmethod
85
91
  def timeout_must_be_positive(cls, value: float) -> float:
86
- """Enforce timeout > 0."""
87
92
  if value <= 0:
88
- raise ValueError("timeout must be > 0")
93
+ raise ValueError("timeout values must be > 0")
89
94
  return value
90
95
 
91
96
  @field_validator("max_retries")
@@ -0,0 +1,84 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """K-EXAONE/FriendliAI reasoning payload policy."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ from collections.abc import Mapping
8
+ from dataclasses import dataclass
9
+ from typing import Literal
10
+
11
+ ReasoningMode = Literal["fast", "balanced", "deep", "diagnostic", "auto"]
12
+ ReasoningModeSource = Literal["env", "session", "legacy-env", "default"]
13
+
14
+ _MODES: set[str] = {"fast", "balanced", "deep", "diagnostic", "auto"}
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class ResolvedReasoningPolicy:
19
+ """Provider-facing reasoning policy for one request."""
20
+
21
+ mode: ReasoningMode
22
+ source: ReasoningModeSource
23
+ enable_thinking: bool
24
+ parse_reasoning: bool
25
+ include_reasoning: bool
26
+ persist_thinking: bool = False
27
+
28
+
29
+ def parse_reasoning_mode(value: object) -> ReasoningMode | None:
30
+ """Return a valid reasoning mode from an untrusted value."""
31
+ if value is None:
32
+ return None
33
+ normalized = str(value).lower()
34
+ if normalized in _MODES:
35
+ return normalized # type: ignore[return-value]
36
+ return None
37
+
38
+
39
+ def resolve_reasoning_policy(
40
+ reasoning_mode: ReasoningMode | str | None = None,
41
+ *,
42
+ env: Mapping[str, str] | None = None,
43
+ ) -> ResolvedReasoningPolicy:
44
+ """Resolve request, settings, and env state into FriendliAI payload fields."""
45
+ effective_env = os.environ if env is None else env
46
+ env_mode = parse_reasoning_mode(effective_env.get("UMMAYA_K_EXAONE_REASONING_MODE"))
47
+ if env_mode is not None:
48
+ return _policy_for(env_mode, "env")
49
+
50
+ explicit_mode = parse_reasoning_mode(reasoning_mode)
51
+ if explicit_mode is not None:
52
+ return _policy_for(explicit_mode, "session")
53
+
54
+ legacy_mode = _legacy_thinking_mode(effective_env)
55
+ if legacy_mode is not None:
56
+ return _policy_for(legacy_mode, "legacy-env")
57
+
58
+ return _policy_for("balanced", "default")
59
+
60
+
61
+ def _legacy_thinking_mode(env: Mapping[str, str]) -> ReasoningMode | None:
62
+ raw = env.get("UMMAYA_K_EXAONE_THINKING")
63
+ if raw is None:
64
+ return None
65
+ normalized = raw.lower()
66
+ if normalized in {"1", "true", "yes"}:
67
+ return "deep"
68
+ if normalized in {"0", "false", "no"}:
69
+ return "fast"
70
+ return None
71
+
72
+
73
+ def _policy_for(
74
+ mode: ReasoningMode,
75
+ source: ReasoningModeSource,
76
+ ) -> ResolvedReasoningPolicy:
77
+ enable_thinking = mode in {"deep", "diagnostic"}
78
+ return ResolvedReasoningPolicy(
79
+ mode=mode,
80
+ source=source,
81
+ enable_thinking=enable_thinking,
82
+ parse_reasoning=True,
83
+ include_reasoning=enable_thinking,
84
+ )
@@ -7,6 +7,7 @@ Exports the active primitive symbols that make up the main-tool surface:
7
7
  - ``locate``: geocoding (re-exported from Spec 022).
8
8
  - ``send``: write-transaction absorber (Spec 031 US1, T024).
9
9
  - ``check``: delegation-only identity binding (Spec 031 US2, T042).
10
+ - ``document``: local public-document authoring and review harness.
10
11
 
11
12
  ``subscribe`` is intentionally not part of the active surface. National alert
12
13
  and notice subscriptions are app/phone push-notification concerns, not a CLI
@@ -18,6 +19,7 @@ from __future__ import annotations
18
19
  from collections.abc import Callable
19
20
  from typing import Any
20
21
 
22
+ from ummaya.primitives.document import document
21
23
  from ummaya.primitives.submit import send
22
24
  from ummaya.primitives.verify import check
23
25
  from ummaya.tools.lookup import find
@@ -46,6 +48,7 @@ PRIMITIVE_REGISTRY: dict[str, Callable[..., Any]] = {
46
48
  "locate": locate,
47
49
  "send": send,
48
50
  "check": check,
51
+ "document": document,
49
52
  }
50
53
 
51
54
  # Subset of ``PRIMITIVE_REGISTRY`` whose invocation requires a Spec 033
@@ -64,9 +67,9 @@ PRIMITIVE_REGISTRY: dict[str, Callable[..., Any]] = {
64
67
  #
65
68
  # The complement (``PRIMITIVE_REGISTRY.keys() - GATED_PRIMITIVES``) is the
66
69
  # fully auto-allowed set: find / locate.
67
- GATED_PRIMITIVES: frozenset[str] = frozenset({"check", "send"})
70
+ GATED_PRIMITIVES: frozenset[str] = frozenset({"check", "send", "document"})
68
71
  LIGHT_GATE_PRIMITIVES: frozenset[str] = frozenset({"check"})
69
- HEAVY_GATE_PRIMITIVES: frozenset[str] = frozenset({"send"})
72
+ HEAVY_GATE_PRIMITIVES: frozenset[str] = frozenset({"send", "document"})
70
73
 
71
74
  # ``__all__`` enumerates the LLM-visible primitive *surface* — the active root
72
75
  # verbs. The metadata constants ``PRIMITIVE_REGISTRY``
@@ -77,6 +80,7 @@ __all__ = [
77
80
  "locate",
78
81
  "send",
79
82
  "check",
83
+ "document",
80
84
  ]
81
85
  # Metadata constants (GATED_PRIMITIVES / LIGHT_GATE_PRIMITIVES /
82
86
  # HEAVY_GATE_PRIMITIVES / PRIMITIVE_REGISTRY) are intentionally NOT in __all__
@@ -36,7 +36,7 @@ logger = logging.getLogger(__name__)
36
36
  # Accepts single-scope strings AND comma-joined multi-scope strings for US1.
37
37
  # ---------------------------------------------------------------------------
38
38
 
39
- _SCOPE_VERBS = r"find|locate|send|check|lookup|resolve_location|submit|verify"
39
+ _SCOPE_VERBS = r"find|locate|send|check|document|lookup|resolve_location|submit|verify"
40
40
 
41
41
  _SINGLE_SCOPE_PATTERN: re.Pattern[str] = re.compile(rf"^({_SCOPE_VERBS}):[a-z0-9_]+\.[a-z0-9_-]+$")
42
42
 
@@ -0,0 +1,28 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Document primitive placeholder.
3
+
4
+ The model-facing `document` tool is registered as a concrete GovAPITool by the
5
+ document harness. This coroutine exists so primitive registries and IPC guards
6
+ can identify `document` as an allowed top-level primitive name.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+
14
+ async def document(*_args: object, **_kwargs: object) -> dict[str, Any]:
15
+ """Document primitive marker.
16
+
17
+ Runtime execution is owned by `ummaya.tools.documents.registry` because it
18
+ needs session-local artifact stores and format-engine adapters.
19
+ """
20
+
21
+ return {
22
+ "kind": "error",
23
+ "reason": "direct_document_primitive_not_bound",
24
+ "message": "The document primitive must be executed through the registered document tool.",
25
+ }
26
+
27
+
28
+ __all__ = ["document"]
@@ -104,9 +104,6 @@ class UmmayaSettings(BaseSettings):
104
104
  live_adapter_proxy_timeout_seconds: float = Field(default=30.0, gt=0)
105
105
  """HTTP timeout for operator-managed live adapter proxy calls."""
106
106
 
107
- live_adapter_proxy_token: str = Field(default="")
108
- """Optional bearer token for private/self-hosted live adapter gateways."""
109
-
110
107
  live_adapter_gateway_token: str = Field(default="")
111
108
  """Optional bearer token required by the hosted live adapter gateway server."""
112
109