lettactl 0.5.8 → 0.5.9

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 (40) hide show
  1. package/dist/commands/apply-template.d.ts +12 -0
  2. package/dist/commands/apply-template.d.ts.map +1 -0
  3. package/dist/commands/apply-template.js +118 -0
  4. package/dist/commands/apply-template.js.map +1 -0
  5. package/dist/commands/apply.d.ts.map +1 -1
  6. package/dist/commands/apply.js +42 -358
  7. package/dist/commands/apply.js.map +1 -1
  8. package/dist/lib/agent-manager.d.ts +0 -8
  9. package/dist/lib/agent-manager.d.ts.map +1 -1
  10. package/dist/lib/agent-manager.js +10 -58
  11. package/dist/lib/agent-manager.js.map +1 -1
  12. package/dist/lib/apply-helpers.d.ts +29 -0
  13. package/dist/lib/apply-helpers.d.ts.map +1 -0
  14. package/dist/lib/apply-helpers.js +224 -0
  15. package/dist/lib/apply-helpers.js.map +1 -0
  16. package/dist/lib/block-manager.d.ts +6 -29
  17. package/dist/lib/block-manager.d.ts.map +1 -1
  18. package/dist/lib/block-manager.js +76 -218
  19. package/dist/lib/block-manager.js.map +1 -1
  20. package/dist/lib/diff-analyzers.d.ts +17 -0
  21. package/dist/lib/diff-analyzers.d.ts.map +1 -0
  22. package/dist/lib/diff-analyzers.js +172 -0
  23. package/dist/lib/diff-analyzers.js.map +1 -0
  24. package/dist/lib/diff-applier.d.ts +27 -0
  25. package/dist/lib/diff-applier.d.ts.map +1 -0
  26. package/dist/lib/diff-applier.js +196 -0
  27. package/dist/lib/diff-applier.js.map +1 -0
  28. package/dist/lib/diff-engine.d.ts +0 -19
  29. package/dist/lib/diff-engine.d.ts.map +1 -1
  30. package/dist/lib/diff-engine.js +8 -359
  31. package/dist/lib/diff-engine.js.map +1 -1
  32. package/dist/lib/letta-client.d.ts +5 -0
  33. package/dist/lib/letta-client.d.ts.map +1 -1
  34. package/dist/lib/letta-client.js +3 -0
  35. package/dist/lib/letta-client.js.map +1 -1
  36. package/dist/utils/hash-utils.d.ts +9 -0
  37. package/dist/utils/hash-utils.d.ts.map +1 -0
  38. package/dist/utils/hash-utils.js +54 -0
  39. package/dist/utils/hash-utils.js.map +1 -0
  40. package/package.json +1 -1
