@xopcai/xopc 0.0.134 → 0.0.136

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 (215) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +41 -0
  4. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-mQ4lbZRf.js → agent-avatar-dicebear-CER17NHM.js} +1 -1
  5. package/dist/gateway/static/root/assets/agents-Bv5R-N8Q.js +2 -0
  6. package/dist/gateway/static/root/assets/{app-management-settings-panel-Bj2YJiQe.js → app-management-settings-panel-B3Ap_wpX.js} +1 -1
  7. package/dist/gateway/static/root/assets/{appearance-settings-B93olV5Z.js → appearance-settings-3kBO22Xd.js} +1 -1
  8. package/dist/gateway/static/root/assets/apps-page-BHYbGQcZ.js +1 -0
  9. package/dist/gateway/static/root/assets/{archive-plugin-D_yymKFJ.js → archive-plugin-D_vGeNB8.js} +1 -1
  10. package/dist/gateway/static/root/assets/{attachment-load-kfyH3HV-.js → attachment-load-hWV_8pHL.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automation-suggestion-card-DyGBB2WH.js → automation-suggestion-card-DVKmVTIo.js} +1 -1
  12. package/dist/gateway/static/root/assets/automations-page-BCTXpOBp.js +4 -0
  13. package/dist/gateway/static/root/assets/{binary-plugins-BX1AXf98.js → binary-plugins-BuXIeSPc.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-D-WEVNHU.js → block-editor-C1m0u-UU.js} +39 -39
  15. package/dist/gateway/static/root/assets/browser-settings-page-ChUF08V5.js +8 -0
  16. package/dist/gateway/static/root/assets/capability-presets-api-B7WWYaEh.js +1 -0
  17. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-CHExb90z.js → capability-presets-settings-panel-D_Bh1PnA.js} +1 -1
  18. package/dist/gateway/static/root/assets/{channel-recipient-api-RANYrzfa.js → channel-recipient-api-sV06LCht.js} +1 -1
  19. package/dist/gateway/static/root/assets/channels-settings-BI2ChQc7.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-status-swr-MY4Xh4fM.js +1 -0
  21. package/dist/gateway/static/root/assets/connectors-page-qgvEp09M.js +2 -0
  22. package/dist/gateway/static/root/assets/desktop-pet-BxMJlvb-.js +1 -0
  23. package/dist/gateway/static/root/assets/{desktop-pet-settings-C3JDiXSu.js → desktop-pet-settings-DCvpDImn.js} +1 -1
  24. package/dist/gateway/static/root/assets/{directory-picker-path-field-MT71cjgl.js → directory-picker-path-field-D1nxs9PA.js} +1 -1
  25. package/dist/gateway/static/root/assets/dist-Mys7eFJ5.js +1 -0
  26. package/dist/gateway/static/root/assets/{dreaming-settings-Cfx_Qg4R.js → dreaming-settings-EAWkpOgw.js} +3 -3
  27. package/dist/gateway/static/root/assets/{extension-debug-page-Wp23cLkf.js → extension-debug-page-LUQ8dE8z.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-page-B5HRNtVY.js → extension-page-B_mnhoRd.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-settings-page-zPfMpsYi.js → extension-settings-page-BWQsGmvW.js} +1 -1
  30. package/dist/gateway/static/root/assets/{fetch-6HXXdO77.js → fetch-BWXmBizE.js} +1 -1
  31. package/dist/gateway/static/root/assets/{gateway-config-api-CsS_3Ii6.js → gateway-config-api-M9bzPASf.js} +1 -1
  32. package/dist/gateway/static/root/assets/{gateway-settings-DOxh0hei.js → gateway-settings-CbgtpprN.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-startup-retry-BE1M9fH_.js → gateway-startup-retry-BALL91S7.js} +1 -1
  34. package/dist/gateway/static/root/assets/goal-create-dialog-CbP4t9aM.js +1 -0
  35. package/dist/gateway/static/root/assets/goal-detail-page-DlwI-MZZ.js +4 -0
  36. package/dist/gateway/static/root/assets/{goals-config-api--3Vzceq-.js → goals-config-api-BBhFVzBS.js} +1 -1
  37. package/dist/gateway/static/root/assets/goals-page-D26r7knp.js +2 -0
  38. package/dist/gateway/static/root/assets/{goals-settings-Dg_hQNVC.js → goals-settings-DdmVG_TR.js} +1 -1
  39. package/dist/gateway/static/root/assets/{heartbeat-settings-CecoDTZJ.js → heartbeat-settings-DZW2Xq-U.js} +1 -1
  40. package/dist/gateway/static/root/assets/index-CPwVzi9x.css +1 -0
  41. package/dist/gateway/static/root/assets/index-DZOm5_ex.js +262 -0
  42. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BWMLAFxf.js → keyboard-shortcuts-settings-1IWd88-T.js} +1 -1
  43. package/dist/gateway/static/root/assets/logs-page-DxoKJO7-.js +7 -0
  44. package/dist/gateway/static/root/assets/{markdown-split-5tvDwUMS.js → markdown-split-BtdBj32h.js} +1 -1
  45. package/dist/gateway/static/root/assets/{media-plugins-RSjPG4B0.js → media-plugins-CpoPj3s8.js} +1 -1
  46. package/dist/gateway/static/root/assets/messages-CQ_k8RHj.js +3 -0
  47. package/dist/gateway/static/root/assets/models-hub-panel-D1Rp__dV.js +2 -0
  48. package/dist/gateway/static/root/assets/notes-page-CgR1bO2W.js +1 -0
  49. package/dist/gateway/static/root/assets/notes-workbench-ozsnmDTV.js +2 -0
  50. package/dist/gateway/static/root/assets/{page-tabs-DYNWfN6S.js → page-tabs-CVLqCfQB.js} +1 -1
  51. package/dist/gateway/static/root/assets/{preview-open-alternatives-B50PdIc8.js → preview-open-alternatives-CKUXJk2t.js} +1 -1
  52. package/dist/gateway/static/root/assets/project-detail-page-B4dqFAQF.js +1 -0
  53. package/dist/gateway/static/root/assets/projects-page-DlRGXv4a.js +1 -0
  54. package/dist/gateway/static/root/assets/remote-access-hub-rZEYQMZV.js +1 -0
  55. package/dist/gateway/static/root/assets/{save-bar-controls-DggNb0SF.js → save-bar-controls-BtGni268.js} +1 -1
  56. package/dist/gateway/static/root/assets/{schedule-field-T4ti2C8S.js → schedule-field-BDGzKVWn.js} +1 -1
  57. package/dist/gateway/static/root/assets/{schema-form-D6wGEfEa.js → schema-form-DQJsKMCE.js} +1 -1
  58. package/dist/gateway/static/root/assets/sessions-page-DxGoCGU8.js +1 -0
  59. package/dist/gateway/static/root/assets/settings-advanced-gate-4e8ZQwxr.js +1 -0
  60. package/dist/gateway/static/root/assets/{settings-collapsible-section-Cgd_aLBV.js → settings-collapsible-section-c8XGd4-8.js} +1 -1
  61. package/dist/gateway/static/root/assets/{settings-form-section-DnDrLMWZ.js → settings-form-section-By0YiDWo.js} +1 -1
  62. package/dist/gateway/static/root/assets/{settings-loading-skeleton-A2OshMK8.js → settings-loading-skeleton-SYCPY-WM.js} +1 -1
  63. package/dist/gateway/static/root/assets/{settings-page-CXMK9Vb3.js → settings-page-D9NwvnBt.js} +1 -1
  64. package/dist/gateway/static/root/assets/{settings-page-layout-Da_LNPDS.js → settings-page-layout-CrICyGob.js} +1 -1
  65. package/dist/gateway/static/root/assets/setup-status-panel-IjK-hDxf.js +1 -0
  66. package/dist/gateway/static/root/assets/{share-preview-page-BGZV-PCJ.js → share-preview-page-Dfnk9M_m.js} +1 -1
  67. package/dist/gateway/static/root/assets/shares-settings-vQiRBra7.js +1 -0
  68. package/dist/gateway/static/root/assets/skill-reload-api-BIWv778A.js +1 -0
  69. package/dist/gateway/static/root/assets/skills-page-CruV215G.js +2 -0
  70. package/dist/gateway/static/root/assets/{system-settings-panel-D8uzXP0Y.js → system-settings-panel-BBVbSDFD.js} +1 -1
  71. package/dist/gateway/static/root/assets/{typed-models-lib-DQxv2BHa.js → typed-models-lib-CwFayhxu.js} +1 -1
  72. package/dist/gateway/static/root/assets/{use-channel-catalog-BCBfSoHx.js → use-channel-catalog-BKtHOM50.js} +1 -1
  73. package/dist/gateway/static/root/assets/{user-profile-settings-panel-DkZSHsIs.js → user-profile-settings-panel-_KDWzagh.js} +1 -1
  74. package/dist/gateway/static/root/assets/{utils-ivGpUrZi.js → utils-BiUjIHb4.js} +1 -1
  75. package/dist/gateway/static/root/assets/voice-api-key-field-agJqlm4o.js +1 -0
  76. package/dist/gateway/static/root/assets/work-item-detail-page-Bdw4ATCb.js +1 -0
  77. package/dist/gateway/static/root/assets/workflow-run-setup-panel-D-Dy6mTy.js +3 -0
  78. package/dist/gateway/static/root/assets/workflow-start-dialog-GammZ2GK.js +1 -0
  79. package/dist/gateway/static/root/assets/workflows-page-Cw5zrqti.js +18 -0
  80. package/dist/gateway/static/root/index.html +7 -8
  81. package/dist/package.js +1 -1
  82. package/dist/src/agent/image/inbound-image-handling.d.ts +1 -0
  83. package/dist/src/agent/image/inbound-image-handling.js +4 -1
  84. package/dist/src/agent/image/inbound-image-handling.js.map +1 -1
  85. package/dist/src/agent/image/tool-model-config.d.ts +18 -2
  86. package/dist/src/agent/image/tool-model-config.js +100 -14
  87. package/dist/src/agent/image/tool-model-config.js.map +1 -1
  88. package/dist/src/agent/image/vision-detection.js +4 -23
  89. package/dist/src/agent/image/vision-detection.js.map +1 -1
  90. package/dist/src/agent/inbound/attachment-pipeline.js +3 -0
  91. package/dist/src/agent/inbound/attachment-pipeline.js.map +1 -1
  92. package/dist/src/agent/messaging/command-handler.d.ts +5 -2
  93. package/dist/src/agent/messaging/command-handler.js +4 -3
  94. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  95. package/dist/src/agent/service/btw-query.d.ts +2 -0
  96. package/dist/src/agent/service/btw-query.js +4 -2
  97. package/dist/src/agent/service/btw-query.js.map +1 -1
  98. package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
  99. package/dist/src/agent/service/direct-turn-helpers.js +1 -1
  100. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  101. package/dist/src/agent/service/process-direct-streaming.js +58 -1
  102. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  103. package/dist/src/agent/service.js +1 -1
  104. package/dist/src/agent/service.js.map +1 -1
  105. package/dist/src/agent/session/session-inspector.d.ts +2 -1
  106. package/dist/src/agent/session/session-inspector.js +5 -3
  107. package/dist/src/agent/session/session-inspector.js.map +1 -1
  108. package/dist/src/agent/tools/factory.js +2 -1
  109. package/dist/src/agent/tools/factory.js.map +1 -1
  110. package/dist/src/agent/tools/image-tool.d.ts +1 -0
  111. package/dist/src/agent/tools/image-tool.js +4 -1
  112. package/dist/src/agent/tools/image-tool.js.map +1 -1
  113. package/dist/src/chat-commands/builtins/review.js +94 -7
  114. package/dist/src/chat-commands/builtins/review.js.map +1 -1
  115. package/dist/src/chat-commands/context.d.ts +5 -3
  116. package/dist/src/chat-commands/context.js +5 -2
  117. package/dist/src/chat-commands/context.js.map +1 -1
  118. package/dist/src/chat-commands/index.d.ts +1 -1
  119. package/dist/src/chat-commands/index.js.map +1 -1
  120. package/dist/src/chat-commands/types.d.ts +12 -1
  121. package/dist/src/config/agent-profile.js +1 -1
  122. package/dist/src/gateway/agents-admin.d.ts +5 -0
  123. package/dist/src/gateway/agents-admin.js +6 -1
  124. package/dist/src/gateway/agents-admin.js.map +1 -1
  125. package/dist/src/gateway/hono/routes/models.js +2 -1
  126. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  127. package/dist/src/gateway/hono/routes/projects.js +88 -0
  128. package/dist/src/gateway/hono/routes/projects.js.map +1 -1
  129. package/dist/src/gateway/hono/routes/sessions.js +2 -1
  130. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  131. package/dist/src/gateway/hono/routes/workflows.js +1 -0
  132. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  133. package/dist/src/gateway/image-capabilities.d.ts +7 -1
  134. package/dist/src/gateway/image-capabilities.js +16 -3
  135. package/dist/src/gateway/image-capabilities.js.map +1 -1
  136. package/dist/src/gateway/service/sessions-api.d.ts +1 -0
  137. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  138. package/dist/src/gateway/service.js +3 -3
  139. package/dist/src/projects/project-loop.js +2 -2
  140. package/dist/src/projects/project-loop.js.map +1 -1
  141. package/dist/src/review/review-types.js +2 -1
  142. package/dist/src/review/review-types.js.map +1 -1
  143. package/dist/src/session/client-history.d.ts +1 -0
  144. package/dist/src/session/client-history.js +56 -0
  145. package/dist/src/session/client-history.js.map +1 -1
  146. package/dist/src/session/index.js +1 -1
  147. package/dist/src/session/manager.d.ts +1 -0
  148. package/dist/src/session/manager.js.map +1 -1
  149. package/dist/src/session/store.d.ts +1 -0
  150. package/dist/src/session/store.js +30 -1
  151. package/dist/src/session/store.js.map +1 -1
  152. package/dist/src/tui/backends/embedded-backend.js +1 -1
  153. package/dist/src/tui/components/assistant-message.js +10 -3
  154. package/dist/src/tui/components/assistant-message.js.map +1 -1
  155. package/dist/src/tui/components/bash-execution.d.ts +4 -0
  156. package/dist/src/tui/components/bash-execution.js +15 -7
  157. package/dist/src/tui/components/bash-execution.js.map +1 -1
  158. package/dist/src/tui/tool-summary.js +9 -3
  159. package/dist/src/tui/tool-summary.js.map +1 -1
  160. package/dist/src/tui/tui-submit.d.ts +3 -0
  161. package/dist/src/tui/tui-submit.js +24 -1
  162. package/dist/src/tui/tui-submit.js.map +1 -1
  163. package/dist/src/tui/tui.js +29 -2
  164. package/dist/src/tui/tui.js.map +1 -1
  165. package/dist/src/work-items/index.d.ts +1 -0
  166. package/dist/src/work-items/index.js +2 -1
  167. package/dist/src/work-items/work-item-workflow-judge.d.ts +8 -0
  168. package/dist/src/work-items/work-item-workflow-judge.js +50 -0
  169. package/dist/src/work-items/work-item-workflow-judge.js.map +1 -0
  170. package/dist/src/workflows/domain/run.d.ts +17 -0
  171. package/dist/src/workflows/domain/run.js.map +1 -1
  172. package/dist/src/workflows/service/workflow-run-service.d.ts +3 -0
  173. package/dist/src/workflows/service/workflow-run-service.js +67 -0
  174. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  175. package/dist/src/workflows/service/workflow-run-service.types.d.ts +4 -1
  176. package/dist/src/workflows/service/workflow-session-bridge.d.ts +1 -0
  177. package/dist/src/workflows/service/workflow-session-bridge.js +45 -0
  178. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  179. package/package.json +1 -1
  180. package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +0 -41
  181. package/dist/gateway/static/root/assets/agents-BP0iRtiu.js +0 -2
  182. package/dist/gateway/static/root/assets/apps-page-12_2SDFq.js +0 -1
  183. package/dist/gateway/static/root/assets/automations-page-V-tw6y1o.js +0 -4
  184. package/dist/gateway/static/root/assets/browser-settings-page-aMPnxWXi.js +0 -8
  185. package/dist/gateway/static/root/assets/capability-presets-api-CsaY2Z8B.js +0 -1
  186. package/dist/gateway/static/root/assets/channels-settings-N8NujD--.js +0 -1
  187. package/dist/gateway/static/root/assets/channels-status-swr-BG_j4Ci-.js +0 -1
  188. package/dist/gateway/static/root/assets/connectors-page-zBJbFgnK.js +0 -2
  189. package/dist/gateway/static/root/assets/desktop-pet-DfA-hVlt.js +0 -1
  190. package/dist/gateway/static/root/assets/dist-CkobR7mk.js +0 -1
  191. package/dist/gateway/static/root/assets/dist-DokQNVMQ.js +0 -1
  192. package/dist/gateway/static/root/assets/goal-create-dialog-BOWSTGB-.js +0 -1
  193. package/dist/gateway/static/root/assets/goal-detail-page-CCLgkqj1.js +0 -4
  194. package/dist/gateway/static/root/assets/goals-page-CsfIp8Vl.js +0 -2
  195. package/dist/gateway/static/root/assets/index-BOme955B.js +0 -262
  196. package/dist/gateway/static/root/assets/index-DXUkWmeK.css +0 -1
  197. package/dist/gateway/static/root/assets/logs-page-CW7AMYhN.js +0 -7
  198. package/dist/gateway/static/root/assets/messages-DR9ODkb8.js +0 -3
  199. package/dist/gateway/static/root/assets/models-hub-panel-mKfeyGVq.js +0 -2
  200. package/dist/gateway/static/root/assets/notes-page-DYw5zPeJ.js +0 -1
  201. package/dist/gateway/static/root/assets/notes-workbench-SPdKpvSN.js +0 -2
  202. package/dist/gateway/static/root/assets/project-detail-page-D3yP4K3k.js +0 -1
  203. package/dist/gateway/static/root/assets/projects-page-B6EV45NR.js +0 -1
  204. package/dist/gateway/static/root/assets/remote-access-hub-9foX60FQ.js +0 -1
  205. package/dist/gateway/static/root/assets/sessions-page-BReBhxWd.js +0 -1
  206. package/dist/gateway/static/root/assets/settings-advanced-gate-DNDBN4nT.js +0 -1
  207. package/dist/gateway/static/root/assets/setup-status-panel-C8UtBMJ_.js +0 -1
  208. package/dist/gateway/static/root/assets/shares-settings-xXAMKlc7.js +0 -1
  209. package/dist/gateway/static/root/assets/skill-reload-api-CurjHfEM.js +0 -1
  210. package/dist/gateway/static/root/assets/skills-page-BRR7NQkL.js +0 -2
  211. package/dist/gateway/static/root/assets/voice-api-key-field-DCiECZN1.js +0 -1
  212. package/dist/gateway/static/root/assets/work-item-detail-page-DAel5CHH.js +0 -1
  213. package/dist/gateway/static/root/assets/workflow-run-setup-panel-2w0zj9eJ.js +0 -3
  214. package/dist/gateway/static/root/assets/workflow-start-dialog-B5xdDFV-.js +0 -1
  215. package/dist/gateway/static/root/assets/workflows-page-DRhiSwuU.js +0 -18
