dexto 1.1.4 → 1.1.6

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 (130) hide show
  1. package/README.md +480 -0
  2. package/dist/analytics/constants.d.ts +19 -0
  3. package/dist/analytics/constants.d.ts.map +1 -0
  4. package/dist/analytics/constants.js +24 -0
  5. package/dist/analytics/events.d.ts +112 -0
  6. package/dist/analytics/events.d.ts.map +1 -0
  7. package/dist/analytics/events.js +6 -0
  8. package/dist/analytics/index.d.ts +37 -0
  9. package/dist/analytics/index.d.ts.map +1 -0
  10. package/dist/analytics/index.js +145 -0
  11. package/dist/analytics/state.d.ts +23 -0
  12. package/dist/analytics/state.d.ts.map +1 -0
  13. package/dist/analytics/state.js +74 -0
  14. package/dist/analytics/wrapper.d.ts +11 -0
  15. package/dist/analytics/wrapper.d.ts.map +1 -0
  16. package/dist/analytics/wrapper.js +125 -0
  17. package/dist/cli/cli.d.ts +5 -0
  18. package/dist/cli/cli.d.ts.map +1 -1
  19. package/dist/cli/cli.js +10 -4
  20. package/dist/cli/commands/{interactive-commands/session/helpers → helpers}/formatters.d.ts +1 -1
  21. package/dist/cli/commands/helpers/formatters.d.ts.map +1 -0
  22. package/dist/cli/commands/{interactive-commands/session/helpers → helpers}/formatters.js +1 -1
  23. package/dist/cli/commands/install.d.ts.map +1 -1
  24. package/dist/cli/commands/install.js +56 -2
  25. package/dist/cli/commands/interactive-commands/session/index.d.ts +1 -1
  26. package/dist/cli/commands/interactive-commands/session/index.d.ts.map +1 -1
  27. package/dist/cli/commands/interactive-commands/session/index.js +1 -1
  28. package/dist/cli/commands/interactive-commands/session/session-commands.d.ts.map +1 -1
  29. package/dist/cli/commands/interactive-commands/session/session-commands.js +10 -76
  30. package/dist/cli/commands/list-agents.d.ts +2 -2
  31. package/dist/cli/commands/session-commands.d.ts +28 -0
  32. package/dist/cli/commands/session-commands.d.ts.map +1 -0
  33. package/dist/cli/commands/session-commands.js +184 -0
  34. package/dist/cli/commands/setup.d.ts +2 -2
  35. package/dist/cli/commands/setup.d.ts.map +1 -1
  36. package/dist/cli/commands/setup.js +9 -0
  37. package/dist/cli/commands/uninstall.d.ts.map +1 -1
  38. package/dist/cli/commands/uninstall.js +42 -1
  39. package/dist/cli/utils/api-key-setup.js +1 -1
  40. package/dist/index.js +271 -75
  41. package/dist/webui/.next/standalone/.next/static/chunks/854-2a6d5a5297a15d52.js +1 -0
  42. package/dist/webui/.next/standalone/.next/static/chunks/app/{layout-615a56c6184a488f.js → layout-dde711766eda096b.js} +1 -1
  43. package/dist/webui/.next/standalone/.next/static/chunks/app/{page-24123c97236d46cb.js → page-cf95b233c1df6dcd.js} +1 -1
  44. package/dist/webui/.next/standalone/.next/static/css/9cdfb06589a2f6ce.css +3 -0
  45. package/dist/webui/.next/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  46. package/dist/webui/.next/standalone/package.json +2 -1
  47. package/dist/webui/.next/standalone/packages/webui/.next/BUILD_ID +1 -1
  48. package/dist/webui/.next/standalone/packages/webui/.next/app-build-manifest.json +5 -7
  49. package/dist/webui/.next/standalone/packages/webui/.next/build-manifest.json +2 -2
  50. package/dist/webui/.next/standalone/packages/webui/.next/prerender-manifest.json +3 -3
  51. package/dist/webui/.next/standalone/packages/webui/.next/required-server-files.json +1 -1
  52. package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page.js +2 -2
  53. package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page.js.nft.json +1 -1
  54. package/dist/webui/.next/standalone/packages/webui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  55. package/dist/webui/.next/standalone/packages/webui/.next/server/app/page.js +3 -3
  56. package/dist/webui/.next/standalone/packages/webui/.next/server/app/page.js.nft.json +1 -1
  57. package/dist/webui/.next/standalone/packages/webui/.next/server/app/page_client-reference-manifest.js +1 -1
  58. package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page.js +3 -3
  59. package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page.js.nft.json +1 -1
  60. package/dist/webui/.next/standalone/packages/webui/.next/server/app/playground/page_client-reference-manifest.js +1 -1
  61. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/43.js +1 -4
  62. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/619.js +30 -0
  63. package/dist/webui/.next/standalone/packages/webui/.next/server/next-font-manifest.js +1 -1
  64. package/dist/webui/.next/standalone/packages/webui/.next/server/next-font-manifest.json +1 -1
  65. package/dist/webui/.next/standalone/packages/webui/.next/server/pages/500.html +1 -1
  66. package/dist/webui/.next/standalone/packages/webui/.next/server/pages/_error.js +2 -2
  67. package/dist/webui/.next/standalone/packages/webui/.next/server/server-reference-manifest.json +1 -1
  68. package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/854-2a6d5a5297a15d52.js +1 -0
  69. package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/app/{layout-615a56c6184a488f.js → layout-dde711766eda096b.js} +1 -1
  70. package/dist/webui/.next/{static/chunks/app/page-24123c97236d46cb.js → standalone/packages/webui/.next/static/chunks/app/page-cf95b233c1df6dcd.js} +1 -1
  71. package/dist/webui/.next/standalone/packages/webui/.next/static/css/9cdfb06589a2f6ce.css +3 -0
  72. package/dist/webui/.next/standalone/packages/webui/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  73. package/dist/webui/.next/standalone/packages/webui/package.json +1 -1
  74. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo.svg +31 -0
  75. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo_icon.svg +14 -0
  76. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo_icon_light.svg +17 -0
  77. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto/dexto_logo_light.svg +31 -0
  78. package/dist/webui/.next/standalone/packages/webui/server.js +1 -1
  79. package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo.svg +31 -0
  80. package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo_icon.svg +14 -0
  81. package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo_icon_light.svg +17 -0
  82. package/dist/webui/.next/standalone/public/logos/dexto/dexto_logo_light.svg +31 -0
  83. package/dist/webui/.next/static/chunks/854-2a6d5a5297a15d52.js +1 -0
  84. package/dist/webui/.next/static/chunks/app/{layout-615a56c6184a488f.js → layout-dde711766eda096b.js} +1 -1
  85. package/dist/webui/.next/{standalone/packages/webui/.next/static/chunks/app/page-24123c97236d46cb.js → static/chunks/app/page-cf95b233c1df6dcd.js} +1 -1
  86. package/dist/webui/.next/static/css/9cdfb06589a2f6ce.css +3 -0
  87. package/dist/webui/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  88. package/dist/webui/package.json +1 -1
  89. package/dist/webui/public/logos/dexto/dexto_logo.svg +31 -0
  90. package/dist/webui/public/logos/dexto/dexto_logo_icon.svg +14 -0
  91. package/dist/webui/public/logos/dexto/dexto_logo_icon_light.svg +17 -0
  92. package/dist/webui/public/logos/dexto/dexto_logo_light.svg +31 -0
  93. package/package.json +7 -4
  94. package/dist/cli/commands/interactive-commands/session/helpers/formatters.d.ts.map +0 -1
  95. package/dist/webui/.next/standalone/.next/static/chunks/221-608218ab04068cb2.js +0 -1
  96. package/dist/webui/.next/standalone/.next/static/chunks/854-47418382efcea1d4.js +0 -1
  97. package/dist/webui/.next/standalone/.next/static/css/75b11629ebbc461a.css +0 -3
  98. package/dist/webui/.next/standalone/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  99. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/387.js +0 -14
  100. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/392.js +0 -1
  101. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/450.js +0 -139
  102. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/514.js +0 -2
  103. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/531.js +0 -1
  104. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/723.js +0 -1
  105. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/737.js +0 -1
  106. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/767.js +0 -20
  107. package/dist/webui/.next/standalone/packages/webui/.next/server/chunks/89.js +0 -95
  108. package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/221-608218ab04068cb2.js +0 -1
  109. package/dist/webui/.next/standalone/packages/webui/.next/static/chunks/854-47418382efcea1d4.js +0 -1
  110. package/dist/webui/.next/standalone/packages/webui/.next/static/css/75b11629ebbc461a.css +0 -3
  111. package/dist/webui/.next/standalone/packages/webui/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  112. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto_logo.svg +0 -1
  113. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto_logo_light.svg +0 -18
  114. package/dist/webui/.next/standalone/packages/webui/public/logos/dexto_logo_no_text.png +0 -0
  115. package/dist/webui/.next/standalone/public/logos/dexto_logo.svg +0 -1
  116. package/dist/webui/.next/standalone/public/logos/dexto_logo_light.svg +0 -18
  117. package/dist/webui/.next/standalone/public/logos/dexto_logo_no_text.png +0 -0
  118. package/dist/webui/.next/static/chunks/221-608218ab04068cb2.js +0 -1
  119. package/dist/webui/.next/static/chunks/854-47418382efcea1d4.js +0 -1
  120. package/dist/webui/.next/static/css/75b11629ebbc461a.css +0 -3
  121. package/dist/webui/.next/static/media/569ce4b8f30dc480-s.p.woff2 +0 -0
  122. package/dist/webui/public/logos/dexto_logo.svg +0 -1
  123. package/dist/webui/public/logos/dexto_logo_light.svg +0 -18
  124. package/dist/webui/public/logos/dexto_logo_no_text.png +0 -0
  125. /package/dist/webui/.next/standalone/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_buildManifest.js +0 -0
  126. /package/dist/webui/.next/standalone/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_ssgManifest.js +0 -0
  127. /package/dist/webui/.next/standalone/packages/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_buildManifest.js +0 -0
  128. /package/dist/webui/.next/standalone/packages/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_ssgManifest.js +0 -0
  129. /package/dist/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_buildManifest.js +0 -0
  130. /package/dist/webui/.next/static/{abgua6ybDH7yT2b5rvLlU → OsQGbLQi9bS4RzNw_6wgr}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -8,12 +8,14 @@ import { createRequire } from 'module';