@@ -0,0 +1,12 @@
1
+ import { FleetParser } from '../lib/fleet-parser';
2
+ /**
3
+ * Template mode: apply a template config to all existing agents matching a glob pattern.
4
+ * Uses MERGE semantics - adds/updates resources but doesn't remove existing ones.
5
+ */
6
+ export declare function applyTemplateMode(options: {
7
+ file: string;
8
+ match: string;
9
+ dryRun?: boolean;
10
+ root?: string;
11
+ }, config: any, parser: FleetParser, command: any): Promise<void>;
12
+ //# sourceMappingURL=apply-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-template.d.ts","sourceRoot":"","sources":["../../src/commands/apply-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AASlD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACzE,MAAM,EAAE,GAAG,EACX,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,GAAG,GACX,OAAO,CAAC,IAAI,CAAC,CA4Hf"}
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyTemplateMode = applyTemplateMode;
4
+ const letta_client_1 = require("../lib/letta-client");
5
+ const block_manager_1 = require("../lib/block-manager");
6
+ const diff_engine_1 = require("../lib/diff-engine");
7
+ const file_content_tracker_1 = require("../lib/file-content-tracker");
8
+ const output_formatter_1 = require("../lib/output-formatter");
9
+ const spinner_1 = require("../lib/spinner");
10
+ const minimatch_1 = require("minimatch");
11
+ /**
12
+ * Template mode: apply a template config to all existing agents matching a glob pattern.
13
+ * Uses MERGE semantics - adds/updates resources but doesn't remove existing ones.
14
+ */
15
+ async function applyTemplateMode(options, config, parser, command) {
16
+ const verbose = command.parent?.opts().verbose || false;
17
+ const spinnerEnabled = (0, spinner_1.getSpinnerEnabled)(command);
18
+ const pattern = options.match;
19
+ const findSpinner = (0, spinner_1.createSpinner)(`Finding agents matching "${pattern}"...`, spinnerEnabled).start();
20
+ // Get all agents from server
21
+ const client = new letta_client_1.LettaClientWrapper();
22
+ const allAgentsResponse = await client.listAgents();
23
+ const allAgents = Array.isArray(allAgentsResponse)
24
+ ? allAgentsResponse
25
+ : allAgentsResponse.items || [];
26
+ // Filter by glob pattern
27
+ const matchingAgents = allAgents.filter((agent) => (0, minimatch_1.minimatch)(agent.name, pattern));
28
+ if (matchingAgents.length === 0) {
29
+ findSpinner.fail(`No agents found matching pattern: ${pattern}`);
30
+ return;
31
+ }
32
+ findSpinner.succeed(`Found ${matchingAgents.length} agents matching "${pattern}"`);
33
+ matchingAgents.forEach((a) => console.log(` - ${a.name}`));
34
+ if (options.dryRun) {
35
+ console.log('\nDry-run mode: no changes will be made');
36
+ return;
37
+ }
38
+ // Initialize managers
39
+ const blockManager = new block_manager_1.BlockManager(client);
40
+ const diffEngine = new diff_engine_1.DiffEngine(client, blockManager, parser.basePath);
41
+ const fileTracker = new file_content_tracker_1.FileContentTracker(parser.basePath, parser.storageBackend);
42
+ const createdFolders = new Map();
43
+ await blockManager.loadExistingBlocks();
44
+ // Process shared blocks from template
45
+ const sharedBlockIds = new Map();
46
+ if (config.shared_blocks?.length) {
47
+ const blockSpinner = (0, spinner_1.createSpinner)('Processing shared blocks...', spinnerEnabled).start();
48
+ for (const sharedBlock of config.shared_blocks) {
49
+ const blockId = await blockManager.getOrCreateSharedBlock(sharedBlock);
50
+ sharedBlockIds.set(sharedBlock.name, blockId);
51
+ if (verbose)
52
+ console.log(` ${sharedBlock.name} -> ${blockId}`);
53
+ }
54
+ blockSpinner.succeed(`Processed ${config.shared_blocks.length} shared blocks`);
55
+ }
56
+ // Get template agent config (first agent in config, or use top-level values)
57
+ const templateAgent = config.agents?.[0];
58
+ const templateTools = templateAgent?.tools || [];
59
+ const templateSharedBlocks = templateAgent?.shared_blocks || [];
60
+ // Generate tool source hashes and register tools
61
+ const toolSourceHashes = fileTracker.generateToolSourceHashes(templateTools, parser.toolConfigs);
62
+ const toolNameToId = await parser.registerRequiredTools(config, client, verbose, toolSourceHashes);
63
+ // Apply template to each matching agent
64
+ for (const existingAgent of matchingAgents) {
65
+ const agentSpinner = (0, spinner_1.createSpinner)(`Analyzing ${existingAgent.name}...`, spinnerEnabled).start();
66
+ try {
67
+ // Build desired config from template (no memoryBlocks - those are instance-specific)
68
+ const desiredConfig = {
69
+ systemPrompt: templateAgent?.system_prompt?.value || '',
70
+ tools: templateTools,
71
+ toolSourceHashes,
72
+ sharedBlocks: templateSharedBlocks,
73
+ };
74
+ // Create minimal AgentVersion for diff engine
75
+ const agentVersion = {
76
+ id: existingAgent.id,
77
+ name: existingAgent.name,
78
+ baseName: existingAgent.name,
79
+ configHashes: { overall: '', systemPrompt: '', tools: '', model: '', memoryBlocks: '', folders: '', sharedBlocks: '' },
80
+ version: 'latest',
81
+ lastUpdated: existingAgent.updated_at || new Date().toISOString()
82
+ };
83
+ // Generate update operations
84
+ const ops = await diffEngine.generateUpdateOperations(agentVersion, desiredConfig, toolNameToId, createdFolders, verbose, sharedBlockIds);
85
+ // MERGE semantics: clear toRemove arrays (don't remove existing resources)
86
+ if (ops.tools)
87
+ ops.tools.toRemove = [];
88
+ if (ops.blocks)
89
+ ops.blocks.toRemove = [];
90
+ if (ops.folders)
91
+ ops.folders.toDetach = [];
92
+ // Recalculate operation count after filtering
93
+ ops.operationCount = 0;
94
+ if (ops.updateFields)
95
+ ops.operationCount += Object.keys(ops.updateFields).length;
96
+ if (ops.tools)
97
+ ops.operationCount += ops.tools.toAdd.length + ops.tools.toUpdate.length;
98
+ if (ops.blocks)
99
+ ops.operationCount += ops.blocks.toAdd.length + ops.blocks.toUpdate.length;
100
+ if (ops.folders)
101
+ ops.operationCount += ops.folders.toAttach.length;
102
+ if (ops.operationCount === 0) {
103
+ agentSpinner.succeed(`${existingAgent.name}: already up to date`);
104
+ continue;
105
+ }
106
+ agentSpinner.stop();
107
+ output_formatter_1.OutputFormatter.showAgentUpdateDiff(ops);
108
+ const updateSpinner = (0, spinner_1.createSpinner)(`Applying updates to ${existingAgent.name}...`, spinnerEnabled).start();
109
+ await diffEngine.applyUpdateOperations(existingAgent.id, ops, verbose);
110
+ updateSpinner.succeed(`${existingAgent.name}: updated successfully`);
111
+ }
112
+ catch (error) {
113
+ agentSpinner.fail(`${existingAgent.name}: ${error.message}`);
114
+ }
115
+ }
116
+ console.log('\nTemplate apply completed');
117
+ }
118
+ //# sourceMappingURL=apply-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-template.js","sourceRoot":"","sources":["../../src/commands/apply-template.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":"AAcA,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,GAAG,iBAwX1I"}
1
+ {"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":"AAYA,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,GAAG,iBAiL1I"}
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.applyCommand = applyCommand;
37
4
  const fleet_parser_1 = require("../lib/fleet-parser");
