ummaya 0.2.4 → 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 (477) hide show
  1. package/README.md +15 -2
  2. package/bin/ummaya +10 -1
  3. package/npm-shrinkwrap.json +253 -2
  4. package/package.json +5 -1
  5. package/prompts/manifest.yaml +1 -1
  6. package/prompts/system_v1.md +1 -0
  7. package/pyproject.toml +26 -2
  8. package/specs/2803-document-production-hardening/contracts/document-tools.schema.json +1043 -0
  9. package/src/ummaya/_canonical/__init__.py +2 -0
  10. package/src/ummaya/engine/engine.py +29 -132
  11. package/src/ummaya/evidence/__init__.py +21 -2
  12. package/src/ummaya/evidence/dataset_contract.py +193 -0
  13. package/src/ummaya/evidence/document_authoring_cases.py +33 -0
  14. package/src/ummaya/evidence/document_harness.py +313 -0
  15. package/src/ummaya/evidence/document_viewer_ux.py +391 -0
  16. package/src/ummaya/evidence/gates.py +70 -0
  17. package/src/ummaya/evidence/json_types.py +20 -0
  18. package/src/ummaya/evidence/models.py +88 -1
  19. package/src/ummaya/evidence/output_payload.py +89 -0
  20. package/src/ummaya/evidence/payload_documents.py +233 -0
  21. package/src/ummaya/evidence/route_contracts.py +224 -0
  22. package/src/ummaya/evidence/route_helpers.py +150 -0
  23. package/src/ummaya/evidence/runner.py +81 -212
  24. package/src/ummaya/evidence/source_provenance.py +246 -0
  25. package/src/ummaya/evidence/source_provenance_redaction.py +176 -0
  26. package/src/ummaya/evidence/tool_layer.py +39 -0
  27. package/src/ummaya/evidence/tool_layer_models.py +151 -0
  28. package/src/ummaya/ipc/adapter_manifest_emitter.py +26 -10
  29. package/src/ummaya/ipc/document_intent_normalization.py +185 -0
  30. package/src/ummaya/ipc/frame_schema.py +5 -5
  31. package/src/ummaya/ipc/route_diagnostics.py +73 -0
  32. package/src/ummaya/ipc/stdio.py +1109 -477
  33. package/src/ummaya/llm/client.py +102 -3
  34. package/src/ummaya/llm/config.py +8 -3
  35. package/src/ummaya/primitives/__init__.py +6 -2
  36. package/src/ummaya/primitives/delegation.py +1 -1
  37. package/src/ummaya/primitives/document.py +28 -0
  38. package/src/ummaya/settings.py +0 -3
  39. package/src/ummaya/tools/discovery_bridge.py +17 -1
  40. package/src/ummaya/tools/documents/__init__.py +297 -0
  41. package/src/ummaya/tools/documents/adapter_registry.py +487 -0
  42. package/src/ummaya/tools/documents/archive_container_probe.py +167 -0
  43. package/src/ummaya/tools/documents/artifact_store.py +454 -0
  44. package/src/ummaya/tools/documents/authoring.py +283 -0
  45. package/src/ummaya/tools/documents/baselines.py +114 -0
  46. package/src/ummaya/tools/documents/capability.py +331 -0
  47. package/src/ummaya/tools/documents/contracts.py +112 -0
  48. package/src/ummaya/tools/documents/conversion.py +521 -0
  49. package/src/ummaya/tools/documents/diff.py +275 -0
  50. package/src/ummaya/tools/documents/engines.py +163 -0
  51. package/src/ummaya/tools/documents/evaluation.py +291 -0
  52. package/src/ummaya/tools/documents/explicit_values.py +108 -0
  53. package/src/ummaya/tools/documents/fixtures.py +174 -0
  54. package/src/ummaya/tools/documents/format_completion_audit.py +471 -0
  55. package/src/ummaya/tools/documents/formats/__init__.py +2 -0
  56. package/src/ummaya/tools/documents/formats/archive.py +528 -0
  57. package/src/ummaya/tools/documents/formats/base.py +41 -0
  58. package/src/ummaya/tools/documents/formats/code_file.py +211 -0
  59. package/src/ummaya/tools/documents/formats/data_file.py +272 -0
  60. package/src/ummaya/tools/documents/formats/hwp.py +284 -0
  61. package/src/ummaya/tools/documents/formats/hwpx.py +1837 -0
  62. package/src/ummaya/tools/documents/formats/odf.py +435 -0
  63. package/src/ummaya/tools/documents/formats/ooxml.py +1030 -0
  64. package/src/ummaya/tools/documents/formats/passive.py +766 -0
  65. package/src/ummaya/tools/documents/formats/pdf.py +702 -0
  66. package/src/ummaya/tools/documents/formats/text_web.py +268 -0
  67. package/src/ummaya/tools/documents/hwp_conversion_probe.py +178 -0
  68. package/src/ummaya/tools/documents/hwp_direct_candidate.py +141 -0
  69. package/src/ummaya/tools/documents/inspection.py +289 -0
  70. package/src/ummaya/tools/documents/intake.py +1079 -0
  71. package/src/ummaya/tools/documents/legacy_office_promotion_probe.py +366 -0
  72. package/src/ummaya/tools/documents/models.py +1598 -0
  73. package/src/ummaya/tools/documents/odf_promotion_probe.py +167 -0
  74. package/src/ummaya/tools/documents/orchestrator.py +96 -0
  75. package/src/ummaya/tools/documents/passive_capability_probe.py +251 -0
  76. package/src/ummaya/tools/documents/patch.py +170 -0
  77. package/src/ummaya/tools/documents/pdfa_conformance.py +284 -0
  78. package/src/ummaya/tools/documents/pdfa_promotion_probe.py +198 -0
  79. package/src/ummaya/tools/documents/permissions.py +110 -0
  80. package/src/ummaya/tools/documents/planner.py +616 -0
  81. package/src/ummaya/tools/documents/registry.py +2733 -0
  82. package/src/ummaya/tools/documents/render.py +978 -0
  83. package/src/ummaya/tools/documents/render_comparison.py +113 -0
  84. package/src/ummaya/tools/documents/render_comparison_models.py +74 -0
  85. package/src/ummaya/tools/documents/render_comparison_regions.py +73 -0
  86. package/src/ummaya/tools/documents/render_comparison_style.py +161 -0
  87. package/src/ummaya/tools/documents/reread.py +157 -0
  88. package/src/ummaya/tools/documents/runtime_authoring.py +244 -0
  89. package/src/ummaya/tools/documents/runtime_authoring_bundle.py +76 -0
  90. package/src/ummaya/tools/documents/scorecard.py +184 -0
  91. package/src/ummaya/tools/documents/socratic_planner.py +193 -0
  92. package/src/ummaya/tools/documents/style.py +48 -0
  93. package/src/ummaya/tools/documents/tool_defs.py +523 -0
  94. package/src/ummaya/tools/documents/validate.py +347 -0
  95. package/src/ummaya/tools/executor.py +29 -0
  96. package/src/ummaya/tools/live_proxy.py +0 -3
  97. package/src/ummaya/tools/models.py +5 -1
  98. package/src/ummaya/tools/register_all.py +8 -0
  99. package/src/ummaya/tools/registry.py +10 -1
  100. package/src/ummaya/tools/routing/__init__.py +59 -0
  101. package/src/ummaya/tools/routing/builder.py +105 -0
  102. package/src/ummaya/tools/routing/cards.py +29 -0
  103. package/src/ummaya/tools/routing/decision_service.py +534 -0
  104. package/src/ummaya/tools/routing/decision_types.py +74 -0
  105. package/src/ummaya/tools/routing/feasibility.py +122 -0
  106. package/src/ummaya/tools/routing/intent.py +17 -0
  107. package/src/ummaya/tools/routing/intent_extractor.py +207 -0
  108. package/src/ummaya/tools/routing/intent_patterns.py +160 -0
  109. package/src/ummaya/tools/routing/intent_public_data.py +150 -0
  110. package/src/ummaya/tools/routing/intent_types.py +48 -0
  111. package/src/ummaya/tools/routing/lint.py +78 -0
  112. package/src/ummaya/tools/routing/metadata.py +174 -0
  113. package/src/ummaya/tools/routing/projection.py +340 -0
  114. package/src/ummaya/tools/routing/retrieval_policy.py +629 -0
  115. package/src/ummaya/tools/routing/schema.py +81 -0
  116. package/src/ummaya/tools/routing/types.py +96 -0
  117. package/src/ummaya/tools/routing_index.py +2 -2
  118. package/src/ummaya/tools/search.py +34 -746
  119. package/tests/fixtures/documents/public_forms/baselines.yaml +113 -0
  120. package/tui/package.json +1 -1
  121. package/tui/src/.cc-byte-identical-whitelist.yaml +266 -0
  122. package/tui/src/QueryEngine.ts +12 -8
  123. package/tui/src/bridge/inboundAttachments.ts +3 -3
  124. package/tui/src/cli/handlers/auth.ts +3 -12
  125. package/tui/src/cli/print.ts +7 -7
  126. package/tui/src/commands/insights.ts +1 -1
  127. package/tui/src/commands/install-github-app/types.ts +8 -30
  128. package/tui/src/commands/plugin/types.ts +6 -28
  129. package/tui/src/commands/plugin/unifiedTypes.ts +4 -26
  130. package/tui/src/commands/rename/generateSessionName.ts +1 -1
  131. package/tui/src/components/Feedback.tsx +1 -1
  132. package/tui/src/components/LogoV2/EmergencyTip.tsx +11 -2
  133. package/tui/src/components/LogoV2/WelcomeV2.tsx +1 -3
  134. package/tui/src/components/ScrollKeybindingHandler.tsx +6 -6
  135. package/tui/src/components/Spinner/types.ts +6 -28
  136. package/tui/src/components/agents/generateAgent.ts +1 -1
  137. package/tui/src/components/agents/new-agent-creation/types.ts +4 -26
  138. package/tui/src/components/config/EnvSecretIsolatedEditor.tsx +1 -1
  139. package/tui/src/components/mcp/types.ts +16 -38
  140. package/tui/src/components/messages/AssistantToolUseMessage.tsx +3 -2
  141. package/tui/src/components/messages/UserCrossSessionMessage.ts +16 -4
  142. package/tui/src/components/messages/UserForkBoilerplateMessage.ts +16 -4
  143. package/tui/src/components/messages/UserGitHubWebhookMessage.ts +16 -4
  144. package/tui/src/components/messages/UserToolResultMessage/utils.tsx +3 -2
  145. package/tui/src/components/permissions/MonitorPermissionRequest/MonitorPermissionRequest.ts +9 -4
  146. package/tui/src/components/permissions/ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.ts +9 -4
  147. package/tui/src/components/primitive/DocumentSocraticReviewBlock.tsx +129 -0
  148. package/tui/src/components/primitive/DocumentToolResultCard.tsx +224 -0
  149. package/tui/src/components/primitive/documentSocraticReview.ts +215 -0
  150. package/tui/src/components/primitive/index.tsx +43 -1
  151. package/tui/src/components/primitive/types.ts +137 -0
  152. package/tui/src/components/ui/option.ts +4 -26
  153. package/tui/src/constants/common.ts +0 -2
  154. package/tui/src/constants/prompts.ts +4 -3
  155. package/tui/src/constants/querySource.ts +4 -26
  156. package/tui/src/entrypoints/sdk/controlTypes.ts +26 -48
  157. package/tui/src/entrypoints/sdk/coreTypes.generated.ts +3 -25
  158. package/tui/src/entrypoints/sdk/runtimeTypes.ts +38 -60
  159. package/tui/src/entrypoints/sdk/sdkUtilityTypes.ts +4 -26
  160. package/tui/src/entrypoints/sdk/settingsTypes.generated.ts +3 -25
  161. package/tui/src/entrypoints/sdk/toolTypes.ts +3 -25
  162. package/tui/src/hooks/toolPermission/handlers/interactiveHandler.ts +10 -0
  163. package/tui/src/hooks/useApiKeyVerification.ts +1 -1
  164. package/tui/src/hooks/useVirtualScroll.ts +1 -1
  165. package/tui/src/ink/ink.tsx +33 -14
  166. package/tui/src/ink/reconciler.ts +2 -3
  167. package/tui/src/ink/render-to-screen.ts +30 -10
  168. package/tui/src/ipc/bridge.ts +62 -15
  169. package/tui/src/ipc/bridgeSingleton.ts +5 -1
  170. package/tui/src/ipc/codec.ts +3 -3
  171. package/tui/src/ipc/frames.generated.ts +12 -12
  172. package/tui/src/ipc/llmClient.ts +151 -27
  173. package/tui/src/ipc/schema/frame.schema.json +1 -1
  174. package/tui/src/keybindings/defaultBindings.ts +4 -0
  175. package/tui/src/main.tsx +29 -11
  176. package/tui/src/native-ts/file-index/index.ts +33 -3
  177. package/tui/src/observability/surface.ts +2 -2
  178. package/tui/src/probes/toolRegistryProbe.tsx +3 -1
  179. package/tui/src/projectOnboardingState.ts +7 -6
  180. package/tui/src/query/chatMessageTypes.ts +18 -0
  181. package/tui/src/query/chatMessagesBuilder.ts +1 -1
  182. package/tui/src/query/deps.ts +1 -1
  183. package/tui/src/query/messageGuards.ts +106 -0
  184. package/tui/src/query/publicDataTerminalRepair.ts +384 -0
  185. package/tui/src/query/run.ts +1075 -0
  186. package/tui/src/query/supportBoundary.ts +168 -0
  187. package/tui/src/query/toolResultErrors.ts +103 -0
  188. package/tui/src/query/toolRunner.ts +687 -0
  189. package/tui/src/query/unavailableToolRepair.ts +118 -0
  190. package/tui/src/query.ts +9 -2186
  191. package/tui/src/screens/REPL.tsx +40 -29
  192. package/tui/src/services/api/adapterManifest.ts +4 -0
  193. package/tui/src/services/api/backendChat/events.ts +117 -0
  194. package/tui/src/services/api/backendChat/finalMessage.ts +40 -0
  195. package/tui/src/services/api/backendChat/frame.ts +9 -0
  196. package/tui/src/services/api/backendChat/streaming.ts +430 -0
  197. package/tui/src/services/api/backendChat/types.ts +62 -0
  198. package/tui/src/services/api/backendChat.ts +1 -0
  199. package/tui/src/services/api/client.ts +65 -2
  200. package/tui/src/services/api/errorUtils.ts +5 -5
  201. package/tui/src/services/api/errors.ts +1 -1
  202. package/tui/src/services/api/logging.ts +1 -1
  203. package/tui/src/services/api/ummaya/evidence.ts +194 -0
  204. package/tui/src/services/api/ummaya/messages.ts +255 -0
  205. package/tui/src/services/api/ummaya/nonStreaming.ts +66 -0
  206. package/tui/src/services/api/ummaya/provider.ts +200 -0
  207. package/tui/src/services/api/ummaya/reasoning.ts +24 -0
  208. package/tui/src/services/api/ummaya/request.ts +200 -0
  209. package/tui/src/services/api/ummaya/selectionContext.ts +240 -0
  210. package/tui/src/services/api/ummaya/streaming.ts +365 -0
  211. package/tui/src/services/api/ummaya/streamingPayload.ts +129 -0
  212. package/tui/src/services/api/ummaya/streamingReader.ts +40 -0
  213. package/tui/src/services/api/ummaya/toolSelection.ts +217 -0
  214. package/tui/src/services/api/ummaya/types.ts +110 -0
  215. package/tui/src/services/api/ummaya/usage.ts +30 -0
  216. package/tui/src/services/api/ummaya.ts +26 -418
  217. package/tui/src/services/api/withRetry.ts +1 -1
  218. package/tui/src/services/awaySummary.ts +2 -2
  219. package/tui/src/services/claudeAiLimits.ts +1 -1
  220. package/tui/src/services/compact/autoCompact.ts +1 -1
  221. package/tui/src/services/compact/compact.ts +1 -1
  222. package/tui/src/services/lsp/types.ts +8 -30
  223. package/tui/src/services/tips/types.ts +6 -28
  224. package/tui/src/services/tokenEstimation.ts +1 -1
  225. package/tui/src/services/toolRegistry/bootGuard.ts +5 -5
  226. package/tui/src/services/toolUseSummary/toolUseSummaryGenerator.ts +1 -1
  227. package/tui/src/services/tools/toolExecution.ts +94 -1
  228. package/tui/src/store/pendingPermissionSlot.ts +1 -1
  229. package/tui/src/store/session-store.ts +10 -36
  230. package/tui/src/stubs/any-stub.ts +15 -10
  231. package/tui/src/stubs/color-diff-napi.ts +37 -23
  232. package/tui/src/stubs/globals.d.ts +3 -3
  233. package/tui/src/stubs/macro-preload.ts +23 -12
  234. package/tui/src/tools/AdapterTool/AdapterTool.ts +1207 -714
  235. package/tui/src/tools/AdapterTool/routeDiagnostics.ts +75 -0
  236. package/tui/src/tools/AgentTool/AgentTool.tsx +84 -1371
  237. package/tui/src/tools/AgentTool/agentToolHandoff.ts +114 -0
  238. package/tui/src/tools/AgentTool/agentToolPartialResult.ts +16 -0
  239. package/tui/src/tools/AgentTool/agentToolProgress.ts +32 -0
  240. package/tui/src/tools/AgentTool/agentToolResolver.ts +161 -0
  241. package/tui/src/tools/AgentTool/agentToolResult.ts +163 -0
  242. package/tui/src/tools/AgentTool/agentToolUtils.ts +14 -686
  243. package/tui/src/tools/AgentTool/asyncAgentLifecycle.ts +208 -0
  244. package/tui/src/tools/AgentTool/asyncLifecycle.ts +153 -0
  245. package/tui/src/tools/AgentTool/backgroundedCompletion.ts +126 -0
  246. package/tui/src/tools/AgentTool/backgroundedLifecycle.ts +174 -0
  247. package/tui/src/tools/AgentTool/foregroundBackground.ts +83 -0
  248. package/tui/src/tools/AgentTool/foregroundDrain.tsx +133 -0
  249. package/tui/src/tools/AgentTool/foregroundFinalize.ts +98 -0
  250. package/tui/src/tools/AgentTool/foregroundLifecycle.tsx +237 -0
  251. package/tui/src/tools/AgentTool/foregroundProgress.tsx +169 -0
  252. package/tui/src/tools/AgentTool/foregroundTask.ts +89 -0
  253. package/tui/src/tools/AgentTool/forkSubagent.ts +1 -12
  254. package/tui/src/tools/AgentTool/forkSubagentGate.ts +34 -0
  255. package/tui/src/tools/AgentTool/launchRouting.ts +203 -0
  256. package/tui/src/tools/AgentTool/lifecycle.ts +244 -0
  257. package/tui/src/tools/AgentTool/mcpRouting.ts +73 -0
  258. package/tui/src/tools/AgentTool/orchestrationSupport.ts +70 -0
  259. package/tui/src/tools/AgentTool/permissions.ts +39 -0
  260. package/tui/src/tools/AgentTool/promptSetup.ts +181 -0
  261. package/tui/src/tools/AgentTool/remoteRouting.ts +62 -0
  262. package/tui/src/tools/AgentTool/resultMapping.ts +116 -0
  263. package/tui/src/tools/AgentTool/resumeAgent.ts +39 -107
  264. package/tui/src/tools/AgentTool/resumeAgentHelpers.ts +140 -0
  265. package/tui/src/tools/AgentTool/runAgent.ts +1 -1
  266. package/tui/src/tools/AgentTool/runtimeConfig.ts +57 -0
  267. package/tui/src/tools/AgentTool/schemas.ts +196 -0
  268. package/tui/src/tools/AgentTool/sourceVerificationPropagation.ts +263 -0
  269. package/tui/src/tools/AgentTool/worktreeLifecycle.ts +105 -0
  270. package/tui/src/tools/AskUserQuestionTool/AskUserQuestionTool.tsx +174 -202
  271. package/tui/src/tools/BashTool/BashTool.tsx +71 -1072
  272. package/tui/src/tools/BashTool/bashCommandHelpers.ts +12 -12
  273. package/tui/src/tools/BashTool/bashPermissions/astPreflight.ts +173 -0
  274. package/tui/src/tools/BashTool/bashPermissions/classifierChecks.ts +199 -0
  275. package/tui/src/tools/BashTool/bashPermissions/compoundGuards.ts +53 -0
  276. package/tui/src/tools/BashTool/bashPermissions/constants.ts +99 -0
  277. package/tui/src/tools/BashTool/bashPermissions/index.ts +38 -0
  278. package/tui/src/tools/BashTool/bashPermissions/legacyMisparsing.ts +62 -0
  279. package/tui/src/tools/BashTool/bashPermissions/main.ts +135 -0
  280. package/tui/src/tools/BashTool/bashPermissions/normalizedCommands.ts +33 -0
  281. package/tui/src/tools/BashTool/bashPermissions/operatorFlow.ts +98 -0
  282. package/tui/src/tools/BashTool/bashPermissions/permissionChecks.ts +200 -0
  283. package/tui/src/tools/BashTool/bashPermissions/prefixSuggestions.ts +88 -0
  284. package/tui/src/tools/BashTool/bashPermissions/promptClassifierRules.ts +125 -0
  285. package/tui/src/tools/BashTool/bashPermissions/ruleDelegates.ts +19 -0
  286. package/tui/src/tools/BashTool/bashPermissions/ruleMatching.ts +145 -0
  287. package/tui/src/tools/BashTool/bashPermissions/sandboxAutoAllow.ts +75 -0
  288. package/tui/src/tools/BashTool/bashPermissions/subcommandFlow.ts +205 -0
  289. package/tui/src/tools/BashTool/bashPermissions/subcommandGuards.ts +73 -0
  290. package/tui/src/tools/BashTool/bashPermissions/subcommandResultHelpers.ts +116 -0
  291. package/tui/src/tools/BashTool/bashPermissions/types.ts +26 -0
  292. package/tui/src/tools/BashTool/bashPermissions/wrapperStripping.ts +139 -0
  293. package/tui/src/tools/BashTool/bashPermissions.ts +26 -2621
  294. package/tui/src/tools/BashTool/call.ts +202 -0
  295. package/tui/src/tools/BashTool/callLoader.ts +35 -0
  296. package/tui/src/tools/BashTool/commandClassification.ts +151 -0
  297. package/tui/src/tools/BashTool/commandClassificationLoader.ts +40 -0
  298. package/tui/src/tools/BashTool/cwdReset.ts +33 -0
  299. package/tui/src/tools/BashTool/lineTruncation.ts +11 -0
  300. package/tui/src/tools/BashTool/modeValidation.ts +13 -1
  301. package/tui/src/tools/BashTool/outputPersistence.ts +42 -0
  302. package/tui/src/tools/BashTool/permissionClassification.ts +66 -0
  303. package/tui/src/tools/BashTool/permissionLoader.ts +44 -0
  304. package/tui/src/tools/BashTool/resultLoader.ts +29 -0
  305. package/tui/src/tools/BashTool/resultMapping.ts +83 -0
  306. package/tui/src/tools/BashTool/sandboxPolicy.ts +79 -0
  307. package/tui/src/tools/BashTool/schemas.ts +65 -0
  308. package/tui/src/tools/BashTool/sedEditExecution.ts +59 -0
  309. package/tui/src/tools/BashTool/shellExecution.tsx +245 -0
  310. package/tui/src/tools/BashTool/shellOutputUtils.ts +85 -0
  311. package/tui/src/tools/BashTool/shellPermissionGauntlet.ts +97 -0
  312. package/tui/src/tools/BashTool/uiLoader.ts +37 -0
  313. package/tui/src/tools/BriefTool/upload.ts +1 -1
  314. package/tui/src/tools/CalculatorTool/parser.ts +2 -2
  315. package/tui/src/tools/DocumentPrimitive/DocumentPrimitive.ts +262 -0
  316. package/tui/src/tools/DocumentPrimitive/dispatchNormalization.ts +270 -0
  317. package/tui/src/tools/DocumentPrimitive/documentDestinationPath.ts +18 -0
  318. package/tui/src/tools/DocumentPrimitive/documentMutationGuard.ts +22 -0
  319. package/tui/src/tools/DocumentPrimitive/documentPatchNormalization.ts +248 -0
  320. package/tui/src/tools/DocumentPrimitive/documentSourceVerification.ts +245 -0
  321. package/tui/src/tools/DocumentPrimitive/documentSourceVerificationFields.ts +103 -0
  322. package/tui/src/tools/DocumentPrimitive/modelVisibleOutput.ts +40 -0
  323. package/tui/src/tools/DocumentPrimitive/prompt.ts +35 -0
  324. package/tui/src/tools/FileEditTool/FileEditTool.ts +9 -507
  325. package/tui/src/tools/FileEditTool/call.ts +228 -0
  326. package/tui/src/tools/FileEditTool/validateInput.ts +196 -0
  327. package/tui/src/tools/FileReadTool/imageProcessor.ts +13 -0
  328. package/tui/src/tools/FileWriteTool/FileWriteTool.ts +7 -300
  329. package/tui/src/tools/FileWriteTool/call.ts +223 -0
  330. package/tui/src/tools/FileWriteTool/validateInput.ts +80 -0
  331. package/tui/src/tools/ListMcpResourcesTool/ListMcpResourcesTool.ts +19 -3
  332. package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +25 -32
  333. package/tui/src/tools/LookupPrimitive/prompt.ts +0 -2
  334. package/tui/src/tools/MCPTool/trustPolicy.ts +118 -0
  335. package/tui/src/tools/McpAuthTool/McpAuthTool.ts +21 -3
  336. package/tui/src/tools/NotebookEditTool/NotebookEditTool.ts +7 -326
  337. package/tui/src/tools/NotebookEditTool/call.ts +254 -0
  338. package/tui/src/tools/NotebookEditTool/notebookModel.ts +51 -0
  339. package/tui/src/tools/NotebookEditTool/validateInput.ts +142 -0
  340. package/tui/src/tools/PowerShellTool/PowerShellTool.tsx +46 -937
  341. package/tui/src/tools/PowerShellTool/acceptEditsCommandValidation.ts +162 -0
  342. package/tui/src/tools/PowerShellTool/call.ts +179 -0
  343. package/tui/src/tools/PowerShellTool/callLoader.ts +37 -0
  344. package/tui/src/tools/PowerShellTool/commandClassification.ts +86 -0
  345. package/tui/src/tools/PowerShellTool/modeValidation.ts +25 -332
  346. package/tui/src/tools/PowerShellTool/outputPersistence.ts +42 -0
  347. package/tui/src/tools/PowerShellTool/permissionClassification.ts +28 -0
  348. package/tui/src/tools/PowerShellTool/resultLoader.ts +31 -0
  349. package/tui/src/tools/PowerShellTool/resultMapping.ts +75 -0
  350. package/tui/src/tools/PowerShellTool/schemas.ts +40 -0
  351. package/tui/src/tools/PowerShellTool/shellExecution.tsx +258 -0
  352. package/tui/src/tools/PowerShellTool/symlinkModeValidation.ts +44 -0
  353. package/tui/src/tools/PowerShellTool/uiLoader.ts +37 -0
  354. package/tui/src/tools/PowerShellTool/validation.ts +39 -0
  355. package/tui/src/tools/ReadMcpResourceTool/ReadMcpResourceTool.ts +19 -3
  356. package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +1 -11
  357. package/tui/src/tools/ResolveLocationPrimitive/prompt.ts +2 -6
  358. package/tui/src/tools/SkillTool/SkillTool.ts +2 -2
  359. package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +27 -10
  360. package/tui/src/tools/TaskCreateTool/TaskCreateTool.ts +16 -2
  361. package/tui/src/tools/TaskGetTool/TaskGetTool.ts +23 -3
  362. package/tui/src/tools/TaskListTool/TaskListTool.ts +22 -4
  363. package/tui/src/tools/TaskOutputTool/TaskOutputTool.tsx +46 -547
  364. package/tui/src/tools/TaskOutputTool/lookup.ts +216 -0
  365. package/tui/src/tools/TaskOutputTool/render.tsx +257 -0
  366. package/tui/src/tools/TaskOutputTool/schemas.ts +55 -0
  367. package/tui/src/tools/TaskOutputTool/serialization.ts +36 -0
  368. package/tui/src/tools/TaskStopTool/TaskStopTool.ts +10 -0
  369. package/tui/src/tools/TaskUpdateTool/TaskUpdateTool.ts +14 -364
  370. package/tui/src/tools/TaskUpdateTool/completion.ts +62 -0
  371. package/tui/src/tools/TaskUpdateTool/schemas.ts +62 -0
  372. package/tui/src/tools/TaskUpdateTool/serialization.ts +46 -0
  373. package/tui/src/tools/TaskUpdateTool/statusUpdate.ts +247 -0
  374. package/tui/src/tools/TodoWriteTool/TodoWriteTool.ts +21 -2
  375. package/tui/src/tools/ToolSearchTool/ToolSearchTool.ts +21 -302
  376. package/tui/src/tools/ToolSearchTool/ccSupportTools.ts +223 -0
  377. package/tui/src/tools/ToolSearchTool/descriptionCache.ts +50 -0
  378. package/tui/src/tools/ToolSearchTool/keywordSearch.ts +216 -0
  379. package/tui/src/tools/ToolSearchTool/prompt.ts +10 -4
  380. package/tui/src/tools/ToolSearchTool/resultMapping.ts +30 -0
  381. package/tui/src/tools/ToolSearchTool/schemas.ts +30 -0
  382. package/tui/src/tools/ToolSearchTool/searchPool.ts +47 -0
  383. package/tui/src/tools/ToolSearchTool/supportIntentHints.ts +140 -0
  384. package/tui/src/tools/TranslateTool/TranslateTool.ts +1 -1
  385. package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +2 -1
  386. package/tui/src/tools/WebFetchTool/WebFetchTool.ts +43 -138
  387. package/tui/src/tools/WebFetchTool/call.ts +227 -0
  388. package/tui/src/tools/WebFetchTool/resolvedAddressSafety.ts +78 -0
  389. package/tui/src/tools/WebFetchTool/sourceVerification.ts +204 -0
  390. package/tui/src/tools/WebFetchTool/types.ts +23 -0
  391. package/tui/src/tools/WebFetchTool/urlSafety.ts +181 -0
  392. package/tui/src/tools/WebFetchTool/utils.ts +1 -1
  393. package/tui/src/tools/WebSearchTool/UI.tsx +0 -1
  394. package/tui/src/tools/WebSearchTool/WebSearchTool.ts +9 -313
  395. package/tui/src/tools/WebSearchTool/call.ts +33 -0
  396. package/tui/src/tools/WebSearchTool/responseMapping.ts +190 -0
  397. package/tui/src/tools/WebSearchTool/resultBlock.ts +47 -0
  398. package/tui/src/tools/WebSearchTool/schemas.ts +47 -0
  399. package/tui/src/tools/WebSearchTool/toolSchema.ts +12 -0
  400. package/tui/src/tools/WorkspaceToolAdapter/WorkspaceToolAdapter.ts +79 -0
  401. package/tui/src/tools/WorkspaceToolAdapter/allowedRootPolicy.ts +85 -0
  402. package/tui/src/tools/WorkspaceToolAdapter/documentFormatGuards.ts +73 -0
  403. package/tui/src/tools/WorkspaceToolAdapter/inputNormalization.ts +105 -0
  404. package/tui/src/tools/WorkspaceToolAdapter/mcpExposurePolicy.ts +64 -0
  405. package/tui/src/tools/WorkspaceToolAdapter/toolDefFactory.ts +215 -0
  406. package/tui/src/tools/WorkspaceToolAdapter/toolNames.ts +6 -0
  407. package/tui/src/tools/WorkspaceToolAdapter/workspacePolicy.ts +15 -0
  408. package/tui/src/tools/_shared/dispatchPrimitive.ts +6 -6
  409. package/tui/src/tools/_shared/documentChangeToPatch.ts +125 -0
  410. package/tui/src/tools/_shared/documentDispatchArguments.ts +87 -0
  411. package/tui/src/tools/_shared/documentPrimitiveTimeout.ts +13 -0
  412. package/tui/src/tools/_shared/documentToolResultRender.ts +98 -0
  413. package/tui/src/tools/_shared/pendingCallRegistry.ts +1 -6
  414. package/tui/src/tools/_shared/rootPrimitiveInput.ts +1 -0
  415. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPatterns.ts +58 -0
  416. package/tui/src/tools/_shared/toolChoiceRepair/documentCompletionPrompt.ts +271 -0
  417. package/tui/src/tools/_shared/toolChoiceRepair/documentRepair.ts +452 -0
  418. package/tui/src/tools/_shared/toolChoiceRepair/messageAccess.ts +80 -0
  419. package/tui/src/tools/_shared/toolChoiceRepair/publicDataRepair.ts +92 -0
  420. package/tui/src/tools/_shared/toolChoiceRepair/supportRepair.ts +135 -0
  421. package/tui/src/tools/_shared/toolChoiceRepair.ts +55 -860
  422. package/tui/src/tools/shared/mockDisclaimer.ts +1 -1
  423. package/tui/src/tools.ts +39 -190
  424. package/tui/src/types/fileSuggestion.ts +4 -26
  425. package/tui/src/types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts +186 -148
  426. package/tui/src/types/generated/events_mono/common/v1/auth.ts +25 -11
  427. package/tui/src/types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts +47 -30
  428. package/tui/src/types/generated/google/protobuf/timestamp.ts +21 -7
  429. package/tui/src/types/message.ts +80 -102
  430. package/tui/src/types/messageQueueTypes.ts +6 -28
  431. package/tui/src/types/notebook.ts +16 -38
  432. package/tui/src/types/statusLine.ts +4 -26
  433. package/tui/src/types/tools.ts +24 -46
  434. package/tui/src/types/utils.ts +6 -28
  435. package/tui/src/upstreamproxy/relay.ts +7 -3
  436. package/tui/src/upstreamproxy/upstreamproxy.ts +1 -1
  437. package/tui/src/utils/assistantMessageFactories.ts +9 -3
  438. package/tui/src/utils/auth.ts +129 -139
  439. package/tui/src/utils/bash/ast.ts +23 -23
  440. package/tui/src/utils/bash/bashParser.ts +5 -5
  441. package/tui/src/utils/billing.ts +1 -1
  442. package/tui/src/utils/collapseReadSearch.ts +3 -3
  443. package/tui/src/utils/cronTasks.ts +1 -1
  444. package/tui/src/utils/execFileNoThrow.ts +1 -1
  445. package/tui/src/utils/filePersistence/types.ts +16 -38
  446. package/tui/src/utils/forkedAgent.ts +1 -1
  447. package/tui/src/utils/gracefulShutdown.ts +4 -4
  448. package/tui/src/utils/heapDumpService.ts +12 -8
  449. package/tui/src/utils/hooks/apiQueryHookHelper.ts +1 -1
  450. package/tui/src/utils/hooks/execPromptHook.ts +1 -1
  451. package/tui/src/utils/hooks/skillImprovement.ts +1 -1
  452. package/tui/src/utils/mcp/dateTimeParser.ts +1 -1
  453. package/tui/src/utils/messages.ts +18 -0
  454. package/tui/src/utils/migrateSessions.ts +3 -3
  455. package/tui/src/utils/model/model.ts +6 -6
  456. package/tui/src/utils/permissions/yoloClassifier.ts +1 -1
  457. package/tui/src/utils/plugins/headlessPluginInstall.ts +1 -1
  458. package/tui/src/utils/plugins/mcpPluginIntegration.ts +1 -1
  459. package/tui/src/utils/plugins/mcpbHandler.ts +1 -1
  460. package/tui/src/utils/plugins/pluginLoader.ts +8 -8
  461. package/tui/src/utils/protectedNamespace.ts +5 -3
  462. package/tui/src/utils/rawJsonToolCall.ts +242 -0
  463. package/tui/src/utils/ripgrep.ts +16 -7
  464. package/tui/src/utils/sessionTitle.ts +1 -1
  465. package/tui/src/utils/settings/permissionValidation.ts +14 -2
  466. package/tui/src/utils/shell/prefix.ts +1 -1
  467. package/tui/src/utils/sideQuery.ts +1 -1
  468. package/tui/src/utils/systemThemeWatcher.ts +13 -3
  469. package/tui/src/utils/teleport.tsx +1 -1
  470. package/uv.lock +400 -14
  471. package/tui/src/services/api/claude.ts +0 -3540
  472. package/tui/src/tools/_shared/directPublicDataGuard.ts +0 -362
  473. package/tui/src/tools/_shared/kmaAnalysisGuard.ts +0 -197
  474. package/tui/src/tools/_shared/kmaAviationGuard.ts +0 -70
  475. package/tui/src/tools/_shared/nmcAedGuard.ts +0 -234
  476. package/tui/src/tools/_shared/protectedCheckGuard.ts +0 -207
  477. package/tui/src/tools/_shared/textToolCallGuard.ts +0 -91
