@botpress/adk 1.16.7 → 1.18.0-beta.1

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 (156) hide show
  1. package/dist/agent-init/agent-project-generator.d.ts +32 -8
  2. package/dist/agent-init/agent-project-generator.d.ts.map +1 -1
  3. package/dist/agent-init/index.d.ts +1 -0
  4. package/dist/agent-init/index.d.ts.map +1 -1
  5. package/dist/agent-project/agent-project.d.ts +13 -1
  6. package/dist/agent-project/agent-project.d.ts.map +1 -1
  7. package/dist/agent-project/agent-resolver.d.ts +4 -3
  8. package/dist/agent-project/agent-resolver.d.ts.map +1 -1
  9. package/dist/agent-project/config-writer.d.ts +33 -0
  10. package/dist/agent-project/config-writer.d.ts.map +1 -1
  11. package/dist/agent-project/dependencies-parser.d.ts.map +1 -1
  12. package/dist/agent-project/index.d.ts +1 -1
  13. package/dist/agent-project/index.d.ts.map +1 -1
  14. package/dist/agent-project/types.d.ts +48 -22
  15. package/dist/agent-project/types.d.ts.map +1 -1
  16. package/dist/agent-project/validation-errors.d.ts.map +1 -1
  17. package/dist/auth/credentials.d.ts +15 -1
  18. package/dist/auth/credentials.d.ts.map +1 -1
  19. package/dist/auth/index.d.ts +2 -0
  20. package/dist/auth/index.d.ts.map +1 -1
  21. package/dist/bot-generator/dev-id-manager.d.ts.map +1 -1
  22. package/dist/bot-generator/generator.d.ts.map +1 -1
  23. package/dist/commands/base-command.d.ts.map +1 -1
  24. package/dist/commands/bp-add-command.d.ts.map +1 -1
  25. package/dist/commands/bp-build-command.d.ts.map +1 -1
  26. package/dist/commands/bp-chat-command.d.ts.map +1 -1
  27. package/dist/commands/bp-deploy-command.d.ts.map +1 -1
  28. package/dist/commands/bp-dev-command.d.ts +2 -2
  29. package/dist/commands/bp-dev-command.d.ts.map +1 -1
  30. package/dist/commands/opencode-command.d.ts +2 -2
  31. package/dist/commands/opencode-command.d.ts.map +1 -1
  32. package/dist/commands/opencode-config.d.ts +1 -1
  33. package/dist/commands/opencode-config.d.ts.map +1 -1
  34. package/dist/config/coerce-config-value.d.ts.map +1 -1
  35. package/dist/config/manager.d.ts +5 -5
  36. package/dist/config/manager.d.ts.map +1 -1
  37. package/dist/eval/client.d.ts +8 -0
  38. package/dist/eval/client.d.ts.map +1 -1
  39. package/dist/eval/graders/index.d.ts +1 -0
  40. package/dist/eval/graders/index.d.ts.map +1 -1
  41. package/dist/eval/graders/llm.d.ts +6 -2
  42. package/dist/eval/graders/llm.d.ts.map +1 -1
  43. package/dist/eval/graders/response.d.ts +1 -0
  44. package/dist/eval/graders/response.d.ts.map +1 -1
  45. package/dist/eval/graders/state.d.ts +1 -1
  46. package/dist/eval/graders/state.d.ts.map +1 -1
  47. package/dist/eval/graders/tables.d.ts.map +1 -1
  48. package/dist/eval/graders/timing.d.ts +7 -0
  49. package/dist/eval/graders/timing.d.ts.map +1 -0
  50. package/dist/eval/graders/workflow.d.ts.map +1 -1
  51. package/dist/eval/index.d.ts +3 -3
  52. package/dist/eval/index.d.ts.map +1 -1
  53. package/dist/eval/loader.d.ts +2 -1
  54. package/dist/eval/loader.d.ts.map +1 -1
  55. package/dist/eval/runner.d.ts +1 -0
  56. package/dist/eval/runner.d.ts.map +1 -1
  57. package/dist/eval/traces.d.ts.map +1 -1
  58. package/dist/eval/types.d.ts +62 -4
  59. package/dist/eval/types.d.ts.map +1 -1
  60. package/dist/file-watcher/watcher.d.ts +9 -0
  61. package/dist/file-watcher/watcher.d.ts.map +1 -1
  62. package/dist/generators/client-wrapper.d.ts.map +1 -1
  63. package/dist/generators/conversation-types.d.ts.map +1 -1
  64. package/dist/generators/integration-types.d.ts.map +1 -1
  65. package/dist/generators/interface-types.d.ts.map +1 -1
  66. package/dist/generators/plugin-types.d.ts.map +1 -1
  67. package/dist/generators/table-types.d.ts.map +1 -1
  68. package/dist/generators/tests.d.ts.map +1 -1
  69. package/dist/generators/workflow-types.d.ts.map +1 -1
  70. package/dist/index.d.ts +6 -5
  71. package/dist/index.d.ts.map +1 -1
  72. package/dist/index.js +1194 -594
  73. package/dist/index.js.map +62 -61
  74. package/dist/integrations/checker.d.ts +2 -2
  75. package/dist/integrations/checker.d.ts.map +1 -1
  76. package/dist/integrations/config-utils.d.ts +4 -3
  77. package/dist/integrations/config-utils.d.ts.map +1 -1
  78. package/dist/integrations/operations.d.ts +19 -6
  79. package/dist/integrations/operations.d.ts.map +1 -1
  80. package/dist/integrations/types.d.ts +1 -1
  81. package/dist/integrations/types.d.ts.map +1 -1
  82. package/dist/interfaces/manager.d.ts.map +1 -1
  83. package/dist/interfaces/types.d.ts +1 -1
  84. package/dist/interfaces/types.d.ts.map +1 -1
  85. package/dist/knowledge/manager.d.ts.map +1 -1
  86. package/dist/plugins/types.d.ts +1 -1
  87. package/dist/plugins/types.d.ts.map +1 -1
  88. package/dist/preflight/agent-config-sync.d.ts +2 -1
  89. package/dist/preflight/agent-config-sync.d.ts.map +1 -1
  90. package/dist/preflight/checker.d.ts.map +1 -1
  91. package/dist/preflight/types.d.ts +8 -8
  92. package/dist/preflight/types.d.ts.map +1 -1
  93. package/dist/runner/index.d.ts +1 -1
  94. package/dist/runner/script-runner.d.ts +1 -1
  95. package/dist/runner/script-runner.d.ts.map +1 -1
  96. package/dist/tables/table-manager.d.ts.map +1 -1
  97. package/dist/tables/types.d.ts +2 -1
  98. package/dist/tables/types.d.ts.map +1 -1
  99. package/dist/templates/README.md +101 -0
  100. package/dist/templates/blank/README.md +36 -0
  101. package/dist/templates/blank/agent.config.ts +49 -0
  102. package/dist/templates/blank/package.json +17 -0
  103. package/dist/templates/blank/src/actions/index.ts +19 -0
  104. package/dist/templates/blank/src/conversations/index.ts +16 -0
  105. package/dist/templates/blank/src/knowledge/index.ts +17 -0
  106. package/dist/templates/blank/src/tables/index.ts +19 -0
  107. package/dist/templates/blank/src/triggers/index.ts +20 -0
  108. package/dist/templates/blank/src/workflows/index.ts +23 -0
  109. package/dist/templates/blank/tsconfig.json +22 -0
  110. package/dist/templates/crm-enrichment/README.md +85 -0
  111. package/dist/templates/crm-enrichment/agent.config.ts +33 -0
  112. package/dist/templates/crm-enrichment/package.json +17 -0
  113. package/dist/templates/crm-enrichment/src/actions/enrich-contact.ts +81 -0
  114. package/dist/templates/crm-enrichment/src/conversations/index.ts +14 -0
  115. package/dist/templates/crm-enrichment/src/knowledge/index.ts +17 -0
  116. package/dist/templates/crm-enrichment/src/tables/contacts.ts +43 -0
  117. package/dist/templates/crm-enrichment/src/triggers/daily-enrichment.ts +30 -0
  118. package/dist/templates/crm-enrichment/src/workflows/enrichment-pipeline.ts +171 -0
  119. package/dist/templates/crm-enrichment/tsconfig.json +22 -0
  120. package/dist/templates/hello-world/README.md +46 -0
  121. package/dist/templates/hello-world/agent.config.ts +48 -0
  122. package/dist/templates/hello-world/package.json +17 -0
  123. package/dist/templates/hello-world/src/actions/index.ts +19 -0
  124. package/dist/templates/hello-world/src/conversations/index.ts +10 -0
  125. package/dist/templates/hello-world/src/knowledge/index.ts +17 -0
  126. package/dist/templates/hello-world/src/tables/index.ts +19 -0
  127. package/dist/templates/hello-world/src/triggers/index.ts +20 -0
  128. package/dist/templates/hello-world/src/workflows/index.ts +23 -0
  129. package/dist/templates/hello-world/tsconfig.json +22 -0
  130. package/dist/templates/knowledge-assistant/README.md +66 -0
  131. package/dist/templates/knowledge-assistant/agent.config.ts +26 -0
  132. package/dist/templates/knowledge-assistant/package.json +17 -0
  133. package/dist/templates/knowledge-assistant/src/actions/index.ts +19 -0
  134. package/dist/templates/knowledge-assistant/src/actions/search-docs.ts +50 -0
  135. package/dist/templates/knowledge-assistant/src/conversations/index.ts +33 -0
  136. package/dist/templates/knowledge-assistant/src/knowledge/docs.ts +23 -0
  137. package/dist/templates/knowledge-assistant/src/knowledge/getting-started.md +49 -0
  138. package/dist/templates/knowledge-assistant/src/tables/index.ts +21 -0
  139. package/dist/templates/knowledge-assistant/src/triggers/index.ts +17 -0
  140. package/dist/templates/knowledge-assistant/src/workflows/index.ts +28 -0
  141. package/dist/templates/knowledge-assistant/tsconfig.json +22 -0
  142. package/dist/templates/slack-triage/README.md +74 -0
  143. package/dist/templates/slack-triage/agent.config.ts +35 -0
  144. package/dist/templates/slack-triage/evals/triage-basic.eval.ts +55 -0
  145. package/dist/templates/slack-triage/package.json +17 -0
  146. package/dist/templates/slack-triage/src/actions/classify-request.ts +65 -0
  147. package/dist/templates/slack-triage/src/conversations/slack-dm.ts +72 -0
  148. package/dist/templates/slack-triage/src/knowledge/team-directory.md +33 -0
  149. package/dist/templates/slack-triage/src/tables/routing-rules.ts +38 -0
  150. package/dist/templates/slack-triage/src/triggers/new-message.ts +44 -0
  151. package/dist/templates/slack-triage/src/workflows/triage-flow.ts +104 -0
  152. package/dist/templates/slack-triage/tsconfig.json +22 -0
  153. package/dist/templates/template.config.json +47 -0
  154. package/dist/utils/json-ordering.d.ts +5 -4
  155. package/dist/utils/json-ordering.d.ts.map +1 -1
  156. package/package.json +31 -38
package/dist/index.js CHANGED
@@ -15,19 +15,22 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
15
15
 
16
16
  // src/agent-project/types.ts
17
17
  import { z } from "@botpress/sdk";