8
8
  import { Command } from 'commander';
9
9
  import * as p from '@clack/prompts';
10
10
  import chalk from 'chalk';
11
+ import { initAnalytics, capture } from './analytics/index.js';
12
+ import { withAnalytics, safeExit, ExitSignal } from './analytics/wrapper.js';
11
13
  // Use createRequire to import package.json without experimental warning
12
14
  const require = createRequire(import.meta.url);
13
15
  const pkg = require('../package.json');
14
16
  import { logger, getProviderFromModel, getAllSupportedModels, DextoAgent, loadAgentConfig, } from '@dexto/core';
15
17
  import { resolveAgentPath, getAgentRegistry, isPath, resolveApiKeyForProvider } from '@dexto/core';
16
- import { startAiCli, startHeadlessCli } from './cli/cli.js';
18
+ import { startAiCli, startHeadlessCli, loadMostRecentSession } from './cli/cli.js';
17
19
  import { startApiServer } from './api/server.js';
18
20
  import { startDiscordBot } from './discord/bot.js';
19
21
  import { startTelegramBot } from './telegram/bot.js';
@@ -23,6 +25,7 @@ import { applyCLIOverrides } from './config/cli-overrides.js';
23
25
  import { getPort } from '@dexto/core';
24
26
  import { createDextoProject, createTsconfigJson, addDextoScriptsToPackageJson, postCreateDexto, initDexto, postInitDexto, getUserInputToInitDextoApp, } from './cli/commands/index.js';
25
27
  import { handleSetupCommand, handleInstallCommand, handleUninstallCommand, handleListAgentsCommand, handleWhichCommand, } from './cli/commands/index.js';
28
+ import { handleSessionListCommand, handleSessionHistoryCommand, handleSessionDeleteCommand, handleSessionSearchCommand, } from './cli/commands/session-commands.js';
26
29
  import { requiresSetup } from './cli/utils/setup-utils.js';
27
30
  import { checkForFileInCurrentDirectory, FileNotFoundError } from './cli/utils/package-mgmt.js';
28
31
  import { startNextJsWebServer } from './web.js';
@@ -30,19 +33,22 @@ import { initializeMcpServer, createMcpTransport } from './api/mcp/mcp_handler.j
30
33
  import { createAgentCard } from '@dexto/core';
31
34
  import { initializeMcpToolAggregationServer } from './api/mcp/tool-aggregation-handler.js';
32
35
  const program = new Command();
36
+ // Initialize analytics early (no-op if disabled)
37
+ await initAnalytics({ appVersion: pkg.version });
33
38
  // 1) GLOBAL OPTIONS
34
39
  program
35
40
  .name('dexto')
36
41
  .description('AI-powered CLI and WebUI for interacting with MCP servers')
37
42
  .version(pkg.version, '-v, --version', 'output the current version')
38
43
  .option('-a, --agent <name|path>', 'Agent name or path to agent config file')
39
- .option('-p, --prompt <text>', 'One-shot prompt text. Alternatively provide a single quoted string as positional argument.')
44
+ .option('-p, --prompt <text>', 'Run prompt and exit. Alternatively provide a single quoted string as positional argument.')
40
45
  .option('-s, --strict', 'Require all server connections to succeed')
41
46
  .option('--no-verbose', 'Disable verbose output')
42
47
  .option('--no-interactive', 'Disable interactive prompts and API key setup')
43
48
  .option('-m, --model <model>', 'Specify the LLM model to use')
44
- .option('-r, --router <router>', 'Specify the LLM router to use (vercel or in-built)')
45
- .option('--new-session [sessionId]', 'Start with a new session (optionally specify session ID)')
49
+ .option('--router <router>', 'Specify the LLM router to use (vercel or in-built)')
50
+ .option('-c, --continue', 'Continue most recent conversation')
51
+ .option('-r, --resume <sessionId>', 'Resume session by ID')
46
52
  .option('--mode <mode>', 'The application in which dexto should talk to you - cli | web | server | discord | telegram | mcp', 'cli')
