miladyai 2.0.0-alpha.27

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 (241) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +7 -0
  2. package/dist/actions/emote.js +64 -0
  3. package/dist/actions/restart.js +81 -0
  4. package/dist/actions/send-message.js +152 -0
  5. package/dist/agent-admin-routes.js +82 -0
  6. package/dist/agent-lifecycle-routes.js +79 -0
  7. package/dist/agent-transfer-routes.js +102 -0
  8. package/dist/api/agent-admin-routes.js +82 -0
  9. package/dist/api/agent-lifecycle-routes.js +79 -0
  10. package/dist/api/agent-transfer-routes.js +102 -0
  11. package/dist/api/apps-hyperscape-routes.js +58 -0
  12. package/dist/api/apps-routes.js +114 -0
  13. package/dist/api/auth-routes.js +56 -0
  14. package/dist/api/autonomy-routes.js +44 -0
  15. package/dist/api/bug-report-routes.js +111 -0
  16. package/dist/api/character-routes.js +195 -0
  17. package/dist/api/cloud-routes.js +330 -0
  18. package/dist/api/cloud-status-routes.js +155 -0
  19. package/dist/api/compat-utils.js +111 -0
  20. package/dist/api/database.js +735 -0
  21. package/dist/api/diagnostics-routes.js +205 -0
  22. package/dist/api/drop-service.js +134 -0
  23. package/dist/api/early-logs.js +86 -0
  24. package/dist/api/http-helpers.js +131 -0
  25. package/dist/api/knowledge-routes.js +534 -0
  26. package/dist/api/memory-bounds.js +71 -0
  27. package/dist/api/models-routes.js +28 -0
  28. package/dist/api/og-tracker.js +36 -0
  29. package/dist/api/permissions-routes.js +109 -0
  30. package/dist/api/plugin-validation.js +198 -0
  31. package/dist/api/provider-switch-config.js +41 -0
  32. package/dist/api/registry-routes.js +86 -0
  33. package/dist/api/registry-service.js +164 -0
  34. package/dist/api/sandbox-routes.js +1112 -0
  35. package/dist/api/server.js +7949 -0
  36. package/dist/api/subscription-routes.js +172 -0
  37. package/dist/api/terminal-run-limits.js +24 -0
  38. package/dist/api/training-routes.js +158 -0
  39. package/dist/api/trajectory-routes.js +300 -0
  40. package/dist/api/trigger-routes.js +246 -0
  41. package/dist/api/twitter-verify.js +134 -0
  42. package/dist/api/tx-service.js +108 -0
  43. package/dist/api/wallet-routes.js +266 -0
  44. package/dist/api/wallet.js +568 -0
  45. package/dist/api/whatsapp-routes.js +182 -0
  46. package/dist/api/zip-utils.js +109 -0
  47. package/dist/apps-hyperscape-routes.js +58 -0
  48. package/dist/apps-routes.js +114 -0
  49. package/dist/ascii.js +20 -0
  50. package/dist/auth/anthropic.js +44 -0
  51. package/dist/auth/apply-stealth.js +41 -0
  52. package/dist/auth/claude-code-stealth.js +78 -0
  53. package/dist/auth/credentials.js +156 -0
  54. package/dist/auth/index.js +5 -0
  55. package/dist/auth/openai-codex.js +66 -0
  56. package/dist/auth/types.js +9 -0
  57. package/dist/auth-routes.js +56 -0
  58. package/dist/autonomy-routes.js +44 -0
  59. package/dist/bug-report-routes.js +111 -0
  60. package/dist/build-info.json +6 -0
  61. package/dist/character-routes.js +195 -0
  62. package/dist/cli/argv.js +63 -0
  63. package/dist/cli/banner.js +34 -0
  64. package/dist/cli/cli-name.js +21 -0
  65. package/dist/cli/cli-utils.js +16 -0
  66. package/dist/cli/git-commit.js +78 -0
  67. package/dist/cli/parse-duration.js +15 -0
  68. package/dist/cli/plugins-cli.js +590 -0
  69. package/dist/cli/profile-utils.js +9 -0
  70. package/dist/cli/profile.js +95 -0
  71. package/dist/cli/program/build-program.js +17 -0
  72. package/dist/cli/program/command-registry.js +23 -0
  73. package/dist/cli/program/help.js +47 -0
  74. package/dist/cli/program/preaction.js +33 -0
  75. package/dist/cli/program/register.config.js +106 -0
  76. package/dist/cli/program/register.configure.js +20 -0
  77. package/dist/cli/program/register.dashboard.js +124 -0
  78. package/dist/cli/program/register.models.js +23 -0
  79. package/dist/cli/program/register.setup.js +36 -0
  80. package/dist/cli/program/register.start.js +22 -0
  81. package/dist/cli/program/register.subclis.js +70 -0
  82. package/dist/cli/program/register.tui.js +163 -0
  83. package/dist/cli/program/register.update.js +154 -0
  84. package/dist/cli/program.js +3 -0
  85. package/dist/cli/run-main.js +37 -0
  86. package/dist/cli/version.js +7 -0
  87. package/dist/cloud/validate-url.js +93 -0
  88. package/dist/cloud-routes.js +330 -0
  89. package/dist/cloud-status-routes.js +155 -0
  90. package/dist/compat-utils.js +111 -0
  91. package/dist/config/config.js +69 -0
  92. package/dist/config/env-vars.js +19 -0
  93. package/dist/config/includes.js +121 -0
  94. package/dist/config/object-utils.js +7 -0
  95. package/dist/config/paths.js +38 -0
  96. package/dist/config/plugin-auto-enable.js +231 -0
  97. package/dist/config/schema.js +864 -0
  98. package/dist/config/telegram-custom-commands.js +76 -0
  99. package/dist/config/zod-schema.agent-runtime.js +519 -0
  100. package/dist/config/zod-schema.core.js +538 -0
  101. package/dist/config/zod-schema.hooks.js +103 -0
  102. package/dist/config/zod-schema.js +488 -0
  103. package/dist/config/zod-schema.providers-core.js +785 -0
  104. package/dist/config/zod-schema.session.js +73 -0
  105. package/dist/core-plugins.js +37 -0
  106. package/dist/custom-actions.js +250 -0
  107. package/dist/database.js +735 -0
  108. package/dist/diagnostics/integration-observability.js +57 -0
  109. package/dist/diagnostics-routes.js +205 -0
  110. package/dist/drop-service.js +134 -0
  111. package/dist/early-logs.js +24 -0
  112. package/dist/eliza.js +2061 -0
  113. package/dist/emotes/catalog.js +271 -0
  114. package/dist/entry.js +40 -0
  115. package/dist/hooks/discovery.js +167 -0
  116. package/dist/hooks/eligibility.js +64 -0
  117. package/dist/hooks/index.js +4 -0
  118. package/dist/hooks/loader.js +147 -0
  119. package/dist/hooks/registry.js +55 -0
  120. package/dist/http-helpers.js +131 -0
  121. package/dist/index.js +49 -0
  122. package/dist/knowledge-routes.js +534 -0
  123. package/dist/memory-bounds.js +71 -0
  124. package/dist/milady-plugin.js +90 -0
  125. package/dist/models-routes.js +28 -0
  126. package/dist/onboarding-names.js +78 -0
  127. package/dist/onboarding-presets.js +922 -0
  128. package/dist/package.json +1 -0
  129. package/dist/permissions-routes.js +109 -0
  130. package/dist/plugin-validation.js +107 -0
  131. package/dist/plugins/whatsapp/actions.js +91 -0
  132. package/dist/plugins/whatsapp/index.js +16 -0
  133. package/dist/plugins/whatsapp/service.js +270 -0
  134. package/dist/provider-switch-config.js +41 -0
  135. package/dist/providers/admin-trust.js +46 -0
  136. package/dist/providers/autonomous-state.js +101 -0
  137. package/dist/providers/session-bridge.js +86 -0
  138. package/dist/providers/session-utils.js +36 -0
  139. package/dist/providers/simple-mode.js +50 -0
  140. package/dist/providers/ui-catalog.js +15 -0
  141. package/dist/providers/workspace-provider.js +93 -0
  142. package/dist/providers/workspace.js +348 -0
  143. package/dist/registry-routes.js +86 -0
  144. package/dist/registry-service.js +164 -0
  145. package/dist/restart.js +40 -0
  146. package/dist/runtime/core-plugins.js +37 -0
  147. package/dist/runtime/custom-actions.js +250 -0
  148. package/dist/runtime/eliza.js +2061 -0
  149. package/dist/runtime/embedding-manager-support.js +185 -0
  150. package/dist/runtime/embedding-manager.js +193 -0
  151. package/dist/runtime/embedding-presets.js +54 -0
  152. package/dist/runtime/embedding-state.js +8 -0
  153. package/dist/runtime/milady-plugin.js +90 -0
  154. package/dist/runtime/onboarding-names.js +78 -0
  155. package/dist/runtime/restart.js +40 -0
  156. package/dist/runtime/version.js +7 -0
  157. package/dist/sandbox-routes.js +1112 -0
  158. package/dist/security/audit-log.js +149 -0
  159. package/dist/security/network-policy.js +70 -0
  160. package/dist/server.js +7949 -0
  161. package/dist/services/agent-export.js +559 -0
  162. package/dist/services/app-manager.js +389 -0
  163. package/dist/services/browser-capture.js +86 -0
  164. package/dist/services/fallback-training-service.js +128 -0
  165. package/dist/services/mcp-marketplace.js +134 -0
  166. package/dist/services/plugin-installer.js +396 -0
  167. package/dist/services/plugin-manager-types.js +15 -0
  168. package/dist/services/registry-client-app-meta.js +144 -0
  169. package/dist/services/registry-client-endpoints.js +166 -0
  170. package/dist/services/registry-client-local.js +271 -0
  171. package/dist/services/registry-client-network.js +93 -0
  172. package/dist/services/registry-client-queries.js +70 -0
  173. package/dist/services/registry-client.js +157 -0
  174. package/dist/services/sandbox-engine.js +511 -0
  175. package/dist/services/sandbox-manager.js +297 -0
  176. package/dist/services/self-updater.js +175 -0
  177. package/dist/services/skill-catalog-client.js +119 -0
  178. package/dist/services/skill-marketplace.js +521 -0
  179. package/dist/services/stream-manager.js +236 -0
  180. package/dist/services/update-checker.js +121 -0
  181. package/dist/services/update-notifier.js +29 -0
  182. package/dist/services/version-compat.js +78 -0
  183. package/dist/services/whatsapp-pairing.js +196 -0
  184. package/dist/shared/ui-catalog-prompt.js +728 -0
  185. package/dist/subscription-routes.js +172 -0
  186. package/dist/terminal/links.js +19 -0
  187. package/dist/terminal/palette.js +14 -0
  188. package/dist/terminal/theme.js +25 -0
  189. package/dist/terminal-run-limits.js +24 -0
  190. package/dist/training-routes.js +158 -0
  191. package/dist/trajectory-routes.js +300 -0
  192. package/dist/trigger-routes.js +246 -0
  193. package/dist/triggers/action.js +218 -0
  194. package/dist/triggers/runtime.js +281 -0
  195. package/dist/triggers/scheduling.js +295 -0
  196. package/dist/triggers/types.js +5 -0
  197. package/dist/tui/components/assistant-message.js +76 -0
  198. package/dist/tui/components/chat-editor.js +34 -0
  199. package/dist/tui/components/embeddings-overlay.js +46 -0
  200. package/dist/tui/components/footer.js +60 -0
  201. package/dist/tui/components/index.js +15 -0
  202. package/dist/tui/components/modal-frame.js +45 -0
  203. package/dist/tui/components/modal-style.js +15 -0
  204. package/dist/tui/components/model-selector.js +70 -0
  205. package/dist/tui/components/pinned-chat-layout.js +46 -0
  206. package/dist/tui/components/plugins-endpoints-tab.js +196 -0
  207. package/dist/tui/components/plugins-installed-tab-view.js +69 -0
  208. package/dist/tui/components/plugins-installed-tab.js +319 -0
  209. package/dist/tui/components/plugins-overlay-catalog.js +81 -0
  210. package/dist/tui/components/plugins-overlay-data-api.js +21 -0
  211. package/dist/tui/components/plugins-overlay-data-shared.js +20 -0
  212. package/dist/tui/components/plugins-overlay-data.js +323 -0
  213. package/dist/tui/components/plugins-overlay.js +117 -0
  214. package/dist/tui/components/plugins-store-tab.js +148 -0
  215. package/dist/tui/components/settings-overlay.js +61 -0
  216. package/dist/tui/components/status-bar.js +64 -0
  217. package/dist/tui/components/tool-execution.js +68 -0
  218. package/dist/tui/components/user-message.js +22 -0
  219. package/dist/tui/eliza-tui-bridge.js +606 -0
  220. package/dist/tui/index.js +370 -0
  221. package/dist/tui/modal-presets.js +33 -0
  222. package/dist/tui/model-spec.js +46 -0
  223. package/dist/tui/sse-parser.js +78 -0
  224. package/dist/tui/theme.js +110 -0
  225. package/dist/tui/titlebar-spinner.js +62 -0
  226. package/dist/tui/tui-app.js +311 -0
  227. package/dist/tui/ws-client.js +215 -0
  228. package/dist/twitter-verify.js +134 -0
  229. package/dist/tx-service.js +108 -0
  230. package/dist/utils/exec-safety.js +17 -0
  231. package/dist/utils/globals.js +20 -0
  232. package/dist/utils/milady-root.js +61 -0
  233. package/dist/utils/number-parsing.js +37 -0
  234. package/dist/version-resolver.js +37 -0
  235. package/dist/version.js +7 -0
  236. package/dist/wallet-routes.js +266 -0
  237. package/dist/wallet.js +568 -0
  238. package/dist/whatsapp-routes.js +182 -0
  239. package/dist/zip-utils.js +109 -0
  240. package/milady.mjs +14 -0
  241. package/package.json +111 -0