18
- var pluginDependencyMappingSchema, dependenciesSchema, agentInfoSchema, ValidationErrorCode, ValidationSeverity, ProjectState;
18
+ var pluginDependencyMappingSchema, dependenciesSchema, agentInfoSchema, agentLocalInfoSchema, ValidationErrorCode, ValidationSeverity, ProjectState;
19
19
  var init_types = __esm(() => {
20
20
  pluginDependencyMappingSchema = z.object({
21
21
  integrationAlias: z.string(),
22
22
  integrationInterfaceAlias: z.string().optional()
23
23
  });
24
24
  dependenciesSchema = z.object({
25
- integrations: z.record(z.object({
26
- version: z.string(),
27
- enabled: z.boolean(),
28
- configurationType: z.string().optional(),
29
- config: z.record(z.any()).optional()
30
- })).optional(),
25
+ integrations: z.record(z.union([
26
+ z.string(),
27
+ z.object({
28
+ version: z.string(),
29
+ enabled: z.boolean(),
30
+ configurationType: z.string().optional(),
31
+ config: z.record(z.any()).optional()
32
+ })
33
+ ])).optional(),
31
34
  plugins: z.record(z.object({
32
35
  version: z.string(),
33
36
  config: z.record(z.any()).optional(),
@@ -40,6 +43,12 @@ var init_types = __esm(() => {
40
43
  apiUrl: z.string().optional().describe("The Botpress API URL (e.g., https://api.botpress.cloud)"),
41
44
  devId: z.string().optional().describe("The development ID used during local development")
42
45
  });
46
+ agentLocalInfoSchema = z.object({
47
+ botId: z.string().optional().describe("The bot ID (overrides agent.json for local development)"),
48
+ workspaceId: z.string().optional().describe("The workspace ID (overrides agent.json for local development)"),
49
+ apiUrl: z.string().optional().describe("The Botpress API URL (overrides agent.json for local development)"),
50
+ devId: z.string().optional().describe("The development bot ID used during local development")
51
+ });
43
52
  ((ValidationErrorCode2) => {
44
53
  ValidationErrorCode2["DIRECTORY_NOT_FOUND"] = "DIRECTORY_NOT_FOUND";
45
54
  ValidationErrorCode2["DIRECTORY_ACCESS_ERROR"] = "DIRECTORY_ACCESS_ERROR";
@@ -467,9 +476,46 @@ var init_constants = __esm(() => {
467
476
  // src/agent-project/agent-resolver.ts
468
477
  import fs from "fs/promises";
469
478
  import path from "path";
479
+ async function readLocalInfo(agentPath) {
480
+ const localPath = path.join(agentPath, "agent.local.json");
481
+ try {
482
+ const localContent = await fs.readFile(localPath, "utf-8");
483
+ const localData = JSON.parse(localContent);
484
+ const localResult = agentLocalInfoSchema.safeParse(localData);
485
+ if (!localResult.success) {
486
+ const issue = localResult.error.errors[0];
487
+ throw ValidationErrors.warning(`agent.local.json has an invalid field: ${issue?.path.join(".")} — ${issue?.message}`, "agent.local.json");
488
+ }
489
+ return localResult.data;
490
+ } catch (error) {
491
+ if (error.code === "ENOENT") {
492
+ return null;
493
+ }
494
+ if (ValidationErrors.isValidationError(error)) {
495
+ throw error;
496
+ }
497
+ throw ValidationErrors.warning(`Failed to read agent.local.json: ${error.message}`, "agent.local.json");
498
+ }
499
+ }
500
+ function applyLocalOverrides(agentInfo, localInfo) {
501
+ if (localInfo.botId) {
502
+ agentInfo.botId = localInfo.botId;
503
+ }
504
+ if (localInfo.workspaceId) {
505
+ agentInfo.workspaceId = localInfo.workspaceId;
506
+ }
507
+ if (localInfo.apiUrl) {
508
+ agentInfo.apiUrl = localInfo.apiUrl;
509
+ }
510
+ if (localInfo.devId) {
511
+ agentInfo.devId = localInfo.devId;
512
+ }
513
+ }
470
514
  async function resolveAgent(agentPath, options = {}) {
471
515
  const { required = false, requireWorkspace = false, requireBot = false } = options;
472
516
  const agentJsonPath = path.join(agentPath, "agent.json");
517
+ const localInfo = await readLocalInfo(agentPath);
518
+ let agentInfo = null;
473
519
  try {
474
520
  const agentJsonContent = await fs.readFile(agentJsonPath, "utf-8");
475
521
  let agentData;
@@ -483,29 +529,42 @@ async function resolveAgent(agentPath, options = {}) {
483
529
  const zodError = validationResult.error.errors[0];
484
530
  throw ValidationErrors.invalidConfigSchema("agent.json", zodError?.path.join(".") || "unknown", zodError?.message || "Invalid schema");
485
531
  }
486
- const agentInfo = validationResult.data;
487
- if (!agentInfo.apiUrl) {
488
- agentInfo.apiUrl = DEFAULT_API_URL;
489
- }
490
- if (requireWorkspace && !agentInfo.workspaceId) {
491
- throw ValidationErrors.workspaceIdMissing();
492
- }
493
- if (requireBot && !agentInfo.botId) {
494
- throw ValidationErrors.botIdMissing();
495
- }
496
- return agentInfo;
532
+ agentInfo = validationResult.data;
497
533
  } catch (error) {
498
- if (error.code === "ENOENT") {
499
- if (required || requireWorkspace || requireBot) {
500
- throw ValidationErrors.requiredFileMissing("agent.json");
501
- }
534
+ if (error.code === "ENOENT") {} else if (ValidationErrors.isValidationError(error)) {
535
+ throw error;
536
+ } else {
537
+ throw ValidationErrors.warning(`Failed to read agent.json: ${error.message}`, "agent.json");
538
+ }
539
+ }
540
+ if (!agentInfo) {
541
+ if (localInfo?.botId && localInfo?.workspaceId) {
542
+ agentInfo = {
543
+ botId: localInfo.botId,
544
+ workspaceId: localInfo.workspaceId,
545
+ apiUrl: localInfo.apiUrl,
546
+ devId: localInfo.devId
547
+ };
548
+ } else if (required || requireWorkspace || requireBot) {
549
+ throw ValidationErrors.requiredFileMissing("agent.json or agent.local.json");
550
+ } else {
502
551
  return null;
503
552
  }
504
- if (ValidationErrors.isValidationError(error)) {
505
- throw error;
553
+ } else {
554
+ if (localInfo) {
555
+ applyLocalOverrides(agentInfo, localInfo);
506
556
  }
507
- throw ValidationErrors.warning(`Failed to read agent.json: ${error.message}`, "agent.json");
508
557
  }
558
+ if (!agentInfo.apiUrl) {
559
+ agentInfo.apiUrl = DEFAULT_API_URL;
560
+ }
561
+ if (requireWorkspace && !agentInfo.workspaceId) {
562
+ throw ValidationErrors.workspaceIdMissing();
563
+ }
564
+ if (requireBot && !agentInfo.botId) {
565
+ throw ValidationErrors.botIdMissing();
566
+ }
567
+ return agentInfo;
509
568
  }
510
569
  var init_agent_resolver = __esm(() => {
511
570
  init_types();
@@ -525,10 +584,14 @@ import os from "os";
525
584
  class CredentialsManager {
526
585
  credentialsPath;
527
586
  configDir;
587
+ profileOverride;
528
588
  constructor() {
529
589
  this.configDir = path2.join(os.homedir(), ".adk");
530
590
  this.credentialsPath = path2.join(this.configDir, "credentials");
531
591
  }
592
+ setProfileOverride(profile) {
593
+ this.profileOverride = profile;
594
+ }
532
595
  async ensureConfigDir() {
533
596
  try {
534
597
  await fs2.mkdir(this.configDir, { recursive: true });
@@ -644,23 +707,53 @@ class CredentialsManager {
644
707
  await this.writeCredentials(store);
645
708
  }
646
709
  async getActiveCredentials() {
647
- const envProfile = process.env.ADK_PROFILE;
648
- const credentials = await this.getCredentials(envProfile);
710
+ const profileName = this.profileOverride || process.env.ADK_PROFILE;
711
+ const credentials = await this.getCredentials(profileName);
649
712
  if (!credentials) {
650
- const profileName = envProfile || "default";
651
- throw new Error(`No credentials found for profile '${profileName}'. ` + `Please run 'adk login' to authenticate.`);
713
+ const displayName = profileName || "default";
714
+ throw new Error(`No credentials found for profile '${displayName}'. ` + `Please run 'adk login' to authenticate.`);
652
715
  }
653
716
  return credentials;
654
717
  }
718
+ findProfileByApiUrl(store, apiUrl) {
719
+ const normalizedUrl = apiUrl.replace(/\/+$/, "");
720
+ for (const [, credentials] of Object.entries(store.profiles)) {
721
+ if (!credentials.apiUrl)
722
+ continue;
723
+ const profileUrl = credentials.apiUrl.replace(/\/+$/, "");
724
+ if (profileUrl === normalizedUrl) {
725
+ return credentials;
726
+ }
727
+ }
728
+ return null;
729
+ }
655
730
  async getAgentCredentials(agentPath) {
656
- const baseCredentials = await this.getActiveCredentials();
657
731
  const agentInfo = await resolveAgent(agentPath, {
658
732
  required: true,
659
733
  requireWorkspace: true
660
734
  });
735
+ const agentApiUrl = agentInfo.apiUrl;
736
+ let baseCredentials;
737
+ const hasExplicitProfile = !!(this.profileOverride || process.env.ADK_PROFILE);
738
+ if (hasExplicitProfile) {
739
+ baseCredentials = await this.getActiveCredentials();
740
+ } else {
741
+ const store = await this.readCredentials();
742
+ const matchingCredentials = this.findProfileByApiUrl(store, agentApiUrl);
743
+ if (matchingCredentials) {
744
+ baseCredentials = matchingCredentials;
745
+ } else {
746
+ const profileName = store.currentProfile || "default";
747
+ const activeCredentials = store.profiles[profileName];
748
+ if (!activeCredentials) {
749
+ throw new Error(`No credentials found for profile '${profileName}'. ` + `Please run 'adk login' to authenticate.`);
750
+ }
751
+ baseCredentials = activeCredentials;
752
+ }
753
+ }
661
754
  return {
662
755
  ...baseCredentials,
663
- apiUrl: agentInfo.apiUrl || baseCredentials.apiUrl,
756
+ apiUrl: agentApiUrl,
664
757
  workspaceId: agentInfo.workspaceId,
665
758
  botId: agentInfo.botId
666
759
  };
@@ -729,7 +822,7 @@ var _format = null, _formatLoaded = false, formatCode = async (code, filepath) =
729
822
  `));
730
823
  return code;
731
824
  }
732
- }, ADK_VERSION = "1.16.7", relative2 = (from, to) => {
825
+ }, ADK_VERSION = "1.18.0-beta.1", relative2 = (from, to) => {
733
826
  const fromDir = path10.dirname(from);
734
827
  const relative3 = path10.relative(fromDir, to);
735
828
  return relative3.startsWith(".") ? relative3 : `./${relative3}`;
@@ -860,16 +953,31 @@ var init_integration_action_types = __esm(() => {
860
953
  var require_package = __commonJS((exports, module) => {
861
954
  module.exports = {
862
955
  name: "@botpress/adk",
863
- version: "1.16.7",
956
+ version: "1.18.0-beta.1",
864
957
  description: "Core ADK library for building AI agents on Botpress",
865
- type: "module",
866
- main: "dist/index.js",
867
- types: "dist/index.d.ts",
958
+ keywords: [
959
+ "adk",
960
+ "agent",
961
+ "ai",
962
+ "botpress",
963
+ "chatbot",
964
+ "conversational-ai",
965
+ "development-kit"
966
+ ],
967
+ license: "MIT",
968
+ author: "Botpress",
969
+ repository: {
970
+ type: "git",
971
+ url: "https://github.com/botpress/adk"
972
+ },
868
973
  files: [
869
974
  "dist/**/*",
870
975
  "package.json",
871
976
  "README.md"
872
977
  ],
978
+ type: "module",
979
+ main: "dist/index.js",
980
+ types: "dist/index.d.ts",
873
981
  exports: {
874
982
  ".": {
875
983
  types: "./dist/index.d.ts",
@@ -884,33 +992,18 @@ var require_package = __commonJS((exports, module) => {
884
992
  watch: "bun run build:types && bun build ./src/index.ts --outdir ./dist --target node --format esm --sourcemap --watch",
885
993
  clean: "rm -rf dist",
886
994
  prepublishOnly: "bun run clean && bun run build",
887
- test: "vitest run",
888
- "test:watch": "vitest",
889
- "test:ui": "vitest --ui",
890
- "test:coverage": "vitest run --coverage"
891
- },
892
- keywords: [
893
- "botpress",
894
- "adk",
895
- "agent",
896
- "ai",
897
- "chatbot",
898
- "conversational-ai",
899
- "development-kit"
900
- ],
901
- author: "Botpress",
902
- license: "MIT",
903
- repository: {
904
- type: "git",
905
- url: "https://github.com/botpress/adk"
995
+ test: "bun test",
996
+ "test:watch": "bun test --watch",
997
+ "test:coverage": "bun test",
998
+ "check:type": "tsc --noEmit"
906
999
  },
907
1000
  dependencies: {
908
- "@botpress/chat": "^0.5.5",
909
- "@botpress/cli": "5.2.0",
910
- "@botpress/client": "1.35.0",
911
- "@botpress/cognitive": "0.3.14",
912
- "@botpress/runtime": "^1.16.7",
913
- "@botpress/sdk": "5.4.3",
1001
+ "@botpress/chat": "0.5.5",
1002
+ "@botpress/cli": "6.2.2",
1003
+ "@botpress/client": "1.38.1",
1004
+ "@botpress/cognitive": "0.4.2",
1005
+ "@botpress/runtime": "^1.18.0-beta.1",
1006
+ "@botpress/sdk": "6.3.1",
914
1007
  "@bpinternal/jex": "^1.2.4",
915
1008
  "@bpinternal/yargs-extra": "^0.0.21",
916
1009
  "@parcel/watcher": "^2.5.1",
@@ -923,19 +1016,12 @@ var require_package = __commonJS((exports, module) => {
923
1016
  semver: "^7.7.2",
924
1017
  "ts-morph": "^27.0.2"
925
1018
  },
926
- devDependencies: {
927
- "@types/debug": "^4.1.12",
928
- "@types/glob": "^9.0.0",
929
- "@types/luxon": "^3.7.1",
930
- "@types/semver": "^7.7.1",
931
- dotenv: "^17.2.3"
932
- },
933
1019
  peerDependencies: {
934
1020
  typescript: ">=4.5.0"
935
1021
  },
936
1022
  engines: {
937
- node: ">=22.0.0",
938
- bun: ">=1.3.9"
1023
+ bun: ">=1.3.9",
1024
+ node: ">=22.0.0"
939
1025
  },
940
1026
  packageManager: "bun@1.3.9"
941
1027
  };
@@ -1104,6 +1190,9 @@ class Auth {
1104
1190
  async setCurrentProfile(profileName) {
1105
1191
  return this.credentialsManager.setCurrentProfile(profileName);
1106
1192
  }
1193
+ setProfileOverride(profile) {
1194
+ this.credentialsManager.setProfileOverride(profile);
1195
+ }
1107
1196
  async getActiveCredentials() {
1108
1197
  return this.credentialsManager.getActiveCredentials();
1109
1198
  }
@@ -1153,7 +1242,7 @@ import os3 from "os";
1153
1242
  import path4 from "path";
1154
1243
  import createDebug from "debug";
1155
1244
  var debug = createDebug("adk:bp-cli");
1156
- var BP_CLI_VERSION = "5.2.0";
1245
+ var BP_CLI_VERSION = "6.2.2";
1157
1246
  var BP_CLI_INSTALL_ALL = path4.join(os3.homedir(), ".adk", `bp-cli`);
1158
1247
  var BP_CLI_INSTALL_DIR = path4.join(BP_CLI_INSTALL_ALL, BP_CLI_VERSION);
1159
1248
  var BP_CLI_BIN_PATH = path4.join(BP_CLI_INSTALL_DIR, "node_modules", "@botpress", "cli", "bin.js");
@@ -1648,9 +1737,9 @@ class BpDevCommand extends BaseCommand {
1648
1737
  if (sourceMap) {
1649
1738
  bpArgs.push("--sourceMap");
1650
1739
  }
1651
- const consoleUrl = `http://localhost:${this.options.internalOtlpPort}`;
1740
+ const spanIngestUrl = `http://localhost:${this.options.spanIngestPort}`;
1652
1741
  const otlpEndpoint = this.options.otlpPort ? `http://localhost:${this.options.otlpPort}` : undefined;
1653
- const traceLines = [`[bp-dev] Trace endpoints:`, ` ADK_CONSOLE_URL=${consoleUrl}`];
1742
+ const traceLines = [`[bp-dev] Trace endpoints:`, ` ADK_SPAN_INGEST_URL=${spanIngestUrl}`];
1654
1743
  if (otlpEndpoint) {
1655
1744
  traceLines.push(` OTEL_EXPORTER_OTLP_ENDPOINT=${otlpEndpoint}`);
1656
1745
  }
@@ -1673,7 +1762,7 @@ class BpDevCommand extends BaseCommand {
1673
1762
  },
1674
1763
  WORKER_MODE: "true",
1675
1764
  WORKER_LIFETIME_MS: process.env.WORKER_LIFETIME_MS || "120000",
1676
- ADK_CONSOLE_URL: consoleUrl,
1765
+ ADK_SPAN_INGEST_URL: spanIngestUrl,
1677
1766
  ...otlpEndpoint && { OTEL_EXPORTER_OTLP_ENDPOINT: otlpEndpoint },
1678
1767
  ...this.options.botName && { ADK_BOT_NAME: this.options.botName },
1679
1768
  ADK_DIRECTORY: join2(botPath, ".."),
@@ -1902,10 +1991,11 @@ class BpChatCommand extends BaseCommand {
1902
1991
  try {
1903
1992
  await this.childProcess;
1904
1993
  } catch (error) {
1994
+ const errObj = error != null && typeof error === "object" ? error : {};
1905
1995
  this.emit("error", {
1906
- exitCode: error.exitCode || 1,
1907
- stderr: error.stderr || "",
1908
- message: error.message || "Chat command failed"
1996
+ exitCode: "exitCode" in errObj ? errObj.exitCode || 1 : 1,
1997
+ stderr: "stderr" in errObj ? errObj.stderr || "" : "",
1998
+ message: error instanceof Error ? error.message || "Chat command failed" : "Chat command failed"
1909
1999
  });
1910
2000
  throw error;
1911
2001
  }
@@ -1986,8 +2076,8 @@ You also have access to Botpress documentation search for platform-level questio
1986
2076
  **Never read or display the contents of .env files or credentials.** If you need to verify a configuration value, ask the developer to confirm it.`;
1987
2077
  function buildOpenCodeConfig(options) {
1988
2078
  const command = [options.adkBinPath, "mcp", "--cwd", options.agentPath];
1989
- if (options.devServerPort)
1990
- command.push("--port", String(options.devServerPort));
2079
+ if (options.uiServerPort)
2080
+ command.push("--port", String(options.uiServerPort));
1991
2081
  return {
1992
2082
  ...options.openCodePort && {
1993
2083
  server: {
@@ -2060,7 +2150,7 @@ class OpenCodeCommand extends BaseCommand {
2060
2150
  const opencodeConfig = JSON.stringify(buildOpenCodeConfig({
2061
2151
  adkBinPath: adkBin,
2062
2152
  agentPath: cwd,
2063
- devServerPort: this.options.devServerPort,
2153
+ uiServerPort: this.options.uiServerPort,
2064
2154
  openCodePort: port,
2065
2155
  corsOrigins
2066
2156
  }));
@@ -2122,7 +2212,13 @@ class OpenCodeCommand extends BaseCommand {
2122
2212
  } catch {}
2123
2213
  await new Promise((r) => setTimeout(r, 500));
2124
2214
  }
2125
- this.emitReady();
2215
+ if (!this.readyEmitted && !this.killed) {
2216
+ this.emit("progress", {
2217
+ type: "error",
2218
+ startTime: Date.now(),
2219
+ data: { reason: "OpenCode server did not respond in time" }
2220
+ });
2221
+ }
2126
2222
  }
2127
2223
  kill(signal = "SIGTERM") {
2128
2224
  this.killed = true;
@@ -2207,7 +2303,8 @@ function getChatClient(cwd = process.cwd()) {
2207
2303
  return Client4;
2208
2304
  }
2209
2305
  // src/utils/json-ordering.ts
2210
- var agentInfoKeyOrder = ["botId", "workspaceId", "apiUrl", "devId"];
2306
+ var agentInfoKeyOrder = ["botId", "workspaceId", "apiUrl"];
2307
+ var agentLocalInfoKeyOrder = ["botId", "workspaceId", "apiUrl", "devId"];
2211
2308
  var dependenciesKeyOrder = ["integrations"];
2212
2309
  var integrationKeyOrder = ["version", "enabled", "configurationType", "config"];
2213
2310
  function orderKeys(obj, keyOrder) {
@@ -2258,6 +2355,7 @@ function stringifyWithOrder(obj, keyOrder, space = 2) {
2258
2355
  }
2259
2356
  // src/agent-project/agent-project.ts
2260
2357
  import { BuiltInActions, BuiltInWorkflows, Errors as Errors2, Primitives as Primitives2, setAdkCommand } from "@botpress/runtime/internal";
2358
+ import { Autonomous } from "@botpress/runtime";
2261
2359
  import createDebug2 from "debug";
2262
2360
  import { createRequire as createRequire3 } from "module";
2263
2361
  import fs10 from "fs/promises";
@@ -2292,7 +2390,7 @@ class AssetsCacheManager {
2292
2390
  const content = await fs4.readFile(this.cachePath, "utf-8");
2293
2391
  this.cache = JSON.parse(content);
2294
2392
  return this.cache;
2295
- } catch (error) {
2393
+ } catch {
2296
2394
  this.cache = {
2297
2395
  version: "1.0",
2298
2396
  entries: {}
@@ -2825,7 +2923,7 @@ class EnhancedIntegrationCache {
2825
2923
 
2826
2924
  // src/agent-project/dependencies-parser.ts
2827
2925
  init_validation_errors();
2828
- import { z as z2, ZodIssueCode } from "@botpress/sdk";
2926
+ import { z as z2 } from "@botpress/sdk";
2829
2927
  var INTEGRATION_ALIAS_MIN_LENGTH = 2;
2830
2928
  var INTEGRATION_ALIAS_MAX_LENGTH = 100;
2831
2929
  var INTEGRATION_ALIAS_REGEX = /^(?:[a-z][a-z0-9_-]*\/)?[a-z][a-z0-9_-]*$/;
@@ -2836,10 +2934,10 @@ var integrationRefSchema = z2.string().transform((val, ctx) => {
2836
2934
  const match = val.match(/^(?:([^/]+)\/)?([^@]+)@(.+)$/);
2837
2935
  if (!match) {
2838
2936
  ctx.addIssue({
2839
- code: ZodIssueCode.custom,
2937
+ code: "custom",
2840
2938
  message: `Invalid integration version format: ${val}. Expected format: 'name@version' or 'workspace/name@version'`
2841
2939
  });
2842
- return z2.NEVER;
2940
+ return;
2843
2941
  }
2844
2942
  const [, workspace, name, version] = match;
2845
2943
  return {
@@ -2876,7 +2974,8 @@ class IntegrationParser {
2876
2974
  errors.push(ValidationErrors.invalidIntegrationAlias(alias));
2877
2975
  continue;
2878
2976
  }
2879
- const ref = this.parseIntegrationRef(value.version);
2977
+ const normalized = typeof value === "string" ? { version: value } : value;
2978
+ const ref = this.parseIntegrationRef(normalized.version);
2880
2979
  const versionResult = versionSchema.safeParse(ref.version);
2881
2980
  if (!versionResult.success) {
2882
2981
  errors.push(ValidationErrors.invalidVersionFormat(alias, ref.version));
@@ -2884,9 +2983,9 @@ class IntegrationParser {
2884
2983
  integrations.push({
2885
2984
  alias,
2886
2985
  ref,
2887
- enabled: value.enabled,
2888
- configurationType: value.configurationType,
2889
- config: value.config
2986
+ enabled: "enabled" in normalized ? normalized.enabled : undefined,
2987
+ configurationType: "configurationType" in normalized ? normalized.configurationType : undefined,
2988
+ config: "config" in normalized ? normalized.config : undefined
2890
2989
  });
2891
2990
  } catch (error) {
2892
2991
  errors.push(ValidationErrors.invalidDependenciesSyntax(`Invalid integration '${alias}': ${error instanceof Error ? error.message : String(error)}`));
@@ -2938,10 +3037,10 @@ var pluginRefSchema = z2.string().transform((val, ctx) => {
2938
3037
  const match = val.match(/^([^/@]+)@(.+)$/);
2939
3038
  if (!match) {
2940
3039
  ctx.addIssue({
2941
- code: ZodIssueCode.custom,
3040
+ code: "custom",
2942
3041
  message: `Invalid plugin version format: ${val}. Expected format: 'name@version' (no workspace prefix)`
2943
3042
  });
2944
- return z2.NEVER;
3043
+ return;
2945
3044
  }
2946
3045
  const [, name, version] = match;
2947
3046
  return {
@@ -2998,7 +3097,7 @@ class PluginParser {
2998
3097
  if (!pluginConfig.dependencies) {
2999
3098
  continue;
3000
3099
  }
3001
- for (const [depAlias, depMapping] of Object.entries(pluginConfig.dependencies)) {
3100
+ for (const [_depAlias, depMapping] of Object.entries(pluginConfig.dependencies)) {
3002
3101
  if (!integrationAliases.includes(depMapping.integrationAlias)) {
3003
3102
  errors.push(ValidationErrors.invalidPluginDependency(alias, depMapping.integrationAlias, integrationAliases));
3004
3103
  }
@@ -3203,7 +3302,7 @@ class IntegrationManager {
3203
3302
  return { valid: false, errors, warnings };
3204
3303
  }
3205
3304
  const hasChannels = integration.definition.channels && Object.keys(integration.definition.channels).length > 0;
3206
- if (!integration.config) {
3305
+ if (!integration.config && integration.enabled !== undefined) {
3207
3306
  let requiresConfig = false;
3208
3307
  if (integration.definition.configurations) {
3209
3308
  requiresConfig = Object.values(integration.definition.configurations).some((config) => config.identifier?.required === true);
@@ -3269,7 +3368,7 @@ class IntegrationCache {
3269
3368
  }
3270
3369
  }
3271
3370
  return cached.definition;
3272
- } catch (error) {
3371
+ } catch {
3273
3372
  return null;
3274
3373
  }
3275
3374
  }
@@ -3473,6 +3572,7 @@ class HubCache {
3473
3572
  // src/agent-project/config-writer.ts
3474
3573
  init_utils();
3475
3574
  import { Project, SyntaxKind } from "ts-morph";
3575
+ import { readFileSync } from "fs";
3476
3576
  import * as path11 from "path";
3477
3577
 
3478
3578
  class ConfigWriter {
@@ -3482,7 +3582,9 @@ class ConfigWriter {
3482
3582
  }
3483
3583
  loadConfig() {
3484
3584
  const project = new Project;
3485
- const sourceFile = project.addSourceFileAtPath(this.configPath);
3585
+ const sourceFile = project.createSourceFile(this.configPath, readFileSync(this.configPath, "utf-8"), {
3586
+ overwrite: true
3587
+ });
3486
3588
  const defineConfigCall = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression).find((call) => {
3487
3589
  return call.getExpression().getText() === "defineConfig";
3488
3590
  });
@@ -3502,6 +3604,47 @@ class ConfigWriter {
3502
3604
  sourceFile.replaceWithText(formatted);
3503
3605
  await sourceFile.save();
3504
3606
  }
3607
+ serializeDefaultModelSelection(value) {
3608
+ if (Array.isArray(value)) {
3609
+ return `[${value.map((entry) => `'${entry.replace(/'/g, "\\'")}'`).join(", ")}]`;
3610
+ }
3611
+ return `'${value.replace(/'/g, "\\'")}'`;
3612
+ }
3613
+ getIntegrationConfigMigrationCandidates() {
3614
+ const { sourceFile, configObject } = this.loadConfig();
3615
+ const dependenciesProp = configObject.getProperty("dependencies");
3616
+ if (!dependenciesProp) {
3617
+ return { sourceFile, candidates: [] };
3618
+ }
3619
+ const depsInit = dependenciesProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
3620
+ if (!depsInit) {
3621
+ return { sourceFile, candidates: [] };
3622
+ }
3623
+ const integrationsProp = depsInit.getProperty("integrations");
3624
+ if (!integrationsProp) {
3625
+ return { sourceFile, candidates: [] };
3626
+ }
3627
+ const integrationsInit = integrationsProp.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
3628
+ if (!integrationsInit) {
3629
+ return { sourceFile, candidates: [] };
3630
+ }
3631
+ const candidates = integrationsInit.getProperties().flatMap((prop) => {
3632
+ if (!prop.isKind(SyntaxKind.PropertyAssignment)) {
3633
+ return [];
3634
+ }
3635
+ const assignment = prop;
3636
+ const initializer = assignment.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
3637
+ if (!initializer) {
3638
+ return [];
3639
+ }
3640
+ const hasConfigProperty = initializer.getProperty("config") !== undefined;
3641
+ const versionProp = initializer.getProperty("version");
3642
+ const versionInitializer = versionProp?.getInitializer();
3643
+ const versionInitializerText = versionInitializer && (versionInitializer.isKind(SyntaxKind.StringLiteral) || versionInitializer.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)) ? versionInitializer.getText() : undefined;
3644
+ return [{ alias: assignment.getName(), assignment, hasConfigProperty, versionInitializerText }];
3645
+ });
3646
+ return { sourceFile, candidates };
3647
+ }
3505
3648
  async updateDependencies(dependencies) {
3506
3649
  const { sourceFile, configObject } = this.loadConfig();
3507
3650
  let dependenciesProperty = configObject.getProperty("dependencies");
@@ -3523,6 +3666,67 @@ class ConfigWriter {
3523
3666
  }
3524
3667
  await this.saveConfig(sourceFile);
3525
3668
  }
3669
+ async updateDefaultModels(updates) {
3670
+ const requestedEntries = Object.entries(updates).filter(([, value]) => value !== undefined);
3671
+ if (requestedEntries.length === 0) {
3672
+ return;
3673
+ }
3674
+ const { sourceFile, configObject } = this.loadConfig();
3675
+ let defaultModelsProperty = configObject.getProperty("defaultModels");
3676
+ if (!defaultModelsProperty) {
3677
+ defaultModelsProperty = configObject.addPropertyAssignment({
3678
+ name: "defaultModels",
3679
+ initializer: "{}"
3680
+ });
3681
+ }
3682
+ const defaultModelsObject = defaultModelsProperty.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression);
3683
+ if (!defaultModelsObject) {
3684
+ throw new Error("defaultModels must be an object literal in agent.config.ts");
3685
+ }
3686
+ for (const [key, value] of requestedEntries) {
3687
+ const existingProperty = defaultModelsObject.getProperty(key);
3688
+ const initializer = this.serializeDefaultModelSelection(value);
3689
+ if (existingProperty) {
3690
+ existingProperty.setInitializer(initializer);
3691
+ } else {
3692
+ defaultModelsObject.addPropertyAssignment({
3693
+ name: key,
3694
+ initializer
3695
+ });
3696
+ }
3697
+ }
3698
+ await this.saveConfig(sourceFile);
3699
+ }
3700
+ async migrateIntegrationsToStringFormat() {
3701
+ const { sourceFile, candidates } = this.getIntegrationConfigMigrationCandidates();
3702
+ const migrated = [];
3703
+ for (const candidate of candidates) {
3704
+ if (!candidate.versionInitializerText || candidate.hasConfigProperty) {
3705
+ continue;
3706
+ }
3707
+ candidate.assignment.setInitializer(candidate.versionInitializerText);
3708
+ migrated.push(candidate.alias);
3709
+ }
3710
+ if (migrated.length > 0) {
3711
+ await this.saveConfig(sourceFile);
3712
+ }
3713
+ return migrated;
3714
+ }
3715
+ getIntegrationConfigMigrationState() {
3716
+ try {
3717
+ const { candidates } = this.getIntegrationConfigMigrationCandidates();
3718
+ const deprecatedAliases = candidates.map((candidate) => candidate.alias);
3719
+ const migratableAliases = candidates.filter((candidate) => candidate.versionInitializerText && !candidate.hasConfigProperty).map((candidate) => candidate.alias);
3720
+ return { deprecatedAliases, migratableAliases };
3721
+ } catch (error) {
3722
+ const message = error instanceof Error ? error.message : String(error);
3723
+ console.error(`Configuration update failed with error: ${message} — please verify that there are no syntax errors in your agent.config.ts`);
3724
+ return { deprecatedAliases: [], migratableAliases: [] };
3725
+ }
3726
+ }
3727
+ getObjectFormatIntegrations() {
3728
+ return this.getIntegrationConfigMigrationState().deprecatedAliases;
3729
+ }
3526
3730
  async updateConfiguration(updates) {
3527
3731
  const { sourceFile, configObject } = this.loadConfig();
3528
3732
  const hasAdds = updates.some((u) => u.action === "add");
@@ -3634,6 +3838,66 @@ class IntegrationOperations {
3634
3838
  }
3635
3839
  return { name, version, fullName: name };
3636
3840
  }
3841
+ isIntegrationVersionId(input) {
3842
+ return input.startsWith("intver_");
3843
+ }
3844
+ isResourceNotFoundError(error) {
3845
+ return Boolean(error && typeof error === "object" && "type" in error && error.type === "ResourceNotFound");
3846
+ }
3847
+ getCanonicalIntegrationFullName(integration) {
3848
+ if (integration.public) {
3849
+ return integration.name;
3850
+ }
3851
+ const workspaceHandle = integration.ownerWorkspace?.handle;
3852
+ return workspaceHandle ? `${workspaceHandle}/${integration.name}` : integration.name;
3853
+ }
3854
+ async resolveIntegrationVersionId(id) {
3855
+ const client = await this.getClient();
3856
+ try {
3857
+ const response = await client.getIntegration({ id });
3858
+ return response.integration;
3859
+ } catch (error) {
3860
+ if (!this.isResourceNotFoundError(error)) {
3861
+ throw error;
3862
+ }
3863
+ }
3864
+ try {
3865
+ const response = await client.getPublicIntegrationById({ id });
3866
+ return response.integration;
3867
+ } catch (error) {
3868
+ if (this.isResourceNotFoundError(error)) {
3869
+ throw new Error(`Integration "${id}" not found`);
3870
+ }
3871
+ throw error;
3872
+ }
3873
+ }
3874
+ async resolveAddIntegrationInput(options) {
3875
+ const requestedVersion = options.version || "latest";
3876
+ if (!this.isIntegrationVersionId(options.name)) {
3877
+ const ref = this.parseIntegrationRef(`${options.name}@${requestedVersion}`);
3878
+ return {
3879
+ name: ref.name,
3880
+ fullName: ref.fullName,
3881
+ version: ref.version,
3882
+ alias: options.alias || ref.name,
3883
+ ref: `${ref.fullName}@${ref.version}`,
3884
+ isIntegrationId: false
3885
+ };
3886
+ }
3887
+ if (options.version && options.version !== "latest") {
3888
+ throw new Error("Integration version IDs are already version-pinned and do not accept an additional @version override");
3889
+ }
3890
+ const integration = await this.resolveIntegrationVersionId(options.name);
3891
+ const fullName = this.getCanonicalIntegrationFullName(integration);
3892
+ return {
3893
+ name: integration.name,
3894
+ fullName,
3895
+ version: integration.version,
3896
+ alias: options.alias || integration.name,
3897
+ ref: `${fullName}@${integration.version}`,
3898
+ isIntegrationId: true
3899
+ };
3900
+ }
3637
3901
  async getIntegrationVersions(name) {
3638
3902
  const client = await this.getClient();
3639
3903
  const ref = this.parseIntegrationRef(name);
@@ -3667,9 +3931,8 @@ class IntegrationOperations {
3667
3931
  }
3668
3932
  async addIntegration(options) {
3669
3933
  const project = await AgentProject.load(this.projectPath || process2.cwd());
3670
- const { name, version = "latest" } = options;
3671
- const ref = this.parseIntegrationRef(`${name}@${version}`);
3672
- const alias = options.alias || ref.name;
3934
+ const resolvedInput = await this.resolveAddIntegrationInput(options);
3935
+ const { alias } = resolvedInput;
3673
3936
  const dependencies = project.dependencies || { integrations: {} };
3674
3937
  if (!dependencies.integrations) {
3675
3938
  dependencies.integrations = {};
@@ -3686,11 +3949,17 @@ class IntegrationOperations {
3686
3949
  } else {
3687
3950
  existingVersion = "unknown";
3688
3951
  }
3689
- if (!version || version === "latest" || version === existingVersion) {
3952
+ const effectiveVersionSpecified = Boolean(options.version) || resolvedInput.isIntegrationId;
3953
+ if (!effectiveVersionSpecified || resolvedInput.version === "latest" || resolvedInput.version === existingVersion) {
3690
3954
  return {
3691
3955
  success: false,
3692
3956
  message: `Integration '${alias}' already exists with version ${existingVersion}. Use 'adk upgrade ${alias}' to upgrade to the latest version.`,
3693
- existingVersion
3957
+ existingVersion,
3958
+ name: resolvedInput.name,
3959
+ fullName: resolvedInput.fullName,
3960
+ alias: resolvedInput.alias,
3961
+ version: existingVersion,
3962
+ ref: `${resolvedInput.fullName}@${existingVersion}`
3694
3963
  };
3695
3964
  }
3696
3965
  }
@@ -3702,7 +3971,7 @@ class IntegrationOperations {
3702
3971
  const loadResult = await tempManager.loadIntegrations({
3703
3972
  integrations: {
3704
3973
  [alias]: {
3705
- version: `${ref.fullName}@${ref.version}`,
3974
+ version: resolvedInput.ref,
3706
3975
  enabled: true
3707
3976
  }
3708
3977
  }
@@ -3720,88 +3989,28 @@ class IntegrationOperations {
3720
3989
  throw new Error(`Failed to add integration: ${error instanceof Error ? error.message : String(error)}`);
3721
3990
  }
3722
3991
  const actualVersion = integrationDefinition.version;
3723
- const integrationVersion = `${ref.fullName}@${actualVersion}`;
3992
+ const integrationVersion = `${resolvedInput.fullName}@${actualVersion}`;
3724
3993
  const existingEntry = dependencies.integrations[alias];
3725
- const existingConfig = existingEntry && typeof existingEntry === "object" ? existingEntry.config : undefined;
3726
- const existingConfigurationType = existingEntry && typeof existingEntry === "object" ? existingEntry.configurationType : undefined;
3727
- const configurationType = existingConfigurationType || this.selectBestConfigurationType(integrationDefinition.configurations);
3728
- let requiresConfiguration = false;
3729
- const selectedConfigDef = configurationType && integrationDefinition.configurations?.[configurationType] || integrationDefinition.configurations?.["default"] || integrationDefinition.configuration;
3730
- if (selectedConfigDef) {
3731
- if (selectedConfigDef.identifier?.required === true) {
3732
- requiresConfiguration = true;
3733
- }
3734
- if (selectedConfigDef.schema?.required && selectedConfigDef.schema.required.length > 0) {
3735
- requiresConfiguration = true;
3736
- }
3737
- }
3738
- const config = requiresConfiguration && !existingConfig ? this.getPlaceholderConfig(integrationDefinition, configurationType) : existingConfig;
3739
3994
  if (existingEntry && typeof existingEntry === "object" && "version" in existingEntry) {
3740
- const resolvedConfigType = existingEntry.configurationType || configurationType;
3741
3995
  dependencies.integrations[alias] = {
3742
- version: integrationVersion,
3743
- enabled: existingEntry.enabled,
3744
- ...resolvedConfigType ? { configurationType: resolvedConfigType } : {},
3745
- config: existingEntry.config
3996
+ ...existingEntry,
3997
+ version: integrationVersion
3746
3998
  };
3747
3999
  } else {
3748
- dependencies.integrations[alias] = {
3749
- version: integrationVersion,
3750
- enabled: !requiresConfiguration,
3751
- ...configurationType ? { configurationType } : {},
3752
- config
3753
- };
4000
+ dependencies.integrations[alias] = integrationVersion;
3754
4001
  }
3755
4002
  const configWriter = new ConfigWriter(project.path);
3756
4003
  await configWriter.updateDependencies(dependencies);
3757
4004
  return {
3758
4005
  success: true,
3759
- requiresConfiguration,
3760
- newVersion: actualVersion
4006
+ newVersion: actualVersion,
4007
+ name: resolvedInput.name,
4008
+ fullName: resolvedInput.fullName,
4009
+ alias: resolvedInput.alias,
4010
+ version: actualVersion,
4011
+ ref: integrationVersion
3761
4012
  };
3762
4013
  }
3763
- selectBestConfigurationType(configurations) {
3764
- if (!configurations || Object.keys(configurations).length === 0) {
3765
- return;
3766
- }
3767
- const keys = Object.keys(configurations);
3768
- if (keys.length === 1) {
3769
- return keys[0];
3770
- }
3771
- const priority = ["apiKey", "api", "api_key", "token", "default", "refreshToken", "oauth", "oauth2"];
3772
- for (const preferred of priority) {
3773
- const match = keys.find((k) => k.toLowerCase() === preferred.toLowerCase());
3774
- if (match)
3775
- return match;
3776
- }
3777
- return keys[0];
3778
- }
3779
- getPlaceholderConfig(integrationDefinition, configurationType) {
3780
- const configPlaceholder = {};
3781
- const configDef = configurationType && integrationDefinition.configurations?.[configurationType] || integrationDefinition.configurations?.["default"] || integrationDefinition.configuration;
3782
- if (configDef) {
3783
- const schema = configDef.schema;
3784
- if (schema?.required && schema.required.length > 0) {
3785
- const properties = schema.properties || {};
3786
- for (const fieldName of schema.required) {
3787
- const fieldDef = properties[fieldName];
3788
- if (fieldDef?.type === "string") {
3789
- configPlaceholder[fieldName] = `YOUR_${fieldName.toUpperCase()}_HERE`;
3790
- } else if (fieldDef?.type === "number" || fieldDef?.type === "integer") {
3791
- configPlaceholder[fieldName] = 0;
3792
- } else if (fieldDef?.type === "boolean") {
3793
- configPlaceholder[fieldName] = false;
3794
- } else {
3795
- configPlaceholder[fieldName] = null;
3796
- }
3797
- }
3798
- if (Object.keys(configPlaceholder).length > 0) {
3799
- return configPlaceholder;
3800
- }
3801
- }
3802
- }
3803
- return;
3804
- }
3805
4014
  async removeIntegration(name) {
3806
4015
  const project = await AgentProject.load(this.projectPath || process2.cwd());
3807
4016
  const dependencies = project.dependencies;
@@ -3840,10 +4049,8 @@ class IntegrationOperations {
3840
4049
  }
3841
4050
  const versionInfo = await this.getIntegrationVersions(ref.fullName);
3842
4051
  if (versionInfo.latestVersion && versionInfo.latestVersion !== currentVersion) {
3843
- dependencies.integrations[alias] = {
3844
- ...value,
3845
- version: `${ref.fullName}@${versionInfo.latestVersion}`
3846
- };
4052
+ const newVersion = `${ref.fullName}@${versionInfo.latestVersion}`;
4053
+ dependencies.integrations[alias] = typeof value === "string" ? newVersion : { ...value, version: newVersion };
3847
4054
  upgraded.push(`${alias} (${currentVersion} → ${versionInfo.latestVersion})`);
3848
4055
  }
3849
4056
  } catch {
@@ -3932,7 +4139,7 @@ async function fetchServerIntegrationConfigs(project, targetBotId) {
3932
4139
  const { devId, botId } = project.agentInfo ?? {};
3933
4140
  const targetId = targetBotId || devId || botId;
3934
4141
  if (!targetId) {
3935
- return { configs: {}, fetched: false, skipped: true };
4142
+ return { configs: {}, enabledStates: {}, fetched: false, skipped: true };
3936
4143
  }
3937
4144
  try {
3938
4145
  const credentials = await auth.getActiveCredentials();
@@ -3955,18 +4162,26 @@ async function fetchServerIntegrationConfigs(project, targetBotId) {
3955
4162
  }
3956
4163
  } catch (err) {
3957
4164
  const message = err instanceof Error ? err.message : String(err);
3958
- return { configs: {}, fetched: false, skipped: false, error: message };
4165
+ return {
4166
+ configs: {},
4167
+ enabledStates: {},
4168
+ fetched: false,
4169
+ skipped: false,
4170
+ error: message
4171
+ };
3959
4172
  }
3960
4173
  }
3961
4174
  async function fetchBotConfigs(client, botId) {
3962
4175
  const { bot } = await client.getBot({ id: botId });
3963
4176
  const configs = {};
4177
+ const enabledStates = {};
3964
4178
  for (const [alias, integration] of Object.entries(bot.integrations || {})) {
3965
4179
  if (integration.configuration && Object.keys(integration.configuration).length > 0) {
3966
4180
  configs[alias] = integration.configuration;
3967
4181
  }
4182
+ enabledStates[alias] = integration.enabled;
3968
4183
  }
3969
- return { configs, fetched: true, skipped: false };
4184
+ return { configs, enabledStates, fetched: true, skipped: false };
3970
4185
  }
3971
4186
 
3972
4187
  // src/integrations/checker.ts
@@ -4080,7 +4295,7 @@ class IntegrationChecker {
4080
4295
  }
4081
4296
  }
4082
4297
  }
4083
- const desiredInstallStatus = enabled === false ? "disabled" : "enabled";
4298
+ const desiredInstallStatus = enabled === true ? "enabled" : enabled === false ? "disabled" : installedIntegration ? installedIntegration.enabled ? "enabled" : "disabled" : "disabled";
4084
4299
  const result = {
4085
4300
  alias,
4086
4301
  name: ref.fullName,
@@ -4108,7 +4323,7 @@ class IntegrationChecker {
4108
4323
  }
4109
4324
  }
4110
4325
  if (!installedIntegration) {
4111
- result.needsInstall = enabled !== false;
4326
+ result.needsInstall = true;
4112
4327
  if (result.needsInstall && config) {
4113
4328
  result.needsConfiguration = true;
4114
4329
  }
@@ -4128,11 +4343,7 @@ class IntegrationChecker {
4128
4343
  }
4129
4344
  }
4130
4345
  if (enabled !== undefined && enabled !== installedIntegration.enabled) {
4131
- if (enabled === false) {
4132
- result.needsRemoval = true;
4133
- } else if (enabled === true && result.installStatus === "disabled") {
4134
- result.needsConfiguration = true;
4135
- }
4346
+ result.needsUpdate = true;
4136
4347
  }
4137
4348
  }
4138
4349
  return result;
@@ -4658,7 +4869,7 @@ async function expandExports(options) {
4658
4869
  });
4659
4870
  return {};
4660
4871
  }
4661
- currentError = currentError.cause;
4872
+ currentError = currentError instanceof Error ? currentError.cause : undefined;
4662
4873
  }
4663
4874
  throw importError;
4664
4875
  }
@@ -4669,6 +4880,21 @@ init_agent_resolver();
4669
4880
  init_types();
4670
4881
  init_validation_errors();
4671
4882
  var debug2 = createDebug2("adk:agent-project");
4883
+ function getToolDefinition(value) {
4884
+ if (typeof value !== "object" || value === null) {
4885
+ return;
4886
+ }
4887
+ const maybeTool = value;
4888
+ const isAutonomousToolInstance = value instanceof Autonomous.Tool;
4889
+ const looksLikeAutonomousTool = maybeTool.constructor?.name === Autonomous.Tool.name && typeof maybeTool.name === "string" && typeof maybeTool.execute === "function" && typeof maybeTool.toJSON === "function";
4890
+ if (!isAutonomousToolInstance && !looksLikeAutonomousTool) {
4891
+ return;
4892
+ }
4893
+ return {
4894
+ name: maybeTool.name,
4895
+ description: maybeTool.description
4896
+ };
4897
+ }
4672
4898
 
4673
4899
  class AgentProject {
4674
4900
  static _projectCache = new Map;
@@ -4692,6 +4918,7 @@ class AgentProject {
4692
4918
  _workflows = [];
4693
4919
  _actions = [];
4694
4920
  _tables = [];
4921
+ _tools = [];
4695
4922
  constructor(projectPath, options = {}) {
4696
4923
  this._options = options;
4697
4924
  this._path = path13.resolve(projectPath);
@@ -4748,6 +4975,9 @@ class AgentProject {
4748
4975
  get tables() {
4749
4976
  return this._tables;
4750
4977
  }
4978
+ get tools() {
4979
+ return this._tools;
4980
+ }
4751
4981
  get config() {
4752
4982
  return this._config;
4753
4983
  }
@@ -4786,6 +5016,7 @@ class AgentProject {
4786
5016
  this._workflows = [];
4787
5017
  this._actions = [];
4788
5018
  this._tables = [];
5019
+ this._tools = [];
4789
5020
  this._errors = [];
4790
5021
  this._warnings = [];
4791
5022
  const validation = await this.validate();
@@ -4924,20 +5155,84 @@ class AgentProject {
4924
5155
  return this._assetsManager;
4925
5156
  }
4926
5157
  async createAgentInfo(info) {
5158
+ const { devId, ...agentJsonInfo } = info;
4927
5159
  const agentPath = path13.join(this._path, "agent.json");
4928
- const agentContent = stringifyWithOrder(info, agentInfoKeyOrder);
5160
+ const agentContent = stringifyWithOrder(agentJsonInfo, agentInfoKeyOrder);
4929
5161
  await fs10.writeFile(agentPath, agentContent);
4930
5162
  this._agentInfo = info;
5163
+ if (devId) {
5164
+ await this.createAgentLocalInfo({ devId });
5165
+ }
4931
5166
  }
4932
5167
  async updateAgentInfo(updates) {
4933
5168
  if (!this._agentInfo) {
4934
5169
  throw new Error("No agent.json found. Use createAgentInfo() first.");
4935
5170
  }
4936
- const updatedInfo = { ...this._agentInfo, ...updates };
5171
+ const { devId, ...agentJsonUpdates } = updates;
5172
+ const updatedInfo = { ...this._agentInfo, ...agentJsonUpdates };
5173
+ const { devId: _devId, ...agentJsonData } = updatedInfo;
4937
5174
  const agentPath = path13.join(this._path, "agent.json");
4938
- const agentContent = stringifyWithOrder(updatedInfo, agentInfoKeyOrder);
5175
+ const agentContent = stringifyWithOrder(agentJsonData, agentInfoKeyOrder);
4939
5176
  await fs10.writeFile(agentPath, agentContent);
4940
5177
  this._agentInfo = updatedInfo;
5178
+ if (devId !== undefined) {
5179
+ await this.updateAgentLocalInfo({ devId });
5180
+ }
5181
+ }
5182
+ async createAgentLocalInfo(info) {
5183
+ const localPath = path13.join(this._path, "agent.local.json");
5184
+ let existing = {};
5185
+ try {
5186
+ const content2 = await fs10.readFile(localPath, "utf-8");
5187
+ existing = JSON.parse(content2);
5188
+ } catch {}
5189
+ const merged = { ...existing, ...info };
5190
+ const content = stringifyWithOrder(merged, agentLocalInfoKeyOrder);
5191
+ await fs10.writeFile(localPath, content);
5192
+ if (this._agentInfo) {
5193
+ if (merged.botId)
5194
+ this._agentInfo.botId = merged.botId;
5195
+ if (merged.workspaceId)
5196
+ this._agentInfo.workspaceId = merged.workspaceId;
5197
+ if (merged.apiUrl)
5198
+ this._agentInfo.apiUrl = merged.apiUrl;
5199
+ if (merged.devId)
5200
+ this._agentInfo.devId = merged.devId;
5201
+ } else if (merged.botId && merged.workspaceId) {
5202
+ this._agentInfo = {
5203
+ botId: merged.botId,
5204
+ workspaceId: merged.workspaceId,
5205
+ apiUrl: merged.apiUrl,
5206
+ devId: merged.devId
5207
+ };
5208
+ }
5209
+ }
5210
+ async updateAgentLocalInfo(updates) {
5211
+ const localPath = path13.join(this._path, "agent.local.json");
5212
+ let existing = {};
5213
+ try {
5214
+ const content2 = await fs10.readFile(localPath, "utf-8");
5215
+ existing = JSON.parse(content2);
5216
+ } catch {}
5217
+ const updated = { ...existing, ...updates };
5218
+ for (const key of Object.keys(updated)) {
5219
+ if (updated[key] === undefined) {
5220
+ delete updated[key];
5221
+ }
5222
+ }
5223
+ const content = stringifyWithOrder(updated, agentLocalInfoKeyOrder);
5224
+ await fs10.writeFile(localPath, content);
5225
+ if (this._agentInfo) {
5226
+ const local = updated;
5227
+ if (local.botId)
5228
+ this._agentInfo.botId = local.botId;
5229
+ if (local.workspaceId)
5230
+ this._agentInfo.workspaceId = local.workspaceId;
5231
+ if (local.apiUrl)
5232
+ this._agentInfo.apiUrl = local.apiUrl;
5233
+ if (local.devId)
5234
+ this._agentInfo.devId = local.devId;
5235
+ }
4941
5236
  }
4942
5237
  requiresAgentInfo(operation) {
4943
5238
  if (!this._agentInfo?.botId) {
@@ -5128,6 +5423,32 @@ ${err.stack?.split(`
5128
5423
  console.warn(`Failed to register data source workflows for ${kbPath}:`, error);
5129
5424
  }
5130
5425
  }
5426
+ isBarrelReexport(existing, newPath, newExport, newDefinition) {
5427
+ const isNewBarrel = /^index\.[tj]s$/i.test(path13.basename(newPath));
5428
+ const isExistingBarrel = /^index\.[tj]s$/i.test(path13.basename(existing.path));
5429
+ if (!isNewBarrel && !isExistingBarrel) {
5430
+ return false;
5431
+ }
5432
+ if (isExistingBarrel && !isNewBarrel) {
5433
+ existing.path = newPath;
5434
+ existing.export = newExport;
5435
+ existing.definition = newDefinition;
5436
+ }
5437
+ return true;
5438
+ }
5439
+ isToolBarrelReexport(existingPath, newPath) {
5440
+ const isNewBarrel = /^index\.[tj]s$/i.test(path13.basename(newPath));
5441
+ const isExistingBarrel = /^index\.[tj]s$/i.test(path13.basename(existingPath));
5442
+ if (!isNewBarrel && !isExistingBarrel) {
5443
+ return false;
5444
+ }
5445
+ return true;
5446
+ }
5447
+ shouldPreferToolDefinition(existingPath, newPath) {
5448
+ const isNewBarrel = /^index\.[tj]s$/i.test(path13.basename(newPath));
5449
+ const isExistingBarrel = /^index\.[tj]s$/i.test(path13.basename(existingPath));
5450
+ return isExistingBarrel && !isNewBarrel;
5451
+ }
5131
5452
  getChannelsList(channelSpec) {
5132
5453
  if (channelSpec === "*") {
5133
5454
  return ["*"];
@@ -5210,6 +5531,9 @@ ${err.stack?.split(`
5210
5531
  return existingChannels.some((ch) => newChannels.includes(ch));
5211
5532
  });
5212
5533
  if (overlapping) {
5534
+ if (this.isBarrelReexport(overlapping, relPath, key, definition)) {
5535
+ continue;
5536
+ }
5213
5537
  this._warnings.push({
5214
5538
  $type: "ValidationError",
5215
5539
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5227,6 +5551,9 @@ ${err.stack?.split(`
5227
5551
  } else if (Primitives2.Definitions.isKnowledgeDefinition(definition)) {
5228
5552
  const existing = this._knowledge.find((p) => p.definition.name === definition.name);
5229
5553
  if (existing) {
5554
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5555
+ continue;
5556
+ }
5230
5557
  this._warnings.push({
5231
5558
  $type: "ValidationError",
5232
5559
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5245,6 +5572,9 @@ ${err.stack?.split(`
5245
5572
  } else if (Primitives2.Definitions.isTriggerDefinition(definition)) {
5246
5573
  const existing = this._triggers.find((p) => p.definition.name === definition.name);
5247
5574
  if (existing) {
5575
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5576
+ continue;
5577
+ }
5248
5578
  this._warnings.push({
5249
5579
  $type: "ValidationError",
5250
5580
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5262,6 +5592,9 @@ ${err.stack?.split(`
5262
5592
  } else if (Primitives2.Definitions.isWorkflowDefinition(definition)) {
5263
5593
  const existing = this._workflows.find((p) => p.definition.name === definition.name);
5264
5594
  if (existing) {
5595
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5596
+ continue;
5597
+ }
5265
5598
  this._warnings.push({
5266
5599
  $type: "ValidationError",
5267
5600
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5279,6 +5612,9 @@ ${err.stack?.split(`
5279
5612
  } else if (Primitives2.Definitions.isActionDefinition(definition)) {
5280
5613
  const existing = this._actions.find((p) => p.definition.name === definition.name);
5281
5614
  if (existing) {
5615
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5616
+ continue;
5617
+ }
5282
5618
  this._warnings.push({
5283
5619
  $type: "ValidationError",
5284
5620
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5296,6 +5632,9 @@ ${err.stack?.split(`
5296
5632
  } else if (Primitives2.Definitions.isTableDefinition(definition)) {
5297
5633
  const existing = this._tables.find((p) => p.definition.name === definition.name);
5298
5634
  if (existing) {
5635
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5636
+ continue;
5637
+ }
5299
5638
  this._warnings.push({
5300
5639
  $type: "ValidationError",
5301
5640
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5310,6 +5649,35 @@ ${err.stack?.split(`
5310
5649
  export: key,
5311
5650
  path: relPath
5312
5651
  });
5652
+ } else {
5653
+ const toolDefinition = getToolDefinition(expandedExports[key]);
5654
+ if (!toolDefinition) {
5655
+ continue;
5656
+ }
5657
+ const existing = this._tools.find((tool) => tool.definition.name === toolDefinition.name);
5658
+ if (existing) {
5659
+ if (this.isToolBarrelReexport(existing.path, relPath)) {
5660
+ if (this.shouldPreferToolDefinition(existing.path, relPath)) {
5661
+ existing.path = relPath;
5662
+ existing.export = key;
5663
+ existing.definition = toolDefinition;
5664
+ }
5665
+ continue;
5666
+ }
5667
+ this._warnings.push({
5668
+ $type: "ValidationError",
5669
+ code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
5670
+ severity: "warning" /* WARNING */,
5671
+ message: `Duplicate tool definition found: ${filename} -> ${key} (already defined in ${existing.path} -> ${existing.export})`,
5672
+ file: relPath
5673
+ });
5674
+ continue;
5675
+ }
5676
+ this._tools.push({
5677
+ definition: toolDefinition,
5678
+ export: key,
5679
+ path: relPath
5680
+ });
5313
5681
  }
5314
5682
  }
5315
5683
  } catch (error) {
@@ -5593,6 +5961,8 @@ class ConfigManager {
5593
5961
  init_utils();
5594
5962
  import * as fs11 from "fs";
5595
5963
  import * as path15 from "path";
5964
+ import { fileURLToPath } from "url";
5965
+ import { createRequire as createRequire4 } from "module";
5596
5966
 
5597
5967
  // src/agent-init/ai-assistant-instructions.template.md
5598
5968
  var ai_assistant_instructions_template_default = `# Botpress ADK Agent
@@ -5619,22 +5989,38 @@ adk deploy # Deploy to Botpress Cloud
5619
5989
  adk chat # Chat with your agent in the terminal
5620
5990
  \`\`\`
5621
5991
 
5622
- ## AI Coding Assistant Skills
5992
+ ## MCP Tools (ADK Dev Server)
5623
5993
 
5624
- This project uses the Botpress ADK. Before making changes, use the relevant skill:
5994
+ This project includes an MCP server that provides AI coding assistants with deep ADK integration.
5995
+ Run \`adk mcp:init --all\` to generate configuration for your editor, or use the tools below directly.
5625
5996
 
5626
- | Skill | Use for |
5627
- | ------------------ | ------------------------------------------------ |
5628
- | \`/adk\` | ADK concepts, patterns, and API reference |
5629
- | \`/adk-integration\` | Finding and using Botpress integrations |
5630
- | \`/adk-debugger\` | Debugging with traces and test conversations |
5631
- | \`/adk-frontend\` | Building frontends that connect to ADK bots |
5997
+ ### Debugging & Testing
5632
5998
 
5633
- If these skills are not installed, install them:
5999
+ | Tool | Use for |
6000
+ | ------------------ | ------------------------------------------------------------ |
6001
+ | \`adk_send_message\` | Send a test message to the running bot and receive responses |
6002
+ | \`adk_query_traces\` | Query trace spans for debugging conversations and workflows |
6003
+ | \`adk_get_dev_logs\` | Get dev server logs, build output, errors, and warnings |
5634
6004
 
5635
- \`\`\`
5636
- npx skills add botpress/skills --skill adk
5637
- \`\`\`
6005
+ ### Project & Integration Management
6006
+
6007
+ | Tool | Use for |
6008
+ | ------------------------- | ----------------------------------------------------------- |
6009
+ | \`adk_get_agent_info\` | Get project info: name, version, and all primitives |
6010
+ | \`adk_search_integrations\` | Search available integrations on the Botpress Hub |
6011
+ | \`adk_get_integration\` | Get detailed info about an integration before adding it |
6012
+ | \`adk_add_integration\` | Add an integration to the project (updates agent.config.ts) |
6013
+
6014
+ ### Workflows
6015
+
6016
+ | Tool | Use for |
6017
+ | -------------------- | ---------------------------------------- |
6018
+ | \`adk_list_workflows\` | Deprecated compat workflow discovery |
6019
+ | \`adk_start_workflow\` | Start a workflow or get its input schema |
6020
+
6021
+ > **Tip:** Prefer \`adk workflows\` in the terminal to discover workflow names and metadata. Keep \`adk_list_workflows\` for MCP compatibility only.
6022
+
6023
+ > **Tip:** The dev server must be running (\`adk dev\`) for debugging and testing tools to work.
5638
6024
 
5639
6025
  ## Project Overview
5640
6026
 
@@ -5650,6 +6036,30 @@ npx skills add botpress/skills --skill adk
5650
6036
  `;
5651
6037
 
5652
6038
  // src/agent-init/agent-project-generator.ts
6039
+ var TEXT_EXTENSIONS = new Set([
6040
+ ".ts",
6041
+ ".tsx",
6042
+ ".js",
6043
+ ".jsx",
6044
+ ".mjs",
6045
+ ".cjs",
6046
+ ".json",
6047
+ ".md",
6048
+ ".txt",
6049
+ ".yml",
6050
+ ".yaml",
6051
+ ".html",
6052
+ ".css",
6053
+ ".svg",
6054
+ ".gitignore"
6055
+ ]);
6056
+ function isTextFile(filename) {
6057
+ if (filename === ".gitignore")
6058
+ return true;
6059
+ const ext = path15.extname(filename).toLowerCase();
6060
+ return TEXT_EXTENSIONS.has(ext);
6061
+ }
6062
+
5653
6063
  class AgentProjectGenerator {
5654
6064
  projectPath;
5655
6065
  projectName;
@@ -5661,16 +6071,125 @@ class AgentProjectGenerator {
5661
6071
  this.packageManager = packageManager;
5662
6072
  this.template = template;
5663
6073
  }
5664
- async generate() {
5665
- this.ensureEmptyDirectory();
5666
- this.createPackageJson();
5667
- await this.createAgentConfig();
5668
- this.createTsConfig();
5669
- this.createAgentJson();
5670
- this.createGitIgnore();
5671
- await this.createReadme();
5672
- this.createAIAssistantInstructions();
5673
- await this.createSourceStructure();
6074
+ async generate() {
6075
+ this.ensureEmptyDirectory();
6076
+ const templateDir = this.resolveTemplateDir();
6077
+ await this.copyTemplateFiles(templateDir);
6078
+ if (!fs11.existsSync(path15.join(this.projectPath, "agent.json"))) {
6079
+ this.createAgentJson();
6080
+ }
6081
+ if (!fs11.existsSync(path15.join(this.projectPath, "CLAUDE.md"))) {
6082
+ this.createAIAssistantInstructions();
6083
+ }
6084
+ }
6085
+ static getAvailableTemplates() {
6086
+ try {
6087
+ const templatesRoot = AgentProjectGenerator.getTemplatesRoot();
6088
+ const configPath = path15.join(templatesRoot, "template.config.json");
6089
+ if (!fs11.existsSync(configPath)) {
6090
+ return [];
6091
+ }
6092
+ const raw = fs11.readFileSync(configPath, "utf-8");
6093
+ const registry = JSON.parse(raw);
6094
+ return registry.templates;
6095
+ } catch {
6096
+ return [];
6097
+ }
6098
+ }
6099
+ getPostInitCommands() {
6100
+ const templates = AgentProjectGenerator.getAvailableTemplates();
6101
+ const config = templates.find((t) => t.name === this.template);
6102
+ return config?.postInit ?? [];
6103
+ }
6104
+ static _templatesRootOverride = null;
6105
+ static setTemplatesRoot(dir) {
6106
+ AgentProjectGenerator._templatesRootOverride = dir;
6107
+ }
6108
+ static getTemplatesRoot() {
6109
+ if (AgentProjectGenerator._templatesRootOverride) {
6110
+ return AgentProjectGenerator._templatesRootOverride;
6111
+ }
6112
+ const candidates = [];
6113
+ try {
6114
+ const esmRequire = createRequire4(import.meta.url);
6115
+ const adkPkgPath = esmRequire.resolve("@botpress/adk/package.json");
6116
+ const adkRoot = path15.dirname(adkPkgPath);
6117
+ candidates.push(path15.join(adkRoot, "dist", "templates"));
6118
+ candidates.push(path15.join(adkRoot, "templates"));
6119
+ } catch {}
6120
+ const thisDir = path15.dirname(fileURLToPath(import.meta.url));
6121
+ candidates.push(path15.resolve(thisDir, "templates"));
6122
+ candidates.push(path15.resolve(thisDir, "../../templates"));
6123
+ candidates.push(path15.join(path15.dirname(process.execPath), "templates"));
6124
+ for (const candidate of candidates) {
6125
+ if (fs11.existsSync(candidate) && fs11.existsSync(path15.join(candidate, "template.config.json"))) {
6126
+ return candidate;
6127
+ }
6128
+ }
6129
+ const tried = candidates.map((c) => ` - ${c}`).join(`
6130
+ `);
6131
+ throw new Error(`Could not find templates directory.
6132
+
6133
+ Searched:
6134
+ ${tried}
6135
+
6136
+ Ensure the ADK package is properly installed or run the CLI build.`);
6137
+ }
6138
+ resolveTemplateDir() {
6139
+ const templatesRoot = AgentProjectGenerator.getTemplatesRoot();
6140
+ const templateDir = path15.join(templatesRoot, this.template);
6141
+ if (!fs11.existsSync(templateDir)) {
6142
+ const available = AgentProjectGenerator.getAvailableTemplates();
6143
+ const names = available.map((t) => ` - ${t.name}: ${t.description}`).join(`
6144
+ `);
6145
+ throw new Error(`Unknown template: "${this.template}"
6146
+
6147
+ Available templates:
6148
+ ${names || " (none found)"}`);
6149
+ }
6150
+ const configFile = path15.join(templateDir, "agent.config.ts");
6151
+ if (!fs11.existsSync(configFile)) {
6152
+ throw new Error(`Template "${this.template}" is missing agent.config.ts`);
6153
+ }
6154
+ return templateDir;
6155
+ }
6156
+ async copyTemplateFiles(templateDir) {
6157
+ const substitutions = {
6158
+ "{{projectName}}": this.projectName,
6159
+ "{{runtimeVersion}}": "1.18.0-beta.1",
6160
+ "{{packageManager}}": this.packageManager
6161
+ };
6162
+ await this.copyDirRecursive(templateDir, this.projectPath, substitutions);
6163
+ }
6164
+ async copyDirRecursive(src, dest, substitutions) {
6165
+ if (!fs11.existsSync(dest)) {
6166
+ fs11.mkdirSync(dest, { recursive: true });
6167
+ }
6168
+ const entries = fs11.readdirSync(src, { withFileTypes: true });
6169
+ for (const entry of entries) {
6170
+ const srcPath = path15.join(src, entry.name);
6171
+ const destPath = path15.join(dest, entry.name);
6172
+ if (entry.isDirectory()) {
6173
+ await this.copyDirRecursive(srcPath, destPath, substitutions);
6174
+ } else if (isTextFile(entry.name)) {
6175
+ let content = fs11.readFileSync(srcPath, "utf-8");
6176
+ for (const [placeholder, value] of Object.entries(substitutions)) {
6177
+ content = content.replaceAll(placeholder, value);
6178
+ }
6179
+ const remaining = content.match(/\{\{[a-zA-Z]+\}\}/g);
6180
+ if (remaining) {
6181
+ console.warn(`Warning: unresolved placeholders in ${entry.name}: ${remaining.join(", ")}`);
6182
+ }
6183
+ if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
6184
+ try {
6185
+ content = await formatCode(content, destPath);
6186
+ } catch {}
6187
+ }
6188
+ fs11.writeFileSync(destPath, content);
6189
+ } else {
6190
+ fs11.copyFileSync(srcPath, destPath);
6191
+ }
6192
+ }
5674
6193
  }
5675
6194
  ensureEmptyDirectory() {
5676
6195
  if (!fs11.existsSync(this.projectPath)) {
@@ -5681,229 +6200,19 @@ class AgentProjectGenerator {
5681
6200
  throw new Error(`Directory ${this.projectPath} is not empty. Please use an empty directory.`);
5682
6201
  }
5683
6202
  }
5684
- createPackageJson() {
5685
- const packageJson = {
5686
- name: this.projectName,
5687
- version: "1.0.0",
5688
- description: `A Botpress Agent built with the ADK`,
5689
- type: "module",
5690
- packageManager: this.packageManager === "npm" ? undefined : `${this.packageManager}@latest`,
5691
- scripts: {
5692
- dev: "adk dev",
5693
- build: "adk build",
5694
- deploy: "adk deploy"
5695
- },
5696
- dependencies: {
5697
- "@botpress/runtime": `^${"1.16.7"}`
5698
- },
5699
- devDependencies: {
5700
- typescript: "^5.9.3"
5701
- }
5702
- };
5703
- if (packageJson.packageManager === undefined) {
5704
- delete packageJson.packageManager;
5705
- }
5706
- this.writeJsonFile("package.json", packageJson);
5707
- }
5708
- getDefaultDependencies() {
5709
- const dependencies = {
5710
- integrations: {}
5711
- };
5712
- if (this.template === "hello-world") {
5713
- dependencies.integrations = {
5714
- chat: {
5715
- version: "chat@latest",
5716
- enabled: true
5717
- },
5718
- webchat: {
5719
- version: "webchat@latest",
5720
- enabled: true
5721
- }
5722
- };
5723
- }
5724
- return dependencies;
5725
- }
5726
- async createAgentConfig() {
5727
- const dependencies = this.getDefaultDependencies();
5728
- const integrationsJson = JSON.stringify(dependencies.integrations, null, 4).replace(/\n/g, `
5729
- `);
5730
- const defaultModels = this.template === "hello-world" ? `
5731
- defaultModels: {
5732
- autonomous: "cerebras:gpt-oss-120b",
5733
- zai: "cerebras:gpt-oss-120b",
5734
- },
5735
- ` : "";
5736
- const agentConfig = `import { z, defineConfig } from '@botpress/runtime';
5737
-
5738
- export default defineConfig({
5739
- name: '${this.projectName}',
5740
- description: 'An AI agent built with Botpress ADK',
5741
- ${defaultModels}
5742
- bot: {
5743
- state: z.object({}),
5744
- },
5745
-
5746
- user: {
5747
- state: z.object({}),
5748
- },
5749
-
5750
- dependencies: {
5751
- integrations: ${integrationsJson},
5752
- },
5753
- });
5754
- `;
5755
- await this.writeFormattedFile("agent.config.ts", agentConfig);
5756
- }
5757
- createTsConfig() {
5758
- const tsConfig = {
5759
- compilerOptions: {
5760
- target: "ES2022",
5761
- module: "ES2022",
5762
- moduleResolution: "Bundler",
5763
- lib: ["ES2022", "DOM"],
5764
- outDir: "./dist",
5765
- rootDir: ".",
5766
- strict: true,
5767
- esModuleInterop: true,
5768
- allowSyntheticDefaultImports: true,
5769
- skipLibCheck: true,
5770
- forceConsistentCasingInFileNames: true,
5771
- moduleDetection: "force",
5772
- resolveJsonModule: true,
5773
- paths: {
5774
- "@botpress/runtime/_types/*": ["./.adk/*-types"]
5775
- }
5776
- },
5777
- include: ["src/**/*", ".adk/**/*"],
5778
- exclude: ["node_modules", "dist"]
5779
- };
5780
- this.writeJsonFile("tsconfig.json", tsConfig);
5781
- }
5782
6203
  createAgentJson() {
5783
6204
  const agentJson = {};
5784
6205
  this.writeJsonFile("agent.json", agentJson);
5785
6206
  }
5786
- createGitIgnore() {
5787
- const gitIgnore = `# Dependencies
5788
- node_modules/
5789
- .pnpm-store/
5790
-
5791
- # Build outputs
5792
- dist/
5793
- .adk/
5794
-
5795
- # Environment files
5796
- .env
5797
- .env.local
5798
- .env.production
5799
-
5800
- # IDE files
5801
- .vscode/
5802
- .idea/
5803
- *.swp
5804
- *.swo
5805
-
5806
- # OS files
5807
- .DS_Store
5808
- Thumbs.db
5809
-
5810
- # Logs
5811
- *.log
5812
- logs/
5813
-
5814
- # Runtime files
5815
- *.pid
5816
- *.seed
5817
- *.pid.lock
5818
- `;
5819
- this.writeFile(".gitignore", gitIgnore);
5820
- }
5821
- async createReadme() {
5822
- const installCommand = this.packageManager === "npm" ? "npm install" : this.packageManager === "yarn" ? "yarn install" : `${this.packageManager} install`;
5823
- const readme = `# ${this.projectName}
5824
-
5825
- A Botpress Agent built with the ADK.
5826
-
5827
- ## Getting Started
5828
-
5829
- 1. Install dependencies:
5830
- \`\`\`bash
5831
- ${installCommand}
5832
- \`\`\`
5833
-
5834
- 2. Start development server:
5835
- \`\`\`bash
5836
- adk dev
5837
- \`\`\`
5838
-
5839
- 3. Deploy your agent:
5840
- \`\`\`bash
5841
- adk deploy
5842
- \`\`\`
5843
-
5844
- ## Project Structure
5845
-
5846
- - \`src/actions/\` - Define callable functions
5847
- - \`src/workflows/\` - Define long-running processes
5848
- - \`src/conversations/\` - Define conversation handlers
5849
- - \`src/tables/\` - Define data storage schemas
5850
- - \`src/triggers/\` - Define event subscriptions
5851
- - \`src/knowledge/\` - Add knowledge base files
5852
-
5853
- ## Learn More
5854
-
5855
- - [ADK Documentation](https://botpress.com/docs/adk)
5856
- - [Botpress Platform](https://botpress.com)
5857
- `;
5858
- await this.writeFormattedFile("README.md", readme);
5859
- }
5860
6207
  createAIAssistantInstructions() {
5861
6208
  const content = ai_assistant_instructions_template_default;
5862
6209
  this.writeFile("CLAUDE.md", content);
5863
6210
  this.writeFile("AGENTS.md", content);
5864
6211
  }
5865
- async createSourceStructure() {
5866
- const srcPath = path15.join(this.projectPath, "src");
5867
- fs11.mkdirSync(srcPath);
5868
- const subdirectories = ["actions", "conversations", "knowledge", "tables", "triggers", "workflows"];
5869
- for (const subdir of subdirectories) {
5870
- const subdirPath = path15.join(srcPath, subdir);
5871
- fs11.mkdirSync(subdirPath);
5872
- if (subdir === "conversations" && this.template === "hello-world") {
5873
- const conversationContent = `import { Conversation } from "@botpress/runtime";
5874
-
5875
- export default new Conversation({
5876
- channel: "*",
5877
- handler: async ({ execute }) => {
5878
- await execute({
5879
- instructions: \`You are a helpful AI assistant built with Botpress ADK. You can assist users with their questions and tasks.\`,
5880
- });
5881
- },
5882
- });
5883
- `;
5884
- await this.writeFormattedFile(path15.join("src", subdir, "index.ts"), conversationContent);
5885
- } else {
5886
- const placeholderContent = `/**
5887
- * TODO: Add your ${subdir} here
5888
- *
5889
- * This is a placeholder file to initialize the ${subdir} directory.
5890
- * You can delete this file once you add your own ${subdir}.
5891
- */
5892
-
5893
- export default {};
5894
- `;
5895
- await this.writeFormattedFile(path15.join("src", subdir, "index.ts"), placeholderContent);
5896
- }
5897
- }
5898
- }
5899
6212
  writeFile(relativePath, content) {
5900
6213
  const filePath = path15.join(this.projectPath, relativePath);
5901
6214
  fs11.writeFileSync(filePath, content);
5902
6215
  }
5903
- async writeFormattedFile(relativePath, content) {
5904
- const formatted = await formatCode(content, relativePath);
5905
- this.writeFile(relativePath, formatted);
5906
- }
5907
6216
  writeJsonFile(relativePath, data) {
5908
6217
  this.writeFile(relativePath, JSON.stringify(data, null, 2) + `
5909
6218
  `);
@@ -6227,7 +6536,7 @@ if (typeof globalThis !== 'undefined') {
6227
6536
  }
6228
6537
  }
6229
6538
  // src/generators/integration-types.ts
6230
- import { transforms } from "@botpress/sdk";
6539
+ import { z as z3 } from "@botpress/sdk";
6231
6540
  import crypto2 from "crypto";
6232
6541
  import path18 from "path";
6233
6542
 
@@ -6255,6 +6564,7 @@ function getPluginAlias(pluginName) {
6255
6564
  }
6256
6565
 
6257
6566
  // src/generators/integration-types.ts
6567
+ var { transforms } = z3;
6258
6568
  var getIntegrationHash = (integration) => {
6259
6569
  return crypto2.createHash("sha256").update(`${integration.alias}|${integration.definition?.id}|${integration.definition?.version}|${integration.definition?.updatedAt}`).digest("hex");
6260
6570
  };
@@ -6487,9 +6797,10 @@ Description: ${tag?.description}`);
6487
6797
  }
6488
6798
  if (integration.definition?.configurations) {
6489
6799
  for (const [key, config] of Object.entries(integration.definition.configurations)) {
6490
- const title = config.title || key;
6491
- const description = config.description || "";
6492
- const schema = config.schema;
6800
+ const configRecord = config;
6801
+ const title = configRecord.title || key;
6802
+ const description = configRecord.description || "";
6803
+ const schema = configRecord.schema;
6493
6804
  let tsType = "{}";
6494
6805
  if (schema) {
6495
6806
  tsType = transforms.fromJSONSchema(schema).toTypescriptType();
@@ -6606,12 +6917,14 @@ async function generateClientWrapper(project) {
6606
6917
  let schema;
6607
6918
  let computed = false;
6608
6919
  if (typeof colDef === "object" && colDef !== null && "schema" in colDef) {
6609
- schema = colDef.schema;
6610
- computed = colDef.computed || false;
6920
+ const colDefRecord = colDef;
6921
+ schema = colDefRecord.schema;
6922
+ computed = colDefRecord.computed || false;
6611
6923
  } else {
6612
6924
  schema = colDef;
6613
6925
  }
6614
- const tsType = schema.toTypescriptType ? schema.toTypescriptType({ treatDefaultAsOptional: true }) : "any";
6926
+ const schemaObj = schema;
6927
+ const tsType = typeof schemaObj.toTypescriptType === "function" ? schemaObj.toTypescriptType({ treatDefaultAsOptional: true }) : "any";
6615
6928
  if (!computed) {
6616
6929
  inputColumns.push(`${colName}: ${tsType}`);
6617
6930
  }
@@ -6921,15 +7234,16 @@ export function createAdkClient(client: Client): AdkClient {
6921
7234
  }
6922
7235
  // src/bot-generator/generator.ts
6923
7236
  import dedent from "dedent";
6924
- import { existsSync as existsSync8 } from "fs";
7237
+ import { existsSync as existsSync7 } from "fs";
6925
7238
  import fs18 from "fs/promises";
6926
7239
  import path38 from "path";
6927
7240
 
6928
7241
  // src/generators/plugin-types.ts
6929
- import { transforms as transforms2 } from "@botpress/sdk";
7242
+ import { z as z4 } from "@botpress/sdk";
6930
7243
  import crypto4 from "crypto";
6931
7244
  import path20 from "path";
6932
7245
  init_utils();
7246
+ var { transforms: transforms2 } = z4;
6933
7247
  function stripRefs(schema) {
6934
7248
  if (typeof schema !== "object" || schema === null) {
6935
7249
  return schema;
@@ -7075,10 +7389,11 @@ export type PluginActions = PluginsMap<Plugins>;
7075
7389
  }
7076
7390
 
7077
7391
  // src/generators/interface-types.ts
7078
- import { transforms as transforms3 } from "@botpress/sdk";
7392
+ import { z as z5 } from "@botpress/sdk";
7079
7393
  import crypto5 from "crypto";
7080
7394
  import path22 from "path";
7081
7395
  init_utils();
7396
+ var { transforms: transforms3 } = z5;
7082
7397
  var sameMajorVersion = (a, b) => {
7083
7398
  const majorA = a.split(".")[0];
7084
7399
  const majorB = b.split(".")[0];
@@ -7804,8 +8119,10 @@ async function linkSdk(agentDir, botDir) {
7804
8119
  // src/bot-generator/dev-id-manager.ts
7805
8120
  import path32 from "path";
7806
8121
  import fs14 from "fs/promises";
7807
- import { existsSync as existsSync4 } from "fs";
8122
+ import createDebug3 from "debug";
7808
8123
  import { Client as Client14 } from "@botpress/client";
8124
+ var debug3 = createDebug3("adk:dev-id-manager");
8125
+
7809
8126
  class DevIdManager {
7810
8127
  projectPath;
7811
8128
  botProjectPath;
@@ -7840,11 +8157,26 @@ class DevIdManager {
7840
8157
  }
7841
8158
  async readProjectCache() {
7842
8159
  try {
7843
- if (existsSync4(this.projectCachePath)) {
7844
- const content = await fs14.readFile(this.projectCachePath, "utf-8");
7845
- return JSON.parse(content);
8160
+ const content = await fs14.readFile(this.projectCachePath, "utf-8");
8161
+ if (content.trim().length === 0) {
8162
+ debug3("project cache is empty, treating as bootstrap state: %s", this.projectCachePath);
8163
+ return {};
8164
+ }
8165
+ const parsed = JSON.parse(content);
8166
+ if (!isProjectCache(parsed)) {
8167
+ debug3("project cache contains unexpected JSON type, treating as bootstrap state: %s", this.projectCachePath);
8168
+ return {};
7846
8169
  }
8170
+ return parsed;
7847
8171
  } catch (error) {
8172
+ if (isMissingFileError(error)) {
8173
+ debug3("project cache missing, treating as bootstrap state: %s", this.projectCachePath);
8174
+ return {};
8175
+ }
8176
+ if (error instanceof SyntaxError) {
8177
+ debug3("project cache contains transient invalid JSON, treating as bootstrap state: %s (%O)", this.projectCachePath, error);
8178
+ return {};
8179
+ }
7848
8180
  console.error("Error reading project.cache.json:", error);
7849
8181
  }
7850
8182
  return {};
@@ -7864,7 +8196,7 @@ class DevIdManager {
7864
8196
  if (!project.agentInfo) {
7865
8197
  throw ValidationErrors.agentNotLinked();
7866
8198
  }
7867
- await project.updateAgentInfo({
8199
+ await project.updateAgentLocalInfo({
7868
8200
  devId: projectCache.devId
7869
8201
  });
7870
8202
  }
@@ -7891,18 +8223,24 @@ class DevIdManager {
7891
8223
  const client = await this.getClient();
7892
8224
  await client.getBot({ id: agentInfo.devId });
7893
8225
  return true;
7894
- } catch (error) {
8226
+ } catch {
7895
8227
  return false;
7896
8228
  }
7897
8229
  }
7898
8230
  return false;
7899
8231
  }
7900
8232
  }
8233
+ function isMissingFileError(error) {
8234
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
8235
+ }
8236
+ function isProjectCache(value) {
8237
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8238
+ }
7901
8239
 
7902
8240
  // src/bot-generator/integration-sync.ts
7903
8241
  import path33 from "path";
7904
8242
  import fs15 from "fs/promises";
7905
- import { existsSync as existsSync5 } from "fs";
8243
+ import { existsSync as existsSync4 } from "fs";
7906
8244
  class IntegrationSync {
7907
8245
  projectPath;
7908
8246
  botProjectPath;
@@ -7942,12 +8280,12 @@ class IntegrationSync {
7942
8280
  }
7943
8281
  async isIntegrationSynced(integration) {
7944
8282
  const targetFolder = path33.join(this.bpModulesPath, `integration_${integration.alias}`);
7945
- if (!existsSync5(targetFolder)) {
8283
+ if (!existsSync4(targetFolder)) {
7946
8284
  return false;
7947
8285
  }
7948
8286
  try {
7949
8287
  const indexPath = path33.join(targetFolder, "index.ts");
7950
- if (!existsSync5(indexPath)) {
8288
+ if (!existsSync4(indexPath)) {
7951
8289
  return false;
7952
8290
  }
7953
8291
  const indexContent = await fs15.readFile(indexPath, "utf-8");
@@ -7980,17 +8318,17 @@ class IntegrationSync {
7980
8318
  async renameIntegrationFolder(integration) {
7981
8319
  const sourceFolder = path33.join(this.bpModulesPath, integration.name.replace("/", "-"));
7982
8320
  const targetFolder = path33.join(this.bpModulesPath, `integration_${integration.alias}`);
7983
- if (!existsSync5(sourceFolder)) {
8321
+ if (!existsSync4(sourceFolder)) {
7984
8322
  throw new Error(`Integration folder not found: ${sourceFolder}`);
7985
8323
  }
7986
- if (existsSync5(targetFolder)) {
8324
+ if (existsSync4(targetFolder)) {
7987
8325
  await fs15.rm(targetFolder, { recursive: true, force: true });
7988
8326
  }
7989
8327
  await fs15.rename(sourceFolder, targetFolder);
7990
8328
  }
7991
8329
  async removeIntegrationFolder(alias) {
7992
8330
  const targetFolder = path33.join(this.bpModulesPath, `integration_${alias}`);
7993
- if (existsSync5(targetFolder)) {
8331
+ if (existsSync4(targetFolder)) {
7994
8332
  await fs15.rm(targetFolder, { recursive: true, force: true });
7995
8333
  }
7996
8334
  }
@@ -8025,7 +8363,7 @@ class IntegrationSync {
8025
8363
  // src/bot-generator/interface-sync.ts
8026
8364
  import path34 from "path";
8027
8365
  import fs16 from "fs/promises";
8028
- import { existsSync as existsSync6 } from "fs";
8366
+ import { existsSync as existsSync5 } from "fs";
8029
8367
  init_constants();
8030
8368
  class InterfaceSync {
8031
8369
  projectPath;
@@ -8053,12 +8391,12 @@ class InterfaceSync {
8053
8391
  }
8054
8392
  async isInterfaceSynced(interfaceInfo) {
8055
8393
  const targetFolder = path34.join(this.bpModulesPath, `interface_${pascalCase(interfaceInfo.alias)}`);
8056
- if (!existsSync6(targetFolder)) {
8394
+ if (!existsSync5(targetFolder)) {
8057
8395
  return false;
8058
8396
  }
8059
8397
  try {
8060
8398
  const indexPath = path34.join(targetFolder, "index.ts");
8061
- if (!existsSync6(indexPath)) {
8399
+ if (!existsSync5(indexPath)) {
8062
8400
  return false;
8063
8401
  }
8064
8402
  const indexContent = await fs16.readFile(indexPath, "utf-8");
@@ -8105,17 +8443,17 @@ class InterfaceSync {
8105
8443
  async renameInterfaceFolder(interfaceInfo) {
8106
8444
  const sourceFolder = path34.join(this.bpModulesPath, interfaceInfo.name);
8107
8445
  const targetFolder = path34.join(this.bpModulesPath, `interface_${pascalCase(interfaceInfo.alias)}`);
8108
- if (!existsSync6(sourceFolder)) {
8446
+ if (!existsSync5(sourceFolder)) {
8109
8447
  throw new Error(`Interface folder not found: ${sourceFolder}`);
8110
8448
  }
8111
- if (existsSync6(targetFolder)) {
8449
+ if (existsSync5(targetFolder)) {
8112
8450
  await fs16.rm(targetFolder, { recursive: true, force: true });
8113
8451
  }
8114
8452
  await fs16.rename(sourceFolder, targetFolder);
8115
8453
  }
8116
8454
  async removeInterfaceFolder(alias) {
8117
8455
  const targetFolder = path34.join(this.bpModulesPath, `interface_${pascalCase(alias)}`);
8118
- if (existsSync6(targetFolder)) {
8456
+ if (existsSync5(targetFolder)) {
8119
8457
  await fs16.rm(targetFolder, { recursive: true, force: true });
8120
8458
  }
8121
8459
  }
@@ -8150,7 +8488,7 @@ class InterfaceSync {
8150
8488
  // src/bot-generator/plugin-sync.ts
8151
8489
  import path35 from "path";
8152
8490
  import fs17 from "fs/promises";
8153
- import { existsSync as existsSync7 } from "fs";
8491
+ import { existsSync as existsSync6 } from "fs";
8154
8492
  class PluginSync {
8155
8493
  projectPath;
8156
8494
  botProjectPath;
@@ -8182,12 +8520,12 @@ class PluginSync {
8182
8520
  }
8183
8521
  async isPluginSynced(plugin) {
8184
8522
  const targetFolder = path35.join(this.bpModulesPath, `plugin_${plugin.alias}`);
8185
- if (!existsSync7(targetFolder)) {
8523
+ if (!existsSync6(targetFolder)) {
8186
8524
  return false;
8187
8525
  }
8188
8526
  try {
8189
8527
  const indexPath = path35.join(targetFolder, "index.ts");
8190
- if (!existsSync7(indexPath)) {
8528
+ if (!existsSync6(indexPath)) {
8191
8529
  return false;
8192
8530
  }
8193
8531
  const indexContent = await fs17.readFile(indexPath, "utf-8");
@@ -8220,17 +8558,17 @@ class PluginSync {
8220
8558
  async renamePluginFolder(plugin) {
8221
8559
  const sourceFolder = path35.join(this.bpModulesPath, plugin.name.replace("/", "-"));
8222
8560
  const targetFolder = path35.join(this.bpModulesPath, `plugin_${plugin.alias}`);
8223
- if (!existsSync7(sourceFolder)) {
8561
+ if (!existsSync6(sourceFolder)) {
8224
8562
  throw new Error(`Plugin folder not found: ${sourceFolder}`);
8225
8563
  }
8226
- if (existsSync7(targetFolder)) {
8564
+ if (existsSync6(targetFolder)) {
8227
8565
  await fs17.rm(targetFolder, { recursive: true, force: true });
8228
8566
  }
8229
8567
  await fs17.rename(sourceFolder, targetFolder);
8230
8568
  }
8231
8569
  async removePluginFolder(alias) {
8232
8570
  const targetFolder = path35.join(this.bpModulesPath, `plugin_${alias}`);
8233
- if (existsSync7(targetFolder)) {
8571
+ if (existsSync6(targetFolder)) {
8234
8572
  await fs17.rm(targetFolder, { recursive: true, force: true });
8235
8573
  }
8236
8574
  }
@@ -8264,10 +8602,11 @@ class PluginSync {
8264
8602
 
8265
8603
  // src/bot-generator/generator.ts
8266
8604
  init_utils();
8267
- import { transforms as transforms4 } from "@botpress/sdk";
8605
+ import { z as z6 } from "@botpress/sdk";
8268
8606
  init_constants();
8269
8607
  import { BuiltInActions as BuiltInActions2, BuiltInWorkflows as BuiltInWorkflows4, Primitives as Primitives3 } from "@botpress/runtime/internal";
8270
8608
  import { BUILT_IN_TAGS as BUILT_IN_TAGS2 } from "@botpress/runtime/definition";
8609
+ var { transforms: transforms4 } = z6;
8271
8610
  var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
8272
8611
  function isBuiltinWorkflow3(name) {
8273
8612
  return !!Object.values(BuiltInWorkflows4).find((x) => x.name === name);
@@ -8292,7 +8631,7 @@ class BotGenerator {
8292
8631
  }
8293
8632
  async listFilesRecursive(rootDir) {
8294
8633
  try {
8295
- if (!existsSync8(rootDir))
8634
+ if (!existsSync7(rootDir))
8296
8635
  return [];
8297
8636
  const result = [];
8298
8637
  const walk = async (dir, relativeBase) => {
@@ -8314,7 +8653,7 @@ class BotGenerator {
8314
8653
  }
8315
8654
  }
8316
8655
  async removeEmptyDirectories(rootDir) {
8317
- if (!existsSync8(rootDir))
8656
+ if (!existsSync7(rootDir))
8318
8657
  return;
8319
8658
  const removeIfEmpty = async (dir) => {
8320
8659
  const entries = await fs18.readdir(dir, { withFileTypes: true });
@@ -8520,7 +8859,7 @@ class BotGenerator {
8520
8859
  const integrations = await manager3.loadIntegrations(project.dependencies || {});
8521
8860
  const channels = [];
8522
8861
  for (const integration of integrations.integrations) {
8523
- if (integration.enabled && integration.definition) {
8862
+ if (integration.definition) {
8524
8863
  const alias = integration.alias;
8525
8864
  for (const channelName of Object.keys(integration.definition.channels || {})) {
8526
8865
  channels.push(`"${alias}.${channelName}"`);
@@ -8578,7 +8917,7 @@ declare module "@botpress/runtime/_types/state" {
8578
8917
  const interfacesDir = path38.join(this.projectPath, ".adk", "interfaces");
8579
8918
  const existingInterfaceFiles = await this.listFilesRecursive(interfacesDir);
8580
8919
  const interfaces = await manager3.loadInterfaces(project.dependencies || {}).then((result) => result.interfaces.filter((int) => int.definition).map((x) => x.definition));
8581
- const integrationsWithAlias = await integrationManager.loadIntegrations(project.dependencies || {}).then((result) => result.integrations.filter((int) => int.enabled && int.definition).map((x) => ({ alias: x.alias, definition: x.definition })));
8920
+ const integrationsWithAlias = await integrationManager.loadIntegrations(project.dependencies || {}).then((result) => result.integrations.filter((int) => int.definition).map((x) => ({ alias: x.alias, definition: x.definition })));
8582
8921
  let imports = new Set;
8583
8922
  let aliases = new Set;
8584
8923
  let files = new Set;
@@ -8643,10 +8982,7 @@ declare module "@botpress/runtime/_types/state" {
8643
8982
  const integrations = project.dependencies?.integrations || {};
8644
8983
  const imports = [];
8645
8984
  const integrationDefs = [];
8646
- for (const [alias, config] of Object.entries(integrations)) {
8647
- if (config.enabled === false) {
8648
- continue;
8649
- }
8985
+ for (const [alias] of Object.entries(integrations)) {
8650
8986
  const normalizedAlias = getIntegrationAlias(alias);
8651
8987
  imports.push(`import integration_${normalizedAlias} from "../bp_modules/integration_${normalizedAlias}";`);
8652
8988
  integrationDefs.push(`"${alias}": integration_${normalizedAlias}`);
@@ -8748,21 +9084,21 @@ declare module "@botpress/runtime/_types/state" {
8748
9084
  async generateBotDefinition() {
8749
9085
  const project = await AgentProject.load(this.projectPath);
8750
9086
  const integrations = project.integrations;
8751
- const enabledIntegrations = integrations.filter((i) => i.enabled);
8752
9087
  const isDeployOrBuild = this.adkCommand === "adk-deploy" || this.adkCommand === "adk-build";
8753
9088
  const configTargetBotId = isDeployOrBuild ? project.agentInfo?.botId : undefined;
8754
9089
  const serverConfigResult = await fetchServerIntegrationConfigs(project, configTargetBotId);
8755
- this.reportServerConfigSync(serverConfigResult, enabledIntegrations);
9090
+ this.reportServerConfigSync(serverConfigResult, integrations);
8756
9091
  const imports = [];
8757
9092
  const addIntegrations = [];
8758
- for (const integration of enabledIntegrations) {
9093
+ for (const integration of integrations) {
8759
9094
  const { alias, configurationType, config } = integration;
8760
9095
  const importName = `integration_${getIntegrationAlias(alias)}`;
8761
9096
  imports.push(`import ${importName} from "./bp_modules/${importName}";`);
8762
9097
  const configType = configurationType && configurationType !== "default" ? `, configurationType: "${configurationType}"` : "";
8763
9098
  const mergedConfig = mergeIntegrationConfig(serverConfigResult.configs[alias], config);
8764
9099
  const configData = Object.keys(mergedConfig).length > 0 ? `, configuration: ${JSON.stringify(mergedConfig)}` : "";
8765
- addIntegrations.push(`bot.addIntegration(${importName}, { alias: "${alias}", enabled: true${configType}${configData} });`);
9100
+ const enabled = integration.enabled ?? serverConfigResult.enabledStates[alias] ?? false;
9101
+ addIntegrations.push(`bot.addIntegration(${importName}, { alias: "${alias}", enabled: ${enabled}${configType}${configData} });`);
8766
9102
  }
8767
9103
  const depRefErrors = PluginParser.validateDependencyReferences(project.dependencies || {});
8768
9104
  if (depRefErrors.length > 0) {
@@ -9191,7 +9527,7 @@ export default bot;`;
9191
9527
  async copyAssets() {
9192
9528
  const assetsPath = path38.join(this.projectPath, "assets");
9193
9529
  const targetPath = path38.join(this.outputPath, "assets");
9194
- if (existsSync8(assetsPath)) {
9530
+ if (existsSync7(assetsPath)) {
9195
9531
  await fs18.mkdir(targetPath, { recursive: true });
9196
9532
  await this.copyDirectory(assetsPath, targetPath);
9197
9533
  }
@@ -9281,7 +9617,9 @@ export default bot;`;
9281
9617
  }
9282
9618
  {
9283
9619
  const dest = path38.join(srcDir, "triggers.ts");
9284
- const { transforms: transforms5 } = await import("@botpress/sdk");
9620
+ const {
9621
+ z: { transforms: transforms5 }
9622
+ } = await import("@botpress/sdk");
9285
9623
  const imports = new Map;
9286
9624
  const exports = new Set;
9287
9625
  const payloadTypes = {};
@@ -9527,7 +9865,7 @@ export default bot;`;
9527
9865
  }
9528
9866
  async copyAssetsRuntime() {
9529
9867
  const assetsRuntimePath = path38.join(this.projectPath, ".adk", "assets-runtime.ts");
9530
- if (existsSync8(assetsRuntimePath)) {
9868
+ if (existsSync7(assetsRuntimePath)) {
9531
9869
  const content = await fs18.readFile(assetsRuntimePath, "utf-8");
9532
9870
  await createFile(path38.join(this.outputPath, "src", "assets-runtime.ts"), await formatCode(content));
9533
9871
  }
@@ -9569,7 +9907,9 @@ async function generateBotProject(options) {
9569
9907
  }
9570
9908
  // src/tables/table-manager.ts
9571
9909
  import { Client as Client15 } from "@botpress/client";
9572
- import { transforms as transforms5 } from "@botpress/sdk";
9910
+ import { z as z7 } from "@botpress/sdk";
9911
+ var { transforms: transforms5 } = z7;
9912
+
9573
9913
  class TableManager {
9574
9914
  client;
9575
9915
  botId;
@@ -9600,13 +9940,14 @@ class TableManager {
9600
9940
  }
9601
9941
  getSchemaType(schema) {
9602
9942
  if (schema.type) {
9603
- if (schema.type === "array" && schema.items?.type) {
9604
- return `array<${schema.items.type}>`;
9943
+ const items = schema.items;
9944
+ if (schema.type === "array" && items?.type) {
9945
+ return `array<${items.type}>`;
9605
9946
  }
9606
- return schema.type;
9947
+ return String(schema.type);
9607
9948
  }
9608
9949
  if (schema.$ref) {
9609
- return schema.$ref.split("/").pop() || "unknown";
9950
+ return String(schema.$ref).split("/").pop() || "unknown";
9610
9951
  }
9611
9952
  return "unknown";
9612
9953
  }
@@ -9617,8 +9958,8 @@ class TableManager {
9617
9958
  } else if (Math.abs(oldPosition - newPosition) <= 1) {
9618
9959
  score += 1;
9619
9960
  }
9620
- const oldDesc = (oldSchema?.description || "").toLowerCase();
9621
- const newDesc = (newSchema?.description || "").toLowerCase();
9961
+ const oldDesc = String(oldSchema?.description || "").toLowerCase();
9962
+ const newDesc = String(newSchema?.description || "").toLowerCase();
9622
9963
  if (oldDesc && newDesc && oldDesc === newDesc) {
9623
9964
  score += 5;
9624
9965
  } else if (oldDesc && newDesc && oldDesc.length > 5 && newDesc.length > 5) {
@@ -9633,8 +9974,10 @@ class TableManager {
9633
9974
  }
9634
9975
  }
9635
9976
  }
9636
- const oldOptional = oldSchema?.type?.includes("null") || oldSchema?.nullable === true;
9637
- const newOptional = newSchema?.type?.includes("null") || newSchema?.nullable === true;
9977
+ const oldType = oldSchema?.type;
9978
+ const newType = newSchema?.type;
9979
+ const oldOptional = typeof oldType === "string" && oldType.includes("null") || oldSchema?.nullable === true;
9980
+ const newOptional = typeof newType === "string" && newType.includes("null") || newSchema?.nullable === true;
9638
9981
  if (oldOptional === newOptional) {
9639
9982
  score += 1;
9640
9983
  }
@@ -10586,6 +10929,7 @@ class KnowledgeManager {
10586
10929
  const sourcesToSync = options.force ? item.sources || [] : (item.sources || []).filter((s) => s.needsSync);
10587
10930
  const directorySourcesToSync = sourcesToSync.filter((s) => s.dsType === "document");
10588
10931
  const websiteSourcesToSync = sourcesToSync.filter((s) => s.dsType === "web-page");
10932
+ let currentTags = { ...remoteKb.tags ?? {} };
10589
10933
  for (const sourceStatus of directorySourcesToSync) {
10590
10934
  const source = kbRef.definition.sources?.find((s) => s.id === sourceStatus.dsId);
10591
10935
  if (!source || !DataSource.isDirectory(source))
@@ -10598,27 +10942,52 @@ class KnowledgeManager {
10598
10942
  syncOutput.deleted.push(...sourceOutput.deleted);
10599
10943
  syncOutput.errors.push(...sourceOutput.errors);
10600
10944
  const sourceHash = await this.computeDirectorySourceHash(source.directoryPath, source.filterFn);
10601
- await this.updateSourceHash(remoteKb.id, item.kb.name, source.id, sourceHash, remoteKb.tags);
10945
+ await this.updateSourceHash(remoteKb.id, item.kb.name, source.id, sourceHash, currentTags);
10946
+ currentTags = {
10947
+ ...currentTags,
10948
+ [sourceTag(source.id, "hash")]: sourceHash,
10949
+ [sourceTag(source.id, "lastupdatedat")]: new Date().toISOString()
10950
+ };
10602
10951
  }
10603
- if (websiteSourcesToSync.length > 0) {
10952
+ for (const sourceStatus of websiteSourcesToSync) {
10953
+ const source = kbRef.definition.sources?.find((s) => s.id === sourceStatus.dsId);
10954
+ if (!source || !DataSource.isWebsite(source))
10955
+ continue;
10956
+ const config = source.getConfig();
10957
+ if (config.mode === "website") {
10958
+ console.warn(` ⚠️ Skipping website source "${source.id}": website mode requires browser integration for URL discovery. Use WebsiteSource.fromSitemap() instead.`);
10959
+ continue;
10960
+ }
10961
+ if (config.fetchStrategy === "integration:browser") {
10962
+ console.warn(` ⚠️ Skipping website source "${source.id}": integration:browser fetch strategy requires browser integration. Use fetch: 'node:fetch' or a custom fetch function instead.`);
10963
+ continue;
10964
+ }
10965
+ await this.deleteLegacyWebsiteFiles(item.kb.name, sourceStatus.dsId);
10604
10966
  try {
10605
- for (const sourceStatus of websiteSourcesToSync) {
10606
- await this.deleteLegacyWebsiteFiles(item.kb.name, sourceStatus.dsId);
10607
- }
10608
- console.log(` Triggering website sync workflow for ${websiteSourcesToSync.length} source(s)...`);
10609
- const { workflowId } = await this.syncWebsiteSource(item.kb.name, remoteKb.id, options.force || false);
10610
- result.websiteSyncs.push({ kbName: item.kb.name, workflowId });
10611
- for (const sourceStatus of websiteSourcesToSync) {
10612
- const source = kbRef.definition.sources?.find((s) => s.id === sourceStatus.dsId);
10613
- if (!source || !DataSource.isWebsite(source))
10614
- continue;
10615
- const config = source.getConfig();
10616
- const configHash = this.computeConfigHash(config);
10617
- await this.updateSourceHash(remoteKb.id, item.kb.name, source.id, configHash, remoteKb.tags);
10618
- }
10967
+ this.assertBotId("sync website source");
10968
+ console.log(` Syncing website source "${source.id}" locally...`);
10969
+ const sourceOutput = await source.syncDirect(client, this.botId, {
10970
+ dsId: source.id,
10971
+ kbName: item.kb.name,
10972
+ kbId: remoteKb.id,
10973
+ force: options.force || false
10974
+ });
10975
+ syncOutput.processed += sourceOutput.processed;
10976
+ syncOutput.added.push(...sourceOutput.added);
10977
+ syncOutput.updated.push(...sourceOutput.updated);
10978
+ syncOutput.deleted.push(...sourceOutput.deleted);
10979
+ syncOutput.errors.push(...sourceOutput.errors.map((e) => typeof e === "string" ? { file: source.id, error: e } : e));
10980
+ const configHash = this.computeConfigHash(config);
10981
+ await this.updateSourceHash(remoteKb.id, item.kb.name, source.id, configHash, currentTags);
10982
+ currentTags = {
10983
+ ...currentTags,
10984
+ [sourceTag(source.id, "hash")]: configHash,
10985
+ [sourceTag(source.id, "lastupdatedat")]: new Date().toISOString()
10986
+ };
10619
10987
  } catch (error) {
10620
10988
  const errorMsg = error instanceof Error ? error.message : String(error);
10621
- console.warn(` Warning: Could not trigger website sync: ${errorMsg}`);
10989
+ console.error(` Failed to sync website source "${source.id}": ${errorMsg}`);
10990
+ syncOutput.errors.push({ file: source.id, error: errorMsg });
10622
10991
  }
10623
10992
  }
10624
10993
  result.synced.push({ name: item.kb.name, result: syncOutput });
@@ -10762,7 +11131,7 @@ class KnowledgeManager {
10762
11131
  }
10763
11132
  }
10764
11133
  // src/knowledge/sync-formatter.ts
10765
- import { readFileSync } from "fs";
11134
+ import { readFileSync as readFileSync3 } from "fs";
10766
11135
  import { join as join8 } from "path";
10767
11136
  var getAdkVersion = () => {
10768
11137
  try {
@@ -10771,7 +11140,7 @@ var getAdkVersion = () => {
10771
11140
  } catch {
10772
11141
  try {
10773
11142
  const adkPackagePath = join8(process.cwd(), "node_modules/@botpress/adk/package.json");
10774
- const pkg = JSON.parse(readFileSync(adkPackagePath, "utf-8"));
11143
+ const pkg = JSON.parse(readFileSync3(adkPackagePath, "utf-8"));
10775
11144
  return pkg.version;
10776
11145
  } catch {
10777
11146
  return "unknown";
@@ -10862,10 +11231,10 @@ class KBSyncFormatter {
10862
11231
  }
10863
11232
  }
10864
11233
  // src/file-watcher/watcher.ts
10865
- import { watch as watch2, readdirSync as readdirSync2 } from "fs";
11234
+ import { watch as watch2, readdirSync as readdirSync2, statSync } from "fs";
10866
11235
  import { EventEmitter as EventEmitter3 } from "events";
10867
11236
  import { join as join9, relative as relative3 } from "path";
10868
- import { existsSync as existsSync9 } from "fs";
11237
+ import { existsSync as existsSync8 } from "fs";
10869
11238
 
10870
11239
  class FileWatcher2 extends EventEmitter3 {
10871
11240
  projectPath;
@@ -10883,12 +11252,12 @@ class FileWatcher2 extends EventEmitter3 {
10883
11252
  const rootFiles = ["package.json", "agent.json", "agent.config.ts"];
10884
11253
  for (const file of rootFiles) {
10885
11254
  const filePath = join9(this.projectPath, file);
10886
- if (existsSync9(filePath)) {
11255
+ if (existsSync8(filePath)) {
10887
11256
  this.watchFile(filePath);
10888
11257
  }
10889
11258
  }
10890
11259
  const srcPath = join9(this.projectPath, "src");
10891
- if (existsSync9(srcPath)) {
11260
+ if (existsSync8(srcPath)) {
10892
11261
  this.initializeDirectoryState(srcPath);
10893
11262
  this.watchDirectory(srcPath);
10894
11263
  }
@@ -10904,7 +11273,7 @@ class FileWatcher2 extends EventEmitter3 {
10904
11273
  this.updateFileState(fullPath);
10905
11274
  }
10906
11275
  }
10907
- } catch (error) {}
11276
+ } catch {}
10908
11277
  }
10909
11278
  watchFile(filePath) {
10910
11279
  if (this.watchers.has(filePath)) {
@@ -10937,7 +11306,13 @@ class FileWatcher2 extends EventEmitter3 {
10937
11306
  }
10938
11307
  }
10939
11308
  handleFileChange(filePath) {
10940
- const fileExists = existsSync9(filePath);
11309
+ try {
11310
+ if (existsSync8(filePath) && statSync(filePath).isDirectory()) {
11311
+ this.scanDirectoryForChanges(filePath);
11312
+ return;
11313
+ }
11314
+ } catch {}
11315
+ const fileExists = existsSync8(filePath);
10941
11316
  const previousState = this.fileStates.get(filePath);
10942
11317
  let changeType;
10943
11318
  if (!fileExists && previousState !== undefined) {
@@ -10957,12 +11332,7 @@ class FileWatcher2 extends EventEmitter3 {
10957
11332
  path: relativePath,
10958
11333
  type: changeType
10959
11334
  });
10960
- if (this.debounceTimer) {
10961
- clearTimeout(this.debounceTimer);
10962
- }
10963
- this.debounceTimer = setTimeout(() => {
10964
- this.emitPendingChanges();
10965
- }, this.debounceMs);
11335
+ this.scheduleDebouncedEmit();
10966
11336
  }
10967
11337
  emitPendingChanges() {
10968
11338
  if (this.pendingChanges.size === 0) {
@@ -10976,8 +11346,46 @@ class FileWatcher2 extends EventEmitter3 {
10976
11346
  };
10977
11347
  this.emit("change", event);
10978
11348
  }
11349
+ scanDirectoryForChanges(dirPath) {
11350
+ try {
11351
+ const entries = readdirSync2(dirPath, { withFileTypes: true });
11352
+ const currentFiles = new Set;
11353
+ for (const entry of entries) {
11354
+ const fullPath = join9(dirPath, entry.name);
11355
+ if (entry.isFile()) {
11356
+ currentFiles.add(fullPath);
11357
+ if (!this.fileStates.has(fullPath)) {
11358
+ this.updateFileState(fullPath);
11359
+ const relativePath = relative3(this.projectPath, fullPath);
11360
+ this.pendingChanges.set(relativePath, { path: relativePath, type: "added" });
11361
+ this.scheduleDebouncedEmit();
11362
+ }
11363
+ } else if (entry.isDirectory()) {
11364
+ this.scanDirectoryForChanges(fullPath);
11365
+ }
11366
+ }
11367
+ for (const [trackedPath] of this.fileStates) {
11368
+ if (trackedPath.startsWith(dirPath) && !trackedPath.includes("/", dirPath.length + 1)) {
11369
+ if (!currentFiles.has(trackedPath)) {
11370
+ this.fileStates.delete(trackedPath);
11371
+ const relativePath = relative3(this.projectPath, trackedPath);
11372
+ this.pendingChanges.set(relativePath, { path: relativePath, type: "deleted" });
11373
+ this.scheduleDebouncedEmit();
11374
+ }
11375
+ }
11376
+ }
11377
+ } catch {}
11378
+ }
11379
+ scheduleDebouncedEmit() {
11380
+ if (this.debounceTimer) {
11381
+ clearTimeout(this.debounceTimer);
11382
+ }
11383
+ this.debounceTimer = setTimeout(() => {
11384
+ this.emitPendingChanges();
11385
+ }, this.debounceMs);
11386
+ }
10979
11387
  updateFileState(filePath) {
10980
- if (existsSync9(filePath)) {
11388
+ if (existsSync8(filePath)) {
10981
11389
  this.fileStates.set(filePath, Date.now());
10982
11390
  }
10983
11391
  }
@@ -11059,7 +11467,7 @@ class AgentConfigSyncManager {
11059
11467
  }
11060
11468
 
11061
11469
  // src/preflight/formatter.ts
11062
- import { readFileSync as readFileSync2 } from "fs";
11470
+ import { readFileSync as readFileSync4 } from "fs";
11063
11471
  import { join as join10 } from "path";
11064
11472
  var getAdkVersion2 = () => {
11065
11473
  try {
@@ -11068,7 +11476,7 @@ var getAdkVersion2 = () => {
11068
11476
  } catch {
11069
11477
  try {
11070
11478
  const adkPackagePath = join10(process.cwd(), "node_modules/@botpress/adk/package.json");
11071
- const pkg = JSON.parse(readFileSync2(adkPackagePath, "utf-8"));
11479
+ const pkg = JSON.parse(readFileSync4(adkPackagePath, "utf-8"));
11072
11480
  return pkg.version;
11073
11481
  } catch {
11074
11482
  return "unknown";
@@ -11250,9 +11658,6 @@ class PreflightChecker {
11250
11658
  const toRemove = [];
11251
11659
  const toUpdate = [];
11252
11660
  for (const result of results) {
11253
- if (result.desiredInstallStatus === "disabled" && !result.needsRemoval && !result.needsConfiguration) {
11254
- continue;
11255
- }
11256
11661
  if (result.needsInstall) {
11257
11662
  toInstall.push({
11258
11663
  alias: result.alias,
@@ -11372,7 +11777,7 @@ class PreflightChecker {
11372
11777
  }
11373
11778
  // src/runner/script-runner.ts
11374
11779
  import dedent2 from "dedent";
11375
- import { existsSync as existsSync10 } from "fs";
11780
+ import { existsSync as existsSync9 } from "fs";
11376
11781
  import fs20 from "fs/promises";
11377
11782
  import path41 from "path";
11378
11783
  import { spawn } from "child_process";
@@ -11410,7 +11815,7 @@ class ScriptRunner {
11410
11815
  const botPath = path41.join(this.projectPath, ".adk", "bot");
11411
11816
  const runnerPath = path41.join(botPath, "src", "script-runner.ts");
11412
11817
  const botpressTypesPath = path41.join(botPath, ".botpress", "implementation", "index.ts");
11413
- const needsRegenerate = this.forceRegenerate || !existsSync10(runnerPath) || !existsSync10(botpressTypesPath);
11818
+ const needsRegenerate = this.forceRegenerate || !existsSync9(runnerPath) || !existsSync9(botpressTypesPath);
11414
11819
  if (needsRegenerate) {
11415
11820
  try {
11416
11821
  await generateAssetsTypes(project.path);
@@ -11568,7 +11973,7 @@ class ScriptRunner {
11568
11973
  initialize: async () => {
11569
11974
  const botModule = await import(runtimePath);
11570
11975
  const runtimeModule = await import("@botpress/runtime/runtime");
11571
- const { Autonomous } = await import("@botpress/runtime");
11976
+ const { Autonomous: Autonomous2 } = await import("@botpress/runtime");
11572
11977
  const { context, agentRegistry } = runtimeModule;
11573
11978
  const { Client: Client18 } = await import("@botpress/client");
11574
11979
  const { BotSpecificClient, BotLogger } = await import("@botpress/sdk");
@@ -11590,7 +11995,7 @@ class ScriptRunner {
11590
11995
  botId,
11591
11996
  client,
11592
11997
  cognitive,
11593
- citations: new Autonomous.CitationsManager,
11998
+ citations: new Autonomous2.CitationsManager,
11594
11999
  logger,
11595
12000
  configuration: configuration ?? {},
11596
12001
  integrations: agentRegistry.integrations,
@@ -11606,7 +12011,7 @@ class ScriptRunner {
11606
12011
  async run(scriptPath, options = {}) {
11607
12012
  const { botPath, runnerPath, project } = await this.prepare();
11608
12013
  const absoluteScriptPath = path41.isAbsolute(scriptPath) ? scriptPath : path41.resolve(this.projectPath, scriptPath);
11609
- if (!existsSync10(absoluteScriptPath)) {
12014
+ if (!existsSync9(absoluteScriptPath)) {
11610
12015
  throw new Error(`Script not found: ${absoluteScriptPath}`);
11611
12016
  }
11612
12017
  const botId = this.prod ? project.agentInfo?.botId : project.agentInfo?.devId || project.agentInfo?.botId;
@@ -11695,42 +12100,78 @@ Either run from within an agent project directory, or provide projectPath explic
11695
12100
  return runner.setupTestRuntime({ env: options.env });
11696
12101
  }
11697
12102
  // src/eval/types.ts
11698
- function defineEval(def) {
11699
- return def;
12103
+ class Eval {
12104
+ name;
12105
+ description;
12106
+ tags;
12107
+ type;
12108
+ setup;
12109
+ conversation;
12110
+ outcome;
12111
+ options;
12112
+ constructor(def) {
12113
+ this.name = def.name;
12114
+ this.description = def.description;
12115
+ this.tags = def.tags;
12116
+ this.type = def.type;
12117
+ this.setup = def.setup;
12118
+ this.conversation = def.conversation;
12119
+ this.outcome = def.outcome;
12120
+ this.options = def.options;
12121
+ }
11700
12122
  }
11701
12123
  // src/eval/loader.ts
11702
- import { readdirSync as readdirSync3, existsSync as existsSync11 } from "fs";
12124
+ import { readdirSync as readdirSync3, existsSync as existsSync10 } from "fs";
11703
12125
  import { resolve as resolve3 } from "path";
12126
+ function isEvalDefinition(value) {
12127
+ return value !== null && typeof value === "object" && typeof value.name === "string" && value.name !== "" && Array.isArray(value.conversation);
12128
+ }
11704
12129
  async function loadEvalFile(filePath) {
11705
12130
  const absPath = resolve3(filePath);
11706
12131
  const mod = await import(absPath);
11707
- const def = mod.default;
11708
- if (!def || typeof def !== "object" || !def.name || !Array.isArray(def.conversation)) {
11709
- throw new Error(`Invalid eval file ${filePath}: must export default a defineEval({...}) object with name and conversation`);
12132
+ const results = [];
12133
+ for (const [key, value] of Object.entries(mod)) {
12134
+ if (key === "__esModule")
12135
+ continue;
12136
+ if (value instanceof Eval || isEvalDefinition(value)) {
12137
+ results.push(value);
12138
+ }
12139
+ }
12140
+ if (results.length === 0) {
12141
+ throw new Error(`Invalid eval file ${filePath}: no valid evals found. Export one or more \`new Eval({...})\` instances (as default or named exports).`);
11710
12142
  }
11711
- return def;
12143
+ return results;
11712
12144
  }
11713
12145
  async function loadEvalsFromDir(dirPath) {
11714
12146
  const absDir = resolve3(dirPath);
11715
- if (!existsSync11(absDir)) {
12147
+ if (!existsSync10(absDir)) {
11716
12148
  return [];
11717
12149
  }
11718
12150
  const files = readdirSync3(absDir).filter((f) => f.endsWith(".eval.ts"));
11719
12151
  const evals = [];
11720
12152
  for (const f of files) {
11721
- evals.push(await loadEvalFile(`${absDir}/${f}`));
12153
+ const defs = await loadEvalFile(`${absDir}/${f}`);
12154
+ evals.push(...defs);
12155
+ }
12156
+ const seen = new Set;
12157
+ for (const e of evals) {
12158
+ if (seen.has(e.name)) {
12159
+ throw new Error(`Duplicate eval name "${e.name}" found in ${dirPath}`);
12160
+ }
12161
+ seen.add(e.name);
11722
12162
  }
11723
12163
  return evals;
11724
12164
  }
11725
12165
  async function loadEvalByName(dirPath, name) {
11726
12166
  const absDir = resolve3(dirPath);
11727
- if (!existsSync11(absDir))
12167
+ if (!existsSync10(absDir))
11728
12168
  return null;
11729
12169
  const files = readdirSync3(absDir).filter((f) => f.endsWith(".eval.ts"));
11730
12170
  for (const f of files) {
11731
- const def = await loadEvalFile(`${absDir}/${f}`);
11732
- if (def.name === name)
11733
- return def;
12171
+ const defs = await loadEvalFile(`${absDir}/${f}`);
12172
+ const found = defs.find((d) => d.name === name);
12173
+ if (found)
12174
+ return found;
11734
12175
  }
11735
12176
  return null;
11736
12177
  }
@@ -11755,6 +12196,7 @@ function filterEvals(evals, filter) {
11755
12196
  }
11756
12197
  // src/eval/runner.ts
11757
12198
  import { Client as BpClient2 } from "@botpress/client";
12199
+ import { BUILT_IN_STATES as BUILT_IN_STATES2 } from "@botpress/runtime/internal";
11758
12200
 
11759
12201
  // src/eval/client.ts
11760
12202
  import { Client as BpClient } from "@botpress/client";
@@ -11775,20 +12217,21 @@ class ChatSession {
11775
12217
  }
11776
12218
  return this.client.user.id;
11777
12219
  }
11778
- async sendMessage(message, options = {}) {
12220
+ async ensureConversation() {
11779
12221
  if (!this.client) {
11780
12222
  throw new Error("ChatSession not connected. Call connect() first.");
11781
12223
  }
11782
- const { timeout = 30000, idleTimeout = 3000 } = options;
11783
12224
  if (!this.conversationId) {
11784
12225
  const conv = await this.client.createConversation({});
11785
12226
  this.conversationId = conv.conversation.id;
12227
+ return conv.conversation.id;
11786
12228
  }
11787
- const conversationId = this.conversationId;
12229
+ return this.conversationId;
12230
+ }
12231
+ async _awaitResponses(conversationId, trigger, options = {}) {
12232
+ const { timeout = 30000, idleTimeout = 3000 } = options;
11788
12233
  const responses = [];
11789
- const listener = await this.client.listenConversation({
11790
- id: conversationId
11791
- });
12234
+ const listener = await this.client.listenConversation({ id: conversationId });
11792
12235
  return new Promise((resolve4, reject) => {
11793
12236
  let idleTimer = null;
11794
12237
  let resolved = false;
@@ -11808,7 +12251,7 @@ class ChatSession {
11808
12251
  };
11809
12252
  const overallTimer = setTimeout(() => {
11810
12253
  if (!resolved) {
11811
- if (responses.length > 0) {
12254
+ if (responses.length > 0 || options.expectSilence) {
11812
12255
  done();
11813
12256
  } else {
11814
12257
  resolved = true;
@@ -11831,10 +12274,7 @@ class ChatSession {
11831
12274
  done();
11832
12275
  }
11833
12276
  });
11834
- this.client.createMessage({
11835
- conversationId,
11836
- payload: { type: "text", text: message }
11837
- }).then(() => {
12277
+ trigger().then(() => {
11838
12278
  resetIdle();
11839
12279
  }).catch((err) => {
11840
12280
  clearTimeout(overallTimer);
@@ -11845,6 +12285,20 @@ class ChatSession {
11845
12285
  });
11846
12286
  });
11847
12287
  }
12288
+ async sendMessage(message, options = {}) {
12289
+ if (!this.client) {
12290
+ throw new Error("ChatSession not connected. Call connect() first.");
12291
+ }
12292
+ const conversationId = await this.ensureConversation();
12293
+ return this._awaitResponses(conversationId, () => this.client.createMessage({ conversationId, payload: { type: "text", text: message } }), options);
12294
+ }
12295
+ async sendEvent(type, payload, options = {}) {
12296
+ if (!this.client) {
12297
+ throw new Error("ChatSession not connected. Call connect() first.");
12298
+ }
12299
+ const conversationId = await this.ensureConversation();
12300
+ return this._awaitResponses(conversationId, () => this.client.createEvent({ type, payload, conversationId }), options);
12301
+ }
11848
12302
  }
11849
12303
  async function discoverWebhookId(botId, token, apiUrl) {
11850
12304
  const client = new BpClient({ token, botId, apiUrl });
@@ -11852,7 +12306,7 @@ async function discoverWebhookId(botId, token, apiUrl) {
11852
12306
  const integrations = bot.integrations || {};
11853
12307
  const chat = Object.values(integrations).find((int) => int.name === "chat");
11854
12308
  const webhookId = chat?.webhookId;
11855
- if (!webhookId) {
12309
+ if (!webhookId || typeof webhookId !== "string") {
11856
12310
  throw new Error("No chat integration found on bot. Make sure the bot has the chat integration enabled.");
11857
12311
  }
11858
12312
  return webhookId;
@@ -11914,9 +12368,11 @@ async function getTraceData(conversationId, devServerUrl, options = {}) {
11914
12368
  }
11915
12369
 
11916
12370
  // src/eval/graders/llm.ts
11917
- import { Cognitive } from "@botpress/cognitive";
12371
+ import { Cognitive } from "@botpress/runtime";
11918
12372
  import { Client as Client18 } from "@botpress/client";
11919
- var JUDGE_SYSTEM_PROMPT = `You are an evaluation judge for a chatbot. You will be given:
12373
+ var DEFAULT_PASS_THRESHOLD = 3;
12374
+ function buildJudgeSystemPrompt(passThreshold) {
12375
+ return `You are an evaluation judge for a chatbot. You will be given:
11920
12376
  - The user's message
11921
12377
  - The bot's response
11922
12378
  - Grading criteria
@@ -11937,32 +12393,44 @@ Scoring guide:
11937
12393
  - 2: Barely meets criteria, significant issues
11938
12394
  - 1: Does not meet criteria
11939
12395
 
11940
- A score of 3 or above is a pass.`;
12396
+ A score of ${passThreshold} or above is a pass.`;
12397
+ }
12398
+ var MODEL_ALIASES = ["fast", "best"];
11941
12399
  var _cognitive = null;
11942
- function getCognitive() {
11943
- if (_cognitive)
11944
- return _cognitive;
11945
- const token = process.env.BP_TOKEN || process.env.ADK_TOKEN;
11946
- const botId = process.env.ADK_BOT_ID;
11947
- const apiUrl = process.env.ADK_API_URL || "https://api.botpress.cloud";
11948
- if (!token || !botId)
11949
- return null;
11950
- const client = new Client18({ token, apiUrl, botId });
11951
- _cognitive = new Cognitive({ client, __experimental_beta: true });
11952
- return _cognitive;
12400
+ var _availableModels = [];
12401
+ var _judgeModel = "fast";
12402
+ function _isModelValid(model) {
12403
+ if (MODEL_ALIASES.includes(model))
12404
+ return true;
12405
+ if (_availableModels.length === 0)
12406
+ return false;
12407
+ return _availableModels.includes(model);
11953
12408
  }
11954
- function initLLMJudge(credentials) {
12409
+ async function initLLMJudge(credentials, options) {
12410
+ const log = options?.logger ?? console;
11955
12411
  const client = new Client18({
11956
12412
  token: credentials.token,
11957
12413
  apiUrl: credentials.apiUrl,
11958
12414
  botId: credentials.botId
11959
12415
  });
11960
12416
  _cognitive = new Cognitive({ client, __experimental_beta: true });
12417
+ try {
12418
+ _availableModels = Array.from((await _cognitive.fetchRemoteModels()).keys());
12419
+ } catch (err) {
12420
+ log.warn(`Failed to fetch available models: ${err.message}. LLM judge will be unavailable.`);
12421
+ _availableModels = [];
12422
+ }
12423
+ _judgeModel = options?.model ?? "fast";
12424
+ if (_availableModels.length === 0) {
12425
+ log.warn("No available Cognitive models: cannot validate judge model.");
12426
+ } else if (!_isModelValid(_judgeModel)) {
12427
+ log.warn(`Configured LLM judge model "${_judgeModel}" is invalid. Run "adk models" to list available models.`);
12428
+ }
11961
12429
  }
11962
12430
  async function gradeLLMJudge(botResponse, criteria, context) {
12431
+ const threshold = Math.max(1, Math.min(5, context.passThreshold ?? DEFAULT_PASS_THRESHOLD));
11963
12432
  try {
11964
- const cognitive = getCognitive();
11965
- if (!cognitive) {
12433
+ if (!_cognitive) {
11966
12434
  return {
11967
12435
  assertion: `llm_judge: "${criteria}"`,
11968
12436
  pass: true,
@@ -11970,11 +12438,19 @@ async function gradeLLMJudge(botResponse, criteria, context) {
11970
12438
  actual: "SKIPPED — LLM judge unavailable: no credentials configured"
11971
12439
  };
11972
12440
  }
11973
- const { output } = await cognitive.generateContent({
11974
- model: "fast",
12441
+ if (!_isModelValid(_judgeModel)) {
12442
+ return {
12443
+ assertion: `llm_judge: "${criteria}"`,
12444
+ pass: false,
12445
+ expected: criteria,
12446
+ actual: "FAILED — invalid LLM judge model"
12447
+ };
12448
+ }
12449
+ const { output } = await _cognitive.generateContent({
12450
+ model: _judgeModel,
11975
12451
  temperature: 0,
11976
12452
  responseFormat: "json_object",
11977
- systemPrompt: JUDGE_SYSTEM_PROMPT,
12453
+ systemPrompt: buildJudgeSystemPrompt(threshold),
11978
12454
  messages: [
11979
12455
  {
11980
12456
  role: "user",
@@ -11999,9 +12475,9 @@ Criteria: ${criteria}`
11999
12475
  const verdict = JSON.parse(content);
12000
12476
  return {
12001
12477
  assertion: `llm_judge: "${criteria}"`,
12002
- pass: verdict.score >= 3,
12478
+ pass: verdict.score >= threshold,
12003
12479
  expected: criteria,
12004
- actual: `Score ${verdict.score}/5 — ${verdict.reason}`
12480
+ actual: `Score ${verdict.score}/5 (threshold: ${threshold}) — ${verdict.reason}`
12005
12481
  };
12006
12482
  } catch (err) {
12007
12483
  return {
@@ -12049,7 +12525,10 @@ async function gradeResponse(botResponse, assertions, context) {
12049
12525
  continue;
12050
12526
  }
12051
12527
  if ("llm_judge" in assertion) {
12052
- const result = await gradeLLMJudge(botResponse, assertion.llm_judge, context);
12528
+ const result = await gradeLLMJudge(botResponse, assertion.llm_judge, {
12529
+ userMessage: context.userMessage,
12530
+ passThreshold: context.judgePassThreshold
12531
+ });
12053
12532
  results.push(result);
12054
12533
  continue;
12055
12534
  }
@@ -12201,6 +12680,7 @@ function gradeTools(toolCalls, assertions) {
12201
12680
  }
12202
12681
 
12203
12682
  // src/eval/graders/state.ts
12683
+ import { BUILT_IN_STATES } from "@botpress/runtime/internal";
12204
12684
  function parseStatePath(path42) {
12205
12685
  const dot = path42.indexOf(".");
12206
12686
  if (dot === -1) {
@@ -12210,11 +12690,16 @@ function parseStatePath(path42) {
12210
12690
  const field = path42.slice(dot + 1);
12211
12691
  switch (prefix) {
12212
12692
  case "bot":
12213
- return { type: "bot", stateName: "botState", stateId: (ctx) => ctx.botId, field };
12693
+ return { type: "bot", stateName: BUILT_IN_STATES.bot, stateId: (ctx) => ctx.botId, field };
12214
12694
  case "user":
12215
- return { type: "user", stateName: "userState", stateId: (ctx) => ctx.userId, field };
12695
+ return { type: "user", stateName: BUILT_IN_STATES.user, stateId: (ctx) => ctx.userId, field };
12216
12696
  case "conversation":
12217
- return { type: "conversation", stateName: "conversationState", stateId: (ctx) => ctx.conversationId, field };
12697
+ return {
12698
+ type: "conversation",
12699
+ stateName: BUILT_IN_STATES.conversation,
12700
+ stateId: (ctx) => ctx.conversationId,
12701
+ field
12702
+ };
12218
12703
  default:
12219
12704
  throw new Error(`Unknown state type "${prefix}" in path "${path42}" — expected bot, user, or conversation`);
12220
12705
  }
@@ -12225,7 +12710,9 @@ async function fetchState(client, type, id, name) {
12225
12710
  const payload = result.state?.payload;
12226
12711
  return payload?.value ?? payload ?? null;
12227
12712
  } catch (err) {
12228
- if (err?.code === 404 || err?.message?.includes("404") || err?.message?.includes("doesn't exist")) {
12713
+ const code = err != null && typeof err === "object" && "code" in err ? err.code : undefined;
12714
+ const message = err instanceof Error ? err.message : "";
12715
+ if (code === 404 || message.includes("404") || message.includes("doesn't exist")) {
12229
12716
  return null;
12230
12717
  }
12231
12718
  throw err;
@@ -12249,7 +12736,7 @@ async function snapshotState(client, assertions, ctx) {
12249
12736
  if (assertion.changed === undefined)
12250
12737
  continue;
12251
12738
  const parsed = parseStatePath(assertion.path);
12252
- if (parsed.type === "conversation")
12739
+ if (parsed.type === "conversation" && !ctx.conversationId)
12253
12740
  continue;
12254
12741
  const payload = await fetchState(client, parsed.type, parsed.stateId(ctx), parsed.stateName);
12255
12742
  snapshots.set(assertion.path, payload ? payload[parsed.field] : undefined);
@@ -12462,6 +12949,22 @@ function gradeWorkflows(spans, assertions) {
12462
12949
  return results;
12463
12950
  }
12464
12951
 
12952
+ // src/eval/graders/timing.ts
12953
+ function gradeTiming(botDuration, assertions) {
12954
+ const results = [];
12955
+ for (const assertion of assertions) {
12956
+ const pass = matchValue(assertion.response_time, botDuration);
12957
+ const expected = operatorToString(assertion.response_time);
12958
+ results.push({
12959
+ assertion: `response_time ${expected}`,
12960
+ pass,
12961
+ expected: `Response time ${expected}`,
12962
+ actual: `${botDuration}ms`
12963
+ });
12964
+ }
12965
+ return results;
12966
+ }
12967
+
12465
12968
  // src/eval/graders/outcome.ts
12466
12969
  async function snapshotOutcomeState(client, evalDef, ctx) {
12467
12970
  if (!evalDef.outcome?.state) {
@@ -12492,9 +12995,64 @@ async function gradeOutcome(client, evalDef, ctx, traceSpans, preSnapshots) {
12492
12995
  // src/eval/runner.ts
12493
12996
  import { randomUUID } from "crypto";
12494
12997
  var DEFAULT_IDLE_TIMEOUT = 15000;
12998
+ var WORKFLOW_TRIGGER_TIMEOUT_MS = 5 * 60 * 1000;
12999
+ function buildStatePayload(value) {
13000
+ return {
13001
+ value,
13002
+ location: { type: "state" }
13003
+ };
13004
+ }
13005
+ async function seedEvalState(client, ctx, setup) {
13006
+ const state = setup?.state;
13007
+ if (!state)
13008
+ return;
13009
+ const writes = [];
13010
+ if (state.bot) {
13011
+ writes.push(client.setState({
13012
+ type: "bot",
13013
+ id: ctx.botId,
13014
+ name: BUILT_IN_STATES2.bot,
13015
+ payload: buildStatePayload(state.bot)
13016
+ }));
13017
+ }
13018
+ if (state.user) {
13019
+ writes.push(client.setState({
13020
+ type: "user",
13021
+ id: ctx.userId,
13022
+ name: BUILT_IN_STATES2.user,
13023
+ payload: buildStatePayload(state.user)
13024
+ }));
13025
+ }
13026
+ if (state.conversation) {
13027
+ if (!ctx.conversationId) {
13028
+ throw new Error("Cannot seed conversation state before a conversation is created.");
13029
+ }
13030
+ writes.push(client.setState({
13031
+ type: "conversation",
13032
+ id: ctx.conversationId,
13033
+ name: BUILT_IN_STATES2.conversation,
13034
+ payload: buildStatePayload(state.conversation)
13035
+ }));
13036
+ }
13037
+ await Promise.all(writes);
13038
+ }
13039
+ async function triggerEvalWorkflow(client, ctx, setup) {
13040
+ const workflow = setup?.workflow;
13041
+ if (!workflow)
13042
+ return;
13043
+ await client.createWorkflow({
13044
+ name: workflow.trigger,
13045
+ input: workflow.input ?? {},
13046
+ status: "pending",
13047
+ conversationId: ctx.conversationId,
13048
+ userId: ctx.userId,
13049
+ timeoutAt: new Date(Date.now() + WORKFLOW_TRIGGER_TIMEOUT_MS).toISOString()
13050
+ });
13051
+ }
12495
13052
  async function runEval(evalDef, connection, options = {}) {
12496
13053
  const devServerUrl = options.devServerUrl || "http://localhost:3001";
12497
13054
  const idleTimeout = evalDef.options?.idleTimeout ?? options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
13055
+ const judgePassThreshold = evalDef.options?.judgePassThreshold ?? options.judgePassThreshold;
12498
13056
  const start = Date.now();
12499
13057
  const turns = [];
12500
13058
  let outcomeAssertions = [];
@@ -12512,34 +13070,67 @@ async function runEval(evalDef, connection, options = {}) {
12512
13070
  }
12513
13071
  return bpClient;
12514
13072
  };
13073
+ let lastConversationId = "";
13074
+ if (evalDef.setup?.state?.conversation || evalDef.setup?.workflow) {
13075
+ lastConversationId = await session.ensureConversation();
13076
+ }
13077
+ await seedEvalState(getBpClient(), {
13078
+ botId: connection.botId,
13079
+ userId: session.userId,
13080
+ conversationId: lastConversationId || undefined
13081
+ }, evalDef.setup);
13082
+ await triggerEvalWorkflow(getBpClient(), {
13083
+ userId: session.userId,
13084
+ conversationId: lastConversationId || undefined
13085
+ }, evalDef.setup);
12515
13086
  let preSnapshots = new Map;
12516
13087
  if (evalDef.outcome?.state) {
12517
13088
  const ctx = {
12518
13089
  botId: connection.botId,
12519
13090
  userId: session.userId,
12520
- conversationId: ""
13091
+ conversationId: lastConversationId
12521
13092
  };
12522
13093
  preSnapshots = await snapshotOutcomeState(getBpClient(), evalDef, ctx);
12523
13094
  }
12524
13095
  let previousToolCallCount = 0;
12525
- let lastConversationId = "";
12526
13096
  let allSpans = [];
12527
13097
  for (let i = 0;i < evalDef.conversation.length; i++) {
12528
13098
  const turn = evalDef.conversation[i];
12529
13099
  const turnStart = Date.now();
12530
- const result = await session.sendMessage(turn.user, {
12531
- timeout: Math.max(30000, idleTimeout * 2),
12532
- idleTimeout
12533
- });
13100
+ if (turn.expectSilence && turn.assert?.response) {
13101
+ throw new Error(`Turn ${i + 1}: 'expectSilence' and 'assert.response' are mutually exclusive.`);
13102
+ }
13103
+ if (turn.user && turn.event) {
13104
+ throw new Error(`Turn ${i + 1}: 'user' and 'event' are mutually exclusive.`);
13105
+ }
13106
+ if (!turn.user && !turn.event) {
13107
+ throw new Error(`Turn ${i + 1}: must have either 'user' or 'event'.`);
13108
+ }
13109
+ const sendOptions = { timeout: Math.max(30000, idleTimeout * 2), idleTimeout, expectSilence: turn.expectSilence };
13110
+ const result = turn.event ? await session.sendEvent(turn.event.type, turn.event.payload ?? {}, sendOptions) : await session.sendMessage(turn.user, sendOptions);
12534
13111
  const botDuration = Date.now() - turnStart;
13112
+ if (!turn.expectSilence && result.responses.length === 0) {
13113
+ throw new Error(`Turn ${i + 1}: bot produced no response.`);
13114
+ }
12535
13115
  lastConversationId = result.conversationId;
12536
13116
  const botResponse = result.responses.map((r) => r.text).join(`
12537
13117
  `);
13118
+ const turnLabel = turn.event ? `[event: ${turn.event.type}]` : turn.user;
12538
13119
  const evalStart = Date.now();
12539
13120
  let assertions = [];
13121
+ if (turn.expectSilence) {
13122
+ const wasSilent = result.responses.length === 0;
13123
+ assertions.push({
13124
+ assertion: "no_response",
13125
+ pass: wasSilent,
13126
+ expected: "No response",
13127
+ actual: wasSilent ? "No response" : `Bot responded: "${botResponse}"`
13128
+ });
13129
+ }
12540
13130
  if (turn.assert?.response) {
12541
13131
  assertions = await gradeResponse(botResponse, turn.assert.response, {
12542
- userMessage: turn.user
13132
+ userMessage: turnLabel,
13133
+ judgePassThreshold
12543
13134
  });
12544
13135
  }
12545
13136
  if (turn.assert?.tools) {
@@ -12606,11 +13197,15 @@ async function runEval(evalDef, connection, options = {}) {
12606
13197
  const workflowResults = gradeWorkflows(allSpans, turn.assert.workflow);
12607
13198
  assertions.push(...workflowResults);
12608
13199
  }
13200
+ if (turn.assert?.timing) {
13201
+ const timingResults = gradeTiming(botDuration, turn.assert.timing);
13202
+ assertions.push(...timingResults);
13203
+ }
12609
13204
  const turnPass = assertions.every((a) => a.pass);
12610
13205
  const evalDuration = Date.now() - evalStart;
12611
13206
  turns.push({
12612
13207
  turnNumber: i + 1,
12613
- userMessage: turn.user,
13208
+ userMessage: turnLabel,
12614
13209
  botResponse,
12615
13210
  assertions,
12616
13211
  pass: turnPass,
@@ -12672,11 +13267,11 @@ async function runEval(evalDef, connection, options = {}) {
12672
13267
  async function runEvalSuite(config, filter) {
12673
13268
  const start = Date.now();
12674
13269
  const runId = randomUUID().replace(/-/g, "").slice(0, 26);
12675
- initLLMJudge({
13270
+ await initLLMJudge({
12676
13271
  token: config.credentials.token,
12677
13272
  apiUrl: config.credentials.apiUrl,
12678
13273
  botId: config.credentials.botId
12679
- });
13274
+ }, { model: config.evalOptions?.judgeModel, logger: config.logger });
12680
13275
  const evalsDir = `${config.agentPath}/evals`;
12681
13276
  const allEvals = await loadEvalsFromDir(evalsDir);
12682
13277
  const evals = filterEvals(allEvals, filter);
@@ -12708,7 +13303,11 @@ async function runEvalSuite(config, filter) {
12708
13303
  for (let i = 0;i < evals.length; i++) {
12709
13304
  const evalDef = evals[i];
12710
13305
  config.onProgress?.({ type: "eval_start", evalName: evalDef.name, index: i });
12711
- const report = await runEval(evalDef, connection, { devServerUrl, idleTimeout: config.evalOptions?.idleTimeout });
13306
+ const report = await runEval(evalDef, connection, {
13307
+ devServerUrl,
13308
+ idleTimeout: config.evalOptions?.idleTimeout,
13309
+ judgePassThreshold: config.evalOptions?.judgePassThreshold
13310
+ });
12712
13311
  reports.push(report);
12713
13312
  config.onProgress?.({ type: "eval_complete", evalName: evalDef.name, index: i, report });
12714
13313
  }
@@ -12726,11 +13325,11 @@ async function runEvalSuite(config, filter) {
12726
13325
  return runReport;
12727
13326
  }
12728
13327
  // src/eval/store.ts
12729
- import { existsSync as existsSync12, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync3, readdirSync as readdirSync4 } from "fs";
13328
+ import { existsSync as existsSync11, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "fs";
12730
13329
  import { join as join11 } from "path";
12731
13330
  function getRunsDir(agentPath) {
12732
13331
  const dir = join11(agentPath, ".adk", "evals", "runs");
12733
- if (!existsSync12(dir)) {
13332
+ if (!existsSync11(dir)) {
12734
13333
  mkdirSync2(dir, { recursive: true });
12735
13334
  }
12736
13335
  return dir;
@@ -12748,19 +13347,19 @@ function loadRunResult(agentPath, runId) {
12748
13347
  for (const file of files) {
12749
13348
  if (file.includes(runId)) {
12750
13349
  const filepath = join11(dir, file);
12751
- return JSON.parse(readFileSync3(filepath, "utf-8"));
13350
+ return JSON.parse(readFileSync5(filepath, "utf-8"));
12752
13351
  }
12753
13352
  }
12754
13353
  return null;
12755
13354
  }
12756
13355
  function listRunResults(agentPath, limit = 50) {
12757
13356
  const dir = getRunsDir(agentPath);
12758
- if (!existsSync12(dir))
13357
+ if (!existsSync11(dir))
12759
13358
  return [];
12760
13359
  const files = readdirSync4(dir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, limit);
12761
13360
  return files.map((file) => {
12762
13361
  const filepath = join11(dir, file);
12763
- return JSON.parse(readFileSync3(filepath, "utf-8"));
13362
+ return JSON.parse(readFileSync5(filepath, "utf-8"));
12764
13363
  });
12765
13364
  }
12766
13365
  function getLatestRun(agentPath) {
@@ -12794,11 +13393,11 @@ export {
12794
13393
  generateAssetsRuntime,
12795
13394
  filterEvals,
12796
13395
  dependenciesKeyOrder,
12797
- defineEval,
12798
13396
  coerceConfigValue,
12799
13397
  buildOpenCodeConfig,
12800
13398
  bpCliImporter,
12801
13399
  auth,
13400
+ agentLocalInfoKeyOrder,
12802
13401
  agentInfoKeyOrder,
12803
13402
  ValidationSeverity,
12804
13403
  ValidationErrors,
@@ -12825,6 +13424,7 @@ export {
12825
13424
  IntegrationCache,
12826
13425
  HubCache,
12827
13426
  FileWatcher2 as FileWatcher,
13427
+ Eval,
12828
13428
  EnhancedInterfaceCache,
12829
13429
  DevIdManager,
12830
13430
  CredentialsManager,
@@ -12845,4 +13445,4 @@ export {
12845
13445
  AgentProject
12846
13446
  };
12847
13447
 
12848
- //# debugId=BA9CF8B5F775A63B64756E2164756E21
13448
+ //# debugId=E3104143A79BCF1564756E2164756E21