@@ -40,12 +7,10 @@ const block_manager_1 = require("../lib/block-manager");
40
7
  const agent_manager_1 = require("../lib/agent-manager");
41
8
  const diff_engine_1 = require("../lib/diff-engine");
42
9
  const file_content_tracker_1 = require("../lib/file-content-tracker");
43
- const output_formatter_1 = require("../lib/output-formatter");
44
10
  const spinner_1 = require("../lib/spinner");
45
11
  const storage_backend_1 = require("../lib/storage-backend");
46
- const minimatch_1 = require("minimatch");
47
- const fs = __importStar(require("fs"));
48
- const path = __importStar(require("path"));
12
+ const apply_template_1 = require("./apply-template");
13
+ const apply_helpers_1 = require("../lib/apply-helpers");
49
14
  async function applyCommand(options, command) {
50
15
  const verbose = command.parent?.opts().verbose || false;
51
16
  try {
@@ -78,7 +43,7 @@ async function applyCommand(options, command) {
78
43
  console.log(`Found ${config.agents.length} agents in configuration`);
79
44
  // Template mode: apply config to existing agents matching pattern
80
45
  if (options.match) {
81
- await applyTemplateMode({ ...options, match: options.match }, config, parser, command);
46
+ await (0, apply_template_1.applyTemplateMode)({ ...options, match: options.match }, config, parser, command);
82
47
  return;
83
48
  }
84
49
  if (options.dryRun) {
@@ -97,25 +62,16 @@ async function applyCommand(options, command) {
97
62
  const agentManager = new agent_manager_1.AgentManager(client);
98
63
  const diffEngine = new diff_engine_1.DiffEngine(client, blockManager, parser.basePath);
99
64
  const fileTracker = new file_content_tracker_1.FileContentTracker(parser.basePath, parser.storageBackend);
100
- const createdFolders = new Map(); // folder name -> folder id
101
- // Load existing resources for versioning
65
+ // Load existing resources
102
66
  if (verbose)
103
67
  console.log('Loading existing blocks...');
104
68
  await blockManager.loadExistingBlocks();
105
69
  if (verbose)
106
70
  console.log('Loading existing agents...');
107
71
  await agentManager.loadExistingAgents();
108
- // Process shared blocks first
109
- const sharedBlockIds = new Map();
110
- if (config.shared_blocks) {
111
- if (verbose)
112
- console.log('Processing shared blocks...');
113
- for (const sharedBlock of config.shared_blocks) {
114
- const blockId = await blockManager.getOrCreateSharedBlock(sharedBlock);
115
- sharedBlockIds.set(sharedBlock.name, blockId);
116
- }
117
- }
118
- // Generate tool source hashes for all tools in config
72
+ // Process shared blocks
73
+ const sharedBlockIds = await (0, apply_helpers_1.processSharedBlocks)(config, blockManager, verbose);
74
+ // Generate tool source hashes and register tools
119
75
  const allToolNames = new Set();
120
76
  for (const agent of config.agents) {
121
77
  for (const toolName of agent.tools || []) {
@@ -123,69 +79,13 @@ async function applyCommand(options, command) {
123
79
  }
124
80
  }
125
81
  const globalToolSourceHashes = fileTracker.generateToolSourceHashes(Array.from(allToolNames), parser.toolConfigs);
126
- // Register required tools
127
82
  if (verbose)
128
83
  console.log('Registering tools...');
129
84
  const toolNameToId = await parser.registerRequiredTools(config, client, verbose, globalToolSourceHashes);
130
- // Create/get all folders with duplicate prevention
131
- if (verbose)
132
- console.log('Processing folders...');
133
- const foldersResponse = await client.listFolders();
134
- const existingFolders = Array.isArray(foldersResponse) ? foldersResponse : foldersResponse.items || [];
135
- for (const agent of config.agents) {
136
- if (options.agent && !agent.name.includes(options.agent))
137
- continue;
138
- if (agent.folders) {
139
- for (const folderConfig of agent.folders) {
140
- if (createdFolders.has(folderConfig.name)) {
141
- if (verbose)
142
- console.log(`Using existing folder: ${folderConfig.name}`);
143
- continue;
144
- }
145
- // Check if folder already exists
146
- let folder = existingFolders.find((f) => f.name === folderConfig.name);
147
- if (!folder) {
148
- if (verbose)
149
- console.log(`Creating folder: ${folderConfig.name}`);
150
- folder = await client.createFolder({
151
- name: folderConfig.name,
152
- embedding: agent.embedding || "letta/letta-free"
153
- });
154
- console.log(`Created folder: ${folderConfig.name}`);
155
- createdFolders.set(folderConfig.name, folder.id);
156
- // Upload files only to newly created folders
157
- if (verbose)
158
- console.log(`Uploading ${folderConfig.files.length} files...`);
159
- for (const filePath of folderConfig.files) {
160
- try {
161
- const resolvedPath = path.resolve(parser.basePath, filePath);
162
- if (!fs.existsSync(resolvedPath)) {
163
- console.warn(`File not found, skipping: ${filePath}`);
164
- continue;
165
- }
166
- if (verbose)
167
- console.log(` Uploading ${filePath}...`);
168
- const fileStream = fs.createReadStream(resolvedPath);
169
- await client.uploadFileToFolder(fileStream, folder.id, path.basename(filePath));
170
- if (verbose)
171
- console.log(` Uploaded: ${filePath}`);
172
- }
173
- catch (error) {
174
- console.error(` Failed to upload ${filePath}:`, error.message);
175
- }
176
- }
177
- }
178
- else {
179
- if (verbose)
180
- console.log(`Using existing folder: ${folderConfig.name}`);
181
- if (verbose)
182
- console.log(' (Skipping file upload - files already exist)');
183
- createdFolders.set(folderConfig.name, folder.id);
184
- }
185
- }
186
- }
187
- }
188
- // Create agents with memory blocks
85
+ // Process folders
86
+ const createdFolders = await (0, apply_helpers_1.processFolders)(config, client, parser, options, verbose);
87
+ // Process agents
88
+ const spinnerEnabled = (0, spinner_1.getSpinnerEnabled)(command);
189
89
  if (verbose)
190
90
  console.log('Processing agents...');
191
91
  for (const agent of config.agents) {
@@ -199,177 +99,68 @@ async function applyCommand(options, command) {
199
99
  console.log(` Folders: ${agent.folders?.length || 0}`);
200
100
  }
201
101
  try {
202
- // Generate file content hashes for change detection
102
+ // Generate hashes for change detection
203
103
  const folderContentHashes = fileTracker.generateFolderFileHashes(agent.folders || []);
204
- // Generate tool source code hashes for change detection
205
104
  const toolSourceHashes = fileTracker.generateToolSourceHashes(agent.tools || [], parser.toolConfigs);
206
- // Generate memory block file content hashes for change detection
207
105
  const memoryBlockFileHashes = await fileTracker.generateMemoryBlockFileHashes(agent.memory_blocks || []);
208
- // Check if agent needs to be created based on complete configuration
209
- const { agentName, shouldCreate, existingAgent } = await agentManager.getOrCreateAgentName(agent.name, {
106
+ // Build agent config
107
+ const agentConfig = {
210
108
  systemPrompt: agent.system_prompt.value || '',
211
109
  tools: agent.tools || [],
212
110
  toolSourceHashes,
213
111
  model: agent.llm_config?.model,
214
112
  embedding: agent.embedding,
215
113
  contextWindow: agent.llm_config?.context_window,
216
- memoryBlocks: (agent.memory_blocks || []).map(block => ({
114
+ memoryBlocks: (agent.memory_blocks || []).map((block) => ({
217
115
  name: block.name,
218
116
  description: block.description,
219
117
  limit: block.limit,
220
118
  value: block.value || ''
221
119
  })),
222
120
  memoryBlockFileHashes,
223
- folders: (agent.folders || []).map(folder => ({
121
+ folders: (agent.folders || []).map((folder) => ({
224
122
  name: folder.name,
225
123
  files: folder.files,
226
124
  fileContentHashes: folderContentHashes.get(folder.name) || {}
227
125
  })),
228
126
  sharedBlocks: agent.shared_blocks || []
229
- }, verbose);
127
+ };
128
+ // Check if agent exists
129
+ const { agentName, shouldCreate, existingAgent } = await agentManager.getOrCreateAgentName(agent.name, agentConfig, verbose);
230
130
  if (!shouldCreate && existingAgent) {
231
- // Agent exists but may need partial updates
232
- const agentConfig = {
233
- systemPrompt: agent.system_prompt.value || '',
234
- tools: agent.tools || [],
235
- toolSourceHashes,
236
- model: agent.llm_config?.model,
237
- embedding: agent.embedding,
238
- contextWindow: agent.llm_config?.context_window,
239
- memoryBlocks: (agent.memory_blocks || []).map(block => ({
240
- name: block.name,
241
- description: block.description,
242
- limit: block.limit,
243
- value: block.value || ''
244
- })),
245
- memoryBlockFileHashes,
246
- folders: (agent.folders || []).map(folder => ({
247
- name: folder.name,
248
- files: folder.files,
249
- fileContentHashes: folderContentHashes.get(folder.name) || {}
250
- })),
251
- sharedBlocks: agent.shared_blocks || []
252
- };
253
- // Check if any granular changes are needed
131
+ // Check if changes needed
254
132
  const changes = agentManager.getConfigChanges(existingAgent, agentConfig);
255
133
  if (!changes.hasChanges) {
256
134
  console.log(`Agent ${agent.name} already exists and is up to date`);
257
135
  continue;
258
136
  }
259
- // Apply partial updates to preserve conversation history
260
- console.log(`Updating agent ${agent.name}:`);
261
- const spinnerEnabled = (0, spinner_1.getSpinnerEnabled)(command);
262
- const spinner = (0, spinner_1.createSpinner)(`Analyzing changes for ${agent.name}...`, spinnerEnabled).start();
263
- try {
264
- const updateOperations = await diffEngine.generateUpdateOperations(existingAgent, { ...agentConfig, memoryBlockFileHashes }, toolNameToId, createdFolders, verbose, sharedBlockIds);
265
- spinner.stop();
266
- // Show granular diff information
267
- output_formatter_1.OutputFormatter.showAgentUpdateDiff(updateOperations);
268
- const updateSpinner = (0, spinner_1.createSpinner)(`Applying updates to ${agent.name}...`, spinnerEnabled).start();
269
- await diffEngine.applyUpdateOperations(existingAgent.id, updateOperations, verbose);
270
- // Update registry with new hashes
271
- agentManager.updateRegistry(existingAgent.name, agentConfig, existingAgent.id);
272
- updateSpinner.succeed(`Agent ${agent.name} updated successfully (conversation history preserved)`);
273
- }
274
- catch (error) {
275
- spinner.fail(`Failed to update agent ${agent.name}`);
276
- throw error;
277
- }
278
- continue;
279
- }
280
- // Collect all block IDs (shared + agent-specific)
281
- const blockIds = [];
282
- // Add shared blocks for this agent
283
- if (agent.shared_blocks) {
284
- for (const sharedBlockName of agent.shared_blocks) {
285
- const sharedBlockId = sharedBlockIds.get(sharedBlockName);
286
- if (sharedBlockId) {
287
- blockIds.push(sharedBlockId);
288
- if (verbose)
289
- console.log(` Using shared block: ${sharedBlockName}`);
290
- }
291
- else {
292
- console.warn(` Shared block not found: ${sharedBlockName}`);
293
- }
294
- }
295
- }
296
- // Create agent-specific memory blocks
297
- if (agent.memory_blocks) {
298
- for (const block of agent.memory_blocks) {
299
- if (verbose)
300
- console.log(` Processing memory block: ${block.name}`);
301
- const blockId = await blockManager.getOrCreateAgentBlock(block, agent.name);
302
- blockIds.push(blockId);
303
- }
304
- }
305
- // Create agent
306
- const creationSpinner = (0, spinner_1.createSpinner)(`Creating agent ${agentName}...`, (0, spinner_1.getSpinnerEnabled)(command)).start();
307
- try {
308
- // Resolve tool names to IDs
309
- const toolIds = [];
310
- if (agent.tools) {
311
- for (const toolName of agent.tools) {
312
- const toolId = toolNameToId.get(toolName);
313
- if (toolId) {
314
- toolIds.push(toolId);
315
- }
316
- else {
317
- console.warn(` Tool not found: ${toolName}`);
318
- }
319
- }
320
- }
321
- const createdAgent = await client.createAgent({
322
- name: agentName,
323
- model: agent.llm_config?.model || "google_ai/gemini-2.5-pro",
324
- embedding: agent.embedding || "letta/letta-free",
325
- system: agent.system_prompt.value || '',
326
- block_ids: blockIds,
327
- context_window_limit: agent.llm_config?.context_window || 64000
137
+ // Update existing agent
138
+ await (0, apply_helpers_1.updateExistingAgent)(agent, existingAgent, agentConfig, {
139
+ diffEngine,
140
+ agentManager,
141
+ toolNameToId,
142
+ createdFolders,
143
+ sharedBlockIds,
144
+ spinnerEnabled,
145
+ verbose
328
146
  });
329
- // Attach tools after agent creation (same as update path)
330
- for (const toolId of toolIds) {
331
- if (verbose)
332
- console.log(` Attaching tool: ${toolId}`);
333
- await client.attachToolToAgent(createdAgent.id, toolId);
334
- }
335
- // Update agent registry with new agent
336
- agentManager.updateRegistry(agentName, {
337
- systemPrompt: agent.system_prompt.value || '',
338
- tools: agent.tools || [],
339
- model: agent.llm_config?.model,
340
- embedding: agent.embedding,
341
- contextWindow: agent.llm_config?.context_window,
342
- memoryBlocks: (agent.memory_blocks || []).map(block => ({
343
- name: block.name,
344
- description: block.description,
345
- limit: block.limit,
346
- value: block.value || ''
347
- })),
348
- folders: agent.folders || [],
349
- sharedBlocks: agent.shared_blocks || []
350
- }, createdAgent.id);
351
- // Attach folders to agent
352
- if (agent.folders) {
353
- for (const folderConfig of agent.folders) {
354
- const folderId = createdFolders.get(folderConfig.name);
355
- if (folderId) {
356
- if (verbose)
357
- console.log(` Attaching folder ${folderConfig.name}`);
358
- await client.attachFolderToAgent(createdAgent.id, folderId);
359
- if (verbose)
360
- console.log(` Folder attached`);
361
- }
362
- }
363
- }
364
- creationSpinner.succeed(`Agent ${agentName} created successfully`);
365
147
  }
366
- catch (error) {
367
- creationSpinner.fail(`Failed to create agent ${agentName}`);
368
- throw error;
148
+ else {
149
+ // Create new agent
150
+ await (0, apply_helpers_1.createNewAgent)(agent, agentName, {
151
+ client,
152
+ blockManager,
153
+ agentManager,
154
+ toolNameToId,
155
+ createdFolders,
156
+ sharedBlockIds,
157
+ spinnerEnabled,
158
+ verbose
159
+ });
369
160
  }
370
161
  }
371
162
  catch (error) {
372
- console.error(`Failed to create agent ${agent.name}:`, error.message);
163
+ console.error(`Failed to process agent ${agent.name}:`, error.message);
373
164
  throw error;
374
165
  }
375
166
  }
@@ -380,111 +171,4 @@ async function applyCommand(options, command) {
380
171
  process.exit(1);
381
172
  }
382
173
  }
383
- /**
384
- * Template mode: apply a template config to all existing agents matching a glob pattern.
385
- * Uses MERGE semantics - adds/updates resources but doesn't remove existing ones.
386
- */
387
- async function applyTemplateMode(options, config, parser, command) {
388
- const verbose = command.parent?.opts().verbose || false;
389
- const spinnerEnabled = (0, spinner_1.getSpinnerEnabled)(command);
390
- const pattern = options.match;
391
- const findSpinner = (0, spinner_1.createSpinner)(`Finding agents matching "${pattern}"...`, spinnerEnabled).start();
392
- // Get all agents from server
393
- const client = new letta_client_1.LettaClientWrapper();
394
- const allAgentsResponse = await client.listAgents();
395
- const allAgents = Array.isArray(allAgentsResponse)
396
- ? allAgentsResponse
397
- : allAgentsResponse.items || [];
398
- // Filter by glob pattern
399
- const matchingAgents = allAgents.filter((agent) => (0, minimatch_1.minimatch)(agent.name, pattern));
400
- if (matchingAgents.length === 0) {
401
- findSpinner.fail(`No agents found matching pattern: ${pattern}`);
402
- return;
403
- }
404
- findSpinner.succeed(`Found ${matchingAgents.length} agents matching "${pattern}"`);
405
- matchingAgents.forEach((a) => console.log(` - ${a.name}`));
406
- if (options.dryRun) {
407
- console.log('\nDry-run mode: no changes will be made');
408
- return;
409
- }
410
- // Initialize managers
411
- const blockManager = new block_manager_1.BlockManager(client);
412
- const diffEngine = new diff_engine_1.DiffEngine(client, blockManager, parser.basePath);
413
- const fileTracker = new file_content_tracker_1.FileContentTracker(parser.basePath, parser.storageBackend);
414
- const createdFolders = new Map();
415
- await blockManager.loadExistingBlocks();
416
- // Process shared blocks from template
417
- const sharedBlockIds = new Map();
418
- if (config.shared_blocks?.length) {
419
- const blockSpinner = (0, spinner_1.createSpinner)('Processing shared blocks...', spinnerEnabled).start();
420
- for (const sharedBlock of config.shared_blocks) {
421
- const blockId = await blockManager.getOrCreateSharedBlock(sharedBlock);
422
- sharedBlockIds.set(sharedBlock.name, blockId);
423
- if (verbose)
424
- console.log(` ${sharedBlock.name} -> ${blockId}`);
425
- }
426
- blockSpinner.succeed(`Processed ${config.shared_blocks.length} shared blocks`);
427
- }
428
- // Get template agent config (first agent in config, or use top-level values)
429
- const templateAgent = config.agents?.[0];
430
- const templateTools = templateAgent?.tools || [];
431
- const templateSharedBlocks = templateAgent?.shared_blocks || [];
432
- // Generate tool source hashes and register tools
433
- const toolSourceHashes = fileTracker.generateToolSourceHashes(templateTools, parser.toolConfigs);
434
- const toolNameToId = await parser.registerRequiredTools(config, client, verbose, toolSourceHashes);
435
- // Apply template to each matching agent
436
- for (const existingAgent of matchingAgents) {
437
- const agentSpinner = (0, spinner_1.createSpinner)(`Analyzing ${existingAgent.name}...`, spinnerEnabled).start();
438
- try {
439
- // Build desired config from template (no memoryBlocks - those are instance-specific)
440
- const desiredConfig = {
441
- systemPrompt: templateAgent?.system_prompt?.value || '',
442
- tools: templateTools,
443
- toolSourceHashes,
444
- sharedBlocks: templateSharedBlocks,
445
- };
446
- // Create minimal AgentVersion for diff engine
447
- const agentVersion = {
448
- id: existingAgent.id,
449
- name: existingAgent.name,
450
- baseName: existingAgent.name,
451
- configHashes: { overall: '', systemPrompt: '', tools: '', model: '', memoryBlocks: '', folders: '', sharedBlocks: '' },
452
- version: 'latest',
453
- lastUpdated: existingAgent.updated_at || new Date().toISOString()
454
- };
455
- // Generate update operations
456
- const ops = await diffEngine.generateUpdateOperations(agentVersion, desiredConfig, toolNameToId, createdFolders, verbose, sharedBlockIds);
457
- // MERGE semantics: clear toRemove arrays (don't remove existing resources)
458
- if (ops.tools)
459
- ops.tools.toRemove = [];
460
- if (ops.blocks)
461
- ops.blocks.toRemove = [];
462
- if (ops.folders)
463
- ops.folders.toDetach = [];
464
- // Recalculate operation count after filtering
465
- ops.operationCount = 0;
466
- if (ops.updateFields)
467
- ops.operationCount += Object.keys(ops.updateFields).length;
468
- if (ops.tools)
469
- ops.operationCount += ops.tools.toAdd.length + ops.tools.toUpdate.length;
470
- if (ops.blocks)
471
- ops.operationCount += ops.blocks.toAdd.length + ops.blocks.toUpdate.length;
472
- if (ops.folders)
473
- ops.operationCount += ops.folders.toAttach.length;
474
- if (ops.operationCount === 0) {
475
- agentSpinner.succeed(`${existingAgent.name}: already up to date`);
476
- continue;
477
- }
478
- agentSpinner.stop();
479
- output_formatter_1.OutputFormatter.showAgentUpdateDiff(ops);
480
- const updateSpinner = (0, spinner_1.createSpinner)(`Applying updates to ${existingAgent.name}...`, spinnerEnabled).start();
481
- await diffEngine.applyUpdateOperations(existingAgent.id, ops, verbose);
482
- updateSpinner.succeed(`${existingAgent.name}: updated successfully`);
483
- }
484
- catch (error) {
485
- agentSpinner.fail(`${existingAgent.name}: ${error.message}`);
486
- }
487
- }
488
- console.log('\nTemplate apply completed');
489
- }
490
174
  //# sourceMappingURL=apply.js.map