donobu 5.37.1 → 5.38.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.
@@ -0,0 +1,6 @@
1
+ import { DonobuException } from './DonobuException';
2
+ export declare class FlowIdCollisionException extends DonobuException {
3
+ private constructor();
4
+ static forId(flowId: string): FlowIdCollisionException;
5
+ }
6
+ //# sourceMappingURL=FlowIdCollisionException.d.ts.map
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowIdCollisionException = void 0;
4
+ const DonobuException_1 = require("./DonobuException");
5
+ class FlowIdCollisionException extends DonobuException_1.DonobuException {
6
+ constructor(message) {
7
+ super(message, undefined, 409);
8
+ }
9
+ static forId(flowId) {
10
+ return new FlowIdCollisionException(`A flow with ID '${flowId}' already exists. Client-supplied flow IDs must be unique.`);
11
+ }
12
+ }
13
+ exports.FlowIdCollisionException = FlowIdCollisionException;
14
+ //# sourceMappingURL=FlowIdCollisionException.js.map
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod/v4';
1
2
  export { AnthropicGptClient } from './clients/AnthropicGptClient';
2
3
  export { DonobuGptClient } from './clients/DonobuGptClient';
3
4
  export { fixAssertFields, GoogleGenerativeAiGptClient, } from './clients/GoogleGenerativeAiGptClient';
@@ -8,6 +9,7 @@ export { VercelAiGptClient } from './clients/VercelAiGptClient';
8
9
  export { runGenerateSiteTests } from './codegen/runGenerateSiteTests';
9
10
  export { env } from './envVars';
10
11
  export * from './exceptions/DonobuException';
12
+ export * from './exceptions/FlowIdCollisionException';
11
13
  export * from './exceptions/FlowNotFoundException';
12
14
  export * from './exceptions/GptApiKeysNotSetupException';
13
15
  export * from './exceptions/GptPlatformAuthenticationFailedException';
@@ -77,8 +79,51 @@ export * from './utils/TargetUtils';
77
79
  * Starts a Donobu API server at the given port. The server assumes that the
78
80
  * Playwright browsers have been installed.
79
81
  */
