@dexto/agent-management 1.6.0 → 1.6.1

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 (46) hide show
  1. package/dist/config/config-enrichment.cjs +8 -3
  2. package/dist/config/config-enrichment.d.ts +6 -0
  3. package/dist/config/config-enrichment.d.ts.map +1 -1
  4. package/dist/config/config-enrichment.js +8 -3
  5. package/dist/preferences/loader.cjs +21 -6
  6. package/dist/preferences/loader.d.ts +4 -0
  7. package/dist/preferences/loader.d.ts.map +1 -1
  8. package/dist/preferences/loader.js +21 -6
  9. package/dist/preferences/schemas.cjs +5 -1
  10. package/dist/preferences/schemas.d.ts +32 -0
  11. package/dist/preferences/schemas.d.ts.map +1 -1
  12. package/dist/preferences/schemas.js +5 -1
  13. package/dist/registry/types.cjs +4 -0
  14. package/dist/registry/types.d.ts +8 -0
  15. package/dist/registry/types.d.ts.map +1 -1
  16. package/dist/registry/types.js +4 -0
  17. package/dist/runtime/AgentRuntime.cjs +23 -15
  18. package/dist/runtime/AgentRuntime.d.ts.map +1 -1
  19. package/dist/runtime/AgentRuntime.js +23 -15
  20. package/dist/runtime/approval-delegation.cjs +6 -3
  21. package/dist/runtime/approval-delegation.d.ts +2 -1
  22. package/dist/runtime/approval-delegation.d.ts.map +1 -1
  23. package/dist/runtime/approval-delegation.js +6 -3
  24. package/dist/runtime/schemas.cjs +1 -1
  25. package/dist/runtime/schemas.js +1 -1
  26. package/dist/runtime/types.d.ts +1 -1
  27. package/dist/runtime/types.d.ts.map +1 -1
  28. package/dist/tool-factories/agent-spawner/factory.cjs +63 -26
  29. package/dist/tool-factories/agent-spawner/factory.d.ts.map +1 -1
  30. package/dist/tool-factories/agent-spawner/factory.js +61 -24
  31. package/dist/tool-factories/agent-spawner/runtime.cjs +118 -14
  32. package/dist/tool-factories/agent-spawner/runtime.d.ts +5 -0
  33. package/dist/tool-factories/agent-spawner/runtime.d.ts.map +1 -1
  34. package/dist/tool-factories/agent-spawner/runtime.js +119 -15
  35. package/dist/tool-factories/agent-spawner/schemas.cjs +2 -2
  36. package/dist/tool-factories/agent-spawner/schemas.d.ts +1 -1
  37. package/dist/tool-factories/agent-spawner/schemas.d.ts.map +1 -1
  38. package/dist/tool-factories/agent-spawner/schemas.js +2 -2
  39. package/dist/tool-factories/agent-spawner/spawn-agent-tool.cjs +10 -1
  40. package/dist/tool-factories/agent-spawner/spawn-agent-tool.d.ts.map +1 -1
  41. package/dist/tool-factories/agent-spawner/spawn-agent-tool.js +10 -1
  42. package/dist/utils/path.cjs +10 -1
  43. package/dist/utils/path.d.ts +5 -2
  44. package/dist/utils/path.d.ts.map +1 -1
  45. package/dist/utils/path.js +10 -1
  46. package/package.json +6 -6
@@ -1,10 +1,11 @@
1
+ import { randomUUID } from "crypto";
1
2
  import { DextoRuntimeError, ErrorType } from "@dexto/core";
2
3
  import { AgentRuntime } from "../../runtime/AgentRuntime.js";
3
4
  import { createDelegatingApprovalHandler } from "../../runtime/approval-delegation.js";
4
5
  import { loadAgentConfig } from "../../config/loader.js";
5
6
  import { getAgentRegistry } from "../../registry/registry.js";
6
7
  import { deriveDisplayName } from "../../registry/types.js";
7
- import { resolveBundledScript } from "../../utils/path.js";
8
+ import { getDextoPath, resolveBundledScript } from "../../utils/path.js";
8
9
  import * as path from "path";
