@dexto/agent-management 1.5.3 → 1.5.5

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.
@@ -5,9 +5,10 @@
5
5
  * This layer runs before agent initialization and injects explicit paths
6
6
  * into the configuration, eliminating the need for core services to resolve paths themselves.
7
7
  *
8
- * Also discovers command prompts from:
9
- * - Local: <projectRoot>/commands/ (in dev mode or dexto-project context)
10
- * - Global: ~/.dexto/commands/
8
+ * Also discovers command prompts from (in priority order):
9
+ * - Local: <projectRoot>/commands/ (dexto-source dev mode or dexto-project only)
10
+ * - Local: <cwd>/.dexto/commands/, <cwd>/.claude/commands/, <cwd>/.cursor/commands/
11
+ * - Global: ~/.dexto/commands/, ~/.claude/commands/, ~/.cursor/commands/
11
12
  *
12
13
  * Core services now require explicit paths - this enrichment layer provides them.
13
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config-enrichment.d.ts","sourceRoot":"","sources":["../../src/config/config-enrichment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,OAAO,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE7F;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAoC9E;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,mGAAmG;IACnG,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAClD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE,wBAAwB,GAAG,OAAY,GACjD,WAAW,CAoKb"}
1
+ {"version":3,"file":"config-enrichment.d.ts","sourceRoot":"","sources":["../../src/config/config-enrichment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,OAAO,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE7F;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAoC9E;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,mGAAmG;IACnG,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAClD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,GAAE,wBAAwB,GAAG,OAAY,GACjD,WAAW,CAoKb"}
@@ -39,6 +39,19 @@ var import_fs = require("fs");
39
39
  function discoverCommandPrompts() {
40
40
  const prompts = [];
41
41
  const seenFiles = /* @__PURE__ */ new Set();
42
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
43
+ const cwd = process.cwd();
44
+ const scanAndAdd = (dir) => {
45
+ if (!(0, import_fs.existsSync)(dir)) return;
46
+ const files = scanCommandsDirectory(dir);
47
+ for (const file of files) {
48
+ const basename = path.basename(file).toLowerCase();
49
+ if (!seenFiles.has(basename)) {
50
+ seenFiles.add(basename);
51
+ prompts.push({ type: "file", file });
52
+ }
53
+ }
54
+ };
42
55
  const context = (0, import_execution_context.getExecutionContext)();
43
56
  let localCommandsDir = null;
44
57
  switch (context) {
@@ -62,26 +75,18 @@ function discoverCommandPrompts() {
62
75
  case "global-cli":
63
76
  break;
64
77
  }
65
- const globalCommandsDir = (0, import_path.getDextoGlobalPath)("commands");
66
- if (localCommandsDir && (0, import_fs.existsSync)(localCommandsDir)) {
67
- const files = scanCommandsDirectory(localCommandsDir);
68
- for (const file of files) {
69
- const basename = path.basename(file).toLowerCase();
70
- if (!seenFiles.has(basename)) {
71
- seenFiles.add(basename);
72
- prompts.push({ type: "file", file });
73
- }
74
- }
78
+ if (localCommandsDir) {
79
+ scanAndAdd(localCommandsDir);
75
80
  }
76
- if ((0, import_fs.existsSync)(globalCommandsDir)) {
77
- const files = scanCommandsDirectory(globalCommandsDir);
78
- for (const file of files) {
79
- const basename = path.basename(file).toLowerCase();
80
- if (!seenFiles.has(basename)) {
81
- seenFiles.add(basename);
82
- prompts.push({ type: "file", file });
83
- }
84
- }
81
+ scanAndAdd(path.join(cwd, ".dexto", "commands"));
82
+ scanAndAdd(path.join(cwd, ".claude", "commands"));
83
+ scanAndAdd(path.join(cwd, ".cursor", "commands"));
84
+ scanAndAdd((0, import_path.getDextoGlobalPath)("commands"));
85
+ if (homeDir) {
86
+ scanAndAdd(path.join(homeDir, ".claude", "commands"));
87
+ }
88
+ if (homeDir) {
89
+ scanAndAdd(path.join(homeDir, ".cursor", "commands"));
85
90
  }
86
91
  return prompts;
87
92
  }
@@ -15,12 +15,20 @@ export interface FilePromptEntry {
15
15
  /**
16
16
  * Discovers command prompts from commands/ directories.
17
17
  *
18
- * Discovery locations based on execution context:
19
- * - dexto-source (dev mode): <sourceRoot>/commands/
20
- * - dexto-project: <projectRoot>/commands/
21
- * - global-cli: skip local (use global only)
18
+ * Discovery locations (in priority order):
22
19
  *
23
- * Global commands (~/.dexto/commands/) are always included.
20
+ * Local commands (project-specific):
21
+ * 1. <projectRoot>/commands/ (dexto-source dev mode or dexto-project only)
22
+ * 2. <cwd>/.dexto/commands/
23
+ * 3. <cwd>/.claude/commands/ (Claude Code compatibility)
24
+ * 4. <cwd>/.cursor/commands/ (Cursor compatibility)
25
+ *
26
+ * Global commands (user-wide):
27
+ * 5. ~/.dexto/commands/
28
+ * 6. ~/.claude/commands/ (Claude Code compatibility)
29
+ * 7. ~/.cursor/commands/ (Cursor compatibility)
30
+ *
31
+ * Files with the same basename are deduplicated (first found wins).
24
32
  *
25
33
  * @returns Array of file prompt entries for discovered .md files
26
34
  */
@@ -1 +1 @@
1
- {"version":3,"file":"discover-prompts.d.ts","sourceRoot":"","sources":["../../src/config/discover-prompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,EAAE,CA8D1D;AAiCD;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,IAAI,CA0B5D"}
1
+ {"version":3,"file":"discover-prompts.d.ts","sourceRoot":"","sources":["../../src/config/discover-prompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,EAAE,CAkF1D;AAiCD;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,IAAI,CA0B5D"}
@@ -9,6 +9,19 @@ import { existsSync, readdirSync } from "fs";
9
9
  function discoverCommandPrompts() {
10
10
  const prompts = [];
11
11
  const seenFiles = /* @__PURE__ */ new Set();
12
+ const homeDir = process.env.HOME || process.env.USERPROFILE || "";
13
+ const cwd = process.cwd();
14
+ const scanAndAdd = (dir) => {
15
+ if (!existsSync(dir)) return;
16
+ const files = scanCommandsDirectory(dir);
17
+ for (const file of files) {
18
+ const basename = path.basename(file).toLowerCase();
19
+ if (!seenFiles.has(basename)) {
20
+ seenFiles.add(basename);
21
+ prompts.push({ type: "file", file });
22
+ }
23
+ }
24
+ };
12
25
  const context = getExecutionContext();
13
26
  let localCommandsDir = null;
14
27
  switch (context) {
@@ -32,26 +45,18 @@ function discoverCommandPrompts() {
32
45
  case "global-cli":
33
46
  break;
34
47
  }
35
- const globalCommandsDir = getDextoGlobalPath("commands");
36
- if (localCommandsDir && existsSync(localCommandsDir)) {
37
- const files = scanCommandsDirectory(localCommandsDir);
38
- for (const file of files) {
39
- const basename = path.basename(file).toLowerCase();
40
- if (!seenFiles.has(basename)) {
41
- seenFiles.add(basename);
42
- prompts.push({ type: "file", file });
43
- }
44
- }
48
+ if (localCommandsDir) {
49
+ scanAndAdd(localCommandsDir);
45
50
  }
46
- if (existsSync(globalCommandsDir)) {
47
- const files = scanCommandsDirectory(globalCommandsDir);
48
- for (const file of files) {
49
- const basename = path.basename(file).toLowerCase();
50
- if (!seenFiles.has(basename)) {
51
- seenFiles.add(basename);
52
- prompts.push({ type: "file", file });
53
- }
54
- }
51
+ scanAndAdd(path.join(cwd, ".dexto", "commands"));
52
+ scanAndAdd(path.join(cwd, ".claude", "commands"));
53
+ scanAndAdd(path.join(cwd, ".cursor", "commands"));
54
+ scanAndAdd(getDextoGlobalPath("commands"));
55
+ if (homeDir) {
56
+ scanAndAdd(path.join(homeDir, ".claude", "commands"));
57
+ }
58
+ if (homeDir) {
59
+ scanAndAdd(path.join(homeDir, ".cursor", "commands"));
55
60
  }
56
61
  return prompts;
57
62
  }
@@ -25,6 +25,7 @@ __export(preferences_exports, {
25
25
  PreferenceErrorCode: () => import_errors.PreferenceErrorCode,
26
26
  PreferenceLLMSchema: () => import_schemas.PreferenceLLMSchema,
27
27
  PreferenceSetupSchema: () => import_schemas.PreferenceSetupSchema,
28
+ PreferenceSoundsSchema: () => import_schemas.PreferenceSoundsSchema,
28
29
  createInitialPreferences: () => import_loader.createInitialPreferences,
29
30
  getGlobalPreferencesPath: () => import_loader.getGlobalPreferencesPath,
30
31
  globalPreferencesExist: () => import_loader.globalPreferencesExist,
@@ -46,6 +47,7 @@ var import_errors = require("./errors.js");
46
47
  PreferenceErrorCode,
47
48
  PreferenceLLMSchema,
48
49
  PreferenceSetupSchema,
50
+ PreferenceSoundsSchema,
49
51
  createInitialPreferences,
50
52
  getGlobalPreferencesPath,
51
53
  globalPreferencesExist,
@@ -1,5 +1,5 @@
1
- export type { GlobalPreferences, PreferenceLLM, PreferenceDefaults, PreferenceSetup, } from './schemas.js';
2
- export { GlobalPreferencesSchema, PreferenceLLMSchema, PreferenceDefaultsSchema, PreferenceSetupSchema, } from './schemas.js';
1
+ export type { GlobalPreferences, PreferenceLLM, PreferenceDefaults, PreferenceSetup, PreferenceSounds, } from './schemas.js';
2
+ export { GlobalPreferencesSchema, PreferenceLLMSchema, PreferenceDefaultsSchema, PreferenceSetupSchema, PreferenceSoundsSchema, } from './schemas.js';
3
3
  export { PREFERENCES_FILE } from './constants.js';
4
4
  export { loadGlobalPreferences, saveGlobalPreferences, globalPreferencesExist, getGlobalPreferencesPath, createInitialPreferences, updateGlobalPreferences, type CreatePreferencesOptions, } from './loader.js';
5
5
  export { PreferenceError, PreferenceErrorCode } from './errors.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preferences/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACR,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,eAAe,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACH,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,GACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EACH,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,wBAAwB,GAChC,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preferences/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACR,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACH,uBAAuB,EACvB,mBAAmB,EACnB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,GACzB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EACH,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,wBAAwB,GAChC,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
@@ -2,7 +2,8 @@ import {
2
2
  GlobalPreferencesSchema,
3
3
  PreferenceLLMSchema,
4
4
  PreferenceDefaultsSchema,
5
- PreferenceSetupSchema
5
+ PreferenceSetupSchema,
6
+ PreferenceSoundsSchema
6
7
  } from "./schemas.js";
7
8
  import { PREFERENCES_FILE } from "./constants.js";
8
9
  import {
@@ -22,6 +23,7 @@ export {
22
23
  PreferenceErrorCode,
23
24
  PreferenceLLMSchema,
24
25
  PreferenceSetupSchema,
26
+ PreferenceSoundsSchema,
25
27
  createInitialPreferences,
26
28
  getGlobalPreferencesPath,
27
29
  globalPreferencesExist,
@@ -59,6 +59,17 @@ async function loadGlobalPreferences() {
59
59
  );
60
60
  }
61
61
  }
62
+ const PREFERENCES_FILE_HEADER = `# Dexto Global Preferences
63
+ # Documentation: https://dexto.dev/docs/configuration/preferences
64
+ #
65
+ # Sound Notifications:
66
+ # Dexto plays sounds for approval requests and task completion.
67
+ # To customize sounds, place audio files in ~/.dexto/sounds/:
68
+ # - approval.wav (or .mp3, .ogg, .aiff, .m4a) - played when tool approval is needed
69
+ # - complete.wav (or .mp3, .ogg, .aiff, .m4a) - played when agent finishes a task
70
+ # Set sounds.enabled: false to disable all sounds.
71
+
72
+ `;
62
73
  async function saveGlobalPreferences(preferences) {
63
74
  const preferencesPath = (0, import_path.getDextoGlobalPath)(import_constants.PREFERENCES_FILE);
64
75
  const validation = import_schemas.GlobalPreferencesSchema.safeParse(preferences);
@@ -74,7 +85,7 @@ async function saveGlobalPreferences(preferences) {
74
85
  lineWidth: 100,
75
86
  minContentWidth: 20
76
87
  });
77
- await import_fs2.promises.writeFile(preferencesPath, yamlContent, "utf-8");
88
+ await import_fs2.promises.writeFile(preferencesPath, PREFERENCES_FILE_HEADER + yamlContent, "utf-8");
78
89
  import_core.logger.debug(
79
90
  `\u2713 Saved global preferences ${JSON.stringify(preferences)} to: ${preferencesPath}`
80
91
  );
@@ -92,49 +103,35 @@ function globalPreferencesExist() {
92
103
  function getGlobalPreferencesPath() {
93
104
  return (0, import_path.getDextoGlobalPath)(import_constants.PREFERENCES_FILE);
94
105
  }
95
- function createInitialPreferences(providerOrOptions, model, apiKeyVar, defaultAgent = "coding-agent") {
96
- if (typeof providerOrOptions === "object") {
97
- const opts = providerOrOptions;
98
- const llmConfig = {
99
- provider: opts.provider,
100
- model: opts.model
101
- };
102
- if (opts.apiKeyVar) {
103
- llmConfig.apiKey = `$${opts.apiKeyVar}`;
104
- }
105
- if (opts.baseURL) {
106
- llmConfig.baseURL = opts.baseURL;
107
- }
108
- if (opts.reasoningEffort) {
109
- llmConfig.reasoningEffort = opts.reasoningEffort;
110
- }
111
- return {
112
- llm: llmConfig,
113
- defaults: {
114
- defaultAgent: opts.defaultAgent || "coding-agent",
115
- defaultMode: opts.defaultMode || "web"
116
- },
117
- setup: {
118
- completed: opts.setupCompleted ?? true,
119
- apiKeyPending: opts.apiKeyPending ?? false,
120
- baseURLPending: opts.baseURLPending ?? false
121
- }
122
- };
106
+ function createInitialPreferences(options) {
107
+ const llmConfig = {
108
+ provider: options.provider,
109
+ model: options.model
110
+ };
111
+ if (options.apiKeyVar) {
112
+ llmConfig.apiKey = "$" + options.apiKeyVar;
113
+ }
114
+ if (options.baseURL) {
115
+ llmConfig.baseURL = options.baseURL;
116
+ }
117
+ if (options.reasoningEffort) {
118
+ llmConfig.reasoningEffort = options.reasoningEffort;
123
119
  }
124
120
  return {
125
- llm: {
126
- provider: providerOrOptions,
127
- model,
128
- apiKey: `$${apiKeyVar}`
129
- },
121
+ llm: llmConfig,
130
122
  defaults: {
131
- defaultAgent,
132
- defaultMode: "web"
123
+ defaultAgent: options.defaultAgent || "coding-agent",
124
+ defaultMode: options.defaultMode || "web"
133
125
  },
134
126
  setup: {
135
- completed: true,
136
- apiKeyPending: false,
137
- baseURLPending: false
127
+ completed: options.setupCompleted ?? true,
128
+ apiKeyPending: options.apiKeyPending ?? false,
129
+ baseURLPending: options.baseURLPending ?? false
130
+ },
131
+ sounds: {
132
+ enabled: options.sounds?.enabled ?? true,
133
+ onApprovalRequired: options.sounds?.onApprovalRequired ?? true,
134
+ onTaskComplete: options.sounds?.onTaskComplete ?? true
138
135
  }
139
136
  };
140
137
  }
@@ -145,9 +142,10 @@ async function updateGlobalPreferences(updates) {
145
142
  ...updates,
146
143
  // LLM section requires complete replacement (high coherence - provider/model/apiKey must match)
147
144
  llm: updates.llm || existing.llm,
148
- // Defaults and setup sections allow partial updates (low coherence - independent fields)
145
+ // Defaults, setup, and sounds sections allow partial updates (low coherence - independent fields)
149
146
  defaults: updates.defaults ? { ...existing.defaults, ...updates.defaults } : existing.defaults,
150
- setup: updates.setup ? { ...existing.setup, ...updates.setup } : existing.setup
147
+ setup: updates.setup ? { ...existing.setup, ...updates.setup } : existing.setup,
148
+ sounds: updates.sounds ? { ...existing.sounds, ...updates.sounds } : existing.sounds
151
149
  };
152
150
  const validation = import_schemas.GlobalPreferencesSchema.safeParse(merged);
153
151
  if (!validation.success) {
@@ -41,17 +41,18 @@ export interface CreatePreferencesOptions {
41
41
  apiKeyPending?: boolean;
42
42
  /** Whether baseURL setup was skipped and needs to be configured later */
43
43
  baseURLPending?: boolean;
44
+ /** Sound notification preferences */
45
+ sounds?: {
46
+ enabled?: boolean;
47
+ onApprovalRequired?: boolean;
48
+ onTaskComplete?: boolean;
49
+ };
44
50
  }
45
51
  /**
46
52
  * Create initial preferences from setup data
47
53
  * @param options Configuration options for preferences
48
54
  */
49
55
  export declare function createInitialPreferences(options: CreatePreferencesOptions): GlobalPreferences;
50
- /**
51
- * Create initial preferences from setup data (legacy signature)
52
- * @deprecated Use options object instead
53
- */
54
- export declare function createInitialPreferences(provider: LLMProvider, model: string, apiKeyVar: string, defaultAgent?: string): GlobalPreferences;
55
56
  /**
56
57
  * Updates type that allows partial nested objects
57
58
  */
@@ -59,6 +60,7 @@ export type GlobalPreferencesUpdates = {
59
60
  llm?: GlobalPreferences['llm'];
60
61
  defaults?: Partial<GlobalPreferences['defaults']>;
61
62
  setup?: Partial<GlobalPreferences['setup']>;
63
+ sounds?: Partial<GlobalPreferences['sounds']>;
62
64
  };
63
65
  /**
64
66
  * Update specific preference sections
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/preferences/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAI/E;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CA+BxE;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCzF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAAC;AAE/F;;;GAGG;AACH,wBAAgB,wBAAwB,CACpC,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,GACtB,iBAAiB,CAAC;AAgErB;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACnC,GAAG,CAAC,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;CAC/C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,wBAAwB,GAClC,OAAO,CAAC,iBAAiB,CAAC,CA2B5B"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/preferences/loader.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAI/E;;;;;GAKG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CA+BxE;AAiBD;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCzF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAGhD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAEjD;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,KAAK,CAAC;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC3E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,MAAM,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,cAAc,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;CACL;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAsC7F;AAED;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACnC,GAAG,CAAC,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;CACjD,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,wBAAwB,GAClC,OAAO,CAAC,iBAAiB,CAAC,CA4B5B"}
@@ -31,6 +31,17 @@ async function loadGlobalPreferences() {
31
31
  );
32
32
  }
33
33
  }
34
+ const PREFERENCES_FILE_HEADER = `# Dexto Global Preferences
35
+ # Documentation: https://dexto.dev/docs/configuration/preferences
36
+ #
37
+ # Sound Notifications:
38
+ # Dexto plays sounds for approval requests and task completion.
39
+ # To customize sounds, place audio files in ~/.dexto/sounds/:
40
+ # - approval.wav (or .mp3, .ogg, .aiff, .m4a) - played when tool approval is needed
41
+ # - complete.wav (or .mp3, .ogg, .aiff, .m4a) - played when agent finishes a task
42
+ # Set sounds.enabled: false to disable all sounds.
43
+
44
+ `;
34
45
  async function saveGlobalPreferences(preferences) {
35
46
  const preferencesPath = getDextoGlobalPath(PREFERENCES_FILE);
36
47
  const validation = GlobalPreferencesSchema.safeParse(preferences);
@@ -46,7 +57,7 @@ async function saveGlobalPreferences(preferences) {
46
57
  lineWidth: 100,
47
58
  minContentWidth: 20
48
59
  });
49
- await fs.writeFile(preferencesPath, yamlContent, "utf-8");
60
+ await fs.writeFile(preferencesPath, PREFERENCES_FILE_HEADER + yamlContent, "utf-8");
50
61
  logger.debug(
51
62
  `\u2713 Saved global preferences ${JSON.stringify(preferences)} to: ${preferencesPath}`
52
63
  );
@@ -64,49 +75,35 @@ function globalPreferencesExist() {
64
75
  function getGlobalPreferencesPath() {
65
76
  return getDextoGlobalPath(PREFERENCES_FILE);
66
77
  }
67
- function createInitialPreferences(providerOrOptions, model, apiKeyVar, defaultAgent = "coding-agent") {
68
- if (typeof providerOrOptions === "object") {
69
- const opts = providerOrOptions;
70
- const llmConfig = {
71
- provider: opts.provider,
72
- model: opts.model
73
- };
74
- if (opts.apiKeyVar) {
75
- llmConfig.apiKey = `$${opts.apiKeyVar}`;
76
- }
77
- if (opts.baseURL) {
78
- llmConfig.baseURL = opts.baseURL;
79
- }
80
- if (opts.reasoningEffort) {
81
- llmConfig.reasoningEffort = opts.reasoningEffort;
82
- }
83
- return {
84
- llm: llmConfig,
85
- defaults: {
86
- defaultAgent: opts.defaultAgent || "coding-agent",
87
- defaultMode: opts.defaultMode || "web"
88
- },
89
- setup: {
90
- completed: opts.setupCompleted ?? true,
91
- apiKeyPending: opts.apiKeyPending ?? false,
92
- baseURLPending: opts.baseURLPending ?? false
93
- }
94
- };
78
+ function createInitialPreferences(options) {
79
+ const llmConfig = {
80
+ provider: options.provider,
81
+ model: options.model
82
+ };
83
+ if (options.apiKeyVar) {
84
+ llmConfig.apiKey = "$" + options.apiKeyVar;
85
+ }
86
+ if (options.baseURL) {
87
+ llmConfig.baseURL = options.baseURL;
88
+ }
89
+ if (options.reasoningEffort) {
90
+ llmConfig.reasoningEffort = options.reasoningEffort;
95
91
  }
96
92
  return {
97
- llm: {
98
- provider: providerOrOptions,
99
- model,
100
- apiKey: `$${apiKeyVar}`
101
- },
93
+ llm: llmConfig,
102
94
  defaults: {
103
- defaultAgent,
104
- defaultMode: "web"
95
+ defaultAgent: options.defaultAgent || "coding-agent",
96
+ defaultMode: options.defaultMode || "web"
105
97
  },
106
98
  setup: {
107
- completed: true,
108
- apiKeyPending: false,
109
- baseURLPending: false
99
+ completed: options.setupCompleted ?? true,
100
+ apiKeyPending: options.apiKeyPending ?? false,
101
+ baseURLPending: options.baseURLPending ?? false
102
+ },
103
+ sounds: {
104
+ enabled: options.sounds?.enabled ?? true,
105
+ onApprovalRequired: options.sounds?.onApprovalRequired ?? true,
106
+ onTaskComplete: options.sounds?.onTaskComplete ?? true
110
107
  }
111
108
  };
112
109
  }
@@ -117,9 +114,10 @@ async function updateGlobalPreferences(updates) {
117
114
  ...updates,
118
115
  // LLM section requires complete replacement (high coherence - provider/model/apiKey must match)
119
116
  llm: updates.llm || existing.llm,
120
- // Defaults and setup sections allow partial updates (low coherence - independent fields)
117
+ // Defaults, setup, and sounds sections allow partial updates (low coherence - independent fields)
121
118
  defaults: updates.defaults ? { ...existing.defaults, ...updates.defaults } : existing.defaults,
122
- setup: updates.setup ? { ...existing.setup, ...updates.setup } : existing.setup
119
+ setup: updates.setup ? { ...existing.setup, ...updates.setup } : existing.setup,
120
+ sounds: updates.sounds ? { ...existing.sounds, ...updates.sounds } : existing.sounds
123
121
  };
124
122
  const validation = GlobalPreferencesSchema.safeParse(merged);
125
123
  if (!validation.success) {
@@ -21,7 +21,8 @@ __export(schemas_exports, {
21
21
  GlobalPreferencesSchema: () => GlobalPreferencesSchema,
22
22
  PreferenceDefaultsSchema: () => PreferenceDefaultsSchema,
23
23
  PreferenceLLMSchema: () => PreferenceLLMSchema,
24
- PreferenceSetupSchema: () => PreferenceSetupSchema
24
+ PreferenceSetupSchema: () => PreferenceSetupSchema,
25
+ PreferenceSoundsSchema: () => PreferenceSoundsSchema
25
26
  });
26
27
  module.exports = __toCommonJS(schemas_exports);
27
28
  var import_zod = require("zod");
@@ -80,11 +81,21 @@ const PreferenceSetupSchema = import_zod.z.object({
80
81
  apiKeyPending: import_zod.z.boolean().default(false).describe("Whether API key setup was skipped and needs to be configured later"),
81
82
  baseURLPending: import_zod.z.boolean().default(false).describe("Whether baseURL setup was skipped and needs to be configured later")
82
83
  }).strict();
84
+ const PreferenceSoundsSchema = import_zod.z.object({
85
+ enabled: import_zod.z.boolean().default(true).describe("Enable sound notifications (default: true)"),
86
+ onApprovalRequired: import_zod.z.boolean().default(true).describe(
87
+ "Play sound when tool approval is required (default: true when sounds enabled)"
88
+ ),
89
+ onTaskComplete: import_zod.z.boolean().default(true).describe("Play sound when agent task completes (default: true when sounds enabled)")
90
+ }).strict();
83
91
  const GlobalPreferencesSchema = import_zod.z.object({
84
92
  llm: PreferenceLLMSchema.describe("LLM configuration preferences"),
85
93
  defaults: PreferenceDefaultsSchema.describe("Default behavior preferences (required)"),
86
94
  setup: PreferenceSetupSchema.default({ completed: false }).describe(
87
95
  "Setup completion tracking"
96
+ ),
97
+ sounds: PreferenceSoundsSchema.default({}).describe(
98
+ "Sound notification preferences (defaults applied for legacy preferences)"
88
99
  )
89
100
  }).strict();
90
101
  // Annotate the CommonJS export names for ESM import in node:
@@ -92,5 +103,6 @@ const GlobalPreferencesSchema = import_zod.z.object({
92
103
  GlobalPreferencesSchema,
93
104
  PreferenceDefaultsSchema,
94
105
  PreferenceLLMSchema,
95
- PreferenceSetupSchema
106
+ PreferenceSetupSchema,
107
+ PreferenceSoundsSchema
96
108
  });
@@ -53,6 +53,19 @@ export declare const PreferenceSetupSchema: z.ZodObject<{
53
53
  apiKeyPending?: boolean | undefined;
54
54
  baseURLPending?: boolean | undefined;
55
55
  }>;
56
+ export declare const PreferenceSoundsSchema: z.ZodObject<{
57
+ enabled: z.ZodDefault<z.ZodBoolean>;
58
+ onApprovalRequired: z.ZodDefault<z.ZodBoolean>;
59
+ onTaskComplete: z.ZodDefault<z.ZodBoolean>;
60
+ }, "strict", z.ZodTypeAny, {
61
+ enabled: boolean;
62
+ onApprovalRequired: boolean;
63
+ onTaskComplete: boolean;
64
+ }, {
65
+ enabled?: boolean | undefined;
66
+ onApprovalRequired?: boolean | undefined;
67
+ onTaskComplete?: boolean | undefined;
68
+ }>;
56
69
  export declare const GlobalPreferencesSchema: z.ZodObject<{
57
70
  llm: z.ZodEffects<z.ZodObject<{
58
71
  provider: z.ZodEnum<["openai", "openai-compatible", "anthropic", "google", "groq", "xai", "cohere", "openrouter", "litellm", "glama", "vertex", "bedrock", "local", "ollama"]>;
@@ -108,6 +121,19 @@ export declare const GlobalPreferencesSchema: z.ZodObject<{
108
121
  apiKeyPending?: boolean | undefined;
109
122
  baseURLPending?: boolean | undefined;
110
123
  }>>;
124
+ sounds: z.ZodDefault<z.ZodObject<{
125
+ enabled: z.ZodDefault<z.ZodBoolean>;
126
+ onApprovalRequired: z.ZodDefault<z.ZodBoolean>;
127
+ onTaskComplete: z.ZodDefault<z.ZodBoolean>;
128
+ }, "strict", z.ZodTypeAny, {
129
+ enabled: boolean;
130
+ onApprovalRequired: boolean;
131
+ onTaskComplete: boolean;
132
+ }, {
133
+ enabled?: boolean | undefined;
134
+ onApprovalRequired?: boolean | undefined;
135
+ onTaskComplete?: boolean | undefined;
136
+ }>>;
111
137
  }, "strict", z.ZodTypeAny, {
112
138
  llm: {
113
139
  provider: "openai" | "openai-compatible" | "anthropic" | "google" | "groq" | "xai" | "cohere" | "openrouter" | "litellm" | "glama" | "vertex" | "bedrock" | "local" | "ollama";
@@ -125,6 +151,11 @@ export declare const GlobalPreferencesSchema: z.ZodObject<{
125
151
  apiKeyPending: boolean;
126
152
  baseURLPending: boolean;
127
153
  };
154
+ sounds: {
155
+ enabled: boolean;
156
+ onApprovalRequired: boolean;
157
+ onTaskComplete: boolean;
158
+ };
128
159
  }, {
129
160
  llm: {
130
161
  provider: "openai" | "openai-compatible" | "anthropic" | "google" | "groq" | "xai" | "cohere" | "openrouter" | "litellm" | "glama" | "vertex" | "bedrock" | "local" | "ollama";
@@ -142,9 +173,15 @@ export declare const GlobalPreferencesSchema: z.ZodObject<{
142
173
  apiKeyPending?: boolean | undefined;
143
174
  baseURLPending?: boolean | undefined;
144
175
  } | undefined;
176
+ sounds?: {
177
+ enabled?: boolean | undefined;
178
+ onApprovalRequired?: boolean | undefined;
179
+ onTaskComplete?: boolean | undefined;
180
+ } | undefined;
145
181
  }>;
146
182
  export type PreferenceLLM = z.output<typeof PreferenceLLMSchema>;
147
183
  export type PreferenceDefaults = z.output<typeof PreferenceDefaultsSchema>;
148
184
  export type PreferenceSetup = z.output<typeof PreferenceSetupSchema>;
185
+ export type PreferenceSounds = z.output<typeof PreferenceSoundsSchema>;
149
186
  export type GlobalPreferences = z.output<typeof GlobalPreferencesSchema>;
150
187
  //# sourceMappingURL=schemas.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/preferences/schemas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsE1B,CAAC;AAEP,eAAO,MAAM,wBAAwB;;;;;;;;;EAYxB,CAAC;AAEd,eAAO,MAAM,qBAAqB;;;;;;;;;;;;EAYrB,CAAC;AAEd,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUvB,CAAC;AAGd,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACjE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC3E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/preferences/schemas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsE1B,CAAC;AAEP,eAAO,MAAM,wBAAwB;;;;;;;;;EAYxB,CAAC;AAEd,eAAO,MAAM,qBAAqB;;;;;;;;;;;;EAYrB,CAAC;AAEd,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EActB,CAAC;AAEd,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcvB,CAAC;AAGd,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACjE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC3E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
@@ -60,16 +60,27 @@ const PreferenceSetupSchema = z.object({
60
60
  apiKeyPending: z.boolean().default(false).describe("Whether API key setup was skipped and needs to be configured later"),
61
61
  baseURLPending: z.boolean().default(false).describe("Whether baseURL setup was skipped and needs to be configured later")
62
62
  }).strict();
63
+ const PreferenceSoundsSchema = z.object({
64
+ enabled: z.boolean().default(true).describe("Enable sound notifications (default: true)"),
65
+ onApprovalRequired: z.boolean().default(true).describe(
66
+ "Play sound when tool approval is required (default: true when sounds enabled)"
67
+ ),
68
+ onTaskComplete: z.boolean().default(true).describe("Play sound when agent task completes (default: true when sounds enabled)")
69
+ }).strict();
63
70
  const GlobalPreferencesSchema = z.object({
64
71
  llm: PreferenceLLMSchema.describe("LLM configuration preferences"),
65
72
  defaults: PreferenceDefaultsSchema.describe("Default behavior preferences (required)"),
66
73
  setup: PreferenceSetupSchema.default({ completed: false }).describe(
67
74
  "Setup completion tracking"
75
+ ),
76
+ sounds: PreferenceSoundsSchema.default({}).describe(
77
+ "Sound notification preferences (defaults applied for legacy preferences)"
68
78
  )
69
79
  }).strict();
70
80
  export {
71
81
  GlobalPreferencesSchema,
72
82
  PreferenceDefaultsSchema,
73
83
  PreferenceLLMSchema,
74
- PreferenceSetupSchema
84
+ PreferenceSetupSchema,
85
+ PreferenceSoundsSchema
75
86
  };
@@ -110,7 +110,7 @@ class RuntimeService {
110
110
  }
111
111
  /**
112
112
  * Set up progress event emission for a sub-agent.
113
- * Subscribes to llm:tool-call events and emits service:event with progress data.
113
+ * Subscribes to llm:tool-call and llm:response events and emits service:event with progress data.
114
114
  *
115
115
  * @returns Cleanup function to unsubscribe from events
116
116
  */
@@ -126,8 +126,26 @@ class RuntimeService {
126
126
  `[Progress] Setting up progress tracking for sub-agent ${subAgentHandle.agentId} (toolCallId: ${toolCallId}, sessionId: ${sessionId})`
127
127
  );
128
128
  let toolCount = 0;
129
+ const tokenUsage = { input: 0, output: 0, total: 0 };
130
+ let currentTool = "";
129
131
  const subAgentBus = subAgentHandle.agent.agentEventBus;
130
132
  const parentBus = this.parentAgent.agentEventBus;
133
+ const emitProgress = (tool, args) => {
134
+ parentBus.emit("service:event", {
135
+ service: "agent-spawner",
136
+ event: "progress",
137
+ toolCallId,
138
+ sessionId,
139
+ data: {
140
+ task: input.task,
141
+ agentId: input.agentId ?? "default",
142
+ toolsCalled: toolCount,
143
+ currentTool: tool,
144
+ currentArgs: args,
145
+ tokenUsage: { ...tokenUsage }
146
+ }
147
+ });
148
+ };
131
149
  const toolCallHandler = (event) => {
132
150
  toolCount++;
133
151
  let displayToolName = event.toolName;
@@ -141,26 +159,28 @@ class RuntimeService {
141
159
  displayToolName = parts.slice(2).join("--");
142
160
  }
143
161
  }
162
+ currentTool = displayToolName;
144
163
  this.logger.debug(
145
164
  `[Progress] Sub-agent tool call #${toolCount}: ${displayToolName} (toolCallId: ${toolCallId})`
146
165
  );
147
- parentBus.emit("service:event", {
148
- service: "agent-spawner",
149
- event: "progress",
150
- toolCallId,
151
- sessionId,
152
- data: {
153
- task: input.task,
154
- agentId: input.agentId ?? "default",
155
- toolsCalled: toolCount,
156
- currentTool: displayToolName,
157
- currentArgs: event.args
158
- }
159
- });
166
+ emitProgress(displayToolName, event.args);
167
+ };
168
+ const responseHandler = (event) => {
169
+ if (event.tokenUsage) {
170
+ tokenUsage.input = event.tokenUsage.inputTokens ?? 0;
171
+ tokenUsage.output += event.tokenUsage.outputTokens ?? 0;
172
+ tokenUsage.total = tokenUsage.input + tokenUsage.output;
173
+ this.logger.debug(
174
+ `[Progress] Sub-agent tokens: input=${tokenUsage.input}, cumOutput=${tokenUsage.output}, total=${tokenUsage.total}`
175
+ );
176
+ emitProgress(currentTool || "processing");
177
+ }
160
178
  };
161
179
  subAgentBus.on("llm:tool-call", toolCallHandler);
180
+ subAgentBus.on("llm:response", responseHandler);
162
181
  return () => {
163
182
  subAgentBus.off("llm:tool-call", toolCallHandler);
183
+ subAgentBus.off("llm:response", responseHandler);
164
184
  };
165
185
  }
166
186
  /**
@@ -53,7 +53,7 @@ export declare class RuntimeService {
53
53
  }): Promise<SpawnAgentOutput>;
54
54
  /**
55
55
  * Set up progress event emission for a sub-agent.
56
- * Subscribes to llm:tool-call events and emits service:event with progress data.
56
+ * Subscribes to llm:tool-call and llm:response events and emits service:event with progress data.
57
57
  *
58
58
  * @returns Cleanup function to unsubscribe from events
59
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-service.d.ts","sourceRoot":"","sources":["../../src/tool-provider/runtime-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAKzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,MAAM,CAAe;gBAEjB,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,YAAY;IAuBrF;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,KAAK,EAAE;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8C7B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAqE7B;;OAEG;YACW,oBAAoB;IAgKlC;;;;;;OAMG;YACW,mBAAmB;IAuEjC;;;OAGG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAoB1C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAIjC"}
1
+ {"version":3,"file":"runtime-service.d.ts","sourceRoot":"","sources":["../../src/tool-provider/runtime-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAKzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,qBAAa,cAAc;IACvB,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,MAAM,CAAe;gBAEjB,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,YAAY;IAuBrF;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,KAAK,EAAE;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8C7B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAiH7B;;OAEG;YACW,oBAAoB;IAgKlC;;;;;;OAMG;YACW,mBAAmB;IAuEjC;;;OAGG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAoB1C;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAIjC"}
@@ -87,7 +87,7 @@ class RuntimeService {
87
87
  }
88
88
  /**
89
89
  * Set up progress event emission for a sub-agent.
90
- * Subscribes to llm:tool-call events and emits service:event with progress data.
90
+ * Subscribes to llm:tool-call and llm:response events and emits service:event with progress data.
91
91
  *
92
92
  * @returns Cleanup function to unsubscribe from events
93
93
  */
@@ -103,8 +103,26 @@ class RuntimeService {
103
103
  `[Progress] Setting up progress tracking for sub-agent ${subAgentHandle.agentId} (toolCallId: ${toolCallId}, sessionId: ${sessionId})`
104
104
  );
105
105
  let toolCount = 0;
106
+ const tokenUsage = { input: 0, output: 0, total: 0 };
107
+ let currentTool = "";
106
108
  const subAgentBus = subAgentHandle.agent.agentEventBus;
107
109
  const parentBus = this.parentAgent.agentEventBus;
110
+ const emitProgress = (tool, args) => {
111
+ parentBus.emit("service:event", {
112
+ service: "agent-spawner",
113
+ event: "progress",
114
+ toolCallId,
115
+ sessionId,
116
+ data: {
117
+ task: input.task,
118
+ agentId: input.agentId ?? "default",
119
+ toolsCalled: toolCount,
120
+ currentTool: tool,
121
+ currentArgs: args,
122
+ tokenUsage: { ...tokenUsage }
123
+ }
124
+ });
125
+ };
108
126
  const toolCallHandler = (event) => {
109
127
  toolCount++;
110
128
  let displayToolName = event.toolName;
@@ -118,26 +136,28 @@ class RuntimeService {
118
136
  displayToolName = parts.slice(2).join("--");
119
137
  }
120
138
  }
139
+ currentTool = displayToolName;
121
140
  this.logger.debug(
122
141
  `[Progress] Sub-agent tool call #${toolCount}: ${displayToolName} (toolCallId: ${toolCallId})`
123
142
  );
124
- parentBus.emit("service:event", {
125
- service: "agent-spawner",
126
- event: "progress",
127
- toolCallId,
128
- sessionId,
129
- data: {
130
- task: input.task,
131
- agentId: input.agentId ?? "default",
132
- toolsCalled: toolCount,
133
- currentTool: displayToolName,
134
- currentArgs: event.args
135
- }
136
- });
143
+ emitProgress(displayToolName, event.args);
144
+ };
145
+ const responseHandler = (event) => {
146
+ if (event.tokenUsage) {
147
+ tokenUsage.input = event.tokenUsage.inputTokens ?? 0;
148
+ tokenUsage.output += event.tokenUsage.outputTokens ?? 0;
149
+ tokenUsage.total = tokenUsage.input + tokenUsage.output;
150
+ this.logger.debug(
151
+ `[Progress] Sub-agent tokens: input=${tokenUsage.input}, cumOutput=${tokenUsage.output}, total=${tokenUsage.total}`
152
+ );
153
+ emitProgress(currentTool || "processing");
154
+ }
137
155
  };
138
156
  subAgentBus.on("llm:tool-call", toolCallHandler);
157
+ subAgentBus.on("llm:response", responseHandler);
139
158
  return () => {
140
159
  subAgentBus.off("llm:tool-call", toolCallHandler);
160
+ subAgentBus.off("llm:response", responseHandler);
141
161
  };
142
162
  }
143
163
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/agent-management",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "yaml": "^2.7.1",
18
18
  "zod": "^3.25.0",
19
- "@dexto/core": "1.5.3"
19
+ "@dexto/core": "1.5.5"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "^22.13.5"