@inkeep/agents-cli 0.23.1 → 0.23.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +510 -622
  2. package/package.json +8 -3
package/dist/index.js CHANGED
@@ -207703,8 +207703,8 @@ ${options.prefix}` : "\n" : options.prefix
207703
207703
  )
207704
207704
  };
207705
207705
  }
207706
- isValidGeneratedFileWatcher(generateFile2, watcher) {
207707
- return this.toPath(generateFile2) === watcher.generatedFilePath;
207706
+ isValidGeneratedFileWatcher(generateFile, watcher) {
207707
+ return this.toPath(generateFile) === watcher.generatedFilePath;
207708
207708
  }
207709
207709
  clearGeneratedFileWatch() {
207710
207710
  if (this.generatedFilesMap) {
@@ -234014,7 +234014,12 @@ var init_env2 = __esm({
234014
234014
  // Secrets loaded from .env files (relative to where CLI is executed)
234015
234015
  ANTHROPIC_API_KEY: z12.string().optional(),
234016
234016
  OPENAI_API_KEY: z12.string().optional(),
234017
- GOOGLE_API_KEY: z12.string().optional()
234017
+ GOOGLE_API_KEY: z12.string().optional(),
234018
+ // Langfuse configuration for LLM observability
234019
+ LANGFUSE_SECRET_KEY: z12.string().optional(),
234020
+ LANGFUSE_PUBLIC_KEY: z12.string().optional(),
234021
+ LANGFUSE_BASEURL: z12.string().optional().default("https://cloud.langfuse.com"),
234022
+ LANGFUSE_ENABLED: z12.string().optional().transform((val) => val === "true")
234018
234023
  });
234019
234024
  parseEnv2 = () => {
234020
234025
  try {
@@ -234035,6 +234040,47 @@ ${error.message}`
234035
234040
  }
234036
234041
  });
234037
234042
 
234043
+ // src/instrumentation.ts
234044
+ import { registerOTel } from "@vercel/otel";
234045
+ import { LangfuseExporter } from "langfuse-vercel";
234046
+ function isLangfuseConfigured() {
234047
+ return !!(langfuseEnabled && langfuseSecretKey && langfusePublicKey);
234048
+ }
234049
+ function initializeInstrumentation() {
234050
+ if (!isLangfuseConfigured()) {
234051
+ if (process.env.DEBUG) {
234052
+ console.log("[Langfuse] Tracing disabled - missing configuration");
234053
+ }
234054
+ return;
234055
+ }
234056
+ try {
234057
+ registerOTel({
234058
+ serviceName: "inkeep-agents-cli",
234059
+ traceExporter: new LangfuseExporter({
234060
+ secretKey: langfuseSecretKey,
234061
+ publicKey: langfusePublicKey,
234062
+ baseUrl: process.env.LANGFUSE_BASEURL || "https://cloud.langfuse.com"
234063
+ })
234064
+ });
234065
+ if (process.env.DEBUG) {
234066
+ console.log("[Langfuse] Tracing initialized successfully");
234067
+ }
234068
+ } catch (error) {
234069
+ console.warn("[Langfuse] Failed to initialize tracing:", error);
234070
+ }
234071
+ }
234072
+ var langfuseEnabled, langfuseSecretKey, langfusePublicKey;
234073
+ var init_instrumentation = __esm({
234074
+ "src/instrumentation.ts"() {
234075
+ "use strict";
234076
+ init_esm_shims();
234077
+ langfuseEnabled = process.env.LANGFUSE_ENABLED === "true";
234078
+ langfuseSecretKey = process.env.LANGFUSE_SECRET_KEY;
234079
+ langfusePublicKey = process.env.LANGFUSE_PUBLIC_KEY;
234080
+ initializeInstrumentation();
234081
+ }
234082
+ });
234083
+
234038
234084
  // src/utils/tsx-loader.ts
234039
234085
  import { extname } from "path";
234040
234086
  import { pathToFileURL } from "url";
@@ -234440,8 +234486,10 @@ __export(pull_llm_generate_exports, {
234440
234486
  createModel: () => createModel,
234441
234487
  detectAvailableProvider: () => detectAvailableProvider,
234442
234488
  generateAgentFile: () => generateAgentFile,
234489
+ generateAllFilesInBatch: () => generateAllFilesInBatch,
234443
234490
  generateArtifactComponentFile: () => generateArtifactComponentFile,
234444
234491
  generateDataComponentFile: () => generateDataComponentFile,
234492
+ generateEnvironmentFileTemplate: () => generateEnvironmentFileTemplate,
234445
234493
  generateEnvironmentFiles: () => generateEnvironmentFiles,
234446
234494
  generateIndexFile: () => generateIndexFile,
234447
234495
  generateStatusComponentFile: () => generateStatusComponentFile,
@@ -234578,8 +234626,19 @@ async function generateTextWithPlaceholders(model, data, promptTemplate, options
234578
234626
  model,
234579
234627
  prompt,
234580
234628
  ...options,
234581
- ...reasoningConfig
234629
+ ...reasoningConfig,
234582
234630
  // Merge in reasoning/thinking config if provided
234631
+ // Enable Langfuse telemetry if configured
234632
+ ...isLangfuseConfigured() && {
234633
+ experimental_telemetry: {
234634
+ isEnabled: true,
234635
+ metadata: {
234636
+ fileType: context?.fileType || "unknown",
234637
+ placeholderCount: Object.keys(replacements).length,
234638
+ promptSize: prompt.length
234639
+ }
234640
+ }
234641
+ }
234583
234642
  });
234584
234643
  const restoredText = restorePlaceholders(text2, replacements);
234585
234644
  if (debug && Object.keys(replacements).length > 0) {
@@ -234648,8 +234707,8 @@ Generate ONLY the TypeScript code without any markdown or explanations.`;
234648
234707
  },
234649
234708
  false,
234650
234709
  // debug
234651
- void 0,
234652
- // context
234710
+ { fileType: "index" },
234711
+ // context - for Langfuse metadata
234653
234712
  reasoningConfig
234654
234713
  // reasoning config
234655
234714
  );
@@ -234904,8 +234963,8 @@ Generate ONLY the TypeScript code without any markdown or explanations.`;
234904
234963
  },
234905
234964
  debug,
234906
234965
  // Pass debug flag to show placeholder optimization info
234907
- void 0,
234908
- // context
234966
+ { fileType: "agent" },
234967
+ // context - for Langfuse metadata
234909
234968
  reasoningConfig
234910
234969
  // reasoning config
234911
234970
  );
@@ -235001,8 +235060,8 @@ Generate ONLY the TypeScript code without any markdown or explanations.`;
235001
235060
  },
235002
235061
  false,
235003
235062
  // debug
235004
- void 0,
235005
- // context
235063
+ { fileType: "tool" },
235064
+ // context - for Langfuse metadata
235006
235065
  reasoningConfig
235007
235066
  // reasoning config
235008
235067
  );
@@ -235079,8 +235138,8 @@ Generate ONLY the TypeScript code without any markdown or explanations.`;
235079
235138
  },
235080
235139
  false,
235081
235140
  // debug
235082
- void 0,
235083
- // context
235141
+ { fileType: "data_component" },
235142
+ // context - for Langfuse metadata
235084
235143
  reasoningConfig
235085
235144
  // reasoning config
235086
235145
  );
@@ -235162,8 +235221,8 @@ Generate ONLY the TypeScript code without any markdown or explanations.`;
235162
235221
  },
235163
235222
  false,
235164
235223
  // debug
235165
- void 0,
235166
- // context
235224
+ { fileType: "artifact_component" },
235225
+ // context - for Langfuse metadata
235167
235226
  reasoningConfig
235168
235227
  // reasoning config
235169
235228
  );
@@ -235243,13 +235302,84 @@ Generate ONLY the TypeScript code without any markdown or explanations.`;
235243
235302
  },
235244
235303
  false,
235245
235304
  // debug
235246
- void 0,
235247
- // context
235305
+ { fileType: "status_component" },
235306
+ // context - for Langfuse metadata
235248
235307
  reasoningConfig
235249
235308
  // reasoning config
235250
235309
  );
235251
235310
  writeFileSync3(outputPath, cleanGeneratedCode(text2));
235252
235311
  }
