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,370 @@
1
+ import { loadMiladyConfig, saveMiladyConfig } from "../config/config.js";
2
+ import { DEFAULT_MODELS_DIR, ensureModel } from "../runtime/embedding-manager-support.js";
3
+ import { EMBEDDING_PRESETS } from "../runtime/embedding-presets.js";
4
+ import { MiladyEmbeddingManager } from "../runtime/embedding-manager.js";
5
+ import { getEmbeddingState } from "../runtime/embedding-state.js";
6
+ import { ElizaTUIBridge } from "./eliza-tui-bridge.js";
7
+ import { resolveTuiModelSpec } from "./model-spec.js";
8
+ import { MiladyTUI } from "./tui-app.js";
9
+ import process from "node:process";
10
+ import path from "node:path";
11
+ import { stringToUuid } from "@elizaos/core";
12
+ import fs from "node:fs";
13
+ import { createPiCredentialProvider, getPiModel, parseModelSpec, registerPiAiModelHandler } from "@elizaos/plugin-pi-ai";
14
+ import { Text } from "@mariozechner/pi-tui";
15
+
16
+ //#region src/tui/index.ts
17
+ function formatDownloadSize(mb) {
18
+ return mb >= 1e3 ? `${(mb / 1e3).toFixed(1)}GB` : `${mb}MB`;
19
+ }
20
+ function isModelDownloaded(filename) {
21
+ return fs.existsSync(path.join(DEFAULT_MODELS_DIR, filename));
22
+ }
23
+ const VALID_TIERS = new Set([
24
+ "fallback",
25
+ "standard",
26
+ "performance"
27
+ ]);
28
+ function getEmbeddingOptions() {
29
+ const state = getEmbeddingState();
30
+ if (!state) return [];
31
+ return [
32
+ "fallback",
33
+ "standard",
34
+ "performance"
35
+ ].map((tier) => {
36
+ const preset = EMBEDDING_PRESETS[tier];
37
+ return {
38
+ tier,
39
+ label: preset.label,
40
+ dimensions: preset.dimensions,
41
+ downloaded: isModelDownloaded(preset.model),
42
+ active: state.preset?.tier === tier
43
+ };
44
+ });
45
+ }
46
+ async function switchEmbeddingTier(tier, tui) {
47
+ const state = getEmbeddingState();
48
+ if (!state) {
49
+ tui.addToChatContainer(new Text("Embedding manager not available.", 1, 0));
50
+ return;
51
+ }
52
+ const preset = EMBEDDING_PRESETS[tier];
53
+ if (state.preset?.tier === tier) {
54
+ tui.addToChatContainer(new Text(`Already using ${preset.label} (${preset.model})`, 1, 0));
55
+ return;
56
+ }
57
+ if (preset.dimensions !== state.dimensions) tui.addToChatContainer(new Text(`⚠ Dimensions changing (${state.dimensions} → ${preset.dimensions}). Existing memory embeddings will be re-indexed on next access.`, 1, 0));
58
+ if (!isModelDownloaded(preset.model)) {
59
+ tui.addToChatContainer(new Text(`Downloading ${preset.model} (${formatDownloadSize(preset.downloadSizeMB)})…`, 1, 0));
60
+ try {
61
+ await ensureModel(DEFAULT_MODELS_DIR, preset.modelRepo, preset.model);
62
+ } catch (err) {
63
+ const msg = err instanceof Error ? err.message : String(err);
64
+ tui.addToChatContainer(new Text(`Download failed: ${msg}`, 1, 0));
65
+ return;
66
+ }
67
+ }
68
+ try {
69
+ await state.manager.dispose();
70
+ } catch {}
71
+ state.manager = new MiladyEmbeddingManager({
72
+ model: preset.model,
73
+ modelRepo: preset.modelRepo,
74
+ dimensions: preset.dimensions,
75
+ gpuLayers: preset.gpuLayers
76
+ });
77
+ state.preset = preset;
78
+ state.dimensions = preset.dimensions;
79
+ try {
80
+ const cfg = loadMiladyConfig();
81
+ cfg.embedding = {
82
+ ...cfg.embedding,
83
+ model: preset.model,
84
+ modelRepo: preset.modelRepo,
85
+ dimensions: preset.dimensions,
86
+ gpuLayers: preset.gpuLayers
87
+ };
88
+ saveMiladyConfig(cfg);
89
+ } catch (err) {
90
+ const msg = err instanceof Error ? err.message : String(err);
91
+ tui.addToChatContainer(new Text(`Warning: could not save config: ${msg}`, 1, 0));
92
+ }
93
+ tui.addToChatContainer(new Text(`Switched embedding model to ${preset.label} (${preset.model}, ${preset.dimensions} dims)`, 1, 0));
94
+ }
95
+ function createApiModeRuntimeStub(agentName) {
96
+ const noopAsync = async () => {};
97
+ return {
98
+ agentId: stringToUuid(`milady-tui-api:${agentName}`),
99
+ character: { name: agentName },
100
+ getSetting: () => void 0,
101
+ setSetting: () => {},
102
+ registerEvent: () => {},
103
+ ensureWorldExists: noopAsync,
104
+ ensureRoomExists: noopAsync,
105
+ ensureConnection: noopAsync,
106
+ stop: noopAsync
107
+ };
108
+ }
109
+ async function launchTUI(runtime, options = {}) {
110
+ const apiMode = Boolean(options.apiBaseUrl);
111
+ const piCreds = await createPiCredentialProvider();
112
+ const miladyConfig = loadMiladyConfig();
113
+ const fallbackAgentName = miladyConfig.agents?.list?.[0]?.name ?? "milady";
114
+ if (!runtime && !apiMode) throw new Error("Runtime is required when API mode is not configured");
115
+ const runtimeRef = runtime ?? createApiModeRuntimeStub(fallbackAgentName);
116
+ const configPrimaryModel = miladyConfig.agents?.defaults?.model?.primary;
117
+ const configEnv = miladyConfig.env;
118
+ const configPiAiModelSpec = configEnv?.vars?.PI_AI_MODEL_SPEC ?? (typeof configEnv?.PI_AI_MODEL_SPEC === "string" ? configEnv.PI_AI_MODEL_SPEC : void 0);
119
+ const runtimeModelProvider = runtimeRef.getSetting("MODEL_PROVIDER");
120
+ const { provider, id } = parseModelSpec(resolveTuiModelSpec({
121
+ modelOverride: options.modelOverride,
122
+ configPrimaryModelSpec: configPrimaryModel,
123
+ configPiAiModelSpec,
124
+ runtimeModelSpec: runtimeModelProvider,
125
+ piDefaultModelSpec: await piCreds.getDefaultModelSpec(),
126
+ hasCredentials: (provider) => piCreds.hasCredentials(provider)
127
+ }));
128
+ const largeModel = getPiModel(provider, id);
129
+ const smallModel = largeModel;
130
+ const tui = new MiladyTUI({
131
+ runtime: runtimeRef,
132
+ apiBaseUrl: options.apiBaseUrl,
133
+ modelRegistry: { authStorage: {
134
+ getApiKey: (provider) => piCreds.getApiKey(provider),
135
+ get: async (_provider) => void 0
136
+ } }
137
+ });
138
+ const bridge = new ElizaTUIBridge(runtimeRef, tui, {
139
+ apiBaseUrl: options.apiBaseUrl,
140
+ apiToken: options.apiToken
141
+ });
142
+ const controller = apiMode ? null : registerPiAiModelHandler(runtimeRef, {
143
+ largeModel,
144
+ smallModel,
145
+ onStreamEvent: (event) => bridge.onStreamEvent(event),
146
+ getAbortSignal: () => bridge.getAbortSignal(),
147
+ priority: 2e4,
148
+ getApiKey: (p) => piCreds.getApiKey(p)
149
+ });
150
+ if (controller) tui.getStatusBar().update({
151
+ modelId: controller.getLargeModel().id,
152
+ modelProvider: controller.getLargeModel().provider
153
+ });
154
+ else tui.getStatusBar().update({
155
+ modelId: "backend",
156
+ modelProvider: "api"
157
+ });
158
+ const switchModel = (model) => {
159
+ if (!controller) {
160
+ tui.addToChatContainer(new Text("Model switching is managed by the connected API runtime in API mode.", 1, 0));
161
+ return;
162
+ }
163
+ controller.setLargeModel(model);
164
+ controller.setSmallModel(model);
165
+ runtimeRef.setSetting("MODEL_PROVIDER", `${model.provider}/${model.id}`);
166
+ tui.getStatusBar().update({
167
+ modelId: model.id,
168
+ modelProvider: model.provider
169
+ });
170
+ if (!piCreds.hasCredentials(model.provider)) tui.addToChatContainer(new Text(`Warning: no credentials found for provider "${model.provider}" (neither Milady env nor pi auth). Model calls may fail.`, 1, 0));
171
+ tui.addToChatContainer(new Text(`Switched model to ${model.provider}/${model.id}`, 1, 0));
172
+ };
173
+ tui.setOnSubmit(async (text) => {
174
+ if (text.startsWith("/")) {
175
+ const [cmdRaw, ...args] = text.slice(1).trim().split(/\s+/);
176
+ const cmd = (cmdRaw ?? "").toLowerCase();
177
+ const argText = args.join(" ").trim();
178
+ try {
179
+ if (cmd === "model" || cmd === "models") {
180
+ if (apiMode) {
181
+ tui.addToChatContainer(new Text("Model selection is managed by the connected API runtime in API mode.", 1, 0));
182
+ return;
183
+ }
184
+ if (!argText) {
185
+ tui.openModelSelector();
186
+ return;
187
+ }
188
+ const { provider: p, id: m } = parseModelSpec(argText);
189
+ switchModel(getPiModel(p, m));
190
+ return;
191
+ }
192
+ if (cmd === "embeddings") {
193
+ if (apiMode) {
194
+ tui.addToChatContainer(new Text("Embedding controls are only available in local runtime mode.", 1, 0));
195
+ return;
196
+ }
197
+ if (!argText) {
198
+ tui.openEmbeddings();
199
+ return;
200
+ }
201
+ const tier = argText.toLowerCase();
202
+ if (!VALID_TIERS.has(tier)) {
203
+ tui.addToChatContainer(new Text(`Unknown tier "${argText}". Use: fallback, standard, or performance`, 1, 0));
204
+ return;
205
+ }
206
+ await switchEmbeddingTier(tier, tui);
207
+ return;
208
+ }
209
+ if (cmd === "knowledge" || cmd === "ctx") {
210
+ if (apiMode) {
211
+ tui.addToChatContainer(new Text("Knowledge enrichment settings are managed by the connected API runtime in API mode.", 1, 0));
212
+ return;
213
+ }
214
+ const cfg = loadMiladyConfig();
215
+ const ctxEnabled = cfg.knowledge?.contextualEnrichment === true;
216
+ if (!argText) {
217
+ const embState = getEmbeddingState();
218
+ const embModel = embState?.preset ? `${embState.preset.model} (local, ${embState.preset.dimensions}d)` : "unknown";
219
+ if (ctxEnabled) {
220
+ const lm = controller?.getLargeModel();
221
+ const cloudModel = lm ? `${lm.provider}/${lm.id} (via pi-ai)` : "unknown (controller unavailable)";
222
+ tui.addToChatContainer(new Text([
223
+ "Knowledge Enrichment: ON",
224
+ ` Cloud model: ${cloudModel}`,
225
+ ` Embedding model: ${embModel}`,
226
+ ` Docs path: ${cfg.knowledge?.docsPath ?? "./docs"}`
227
+ ].join("\n"), 1, 0));
228
+ } else tui.addToChatContainer(new Text([
229
+ "Knowledge Enrichment: OFF",
230
+ ` Embedding model: ${embModel}`,
231
+ " Enable with: /knowledge on"
232
+ ].join("\n"), 1, 0));
233
+ return;
234
+ }
235
+ const action = argText.toLowerCase();
236
+ if (action === "on") {
237
+ if (ctxEnabled) {
238
+ tui.addToChatContainer(new Text("Knowledge enrichment is already enabled.", 1, 0));
239
+ return;
240
+ }
241
+ cfg.knowledge = {
242
+ ...cfg.knowledge,
243
+ contextualEnrichment: true
244
+ };
245
+ try {
246
+ saveMiladyConfig(cfg);
247
+ } catch (err) {
248
+ const msg = err instanceof Error ? err.message : String(err);
249
+ tui.addToChatContainer(new Text(`Could not save config: ${msg}`, 1, 0));
250
+ return;
251
+ }
252
+ runtimeRef.setSetting("CTX_KNOWLEDGE_ENABLED", "true");
253
+ tui.addToChatContainer(new Text("Knowledge enrichment enabled. Takes effect on next document ingestion.\nDocument text will be sent to your cloud provider for enrichment; embeddings stay local.", 1, 0));
254
+ return;
255
+ }
256
+ if (action === "off") {
257
+ if (!ctxEnabled) {
258
+ tui.addToChatContainer(new Text("Knowledge enrichment is already disabled.", 1, 0));
259
+ return;
260
+ }
261
+ cfg.knowledge = {
262
+ ...cfg.knowledge,
263
+ contextualEnrichment: false
264
+ };
265
+ try {
266
+ saveMiladyConfig(cfg);
267
+ } catch (err) {
268
+ const msg = err instanceof Error ? err.message : String(err);
269
+ tui.addToChatContainer(new Text(`Could not save config: ${msg}`, 1, 0));
270
+ return;
271
+ }
272
+ runtimeRef.setSetting("CTX_KNOWLEDGE_ENABLED", null);
273
+ tui.addToChatContainer(new Text("Knowledge enrichment disabled. Existing enriched chunks are not affected.", 1, 0));
274
+ return;
275
+ }
276
+ tui.addToChatContainer(new Text("Usage: /knowledge [on|off] — toggle contextual enrichment", 1, 0));
277
+ return;
278
+ }
279
+ if (cmd === "help") {
280
+ tui.addToChatContainer(new Text([
281
+ "Commands:",
282
+ " /model open model selector",
283
+ " /model <p/id> switch model (e.g. anthropic/claude-sonnet-4-20250514)",
284
+ " /embeddings open embedding model popup",
285
+ " /embeddings <t> switch embedding (fallback|standard|performance)",
286
+ " /knowledge show knowledge enrichment status",
287
+ " /knowledge on enable contextual enrichment (cloud LLM + local embeddings)",
288
+ " /knowledge off disable contextual enrichment",
289
+ " /clear clear chat",
290
+ " /settings open settings panel",
291
+ " /plugins open plugin manager",
292
+ " /exit quit"
293
+ ].join("\n"), 1, 0));
294
+ return;
295
+ }
296
+ if (cmd === "clear") {
297
+ tui.clearChat();
298
+ return;
299
+ }
300
+ if (cmd === "settings") {
301
+ tui.openSettings();
302
+ return;
303
+ }
304
+ if (cmd === "plugins") {
305
+ tui.openPlugins();
306
+ return;
307
+ }
308
+ if (cmd === "exit" || cmd === "quit") {
309
+ bridge.dispose();
310
+ await tui.stop();
311
+ await runtimeRef.stop();
312
+ process.exit(0);
313
+ }
314
+ tui.addToChatContainer(new Text(`Unknown command: /${cmd}. Try /help`, 1, 0));
315
+ return;
316
+ } catch (err) {
317
+ const msg = err instanceof Error ? err.message : String(err);
318
+ tui.addToChatContainer(new Text(`Command error: ${msg}`, 1, 0));
319
+ return;
320
+ }
321
+ }
322
+ await bridge.handleUserInput(text);
323
+ });
324
+ tui.setOnToggleToolExpand((expanded) => bridge.setToolOutputExpanded(expanded));
325
+ tui.setOnToggleThinking((enabled) => bridge.setShowThinking(enabled));
326
+ tui.setOnCtrlC(() => {
327
+ if (bridge.getIsProcessing()) {
328
+ bridge.abortInFlight();
329
+ return;
330
+ }
331
+ (async () => {
332
+ try {
333
+ bridge.dispose();
334
+ await tui.stop();
335
+ } finally {
336
+ await runtimeRef.stop();
337
+ process.exit(0);
338
+ }
339
+ })();
340
+ });
341
+ if (!apiMode && controller) {
342
+ tui.setModelSelectorHandlers({
343
+ getCurrentModel: () => controller.getLargeModel(),
344
+ hasCredentials: (provider) => piCreds.hasCredentials(provider),
345
+ onSelectModel: (model) => {
346
+ try {
347
+ switchModel(model);
348
+ } catch (err) {
349
+ const msg = err instanceof Error ? err.message : String(err);
350
+ tui.addToChatContainer(new Text(`Model switch error: ${msg}`, 1, 0));
351
+ }
352
+ }
353
+ });
354
+ tui.setEmbeddingHandlers({
355
+ getOptions: () => getEmbeddingOptions(),
356
+ onSelectTier: async (tier) => {
357
+ await switchEmbeddingTier(tier, tui);
358
+ }
359
+ });
360
+ }
361
+ await bridge.initialize();
362
+ try {
363
+ await tui.start();
364
+ } finally {
365
+ bridge.dispose();
366
+ }
367
+ }
368
+
369
+ //#endregion
370
+ export { launchTUI };
@@ -0,0 +1,33 @@
1
+ //#region src/tui/modal-presets.ts
2
+ /**
3
+ * Shared popup sizing presets for a consistent overlay rhythm.
4
+ */
5
+ const MODAL_PRESETS = {
6
+ compact: {
7
+ anchor: "center",
8
+ width: "50%",
9
+ maxHeight: "50%",
10
+ margin: 2
11
+ },
12
+ standard: {
13
+ anchor: "center",
14
+ width: "60%",
15
+ maxHeight: "70%",
16
+ margin: 2
17
+ },
18
+ wide: {
19
+ anchor: "center",
20
+ width: "80%",
21
+ maxHeight: "85%",
22
+ margin: 2
23
+ },
24
+ xwide: {
25
+ anchor: "center",
26
+ width: "90%",
27
+ maxHeight: "80%",
28
+ margin: 2
29
+ }
30
+ };
31
+
32
+ //#endregion
33
+ export { MODAL_PRESETS };
@@ -0,0 +1,46 @@
1
+ import { DEFAULT_PI_MODEL_SPEC, parseModelSpec } from "@elizaos/plugin-pi-ai";
2
+
3
+ //#region src/tui/model-spec.ts
4
+ function toValidModelSpec(spec) {
5
+ if (!spec) return void 0;
6
+ const normalized = spec.trim();
7
+ if (!normalized) return void 0;
8
+ try {
9
+ parseModelSpec(normalized);
10
+ return normalized;
11
+ } catch {
12
+ return;
13
+ }
14
+ }
15
+ /**
16
+ * Select the model spec used by the TUI pi-ai bridge.
17
+ *
18
+ * Priority:
19
+ * 1) explicit CLI override (--model)
20
+ * 2) milady config primary model (agents.defaults.model.primary)
21
+ * 3) milady config PI_AI_MODEL_SPEC
22
+ * 4) runtime MODEL_PROVIDER
23
+ * 5) pi settings default (settings.json)
24
+ * 6) built-in safe default
25
+ *
26
+ * Candidate specs are only used when credentials exist for the provider.
27
+ */
28
+ function resolveTuiModelSpec(params) {
29
+ const defaultSpec = toValidModelSpec(params.piDefaultModelSpec) ?? DEFAULT_PI_MODEL_SPEC;
30
+ const candidates = [
31
+ params.modelOverride,
32
+ params.configPrimaryModelSpec,
33
+ params.configPiAiModelSpec,
34
+ params.runtimeModelSpec
35
+ ];
36
+ for (const candidate of candidates) {
37
+ const spec = toValidModelSpec(candidate);
38
+ if (!spec) continue;
39
+ const { provider } = parseModelSpec(spec);
40
+ if (params.hasCredentials(provider)) return spec;
41
+ }
42
+ return defaultSpec;
43
+ }
44
+
45
+ //#endregion
46
+ export { resolveTuiModelSpec };
@@ -0,0 +1,78 @@
1
+ //#region src/tui/sse-parser.ts
2
+ /**
3
+ * Find the next SSE event boundary in the buffer.
4
+ * Supports both LF (\n\n) and CRLF (\r\n\r\n) framing.
5
+ */
6
+ function findSseEventBreak(buffer) {
7
+ const lfBreak = buffer.indexOf("\n\n");
8
+ const crlfBreak = buffer.indexOf("\r\n\r\n");
9
+ if (lfBreak === -1 && crlfBreak === -1) return null;
10
+ if (lfBreak === -1) return {
11
+ index: crlfBreak,
12
+ length: 4
13
+ };
14
+ if (crlfBreak === -1) return {
15
+ index: lfBreak,
16
+ length: 2
17
+ };
18
+ return lfBreak < crlfBreak ? {
19
+ index: lfBreak,
20
+ length: 2
21
+ } : {
22
+ index: crlfBreak,
23
+ length: 4
24
+ };
25
+ }
26
+ /**
27
+ * Split a chunk buffer into complete SSE raw events + the remaining tail.
28
+ */
29
+ function drainSseEvents(buffer) {
30
+ const events = [];
31
+ let remaining = buffer;
32
+ let eventBreak = findSseEventBreak(remaining);
33
+ while (eventBreak) {
34
+ events.push(remaining.slice(0, eventBreak.index));
35
+ remaining = remaining.slice(eventBreak.index + eventBreak.length);
36
+ eventBreak = findSseEventBreak(remaining);
37
+ }
38
+ return {
39
+ events,
40
+ remaining
41
+ };
42
+ }
43
+ /**
44
+ * Extract `data:` payloads from one raw SSE event block.
45
+ */
46
+ function extractSseDataPayloads(rawEvent) {
47
+ const payloads = [];
48
+ for (const line of rawEvent.split(/\r?\n/)) {
49
+ if (!line.startsWith("data:")) continue;
50
+ const payload = line.slice(5).trim();
51
+ if (!payload) continue;
52
+ payloads.push(payload);
53
+ }
54
+ return payloads;
55
+ }
56
+ /**
57
+ * Parse JSON payload for conversation streaming events.
58
+ */
59
+ function parseConversationStreamPayload(payload) {
60
+ let parsed;
61
+ try {
62
+ parsed = JSON.parse(payload);
63
+ } catch {
64
+ return null;
65
+ }
66
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
67
+ const record = parsed;
68
+ return {
69
+ type: typeof record.type === "string" ? record.type : void 0,
70
+ text: typeof record.text === "string" ? record.text : void 0,
71
+ fullText: typeof record.fullText === "string" ? record.fullText : void 0,
72
+ agentName: typeof record.agentName === "string" ? record.agentName : void 0,
73
+ message: typeof record.message === "string" ? record.message : void 0
74
+ };
75
+ }
76
+
77
+ //#endregion
78
+ export { drainSseEvents, extractSseDataPayloads, parseConversationStreamPayload };
@@ -0,0 +1,110 @@
1
+ import chalk from "chalk";
2
+ import { highlight, supportsLanguage } from "cli-highlight";
3
+
4
+ //#region src/tui/theme.ts
5
+ const ACCENT = "#E879F9";
6
+ const ACCENT_DIM = "#A855F7";
7
+ const MUTED = "#808080";
8
+ const DIM = "#666666";
9
+ const SUCCESS = "#b5bd68";
10
+ const ERROR = "#cc6666";
11
+ const WARNING = "#FBBF24";
12
+ const INFO = "#60A5FA";
13
+ const USER_MSG_BG = "#2D1B3D";
14
+ const TOOL_PENDING_BG = "#282832";
15
+ const TOOL_SUCCESS_BG = "#283228";
16
+ const TOOL_ERROR_BG = "#3c2828";
17
+ const MD_HEADING = "#f0c674";
18
+ const MD_LINK = "#81a2be";
19
+ const MD_CODE = ACCENT;
20
+ const MD_CODE_BLOCK = SUCCESS;
21
+ const SYN_COMMENT = "#6A9955";
22
+ const SYN_KEYWORD = "#C586C0";
23
+ const SYN_FUNCTION = "#DCDCAA";
24
+ const SYN_VARIABLE = "#9CDCFE";
25
+ const SYN_STRING = "#CE9178";
26
+ const SYN_NUMBER = "#B5CEA8";
27
+ const SYN_TYPE = "#4EC9B0";
28
+ const SYN_OPERATOR = "#D4D4D4";
29
+ const SYN_PUNCTUATION = "#808080";
30
+ const hex = (value) => chalk.hex(value);
31
+ const bg = (value) => chalk.bgHex(value);
32
+ /** cli-highlight theme mapped to Milady's palette. */
33
+ const cliHighlightTheme = {
34
+ keyword: (s) => hex(SYN_KEYWORD)(s),
35
+ built_in: (s) => hex(SYN_TYPE)(s),
36
+ literal: (s) => hex(SYN_NUMBER)(s),
37
+ number: (s) => hex(SYN_NUMBER)(s),
38
+ string: (s) => hex(SYN_STRING)(s),
39
+ comment: (s) => hex(SYN_COMMENT)(s),
40
+ function: (s) => hex(SYN_FUNCTION)(s),
41
+ title: (s) => hex(SYN_FUNCTION)(s),
42
+ class: (s) => hex(SYN_TYPE)(s),
43
+ type: (s) => hex(SYN_TYPE)(s),
44
+ attr: (s) => hex(SYN_VARIABLE)(s),
45
+ variable: (s) => hex(SYN_VARIABLE)(s),
46
+ params: (s) => hex(SYN_VARIABLE)(s),
47
+ operator: (s) => hex(SYN_OPERATOR)(s),
48
+ punctuation: (s) => hex(SYN_PUNCTUATION)(s)
49
+ };
50
+ const miladySelectListTheme = {
51
+ selectedPrefix: (text) => hex(ACCENT)(text),
52
+ selectedText: (text) => chalk.bold(hex(ACCENT)(text)),
53
+ description: (text) => hex(MUTED)(text),
54
+ scrollInfo: (text) => hex(MUTED)(text),
55
+ noMatch: (text) => hex(MUTED)(text)
56
+ };
57
+ const miladyEditorTheme = {
58
+ borderColor: (text) => hex(DIM)(text),
59
+ selectList: miladySelectListTheme
60
+ };
61
+ const miladyMarkdownTheme = {
62
+ heading: (text) => chalk.bold(hex(MD_HEADING)(text)),
63
+ link: (text) => hex(MD_LINK)(text),
64
+ linkUrl: (text) => hex(DIM)(text),
65
+ code: (text) => hex(MD_CODE)(text),
66
+ codeBlock: (text) => hex(MD_CODE_BLOCK)(text),
67
+ codeBlockBorder: (text) => hex(MUTED)(text),
68
+ quote: (text) => hex(MUTED)(text),
69
+ quoteBorder: (text) => hex(MUTED)(text),
70
+ hr: (text) => hex(MUTED)(text),
71
+ listBullet: (text) => hex(ACCENT_DIM)(text),
72
+ bold: (text) => chalk.bold(text),
73
+ italic: (text) => chalk.italic(hex(ACCENT_DIM)(text)),
74
+ strikethrough: (text) => chalk.strikethrough(text),
75
+ underline: (text) => chalk.underline(text),
76
+ highlightCode: (code, lang) => {
77
+ const validLang = lang && supportsLanguage(lang) ? lang : void 0;
78
+ try {
79
+ return highlight(code, {
80
+ language: validLang,
81
+ ignoreIllegals: true,
82
+ theme: cliHighlightTheme
83
+ }).split("\n");
84
+ } catch {
85
+ return code.split("\n").map((line) => hex(MD_CODE_BLOCK)(line));
86
+ }
87
+ }
88
+ };
89
+ const tuiTheme = {
90
+ accent: (text) => hex(ACCENT)(text),
91
+ accentDim: (text) => hex(ACCENT_DIM)(text),
92
+ muted: (text) => hex(MUTED)(text),
93
+ dim: (text) => hex(DIM)(text),
94
+ success: (text) => hex(SUCCESS)(text),
95
+ error: (text) => hex(ERROR)(text),
96
+ warning: (text) => hex(WARNING)(text),
97
+ info: (text) => hex(INFO)(text),
98
+ bold: (text) => chalk.bold(text),
99
+ italic: (text) => chalk.italic(text),
100
+ userMsgBg: (text) => bg(USER_MSG_BG)(text),
101
+ toolPendingBg: (text) => bg(TOOL_PENDING_BG)(text),
102
+ toolSuccessBg: (text) => bg(TOOL_SUCCESS_BG)(text),
103
+ toolErrorBg: (text) => bg(TOOL_ERROR_BG)(text),
104
+ markdown: miladyMarkdownTheme,
105
+ editor: miladyEditorTheme,
106
+ selectList: miladySelectListTheme
107
+ };
108
+
109
+ //#endregion
110
+ export { miladyMarkdownTheme, tuiTheme };
@@ -0,0 +1,62 @@
1
+ import path from "node:path";
2
+
3
+ //#region src/tui/titlebar-spinner.ts
4
+ const BRAILLE_FRAMES = [
5
+ "⠋",
6
+ "⠙",
7
+ "⠹",
8
+ "⠸",
9
+ "⠼",
10
+ "⠴",
11
+ "⠦",
12
+ "⠧",
13
+ "⠇",
14
+ "⠏"
15
+ ];
16
+ /**
17
+ * Lightweight titlebar spinner used by the Milady TUI while a request is in
18
+ * flight.
19
+ */
20
+ var TitlebarSpinner = class {
21
+ constructor(options) {
22
+ this.options = options;
23
+ this.timer = null;
24
+ this.frameIndex = 0;
25
+ this.baseTitle = getDefaultTitle();
26
+ }
27
+ setBaseTitle(title) {
28
+ this.baseTitle = title.trim() || getDefaultTitle();
29
+ if (!this.timer) this.applyTitle(this.baseTitle);
30
+ }
31
+ start() {
32
+ this.stop();
33
+ const intervalMs = this.options.intervalMs ?? 80;
34
+ this.timer = setInterval(() => {
35
+ const frame = BRAILLE_FRAMES[this.frameIndex % BRAILLE_FRAMES.length] ?? "⠋";
36
+ this.applyTitle(`${frame} ${this.baseTitle}`);
37
+ this.frameIndex += 1;
38
+ }, intervalMs);
39
+ }
40
+ stop() {
41
+ if (this.timer) {
42
+ clearInterval(this.timer);
43
+ this.timer = null;
44
+ }
45
+ this.frameIndex = 0;
46
+ this.applyTitle(this.baseTitle);
47
+ }
48
+ dispose() {
49
+ this.stop();
50
+ }
51
+ applyTitle(title) {
52
+ try {
53
+ this.options.setTitle(title);
54
+ } catch {}
55
+ }
56
+ };
57
+ function getDefaultTitle() {
58
+ return `milady - ${path.basename(process.cwd())}`;
59
+ }
60
+
61
+ //#endregion
62
+ export { TitlebarSpinner };