wave-agent-sdk 0.0.1 → 0.0.2

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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Configuration validator utilities for Agent Constructor Configuration
3
+ * Validates configuration values for correctness and security
4
+ */
5
+ import { GatewayConfig } from "../types.js";
6
+ export declare class ConfigValidator {
7
+ /**
8
+ * Validates gateway configuration
9
+ * @param config - Configuration to validate
10
+ * @throws ConfigurationError with descriptive message if invalid
11
+ */
12
+ static validateGatewayConfig(config: GatewayConfig): void;
13
+ /**
14
+ * Validates token limit value
15
+ * @param tokenLimit - Token limit to validate
16
+ * @throws ConfigurationError if invalid
17
+ */
18
+ static validateTokenLimit(tokenLimit: number): void;
19
+ /**
20
+ * Validates model configuration (basic validation)
21
+ * @param agentModel - Agent model string
22
+ * @param fastModel - Fast model string
23
+ * @throws ConfigurationError if invalid
24
+ */
25
+ static validateModelConfig(agentModel: string, fastModel: string): void;
26
+ }
27
+ /**
28
+ * Static configuration validator instance
29
+ * Implements ConfigurationValidator interface from types.ts
30
+ */
31
+ export declare const configValidator: {
32
+ validateGatewayConfig: typeof ConfigValidator.validateGatewayConfig;
33
+ validateTokenLimit: typeof ConfigValidator.validateTokenLimit;
34
+ validateModelConfig: typeof ConfigValidator.validateModelConfig;
35
+ };
36
+ //# sourceMappingURL=configValidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configValidator.d.ts","sourceRoot":"","sources":["../../src/utils/configValidator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAqC,MAAM,aAAa,CAAC;AAE/E,qBAAa,eAAe;IAC1B;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IA+CzD;;;;OAIG;IACH,MAAM,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IA0BnD;;;;;OAKG;IACH,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAyBxE;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;CAI3B,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Configuration validator utilities for Agent Constructor Configuration
3
+ * Validates configuration values for correctness and security
4
+ */
5
+ import { ConfigurationError, CONFIG_ERRORS } from "../types.js";
6
+ export class ConfigValidator {
7
+ /**
8
+ * Validates gateway configuration
9
+ * @param config - Configuration to validate
10
+ * @throws ConfigurationError with descriptive message if invalid
11
+ */
12
+ static validateGatewayConfig(config) {
13
+ // Validate API key
14
+ if (!config.apiKey || typeof config.apiKey !== "string") {
15
+ throw new ConfigurationError(CONFIG_ERRORS.EMPTY_API_KEY, "apiKey", config.apiKey);
16
+ }
17
+ if (config.apiKey.trim() === "") {
18
+ throw new ConfigurationError(CONFIG_ERRORS.EMPTY_API_KEY, "apiKey", config.apiKey);
19
+ }
20
+ // Validate base URL
21
+ if (!config.baseURL || typeof config.baseURL !== "string") {
22
+ throw new ConfigurationError(CONFIG_ERRORS.EMPTY_BASE_URL, "baseURL", config.baseURL);
23
+ }
24
+ if (config.baseURL.trim() === "") {
25
+ throw new ConfigurationError(CONFIG_ERRORS.EMPTY_BASE_URL, "baseURL", config.baseURL);
26
+ }
27
+ // Basic URL format validation
28
+ try {
29
+ new URL(config.baseURL);
30
+ }
31
+ catch {
32
+ throw new ConfigurationError(`Base URL must be a valid URL format. Received: ${config.baseURL}`, "baseURL", config.baseURL);
33
+ }
34
+ }
35
+ /**
36
+ * Validates token limit value
37
+ * @param tokenLimit - Token limit to validate
38
+ * @throws ConfigurationError if invalid
39
+ */
40
+ static validateTokenLimit(tokenLimit) {
41
+ if (typeof tokenLimit !== "number") {
42
+ throw new ConfigurationError(CONFIG_ERRORS.INVALID_TOKEN_LIMIT, "tokenLimit", tokenLimit);
43
+ }
44
+ if (!Number.isInteger(tokenLimit)) {
45
+ throw new ConfigurationError(CONFIG_ERRORS.INVALID_TOKEN_LIMIT, "tokenLimit", tokenLimit);
46
+ }
47
+ if (tokenLimit <= 0) {
48
+ throw new ConfigurationError(CONFIG_ERRORS.INVALID_TOKEN_LIMIT, "tokenLimit", tokenLimit);
49
+ }
50
+ }
51
+ /**
52
+ * Validates model configuration (basic validation)
53
+ * @param agentModel - Agent model string
54
+ * @param fastModel - Fast model string
55
+ * @throws ConfigurationError if invalid
56
+ */
57
+ static validateModelConfig(agentModel, fastModel) {
58
+ if (!agentModel ||
59
+ typeof agentModel !== "string" ||
60
+ agentModel.trim() === "") {
61
+ throw new ConfigurationError("Agent model must be a non-empty string.", "agentModel", agentModel);
62
+ }
63
+ if (!fastModel ||
64
+ typeof fastModel !== "string" ||
65
+ fastModel.trim() === "") {
66
+ throw new ConfigurationError("Fast model must be a non-empty string.", "fastModel", fastModel);
67
+ }
68
+ }
69
+ }
70
+ /**
71
+ * Static configuration validator instance
72
+ * Implements ConfigurationValidator interface from types.ts
73
+ */
74
+ export const configValidator = {
75
+ validateGatewayConfig: ConfigValidator.validateGatewayConfig,
76
+ validateTokenLimit: ConfigValidator.validateTokenLimit,
77
+ validateModelConfig: ConfigValidator.validateModelConfig,
78
+ };
@@ -22,6 +22,16 @@ export declare const USER_MEMORY_FILE: string;
22
22
  * AI related constants