47
53
  .option('--web-port <port>', 'optional port for the web UI', '3000')
48
54
  .option('--no-auto-install', 'Disable automatic installation of missing agents from registry')
@@ -51,13 +57,22 @@ program
51
57
  program
52
58
  .command('create-app')
53
59
  .description('Scaffold a new Dexto Typescript app')
54
- .action(async () => {
60
+ .action(withAnalytics('create-app', async () => {
55
61
  try {
56
62
  p.intro(chalk.inverse('Dexto Create App'));
57
63
  // first setup the initial files in the project and get the project path
58
64
  const appPath = await createDextoProject();
59
65
  // then get user inputs for directory, llm etc.
60
66
  const userInput = await getUserInputToInitDextoApp();
67
+ try {
68
+ capture('dexto_create', {
69
+ provider: userInput.llmProvider,
70
+ providedKey: Boolean(userInput.llmApiKey),
71
+ });
72
+ }
73
+ catch {
74
+ // Analytics failures should not block CLI execution.
75
+ }
61
76
  // move to project directory, then add the dexto scripts to the package.json and create the tsconfig.json
62
77
  process.chdir(appPath);
63
78
  await addDextoScriptsToPackageJson(userInput.directory, appPath);
@@ -67,18 +82,20 @@ program
67
82
  p.outro(chalk.greenBright('Dexto app created and initialized successfully!'));
68
83
  // add notes for users to get started with their newly created Dexto project
69
84
  await postCreateDexto(appPath, userInput.directory);
70
- process.exit(0);
85
+ safeExit('create-app', 0);
71
86
  }
72
87
  catch (err) {
88
+ if (err instanceof ExitSignal)
89
+ throw err;
73
90
  console.error(`❌ dexto create-app command failed: ${err}`);
74
- process.exit(1);
91
+ safeExit('create-app', 1, 'error');
75
92
  }
76
- });
93
+ }));
77
94
  // 3) `init-app` SUB-COMMAND
78
95
  program
79
96
  .command('init-app')
80
97
  .description('Initialize an existing Typescript app with Dexto')
81
- .action(async () => {
98
+ .action(withAnalytics('init-app', async () => {
82
99
  try {
83
100
  // pre-condition: check that package.json and tsconfig.json exist in current directory to know that project is valid
84
101
  await checkForFileInCurrentDirectory('package.json');
@@ -86,22 +103,33 @@ program
86
103
  // start intro
87
104
  p.intro(chalk.inverse('Dexto Init App'));
88
105
  const userInput = await getUserInputToInitDextoApp();
106
+ try {
107
+ capture('dexto_init', {
108
+ provider: userInput.llmProvider,
109
+ providedKey: Boolean(userInput.llmApiKey),
110
+ });
111
+ }
112
+ catch {
113
+ // Analytics failures should not block CLI execution.
114
+ }
89
115
  await initDexto(userInput.directory, userInput.createExampleFile, userInput.llmProvider, userInput.llmApiKey);
90
116
  p.outro(chalk.greenBright('Dexto app initialized successfully!'));
91
117
  // add notes for users to get started with their new initialized Dexto project
92
118
  await postInitDexto(userInput.directory);
93
- process.exit(0);
119
+ safeExit('init-app', 0);
94
120
  }
95
121
  catch (err) {
122
+ if (err instanceof ExitSignal)
123
+ throw err;
96
124
  // if the package.json or tsconfig.json is not found, we give instructions to create a new project
97
125
  if (err instanceof FileNotFoundError) {
98
126
  console.error(`❌ ${err.message} Run "dexto create-app" to create a new app`);
99
- process.exit(1);
127
+ safeExit('init-app', 1, 'file-not-found');
100
128
  }
101
129
  console.error(`❌ Initialization failed: ${err}`);
102
- process.exit(1);
130
+ safeExit('init-app', 1, 'error');
103
131
  }
104
- });
132
+ }));
105
133
  // 4) `setup` SUB-COMMAND
106
134
  program
107
135
  .command('setup')
@@ -111,16 +139,18 @@ program
111
139
  .option('--default-agent <agent>', 'Default agent name (default: default-agent)')
112
140
  .option('--no-interactive', 'Skip interactive prompts and API key setup')
113
141
  .option('--force', 'Overwrite existing setup without confirmation')
114
- .action(async (options) => {
142
+ .action(withAnalytics('setup', async (options) => {
115
143
  try {
116
144
  await handleSetupCommand(options);
117
- process.exit(0);
145
+ safeExit('setup', 0);
118
146
  }
119
147
  catch (err) {
148
+ if (err instanceof ExitSignal)
149
+ throw err;
120
150
  console.error(`❌ dexto setup command failed: ${err}. Check logs in ~/.dexto/logs/dexto.log for more information`);
121
- process.exit(1);
151
+ safeExit('setup', 1, 'error');
122
152
  }
123
- });
153
+ }));
124
154
  // 5) `install` SUB-COMMAND
125
155
  program
126
156
  .command('install [agents...]')
@@ -128,32 +158,36 @@ program
128
158
  .option('--all', 'Install all available agents from registry')
129
159
  .option('--no-inject-preferences', 'Skip injecting global preferences into installed agents')
130
160
  .option('--force', 'Force reinstall even if agent is already installed')
131
- .action(async (agents = [], options) => {
161
+ .action(withAnalytics('install', async (agents = [], options) => {
132
162
  try {
133
163
  await handleInstallCommand(agents, options);
134
- process.exit(0);
164
+ safeExit('install', 0);
135
165
  }
136
166
  catch (err) {
167
+ if (err instanceof ExitSignal)
168
+ throw err;
137
169
  console.error(`❌ dexto install command failed: ${err}`);
138
- process.exit(1);
170
+ safeExit('install', 1, 'error');
139
171
  }
140
- });
172
+ }));
141
173
  // 6) `uninstall` SUB-COMMAND
142
174
  program
143
175
  .command('uninstall [agents...]')
144
176
  .description('Uninstall agents from the local installation')
145
177
  .option('--all', 'Uninstall all installed agents')
146
178
  .option('--force', 'Force uninstall even if agent is protected (e.g., default-agent)')
147
- .action(async (agents, options) => {
179
+ .action(withAnalytics('uninstall', async (agents, options) => {
148
180
  try {
149
181
  await handleUninstallCommand(agents, options);
150
- process.exit(0);
182
+ safeExit('uninstall', 0);
151
183
  }
152
184
  catch (err) {
185
+ if (err instanceof ExitSignal)
186
+ throw err;
153
187
  console.error(`❌ dexto uninstall command failed: ${err}`);
154
- process.exit(1);
188
+ safeExit('uninstall', 1, 'error');
155
189
  }
156
- });
190
+ }));
157
191
  // 7) `list-agents` SUB-COMMAND
158
192
  program
159
193
  .command('list-agents')
@@ -161,31 +195,153 @@ program
161
195
  .option('--verbose', 'Show detailed agent information')
