dexto 1.5.5 → 1.5.7

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 (234) hide show
  1. package/README.md +13 -0
  2. package/dist/agents/agent-template.yml +1 -1
  3. package/dist/agents/coding-agent/coding-agent.yml +17 -2
  4. package/dist/agents/coding-agent/skills/code-review.md +46 -0
  5. package/dist/agents/explore-agent/explore-agent.yml +2 -0
  6. package/dist/agents/podcast-agent/podcast-agent.yml +1 -1
  7. package/dist/analytics/events.d.ts +1 -1
  8. package/dist/analytics/events.d.ts.map +1 -1
  9. package/dist/api/server-hono.d.ts.map +1 -1
  10. package/dist/api/server-hono.js +55 -10
  11. package/dist/cli/assets/dexto-logo.svg +31 -0
  12. package/dist/cli/auth/api-client.d.ts +49 -0
  13. package/dist/cli/auth/api-client.d.ts.map +1 -0
  14. package/dist/cli/auth/api-client.js +127 -0
  15. package/dist/cli/auth/constants.d.ts +23 -0
  16. package/dist/cli/auth/constants.d.ts.map +1 -0
  17. package/dist/cli/auth/constants.js +24 -0
  18. package/dist/cli/auth/index.d.ts +5 -0
  19. package/dist/cli/auth/index.d.ts.map +1 -0
  20. package/dist/cli/auth/index.js +6 -0
  21. package/dist/cli/auth/oauth.d.ts +26 -0
  22. package/dist/cli/auth/oauth.d.ts.map +1 -0
  23. package/dist/cli/auth/oauth.js +327 -0
  24. package/dist/cli/auth/service.d.ts +20 -0
  25. package/dist/cli/auth/service.d.ts.map +1 -0
  26. package/dist/cli/auth/service.js +147 -0
  27. package/dist/cli/commands/auth/index.d.ts +4 -0
  28. package/dist/cli/commands/auth/index.d.ts.map +1 -0
  29. package/dist/cli/commands/auth/index.js +4 -0
  30. package/dist/cli/commands/auth/login.d.ts +9 -0
  31. package/dist/cli/commands/auth/login.d.ts.map +1 -0
  32. package/dist/cli/commands/auth/login.js +255 -0
  33. package/dist/cli/commands/auth/logout.d.ts +5 -0
  34. package/dist/cli/commands/auth/logout.d.ts.map +1 -0
  35. package/dist/cli/commands/auth/logout.js +51 -0
  36. package/dist/cli/commands/auth/status.d.ts +2 -0
  37. package/dist/cli/commands/auth/status.d.ts.map +1 -0
  38. package/dist/cli/commands/auth/status.js +22 -0
  39. package/dist/cli/commands/billing/index.d.ts +2 -0
  40. package/dist/cli/commands/billing/index.d.ts.map +1 -0
  41. package/dist/cli/commands/billing/index.js +2 -0
  42. package/dist/cli/commands/billing/status.d.ts +6 -0
  43. package/dist/cli/commands/billing/status.d.ts.map +1 -0
  44. package/dist/cli/commands/billing/status.js +60 -0
  45. package/dist/cli/commands/index.d.ts +4 -0
  46. package/dist/cli/commands/index.d.ts.map +1 -1
  47. package/dist/cli/commands/index.js +9 -0
  48. package/dist/cli/commands/interactive-commands/auth/index.d.ts +12 -0
  49. package/dist/cli/commands/interactive-commands/auth/index.d.ts.map +1 -0
  50. package/dist/cli/commands/interactive-commands/auth/index.js +20 -0
  51. package/dist/cli/commands/interactive-commands/command-parser.d.ts +5 -0
  52. package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
  53. package/dist/cli/commands/interactive-commands/command-parser.js +6 -0
  54. package/dist/cli/commands/interactive-commands/commands.d.ts +1 -0
  55. package/dist/cli/commands/interactive-commands/commands.d.ts.map +1 -1
  56. package/dist/cli/commands/interactive-commands/commands.js +10 -0
  57. package/dist/cli/commands/interactive-commands/export/index.d.ts +13 -0
  58. package/dist/cli/commands/interactive-commands/export/index.d.ts.map +1 -0
  59. package/dist/cli/commands/interactive-commands/export/index.js +21 -0
  60. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  61. package/dist/cli/commands/interactive-commands/general-commands.js +1 -0
  62. package/dist/cli/commands/interactive-commands/mcp/index.d.ts +2 -2
  63. package/dist/cli/commands/interactive-commands/mcp/index.d.ts.map +1 -1
  64. package/dist/cli/commands/interactive-commands/mcp/index.js +4 -7
  65. package/dist/cli/commands/interactive-commands/model/index.d.ts +2 -2
  66. package/dist/cli/commands/interactive-commands/model/index.d.ts.map +1 -1
  67. package/dist/cli/commands/interactive-commands/model/index.js +4 -7
  68. package/dist/cli/commands/interactive-commands/plugin/index.d.ts +13 -0
  69. package/dist/cli/commands/interactive-commands/plugin/index.d.ts.map +1 -0
  70. package/dist/cli/commands/interactive-commands/plugin/index.js +18 -0
  71. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts +3 -1
  72. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
  73. package/dist/cli/commands/interactive-commands/prompt-commands.js +72 -36
  74. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
  75. package/dist/cli/commands/interactive-commands/system/system-commands.js +2 -3
  76. package/dist/cli/commands/plugin.d.ts +161 -0
  77. package/dist/cli/commands/plugin.d.ts.map +1 -0
  78. package/dist/cli/commands/plugin.js +376 -0
  79. package/dist/cli/commands/setup.d.ts +9 -9
  80. package/dist/cli/commands/setup.d.ts.map +1 -1
  81. package/dist/cli/commands/setup.js +325 -37
  82. package/dist/cli/commands/sync-agents.d.ts +44 -0
  83. package/dist/cli/commands/sync-agents.d.ts.map +1 -0
  84. package/dist/cli/commands/sync-agents.js +483 -0
  85. package/dist/cli/ink-cli/InkCLIRefactored.d.ts +14 -1
  86. package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
  87. package/dist/cli/ink-cli/InkCLIRefactored.js +8 -2
  88. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts +1 -1
  89. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  90. package/dist/cli/ink-cli/components/ApprovalPrompt.js +80 -12
  91. package/dist/cli/ink-cli/components/Footer.d.ts +2 -1
  92. package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
  93. package/dist/cli/ink-cli/components/Footer.js +6 -2
  94. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.d.ts.map +1 -1
  95. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +15 -7
  96. package/dist/cli/ink-cli/components/StatusBar.d.ts +9 -1
  97. package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
  98. package/dist/cli/ink-cli/components/StatusBar.js +17 -5
  99. package/dist/cli/ink-cli/components/TodoPanel.d.ts +11 -8
  100. package/dist/cli/ink-cli/components/TodoPanel.d.ts.map +1 -1
  101. package/dist/cli/ink-cli/components/TodoPanel.js +38 -36
  102. package/dist/cli/ink-cli/components/chat/Header.d.ts.map +1 -1
  103. package/dist/cli/ink-cli/components/chat/Header.js +1 -1
  104. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  105. package/dist/cli/ink-cli/components/chat/MessageItem.js +14 -1
  106. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
  107. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  108. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +16 -4
  109. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  110. package/dist/cli/ink-cli/components/modes/StaticCLI.js +4 -1
  111. package/dist/cli/ink-cli/components/overlays/ExportWizard.d.ts +22 -0
  112. package/dist/cli/ink-cli/components/overlays/ExportWizard.d.ts.map +1 -0
  113. package/dist/cli/ink-cli/components/overlays/ExportWizard.js +308 -0
  114. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts +1 -0
  115. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.d.ts.map +1 -1
  116. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +31 -20
  117. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts +20 -0
  118. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.d.ts.map +1 -0
  119. package/dist/cli/ink-cli/components/overlays/MarketplaceAddPrompt.js +81 -0
  120. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts +31 -0
  121. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.d.ts.map +1 -0
  122. package/dist/cli/ink-cli/components/overlays/MarketplaceBrowser.js +297 -0
  123. package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.d.ts.map +1 -1
  124. package/dist/cli/ink-cli/components/overlays/McpRemoveSelector.js +7 -1
  125. package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts +1 -1
  126. package/dist/cli/ink-cli/components/overlays/McpServerActions.d.ts.map +1 -1
  127. package/dist/cli/ink-cli/components/overlays/McpServerActions.js +9 -0
  128. package/dist/cli/ink-cli/components/overlays/McpServerList.d.ts.map +1 -1
  129. package/dist/cli/ink-cli/components/overlays/McpServerList.js +9 -2
  130. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  131. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +15 -2
  132. package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts +27 -0
  133. package/dist/cli/ink-cli/components/overlays/PluginActions.d.ts.map +1 -0
  134. package/dist/cli/ink-cli/components/overlays/PluginActions.js +66 -0
  135. package/dist/cli/ink-cli/components/overlays/PluginList.d.ts +21 -0
  136. package/dist/cli/ink-cli/components/overlays/PluginList.d.ts.map +1 -0
  137. package/dist/cli/ink-cli/components/overlays/PluginList.js +70 -0
  138. package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts +21 -0
  139. package/dist/cli/ink-cli/components/overlays/PluginManager.d.ts.map +1 -0
  140. package/dist/cli/ink-cli/components/overlays/PluginManager.js +63 -0
  141. package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
  142. package/dist/cli/ink-cli/components/overlays/PromptList.js +4 -1
  143. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts +2 -1
  144. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -1
  145. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +61 -2
  146. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts +4 -2
  147. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts.map +1 -1
  148. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +4 -4
  149. package/dist/cli/ink-cli/constants/tips.js +2 -2
  150. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  151. package/dist/cli/ink-cli/containers/InputContainer.js +31 -3
  152. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  153. package/dist/cli/ink-cli/containers/OverlayContainer.js +260 -11
  154. package/dist/cli/ink-cli/hooks/index.d.ts +1 -0
  155. package/dist/cli/ink-cli/hooks/index.d.ts.map +1 -1
  156. package/dist/cli/ink-cli/hooks/index.js +1 -0
  157. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  158. package/dist/cli/ink-cli/hooks/useCLIState.js +3 -0
  159. package/dist/cli/ink-cli/hooks/useGitBranch.d.ts +13 -0
  160. package/dist/cli/ink-cli/hooks/useGitBranch.d.ts.map +1 -0
  161. package/dist/cli/ink-cli/hooks/useGitBranch.js +35 -0
  162. package/dist/cli/ink-cli/hooks/useInputOrchestrator.d.ts.map +1 -1
  163. package/dist/cli/ink-cli/hooks/useInputOrchestrator.js +50 -6
  164. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  165. package/dist/cli/ink-cli/services/processStream.js +42 -10
  166. package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
  167. package/dist/cli/ink-cli/state/initialState.js +3 -0
  168. package/dist/cli/ink-cli/state/types.d.ts +16 -1
  169. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  170. package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
  171. package/dist/cli/ink-cli/utils/commandOverlays.js +2 -0
  172. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +14 -1
  173. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  174. package/dist/cli/ink-cli/utils/messageFormatting.js +68 -8
  175. package/dist/cli/ink-cli/utils/toolUtils.d.ts +11 -0
  176. package/dist/cli/ink-cli/utils/toolUtils.d.ts.map +1 -1
  177. package/dist/cli/ink-cli/utils/toolUtils.js +17 -0
  178. package/dist/cli/mcp/index.d.ts +8 -0
  179. package/dist/cli/mcp/index.d.ts.map +1 -0
  180. package/dist/cli/mcp/index.js +7 -0
  181. package/dist/cli/mcp/oauth-factory.d.ts +6 -0
  182. package/dist/cli/mcp/oauth-factory.d.ts.map +1 -0
  183. package/dist/cli/mcp/oauth-factory.js +25 -0
  184. package/dist/cli/mcp/oauth-provider.d.ts +10 -0
  185. package/dist/cli/mcp/oauth-provider.d.ts.map +1 -0
  186. package/dist/cli/mcp/oauth-provider.js +77 -0
  187. package/dist/cli/mcp/oauth-redirect.d.ts +3 -0
  188. package/dist/cli/mcp/oauth-redirect.d.ts.map +1 -0
  189. package/dist/cli/mcp/oauth-redirect.js +4 -0
  190. package/dist/cli/mcp/oauth-server.d.ts +2 -0
  191. package/dist/cli/mcp/oauth-server.d.ts.map +1 -0
  192. package/dist/cli/mcp/oauth-server.js +70 -0
  193. package/dist/cli/mcp/oauth-store.d.ts +10 -0
  194. package/dist/cli/mcp/oauth-store.d.ts.map +1 -0
  195. package/dist/cli/mcp/oauth-store.js +27 -0
  196. package/dist/cli/mcp/oauth-ui.d.ts +2 -0
  197. package/dist/cli/mcp/oauth-ui.d.ts.map +1 -0
  198. package/dist/cli/mcp/oauth-ui.js +12 -0
  199. package/dist/cli/mcp/oauth-utils.d.ts +2 -0
  200. package/dist/cli/mcp/oauth-utils.d.ts.map +1 -0
  201. package/dist/cli/mcp/oauth-utils.js +17 -0
  202. package/dist/cli/utils/api-key-setup.d.ts.map +1 -1
  203. package/dist/cli/utils/api-key-setup.js +13 -90
  204. package/dist/cli/utils/api-key-verification.d.ts.map +1 -1
  205. package/dist/cli/utils/api-key-verification.js +36 -0
  206. package/dist/cli/utils/config-validation.d.ts +3 -1
  207. package/dist/cli/utils/config-validation.d.ts.map +1 -1
  208. package/dist/cli/utils/config-validation.js +42 -19
  209. package/dist/cli/utils/dexto-auth-check.d.ts +53 -0
  210. package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -0
  211. package/dist/cli/utils/dexto-auth-check.js +104 -0
  212. package/dist/cli/utils/dexto-setup.d.ts +8 -0
  213. package/dist/cli/utils/dexto-setup.d.ts.map +1 -0
  214. package/dist/cli/utils/dexto-setup.js +17 -0
  215. package/dist/cli/utils/options.d.ts.map +1 -1
  216. package/dist/cli/utils/options.js +5 -1
  217. package/dist/cli/utils/provider-setup.d.ts +4 -0
  218. package/dist/cli/utils/provider-setup.d.ts.map +1 -1
  219. package/dist/cli/utils/provider-setup.js +20 -0
  220. package/dist/cli/utils/version-check.d.ts +45 -0
  221. package/dist/cli/utils/version-check.d.ts.map +1 -0
  222. package/dist/cli/utils/version-check.js +195 -0
  223. package/dist/config/cli-overrides.d.ts +17 -8
  224. package/dist/config/cli-overrides.d.ts.map +1 -1
  225. package/dist/config/cli-overrides.js +36 -22
  226. package/dist/config/effective-llm.d.ts +123 -0
  227. package/dist/config/effective-llm.d.ts.map +1 -0
  228. package/dist/config/effective-llm.js +171 -0
  229. package/dist/index.js +451 -126
  230. package/dist/webui/assets/index-C9JXwpvo.css +1 -0
  231. package/dist/webui/assets/{index-DVQWNLpT.js → index-Dl3mj53P.js} +217 -217
  232. package/dist/webui/index.html +2 -2
  233. package/package.json +9 -8
  234. package/dist/webui/assets/index-BglIVTSG.css +0 -1