@@ -1 +1 @@
1
- {"version":3,"file":"tool-summary.js","names":[],"sources":["../../../src/tui/tool-summary.ts"],"sourcesContent":["import { basename, dirname } from 'node:path';\n\nimport { Text } from '@earendil-works/pi-tui';\n\nimport type { TuiToolContentBlock } from './tui-tool-result.js';\n\nconst MAX_ARG_VALUE_LENGTH = 120;\nconst MAX_PATH_LABEL_LENGTH = 72;\n\nexport type ToolSummaryKind = 'read' | 'search' | 'exec' | 'mcp' | 'generic';\n\nexport interface ToolSummaryContext {\n toolName: string;\n args: unknown;\n output: string;\n content?: TuiToolContentBlock[];\n details: unknown;\n isError: boolean;\n expandKey: string;\n}\n\nexport function formatArgsSummary(args: unknown, toolName: string): string {\n if (!args || typeof args !== 'object') return '';\n const compactRead = isReadStyleTool(toolName) ? formatCompactReadArgs(args) : null;\n if (compactRead) return compactRead;\n const command = isExecStyleTool(toolName) ? commandFromArgs(args) : null;\n if (command) return command;\n const entries = Object.entries(args as Record<string, unknown>);\n if (entries.length === 0) return '';\n return entries\n .map(([key, value]) => {\n const stringValue = formatArgValue(value);\n const truncated =\n stringValue.length > MAX_ARG_VALUE_LENGTH\n ? `${stringValue.slice(0, MAX_ARG_VALUE_LENGTH - 3)}...`\n : stringValue;\n return `${key}=${truncated}`;\n })\n .join(', ');\n}\n\nexport function getToolSummaryKind(toolName: string): ToolSummaryKind {\n if (isReadStyleTool(toolName)) return 'read';\n if (isSearchStyleTool(toolName)) return 'search';\n if (isExecStyleTool(toolName)) return 'exec';\n if (isMcpStyleTool(toolName)) return 'mcp';\n return 'generic';\n}\n\nexport function isReadStyleTool(toolName: string): boolean {\n const base = baseToolName(toolName);\n return ['read', 'read_file', 'memory_get'].includes(base);\n}\n\nexport function isSearchStyleTool(toolName: string): boolean {\n const base = baseToolName(toolName);\n return [\n 'search',\n 'grep',\n 'rg',\n 'ripgrep',\n 'search_code',\n 'file_search',\n 'list_files',\n ].includes(base);\n}\n\nexport function isExecStyleTool(toolName: string): boolean {\n const base = baseToolName(toolName);\n return [\n 'bash',\n 'shell',\n 'exec',\n 'run_command',\n 'exec_command',\n 'terminal',\n ].includes(base);\n}\n\nexport function isMcpStyleTool(toolName: string): boolean {\n return toolName.includes('__');\n}\n\nexport function displayToolName(toolName: string): string {\n const parts = toolName.split('__');\n if (parts.length === 2 && parts[0] && parts[1]) {\n return `${parts[0]}.${parts[1]}`;\n }\n return toolName;\n}\n\nexport function compactPath(path: string, maxLength = MAX_PATH_LABEL_LENGTH): string {\n const normalized = path.replace(/\\\\/g, '/');\n if (normalized.length <= maxLength) return normalized;\n const base = basename(normalized);\n const parent = basename(dirname(normalized));\n const suffix = parent ? `${parent}/${base}` : base;\n if (suffix.length + 2 <= maxLength) return `.../${suffix}`;\n return `.../${base.slice(Math.max(0, base.length - maxLength + 3))}`;\n}\n\nexport function formatCollapsedToolSummary(ctx: ToolSummaryContext): string {\n if (ctx.isError) {\n const rows = nonBlankLines(ctx.output).length;\n const rowLabel = rows === 1 ? '1 error line' : `${rows} error lines`;\n return `error; ${rowLabel}; ${ctx.expandKey} to expand`;\n }\n\n switch (getToolSummaryKind(ctx.toolName)) {\n case 'read': {\n const rows = new Text(ctx.output, 1, 0).render(80).length;\n const plural = rows === 1 ? 'row' : 'rows';\n return ` ${rows} ${plural}; ${ctx.expandKey} to expand`;\n }\n case 'search':\n return formatSearchSummary(ctx.output, ctx.expandKey);\n case 'exec':\n return formatExecSummary(ctx.output, ctx.details, ctx.isError, ctx.expandKey);\n case 'mcp':\n return formatMcpSummary(ctx.output, ctx.content, ctx.expandKey);\n case 'generic':\n return '';\n }\n}\n\nexport function formatExecExpandedOutput(output: string, details: unknown): string {\n const parsed = parseExecDetails(details);\n const sections: string[] = [];\n const stdout = parsed.stdout ?? output;\n if (stdout.trim()) {\n sections.push(`stdout\\n${stdout}`);\n }\n if (parsed.stderr?.trim()) {\n sections.push(`stderr\\n${parsed.stderr}`);\n }\n if (parsed.exitCode !== undefined) {\n sections.push(`exit ${parsed.exitCode}`);\n }\n return sections.join('\\n');\n}\n\nexport function extractExitCode(details: unknown): number | undefined {\n return parseExecDetails(details).exitCode;\n}\n\nfunction formatCompactReadArgs(args: unknown): string | null {\n if (!args || typeof args !== 'object') return null;\n const record = args as Record<string, unknown>;\n const rawPath = typeof record.path === 'string'\n ? record.path\n : typeof record.file_path === 'string'\n ? record.file_path\n : undefined;\n if (!rawPath) return null;\n\n const normalized = rawPath.replace(/\\\\/g, '/');\n const base = basename(normalized);\n let label: string;\n if (base === 'SKILL.md') {\n const parent = basename(dirname(normalized));\n label = parent ? `[skill] ${parent}` : '[skill]';\n } else if (base === 'AGENTS.md') {\n label = `read resource ${compactPath(normalized)}`;\n } else {\n label = compactPath(normalized);\n }\n\n const start = firstFiniteNumber(record.offset, record.from, record.start, record.line);\n const count = firstFiniteNumber(record.limit, record.lines, record.count);\n if (start != null && count != null && count > 0) {\n label += `:${start}-${start + count - 1}`;\n }\n return label;\n}\n\nfunction commandFromArgs(args: unknown): string | null {\n if (!args || typeof args !== 'object') return null;\n const record = args as Record<string, unknown>;\n const raw = record.command ?? record.cmd ?? record.script;\n if (Array.isArray(raw)) return raw.map(String).join(' ');\n if (typeof raw === 'string' && raw.trim()) return raw.trim();\n return null;\n}\n\nfunction baseToolName(toolName: string): string {\n return toolName.split('__').pop()?.toLowerCase() ?? toolName.toLowerCase();\n}\n\nfunction firstFiniteNumber(...values: unknown[]): number | null {\n for (const value of values) {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.floor(value);\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return Math.floor(parsed);\n }\n }\n return null;\n}\n\nfunction formatArgValue(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'bigint') return `${value.toString()}n`;\n if (typeof value === 'function') return '[function]';\n if (typeof value === 'symbol') return String(value);\n if (value instanceof Error) return value.message || value.name;\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n\nfunction formatSearchSummary(output: string, expandKey: string): string {\n const lines = nonBlankLines(output);\n const paths = new Set<string>();\n let matchCount = 0;\n for (const line of lines) {\n const path = extractResultPath(line);\n if (path) paths.add(compactPath(path, 48));\n if (line.trim()) matchCount += 1;\n }\n\n const matchLabel = matchCount === 1 ? '1 result' : `${matchCount} results`;\n const fileLabel = paths.size === 0\n ? ''\n : paths.size === 1\n ? ` in ${[...paths][0]}`\n : ` in ${paths.size} files`;\n const samples = [...paths].slice(0, 3).join(', ');\n const sampleSuffix = samples && paths.size > 1 ? `: ${samples}` : '';\n return `${matchLabel}${fileLabel}${sampleSuffix}; ${expandKey} to expand`;\n}\n\nfunction formatExecSummary(\n output: string,\n details: unknown,\n isError: boolean,\n expandKey: string,\n): string {\n const parsed = parseExecDetails(details);\n const outputText = [parsed.stdout, parsed.stderr].filter(Boolean).join('\\n') || output;\n const rows = nonBlankLines(outputText).length;\n const exit = parsed.exitCode === undefined\n ? isError ? 'failed' : 'completed'\n : `exit ${parsed.exitCode}`;\n const rowLabel = rows === 1 ? '1 output line' : `${rows} output lines`;\n return `${exit}; ${rowLabel}; ${expandKey} to expand`;\n}\n\nfunction formatMcpSummary(\n output: string,\n content: TuiToolContentBlock[] | undefined,\n expandKey: string,\n): string {\n if (content?.length) {\n const counts = new Map<string, number>();\n for (const block of content) {\n counts.set(block.type, (counts.get(block.type) ?? 0) + 1);\n }\n const label = [...counts.entries()]\n .map(([type, count]) => `${count} ${type}`)\n .join(', ');\n return `${label}; ${expandKey} to expand`;\n }\n const rows = nonBlankLines(output).length;\n const rowLabel = rows === 1 ? '1 result line' : `${rows} result lines`;\n return `${rowLabel}; ${expandKey} to expand`;\n}\n\nfunction parseExecDetails(details: unknown): {\n exitCode?: number;\n stdout?: string;\n stderr?: string;\n} {\n if (!details || typeof details !== 'object') return {};\n const record = details as Record<string, unknown>;\n return {\n exitCode: numberValue(record.exitCode ?? record.exit_code ?? record.code ?? record.status),\n stdout: stringValue(record.stdout ?? record.output),\n stderr: stringValue(record.stderr ?? record.errorOutput ?? record.error_output),\n };\n}\n\nfunction numberValue(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.trunc(value);\n if (typeof value === 'string' && /^-?\\d+$/.test(value)) return Number(value);\n return undefined;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction nonBlankLines(text: string): string[] {\n return text.split('\\n').filter((line) => line.trim().length > 0);\n}\n\nfunction extractResultPath(line: string): string | null {\n const trimmed = line.trim();\n const colonPath = trimmed.match(/^(.+?):\\d+(?::\\d+)?:/);\n if (colonPath?.[1]) return colonPath[1];\n const jsonPath = trimmed.match(/\"?(?:path|file|file_path)\"?\\s*[:=]\\s*\"([^\"]+)\"/i);\n if (jsonPath?.[1]) return jsonPath[1];\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tool-summary.js","names":[],"sources":["../../../src/tui/tool-summary.ts"],"sourcesContent":["import { basename, dirname } from 'node:path';\n\nimport { Text } from '@earendil-works/pi-tui';\n\nimport type { TuiToolContentBlock } from './tui-tool-result.js';\n\nconst MAX_ARG_VALUE_LENGTH = 120;\nconst MAX_PATH_LABEL_LENGTH = 72;\n\nexport type ToolSummaryKind = 'read' | 'search' | 'exec' | 'mcp' | 'generic';\n\nexport interface ToolSummaryContext {\n toolName: string;\n args: unknown;\n output: string;\n content?: TuiToolContentBlock[];\n details: unknown;\n isError: boolean;\n expandKey: string;\n}\n\nexport function formatArgsSummary(args: unknown, toolName: string): string {\n if (!args || typeof args !== 'object') return '';\n const compactRead = isReadStyleTool(toolName) ? formatCompactReadArgs(args) : null;\n if (compactRead) return compactRead;\n const command = isExecStyleTool(toolName) ? commandFromArgs(args) : null;\n if (command) return command;\n const entries = Object.entries(args as Record<string, unknown>);\n if (entries.length === 0) return '';\n return entries\n .map(([key, value]) => {\n const stringValue = formatArgValue(value);\n const truncated =\n stringValue.length > MAX_ARG_VALUE_LENGTH\n ? `${stringValue.slice(0, MAX_ARG_VALUE_LENGTH - 3)}...`\n : stringValue;\n return `${key}=${truncated}`;\n })\n .join(', ');\n}\n\nexport function getToolSummaryKind(toolName: string): ToolSummaryKind {\n if (isReadStyleTool(toolName)) return 'read';\n if (isSearchStyleTool(toolName)) return 'search';\n if (isExecStyleTool(toolName)) return 'exec';\n if (isMcpStyleTool(toolName)) return 'mcp';\n return 'generic';\n}\n\nexport function isReadStyleTool(toolName: string): boolean {\n const base = baseToolName(toolName);\n return ['read', 'read_file', 'memory_get', 'get_code_snippet', 'prepare_diff'].includes(base);\n}\n\nexport function isSearchStyleTool(toolName: string): boolean {\n const base = baseToolName(toolName);\n return [\n 'search',\n 'grep',\n 'rg',\n 'ripgrep',\n 'search_code',\n 'file_search',\n 'list_files',\n 'search_graph',\n 'query_graph',\n 'trace_path',\n 'get_architecture',\n ].includes(base);\n}\n\nexport function isExecStyleTool(toolName: string): boolean {\n const base = baseToolName(toolName);\n return [\n 'bash',\n 'shell',\n 'exec',\n 'run_command',\n 'exec_command',\n 'terminal',\n ].includes(base);\n}\n\nexport function isMcpStyleTool(toolName: string): boolean {\n return toolName.includes('__');\n}\n\nexport function displayToolName(toolName: string): string {\n const parts = toolName.split('__');\n if (parts.length === 2 && parts[0] && parts[1]) {\n return `${parts[0]}.${parts[1]}`;\n }\n return toolName;\n}\n\nexport function compactPath(path: string, maxLength = MAX_PATH_LABEL_LENGTH): string {\n const normalized = path.replace(/\\\\/g, '/');\n if (normalized.length <= maxLength) return normalized;\n const base = basename(normalized);\n const parent = basename(dirname(normalized));\n const suffix = parent ? `${parent}/${base}` : base;\n if (suffix.length + 2 <= maxLength) return `.../${suffix}`;\n return `.../${base.slice(Math.max(0, base.length - maxLength + 3))}`;\n}\n\nexport function formatCollapsedToolSummary(ctx: ToolSummaryContext): string {\n if (ctx.isError) {\n const rows = nonBlankLines(ctx.output).length;\n const rowLabel = rows === 1 ? '1 error line' : `${rows} error lines`;\n return `error; ${rowLabel}; ${ctx.expandKey} to expand`;\n }\n\n switch (getToolSummaryKind(ctx.toolName)) {\n case 'read': {\n const rows = new Text(ctx.output, 1, 0).render(80).length;\n const plural = rows === 1 ? 'row' : 'rows';\n return ` ${rows} ${plural}; ${ctx.expandKey} to expand`;\n }\n case 'search':\n return formatSearchSummary(ctx.output, ctx.expandKey);\n case 'exec':\n return formatExecSummary(ctx.output, ctx.details, ctx.isError, ctx.expandKey);\n case 'mcp':\n return formatMcpSummary(ctx.output, ctx.content, ctx.expandKey);\n case 'generic':\n return '';\n }\n}\n\nexport function formatExecExpandedOutput(output: string, details: unknown): string {\n const parsed = parseExecDetails(details);\n const sections: string[] = [];\n const stdout = parsed.stdout ?? output;\n if (stdout.trim()) {\n sections.push(`stdout\\n${stdout}`);\n }\n if (parsed.stderr?.trim()) {\n sections.push(`stderr\\n${parsed.stderr}`);\n }\n if (parsed.exitCode !== undefined) {\n sections.push(`exit ${parsed.exitCode}`);\n }\n return sections.join('\\n');\n}\n\nexport function extractExitCode(details: unknown): number | undefined {\n return parseExecDetails(details).exitCode;\n}\n\nfunction formatCompactReadArgs(args: unknown): string | null {\n if (!args || typeof args !== 'object') return null;\n const record = args as Record<string, unknown>;\n const rawPath = typeof record.path === 'string'\n ? record.path\n : typeof record.file_path === 'string'\n ? record.file_path\n : undefined;\n if (!rawPath) return null;\n\n const normalized = rawPath.replace(/\\\\/g, '/');\n const base = basename(normalized);\n let label: string;\n if (base === 'SKILL.md') {\n const parent = basename(dirname(normalized));\n label = parent ? `[skill] ${parent}` : '[skill]';\n } else if (base === 'AGENTS.md') {\n label = `read resource ${compactPath(normalized)}`;\n } else {\n label = compactPath(normalized);\n }\n\n const start = firstFiniteNumber(record.offset, record.from, record.start, record.line);\n const count = firstFiniteNumber(record.limit, record.lines, record.count);\n if (start != null && count != null && count > 0) {\n label += `:${start}-${start + count - 1}`;\n }\n return label;\n}\n\nfunction commandFromArgs(args: unknown): string | null {\n if (!args || typeof args !== 'object') return null;\n const record = args as Record<string, unknown>;\n const raw = record.command ?? record.cmd ?? record.script;\n if (Array.isArray(raw)) return raw.map(String).join(' ');\n if (typeof raw === 'string' && raw.trim()) return raw.trim();\n return null;\n}\n\nfunction baseToolName(toolName: string): string {\n return toolName.split(/__|\\./).pop()?.toLowerCase() ?? toolName.toLowerCase();\n}\n\nfunction firstFiniteNumber(...values: unknown[]): number | null {\n for (const value of values) {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.floor(value);\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return Math.floor(parsed);\n }\n }\n return null;\n}\n\nfunction formatArgValue(value: unknown): string {\n if (typeof value === 'string') return value;\n if (typeof value === 'bigint') return `${value.toString()}n`;\n if (typeof value === 'function') return '[function]';\n if (typeof value === 'symbol') return String(value);\n if (value instanceof Error) return value.message || value.name;\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n\nfunction formatSearchSummary(output: string, expandKey: string): string {\n const lines = nonBlankLines(output);\n const paths = new Set<string>();\n let matchCount = 0;\n for (const line of lines) {\n const path = extractResultPath(line);\n if (path) paths.add(compactPath(path, 48));\n if (line.trim()) matchCount += 1;\n }\n\n const matchLabel = matchCount === 1 ? '1 result' : `${matchCount} results`;\n const fileLabel = paths.size === 0\n ? ''\n : paths.size === 1\n ? ` in ${[...paths][0]}`\n : ` in ${paths.size} files`;\n const samples = [...paths].slice(0, 3).join(', ');\n const sampleSuffix = samples && paths.size > 1 ? `: ${samples}` : '';\n return `${matchLabel}${fileLabel}${sampleSuffix}; ${expandKey} to expand`;\n}\n\nfunction formatExecSummary(\n output: string,\n details: unknown,\n isError: boolean,\n expandKey: string,\n): string {\n const parsed = parseExecDetails(details);\n const outputText = [parsed.stdout, parsed.stderr].filter(Boolean).join('\\n') || output;\n const rows = nonBlankLines(outputText).length;\n const exit = parsed.exitCode === undefined\n ? isError ? 'failed' : 'completed'\n : `exit ${parsed.exitCode}`;\n const rowLabel = rows === 1 ? '1 output line' : `${rows} output lines`;\n return `${exit}; ${rowLabel}; ${expandKey} to expand`;\n}\n\nfunction formatMcpSummary(\n output: string,\n content: TuiToolContentBlock[] | undefined,\n expandKey: string,\n): string {\n if (content?.length) {\n const counts = new Map<string, number>();\n for (const block of content) {\n counts.set(block.type, (counts.get(block.type) ?? 0) + 1);\n }\n const label = [...counts.entries()]\n .map(([type, count]) => `${count} ${type}`)\n .join(', ');\n return `${label}; ${expandKey} to expand`;\n }\n const rows = nonBlankLines(output).length;\n const rowLabel = rows === 1 ? '1 result line' : `${rows} result lines`;\n return `${rowLabel}; ${expandKey} to expand`;\n}\n\nfunction parseExecDetails(details: unknown): {\n exitCode?: number;\n stdout?: string;\n stderr?: string;\n} {\n if (!details || typeof details !== 'object') return {};\n const record = details as Record<string, unknown>;\n return {\n exitCode: numberValue(record.exitCode ?? record.exit_code ?? record.code ?? record.status),\n stdout: stringValue(record.stdout ?? record.output),\n stderr: stringValue(record.stderr ?? record.errorOutput ?? record.error_output),\n };\n}\n\nfunction numberValue(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value)) return Math.trunc(value);\n if (typeof value === 'string' && /^-?\\d+$/.test(value)) return Number(value);\n return undefined;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction nonBlankLines(text: string): string[] {\n return text.split('\\n').filter((line) => line.trim().length > 0);\n}\n\nfunction extractResultPath(line: string): string | null {\n const trimmed = line.trim();\n const colonPath = trimmed.match(/^(.+?):\\d+(?::\\d+)?:/);\n if (colonPath?.[1]) return colonPath[1];\n const jsonPath = trimmed.match(/\"?(?:path|file|file_path)\"?\\s*[:=]\\s*\"([^\"]+)\"/i);\n if (jsonPath?.[1]) return jsonPath[1];\n return null;\n}\n"],"mappings":"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"}
@@ -10,6 +10,9 @@ export declare function createEditorSubmitHandler(params: {
10
10
  handleCommand: (value: string) => void | Promise<void>;
11
11
  sendMessage: (value: string) => void | Promise<void>;
12
12
  handleBangLine: (value: string) => void | Promise<void>;
13
+ getMode?: () => 'chat' | 'shell';
14
+ enterShellMode?: () => void;
15
+ exitShellMode?: () => void;
13
16
  isAgentBusy?: () => boolean;
14
17
  steerWhileBusy?: (value: string) => void | Promise<void>;
15
18
  hasPendingAttachments?: () => boolean;
@@ -9,10 +9,28 @@ function normalizeLowercaseStringOrEmpty(value) {
9
9
  function createEditorSubmitHandler(params) {
10
10
  return (text) => {
11
11
  const raw = text;
12
+ const rawTrimmed = raw.trim();
13
+ const mode = params.getMode?.() ?? "chat";
12
14
  const hasAttachments = params.hasPendingAttachments?.() === true;
13
- const value = raw.trim() || (hasAttachments ? params.defaultAttachmentMessage ?? "Please analyze the attachment(s)." : "");
15
+ const value = rawTrimmed || (hasAttachments ? params.defaultAttachmentMessage ?? "Please analyze the attachment(s)." : "");
14
16
  params.editor.setText("");
17
+ if (mode === "shell") {
18
+ if (!rawTrimmed) {
19
+ params.exitShellMode?.();
20
+ return;
21
+ }
22
+ params.editor.addToHistory(rawTrimmed);
23
+ const line = rawTrimmed.startsWith("!") ? rawTrimmed : `!${rawTrimmed}`;
24
+ Promise.resolve(params.handleBangLine(line)).finally(() => {
25
+ params.exitShellMode?.();
26
+ });
27
+ return;
28
+ }
15
29
  if (!value) return;
30
+ if (value === "!") {
31
+ params.enterShellMode?.();
32
+ return;
33
+ }
16
34
  if (raw.startsWith("!") && raw !== "!") {
17
35
  params.editor.addToHistory(raw);
18
36
  params.handleBangLine(raw);
@@ -77,6 +95,11 @@ function createSubmitBurstCoalescer(params) {
77
95
  params.submit(value);
78
96
  return;
79
97
  }
98
+ if (value.trim() === "!" || value.startsWith("!")) {
99
+ flushPending();
100
+ params.submit(value);
101
+ return;
102
+ }
80
103
  if (value.includes("\n")) {
81
104
  flushPending();
82
105
  params.submit(value);
@@ -1 +1 @@
1
- {"version":3,"file":"tui-submit.js","names":[],"sources":["../../../src/tui/tui-submit.ts"],"sourcesContent":["/**\n * Submit handling aligned with openclaw: `!` local shell lines + paste burst coalescing\n * for terminals that split multiline paste into rapid single-line submits.\n */\n\nfunction normalizeLowercaseStringOrEmpty(value: string): string {\n return value.trim().toLowerCase();\n}\n\nexport function createEditorSubmitHandler(params: {\n editor: {\n setText: (value: string) => void;\n addToHistory: (value: string) => void;\n };\n handleCommand: (value: string) => void | Promise<void>;\n sendMessage: (value: string) => void | Promise<void>;\n handleBangLine: (value: string) => void | Promise<void>;\n isAgentBusy?: () => boolean;\n steerWhileBusy?: (value: string) => void | Promise<void>;\n hasPendingAttachments?: () => boolean;\n defaultAttachmentMessage?: string;\n}) {\n return (text: string) => {\n const raw = text;\n const hasAttachments = params.hasPendingAttachments?.() === true;\n const value = raw.trim() || (hasAttachments ? (params.defaultAttachmentMessage ?? 'Please analyze the attachment(s).') : '');\n params.editor.setText('');\n\n if (!value) {\n return;\n }\n\n if (raw.startsWith('!') && raw !== '!') {\n params.editor.addToHistory(raw);\n void params.handleBangLine(raw);\n return;\n }\n\n params.editor.addToHistory(value);\n\n if (value.startsWith('/')) {\n void params.handleCommand(value);\n return;\n }\n\n if (params.isAgentBusy?.() && params.steerWhileBusy) {\n void params.steerWhileBusy(value);\n return;\n }\n\n void params.sendMessage(value);\n };\n}\n\nexport function shouldEnableWindowsGitBashPasteFallback(params?: {\n platform?: string;\n env?: NodeJS.ProcessEnv;\n}): boolean {\n const platform = params?.platform ?? process.platform;\n const env = params?.env ?? process.env;\n const termProgram = normalizeLowercaseStringOrEmpty(env.TERM_PROGRAM ?? '');\n\n if (platform === 'darwin') {\n if (termProgram.includes('iterm') || termProgram.includes('apple_terminal')) {\n return true;\n }\n return false;\n }\n\n if (platform !== 'win32') {\n return false;\n }\n\n const msystem = (env.MSYSTEM ?? '').toUpperCase();\n const shell = env.SHELL ?? '';\n if (msystem.startsWith('MINGW') || msystem.startsWith('MSYS')) {\n return true;\n }\n if (normalizeLowercaseStringOrEmpty(shell).includes('bash')) {\n return true;\n }\n return termProgram.includes('mintty');\n}\n\nexport function createSubmitBurstCoalescer(params: {\n submit: (value: string) => void;\n enabled: boolean;\n burstWindowMs?: number;\n now?: () => number;\n setTimer?: typeof setTimeout;\n clearTimer?: typeof clearTimeout;\n}) {\n const windowMs = Math.max(1, params.burstWindowMs ?? 50);\n const now = params.now ?? (() => Date.now());\n const setTimer = params.setTimer ?? setTimeout;\n const clearTimer = params.clearTimer ?? clearTimeout;\n let pending: string | null = null;\n let pendingAt = 0;\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n const clearFlushTimer = () => {\n if (!flushTimer) {\n return;\n }\n clearTimer(flushTimer);\n flushTimer = null;\n };\n\n const flushPending = () => {\n if (pending === null) {\n return;\n }\n const flushed = pending;\n pending = null;\n pendingAt = 0;\n clearFlushTimer();\n params.submit(flushed);\n };\n\n const scheduleFlush = () => {\n clearFlushTimer();\n flushTimer = setTimer(() => {\n flushPending();\n }, windowMs);\n };\n\n return (value: string) => {\n if (!params.enabled) {\n params.submit(value);\n return;\n }\n if (value.includes('\\n')) {\n flushPending();\n params.submit(value);\n return;\n }\n const ts = now();\n if (pending === null) {\n pending = value;\n pendingAt = ts;\n scheduleFlush();\n return;\n }\n if (ts - pendingAt <= windowMs) {\n pending = `${pending}\\n${value}`;\n pendingAt = ts;\n scheduleFlush();\n return;\n }\n flushPending();\n pending = value;\n pendingAt = ts;\n scheduleFlush();\n };\n}\n"],"mappings":";;;;;AAKA,SAAS,gCAAgC,OAAuB;AAC9D,QAAO,MAAM,MAAM,CAAC,aAAa;;AAGnC,SAAgB,0BAA0B,QAYvC;AACD,SAAQ,SAAiB;EACvB,MAAM,MAAM;EACZ,MAAM,iBAAiB,OAAO,yBAAyB,KAAK;EAC5D,MAAM,QAAQ,IAAI,MAAM,KAAK,iBAAkB,OAAO,4BAA4B,sCAAuC;AACzH,SAAO,OAAO,QAAQ,GAAG;AAEzB,MAAI,CAAC,MACH;AAGF,MAAI,IAAI,WAAW,IAAI,IAAI,QAAQ,KAAK;AACtC,UAAO,OAAO,aAAa,IAAI;AAC1B,UAAO,eAAe,IAAI;AAC/B;;AAGF,SAAO,OAAO,aAAa,MAAM;AAEjC,MAAI,MAAM,WAAW,IAAI,EAAE;AACpB,UAAO,cAAc,MAAM;AAChC;;AAGF,MAAI,OAAO,eAAe,IAAI,OAAO,gBAAgB;AAC9C,UAAO,eAAe,MAAM;AACjC;;AAGG,SAAO,YAAY,MAAM;;;AAIlC,SAAgB,wCAAwC,QAG5C;CACV,MAAM,WAAW,QAAQ,YAAY,QAAQ;CAC7C,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,cAAc,gCAAgC,IAAI,gBAAgB,GAAG;AAE3E,KAAI,aAAa,UAAU;AACzB,MAAI,YAAY,SAAS,QAAQ,IAAI,YAAY,SAAS,iBAAiB,CACzE,QAAO;AAET,SAAO;;AAGT,KAAI,aAAa,QACf,QAAO;CAGT,MAAM,WAAW,IAAI,WAAW,IAAI,aAAa;CACjD,MAAM,QAAQ,IAAI,SAAS;AAC3B,KAAI,QAAQ,WAAW,QAAQ,IAAI,QAAQ,WAAW,OAAO,CAC3D,QAAO;AAET,KAAI,gCAAgC,MAAM,CAAC,SAAS,OAAO,CACzD,QAAO;AAET,QAAO,YAAY,SAAS,SAAS;;AAGvC,SAAgB,2BAA2B,QAOxC;CACD,MAAM,WAAW,KAAK,IAAI,GAAG,OAAO,iBAAiB,GAAG;CACxD,MAAM,MAAM,OAAO,cAAc,KAAK,KAAK;CAC3C,MAAM,WAAW,OAAO,YAAY;CACpC,MAAM,aAAa,OAAO,cAAc;CACxC,IAAI,UAAyB;CAC7B,IAAI,YAAY;CAChB,IAAI,aAAmD;CAEvD,MAAM,wBAAwB;AAC5B,MAAI,CAAC,WACH;AAEF,aAAW,WAAW;AACtB,eAAa;;CAGf,MAAM,qBAAqB;AACzB,MAAI,YAAY,KACd;EAEF,MAAM,UAAU;AAChB,YAAU;AACV,cAAY;AACZ,mBAAiB;AACjB,SAAO,OAAO,QAAQ;;CAGxB,MAAM,sBAAsB;AAC1B,mBAAiB;AACjB,eAAa,eAAe;AAC1B,iBAAc;KACb,SAAS;;AAGd,SAAQ,UAAkB;AACxB,MAAI,CAAC,OAAO,SAAS;AACnB,UAAO,OAAO,MAAM;AACpB;;AAEF,MAAI,MAAM,SAAS,KAAK,EAAE;AACxB,iBAAc;AACd,UAAO,OAAO,MAAM;AACpB;;EAEF,MAAM,KAAK,KAAK;AAChB,MAAI,YAAY,MAAM;AACpB,aAAU;AACV,eAAY;AACZ,kBAAe;AACf;;AAEF,MAAI,KAAK,aAAa,UAAU;AAC9B,aAAU,GAAG,QAAQ,IAAI;AACzB,eAAY;AACZ,kBAAe;AACf;;AAEF,gBAAc;AACd,YAAU;AACV,cAAY;AACZ,iBAAe"}
1
+ {"version":3,"file":"tui-submit.js","names":[],"sources":["../../../src/tui/tui-submit.ts"],"sourcesContent":["/**\n * Submit handling aligned with openclaw: `!` local shell lines + paste burst coalescing\n * for terminals that split multiline paste into rapid single-line submits.\n */\n\nfunction normalizeLowercaseStringOrEmpty(value: string): string {\n return value.trim().toLowerCase();\n}\n\nexport function createEditorSubmitHandler(params: {\n editor: {\n setText: (value: string) => void;\n addToHistory: (value: string) => void;\n };\n handleCommand: (value: string) => void | Promise<void>;\n sendMessage: (value: string) => void | Promise<void>;\n handleBangLine: (value: string) => void | Promise<void>;\n getMode?: () => 'chat' | 'shell';\n enterShellMode?: () => void;\n exitShellMode?: () => void;\n isAgentBusy?: () => boolean;\n steerWhileBusy?: (value: string) => void | Promise<void>;\n hasPendingAttachments?: () => boolean;\n defaultAttachmentMessage?: string;\n}) {\n return (text: string) => {\n const raw = text;\n const rawTrimmed = raw.trim();\n const mode = params.getMode?.() ?? 'chat';\n const hasAttachments = params.hasPendingAttachments?.() === true;\n const value = rawTrimmed || (hasAttachments ? (params.defaultAttachmentMessage ?? 'Please analyze the attachment(s).') : '');\n params.editor.setText('');\n\n if (mode === 'shell') {\n if (!rawTrimmed) {\n params.exitShellMode?.();\n return;\n }\n params.editor.addToHistory(rawTrimmed);\n const line = rawTrimmed.startsWith('!') ? rawTrimmed : `!${rawTrimmed}`;\n void Promise.resolve(params.handleBangLine(line)).finally(() => {\n params.exitShellMode?.();\n });\n return;\n }\n\n if (!value) {\n return;\n }\n\n if (value === '!') {\n params.enterShellMode?.();\n return;\n }\n\n if (raw.startsWith('!') && raw !== '!') {\n params.editor.addToHistory(raw);\n void params.handleBangLine(raw);\n return;\n }\n\n params.editor.addToHistory(value);\n\n if (value.startsWith('/')) {\n void params.handleCommand(value);\n return;\n }\n\n if (params.isAgentBusy?.() && params.steerWhileBusy) {\n void params.steerWhileBusy(value);\n return;\n }\n\n void params.sendMessage(value);\n };\n}\n\nexport function shouldEnableWindowsGitBashPasteFallback(params?: {\n platform?: string;\n env?: NodeJS.ProcessEnv;\n}): boolean {\n const platform = params?.platform ?? process.platform;\n const env = params?.env ?? process.env;\n const termProgram = normalizeLowercaseStringOrEmpty(env.TERM_PROGRAM ?? '');\n\n if (platform === 'darwin') {\n if (termProgram.includes('iterm') || termProgram.includes('apple_terminal')) {\n return true;\n }\n return false;\n }\n\n if (platform !== 'win32') {\n return false;\n }\n\n const msystem = (env.MSYSTEM ?? '').toUpperCase();\n const shell = env.SHELL ?? '';\n if (msystem.startsWith('MINGW') || msystem.startsWith('MSYS')) {\n return true;\n }\n if (normalizeLowercaseStringOrEmpty(shell).includes('bash')) {\n return true;\n }\n return termProgram.includes('mintty');\n}\n\nexport function createSubmitBurstCoalescer(params: {\n submit: (value: string) => void;\n enabled: boolean;\n burstWindowMs?: number;\n now?: () => number;\n setTimer?: typeof setTimeout;\n clearTimer?: typeof clearTimeout;\n}) {\n const windowMs = Math.max(1, params.burstWindowMs ?? 50);\n const now = params.now ?? (() => Date.now());\n const setTimer = params.setTimer ?? setTimeout;\n const clearTimer = params.clearTimer ?? clearTimeout;\n let pending: string | null = null;\n let pendingAt = 0;\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n const clearFlushTimer = () => {\n if (!flushTimer) {\n return;\n }\n clearTimer(flushTimer);\n flushTimer = null;\n };\n\n const flushPending = () => {\n if (pending === null) {\n return;\n }\n const flushed = pending;\n pending = null;\n pendingAt = 0;\n clearFlushTimer();\n params.submit(flushed);\n };\n\n const scheduleFlush = () => {\n clearFlushTimer();\n flushTimer = setTimer(() => {\n flushPending();\n }, windowMs);\n };\n\n return (value: string) => {\n if (!params.enabled) {\n params.submit(value);\n return;\n }\n if (value.trim() === '!' || value.startsWith('!')) {\n flushPending();\n params.submit(value);\n return;\n }\n if (value.includes('\\n')) {\n flushPending();\n params.submit(value);\n return;\n }\n const ts = now();\n if (pending === null) {\n pending = value;\n pendingAt = ts;\n scheduleFlush();\n return;\n }\n if (ts - pendingAt <= windowMs) {\n pending = `${pending}\\n${value}`;\n pendingAt = ts;\n scheduleFlush();\n return;\n }\n flushPending();\n pending = value;\n pendingAt = ts;\n scheduleFlush();\n };\n}\n"],"mappings":";;;;;AAKA,SAAS,gCAAgC,OAAuB;AAC9D,QAAO,MAAM,MAAM,CAAC,aAAa;;AAGnC,SAAgB,0BAA0B,QAevC;AACD,SAAQ,SAAiB;EACvB,MAAM,MAAM;EACZ,MAAM,aAAa,IAAI,MAAM;EAC7B,MAAM,OAAO,OAAO,WAAW,IAAI;EACnC,MAAM,iBAAiB,OAAO,yBAAyB,KAAK;EAC5D,MAAM,QAAQ,eAAe,iBAAkB,OAAO,4BAA4B,sCAAuC;AACzH,SAAO,OAAO,QAAQ,GAAG;AAEzB,MAAI,SAAS,SAAS;AACpB,OAAI,CAAC,YAAY;AACf,WAAO,iBAAiB;AACxB;;AAEF,UAAO,OAAO,aAAa,WAAW;GACtC,MAAM,OAAO,WAAW,WAAW,IAAI,GAAG,aAAa,IAAI;AACtD,WAAQ,QAAQ,OAAO,eAAe,KAAK,CAAC,CAAC,cAAc;AAC9D,WAAO,iBAAiB;KACxB;AACF;;AAGF,MAAI,CAAC,MACH;AAGF,MAAI,UAAU,KAAK;AACjB,UAAO,kBAAkB;AACzB;;AAGF,MAAI,IAAI,WAAW,IAAI,IAAI,QAAQ,KAAK;AACtC,UAAO,OAAO,aAAa,IAAI;AAC1B,UAAO,eAAe,IAAI;AAC/B;;AAGF,SAAO,OAAO,aAAa,MAAM;AAEjC,MAAI,MAAM,WAAW,IAAI,EAAE;AACpB,UAAO,cAAc,MAAM;AAChC;;AAGF,MAAI,OAAO,eAAe,IAAI,OAAO,gBAAgB;AAC9C,UAAO,eAAe,MAAM;AACjC;;AAGG,SAAO,YAAY,MAAM;;;AAIlC,SAAgB,wCAAwC,QAG5C;CACV,MAAM,WAAW,QAAQ,YAAY,QAAQ;CAC7C,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,cAAc,gCAAgC,IAAI,gBAAgB,GAAG;AAE3E,KAAI,aAAa,UAAU;AACzB,MAAI,YAAY,SAAS,QAAQ,IAAI,YAAY,SAAS,iBAAiB,CACzE,QAAO;AAET,SAAO;;AAGT,KAAI,aAAa,QACf,QAAO;CAGT,MAAM,WAAW,IAAI,WAAW,IAAI,aAAa;CACjD,MAAM,QAAQ,IAAI,SAAS;AAC3B,KAAI,QAAQ,WAAW,QAAQ,IAAI,QAAQ,WAAW,OAAO,CAC3D,QAAO;AAET,KAAI,gCAAgC,MAAM,CAAC,SAAS,OAAO,CACzD,QAAO;AAET,QAAO,YAAY,SAAS,SAAS;;AAGvC,SAAgB,2BAA2B,QAOxC;CACD,MAAM,WAAW,KAAK,IAAI,GAAG,OAAO,iBAAiB,GAAG;CACxD,MAAM,MAAM,OAAO,cAAc,KAAK,KAAK;CAC3C,MAAM,WAAW,OAAO,YAAY;CACpC,MAAM,aAAa,OAAO,cAAc;CACxC,IAAI,UAAyB;CAC7B,IAAI,YAAY;CAChB,IAAI,aAAmD;CAEvD,MAAM,wBAAwB;AAC5B,MAAI,CAAC,WACH;AAEF,aAAW,WAAW;AACtB,eAAa;;CAGf,MAAM,qBAAqB;AACzB,MAAI,YAAY,KACd;EAEF,MAAM,UAAU;AAChB,YAAU;AACV,cAAY;AACZ,mBAAiB;AACjB,SAAO,OAAO,QAAQ;;CAGxB,MAAM,sBAAsB;AAC1B,mBAAiB;AACjB,eAAa,eAAe;AAC1B,iBAAc;KACb,SAAS;;AAGd,SAAQ,UAAkB;AACxB,MAAI,CAAC,OAAO,SAAS;AACnB,UAAO,OAAO,MAAM;AACpB;;AAEF,MAAI,MAAM,MAAM,KAAK,OAAO,MAAM,WAAW,IAAI,EAAE;AACjD,iBAAc;AACd,UAAO,OAAO,MAAM;AACpB;;AAEF,MAAI,MAAM,SAAS,KAAK,EAAE;AACxB,iBAAc;AACd,UAAO,OAAO,MAAM;AACpB;;EAEF,MAAM,KAAK,KAAK;AAChB,MAAI,YAAY,MAAM;AACpB,aAAU;AACV,eAAY;AACZ,kBAAe;AACf;;AAEF,MAAI,KAAK,aAAa,UAAU;AAC9B,aAAU,GAAG,QAAQ,IAAI;AACzB,eAAY;AACZ,kBAAe;AACf;;AAEF,gBAAc;AACd,YAAU;AACV,cAAY;AACZ,iBAAe"}
@@ -368,10 +368,11 @@ async function runTui(opts) {
368
368
  root.addChild(bottomBar);
369
369
  tui.addChild(root);
370
370
  tui.setFocus(editor);
371
+ let editorMode = "chat";
371
372
  let isBashMode = false;
372
373
  let isBashExcludeContext = false;
373
374
  const updateEditorBorderColor = () => {
374
- if (isBashMode) editor.borderColor = isBashExcludeContext ? getBashExcludeBorderColor() : getBashModeBorderColor();
375
+ if (editorMode === "shell" || isBashMode) editor.borderColor = isBashExcludeContext ? getBashExcludeBorderColor() : getBashModeBorderColor();
375
376
  else {
376
377
  const level = state.sessionInfo.thinkingLevel ?? opts.thinking ?? "off";
377
378
  editor.borderColor = getThinkingBorderColor(level);
@@ -381,7 +382,7 @@ async function runTui(opts) {
381
382
  editor.onChange = (text) => {
382
383
  const trimmed = text.trimStart();
383
384
  const nextExclude = trimmed.startsWith("!!");
384
- const nextBash = trimmed.startsWith("!");
385
+ const nextBash = editorMode === "shell" || trimmed.startsWith("!");
385
386
  if (nextBash !== isBashMode || nextExclude !== isBashExcludeContext) {
386
387
  isBashMode = nextBash;
387
388
  isBashExcludeContext = nextExclude;
@@ -1717,6 +1718,24 @@ async function runTui(opts) {
1717
1718
  });
1718
1719
  }
1719
1720
  });
1721
+ const enterShellMode = () => {
1722
+ editorMode = "shell";
1723
+ editor.setText("");
1724
+ isBashMode = true;
1725
+ isBashExcludeContext = false;
1726
+ updateEditorBorderColor();
1727
+ updateFooter();
1728
+ tui.requestRender();
1729
+ };
1730
+ const exitShellMode = () => {
1731
+ if (editorMode !== "shell") return;
1732
+ editorMode = "chat";
1733
+ isBashMode = editor.getText().trimStart().startsWith("!");
1734
+ isBashExcludeContext = editor.getText().trimStart().startsWith("!!");
1735
+ updateEditorBorderColor();
1736
+ updateFooter();
1737
+ tui.requestRender();
1738
+ };
1720
1739
  const submitBurst = createSubmitBurstCoalescer({
1721
1740
  submit: createEditorSubmitHandler({
1722
1741
  editor: {
@@ -1726,6 +1745,9 @@ async function runTui(opts) {
1726
1745
  handleCommand,
1727
1746
  sendMessage,
1728
1747
  handleBangLine: runLocalShellLine,
1748
+ getMode: () => editorMode,
1749
+ enterShellMode,
1750
+ exitShellMode,
1729
1751
  isAgentBusy,
1730
1752
  steerWhileBusy: steerMessage,
1731
1753
  hasPendingAttachments: () => pendingImageAttachments.length > 0,
@@ -1879,6 +1901,11 @@ async function runTui(opts) {
1879
1901
  }
1880
1902
  };
1881
1903
  defaultEditor.onEscape = () => {
1904
+ if (editorMode === "shell") {
1905
+ editor.setText("");
1906
+ exitShellMode();
1907
+ return;
1908
+ }
1882
1909
  if (state.activeRunId) {
1883
1910
  abortActive();
1884
1911
  return;