gencode-ai 0.1.3 → 0.3.0
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.
- package/README.md +2 -1
- package/dist/agent/agent.d.ts +44 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +130 -11
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/types.d.ts +11 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/checkpointing/checkpoint-manager.d.ts +87 -0
- package/dist/checkpointing/checkpoint-manager.d.ts.map +1 -0
- package/dist/checkpointing/checkpoint-manager.js +281 -0
- package/dist/checkpointing/checkpoint-manager.js.map +1 -0
- package/dist/checkpointing/index.d.ts +29 -0
- package/dist/checkpointing/index.d.ts.map +1 -0
- package/dist/checkpointing/index.js +29 -0
- package/dist/checkpointing/index.js.map +1 -0
- package/dist/checkpointing/types.d.ts +98 -0
- package/dist/checkpointing/types.d.ts.map +1 -0
- package/dist/checkpointing/types.js +7 -0
- package/dist/checkpointing/types.js.map +1 -0
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +171 -14
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
- package/dist/cli/components/CommandSuggestions.js +5 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -1
- package/dist/cli/components/Messages.d.ts +7 -1
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +12 -3
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/ModeIndicator.d.ts +42 -0
- package/dist/cli/components/ModeIndicator.d.ts.map +1 -0
- package/dist/cli/components/ModeIndicator.js +52 -0
- package/dist/cli/components/ModeIndicator.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +4 -3
- package/dist/cli/components/ModelSelector.d.ts.map +1 -1
- package/dist/cli/components/ModelSelector.js +54 -37
- package/dist/cli/components/ModelSelector.js.map +1 -1
- package/dist/cli/components/PlanApproval.d.ts +36 -0
- package/dist/cli/components/PlanApproval.d.ts.map +1 -0
- package/dist/cli/components/PlanApproval.js +154 -0
- package/dist/cli/components/PlanApproval.js.map +1 -0
- package/dist/cli/components/ProviderManager.d.ts +2 -2
- package/dist/cli/components/ProviderManager.d.ts.map +1 -1
- package/dist/cli/components/ProviderManager.js +137 -156
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/components/theme.d.ts +2 -0
- package/dist/cli/components/theme.d.ts.map +1 -1
- package/dist/cli/components/theme.js +3 -0
- package/dist/cli/components/theme.js.map +1 -1
- package/dist/cli/index.js +30 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +5 -5
- package/dist/config/levels.d.ts.map +1 -1
- package/dist/config/levels.js +20 -20
- package/dist/config/levels.js.map +1 -1
- package/dist/config/merger.js +1 -1
- package/dist/config/merger.js.map +1 -1
- package/dist/config/providers-config.d.ts +8 -5
- package/dist/config/providers-config.d.ts.map +1 -1
- package/dist/config/providers-config.js +19 -22
- package/dist/config/providers-config.js.map +1 -1
- package/dist/config/test-utils.d.ts +2 -2
- package/dist/config/test-utils.d.ts.map +1 -1
- package/dist/config/test-utils.js +4 -4
- package/dist/config/test-utils.js.map +1 -1
- package/dist/config/types.d.ts +23 -17
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +14 -14
- package/dist/config/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +25 -12
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +241 -112
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/test-utils.d.ts +1 -1
- package/dist/memory/test-utils.d.ts.map +1 -1
- package/dist/memory/test-utils.js +3 -3
- package/dist/memory/test-utils.js.map +1 -1
- package/dist/memory/types.d.ts +20 -10
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +13 -13
- package/dist/memory/types.js.map +1 -1
- package/dist/migration/migrate.d.ts +24 -0
- package/dist/migration/migrate.d.ts.map +1 -0
- package/dist/migration/migrate.js +164 -0
- package/dist/migration/migrate.js.map +1 -0
- package/dist/permissions/persistence.d.ts +2 -2
- package/dist/permissions/persistence.js +4 -4
- package/dist/permissions/persistence.js.map +1 -1
- package/dist/planning/index.d.ts +13 -0
- package/dist/planning/index.d.ts.map +1 -0
- package/dist/planning/index.js +15 -0
- package/dist/planning/index.js.map +1 -0
- package/dist/planning/plan-file.d.ts +59 -0
- package/dist/planning/plan-file.d.ts.map +1 -0
- package/dist/planning/plan-file.js +278 -0
- package/dist/planning/plan-file.js.map +1 -0
- package/dist/planning/state.d.ts +127 -0
- package/dist/planning/state.d.ts.map +1 -0
- package/dist/planning/state.js +261 -0
- package/dist/planning/state.js.map +1 -0
- package/dist/planning/tools/enter-plan-mode.d.ts +25 -0
- package/dist/planning/tools/enter-plan-mode.d.ts.map +1 -0
- package/dist/planning/tools/enter-plan-mode.js +98 -0
- package/dist/planning/tools/enter-plan-mode.js.map +1 -0
- package/dist/planning/tools/exit-plan-mode.d.ts +24 -0
- package/dist/planning/tools/exit-plan-mode.d.ts.map +1 -0
- package/dist/planning/tools/exit-plan-mode.js +149 -0
- package/dist/planning/tools/exit-plan-mode.js.map +1 -0
- package/dist/planning/types.d.ts +100 -0
- package/dist/planning/types.d.ts.map +1 -0
- package/dist/planning/types.js +28 -0
- package/dist/planning/types.js.map +1 -0
- package/dist/pricing/calculator.d.ts +21 -0
- package/dist/pricing/calculator.d.ts.map +1 -0
- package/dist/pricing/calculator.js +59 -0
- package/dist/pricing/calculator.js.map +1 -0
- package/dist/pricing/index.d.ts +7 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +7 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/pricing/models.d.ts +20 -0
- package/dist/pricing/models.d.ts.map +1 -0
- package/dist/pricing/models.js +322 -0
- package/dist/pricing/models.js.map +1 -0
- package/dist/pricing/types.d.ts +30 -0
- package/dist/pricing/types.d.ts.map +1 -0
- package/dist/pricing/types.js +5 -0
- package/dist/pricing/types.js.map +1 -0
- package/dist/prompts/index.d.ts +5 -4
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +11 -8
- package/dist/prompts/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +24 -10
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +28 -14
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +20 -10
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +48 -24
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +19 -8
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/registry.d.ts +48 -34
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +72 -88
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/store.d.ts +43 -17
- package/dist/providers/store.d.ts.map +1 -1
- package/dist/providers/store.js +112 -19
- package/dist/providers/store.js.map +1 -1
- package/dist/providers/types.d.ts +25 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/vertex-ai.d.ts +15 -7
- package/dist/providers/vertex-ai.d.ts.map +1 -1
- package/dist/providers/vertex-ai.js +63 -23
- package/dist/providers/vertex-ai.js.map +1 -1
- package/dist/session/manager.d.ts +4 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +8 -0
- package/dist/session/manager.js.map +1 -1
- package/dist/session/types.js +1 -1
- package/dist/session/types.js.map +1 -1
- package/dist/tools/index.d.ts +7 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/registry.d.ts +13 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +79 -2
- package/dist/tools/registry.js.map +1 -1
- package/docs/config-system-comparison.md +50 -50
- package/docs/cost-tracking-comparison.md +904 -0
- package/docs/memory-system.md +124 -31
- package/docs/operating-modes.md +96 -0
- package/docs/permissions.md +2 -2
- package/docs/proposals/0006-memory-system.md +4 -4
- package/docs/proposals/0008-checkpointing.md +109 -2
- package/docs/proposals/0011-custom-commands.md +2 -1
- package/docs/proposals/0021-skills-system.md +2 -1
- package/docs/proposals/0023-permission-enhancements.md +2 -2
- package/docs/proposals/0025-cost-tracking.md +60 -2
- package/docs/proposals/0033-enterprise-deployment.md +1 -1
- package/docs/proposals/0041-configuration-system.md +17 -19
- package/docs/proposals/0042-prompt-optimization.md +17 -9
- package/docs/proposals/README.md +6 -6
- package/docs/providers.md +94 -9
- package/examples/test-checkpointing.ts +121 -0
- package/examples/test-cost-tracking.ts +77 -0
- package/examples/test-interrupt-cleanup.ts +94 -0
- package/package.json +3 -2
- package/scripts/migrate.ts +449 -0
- package/src/agent/agent.ts +161 -12
- package/src/agent/types.ts +11 -1
- package/src/checkpointing/checkpoint-manager.ts +327 -0
- package/src/checkpointing/index.ts +45 -0
- package/src/checkpointing/types.ts +104 -0
- package/src/cli/components/App.tsx +221 -13
- package/src/cli/components/CommandSuggestions.tsx +5 -0
- package/src/cli/components/Messages.tsx +24 -5
- package/src/cli/components/ModeIndicator.tsx +174 -0
- package/src/cli/components/ModelSelector.tsx +62 -43
- package/src/cli/components/PlanApproval.tsx +327 -0
- package/src/cli/components/ProviderManager.tsx +278 -323
- package/src/cli/components/theme.ts +3 -0
- package/src/cli/index.tsx +36 -17
- package/src/config/index.ts +5 -3
- package/src/config/levels.test.ts +22 -22
- package/src/config/levels.ts +22 -22
- package/src/config/loader.test.ts +14 -14
- package/src/config/manager.test.ts +19 -19
- package/src/config/merger.test.ts +23 -23
- package/src/config/merger.ts +1 -1
- package/src/config/providers-config.ts +23 -21
- package/src/config/test-utils.ts +6 -6
- package/src/config/types.ts +30 -20
- package/src/index.ts +15 -0
- package/src/memory/memory-manager.test.ts +242 -24
- package/src/memory/memory-manager.ts +270 -141
- package/src/memory/test-utils.ts +4 -4
- package/src/memory/types.ts +28 -17
- package/src/permissions/persistence.ts +4 -4
- package/src/planning/index.ts +53 -0
- package/src/planning/plan-file.ts +326 -0
- package/src/planning/state.ts +305 -0
- package/src/planning/tools/enter-plan-mode.ts +111 -0
- package/src/planning/tools/exit-plan-mode.ts +170 -0
- package/src/planning/types.ts +150 -0
- package/src/pricing/calculator.ts +71 -0
- package/src/pricing/index.ts +7 -0
- package/src/pricing/models.ts +334 -0
- package/src/pricing/types.ts +32 -0
- package/src/prompts/index.ts +13 -9
- package/src/providers/anthropic.ts +30 -10
- package/src/providers/gemini.ts +34 -14
- package/src/providers/index.ts +76 -33
- package/src/providers/openai.ts +26 -8
- package/src/providers/registry.ts +116 -111
- package/src/providers/store.ts +130 -28
- package/src/providers/types.ts +36 -1
- package/src/providers/vertex-ai.ts +70 -23
- package/src/session/manager.ts +9 -0
- package/src/session/types.ts +1 -1
- package/src/tools/index.ts +8 -0
- package/src/tools/registry.ts +95 -2
- package/.gencode/settings.local.json +0 -7
- package/CLAUDE.md +0 -86
package/src/agent/agent.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Agent - Core agent implementation with tool loop and session support
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { LLMProvider, Message, ToolResultContent } from '../providers/types.js';
|
|
6
|
-
import { createProvider, inferProvider } from '../providers/index.js';
|
|
5
|
+
import type { LLMProvider, Message, ToolResultContent, Provider, AuthMethod } from '../providers/types.js';
|
|
6
|
+
import { createProvider, inferProvider, inferAuthMethod } from '../providers/index.js';
|
|
7
7
|
import { ToolRegistry, createDefaultRegistry } from '../tools/index.js';
|
|
8
8
|
import {
|
|
9
9
|
PermissionManager,
|
|
@@ -17,6 +17,12 @@ import { MemoryManager, type LoadedMemory } from '../memory/index.js';
|
|
|
17
17
|
import type { AgentConfig, AgentEvent } from './types.js';
|
|
18
18
|
import { buildSystemPromptForModel, debugPromptLoading } from '../prompts/index.js';
|
|
19
19
|
import type { Question, QuestionAnswer } from '../tools/types.js';
|
|
20
|
+
import {
|
|
21
|
+
getPlanModeManager,
|
|
22
|
+
type PlanModeManager,
|
|
23
|
+
type ModeType,
|
|
24
|
+
type AllowedPrompt,
|
|
25
|
+
} from '../planning/index.js';
|
|
20
26
|
|
|
21
27
|
// Type for askUser callback
|
|
22
28
|
export type AskUserCallback = (questions: Question[]) => Promise<QuestionAnswer[]>;
|
|
@@ -27,6 +33,7 @@ export class Agent {
|
|
|
27
33
|
private permissions: PermissionManager;
|
|
28
34
|
private sessionManager: SessionManager;
|
|
29
35
|
private memoryManager: MemoryManager;
|
|
36
|
+
private planModeManager: PlanModeManager;
|
|
30
37
|
private config: AgentConfig;
|
|
31
38
|
private messages: Message[] = [];
|
|
32
39
|
private sessionId: string | null = null;
|
|
@@ -40,7 +47,10 @@ export class Agent {
|
|
|
40
47
|
...config,
|
|
41
48
|
};
|
|
42
49
|
|
|
43
|
-
this.provider = createProvider({
|
|
50
|
+
this.provider = createProvider({
|
|
51
|
+
provider: config.provider,
|
|
52
|
+
authMethod: config.authMethod,
|
|
53
|
+
});
|
|
44
54
|
this.registry = createDefaultRegistry();
|
|
45
55
|
this.permissions = new PermissionManager({
|
|
46
56
|
config: config.permissions,
|
|
@@ -48,6 +58,7 @@ export class Agent {
|
|
|
48
58
|
});
|
|
49
59
|
this.sessionManager = new SessionManager();
|
|
50
60
|
this.memoryManager = new MemoryManager();
|
|
61
|
+
this.planModeManager = getPlanModeManager();
|
|
51
62
|
}
|
|
52
63
|
|
|
53
64
|
/**
|
|
@@ -133,10 +144,99 @@ export class Agent {
|
|
|
133
144
|
*/
|
|
134
145
|
async loadMemory(): Promise<LoadedMemory> {
|
|
135
146
|
const cwd = this.config.cwd ?? process.cwd();
|
|
136
|
-
|
|
147
|
+
|
|
148
|
+
// Determine memory merge strategy (priority: env var > config > default)
|
|
149
|
+
const envStrategy = process.env.GEN_MEMORY_STRATEGY as
|
|
150
|
+
| 'fallback'
|
|
151
|
+
| 'both'
|
|
152
|
+
| 'gen-only'
|
|
153
|
+
| 'claude-only'
|
|
154
|
+
| undefined;
|
|
155
|
+
const strategy =
|
|
156
|
+
envStrategy ?? this.config.memoryMergeStrategy ?? 'fallback';
|
|
157
|
+
|
|
158
|
+
this.loadedMemory = await this.memoryManager.load({ cwd, strategy });
|
|
159
|
+
|
|
160
|
+
// Log verbose summary if verbose mode is enabled
|
|
161
|
+
if (this.config.verbose) {
|
|
162
|
+
console.log(this.memoryManager.getVerboseSummary(strategy));
|
|
163
|
+
}
|
|
164
|
+
|
|
137
165
|
return this.loadedMemory;
|
|
138
166
|
}
|
|
139
167
|
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// Plan Mode
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get plan mode manager for external access
|
|
174
|
+
*/
|
|
175
|
+
getPlanModeManager(): PlanModeManager {
|
|
176
|
+
return this.planModeManager;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Check if plan mode is active
|
|
181
|
+
*/
|
|
182
|
+
isPlanModeActive(): boolean {
|
|
183
|
+
return this.planModeManager.isActive();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get current mode (build or plan)
|
|
188
|
+
*/
|
|
189
|
+
getCurrentMode(): ModeType {
|
|
190
|
+
return this.planModeManager.getCurrentMode();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Enter plan mode programmatically
|
|
195
|
+
*/
|
|
196
|
+
async enterPlanMode(taskDescription?: string): Promise<string> {
|
|
197
|
+
const { createPlanFile } = await import('../planning/index.js');
|
|
198
|
+
const cwd = this.config.cwd ?? process.cwd();
|
|
199
|
+
const planFile = await createPlanFile(cwd, taskDescription);
|
|
200
|
+
this.planModeManager.enter(planFile.path, taskDescription);
|
|
201
|
+
return planFile.path;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Exit plan mode
|
|
206
|
+
*/
|
|
207
|
+
exitPlanMode(approved: boolean = false): void {
|
|
208
|
+
if (approved) {
|
|
209
|
+
// Add allowed prompts from plan mode to permissions
|
|
210
|
+
const allowedPrompts = this.planModeManager.getRequestedPermissions();
|
|
211
|
+
if (allowedPrompts.length > 0) {
|
|
212
|
+
this.permissions.addAllowedPrompts(allowedPrompts);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
this.planModeManager.exit(approved);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Toggle plan mode
|
|
220
|
+
*/
|
|
221
|
+
async togglePlanMode(): Promise<void> {
|
|
222
|
+
if (this.planModeManager.isActive()) {
|
|
223
|
+
this.planModeManager.exit(false);
|
|
224
|
+
} else {
|
|
225
|
+
await this.enterPlanMode();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get plan mode requested permissions
|
|
231
|
+
*/
|
|
232
|
+
getPlanModePermissions(): AllowedPrompt[] {
|
|
233
|
+
return this.planModeManager.getRequestedPermissions();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Session Management
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
140
240
|
/**
|
|
141
241
|
* Get current session ID
|
|
142
242
|
*/
|
|
@@ -153,15 +253,30 @@ export class Agent {
|
|
|
153
253
|
|
|
154
254
|
/**
|
|
155
255
|
* Set the model to use (auto-switches provider if needed)
|
|
256
|
+
* @param model Model ID to use
|
|
257
|
+
* @param provider Optional: explicit provider (otherwise inferred from model name)
|
|
258
|
+
* @param authMethod Optional: explicit auth method (otherwise inferred or use current)
|
|
156
259
|
*/
|
|
157
|
-
setModel(model: string): void {
|
|
260
|
+
setModel(model: string, provider?: string, authMethod?: string): void {
|
|
158
261
|
this.config.model = model;
|
|
159
262
|
|
|
160
|
-
//
|
|
161
|
-
const newProvider = inferProvider(model);
|
|
162
|
-
|
|
263
|
+
// Determine new provider and authMethod
|
|
264
|
+
const newProvider = (provider as Provider | undefined) ?? inferProvider(model);
|
|
265
|
+
const newAuthMethod = (authMethod as AuthMethod | undefined) ??
|
|
266
|
+
inferAuthMethod(model) ??
|
|
267
|
+
this.config.authMethod;
|
|
268
|
+
|
|
269
|
+
// Recreate provider if either provider or authMethod changed
|
|
270
|
+
const providerChanged = newProvider !== this.config.provider;
|
|
271
|
+
const authMethodChanged = newAuthMethod !== this.config.authMethod;
|
|
272
|
+
|
|
273
|
+
if (providerChanged || authMethodChanged) {
|
|
163
274
|
this.config.provider = newProvider;
|
|
164
|
-
this.
|
|
275
|
+
this.config.authMethod = newAuthMethod;
|
|
276
|
+
this.provider = createProvider({
|
|
277
|
+
provider: newProvider,
|
|
278
|
+
authMethod: newAuthMethod,
|
|
279
|
+
});
|
|
165
280
|
}
|
|
166
281
|
}
|
|
167
282
|
|
|
@@ -172,6 +287,13 @@ export class Agent {
|
|
|
172
287
|
return this.config.model;
|
|
173
288
|
}
|
|
174
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Get current provider
|
|
292
|
+
*/
|
|
293
|
+
getProvider(): Provider {
|
|
294
|
+
return this.config.provider;
|
|
295
|
+
}
|
|
296
|
+
|
|
175
297
|
/**
|
|
176
298
|
* List available models from the provider API
|
|
177
299
|
*/
|
|
@@ -290,8 +412,8 @@ export class Agent {
|
|
|
290
412
|
while (turns < maxTurns) {
|
|
291
413
|
turns++;
|
|
292
414
|
|
|
293
|
-
// Get tool definitions
|
|
294
|
-
const toolDefs = this.registry.
|
|
415
|
+
// Get tool definitions (filtered by plan mode if active)
|
|
416
|
+
const toolDefs = this.registry.getFilteredDefinitions(this.config.tools);
|
|
295
417
|
|
|
296
418
|
// Call LLM
|
|
297
419
|
let response;
|
|
@@ -341,7 +463,7 @@ export class Agent {
|
|
|
341
463
|
await this.sessionManager.addMessage({ role: 'assistant', content: response.content });
|
|
342
464
|
|
|
343
465
|
if (response.stopReason !== 'tool_use' || toolCalls.length === 0) {
|
|
344
|
-
yield { type: 'done', text: textContent };
|
|
466
|
+
yield { type: 'done', text: textContent, usage: response.usage, cost: response.cost };
|
|
345
467
|
return;
|
|
346
468
|
}
|
|
347
469
|
|
|
@@ -388,6 +510,33 @@ export class Agent {
|
|
|
388
510
|
this.sessionManager.clearMessages();
|
|
389
511
|
}
|
|
390
512
|
|
|
513
|
+
/**
|
|
514
|
+
* Clean up incomplete tool use messages (after interruption)
|
|
515
|
+
* Removes the last assistant message if it contains tool_use without corresponding tool_result
|
|
516
|
+
*/
|
|
517
|
+
cleanupIncompleteMessages(): void {
|
|
518
|
+
if (this.messages.length === 0) return;
|
|
519
|
+
|
|
520
|
+
const lastMessage = this.messages[this.messages.length - 1];
|
|
521
|
+
|
|
522
|
+
// Check if last message is an assistant message with tool_use
|
|
523
|
+
if (lastMessage.role === 'assistant' && Array.isArray(lastMessage.content)) {
|
|
524
|
+
const hasToolUse = lastMessage.content.some((c) => c.type === 'tool_use');
|
|
525
|
+
|
|
526
|
+
if (hasToolUse) {
|
|
527
|
+
// Remove the incomplete assistant message
|
|
528
|
+
this.messages.pop();
|
|
529
|
+
|
|
530
|
+
// Also remove from session manager
|
|
531
|
+
// Note: SessionManager should have corresponding cleanup method
|
|
532
|
+
const messages = this.sessionManager.getMessages();
|
|
533
|
+
if (messages.length > 0 && messages[messages.length - 1].role === 'assistant') {
|
|
534
|
+
this.sessionManager.removeLastMessage();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
391
540
|
/**
|
|
392
541
|
* Get conversation history
|
|
393
542
|
*/
|
package/src/agent/types.ts
CHANGED
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { PermissionConfig } from '../permissions/types.js';
|
|
6
|
+
import type { CostEstimate } from '../pricing/types.js';
|
|
7
|
+
import type { Provider, AuthMethod } from '../providers/types.js';
|
|
6
8
|
|
|
7
9
|
export interface AgentConfig {
|
|
8
|
-
provider:
|
|
10
|
+
provider: Provider;
|
|
11
|
+
authMethod?: AuthMethod;
|
|
9
12
|
model: string;
|
|
10
13
|
systemPrompt?: string;
|
|
11
14
|
tools?: string[];
|
|
12
15
|
cwd?: string;
|
|
13
16
|
maxTurns?: number;
|
|
14
17
|
permissions?: Partial<PermissionConfig>;
|
|
18
|
+
memoryMergeStrategy?: 'fallback' | 'both' | 'gen-only' | 'claude-only';
|
|
19
|
+
verbose?: boolean;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
// Agent Events
|
|
@@ -59,6 +64,11 @@ export interface AgentEventError {
|
|
|
59
64
|
export interface AgentEventDone {
|
|
60
65
|
type: 'done';
|
|
61
66
|
text: string;
|
|
67
|
+
usage?: {
|
|
68
|
+
inputTokens: number;
|
|
69
|
+
outputTokens: number;
|
|
70
|
+
};
|
|
71
|
+
cost?: CostEstimate;
|
|
62
72
|
}
|
|
63
73
|
|
|
64
74
|
export interface AgentEventAskUser {
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint Manager - Core checkpointing logic
|
|
3
|
+
*
|
|
4
|
+
* Tracks file changes and provides rewind capabilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs/promises';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import type {
|
|
10
|
+
FileCheckpoint,
|
|
11
|
+
CheckpointSession,
|
|
12
|
+
RewindOptions,
|
|
13
|
+
RewindResult,
|
|
14
|
+
CheckpointSummary,
|
|
15
|
+
RecordChangeInput,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generates a unique ID for checkpoints
|
|
20
|
+
*/
|
|
21
|
+
function generateId(): string {
|
|
22
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* CheckpointManager manages file change tracking and rewind operations.
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* const manager = new CheckpointManager('session-123');
|
|
30
|
+
* manager.recordChange({ path: '/path/to/file', changeType: 'modify', ... });
|
|
31
|
+
* await manager.rewind({ all: true });
|
|
32
|
+
*/
|
|
33
|
+
export class CheckpointManager {
|
|
34
|
+
private session: CheckpointSession;
|
|
35
|
+
|
|
36
|
+
constructor(sessionId: string = 'default') {
|
|
37
|
+
this.session = {
|
|
38
|
+
sessionId,
|
|
39
|
+
checkpoints: [],
|
|
40
|
+
createdAt: new Date(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the session ID
|
|
46
|
+
*/
|
|
47
|
+
getSessionId(): string {
|
|
48
|
+
return this.session.sessionId;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Record a file change as a checkpoint
|
|
53
|
+
*/
|
|
54
|
+
recordChange(input: RecordChangeInput): FileCheckpoint {
|
|
55
|
+
const checkpoint: FileCheckpoint = {
|
|
56
|
+
id: generateId(),
|
|
57
|
+
path: input.path,
|
|
58
|
+
changeType: input.changeType,
|
|
59
|
+
timestamp: new Date(),
|
|
60
|
+
previousContent: input.previousContent,
|
|
61
|
+
newContent: input.newContent,
|
|
62
|
+
toolName: input.toolName,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
this.session.checkpoints.push(checkpoint);
|
|
66
|
+
return checkpoint;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get all checkpoints
|
|
71
|
+
*/
|
|
72
|
+
getCheckpoints(): FileCheckpoint[] {
|
|
73
|
+
return [...this.session.checkpoints];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get checkpoints for a specific file
|
|
78
|
+
*/
|
|
79
|
+
getFileHistory(filePath: string): FileCheckpoint[] {
|
|
80
|
+
return this.session.checkpoints.filter((cp) => cp.path === filePath);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get a summary of all changes
|
|
85
|
+
*/
|
|
86
|
+
getSummary(): CheckpointSummary {
|
|
87
|
+
const summary: CheckpointSummary = {
|
|
88
|
+
created: 0,
|
|
89
|
+
modified: 0,
|
|
90
|
+
deleted: 0,
|
|
91
|
+
total: this.session.checkpoints.length,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
for (const cp of this.session.checkpoints) {
|
|
95
|
+
switch (cp.changeType) {
|
|
96
|
+
case 'create':
|
|
97
|
+
summary.created++;
|
|
98
|
+
break;
|
|
99
|
+
case 'modify':
|
|
100
|
+
summary.modified++;
|
|
101
|
+
break;
|
|
102
|
+
case 'delete':
|
|
103
|
+
summary.deleted++;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return summary;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check if there are any checkpoints
|
|
113
|
+
*/
|
|
114
|
+
hasCheckpoints(): boolean {
|
|
115
|
+
return this.session.checkpoints.length > 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get the number of checkpoints
|
|
120
|
+
*/
|
|
121
|
+
getCheckpointCount(): number {
|
|
122
|
+
return this.session.checkpoints.length;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Rewind changes based on options
|
|
127
|
+
*/
|
|
128
|
+
async rewind(options: RewindOptions): Promise<RewindResult> {
|
|
129
|
+
const result: RewindResult = {
|
|
130
|
+
success: true,
|
|
131
|
+
revertedFiles: [],
|
|
132
|
+
errors: [],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Determine which checkpoints to rewind
|
|
136
|
+
let checkpointsToRewind: FileCheckpoint[] = [];
|
|
137
|
+
|
|
138
|
+
if (options.checkpointId) {
|
|
139
|
+
// Rewind specific checkpoint
|
|
140
|
+
const cp = this.session.checkpoints.find((c) => c.id === options.checkpointId);
|
|
141
|
+
if (cp) {
|
|
142
|
+
checkpointsToRewind = [cp];
|
|
143
|
+
}
|
|
144
|
+
} else if (options.path) {
|
|
145
|
+
// Rewind all changes to a specific file (in reverse order)
|
|
146
|
+
checkpointsToRewind = this.session.checkpoints
|
|
147
|
+
.filter((c) => c.path === options.path)
|
|
148
|
+
.reverse();
|
|
149
|
+
} else if (options.count) {
|
|
150
|
+
// Rewind last N changes (in reverse order)
|
|
151
|
+
checkpointsToRewind = this.session.checkpoints.slice(-options.count).reverse();
|
|
152
|
+
} else if (options.all) {
|
|
153
|
+
// Rewind all changes (in reverse order)
|
|
154
|
+
checkpointsToRewind = [...this.session.checkpoints].reverse();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Apply reverts
|
|
158
|
+
for (const checkpoint of checkpointsToRewind) {
|
|
159
|
+
try {
|
|
160
|
+
await this.revertCheckpoint(checkpoint);
|
|
161
|
+
result.revertedFiles.push({
|
|
162
|
+
path: checkpoint.path,
|
|
163
|
+
action: this.getRevertAction(checkpoint),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Remove the checkpoint from session
|
|
167
|
+
const index = this.session.checkpoints.findIndex((c) => c.id === checkpoint.id);
|
|
168
|
+
if (index !== -1) {
|
|
169
|
+
this.session.checkpoints.splice(index, 1);
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
result.success = false;
|
|
173
|
+
result.errors.push({
|
|
174
|
+
path: checkpoint.path,
|
|
175
|
+
error: error instanceof Error ? error.message : String(error),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Revert a single checkpoint
|
|
185
|
+
*/
|
|
186
|
+
private async revertCheckpoint(checkpoint: FileCheckpoint): Promise<void> {
|
|
187
|
+
switch (checkpoint.changeType) {
|
|
188
|
+
case 'create':
|
|
189
|
+
// File was created, delete it to revert
|
|
190
|
+
await fs.unlink(checkpoint.path);
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
case 'modify':
|
|
194
|
+
// File was modified, restore previous content
|
|
195
|
+
if (checkpoint.previousContent !== null) {
|
|
196
|
+
await fs.writeFile(checkpoint.path, checkpoint.previousContent, 'utf-8');
|
|
197
|
+
}
|
|
198
|
+
break;
|
|
199
|
+
|
|
200
|
+
case 'delete':
|
|
201
|
+
// File was deleted, recreate it with previous content
|
|
202
|
+
if (checkpoint.previousContent !== null) {
|
|
203
|
+
await fs.mkdir(path.dirname(checkpoint.path), { recursive: true });
|
|
204
|
+
await fs.writeFile(checkpoint.path, checkpoint.previousContent, 'utf-8');
|
|
205
|
+
}
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get the action that will be taken to revert a checkpoint
|
|
212
|
+
*/
|
|
213
|
+
private getRevertAction(checkpoint: FileCheckpoint): 'restored' | 'deleted' | 'recreated' {
|
|
214
|
+
switch (checkpoint.changeType) {
|
|
215
|
+
case 'create':
|
|
216
|
+
return 'deleted';
|
|
217
|
+
case 'modify':
|
|
218
|
+
return 'restored';
|
|
219
|
+
case 'delete':
|
|
220
|
+
return 'recreated';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Clear all checkpoints
|
|
226
|
+
*/
|
|
227
|
+
clearCheckpoints(): void {
|
|
228
|
+
this.session.checkpoints = [];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Format checkpoints for display
|
|
233
|
+
*/
|
|
234
|
+
formatCheckpointList(includeUsage: boolean = false): string {
|
|
235
|
+
if (this.session.checkpoints.length === 0) {
|
|
236
|
+
return 'No file changes in this session.';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const lines: string[] = [];
|
|
240
|
+
|
|
241
|
+
this.session.checkpoints.forEach((cp, index) => {
|
|
242
|
+
const timeAgo = this.formatTimeAgo(cp.timestamp);
|
|
243
|
+
const action = this.formatChangeType(cp.changeType);
|
|
244
|
+
const fileName = cp.path.split('/').pop() || cp.path;
|
|
245
|
+
lines.push(` [${index + 1}] ${timeAgo.padEnd(8)} ${fileName.padEnd(30)} (${action})`);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const summary = this.getSummary();
|
|
249
|
+
lines.push('');
|
|
250
|
+
lines.push(
|
|
251
|
+
`Total: ${summary.created} created, ${summary.modified} modified, ${summary.deleted} deleted`
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
if (includeUsage) {
|
|
255
|
+
lines.push('');
|
|
256
|
+
lines.push('Usage: /rewind [n] to revert change #n, /rewind all to revert all');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return lines.join('\n');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Format a change type for display
|
|
264
|
+
*/
|
|
265
|
+
private formatChangeType(changeType: string): string {
|
|
266
|
+
switch (changeType) {
|
|
267
|
+
case 'create':
|
|
268
|
+
return 'created';
|
|
269
|
+
case 'modify':
|
|
270
|
+
return 'modified';
|
|
271
|
+
case 'delete':
|
|
272
|
+
return 'deleted';
|
|
273
|
+
default:
|
|
274
|
+
return changeType;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Format a timestamp as relative time
|
|
280
|
+
*/
|
|
281
|
+
private formatTimeAgo(date: Date): string {
|
|
282
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
283
|
+
|
|
284
|
+
if (seconds < 60) {
|
|
285
|
+
return `${seconds}s ago`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const minutes = Math.floor(seconds / 60);
|
|
289
|
+
if (minutes < 60) {
|
|
290
|
+
return `${minutes}m ago`;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const hours = Math.floor(minutes / 60);
|
|
294
|
+
return `${hours}h ago`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ============================================================================
|
|
299
|
+
// Singleton Instance
|
|
300
|
+
// ============================================================================
|
|
301
|
+
|
|
302
|
+
let globalCheckpointManager: CheckpointManager | null = null;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get the global checkpoint manager instance
|
|
306
|
+
*/
|
|
307
|
+
export function getCheckpointManager(): CheckpointManager {
|
|
308
|
+
if (!globalCheckpointManager) {
|
|
309
|
+
globalCheckpointManager = new CheckpointManager();
|
|
310
|
+
}
|
|
311
|
+
return globalCheckpointManager;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Initialize checkpoint manager with a session ID
|
|
316
|
+
*/
|
|
317
|
+
export function initCheckpointManager(sessionId: string): CheckpointManager {
|
|
318
|
+
globalCheckpointManager = new CheckpointManager(sessionId);
|
|
319
|
+
return globalCheckpointManager;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Reset the global checkpoint manager (for testing)
|
|
324
|
+
*/
|
|
325
|
+
export function resetCheckpointManager(): void {
|
|
326
|
+
globalCheckpointManager = null;
|
|
327
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpointing Module
|
|
3
|
+
*
|
|
4
|
+
* Provides automatic tracking of file changes with undo/rewind capabilities.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { getCheckpointManager, initCheckpointManager } from './checkpointing';
|
|
8
|
+
*
|
|
9
|
+
* // Initialize at session start
|
|
10
|
+
* const manager = initCheckpointManager('session-123');
|
|
11
|
+
*
|
|
12
|
+
* // Record changes (done automatically by ToolRegistry)
|
|
13
|
+
* manager.recordChange({
|
|
14
|
+
* path: '/path/to/file.ts',
|
|
15
|
+
* changeType: 'modify',
|
|
16
|
+
* previousContent: 'old content',
|
|
17
|
+
* newContent: 'new content',
|
|
18
|
+
* toolName: 'Edit'
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // List changes
|
|
22
|
+
* console.log(manager.formatCheckpointList());
|
|
23
|
+
*
|
|
24
|
+
* // Rewind changes
|
|
25
|
+
* await manager.rewind({ all: true });
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Type exports
|
|
29
|
+
export type {
|
|
30
|
+
ChangeType,
|
|
31
|
+
FileCheckpoint,
|
|
32
|
+
CheckpointSession,
|
|
33
|
+
RewindOptions,
|
|
34
|
+
RewindResult,
|
|
35
|
+
CheckpointSummary,
|
|
36
|
+
RecordChangeInput,
|
|
37
|
+
} from './types.js';
|
|
38
|
+
|
|
39
|
+
// Manager exports
|
|
40
|
+
export {
|
|
41
|
+
CheckpointManager,
|
|
42
|
+
getCheckpointManager,
|
|
43
|
+
initCheckpointManager,
|
|
44
|
+
resetCheckpointManager,
|
|
45
|
+
} from './checkpoint-manager.js';
|