package/dist/index.js CHANGED
@@ -14,19 +14,36 @@ import { withAnalytics, safeExit, ExitSignal } from './analytics/wrapper.js';
14
14
  // Use createRequire to import package.json without experimental warning
15
15
  const require = createRequire(import.meta.url);
16
16
  const pkg = require('../package.json');
17
- import { logger, getProviderFromModel, getAllSupportedModels, DextoAgent, isPath, resolveApiKeyForProvider, getPrimaryApiKeyEnvVar, } from '@dexto/core';
18
- import { resolveAgentPath, loadAgentConfig, globalPreferencesExist, loadGlobalPreferences, resolveBundledScript, } from '@dexto/agent-management';
17
+ // Set CLI version for Dexto Gateway usage tracking
18
+ process.env.DEXTO_CLI_VERSION = pkg.version;
19
+ // Populate DEXTO_API_KEY for Dexto gateway routing
20
+ // Resolution order in getDextoApiKey():
21
+ // 1. Explicit env var (CI, testing, account override)
22
+ // 2. auth.json from `dexto login`
23
+ import { isDextoAuthEnabled } from '@dexto/agent-management';
24
+ if (isDextoAuthEnabled()) {
25
+ const { getDextoApiKey } = await import('./cli/auth/index.js');
26
+ const dextoApiKey = await getDextoApiKey();
27
+ if (dextoApiKey) {
28
+ process.env.DEXTO_API_KEY = dextoApiKey;
29
+ }
30
+ }
31
+ import { logger, DextoLogger, FileTransport, DextoLogComponent, getProviderFromModel, getAllSupportedModels, DextoAgent, isPath, resolveApiKeyForProvider, getPrimaryApiKeyEnvVar, } from '@dexto/core';
32
+ import { resolveAgentPath, loadAgentConfig, globalPreferencesExist, loadGlobalPreferences, resolveBundledScript, getDextoPath, } from '@dexto/agent-management';
19
33
  import { startHonoApiServer } from './api/server-hono.js';