23
23
  */
24
24
  export declare const DEFAULT_TOKEN_LIMIT = 64000;
25
+ /**
26
+ * @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
27
+ * They are maintained for backward compatibility with existing code that might still reference them,
28
+ * but the actual AI services now use configuration injection.
29
+ */
25
30
  export declare const FAST_MODEL_ID: string;
31
+ /**
32
+ * @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
33
+ * They are maintained for backward compatibility with existing code that might still reference them,
34
+ * but the actual AI services now use configuration injection.
35
+ */
26
36
  export declare const AGENT_MODEL_ID: string;
27
37
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAmC,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAA0C,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAA8C,CAAC;AAE5E;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,eAAO,MAAM,aAAa,QAAoD,CAAC;AAE/E,eAAO,MAAM,cAAc,QAC2B,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAmC,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAEhF;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAA0C,CAAC;AAE3E;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAA8C,CAAC;AAE5E;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAAoD,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAC2B,CAAC"}
@@ -24,5 +24,15 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "user-memory.md");
24
24
  * AI related constants
25
25
  */
26
26
  export const DEFAULT_TOKEN_LIMIT = 64000; // Default token limit
27
+ /**
28
+ * @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
29
+ * They are maintained for backward compatibility with existing code that might still reference them,
30
+ * but the actual AI services now use configuration injection.
31
+ */
27
32
  export const FAST_MODEL_ID = process.env.AIGW_FAST_MODEL || "gemini-2.5-flash";
33
+ /**
34
+ * @deprecated These constants are now legacy. Use ModelConfig through Agent constructor instead.
35
+ * They are maintained for backward compatibility with existing code that might still reference them,
36
+ * but the actual AI services now use configuration injection.
37
+ */
28
38
  export const AGENT_MODEL_ID = process.env.AIGW_MODEL || "claude-sonnet-4-20250514";
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.2",
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
@@ -13,10 +13,32 @@ import {
13
13
  } from "./managers/backgroundBashManager.js";
14
14
  import { SlashCommandManager } from "./managers/slashCommandManager.js";
15
15
  import type { SlashCommand, CustomSlashCommand } from "./types.js";
16
- import type { Message, Logger, McpServerStatus } from "./types.js";
16
+ import type {
17
+ Message,
18
+ Logger,
19
+ McpServerStatus,
20
+ GatewayConfig,
21
+ ModelConfig,
22
+ } from "./types.js";
17
23
  import { HookManager } from "./hooks/index.js";
