@botpress/adk 1.16.6 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/agent-init/agent-project-generator.d.ts.map +1 -1
  2. package/dist/agent-project/agent-project.d.ts +9 -1
  3. package/dist/agent-project/agent-project.d.ts.map +1 -1
  4. package/dist/agent-project/agent-resolver.d.ts.map +1 -1
  5. package/dist/agent-project/dependencies-parser.d.ts.map +1 -1
  6. package/dist/agent-project/types.d.ts +27 -19
  7. package/dist/agent-project/types.d.ts.map +1 -1
  8. package/dist/agent-project/validation-errors.d.ts.map +1 -1
  9. package/dist/auth/credentials.d.ts +15 -1
  10. package/dist/auth/credentials.d.ts.map +1 -1
  11. package/dist/auth/index.d.ts +2 -0
  12. package/dist/auth/index.d.ts.map +1 -1
  13. package/dist/bot-generator/generator.d.ts.map +1 -1
  14. package/dist/commands/base-command.d.ts.map +1 -1
  15. package/dist/commands/bp-add-command.d.ts.map +1 -1
  16. package/dist/commands/bp-build-command.d.ts.map +1 -1
  17. package/dist/commands/bp-chat-command.d.ts.map +1 -1
  18. package/dist/commands/bp-deploy-command.d.ts.map +1 -1
  19. package/dist/commands/bp-dev-command.d.ts +6 -0
  20. package/dist/commands/bp-dev-command.d.ts.map +1 -1
  21. package/dist/commands/index.d.ts +4 -0
  22. package/dist/commands/index.d.ts.map +1 -1
  23. package/dist/commands/opencode-command.d.ts +45 -0
  24. package/dist/commands/opencode-command.d.ts.map +1 -0
  25. package/dist/commands/opencode-config.d.ts +29 -0
  26. package/dist/commands/opencode-config.d.ts.map +1 -0
  27. package/dist/config/coerce-config-value.d.ts.map +1 -1
  28. package/dist/config/manager.d.ts +5 -5
  29. package/dist/config/manager.d.ts.map +1 -1
  30. package/dist/eval/client.d.ts +8 -0
  31. package/dist/eval/client.d.ts.map +1 -1
  32. package/dist/eval/graders/index.d.ts +1 -0
  33. package/dist/eval/graders/index.d.ts.map +1 -1
  34. package/dist/eval/graders/llm.d.ts +6 -2
  35. package/dist/eval/graders/llm.d.ts.map +1 -1
  36. package/dist/eval/graders/outcome.d.ts +3 -2
  37. package/dist/eval/graders/outcome.d.ts.map +1 -1
  38. package/dist/eval/graders/response.d.ts +1 -0
  39. package/dist/eval/graders/response.d.ts.map +1 -1
  40. package/dist/eval/graders/state.d.ts +1 -1
  41. package/dist/eval/graders/state.d.ts.map +1 -1
  42. package/dist/eval/graders/tables.d.ts.map +1 -1
  43. package/dist/eval/graders/timing.d.ts +7 -0
  44. package/dist/eval/graders/timing.d.ts.map +1 -0
  45. package/dist/eval/graders/workflow.d.ts +3 -2
  46. package/dist/eval/graders/workflow.d.ts.map +1 -1
  47. package/dist/eval/index.d.ts +2 -2
  48. package/dist/eval/index.d.ts.map +1 -1
  49. package/dist/eval/runner.d.ts +2 -3
  50. package/dist/eval/runner.d.ts.map +1 -1
  51. package/dist/eval/traces.d.ts.map +1 -1
  52. package/dist/eval/types.d.ts +56 -14
  53. package/dist/eval/types.d.ts.map +1 -1
  54. package/dist/file-watcher/watcher.d.ts +9 -0
  55. package/dist/file-watcher/watcher.d.ts.map +1 -1
  56. package/dist/generators/client-wrapper.d.ts.map +1 -1
  57. package/dist/generators/conversation-types.d.ts.map +1 -1
  58. package/dist/generators/integration-types.d.ts.map +1 -1
  59. package/dist/generators/interface-types.d.ts.map +1 -1
  60. package/dist/generators/plugin-types.d.ts.map +1 -1
  61. package/dist/generators/table-types.d.ts.map +1 -1
  62. package/dist/generators/tests.d.ts.map +1 -1
  63. package/dist/generators/utils.d.ts +0 -11
  64. package/dist/generators/utils.d.ts.map +1 -1
  65. package/dist/generators/workflow-types.d.ts.map +1 -1
  66. package/dist/index.d.ts +3 -3
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +792 -194
  69. package/dist/index.js.map +60 -57
  70. package/dist/integrations/checker.d.ts +2 -2
  71. package/dist/integrations/checker.d.ts.map +1 -1
  72. package/dist/integrations/config-utils.d.ts +3 -3
  73. package/dist/integrations/config-utils.d.ts.map +1 -1
  74. package/dist/integrations/types.d.ts +1 -1
  75. package/dist/integrations/types.d.ts.map +1 -1
  76. package/dist/interfaces/manager.d.ts.map +1 -1
  77. package/dist/interfaces/types.d.ts +1 -1
  78. package/dist/interfaces/types.d.ts.map +1 -1
  79. package/dist/knowledge/manager.d.ts.map +1 -1
  80. package/dist/plugins/types.d.ts +1 -1
  81. package/dist/plugins/types.d.ts.map +1 -1
  82. package/dist/preflight/agent-config-sync.d.ts +2 -1
  83. package/dist/preflight/agent-config-sync.d.ts.map +1 -1
  84. package/dist/preflight/types.d.ts +8 -8
  85. package/dist/preflight/types.d.ts.map +1 -1
  86. package/dist/runner/index.d.ts +1 -1
  87. package/dist/runner/script-runner.d.ts +1 -1
  88. package/dist/runner/script-runner.d.ts.map +1 -1
  89. package/dist/tables/table-manager.d.ts.map +1 -1
  90. package/dist/tables/types.d.ts +2 -1
  91. package/dist/tables/types.d.ts.map +1 -1
  92. package/dist/utils/json-ordering.d.ts +5 -4
  93. package/dist/utils/json-ordering.d.ts.map +1 -1
  94. package/package.json +33 -32
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ 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(),
@@ -40,6 +40,9 @@ var init_types = __esm(() => {
40
40
  apiUrl: z.string().optional().describe("The Botpress API URL (e.g., https://api.botpress.cloud)"),
41
41
  devId: z.string().optional().describe("The development ID used during local development")
42
42
  });