20
34
  import { validateCliOptions, handleCliOptionsError } from './cli/utils/options.js';
21
35
  import { validateAgentConfig } from './cli/utils/config-validation.js';
22
- import { applyCLIOverrides, applyUserPreferences, checkAgentCompatibility, } from './config/cli-overrides.js';
36
+ import { applyCLIOverrides, applyUserPreferences } from './config/cli-overrides.js';
23
37
  import { enrichAgentConfig } from '@dexto/agent-management';
24
38
  import { getPort } from './utils/port-utils.js';
25
39
  import { createDextoProject, createImage, getUserInputToInitDextoApp, initDexto, postInitDexto, } from './cli/commands/index.js';
26
- import { handleSetupCommand, handleInstallCommand, handleUninstallCommand, handleListAgentsCommand, handleWhichCommand, } from './cli/commands/index.js';
40
+ import { handleSetupCommand, handleInstallCommand, handleUninstallCommand, handleListAgentsCommand, handleWhichCommand, handleSyncAgentsCommand, shouldPromptForSync, markSyncDismissed, clearSyncDismissed, handleLoginCommand, handleLogoutCommand, handleStatusCommand, handleBillingStatusCommand, handlePluginListCommand, handlePluginInstallCommand, handlePluginUninstallCommand, handlePluginValidateCommand,
41
+ // Marketplace handlers
42
+ handleMarketplaceAddCommand, handleMarketplaceRemoveCommand, handleMarketplaceUpdateCommand, handleMarketplaceListCommand, handleMarketplacePluginsCommand, handleMarketplaceInstallCommand, } from './cli/commands/index.js';
27
43
  import { handleSessionListCommand, handleSessionHistoryCommand, handleSessionDeleteCommand, handleSessionSearchCommand, } from './cli/commands/session-commands.js';
28
44
  import { requiresSetup } from './cli/utils/setup-utils.js';
29
45
  import { checkForFileInCurrentDirectory, FileNotFoundError } from './cli/utils/package-mgmt.js';
46
+ import { checkForUpdates, displayUpdateNotification } from './cli/utils/version-check.js';
30
47
  import { resolveWebRoot } from './web.js';
31
48
  import { initializeMcpServer, createMcpTransport } from '@dexto/server';
32
49
  import { createAgentCard } from '@dexto/core';
@@ -34,6 +51,9 @@ import { initializeMcpToolAggregationServer } from './api/mcp/tool-aggregation-h
34
51
  const program = new Command();
35
52
  // Initialize analytics early (no-op if disabled)
36
53
  await initAnalytics({ appVersion: pkg.version });
