wave-agent-sdk 0.0.1 → 0.0.3

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 (82) hide show
  1. package/dist/agent.d.ts +37 -3
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +82 -5
  4. package/dist/index.d.ts +0 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +0 -1
  7. package/dist/managers/aiManager.d.ts +7 -1
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +11 -5
  10. package/dist/managers/messageManager.d.ts +8 -0
  11. package/dist/managers/messageManager.d.ts.map +1 -1
  12. package/dist/managers/messageManager.js +26 -2
  13. package/dist/managers/skillManager.d.ts +4 -5
  14. package/dist/managers/skillManager.d.ts.map +1 -1
  15. package/dist/managers/skillManager.js +6 -82
  16. package/dist/managers/subagentManager.d.ts +96 -0
  17. package/dist/managers/subagentManager.d.ts.map +1 -0
  18. package/dist/managers/subagentManager.js +261 -0
  19. package/dist/managers/toolManager.d.ts +33 -1
  20. package/dist/managers/toolManager.d.ts.map +1 -1
  21. package/dist/managers/toolManager.js +43 -5
  22. package/dist/services/aiService.d.ts +5 -0
  23. package/dist/services/aiService.d.ts.map +1 -1
  24. package/dist/services/aiService.js +58 -28
  25. package/dist/services/session.d.ts.map +1 -1
  26. package/dist/services/session.js +4 -0
  27. package/dist/tools/grepTool.d.ts.map +1 -1
  28. package/dist/tools/grepTool.js +8 -6
  29. package/dist/tools/readTool.d.ts.map +1 -1
  30. package/dist/tools/readTool.js +36 -6
  31. package/dist/tools/skillTool.d.ts +8 -0
  32. package/dist/tools/skillTool.d.ts.map +1 -0
  33. package/dist/tools/skillTool.js +72 -0
  34. package/dist/tools/taskTool.d.ts +8 -0
  35. package/dist/tools/taskTool.d.ts.map +1 -0
  36. package/dist/tools/taskTool.js +109 -0
  37. package/dist/tools/todoWriteTool.d.ts +6 -0
  38. package/dist/tools/todoWriteTool.d.ts.map +1 -0
  39. package/dist/tools/todoWriteTool.js +203 -0
  40. package/dist/types.d.ts +65 -1
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/types.js +16 -0
  43. package/dist/utils/configResolver.d.ts +38 -0
  44. package/dist/utils/configResolver.d.ts.map +1 -0
  45. package/dist/utils/configResolver.js +106 -0
  46. package/dist/utils/configValidator.d.ts +36 -0
  47. package/dist/utils/configValidator.d.ts.map +1 -0
  48. package/dist/utils/configValidator.js +78 -0
  49. package/dist/utils/constants.d.ts +10 -0
  50. package/dist/utils/constants.d.ts.map +1 -1
  51. package/dist/utils/constants.js +10 -0
  52. package/dist/utils/fileFormat.d.ts +17 -0
  53. package/dist/utils/fileFormat.d.ts.map +1 -0
  54. package/dist/utils/fileFormat.js +35 -0
  55. package/dist/utils/messageOperations.d.ts +18 -0
  56. package/dist/utils/messageOperations.d.ts.map +1 -1
  57. package/dist/utils/messageOperations.js +43 -0
  58. package/dist/utils/subagentParser.d.ts +19 -0
  59. package/dist/utils/subagentParser.d.ts.map +1 -0
  60. package/dist/utils/subagentParser.js +159 -0
  61. package/package.json +11 -15
  62. package/src/agent.ts +130 -9
  63. package/src/index.ts +0 -1
  64. package/src/managers/aiManager.ts +22 -10
  65. package/src/managers/messageManager.ts +55 -1
  66. package/src/managers/skillManager.ts +7 -96
  67. package/src/managers/subagentManager.ts +368 -0
  68. package/src/managers/toolManager.ts +50 -5
  69. package/src/services/aiService.ts +92 -36
  70. package/src/services/session.ts +5 -0
  71. package/src/tools/grepTool.ts +9 -6
  72. package/src/tools/readTool.ts +40 -6
  73. package/src/tools/skillTool.ts +82 -0
  74. package/src/tools/taskTool.ts +128 -0
  75. package/src/tools/todoWriteTool.ts +232 -0
  76. package/src/types.ts +85 -1
  77. package/src/utils/configResolver.ts +142 -0
  78. package/src/utils/configValidator.ts +133 -0
  79. package/src/utils/constants.ts +10 -0
  80. package/src/utils/fileFormat.ts +40 -0
  81. package/src/utils/messageOperations.ts +80 -0
  82. package/src/utils/subagentParser.ts +223 -0