43
+ agentLocalInfoSchema = z.object({
44
+ devId: z.string().optional().describe("The development bot ID used during local development")
45
+ });
43
46
  ((ValidationErrorCode2) => {
44
47
  ValidationErrorCode2["DIRECTORY_NOT_FOUND"] = "DIRECTORY_NOT_FOUND";
45
48
  ValidationErrorCode2["DIRECTORY_ACCESS_ERROR"] = "DIRECTORY_ACCESS_ERROR";
@@ -487,6 +490,15 @@ async function resolveAgent(agentPath, options = {}) {
487
490
  if (!agentInfo.apiUrl) {
488
491
  agentInfo.apiUrl = DEFAULT_API_URL;
489
492
  }
493
+ const localPath = path.join(agentPath, "agent.local.json");
494
+ try {
495
+ const localContent = await fs.readFile(localPath, "utf-8");
496
+ const localData = JSON.parse(localContent);
497
+ const localResult = agentLocalInfoSchema.safeParse(localData);
498
+ if (localResult.success && localResult.data.devId) {
499
+ agentInfo.devId = localResult.data.devId;
500
+ }
501
+ } catch {}
490
502
  if (requireWorkspace && !agentInfo.workspaceId) {
491
503
  throw ValidationErrors.workspaceIdMissing();
492
504
  }
@@ -525,10 +537,14 @@ import os from "os";
525
537
  class CredentialsManager {
526
538
  credentialsPath;
527
539
  configDir;
540
+ profileOverride;
528
541
  constructor() {
529
542
  this.configDir = path2.join(os.homedir(), ".adk");
530
543
  this.credentialsPath = path2.join(this.configDir, "credentials");
531
544
  }
545
+ setProfileOverride(profile) {
546
+ this.profileOverride = profile;
547
+ }
532
548
  async ensureConfigDir() {
533
549
  try {
534
550
  await fs2.mkdir(this.configDir, { recursive: true });
@@ -644,23 +660,54 @@ class CredentialsManager {
644
660
  await this.writeCredentials(store);
645
661
  }
646
662
  async getActiveCredentials() {
647
- const envProfile = process.env.ADK_PROFILE;
648
- const credentials = await this.getCredentials(envProfile);
663
+ const profileName = this.profileOverride || process.env.ADK_PROFILE;
664
+ const credentials = await this.getCredentials(profileName);
649
665
  if (!credentials) {
650
- const profileName = envProfile || "default";
651
- throw new Error(`No credentials found for profile '${profileName}'. ` + `Please run 'adk login' to authenticate.`);
666
+ const displayName = profileName || "default";
667
+ throw new Error(`No credentials found for profile '${displayName}'. ` + `Please run 'adk login' to authenticate.`);
652
668
  }
653
669
  return credentials;
654
670
  }
671
+ findProfileByApiUrl(store, apiUrl) {
672
+ const normalizedUrl = apiUrl.replace(/\/+$/, "");
673
+ for (const [, credentials] of Object.entries(store.profiles)) {
674
+ if (!credentials.apiUrl)
675
+ continue;
676
+ const profileUrl = credentials.apiUrl.replace(/\/+$/, "");
677
+ if (profileUrl === normalizedUrl) {
678
+ return credentials;
679
+ }
680
+ }
681
+ return null;
682
+ }
655
683
  async getAgentCredentials(agentPath) {
656
- const baseCredentials = await this.getActiveCredentials();
657
684
  const agentInfo = await resolveAgent(agentPath, {
658
685
  required: true,
659
686
  requireWorkspace: true
660
687
  });
688
+ const agentApiUrl = agentInfo.apiUrl;
689
+ let baseCredentials;
690
+ const hasExplicitProfile = !!(this.profileOverride || process.env.ADK_PROFILE);
691
+ if (hasExplicitProfile) {
692
+ baseCredentials = await this.getActiveCredentials();
693
+ } else {
694
+ const store = await this.readCredentials();
695
+ const matchingCredentials = this.findProfileByApiUrl(store, agentApiUrl);
696
+ if (matchingCredentials) {
697
+ console.info(`Using profile matching agent.json API URL (${agentApiUrl})`);
698
+ baseCredentials = matchingCredentials;
699
+ } else {
700
+ const profileName = store.currentProfile || "default";
701
+ const activeCredentials = store.profiles[profileName];
702
+ if (!activeCredentials) {
703
+ throw new Error(`No credentials found for profile '${profileName}'. ` + `Please run 'adk login' to authenticate.`);
704
+ }
705
+ baseCredentials = activeCredentials;
706
+ }
707
+ }
661
708
  return {
662
709
  ...baseCredentials,
663
- apiUrl: agentInfo.apiUrl || baseCredentials.apiUrl,
710
+ apiUrl: agentApiUrl,
664
711
  workspaceId: agentInfo.workspaceId,
665
712
  botId: agentInfo.botId
666
713
  };
@@ -682,7 +729,16 @@ var init_fs = () => {};
682
729
 
683
730
  // src/generators/utils.ts
684
731
  import path10 from "path";
685
- import { format } from "prettier";
732
+ async function getFormat() {
733
+ if (!_formatLoaded) {
734
+ _formatLoaded = true;
735
+ try {
736
+ const oxfmt = await import("oxfmt");
737
+ _format = oxfmt.format;
738
+ } catch {}
739
+ }
740
+ return _format;
741
+ }
686
742
  function toMultilineComment(comment) {
687
743
  if (!comment || comment.trim() === "") {
688
744
  return "";
@@ -702,40 +758,30 @@ function toMultilineComment(comment) {
702
758
  result += " */";
703
759
  return result;
704
760
  }
705
- var PRETTIER_CONFIG, formatCode = async (code, filepath) => {
761
+ var _format = null, _formatLoaded = false, formatCode = async (code, filepath) => {
706
762
  try {
707
763
  if (!code || code.length > 1e6) {
708
764
  return code;
709
765
  }
710
- return await format(code, {
711
- ...PRETTIER_CONFIG,
712
- filepath: filepath || "file.ts"
713
- });
766
+ const format = await getFormat();
767
+ if (!format)
768
+ return code;
769
+ const fileName = filepath || "file.ts";
770
+ const result = await format(fileName, code);
771
+ return result.code;
714
772
  } catch (err) {
715
- console.warn("Failed to format code with Prettier:", err);
773
+ console.warn("Failed to format code with oxfmt:", err);
716
774
  console.warn(code.slice(0, 1000).split(`
717
775
  `).map((l, i) => ` ${i.toString().padStart(2, "0")} | ${l}`).join(`
718
776
  `));
719
777
  return code;
720
778
  }
721
- }, ADK_VERSION = "1.16.6", relative2 = (from, to) => {
779
+ }, ADK_VERSION = "1.17.0", relative2 = (from, to) => {
722
780
  const fromDir = path10.dirname(from);
723
781
  const relative3 = path10.relative(fromDir, to);
724
782
  return relative3.startsWith(".") ? relative3 : `./${relative3}`;
725
783
  };
726
- var init_utils = __esm(() => {
727
- PRETTIER_CONFIG = {
728
- semi: true,
729
- singleQuote: false,
730
- tabWidth: 2,
731
- trailingComma: "es5",
732
- printWidth: 80,
733
- arrowParens: "always",
734
- requirePragma: false,
735
- insertPragma: false,
736
- proseWrap: "preserve"
737
- };
738
- });
784
+ var init_utils = () => {};
739
785
 
740
786
  // src/generators/action-types.ts
741
787
  var exports_action_types = {};
@@ -861,16 +907,31 @@ var init_integration_action_types = __esm(() => {
861
907
  var require_package = __commonJS((exports, module) => {
862
908
  module.exports = {
863
909
  name: "@botpress/adk",
864
- version: "1.16.6",
910
+ version: "1.17.0",
865
911
  description: "Core ADK library for building AI agents on Botpress",
866
- type: "module",
867
- main: "dist/index.js",
868
- types: "dist/index.d.ts",
912
+ keywords: [
913
+ "adk",
914
+ "agent",
915
+ "ai",
916
+ "botpress",
917
+ "chatbot",
918
+ "conversational-ai",
919
+ "development-kit"
920
+ ],
921
+ license: "MIT",
922
+ author: "Botpress",
923
+ repository: {
924
+ type: "git",
925
+ url: "https://github.com/botpress/adk"
926
+ },
869
927
  files: [
870
928
  "dist/**/*",
871
929
  "package.json",
872
930
  "README.md"
873
931
  ],
932
+ type: "module",
933
+ main: "dist/index.js",
934
+ types: "dist/index.d.ts",
874
935
  exports: {
875
936
  ".": {
876
937
  types: "./dist/index.d.ts",
@@ -885,33 +946,18 @@ var require_package = __commonJS((exports, module) => {
885
946
  watch: "bun run build:types && bun build ./src/index.ts --outdir ./dist --target node --format esm --sourcemap --watch",
886
947
  clean: "rm -rf dist",
887
948
  prepublishOnly: "bun run clean && bun run build",
888
- test: "vitest run",
889
- "test:watch": "vitest",
890
- "test:ui": "vitest --ui",
891
- "test:coverage": "vitest run --coverage"
892
- },
893
- keywords: [
894
- "botpress",
895
- "adk",
896
- "agent",
897
- "ai",
898
- "chatbot",
899
- "conversational-ai",
900
- "development-kit"
901
- ],
902
- author: "Botpress",
903
- license: "MIT",
904
- repository: {
905
- type: "git",
906
- url: "https://github.com/botpress/adk"
949
+ test: "bun test",
950
+ "test:watch": "bun test --watch",
951
+ "test:coverage": "bun test",
952
+ "check:type": "tsc --noEmit"
907
953
  },
908
954
  dependencies: {
909
- "@botpress/chat": "^0.5.5",
910
- "@botpress/cli": "^5.2.0",
911
- "@botpress/client": "^1.35.0",
912
- "@botpress/cognitive": "^0.3.14",
913
- "@botpress/runtime": "^1.16.6",
914
- "@botpress/sdk": "^5.4.3",
955
+ "@botpress/chat": "0.5.5",
956
+ "@botpress/cli": "6.2.2",
957
+ "@botpress/client": "1.38.1",
958
+ "@botpress/cognitive": "0.4.2",
959
+ "@botpress/runtime": "^1.17.0",
960
+ "@botpress/sdk": "6.3.1",
915
961
  "@bpinternal/jex": "^1.2.4",
916
962
  "@bpinternal/yargs-extra": "^0.0.21",
917
963
  "@parcel/watcher": "^2.5.1",
@@ -920,11 +966,12 @@ var require_package = __commonJS((exports, module) => {
920
966
  execa: "9.6.1",
921
967
  glob: "^11.1.0",
922
968
  luxon: "^3.7.2",
923
- prettier: "^3.7.4",
969
+ oxfmt: "^0.41.0",
924
970
  semver: "^7.7.2",
925
971
  "ts-morph": "^27.0.2"
926
972
  },
927
973
  devDependencies: {
974
+ "@bpinternal/zui": "2.1.0",
928
975
  "@types/debug": "^4.1.12",
929
976
  "@types/glob": "^9.0.0",
930
977
  "@types/luxon": "^3.7.1",
@@ -935,8 +982,8 @@ var require_package = __commonJS((exports, module) => {
935
982
  typescript: ">=4.5.0"
936
983
  },
937
984
  engines: {
938
- node: ">=22.0.0",
939
- bun: ">=1.3.9"
985
+ bun: ">=1.3.9",
986
+ node: ">=22.0.0"
940
987
  },
941
988
  packageManager: "bun@1.3.9"
942
989
  };
@@ -1105,6 +1152,9 @@ class Auth {
1105
1152
  async setCurrentProfile(profileName) {
1106
1153
  return this.credentialsManager.setCurrentProfile(profileName);
1107
1154
  }
1155
+ setProfileOverride(profile) {
1156
+ this.credentialsManager.setProfileOverride(profile);
1157
+ }
1108
1158
  async getActiveCredentials() {
1109
1159
  return this.credentialsManager.getActiveCredentials();
1110
1160
  }
@@ -1154,7 +1204,7 @@ import os3 from "os";
1154
1204
  import path4 from "path";
1155
1205
  import createDebug from "debug";
1156
1206
  var debug = createDebug("adk:bp-cli");
1157
- var BP_CLI_VERSION = "5.5.6";
1207
+ var BP_CLI_VERSION = "6.2.2";
1158
1208
  var BP_CLI_INSTALL_ALL = path4.join(os3.homedir(), ".adk", `bp-cli`);
1159
1209
  var BP_CLI_INSTALL_DIR = path4.join(BP_CLI_INSTALL_ALL, BP_CLI_VERSION);
1160
1210
  var BP_CLI_BIN_PATH = path4.join(BP_CLI_INSTALL_DIR, "node_modules", "@botpress", "cli", "bin.js");
@@ -1649,6 +1699,15 @@ class BpDevCommand extends BaseCommand {
1649
1699
  if (sourceMap) {
1650
1700
  bpArgs.push("--sourceMap");
1651
1701
  }
1702
+ const spanIngestUrl = `http://localhost:${this.options.spanIngestPort}`;
1703
+ const otlpEndpoint = this.options.otlpPort ? `http://localhost:${this.options.otlpPort}` : undefined;
1704
+ const traceLines = [`[bp-dev] Trace endpoints:`, ` ADK_SPAN_INGEST_URL=${spanIngestUrl}`];
1705
+ if (otlpEndpoint) {
1706
+ traceLines.push(` OTEL_EXPORTER_OTLP_ENDPOINT=${otlpEndpoint}`);
1707
+ }
1708
+ this.emit("stdout", traceLines.join(`
1709
+ `) + `
1710
+ `);
1652
1711
  this.childProcess = execa4(bpCommand, bpArgs, {
1653
1712
  cwd: botPath,
1654
1713
  env: {
@@ -1665,6 +1724,9 @@ class BpDevCommand extends BaseCommand {
1665
1724
  },
1666
1725
  WORKER_MODE: "true",
1667
1726
  WORKER_LIFETIME_MS: process.env.WORKER_LIFETIME_MS || "120000",
1727
+ ADK_SPAN_INGEST_URL: spanIngestUrl,
1728
+ ...otlpEndpoint && { OTEL_EXPORTER_OTLP_ENDPOINT: otlpEndpoint },
1729
+ ...this.options.botName && { ADK_BOT_NAME: this.options.botName },
1668
1730
  ADK_DIRECTORY: join2(botPath, ".."),
1669
1731
  AGENT_DIRECTORY: this.options.agentPath,
1670
1732
  NODE_OPTIONS: `${process.env.NODE_OPTIONS || ""} --enable-source-maps`.trim()
@@ -1891,10 +1953,11 @@ class BpChatCommand extends BaseCommand {
1891
1953
  try {
1892
1954
  await this.childProcess;
1893
1955
  } catch (error) {
1956
+ const errObj = error != null && typeof error === "object" ? error : {};
1894
1957
  this.emit("error", {
1895
- exitCode: error.exitCode || 1,
1896
- stderr: error.stderr || "",
1897
- message: error.message || "Chat command failed"
1958
+ exitCode: "exitCode" in errObj ? errObj.exitCode || 1 : 1,
1959
+ stderr: "stderr" in errObj ? errObj.stderr || "" : "",
1960
+ message: error instanceof Error ? error.message || "Chat command failed" : "Chat command failed"
1898
1961
  });
1899
1962
  throw error;
1900
1963
  }
@@ -1905,6 +1968,227 @@ class BpChatCommand extends BaseCommand {
1905
1968
  }
1906
1969
  }
1907
1970
  }
1971
+ // src/commands/opencode-command.ts
1972
+ import { execa as execa6 } from "execa";
1973
+
1974
+ // src/commands/opencode-config.ts
1975
+ var COPILOT_PROMPT = `You are the ADK Copilot — an expert assistant embedded in the Botpress Agent Development Kit control panel.
1976
+
1977
+ ## Who you help
1978
+ A developer building an AI agent with the ADK. They're running \`adk dev\` and have the control panel open alongside you. They can see traces, logs, integrations, workflows, and tables in the UI.
1979
+
1980
+ ## What you know
1981
+ The ADK is a high-level framework built on Botpress. An agent project has:
1982
+ - /actions — strongly-typed callable functions (Action from @botpress/runtime)
1983
+ - /tools — LLM-callable interfaces with natural language descriptions (Tool from @botpress/runtime)
1984
+ - /workflows — step-based, resumable long-running processes (Workflow from @botpress/runtime)
1985
+ - /conversations — channel-specific interaction handlers (Conversation from @botpress/runtime)
1986
+ - /tables — schema-validated data storage with semantic search (Table from @botpress/runtime)
1987
+ - /triggers — event subscription system (Trigger from @botpress/runtime)
1988
+ - /knowledge — RAG knowledge base documents
1989
+ - agent.config.ts — agent metadata, integrations, model configuration, variables
1990
+
1991
+ The ADK compiles these high-level primitives down to Botpress SDK primitives. Default to ADK terms. Only drop to Botpress SDK concepts when the problem requires it (SDK-level errors, compilation issues, or when the developer explicitly asks).
1992
+
1993
+ Schemas use \`z\` from @botpress/sdk (a Zod fork) — never import Zod directly.
1994
+
1995
+ You also have access to Botpress documentation search for platform-level questions (SDK, API, integrations).
1996
+
1997
+ ## How you work
1998
+
1999
+ **Orient on first interaction.** If you haven't yet, use \`adk_get_agent_info\` to learn the project's structure, primitives, and integrations before answering.
2000
+
2001
+ **Conceptual vs. project questions.** Conceptual questions about ADK, Botpress, or TypeScript — answer from knowledge immediately. Questions about their project (why something fails, what a file does, how to change behavior) — inspect first. If in doubt, answer what you can immediately and inspect in parallel.
2002
+
2003
+ **Read freely, write carefully.** Inspecting files, querying traces, and searching the hub are always safe — do them without asking. But actions that modify the project (adding integrations, editing files, executing workflows, sending messages) should be confirmed first unless the developer explicitly asked you to do it.
2004
+
2005
+ **Debug by matching the approach to the problem:**
2006
+ - Developer gives you an error message → start from the error, don't begin at "step 1"
2007
+ - Build or type error → check the file and generated types. Traces won't help.
2008
+ - Runtime behavior is wrong ("it responds wrong") → query traces (\`adk_query_traces\`) for the conversation to find the failing span
2009
+ - Nothing happens → check dev logs (\`adk_get_dev_logs\`) for silent failures, then check if the handler is registered
2010
+ - When in doubt → start with \`adk_get_dev_logs\` with error filtering, then query traces
2011
+
2012
+ **Integrations.** First check if the integration already exists in the project. If it does, inspect its current configuration. Only search the hub (\`adk_search_integrations\`) when adding a new integration. Get details (\`adk_get_integration\`) to understand actions, events, channels, and config requirements. Add it (\`adk_add_integration\`) after the developer confirms.
2013
+
2014
+ **Test iteratively.** Send messages to the running bot (\`adk_send_message\`) to verify behavior. To continue a conversation, pass back both the \`conversationId\` and \`userKey\` from the previous response. Only test against the local dev bot.
2015
+
2016
+ **Workflows.** Get the input schema first by calling \`adk_start_workflow\` without a payload, then execute by calling it again with \`payload\` set to the correct input. For workflows with no required input, pass \`payload: {}\` — omitting payload returns the schema, it does not execute.
2017
+
2018
+ **Handle failures.** If a tool returns an error, tell the developer what happened and suggest a concrete next step (e.g., "Dev server isn't running — start it with \`adk dev\`"). Don't silently retry or ignore errors.
2019
+
2020
+ **Edit with precision.** When modifying code, change only what's needed to solve the problem. Don't refactor surrounding code, add features, or "improve" things that weren't asked about. When writing ADK primitives, match ADK conventions. For utility code, match the patterns already in the project.
2021
+
2022
+ ## How you communicate
2023
+
2024
+ **Lead with the answer.** First sentence is the diagnosis, the solution, or the action you took. Context and explanation come after, if needed.
2025
+
2026
+ **Show, don't describe.** Instead of "you should add error handling," show the code change. Instead of "the trace shows a failure," show the relevant span data and what it means.
2027
+
2028
+ **Match the developer's energy.** Short question → short answer. Detailed question → detailed response. "Why is this broken?" → diagnosis + fix. "How do workflows work?" → teach.
2029
+
2030
+ **Hypothesize while verifying.** If you have a likely diagnosis, say so while you check. "This usually means X — checking your trace now" is better than silence followed by an answer.
2031
+
2032
+ **When you fix something, explain what you changed and why.** Don't apply changes silently — the developer needs to understand the fix to trust it and learn from it.
2033
+
2034
+ **When you don't know, say so plainly.** "I don't have enough context — can you share the error message?" is fine.
2035
+
2036
+ **Skip the filler.** No "Great question!", no "Let me help you with that." Just do it.
2037
+
2038
+ **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.`;
2039
+ function buildOpenCodeConfig(options) {
2040
+ const command = [options.adkBinPath, "mcp", "--cwd", options.agentPath];
2041
+ if (options.uiServerPort)
2042
+ command.push("--port", String(options.uiServerPort));
2043
+ return {
2044
+ ...options.openCodePort && {
2045
+ server: {
2046
+ port: options.openCodePort,
2047
+ ...options.corsOrigins?.length && { cors: options.corsOrigins }
2048
+ }
2049
+ },
2050
+ mcp: {
2051
+ adk: {
2052
+ type: "local",
2053
+ command
2054
+ }
2055
+ },
2056
+ agent: {
2057
+ default: {
2058
+ description: "ADK Copilot — helps build and debug Botpress ADK agents",
2059
+ prompt: COPILOT_PROMPT
2060
+ }
2061
+ }
2062
+ };
2063
+ }
2064
+
2065
+ // src/commands/opencode-command.ts
2066
+ class OpenCodeCommand extends BaseCommand {
2067
+ options;
2068
+ childProcess = null;
2069
+ killed = false;
2070
+ readyEmitted = false;
2071
+ constructor(options) {
2072
+ super();
2073
+ this.options = options;
2074
+ }
2075
+ static async isInstalled() {
2076
+ try {
2077
+ const cmd = process.platform === "win32" ? "where" : "which";
2078
+ await execa6(cmd, ["opencode"]);
2079
+ return true;
2080
+ } catch {
2081
+ return false;
2082
+ }
2083
+ }
2084
+ emitReady() {
2085
+ if (this.readyEmitted || this.killed)
2086
+ return;
2087
+ this.readyEmitted = true;
2088
+ this.emit("progress", {
2089
+ type: "ready",
2090
+ startTime: Date.now(),
2091
+ data: { port: this.options.port }
2092
+ });
2093
+ }
2094
+ async run() {
2095
+ const { port, cwd, corsOrigins = [] } = this.options;
2096
+ this.emit("progress", { type: "checking", startTime: Date.now() });
2097
+ const installed = await OpenCodeCommand.isInstalled();
2098
+ if (!installed) {
2099
+ this.emit("progress", {
2100
+ type: "error",
2101
+ startTime: Date.now(),
2102
+ data: { reason: "opencode not installed" }
2103
+ });
2104
+ return;
2105
+ }
2106
+ this.emit("progress", { type: "starting", startTime: Date.now() });
2107
+ const args = ["serve", "--port", String(port)];
2108
+ for (const origin of corsOrigins) {
2109
+ args.push("--cors", origin);
2110
+ }
2111
+ const adkBin = this.options.adkBinPath || "adk";
2112
+ const opencodeConfig = JSON.stringify(buildOpenCodeConfig({
2113
+ adkBinPath: adkBin,
2114
+ agentPath: cwd,
2115
+ uiServerPort: this.options.uiServerPort,
2116
+ openCodePort: port,
2117
+ corsOrigins
2118
+ }));
2119
+ this.childProcess = execa6("opencode", args, {
2120
+ cwd,
2121
+ env: { ...process.env, OPENCODE_CONFIG_CONTENT: opencodeConfig },
2122
+ stdio: ["ignore", "pipe", "pipe"]
2123
+ });
2124
+ if (this.childProcess.stdout) {
2125
+ this.childProcess.stdout.on("data", (data) => {
2126
+ this.emit("stdout", data.toString());
2127
+ });
2128
+ }
2129
+ if (this.childProcess.stderr) {
2130
+ this.childProcess.stderr.on("data", (data) => {
2131
+ const text = data.toString();
2132
+ this.emit("stderr", text);
2133
+ const lower = text.toLowerCase();
2134
+ if (lower.includes("eaddrinuse") || lower.includes("address already in use")) {
2135
+ this.emit("progress", {
2136
+ type: "error",
2137
+ startTime: Date.now(),
2138
+ data: { reason: `Port ${port} already in use` }
2139
+ });
2140
+ this.kill();
2141
+ }
2142
+ });
2143
+ }
2144
+ this.childProcess.then(() => {
2145
+ if (!this.killed && !this.readyEmitted) {
2146
+ this.readyEmitted = true;
2147
+ this.emit("progress", {
2148
+ type: "error",
2149
+ startTime: Date.now(),
2150
+ data: { reason: "OpenCode process exited unexpectedly" }
2151
+ });
2152
+ }
2153
+ }, (error) => {
2154
+ if (this.killed)
2155
+ return;
2156
+ this.readyEmitted = true;
2157
+ this.emit("progress", {
2158
+ type: "error",
2159
+ startTime: Date.now(),
2160
+ data: { reason: error.message || "OpenCode process exited unexpectedly" }
2161
+ });
2162
+ });
2163
+ this.pollUntilReady(port);
2164
+ }
2165
+ async pollUntilReady(port) {
2166
+ const deadline = Date.now() + 15000;
2167
+ while (!this.killed && !this.readyEmitted && Date.now() < deadline) {
2168
+ try {
2169
+ const res = await fetch(`http://localhost:${port}/`);
2170
+ if (res.ok || res.status < 500) {
2171
+ this.emitReady();
2172
+ return;
2173
+ }
2174
+ } catch {}
2175
+ await new Promise((r) => setTimeout(r, 500));
2176
+ }
2177
+ if (!this.readyEmitted && !this.killed) {
2178
+ this.emit("progress", {
2179
+ type: "error",
2180
+ startTime: Date.now(),
2181
+ data: { reason: "OpenCode server did not respond in time" }
2182
+ });
2183
+ }
2184
+ }
2185
+ kill(signal = "SIGTERM") {
2186
+ this.killed = true;
2187
+ if (this.childProcess) {
2188
+ this.childProcess.kill(signal);
2189
+ }
2190
+ }
2191
+ }
1908
2192
  // src/workspace/workspace-cache.ts
1909
2193
  import { Client as Client3 } from "@botpress/client";
1910
2194
  class WorkspaceCache {
@@ -1981,7 +2265,8 @@ function getChatClient(cwd = process.cwd()) {
1981
2265
  return Client4;
1982
2266
  }
1983
2267
  // src/utils/json-ordering.ts
1984
- var agentInfoKeyOrder = ["botId", "workspaceId", "apiUrl", "devId"];
2268
+ var agentInfoKeyOrder = ["botId", "workspaceId", "apiUrl"];
2269
+ var agentLocalInfoKeyOrder = ["devId"];
1985
2270
  var dependenciesKeyOrder = ["integrations"];
1986
2271
  var integrationKeyOrder = ["version", "enabled", "configurationType", "config"];
1987
2272
  function orderKeys(obj, keyOrder) {
@@ -2066,7 +2351,7 @@ class AssetsCacheManager {
2066
2351
  const content = await fs4.readFile(this.cachePath, "utf-8");
2067
2352
  this.cache = JSON.parse(content);
2068
2353
  return this.cache;
2069
- } catch (error) {
2354
+ } catch {
2070
2355
  this.cache = {
2071
2356
  version: "1.0",
2072
2357
  entries: {}
@@ -2599,7 +2884,7 @@ class EnhancedIntegrationCache {
2599
2884
 
2600
2885
  // src/agent-project/dependencies-parser.ts
2601
2886
  init_validation_errors();
2602
- import { z as z2, ZodIssueCode } from "@botpress/sdk";
2887
+ import { z as z2 } from "@botpress/sdk";
2603
2888
  var INTEGRATION_ALIAS_MIN_LENGTH = 2;
2604
2889
  var INTEGRATION_ALIAS_MAX_LENGTH = 100;
2605
2890
  var INTEGRATION_ALIAS_REGEX = /^(?:[a-z][a-z0-9_-]*\/)?[a-z][a-z0-9_-]*$/;
@@ -2610,10 +2895,10 @@ var integrationRefSchema = z2.string().transform((val, ctx) => {
2610
2895
  const match = val.match(/^(?:([^/]+)\/)?([^@]+)@(.+)$/);
2611
2896
  if (!match) {
2612
2897
  ctx.addIssue({
2613
- code: ZodIssueCode.custom,
2898
+ code: "custom",
2614
2899
  message: `Invalid integration version format: ${val}. Expected format: 'name@version' or 'workspace/name@version'`
2615
2900
  });
2616
- return z2.NEVER;
2901
+ return;
2617
2902
  }
2618
2903
  const [, workspace, name, version] = match;
2619
2904
  return {
@@ -2712,10 +2997,10 @@ var pluginRefSchema = z2.string().transform((val, ctx) => {
2712
2997
  const match = val.match(/^([^/@]+)@(.+)$/);
2713
2998
  if (!match) {
2714
2999
  ctx.addIssue({
2715
- code: ZodIssueCode.custom,
3000
+ code: "custom",
2716
3001
  message: `Invalid plugin version format: ${val}. Expected format: 'name@version' (no workspace prefix)`
2717
3002
  });
2718
- return z2.NEVER;
3003
+ return;
2719
3004
  }
2720
3005
  const [, name, version] = match;
2721
3006
  return {
@@ -2772,7 +3057,7 @@ class PluginParser {
2772
3057
  if (!pluginConfig.dependencies) {
2773
3058
  continue;
2774
3059
  }
2775
- for (const [depAlias, depMapping] of Object.entries(pluginConfig.dependencies)) {
3060
+ for (const [_depAlias, depMapping] of Object.entries(pluginConfig.dependencies)) {
2776
3061
  if (!integrationAliases.includes(depMapping.integrationAlias)) {
2777
3062
  errors.push(ValidationErrors.invalidPluginDependency(alias, depMapping.integrationAlias, integrationAliases));
2778
3063
  }
@@ -3043,7 +3328,7 @@ class IntegrationCache {
3043
3328
  }
3044
3329
  }
3045
3330
  return cached.definition;
3046
- } catch (error) {
3331
+ } catch {
3047
3332
  return null;
3048
3333
  }
3049
3334
  }
@@ -4432,7 +4717,7 @@ async function expandExports(options) {
4432
4717
  });
4433
4718
  return {};
4434
4719
  }
4435
- currentError = currentError.cause;
4720
+ currentError = currentError instanceof Error ? currentError.cause : undefined;
4436
4721
  }
4437
4722
  throw importError;
4438
4723
  }
@@ -4698,20 +4983,62 @@ class AgentProject {
4698
4983
  return this._assetsManager;
4699
4984
  }
4700
4985
  async createAgentInfo(info) {
4986
+ const { devId, ...agentJsonInfo } = info;
4701
4987
  const agentPath = path13.join(this._path, "agent.json");
4702
- const agentContent = stringifyWithOrder(info, agentInfoKeyOrder);
4988
+ const agentContent = stringifyWithOrder(agentJsonInfo, agentInfoKeyOrder);
4703
4989
  await fs10.writeFile(agentPath, agentContent);
4704
4990
  this._agentInfo = info;
4991
+ if (devId) {
4992
+ await this.createAgentLocalInfo({ devId });
4993
+ }
4705
4994
  }
4706
4995
  async updateAgentInfo(updates) {
4707
4996
  if (!this._agentInfo) {
4708
4997
  throw new Error("No agent.json found. Use createAgentInfo() first.");
4709
4998
  }
4710
- const updatedInfo = { ...this._agentInfo, ...updates };
4999
+ const { devId, ...agentJsonUpdates } = updates;
5000
+ const updatedInfo = { ...this._agentInfo, ...agentJsonUpdates };
5001
+ const { devId: _devId, ...agentJsonData } = updatedInfo;
4711
5002
  const agentPath = path13.join(this._path, "agent.json");
4712
- const agentContent = stringifyWithOrder(updatedInfo, agentInfoKeyOrder);
5003
+ const agentContent = stringifyWithOrder(agentJsonData, agentInfoKeyOrder);
4713
5004
  await fs10.writeFile(agentPath, agentContent);
4714
5005
  this._agentInfo = updatedInfo;
5006
+ if (devId !== undefined) {
5007
+ await this.updateAgentLocalInfo({ devId });
5008
+ }
5009
+ }
5010
+ async createAgentLocalInfo(info) {
5011
+ const localPath = path13.join(this._path, "agent.local.json");
5012
+ let existing = {};
5013
+ try {
5014
+ const content2 = await fs10.readFile(localPath, "utf-8");
5015
+ existing = JSON.parse(content2);
5016
+ } catch {}
5017
+ const merged = { ...existing, ...info };
5018
+ const content = stringifyWithOrder(merged, agentLocalInfoKeyOrder);
5019
+ await fs10.writeFile(localPath, content);
5020
+ if (this._agentInfo) {
5021
+ this._agentInfo.devId = merged.devId;
5022
+ }
5023
+ }
5024
+ async updateAgentLocalInfo(updates) {
5025
+ const localPath = path13.join(this._path, "agent.local.json");
5026
+ let existing = {};
5027
+ try {
5028
+ const content2 = await fs10.readFile(localPath, "utf-8");
5029
+ existing = JSON.parse(content2);
5030
+ } catch {}
5031
+ const updated = { ...existing, ...updates };
5032
+ for (const key of Object.keys(updated)) {
5033
+ if (updated[key] === undefined) {
5034
+ delete updated[key];
5035
+ }
5036
+ }
5037
+ const content = stringifyWithOrder(updated, agentLocalInfoKeyOrder);
5038
+ await fs10.writeFile(localPath, content);
5039
+ if (this._agentInfo) {
5040
+ this._agentInfo.devId = updated.devId;
5041
+ }
4715
5042
  }
4716
5043
  requiresAgentInfo(operation) {
4717
5044
  if (!this._agentInfo?.botId) {
@@ -4902,6 +5229,19 @@ ${err.stack?.split(`
4902
5229
  console.warn(`Failed to register data source workflows for ${kbPath}:`, error);
4903
5230
  }
4904
5231
  }
5232
+ isBarrelReexport(existing, newPath, newExport, newDefinition) {
5233
+ const isNewBarrel = /^index\.[tj]s$/i.test(path13.basename(newPath));
5234
+ const isExistingBarrel = /^index\.[tj]s$/i.test(path13.basename(existing.path));
5235
+ if (!isNewBarrel && !isExistingBarrel) {
5236
+ return false;
5237
+ }
5238
+ if (isExistingBarrel && !isNewBarrel) {
5239
+ existing.path = newPath;
5240
+ existing.export = newExport;
5241
+ existing.definition = newDefinition;
5242
+ }
5243
+ return true;
5244
+ }
4905
5245
  getChannelsList(channelSpec) {
4906
5246
  if (channelSpec === "*") {
4907
5247
  return ["*"];
@@ -4984,6 +5324,9 @@ ${err.stack?.split(`
4984
5324
  return existingChannels.some((ch) => newChannels.includes(ch));
4985
5325
  });
4986
5326
  if (overlapping) {
5327
+ if (this.isBarrelReexport(overlapping, relPath, key, definition)) {
5328
+ continue;
5329
+ }
4987
5330
  this._warnings.push({
4988
5331
  $type: "ValidationError",
4989
5332
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5001,6 +5344,9 @@ ${err.stack?.split(`
5001
5344
  } else if (Primitives2.Definitions.isKnowledgeDefinition(definition)) {
5002
5345
  const existing = this._knowledge.find((p) => p.definition.name === definition.name);
5003
5346
  if (existing) {
5347
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5348
+ continue;
5349
+ }
5004
5350
  this._warnings.push({
5005
5351
  $type: "ValidationError",
5006
5352
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5019,6 +5365,9 @@ ${err.stack?.split(`
5019
5365
  } else if (Primitives2.Definitions.isTriggerDefinition(definition)) {
5020
5366
  const existing = this._triggers.find((p) => p.definition.name === definition.name);
5021
5367
  if (existing) {
5368
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5369
+ continue;
5370
+ }
5022
5371
  this._warnings.push({
5023
5372
  $type: "ValidationError",
5024
5373
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5036,6 +5385,9 @@ ${err.stack?.split(`
5036
5385
  } else if (Primitives2.Definitions.isWorkflowDefinition(definition)) {
5037
5386
  const existing = this._workflows.find((p) => p.definition.name === definition.name);
5038
5387
  if (existing) {
5388
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5389
+ continue;
5390
+ }
5039
5391
  this._warnings.push({
5040
5392
  $type: "ValidationError",
5041
5393
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5053,6 +5405,9 @@ ${err.stack?.split(`
5053
5405
  } else if (Primitives2.Definitions.isActionDefinition(definition)) {
5054
5406
  const existing = this._actions.find((p) => p.definition.name === definition.name);
5055
5407
  if (existing) {
5408
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5409
+ continue;
5410
+ }
5056
5411
  this._warnings.push({
5057
5412
  $type: "ValidationError",
5058
5413
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5070,6 +5425,9 @@ ${err.stack?.split(`
5070
5425
  } else if (Primitives2.Definitions.isTableDefinition(definition)) {
5071
5426
  const existing = this._tables.find((p) => p.definition.name === definition.name);
5072
5427
  if (existing) {
5428
+ if (this.isBarrelReexport(existing, relPath, key, definition)) {
5429
+ continue;
5430
+ }
5073
5431
  this._warnings.push({
5074
5432
  $type: "ValidationError",
5075
5433
  code: "DUPLICATE_PRIMITIVE" /* DUPLICATE_PRIMITIVE */,
@@ -5393,22 +5751,36 @@ adk deploy # Deploy to Botpress Cloud
5393
5751
  adk chat # Chat with your agent in the terminal
5394
5752
  \`\`\`
5395
5753
 
5396
- ## AI Coding Assistant Skills
5754
+ ## MCP Tools (ADK Dev Server)
5397
5755
 
5398
- This project uses the Botpress ADK. Before making changes, use the relevant skill:
5756
+ This project includes an MCP server that provides AI coding assistants with deep ADK integration.
5757
+ Run \`adk mcp:init --all\` to generate configuration for your editor, or use the tools below directly.
5399
5758
 
5400
- | Skill | Use for |
5401
- | ------------------ | ------------------------------------------------ |
5402
- | \`/adk\` | ADK concepts, patterns, and API reference |
5403
- | \`/adk-integration\` | Finding and using Botpress integrations |
5404
- | \`/adk-debugger\` | Debugging with traces and test conversations |
5405
- | \`/adk-frontend\` | Building frontends that connect to ADK bots |
5759
+ ### Debugging & Testing
5406
5760
 
5407
- If these skills are not installed, install them:
5761
+ | Tool | Use for |
5762
+ | ------------------ | ------------------------------------------------------------ |
5763
+ | \`adk_send_message\` | Send a test message to the running bot and receive responses |
5764
+ | \`adk_query_traces\` | Query trace spans for debugging conversations and workflows |
5765
+ | \`adk_get_dev_logs\` | Get dev server logs, build output, errors, and warnings |
5408
5766
 
5409
- \`\`\`
5410
- npx skills add botpress/skills --skill adk
5411
- \`\`\`
5767
+ ### Project & Integration Management
5768
+
5769
+ | Tool | Use for |
5770
+ | ------------------------- | ----------------------------------------------------------- |
5771
+ | \`adk_get_agent_info\` | Get project info: name, version, and all primitives |
5772
+ | \`adk_search_integrations\` | Search available integrations on the Botpress Hub |
5773
+ | \`adk_get_integration\` | Get detailed info about an integration before adding it |
5774
+ | \`adk_add_integration\` | Add an integration to the project (updates agent.config.ts) |
5775
+
5776
+ ### Workflows
5777
+
5778
+ | Tool | Use for |
5779
+ | -------------------- | ------------------------------------------ |
5780
+ | \`adk_list_workflows\` | List available workflows with descriptions |
5781
+ | \`adk_start_workflow\` | Start a workflow or get its input schema |
5782
+
5783
+ > **Tip:** The dev server must be running (\`adk dev\`) for debugging and testing tools to work.
5412
5784
 
5413
5785
  ## Project Overview
5414
5786
 
@@ -5468,7 +5840,7 @@ class AgentProjectGenerator {
5468
5840
  deploy: "adk deploy"
5469
5841
  },
5470
5842
  dependencies: {
5471
- "@botpress/runtime": `^${"1.16.6"}`
5843
+ "@botpress/runtime": `^${"1.17.0"}`
5472
5844
  },
5473
5845
  devDependencies: {
5474
5846
  typescript: "^5.9.3"
@@ -5571,6 +5943,12 @@ dist/
5571
5943
  .env.local
5572
5944
  .env.production
5573
5945
 
5946
+ # Local development
5947
+ agent.local.json
5948
+
5949
+ # MCP configuration (auto-generated by adk mcp:init)
5950
+ .mcp.json
5951
+
5574
5952
  # IDE files
5575
5953
  .vscode/
5576
5954
  .idea/
@@ -6001,7 +6379,7 @@ if (typeof globalThis !== 'undefined') {
6001
6379
  }
6002
6380
  }
6003
6381
  // src/generators/integration-types.ts
6004
- import { transforms } from "@botpress/sdk";
6382
+ import { z as z3 } from "@botpress/sdk";
6005
6383
  import crypto2 from "crypto";
6006
6384
  import path18 from "path";
6007
6385
 
@@ -6029,6 +6407,7 @@ function getPluginAlias(pluginName) {
6029
6407
  }
6030
6408
 
6031
6409
  // src/generators/integration-types.ts
6410
+ var { transforms } = z3;
6032
6411
  var getIntegrationHash = (integration) => {
6033
6412
  return crypto2.createHash("sha256").update(`${integration.alias}|${integration.definition?.id}|${integration.definition?.version}|${integration.definition?.updatedAt}`).digest("hex");
6034
6413
  };
@@ -6261,9 +6640,10 @@ Description: ${tag?.description}`);
6261
6640
  }
6262
6641
  if (integration.definition?.configurations) {
6263
6642
  for (const [key, config] of Object.entries(integration.definition.configurations)) {
6264
- const title = config.title || key;
6265
- const description = config.description || "";
6266
- const schema = config.schema;
6643
+ const configRecord = config;
6644
+ const title = configRecord.title || key;
6645
+ const description = configRecord.description || "";
6646
+ const schema = configRecord.schema;
6267
6647
  let tsType = "{}";
6268
6648
  if (schema) {
6269
6649
  tsType = transforms.fromJSONSchema(schema).toTypescriptType();
@@ -6380,12 +6760,14 @@ async function generateClientWrapper(project) {
6380
6760
  let schema;
6381
6761
  let computed = false;
6382
6762
  if (typeof colDef === "object" && colDef !== null && "schema" in colDef) {
6383
- schema = colDef.schema;
6384
- computed = colDef.computed || false;
6763
+ const colDefRecord = colDef;
6764
+ schema = colDefRecord.schema;
6765
+ computed = colDefRecord.computed || false;
6385
6766
  } else {
6386
6767
  schema = colDef;
6387
6768
  }
6388
- const tsType = schema.toTypescriptType ? schema.toTypescriptType({ treatDefaultAsOptional: true }) : "any";
6769
+ const schemaObj = schema;
6770
+ const tsType = typeof schemaObj.toTypescriptType === "function" ? schemaObj.toTypescriptType({ treatDefaultAsOptional: true }) : "any";
6389
6771
  if (!computed) {
6390
6772
  inputColumns.push(`${colName}: ${tsType}`);
6391
6773
  }
@@ -6700,10 +7082,11 @@ import fs18 from "fs/promises";
6700
7082
  import path38 from "path";
6701
7083
 
6702
7084
  // src/generators/plugin-types.ts
6703
- import { transforms as transforms2 } from "@botpress/sdk";
7085
+ import { z as z4 } from "@botpress/sdk";
6704
7086
  import crypto4 from "crypto";
6705
7087
  import path20 from "path";
6706
7088
  init_utils();
7089
+ var { transforms: transforms2 } = z4;
6707
7090
  function stripRefs(schema) {
6708
7091
  if (typeof schema !== "object" || schema === null) {
6709
7092
  return schema;
@@ -6849,10 +7232,11 @@ export type PluginActions = PluginsMap<Plugins>;
6849
7232
  }
6850
7233
 
6851
7234
  // src/generators/interface-types.ts
6852
- import { transforms as transforms3 } from "@botpress/sdk";
7235
+ import { z as z5 } from "@botpress/sdk";
6853
7236
  import crypto5 from "crypto";
6854
7237
  import path22 from "path";
6855
7238
  init_utils();
7239
+ var { transforms: transforms3 } = z5;
6856
7240
  var sameMajorVersion = (a, b) => {
6857
7241
  const majorA = a.split(".")[0];
6858
7242
  const majorB = b.split(".")[0];
@@ -7638,7 +8022,7 @@ class DevIdManager {
7638
8022
  if (!project.agentInfo) {
7639
8023
  throw ValidationErrors.agentNotLinked();
7640
8024
  }
7641
- await project.updateAgentInfo({
8025
+ await project.updateAgentLocalInfo({
7642
8026
  devId: projectCache.devId
7643
8027
  });
7644
8028
  }
@@ -7665,7 +8049,7 @@ class DevIdManager {
7665
8049
  const client = await this.getClient();
7666
8050
  await client.getBot({ id: agentInfo.devId });
7667
8051
  return true;
7668
- } catch (error) {
8052
+ } catch {
7669
8053
  return false;
7670
8054
  }
7671
8055
  }
@@ -8038,10 +8422,11 @@ class PluginSync {
8038
8422
 
8039
8423
  // src/bot-generator/generator.ts
8040
8424
  init_utils();
8041
- import { transforms as transforms4 } from "@botpress/sdk";
8425
+ import { z as z6 } from "@botpress/sdk";
8042
8426
  init_constants();
8043
8427
  import { BuiltInActions as BuiltInActions2, BuiltInWorkflows as BuiltInWorkflows4, Primitives as Primitives3 } from "@botpress/runtime/internal";
8044
8428
  import { BUILT_IN_TAGS as BUILT_IN_TAGS2 } from "@botpress/runtime/definition";
8429
+ var { transforms: transforms4 } = z6;
8045
8430
  var plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
8046
8431
  function isBuiltinWorkflow3(name) {
8047
8432
  return !!Object.values(BuiltInWorkflows4).find((x) => x.name === name);
@@ -9055,7 +9440,9 @@ export default bot;`;
9055
9440
  }
9056
9441
  {
9057
9442
  const dest = path38.join(srcDir, "triggers.ts");
9058
- const { transforms: transforms5 } = await import("@botpress/sdk");
9443
+ const {
9444
+ z: { transforms: transforms5 }
9445
+ } = await import("@botpress/sdk");
9059
9446
  const imports = new Map;
9060
9447
  const exports = new Set;
9061
9448
  const payloadTypes = {};
@@ -9343,7 +9730,9 @@ async function generateBotProject(options) {
9343
9730
  }
9344
9731
  // src/tables/table-manager.ts
9345
9732
  import { Client as Client15 } from "@botpress/client";
9346
- import { transforms as transforms5 } from "@botpress/sdk";
9733
+ import { z as z7 } from "@botpress/sdk";
9734
+ var { transforms: transforms5 } = z7;
9735
+
9347
9736
  class TableManager {
9348
9737
  client;
9349
9738
  botId;
@@ -9374,13 +9763,14 @@ class TableManager {
9374
9763
  }
9375
9764
  getSchemaType(schema) {
9376
9765
  if (schema.type) {
9377
- if (schema.type === "array" && schema.items?.type) {
9378
- return `array<${schema.items.type}>`;
9766
+ const items = schema.items;
9767
+ if (schema.type === "array" && items?.type) {
9768
+ return `array<${items.type}>`;
9379
9769
  }
9380
- return schema.type;
9770
+ return String(schema.type);
9381
9771
  }
9382
9772
  if (schema.$ref) {
9383
- return schema.$ref.split("/").pop() || "unknown";
9773
+ return String(schema.$ref).split("/").pop() || "unknown";
9384
9774
  }
9385
9775
  return "unknown";
9386
9776
  }
@@ -9391,8 +9781,8 @@ class TableManager {
9391
9781
  } else if (Math.abs(oldPosition - newPosition) <= 1) {
9392
9782
  score += 1;
9393
9783
  }
9394
- const oldDesc = (oldSchema?.description || "").toLowerCase();
9395
- const newDesc = (newSchema?.description || "").toLowerCase();
9784
+ const oldDesc = String(oldSchema?.description || "").toLowerCase();
9785
+ const newDesc = String(newSchema?.description || "").toLowerCase();
9396
9786
  if (oldDesc && newDesc && oldDesc === newDesc) {
9397
9787
  score += 5;
9398
9788
  } else if (oldDesc && newDesc && oldDesc.length > 5 && newDesc.length > 5) {
@@ -9407,8 +9797,10 @@ class TableManager {
9407
9797
  }
9408
9798
  }
9409
9799
  }
9410
- const oldOptional = oldSchema?.type?.includes("null") || oldSchema?.nullable === true;
9411
- const newOptional = newSchema?.type?.includes("null") || newSchema?.nullable === true;
9800
+ const oldType = oldSchema?.type;
9801
+ const newType = newSchema?.type;
9802
+ const oldOptional = typeof oldType === "string" && oldType.includes("null") || oldSchema?.nullable === true;
9803
+ const newOptional = typeof newType === "string" && newType.includes("null") || newSchema?.nullable === true;
9412
9804
  if (oldOptional === newOptional) {
9413
9805
  score += 1;
9414
9806
  }
@@ -10636,7 +11028,7 @@ class KBSyncFormatter {
10636
11028
  }
10637
11029
  }
10638
11030
  // src/file-watcher/watcher.ts
10639
- import { watch as watch2, readdirSync as readdirSync2 } from "fs";
11031
+ import { watch as watch2, readdirSync as readdirSync2, statSync } from "fs";
10640
11032
  import { EventEmitter as EventEmitter3 } from "events";
10641
11033
  import { join as join9, relative as relative3 } from "path";
10642
11034
  import { existsSync as existsSync9 } from "fs";
@@ -10678,7 +11070,7 @@ class FileWatcher2 extends EventEmitter3 {
10678
11070
  this.updateFileState(fullPath);
10679
11071
  }
10680
11072
  }
10681
- } catch (error) {}
11073
+ } catch {}
10682
11074
  }
10683
11075
  watchFile(filePath) {
10684
11076
  if (this.watchers.has(filePath)) {
@@ -10711,6 +11103,12 @@ class FileWatcher2 extends EventEmitter3 {
10711
11103
  }
10712
11104
  }
10713
11105
  handleFileChange(filePath) {
11106
+ try {
11107
+ if (existsSync9(filePath) && statSync(filePath).isDirectory()) {
11108
+ this.scanDirectoryForChanges(filePath);
11109
+ return;
11110
+ }
11111
+ } catch {}
10714
11112
  const fileExists = existsSync9(filePath);
10715
11113
  const previousState = this.fileStates.get(filePath);
10716
11114
  let changeType;
@@ -10731,12 +11129,7 @@ class FileWatcher2 extends EventEmitter3 {
10731
11129
  path: relativePath,
10732
11130
  type: changeType
10733
11131
  });
10734
- if (this.debounceTimer) {
10735
- clearTimeout(this.debounceTimer);
10736
- }
10737
- this.debounceTimer = setTimeout(() => {
10738
- this.emitPendingChanges();
10739
- }, this.debounceMs);
11132
+ this.scheduleDebouncedEmit();
10740
11133
  }
10741
11134
  emitPendingChanges() {
10742
11135
  if (this.pendingChanges.size === 0) {
@@ -10750,6 +11143,44 @@ class FileWatcher2 extends EventEmitter3 {
10750
11143
  };
10751
11144
  this.emit("change", event);
10752
11145
  }
11146
+ scanDirectoryForChanges(dirPath) {
11147
+ try {
11148
+ const entries = readdirSync2(dirPath, { withFileTypes: true });
11149
+ const currentFiles = new Set;
11150
+ for (const entry of entries) {
11151
+ const fullPath = join9(dirPath, entry.name);
11152
+ if (entry.isFile()) {
11153
+ currentFiles.add(fullPath);
11154
+ if (!this.fileStates.has(fullPath)) {
11155
+ this.updateFileState(fullPath);
11156
+ const relativePath = relative3(this.projectPath, fullPath);
11157
+ this.pendingChanges.set(relativePath, { path: relativePath, type: "added" });
11158
+ this.scheduleDebouncedEmit();
11159
+ }
11160
+ } else if (entry.isDirectory()) {
11161
+ this.scanDirectoryForChanges(fullPath);
11162
+ }
11163
+ }
11164
+ for (const [trackedPath] of this.fileStates) {
11165
+ if (trackedPath.startsWith(dirPath) && !trackedPath.includes("/", dirPath.length + 1)) {
11166
+ if (!currentFiles.has(trackedPath)) {
11167
+ this.fileStates.delete(trackedPath);
11168
+ const relativePath = relative3(this.projectPath, trackedPath);
11169
+ this.pendingChanges.set(relativePath, { path: relativePath, type: "deleted" });
11170
+ this.scheduleDebouncedEmit();
11171
+ }
11172
+ }
11173
+ }
11174
+ } catch {}
11175
+ }
11176
+ scheduleDebouncedEmit() {
11177
+ if (this.debounceTimer) {
11178
+ clearTimeout(this.debounceTimer);
11179
+ }
11180
+ this.debounceTimer = setTimeout(() => {
11181
+ this.emitPendingChanges();
11182
+ }, this.debounceMs);
11183
+ }
10753
11184
  updateFileState(filePath) {
10754
11185
  if (existsSync9(filePath)) {
10755
11186
  this.fileStates.set(filePath, Date.now());
@@ -11529,6 +11960,7 @@ function filterEvals(evals, filter) {
11529
11960
  }
11530
11961
  // src/eval/runner.ts
11531
11962
  import { Client as BpClient2 } from "@botpress/client";
11963
+ import { BUILT_IN_STATES as BUILT_IN_STATES2 } from "@botpress/runtime/internal";
11532
11964
 
11533
11965
  // src/eval/client.ts
11534
11966
  import { Client as BpClient } from "@botpress/client";
@@ -11549,20 +11981,21 @@ class ChatSession {
11549
11981
  }
11550
11982
  return this.client.user.id;
11551
11983
  }
11552
- async sendMessage(message, options = {}) {
11984
+ async ensureConversation() {
11553
11985
  if (!this.client) {
11554
11986
  throw new Error("ChatSession not connected. Call connect() first.");
11555
11987
  }
11556
- const { timeout = 30000, idleTimeout = 3000 } = options;
11557
11988
  if (!this.conversationId) {
11558
11989
  const conv = await this.client.createConversation({});
11559
11990
  this.conversationId = conv.conversation.id;
11991
+ return conv.conversation.id;
11560
11992
  }
11561
- const conversationId = this.conversationId;
11993
+ return this.conversationId;
11994
+ }
11995
+ async _awaitResponses(conversationId, trigger, options = {}) {
11996
+ const { timeout = 30000, idleTimeout = 3000 } = options;
11562
11997
  const responses = [];
11563
- const listener = await this.client.listenConversation({
11564
- id: conversationId
11565
- });
11998
+ const listener = await this.client.listenConversation({ id: conversationId });
11566
11999
  return new Promise((resolve4, reject) => {
11567
12000
  let idleTimer = null;
11568
12001
  let resolved = false;
@@ -11582,7 +12015,7 @@ class ChatSession {
11582
12015
  };
11583
12016
  const overallTimer = setTimeout(() => {
11584
12017
  if (!resolved) {
11585
- if (responses.length > 0) {
12018
+ if (responses.length > 0 || options.expectSilence) {
11586
12019
  done();
11587
12020
  } else {
11588
12021
  resolved = true;
@@ -11605,10 +12038,7 @@ class ChatSession {
11605
12038
  done();
11606
12039
  }
11607
12040
  });
11608
- this.client.createMessage({
11609
- conversationId,
11610
- payload: { type: "text", text: message }
11611
- }).then(() => {
12041
+ trigger().then(() => {
11612
12042
  resetIdle();
11613
12043
  }).catch((err) => {
11614
12044
  clearTimeout(overallTimer);
@@ -11619,6 +12049,20 @@ class ChatSession {
11619
12049
  });
11620
12050
  });
11621
12051
  }
12052
+ async sendMessage(message, options = {}) {
12053
+ if (!this.client) {
12054
+ throw new Error("ChatSession not connected. Call connect() first.");
12055
+ }
12056
+ const conversationId = await this.ensureConversation();
12057
+ return this._awaitResponses(conversationId, () => this.client.createMessage({ conversationId, payload: { type: "text", text: message } }), options);
12058
+ }
12059
+ async sendEvent(type, payload, options = {}) {
12060
+ if (!this.client) {
12061
+ throw new Error("ChatSession not connected. Call connect() first.");
12062
+ }
12063
+ const conversationId = await this.ensureConversation();
12064
+ return this._awaitResponses(conversationId, () => this.client.createEvent({ type, payload, conversationId }), options);
12065
+ }
11622
12066
  }
11623
12067
  async function discoverWebhookId(botId, token, apiUrl) {
11624
12068
  const client = new BpClient({ token, botId, apiUrl });
@@ -11626,14 +12070,14 @@ async function discoverWebhookId(botId, token, apiUrl) {
11626
12070
  const integrations = bot.integrations || {};
11627
12071
  const chat = Object.values(integrations).find((int) => int.name === "chat");
11628
12072
  const webhookId = chat?.webhookId;
11629
- if (!webhookId) {
12073
+ if (!webhookId || typeof webhookId !== "string") {
11630
12074
  throw new Error("No chat integration found on bot. Make sure the bot has the chat integration enabled.");
11631
12075
  }
11632
12076
  return webhookId;
11633
12077
  }
11634
12078
 
11635
12079
  // src/eval/traces.ts
11636
- async function fetchTraceSpans(conversationId, devServerUrl) {
12080
+ async function fetchSpans(conversationId, devServerUrl) {
11637
12081
  const url = `${devServerUrl}/api/traces/query?attributeName=conversationId&attributeValue=${encodeURIComponent(conversationId)}&count=1000`;
11638
12082
  const res = await fetch(url);
11639
12083
  if (!res.ok) {
@@ -11643,25 +12087,26 @@ async function fetchTraceSpans(conversationId, devServerUrl) {
11643
12087
  return Array.isArray(data) ? data : data.spans || [];
11644
12088
  }
11645
12089
  function extractToolCalls(spans) {
11646
- const toolEndSpans = spans.filter((span) => span.name === "autonomous.tool" && span.t === "end" && span.attrs?.["autonomous.tool.name"]);
12090
+ const data = (span) => span.data && typeof span.data === "object" ? span.data : {};
12091
+ const toolEndSpans = spans.filter((span) => span.name === "autonomous.tool" && (span.status === "ok" || span.status === "error") && data(span)["autonomous.tool.name"]);
11647
12092
  const seen = new Set;
11648
12093
  const unique = toolEndSpans.filter((span) => {
11649
- if (seen.has(span.spanId))
12094
+ if (seen.has(span.id.span))
11650
12095
  return false;
11651
- seen.add(span.spanId);
12096
+ seen.add(span.id.span);
11652
12097
  return true;
11653
12098
  });
11654
- return unique.sort((a, b) => (a.endNs ?? 0) - (b.endNs ?? 0)).map((span) => {
11655
- const attrs = span.attrs;
12099
+ return unique.sort((a, b) => (a.timing.endedAt ?? 0) - (b.timing.endedAt ?? 0)).map((span) => {
12100
+ const d = data(span);
11656
12101
  let input = {};
11657
12102
  try {
11658
- input = JSON.parse(attrs["autonomous.tool.input"]);
12103
+ input = JSON.parse(d["autonomous.tool.input"]);
11659
12104
  } catch {}
11660
12105
  return {
11661
- name: attrs["autonomous.tool.name"],
12106
+ name: d["autonomous.tool.name"],
11662
12107
  input,
11663
- output: attrs["autonomous.tool.output"] || "",
11664
- status: attrs["autonomous.tool.status"] || "unknown"
12108
+ output: d["autonomous.tool.output"] || "",
12109
+ status: d["autonomous.tool.status"] || "unknown"
11665
12110
  };
11666
12111
  });
11667
12112
  }
@@ -11676,7 +12121,7 @@ async function getTraceData(conversationId, devServerUrl, options = {}) {
11676
12121
  if (attempt > 0) {
11677
12122
  await new Promise((resolve4) => setTimeout(resolve4, retryDelay));
11678
12123
  }
11679
- spans = await fetchTraceSpans(conversationId, devServerUrl);
12124
+ spans = await fetchSpans(conversationId, devServerUrl);
11680
12125
  allToolCalls = extractToolCalls(spans);
11681
12126
  if (!expectNew || allToolCalls.length > previousCount) {
11682
12127
  break;
@@ -11687,9 +12132,11 @@ async function getTraceData(conversationId, devServerUrl, options = {}) {
11687
12132
  }
11688
12133
 
11689
12134
  // src/eval/graders/llm.ts
11690
- import { Cognitive } from "@botpress/cognitive";
12135
+ import { Cognitive } from "@botpress/runtime";
11691
12136
  import { Client as Client18 } from "@botpress/client";
11692
- var JUDGE_SYSTEM_PROMPT = `You are an evaluation judge for a chatbot. You will be given:
12137
+ var DEFAULT_PASS_THRESHOLD = 3;
12138
+ function buildJudgeSystemPrompt(passThreshold) {
12139
+ return `You are an evaluation judge for a chatbot. You will be given:
11693
12140
  - The user's message
11694
12141
  - The bot's response
11695
12142
  - Grading criteria
@@ -11710,32 +12157,44 @@ Scoring guide:
11710
12157
  - 2: Barely meets criteria, significant issues
11711
12158
  - 1: Does not meet criteria
11712
12159
 
11713
- A score of 3 or above is a pass.`;
12160
+ A score of ${passThreshold} or above is a pass.`;
12161
+ }
12162
+ var MODEL_ALIASES = ["fast", "best"];
11714
12163
  var _cognitive = null;
11715
- function getCognitive() {
11716
- if (_cognitive)
11717
- return _cognitive;
11718
- const token = process.env.BP_TOKEN || process.env.ADK_TOKEN;
11719
- const botId = process.env.ADK_BOT_ID;
11720
- const apiUrl = process.env.ADK_API_URL || "https://api.botpress.cloud";
11721
- if (!token || !botId)
11722
- return null;
11723
- const client = new Client18({ token, apiUrl, botId });
11724
- _cognitive = new Cognitive({ client, __experimental_beta: true });
11725
- return _cognitive;
12164
+ var _availableModels = [];
12165
+ var _judgeModel = "fast";
12166
+ function _isModelValid(model) {
12167
+ if (MODEL_ALIASES.includes(model))
12168
+ return true;
12169
+ if (_availableModels.length === 0)
12170
+ return false;
12171
+ return _availableModels.includes(model);
11726
12172
  }
11727
- function initLLMJudge(credentials) {
12173
+ async function initLLMJudge(credentials, options) {
12174
+ const log = options?.logger ?? console;
11728
12175
  const client = new Client18({
11729
12176
  token: credentials.token,
11730
12177
  apiUrl: credentials.apiUrl,
11731
12178
  botId: credentials.botId
11732
12179
  });
11733
12180
  _cognitive = new Cognitive({ client, __experimental_beta: true });
12181
+ try {
12182
+ _availableModels = Array.from((await _cognitive.fetchRemoteModels()).keys());
12183
+ } catch (err) {
12184
+ log.warn(`Failed to fetch available models: ${err.message}. LLM judge will be unavailable.`);
12185
+ _availableModels = [];
12186
+ }
12187
+ _judgeModel = options?.model ?? "fast";
12188
+ if (_availableModels.length === 0) {
12189
+ log.warn("No available Cognitive models: cannot validate judge model.");
12190
+ } else if (!_isModelValid(_judgeModel)) {
12191
+ log.warn(`Configured LLM judge model "${_judgeModel}" is invalid. Run "adk models" to list available models.`);
12192
+ }
11734
12193
  }
11735
12194
  async function gradeLLMJudge(botResponse, criteria, context) {
12195
+ const threshold = Math.max(1, Math.min(5, context.passThreshold ?? DEFAULT_PASS_THRESHOLD));
11736
12196
  try {
11737
- const cognitive = getCognitive();
11738
- if (!cognitive) {
12197
+ if (!_cognitive) {
11739
12198
  return {
11740
12199
  assertion: `llm_judge: "${criteria}"`,
11741
12200
  pass: true,
@@ -11743,11 +12202,19 @@ async function gradeLLMJudge(botResponse, criteria, context) {
11743
12202
  actual: "SKIPPED — LLM judge unavailable: no credentials configured"
11744
12203
  };
11745
12204
  }
11746
- const { output } = await cognitive.generateContent({
11747
- model: "fast",
12205
+ if (!_isModelValid(_judgeModel)) {
12206
+ return {
12207
+ assertion: `llm_judge: "${criteria}"`,
12208
+ pass: false,
12209
+ expected: criteria,
12210
+ actual: "FAILED — invalid LLM judge model"
12211
+ };
12212
+ }
12213
+ const { output } = await _cognitive.generateContent({
12214
+ model: _judgeModel,
11748
12215
  temperature: 0,
11749
12216
  responseFormat: "json_object",
11750
- systemPrompt: JUDGE_SYSTEM_PROMPT,
12217
+ systemPrompt: buildJudgeSystemPrompt(threshold),
11751
12218
  messages: [
11752
12219
  {
11753
12220
  role: "user",
@@ -11772,9 +12239,9 @@ Criteria: ${criteria}`
11772
12239
  const verdict = JSON.parse(content);
11773
12240
  return {
11774
12241
  assertion: `llm_judge: "${criteria}"`,
11775
- pass: verdict.score >= 3,
12242
+ pass: verdict.score >= threshold,
11776
12243
  expected: criteria,
11777
- actual: `Score ${verdict.score}/5 — ${verdict.reason}`
12244
+ actual: `Score ${verdict.score}/5 (threshold: ${threshold}) — ${verdict.reason}`
11778
12245
  };
11779
12246
  } catch (err) {
11780
12247
  return {
@@ -11822,7 +12289,10 @@ async function gradeResponse(botResponse, assertions, context) {
11822
12289
  continue;
11823
12290
  }
11824
12291
  if ("llm_judge" in assertion) {
11825
- const result = await gradeLLMJudge(botResponse, assertion.llm_judge, context);
12292
+ const result = await gradeLLMJudge(botResponse, assertion.llm_judge, {
12293
+ userMessage: context.userMessage,
12294
+ passThreshold: context.judgePassThreshold
12295
+ });
11826
12296
  results.push(result);
11827
12297
  continue;
11828
12298
  }
@@ -11974,6 +12444,7 @@ function gradeTools(toolCalls, assertions) {
11974
12444
  }
11975
12445
 
11976
12446
  // src/eval/graders/state.ts
12447
+ import { BUILT_IN_STATES } from "@botpress/runtime/internal";
11977
12448
  function parseStatePath(path42) {
11978
12449
  const dot = path42.indexOf(".");
11979
12450
  if (dot === -1) {
@@ -11983,11 +12454,16 @@ function parseStatePath(path42) {
11983
12454
  const field = path42.slice(dot + 1);
11984
12455
  switch (prefix) {
11985
12456
  case "bot":
11986
- return { type: "bot", stateName: "botState", stateId: (ctx) => ctx.botId, field };
12457
+ return { type: "bot", stateName: BUILT_IN_STATES.bot, stateId: (ctx) => ctx.botId, field };
11987
12458
  case "user":
11988
- return { type: "user", stateName: "userState", stateId: (ctx) => ctx.userId, field };
12459
+ return { type: "user", stateName: BUILT_IN_STATES.user, stateId: (ctx) => ctx.userId, field };
11989
12460
  case "conversation":
11990
- return { type: "conversation", stateName: "conversationState", stateId: (ctx) => ctx.conversationId, field };
12461
+ return {
12462
+ type: "conversation",
12463
+ stateName: BUILT_IN_STATES.conversation,
12464
+ stateId: (ctx) => ctx.conversationId,
12465
+ field
12466
+ };
11991
12467
  default:
11992
12468
  throw new Error(`Unknown state type "${prefix}" in path "${path42}" — expected bot, user, or conversation`);
11993
12469
  }
@@ -11998,7 +12474,9 @@ async function fetchState(client, type, id, name) {
11998
12474
  const payload = result.state?.payload;
11999
12475
  return payload?.value ?? payload ?? null;
12000
12476
  } catch (err) {
12001
- if (err?.code === 404 || err?.message?.includes("404") || err?.message?.includes("doesn't exist")) {
12477
+ const code = err != null && typeof err === "object" && "code" in err ? err.code : undefined;
12478
+ const message = err instanceof Error ? err.message : "";
12479
+ if (code === 404 || message.includes("404") || message.includes("doesn't exist")) {
12002
12480
  return null;
12003
12481
  }
12004
12482
  throw err;
@@ -12022,7 +12500,7 @@ async function snapshotState(client, assertions, ctx) {
12022
12500
  if (assertion.changed === undefined)
12023
12501
  continue;
12024
12502
  const parsed = parseStatePath(assertion.path);
12025
- if (parsed.type === "conversation")
12503
+ if (parsed.type === "conversation" && !ctx.conversationId)
12026
12504
  continue;
12027
12505
  const payload = await fetchState(client, parsed.type, parsed.stateId(ctx), parsed.stateName);
12028
12506
  snapshots.set(assertion.path, payload ? payload[parsed.field] : undefined);
@@ -12199,9 +12677,11 @@ async function gradeTables(client, assertions) {
12199
12677
  // src/eval/graders/workflow.ts
12200
12678
  function gradeWorkflows(spans, assertions) {
12201
12679
  const results = [];
12680
+ const data = (span) => span.data && typeof span.data === "object" ? span.data : {};
12202
12681
  for (const assertion of assertions) {
12203
12682
  const workflowSpans = spans.filter((span) => {
12204
- const wfName = span.attrs?.["workflow.name"] || span.attrs?.["workflowName"];
12683
+ const d = data(span);
12684
+ const wfName = d["workflow.name"] || d["workflowName"];
12205
12685
  return wfName === assertion.name;
12206
12686
  });
12207
12687
  if (assertion.entered !== undefined) {
@@ -12216,8 +12696,9 @@ function gradeWorkflows(spans, assertions) {
12216
12696
  }
12217
12697
  if (assertion.completed !== undefined) {
12218
12698
  const completedSpans = workflowSpans.filter((span) => {
12219
- const status = span.attrs?.["workflow.status"];
12220
- return status === "completed" || span.t === "end" && span.name?.includes("workflow");
12699
+ const d = data(span);
12700
+ const status = d["workflow.status.final"];
12701
+ return status === "completed" || (span.status === "ok" || span.status === "error") && span.name?.includes("workflow");
12221
12702
  });
12222
12703
  const didComplete = completedSpans.length > 0;
12223
12704
  const pass = assertion.completed ? didComplete : !didComplete;
@@ -12232,6 +12713,22 @@ function gradeWorkflows(spans, assertions) {
12232
12713
  return results;
12233
12714
  }
12234
12715
 
12716
+ // src/eval/graders/timing.ts
12717
+ function gradeTiming(botDuration, assertions) {
12718
+ const results = [];
12719
+ for (const assertion of assertions) {
12720
+ const pass = matchValue(assertion.response_time, botDuration);
12721
+ const expected = operatorToString(assertion.response_time);
12722
+ results.push({
12723
+ assertion: `response_time ${expected}`,
12724
+ pass,
12725
+ expected: `Response time ${expected}`,
12726
+ actual: `${botDuration}ms`
12727
+ });
12728
+ }
12729
+ return results;
12730
+ }
12731
+
12235
12732
  // src/eval/graders/outcome.ts
12236
12733
  async function snapshotOutcomeState(client, evalDef, ctx) {
12237
12734
  if (!evalDef.outcome?.state) {
@@ -12261,8 +12758,65 @@ async function gradeOutcome(client, evalDef, ctx, traceSpans, preSnapshots) {
12261
12758
 
12262
12759
  // src/eval/runner.ts
12263
12760
  import { randomUUID } from "crypto";
12761
+ var DEFAULT_IDLE_TIMEOUT = 15000;
12762
+ var WORKFLOW_TRIGGER_TIMEOUT_MS = 5 * 60 * 1000;
12763
+ function buildStatePayload(value) {
12764
+ return {
12765
+ value,
12766
+ location: { type: "state" }
12767
+ };
12768
+ }
12769
+ async function seedEvalState(client, ctx, setup) {
12770
+ const state = setup?.state;
12771
+ if (!state)
12772
+ return;
12773
+ const writes = [];
12774
+ if (state.bot) {
12775
+ writes.push(client.setState({
12776
+ type: "bot",
12777
+ id: ctx.botId,
12778
+ name: BUILT_IN_STATES2.bot,
12779
+ payload: buildStatePayload(state.bot)
12780
+ }));
12781
+ }
12782
+ if (state.user) {
12783
+ writes.push(client.setState({
12784
+ type: "user",
12785
+ id: ctx.userId,
12786
+ name: BUILT_IN_STATES2.user,
12787
+ payload: buildStatePayload(state.user)
12788
+ }));
12789
+ }
12790
+ if (state.conversation) {
12791
+ if (!ctx.conversationId) {
12792
+ throw new Error("Cannot seed conversation state before a conversation is created.");
12793
+ }
12794
+ writes.push(client.setState({
12795
+ type: "conversation",
12796
+ id: ctx.conversationId,
12797
+ name: BUILT_IN_STATES2.conversation,
12798
+ payload: buildStatePayload(state.conversation)
12799
+ }));
12800
+ }
12801
+ await Promise.all(writes);
12802
+ }
12803
+ async function triggerEvalWorkflow(client, ctx, setup) {
12804
+ const workflow = setup?.workflow;
12805
+ if (!workflow)
12806
+ return;
12807
+ await client.createWorkflow({
12808
+ name: workflow.trigger,
12809
+ input: workflow.input ?? {},
12810
+ status: "pending",
12811
+ conversationId: ctx.conversationId,
12812
+ userId: ctx.userId,
12813
+ timeoutAt: new Date(Date.now() + WORKFLOW_TRIGGER_TIMEOUT_MS).toISOString()
12814
+ });
12815
+ }
12264
12816
  async function runEval(evalDef, connection, options = {}) {
12265
12817
  const devServerUrl = options.devServerUrl || "http://localhost:3001";
12818
+ const idleTimeout = evalDef.options?.idleTimeout ?? options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT;
12819
+ const judgePassThreshold = evalDef.options?.judgePassThreshold ?? options.judgePassThreshold;
12266
12820
  const start = Date.now();
12267
12821
  const turns = [];
12268
12822
  let outcomeAssertions = [];
@@ -12280,34 +12834,67 @@ async function runEval(evalDef, connection, options = {}) {
12280
12834
  }
12281
12835
  return bpClient;
12282
12836
  };
12837
+ let lastConversationId = "";
12838
+ if (evalDef.setup?.state?.conversation || evalDef.setup?.workflow) {
12839
+ lastConversationId = await session.ensureConversation();
12840
+ }
12841
+ await seedEvalState(getBpClient(), {
12842
+ botId: connection.botId,
12843
+ userId: session.userId,
12844
+ conversationId: lastConversationId || undefined
12845
+ }, evalDef.setup);
12846
+ await triggerEvalWorkflow(getBpClient(), {
12847
+ userId: session.userId,
12848
+ conversationId: lastConversationId || undefined
12849
+ }, evalDef.setup);
12283
12850
  let preSnapshots = new Map;
12284
12851
  if (evalDef.outcome?.state) {
12285
12852
  const ctx = {
12286
12853
  botId: connection.botId,
12287
12854
  userId: session.userId,
12288
- conversationId: ""
12855
+ conversationId: lastConversationId
12289
12856
  };
12290
12857
  preSnapshots = await snapshotOutcomeState(getBpClient(), evalDef, ctx);
12291
12858
  }
12292
12859
  let previousToolCallCount = 0;
12293
- let lastConversationId = "";
12294
- let allTraceSpans = [];
12860
+ let allSpans = [];
12295
12861
  for (let i = 0;i < evalDef.conversation.length; i++) {
12296
12862
  const turn = evalDef.conversation[i];
12297
12863
  const turnStart = Date.now();
12298
- const result = await session.sendMessage(turn.user, {
12299
- timeout: 30000,
12300
- idleTimeout: 3000
12301
- });
12864
+ if (turn.expectSilence && turn.assert?.response) {
12865
+ throw new Error(`Turn ${i + 1}: 'expectSilence' and 'assert.response' are mutually exclusive.`);
12866
+ }
12867
+ if (turn.user && turn.event) {
12868
+ throw new Error(`Turn ${i + 1}: 'user' and 'event' are mutually exclusive.`);
12869
+ }
12870
+ if (!turn.user && !turn.event) {
12871
+ throw new Error(`Turn ${i + 1}: must have either 'user' or 'event'.`);
12872
+ }
12873
+ const sendOptions = { timeout: Math.max(30000, idleTimeout * 2), idleTimeout, expectSilence: turn.expectSilence };
12874
+ const result = turn.event ? await session.sendEvent(turn.event.type, turn.event.payload ?? {}, sendOptions) : await session.sendMessage(turn.user, sendOptions);
12302
12875
  const botDuration = Date.now() - turnStart;
12876
+ if (!turn.expectSilence && result.responses.length === 0) {
12877
+ throw new Error(`Turn ${i + 1}: bot produced no response.`);
12878
+ }
12303
12879
  lastConversationId = result.conversationId;
12304
12880
  const botResponse = result.responses.map((r) => r.text).join(`
12305
12881
  `);
12882
+ const turnLabel = turn.event ? `[event: ${turn.event.type}]` : turn.user;
12306
12883
  const evalStart = Date.now();
12307
12884
  let assertions = [];
12885
+ if (turn.expectSilence) {
12886
+ const wasSilent = result.responses.length === 0;
12887
+ assertions.push({
12888
+ assertion: "no_response",
12889
+ pass: wasSilent,
12890
+ expected: "No response",
12891
+ actual: wasSilent ? "No response" : `Bot responded: "${botResponse}"`
12892
+ });
12893
+ }
12308
12894
  if (turn.assert?.response) {
12309
12895
  assertions = await gradeResponse(botResponse, turn.assert.response, {
12310
- userMessage: turn.user
12896
+ userMessage: turnLabel,
12897
+ judgePassThreshold
12311
12898
  });
12312
12899
  }
12313
12900
  if (turn.assert?.tools) {
@@ -12318,7 +12905,7 @@ async function runEval(evalDef, connection, options = {}) {
12318
12905
  expectNewCalls
12319
12906
  });
12320
12907
  previousToolCallCount = traceData.totalToolCallCount;
12321
- allTraceSpans = traceData.raw;
12908
+ allSpans = traceData.raw;
12322
12909
  const toolResults = gradeTools(traceData.toolCalls, turn.assert.tools);
12323
12910
  assertions = [...assertions, ...toolResults];
12324
12911
  } catch (err) {
@@ -12365,20 +12952,24 @@ async function runEval(evalDef, connection, options = {}) {
12365
12952
  }
12366
12953
  }
12367
12954
  if (turn.assert?.workflow) {
12368
- if (allTraceSpans.length === 0) {
12955
+ if (allSpans.length === 0) {
12369
12956
  try {
12370
12957
  const traceData = await getTraceData(result.conversationId, devServerUrl);
12371
- allTraceSpans = traceData.raw;
12958
+ allSpans = traceData.raw;
12372
12959
  } catch {}
12373
12960
  }
12374
- const workflowResults = gradeWorkflows(allTraceSpans, turn.assert.workflow);
12961
+ const workflowResults = gradeWorkflows(allSpans, turn.assert.workflow);
12375
12962
  assertions.push(...workflowResults);
12376
12963
  }
12964
+ if (turn.assert?.timing) {
12965
+ const timingResults = gradeTiming(botDuration, turn.assert.timing);
12966
+ assertions.push(...timingResults);
12967
+ }
12377
12968
  const turnPass = assertions.every((a) => a.pass);
12378
12969
  const evalDuration = Date.now() - evalStart;
12379
12970
  turns.push({
12380
12971
  turnNumber: i + 1,
12381
- userMessage: turn.user,
12972
+ userMessage: turnLabel,
12382
12973
  botResponse,
12383
12974
  assertions,
12384
12975
  pass: turnPass,
@@ -12387,10 +12978,10 @@ async function runEval(evalDef, connection, options = {}) {
12387
12978
  });
12388
12979
  }
12389
12980
  if (evalDef.outcome) {
12390
- if (allTraceSpans.length === 0 && lastConversationId && evalDef.outcome.workflow) {
12981
+ if (allSpans.length === 0 && lastConversationId && evalDef.outcome.workflow) {
12391
12982
  try {
12392
12983
  const traceData = await getTraceData(lastConversationId, devServerUrl);
12393
- allTraceSpans = traceData.raw;
12984
+ allSpans = traceData.raw;
12394
12985
  } catch {}
12395
12986
  }
12396
12987
  const ctx = {
@@ -12399,7 +12990,7 @@ async function runEval(evalDef, connection, options = {}) {
12399
12990
  conversationId: lastConversationId
12400
12991
  };
12401
12992
  try {
12402
- outcomeAssertions = await gradeOutcome(getBpClient(), evalDef, ctx, allTraceSpans, preSnapshots);
12993
+ outcomeAssertions = await gradeOutcome(getBpClient(), evalDef, ctx, allSpans, preSnapshots);
12403
12994
  } catch (err) {
12404
12995
  outcomeAssertions = [
12405
12996
  {
@@ -12440,11 +13031,11 @@ async function runEval(evalDef, connection, options = {}) {
12440
13031
  async function runEvalSuite(config, filter) {
12441
13032
  const start = Date.now();
12442
13033
  const runId = randomUUID().replace(/-/g, "").slice(0, 26);
12443
- initLLMJudge({
13034
+ await initLLMJudge({
12444
13035
  token: config.credentials.token,
12445
13036
  apiUrl: config.credentials.apiUrl,
12446
13037
  botId: config.credentials.botId
12447
- });
13038
+ }, { model: config.evalOptions?.judgeModel, logger: config.logger });
12448
13039
  const evalsDir = `${config.agentPath}/evals`;
12449
13040
  const allEvals = await loadEvalsFromDir(evalsDir);
12450
13041
  const evals = filterEvals(allEvals, filter);
@@ -12476,7 +13067,11 @@ async function runEvalSuite(config, filter) {
12476
13067
  for (let i = 0;i < evals.length; i++) {
12477
13068
  const evalDef = evals[i];
12478
13069
  config.onProgress?.({ type: "eval_start", evalName: evalDef.name, index: i });
12479
- const report = await runEval(evalDef, connection, { devServerUrl });
13070
+ const report = await runEval(evalDef, connection, {
13071
+ devServerUrl,
13072
+ idleTimeout: config.evalOptions?.idleTimeout,
13073
+ judgePassThreshold: config.evalOptions?.judgePassThreshold
13074
+ });
12480
13075
  reports.push(report);
12481
13076
  config.onProgress?.({ type: "eval_complete", evalName: evalDef.name, index: i, report });
12482
13077
  }
@@ -12564,8 +13159,10 @@ export {
12564
13159
  dependenciesKeyOrder,
12565
13160
  defineEval,
12566
13161
  coerceConfigValue,
13162
+ buildOpenCodeConfig,
12567
13163
  bpCliImporter,
12568
13164
  auth,
13165
+ agentLocalInfoKeyOrder,
12569
13166
  agentInfoKeyOrder,
12570
13167
  ValidationSeverity,
12571
13168
  ValidationErrors,
@@ -12576,6 +13173,7 @@ export {
12576
13173
  PreflightFormatter,
12577
13174
  PreflightChecker,
12578
13175
  PluginParser,
13176
+ OpenCodeCommand,
12579
13177
  KnowledgeManager,
12580
13178
  KBSyncOperation,
12581
13179
  KBSyncFormatter,
@@ -12611,4 +13209,4 @@ export {
12611
13209
  AgentProject
12612
13210
  };
12613
13211
 
12614
- //# debugId=B46A4264E95B176864756E2164756E21
13212
+ //# debugId=984405F8F441F45F64756E2164756E21