162
196
  .option('--installed', 'Show only installed agents')
163
197
  .option('--available', 'Show only available agents')
164
- .action(async (options) => {
198
+ .action(withAnalytics('list-agents', async (options) => {
165
199
  try {
166
200
  await handleListAgentsCommand(options);
167
- process.exit(0);
201
+ safeExit('list-agents', 0);
168
202
  }
169
203
  catch (err) {
204
+ if (err instanceof ExitSignal)
205
+ throw err;
170
206
  console.error(`❌ dexto list-agents command failed: ${err}`);
171
- process.exit(1);
207
+ safeExit('list-agents', 1, 'error');
172
208
  }
173
- });
209
+ }));
174
210
  // 8) `which` SUB-COMMAND
175
211
  program
176
212
  .command('which <agent>')
177
213
  .description('Show the path to an agent')
178
- .action(async (agent) => {
214
+ .action(withAnalytics('which', async (agent) => {
179
215
  try {
180
216
  await handleWhichCommand(agent);
181
- process.exit(0);
217
+ safeExit('which', 0);
182
218
  }
183
219
  catch (err) {
220
+ if (err instanceof ExitSignal)
221
+ throw err;
184
222
  console.error(`❌ dexto which command failed: ${err}`);
185
- process.exit(1);
223
+ safeExit('which', 1, 'error');
224
+ }
225
+ }));
226
+ // Helper to bootstrap a minimal agent for non-interactive session/search ops
227
+ async function bootstrapAgentFromGlobalOpts() {
228
+ const globalOpts = program.opts();
229
+ const resolvedPath = await resolveAgentPath(globalOpts.agent, globalOpts.autoInstall !== false, true);
230
+ const rawConfig = await loadAgentConfig(resolvedPath);
231
+ const mergedConfig = applyCLIOverrides(rawConfig, globalOpts);
232
+ const agent = new DextoAgent(mergedConfig, globalOpts.agent);
233
+ await agent.start();
234
+ // Register graceful shutdown
235
+ const shutdown = async () => {
236
+ try {
237
+ await agent.stop();
238
+ }
239
+ catch (_err) {
240
+ // Ignore shutdown errors
241
+ }
242
+ };
243
+ process.on('SIGINT', shutdown);
244
+ process.on('SIGTERM', shutdown);
245
+ return agent;
246
+ }
247
+ // 9) `session` SUB-COMMAND
248
+ const sessionCommand = program.command('session').description('Manage chat sessions');
249
+ sessionCommand
250
+ .command('list')
251
+ .description('List all sessions')
252
+ .action(withAnalytics('session list', async () => {
253
+ try {
254
+ const agent = await bootstrapAgentFromGlobalOpts();
255
+ await handleSessionListCommand(agent);
256
+ await agent.stop();
257
+ safeExit('session list', 0);
186
258
  }
187
- });
188
- // 9) `mcp` SUB-COMMAND
259
+ catch (err) {
260
+ if (err instanceof ExitSignal)
261
+ throw err;
262
+ console.error(`❌ dexto session list command failed: ${err}`);
263
+ safeExit('session list', 1, 'error');
264
+ }
265
+ }));
266
+ sessionCommand
267
+ .command('history')
268
+ .description('Show session history')
269
+ .argument('[sessionId]', 'Session ID (defaults to current session)')
270
+ .action(withAnalytics('session history', async (sessionId) => {
271
+ try {
272
+ const agent = await bootstrapAgentFromGlobalOpts();
273
+ await handleSessionHistoryCommand(agent, sessionId);
274
+ await agent.stop();
275
+ safeExit('session history', 0);
276
+ }
277
+ catch (err) {
278
+ if (err instanceof ExitSignal)
279
+ throw err;
280
+ console.error(`❌ dexto session history command failed: ${err}`);
281
+ safeExit('session history', 1, 'error');
282
+ }
283
+ }));
284
+ sessionCommand
285
+ .command('delete')
286
+ .description('Delete a session')
287
+ .argument('<sessionId>', 'Session ID to delete')
288
+ .action(withAnalytics('session delete', async (sessionId) => {
289
+ try {
290
+ const agent = await bootstrapAgentFromGlobalOpts();
291
+ await handleSessionDeleteCommand(agent, sessionId);
292
+ await agent.stop();
293
+ safeExit('session delete', 0);
294
+ }
295
+ catch (err) {
296
+ if (err instanceof ExitSignal)
297
+ throw err;
298
+ console.error(`❌ dexto session delete command failed: ${err}`);
299
+ safeExit('session delete', 1, 'error');
300
+ }
301
+ }));
302
+ // 10) `search` SUB-COMMAND
303
+ program
304
+ .command('search')
305
+ .description('Search session history')
306
+ .argument('<query>', 'Search query')
307
+ .option('--session <sessionId>', 'Search in specific session')
308
+ .option('--role <role>', 'Filter by role (user, assistant, system, tool)')
309
+ .option('--limit <number>', 'Limit number of results', '10')
310
+ .action(withAnalytics('search', async (query, options) => {
311
+ try {
312
+ const agent = await bootstrapAgentFromGlobalOpts();
313
+ const searchOptions = {};
314
+ if (options.session) {
315
+ searchOptions.sessionId = options.session;
316
+ }
317
+ if (options.role) {
318
+ const allowed = new Set(['user', 'assistant', 'system', 'tool']);
319
+ if (!allowed.has(options.role)) {
320
+ console.error(`❌ Invalid role: ${options.role}. Use one of: user, assistant, system, tool`);
321
+ safeExit('search', 1, 'invalid-role');
322
+ }
323
+ searchOptions.role = options.role;
324
+ }
325
+ if (options.limit) {
326
+ const parsed = parseInt(options.limit, 10);
327
+ if (Number.isNaN(parsed) || parsed <= 0) {
328
+ console.error(`❌ Invalid --limit: ${options.limit}. Use a positive integer (e.g., 10).`);
329
+ safeExit('search', 1, 'invalid-limit');
330
+ }
331
+ searchOptions.limit = parsed;
332
+ }
333
+ await handleSessionSearchCommand(agent, query, searchOptions);
334
+ await agent.stop();
335
+ safeExit('search', 0);
336
+ }
337
+ catch (err) {
338
+ if (err instanceof ExitSignal)
339
+ throw err;
340
+ console.error(`❌ dexto search command failed: ${err}`);
341
+ safeExit('search', 1, 'error');
342
+ }
343
+ }));
344
+ // 11) `mcp` SUB-COMMAND
189
345
  // For now, this mode simply aggregates and re-expose tools from configured MCP servers (no agent)
190
346
  // dexto --mode mcp will be moved to this sub-command in the future
191
347
  program
@@ -196,13 +352,13 @@ program
196
352
  .option('--group-servers', 'Aggregate and re-expose tools from configured MCP servers (required for now)')
197
353
  .option('--name <n>', 'Name for the MCP server', 'dexto-tools')
198
354
  .option('--version <version>', 'Version for the MCP server', '1.0.0')