80
- export declare function startDonobuServer({ port, controlPanelHost, }?: {
82
+ export declare function startDonobuServer({ port, controlPanelHost, environ, }?: {
81
83
  port?: number | undefined;
82
84
  controlPanelHost?: import("./models/ControlPanel").ControlPanelFactory | undefined;
85
+ environ?: import("env-struct").Env<{
86
+ BASE64_GPT_CONFIG: z.ZodOptional<z.ZodString>;
87
+ BROWSERBASE_API_KEY: z.ZodOptional<z.ZodString>;
88
+ BROWSERBASE_PROJECT_ID: z.ZodOptional<z.ZodString>;
89
+ DONOBU_DEPLOYMENT_ENVIRONMENT: z.ZodOptional<z.ZodEnum<{
90
+ LOCAL: "LOCAL";
91
+ DONOBU_HOSTED_MULTI_TENANT: "DONOBU_HOSTED_MULTI_TENANT";
92
+ DONOBU_HOSTED_SINGLE_TENANT: "DONOBU_HOSTED_SINGLE_TENANT";
93
+ }>>;
94
+ DONOBU_API_BASE_URL: z.ZodDefault<z.ZodString>;
95
+ ANTHROPIC_API_KEY: z.ZodOptional<z.ZodString>;
96
+ ANTHROPIC_MODEL_NAME: z.ZodOptional<z.ZodString>;
97
+ GOOGLE_GENERATIVE_AI_API_KEY: z.ZodOptional<z.ZodString>;
98
+ GOOGLE_GENERATIVE_AI_MODEL_NAME: z.ZodOptional<z.ZodString>;
99
+ OLLAMA_MODEL_NAME: z.ZodOptional<z.ZodString>;
100
+ OLLAMA_API_URL: z.ZodOptional<z.ZodString>;
101
+ OPENAI_API_KEY: z.ZodOptional<z.ZodString>;
102
+ OPENAI_API_MODEL_NAME: z.ZodOptional<z.ZodString>;
103
+ PERSISTENCE_PRIORITY: z.ZodDefault<z.ZodArray<z.ZodString>>;
104
+ AWS_BEDROCK_MODEL_NAME: z.ZodOptional<z.ZodString>;
105
+ AWS_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
106
+ AWS_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
107
+ DONOBU_API_KEY: z.ZodOptional<z.ZodString>;
108
+ }, {
109
+ BASE64_GPT_CONFIG?: string | undefined;
110
+ BROWSERBASE_API_KEY?: string | undefined;
111
+ BROWSERBASE_PROJECT_ID?: string | undefined;
112
+ DONOBU_DEPLOYMENT_ENVIRONMENT?: "LOCAL" | "DONOBU_HOSTED_MULTI_TENANT" | "DONOBU_HOSTED_SINGLE_TENANT" | undefined;
113
+ DONOBU_API_BASE_URL: string;
114
+ ANTHROPIC_API_KEY?: string | undefined;
115
+ ANTHROPIC_MODEL_NAME?: string | undefined;
116
+ GOOGLE_GENERATIVE_AI_API_KEY?: string | undefined;
117
+ GOOGLE_GENERATIVE_AI_MODEL_NAME?: string | undefined;
118
+ OLLAMA_MODEL_NAME?: string | undefined;
119
+ OLLAMA_API_URL?: string | undefined;
120
+ OPENAI_API_KEY?: string | undefined;
121
+ OPENAI_API_MODEL_NAME?: string | undefined;
122
+ PERSISTENCE_PRIORITY: string[];
123
+ AWS_BEDROCK_MODEL_NAME?: string | undefined;
124
+ AWS_ACCESS_KEY_ID?: string | undefined;
125
+ AWS_SECRET_ACCESS_KEY?: string | undefined;
126
+ DONOBU_API_KEY?: string | undefined;
127
+ }> | undefined;
83
128
  }): Promise<void>;
84
129
  //# sourceMappingURL=main.d.ts.map
package/dist/esm/main.js CHANGED
@@ -44,6 +44,7 @@ Object.defineProperty(exports, "runGenerateSiteTests", { enumerable: true, get:
44
44
  var envVars_2 = require("./envVars");
45
45
  Object.defineProperty(exports, "env", { enumerable: true, get: function () { return envVars_2.env; } });
46
46
  __exportStar(require("./exceptions/DonobuException"), exports);
47
+ __exportStar(require("./exceptions/FlowIdCollisionException"), exports);
47
48
  __exportStar(require("./exceptions/FlowNotFoundException"), exports);
48
49
  __exportStar(require("./exceptions/GptApiKeysNotSetupException"), exports);
49
50
  __exportStar(require("./exceptions/GptPlatformAuthenticationFailedException"), exports);
@@ -112,9 +113,9 @@ const DEFAULT_PORT = 31000;
112
113
  * Starts a Donobu API server at the given port. The server assumes that the
113
114
  * Playwright browsers have been installed.
114
115
  */
115
- async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, } = {}) {
116
+ async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
116
117
  try {
117
- const adminController = await AdminApiController_1.AdminApiController.create(envVars_1.env.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, envVars_1.env);
118
+ const adminController = await AdminApiController_1.AdminApiController.create(environ.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, environ);
118
119
  adminController.start(port);
119
120
  Logger_1.appLogger.info(`Donobu API server available on http://localhost:${port}`);
120
121
  // Keep the process running until a process signal is detected.
@@ -186,6 +186,12 @@ export declare class DonobuFlowsManager {
186
186
  * back to the primary layer (the SQLite FK will reject if applicable;
187
187
  * non-DB layers will accept the dangling reference).
188
188
  */
189
+ /**
190
+ * Best-effort check that no flow with the given ID exists in any persistence
191
+ * layer. Throws {@link FlowIdCollisionException} on hit. Note: this is RACY
192
+ * — concurrent callers with the same ID can both pass the check.
193
+ */
194
+ private assertFlowIdAvailable;
189
195
  private resolveLayerForFlowCreate;
190
196
  private findTestLayerKey;
191
197
  private isLocallyRunning;
@@ -45,6 +45,7 @@ const CodeGenerator_1 = require("../codegen/CodeGenerator");
45
45
  const ActiveFlowNotFoundException_1 = require("../exceptions/ActiveFlowNotFoundException");
46
46
  const BrowserStateNotFoundException_1 = require("../exceptions/BrowserStateNotFoundException");
47
47
  const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
48
+ const FlowIdCollisionException_1 = require("../exceptions/FlowIdCollisionException");
48
49
  const FlowNotFoundException_1 = require("../exceptions/FlowNotFoundException");
49
50
  const InvalidParamValueException_1 = require("../exceptions/InvalidParamValueException");
50
51
  const TestNotFoundException_1 = require("../exceptions/TestNotFoundException");
@@ -96,7 +97,10 @@ class DonobuFlowsManager {
96
97
  * related parameters are will be ignored.
97
98
  */
98
99
  async createFlow(flowParams, gptClient, browserContextOverride) {
99
- const flowId = (0, crypto_1.randomUUID)();
100
+ if (flowParams.id) {
101
+ await this.assertFlowIdAvailable(flowParams.id);
102
+ }
103
+ const flowId = flowParams.id ?? (0, crypto_1.randomUUID)();
100
104
  const logBuffer = new FlowLogBuffer_1.FlowLogBuffer();
101
105
  return Logger_1.loggingContext.run({ flowId, logBuffer }, async () => {
102
106
  const messageDuration = flowParams.defaultMessageDuration ??
@@ -670,6 +674,27 @@ class DonobuFlowsManager {
670
674
  * back to the primary layer (the SQLite FK will reject if applicable;
671
675
  * non-DB layers will accept the dangling reference).
672
676
  */
677
+ /**
678
+ * Best-effort check that no flow with the given ID exists in any persistence
679
+ * layer. Throws {@link FlowIdCollisionException} on hit. Note: this is RACY
680
+ * — concurrent callers with the same ID can both pass the check.
681
+ */
682
+ async assertFlowIdAvailable(flowId) {
683
+ for (const persistence of await this.flowsPersistenceRegistry.getAll()) {
684
+ try {
685
+ await persistence.getFlowMetadataById(flowId);
686
+ throw FlowIdCollisionException_1.FlowIdCollisionException.forId(flowId);
687
+ }
688
+ catch (error) {
689
+ if (error instanceof FlowIdCollisionException_1.FlowIdCollisionException) {
690
+ throw error;
691
+ }
692
+ if (!(error instanceof FlowNotFoundException_1.FlowNotFoundException)) {
693
+ throw error;
694
+ }
695
+ }
696
+ }
697
+ }
673
698
  async resolveLayerForFlowCreate(testId) {
674
699
  if (!testId) {
675
700
  return this.flowsPersistenceRegistry.get();
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod/v4';
2
2
  export declare const CreateDonobuFlowSchema: z.ZodObject<{
3
+ id: z.ZodOptional<z.ZodString>;
3
4
  target: z.ZodString;
4
5
  web: z.ZodOptional<z.ZodObject<{
5
6
  browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
@@ -7,7 +7,21 @@ const FlowMetadata_1 = require("./FlowMetadata");
7
7
  const ProposedToolCall_1 = require("./ProposedToolCall");
8
8
  const RunMode_1 = require("./RunMode");
9
9
  exports.CreateDonobuFlowSchema = v4_1.z
10
- .object({
10
+ .looseObject({
11
+ id: v4_1.z
12
+ .string()
13
+ .min(1)
14
+ .max(128)
15
+ .regex(/^[a-zA-Z0-9._:-]+$/)
16
+ .optional()
17
+ .describe(`⚠️ ADVANCED — DO NOT USE unless you have a specific reason.
18
+ Pre-assigned flow ID. Useful only when the caller must know the ID before
19
+ flow creation completes (e.g. orchestrators that pre-allocate IDs to track
20
+ an in-flight execution). The SDK does a best-effort collision check against
21
+ the persistence layer and throws FlowIdCollisionException if the ID already
22
+ exists, but concurrent calls with the same ID are RACY: there is no locking,
23
+ so two callers can both pass the check and create dueling rows. If you do
24
+ not need this, omit it and let the SDK mint an ID.`),
11
25
  target: v4_1.z
12
26
  .string()
13
27
  .describe('The target type: "web", "mobile", or a plugin-provided target.'),
@@ -69,6 +83,5 @@ is not defined or unresolvable, the default flow agent configuration will be use
69
83
  .optional()
70
84
  .describe('The ID of the test that this flow is associated with. Only optional to support legacy flow creation scenarios.'),
71
85
  })
72
- .passthrough()
73
86
  .describe('This is the expected payload for a request to create a new Donobu flow');
74
87
  //# sourceMappingURL=CreateDonobuFlow.js.map
@@ -4,6 +4,7 @@ import type { ToolCallResult } from '../models/ToolCallResult';
4
4
  import { Tool } from './Tool';
5
5
  export declare const TriggerDonobuFlowCoreSchema: z.ZodObject<{
6
6
  donobuFlowParameters: z.ZodObject<{
7
+ id: z.ZodOptional<z.ZodString>;
7
8
  target: z.ZodString;
8
9
  web: z.ZodOptional<z.ZodObject<{
9
10
  browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
@@ -151,6 +152,7 @@ export declare const TriggerDonobuFlowCoreSchema: z.ZodObject<{
151
152
  }, z.core.$strip>;
152
153
  export declare const TriggerDonobuFlowGptSchema: z.ZodObject<{
153
154
  donobuFlowParameters: z.ZodObject<{
155
+ id: z.ZodOptional<z.ZodString>;
154
156
  target: z.ZodString;
155
157
  web: z.ZodOptional<z.ZodObject<{
156
158
  browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
@@ -0,0 +1,6 @@
1
+ import { DonobuException } from './DonobuException';
2
+ export declare class FlowIdCollisionException extends DonobuException {
3
+ private constructor();
4
+ static forId(flowId: string): FlowIdCollisionException;
5
+ }
6
+ //# sourceMappingURL=FlowIdCollisionException.d.ts.map
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowIdCollisionException = void 0;
4
+ const DonobuException_1 = require("./DonobuException");
5
+ class FlowIdCollisionException extends DonobuException_1.DonobuException {
6
+ constructor(message) {
7
+ super(message, undefined, 409);
8
+ }
9
+ static forId(flowId) {
10
+ return new FlowIdCollisionException(`A flow with ID '${flowId}' already exists. Client-supplied flow IDs must be unique.`);
11
+ }
12
+ }
13
+ exports.FlowIdCollisionException = FlowIdCollisionException;
14
+ //# sourceMappingURL=FlowIdCollisionException.js.map
package/dist/main.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod/v4';
1
2
  export { AnthropicGptClient } from './clients/AnthropicGptClient';
2
3
  export { DonobuGptClient } from './clients/DonobuGptClient';
3
4
  export { fixAssertFields, GoogleGenerativeAiGptClient, } from './clients/GoogleGenerativeAiGptClient';
@@ -8,6 +9,7 @@ export { VercelAiGptClient } from './clients/VercelAiGptClient';
8
9
  export { runGenerateSiteTests } from './codegen/runGenerateSiteTests';
9
10
  export { env } from './envVars';
10
11
  export * from './exceptions/DonobuException';
12
+ export * from './exceptions/FlowIdCollisionException';
11
13
  export * from './exceptions/FlowNotFoundException';
12
14
  export * from './exceptions/GptApiKeysNotSetupException';
13
15
  export * from './exceptions/GptPlatformAuthenticationFailedException';
@@ -77,8 +79,51 @@ export * from './utils/TargetUtils';
77
79
  * Starts a Donobu API server at the given port. The server assumes that the
78
80
  * Playwright browsers have been installed.
79
81
  */
80
- export declare function startDonobuServer({ port, controlPanelHost, }?: {
82
+ export declare function startDonobuServer({ port, controlPanelHost, environ, }?: {
81
83
  port?: number | undefined;
82
84
  controlPanelHost?: import("./models/ControlPanel").ControlPanelFactory | undefined;
85
+ environ?: import("env-struct").Env<{
86
+ BASE64_GPT_CONFIG: z.ZodOptional<z.ZodString>;
87
+ BROWSERBASE_API_KEY: z.ZodOptional<z.ZodString>;
88
+ BROWSERBASE_PROJECT_ID: z.ZodOptional<z.ZodString>;
89
+ DONOBU_DEPLOYMENT_ENVIRONMENT: z.ZodOptional<z.ZodEnum<{
90
+ LOCAL: "LOCAL";
91
+ DONOBU_HOSTED_MULTI_TENANT: "DONOBU_HOSTED_MULTI_TENANT";
92
+ DONOBU_HOSTED_SINGLE_TENANT: "DONOBU_HOSTED_SINGLE_TENANT";
93
+ }>>;
94
+ DONOBU_API_BASE_URL: z.ZodDefault<z.ZodString>;
95
+ ANTHROPIC_API_KEY: z.ZodOptional<z.ZodString>;
96
+ ANTHROPIC_MODEL_NAME: z.ZodOptional<z.ZodString>;
97
+ GOOGLE_GENERATIVE_AI_API_KEY: z.ZodOptional<z.ZodString>;
98
+ GOOGLE_GENERATIVE_AI_MODEL_NAME: z.ZodOptional<z.ZodString>;
99
+ OLLAMA_MODEL_NAME: z.ZodOptional<z.ZodString>;
100
+ OLLAMA_API_URL: z.ZodOptional<z.ZodString>;
101
+ OPENAI_API_KEY: z.ZodOptional<z.ZodString>;
102
+ OPENAI_API_MODEL_NAME: z.ZodOptional<z.ZodString>;
103
+ PERSISTENCE_PRIORITY: z.ZodDefault<z.ZodArray<z.ZodString>>;
104
+ AWS_BEDROCK_MODEL_NAME: z.ZodOptional<z.ZodString>;
105
+ AWS_ACCESS_KEY_ID: z.ZodOptional<z.ZodString>;
106
+ AWS_SECRET_ACCESS_KEY: z.ZodOptional<z.ZodString>;
107
+ DONOBU_API_KEY: z.ZodOptional<z.ZodString>;
108
+ }, {
109
+ BASE64_GPT_CONFIG?: string | undefined;
110
+ BROWSERBASE_API_KEY?: string | undefined;
111
+ BROWSERBASE_PROJECT_ID?: string | undefined;
112
+ DONOBU_DEPLOYMENT_ENVIRONMENT?: "LOCAL" | "DONOBU_HOSTED_MULTI_TENANT" | "DONOBU_HOSTED_SINGLE_TENANT" | undefined;
113
+ DONOBU_API_BASE_URL: string;
114
+ ANTHROPIC_API_KEY?: string | undefined;
115
+ ANTHROPIC_MODEL_NAME?: string | undefined;
116
+ GOOGLE_GENERATIVE_AI_API_KEY?: string | undefined;
117
+ GOOGLE_GENERATIVE_AI_MODEL_NAME?: string | undefined;
118
+ OLLAMA_MODEL_NAME?: string | undefined;
119
+ OLLAMA_API_URL?: string | undefined;
120
+ OPENAI_API_KEY?: string | undefined;
121
+ OPENAI_API_MODEL_NAME?: string | undefined;
122
+ PERSISTENCE_PRIORITY: string[];
123
+ AWS_BEDROCK_MODEL_NAME?: string | undefined;
124
+ AWS_ACCESS_KEY_ID?: string | undefined;
125
+ AWS_SECRET_ACCESS_KEY?: string | undefined;
126
+ DONOBU_API_KEY?: string | undefined;
127
+ }> | undefined;
83
128
  }): Promise<void>;
84
129
  //# sourceMappingURL=main.d.ts.map
package/dist/main.js CHANGED
@@ -44,6 +44,7 @@ Object.defineProperty(exports, "runGenerateSiteTests", { enumerable: true, get:
44
44
  var envVars_2 = require("./envVars");
45
45
  Object.defineProperty(exports, "env", { enumerable: true, get: function () { return envVars_2.env; } });
46
46
  __exportStar(require("./exceptions/DonobuException"), exports);
47
+ __exportStar(require("./exceptions/FlowIdCollisionException"), exports);
47
48
  __exportStar(require("./exceptions/FlowNotFoundException"), exports);
48
49
  __exportStar(require("./exceptions/GptApiKeysNotSetupException"), exports);
49
50
  __exportStar(require("./exceptions/GptPlatformAuthenticationFailedException"), exports);
@@ -112,9 +113,9 @@ const DEFAULT_PORT = 31000;
112
113
  * Starts a Donobu API server at the given port. The server assumes that the
113
114
  * Playwright browsers have been installed.
114
115
  */
115
- async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, } = {}) {
116
+ async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
116
117
  try {
117
- const adminController = await AdminApiController_1.AdminApiController.create(envVars_1.env.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, envVars_1.env);
118
+ const adminController = await AdminApiController_1.AdminApiController.create(environ.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, environ);
118
119
  adminController.start(port);
119
120
  Logger_1.appLogger.info(`Donobu API server available on http://localhost:${port}`);
120
121
  // Keep the process running until a process signal is detected.
@@ -186,6 +186,12 @@ export declare class DonobuFlowsManager {
186
186
  * back to the primary layer (the SQLite FK will reject if applicable;
187
187
  * non-DB layers will accept the dangling reference).
188
188
  */
189
+ /**
190
+ * Best-effort check that no flow with the given ID exists in any persistence
191
+ * layer. Throws {@link FlowIdCollisionException} on hit. Note: this is RACY
192
+ * — concurrent callers with the same ID can both pass the check.
193
+ */
194
+ private assertFlowIdAvailable;
189
195
  private resolveLayerForFlowCreate;
190
196
  private findTestLayerKey;
191
197
  private isLocallyRunning;
@@ -45,6 +45,7 @@ const CodeGenerator_1 = require("../codegen/CodeGenerator");
45
45
  const ActiveFlowNotFoundException_1 = require("../exceptions/ActiveFlowNotFoundException");
46
46
  const BrowserStateNotFoundException_1 = require("../exceptions/BrowserStateNotFoundException");
47
47
  const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
48
+ const FlowIdCollisionException_1 = require("../exceptions/FlowIdCollisionException");
48
49
  const FlowNotFoundException_1 = require("../exceptions/FlowNotFoundException");
49
50
  const InvalidParamValueException_1 = require("../exceptions/InvalidParamValueException");
50
51
  const TestNotFoundException_1 = require("../exceptions/TestNotFoundException");
@@ -96,7 +97,10 @@ class DonobuFlowsManager {
96
97
  * related parameters are will be ignored.
97
98
  */
98
99
  async createFlow(flowParams, gptClient, browserContextOverride) {
99
- const flowId = (0, crypto_1.randomUUID)();
100
+ if (flowParams.id) {
101
+ await this.assertFlowIdAvailable(flowParams.id);
102
+ }
103
+ const flowId = flowParams.id ?? (0, crypto_1.randomUUID)();
100
104
  const logBuffer = new FlowLogBuffer_1.FlowLogBuffer();
101
105
  return Logger_1.loggingContext.run({ flowId, logBuffer }, async () => {
102
106
  const messageDuration = flowParams.defaultMessageDuration ??
@@ -670,6 +674,27 @@ class DonobuFlowsManager {
670
674
  * back to the primary layer (the SQLite FK will reject if applicable;
671
675
  * non-DB layers will accept the dangling reference).
672
676
  */
677
+ /**
678
+ * Best-effort check that no flow with the given ID exists in any persistence
679
+ * layer. Throws {@link FlowIdCollisionException} on hit. Note: this is RACY
680
+ * — concurrent callers with the same ID can both pass the check.
681
+ */
682
+ async assertFlowIdAvailable(flowId) {
683
+ for (const persistence of await this.flowsPersistenceRegistry.getAll()) {
684
+ try {
685
+ await persistence.getFlowMetadataById(flowId);
686
+ throw FlowIdCollisionException_1.FlowIdCollisionException.forId(flowId);
687
+ }
688
+ catch (error) {
689
+ if (error instanceof FlowIdCollisionException_1.FlowIdCollisionException) {
690
+ throw error;
691
+ }
692
+ if (!(error instanceof FlowNotFoundException_1.FlowNotFoundException)) {
693
+ throw error;
694
+ }
695
+ }
696
+ }
697
+ }
673
698
  async resolveLayerForFlowCreate(testId) {
674
699
  if (!testId) {
675
700
  return this.flowsPersistenceRegistry.get();
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod/v4';
2
2
  export declare const CreateDonobuFlowSchema: z.ZodObject<{
3
+ id: z.ZodOptional<z.ZodString>;
3
4
  target: z.ZodString;
4
5
  web: z.ZodOptional<z.ZodObject<{
5
6
  browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
@@ -7,7 +7,21 @@ const FlowMetadata_1 = require("./FlowMetadata");
7
7
  const ProposedToolCall_1 = require("./ProposedToolCall");
8
8
  const RunMode_1 = require("./RunMode");
9
9
  exports.CreateDonobuFlowSchema = v4_1.z
10
- .object({
10
+ .looseObject({
11
+ id: v4_1.z
12
+ .string()
13
+ .min(1)
14
+ .max(128)
15
+ .regex(/^[a-zA-Z0-9._:-]+$/)
16
+ .optional()
17
+ .describe(`⚠️ ADVANCED — DO NOT USE unless you have a specific reason.
18
+ Pre-assigned flow ID. Useful only when the caller must know the ID before
19
+ flow creation completes (e.g. orchestrators that pre-allocate IDs to track
20
+ an in-flight execution). The SDK does a best-effort collision check against
21
+ the persistence layer and throws FlowIdCollisionException if the ID already
22
+ exists, but concurrent calls with the same ID are RACY: there is no locking,
23
+ so two callers can both pass the check and create dueling rows. If you do
24
+ not need this, omit it and let the SDK mint an ID.`),
11
25
  target: v4_1.z
12
26
  .string()
13
27
  .describe('The target type: "web", "mobile", or a plugin-provided target.'),
@@ -69,6 +83,5 @@ is not defined or unresolvable, the default flow agent configuration will be use
69
83
  .optional()
70
84
  .describe('The ID of the test that this flow is associated with. Only optional to support legacy flow creation scenarios.'),
71
85
  })
72
- .passthrough()
73
86
  .describe('This is the expected payload for a request to create a new Donobu flow');
74
87
  //# sourceMappingURL=CreateDonobuFlow.js.map
@@ -4,6 +4,7 @@ import type { ToolCallResult } from '../models/ToolCallResult';
4
4
  import { Tool } from './Tool';
5
5
  export declare const TriggerDonobuFlowCoreSchema: z.ZodObject<{
6
6
  donobuFlowParameters: z.ZodObject<{
7
+ id: z.ZodOptional<z.ZodString>;
7
8
  target: z.ZodString;
8
9
  web: z.ZodOptional<z.ZodObject<{
9
10
  browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
@@ -151,6 +152,7 @@ export declare const TriggerDonobuFlowCoreSchema: z.ZodObject<{
151
152
  }, z.core.$strip>;
152
153
  export declare const TriggerDonobuFlowGptSchema: z.ZodObject<{
153
154
  donobuFlowParameters: z.ZodObject<{
155
+ id: z.ZodOptional<z.ZodString>;
154
156
  target: z.ZodString;
155
157
  web: z.ZodOptional<z.ZodObject<{
156
158
  browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.37.1",
3
+ "version": "5.38.1",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",