18
-
24
+ import { configResolver } from "./utils/configResolver.js";
25
+ import { configValidator } from "./utils/configValidator.js";
26
+
27
+ /**
28
+ * Configuration options for Agent instances
29
+ *
30
+ * IMPORTANT: This interface is used by both Agent constructor and Agent.create()
31
+ * Any changes to this interface must be compatible with both methods.
32
+ */
19
33
  export interface AgentOptions {
34
+ // Optional configuration with environment fallbacks
35
+ apiKey?: string;
36
+ baseURL?: string;
37
+ agentModel?: string;
38
+ fastModel?: string;
39
+ tokenLimit?: number;
40
+
41
+ // Existing options (preserved)
20
42
  callbacks?: AgentCallbacks;
21
43
  restoreSessionId?: string;
22
44
  continueLastSession?: boolean;
@@ -49,14 +71,51 @@ export class Agent {
49
71
  private workdir: string; // Working directory
50
72
  private systemPrompt?: string; // Custom system prompt
51
73
 
52
- // Private constructor to prevent direct instantiation
53
- private constructor(options: AgentOptions) {
74
+ // Configuration properties
75
+ private gatewayConfig: GatewayConfig;
76
+ private modelConfig: ModelConfig;
77
+ private tokenLimit: number;
78
+
79
+ /**
80
+ * Agent constructor - handles configuration resolution and validation
81
+ *
82
+ * IMPORTANT: Keep this constructor's signature exactly the same as Agent.create()
83
+ * to maintain API consistency. Both methods should accept the same AgentOptions.
84
+ *
85
+ * @param options - Configuration options for the Agent instance
86
+ */
87
+ constructor(options: AgentOptions) {
54
88
  const { callbacks = {}, logger, workdir, systemPrompt } = options;
55
89
 
90
+ // Resolve configuration from constructor args and environment variables
91
+ const gatewayConfig = configResolver.resolveGatewayConfig(
92
+ options.apiKey,
93
+ options.baseURL,
94
+ );
95
+ const modelConfig = configResolver.resolveModelConfig(
96
+ options.agentModel,
97
+ options.fastModel,
98
+ );
99
+ const tokenLimit = configResolver.resolveTokenLimit(options.tokenLimit);
100
+
101
+ // Validate resolved configuration
102
+ configValidator.validateGatewayConfig(gatewayConfig);
103
+ configValidator.validateTokenLimit(tokenLimit);
104
+ configValidator.validateModelConfig(
105
+ modelConfig.agentModel,
106
+ modelConfig.fastModel,
107
+ );
108
+
56
109
  this.callbacks = callbacks;
57
110
  this.logger = logger; // Save the passed logger
58
111
  this.workdir = workdir || process.cwd(); // Set working directory, default to current working directory
59
112
  this.systemPrompt = systemPrompt; // Save custom system prompt
113
+
114
+ // Store resolved configuration
115
+ this.gatewayConfig = gatewayConfig;
116
+ this.modelConfig = modelConfig;
117
+ this.tokenLimit = tokenLimit;
118
+
60
119
  this.backgroundBashManager = new BackgroundBashManager({
61
120
  callbacks,
62
121
  workdir: this.workdir,
@@ -77,7 +136,7 @@ export class Agent {
77
136
  logger: this.logger,
78
137
  });
79
138
 
80
- // Initialize AI manager
139
+ // Initialize AI manager with resolved configuration
81
140
  this.aiManager = new AIManager({
82
141
  messageManager: this.messageManager,
83
142
  toolManager: this.toolManager,
@@ -87,6 +146,9 @@ export class Agent {
87
146
  callbacks,
88
147
  workdir: this.workdir,
89
148
  systemPrompt: this.systemPrompt,
149
+ gatewayConfig: this.gatewayConfig,
150
+ modelConfig: this.modelConfig,
151
+ tokenLimit: this.tokenLimit,
90
152
  });
91
153
 
92
154
  // Initialize command manager
@@ -154,8 +216,17 @@ export class Agent {
154
216
  return this.backgroundBashManager.killShell(id);
155
217
  }
156
218
 
157
- /** Static async factory method */
219
+ /**
220
+ * Static async factory method for creating Agent instances
221
+ *
222
+ * IMPORTANT: Keep this method's signature exactly the same as the constructor
223
+ * to maintain consistency and avoid confusion for users of the API.
224
+ *
225
+ * @param options - Same AgentOptions interface used by constructor
226
+ * @returns Promise<Agent> - Fully initialized Agent instance
227
+ */
158
228
  static async create(options: AgentOptions): Promise<Agent> {
229
+ // Create Agent instance - configuration resolution and validation now happens in constructor
159
230
  const instance = new Agent(options);
160
231
  await instance.initialize({
161
232
  restoreSessionId: options.restoreSessionId,
@@ -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,
@@ -432,11 +444,14 @@ export class AIManager {
432
444
  if (recursionDepth === 0) {
433
445
  this.setIsLoading(false);
434
446
 
435
- // Save session before executing Stop hooks
436
- await this.messageManager.saveSession();
447
+ // Save session and execute Stop hooks in parallel but maintain order
448
+ (async () => {
449
+ // Save session before executing Stop hooks
450
+ await this.messageManager.saveSession();
437
451
 
438
- // Execute Stop hooks when AI response cycle completes
439
- await this.executeStopHooks();
452
+ // Execute Stop hooks when AI response cycle completes
453
+ await this.executeStopHooks();
454
+ })();
440
455
  }
441
456
  }
442
457
  }
@@ -5,12 +5,15 @@ import {
5
5
  ChatCompletionMessageParam,
6
6
  ChatCompletionFunctionTool,
7
7
  } from "openai/resources.js";
8
- import { FAST_MODEL_ID, AGENT_MODEL_ID } from "@/utils/constants.js";
8
+ import type { GatewayConfig, ModelConfig } from "../types.js";
9
9
 
10
10
  /**
11
- * Model configuration type, based on OpenAI parameters but excluding messages
11
+ * OpenAI model configuration type, based on OpenAI parameters but excluding messages
12
12
  */
13
- type ModelConfig = Omit<ChatCompletionCreateParamsNonStreaming, "messages">;
13
+ type OpenAIModelConfig = Omit<
14
+ ChatCompletionCreateParamsNonStreaming,
15
+ "messages"
16
+ >;
14
17
 
15
18
  /**
16
19
  * Get specific configuration parameters based on model name
@@ -20,9 +23,9 @@ type ModelConfig = Omit<ChatCompletionCreateParamsNonStreaming, "messages">;
20
23
  */
21
24
  function getModelConfig(
22
25
  modelName: string,
23
- baseConfig: Partial<ModelConfig> = {},
24
- ): ModelConfig {
25
- const config: ModelConfig = {
26
+ baseConfig: Partial<OpenAIModelConfig> = {},
27
+ ): OpenAIModelConfig {
28
+ const config: OpenAIModelConfig = {
26
29
  model: modelName,
27
30
  stream: false,
28
31
  ...baseConfig,
@@ -37,13 +40,12 @@ function getModelConfig(
37
40
  return config;
38
41
  }
39
42
 
40
- // Initialize OpenAI client with environment variables
41
- const openai = new OpenAI({
42
- apiKey: process.env.AIGW_TOKEN,
43
- baseURL: process.env.AIGW_URL,
44
- });
45
-
46
43
  export interface CallAgentOptions {
44
+ // Resolved configuration
45
+ gatewayConfig: GatewayConfig;
46
+ modelConfig: ModelConfig;
47
+
48
+ // Existing parameters (preserved)
47
49
  messages: ChatCompletionMessageParam[];
48
50
  sessionId?: string;
49
51
  abortSignal?: AbortSignal;
@@ -67,10 +69,25 @@ export interface CallAgentResult {
67
69
  export async function callAgent(
68
70
  options: CallAgentOptions,
69
71
  ): Promise<CallAgentResult> {
70
- const { messages, abortSignal, memory, workdir, tools, model, systemPrompt } =
71
- options;
72
+ const {
73
+ gatewayConfig,
74
+ modelConfig,
75
+ messages,
76
+ abortSignal,
77
+ memory,
78
+ workdir,
79
+ tools,
80
+ model,
81
+ systemPrompt,
82
+ } = options;
72
83
 
73
84
  try {
85
+ // Create OpenAI client with injected configuration
86
+ const openai = new OpenAI({
87
+ apiKey: gatewayConfig.apiKey,
88
+ baseURL: gatewayConfig.baseURL,
89
+ });
90
+
74
91
  // Build system prompt content
75
92
  let systemContent: string;
76
93
 
@@ -101,15 +118,15 @@ ${workdir}
101
118
  const openaiMessages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] =
102
119
  [systemMessage, ...messages];
103
120
 
104
- // Get model configuration
105
- const modelConfig = getModelConfig(model || AGENT_MODEL_ID, {
121
+ // Get model configuration - use injected modelConfig with optional override
122
+ const openaiModelConfig = getModelConfig(model || modelConfig.agentModel, {
106
123
  temperature: 0,
107
124
  max_completion_tokens: 32768,
108
125
  });
109
126
 
110
127
  // Prepare API call parameters
111
128
  const createParams: ChatCompletionCreateParamsNonStreaming = {
112
- ...modelConfig,
129
+ ...openaiModelConfig,
113
130
  messages: openaiMessages,
114
131
  };
115
132
 
@@ -160,6 +177,11 @@ ${workdir}
160
177
  }
161
178
 
162
179
  export interface CompressMessagesOptions {
180
+ // Resolved configuration
181
+ gatewayConfig: GatewayConfig;
182
+ modelConfig: ModelConfig;
183
+
184
+ // Existing parameters
163
185
  messages: ChatCompletionMessageParam[];
164
186
  abortSignal?: AbortSignal;
165
187
  }
@@ -167,10 +189,16 @@ export interface CompressMessagesOptions {
167
189
  export async function compressMessages(
168
190
  options: CompressMessagesOptions,
169
191
  ): Promise<string> {
170
- const { messages, abortSignal } = options;
192
+ const { gatewayConfig, modelConfig, messages, abortSignal } = options;
193
+
194
+ // Create OpenAI client with injected configuration
195
+ const openai = new OpenAI({
196
+ apiKey: gatewayConfig.apiKey,
197
+ baseURL: gatewayConfig.baseURL,
198
+ });
171
199
 
172
- // Get model configuration
173
- const modelConfig = getModelConfig(FAST_MODEL_ID, {
200
+ // Get model configuration - use injected fast model
201
+ const openaiModelConfig = getModelConfig(modelConfig.fastModel, {
174
202
  temperature: 0.1,
175
203
  max_tokens: 1500,
176
204
  });
@@ -178,7 +206,7 @@ export async function compressMessages(
178
206
  try {
179
207
  const response = await openai.chat.completions.create(
180
208
  {
181
- ...modelConfig,
209
+ ...openaiModelConfig,
182
210
  messages: [
183
211
  {
184
212
  role: "system",
@@ -76,6 +76,11 @@ export async function saveSession(
76
76
  return;
77
77
  }
78
78
 
79
+ // Do not save if there are no messages
80
+ if (messages.length === 0) {
81
+ return;
82
+ }
83
+
79
84
  await ensureSessionDir();
80
85
 
81
86
  // Filter out diff blocks before saving
package/src/types.ts CHANGED
@@ -258,3 +258,78 @@ export const SKILL_DEFAULTS = {
258
258
  SCAN_TIMEOUT: 5000,
259
259
  LOAD_TIMEOUT: 2000,
260
260
  } as const;
261
+
262
+ // Configuration types for Agent Constructor Configuration feature
263
+ export interface GatewayConfig {
264
+ apiKey: string;
265
+ baseURL: string;
266
+ }
267
+
268
+ export interface ModelConfig {
269
+ agentModel: string;
270
+ fastModel: string;
271
+ }
272
+
273
+ export interface ConfigurationResolver {
274
+ /**
275
+ * Resolves gateway configuration from constructor args and environment
276
+ * @param apiKey - API key from constructor (optional)
277
+ * @param baseURL - Base URL from constructor (optional)
278
+ * @returns Resolved gateway configuration
279
+ * @throws Error if required configuration is missing after fallbacks
280
+ */
281
+ resolveGatewayConfig(apiKey?: string, baseURL?: string): GatewayConfig;
282
+
283
+ /**
284
+ * Resolves model configuration with fallbacks
285
+ * @param agentModel - Agent model from constructor (optional)
286
+ * @param fastModel - Fast model from constructor (optional)
287
+ * @returns Resolved model configuration with defaults
288
+ */
289
+ resolveModelConfig(agentModel?: string, fastModel?: string): ModelConfig;
290
+
291
+ /**
292
+ * Resolves token limit with fallbacks
293
+ * @param constructorLimit - Token limit from constructor (optional)
294
+ * @returns Resolved token limit
295
+ */
296
+ resolveTokenLimit(constructorLimit?: number): number;
297
+ }
298
+
299
+ export interface ConfigurationValidator {
300
+ /**
301
+ * Validates gateway configuration
302
+ * @param config - Configuration to validate
303
+ * @throws Error with descriptive message if invalid
304
+ */
305
+ validateGatewayConfig(config: GatewayConfig): void;
306
+
307
+ /**
308
+ * Validates token limit value
309
+ * @param tokenLimit - Token limit to validate
310
+ * @throws Error if invalid
311
+ */
312
+ validateTokenLimit(tokenLimit: number): void;
313
+ }
314
+
315
+ export class ConfigurationError extends Error {
316
+ constructor(
317
+ message: string,
318
+ public readonly field: string,
319
+ public readonly provided?: unknown,
320
+ ) {
321
+ super(message);
322
+ this.name = "ConfigurationError";
323
+ }
324
+ }
325
+
326
+ // Standard error messages
327
+ export const CONFIG_ERRORS = {
328
+ MISSING_API_KEY:
329
+ "Gateway configuration requires apiKey. Provide via constructor or AIGW_TOKEN environment variable.",
330
+ MISSING_BASE_URL:
331
+ "Gateway configuration requires baseURL. Provide via constructor or AIGW_URL environment variable.",
332
+ INVALID_TOKEN_LIMIT: "Token limit must be a positive integer.",
333
+ EMPTY_API_KEY: "API key cannot be empty string.",
334
+ EMPTY_BASE_URL: "Base URL cannot be empty string.",
335
+ } as const;