199
- .action(async (options) => {
355
+ .action(withAnalytics('mcp', async (options) => {
200
356
  try {
201
357
  // Validate that --group-servers flag is provided (mandatory for now)
202
358
  if (!options.groupServers) {
203
359
  console.error('❌ The --group-servers flag is required. This command currently only supports aggregating and re-exposing tools from configured MCP servers.');
204
360
  console.error('Usage: dexto mcp --group-servers');
205
- process.exit(1);
361
+ safeExit('mcp', 1, 'missing-group-servers');
206
362
  }
207
363
  // Load and resolve config
208
364
  // Get the global agent option from the main program
@@ -215,7 +371,7 @@ program
215
371
  // Validate that MCP servers are configured
216
372
  if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) {
217
373
  console.error('❌ No MCP servers configured. Please configure mcpServers in your config file.');
218
- process.exit(1);
374
+ safeExit('mcp', 1, 'no-mcp-servers');
219
375
  }
220
376
  const { ServerConfigsSchema } = await import('@dexto/core');
221
377
  const validatedServers = ServerConfigsSchema.parse(config.mcpServers);
@@ -231,28 +387,37 @@ program
231
387
  logger.info('MCP tool aggregation server started successfully');
232
388
  }
233
389
  catch (err) {
390
+ if (err instanceof ExitSignal)
391
+ throw err;
234
392
  // Write to stderr to avoid interfering with MCP protocol
235
393
  process.stderr.write(`MCP tool aggregation server startup failed: ${err}\n`);
236
- process.exit(1);
394
+ safeExit('mcp', 1, 'mcp-agg-failed');
237
395
  }
238
- });
396
+ }, { timeoutMs: 0 }));
239
397
  // 10) Main dexto CLI - Interactive/One shot (CLI/HEADLESS) or run in other modes (--mode web/discord/telegram)
240
398
  program
241
399
  .argument('[prompt...]', 'Natural-language prompt to run once. If not passed, dexto will start as an interactive CLI')
242
400
  // Main customer facing description
243
- .description('Dexto CLI allows you to talk to Dexto, build custom AI Agents, ' +
244
- 'build complex AI applications like Cursor, and more.\n\n' +
245
- // TODO: Add `dexto tell me about your cli` starter prompt
246
- 'Run dexto interactive CLI with `dexto` or run a one-shot prompt with `dexto -p "<prompt>"` or `dexto "<prompt>"`\n' +
247
- 'Start with a new session using `dexto --new-session [sessionId]`\n' +
248
- 'Run dexto web UI with `dexto --mode web`\n' +
249
- 'Run dexto as a server (REST APIs + WebSockets) with `dexto --mode server`\n' +
250
- 'Run dexto as a discord bot with `dexto --mode discord`\n' +
251
- 'Run dexto as a telegram bot with `dexto --mode telegram`\n' +
252
- 'Run dexto agent as an MCP server with `dexto --mode mcp`\n' +
253
- 'Run dexto as an MCP server aggregator with `dexto mcp --group-servers`\n\n' +
254
- 'Check subcommands for more features. Check https://github.com/truffle-ai/dexto for documentation on how to customize dexto and other examples')
255
- .action(async (prompt = []) => {
401
+ .description('Dexto CLI - AI-powered assistant with session management\n\n' +
402
+ 'Basic Usage:\n' +
403
+ ' dexto Start interactive REPL\n' +
404
+ ' dexto "query" Start REPL with initial prompt\n' +
405
+ ' dexto -p "query" Run query, then exit\n' +
406
+ ' cat file | dexto -p "query" Process piped content\n\n' +
407
+ 'Session Management:\n' +
408
+ ' dexto -c Continue most recent conversation\n' +
409
+ ' dexto -c -p "query" Continue conversation, then exit\n' +
410
+ ' dexto -r "<session-id>" "query" Resume session by ID\n\n' +
411
+ 'Advanced Modes:\n' +
412
+ ' dexto --mode web Run web UI\n' +
413
+ ' dexto --mode server Run as API server\n' +
414
+ ' dexto --mode discord Run as Discord bot\n' +
415
+ ' dexto --mode telegram Run as Telegram bot\n' +
416
+ ' dexto --mode mcp Run as MCP server\n\n' +
417
+ 'Session Commands: dexto session list|history|delete • search\n' +
418
+ 'Search: dexto search <query> [--session <id>] [--role <role>]\n\n' +
419
+ 'See https://github.com/truffle-ai/dexto for more examples and documentation')
420
+ .action(withAnalytics('main', async (prompt = []) => {
256
421
  // ——— ENV CHECK (optional) ———
257
422
  if (!existsSync('.env')) {
258
423
  logger.debug('WARNING: .env file not found; copy .env.example and set your API keys.');
@@ -266,7 +431,7 @@ program
266
431
  else if (opts.prompt !== undefined) {
267
432
  // Explicit empty -p "" was provided
268
433
  console.error('❌ For headless one-shot mode, prompt cannot be empty. Provide a non-empty prompt with -p/--prompt or use positional argument.');
269
- process.exit(1);
434
+ safeExit('main', 1, 'empty-prompt');
270
435
  }
271
436
  else if (prompt.length > 0) {
272
437
  // Enforce quoted single positional argument for headless mode
@@ -275,7 +440,7 @@ program
275
440
  }
276
441
  else {
277
442
  console.error('❌ For headless one-shot mode, pass the prompt in double quotes as a single argument (e.g., "say hello") or use -p/--prompt.');
278
- process.exit(1);
443
+ safeExit('main', 1, 'too-many-positional');
279
444
  }
280
445
  }
281
446
  // Note: Agent selection must be passed via -a/--agent. We no longer interpret
@@ -289,12 +454,12 @@ program
289
454
  catch (err) {
290
455
  console.error(`❌ ${err.message}`);
291
456
  console.error(`Supported models: ${getAllSupportedModels().join(', ')}`);
292
- process.exit(1);
457
+ safeExit('main', 1, 'invalid-model');
293
458
  }
294
459
  const apiKey = resolveApiKeyForProvider(provider);
295
460
  if (!apiKey) {
296
461
  console.error(`❌ Missing API key for provider '${provider}' - please set the appropriate environment variable`);
297
- process.exit(1);
462
+ safeExit('main', 1, 'missing-api-key');
298
463
  }
299
464
  opts.provider = provider;
300
465
  opts.apiKey = apiKey;
@@ -328,7 +493,8 @@ program
328
493
  else {
329
494
  console.log('📋 No agents available in registry');
330
495
  }
331
- process.exit(1);
496
+ safeExit('main', 1, 'agent-not-in-registry');
497
+ return;
332
498
  }
333
499
  }
334
500
  // Check setup state and auto-trigger if needed
@@ -336,7 +502,7 @@ program
336
502
  if (opts.interactive === false) {
337
503
  console.error('❌ Setup required but --no-interactive flag is set.');
338
504
  console.error('💡 Run `dexto setup` to configure preferences first.');
339
- process.exit(1);
505
+ safeExit('main', 1, 'setup-required-non-interactive');
340
506
  }
341
507
  await handleSetupCommand({ interactive: true });
342
508
  }
@@ -350,9 +516,11 @@ program
350
516
  validatedConfig = await validateAgentConfig(mergedConfig, opts.interactive !== false);
351
517
  }
352
518
  catch (err) {
519
+ if (err instanceof ExitSignal)
520
+ throw err;
353
521
  // Config loading failed completely
354
522
  console.error(`❌ Failed to load configuration: ${err}`);
355
- process.exit(1);
523
+ safeExit('main', 1, 'config-load-failed');
356
524
  }
357
525
  // ——— CREATE AGENT ———
358
526
  let agent;
@@ -371,27 +539,55 @@ program
371
539
  agent = new DextoAgent(validatedConfig, opts.agent);
372
540
  // Start the agent (initialize async services)
373
541
  await agent.start();
374
- // Handle --new-session flag
375
- if (opts.newSession !== undefined) {
542
+ // Handle session options - simplified logic
543
+ if (opts.resume) {
544
+ try {
545
+ // Resume specific session by ID
546
+ await agent.loadSessionAsDefault(opts.resume);
547
+ logger.info(`Resumed session: ${opts.resume}`, null, 'cyan');
548
+ }
549
+ catch (err) {
550
+ console.error(`❌ Failed to resume session '${opts.resume}': ${err instanceof Error ? err.message : String(err)}`);
551
+ console.error('💡 Use `dexto session list` to see available sessions');
552
+ safeExit('main', 1, 'resume-failed');
553
+ }
554
+ }
555
+ else if (opts.continue) {
556
+ try {
557
+ // Continue from most recent session
558
+ await loadMostRecentSession(agent);
559
+ // If no sessions existed, create a new one to honor default-new invariant
560
+ const sessionsAfter = await agent.listSessions();
561
+ if (sessionsAfter.length === 0) {
562
+ const session = await agent.createSession();
563
+ await agent.loadSessionAsDefault(session.id);
564
+ logger.info(`Created new session: ${session.id}`, null, 'green');
565
+ }
566
+ }
567
+ catch (err) {
568
+ console.error(`❌ Failed to continue session: ${err instanceof Error ? err.message : String(err)}`);
569
+ safeExit('main', 1, 'continue-failed');
570
+ }
571
+ }
572
+ else {
573
+ // Default behavior: create new session
376
574
  try {
377
- // Use provided session ID or generate a random one
378
- const sessionId = typeof opts.newSession === 'string' && opts.newSession
379
- ? opts.newSession
380
- : undefined; // Let agent generate random ID
381
- const session = await agent.createSession(sessionId);
575
+ const session = await agent.createSession();
382
576
  await agent.loadSessionAsDefault(session.id);
383
- logger.info(`Created and loaded new session: ${session.id}`, null, 'green');
577
+ logger.info(`Created new session: ${session.id}`, null, 'green');
384
578
  }
385
579
  catch (err) {
386
580
  console.error(`❌ Failed to create new session: ${err instanceof Error ? err.message : String(err)}`);
387
- process.exit(1);
581
+ safeExit('main', 1, 'create-session-failed');
388
582
  }
389
583
  }
390
584
  }
391
585
  catch (err) {
586
+ if (err instanceof ExitSignal)
587
+ throw err;
392
588
  // Ensure config errors are shown to user, not hidden in logs
393
589
  console.error(`❌ Configuration Error: ${err.message}`);
394
- process.exit(1);
590
+ safeExit('main', 1, 'config-error');
395
591
  }
396
592
  // ——— Dispatch based on --mode ———
397
593
  switch (opts.mode) {
@@ -404,7 +600,7 @@ program
404
600
  if (headlessInput) {
405
601
  // One shot CLI
406
602
  await startHeadlessCli(agent, headlessInput);
407
- process.exit(0);
603
+ safeExit('main', 0);
408
604
  }
409
605
  else {
410
606
  await startAiCli(agent); // Interactive CLI
@@ -459,7 +655,7 @@ program
459
655
  }
460
656
  catch (err) {
461
657
  console.error('❌ Discord startup failed:', err);
462
- process.exit(1);
658
+ safeExit('main', 1, 'discord-startup-failed');
463
659
  }
464
660
  break;
465
661
  case 'telegram':
@@ -469,7 +665,7 @@ program
469
665
  }
470
666
  catch (err) {
471
667
  console.error('❌ Telegram startup failed:', err);
472
- process.exit(1);
668
+ safeExit('main', 1, 'telegram-startup-failed');
473
669
  }
474
670
  break;
475
671
  // TODO: Remove if server mode is stable and supports mcp
@@ -497,14 +693,14 @@ program
497
693
  catch (err) {
498
694
  // Write to stderr instead of stdout to avoid interfering with MCP protocol
499
695
  process.stderr.write(`MCP server startup failed: ${err}\n`);
500
- process.exit(1);
696
+ safeExit('main', 1, 'mcp-startup-failed');
501
697
  }
502
698
  break;
503
699
  }