54
+ // Start version check early (non-blocking)
55
+ // We'll check the result later and display notification for interactive modes
56
+ const versionCheckPromise = checkForUpdates(pkg.version);
37
57
  /**
38
58
  * Recursively removes null values from an object.
39
59
  * This handles YAML files that have explicit `apiKey: null` entries
@@ -89,7 +109,6 @@ program
89
109
  .option('--port <port>', 'port for the server (default: 3000 for web, 3001 for server mode)')
90
110
  .option('--no-auto-install', 'Disable automatic installation of missing agents from registry')
91
111
  .option('--image <package>', 'Image package to load (e.g., @dexto/image-local). Overrides config image field.')
92
- .option('--privacy-mode', 'Hide full file paths from display (useful for screen recording/sharing). Can also set DEXTO_PRIVACY_MODE=true')
93
112
  .option('--dev', '[maintainers] Use local ./agents instead of ~/.dexto (for dexto repo development)')
94
113
  .enablePositionalOptions();
95
114
  // 2) `create-app` SUB-COMMAND
@@ -190,12 +209,7 @@ program
190
209
  catch (err) {
191
210
  if (err instanceof ExitSignal)
192
211
  throw err;
193
- if (process.env.DEXTO_PRIVACY_MODE === 'true') {
194
- console.error(`❌ dexto setup command failed: ${err}. Check logs for more information`);
195
- }
196
- else {
197
- console.error(`❌ dexto setup command failed: ${err}. Check logs in ~/.dexto/logs/dexto.log for more information`);
198
- }
212
+ console.error(`❌ dexto setup command failed: ${err}. Check logs in ~/.dexto/logs/dexto.log for more information`);
199
213
  safeExit('setup', 1, 'error');
200
214
  }
201
215
  }));
@@ -278,30 +292,221 @@ program
278
292
  safeExit('which', 1, 'error');
279
293
  }
280
294
  }));
295
+ // 10) `sync-agents` SUB-COMMAND
296
+ program
297
+ .command('sync-agents')
298
+ .description('Sync installed agents with bundled versions')
299
+ .option('--list', 'List agent status without updating')
300
+ .option('--force', 'Update all agents without prompting')
301
+ .action(withAnalytics('sync-agents', async (options) => {
302
+ try {
303
+ await handleSyncAgentsCommand(options);
304
+ safeExit('sync-agents', 0);
305
+ }
306
+ catch (err) {
307
+ if (err instanceof ExitSignal)
308
+ throw err;
309
+ console.error(`❌ dexto sync-agents command failed: ${err}`);
310
+ safeExit('sync-agents', 1, 'error');
311
+ }
312
+ }));
313
+ // 11) `plugin` SUB-COMMAND
314
+ const pluginCommand = program.command('plugin').description('Manage plugins');
315
+ pluginCommand
316
+ .command('list')
317
+ .description('List installed plugins')
318
+ .option('--verbose', 'Show detailed plugin information')
319
+ .action(withAnalytics('plugin list', async (options) => {
320
+ try {
321
+ await handlePluginListCommand(options);
322
+ safeExit('plugin list', 0);
323
+ }
324
+ catch (err) {
325
+ if (err instanceof ExitSignal)
326
+ throw err;
327
+ console.error(`❌ dexto plugin list command failed: ${err}`);
328
+ safeExit('plugin list', 1, 'error');
329
+ }
330
+ }));
331
+ pluginCommand
332
+ .command('install')
333
+ .description('Install a plugin from a local directory')
334
+ .requiredOption('--path <path>', 'Path to the plugin directory')
335
+ .option('--scope <scope>', 'Installation scope: user, project, or local', 'user')
336
+ .option('--force', 'Force overwrite if already installed')
337
+ .action(withAnalytics('plugin install', async (options) => {
338
+ try {
339
+ await handlePluginInstallCommand(options);
340
+ safeExit('plugin install', 0);
341
+ }
342
+ catch (err) {
343
+ if (err instanceof ExitSignal)
344
+ throw err;
345
+ console.error(`❌ dexto plugin install command failed: ${err}`);
346
+ safeExit('plugin install', 1, 'error');
347
+ }
348
+ }));
349
+ pluginCommand
350
+ .command('uninstall <name>')
351
+ .description('Uninstall a plugin by name')
352
+ .action(withAnalytics('plugin uninstall', async (name) => {
353
+ try {
354
+ await handlePluginUninstallCommand({ name });
355
+ safeExit('plugin uninstall', 0);
356
+ }
357
+ catch (err) {
358
+ if (err instanceof ExitSignal)
359
+ throw err;
360
+ console.error(`❌ dexto plugin uninstall command failed: ${err}`);
361
+ safeExit('plugin uninstall', 1, 'error');
362
+ }
363
+ }));
364
+ pluginCommand
365
+ .command('validate [path]')
366
+ .description('Validate a plugin directory structure')
367
+ .action(withAnalytics('plugin validate', async (path) => {
368
+ try {
369
+ await handlePluginValidateCommand({ path: path || '.' });
370
+ safeExit('plugin validate', 0);
371
+ }
372
+ catch (err) {
373
+ if (err instanceof ExitSignal)
374
+ throw err;
375
+ console.error(`❌ dexto plugin validate command failed: ${err}`);
376
+ safeExit('plugin validate', 1, 'error');
377
+ }
378
+ }));
379
+ // 12) `plugin marketplace` SUB-COMMANDS
380
+ const marketplaceCommand = pluginCommand
381
+ .command('marketplace')
382
+ .alias('market')
383
+ .description('Manage plugin marketplaces');
384
+ marketplaceCommand
385
+ .command('add <source>')
386
+ .description('Add a marketplace (GitHub: owner/repo, git URL, or local path)')
387
+ .option('--name <name>', 'Custom name for the marketplace')
388
+ .action(withAnalytics('plugin marketplace add', async (source, options) => {
389
+ try {
390
+ await handleMarketplaceAddCommand({ source, name: options.name });
391
+ safeExit('plugin marketplace add', 0);
392
+ }
393
+ catch (err) {
394
+ if (err instanceof ExitSignal)
395
+ throw err;
396
+ console.error(`❌ dexto plugin marketplace add command failed: ${err}`);
397
+ safeExit('plugin marketplace add', 1, 'error');
398
+ }
399
+ }));
400
+ marketplaceCommand
401
+ .command('list')
402
+ .description('List registered marketplaces')
403
+ .option('--verbose', 'Show detailed marketplace information')
404
+ .action(withAnalytics('plugin marketplace list', async (options) => {
405
+ try {
406
+ await handleMarketplaceListCommand(options);
407
+ safeExit('plugin marketplace list', 0);
408
+ }
409
+ catch (err) {
410
+ if (err instanceof ExitSignal)
411
+ throw err;
412
+ console.error(`❌ dexto plugin marketplace list command failed: ${err}`);
413
+ safeExit('plugin marketplace list', 1, 'error');
414
+ }
415
+ }));
416
+ marketplaceCommand
417
+ .command('remove <name>')
418
+ .alias('rm')
419
+ .description('Remove a registered marketplace')
420
+ .action(withAnalytics('plugin marketplace remove', async (name) => {
421
+ try {
422
+ await handleMarketplaceRemoveCommand({ name });
423
+ safeExit('plugin marketplace remove', 0);
424
+ }
425
+ catch (err) {
426
+ if (err instanceof ExitSignal)
427
+ throw err;
428
+ console.error(`❌ dexto plugin marketplace remove command failed: ${err}`);
429
+ safeExit('plugin marketplace remove', 1, 'error');
430
+ }
431
+ }));
432
+ marketplaceCommand
433
+ .command('update [name]')
434
+ .description('Update marketplace(s) from remote (git pull)')
435
+ .action(withAnalytics('plugin marketplace update', async (name) => {
436
+ try {
437
+ await handleMarketplaceUpdateCommand({ name });
438
+ safeExit('plugin marketplace update', 0);
439
+ }
440
+ catch (err) {
441
+ if (err instanceof ExitSignal)
442
+ throw err;
443
+ console.error(`❌ dexto plugin marketplace update command failed: ${err}`);
444
+ safeExit('plugin marketplace update', 1, 'error');
445
+ }
446
+ }));
447
+ marketplaceCommand
448
+ .command('plugins [marketplace]')
449
+ .description('List plugins available in marketplaces')
450
+ .option('--verbose', 'Show plugin descriptions')
451
+ .action(withAnalytics('plugin marketplace plugins', async (marketplace, options) => {
452
+ try {
453
+ await handleMarketplacePluginsCommand({
454
+ marketplace,
455
+ verbose: options?.verbose,
456
+ });
457
+ safeExit('plugin marketplace plugins', 0);
458
+ }
459
+ catch (err) {
460
+ if (err instanceof ExitSignal)
461
+ throw err;
462
+ console.error(`❌ dexto plugin marketplace plugins command failed: ${err}`);
463
+ safeExit('plugin marketplace plugins', 1, 'error');
464
+ }
465
+ }));
466
+ marketplaceCommand
467
+ .command('install <plugin>')
468
+ .description('Install a plugin from marketplace (plugin or plugin@marketplace)')
469
+ .option('--scope <scope>', 'Installation scope: user, project, or local', 'user')
470
+ .option('--force', 'Force reinstall if already exists')
471
+ .action(withAnalytics('plugin marketplace install', async (plugin, options) => {
472
+ try {
473
+ await handleMarketplaceInstallCommand({ ...options, plugin });
474
+ safeExit('plugin marketplace install', 0);
475
+ }
476
+ catch (err) {
477
+ if (err instanceof ExitSignal)
478
+ throw err;
479
+ console.error(`❌ dexto plugin marketplace install command failed: ${err}`);
480
+ safeExit('plugin marketplace install', 1, 'error');
481
+ }
482
+ }));
281
483
  // Helper to bootstrap a minimal agent for non-interactive session/search ops
282
484
  async function bootstrapAgentFromGlobalOpts() {
283
485
  const globalOpts = program.opts();
284
486
  const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false);
285
487
  const rawConfig = await loadAgentConfig(resolvedPath);
286
488
  const mergedConfig = applyCLIOverrides(rawConfig, globalOpts);
287
- const enrichedConfig = enrichAgentConfig(mergedConfig, resolvedPath, {
288
- logLevel: 'info', // CLI uses info-level logging for visibility
289
- });
290
- // Load image dynamically if specified (same priority as main command)
489
+ // Load image first to get bundled plugins
291
490
  // Priority: CLI flag > Agent config > Environment variable > Default
292
- // Images are optional, but default to image-local for convenience
293
491
  const imageName = globalOpts.image || // --image flag
294
- enrichedConfig.image || // image field in agent config
492
+ mergedConfig.image || // image field in agent config
295
493
  process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
296
494
  '@dexto/image-local'; // Default for convenience
495
+ let imageMetadata = null;
297
496
  try {
298
- await import(imageName);
497
+ const imageModule = await import(imageName);
498
+ imageMetadata = imageModule.imageMetadata || null;
299
499
  }
300
500
  catch (_err) {
301
501
  console.error(`❌ Failed to load image '${imageName}'`);
302
502
  console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
303
503
  safeExit('bootstrap', 1, 'image-load-failed');
304
504
  }
505
+ // Enrich config with bundled plugins from image
506
+ const enrichedConfig = enrichAgentConfig(mergedConfig, resolvedPath, {
507
+ logLevel: 'info', // CLI uses info-level logging for visibility
508
+ bundledPlugins: imageMetadata?.bundledPlugins || [],
509
+ });
305
510
  // Override approval config for read-only commands (never run conversations)
306
511
  // This avoids needing to set up unused approval handlers
307
512
  enrichedConfig.toolConfirmation = {
@@ -356,7 +561,7 @@ async function getMostRecentSessionId(agent, includeHeadless = false) {
356
561
  }
357
562
  return mostRecentId;
358
563
  }
359
- // 10) `session` SUB-COMMAND
564
+ // 11) `session` SUB-COMMAND
360
565
  const sessionCommand = program.command('session').description('Manage chat sessions');
361
566
  sessionCommand
362
567
  .command('list')
@@ -411,7 +616,7 @@ sessionCommand
411
616
  safeExit('session delete', 1, 'error');
412
617
  }
413
618
  }));
414
- // 11) `search` SUB-COMMAND
619
+ // 12) `search` SUB-COMMAND
415
620
  program
416
621
  .command('search')
417
622
  .description('Search session history')
@@ -453,7 +658,109 @@ program
453
658
  safeExit('search', 1, 'error');
454
659
  }
455
660
  }));
456
- // 12) `mcp` SUB-COMMAND
661
+ // 13) `auth` SUB-COMMAND GROUP
662
+ const authCommand = program.command('auth').description('Manage authentication');
663
+ authCommand
664
+ .command('login')
665
+ .description('Login to Dexto')
666
+ .option('--api-key <key>', 'Use Dexto API key instead of browser login')
667
+ .option('--no-interactive', 'Disable interactive prompts')
668
+ .action(withAnalytics('auth login', async (options) => {
669
+ try {
670
+ await handleLoginCommand(options);
671
+ safeExit('auth login', 0);
672
+ }
673
+ catch (err) {
674
+ if (err instanceof ExitSignal)
675
+ throw err;
676
+ console.error(`❌ dexto auth login command failed: ${err}`);
677
+ safeExit('auth login', 1, 'error');
678
+ }
679
+ }));
680
+ authCommand
681
+ .command('logout')
682
+ .description('Logout from Dexto')
683
+ .option('--force', 'Skip confirmation prompt')
684
+ .option('--no-interactive', 'Disable interactive prompts')
685
+ .action(withAnalytics('auth logout', async (options) => {
686
+ try {
687
+ await handleLogoutCommand(options);
688
+ safeExit('auth logout', 0);
689
+ }
690
+ catch (err) {
691
+ if (err instanceof ExitSignal)
692
+ throw err;
693
+ console.error(`❌ dexto auth logout command failed: ${err}`);
694
+ safeExit('auth logout', 1, 'error');
695
+ }
696
+ }));
697
+ authCommand
698
+ .command('status')
699
+ .description('Show authentication status')
700
+ .action(withAnalytics('auth status', async () => {
701
+ try {
702
+ await handleStatusCommand();
703
+ safeExit('auth status', 0);
704
+ }
705
+ catch (err) {
706
+ if (err instanceof ExitSignal)
707
+ throw err;
708
+ console.error(`❌ dexto auth status command failed: ${err}`);
709
+ safeExit('auth status', 1, 'error');
710
+ }
711
+ }));
712
+ // Also add convenience aliases at root level
713
+ program
714
+ .command('login')
715
+ .description('Login to Dexto (alias for `dexto auth login`)')
716
+ .option('--api-key <key>', 'Use Dexto API key instead of browser login')
717
+ .option('--no-interactive', 'Disable interactive prompts')
718
+ .action(withAnalytics('login', async (options) => {
719
+ try {
720
+ await handleLoginCommand(options);
721
+ safeExit('login', 0);
722
+ }
723
+ catch (err) {
724
+ if (err instanceof ExitSignal)
725
+ throw err;
726
+ console.error(`❌ dexto login command failed: ${err}`);
727
+ safeExit('login', 1, 'error');
728
+ }
729
+ }));
730
+ program
731
+ .command('logout')
732
+ .description('Logout from Dexto (alias for `dexto auth logout`)')
733
+ .option('--force', 'Skip confirmation prompt')
734
+ .option('--no-interactive', 'Disable interactive prompts')
735
+ .action(withAnalytics('logout', async (options) => {
736
+ try {
737
+ await handleLogoutCommand(options);
738
+ safeExit('logout', 0);
739
+ }
740
+ catch (err) {
741
+ if (err instanceof ExitSignal)
742
+ throw err;
743
+ console.error(`❌ dexto logout command failed: ${err}`);
744
+ safeExit('logout', 1, 'error');
745
+ }
746
+ }));
747
+ // 14) `billing` COMMAND
748
+ program
749
+ .command('billing')
750
+ .description('Show billing status and credit balance')
751
+ .action(withAnalytics('billing', async () => {
752
+ try {
753
+ await handleBillingStatusCommand();
754
+ safeExit('billing', 0);
755
+ }
756
+ catch (err) {
757
+ if (err instanceof ExitSignal)
758
+ throw err;
759
+ console.error(`❌ dexto billing command failed: ${err}`);
760
+ safeExit('billing', 1, 'error');
761
+ }
762
+ }));
763
+ // 15) `mcp` SUB-COMMAND
457
764
  // For now, this mode simply aggregates and re-expose tools from configured MCP servers (no agent)
458
765
  // dexto --mode mcp will be moved to this sub-command in the future
459
766
  program
@@ -477,12 +784,7 @@ program
477
784
  const globalOpts = program.opts();
478
785
  const nameOrPath = globalOpts.agent;
479
786
  const configPath = await resolveAgentPath(nameOrPath, globalOpts.autoInstall !== false);
480
- if (process.env.DEXTO_PRIVACY_MODE === 'true') {
481
- console.log(`📄 Loading Dexto config...`);
482
- }
483
- else {
484
- console.log(`📄 Loading Dexto config from: ${configPath}`);
485
- }
787
+ console.log(`📄 Loading Dexto config from: ${configPath}`);
486
788
  const config = await loadAgentConfig(configPath);
487
789
  logger.info(`Validating MCP servers...`);
488
790
  // Validate that MCP servers are configured
@@ -511,7 +813,7 @@ program
511
813
  safeExit('mcp', 1, 'mcp-agg-failed');
512
814
  }
513
815
  }, { timeoutMs: 0 }));
514
- // 13) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/server/mcp)
816
+ // 16) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/server/mcp)
515
817
  program
516
818
  .argument('[prompt...]', 'Natural-language prompt to run once. If not passed, dexto will start as an interactive CLI')
517
819
  // Main customer facing description
@@ -549,10 +851,6 @@ program
549
851
  logger.debug('WARNING: .env file not found; copy .env.example and set your API keys.');
550
852
  }
551
853
  const opts = program.opts();
552
- // Set privacy mode early to gate all path-related output
553
- if (opts.privacyMode) {
554
- process.env.DEXTO_PRIVACY_MODE = 'true';
555
- }
556
854
  // Set dev mode early to use local repo agents instead of ~/.dexto
557
855
  if (opts.dev) {
558
856
  process.env.DEXTO_DEV_MODE = 'true';
@@ -709,9 +1007,9 @@ program
709
1007
  const rawConfig = await loadAgentConfig(resolvedPath);
710
1008
  let mergedConfig = applyCLIOverrides(rawConfig, opts);
711
1009
  // ——— PREFERENCE-AWARE CONFIG HANDLING ———
712
- // For coding-agent (no explicit agent specified): Apply user preferences
713
- // For specific agents: Check compatibility and warn if needed
714
- const isDefaultAgent = !opts.agent;
1010
+ // User's LLM preferences from preferences.yml apply to ALL agents
1011
+ // See feature-plans/auto-update.md section 8.11 - Three-Layer LLM Resolution
1012
+ const agentId = opts.agent ?? 'coding-agent';
715
1013
  let preferences = null;
716
1014
  if (globalPreferencesExist()) {
717
1015
  try {
@@ -722,10 +1020,42 @@ program
722
1020
  logger.debug('Could not load preferences, continuing without them');
723
1021
  }
724
1022
  }
1023
+ // Check if user is configured for Dexto credits but not authenticated
1024
+ // This can happen if user logged out after setting up with Dexto
1025
+ // Now that preferences apply to ALL agents, we check for any agent
1026
+ // Only run this check when Dexto auth feature is enabled
1027
+ if (isDextoAuthEnabled()) {
1028
+ const { checkDextoAuthState } = await import('./cli/utils/dexto-auth-check.js');
1029
+ const authCheck = await checkDextoAuthState(opts.interactive !== false, agentId);
1030
+ if (!authCheck.shouldContinue) {
1031
+ if (authCheck.action === 'login') {
1032
+ // User wants to log in - run login flow then restart
1033
+ const { handleLoginCommand } = await import('./cli/commands/auth/login.js');
1034
+ await handleLoginCommand({ interactive: true });
1035
+ // Verify key was actually provisioned (provisionKeys silently catches errors)
1036
+ const { canUseDextoProvider } = await import('./cli/utils/dexto-setup.js');
1037
+ if (!(await canUseDextoProvider())) {
1038
+ console.error('\n❌ API key provisioning failed. Please try again or run `dexto setup` to use a different provider.\n');
1039
+ safeExit('main', 1, 'dexto-key-provisioning-failed');
1040
+ }
1041
+ // After login, continue with startup (preferences unchanged, now authenticated)
1042
+ }
1043
+ else if (authCheck.action === 'setup') {
1044
+ // User wants to configure different provider - run setup
1045
+ const { handleSetupCommand } = await import('./cli/commands/setup.js');
1046
+ await handleSetupCommand({ interactive: true, force: true });
1047
+ // Reload preferences after setup
1048
+ preferences = await loadGlobalPreferences();
1049
+ }
1050
+ else {
1051
+ // User cancelled
1052
+ safeExit('main', 0, 'dexto-auth-check-cancelled');
1053
+ }
1054
+ }
1055
+ }
725
1056
  // Check for pending API key setup (user skipped during initial setup)
726
- if (isDefaultAgent &&
727
- preferences?.setup?.apiKeyPending &&
728
- opts.interactive !== false) {
1057
+ // Since preferences now apply to ALL agents, this check runs for any agent
1058
+ if (preferences?.setup?.apiKeyPending && opts.interactive !== false) {
729
1059
  // Check if API key is still missing (user may have set it manually)
730
1060
  const configuredApiKey = resolveApiKeyForProvider(preferences.llm.provider);
731
1061
  if (!configuredApiKey) {
@@ -755,81 +1085,51 @@ program
755
1085
  logger.debug('API key found in environment, cleared pending flag');
756
1086
  }
757
1087
  }
758
- if (isDefaultAgent && preferences) {
759
- // Default-agent: Apply user's LLM preferences at runtime
760
- // This ensures the base agent always uses user's preferred model/provider
1088
+ // Apply user's LLM preferences to ALL agents (not just the default)
1089
+ // See feature-plans/auto-update.md section 8.11 - Three-Layer LLM Resolution:
1090
+ // local.llm ?? preferences.llm ?? bundled.llm
1091
+ // The preferences.llm acts as a "global .local.yml" for LLM settings
1092
+ if (preferences?.llm?.provider && preferences?.llm?.model) {
761
1093
  mergedConfig = applyUserPreferences(mergedConfig, preferences);
762
- logger.debug('Applied user preferences to coding-agent', {
1094
+ logger.debug(`Applied user preferences to ${agentId}`, {
763
1095
  provider: preferences.llm.provider,
764
1096
  model: preferences.llm.model,
765
1097
  });
766
1098
  }
767
- else if (!isDefaultAgent && mergedConfig.llm) {
768
- // Specific agent: Check if user has the required provider configured
769
- const agentProvider = mergedConfig.llm.provider;
770
- const resolvedApiKey = resolveApiKeyForProvider(agentProvider);
771
- const compatibility = checkAgentCompatibility(mergedConfig, preferences, resolvedApiKey);
772
- if (!compatibility.compatible && opts.interactive !== false) {
773
- // User is missing API key for the agent's provider
774
- if (!compatibility.userHasApiKey &&
775
- preferences?.llm?.provider &&
776
- preferences?.llm?.model) {
777
- // User has a default LLM configured - offer choice
778
- const { promptForMissingAgentApiKey } = await import('./cli/utils/api-key-setup.js');
779
- const result = await promptForMissingAgentApiKey(compatibility.agentProvider, compatibility.agentModel, preferences.llm.provider, preferences.llm.model);
780
- if (result.action === 'cancel') {
781
- safeExit('main', 0, 'agent-api-key-cancelled');
782
- }
783
- if (result.action === 'use-default') {
784
- // Apply user's default LLM to the agent config
785
- mergedConfig = applyUserPreferences(mergedConfig, preferences);
786
- // Also resolve the actual API key from environment
787
- // (preferences store env var reference like $GOOGLE_API_KEY, not the actual key)
788
- const userApiKey = resolveApiKeyForProvider(preferences.llm.provider);
789
- if (userApiKey) {
790
- mergedConfig.llm.apiKey = userApiKey;
791
- }
792
- logger.debug('Applied user preferences to agent (user chose default)', {
793
- provider: preferences.llm.provider,
794
- model: preferences.llm.model,
795
- });
796
- }
797
- if (result.action === 'add-key' && result.apiKey) {
798
- // User added the API key - update the config
799
- mergedConfig.llm.apiKey = result.apiKey;
800
- logger.debug('Applied new API key to agent config');
801
- }
802
- }
803
- else {
804
- // No default LLM to fall back to - show warnings only
805
- console.log(chalk.yellow('\n⚠️ Agent Compatibility Notice:'));
806
- for (const warning of compatibility.warnings) {
807
- console.log(chalk.yellow(` ${warning}`));
808
- }
809
- if (compatibility.instructions.length > 0) {
810
- console.log(chalk.dim('\n To fix:'));
811
- for (const instruction of compatibility.instructions) {
812
- console.log(chalk.dim(` ${instruction}`));
813
- }
814
- }
815
- console.log(''); // Empty line for spacing
816
- }
817
- }
818
- }
819
1099
  // Clean up null values from config (can happen from YAML files with explicit nulls)
820
1100
  // This prevents "Expected string, received null" errors for optional fields
821
1101
  const cleanedConfig = cleanNullValues(mergedConfig);
822
- // Enrich config with per-agent paths BEFORE validation
1102
+ // Load image first to get bundled plugins
1103
+ // Priority: CLI flag > Agent config > Environment variable > Default
1104
+ const imageNameForEnrichment = opts.image || // --image flag
1105
+ cleanedConfig.image || // image field in agent config
1106
+ process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
1107
+ '@dexto/image-local'; // Default for convenience
1108
+ let imageMetadataForEnrichment = null;
1109
+ try {
1110
+ const imageModule = await import(imageNameForEnrichment);
1111
+ imageMetadataForEnrichment = imageModule.imageMetadata || null;
1112
+ logger.debug(`Loaded image for enrichment: ${imageNameForEnrichment}`);
1113
+ }
1114
+ catch (err) {
1115
+ console.error(`❌ Failed to load image '${imageNameForEnrichment}'`);
1116
+ if (err instanceof Error) {
1117
+ logger.debug(`Image load error: ${err.message}`);
1118
+ }
1119
+ safeExit('main', 1, 'image-load-failed');
1120
+ }
1121
+ // Enrich config with per-agent paths and bundled plugins BEFORE validation
823
1122
  // Enrichment adds filesystem paths to storage (schema has in-memory defaults)
824
1123
  // Interactive CLI mode: only log to file (console would interfere with chat UI)
825
1124
  const isInteractiveCli = opts.mode === 'cli' && !headlessInput;
826
1125
  const enrichedConfig = enrichAgentConfig(cleanedConfig, resolvedPath, {
827
1126
  isInteractiveCli,
828
1127
  logLevel: 'info', // CLI uses info-level logging for visibility
1128
+ bundledPlugins: imageMetadataForEnrichment?.bundledPlugins || [],
829
1129
  });
830
1130
  // Validate enriched config with interactive setup if needed (for API key issues)
831
1131
  // isInteractiveMode is defined above the try block
832
- const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode });
1132
+ const validationResult = await validateAgentConfig(enrichedConfig, opts.interactive !== false, { strict: !isInteractiveMode, agentPath: resolvedPath });
833
1133
  if (validationResult.success && validationResult.config) {
834
1134
  validatedConfig = validationResult.config;
835
1135
  }
@@ -845,31 +1145,13 @@ program
845
1145
  // Validation failed and user didn't skip - show next steps and exit
846
1146
  safeExit('main', 1, 'config-validation-failed');
847
1147
  }
848
- // ——— LOAD IMAGE DYNAMICALLY (if specified) ———
849
- // Priority: CLI flag > Agent config > Environment variable > Default
850
- // Images are optional, but default to image-local for convenience
851
- const imageName = opts.image || // --image flag
852
- validatedConfig.image || // image field in agent config
853
- process.env.DEXTO_IMAGE || // DEXTO_IMAGE env var
854
- '@dexto/image-local'; // Default for convenience
855
- try {
856
- await import(imageName);
857
- logger.debug(`Loaded image: ${imageName}`);
858
- }
859
- catch (err) {
860
- console.error(`❌ Failed to load image '${imageName}'`);
861
- console.error(`💡 Install it with: ${existsSync('package.json') ? 'npm install' : 'npm install -g'} ${imageName}`);
862
- if (err instanceof Error) {
863
- logger.debug(`Image load error: ${err.message}`);
864
- }
865
- safeExit('main', 1, 'image-load-failed');
866
- }
867
1148
  // Validate that if config specifies an image, it matches what was loaded
868
1149
  // Skip this check if user explicitly provided --image flag (intentional override)
1150
+ // Note: Image was already loaded earlier before enrichment
869
1151
  if (!opts.image &&
870
1152
  validatedConfig.image &&
871
- validatedConfig.image !== imageName) {
872
- console.error(`❌ Config specifies image '${validatedConfig.image}' but '${imageName}' was loaded instead`);
1153
+ validatedConfig.image !== imageNameForEnrichment) {
1154
+ console.error(`❌ Config specifies image '${validatedConfig.image}' but '${imageNameForEnrichment}' was loaded instead`);
873
1155
  console.error(`💡 Either remove 'image' from config or ensure it matches the loaded image`);
874
1156
  safeExit('main', 1, 'image-mismatch');
875
1157
  }
@@ -913,13 +1195,6 @@ program
913
1195
  let agent;
914
1196
  let derivedAgentId;
915
1197
  try {
916
- // Show startup message (respecting privacy mode)
917
- if (process.env.DEXTO_PRIVACY_MODE === 'true') {
918
- console.error(`🚀 Initializing Dexto...`);
919
- }
920
- else {
921
- console.error(`🚀 Initializing Dexto with config: ${resolvedPath}`);
922
- }
923
1198
  // Set run mode for tool confirmation provider
924
1199
  process.env.DEXTO_RUN_MODE = opts.mode;
925
1200
  // Apply --strict flag to all server configs
@@ -932,8 +1207,29 @@ program
932
1207
  // Config is already enriched and validated - ready for agent creation
933
1208
  // DextoAgent will parse/validate again (parse-twice pattern)
934
1209
  // isInteractiveMode is already defined above for validateAgentConfig
1210
+ const sessionLoggerFactory = ({ baseLogger, agentId, sessionId, }) => {
1211
+ // Sanitize sessionId to prevent path traversal attacks
1212
+ // Allow only alphanumeric, dots, hyphens, and underscores
1213
+ const safeSessionId = sessionId.replace(/[^a-zA-Z0-9._-]/g, '_');
1214
+ const logFilePath = getDextoPath('logs', path.join(agentId, `${safeSessionId}.log`));
1215
+ // Standalone per-session file logger.
1216
+ return new DextoLogger({
1217
+ level: baseLogger.getLevel(),
1218
+ agentId,
1219
+ sessionId,
1220
+ component: DextoLogComponent.SESSION,
1221
+ transports: [new FileTransport({ path: logFilePath })],
1222
+ });
1223
+ };
1224
+ const mcpAuthProviderFactory = opts.mode === 'cli'
1225
+ ? (await import('./cli/mcp/oauth-factory.js')).createMcpAuthProviderFactory({
1226
+ logger,
1227
+ })
1228
+ : null;
935
1229
  agent = new DextoAgent(validatedConfig, resolvedPath, {
936
1230
  strict: !isInteractiveMode,
1231
+ sessionLoggerFactory,
1232
+ mcpAuthProviderFactory,
937
1233
  });
938
1234
  // Start the agent (initialize async services)
939
1235
  // - web/server modes: initializeHonoApi will set approval handler and start the agent
@@ -1104,6 +1400,23 @@ program
1104
1400
  // Create session eagerly so slash commands work immediately
1105
1401
  const session = await agent.createSession();
1106
1402
  const cliSessionId = session.id;
1403
+ // Check for updates (will be shown in Ink header)
1404
+ const cliUpdateInfo = await versionCheckPromise;
1405
+ // Check if installed agents differ from bundled and prompt to sync
1406
+ const needsSync = await shouldPromptForSync(pkg.version);
1407
+ if (needsSync) {
1408
+ const shouldSync = await p.confirm({
1409
+ message: 'Agent config updates available. Sync now?',
1410
+ initialValue: true,
1411
+ });
1412
+ if (p.isCancel(shouldSync) || !shouldSync) {
1413
+ await markSyncDismissed(pkg.version);
1414
+ }
1415
+ else {
1416
+ await handleSyncAgentsCommand({ force: true, quiet: true });
1417
+ await clearSyncDismissed();
1418
+ }
1419
+ }
1107
1420
  // Interactive mode - use Ink CLI with session support
1108
1421
  // Suppress console output before starting Ink UI
1109
1422
  const originalConsole = {
@@ -1120,7 +1433,9 @@ program
1120
1433
  let inkError = undefined;
1121
1434
  try {
1122
1435
  const { startInkCliRefactored } = await import('./cli/ink-cli/InkCLIRefactored.js');
1123
- await startInkCliRefactored(agent, cliSessionId);
1436
+ await startInkCliRefactored(agent, cliSessionId, {
1437
+ updateInfo: cliUpdateInfo ?? undefined,
1438
+ });
1124
1439
  }
1125
1440
  catch (error) {
1126
1441
  inkError = error;
@@ -1173,6 +1488,11 @@ program
1173
1488
  // Start single Hono server serving both API and WebUI
1174
1489
  await startHonoApiServer(agent, port, agent.config.agentCard || {}, derivedAgentId, webRoot, webUIConfig);
1175
1490
  console.log(chalk.green(`✅ Server running at ${serverUrl}`));
1491
+ // Show update notification if available
1492
+ const webUpdateInfo = await versionCheckPromise;
1493
+ if (webUpdateInfo) {
1494
+ displayUpdateNotification(webUpdateInfo);
1495
+ }
1176
1496
  // Open WebUI in browser if webRoot is available
1177
1497
  if (webRoot) {
1178
1498
  try {
@@ -1204,6 +1524,11 @@ program
1204
1524
  console.log(' POST /api/reset - Reset conversation');
1205
1525
  console.log(' GET /api/mcp/servers - List MCP servers');
1206
1526
  console.log(' SSE support available for real-time events');
1527
+ // Show update notification if available
1528
+ const serverUpdateInfo = await versionCheckPromise;
1529
+ if (serverUpdateInfo) {
1530
+ displayUpdateNotification(serverUpdateInfo);
1531
+ }
1207
1532
  break;
1208
1533
  }
1209
1534
  // TODO: Remove if server mode is stable and supports mcp
@@ -1254,5 +1579,5 @@ program
1254
1579
  safeExit('main', 1, 'unknown-mode');
1255
1580
  }
1256
1581
  }, { timeoutMs: 0 }));
1257
- // 14) PARSE & EXECUTE
1582
+ // 17) PARSE & EXECUTE
1258
1583
  program.parseAsync(process.argv);