@@ -0,0 +1,606 @@
1
+ import { miladyMarkdownTheme, tuiTheme } from "./theme.js";
2
+ import { AssistantMessageComponent } from "./components/assistant-message.js";
3
+ import { ToolExecutionComponent } from "./components/tool-execution.js";
4
+ import { UserMessageComponent } from "./components/user-message.js";
5
+ import "./components/index.js";
6
+ import { drainSseEvents, extractSseDataPayloads, parseConversationStreamPayload } from "./sse-parser.js";
7
+ import { ApiModeWsClient } from "./ws-client.js";
8
+ import process from "node:process";
9
+ import { ChannelType, EventType, createMessageMemory, stringToUuid } from "@elizaos/core";
10
+ import crypto from "node:crypto";
11
+ import { CancellableLoader, Spacer, Text } from "@mariozechner/pi-tui";
12
+
13
+ //#region src/tui/eliza-tui-bridge.ts
14
+ const TUI_USER_ID = stringToUuid("milady-tui-user");
15
+ var ElizaTUIBridge = class {
16
+ constructor(runtime, tui, opts = {}) {
17
+ this.runtime = runtime;
18
+ this.tui = tui;
19
+ this.isProcessing = false;
20
+ this.showThinking = process.env.MILADY_TUI_SHOW_THINKING === "1";
21
+ this.showStructuredResponse = process.env.MILADY_TUI_SHOW_STRUCTURED_RESPONSE === "1";
22
+ this.debugActionRouting = process.env.MILADY_TUI_DEBUG_ACTION_ROUTING === "1";
23
+ this.abortController = null;
24
+ this.streamedText = "";
25
+ this.thinkingText = "";
26
+ this.structuredThoughtText = "";
27
+ this.currentAssistant = null;
28
+ this.lastAssistantForTurn = null;
29
+ this.assistantFinalizedForTurn = false;
30
+ this.pendingRender = null;
31
+ this.pendingActions = /* @__PURE__ */ new Map();
32
+ this.allToolComponents = /* @__PURE__ */ new Set();
33
+ this.conversationId = null;
34
+ this.conversationRoomId = null;
35
+ this.conversationInitPromise = null;
36
+ this.apiWsClient = null;
37
+ this.pendingProactiveMessages = [];
38
+ this.lastCompletedAssistantText = "";
39
+ this.lastCompletedAssistantAt = 0;
40
+ this.seenProactiveMessageIds = /* @__PURE__ */ new Set();
41
+ this.seenProactiveMessageOrder = [];
42
+ this.proactiveMessageIdLimit = 128;
43
+ this.disposed = false;
44
+ const agentScope = String(this.runtime.agentId);
45
+ this.worldId = stringToUuid(`milady-tui-world:${agentScope}`);
46
+ this.roomId = stringToUuid(`milady-tui-room:${agentScope}`);
47
+ this.channelId = `milady-tui:${agentScope}`;
48
+ this.apiBaseUrl = opts.apiBaseUrl?.trim().replace(/\/+$/, "") || null;
49
+ this.apiTokenOverride = opts.apiToken;
50
+ this.conversationTitle = opts.conversationTitle?.trim() || "TUI Chat";
51
+ }
52
+ getAbortSignal() {
53
+ return this.abortController?.signal;
54
+ }
55
+ getIsProcessing() {
56
+ return this.isProcessing;
57
+ }
58
+ getPiTuiCompat() {
59
+ return this.tui.getTUI();
60
+ }
61
+ getActionEventRouteDecision(payload) {
62
+ const payloadRoomId = payload.roomId ? String(payload.roomId) : "";
63
+ const activeRoomId = this.apiBaseUrl ? this.conversationRoomId ?? "" : String(this.roomId);
64
+ if (!payloadRoomId) return {
65
+ shouldHandle: false,
66
+ reason: "missing_payload_room",
67
+ payloadRoomId,
68
+ activeRoomId
69
+ };
70
+ if (!this.apiBaseUrl) {
71
+ const shouldHandle = payloadRoomId === activeRoomId;
72
+ return {
73
+ shouldHandle,
74
+ reason: shouldHandle ? "runtime_room_match" : "runtime_room_mismatch",
75
+ payloadRoomId,
76
+ activeRoomId
77
+ };
78
+ }
79
+ if (!activeRoomId) return {
80
+ shouldHandle: false,
81
+ reason: "conversation_room_unresolved",
82
+ payloadRoomId,
83
+ activeRoomId
84
+ };
85
+ const shouldHandle = payloadRoomId === activeRoomId;
86
+ return {
87
+ shouldHandle,
88
+ reason: shouldHandle ? "conversation_room_match" : "conversation_room_mismatch",
89
+ payloadRoomId,
90
+ activeRoomId
91
+ };
92
+ }
93
+ logActionRoute(eventType, actionName, decision) {
94
+ if (!this.debugActionRouting) return;
95
+ const mode = this.apiBaseUrl ? "api" : "runtime";
96
+ const payloadRoom = decision.payloadRoomId || "(none)";
97
+ const activeRoom = decision.activeRoomId || "(unset)";
98
+ process.stderr.write(`[milady-tui] ${eventType} action=${actionName} mode=${mode} payloadRoom=${payloadRoom} activeRoom=${activeRoom} handled=${decision.shouldHandle} reason=${decision.reason}\n`);
99
+ }
100
+ async initialize() {
101
+ if (this.disposed) return;
102
+ if (!this.apiBaseUrl) {
103
+ await this.runtime.ensureWorldExists({
104
+ id: this.worldId,
105
+ name: "Milady TUI",
106
+ agentId: this.runtime.agentId
107
+ });
108
+ await this.runtime.ensureRoomExists({
109
+ id: this.roomId,
110
+ name: "Milady TUI",
111
+ type: ChannelType.DM,
112
+ source: "milady-tui",
113
+ worldId: this.worldId,
114
+ channelId: this.channelId,
115
+ metadata: { ownership: { ownerId: TUI_USER_ID } }
116
+ });
117
+ await this.runtime.ensureConnection({
118
+ entityId: TUI_USER_ID,
119
+ roomId: this.roomId,
120
+ worldId: this.worldId,
121
+ worldName: "Milady TUI",
122
+ userName: "User",
123
+ name: "User",
124
+ source: "milady-tui",
125
+ type: ChannelType.DM,
126
+ channelId: this.channelId,
127
+ metadata: { ownership: { ownerId: TUI_USER_ID } }
128
+ });
129
+ } else {
130
+ this.apiWsClient = new ApiModeWsClient({
131
+ apiBaseUrl: this.apiBaseUrl,
132
+ getAuthToken: () => this.getApiToken(),
133
+ onMessage: (data) => this.handleApiWsMessage(data),
134
+ onError: (error) => {
135
+ process.stderr.write(`[milady-tui] websocket error: ${error.message}\n`);
136
+ }
137
+ });
138
+ this.apiWsClient.connect();
139
+ }
140
+ const SKIP_ACTIONS = new Set([
141
+ "reply",
142
+ "respond",
143
+ "continue",
144
+ "ignore",
145
+ "none",
146
+ "wait",
147
+ "action"
148
+ ]);
149
+ this.runtime.registerEvent(EventType.ACTION_STARTED, async (payload) => {
150
+ const p = payload;
151
+ const actionName = p.content.actions?.[0] ?? "action";
152
+ const route = this.getActionEventRouteDecision(p);
153
+ if (!route.shouldHandle) {
154
+ this.logActionRoute("ACTION_STARTED", actionName, route);
155
+ return;
156
+ }
157
+ if (SKIP_ACTIONS.has(actionName.toLowerCase())) {
158
+ this.logActionRoute("ACTION_STARTED", actionName, {
159
+ ...route,
160
+ shouldHandle: false,
161
+ reason: "internal_action_skipped"
162
+ });
163
+ return;
164
+ }
165
+ this.logActionRoute("ACTION_STARTED", actionName, route);
166
+ const actionId = p.content.actionId ?? p.messageId ?? `${actionName}-${Date.now()}`;
167
+ const component = new ToolExecutionComponent(actionName, {}, this.getPiTuiCompat());
168
+ component.setExpanded(this.tui.getToolOutputExpanded());
169
+ this.pendingActions.set(actionId, component);
170
+ this.allToolComponents.add(component);
171
+ this.tui.addToChatContainer(component);
172
+ this.tui.requestRender();
173
+ });
174
+ this.runtime.registerEvent(EventType.ACTION_COMPLETED, async (payload) => {
175
+ const p = payload;
176
+ const actionName = p.content.actions?.[0] ?? "action";
177
+ const route = this.getActionEventRouteDecision(p);
178
+ if (!route.shouldHandle) {
179
+ this.logActionRoute("ACTION_COMPLETED", actionName, route);
180
+ return;
181
+ }
182
+ if (SKIP_ACTIONS.has(actionName.toLowerCase())) {
183
+ this.logActionRoute("ACTION_COMPLETED", actionName, {
184
+ ...route,
185
+ shouldHandle: false,
186
+ reason: "internal_action_skipped"
187
+ });
188
+ return;
189
+ }
190
+ this.logActionRoute("ACTION_COMPLETED", actionName, route);
191
+ let resolvedKey = p.content.actionId ?? p.messageId;
192
+ let component = resolvedKey ? this.pendingActions.get(resolvedKey) : void 0;
193
+ if (!component) {
194
+ const fallback = [...this.pendingActions.entries()].reverse().find(([, c]) => c.render(120)[0]?.includes(actionName));
195
+ if (fallback) {
196
+ resolvedKey = fallback[0];
197
+ component = fallback[1];
198
+ }
199
+ }
200
+ if (!component) return;
201
+ const isError = p.content.actionStatus === "failed";
202
+ const actionResult = p.content.actionResult;
203
+ const text = p.content.text ?? (actionResult ? JSON.stringify(actionResult, null, 2) : "");
204
+ component.updateResult({
205
+ text,
206
+ isError
207
+ });
208
+ if (resolvedKey) this.pendingActions.delete(resolvedKey);
209
+ this.tui.requestRender();
210
+ });
211
+ }
212
+ dispose() {
213
+ if (this.disposed) return;
214
+ this.disposed = true;
215
+ this.abortInFlight();
216
+ this.pendingProactiveMessages = [];
217
+ if (this.pendingRender) {
218
+ clearTimeout(this.pendingRender);
219
+ this.pendingRender = null;
220
+ }
221
+ this.apiWsClient?.close();
222
+ this.apiWsClient = null;
223
+ }
224
+ abortInFlight() {
225
+ if (this.abortController) this.abortController.abort();
226
+ }
227
+ setShowThinking(enabled) {
228
+ this.showThinking = enabled;
229
+ }
230
+ setToolOutputExpanded(expanded) {
231
+ for (const component of this.allToolComponents) component.setExpanded(expanded);
232
+ this.tui.requestRender();
233
+ }
234
+ async handleUserInput(text) {
235
+ if (this.isProcessing) return;
236
+ this.isProcessing = true;
237
+ this.tui.setBusy(true);
238
+ this.abortController = new AbortController();
239
+ this.streamedText = "";
240
+ this.thinkingText = "";
241
+ this.structuredThoughtText = "";
242
+ this.currentAssistant = null;
243
+ this.lastAssistantForTurn = null;
244
+ this.assistantFinalizedForTurn = false;
245
+ try {
246
+ this.tui.addToChatContainer(new UserMessageComponent(text));
247
+ const loader = new CancellableLoader(this.getPiTuiCompat(), (spinner) => tuiTheme.info(spinner), (msg) => tuiTheme.muted(msg), "Thinking… (Esc to cancel)");
248
+ loader.onAbort = () => {
249
+ this.abortInFlight();
250
+ };
251
+ this.tui.setEphemeralStatus(loader);
252
+ this.tui.getStatusBar().update({ isStreaming: true });
253
+ if (this.apiBaseUrl) await this.handleUserInputViaApi(text);
254
+ else await this.handleUserInputViaRuntime(text);
255
+ } catch (error) {
256
+ if (this.abortController?.signal.aborted) return;
257
+ const errText = error instanceof Error ? error.message : String(error);
258
+ this.tui.addToChatContainer(new Text(tuiTheme.error(`Error: ${errText}`), 1, 0));
259
+ this.tui.addToChatContainer(new Spacer(1));
260
+ } finally {
261
+ this.tui.clearEphemeralStatus();
262
+ this.tui.getStatusBar().update({ isStreaming: false });
263
+ this.tui.setBusy(false);
264
+ this.abortController = null;
265
+ this.isProcessing = false;
266
+ this.flushPendingProactiveMessages();
267
+ }
268
+ }
269
+ onStreamEvent(event) {
270
+ if (this.apiBaseUrl) return;
271
+ switch (event.type) {
272
+ case "token":
273
+ if (!event.text) return;
274
+ this.tui.clearEphemeralStatus();
275
+ this.tui.getStatusBar().update({ isStreaming: true });
276
+ this.streamedText += event.text;
277
+ this.ensureAssistantComponent();
278
+ this.scheduleAssistantUpdate();
279
+ break;
280
+ case "thinking":
281
+ if (!event.text) return;
282
+ this.thinkingText += event.text;
283
+ this.currentAssistant?.updateThinking(this.getCombinedThinkingText());
284
+ this.scheduleAssistantUpdate();
285
+ break;
286
+ case "usage":
287
+ if (!event.usage) return;
288
+ this.tui.getStatusBar().update({
289
+ inputTokens: event.usage.inputTokens,
290
+ outputTokens: event.usage.outputTokens,
291
+ isStreaming: false
292
+ });
293
+ this.tui.requestRender();
294
+ break;
295
+ case "done":
296
+ this.tui.clearEphemeralStatus();
297
+ this.tui.getStatusBar().update({ isStreaming: false });
298
+ this.updateAssistantFromText();
299
+ this.finalizeAssistantForTurn();
300
+ this.tui.requestRender();
301
+ break;
302
+ case "error": {
303
+ this.tui.clearEphemeralStatus();
304
+ this.tui.getStatusBar().update({ isStreaming: false });
305
+ const msg = event.error ?? "Model error";
306
+ this.tui.addToChatContainer(new Text(tuiTheme.error(`Error: ${msg}`), 1, 0));
307
+ this.tui.addToChatContainer(new Spacer(1));
308
+ this.tui.requestRender();
309
+ break;
310
+ }
311
+ }
312
+ }
313
+ async handleUserInputViaRuntime(text) {
314
+ const message = createMessageMemory({
315
+ id: crypto.randomUUID(),
316
+ entityId: TUI_USER_ID,
317
+ agentId: this.runtime.agentId,
318
+ roomId: this.roomId,
319
+ content: {
320
+ text,
321
+ source: "milady-tui",
322
+ channelType: ChannelType.DM
323
+ }
324
+ });
325
+ if (!this.runtime.messageService) throw new Error("runtime.messageService is not available");
326
+ await this.runtime.messageService.handleMessage(this.runtime, message, async (response) => {
327
+ if (response.text) {
328
+ this.streamedText = response.text;
329
+ if (!(this.currentAssistant ?? this.lastAssistantForTurn ?? null)) this.ensureAssistantComponent();
330
+ this.updateAssistantFromText();
331
+ this.finalizeAssistantForTurn();
332
+ }
333
+ return [];
334
+ }, { abortSignal: this.abortController?.signal });
335
+ }
336
+ async handleUserInputViaApi(text) {
337
+ const conversationId = await this.ensureConversationId();
338
+ await this.streamConversationMessage(conversationId, text, this.abortController?.signal);
339
+ if (!this.currentAssistant && !this.lastAssistantForTurn && this.streamedText.trim().length > 0) this.ensureAssistantComponent();
340
+ this.updateAssistantFromText();
341
+ this.finalizeAssistantForTurn();
342
+ this.tui.requestRender();
343
+ }
344
+ async ensureConversationId() {
345
+ if (this.conversationId) {
346
+ this.apiWsClient?.setActiveConversationId(this.conversationId);
347
+ return this.conversationId;
348
+ }
349
+ if (!this.conversationInitPromise) this.conversationInitPromise = this.resolveConversationId().finally(() => {
350
+ this.conversationInitPromise = null;
351
+ });
352
+ const resolved = await this.conversationInitPromise;
353
+ this.conversationId = resolved;
354
+ this.apiWsClient?.setActiveConversationId(resolved);
355
+ return resolved;
356
+ }
357
+ async resolveConversationId() {
358
+ const list = await this.apiFetchJson("/api/conversations");
359
+ const title = this.conversationTitle.trim().toLowerCase();
360
+ const existing = [...list.conversations ?? []].filter((c) => typeof c?.id === "string" && c.id.trim().length > 0).filter((c) => c.title?.trim().toLowerCase() === title && typeof c.roomId === "string" && c.roomId.trim().length > 0).sort((a, b) => this.parseTimestamp(b.updatedAt) - this.parseTimestamp(a.updatedAt))[0];
361
+ if (existing?.id) {
362
+ this.conversationRoomId = existing.roomId?.trim() || null;
363
+ return existing.id;
364
+ }
365
+ const created = await this.apiFetchJson("/api/conversations", {
366
+ method: "POST",
367
+ body: JSON.stringify({ title: this.conversationTitle })
368
+ });
369
+ const createdId = created.conversation?.id?.trim();
370
+ if (!createdId) throw new Error("Failed to create TUI conversation");
371
+ this.conversationRoomId = created.conversation?.roomId?.trim() || null;
372
+ return createdId;
373
+ }
374
+ parseTimestamp(value) {
375
+ const ms = Date.parse(value);
376
+ return Number.isFinite(ms) ? ms : 0;
377
+ }
378
+ async streamConversationMessage(conversationId, text, signal) {
379
+ const res = await this.apiFetch(`/api/conversations/${encodeURIComponent(conversationId)}/messages/stream`, {
380
+ method: "POST",
381
+ headers: { Accept: "text/event-stream" },
382
+ body: JSON.stringify({ text }),
383
+ signal
384
+ });
385
+ if (!res.body) throw new Error("Streaming not supported by this runtime");
386
+ const reader = res.body.getReader();
387
+ const decoder = new TextDecoder();
388
+ let buffer = "";
389
+ let fullText = "";
390
+ let doneText = null;
391
+ const parsePayload = (payload) => {
392
+ const parsed = parseConversationStreamPayload(payload);
393
+ if (!parsed) return;
394
+ if (parsed.type === "token") {
395
+ const chunk = parsed.text ?? "";
396
+ if (!chunk) return;
397
+ this.tui.clearEphemeralStatus();
398
+ this.tui.getStatusBar().update({ isStreaming: true });
399
+ fullText += chunk;
400
+ this.streamedText += chunk;
401
+ this.ensureAssistantComponent();
402
+ this.scheduleAssistantUpdate();
403
+ return;
404
+ }
405
+ if (parsed.type === "done") {
406
+ if (typeof parsed.fullText === "string") doneText = parsed.fullText;
407
+ return;
408
+ }
409
+ if (parsed.type === "error") throw new Error(parsed.message ?? "generation failed");
410
+ if (parsed.text) {
411
+ this.tui.clearEphemeralStatus();
412
+ this.tui.getStatusBar().update({ isStreaming: true });
413
+ fullText += parsed.text;
414
+ this.streamedText += parsed.text;
415
+ this.ensureAssistantComponent();
416
+ this.scheduleAssistantUpdate();
417
+ }
418
+ };
419
+ while (true) {
420
+ const { done, value } = await reader.read();
421
+ if (done) break;
422
+ buffer += decoder.decode(value, { stream: true });
423
+ const drained = drainSseEvents(buffer);
424
+ buffer = drained.remaining;
425
+ for (const rawEvent of drained.events) for (const payload of extractSseDataPayloads(rawEvent)) parsePayload(payload);
426
+ }
427
+ if (buffer.trim()) for (const payload of extractSseDataPayloads(buffer)) parsePayload(payload);
428
+ if (typeof doneText === "string") this.streamedText = doneText;
429
+ else if (!this.streamedText && fullText) this.streamedText = fullText;
430
+ }
431
+ getApiToken() {
432
+ if (this.apiTokenOverride !== void 0) return this.apiTokenOverride?.trim() || null;
433
+ return process.env.MILADY_API_TOKEN?.trim() || null;
434
+ }
435
+ handleApiWsMessage(data) {
436
+ if (this.disposed) return;
437
+ if (data.type !== "proactive-message") return;
438
+ const conversationId = typeof data.conversationId === "string" ? data.conversationId.trim() : "";
439
+ if (!conversationId || !this.conversationId) return;
440
+ if (conversationId !== this.conversationId) return;
441
+ const rawMessage = data.message;
442
+ if (!rawMessage || typeof rawMessage !== "object" || Array.isArray(rawMessage)) return;
443
+ const message = rawMessage;
444
+ const messageId = typeof message.id === "string" ? message.id.trim() : "";
445
+ if (messageId) {
446
+ if (this.seenProactiveMessageIds.has(messageId)) return;
447
+ this.rememberProactiveMessageId(messageId);
448
+ }
449
+ const text = typeof message.text === "string" ? message.text.trim() : "";
450
+ if (!text) return;
451
+ if (this.isLikelyDuplicateAssistantText(text)) return;
452
+ if (this.isProcessing) {
453
+ this.queuePendingProactiveMessage(text);
454
+ return;
455
+ }
456
+ this.renderProactiveAssistantMessage(text);
457
+ }
458
+ rememberProactiveMessageId(id) {
459
+ this.seenProactiveMessageIds.add(id);
460
+ this.seenProactiveMessageOrder.push(id);
461
+ if (this.seenProactiveMessageOrder.length <= this.proactiveMessageIdLimit) return;
462
+ const oldest = this.seenProactiveMessageOrder.shift();
463
+ if (oldest) this.seenProactiveMessageIds.delete(oldest);
464
+ }
465
+ queuePendingProactiveMessage(text) {
466
+ this.pendingProactiveMessages.push(text);
467
+ if (this.pendingProactiveMessages.length > 32) this.pendingProactiveMessages.shift();
468
+ }
469
+ flushPendingProactiveMessages() {
470
+ if (this.disposed) return;
471
+ if (this.pendingProactiveMessages.length < 1) return;
472
+ const pending = this.pendingProactiveMessages;
473
+ this.pendingProactiveMessages = [];
474
+ for (const text of pending) {
475
+ if (this.isLikelyDuplicateAssistantText(text)) continue;
476
+ this.renderProactiveAssistantMessage(text);
477
+ }
478
+ }
479
+ isLikelyDuplicateAssistantText(text) {
480
+ const normalized = text.trim();
481
+ if (!normalized) return true;
482
+ if (this.isProcessing && this.streamedText.trim() === normalized) return true;
483
+ if (this.lastCompletedAssistantText === normalized && Date.now() - this.lastCompletedAssistantAt < 1500) return true;
484
+ return false;
485
+ }
486
+ renderProactiveAssistantMessage(text) {
487
+ if (this.disposed) return;
488
+ const component = new AssistantMessageComponent(this.showThinking, miladyMarkdownTheme, this.runtime.character?.name ?? "milady");
489
+ component.updateContent(text);
490
+ component.finalize();
491
+ this.tui.addToChatContainer(component);
492
+ this.tui.requestRender();
493
+ this.lastCompletedAssistantText = text.trim();
494
+ this.lastCompletedAssistantAt = Date.now();
495
+ }
496
+ async apiFetch(path, init) {
497
+ if (!this.apiBaseUrl) throw new Error("API transport is not configured");
498
+ const headers = new Headers(init?.headers);
499
+ if (init?.body != null && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
500
+ const token = this.getApiToken();
501
+ if (token && !headers.has("Authorization")) headers.set("Authorization", `Bearer ${token}`);
502
+ const res = await fetch(`${this.apiBaseUrl}${path}`, {
503
+ ...init,
504
+ headers
505
+ });
506
+ if (!res.ok) {
507
+ const message = await this.readApiError(res);
508
+ throw new Error(message);
509
+ }
510
+ return res;
511
+ }
512
+ async apiFetchJson(path, init) {
513
+ return (await this.apiFetch(path, init)).json();
514
+ }
515
+ async readApiError(res) {
516
+ try {
517
+ const body = await res.json();
518
+ if (typeof body.error === "string" && body.error.trim()) return body.error;
519
+ } catch {}
520
+ try {
521
+ const text = await res.text();
522
+ if (text.trim()) return text.trim();
523
+ } catch {}
524
+ return `HTTP ${res.status}`;
525
+ }
526
+ ensureAssistantComponent() {
527
+ if (this.currentAssistant) return;
528
+ this.currentAssistant = new AssistantMessageComponent(this.showThinking, miladyMarkdownTheme, this.runtime.character?.name ?? "milady");
529
+ this.lastAssistantForTurn = this.currentAssistant;
530
+ this.tui.addToChatContainer(this.currentAssistant);
531
+ this.tui.requestRender();
532
+ }
533
+ scheduleAssistantUpdate() {
534
+ if (this.pendingRender) return;
535
+ this.pendingRender = setTimeout(() => {
536
+ this.pendingRender = null;
537
+ this.updateAssistantFromText();
538
+ this.tui.requestRender();
539
+ }, 33);
540
+ }
541
+ finalizeAssistantForTurn() {
542
+ if (this.assistantFinalizedForTurn) return;
543
+ const component = this.currentAssistant ?? this.lastAssistantForTurn;
544
+ if (!component) return;
545
+ if (this.pendingRender) {
546
+ clearTimeout(this.pendingRender);
547
+ this.pendingRender = null;
548
+ }
549
+ component.finalize();
550
+ const completedText = this.normalizeAssistantText(this.streamedText).text.trim();
551
+ if (completedText) {
552
+ this.lastCompletedAssistantText = completedText;
553
+ this.lastCompletedAssistantAt = Date.now();
554
+ }
555
+ this.streamedText = "";
556
+ this.currentAssistant = null;
557
+ this.assistantFinalizedForTurn = true;
558
+ }
559
+ updateAssistantFromText() {
560
+ const component = this.currentAssistant ?? this.lastAssistantForTurn;
561
+ if (!component) return;
562
+ const normalized = this.normalizeAssistantText(this.streamedText);
563
+ this.structuredThoughtText = normalized.thoughtText;
564
+ component.updateThinking(this.getCombinedThinkingText());
565
+ component.updateContent(normalized.text);
566
+ }
567
+ getCombinedThinkingText() {
568
+ return [this.thinkingText, this.structuredThoughtText].map((p) => p.trim()).filter((p) => p.length > 0).join("\n");
569
+ }
570
+ normalizeAssistantText(raw) {
571
+ if (this.showStructuredResponse) return {
572
+ text: raw,
573
+ thoughtText: ""
574
+ };
575
+ if (raw.indexOf("<response>") < 0) return {
576
+ text: raw,
577
+ thoughtText: ""
578
+ };
579
+ const thought = this.extractXmlTag(raw, "thought") ?? "";
580
+ const text = this.extractXmlTag(raw, "text", { allowPartial: true });
581
+ if (text !== null) return {
582
+ text: text.trimStart(),
583
+ thoughtText: thought.trim()
584
+ };
585
+ return {
586
+ text: "",
587
+ thoughtText: thought.trim()
588
+ };
589
+ }
590
+ extractXmlTag(raw, tag, opts) {
591
+ const open = `<${tag}>`;
592
+ const close = `</${tag}>`;
593
+ const start = raw.indexOf(open);
594
+ if (start < 0) return null;
595
+ const contentStart = start + open.length;
596
+ const end = raw.indexOf(close, contentStart);
597
+ if (end < 0) {
598
+ if (opts?.allowPartial) return raw.slice(contentStart);
599
+ return null;
600
+ }
601
+ return raw.slice(contentStart, end);
602
+ }
603
+ };
604
+
605
+ //#endregion
606
+ export { ElizaTUIBridge };