504
700
  default:
505
701
  console.error(`❌ Unknown mode '${opts.mode}'. Use cli, web, server, discord, telegram, or mcp.`);
506
- process.exit(1);
702
+ safeExit('main', 1, 'unknown-mode');
507
703
  }
508
- });
704
+ }, { timeoutMs: 0 }));
509
705
  // 11) PARSE & EXECUTE
510
706
  program.parseAsync(process.argv);
@@ -0,0 +1 @@
1
+ "use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[854],{1236:(e,t,n)=>{n.d(t,{kz:()=>m,EQ:()=>p,j0:()=>g,PI:()=>f,P2:()=>d,KU:()=>u,Y_:()=>h});var r=n(5789);let o=["apikey","api_key","token","access_token","refresh_token","password","secret"],s=["base64","filedata","file_data","imagedata","image_data","audiodata","audio_data","data"],a=[/\bsk-[A-Za-z0-9]{20,}\b/g,/\bBearer\s+[A-Za-z0-9\-_.=]+\b/gi,/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*/g],i="[REDACTED]",l="[REDACTED_CIRCULAR]";function c(e){return e instanceof Error?e:e&&"object"==typeof e?"message"in e&&"string"==typeof e.message?Error(e.message,{cause:e}):"error"in e&&"string"==typeof e.error?Error(e.error,{cause:e}):"details"in e&&"string"==typeof e.details?Error(e.details,{cause:e}):"description"in e&&"string"==typeof e.description?Error(e.description,{cause:e}):Error(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1e3;try{if("bigint"==typeof e)return e.toString();let n=function e(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:new WeakSet;if("string"==typeof t){let e=t;for(let t of a)e=e.replace(t,i);return e}if(Array.isArray(t))return n.has(t)?l:(n.add(t),t.map(t=>e(t,n)));if(t&&"object"==typeof t){if(n.has(t))return l;n.add(t);let r={};for(let[a,l]of Object.entries(t))if(o.includes(a.toLowerCase()))r[a]=i;else{let o=function(e,t,n){var r;if("string"!=typeof e)return e;let o=t.toLowerCase();return(s.includes(o)||"data"===o&&n&&("mimeType"in n||"filename"in n||"fileName"in n))&&(r=e).length>1e3&&/^[A-Za-z0-9+/=]{1000,}$/.test(r.substring(0,1e3))?"".concat("[FILE_DATA_TRUNCATED]"," (").concat(e.length," chars)"):e}(l,a,t);r[a]=e(o,n)}return r}return t}(e),r=JSON.stringify(n,(e,t)=>t instanceof Error?{name:t.name,message:t.message,stack:t.stack}:"bigint"==typeof t?t.toString():t),c="…(truncated)",u=Number.isFinite(t)&&t>0?Math.floor(t):1e3;if("string"==typeof r){if(r.length<=u)return r;let e=Math.max(0,u-c.length);return"".concat(r.slice(0,e)).concat(c)}return String(e)}catch(t){try{return String(e)}catch(e){return"[Unserializable value]"}}}(e)):"string"==typeof e?Error(e,{cause:e}):Error(String(e),{cause:e})}function u(e){return"object"==typeof e&&null!==e&&"error"in e}function d(e){return"object"==typeof e&&null!==e&&"content"in e&&Array.isArray(e.content)}function f(e){return"object"==typeof e&&null!==e&&"type"in e&&"text"===e.type}function g(e){return"object"==typeof e&&null!==e&&"type"in e&&"image"===e.type}function m(e){return"object"==typeof e&&null!==e&&"type"in e&&"audio"===e.type}function p(e){return"object"==typeof e&&null!==e&&"type"in e&&"file"===e.type}let y=()=>"msg-".concat(Date.now(),"-").concat(Math.random().toString(36).substring(2,9));function h(e,t){let n=(0,r.useRef)(null),[o,s]=(0,r.useState)([]),a=(0,r.useRef)(null),[i,l]=(0,r.useState)("connecting"),[u,d]=(0,r.useState)(!1),[f,g]=(0,r.useState)(null),m=(0,r.useRef)(!1),p=(0,r.useRef)(t);(0,r.useEffect)(()=>{p.current=t},[t]);let h=(0,r.useCallback)(e=>{if(!e)return!1;let t=p.current,n=t?t():null;return!!n&&e===n},[]);(0,r.useEffect)(()=>{let t=new globalThis.WebSocket(e);return n.current=t,t.onopen=()=>l("open"),t.onclose=()=>l("closed"),t.onerror=e=>{l("closed"),g({id:y(),message:"Connection error. Please try again.",timestamp:Date.now(),context:"websocket"})},t.onmessage=e=>{let t;try{t=JSON.parse(e.data)}catch(t){let e=c(t);console.error("[useChat] WebSocket message parse error: ".concat(e.message),{error:e});return}let n=t.data||{};switch(t.event){case"thinking":if(!h(n.sessionId))return;d(!0),s(e=>[...e,{id:y(),role:"system",content:"Dexto is thinking...",createdAt:Date.now()}]);break;case"chunk":{if(!h(n.sessionId))return;let e="string"==typeof n.content?n.content:"";if(!e)break;"reasoning"===n.type?s(t=>{let n=t.filter(e=>"system"!==e.role||"Dexto is thinking..."!==e.content),r=n[n.length-1];if(r&&"assistant"===r.role){let t={...r,reasoning:(r.reasoning||"")+e,createdAt:Date.now()};return[...n.slice(0,-1),t]}return[...n,{id:y(),role:"assistant",content:"",reasoning:e,createdAt:Date.now()}]}):s(t=>{let n=t.filter(e=>"system"!==e.role||"Dexto is thinking..."!==e.content),r=n[n.length-1];if(r&&"assistant"===r.role){let t="string"==typeof r.content?r.content:"",o={...r,content:t+e,createdAt:Date.now()};return[...n.slice(0,-1),o]}return[...n,{id:y(),role:"assistant",content:e,createdAt:Date.now()}]});break}case"response":{if(!h(n.sessionId))return;d(!1);let e="string"==typeof n.text?n.text:"",t="string"==typeof n.reasoning?n.reasoning:void 0,r=n&&"object"==typeof n.tokenUsage?n.tokenUsage:void 0,o="string"==typeof n.model?n.model:void 0,a="string"==typeof n.provider?n.provider:void 0,i="string"==typeof n.router?n.router:void 0,l="string"==typeof n.sessionId?n.sessionId:void 0;s(n=>{let s=n.filter(e=>"system"!==e.role||"Dexto is thinking..."!==e.content),c=s[s.length-1];if(c&&"assistant"===c.role){let n="string"==typeof e?e:"",u={...c,content:n,tokenUsage:r,reasoning:t,model:o,provider:a,router:i,createdAt:Date.now(),sessionId:null!=l?l:c.sessionId};return[...s.slice(0,-1),u]}return[...s,{id:y(),role:"assistant",content:e,createdAt:Date.now(),tokenUsage:r,reasoning:t,model:o,provider:a,router:i,sessionId:l}]}),window.dispatchEvent(new CustomEvent("dexto:response",{detail:{text:e,sessionId:l,reasoning:t,tokenUsage:r,model:o,timestamp:Date.now()}}));break}case"conversationReset":if(!h(n.sessionId))return;d(!1),s([]),a.current=null;break;case"toolCall":{if(!h(n.sessionId))return;let e=n.toolName,t=n.args;s(n=>[...n,{id:y(),role:"tool",content:null,toolName:e,toolArgs:t,createdAt:Date.now()}]);break}case"toolResult":{if(!h(n.sessionId))return;let e=n.toolName,t=n.result,r=t;if(t&&Array.isArray(t.content)){let e=t.content.map(e=>{if("object"==typeof e&&null!==e&&"image"===e.type){if(e.data&&e.mimeType)return{type:"image",base64:e.data,mimeType:e.mimeType};if(e.base64&&e.mimeType)return{type:"image",base64:e.base64,mimeType:e.mimeType};e.image||e.url}else if("object"==typeof e&&null!==e&&"audio"===e.type){if(e.data&&e.mimeType)return{type:"audio",base64:e.data,mimeType:e.mimeType,filename:e.filename};if(e.base64&&e.mimeType)return{type:"audio",base64:e.base64,mimeType:e.mimeType,filename:e.filename};e.audio||e.url}return e});r={...t,content:e}}s(t=>{let n=t.findIndex(t=>"tool"===t.role&&t.toolName===e&&void 0===t.toolResult);if(-1!==n){let e={...t[n],toolResult:r};return[...t.slice(0,n),e,...t.slice(n+1)]}return console.warn("No matching tool call found for result of ".concat(e)),t});break}case"toolConfirmationResponse":break;case"error":{if(!h(n.sessionId))return;if(d(!1),s(e=>e.filter(e=>"system"!==e.role||"Dexto is thinking..."!==e.content)),m.current){m.current=!1;break}if((null==n?void 0:n.context)==="user_cancelled")break;let e=c(n).message;g({id:y(),message:e,timestamp:Date.now(),context:n.context,recoverable:n.recoverable,sessionId:n.sessionId,anchorMessageId:a.current||void 0,detailedIssues:Array.isArray(n.issues)?n.issues:[]})}}},()=>{t.close()}},[e]);let b=(0,r.useCallback)(function(e,t,r,o){var i;let l=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if((null==(i=n.current)?void 0:i.readyState)===globalThis.WebSocket.OPEN){n.current.send(JSON.stringify({type:"message",content:e,imageData:t,fileData:r,sessionId:o,stream:l})),d(!0);let i=y();a.current=i,s(n=>[...n,{id:i,role:"user",content:e,createdAt:Date.now(),sessionId:o,imageData:t,fileData:r}]),window.dispatchEvent(new CustomEvent("dexto:message",{detail:{content:e,sessionId:o,timestamp:Date.now()}}))}else g({id:y(),message:"Cannot send message: connection is not open",timestamp:Date.now(),context:"websocket",recoverable:!0})},[]),w=(0,r.useCallback)(e=>{var t;(null==(t=n.current)?void 0:t.readyState)===globalThis.WebSocket.OPEN&&n.current.send(JSON.stringify({type:"reset",sessionId:e})),s([]),g(null),a.current=null,d(!1)},[]),v=(0,r.useCallback)(e=>{var t;(null==(t=n.current)?void 0:t.readyState)===globalThis.WebSocket.OPEN&&n.current.send(JSON.stringify({type:"cancel",sessionId:e})),d(!1),m.current=!0},[]),k=(0,r.useCallback)(()=>{g(null)},[]);return{messages:o,status:i,sendMessage:b,reset:w,setMessages:s,websocket:n.current,processing:u,cancel:v,activeError:f,clearError:k}}},9854:(e,t,n)=>{n.d(t,{ChatProvider:()=>l,v:()=>c});var r=n(5801),o=n(5789),s=n(1236),a=n(9246);let i=(0,o.createContext)(void 0);function l(e){let{children:t}=e,n=a.env.NEXT_PUBLIC_WS_URL||"ws://localhost:3001";try{let e=new URL(n);"localhost"===e.hostname&&(e.hostname=window.location.hostname,n=e.toString())}catch(e){console.warn("Invalid WS URL:",n)}let[l,c]=(0,o.useState)(null),[u,d]=(0,o.useState)(!0),[f,g]=(0,o.useState)(!0),{messages:m,sendMessage:p,status:y,reset:h,setMessages:b,websocket:w,activeError:v,clearError:k,processing:C,cancel:E}=(0,s.Y_)(n,()=>l),[S,A]=(0,o.useState)(null),T=(0,o.useCallback)(async e=>{try{let t=void 0!==e?e:l,n=await fetch(t?"/api/llm/current?sessionId=".concat(t):"/api/llm/current");if(n.ok){let e=await n.json(),t=e.config||e;A({provider:t.provider,model:t.model,displayName:t.displayName,router:t.router,baseURL:t.baseURL})}}catch(e){}},[l]);(0,o.useEffect)(()=>{S||T()},[S,T]);let{greeting:D}=function(e){let[t,n]=(0,o.useState)(null),[r,s]=(0,o.useState)(!1),[a,i]=(0,o.useState)(null);return(0,o.useEffect)(()=>{let t=new AbortController,{signal:r}=t;return(async()=>{s(!0),i(null);try{var t;let o=e?"/api/greeting?sessionId=".concat(encodeURIComponent(e)):"/api/greeting",s=await fetch(o,{signal:r});if(!s.ok){let e="Failed to fetch greeting: HTTP ".concat(s.status," ").concat(s.statusText);n(null),i(e);return}let a=await s.json();n(null!=(t=a.greeting)?t:null)}catch(t){if((null==t?void 0:t.name)==="AbortError")return;let e=t instanceof Error?t.message:"Failed to fetch greeting";i(e),console.error("Error fetching greeting: ".concat(e))}finally{s(!1)}})(),()=>t.abort()},[e]),{greeting:t,isLoading:r,error:a}}(l),x=(0,o.useCallback)(async()=>{try{let e=await fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})});if(!e.ok)throw Error("Failed to create session");return(await e.json()).session.id}catch(e){return console.error("Error creating auto session:",e),"chat-".concat(Date.now())}},[]),I=(0,o.useCallback)(async(e,t,n)=>{let r=l;!r&&u&&(c(r=await x()),d(!1),await T(r)),r?p(e,t,n,r,f):console.error("No session available for sending message")},[p,l,u,x,f]),N=(0,o.useCallback)(()=>{l&&h(l)},[h,l]),R=(0,o.useCallback)(async e=>{try{let t=await fetch("/api/sessions/".concat(e,"/history"));if(!t.ok){if(404===t.status){if(!(await fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e})})).ok)throw Error("Failed to create session");b([]);return}throw Error("Failed to load session history")}let n=(await t.json()).history||[],r=[];for(let t=0;t<n.length;t++){let o=n[t],s={id:"session-".concat(e,"-").concat(t),role:o.role,content:o.content,createdAt:Date.now()-(n.length-t)*1e3,sessionId:e,tokenUsage:o.tokenUsage,reasoning:o.reasoning,model:o.model,router:o.router,provider:o.provider};if("assistant"===o.role&&o.toolCalls&&o.toolCalls.length>0)o.content&&r.push(s),o.toolCalls.forEach((o,s)=>{var a;let i,l=o.function?JSON.parse(o.function.arguments||"{}"):{},c=(null==(a=o.function)?void 0:a.name)||"unknown";for(let e=t+1;e<n.length;e++){let t=n[e];if("tool"===t.role&&t.toolCallId===o.id){i=t.content;break}}r.push({id:"session-".concat(e,"-").concat(t,"-tool-").concat(s),role:"tool",content:null,createdAt:Date.now()-(n.length-t)*1e3+s,sessionId:e,toolName:c,toolArgs:l,toolResult:i})});else{if("tool"===o.role)continue;r.push(s)}}b(r)}catch(e){console.error("Error loading session history:",e),b([])}},[b,T]),_=(0,o.useCallback)(async e=>{if(e!==l)try{c(e),d(!1),await R(e),await T(e)}catch(e){throw console.error("Error switching session:",e),e}},[l,R]),j=(0,o.useCallback)(()=>{c(null),d(!0),b([]),A(null)},[b]);return(0,o.useEffect)(()=>{let e=e=>{var t;let n=(null==e?void 0:e.detail)||{};if(null==(t=n.config)?void 0:t.llm){let e=n.config.llm;A({provider:e.provider,model:e.model,router:e.router,baseURL:e.baseURL})}},t=e=>{console.log("Servers changed:",e.detail)},n=e=>{let{sessionId:t}=e.detail||{};t===l&&b([])};return window.addEventListener("dexto:configChanged",e),window.addEventListener("dexto:serversChanged",t),window.addEventListener("dexto:conversationReset",n),()=>{window.removeEventListener("dexto:configChanged",e),window.removeEventListener("dexto:serversChanged",t),window.removeEventListener("dexto:conversationReset",n)}},[l,b]),(0,r.jsx)(i.Provider,{value:{messages:m,sendMessage:I,status:y,reset:N,currentSessionId:l,switchSession:_,loadSessionHistory:R,isWelcomeState:u,returnToWelcome:j,isStreaming:f,setStreaming:g,websocket:w,currentLLM:S,refreshCurrentLLM:T,processing:C,cancel:E,activeError:v,clearError:k,greeting:D},children:t})}function c(){let e=(0,o.useContext)(i);if(!e)throw Error("useChatContext must be used within a ChatProvider");return e}}}]);