@@ -0,0 +1,18 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ const LOCAL_DOCUMENT_PATH_RE =
4
+ /(?:~|\/)[^\s"'<>]+?\.(?:hwpx|hwp|docx|pdf|xlsx|pptx|odt|ods|odp|doc|xls|ppt|csv|txt|md|json|xml|html)/giu
5
+
6
+ export function lastLocalDocumentPath(text: string): string | undefined {
7
+ let latest: string | undefined
8
+ for (const match of text.matchAll(LOCAL_DOCUMENT_PATH_RE)) {
9
+ const rawPath = match[0]?.trim()
10
+ if (rawPath === undefined || rawPath === '') continue
11
+ latest = trimTrailingPunctuation(rawPath)
12
+ }
13
+ return latest
14
+ }
15
+
16
+ function trimTrailingPunctuation(path: string): string {
17
+ return path.replace(/[),.;:,。]+$/u, '')
18
+ }
@@ -0,0 +1,22 @@
1
+ import type { ValidationResult } from '../../Tool.js'
2
+ import {
3
+ getFsImplementation,
4
+ resolveDeepestExistingAncestorSync,
5
+ } from '../../utils/fsOperations.js'
6
+ import { documentDerivativeMutationValidation } from '../WorkspaceToolAdapter/documentFormatGuards.js'
7
+
8
+ export function documentDerivativeMutationValidationForResolvedTarget(
9
+ filePath: string,
10
+ ): ValidationResult | null {
11
+ const directValidation = documentDerivativeMutationValidation(filePath)
12
+ if (directValidation !== null) return directValidation
13
+ if (filePath.startsWith('\\\\') || filePath.startsWith('//')) return null
14
+
15
+ const resolvedPath = resolveDeepestExistingAncestorSync(
16
+ getFsImplementation(),
17
+ filePath,
18
+ )
19
+ if (resolvedPath === undefined || resolvedPath === filePath) return null
20
+
21
+ return documentDerivativeMutationValidation(resolvedPath)
22
+ }
@@ -0,0 +1,248 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ import { createHash } from 'node:crypto'
4
+ import {
5
+ enforceDocumentSourceApprovalForDispatch,
6
+ normalizeDocumentSourceSupportedPatchesForDispatch,
7
+ } from './documentSourceVerification.js'
8
+
9
+ const APPROVAL_TOKEN_RE = /승인|\bapprove\b/giu
10
+ const APPROVAL_WORD_RE = /승인|\bapprove\b/iu
11
+ const ISSUED_DRAFT_ID_RE = /^draft-[a-f0-9]{24}$/u
12
+ const DRAFT_SHA256_RE = /^[a-f0-9]{64}$/u
13
+ const DECISION_SPLIT_RE =
14
+ /[.!?。]+|\b(?:but|however|nevertheless|instead|rather|and)\b|(?:하지만|그러나|그렇지만|다만|지만)/iu
15
+ const EXPLICIT_ENGLISH_APPROVAL_SEGMENT_RE =
16
+ /^(?:yes[,.]?\s+)?i\s+approve(?:\s+(?:this|the|that|current|revised|updated|final|draft|version|document|text|content|answer|change|changes))*[.!]?$/iu
17
+ const EXPLICIT_KOREAN_APPROVAL_SEGMENT_RE =
18
+ /^(?:(?:지금|현재|이번|방금|수정한|이|그)\s*)?(?:(?:초안|문안|내용|답변|문서)\s*(?:은|는|을|를)?\s*)?승인(?:해|합니다|할게|하겠습니다)[.!。]?$/u
19
+ const ENGLISH_NEGATION_PREFIX_RE =
20
+ /\b(?:do\s+not|don't|didn't|isn't|cannot|can't|won't|haven't|hasn't|hadn't|never|unable\s+to|refuse\s+to|decline\s+to|not\b)/iu
21
+ const KOREAN_NEGATION_PREFIX_RE = /(?:불\s*|미\s*)$/u
22
+ const KOREAN_NEGATION_SUFFIX_RE =
23
+ /^\s*(?:은|을|이|가|도|만|으로)?\s*(?:하지|않|안|못|거부|거절|반려|보류|취소|처리하지|보지|간주하지|할\s*수\s*없|아님|없)/u
24
+ const EXPLICIT_REJECTION_RE =
25
+ /(?:\bno\b|\breject(?:ed)?\b|\bdisapprove(?:d)?\b|\bunapproved\b|\bdo\s+not\s+treat\s+(?:that|this|it)\s+as\s+approval\b|아니|불\s*승인|미\s*승인|거절|거부|반려|보류|취소|승인(?:으로)?\s*처리하지)/giu
26
+
27
+ type StringPatch = {
28
+ readonly targetPath: string
29
+ readonly value: string
30
+ }
31
+
32
+ type ApprovedDraftFields = {
33
+ readonly draftId: string
34
+ readonly draftSha256: string
35
+ }
36
+
37
+ export function normalizeDocumentMutationPayloadsForDispatch(
38
+ args: Record<string, unknown>,
39
+ userText: string | undefined,
40
+ ): Record<string, unknown> {
41
+ const sourceNormalization = normalizeDocumentSourceSupportedPatchesForDispatch(
42
+ normalizeDocumentStylePayloadsForDispatch(
43
+ normalizeDocumentPatchPayloadsForDispatch(args),
44
+ ),
45
+ userText,
46
+ )
47
+ return enforceDocumentSourceApprovalForDispatch(
48
+ normalizeApprovedDraftPayloadForDispatch(sourceNormalization.args, userText),
49
+ sourceNormalization,
50
+ )
51
+ }
52
+
53
+ function normalizeDocumentPatchPayloadsForDispatch(
54
+ args: Record<string, unknown>,
55
+ ): Record<string, unknown> {
56
+ if (!Array.isArray(args.patches)) return args
57
+ return {
58
+ ...args,
59
+ patches: args.patches.map(normalizeDocumentPatchPayloadForDispatch),
60
+ }
61
+ }
62
+
63
+ function normalizeDocumentPatchPayloadForDispatch(patch: unknown): unknown {
64
+ const record = recordFrom(patch)
65
+ if (record === undefined || !Object.hasOwn(record, 'content')) return patch
66
+ const { content, ...withoutContent } = record
67
+ if (Object.hasOwn(withoutContent, 'value')) return withoutContent
68
+ return {
69
+ ...withoutContent,
70
+ value: content,
71
+ }
72
+ }
73
+
74
+ function normalizeDocumentStylePayloadsForDispatch(
75
+ args: Record<string, unknown>,
76
+ ): Record<string, unknown> {
77
+ if (!Array.isArray(args.styles)) return args
78
+ const styles = args.styles.filter(style => {
79
+ const record = recordFrom(style)
80
+ return record === undefined || Object.keys(record).length > 0
81
+ })
82
+ if (styles.length > 0) return styles.length === args.styles.length ? args : { ...args, styles }
83
+ const { styles: _styles, ...withoutStyles } = args
84
+ return withoutStyles
85
+ }
86
+
87
+ function normalizeApprovedDraftPayloadForDispatch(
88
+ args: Record<string, unknown>,
89
+ userText: string | undefined,
90
+ ): Record<string, unknown> {
91
+ const callerApproval = approvedDraftFieldsFromArgs(args)
92
+ const normalizedArgs = withoutApprovedDraftFields(args)
93
+ if (userText === undefined) return normalizedArgs
94
+ if (!hasAffirmativeApproval(userText)) return normalizedArgs
95
+ if (callerApproval !== undefined) {
96
+ return {
97
+ ...normalizedArgs,
98
+ approved_draft_id: callerApproval.draftId,
99
+ approved_draft_sha256: callerApproval.draftSha256,
100
+ }
101
+ }
102
+ const patches = stringPatchesFromArgs(normalizedArgs)
103
+ if (patches === undefined || patches.length === 0) return normalizedArgs
104
+ const approval = authoringApprovalForPatches(patches)
105
+ return {
106
+ ...normalizedArgs,
107
+ approved_draft_id: approval.draftId,
108
+ approved_draft_sha256: approval.draftSha256,
109
+ }
110
+ }
111
+
112
+ function withoutApprovedDraftFields(args: Record<string, unknown>): Record<string, unknown> {
113
+ const {
114
+ approved_draft_id: _approvedDraftId,
115
+ approved_draft_sha256: _approvedDraftSha256,
116
+ ...withoutApproval
117
+ } = args
118
+ return withoutApproval
119
+ }
120
+
121
+ function approvedDraftFieldsFromArgs(
122
+ args: Record<string, unknown>,
123
+ ): ApprovedDraftFields | undefined {
124
+ const draftId = stringField(args, 'approved_draft_id')
125
+ const draftSha256 = stringField(args, 'approved_draft_sha256')
126
+ if (draftId === undefined || draftSha256 === undefined) return undefined
127
+ if (!ISSUED_DRAFT_ID_RE.test(draftId)) return undefined
128
+ if (!DRAFT_SHA256_RE.test(draftSha256)) return undefined
129
+ return { draftId, draftSha256 }
130
+ }
131
+
132
+ function hasAffirmativeApproval(userText: string): boolean {
133
+ const decisions = decisionSegments(userText).flatMap(approvalDecisionFromSegment)
134
+ return decisions.at(-1) === true
135
+ }
136
+
137
+ function decisionSegments(userText: string): readonly string[] {
138
+ return userText
139
+ .split(DECISION_SPLIT_RE)
140
+ .map(segment => segment.trim())
141
+ .filter(segment => segment !== '')
142
+ }
143
+
144
+ function approvalDecisionFromSegment(segment: string): readonly boolean[] {
145
+ const decisions: Array<{ readonly index: number; readonly approved: boolean }> = []
146
+ for (const match of segment.matchAll(APPROVAL_TOKEN_RE)) {
147
+ const approvalToken = match[0]
148
+ const index = match.index
149
+ if (index !== undefined && isNegatedApprovalToken(segment, index, approvalToken.length)) {
150
+ decisions.push({ index, approved: false })
151
+ }
152
+ }
153
+ for (const match of segment.matchAll(EXPLICIT_REJECTION_RE)) {
154
+ const index = match.index
155
+ if (index !== undefined) {
156
+ decisions.push({ index, approved: false })
157
+ }
158
+ }
159
+ if (decisions.length === 0 && isExplicitApprovalSegment(segment)) {
160
+ decisions.push({ index: 0, approved: true })
161
+ }
162
+ return decisions
163
+ .sort((left, right) => left.index - right.index)
164
+ .map(decision => decision.approved)
165
+ }
166
+
167
+ function isExplicitApprovalSegment(segment: string): boolean {
168
+ const trimmedSegment = segment.trim()
169
+ return EXPLICIT_ENGLISH_APPROVAL_SEGMENT_RE.test(trimmedSegment) ||
170
+ EXPLICIT_KOREAN_APPROVAL_SEGMENT_RE.test(trimmedSegment)
171
+ }
172
+
173
+ function isNegatedApprovalToken(
174
+ segment: string,
175
+ index: number,
176
+ tokenLength: number,
177
+ ): boolean {
178
+ const beforeToken = segment.slice(0, index)
179
+ const afterToken = segment.slice(index + tokenLength)
180
+ return ENGLISH_NEGATION_PREFIX_RE.test(beforeToken) ||
181
+ KOREAN_NEGATION_PREFIX_RE.test(beforeToken) ||
182
+ KOREAN_NEGATION_SUFFIX_RE.test(afterToken)
183
+ }
184
+
185
+ function stringPatchesFromArgs(args: Record<string, unknown>): readonly StringPatch[] | undefined {
186
+ if (!Array.isArray(args.patches)) return []
187
+ const patches: StringPatch[] = []
188
+ for (const patch of args.patches) {
189
+ const record = recordFrom(patch)
190
+ if (record === undefined) return undefined
191
+ const targetPath = stringField(record, 'target_path')
192
+ const value = stringField(record, 'value')
193
+ if (targetPath === undefined || value === undefined) return undefined
194
+ patches.push({ targetPath, value })
195
+ }
196
+ return patches
197
+ }
198
+
199
+ function authoringApprovalForPatches(
200
+ patches: readonly StringPatch[],
201
+ ): { readonly draftId: string; readonly draftSha256: string } {
202
+ if (patches.length === 1) {
203
+ const patch = patches[0]
204
+ if (patch !== undefined) {
205
+ const draftSha256 = sha256Hex(patch.value)
206
+ return {
207
+ draftId: issuedAuthoringDraftId(patch.targetPath, draftSha256),
208
+ draftSha256,
209
+ }
210
+ }
211
+ }
212
+ const draftSha256 = sha256Hex(
213
+ patches
214
+ .map(patch => `${patch.targetPath}\0${sha256Hex(patch.value)}`)
215
+ .join('\0'),
216
+ )
217
+ return {
218
+ draftId: issuedAuthoringDraftId(authoringBundleTargetPath(patches), draftSha256),
219
+ draftSha256,
220
+ }
221
+ }
222
+
223
+ function issuedAuthoringDraftId(targetPath: string, draftSha256: string): string {
224
+ return `draft-${sha256Hex(`${targetPath}\0${draftSha256}`).slice(0, 24)}`
225
+ }
226
+
227
+ function authoringBundleTargetPath(patches: readonly StringPatch[]): string {
228
+ const targetPaths = patches.map(patch => patch.targetPath).join('\0')
229
+ return `bundle:${sha256Hex(targetPaths).slice(0, 24)}`
230
+ }
231
+
232
+ function stringField(
233
+ record: Record<string, unknown>,
234
+ key: string,
235
+ ): string | undefined {
236
+ const value = record[key]
237
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined
238
+ }
239
+
240
+ function sha256Hex(value: string): string {
241
+ return createHash('sha256').update(value, 'utf8').digest('hex')
242
+ }
243
+
244
+ function recordFrom(value: unknown): Record<string, unknown> | undefined {
245
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
246
+ ? Object.fromEntries(Object.entries(value))
247
+ : undefined
248
+ }
@@ -0,0 +1,245 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ import { detectPromptInjection } from '../WebFetchTool/sourceVerification.js'
4
+ import {
5
+ hasApprovedDraft,
6
+ patchRequestsSourceSupport,
7
+ patchTargetPath,
8
+ recordFrom,
9
+ recordsFrom,
10
+ sha256Hex,
11
+ sourceSupportHasUnsafeUrl,
12
+ sourceVerificationRequested,
13
+ stringField,
14
+ withoutApprovalFields,
15
+ withoutPatches,
16
+ } from './documentSourceVerificationFields.js'
17
+
18
+ export const DOCUMENT_SOURCE_VERIFICATION_POLICY =
19
+ 'source_supported_patch_requires_user_approval' as const
20
+
21
+ type QuestionWaitingReason =
22
+ | 'missing_source_support'
23
+ | 'blocked_source_support'
24
+ | 'prompt_injection_source_support'
25
+ | 'stale_source_support'
26
+ | 'missing_user_approval'
27
+
28
+ type QuestionWaitingField = {
29
+ readonly target_path: string
30
+ readonly reason: QuestionWaitingReason
31
+ }
32
+
33
+ type SourcePatchNormalization = {
34
+ readonly args: Record<string, unknown>
35
+ readonly sourceGovernedPatchCount: number
36
+ readonly sourceSupportedPatchCount: number
37
+ }
38
+
39
+ type SourceSupportEvaluation =
40
+ | {
41
+ readonly kind: 'supported'
42
+ readonly sourceSupport: Record<string, unknown>
43
+ }
44
+ | {
45
+ readonly kind: 'blocked'
46
+ readonly reason: QuestionWaitingReason
47
+ }
48
+
49
+ const SHA256_RE = /^[a-f0-9]{64}$/u
50
+
51
+ export function normalizeDocumentSourceSupportedPatchesForDispatch(
52
+ args: Record<string, unknown>,
53
+ userText: string | undefined,
54
+ ): SourcePatchNormalization {
55
+ if (!Array.isArray(args.patches)) {
56
+ return {
57
+ args,
58
+ sourceGovernedPatchCount: 0,
59
+ sourceSupportedPatchCount: 0,
60
+ }
61
+ }
62
+
63
+ const policyRequested = sourceVerificationRequested(args, userText)
64
+ const keptPatches: unknown[] = []
65
+ const questionWaitingFields: QuestionWaitingField[] = []
66
+ let sourceGovernedPatchCount = 0
67
+ let sourceSupportedPatchCount = 0
68
+
69
+ for (const patch of args.patches) {
70
+ const record = recordFrom(patch)
71
+ const patchRequiresSource = policyRequested || patchRequestsSourceSupport(record)
72
+ if (!patchRequiresSource) {
73
+ keptPatches.push(patch)
74
+ continue
75
+ }
76
+
77
+ sourceGovernedPatchCount += 1
78
+ const targetPath = patchTargetPath(record)
79
+ const evaluation = evaluateSourceSupport(record)
80
+ if (evaluation.kind === 'blocked') {
81
+ questionWaitingFields.push({
82
+ target_path: targetPath,
83
+ reason: evaluation.reason,
84
+ })
85
+ continue
86
+ }
87
+
88
+ sourceSupportedPatchCount += 1
89
+ keptPatches.push({
90
+ ...record,
91
+ source_support: evaluation.sourceSupport,
92
+ })
93
+ }
94
+
95
+ if (sourceGovernedPatchCount === 0) {
96
+ return {
97
+ args,
98
+ sourceGovernedPatchCount,
99
+ sourceSupportedPatchCount,
100
+ }
101
+ }
102
+
103
+ const nextArgs =
104
+ keptPatches.length > 0 ? { ...args, patches: keptPatches } : withoutPatches(args)
105
+
106
+ return {
107
+ args: withSourceVerificationMetadata(
108
+ nextArgs,
109
+ sourceSupportedPatchCount,
110
+ questionWaitingFields,
111
+ ),
112
+ sourceGovernedPatchCount,
113
+ sourceSupportedPatchCount,
114
+ }
115
+ }
116
+
117
+ export function enforceDocumentSourceApprovalForDispatch(
118
+ args: Record<string, unknown>,
119
+ normalization: SourcePatchNormalization,
120
+ ): Record<string, unknown> {
121
+ if (
122
+ normalization.sourceGovernedPatchCount === 0 ||
123
+ normalization.sourceSupportedPatchCount === 0 ||
124
+ hasApprovedDraft(args)
125
+ ) {
126
+ return failClosedIfNoPatches(args, normalization.sourceGovernedPatchCount)
127
+ }
128
+
129
+ const blockedFields = recordsFrom(args.patches).flatMap(patch => {
130
+ if (!patchRequestsSourceSupport(patch)) return []
131
+ return [
132
+ {
133
+ target_path: patchTargetPath(patch),
134
+ reason: 'missing_user_approval' as const,
135
+ },
136
+ ]
137
+ })
138
+ const keptPatches = recordsFrom(args.patches).filter(
139
+ patch => !patchRequestsSourceSupport(patch),
140
+ )
141
+ const withoutApproval = withoutApprovalFields(args)
142
+ const nextArgs =
143
+ keptPatches.length > 0
144
+ ? { ...withoutApproval, patches: keptPatches }
145
+ : withoutPatches(withoutApproval)
146
+
147
+ return failClosedIfNoPatches(
148
+ withSourceVerificationMetadata(nextArgs, 0, blockedFields),
149
+ normalization.sourceGovernedPatchCount,
150
+ )
151
+ }
152
+
153
+ function withSourceVerificationMetadata(
154
+ args: Record<string, unknown>,
155
+ sourceVerifiedPatchCount: number,
156
+ questionWaitingFields: readonly QuestionWaitingField[],
157
+ ): Record<string, unknown> {
158
+ const existingFields = Array.isArray(args.question_waiting_fields)
159
+ ? recordsFrom(args.question_waiting_fields)
160
+ : []
161
+ return {
162
+ ...args,
163
+ source_verification_policy: DOCUMENT_SOURCE_VERIFICATION_POLICY,
164
+ source_verified_patch_count: sourceVerifiedPatchCount,
165
+ question_waiting_fields: [...existingFields, ...questionWaitingFields],
166
+ }
167
+ }
168
+
169
+ function failClosedIfNoPatches(
170
+ args: Record<string, unknown>,
171
+ sourceGovernedPatchCount: number,
172
+ ): Record<string, unknown> {
173
+ if (sourceGovernedPatchCount === 0 || Array.isArray(args.patches)) {
174
+ return args
175
+ }
176
+ const {
177
+ approved_draft_id: _approvedDraftId,
178
+ approved_draft_sha256: _approvedDraftSha256,
179
+ destination_path: _destinationPath,
180
+ destination_display_name: _destinationDisplayName,
181
+ ...withoutMutation
182
+ } = args
183
+ return {
184
+ ...withoutMutation,
185
+ operation: 'inspect',
186
+ }
187
+ }
188
+
189
+ function evaluateSourceSupport(
190
+ patch: Record<string, unknown> | null,
191
+ ): SourceSupportEvaluation {
192
+ if (patch === null) return { kind: 'blocked', reason: 'missing_source_support' }
193
+ const value = stringField(patch, 'value')
194
+ if (value === undefined) return { kind: 'blocked', reason: 'missing_source_support' }
195
+ const support = recordFrom(patch.source_support) ?? recordFrom(patch.sourceSupport)
196
+ if (support === null) return { kind: 'blocked', reason: 'missing_source_support' }
197
+ if (support.state === 'blocked' || support.blocked_or_used === 'blocked') {
198
+ return { kind: 'blocked', reason: 'blocked_source_support' }
199
+ }
200
+ if (support.state !== 'source_supported') {
201
+ return { kind: 'blocked', reason: 'missing_source_support' }
202
+ }
203
+ if (sourceSupportHasUnsafeUrl(support)) {
204
+ return { kind: 'blocked', reason: 'blocked_source_support' }
205
+ }
206
+ if (sourceSupportHasPromptInjection(support)) {
207
+ return { kind: 'blocked', reason: 'prompt_injection_source_support' }
208
+ }
209
+ const sourceSha256 = stringField(support, 'source_sha256')
210
+ const citationHandle = stringField(support, 'citation_handle')
211
+ if (
212
+ sourceSha256 === undefined ||
213
+ citationHandle === undefined ||
214
+ !SHA256_RE.test(sourceSha256)
215
+ ) {
216
+ return { kind: 'blocked', reason: 'missing_source_support' }
217
+ }
218
+ if (sourceSha256 !== sha256Hex(value)) {
219
+ return { kind: 'blocked', reason: 'stale_source_support' }
220
+ }
221
+ return {
222
+ kind: 'supported',
223
+ sourceSupport: sanitizeSourceSupport(support),
224
+ }
225
+ }
226
+
227
+ function sourceSupportHasPromptInjection(
228
+ sourceSupport: Record<string, unknown>,
229
+ ): boolean {
230
+ if (sourceSupport.prompt_injection === 'detected') return true
231
+ const sourceText = stringField(sourceSupport, 'source_text')
232
+ return sourceText !== undefined && detectPromptInjection(sourceText) === 'detected'
233
+ }
234
+
235
+ function sanitizeSourceSupport(
236
+ sourceSupport: Record<string, unknown>,
237
+ ): Record<string, unknown> {
238
+ return {
239
+ state: sourceSupport.state,
240
+ citation_handle: sourceSupport.citation_handle,
241
+ source_sha256: sourceSupport.source_sha256,
242
+ observed_at: sourceSupport.observed_at,
243
+ prompt_injection: sourceSupport.prompt_injection,
244
+ }
245
+ }
@@ -0,0 +1,103 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ import { createHash } from 'node:crypto'
4
+ import { validatePublicWebFetchUrl } from '../WebFetchTool/urlSafety.js'
5
+
6
+ const SOURCE_VERIFICATION_TEXT_RE =
7
+ /(근거|출처|검증|source[-\s]?support|source[-\s]?verification|citation|evidence)/iu
8
+
9
+ const SOURCE_SUPPORT_URL_FIELDS = [
10
+ 'source_url',
11
+ 'sourceUrl',
12
+ 'citation_url',
13
+ 'citationUrl',
14
+ ] as const
15
+
16
+ export function recordsFrom(value: unknown): readonly Record<string, unknown>[] {
17
+ if (!Array.isArray(value)) return []
18
+ return value.flatMap(item => {
19
+ const record = recordFrom(item)
20
+ return record === null ? [] : [record]
21
+ })
22
+ }
23
+
24
+ export function recordFrom(value: unknown): Record<string, unknown> | null {
25
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
26
+ ? Object.fromEntries(Object.entries(value))
27
+ : null
28
+ }
29
+
30
+ export function stringField(
31
+ record: Record<string, unknown> | null,
32
+ key: string,
33
+ ): string | undefined {
34
+ if (record === null) return undefined
35
+ const value = record[key]
36
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined
37
+ }
38
+
39
+ export function sha256Hex(value: string): string {
40
+ return createHash('sha256').update(value, 'utf8').digest('hex')
41
+ }
42
+
43
+ export function sourceVerificationRequested(
44
+ args: Record<string, unknown>,
45
+ userText: string | undefined,
46
+ ): boolean {
47
+ if (
48
+ args.requires_source_verification === true ||
49
+ args.source_verification_required === true
50
+ ) {
51
+ return true
52
+ }
53
+ const instruction = stringField(args, 'instruction')
54
+ const text = `${instruction ?? ''}\n${userText ?? ''}`
55
+ return SOURCE_VERIFICATION_TEXT_RE.test(text)
56
+ }
57
+
58
+ export function patchRequestsSourceSupport(
59
+ record: Record<string, unknown> | null,
60
+ ): boolean {
61
+ if (record === null) return false
62
+ return record.requires_source_verification === true ||
63
+ record.source_verification_required === true ||
64
+ record.source_support !== undefined ||
65
+ record.sourceSupport !== undefined
66
+ }
67
+
68
+ export function sourceSupportHasUnsafeUrl(
69
+ sourceSupport: Record<string, unknown>,
70
+ ): boolean {
71
+ for (const field of SOURCE_SUPPORT_URL_FIELDS) {
72
+ const value = stringField(sourceSupport, field)
73
+ if (value !== undefined && !validatePublicWebFetchUrl(value).ok) {
74
+ return true
75
+ }
76
+ }
77
+ return false
78
+ }
79
+
80
+ export function hasApprovedDraft(args: Record<string, unknown>): boolean {
81
+ return typeof args.approved_draft_id === 'string' &&
82
+ typeof args.approved_draft_sha256 === 'string'
83
+ }
84
+
85
+ export function withoutApprovalFields(
86
+ args: Record<string, unknown>,
87
+ ): Record<string, unknown> {
88
+ const {
89
+ approved_draft_id: _approvedDraftId,
90
+ approved_draft_sha256: _approvedDraftSha256,
91
+ ...withoutApproval
92
+ } = args
93
+ return withoutApproval
94
+ }
95
+
96
+ export function withoutPatches(args: Record<string, unknown>): Record<string, unknown> {
97
+ const { patches: _patches, ...withoutPatchPayload } = args
98
+ return withoutPatchPayload
99
+ }
100
+
101
+ export function patchTargetPath(record: Record<string, unknown> | null): string {
102
+ return stringField(record, 'target_path') ?? 'unknown'
103
+ }
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ export function modelVisibleDocumentOutput(output: unknown): unknown {
4
+ return rewriteModelVisibleDocumentValue(output)
5
+ }
6
+
7
+ function rewriteModelVisibleDocumentValue(value: unknown): unknown {
8
+ if (Array.isArray(value)) {
9
+ return value.map(item => rewriteModelVisibleDocumentValue(item))
10
+ }
11
+ const record = recordFrom(value)
12
+ if (record === null) {
13
+ return value
14
+ }
15
+
16
+ const rewritten: Record<string, unknown> = {}
17
+ for (const [key, nested] of Object.entries(record)) {
18
+ rewritten[key] = rewriteModelVisibleDocumentValue(nested)
19
+ }
20
+
21
+ const displayLabel = nonEmptyString(rewritten.display_label)
22
+ if (displayLabel !== undefined && typeof rewritten.target_path === 'string') {
23
+ rewritten.target_path = displayLabel
24
+ }
25
+ return rewritten
26
+ }
27
+
28
+ function recordFrom(value: unknown): Record<string, unknown> | null {
29
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
30
+ ? Object.fromEntries(Object.entries(value))
31
+ : null
32
+ }
33
+
34
+ function nonEmptyString(value: unknown): string | undefined {
35
+ if (typeof value !== 'string') {
36
+ return undefined
37
+ }
38
+ const trimmed = value.trim()
39
+ return trimmed === '' ? undefined : trimmed
40
+ }