@xopcai/xopc 0.0.27 → 0.0.29

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 (239) hide show
  1. package/dist/extensions/telegram/xopc.extension.json +1 -1
  2. package/dist/extensions/weixin/src/adapters/onboard-cli.d.ts +7 -0
  3. package/dist/extensions/weixin/src/adapters/onboard-cli.js +61 -0
  4. package/dist/extensions/weixin/src/adapters/onboard-cli.js.map +1 -0
  5. package/dist/extensions/weixin/src/cli/qr-login.d.ts +5 -0
  6. package/dist/extensions/weixin/src/cli/qr-login.js +1 -1
  7. package/dist/extensions/weixin/src/cli/qr-login.js.map +1 -1
  8. package/dist/extensions/weixin/src/index.js +1 -1
  9. package/dist/extensions/weixin/src/plugin.d.ts +1 -0
  10. package/dist/extensions/weixin/src/plugin.js +2 -0
  11. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  12. package/dist/gateway/static/root/assets/agents-CkgFSiCY.js +216 -0
  13. package/dist/gateway/static/root/assets/agents-CkgFSiCY.js.map +1 -0
  14. package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js → apps-page-Bmq19MS-.js} +2 -2
  15. package/dist/gateway/static/root/assets/{apps-page-CBBh_Ww8.js.map → apps-page-Bmq19MS-.js.map} +1 -1
  16. package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js +9 -0
  17. package/dist/gateway/static/root/assets/channels-settings-CE7jrdkO.js.map +1 -0
  18. package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js +2 -0
  19. package/dist/gateway/static/root/assets/cron-page-BpPPcykJ.js.map +1 -0
  20. package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js → cron-utils-N1PqD2DB.js} +2 -2
  21. package/dist/gateway/static/root/assets/{cron-utils-08gdQfl9.js.map → cron-utils-N1PqD2DB.js.map} +1 -1
  22. package/dist/gateway/static/root/assets/{dist-C1MrygQH.js → dist--p2HQ2QF.js} +2 -2
  23. package/dist/gateway/static/root/assets/{dist-C1MrygQH.js.map → dist--p2HQ2QF.js.map} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js → extension-debug-page-DwHCB_6T.js} +2 -2
  25. package/dist/gateway/static/root/assets/{extension-debug-page-DN3HKUGS.js.map → extension-debug-page-DwHCB_6T.js.map} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js → extension-page-BsYwQIex.js} +2 -2
  27. package/dist/gateway/static/root/assets/{extension-page-CoFDHZtZ.js.map → extension-page-BsYwQIex.js.map} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js → extension-settings-page-nsisEgjB.js} +2 -2
  29. package/dist/gateway/static/root/assets/{extension-settings-page-BcPCu_Go.js.map → extension-settings-page-nsisEgjB.js.map} +1 -1
  30. package/dist/gateway/static/root/assets/index-CR8zUHGR.js +4734 -0
  31. package/dist/gateway/static/root/assets/{index-PfkB8N37.js.map → index-CR8zUHGR.js.map} +1 -1
  32. package/dist/gateway/static/root/assets/index-Dnfha4O2.css +1 -0
  33. package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js +2 -0
  34. package/dist/gateway/static/root/assets/logs-page-CQwdV_Xw.js.map +1 -0
  35. package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js +2 -0
  36. package/dist/gateway/static/root/assets/sessions-page-Be5kIGl_.js.map +1 -0
  37. package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js +2 -0
  38. package/dist/gateway/static/root/assets/settings-page-PodSlNwr.js.map +1 -0
  39. package/dist/gateway/static/root/assets/skills-page-Clg8deH0.js +3 -0
  40. package/dist/gateway/static/root/assets/{skills-page-BmBDCEbY.js.map → skills-page-Clg8deH0.js.map} +1 -1
  41. package/dist/gateway/static/root/index.html +2 -2
  42. package/dist/package.js +1 -1
  43. package/dist/src/agent/lifecycle/hook-handler.d.ts +2 -0
  44. package/dist/src/agent/lifecycle/hook-handler.js +24 -0
  45. package/dist/src/agent/lifecycle/hook-handler.js.map +1 -1
  46. package/dist/src/agent/messaging/command-handler.js +10 -2
  47. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  48. package/dist/src/agent/service/process-direct-streaming.js +77 -20
  49. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  50. package/dist/src/agent/service.d.ts +15 -0
  51. package/dist/src/agent/service.js +21 -1
  52. package/dist/src/agent/service.js.map +1 -1
  53. package/dist/src/channels/weixin/index.js +1 -1
  54. package/dist/src/cli/agent-chat-log-level-preset.d.ts +8 -0
  55. package/dist/src/cli/agent-chat-log-level-preset.js +25 -0
  56. package/dist/src/cli/agent-chat-log-level-preset.js.map +1 -0
  57. package/dist/src/cli/commands/agent/interactive.js +4 -2
  58. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  59. package/dist/src/cli/commands/agent/stream-renderer.d.ts +14 -0
  60. package/dist/src/cli/commands/agent/stream-renderer.js +99 -0
  61. package/dist/src/cli/commands/agent/stream-renderer.js.map +1 -0
  62. package/dist/src/cli/commands/agent.js +2 -2
  63. package/dist/src/cli/commands/agent.js.map +1 -1
  64. package/dist/src/cli/commands/onboard.js +77 -93
  65. package/dist/src/cli/commands/onboard.js.map +1 -1
  66. package/dist/src/cli/commands/tui.d.ts +1 -0
  67. package/dist/src/cli/commands/tui.js +40 -0
  68. package/dist/src/cli/commands/tui.js.map +1 -0
  69. package/dist/src/cli/index.d.ts +2 -0
  70. package/dist/src/cli/index.js +7 -3
  71. package/dist/src/cli/index.js.map +1 -1
  72. package/dist/src/config/schema.d.ts +6 -0
  73. package/dist/src/config/schema.js +11 -3
  74. package/dist/src/config/schema.js.map +1 -1
  75. package/dist/src/extensions/hooks.js +5 -1
  76. package/dist/src/extensions/hooks.js.map +1 -1
  77. package/dist/src/extensions/loader.d.ts +1 -0
  78. package/dist/src/extensions/loader.js +3 -1
  79. package/dist/src/extensions/loader.js.map +1 -1
  80. package/dist/src/extensions/sdk/index.d.ts +1 -1
  81. package/dist/src/extensions/sdk/index.js.map +1 -1
  82. package/dist/src/extensions/types/core.d.ts +8 -0
  83. package/dist/src/extensions/types/hooks.d.ts +16 -1
  84. package/dist/src/extensions/types/hooks.js +1 -0
  85. package/dist/src/extensions/types/hooks.js.map +1 -1
  86. package/dist/src/gateway/agents-admin.d.ts +19 -1
  87. package/dist/src/gateway/agents-admin.js +164 -3
  88. package/dist/src/gateway/agents-admin.js.map +1 -1
  89. package/dist/src/gateway/auth.d.ts +17 -3
  90. package/dist/src/gateway/auth.js +35 -16
  91. package/dist/src/gateway/auth.js.map +1 -1
  92. package/dist/src/gateway/hono/app.js +31 -1
  93. package/dist/src/gateway/hono/app.js.map +1 -1
  94. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -1
  95. package/dist/src/gateway/hono/middleware/auth.js +4 -3
  96. package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
  97. package/dist/src/gateway/hono/middleware/scopes.d.ts +15 -0
  98. package/dist/src/gateway/hono/middleware/scopes.js +41 -0
  99. package/dist/src/gateway/hono/middleware/scopes.js.map +1 -0
  100. package/dist/src/gateway/hono/routes/agents.js +59 -5
  101. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  102. package/dist/src/gateway/hono/routes/config.js +2 -2
  103. package/dist/src/gateway/hono/routes/config.js.map +1 -1
  104. package/dist/src/gateway/hono/routes/public-gateway.js +1 -0
  105. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  106. package/dist/src/gateway/hono/routes/sessions.js +17 -0
  107. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  108. package/dist/src/gateway/security/audit.d.ts +18 -0
  109. package/dist/src/gateway/security/audit.js +68 -0
  110. package/dist/src/gateway/security/audit.js.map +1 -0
  111. package/dist/src/gateway/security/csp.d.ts +19 -0
  112. package/dist/src/gateway/security/csp.js +52 -0
  113. package/dist/src/gateway/security/csp.js.map +1 -0
  114. package/dist/src/gateway/security/dangerous-tools.d.ts +20 -0
  115. package/dist/src/gateway/security/dangerous-tools.js +46 -0
  116. package/dist/src/gateway/security/dangerous-tools.js.map +1 -0
  117. package/dist/src/gateway/security/flood-guard.d.ts +28 -0
  118. package/dist/src/gateway/security/flood-guard.js +42 -0
  119. package/dist/src/gateway/security/flood-guard.js.map +1 -0
  120. package/dist/src/gateway/security/index.d.ts +9 -0
  121. package/dist/src/gateway/security/index.js +10 -0
  122. package/dist/src/gateway/security/known-weak-secrets.d.ts +10 -0
  123. package/dist/src/gateway/security/known-weak-secrets.js +36 -0
  124. package/dist/src/gateway/security/known-weak-secrets.js.map +1 -0
  125. package/dist/src/gateway/security/operator-scopes.d.ts +37 -0
  126. package/dist/src/gateway/security/operator-scopes.js +137 -0
  127. package/dist/src/gateway/security/operator-scopes.js.map +1 -0
  128. package/dist/src/gateway/security/origin-check.d.ts +21 -0
  129. package/dist/src/gateway/security/origin-check.js +56 -0
  130. package/dist/src/gateway/security/origin-check.js.map +1 -0
  131. package/dist/src/gateway/security/preauth-connection-budget.d.ts +17 -0
  132. package/dist/src/gateway/security/preauth-connection-budget.js +49 -0
  133. package/dist/src/gateway/security/preauth-connection-budget.js.map +1 -0
  134. package/dist/src/gateway/security/secret-equal.d.ts +8 -0
  135. package/dist/src/gateway/security/secret-equal.js +30 -0
  136. package/dist/src/gateway/security/secret-equal.js.map +1 -0
  137. package/dist/src/gateway/service.d.ts +3 -1
  138. package/dist/src/gateway/service.js +40 -4
  139. package/dist/src/gateway/service.js.map +1 -1
  140. package/dist/src/session/client-history.d.ts +21 -0
  141. package/dist/src/session/client-history.js +89 -0
  142. package/dist/src/session/client-history.js.map +1 -0
  143. package/dist/src/session/index.d.ts +1 -0
  144. package/dist/src/session/index.js +2 -1
  145. package/dist/src/session/manager.d.ts +2 -0
  146. package/dist/src/session/manager.js +5 -0
  147. package/dist/src/session/manager.js.map +1 -1
  148. package/dist/src/session/thinking-resolve.js +1 -1
  149. package/dist/src/session/thinking-resolve.js.map +1 -1
  150. package/dist/src/tui/backends/embedded-backend.d.ts +42 -0
  151. package/dist/src/tui/backends/embedded-backend.js +173 -0
  152. package/dist/src/tui/backends/embedded-backend.js.map +1 -0
  153. package/dist/src/tui/backends/gateway-sse-backend.d.ts +53 -0
  154. package/dist/src/tui/backends/gateway-sse-backend.js +256 -0
  155. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -0
  156. package/dist/src/tui/chat-history.d.ts +4 -0
  157. package/dist/src/tui/chat-history.js +29 -0
  158. package/dist/src/tui/chat-history.js.map +1 -0
  159. package/dist/src/tui/components/assistant-message.d.ts +6 -0
  160. package/dist/src/tui/components/assistant-message.js +19 -0
  161. package/dist/src/tui/components/assistant-message.js.map +1 -0
  162. package/dist/src/tui/components/chat-log.d.ts +21 -0
  163. package/dist/src/tui/components/chat-log.js +113 -0
  164. package/dist/src/tui/components/chat-log.js.map +1 -0
  165. package/dist/src/tui/components/custom-editor.d.ts +14 -0
  166. package/dist/src/tui/components/custom-editor.js +50 -0
  167. package/dist/src/tui/components/custom-editor.js.map +1 -0
  168. package/dist/src/tui/components/fuzzy-filter.d.ts +17 -0
  169. package/dist/src/tui/components/fuzzy-filter.js +85 -0
  170. package/dist/src/tui/components/fuzzy-filter.js.map +1 -0
  171. package/dist/src/tui/components/searchable-select-list.d.ts +39 -0
  172. package/dist/src/tui/components/searchable-select-list.js +257 -0
  173. package/dist/src/tui/components/searchable-select-list.js.map +1 -0
  174. package/dist/src/tui/components/tool-execution.d.ts +16 -0
  175. package/dist/src/tui/components/tool-execution.js +76 -0
  176. package/dist/src/tui/components/tool-execution.js.map +1 -0
  177. package/dist/src/tui/components/user-message.d.ts +6 -0
  178. package/dist/src/tui/components/user-message.js +22 -0
  179. package/dist/src/tui/components/user-message.js.map +1 -0
  180. package/dist/src/tui/sse-consumer.d.ts +15 -0
  181. package/dist/src/tui/sse-consumer.js +75 -0
  182. package/dist/src/tui/sse-consumer.js.map +1 -0
  183. package/dist/src/tui/stream-assembler.d.ts +22 -0
  184. package/dist/src/tui/stream-assembler.js +63 -0
  185. package/dist/src/tui/stream-assembler.js.map +1 -0
  186. package/dist/src/tui/theme.d.ts +73 -0
  187. package/dist/src/tui/theme.js +157 -0
  188. package/dist/src/tui/theme.js.map +1 -0
  189. package/dist/src/tui/tui-agent-events.d.ts +7 -0
  190. package/dist/src/tui/tui-agent-events.js +103 -0
  191. package/dist/src/tui/tui-agent-events.js.map +1 -0
  192. package/dist/src/tui/tui-backend.d.ts +80 -0
  193. package/dist/src/tui/tui-backend.js +1 -0
  194. package/dist/src/tui/tui-commands.d.ts +23 -0
  195. package/dist/src/tui/tui-commands.js +165 -0
  196. package/dist/src/tui/tui-commands.js.map +1 -0
  197. package/dist/src/tui/tui-lifecycle.d.ts +26 -0
  198. package/dist/src/tui/tui-lifecycle.js +57 -0
  199. package/dist/src/tui/tui-lifecycle.js.map +1 -0
  200. package/dist/src/tui/tui-local-shell.d.ts +28 -0
  201. package/dist/src/tui/tui-local-shell.js +147 -0
  202. package/dist/src/tui/tui-local-shell.js.map +1 -0
  203. package/dist/src/tui/tui-overlays.d.ts +8 -0
  204. package/dist/src/tui/tui-overlays.js +22 -0
  205. package/dist/src/tui/tui-overlays.js.map +1 -0
  206. package/dist/src/tui/tui-picker-overlay.d.ts +26 -0
  207. package/dist/src/tui/tui-picker-overlay.js +69 -0
  208. package/dist/src/tui/tui-picker-overlay.js.map +1 -0
  209. package/dist/src/tui/tui-stdio-filter.d.ts +17 -0
  210. package/dist/src/tui/tui-stdio-filter.js +96 -0
  211. package/dist/src/tui/tui-stdio-filter.js.map +1 -0
  212. package/dist/src/tui/tui-submit.d.ts +25 -0
  213. package/dist/src/tui/tui-submit.js +102 -0
  214. package/dist/src/tui/tui-submit.js.map +1 -0
  215. package/dist/src/tui/tui-suspend.d.ts +10 -0
  216. package/dist/src/tui/tui-suspend.js +18 -0
  217. package/dist/src/tui/tui-suspend.js.map +1 -0
  218. package/dist/src/tui/tui-types.d.ts +86 -0
  219. package/dist/src/tui/tui-types.js +21 -0
  220. package/dist/src/tui/tui-types.js.map +1 -0
  221. package/dist/src/tui/tui.d.ts +5 -0
  222. package/dist/src/tui/tui.js +389 -0
  223. package/dist/src/tui/tui.js.map +1 -0
  224. package/package.json +5 -3
  225. package/dist/gateway/static/root/assets/agents-w8_jzuiX.js +0 -216
  226. package/dist/gateway/static/root/assets/agents-w8_jzuiX.js.map +0 -1
  227. package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js +0 -9
  228. package/dist/gateway/static/root/assets/channels-settings-DUKRPC7C.js.map +0 -1
  229. package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js +0 -2
  230. package/dist/gateway/static/root/assets/cron-page-S18t1yG-.js.map +0 -1
  231. package/dist/gateway/static/root/assets/index-OT4cGzon.css +0 -1
  232. package/dist/gateway/static/root/assets/index-PfkB8N37.js +0 -4734
  233. package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js +0 -2
  234. package/dist/gateway/static/root/assets/logs-page-DoWe1GWy.js.map +0 -1
  235. package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js +0 -2
  236. package/dist/gateway/static/root/assets/sessions-page-2uOYwEwd.js.map +0 -1
  237. package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js +0 -2
  238. package/dist/gateway/static/root/assets/settings-page-fQWswCuq.js.map +0 -1
  239. package/dist/gateway/static/root/assets/skills-page-BmBDCEbY.js +0 -3