9
10
  import { resolveSubAgentLLM } from "./llm-resolution.js";
10
11
  class AgentSpawnerRuntime {
@@ -154,6 +155,10 @@ class AgentSpawnerRuntime {
154
155
  const tokenUsage = { input: 0, output: 0, total: 0 };
155
156
  let currentTool = "";
156
157
  const emitProgress = (tool, args) => {
158
+ const subAgentLogFilePath = this.getSubAgentLogFilePath({
159
+ runtimeAgentId: subAgentHandle.agentId,
160
+ sessionId
161
+ });
157
162
  this.parentAgent.emit("service:event", {
158
163
  service: "agent-spawner",
159
164
  event: "progress",
@@ -162,6 +167,8 @@ class AgentSpawnerRuntime {
162
167
  data: {
163
168
  task: input.task,
164
169
  agentId: input.agentId ?? "default",
170
+ runtimeAgentId: subAgentHandle.agentId,
171
+ ...subAgentLogFilePath ? { subAgentLogFilePath } : {},
165
172
  toolsCalled: toolCount,
166
173
  currentTool: tool,
167
174
  currentArgs: args,
@@ -201,6 +208,44 @@ class AgentSpawnerRuntime {
201
208
  subAgentHandle.agent.off("llm:response", responseHandler);
202
209
  };
203
210
  }
211
+ /**
212
+ * Ensure spawned agent inherits the parent's workspace context.
213
+ */
214
+ async applyParentWorkspace(agent) {
215
+ let parentWorkspace;
216
+ try {
217
+ parentWorkspace = await this.parentAgent.getWorkspace();
218
+ } catch (error) {
219
+ this.logger.warn(
220
+ `Failed to read parent workspace for sub-agent: ${error instanceof Error ? error.message : String(error)}`
221
+ );
222
+ return;
223
+ }
224
+ if (!parentWorkspace?.path) {
225
+ return;
226
+ }
227
+ try {
228
+ await agent.setWorkspace({
229
+ path: parentWorkspace.path,
230
+ ...parentWorkspace.name ? { name: parentWorkspace.name } : {}
231
+ });
232
+ } catch (error) {
233
+ this.logger.warn(
234
+ `Failed to apply parent workspace to sub-agent: ${error instanceof Error ? error.message : String(error)}`
235
+ );
236
+ }
237
+ }
238
+ getSubAgentLogFilePath(options) {
239
+ const { runtimeAgentId, sessionId } = options;
240
+ if (!sessionId) {
241
+ return null;
242
+ }
243
+ const safeSessionId = sessionId.replace(/[^a-zA-Z0-9._-]/g, "_");
244
+ return getDextoPath(
245
+ "logs",
246
+ path.join(this.parentId, `${safeSessionId}.subagent.${runtimeAgentId}.log`)
247
+ );
248
+ }
204
249
  /**
205
250
  * Check if an error is LLM-related (API errors, credit issues, model not found, etc.)
206
251
  */
@@ -222,7 +267,10 @@ class AgentSpawnerRuntime {
222
267
  let llmMode = "subagent";
223
268
  let cleanupProgressTracking;
224
269
  try {
225
- const buildOptions = {};
270
+ const buildOptions = {
271
+ // Pre-generate the runtime agentId so we can deterministically route logs.
272
+ runtimeAgentId: `agent-${randomUUID().slice(0, 8)}`
273
+ };
226
274
  if (input.agentId !== void 0) {
227
275
  buildOptions.agentId = input.agentId;
228
276
  }
@@ -233,6 +281,7 @@ class AgentSpawnerRuntime {
233
281
  let handle;
234
282
  try {
235
283
  handle = await this.runtime.spawnAgent({
284
+ agentId: buildOptions.runtimeAgentId,
236
285
  agentConfig: subAgentConfig,
237
286
  ephemeral: true,
238
287
  group: this.parentId,
@@ -247,6 +296,7 @@ class AgentSpawnerRuntime {
247
296
  const delegatingHandler = createDelegatingApprovalHandler(
248
297
  this.parentAgent.services.approvalManager,
249
298
  agent.config.agentId ?? "unknown",
299
+ sessionId,
250
300
  this.logger
251
301
  );
252
302
  agent.setApprovalHandler(delegatingHandler);
@@ -254,6 +304,7 @@ class AgentSpawnerRuntime {
254
304
  }
255
305
  });
256
306
  spawnedAgentId = handle.agentId;
307
+ await this.applyParentWorkspace(handle.agent);
257
308
  } catch (spawnError) {
258
309
  const isLlmError = this.isLLMError(spawnError);
259
310
  if (isLlmError && input.agentId && llmMode === "subagent") {
@@ -265,6 +316,7 @@ class AgentSpawnerRuntime {
265
316
  buildOptions.inheritLlm = true;
266
317
  subAgentConfig = await this.buildSubAgentConfig(buildOptions, sessionId);
267
318
  handle = await this.runtime.spawnAgent({
319
+ agentId: buildOptions.runtimeAgentId,
268
320
  agentConfig: subAgentConfig,
269
321
  ephemeral: true,
270
322
  group: this.parentId,
@@ -281,6 +333,7 @@ class AgentSpawnerRuntime {
281
333
  const delegatingHandler = createDelegatingApprovalHandler(
282
334
  this.parentAgent.services.approvalManager,
283
335
  agent.config.agentId ?? "unknown",
336
+ sessionId,
284
337
  this.logger
285
338
  );
286
339
  agent.setApprovalHandler(delegatingHandler);
@@ -288,6 +341,7 @@ class AgentSpawnerRuntime {
288
341
  }
289
342
  });
290
343
  spawnedAgentId = handle.agentId;
344
+ await this.applyParentWorkspace(handle.agent);
291
345
  } else {
292
346
  throw spawnError;
293
347
  }
@@ -295,6 +349,23 @@ class AgentSpawnerRuntime {
295
349
  this.logger.info(
296
350
  `Spawned sub-agent '${spawnedAgentId}' for task: ${input.task}${autoApprove ? " (auto-approve)" : ""}${llmMode === "parent" ? " (using parent LLM)" : ""}`
297
351
  );
352
+ const subAgentLogFilePath = this.getSubAgentLogFilePath(
353
+ sessionId ? { runtimeAgentId: buildOptions.runtimeAgentId, sessionId } : { runtimeAgentId: buildOptions.runtimeAgentId }
354
+ );
355
+ if (subAgentLogFilePath) {
356
+ this.logger.info(`Sub-agent logs: ${subAgentLogFilePath}`);
357
+ }
358
+ if (sessionId) {
359
+ const parentSession = await this.parentAgent.getSession(sessionId);
360
+ if (parentSession) {
361
+ parentSession.logger.info("Sub-agent spawned", {
362
+ runtimeAgentId: spawnedAgentId,
363
+ registryAgentId: input.agentId ?? "default",
364
+ task: input.task,
365
+ ...subAgentLogFilePath ? { subAgentLogFilePath } : {}
366
+ });
367
+ }
368
+ }
298
369
  cleanupProgressTracking = this.setupProgressTracking(
299
370
  handle,
300
371
  input,
@@ -328,6 +399,7 @@ class AgentSpawnerRuntime {
328
399
  buildOptions.inheritLlm = true;
329
400
  subAgentConfig = await this.buildSubAgentConfig(buildOptions, sessionId);
330
401
  handle = await this.runtime.spawnAgent({
402
+ agentId: buildOptions.runtimeAgentId,
331
403
  agentConfig: subAgentConfig,
332
404
  ephemeral: true,
333
405
  group: this.parentId,
@@ -344,6 +416,7 @@ class AgentSpawnerRuntime {
344
416
  const delegatingHandler = createDelegatingApprovalHandler(
345
417
  this.parentAgent.services.approvalManager,
346
418
  agent.config.agentId ?? "unknown",
419
+ sessionId,
347
420
  this.logger
348
421
  );
349
422
  agent.setApprovalHandler(delegatingHandler);
@@ -351,6 +424,7 @@ class AgentSpawnerRuntime {
351
424
  }
352
425
  });
353
426
  spawnedAgentId = handle.agentId;
427
+ await this.applyParentWorkspace(handle.agent);
354
428
  this.logger.info(
355
429
  `Re-spawned sub-agent '${spawnedAgentId}' for task: ${input.task} (using parent LLM)`
356
430
  );
@@ -410,13 +484,49 @@ class AgentSpawnerRuntime {
410
484
  * @param sessionId - Optional session ID to get session-specific LLM config
411
485
  */
412
486
  async buildSubAgentConfig(options, sessionId) {
413
- const { agentId, inheritLlm, autoApprove } = options;
487
+ const { agentId, inheritLlm, autoApprove, runtimeAgentId } = options;
414
488
  const parentSettings = this.parentAgent.config;
415
489
  const currentParentLLM = this.parentAgent.getCurrentLLMConfig(sessionId);
416
490
  this.logger.debug(
417
491
  `[AgentSpawnerRuntime] Building sub-agent config with LLM: ${currentParentLLM.provider}/${currentParentLLM.model}` + (sessionId ? ` (sessionId: ${sessionId})` : " (no sessionId)")
418
492
  );
419
493
  const permissionsMode = autoApprove ? "auto-approve" : "manual";
494
+ const parentToolPolicies = parentSettings.permissions?.toolPolicies;
495
+ const mergeToolPolicies = (subAgentPolicies) => {
496
+ const alwaysAllow = [
497
+ ...parentToolPolicies?.alwaysAllow ?? [],
498
+ ...subAgentPolicies?.alwaysAllow ?? []
499
+ ];
500
+ const alwaysDeny = [
501
+ ...parentToolPolicies?.alwaysDeny ?? [],
502
+ ...subAgentPolicies?.alwaysDeny ?? []
503
+ ];
504
+ return {
505
+ alwaysAllow: Array.from(new Set(alwaysAllow)),
506
+ alwaysDeny: Array.from(new Set(alwaysDeny))
507
+ };
508
+ };
509
+ const inheritedLoggerConfig = await (async () => {
510
+ if (!sessionId) {
511
+ return void 0;
512
+ }
513
+ const session = await this.parentAgent.getSession(sessionId);
514
+ if (!session) {
515
+ return void 0;
516
+ }
517
+ const parentSessionLogPath = session.logger.getLogFilePath();
518
+ if (!parentSessionLogPath) {
519
+ return void 0;
520
+ }
521
+ const subAgentLogFilePath = this.getSubAgentLogFilePath({ runtimeAgentId, sessionId });
522
+ if (!subAgentLogFilePath) {
523
+ return void 0;
524
+ }
525
+ return {
526
+ level: session.logger.getLevel(),
527
+ transports: [{ type: "file", path: subAgentLogFilePath }]
528
+ };
529
+ })();
420
530
  if (agentId) {
421
531
  let configPath = null;
422
532
  try {
@@ -459,13 +569,10 @@ class AgentSpawnerRuntime {
459
569
  llm: llmConfig,
460
570
  permissions: {
461
571
  ...loadedConfig.permissions,
462
- mode: permissionsMode
572
+ mode: permissionsMode,
573
+ toolPolicies: mergeToolPolicies(loadedConfig.permissions?.toolPolicies)
463
574
  },
464
- // Suppress sub-agent console logs entirely using silent transport
465
- logger: {
466
- level: "error",
467
- transports: [{ type: "silent" }]
468
- }
575
+ ...inheritedLoggerConfig !== void 0 && { logger: inheritedLoggerConfig }
469
576
  };
470
577
  }
471
578
  this.logger.warn(
@@ -477,15 +584,12 @@ class AgentSpawnerRuntime {
477
584
  // Default system prompt for sub-agents
478
585
  systemPrompt: "You are a helpful sub-agent. Complete the task given to you efficiently and concisely.",
479
586
  permissions: {
480
- mode: permissionsMode
587
+ mode: permissionsMode,
588
+ toolPolicies: mergeToolPolicies(void 0)
481
589
  },
482
590
  // Inherit MCP servers from parent so subagent has tool access
483
591
  mcpServers: parentSettings.mcpServers ? { ...parentSettings.mcpServers } : {},
484
- // Suppress sub-agent console logs entirely using silent transport
485
- logger: {
486
- level: "error",
487
- transports: [{ type: "silent" }]
488
- }
592
+ ...inheritedLoggerConfig !== void 0 && { logger: inheritedLoggerConfig }
489
593
  };
490
594
  return config;
491
595
  }
@@ -28,8 +28,8 @@ const AgentSpawnerConfigSchema = import_zod.z.object({
28
28
  type: import_zod.z.literal("agent-spawner"),
29
29
  /** Maximum concurrent sub-agents this parent can spawn (default: 5) */
30
30
  maxConcurrentAgents: import_zod.z.number().int().positive().default(5).describe("Maximum concurrent sub-agents"),
31
- /** Default timeout for task execution in milliseconds (default: 300000 = 5 min) */
32
- defaultTimeout: import_zod.z.number().int().positive().default(3e5).describe("Default task timeout in milliseconds"),
31
+ /** Default timeout for task execution in milliseconds (default: 3600000 = 1 hour) */
32
+ defaultTimeout: import_zod.z.number().int().nonnegative().default(36e5).describe("Default task timeout in milliseconds (0 = no timeout)"),
33
33
  /** Whether spawning is enabled (default: true) */
34
34
  allowSpawning: import_zod.z.boolean().default(true).describe("Whether agent spawning is enabled"),
35
35
  /**
@@ -12,7 +12,7 @@ export declare const AgentSpawnerConfigSchema: z.ZodObject<{
12
12
  type: z.ZodLiteral<"agent-spawner">;
13
13
  /** Maximum concurrent sub-agents this parent can spawn (default: 5) */
14
14
  maxConcurrentAgents: z.ZodDefault<z.ZodNumber>;
15
- /** Default timeout for task execution in milliseconds (default: 300000 = 5 min) */
15
+ /** Default timeout for task execution in milliseconds (default: 3600000 = 1 hour) */
16
16
  defaultTimeout: z.ZodDefault<z.ZodNumber>;
17
17
  /** Whether spawning is enabled (default: true) */
18
18
  allowSpawning: z.ZodDefault<z.ZodBoolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;GAEG;AACH,eAAO,MAAM,wBAAwB;IAE7B,yCAAyC;;IAGzC,uEAAuE;;IAQvE,mFAAmF;;IAQnF,kDAAkD;;IAGlD;;;;;;;;;;OAUG;;IAMH;;;;;;;;;;;OAWG;;;;;;;;;;;;;;;;EAOuD,CAAC;AAEnE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAM3E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;IAE1B,gDAAgD;;IAGhD,8CAA8C;;IAM9C,qFAAqF;;;;;;;;;;EAGhF,CAAC;AAEd,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;GAEG;AACH,eAAO,MAAM,wBAAwB;IAE7B,yCAAyC;;IAGzC,uEAAuE;;IAQvE,qFAAqF;;IAQrF,kDAAkD;;IAGlD;;;;;;;;;;OAUG;;IAMH;;;;;;;;;;;OAWG;;;;;;;;;;;;;;;;EAOuD,CAAC;AAEnE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAM3E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;IAE1B,gDAAgD;;IAGhD,8CAA8C;;IAM9C,qFAAqF;;;;;;;;;;EAGhF,CAAC;AAEd,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
@@ -4,8 +4,8 @@ const AgentSpawnerConfigSchema = z.object({
4
4
  type: z.literal("agent-spawner"),
5
5
  /** Maximum concurrent sub-agents this parent can spawn (default: 5) */
6
6
  maxConcurrentAgents: z.number().int().positive().default(5).describe("Maximum concurrent sub-agents"),
7
- /** Default timeout for task execution in milliseconds (default: 300000 = 5 min) */
8
- defaultTimeout: z.number().int().positive().default(3e5).describe("Default task timeout in milliseconds"),
7
+ /** Default timeout for task execution in milliseconds (default: 3600000 = 1 hour) */
8
+ defaultTimeout: z.number().int().nonnegative().default(36e5).describe("Default task timeout in milliseconds (0 = no timeout)"),
9
9
  /** Whether spawning is enabled (default: true) */
10
10
  allowSpawning: z.boolean().default(true).describe("Whether agent spawning is enabled"),
11
11
  /**
@@ -21,6 +21,7 @@ __export(spawn_agent_tool_exports, {
21
21
  createSpawnAgentTool: () => createSpawnAgentTool
22
22
  });
23
23
  module.exports = __toCommonJS(spawn_agent_tool_exports);
24
+ var import_core = require("@dexto/core");
24
25
  var import_schemas = require("./schemas.js");
25
26
  function buildDescription(service) {
26
27
  const availableAgents = service.getAvailableAgents();
@@ -57,9 +58,17 @@ ${agentsList}
57
58
  function createSpawnAgentTool(service) {
58
59
  return {
59
60
  id: "spawn_agent",
60
- displayName: "Agent",
61
61
  aliases: ["task"],
62
62
  description: buildDescription(service),
63
+ presentation: {
64
+ describeHeader: (input) => {
65
+ const agentLabel = input.agentId ? input.agentId.replace(/-agent$/, "") : null;
66
+ const title = agentLabel ? agentLabel.charAt(0).toUpperCase() + agentLabel.slice(1) : "Agent";
67
+ const task = typeof input.task === "string" ? input.task : "";
68
+ const argsText = task ? (0, import_core.truncateForHeader)(task, 120) : void 0;
69
+ return (0, import_core.createLocalToolCallHeader)({ title, ...argsText ? { argsText } : {} });
70
+ }
71
+ },
63
72
  inputSchema: import_schemas.SpawnAgentInputSchema,
64
73
  execute: async (input, context) => {
65
74
  const options = {
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-agent-tool.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/spawn-agent-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA4CxD,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,mBAAmB,GAC7B,IAAI,CAAC,OAAO,qBAAqB,CAAC,CA0CpC"}
1
+ {"version":3,"file":"spawn-agent-tool.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/agent-spawner/spawn-agent-tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AA4CxD,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,mBAAmB,GAC7B,IAAI,CAAC,OAAO,qBAAqB,CAAC,CAuDpC"}
@@ -1,3 +1,4 @@
1
+ import { createLocalToolCallHeader, truncateForHeader } from "@dexto/core";
1
2
  import { SpawnAgentInputSchema } from "./schemas.js";
2
3
  function buildDescription(service) {
3
4
  const availableAgents = service.getAvailableAgents();
@@ -34,9 +35,17 @@ ${agentsList}
34
35
  function createSpawnAgentTool(service) {
35
36
  return {
36
37
  id: "spawn_agent",
37
- displayName: "Agent",
38
38
  aliases: ["task"],
39
39
  description: buildDescription(service),
40
+ presentation: {
41
+ describeHeader: (input) => {
42
+ const agentLabel = input.agentId ? input.agentId.replace(/-agent$/, "") : null;
43
+ const title = agentLabel ? agentLabel.charAt(0).toUpperCase() + agentLabel.slice(1) : "Agent";
44
+ const task = typeof input.task === "string" ? input.task : "";
45
+ const argsText = task ? truncateForHeader(task, 120) : void 0;
46
+ return createLocalToolCallHeader({ title, ...argsText ? { argsText } : {} });
47
+ }
48
+ },
40
49
  inputSchema: SpawnAgentInputSchema,
41
50
  execute: async (input, context) => {
42
51
  const options = {
@@ -83,6 +83,15 @@ function getDextoPath(type, filename, startPath) {
83
83
  return filename ? path.join(basePath, filename) : basePath;
84
84
  }
85
85
  function getDextoGlobalPath(type, filename) {
86
+ const isDevMode = process.env.DEXTO_DEV_MODE === "true";
87
+ if (isDevMode && (0, import_execution_context.getExecutionContext)() === "dexto-source") {
88
+ const sourceRoot = (0, import_execution_context.findDextoSourceRoot)();
89
+ if (!sourceRoot) {
90
+ throw new Error("Not in dexto source context");
91
+ }
92
+ const devBasePath = path.join(sourceRoot, ".dexto", type);
93
+ return filename ? path.join(devBasePath, filename) : devBasePath;
94
+ }
86
95
  const basePath = path.join((0, import_os.homedir)(), ".dexto", type);
87
96
  return filename ? path.join(basePath, filename) : basePath;
88
97
  }
@@ -151,7 +160,7 @@ function resolveBundledScript(scriptPath) {
151
160
  );
152
161
  }
153
162
  async function ensureDextoGlobalDirectory() {
154
- const dextoDir = path.join((0, import_os.homedir)(), ".dexto");
163
+ const dextoDir = getDextoGlobalPath("");
155
164
  try {
156
165
  await import_fs2.promises.mkdir(dextoDir, { recursive: true });
157
166
  } catch (error) {
@@ -12,8 +12,11 @@
12
12
  */
13
13
  export declare function getDextoPath(type: string, filename?: string, startPath?: string): string;
14
14
  /**
15
- * Global path resolver that ALWAYS returns paths in the user's home directory
16
- * Used for agent registry and other global-only resources that should not be project-relative
15
+ * Global path resolver for user-global resources that should not be project-relative.
16
+ *
17
+ * Dev mode support:
18
+ * - dexto-source + DEXTO_DEV_MODE=true: Use repo-local `.dexto` (isolated testing)
19
+ * - otherwise: Use global `~/.dexto` (user experience)
17
20
  * @param type Path type (agents, cache, etc.)
18
21
  * @param filename Optional filename to append
19
22
  * @returns Absolute path to the global location (~/.dexto/...)
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAuCxF;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAI1E;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe5E;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW3C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAKhF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAwD/D;AAED;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAUhE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,CAkCzE"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAuCxF;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAc1E;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe5E;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW3C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAKhF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAwD/D;AAED;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAUhE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,CAkCzE"}
@@ -46,6 +46,15 @@ function getDextoPath(type, filename, startPath) {
46
46
  return filename ? path.join(basePath, filename) : basePath;
47
47
  }
48
48
  function getDextoGlobalPath(type, filename) {
49
+ const isDevMode = process.env.DEXTO_DEV_MODE === "true";
50
+ if (isDevMode && getExecutionContext() === "dexto-source") {
51
+ const sourceRoot = findDextoSourceRoot();
52
+ if (!sourceRoot) {
53
+ throw new Error("Not in dexto source context");
54
+ }
55
+ const devBasePath = path.join(sourceRoot, ".dexto", type);
56
+ return filename ? path.join(devBasePath, filename) : devBasePath;
57
+ }
49
58
  const basePath = path.join(homedir(), ".dexto", type);
50
59
  return filename ? path.join(basePath, filename) : basePath;
51
60
  }
@@ -114,7 +123,7 @@ function resolveBundledScript(scriptPath) {
114
123
  );
115
124
  }
116
125
  async function ensureDextoGlobalDirectory() {
117
- const dextoDir = path.join(homedir(), ".dexto");
126
+ const dextoDir = getDextoGlobalPath("");
118
127
  try {
119
128
  await fs.mkdir(dextoDir, { recursive: true });
120
129
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/agent-management",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,10 +16,10 @@
16
16
  "dependencies": {
17
17
  "yaml": "^2.7.1",
18
18
  "zod": "^3.25.0",
19
- "@dexto/agent-config": "1.6.0",
20
- "@dexto/core": "1.6.0",
21
- "@dexto/orchestration": "1.6.0",
22
- "@dexto/tools-builtins": "1.6.0"
19
+ "@dexto/agent-config": "1.6.1",
20
+ "@dexto/core": "1.6.1",
21
+ "@dexto/orchestration": "1.6.1",
22
+ "@dexto/tools-builtins": "1.6.1"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.13.5"
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "sideEffects": false,
38
38
  "scripts": {
39
- "build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tsup && cross-env NODE_OPTIONS='--max-old-space-size=4096' tsc -b tsconfig.json --force --emitDeclarationOnly && node scripts/fix-dist-aliases.mjs",
39
+ "build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tsup && node ../../scripts/clean-tsbuildinfo.mjs && cross-env NODE_OPTIONS='--max-old-space-size=4096' tsc -b tsconfig.json --emitDeclarationOnly && node scripts/fix-dist-aliases.mjs",
40
40
  "dev": "tsup --watch",
41
41
  "typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
42
42
  "lint": "eslint . --ext .ts"