@@ -0,0 +1,159 @@
1
+ import { readFileSync, readdirSync, statSync } from "fs";
2
+ import { join, extname } from "path";
3
+ /**
4
+ * Parse YAML frontmatter from markdown file content
5
+ */
6
+ function parseFrontmatter(content) {
7
+ const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
8
+ const match = content.match(frontmatterRegex);
9
+ if (!match) {
10
+ return { frontmatter: {}, body: content.trim() };
11
+ }
12
+ const [, yamlContent, body] = match;
13
+ const frontmatter = parseYamlFrontmatter(yamlContent);
14
+ return { frontmatter, body: body.trim() };
15
+ }
16
+ /**
17
+ * Simple YAML frontmatter parser for subagent files
18
+ */
19
+ function parseYamlFrontmatter(yamlContent) {
20
+ const frontmatter = {};
21
+ try {
22
+ const lines = yamlContent.split("\n");
23
+ for (const line of lines) {
24
+ const trimmed = line.trim();
25
+ if (!trimmed || trimmed.startsWith("#"))
26
+ continue;
27
+ const colonIndex = trimmed.indexOf(":");
28
+ if (colonIndex === -1)
29
+ continue;
30
+ const key = trimmed.substring(0, colonIndex).trim();
31
+ const value = trimmed
32
+ .substring(colonIndex + 1)
33
+ .trim()
34
+ .replace(/^["']|["']$/g, "");
35
+ if (key && value) {
36
+ // Handle array values for tools
37
+ if (key === "tools" && value) {
38
+ let arrayValue = value;
39
+ if (arrayValue.startsWith("[") && arrayValue.endsWith("]")) {
40
+ arrayValue = arrayValue.slice(1, -1);
41
+ }
42
+ frontmatter[key] = arrayValue
43
+ .split(",")
44
+ .map((s) => s.trim())
45
+ .filter(Boolean);
46
+ }
47
+ else {
48
+ if (key === "name" || key === "description" || key === "model") {
49
+ frontmatter[key] = value;
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ catch {
56
+ // Return empty frontmatter on parse error - validation will catch missing fields
57
+ }
58
+ return frontmatter;
59
+ }
60
+ /**
61
+ * Validate subagent configuration
62
+ */
63
+ function validateConfiguration(config, filePath) {
64
+ if (!config.name) {
65
+ throw new Error(`Missing required field 'name' in ${filePath}`);
66
+ }
67
+ if (!config.description) {
68
+ throw new Error(`Missing required field 'description' in ${filePath}`);
69
+ }
70
+ // Validate name pattern
71
+ const namePattern = /^[a-z][a-z0-9-]*$/;
72
+ if (!namePattern.test(config.name)) {
73
+ throw new Error(`Invalid subagent name '${config.name}' in ${filePath}. Must start with a letter and contain only lowercase letters, numbers, and hyphens.`);
74
+ }
75
+ // Validate model if specified - allow any non-empty string
76
+ if (config.model && typeof config.model !== "string") {
77
+ throw new Error(`Invalid model '${config.model}' in ${filePath}. Must be a string.`);
78
+ }
79
+ }
80
+ /**
81
+ * Parse a single subagent markdown file
82
+ */
83
+ function parseSubagentFile(filePath, scope) {
84
+ try {
85
+ const content = readFileSync(filePath, "utf-8");
86
+ const { frontmatter, body } = parseFrontmatter(content);
87
+ validateConfiguration(frontmatter, filePath);
88
+ if (!body.trim()) {
89
+ throw new Error(`Empty system prompt in ${filePath}`);
90
+ }
91
+ return {
92
+ name: frontmatter.name,
93
+ description: frontmatter.description,
94
+ tools: frontmatter.tools,
95
+ model: frontmatter.model,
96
+ systemPrompt: body,
97
+ filePath,
98
+ scope,
99
+ priority: scope === "project" ? 1 : 2,
100
+ };
101
+ }
102
+ catch (error) {
103
+ throw new Error(`Failed to parse subagent file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
104
+ }
105
+ }
106
+ /**
107
+ * Scan directory for subagent files
108
+ */
109
+ function scanSubagentDirectory(dirPath, scope) {
110
+ const configurations = [];
111
+ try {
112
+ const entries = readdirSync(dirPath);
113
+ for (const entry of entries) {
114
+ const fullPath = join(dirPath, entry);
115
+ const stat = statSync(fullPath);
116
+ if (stat.isFile() && extname(entry) === ".md") {
117
+ try {
118
+ const config = parseSubagentFile(fullPath, scope);
119
+ configurations.push(config);
120
+ }
121
+ catch (parseError) {
122
+ // Log error but continue with other files
123
+ console.warn(`Warning: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
124
+ }
125
+ }
126
+ }
127
+ }
128
+ catch {
129
+ // Directory doesn't exist or can't be read - this is OK
130
+ }
131
+ return configurations;
132
+ }
133
+ /**
134
+ * Load all subagent configurations from project and user directories
135
+ */
136
+ export async function loadSubagentConfigurations(workdir) {
137
+ const projectDir = join(workdir, ".wave", "agents");
138
+ const userDir = join(process.env.HOME || "~", ".wave", "agents");
139
+ const projectConfigs = scanSubagentDirectory(projectDir, "project");
140
+ const userConfigs = scanSubagentDirectory(userDir, "user");
141
+ // Merge configurations, with project configs taking precedence
142
+ const configMap = new Map();
143
+ // Process in reverse priority order (user first, then project)
144
+ for (const config of [...userConfigs, ...projectConfigs]) {
145
+ configMap.set(config.name, config);
146
+ }
147
+ return Array.from(configMap.values()).sort((a, b) => {
148
+ if (a.priority !== b.priority)
149
+ return a.priority - b.priority;
150
+ return a.name.localeCompare(b.name);
151
+ });
152
+ }
153
+ /**
154
+ * Find subagent by exact name match
155
+ */
156
+ export async function findSubagentByName(name, workdir) {
157
+ const configurations = await loadSubagentConfigurations(workdir);
158
+ return configurations.find((config) => config.name === name) || null;
159
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -18,18 +18,6 @@
18
18
  "src",
19
19
  "README.md"
20
20
  ],
21
- "scripts": {
22
- "build": "rimraf dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
23
- "type-check": "tsc --noEmit --incremental",
24
- "dev": "tsc -p tsconfig.build.json --watch & tsc-alias -p tsconfig.build.json --watch",
25
- "test": "vitest run",
26
- "lint": "eslint --cache",
27
- "format": "prettier --write .",
28
- "release:patch": "pnpm version patch && npm publish",
29
- "release:minor": "pnpm version minor && npm publish",
30
- "release:major": "pnpm version major && npm publish",
31
- "prepublishOnly": "npm run build"
32
- },
33
21
  "dependencies": {
34
22
  "@modelcontextprotocol/sdk": "^1.18.2",
35
23
  "@vscode/ripgrep": "^1.15.14",
@@ -47,5 +35,13 @@
47
35
  "engines": {
48
36
  "node": ">=16.0.0"
49
37
  },
50
- "license": "MIT"
51
- }
38
+ "license": "MIT",
39
+ "scripts": {
40
+ "build": "rimraf dist && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
41
+ "type-check": "tsc --noEmit --incremental",
42
+ "dev": "tsc -p tsconfig.build.json --watch & tsc-alias -p tsconfig.build.json --watch",
43
+ "test": "vitest run",
44
+ "lint": "eslint --cache",
45
+ "format": "prettier --write ."
46
+ }
47
+ }
package/src/agent.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  } from "./managers/messageManager.js";
5
5
  import { AIManager } from "./managers/aiManager.js";
6
6
  import { ToolManager } from "./managers/toolManager.js";
7
+ import { SubagentManager } from "./managers/subagentManager.js";
7
8
  import * as memory from "./services/memory.js";
8
9
  import { McpManager, type McpManagerCallbacks } from "./managers/mcpManager.js";
9
10
  import { BashManager } from "./managers/bashManager.js";
@@ -13,10 +14,33 @@ import {
13
14
  } from "./managers/backgroundBashManager.js";
14
15
  import { SlashCommandManager } from "./managers/slashCommandManager.js";
15
16
  import type { SlashCommand, CustomSlashCommand } from "./types.js";
16
- import type { Message, Logger, McpServerStatus } from "./types.js";
17
+ import type {
18
+ Message,
19
+ Logger,
20
+ McpServerStatus,
21
+ GatewayConfig,
22
+ ModelConfig,
23
+ } from "./types.js";
17
24
  import { HookManager } from "./hooks/index.js";
18
-
25
+ import { configResolver } from "./utils/configResolver.js";
26
+ import { configValidator } from "./utils/configValidator.js";
27
+ import { SkillManager } from "./managers/skillManager.js";
28
+
29
+ /**
30
+ * Configuration options for Agent instances
31
+ *
32
+ * IMPORTANT: This interface is used by both Agent constructor and Agent.create()
33
+ * Any changes to this interface must be compatible with both methods.
34
+ */
19
35
  export interface AgentOptions {
36
+ // Optional configuration with environment fallbacks
37
+ apiKey?: string;
38
+ baseURL?: string;
39
+ agentModel?: string;
40
+ fastModel?: string;
41
+ tokenLimit?: number;
42
+
43
+ // Existing options (preserved)
20
44
  callbacks?: AgentCallbacks;
21
45
  restoreSessionId?: string;
22
46
  continueLastSession?: boolean;
@@ -37,32 +61,72 @@ export interface AgentCallbacks
37
61
  export class Agent {
38
62
  private messageManager: MessageManager;
39
63
  private aiManager: AIManager;
40
- private callbacks: AgentCallbacks;
41
64
 
42
65
  private bashManager: BashManager | null = null;
43
66
  private backgroundBashManager: BackgroundBashManager;
44
67
  private logger?: Logger; // Add optional logger property
45
68
  private toolManager: ToolManager; // Add tool registry instance
46
69
  private mcpManager: McpManager; // Add MCP manager instance
70
+ private subagentManager: SubagentManager; // Add subagent manager instance
47
71
  private slashCommandManager: SlashCommandManager; // Add slash command manager instance
48
72
  private hookManager: HookManager; // Add hooks manager instance
49
73
  private workdir: string; // Working directory
50
74
  private systemPrompt?: string; // Custom system prompt
51
75
 
52
- // Private constructor to prevent direct instantiation
53
- private constructor(options: AgentOptions) {
76
+ // Configuration properties
77
+ private gatewayConfig: GatewayConfig;
78
+ private modelConfig: ModelConfig;
79
+ private tokenLimit: number;
80
+
81
+ /**
82
+ * Agent constructor - handles configuration resolution and validation
83
+ *
84
+ * IMPORTANT: Keep this constructor's signature exactly the same as Agent.create()
85
+ * to maintain API consistency. Both methods should accept the same AgentOptions.
86
+ *
87
+ * @param options - Configuration options for the Agent instance
88
+ */
89
+ constructor(options: AgentOptions) {
54
90
  const { callbacks = {}, logger, workdir, systemPrompt } = options;
55
91
 
56
- this.callbacks = callbacks;
92
+ // Resolve configuration from constructor args and environment variables
93
+ const gatewayConfig = configResolver.resolveGatewayConfig(
94
+ options.apiKey,
95
+ options.baseURL,
96
+ );
97
+ const modelConfig = configResolver.resolveModelConfig(
98
+ options.agentModel,
99
+ options.fastModel,
100
+ );
101
+ const tokenLimit = configResolver.resolveTokenLimit(options.tokenLimit);
102
+
103
+ // Validate resolved configuration
104
+ configValidator.validateGatewayConfig(gatewayConfig);
105
+ configValidator.validateTokenLimit(tokenLimit);
106
+ configValidator.validateModelConfig(
107
+ modelConfig.agentModel,
108
+ modelConfig.fastModel,
109
+ );
110
+
57
111
  this.logger = logger; // Save the passed logger
58
112
  this.workdir = workdir || process.cwd(); // Set working directory, default to current working directory
59
113
  this.systemPrompt = systemPrompt; // Save custom system prompt
114
+
115
+ // Store resolved configuration
116
+ this.gatewayConfig = gatewayConfig;
117
+ this.modelConfig = modelConfig;
118
+ this.tokenLimit = tokenLimit;
119
+
60
120
  this.backgroundBashManager = new BackgroundBashManager({
61
121
  callbacks,
62
122
  workdir: this.workdir,
63
123
  });
64
124
  this.mcpManager = new McpManager({ callbacks, logger: this.logger }); // Initialize MCP manager
65
- this.toolManager = new ToolManager({ mcpManager: this.mcpManager }); // Initialize tool registry, pass MCP manager
125
+ this.toolManager = new ToolManager({
126
+ mcpManager: this.mcpManager,
127
+ logger: this.logger,
128
+ }); // Initialize tool registry, pass MCP manager
129
+
66
130
  this.hookManager = new HookManager(
67
131
  this.workdir,
68
132
  undefined,
@@ -77,7 +141,19 @@ export class Agent {
77
141
  logger: this.logger,
78
142
  });
79
143
 
80
- // Initialize AI manager
144
+ // Initialize subagent manager with all dependencies in constructor
145
+ // IMPORTANT: Must be initialized AFTER MessageManager
146
+ this.subagentManager = new SubagentManager({
147
+ workdir: this.workdir,
148
+ parentToolManager: this.toolManager,
149
+ parentMessageManager: this.messageManager,
150
+ logger: this.logger,
151
+ gatewayConfig,
152
+ modelConfig,
153
+ tokenLimit,
154
+ });
155
+
156
+ // Initialize AI manager with resolved configuration
81
157
  this.aiManager = new AIManager({
82
158
  messageManager: this.messageManager,
83
159
  toolManager: this.toolManager,
@@ -87,6 +163,9 @@ export class Agent {
87
163
  callbacks,
88
164
  workdir: this.workdir,
89
165
  systemPrompt: this.systemPrompt,
166
+ gatewayConfig: this.gatewayConfig,
167
+ modelConfig: this.modelConfig,
168
+ tokenLimit: this.tokenLimit,
90
169
  });
91
170
 
92
171
  // Initialize command manager
@@ -154,8 +233,17 @@ export class Agent {
154
233
  return this.backgroundBashManager.killShell(id);
155
234
  }
156
235
 
157
- /** Static async factory method */
236
+ /**
237
+ * Static async factory method for creating Agent instances
238
+ *
239
+ * IMPORTANT: Keep this method's signature exactly the same as the constructor
240
+ * to maintain consistency and avoid confusion for users of the API.
241
+ *
242
+ * @param options - Same AgentOptions interface used by constructor
243
+ * @returns Promise<Agent> - Fully initialized Agent instance
244
+ */
158
245
  static async create(options: AgentOptions): Promise<Agent> {
246
+ // Create Agent instance - configuration resolution and validation now happens in constructor
159
247
  const instance = new Agent(options);
160
248
  await instance.initialize({
161
249
  restoreSessionId: options.restoreSessionId,
@@ -171,6 +259,25 @@ export class Agent {
171
259
  continueLastSession?: boolean;
172
260
  messages?: Message[];
173
261
  }): Promise<void> {
262
+ // Initialize managers first
263
+ try {
264
+ // Initialize SkillManager
265
+ const skillManager = new SkillManager({ logger: this.logger });
266
+ await skillManager.initialize();
267
+
268
+ // Initialize SubagentManager (load and cache configurations)
269
+ await this.subagentManager.initialize();
270
+
271
+ // Initialize built-in tools with dependencies
272
+ this.toolManager.initializeBuiltInTools({
273
+ subagentManager: this.subagentManager,
274
+ skillManager: skillManager,
275
+ });
276
+ } catch (error) {
277
+ this.logger?.error("Failed to initialize managers and tools:", error);
278
+ // Don't throw error to prevent app startup failure
279
+ }
280
+
174
281
  // Initialize MCP servers with auto-connect
175
282
  try {
176
283
  await this.mcpManager.initialize(this.workdir, true);
@@ -224,6 +331,7 @@ export class Agent {
224
331
  this.abortAIMessage();
225
332
  this.abortBashCommand();
226
333
  this.abortSlashCommand();
334
+ this.abortSubagents();
227
335
  }
228
336
 
229
337
  /** Add to input history */
@@ -241,16 +349,29 @@ export class Agent {
241
349
  this.slashCommandManager.abortCurrentCommand();
242
350
  }
243
351
 
352
+ /** Interrupt all subagent execution */
353
+ public abortSubagents(): void {
354
+ this.subagentManager.abortAllInstances();
355
+ }
356
+
357
+ /** Interrupt specific subagent execution */
358
+ public abortSubagent(subagentId: string): boolean {
359
+ return this.subagentManager.abortInstance(subagentId);
360
+ }
361
+
244
362
  /** Destroy managers, clean up resources */
245
363
  public async destroy(): Promise<void> {
246
364
  this.messageManager.saveSession();
247
365
  this.abortAIMessage();
248
366
  this.abortBashCommand();
249
367
  this.abortSlashCommand();
368
+ this.abortSubagents();
250
369
  // Cleanup background bash manager
251
370
  this.backgroundBashManager.cleanup();
252
371
  // Cleanup MCP connections
253
372
  await this.mcpManager.cleanup();
373
+ // Cleanup subagent manager
374
+ this.subagentManager.cleanup();
254
375
  }
255
376
 
256
377
  public async sendMessage(
package/src/index.ts CHANGED
@@ -14,7 +14,6 @@ export * from "./utils/mcpUtils.js";
14
14
  export * from "./utils/messageOperations.js";
15
15
  export * from "./utils/path.js";
16
16
  export * from "./utils/stringUtils.js";
17
- export * from "./utils/markdownParser.js";
18
17
  export * from "./utils/customCommands.js";
19
18
 
20
19
  // Export hooks system
@@ -2,12 +2,11 @@ import { callAgent, compressMessages } from "../services/aiService.js";
2
2
  import { getMessagesToCompress } from "../utils/messageOperations.js";
3
3
  import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
4
4
  import * as memory from "../services/memory.js";
5
- import type { Logger } from "../types.js";
5
+ import type { Logger, GatewayConfig, ModelConfig } from "../types.js";
6
6
  import type { ToolManager } from "./toolManager.js";
7
7
  import type { ToolContext, ToolResult } from "../tools/types.js";
8
8
  import type { MessageManager } from "./messageManager.js";
9
9
  import type { BackgroundBashManager } from "./backgroundBashManager.js";
10
- import { DEFAULT_TOKEN_LIMIT } from "../utils/constants.js";
11
10
  import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
12
11
  import type { HookManager } from "../hooks/index.js";
13
12
  import type { ExtendedHookExecutionContext } from "../hooks/types.js";
@@ -25,6 +24,10 @@ export interface AIManagerOptions {
25
24
  callbacks?: AIManagerCallbacks;
26
25
  workdir: string;
27
26
  systemPrompt?: string;
27
+ // Resolved configuration
28
+ gatewayConfig: GatewayConfig;
29
+ modelConfig: ModelConfig;
30
+ tokenLimit: number;
28
31
  }
29
32
 
30
33
  export class AIManager {
@@ -39,6 +42,11 @@ export class AIManager {
39
42
  private workdir: string;
40
43
  private systemPrompt?: string;
41
44
 
45
+ // Configuration properties
46
+ private gatewayConfig: GatewayConfig;
47
+ private modelConfig: ModelConfig;
48
+ private tokenLimit: number;
49
+
42
50
  constructor(options: AIManagerOptions) {
43
51
  this.messageManager = options.messageManager;
44
52
  this.toolManager = options.toolManager;
@@ -48,6 +56,11 @@ export class AIManager {
48
56
  this.workdir = options.workdir;
49
57
  this.systemPrompt = options.systemPrompt;
50
58
  this.callbacks = options.callbacks ?? {};
59
+
60
+ // Store resolved configuration
61
+ this.gatewayConfig = options.gatewayConfig;
62
+ this.modelConfig = options.modelConfig;
63
+ this.tokenLimit = options.tokenLimit;
51
64
  }
52
65
 
53
66
  private isCompressing: boolean = false;
@@ -125,15 +138,10 @@ export class AIManager {
125
138
  // Update token statistics - display latest token usage
126
139
  this.messageManager.setlatestTotalTokens(usage.total_tokens);
127
140
 
128
- // Check if token limit exceeded
129
- const tokenLimit = parseInt(
130
- process.env.TOKEN_LIMIT || `${DEFAULT_TOKEN_LIMIT}`,
131
- 10,
132
- );
133
-
134
- if (usage.total_tokens > tokenLimit) {
141
+ // Check if token limit exceeded - use injected configuration
142
+ if (usage.total_tokens > this.tokenLimit) {
135
143
  this.logger?.info(
136
- `Token usage exceeded ${tokenLimit}, compressing messages...`,
144
+ `Token usage exceeded ${this.tokenLimit}, compressing messages...`,
137
145
  );
138
146
 
139
147
  // Check if messages need compression
@@ -149,6 +157,8 @@ export class AIManager {
149
157
  this.setIsCompressing(true);
150
158
  try {
151
159
  const compressedContent = await compressMessages({
160
+ gatewayConfig: this.gatewayConfig,
161
+ modelConfig: this.modelConfig,
152
162
  messages: recentChatMessages,
153
163
  abortSignal: abortController.signal,
154
164
  });
@@ -221,6 +231,8 @@ export class AIManager {
221
231
 
222
232
  // Call AI service (non-streaming)
223
233
  const result = await callAgent({
234
+ gatewayConfig: this.gatewayConfig,
235
+ modelConfig: this.modelConfig,
224
236
  messages: recentMessages,
225
237
  sessionId: this.messageManager.getSessionId(),
226
238
  abortSignal: abortController.signal,
@@ -10,6 +10,10 @@ import {
10
10
  addCommandOutputMessage,
11
11
  updateCommandOutputInMessage,
12
12
  completeCommandInMessage,
13
+ addSubagentBlockToMessage,
14
+ updateSubagentBlockInMessage,
15
+ type AddSubagentBlockParams,
16
+ type UpdateSubagentBlockParams,
13
17
  type AgentToolBlockUpdateParams,
14
18
  } from "../utils/messageOperations.js";
15
19
  import type { Logger, Message } from "../types.js";
@@ -48,10 +52,19 @@ export interface MessageManagerCallbacks {
48
52
  type: "project" | "user",
49
53
  storagePath: string,
50
54
  ) => void;
55
+ // Custom command callback
56
+ onCustomCommandAdded?: (
57
+ commandName: string,
58
+ content: string,
59
+ originalInput?: string,
60
+ ) => void;
51
61
  // Bash command callback
52
62
  onAddCommandOutputMessage?: (command: string) => void;
53
63
  onUpdateCommandOutputMessage?: (command: string, output: string) => void;
54
64
  onCompleteCommandMessage?: (command: string, exitCode: number) => void;
65
+ // Subagent callbacks
66
+ onSubAgentBlockAdded?: (subagentId: string) => void;
67
+ onSubAgentBlockUpdated?: (subagentId: string, messages: Message[]) => void;
55
68
  }
56
69
 
57
70
  export interface MessageManagerOptions {
@@ -271,7 +284,7 @@ export class MessageManager {
271
284
  },
272
285
  });
273
286
  this.setMessages(newMessages);
274
- this.callbacks.onUserMessageAdded?.(content);
287
+ this.callbacks.onCustomCommandAdded?.(commandName, content, originalInput);
275
288
  }
276
289
 
277
290
  public addAssistantMessage(
@@ -412,4 +425,45 @@ export class MessageManager {
412
425
  this.setMessages(updatedMessages);
413
426
  this.callbacks.onCompleteCommandMessage?.(command, exitCode);
414
427
  }
428
+
429
+ // Subagent block methods
430
+ public addSubagentBlock(
431
+ subagentId: string,
432
+ subagentName: string,
433
+ status: "active" | "completed" | "error" = "active",
434
+ subagentMessages: Message[] = [],
435
+ ): void {
436
+ const params: AddSubagentBlockParams = {
437
+ messages: this.messages,
438
+ subagentId,
439
+ subagentName,
440
+ status,
441
+ subagentMessages,
442
+ };
443
+ const updatedMessages = addSubagentBlockToMessage(params);
444
+ this.setMessages(updatedMessages);
445
+ this.callbacks.onSubAgentBlockAdded?.(params.subagentId);
446
+ }
447
+
448
+ public updateSubagentBlock(
449
+ subagentId: string,
450
+ updates: Partial<{
451
+ status: "active" | "completed" | "error" | "aborted";
452
+ messages: Message[];
453
+ }>,
454
+ ): void {
455
+ const updatedMessages = updateSubagentBlockInMessage(
456
+ this.messages,
457
+ subagentId,
458
+ updates,
459
+ );
460
+ this.setMessages(updatedMessages);
461
+ const params: UpdateSubagentBlockParams = {
462
+ messages: this.messages,
463
+ subagentId,
464
+ status: updates.status || "active",
465
+ subagentMessages: updates.messages || [],
466
+ };
467
+ this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.messages);
468
+ }
415
469
  }