@@ -12,7 +12,7 @@ async function resolveEffectiveThinkingLevel(sessionConfigStore, sessionKey, req
12
12
  if (fromSession !== void 0) return fromSession;
13
13
  return agentDefault ?? FALLBACK;
14
14
  }
15
- const REASONING_FALLBACK = "off";
15
+ const REASONING_FALLBACK = "stream";
16
16
  /**
17
17
  * Session override > agent default (`agents.defaults.reasoningDefault`).
18
18
  */
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-resolve.js","names":[],"sources":["../../../src/session/thinking-resolve.ts"],"sourcesContent":["/**\n * Resolve effective thinking level: request override > session store > agent default.\n */\n\nimport type { ThinkingLevel } from '@mariozechner/pi-agent-core';\nimport type { SessionConfigStore } from './config-store.js';\nimport { resolveThinkingLevel, resolveReasoningLevel } from './config-store.js';\nimport {\n normalizeThinkLevel,\n type ThinkLevel,\n type ReasoningLevel,\n} from '../agent/transcript/thinking-types.js';\n\nconst FALLBACK: ThinkingLevel = 'medium';\n\n/**\n * @param requestOverride - Raw value from HTTP/API (e.g. Web pill); wins over persisted session when valid.\n */\nexport async function resolveEffectiveThinkingLevel(\n sessionConfigStore: SessionConfigStore,\n sessionKey: string,\n requestOverride?: string | null,\n agentDefault?: ThinkLevel,\n): Promise<ThinkingLevel> {\n const fromRequest = normalizeThinkLevel(requestOverride ?? undefined);\n if (fromRequest !== undefined) {\n return fromRequest as ThinkingLevel;\n }\n\n const fromSession = await resolveThinkingLevel(sessionConfigStore, sessionKey, agentDefault);\n if (fromSession !== undefined) {\n return fromSession as ThinkingLevel;\n }\n\n const def = agentDefault ?? FALLBACK;\n return def as ThinkingLevel;\n}\n\nconst REASONING_FALLBACK: ReasoningLevel = 'off';\n\n/**\n * Session override > agent default (`agents.defaults.reasoningDefault`).\n */\nexport async function resolveEffectiveReasoningLevel(\n sessionConfigStore: SessionConfigStore,\n sessionKey: string,\n agentDefault?: ReasoningLevel,\n): Promise<ReasoningLevel> {\n const def = agentDefault ?? REASONING_FALLBACK;\n const resolved = await resolveReasoningLevel(sessionConfigStore, sessionKey, def);\n return resolved ?? def;\n}\n"],"mappings":";;;AAaA,MAAM,WAA0B;;;;AAKhC,eAAsB,8BACpB,oBACA,YACA,iBACA,cACwB;CACxB,MAAM,cAAc,oBAAoB,mBAAmB,KAAA,EAAU;AACrE,KAAI,gBAAgB,KAAA,EAClB,QAAO;CAGT,MAAM,cAAc,MAAM,qBAAqB,oBAAoB,YAAY,aAAa;AAC5F,KAAI,gBAAgB,KAAA,EAClB,QAAO;AAIT,QADY,gBAAgB;;AAI9B,MAAM,qBAAqC;;;;AAK3C,eAAsB,+BACpB,oBACA,YACA,cACyB;CACzB,MAAM,MAAM,gBAAgB;AAE5B,QAAO,MADgB,sBAAsB,oBAAoB,YAAY,IAAI,IAC9D"}
1
+ {"version":3,"file":"thinking-resolve.js","names":[],"sources":["../../../src/session/thinking-resolve.ts"],"sourcesContent":["/**\n * Resolve effective thinking level: request override > session store > agent default.\n */\n\nimport type { ThinkingLevel } from '@mariozechner/pi-agent-core';\nimport type { SessionConfigStore } from './config-store.js';\nimport { resolveThinkingLevel, resolveReasoningLevel } from './config-store.js';\nimport {\n normalizeThinkLevel,\n type ThinkLevel,\n type ReasoningLevel,\n} from '../agent/transcript/thinking-types.js';\n\nconst FALLBACK: ThinkingLevel = 'medium';\n\n/**\n * @param requestOverride - Raw value from HTTP/API (e.g. Web pill); wins over persisted session when valid.\n */\nexport async function resolveEffectiveThinkingLevel(\n sessionConfigStore: SessionConfigStore,\n sessionKey: string,\n requestOverride?: string | null,\n agentDefault?: ThinkLevel,\n): Promise<ThinkingLevel> {\n const fromRequest = normalizeThinkLevel(requestOverride ?? undefined);\n if (fromRequest !== undefined) {\n return fromRequest as ThinkingLevel;\n }\n\n const fromSession = await resolveThinkingLevel(sessionConfigStore, sessionKey, agentDefault);\n if (fromSession !== undefined) {\n return fromSession as ThinkingLevel;\n }\n\n const def = agentDefault ?? FALLBACK;\n return def as ThinkingLevel;\n}\n\nconst REASONING_FALLBACK: ReasoningLevel = 'stream';\n\n/**\n * Session override > agent default (`agents.defaults.reasoningDefault`).\n */\nexport async function resolveEffectiveReasoningLevel(\n sessionConfigStore: SessionConfigStore,\n sessionKey: string,\n agentDefault?: ReasoningLevel,\n): Promise<ReasoningLevel> {\n const def = agentDefault ?? REASONING_FALLBACK;\n const resolved = await resolveReasoningLevel(sessionConfigStore, sessionKey, def);\n return resolved ?? def;\n}\n"],"mappings":";;;AAaA,MAAM,WAA0B;;;;AAKhC,eAAsB,8BACpB,oBACA,YACA,iBACA,cACwB;CACxB,MAAM,cAAc,oBAAoB,mBAAmB,KAAA,EAAU;AACrE,KAAI,gBAAgB,KAAA,EAClB,QAAO;CAGT,MAAM,cAAc,MAAM,qBAAqB,oBAAoB,YAAY,aAAa;AAC5F,KAAI,gBAAgB,KAAA,EAClB,QAAO;AAIT,QADY,gBAAgB;;AAI9B,MAAM,qBAAqC;;;;AAK3C,eAAsB,+BACpB,oBACA,YACA,cACyB;CACzB,MAAM,MAAM,gBAAgB;AAE5B,QAAO,MADgB,sBAAsB,oBAAoB,YAAY,IAAI,IAC9D"}
@@ -0,0 +1,42 @@
1
+ import type { ChatSendOptions, HistoryMessage, TuiBackend, TuiEvent, TuiModelChoice, TuiSessionItem } from '../tui-backend.js';
2
+ import type { SessionInfo } from '../tui-types.js';
3
+ /**
4
+ * TUI backend that runs the agent in-process (no gateway required).
5
+ *
6
+ * Wraps `AgentService` directly and emits TuiEvents by observing the
7
+ * `MessageBus` output stream.
8
+ */
9
+ export declare class EmbeddedBackend implements TuiBackend {
10
+ private bus;
11
+ private agent;
12
+ private running;
13
+ private chatAbort;
14
+ onEvent?: (evt: TuiEvent) => void;
15
+ onConnected?: () => void;
16
+ onDisconnected?: (reason: string) => void;
17
+ constructor();
18
+ get connectionLabel(): string;
19
+ start(): void;
20
+ stop(): void;
21
+ sendChat(opts: ChatSendOptions): Promise<{
22
+ runId: string;
23
+ }>;
24
+ abortChat(_opts: {
25
+ sessionKey: string;
26
+ runId: string;
27
+ }): Promise<{
28
+ ok: boolean;
29
+ }>;
30
+ loadHistory(opts: {
31
+ sessionKey: string;
32
+ limit?: number;
33
+ }): Promise<{
34
+ messages: HistoryMessage[];
35
+ }>;
36
+ listSessions(): Promise<TuiSessionItem[]>;
37
+ getSessionInfo(_sessionKey: string): Promise<SessionInfo>;
38
+ listModels(): Promise<TuiModelChoice[]>;
39
+ resetSession(_sessionKey: string): Promise<void>;
40
+ patchSession(_sessionKey: string, _patch: Record<string, unknown>): Promise<void>;
41
+ private processOutbound;
42
+ }
@@ -0,0 +1,173 @@
1
+ import { getWorkspacePath } from "../../config/schema.js";
2
+ import { createLogger } from "../../utils/logger/index.js";
3
+ import { init_logger } from "../../utils/logger.js";
4
+ import { loadConfig } from "../../config/loader.js";
5
+ import { getAllProviders, getModelsByProvider, init_providers } from "../../providers/index.js";
6
+ import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
7
+ import "../../infra/bus/index.js";
8
+ import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
9
+ import { messagesToClientHistory } from "../../session/client-history.js";
10
+ import { AgentService } from "../../agent/service.js";
11
+ import "../../agent/index.js";
12
+ import "../../config/index.js";
13
+ //#region src/tui/backends/embedded-backend.ts
14
+ init_providers();
15
+ init_logger();
16
+ const log = createLogger("TUI:Embedded");
17
+ /**
18
+ * TUI backend that runs the agent in-process (no gateway required).
19
+ *
20
+ * Wraps `AgentService` directly and emits TuiEvents by observing the
21
+ * `MessageBus` output stream.
22
+ */
23
+ var EmbeddedBackend = class {
24
+ bus;
25
+ agent = null;
26
+ running = false;
27
+ chatAbort = null;
28
+ onEvent;
29
+ onConnected;
30
+ onDisconnected;
31
+ constructor() {
32
+ this.bus = new MessageBus();
33
+ }
34
+ get connectionLabel() {
35
+ return "local embedded";
36
+ }
37
+ start() {
38
+ if (this.running) return;
39
+ this.running = true;
40
+ const config = loadConfig();
41
+ const workspace = getWorkspacePath(config);
42
+ const modelConfig = config.agents?.defaults?.model;
43
+ const modelId = typeof modelConfig === "string" ? modelConfig : modelConfig?.primary;
44
+ this.agent = new AgentService(this.bus, {
45
+ workspace: workspace ?? process.cwd(),
46
+ model: modelId,
47
+ config
48
+ });
49
+ this.agent.start().catch((err) => {
50
+ const errorMessage = err instanceof Error ? err.message : String(err);
51
+ log.error({
52
+ err,
53
+ errorMessage
54
+ }, `Embedded agent failed: ${errorMessage}`);
55
+ this.onDisconnected?.(errorMessage);
56
+ });
57
+ this.processOutbound();
58
+ queueMicrotask(() => this.onConnected?.());
59
+ }
60
+ stop() {
61
+ this.running = false;
62
+ this.bus.shutdown();
63
+ this.agent?.stop();
64
+ this.agent = null;
65
+ }
66
+ async sendChat(opts) {
67
+ if (!this.agent) throw new Error("Agent not started");
68
+ const runId = crypto.randomUUID();
69
+ this.chatAbort?.abort();
70
+ this.chatAbort = new AbortController();
71
+ const signal = this.chatAbort.signal;
72
+ this.onEvent?.({
73
+ event: "status",
74
+ data: {
75
+ status: "started",
76
+ runId
77
+ }
78
+ });
79
+ (async () => {
80
+ try {
81
+ const messageForAgent = opts.message.trimStart().startsWith("/") ? opts.message : prependEnvelopeTimestamp(opts.message);
82
+ const stream = this.agent.processDirectStreaming(messageForAgent, opts.sessionKey, void 0, opts.thinking, { signal });
83
+ for await (const event of stream) {
84
+ if (signal.aborted) break;
85
+ this.onEvent?.({
86
+ event: event.type,
87
+ data: event
88
+ });
89
+ }
90
+ if (!signal.aborted) this.onEvent?.({
91
+ event: "result",
92
+ data: { ok: true }
93
+ });
94
+ } catch (error) {
95
+ if (signal.aborted) return;
96
+ const errorMessage = error instanceof Error ? error.message : String(error);
97
+ this.onEvent?.({
98
+ event: "error",
99
+ data: { content: errorMessage }
100
+ });
101
+ }
102
+ })();
103
+ return { runId };
104
+ }
105
+ async abortChat(_opts) {
106
+ if (this.chatAbort) {
107
+ this.chatAbort.abort();
108
+ this.chatAbort = null;
109
+ return { ok: true };
110
+ }
111
+ return { ok: false };
112
+ }
113
+ async loadHistory(opts) {
114
+ if (!this.agent) return { messages: [] };
115
+ try {
116
+ const detail = await this.agent.loadSessionDetail(opts.sessionKey);
117
+ if (!detail) return { messages: [] };
118
+ return { messages: messagesToClientHistory(detail.messages, { limit: opts.limit }) };
119
+ } catch (error) {
120
+ const errorMessage = error instanceof Error ? error.message : String(error);
121
+ log.warn({
122
+ err: error,
123
+ errorMessage
124
+ }, `Embedded loadHistory failed: ${errorMessage}`);
125
+ return { messages: [] };
126
+ }
127
+ }
128
+ async listSessions() {
129
+ return [];
130
+ }
131
+ async getSessionInfo(_sessionKey) {
132
+ const modelConfig = loadConfig().agents?.defaults?.model;
133
+ return { model: (typeof modelConfig === "string" ? modelConfig : modelConfig?.primary) ?? void 0 };
134
+ }
135
+ async listModels() {
136
+ const choices = [];
137
+ for (const provider of getAllProviders()) for (const model of getModelsByProvider(provider)) choices.push({
138
+ id: model.id,
139
+ name: model.name ?? model.id,
140
+ provider
141
+ });
142
+ return choices;
143
+ }
144
+ async resetSession(_sessionKey) {
145
+ this.stop();
146
+ this.bus = new MessageBus();
147
+ this.start();
148
+ }
149
+ async patchSession(_sessionKey, _patch) {}
150
+ processOutbound() {
151
+ (async () => {
152
+ while (this.running) try {
153
+ const msg = await this.bus.consumeOutbound();
154
+ log.debug({
155
+ channel: msg.channel,
156
+ chatId: msg.chat_id
157
+ }, "Outbound message");
158
+ } catch (error) {
159
+ if (error instanceof MessageBusShutdownError) break;
160
+ const errorMessage = error instanceof Error ? error.message : String(error);
161
+ log.warn({
162
+ err: error,
163
+ errorMessage
164
+ }, `Outbound processor failed: ${errorMessage}`);
165
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
166
+ }
167
+ })();
168
+ }
169
+ };
170
+ //#endregion
171
+ export { EmbeddedBackend };
172
+
173
+ //# sourceMappingURL=embedded-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded-backend.js","names":[],"sources":["../../../../src/tui/backends/embedded-backend.ts"],"sourcesContent":["import { AgentService } from '../../agent/index.js';\nimport { messagesToClientHistory } from '../../session/client-history.js';\nimport { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { loadConfig, getWorkspacePath } from '../../config/index.js';\nimport { MessageBus, MessageBusShutdownError } from '../../infra/bus/index.js';\nimport { getAllProviders, getModelsByProvider } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\n\nconst log = createLogger('TUI:Embedded');\n\n/**\n * TUI backend that runs the agent in-process (no gateway required).\n *\n * Wraps `AgentService` directly and emits TuiEvents by observing the\n * `MessageBus` output stream.\n */\nexport class EmbeddedBackend implements TuiBackend {\n private bus: MessageBus;\n private agent: AgentService | null = null;\n private running = false;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n\n constructor() {\n this.bus = new MessageBus();\n }\n\n get connectionLabel(): string {\n return 'local embedded';\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n\n const config = loadConfig();\n const workspace = getWorkspacePath(config);\n const modelConfig = config.agents?.defaults?.model;\n const modelId = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n\n this.agent = new AgentService(this.bus, {\n workspace: workspace ?? process.cwd(),\n model: modelId,\n config,\n });\n\n this.agent.start().catch((err) => {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage }, `Embedded agent failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n });\n\n // Process outbound messages in background\n this.processOutbound();\n\n // Signal ready\n queueMicrotask(() => this.onConnected?.());\n }\n\n stop(): void {\n this.running = false;\n this.bus.shutdown();\n void this.agent?.stop();\n this.agent = null;\n }\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n if (!this.agent) throw new Error('Agent not started');\n\n const runId = crypto.randomUUID();\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Run the stream in background so the TUI event loop stays responsive.\n void (async () => {\n try {\n // Prepend envelope timestamp so the model knows the current date/time,\n // matching the behavior of channel pipelines (Telegram, Weixin, etc.).\n // Skip for slash commands — parseSlashCommand requires lines starting with '/'.\n const messageForAgent = opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message);\n\n const stream = this.agent!.processDirectStreaming(\n messageForAgent,\n opts.sessionKey,\n undefined,\n opts.thinking,\n { signal },\n );\n\n for await (const event of stream) {\n if (signal.aborted) break;\n this.onEvent?.({ event: event.type, data: event });\n }\n\n if (!signal.aborted) {\n this.onEvent?.({\n event: 'result',\n data: { ok: true },\n });\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(_opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n if (this.chatAbort) {\n this.chatAbort.abort();\n this.chatAbort = null;\n return { ok: true };\n }\n return { ok: false };\n }\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n if (!this.agent) {\n return { messages: [] };\n }\n try {\n const detail = await this.agent.loadSessionDetail(opts.sessionKey);\n if (!detail) {\n return { messages: [] };\n }\n return {\n messages: messagesToClientHistory(detail.messages, { limit: opts.limit }),\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Embedded loadHistory failed: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n return [];\n }\n\n async getSessionInfo(_sessionKey: string): Promise<SessionInfo> {\n const config = loadConfig();\n const modelConfig = config.agents?.defaults?.model;\n const model = typeof modelConfig === 'string' ? modelConfig : modelConfig?.primary;\n return { model: model ?? undefined };\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n const choices: TuiModelChoice[] = [];\n for (const provider of getAllProviders()) {\n for (const model of getModelsByProvider(provider)) {\n choices.push({\n id: model.id,\n name: model.name ?? model.id,\n provider,\n });\n }\n }\n return choices;\n }\n\n async resetSession(_sessionKey: string): Promise<void> {\n // Restart agent for a clean session\n this.stop();\n this.bus = new MessageBus();\n this.start();\n }\n\n async patchSession(\n _sessionKey: string,\n _patch: Record<string, unknown>,\n ): Promise<void> {\n // Not supported in embedded mode\n }\n\n private processOutbound(): void {\n void (async () => {\n while (this.running) {\n try {\n const msg = await this.bus.consumeOutbound();\n log.debug({ channel: msg.channel, chatId: msg.chat_id }, 'Outbound message');\n } catch (error) {\n if (error instanceof MessageBusShutdownError) break;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Outbound processor failed: ${errorMessage}`);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n })();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { ChatSendOptions, HistoryMessage, TuiBackend, TuiEvent, TuiModelChoice, TuiSessionItem } from '../tui-backend.js';
2
+ import type { SessionInfo } from '../tui-types.js';
3
+ interface GatewaySSEOptions {
4
+ url: string;
5
+ token?: string;
6
+ }
7
+ /**
8
+ * TUI backend that communicates with a running xopc gateway via HTTP + SSE.
9
+ *
10
+ * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`
11
+ * - Broadcast events: `GET /api/events` via long-lived SSE
12
+ * - REST calls for sessions, models, etc.
13
+ */
14
+ export declare class GatewaySseBackend implements TuiBackend {
15
+ private readonly baseUrl;
16
+ private readonly token;
17
+ private eventAbort;
18
+ private chatAbort;
19
+ onEvent?: (evt: TuiEvent) => void;
20
+ onConnected?: () => void;
21
+ onDisconnected?: (reason: string) => void;
22
+ onGap?: (info: {
23
+ expected: number;
24
+ received: number;
25
+ }) => void;
26
+ constructor(opts: GatewaySSEOptions);
27
+ get connectionLabel(): string;
28
+ start(): void;
29
+ stop(): void;
30
+ sendChat(opts: ChatSendOptions): Promise<{
31
+ runId: string;
32
+ }>;
33
+ abortChat(opts: {
34
+ sessionKey: string;
35
+ runId: string;
36
+ }): Promise<{
37
+ ok: boolean;
38
+ }>;
39
+ loadHistory(opts: {
40
+ sessionKey: string;
41
+ limit?: number;
42
+ }): Promise<{
43
+ messages: HistoryMessage[];
44
+ }>;
45
+ listSessions(): Promise<TuiSessionItem[]>;
46
+ getSessionInfo(sessionKey: string): Promise<SessionInfo>;
47
+ listModels(): Promise<TuiModelChoice[]>;
48
+ resetSession(sessionKey: string): Promise<void>;
49
+ patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void>;
50
+ private startEventStream;
51
+ private scheduleReconnect;
52
+ }
53
+ export {};
@@ -0,0 +1,256 @@
1
+ import { createLogger } from "../../utils/logger/index.js";
2
+ import { init_logger } from "../../utils/logger.js";
3
+ import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
4
+ import { consumeSSEStream, parseSSEData } from "../sse-consumer.js";
5
+ //#region src/tui/backends/gateway-sse-backend.ts
6
+ init_logger();
7
+ const log = createLogger("TUI:GatewaySSE");
8
+ /** Fetch wrapper that adds auth headers. */
9
+ async function gatewayFetch(baseUrl, path, token, init) {
10
+ const headers = {
11
+ "Content-Type": "application/json",
12
+ ...token ? { Authorization: `Bearer ${token}` } : {},
13
+ ...init?.headers
14
+ };
15
+ return fetch(`${baseUrl}${path}`, {
16
+ ...init,
17
+ headers
18
+ });
19
+ }
20
+ /**
21
+ * TUI backend that communicates with a running xopc gateway via HTTP + SSE.
22
+ *
23
+ * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`
24
+ * - Broadcast events: `GET /api/events` via long-lived SSE
25
+ * - REST calls for sessions, models, etc.
26
+ */
27
+ var GatewaySseBackend = class {
28
+ baseUrl;
29
+ token;
30
+ eventAbort = null;
31
+ chatAbort = null;
32
+ onEvent;
33
+ onConnected;
34
+ onDisconnected;
35
+ onGap;
36
+ constructor(opts) {
37
+ this.baseUrl = opts.url.replace(/\/+$/, "");
38
+ this.token = opts.token;
39
+ }
40
+ get connectionLabel() {
41
+ return this.baseUrl;
42
+ }
43
+ start() {
44
+ this.startEventStream();
45
+ }
46
+ stop() {
47
+ this.eventAbort?.abort();
48
+ this.eventAbort = null;
49
+ this.chatAbort?.abort();
50
+ this.chatAbort = null;
51
+ }
52
+ async sendChat(opts) {
53
+ this.chatAbort?.abort();
54
+ this.chatAbort = new AbortController();
55
+ const signal = this.chatAbort.signal;
56
+ const runId = crypto.randomUUID();
57
+ this.onEvent?.({
58
+ event: "status",
59
+ data: {
60
+ status: "started",
61
+ runId
62
+ }
63
+ });
64
+ (async () => {
65
+ try {
66
+ const res = await gatewayFetch(this.baseUrl, "/api/agent", this.token, {
67
+ method: "POST",
68
+ headers: { Accept: "text/event-stream" },
69
+ body: JSON.stringify({
70
+ message: opts.message.trimStart().startsWith("/") ? opts.message : prependEnvelopeTimestamp(opts.message),
71
+ channel: "webchat",
72
+ sessionKey: opts.sessionKey,
73
+ thinking: opts.thinking
74
+ }),
75
+ signal
76
+ });
77
+ if (!res.ok) {
78
+ const body = await res.json().catch(() => ({}));
79
+ this.onEvent?.({
80
+ event: "error",
81
+ data: { content: body.error?.message ?? `Gateway error: ${res.status}` }
82
+ });
83
+ return;
84
+ }
85
+ if ((res.headers.get("Content-Type") ?? "").includes("text/event-stream") && res.body) await consumeSSEStream(res.body, (sseEvent) => {
86
+ if (signal.aborted) return;
87
+ const data = parseSSEData(sseEvent.data);
88
+ if (!data) return;
89
+ this.onEvent?.({
90
+ event: sseEvent.event,
91
+ data
92
+ });
93
+ }, signal);
94
+ else {
95
+ const json = await res.json();
96
+ if (json.ok && json.payload?.content) {
97
+ this.onEvent?.({
98
+ event: "token",
99
+ data: { content: json.payload.content }
100
+ });
101
+ this.onEvent?.({
102
+ event: "result",
103
+ data: { ok: true }
104
+ });
105
+ }
106
+ }
107
+ } catch (error) {
108
+ if (signal.aborted) return;
109
+ const errorMessage = error instanceof Error ? error.message : String(error);
110
+ this.onEvent?.({
111
+ event: "error",
112
+ data: { content: errorMessage }
113
+ });
114
+ }
115
+ })();
116
+ return { runId };
117
+ }
118
+ async abortChat(opts) {
119
+ this.chatAbort?.abort();
120
+ this.chatAbort = null;
121
+ try {
122
+ return { ok: (await (await gatewayFetch(this.baseUrl, "/api/agent/abort", this.token, {
123
+ method: "POST",
124
+ body: JSON.stringify({ runId: opts.runId })
125
+ })).json()).ok ?? false };
126
+ } catch {
127
+ return { ok: false };
128
+ }
129
+ }
130
+ async loadHistory(opts) {
131
+ try {
132
+ const params = new URLSearchParams();
133
+ if (opts.limit) params.set("limit", String(opts.limit));
134
+ const qs = params.toString();
135
+ const res = await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(opts.sessionKey)}/messages${qs ? `?${qs}` : ""}`, this.token);
136
+ if (!res.ok) return { messages: [] };
137
+ return { messages: (await res.json()).payload?.messages ?? [] };
138
+ } catch (error) {
139
+ const errorMessage = error instanceof Error ? error.message : String(error);
140
+ log.warn({
141
+ err: error,
142
+ errorMessage
143
+ }, `Failed to load history: ${errorMessage}`);
144
+ return { messages: [] };
145
+ }
146
+ }
147
+ async listSessions() {
148
+ try {
149
+ const res = await gatewayFetch(this.baseUrl, "/api/sessions", this.token);
150
+ if (!res.ok) return [];
151
+ return ((await res.json()).items ?? []).map((s) => ({
152
+ key: s.key,
153
+ displayName: s.name,
154
+ updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : void 0,
155
+ totalTokens: s.estimatedTokens ?? null,
156
+ model: typeof s.customData?.model === "string" ? s.customData.model : typeof s.customData?.modelRef === "string" ? s.customData.modelRef : null
157
+ }));
158
+ } catch {
159
+ return [];
160
+ }
161
+ }
162
+ async getSessionInfo(sessionKey) {
163
+ try {
164
+ const res = await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(sessionKey)}`, this.token);
165
+ if (!res.ok) return {};
166
+ const s = (await res.json()).session;
167
+ if (!s) return {};
168
+ return {
169
+ displayName: s.name,
170
+ totalTokens: s.estimatedTokens ?? void 0,
171
+ model: typeof s.customData?.model === "string" ? s.customData.model : typeof s.customData?.modelRef === "string" ? s.customData.modelRef : void 0,
172
+ modelProvider: typeof s.customData?.modelProvider === "string" ? s.customData.modelProvider : void 0
173
+ };
174
+ } catch {
175
+ return {};
176
+ }
177
+ }
178
+ async listModels() {
179
+ try {
180
+ const res = await gatewayFetch(this.baseUrl, "/api/models", this.token);
181
+ if (!res.ok) return [];
182
+ return (await res.json()).payload?.models ?? [];
183
+ } catch {
184
+ return [];
185
+ }
186
+ }
187
+ async resetSession(sessionKey) {
188
+ await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(sessionKey)}`, this.token, { method: "DELETE" }).catch(() => {});
189
+ }
190
+ async patchSession(sessionKey, patch) {
191
+ await gatewayFetch(this.baseUrl, `/api/sessions/${encodeURIComponent(sessionKey)}`, this.token, {
192
+ method: "PATCH",
193
+ body: JSON.stringify(patch)
194
+ }).catch(() => {});
195
+ }
196
+ startEventStream() {
197
+ this.eventAbort?.abort();
198
+ this.eventAbort = new AbortController();
199
+ const url = new URL(`${this.baseUrl}/api/events`);
200
+ if (this.token) url.searchParams.set("token", this.token);
201
+ const connect = async () => {
202
+ try {
203
+ const res = await fetch(url.toString(), {
204
+ signal: this.eventAbort.signal,
205
+ headers: { Accept: "text/event-stream" }
206
+ });
207
+ if (!res.ok || !res.body) {
208
+ this.onDisconnected?.(`event stream error: ${res.status}`);
209
+ this.scheduleReconnect();
210
+ return;
211
+ }
212
+ this.onConnected?.();
213
+ await consumeSSEStream(res.body, (sseEvent) => {
214
+ if (sseEvent.event === "connected") return;
215
+ if (sseEvent.event === "gap") {
216
+ const gapData = parseSSEData(sseEvent.data);
217
+ if (gapData && typeof gapData.expected === "number" && typeof gapData.received === "number") this.onGap?.({
218
+ expected: gapData.expected,
219
+ received: gapData.received
220
+ });
221
+ return;
222
+ }
223
+ const data = parseSSEData(sseEvent.data);
224
+ if (data !== null) this.onEvent?.({
225
+ event: sseEvent.event,
226
+ data
227
+ });
228
+ }, this.eventAbort.signal);
229
+ if (!this.eventAbort?.signal.aborted) {
230
+ this.onDisconnected?.("stream closed");
231
+ this.scheduleReconnect();
232
+ }
233
+ } catch (error) {
234
+ if (this.eventAbort?.signal.aborted) return;
235
+ const errorMessage = error instanceof Error ? error.message : String(error);
236
+ log.warn({
237
+ err: error,
238
+ errorMessage
239
+ }, `Event stream failed: ${errorMessage}`);
240
+ this.onDisconnected?.(errorMessage);
241
+ this.scheduleReconnect();
242
+ }
243
+ };
244
+ connect();
245
+ }
246
+ scheduleReconnect() {
247
+ if (this.eventAbort?.signal.aborted) return;
248
+ setTimeout(() => {
249
+ if (!this.eventAbort?.signal.aborted) this.startEventStream();
250
+ }, 3e3);
251
+ }
252
+ };
253
+ //#endregion
254
+ export { GatewaySseBackend };
255
+
256
+ //# sourceMappingURL=gateway-sse-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-sse-backend.js","names":[],"sources":["../../../../src/tui/backends/gateway-sse-backend.ts"],"sourcesContent":["import { prependEnvelopeTimestamp } from '../../channels/envelope-timestamp.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { consumeSSEStream, parseSSEData } from '../sse-consumer.js';\nimport type {\n ChatSendOptions,\n HistoryMessage,\n TuiBackend,\n TuiEvent,\n TuiModelChoice,\n TuiSessionItem,\n} from '../tui-backend.js';\nimport type { SessionInfo } from '../tui-types.js';\n\nconst log = createLogger('TUI:GatewaySSE');\n\ninterface GatewaySSEOptions {\n url: string;\n token?: string;\n}\n\n/** Fetch wrapper that adds auth headers. */\nasync function gatewayFetch(\n baseUrl: string,\n path: string,\n token: string | undefined,\n init?: RequestInit,\n): Promise<Response> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(init?.headers as Record<string, string> | undefined),\n };\n return fetch(`${baseUrl}${path}`, { ...init, headers });\n}\n\n/**\n * TUI backend that communicates with a running xopc gateway via HTTP + SSE.\n *\n * - Agent streaming: `POST /api/agent` with `Accept: text/event-stream`\n * - Broadcast events: `GET /api/events` via long-lived SSE\n * - REST calls for sessions, models, etc.\n */\nexport class GatewaySseBackend implements TuiBackend {\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private eventAbort: AbortController | null = null;\n private chatAbort: AbortController | null = null;\n\n onEvent?: (evt: TuiEvent) => void;\n onConnected?: () => void;\n onDisconnected?: (reason: string) => void;\n onGap?: (info: { expected: number; received: number }) => void;\n\n constructor(opts: GatewaySSEOptions) {\n this.baseUrl = opts.url.replace(/\\/+$/, '');\n this.token = opts.token;\n }\n\n get connectionLabel(): string {\n return this.baseUrl;\n }\n\n start(): void {\n this.startEventStream();\n }\n\n stop(): void {\n this.eventAbort?.abort();\n this.eventAbort = null;\n this.chatAbort?.abort();\n this.chatAbort = null;\n }\n\n // ── Agent chat (POST /api/agent → SSE response body) ──\n\n async sendChat(opts: ChatSendOptions): Promise<{ runId: string }> {\n this.chatAbort?.abort();\n this.chatAbort = new AbortController();\n const signal = this.chatAbort.signal;\n const runId = crypto.randomUUID();\n\n // Match EmbeddedBackend: set activeRunId before any token/tool events so TUI state stays on one\n // runId (avoids assistant under \"default\" and tools under the real uuid).\n this.onEvent?.({ event: 'status', data: { status: 'started', runId } });\n\n // Fire-and-forget: run the HTTP request + SSE consumption in background\n // so the TUI event loop stays responsive for keyboard input.\n void (async () => {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent', this.token, {\n method: 'POST',\n headers: { Accept: 'text/event-stream' },\n body: JSON.stringify({\n // Prepend envelope timestamp for regular messages so the model knows\n // the current date/time. Skip for slash commands — parseSlashCommand\n // requires lines starting with '/'.\n message: opts.message.trimStart().startsWith('/')\n ? opts.message\n : prependEnvelopeTimestamp(opts.message),\n channel: 'webchat',\n sessionKey: opts.sessionKey,\n thinking: opts.thinking,\n }),\n signal,\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: { message?: string } };\n this.onEvent?.({\n event: 'error',\n data: { content: body.error?.message ?? `Gateway error: ${res.status}` },\n });\n return;\n }\n\n const contentType = res.headers.get('Content-Type') ?? '';\n\n if (contentType.includes('text/event-stream') && res.body) {\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (signal.aborted) return;\n const data = parseSSEData<Record<string, unknown>>(sseEvent.data);\n if (!data) return;\n this.onEvent?.({ event: sseEvent.event, data });\n },\n signal,\n );\n } else {\n const json = (await res.json()) as { ok?: boolean; payload?: { content?: string } };\n if (json.ok && json.payload?.content) {\n this.onEvent?.({\n event: 'token',\n data: { content: json.payload.content },\n });\n this.onEvent?.({ event: 'result', data: { ok: true } });\n }\n }\n } catch (error) {\n if (signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.onEvent?.({ event: 'error', data: { content: errorMessage } });\n }\n })();\n\n return { runId };\n }\n\n async abortChat(opts: { sessionKey: string; runId: string }): Promise<{ ok: boolean }> {\n this.chatAbort?.abort();\n this.chatAbort = null;\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/agent/abort', this.token, {\n method: 'POST',\n body: JSON.stringify({ runId: opts.runId }),\n });\n const json = (await res.json()) as { ok?: boolean };\n return { ok: json.ok ?? false };\n } catch {\n return { ok: false };\n }\n }\n\n // ── REST helpers ──\n\n async loadHistory(opts: {\n sessionKey: string;\n limit?: number;\n }): Promise<{ messages: HistoryMessage[] }> {\n try {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(opts.sessionKey)}/messages${qs ? `?${qs}` : ''}`,\n this.token,\n );\n if (!res.ok) return { messages: [] };\n const json = (await res.json()) as { ok?: boolean; payload?: { messages?: HistoryMessage[] } };\n return { messages: json.payload?.messages ?? [] };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Failed to load history: ${errorMessage}`);\n return { messages: [] };\n }\n }\n\n async listSessions(): Promise<TuiSessionItem[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/sessions', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n items?: Array<{\n key: string;\n name?: string;\n updatedAt?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n }>;\n };\n return (json.items ?? []).map((s) => ({\n key: s.key,\n displayName: s.name,\n updatedAt: s.updatedAt ? Date.parse(s.updatedAt) : undefined,\n totalTokens: s.estimatedTokens ?? null,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : null,\n }));\n } catch {\n return [];\n }\n }\n\n async getSessionInfo(sessionKey: string): Promise<SessionInfo> {\n try {\n const res = await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n );\n if (!res.ok) return {};\n const json = (await res.json()) as {\n session?: {\n name?: string;\n estimatedTokens?: number;\n customData?: Record<string, unknown>;\n };\n };\n const s = json.session;\n if (!s) return {};\n return {\n displayName: s.name,\n totalTokens: s.estimatedTokens ?? undefined,\n model:\n typeof s.customData?.model === 'string'\n ? s.customData.model\n : typeof s.customData?.modelRef === 'string'\n ? s.customData.modelRef\n : undefined,\n modelProvider:\n typeof s.customData?.modelProvider === 'string' ? s.customData.modelProvider : undefined,\n };\n } catch {\n return {};\n }\n }\n\n async listModels(): Promise<TuiModelChoice[]> {\n try {\n const res = await gatewayFetch(this.baseUrl, '/api/models', this.token);\n if (!res.ok) return [];\n const json = (await res.json()) as {\n ok?: boolean;\n payload?: { models?: TuiModelChoice[] };\n };\n return json.payload?.models ?? [];\n } catch {\n return [];\n }\n }\n\n async resetSession(sessionKey: string): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'DELETE' },\n ).catch(() => {});\n }\n\n async patchSession(sessionKey: string, patch: Record<string, unknown>): Promise<void> {\n await gatewayFetch(\n this.baseUrl,\n `/api/sessions/${encodeURIComponent(sessionKey)}`,\n this.token,\n { method: 'PATCH', body: JSON.stringify(patch) },\n ).catch(() => {});\n }\n\n // ── Broadcast SSE (GET /api/events) ──\n\n private startEventStream(): void {\n this.eventAbort?.abort();\n this.eventAbort = new AbortController();\n\n const url = new URL(`${this.baseUrl}/api/events`);\n if (this.token) url.searchParams.set('token', this.token);\n\n const connect = async () => {\n try {\n const res = await fetch(url.toString(), {\n signal: this.eventAbort!.signal,\n headers: { Accept: 'text/event-stream' },\n });\n\n if (!res.ok || !res.body) {\n this.onDisconnected?.(`event stream error: ${res.status}`);\n this.scheduleReconnect();\n return;\n }\n\n this.onConnected?.();\n\n await consumeSSEStream(\n res.body,\n (sseEvent) => {\n if (sseEvent.event === 'connected') return;\n if (sseEvent.event === 'gap') {\n const gapData = parseSSEData(sseEvent.data) as {\n expected?: unknown;\n received?: unknown;\n } | null;\n if (\n gapData &&\n typeof gapData.expected === 'number' &&\n typeof gapData.received === 'number'\n ) {\n this.onGap?.({ expected: gapData.expected, received: gapData.received });\n }\n return;\n }\n const data = parseSSEData(sseEvent.data);\n if (data !== null) {\n this.onEvent?.({ event: sseEvent.event, data });\n }\n },\n this.eventAbort!.signal,\n );\n\n // Stream ended normally\n if (!this.eventAbort?.signal.aborted) {\n this.onDisconnected?.('stream closed');\n this.scheduleReconnect();\n }\n } catch (error) {\n if (this.eventAbort?.signal.aborted) return;\n const errorMessage = error instanceof Error ? error.message : String(error);\n log.warn({ err: error, errorMessage }, `Event stream failed: ${errorMessage}`);\n this.onDisconnected?.(errorMessage);\n this.scheduleReconnect();\n }\n };\n\n void connect();\n }\n\n private scheduleReconnect(): void {\n if (this.eventAbort?.signal.aborted) return;\n setTimeout(() => {\n if (!this.eventAbort?.signal.aborted) {\n this.startEventStream();\n }\n }, 3000);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { HistoryMessage } from './tui-backend.js';
2
+ import { ChatLog } from './components/chat-log.js';
3
+ /** Replay persisted transcript into the scroll log (synthetic run ids per assistant row). */
4
+ export declare function appendHistoryToChatLog(chatLog: ChatLog, messages: HistoryMessage[], toolsExpanded: boolean): void;
@@ -0,0 +1,29 @@
1
+ //#region src/tui/chat-history.ts
2
+ /** Replay persisted transcript into the scroll log (synthetic run ids per assistant row). */
3
+ function appendHistoryToChatLog(chatLog, messages, toolsExpanded) {
4
+ chatLog.setToolsExpanded(toolsExpanded);
5
+ messages.forEach((hm, idx) => {
6
+ const runId = `history:${idx}`;
7
+ if (hm.role === "user") {
8
+ chatLog.addUser(hm.content);
9
+ return;
10
+ }
11
+ if (hm.role === "system") {
12
+ chatLog.addSystem(hm.content);
13
+ return;
14
+ }
15
+ const tools = hm.toolCalls ?? [];
16
+ for (let t = 0; t < tools.length; t++) {
17
+ const tc = tools[t];
18
+ const tid = `history:${idx}:t:${t}`;
19
+ chatLog.startTool(tid, tc.name, tc.args ?? {}, runId);
20
+ if (tc.result !== void 0) chatLog.updateToolResult(tid, tc.result, tc.isError ?? false);
21
+ }
22
+ const body = hm.content.trim() ? hm.content : " ";
23
+ chatLog.finalizeAssistant(body, runId);
24
+ });
25
+ }
26
+ //#endregion
27
+ export { appendHistoryToChatLog };
28
+
29
+ //# sourceMappingURL=chat-history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-history.js","names":[],"sources":["../../../src/tui/chat-history.ts"],"sourcesContent":["import type { HistoryMessage } from './tui-backend.js';\nimport { ChatLog } from './components/chat-log.js';\n\n/** Replay persisted transcript into the scroll log (synthetic run ids per assistant row). */\nexport function appendHistoryToChatLog(\n chatLog: ChatLog,\n messages: HistoryMessage[],\n toolsExpanded: boolean,\n): void {\n chatLog.setToolsExpanded(toolsExpanded);\n\n messages.forEach((hm, idx) => {\n const runId = `history:${idx}`;\n\n if (hm.role === 'user') {\n chatLog.addUser(hm.content);\n return;\n }\n\n if (hm.role === 'system') {\n chatLog.addSystem(hm.content);\n return;\n }\n\n const tools = hm.toolCalls ?? [];\n for (let t = 0; t < tools.length; t++) {\n const tc = tools[t]!;\n const tid = `history:${idx}:t:${t}`;\n chatLog.startTool(tid, tc.name, tc.args ?? {}, runId);\n if (tc.result !== undefined) {\n chatLog.updateToolResult(tid, tc.result, tc.isError ?? false);\n }\n }\n\n const body = hm.content.trim() ? hm.content : ' ';\n chatLog.finalizeAssistant(body, runId);\n });\n}\n"],"mappings":";;AAIA,SAAgB,uBACd,SACA,UACA,eACM;AACN,SAAQ,iBAAiB,cAAc;AAEvC,UAAS,SAAS,IAAI,QAAQ;EAC5B,MAAM,QAAQ,WAAW;AAEzB,MAAI,GAAG,SAAS,QAAQ;AACtB,WAAQ,QAAQ,GAAG,QAAQ;AAC3B;;AAGF,MAAI,GAAG,SAAS,UAAU;AACxB,WAAQ,UAAU,GAAG,QAAQ;AAC7B;;EAGF,MAAM,QAAQ,GAAG,aAAa,EAAE;AAChC,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,KAAK,MAAM;GACjB,MAAM,MAAM,WAAW,IAAI,KAAK;AAChC,WAAQ,UAAU,KAAK,GAAG,MAAM,GAAG,QAAQ,EAAE,EAAE,MAAM;AACrD,OAAI,GAAG,WAAW,KAAA,EAChB,SAAQ,iBAAiB,KAAK,GAAG,QAAQ,GAAG,WAAW,MAAM;;EAIjE,MAAM,OAAO,GAAG,QAAQ,MAAM,GAAG,GAAG,UAAU;AAC9C,UAAQ,kBAAkB,MAAM,MAAM;GACtC"}
@@ -0,0 +1,6 @@
1
+ import { Container } from '@mariozechner/pi-tui';
2
+ export declare class AssistantMessageComponent extends Container {
3
+ private body;
4
+ constructor(text: string);
5
+ setText(text: string): void;
6
+ }