235312
+ function generateEnvironmentFileTemplate(environmentsDir, environment, credentials) {
235313
+ const { writeFileSync: writeFileSync7 } = require3("node:fs");
235314
+ const { join: join14 } = require3("node:path");
235315
+ let credentialsCode = "";
235316
+ const hasCredentials = credentials && Object.keys(credentials).length > 0;
235317
+ if (hasCredentials) {
235318
+ const credentialEntries = [];
235319
+ for (const [credId, cred] of Object.entries(credentials)) {
235320
+ if (credId === "_meta") continue;
235321
+ const varName = credId.replace(/-/g, "_").replace(/[^a-zA-Z0-9_]/g, "");
235322
+ const params = [
235323
+ `id: '${cred.id || credId}'`,
235324
+ `type: '${cred.type || "api_key"}'`,
235325
+ `credentialStoreId: '${cred.credentialStoreId || "environment"}'`
235326
+ ];
235327
+ if (cred.retrievalParams) {
235328
+ params.push(
235329
+ `retrievalParams: ${JSON.stringify(cred.retrievalParams, null, 6).replace(/\n/g, "\n ")}`
235330
+ );
235331
+ }
235332
+ credentialEntries.push(` ${varName}: credential({
235333
+ ${params.join(",\n ")}
235334
+ })`);
235335
+ }
235336
+ credentialsCode = `
235337
+ ${credentialEntries.join(",\n")}
235338
+ `;
235339
+ } else {
235340
+ credentialsCode = "\n ";
235341
+ }
235342
+ const imports = hasCredentials ? "import { credential, registerEnvironmentSettings } from '@inkeep/agents-sdk';" : "import { registerEnvironmentSettings } from '@inkeep/agents-sdk';";
235343
+ const envContent = `${imports}
235344
+
235345
+ export const ${environment} = registerEnvironmentSettings({
235346
+ credentials: {${credentialsCode}}
235347
+ });
235348
+ `;
235349
+ writeFileSync7(join14(environmentsDir, `${environment}.env.ts`), envContent);
235350
+ updateEnvironmentIndexTemplate(environmentsDir, environment);
235351
+ }
235352
+ function updateEnvironmentIndexTemplate(environmentsDir, newEnvironment) {
235353
+ const { writeFileSync: writeFileSync7, existsSync: existsSync12, readFileSync: readFileSync8 } = require3("node:fs");
235354
+ const { join: join14 } = require3("node:path");
235355
+ const indexPath = join14(environmentsDir, "index.ts");
235356
+ const existingEnvironments = [];
235357
+ if (existsSync12(indexPath)) {
235358
+ const existingContent = readFileSync8(indexPath, "utf-8");
235359
+ const importRegex = /import\s+{\s*(\w+)\s*}\s+from\s+['"]\.\/([\w-]+)\.env['"];?/g;
235360
+ let match;
235361
+ while ((match = importRegex.exec(existingContent)) !== null) {
235362
+ const envName = match[2];
235363
+ if (!existingEnvironments.includes(envName)) {
235364
+ existingEnvironments.push(envName);
235365
+ }
235366
+ }
235367
+ }
235368
+ if (!existingEnvironments.includes(newEnvironment)) {
235369
+ existingEnvironments.push(newEnvironment);
235370
+ }
235371
+ existingEnvironments.sort();
235372
+ const importStatements = existingEnvironments.map((env3) => `import { ${env3} } from './${env3}.env';`).join("\n");
235373
+ const environmentObject = existingEnvironments.map((env3) => ` ${env3},`).join("\n");
235374
+ const indexContent = `import { createEnvironmentSettings } from '@inkeep/agents-sdk';
235375
+ ${importStatements}
235376
+
235377
+ export const envSettings = createEnvironmentSettings({
235378
+ ${environmentObject}
235379
+ });
235380
+ `;
235381
+ writeFileSync7(indexPath, indexContent);
235382
+ }
235253
235383
  async function generateEnvironmentFiles(environmentsDir, credentials, environment = "development") {
235254
235384
  const generateCredentialCode = (cred) => {
235255
235385
  const params = [
@@ -235326,6 +235456,268 @@ export { ${exportStatement} };
235326
235456
  `;
235327
235457
  writeFileSync3(indexPath, indexContent);
235328
235458
  }
235459
+ async function generateAllFilesInBatch(fileSpecs, modelSettings, debug = false, reasoningConfig) {
235460
+ if (fileSpecs.length === 0) {
235461
+ return;
235462
+ }
235463
+ const model = createModel(modelSettings);
235464
+ const typeDefinitions = getTypeDefinitions();
235465
+ const sharedInstructions = `
235466
+ ${NAMING_CONVENTION_RULES}
235467
+
235468
+ ${IMPORT_INSTRUCTIONS}
235469
+ `;
235470
+ const filePrompts = fileSpecs.map((spec, index2) => {
235471
+ let fileSpecificInstructions = "";
235472
+ switch (spec.type) {
235473
+ case "index":
235474
+ fileSpecificInstructions = `
235475
+ REQUIREMENTS FOR INDEX FILE:
235476
+ 1. Import the project function from '@inkeep/agents-sdk'
235477
+ 2. The project object should include all required properties and any optional properties (according to the type definitions) that are present in the project data
235478
+
235479
+ PROJECT JSON EXAMPLE:
235480
+ ${PROJECT_JSON_EXAMPLE}
235481
+
235482
+ EXAMPLE OUTPUT:
235483
+ import { project } from '@inkeep/agents-sdk';
235484
+
235485
+ export const myProject = project({
235486
+ id: 'my-project',
235487
+ name: 'My Project',
235488
+ description: 'test test',
235489
+ models: {
235490
+ base: { model: 'gpt-4o-mini' }
235491
+ }
235492
+ });
235493
+ `;
235494
+ break;
235495
+ case "agent":
235496
+ const importMappings = spec.toolFilenames || spec.componentFilenames ? `IMPORT PATH MAPPINGS (CRITICAL - USE EXACT PATHS):
235497
+ ${generateImportMappings(spec.toolFilenames, spec.componentFilenames)}
235498
+
235499
+ !!! WARNING: Entity IDs \u2260 File Paths !!!
235500
+ - Entity IDs may use underscores or different naming
235501
+ - File paths use kebab-case naming convention
235502
+ - ALWAYS use the exact import paths from the mappings above
235503
+ - NEVER use entity IDs directly as import paths
235504
+
235505
+ ` : "";
235506
+ fileSpecificInstructions = `
235507
+ ${importMappings}REQUIREMENTS FOR AGENT FILE:
235508
+ 1. IMPORTS (CRITICAL - USE FILE PATHS, NOT IDs):
235509
+ - For tool/component imports: Use ONLY the exact file paths from IMPORT PATH MAPPINGS above
235510
+ - Import paths are based on actual file names, not entity IDs
235511
+ - Always use kebab-case file paths (../tools/tool-name, not ../tools/tool_name)
235512
+ - ALWAYS import { agent, subAgent } from '@inkeep/agents-sdk'
235513
+ - ALWAYS import { z } from 'zod' when using ANY Zod schemas (responseSchema, headersSchema, etc.)
235514
+ - ALWAYS import { contextConfig, fetchDefinition, headers } from '@inkeep/agents-core' when agent has contextConfig
235515
+ - Import status components from '../status-components/' when needed
235516
+ 2. Define each agent using the agent() function following the type definitions provided above
235517
+ 3. Create the agent using agent() with proper structure
235518
+ - IMPORTANT: If description is null, undefined, or empty string, omit the description field entirely
235519
+ 4. CRITICAL: Template Literals vs Raw Code:
235520
+ - For STRING VALUES: ALWAYS use template literals with backticks: \`string content\`
235521
+ - This includes: prompt, description, query, url, method, body, defaultValue, etc.
235522
+ - This prevents TypeScript syntax errors with apostrophes (user's, don't, etc.)
235523
+ - IMPORTANT: ANY placeholder that starts with < and ends with > MUST be wrapped in template literals (backticks)
235524
+ - For object keys: use quotes only for keys with hyphens ('Content-Type'), omit for simple identifiers (Authorization)
235525
+
235526
+ EXCEPTION - Schema Fields (NO template literals):
235527
+ - headersSchema: z.object({ ... }) (raw Zod code, NOT a string)
235528
+ - responseSchema: z.object({ ... }) (raw Zod code, NOT a string)
235529
+ - These are TypeScript expressions, not string values
235530
+ 5. For contextConfig (CRITICAL):
235531
+ - NEVER use plain objects for contextConfig
235532
+ - ALWAYS use helper functions: headers(), fetchDefinition(), contextConfig()
235533
+ - Create separate const variables for each helper before the agent definition
235534
+ 6. If you are writing zod schemas make them clean. For example if you see z.union([z.string(), z.null()]) write it as z.string().nullable()
235535
+ `;
235536
+ break;
235537
+ case "tool":
235538
+ fileSpecificInstructions = `
235539
+ REQUIREMENTS FOR TOOL FILE:
235540
+ 1. Import mcpTool from '@inkeep/agents-sdk'
235541
+ 2. CRITICAL: Always include serverUrl property (required by SDK) extracted from config.mcp.server.url
235542
+ 3. CRITICAL: Use individual properties supported by mcpTool - do NOT use nested config object
235543
+ 4. Extract configuration properties and map them to mcpTool's expected properties (serverUrl, transport, etc.)
235544
+ 5. CRITICAL: If credentialReferenceId exists in tool data, add it as a credential property using envSettings.getEnvironmentSetting()
235545
+ 6. Convert credentialReferenceId to credential key format by replacing hyphens with underscores for the getEnvironmentSetting() call (e.g., 'inkeep-api-credential' becomes 'inkeep_api_credential')
235546
+ 7. TRANSPORT CONFIG: If config.mcp.transport exists, extract it as a transport property (not nested in config)
235547
+ 8. NO CONFIG OBJECT: mcpTool does not accept a 'config' property - use individual properties only
235548
+ `;
235549
+ break;
235550
+ case "data_component":
235551
+ fileSpecificInstructions = `
235552
+ REQUIREMENTS FOR DATA COMPONENT FILE:
235553
+ 1. Import dataComponent from '@inkeep/agents-sdk'
235554
+ 2. Import z from 'zod' for schema definitions
235555
+ 3. Create the data component using dataComponent()
235556
+ 4. Include all properties from the component data INCLUDING the 'id' property
235557
+ 5. CRITICAL: All imports must be alphabetically sorted to comply with Biome linting
235558
+ 6. If you are writing zod schemas make them clean. For example if you see z.union([z.string(), z.null()]) write it as z.string().nullable()
235559
+ `;
235560
+ break;
235561
+ case "artifact_component":
235562
+ fileSpecificInstructions = `
235563
+ REQUIREMENTS FOR ARTIFACT COMPONENT FILE:
235564
+ 1. Import artifactComponent from '@inkeep/agents-sdk'
235565
+ 2. Import z from 'zod' and preview from '@inkeep/agents-core' for schema definitions
235566
+ 3. Create the artifact component using artifactComponent()
235567
+ 4. Use preview() helper for fields that should be shown in previews
235568
+ 5. Export following naming convention rules (camelCase version of ID)
235569
+ 6. Include the 'id' property to preserve the original component ID
235570
+ 7. CRITICAL: All imports must be alphabetically sorted to comply with Biome linting
235571
+ 8. If you are writing zod schemas make them clean. For example if you see z.union([z.string(), z.null()]) write it as z.string().nullable()
235572
+ `;
235573
+ break;
235574
+ case "status_component":
235575
+ fileSpecificInstructions = `
235576
+ REQUIREMENTS FOR STATUS COMPONENT FILE:
235577
+ 1. Import statusComponent from '@inkeep/agents-sdk'
235578
+ 2. Import z from 'zod' for schema definitions
235579
+ 3. Create the status component using statusComponent()
235580
+ 4. Export following naming convention rules (camelCase version of ID)
235581
+ 5. Use 'type' field as the identifier (like 'tool_summary')
235582
+ 6. CRITICAL: All imports must be alphabetically sorted to comply with Biome linting
235583
+ 7. If you are writing zod schemas make them clean. For example if you see z.union([z.string(), z.null()]) write it as z.string().nullable()
235584
+ 8. The statusComponent() function handles conversion to .config automatically
235585
+ `;
235586
+ break;
235587
+ }
235588
+ return `
235589
+ --- FILE ${index2 + 1} OF ${fileSpecs.length}: ${spec.outputPath} ---
235590
+ FILE TYPE: ${spec.type}
235591
+ FILE ID: ${spec.id}
235592
+
235593
+ DATA FOR THIS FILE:
235594
+ ${JSON.stringify(spec.data, null, 2)}
235595
+
235596
+ ${fileSpecificInstructions}
235597
+
235598
+ Generate ONLY the TypeScript code for this file without any markdown or explanations.
235599
+ --- END FILE ${index2 + 1} ---
235600
+ `;
235601
+ });
235602
+ const combinedPrompt = `You are generating multiple TypeScript files for an Inkeep project in a single batch.
235603
+
235604
+ ${typeDefinitions}
235605
+
235606
+ ${sharedInstructions}
235607
+
235608
+ CRITICAL INSTRUCTIONS:
235609
+ 1. Generate ${fileSpecs.length} separate TypeScript files
235610
+ 2. Each file MUST be wrapped with its exact separator markers
235611
+ 3. Use the format: --- FILE: <output-path> --- for the start marker
235612
+ 4. Use the format: --- END FILE: <output-path> --- for the end marker
235613
+ 5. Between markers, include ONLY the raw TypeScript code (no markdown, no explanations)
235614
+ 6. DO NOT include triple backticks or "typescript" language identifiers
235615
+ 7. The code between markers should be ready to write directly to a .ts file
235616
+
235617
+ FILE SPECIFICATIONS:
235618
+ ${filePrompts.join("\n\n")}
235619
+
235620
+ OUTPUT FORMAT EXAMPLE:
235621
+ --- FILE: /path/to/file1.ts ---
235622
+ import { project } from '@inkeep/agents-sdk';
235623
+
235624
+ export const myProject = project({
235625
+ id: 'example'
235626
+ });
235627
+ --- END FILE: /path/to/file1.ts ---
235628
+
235629
+ --- FILE: /path/to/file2.ts ---
235630
+ import { agent } from '@inkeep/agents-sdk';
235631
+
235632
+ export const myAgent = agent({
235633
+ id: 'example-agent'
235634
+ });
235635
+ --- END FILE: /path/to/file2.ts ---
235636
+
235637
+ Now generate all ${fileSpecs.length} files following this exact format.`;
235638
+ if (debug) {
235639
+ console.log(`
235640
+ [DEBUG] === Starting BATCH generation for ${fileSpecs.length} files ===`);
235641
+ console.log(`[DEBUG] Combined prompt size: ${combinedPrompt.length} characters`);
235642
+ console.log(`[DEBUG] Model: ${modelSettings.model || "default"}`);
235643
+ console.log(`[DEBUG] Files to generate:`);
235644
+ for (const spec of fileSpecs) {
235645
+ console.log(`[DEBUG] - ${spec.type}: ${spec.outputPath}`);
235646
+ }
235647
+ }
235648
+ try {
235649
+ const startTime = Date.now();
235650
+ const { text: text2 } = await generateText({
235651
+ model,
235652
+ prompt: combinedPrompt,
235653
+ temperature: 0.1,
235654
+ maxOutputTokens: 32e3,
235655
+ // Increased for batch generation
235656
+ abortSignal: AbortSignal.timeout(6e5),
235657
+ // 10 minute timeout for batch
235658
+ ...reasoningConfig,
235659
+ // Enable Langfuse telemetry if configured
235660
+ ...isLangfuseConfigured() && {
235661
+ experimental_telemetry: {
235662
+ isEnabled: true,
235663
+ metadata: {
235664
+ batchGeneration: true,
235665
+ fileCount: fileSpecs.length,
235666
+ fileTypes: fileSpecs.map((s2) => s2.type).join(","),
235667
+ promptSize: combinedPrompt.length
235668
+ }
235669
+ }
235670
+ }
235671
+ });
235672
+ const duration = Date.now() - startTime;
235673
+ if (debug) {
235674
+ console.log(`[DEBUG] LLM response received in ${duration}ms`);
235675
+ console.log(`[DEBUG] Response length: ${text2.length} characters`);
235676
+ console.log(`[DEBUG] Parsing individual files from response...`);
235677
+ }
235678
+ const extractedFiles = parseMultiFileResponse(text2, fileSpecs);
235679
+ if (debug) {
235680
+ console.log(`[DEBUG] Successfully extracted ${extractedFiles.length} files`);
235681
+ }
235682
+ for (const { path: path4, content } of extractedFiles) {
235683
+ const cleanedContent = cleanGeneratedCode(content);
235684
+ writeFileSync3(path4, cleanedContent);
235685
+ if (debug) {
235686
+ console.log(`[DEBUG] Wrote file: ${path4} (${cleanedContent.length} chars)`);
235687
+ }
235688
+ }
235689
+ if (debug) {
235690
+ console.log(`[DEBUG] === Completed BATCH generation ===
235691
+ `);
235692
+ }
235693
+ } catch (error) {
235694
+ if (debug) {
235695
+ console.error(`[DEBUG] === ERROR in batch generation ===`);
235696
+ console.error(`[DEBUG] Error name: ${error.name}`);
235697
+ console.error(`[DEBUG] Error message: ${error.message}`);
235698
+ console.error(`[DEBUG] Full error:`, error);
235699
+ }
235700
+ throw error;
235701
+ }
235702
+ }
235703
+ function parseMultiFileResponse(response, fileSpecs) {
235704
+ const results = [];
235705
+ for (const spec of fileSpecs) {
235706
+ const startMarker = `--- FILE: ${spec.outputPath} ---`;
235707
+ const endMarker = `--- END FILE: ${spec.outputPath} ---`;
235708
+ const startIndex = response.indexOf(startMarker);
235709
+ const endIndex = response.indexOf(endMarker);
235710
+ if (startIndex === -1 || endIndex === -1) {
235711
+ throw new Error(`Failed to find file markers for ${spec.outputPath}`);
235712
+ }
235713
+ const content = response.substring(startIndex + startMarker.length, endIndex).trim();
235714
+ results.push({
235715
+ path: spec.outputPath,
235716
+ content
235717
+ });
235718
+ }
235719
+ return results;
235720
+ }
235329
235721
  async function generateTypeScriptFileWithLLM(agentData, agentId, outputFilePath, modelSettings, retryContext) {
235330
235722
  const fs5 = await import("fs");
235331
235723
  let existingContent = "";
@@ -235469,6 +235861,7 @@ var init_pull_llm_generate = __esm({
235469
235861
  init_esm_shims();
235470
235862
  init_src();
235471
235863
  init_env2();
235864
+ init_instrumentation();
235472
235865
  init_pull_placeholder_system();
235473
235866
  require3 = createRequire2(import.meta.url);
235474
235867
  PROJECT_JSON_EXAMPLE = `
@@ -235671,6 +236064,7 @@ __export(variable_name_registry_exports, {
235671
236064
  });
235672
236065
  function collectAllEntities(projectData) {
235673
236066
  const entities = [];
236067
+ entities.push({ id: projectData.id, type: "project", data: projectData });
235674
236068
  if (projectData.agents) {
235675
236069
  for (const [agentId, agentData] of Object.entries(projectData.agents)) {
235676
236070
  entities.push({ id: agentId, type: "agent", data: agentData });
@@ -235771,8 +236165,9 @@ var init_variable_name_registry = __esm({
235771
236165
  "use strict";
235772
236166
  init_esm_shims();
235773
236167
  DEFAULT_NAMING_CONVENTIONS = {
235774
- subAgentSuffix: "SubAgent",
236168
+ projectSuffix: "Project",
235775
236169
  agentSuffix: "Agent",
236170
+ subAgentSuffix: "SubAgent",
235776
236171
  toolSuffix: null,
235777
236172
  // Usually no suffix needed
235778
236173
  dataComponentSuffix: null,
@@ -235788,6 +236183,7 @@ var init_variable_name_registry = __esm({
235788
236183
  conflicts;
235789
236184
  constructor(conventions = DEFAULT_NAMING_CONVENTIONS) {
235790
236185
  this.registry = {
236186
+ projects: /* @__PURE__ */ new Map(),
235791
236187
  agents: /* @__PURE__ */ new Map(),
235792
236188
  subAgents: /* @__PURE__ */ new Map(),
235793
236189
  tools: /* @__PURE__ */ new Map(),
@@ -235925,6 +236321,8 @@ var init_variable_name_registry = __esm({
235925
236321
  */
235926
236322
  getSuffixForType(entityType) {
235927
236323
  switch (entityType) {
236324
+ case "project":
236325
+ return this.conventions.projectSuffix;
235928
236326
  case "agent":
235929
236327
  return this.conventions.agentSuffix;
235930
236328
  case "subAgent":
@@ -235950,6 +236348,8 @@ var init_variable_name_registry = __esm({
235950
236348
  */
235951
236349
  getRegistryMap(entityType) {
235952
236350
  switch (entityType) {
236351
+ case "project":
236352
+ return this.registry.projects;
235953
236353
  case "agent":
235954
236354
  return this.registry.agents;
235955
236355
  case "subAgent":
@@ -236708,6 +237108,7 @@ Generate ONLY the JSON response, no markdown or explanations.`;
236708
237108
  function formatVariableMappings(registry2, allEntities) {
236709
237109
  let result = "";
236710
237110
  const byType = {
237111
+ project: [],
236711
237112
  agent: [],
236712
237113
  subAgent: [],
236713
237114
  tool: [],
@@ -236740,12 +237141,15 @@ ${type.toUpperCase()}S:
236740
237141
  function formatFileNameMappings(fileNameMappings, allEntities) {
236741
237142
  let result = "";
236742
237143
  const byType = {
237144
+ project: [],
236743
237145
  agent: [],
237146
+ subAgent: [],
236744
237147
  tool: [],
236745
237148
  dataComponent: [],
236746
237149
  artifactComponent: [],
236747
237150
  statusComponent: [],
236748
- credential: []
237151
+ credential: [],
237152
+ environment: []
236749
237153
  };
236750
237154
  for (const entity of allEntities) {
236751
237155
  const fileName = fileNameMappings.get(entity.id);
@@ -236778,6 +237182,8 @@ ${type.toUpperCase()}S:
236778
237182
  }
236779
237183
  function getRegistryMap(registry2, entityType) {
236780
237184
  switch (entityType) {
237185
+ case "project":
237186
+ return registry2.projects;
236781
237187
  case "agent":
236782
237188
  return registry2.agents;
236783
237189
  case "subAgent":
@@ -236792,6 +237198,8 @@ function getRegistryMap(registry2, entityType) {
236792
237198
  return registry2.statusComponents;
236793
237199
  case "credential":
236794
237200
  return registry2.credentials;
237201
+ case "environment":
237202
+ return registry2.environments;
236795
237203
  default:
236796
237204
  throw new Error(`Unknown entity type: ${entityType}`);
236797
237205
  }
@@ -236916,74 +237324,82 @@ import { writeFileSync as writeFileSync4 } from "fs";
236916
237324
  async function generateFilesFromPlan(plan, projectData, dirs, modelSettings, debug = false, reasoningConfig) {
236917
237325
  const startTime = Date.now();
236918
237326
  if (debug) {
236919
- console.log(`[DEBUG] Starting parallel generation of ${plan.files.length} files...`);
237327
+ console.log(`[DEBUG] Starting batch generation of ${plan.files.length} files...`);
237328
+ }
237329
+ const environmentFiles = plan.files.filter((f) => f.type === "environment");
237330
+ const regularFiles = plan.files.filter((f) => f.type !== "environment");
237331
+ if (debug && regularFiles.length > 0) {
237332
+ console.log(`[DEBUG] Batching ${regularFiles.length} regular files into single LLM request...`);
237333
+ }
237334
+ const fileSpecs = regularFiles.map((fileInfo) => {
237335
+ const outputPath = `${dirs.projectRoot}/${fileInfo.path}`;
237336
+ const fileData = extractDataForFile(fileInfo, projectData);
237337
+ let batchType;
237338
+ switch (fileInfo.type) {
237339
+ case "index":
237340
+ batchType = "index";
237341
+ break;
237342
+ case "agent":
237343
+ batchType = "agent";
237344
+ break;
237345
+ case "tool":
237346
+ batchType = "tool";
237347
+ break;
237348
+ case "dataComponent":
237349
+ batchType = "data_component";
237350
+ break;
237351
+ case "artifactComponent":
237352
+ batchType = "artifact_component";
237353
+ break;
237354
+ case "statusComponent":
237355
+ batchType = "status_component";
237356
+ break;
237357
+ default:
237358
+ throw new Error(`Unknown file type for batch generation: ${fileInfo.type}`);
237359
+ }
237360
+ const entityId = fileInfo.entities[0]?.id || fileInfo.path;
237361
+ return {
237362
+ type: batchType,
237363
+ id: entityId,
237364
+ data: fileData,
237365
+ outputPath,
237366
+ toolFilenames: void 0,
237367
+ // TODO: Extract from plan if needed
237368
+ componentFilenames: void 0
237369
+ // TODO: Extract from plan if needed
237370
+ };
237371
+ });
237372
+ if (fileSpecs.length > 0) {
237373
+ await generateAllFilesInBatch(fileSpecs, modelSettings, debug, reasoningConfig);
237374
+ }
237375
+ if (debug && environmentFiles.length > 0) {
237376
+ console.log(`[DEBUG] Generating ${environmentFiles.length} environment files using templates (no LLM)...`);
237377
+ }
237378
+ for (const envFile of environmentFiles) {
237379
+ const envStartTime = Date.now();
237380
+ const outputPath = `${dirs.projectRoot}/${envFile.path}`;
237381
+ const fileData = extractDataForFile(envFile, projectData);
237382
+ const fileName = envFile.path.split("/").pop() || "";
237383
+ if (fileName === "index.ts") {
237384
+ continue;
237385
+ }
237386
+ const envName = fileName.replace(".env.ts", "");
237387
+ if (debug) {
237388
+ console.log(`[DEBUG] \u25B6 Generating ${envFile.path} using template...`);
237389
+ }
237390
+ generateEnvironmentFileTemplate(dirs.environmentsDir, envName, fileData);
237391
+ const envDuration = ((Date.now() - envStartTime) / 1e3).toFixed(1);
237392
+ if (debug) {
237393
+ console.log(`[DEBUG] \u2713 Completed ${envFile.path} (template, ${envDuration}s)`);
237394
+ }
236920
237395
  }
236921
- const tasks2 = plan.files.map(
236922
- (fileInfo, index2) => generateFile(fileInfo, projectData, plan, dirs, modelSettings, debug, reasoningConfig).then(() => {
236923
- if (debug) {
236924
- const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
236925
- console.log(
236926
- `[DEBUG] \u2713 Completed ${index2 + 1}/${plan.files.length}: ${fileInfo.path} (${elapsed}s elapsed)`
236927
- );
236928
- }
236929
- })
236930
- );
236931
- await Promise.all(tasks2);
236932
237396
  const totalTime = ((Date.now() - startTime) / 1e3).toFixed(1);
236933
237397
  if (debug) {
236934
237398
  console.log(
236935
- `[DEBUG] All files generated in ${totalTime}s (${plan.files.length} files in parallel)`
237399
+ `[DEBUG] All files generated in ${totalTime}s (${regularFiles.length} in batch, ${environmentFiles.length} via templates)`
236936
237400
  );
236937
237401
  }
236938
237402
  }
236939
- async function generateFile(fileInfo, projectData, plan, dirs, modelSettings, debug, reasoningConfig) {
236940
- const fileStartTime = Date.now();
236941
- const model = createModel(modelSettings);
236942
- const outputPath = `${dirs.projectRoot}/${fileInfo.path}`;
236943
- const fileData = extractDataForFile(fileInfo, projectData);
236944
- const exampleCode = findExampleCode(fileInfo, plan.patterns);
236945
- const context = {
236946
- plan,
236947
- patterns: plan.patterns,
236948
- fileInfo,
236949
- exampleCode
236950
- };
236951
- const registryInfo = formatRegistryForFile(fileInfo, plan.variableRegistry);
236952
- const promptTemplate = createPromptForFile(fileInfo, fileData, context, registryInfo);
236953
- if (debug) {
236954
- console.log(`[DEBUG] \u25B6 Starting: ${fileInfo.path} (${fileInfo.type})`);
236955
- }
236956
- try {
236957
- const llmStartTime = Date.now();
236958
- const text2 = await generateTextWithPlaceholders(
236959
- model,
236960
- fileData,
236961
- promptTemplate,
236962
- {
236963
- temperature: 0.1,
236964
- maxOutputTokens: fileInfo.type === "agent" ? 16e3 : 4e3,
236965
- abortSignal: AbortSignal.timeout(fileInfo.type === "agent" ? 3e5 : 9e4)
236966
- // Increased for reasoning (5 min for agents, 90s for others)
236967
- },
236968
- debug,
236969
- { fileType: fileInfo.type },
236970
- reasoningConfig
236971
- // Pass reasoning config
236972
- );
236973
- const llmDuration = ((Date.now() - llmStartTime) / 1e3).toFixed(1);
236974
- const cleanedCode = cleanGeneratedCode(text2);
236975
- writeFileSync4(outputPath, cleanedCode);
236976
- const totalDuration = ((Date.now() - fileStartTime) / 1e3).toFixed(1);
236977
- if (debug) {
236978
- console.log(
236979
- `[DEBUG] \u2713 Completed: ${fileInfo.path} (LLM: ${llmDuration}s, Total: ${totalDuration}s)`
236980
- );
236981
- }
236982
- } catch (error) {
236983
- console.error(`[ERROR] Failed to generate ${fileInfo.path}:`, error.message);
236984
- throw error;
236985
- }
236986
- }
236987
237403
  function extractDataForFile(fileInfo, projectData) {
236988
237404
  switch (fileInfo.type) {
236989
237405
  case "index":
@@ -236991,7 +237407,8 @@ function extractDataForFile(fileInfo, projectData) {
236991
237407
  case "agent": {
236992
237408
  const agentId = fileInfo.entities.find((e2) => e2.entityType === "agent")?.id;
236993
237409
  if (agentId && projectData.agents) {
236994
- return projectData.agents[agentId];
237410
+ const agentData = projectData.agents[agentId];
237411
+ return ensureUniqueSubAgentKeys(agentData);
236995
237412
  }
236996
237413
  return {};
236997
237414
  }
@@ -237177,551 +237594,21 @@ function extractDataForFile(fileInfo, projectData) {
237177
237594
  return {};
237178
237595
  }
237179
237596
  }
237180
- function findExampleCode(fileInfo, patterns) {
237181
- return void 0;
237182
- }
237183
- function createPromptForFile(fileInfo, fileData, context, registryInfo) {
237184
- const commonInstructions = `
237185
- ${getTypeDefinitions()}
237186
-
237187
- DETECTED PATTERNS (FOLLOW THESE):
237188
- - File Structure: ${context.patterns.fileStructure.toolsLocation} tools
237189
- - File Naming: ${context.patterns.fileStructure.preferredFileNaming}
237190
- - Export Style: ${context.patterns.codeStyle.exportNaming}
237191
- - Multi-line strings: ${context.patterns.codeStyle.multiLineStrings}
237192
-
237193
- ${context.exampleCode ? `EXAMPLE CODE (your existing style):
237194
- ${context.exampleCode}
237195
- ` : ""}
237196
-
237197
- VARIABLE NAME REGISTRY (MUST USE EXACT NAMES):
237198
- ${registryInfo}
237199
-
237200
- ${NAMING_CONVENTION_RULES}
237201
-
237202
- ${IMPORT_INSTRUCTIONS}
237203
-
237204
- CRITICAL RULES:
237205
- 1. Use EXACT variable names from the registry above - DO NOT modify or "improve" them
237206
- 2. Copy the exact import statements provided in the registry
237207
- 3. The 'id' field in objects keeps the original value
237208
- 4. Variable names must be unique (no conflicts across types)
237209
- 5. Follow detected patterns for code style
237210
- 6. Match existing formatting and conventions
237211
- 7. NEVER generate your own variable names - only use what's provided
237212
- `;
237213
- switch (fileInfo.type) {
237214
- case "index":
237215
- return createIndexPrompt(fileData, context, registryInfo, commonInstructions);
237216
- case "agent":
237217
- return createAgentPrompt(fileData, context, registryInfo, commonInstructions);
237218
- case "tool":
237219
- return createToolPrompt(fileData, context, registryInfo, commonInstructions);
237220
- case "dataComponent":
237221
- return createDataComponentPrompt(fileData, context, registryInfo, commonInstructions);
237222
- case "artifactComponent":
237223
- return createArtifactComponentPrompt(fileData, context, registryInfo, commonInstructions);
237224
- case "statusComponent":
237225
- return createStatusComponentPrompt(fileData, context, registryInfo, commonInstructions);
237226
- case "environment":
237227
- return createEnvironmentPrompt(fileData, context, registryInfo, commonInstructions);
237228
- default:
237229
- throw new Error(`Unknown file type: ${fileInfo.type}`);
237230
- }
237231
- }
237232
- function createIndexPrompt(_projectData, context, _registryInfo, commonInstructions) {
237233
- const importMappings = generateImportMappings2(context.plan);
237234
- return `Generate index.ts for Inkeep project.
237235
-
237236
- PROJECT DATA:
237237
- {{DATA}}
237238
-
237239
- IMPORT MAPPINGS (MUST USE THESE):
237240
- ${importMappings}
237241
-
237242
- ${commonInstructions}
237243
-
237244
- EXAMPLE:
237245
- import { project } from '@inkeep/agents-sdk';
237246
- import { weatherAgent } from './agents/weather-agent';
237247
- import { weatherApi } from './tools/weather-api';
237248
-
237249
- export const myProject = project({
237250
- id: 'my-weather-project',
237251
- name: 'Weather Project',
237252
- models: { base: { model: 'openai/gpt-4o-mini' } },
237253
- agents: () => [weatherAgent],
237254
- tools: () => [weatherApi]
237255
- });
237256
-
237257
- Generate ONLY the TypeScript code without markdown.`;
237258
- }
237259
- function createAgentPrompt(_agentData, context, _registryInfo, commonInstructions) {
237260
- const inlineTools = context.fileInfo.inlineContent || [];
237261
- const hasInlineTools = inlineTools.length > 0;
237262
- return `Generate TypeScript file for Inkeep agent.
237263
-
237264
- AGENT DATA:
237265
- {{DATA}}
237266
-
237267
- ${commonInstructions}
237268
-
237269
- INLINE CONTENT:
237270
- ${hasInlineTools ? `This file should define these tools inline:
237271
- ${inlineTools.map((e2) => `- ${e2.variableName} (${e2.entityType})`).join("\n")}` : "No inline content - import all dependencies"}
237272
-
237273
- ${hasInlineTools ? `
237274
- FUNCTION TOOL API (CRITICAL):
237275
- functionTool({
237276
- name: 'tool-name', // Use 'name' NOT 'id'
237277
- description: 'Tool description',
237278
- inputSchema: {
237279
- type: 'object',
237280
- properties: { ... },
237281
- required: [...]
237282
- },
237283
- execute: async (params: { ... }) => { // Use 'execute' function NOT 'executeCode' string
237284
- // Implementation here
237285
- return { ... };
237286
- }
237287
- })
237288
-
237289
- EXAMPLE:
237290
- const calculateBMI = functionTool({
237291
- name: 'calculate-bmi',
237292
- description: 'Calculates BMI',
237293
- inputSchema: {
237294
- type: 'object',
237295
- properties: {
237296
- weight: { type: 'number', description: 'Weight in kg' },
237297
- height: { type: 'number', description: 'Height in meters' }
237298
- },
237299
- required: ['weight', 'height']
237300
- },
237301
- execute: async (params: { weight: number; height: number }) => {
237302
- try {
237303
- const bmi = params.weight / (params.height * params.height);
237304
- return { bmi: Math.round(bmi * 10) / 10 };
237305
- } catch (error: any) { // Type catch parameter as 'any' for TypeScript
237306
- throw new Error(\`BMI calculation failed: \${error.message}\`);
237307
- }
237308
- }
237309
- });
237310
- ` : ""}
237311
-
237312
- IMPORTS (CRITICAL - MUST BE FIRST):
237313
- ALWAYS import these at the TOP of the file:
237314
- - import { agent, subAgent, functionTool } from '@inkeep/agents-sdk';
237315
- - import { z } from 'zod'; (REQUIRED when using ANY Zod schemas like responseSchema, headersSchema)
237316
- - import { contextConfig, fetchDefinition, headers } from '@inkeep/agents-core'; (REQUIRED when agent has contextConfig)
237317
- - import status components from '../status-components/' when needed
237318
-
237319
- SUBAGENT AND AGENT API (CRITICAL):
237320
- - Use 'canUse' (NOT 'tools') - must be a FUNCTION returning array
237321
- - Use 'canDelegateTo' - must be a FUNCTION returning array
237322
- - Use 'dataComponents' - must be a FUNCTION returning array
237323
- - Use 'subAgents' in agent() - must be a FUNCTION returning array
237324
-
237325
- CONTEXT CONFIG (CRITICAL - NO PLAIN OBJECTS):
237326
- - NEVER use plain objects for contextConfig
237327
- - ALWAYS use helper functions: headers(), fetchDefinition(), contextConfig()
237328
- - Create separate const variables for each helper before the agent definition
237329
- - Pattern:
237330
- const myHeaders = headers({ schema: z.object({ api_key: z.string() }) });
237331
- const myFetch = fetchDefinition({ id: '...', fetchConfig: {...}, responseSchema: z.object({...}) });
237332
- const myContext = contextConfig({ headers: myHeaders, contextVariables: { data: myFetch } });
237333
- export const myAgent = agent({ contextConfig: myContext });
237334
- - Use myHeaders.toTemplate('key_name') for header values in fetchConfig
237335
- - Use myContext.toTemplate('variable.field') for prompt interpolation
237336
-
237337
- FETCHDEFINITION STRUCTURE (CRITICAL - COPY EXACT FORMAT):
237338
- - ALWAYS wrap HTTP config in 'fetchConfig' object
237339
- - COPY the exact authorization format from source data (don't modify headers)
237340
- - Use .nullable() instead of z.union([type, z.null()]) for schemas
237341
- - responseSchema is raw Zod code (NO backticks around it)
237342
- - String values use template literals (backticks for strings)
237343
-
237344
- CORRECT fetchDefinition structure:
237345
- fetchDefinition({
237346
- id: 'fetch-id',
237347
- name: 'Fetch Name',
237348
- trigger: 'initialization',
237349
- fetchConfig: {
237350
- url: 'api-endpoint-url',
237351
- method: 'POST',
237352
- headers: {
237353
- 'Content-Type': 'application/json',
237354
- Authorization: headersVar.toTemplate('key') // COPY EXACT format from source
237355
- },
237356
- body: { query: 'request-body' }
237357
- },
237358
- responseSchema: z.object({
237359
- field: z.string().nullable()
237360
- }),
237361
- transform: 'data.path',
237362
- defaultValue: 'fallback'
237363
- })
237364
-
237365
- STRING LITERALS (CRITICAL - MUST FOLLOW):
237366
- - For STRING VALUES: ALWAYS use template literals (backticks \`)
237367
- - This includes: prompt, description, query, url, method, body, defaultValue, etc.
237368
- - Template literals prevent syntax errors with apostrophes (don't, user's, it's)
237369
- - For object keys that are identifiers (no hyphens), omit quotes: Authorization not 'Authorization'
237370
- - For object keys with hyphens, use quotes: 'Content-Type'
237371
-
237372
- EXCEPTION - Schema Fields (NO template literals):
237373
- - headersSchema: z.object({ ... }) (raw Zod code, NOT a string)
237374
- - responseSchema: z.object({ ... }) (raw Zod code, NOT a string)
237375
- - These are TypeScript expressions, not string values
237376
-
237377
- CORRECT EXAMPLES:
237378
- \u2705 prompt: \`You are a helpful assistant.\` (string value)
237379
- \u2705 query: \`query GetData { field }\` (string value)
237380
- \u2705 responseSchema: z.object({ name: z.string() }) (Zod code, NO backticks)
237381
- \u2705 headersSchema: z.object({ 'inkeep_api_key': z.string() }) (Zod code, NO backticks)
237382
-
237383
- WRONG EXAMPLES:
237384
- \u274C prompt: 'You are a helpful assistant.' (use backticks not single quotes)
237385
- \u274C responseSchema: \`z.object({ name: z.string() })\` (don't wrap Zod in backticks)
237386
-
237387
- STATUS COMPONENTS (CRITICAL):
237388
- - Status components are ALWAYS imported from '../status-components/' directory
237389
- - In statusUpdates.statusComponents array, use statusComponent.config to get the config object
237390
- - NEVER inline status component definitions in the agent file
237391
- - Example: import { toolSummary } from '../status-components/tool-summary'
237392
- - Then use: statusComponents: [toolSummary.config]
237393
-
237394
- \u2705 CORRECT:
237395
- import { toolSummary } from '../status-components/tool-summary';
237396
-
237397
- const weatherSubAgent = subAgent({
237398
- id: 'weather',
237399
- name: 'Weather Sub',
237400
- description: '...',
237401
- prompt: \`You are a helpful assistant.
237402
- When users ask about weather, use your tools.
237403
- Always be clear and concise.\`, // Template literal for multi-line
237404
- canUse: () => [tool1, tool2], // FUNCTION returning array
237405
- canDelegateTo: () => [otherAgent], // FUNCTION returning array
237406
- dataComponents: () => [component1] // FUNCTION returning array
237407
- });
237408
-
237409
- const weatherAgent = agent({
237410
- id: 'weather',
237411
- name: 'Weather Agent',
237412
- defaultSubAgent: weatherSubAgent,
237413
- subAgents: () => [weatherSubAgent], // FUNCTION returning array
237414
- statusUpdates: {
237415
- numEvents: 1,
237416
- timeInSeconds: 1,
237417
- statusComponents: [toolSummary.config] // Use .config
237418
- }
237419
- });
237420
-
237421
- \u274C WRONG:
237422
- prompt: 'Multi-line
237423
- string', // NO - use backticks for multi-line
237424
- tools: [tool1, tool2], // NO - use 'canUse' not 'tools'
237425
- canUse: [tool1, tool2], // NO - must be a function
237426
- subAgents: [weatherSubAgent], // NO - must be a function
237427
- statusComponents: [{ type: '...', ... }], // NO - import from files
237428
-
237429
- Generate ONLY the TypeScript code without markdown.`;
237430
- }
237431
- function createToolPrompt(_toolData, _context, _registryInfo, commonInstructions) {
237432
- return `Generate TypeScript file for Inkeep tool.
237433
-
237434
- TOOL DATA:
237435
- {{DATA}}
237436
-
237437
- ${commonInstructions}
237438
-
237439
- REQUIREMENTS:
237440
- 1. Import mcpTool or functionTool from '@inkeep/agents-sdk'
237441
- 2. Use exact variable name from registry
237442
- 3. Include serverUrl property if MCP tool
237443
- 4. For tools with credentials, import envSettings and use envSettings.getEnvironmentSetting('credential_key')
237444
- 5. CRITICAL: Transport must be an OBJECT format: transport: { type: 'streamable_http' } NOT a string
237445
-
237446
- CREDENTIAL HANDLING (CRITICAL):
237447
- If the tool data includes credential information, you MUST:
237448
- 1. Import { envSettings } from '../environments'
237449
- 2. Use credential: envSettings.getEnvironmentSetting('credential_key') in the tool definition
237450
- 3. Convert credential IDs to underscore format (e.g., 'linear-api' -> 'linear_api')
237451
-
237452
- Example for tool with credential:
237453
- \`\`\`typescript
237454
- import { mcpTool } from '@inkeep/agents-sdk';
237455
- import { envSettings } from '../environments';
237456
-
237457
- export const toolName = mcpTool({
237458
- id: 'tool-id',
237459
- name: 'Tool Name',
237460
- serverUrl: 'https://example.com/mcp',
237461
- credential: envSettings.getEnvironmentSetting('linear_api'), // underscore format
237462
- transport: { type: 'streamable_http' }
237463
- });
237464
- \`\`\`
237465
-
237466
- Generate ONLY the TypeScript code without markdown.`;
237467
- }
237468
- function createDataComponentPrompt(_componentData, _context, _registryInfo, commonInstructions) {
237469
- return `Generate TypeScript file for Inkeep data component.
237470
-
237471
- COMPONENT DATA:
237472
- {{DATA}}
237473
-
237474
- ${commonInstructions}
237475
-
237476
- DATA COMPONENT API (CRITICAL):
237477
- dataComponent({
237478
- id: 'component-id',
237479
- name: 'ComponentName',
237480
- description: 'Component description',
237481
- props: z.object({
237482
- fieldName: z.string().describe('Field description'),
237483
- optionalField: z.number().optional().describe('Optional field description'),
237484
- })
237485
- })
237486
-
237487
- REQUIREMENTS:
237488
- 1. Import dataComponent from '@inkeep/agents-sdk'
237489
- 2. Import z from 'zod' for schema definitions
237490
- 3. Use exact variable name from registry
237491
- 4. Use 'props' property with Zod schema (NOT JSON Schema)
237492
- 5. Include 'id', 'name', and 'description' properties
237493
- 6. Use .describe() for field descriptions
237494
- 7. Use .optional() for optional fields
237495
- 8. Use .nullable() for nullable fields (not z.union([z.string(), z.null()]))
237496
- 9. CRITICAL: All imports must be alphabetically sorted to comply with Biome linting
237497
-
237498
- EXAMPLE:
237499
- import { dataComponent } from '@inkeep/agents-sdk';
237500
- import { z } from 'zod';
237501
-
237502
- export const weatherForecast = dataComponent({
237503
- id: 'weather-forecast',
237504
- name: 'WeatherForecast',
237505
- description: 'Hourly weather forecast',
237506
- props: z.object({
237507
- forecast: z.array(z.object({
237508
- time: z.string().describe('The time of current item E.g. 12PM, 1PM'),
237509
- temperature: z.number().describe('Temperature at given time in Fahrenheit'),
237510
- code: z.number().describe('Weather code at given time'),
237511
- })).describe('The hourly forecast for the weather at a given location'),
237512
- }),
237513
- });
237514
-
237515
- EXAMPLE WITH OPTIONAL FIELDS:
237516
- import { dataComponent } from '@inkeep/agents-sdk';
237517
- import { z } from 'zod';
237518
-
237519
- export const userProfile = dataComponent({
237520
- id: 'user-profile',
237521
- name: 'User Profile',
237522
- description: 'User profile information',
237523
- props: z.object({
237524
- userId: z.string().describe('Unique user identifier'),
237525
- name: z.string().describe('User full name'),
237526
- email: z.string().email().describe('User email address'),
237527
- preferences: z.object({
237528
- theme: z.enum(['light', 'dark']),
237529
- notifications: z.boolean(),
237530
- }).optional().describe('User preferences'),
237531
- }),
237532
- });
237533
-
237534
- Generate ONLY the TypeScript code without markdown.`;
237535
- }
237536
- function createArtifactComponentPrompt(_componentData, _context, _registryInfo, commonInstructions) {
237537
- return `Generate TypeScript file for Inkeep artifact component.
237538
-
237539
- COMPONENT DATA:
237540
- {{DATA}}
237541
-
237542
- ${commonInstructions}
237543
-
237544
- REQUIREMENTS:
237545
- 1. Import artifactComponent from '@inkeep/agents-sdk'
237546
- 2. Import z from 'zod' and preview from '@inkeep/agents-core'
237547
- 3. Use exact variable name from registry
237548
- 4. Use preview() for fields shown in previews
237549
- 5. Include 'id' property
237550
-
237551
- Generate ONLY the TypeScript code without markdown.`;
237552
- }
237553
- function createStatusComponentPrompt(_componentData, _context, _registryInfo, commonInstructions) {
237554
- return `Generate TypeScript file for Inkeep status component.
237555
-
237556
- COMPONENT DATA:
237557
- {{DATA}}
237558
-
237559
- ${commonInstructions}
237560
-
237561
- REQUIREMENTS:
237562
- 1. Import statusComponent from '@inkeep/agents-sdk'
237563
- 2. Import z from 'zod' for schema definitions if detailsSchema is present
237564
- 3. Use exact variable name from registry
237565
- 4. Convert any JSON Schema in detailsSchema to Zod schema
237566
- 5. Use 'type' field as the identifier
237567
- 6. The statusComponent() function handles .config conversion automatically
237568
-
237569
- EXAMPLE:
237570
- import { statusComponent } from '@inkeep/agents-sdk';
237571
- import { z } from 'zod';
237572
-
237573
- export const toolSummary = statusComponent({
237574
- type: 'tool_summary',
237575
- description: 'Summary of tool calls',
237576
- detailsSchema: z.object({
237577
- tool_name: z.string().describe('Name of tool used'),
237578
- summary: z.string().describe('What was accomplished'),
237579
- }),
237580
- });
237581
-
237582
- Generate ONLY the TypeScript code without markdown.`;
237583
- }
237584
- function formatRegistryForFile(fileInfo, _registry) {
237585
- let result = "REQUIRED VARIABLE NAMES (YOU MUST USE ONLY THESE EXACT NAMES):\n\n";
237586
- if (fileInfo.entities.length > 0) {
237587
- result += "Variables to define in this file:\n";
237588
- for (const entity of fileInfo.entities) {
237589
- result += ` - ID "${entity.id}" \u2192 MUST use variable name: ${entity.variableName}
237590
- `;
237591
- }
237592
- result += "\n";
237597
+ function ensureUniqueSubAgentKeys(agentData) {
237598
+ if (!agentData?.subAgents) {
237599
+ return agentData;
237593
237600
  }
237594
- if (fileInfo.inlineContent && fileInfo.inlineContent.length > 0) {
237595
- result += "Inline variables to define in this file:\n";
237596
- for (const entity of fileInfo.inlineContent) {
237597
- result += ` - ID "${entity.id}" \u2192 MUST use variable name: ${entity.variableName}
237598
- `;
237601
+ const transformedData = { ...agentData };
237602
+ const transformedSubAgents = {};
237603
+ for (const [subAgentKey, subAgentData] of Object.entries(agentData.subAgents)) {
237604
+ let uniqueKey = subAgentKey;
237605
+ if (subAgentKey === agentData.id) {
237606
+ uniqueKey = `${subAgentKey}_sub`;
237599
237607
  }
237600
- result += "\n";
237608
+ transformedSubAgents[uniqueKey] = subAgentData;
237601
237609
  }
237602
- if (fileInfo.dependencies.length > 0) {
237603
- result += "!!! EXACT IMPORT STATEMENTS - COPY PRECISELY !!!\n";
237604
- for (const dep of fileInfo.dependencies) {
237605
- result += `import { ${dep.variableName} } from '${dep.fromPath}';
237606
- `;
237607
- }
237608
- result += "\n";
237609
- result += "!!! WARNING: IDs \u2260 FILE PATHS !!!\n";
237610
- result += "Entity IDs (with underscores) are NOT the same as file paths (with kebab-case):\n";
237611
- for (const dep of fileInfo.dependencies) {
237612
- const entity = fileInfo.entities.find((e2) => e2.variableName === dep.variableName) || fileInfo.inlineContent?.find((e2) => e2.variableName === dep.variableName);
237613
- if (entity && entity.id !== dep.fromPath.split("/").pop()?.replace(".ts", "")) {
237614
- result += `- Entity ID: "${entity.id}" \u2192 File path: "${dep.fromPath}"
237615
- `;
237616
- }
237617
- }
237618
- result += "\nCRITICAL: Use the FILE PATHS above, NOT the entity IDs!\n\n";
237619
- }
237620
- return result;
237621
- }
237622
- function generateImportMappings2(plan) {
237623
- let result = "";
237624
- for (const file of plan.files) {
237625
- if (file.type !== "index" && file.type !== "environment") {
237626
- for (const entity of file.entities) {
237627
- const importPath = `./${file.path.replace(".ts", "")}`;
237628
- result += ` - ${entity.variableName} from '${importPath}'
237629
- `;
237630
- }
237631
- }
237632
- }
237633
- return result;
237634
- }
237635
- function createEnvironmentPrompt(credentialData, context, _registryInfo, commonInstructions) {
237636
- const filePath = context.fileInfo.path || "";
237637
- const fileName = filePath.split("/").pop() || "";
237638
- if (fileName === "index.ts") {
237639
- const environmentFiles = context.plan.files.filter(
237640
- (f) => f.type === "environment" && f.path !== "environments/index.ts"
237641
- );
237642
- const imports = environmentFiles.map((envFile) => {
237643
- const envEntity = envFile.entities[0];
237644
- if (envEntity) {
237645
- return `import { ${envEntity.variableName} } from './${envFile.path.replace("environments/", "").replace(".ts", "")}';`;
237646
- }
237647
- return "";
237648
- }).filter(Boolean).join("\n");
237649
- const envSettings = environmentFiles.map((envFile) => {
237650
- const envEntity = envFile.entities[0];
237651
- return envEntity ? ` ${envEntity.variableName},` : "";
237652
- }).filter(Boolean).join("\n");
237653
- return `${commonInstructions}
237654
-
237655
- ENVIRONMENTS INDEX FILE (CRITICAL):
237656
-
237657
- Create an environments/index.ts file that exports environment settings using createEnvironmentSettings.
237658
-
237659
- CREDENTIAL DATA (from project):
237660
- ${JSON.stringify(credentialData, null, 2)}
237661
-
237662
- EXACT IMPORT STATEMENTS (MUST USE THESE):
237663
- ${imports}
237664
-
237665
- ENVIRONMENTS INDEX STRUCTURE (MUST FOLLOW EXACTLY):
237666
-
237667
- import { createEnvironmentSettings } from '@inkeep/agents-sdk';
237668
- ${imports}
237669
-
237670
- export const envSettings = createEnvironmentSettings({
237671
- ${envSettings}
237672
- });
237673
-
237674
- CRITICAL RULES:
237675
- 1. Import createEnvironmentSettings from '@inkeep/agents-sdk'
237676
- 2. Use the EXACT import statements provided above - DO NOT modify them
237677
- 3. Use the EXACT variable names in the createEnvironmentSettings object
237678
- 4. Export envSettings using createEnvironmentSettings()
237679
- 5. Include all environments that have credential files
237680
-
237681
- Generate ONLY the TypeScript code without markdown.`;
237682
- }
237683
- const envName = fileName.replace(".env.ts", "") || "development";
237684
- return `${commonInstructions}
237685
-
237686
- ENVIRONMENT FILE (CRITICAL):
237687
-
237688
- Create an environment file that registers credential settings for the "${envName}" environment.
237689
-
237690
- CREDENTIAL DATA (from project):
237691
- ${JSON.stringify(credentialData, null, 2)}
237692
-
237693
- ENVIRONMENT FILE STRUCTURE (MUST FOLLOW EXACTLY):
237694
-
237695
- import { credential, registerEnvironmentSettings } from '@inkeep/agents-sdk';
237696
-
237697
- export const ${envName} = registerEnvironmentSettings({
237698
- credentials: {
237699
- CREDENTIAL_KEY: credential({
237700
- id: 'CREDENTIAL_ID',
237701
- type: 'CREDENTIAL_TYPE',
237702
- credentialStoreId: 'CREDENTIAL_STORE_ID',
237703
- retrievalParams: {
237704
- key: 'ENV_VARIABLE_NAME'
237705
- }
237706
- })
237707
- }
237708
- });
237709
-
237710
- CRITICAL RULES:
237711
- 1. Import { credential, registerEnvironmentSettings } from '@inkeep/agents-sdk'
237712
- 2. Export a const named "${envName}" (matching the environment)
237713
- 3. Use registerEnvironmentSettings() wrapper
237714
- 4. Create credential() objects for each credential in the data
237715
- 5. Convert credential IDs to environment variable keys (e.g., 'linear-api' -> 'LINEAR_API_KEY')
237716
- 6. Use exact credential IDs, types, and credentialStoreId from the data provided
237717
- 7. Set retrievalParams.key to the environment variable name (uppercase with underscores)
237718
-
237719
- Example for credential with id 'linear-api':
237720
- - Export const: ${envName}
237721
- - Credential key: linear_api (underscore format for object key)
237722
- - retrievalParams.key: 'LINEAR_API_KEY' (uppercase for environment variable)
237723
-
237724
- Generate ONLY the TypeScript code without markdown.`;
237610
+ transformedData.subAgents = transformedSubAgents;
237611
+ return transformedData;
237725
237612
  }
237726
237613
  var init_unified_generator = __esm({
237727
237614
  "src/codegen/unified-generator.ts"() {
@@ -237929,6 +237816,7 @@ var init_plan_storage = __esm({
237929
237816
  // src/index.ts
237930
237817
  init_esm_shims();
237931
237818
  init_env2();
237819
+ init_instrumentation();
237932
237820
  import { readFileSync as readFileSync7 } from "fs";
237933
237821
  import { dirname as dirname6, join as join13 } from "path";
237934
237822
  import { fileURLToPath as fileURLToPath3 } from "url";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-cli",
3
- "version": "0.23.1",
3
+ "version": "0.23.3",
4
4
  "description": "Inkeep CLI tool",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -27,6 +27,10 @@
27
27
  "@babel/types": "^7.23.0",
28
28
  "@clack/prompts": "^0.11.0",
29
29
  "@libsql/client": "^0.15.7",
30
+ "@opentelemetry/api-logs": "^0.206.0",
31
+ "@opentelemetry/instrumentation": "^0.206.0",
32
+ "@opentelemetry/sdk-logs": "^0.206.0",
33
+ "@vercel/otel": "^2.0.1",
30
34
  "ai": "5.0.11",
31
35
  "ast-types": "^0.14.2",
32
36
  "chalk": "^5.3.0",
@@ -40,6 +44,7 @@
40
44
  "inquirer": "^9.2.12",
41
45
  "inquirer-autocomplete-prompt": "^3.0.1",
42
46
  "json-schema-to-zod": "^2.6.1",
47
+ "langfuse-vercel": "^3.38.6",
43
48
  "ora": "^8.0.1",
44
49
  "picocolors": "^1.1.1",
45
50
  "pino": "^9.11.0",
@@ -47,8 +52,8 @@
47
52
  "recast": "^0.23.0",
48
53
  "ts-morph": "^26.0.0",
49
54
  "tsx": "^4.20.5",
50
- "@inkeep/agents-core": "^0.23.1",
51
- "@inkeep/agents-sdk": "^0.23.1"
55
+ "@inkeep/agents-core": "^0.23.3",
56
+ "@inkeep/agents-sdk": "^0.23.3"
52
57
  },
53
58
  "devDependencies": {
54
59
  "@types/degit": "^2.8.6",