@voltagent/core 2.1.1 → 2.1.2

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.
package/dist/index.d.mts CHANGED
@@ -3635,6 +3635,13 @@ interface ObservabilityConfig {
3635
3635
  logger?: Logger;
3636
3636
  resourceAttributes?: Record<string, any>;
3637
3637
  spanFilters?: SpanFilterConfig;
3638
+ /**
3639
+ * Controls whether flushOnFinish() runs automatically.
3640
+ * - "auto": flush only in serverless (default)
3641
+ * - "always": always flush
3642
+ * - "never": never flush
3643
+ */
3644
+ flushOnFinishStrategy?: "auto" | "always" | "never";
3638
3645
  voltOpsSync?: {
3639
3646
  sampling?: ObservabilitySamplingConfig;
3640
3647
  maxQueueSize?: number;
@@ -3875,6 +3882,7 @@ declare class VoltAgentObservability$1 {
3875
3882
  private logger;
3876
3883
  private spanFilterOptions?;
3877
3884
  private instrumentationScopeName;
3885
+ private flushLock;
3878
3886
  constructor(config?: ObservabilityConfig);
3879
3887
  /**
3880
3888
  * Set up span processors
@@ -3914,6 +3922,8 @@ declare class VoltAgentObservability$1 {
3914
3922
  * is incorrectly detected or if we are in a short-lived process.
3915
3923
  */
3916
3924
  flushOnFinish(): Promise<void>;
3925
+ private shouldFlushOnFinish;
3926
+ private withFlushLock;
3917
3927
  getProvider(): NodeTracerProvider;
3918
3928
  getContext(): typeof context;
3919
3929
  getTraceAPI(): typeof trace;
@@ -3940,6 +3950,7 @@ declare class ServerlessVoltAgentObservability {
3940
3950
  private spanFilterOptions?;
3941
3951
  private instrumentationScopeName;
3942
3952
  private spanStack;
3953
+ private flushLock;
3943
3954
  constructor(config?: ObservabilityConfig);
3944
3955
  private setupProcessors;
3945
3956
  private applySpanFilter;
@@ -3975,6 +3986,7 @@ declare class ServerlessVoltAgentObservability {
3975
3986
  * This is the preferred method to call at the end of a request.
3976
3987
  */
3977
3988
  flushOnFinish(): Promise<void>;
3989
+ private withFlushLock;
3978
3990
  getProvider(): BasicTracerProvider;
3979
3991
  getContext(): typeof context;
3980
3992
  getTraceAPI(): typeof trace;
package/dist/index.d.ts CHANGED
@@ -3635,6 +3635,13 @@ interface ObservabilityConfig {
3635
3635
  logger?: Logger;
3636
3636
  resourceAttributes?: Record<string, any>;
3637
3637
  spanFilters?: SpanFilterConfig;
3638
+ /**
3639
+ * Controls whether flushOnFinish() runs automatically.
3640
+ * - "auto": flush only in serverless (default)
3641
+ * - "always": always flush
3642
+ * - "never": never flush
3643
+ */
3644
+ flushOnFinishStrategy?: "auto" | "always" | "never";
3638
3645
  voltOpsSync?: {
3639
3646
  sampling?: ObservabilitySamplingConfig;
3640
3647
  maxQueueSize?: number;
@@ -3875,6 +3882,7 @@ declare class VoltAgentObservability$1 {
3875
3882
  private logger;
3876
3883
  private spanFilterOptions?;
3877
3884
  private instrumentationScopeName;
3885
+ private flushLock;
3878
3886
  constructor(config?: ObservabilityConfig);
3879
3887
  /**
3880
3888
  * Set up span processors
@@ -3914,6 +3922,8 @@ declare class VoltAgentObservability$1 {
3914
3922
  * is incorrectly detected or if we are in a short-lived process.
3915
3923
  */
3916
3924
  flushOnFinish(): Promise<void>;
3925
+ private shouldFlushOnFinish;
3926
+ private withFlushLock;
3917
3927
  getProvider(): NodeTracerProvider;
3918
3928
  getContext(): typeof context;
3919
3929
  getTraceAPI(): typeof trace;
@@ -3940,6 +3950,7 @@ declare class ServerlessVoltAgentObservability {
3940
3950
  private spanFilterOptions?;
3941
3951
  private instrumentationScopeName;
3942
3952
  private spanStack;
3953
+ private flushLock;
3943
3954
  constructor(config?: ObservabilityConfig);
3944
3955
  private setupProcessors;
3945
3956
  private applySpanFilter;
@@ -3975,6 +3986,7 @@ declare class ServerlessVoltAgentObservability {
3975
3986
  * This is the preferred method to call at the end of a request.
3976
3987
  */
3977
3988
  flushOnFinish(): Promise<void>;
3989
+ private withFlushLock;
3978
3990
  getProvider(): BasicTracerProvider;
3979
3991
  getContext(): typeof context;
3980
3992
  getTraceAPI(): typeof trace;
package/dist/index.js CHANGED
@@ -5757,6 +5757,7 @@ var VoltAgentObservability = class {
5757
5757
  logger;
5758
5758
  spanFilterOptions;
5759
5759
  instrumentationScopeName;
5760
+ flushLock = Promise.resolve();
5760
5761
  constructor(config = {}) {
5761
5762
  this.config = config;
5762
5763
  this.logger = getGlobalLogger();
@@ -6008,7 +6009,35 @@ var VoltAgentObservability = class {
6008
6009
  * is incorrectly detected or if we are in a short-lived process.
6009
6010
  */
6010
6011
  async flushOnFinish() {
6011
- await this.forceFlush();
6012
+ if (!this.shouldFlushOnFinish()) {
6013
+ return;
6014
+ }
6015
+ await this.withFlushLock(async () => {
6016
+ await this.forceFlush();
6017
+ });
6018
+ }
6019
+ shouldFlushOnFinish() {
6020
+ const strategy = this.config.flushOnFinishStrategy ?? "auto";
6021
+ if (strategy === "never") {
6022
+ return false;
6023
+ }
6024
+ if (strategy === "always") {
6025
+ return true;
6026
+ }
6027
+ return false;
6028
+ }
6029
+ async withFlushLock(fn) {
6030
+ const previous = this.flushLock;
6031
+ let release;
6032
+ this.flushLock = new Promise((resolve2) => {
6033
+ release = resolve2;
6034
+ });
6035
+ await previous;
6036
+ try {
6037
+ return await fn();
6038
+ } finally {
6039
+ release();
6040
+ }
6012
6041
  }
6013
6042
  getProvider() {
6014
6043
  return this.provider;
@@ -6058,6 +6087,7 @@ var ServerlessVoltAgentObservability = class {
6058
6087
  spanFilterOptions;
6059
6088
  instrumentationScopeName;
6060
6089
  spanStack = [];
6090
+ flushLock = Promise.resolve();
6061
6091
  constructor(config = {}) {
6062
6092
  this.config = { ...config };
6063
6093
  this.instrumentationScopeName = config.instrumentationScopeName || "@voltagent/core";
@@ -6315,11 +6345,18 @@ var ServerlessVoltAgentObservability = class {
6315
6345
  * This is the preferred method to call at the end of a request.
6316
6346
  */
6317
6347
  async flushOnFinish() {
6348
+ const strategy = this.config.flushOnFinishStrategy ?? "auto";
6349
+ if (strategy === "never") {
6350
+ return;
6351
+ }
6318
6352
  const waitUntil = globalThis.___voltagent_wait_until;
6319
- if (waitUntil) {
6353
+ const scheduleFlush = /* @__PURE__ */ __name(() => this.withFlushLock(async () => {
6354
+ await this.forceFlush();
6355
+ }), "scheduleFlush");
6356
+ if (strategy !== "always" && waitUntil) {
6320
6357
  try {
6321
6358
  waitUntil(
6322
- this.forceFlush().catch((err) => {
6359
+ scheduleFlush().catch((err) => {
6323
6360
  console.warn("[voltagent] Background flush failed", err);
6324
6361
  })
6325
6362
  );
@@ -6328,7 +6365,20 @@ var ServerlessVoltAgentObservability = class {
6328
6365
  console.warn("[voltagent] waitUntil failed, falling back to blocking flush", error);
6329
6366
  }
6330
6367
  }
6331
- await this.forceFlush();
6368
+ await scheduleFlush();
6369
+ }
6370
+ async withFlushLock(fn) {
6371
+ const previous = this.flushLock;
6372
+ let release;
6373
+ this.flushLock = new Promise((resolve2) => {
6374
+ release = resolve2;
6375
+ });
6376
+ await previous;
6377
+ try {
6378
+ return await fn();
6379
+ } finally {
6380
+ release();
6381
+ }
6332
6382
  }
6333
6383
  getProvider() {
6334
6384
  return this.provider;
@@ -8065,7 +8115,7 @@ function createWorkflow({
8065
8115
  await hooks?.onStepEnd?.(stateManager.state);
8066
8116
  traceContext.recordCancellation(reason);
8067
8117
  traceContext.end("cancelled");
8068
- await observability.flushOnFinish();
8118
+ await safeFlushOnFinish(observability);
8069
8119
  workflowRegistry.activeExecutions.delete(executionId);
8070
8120
  try {
8071
8121
  await effectiveMemory.updateWorkflowState(executionId, {
@@ -8186,7 +8236,7 @@ function createWorkflow({
8186
8236
  checkpoint
8187
8237
  );
8188
8238
  traceContext.end("suspended");
8189
- await observability.flushOnFinish();
8239
+ await safeFlushOnFinish(observability);
8190
8240
  runLogger.debug(
8191
8241
  `Workflow suspended | user=${options?.userId || "anonymous"} conv=${options?.conversationId || "none"} step=${index}`,
8192
8242
  {
@@ -8315,7 +8365,7 @@ function createWorkflow({
8315
8365
  suspensionMetadata?.checkpoint
8316
8366
  );
8317
8367
  traceContext.end("suspended");
8318
- await observability.flushOnFinish();
8368
+ await safeFlushOnFinish(observability);
8319
8369
  try {
8320
8370
  await saveSuspensionState(
8321
8371
  suspensionMetadata,
@@ -8527,7 +8577,7 @@ function createWorkflow({
8527
8577
  traceContext.setOutput(finalState.result);
8528
8578
  traceContext.setUsage(stateManager.state.usage);
8529
8579
  traceContext.end("completed");
8530
- await observability.flushOnFinish();
8580
+ await safeFlushOnFinish(observability);
8531
8581
  try {
8532
8582
  await effectiveMemory.updateWorkflowState(executionContext.executionId, {
8533
8583
  status: "completed",
@@ -8578,7 +8628,7 @@ function createWorkflow({
8578
8628
  stateManager.cancel(cancellationReason);
8579
8629
  traceContext.recordCancellation(cancellationReason);
8580
8630
  traceContext.end("cancelled");
8581
- await observability.flushOnFinish();
8631
+ await safeFlushOnFinish(observability);
8582
8632
  workflowRegistry.activeExecutions.delete(executionId);
8583
8633
  emitAndCollectEvent({
8584
8634
  type: "workflow-cancelled",
@@ -8627,7 +8677,7 @@ function createWorkflow({
8627
8677
  stateManager.state.suspension?.checkpoint
8628
8678
  );
8629
8679
  traceContext.end("suspended");
8630
- await observability.flushOnFinish();
8680
+ await safeFlushOnFinish(observability);
8631
8681
  if (stateManager.state.status === "suspended") {
8632
8682
  await runTerminalHooks("suspended", { includeEnd: false });
8633
8683
  }
@@ -8647,7 +8697,7 @@ function createWorkflow({
8647
8697
  );
8648
8698
  }
8649
8699
  traceContext.end("error", error);
8650
- await observability.flushOnFinish();
8700
+ await safeFlushOnFinish(observability);
8651
8701
  runLogger.debug(
8652
8702
  `Workflow failed | user=${options?.userId || "anonymous"} conv=${options?.conversationId || "none"} error=${error instanceof Error ? error.message : String(error)}`,
8653
8703
  {
@@ -8980,6 +9030,13 @@ async function executeWithSignalCheck(fn, signal, checkInterval = 100) {
8980
9030
  return Promise.race([fn(), abortPromise]);
8981
9031
  }
8982
9032
  __name(executeWithSignalCheck, "executeWithSignalCheck");
9033
+ async function safeFlushOnFinish(observability) {
9034
+ try {
9035
+ await observability.flushOnFinish();
9036
+ } catch {
9037
+ }
9038
+ }
9039
+ __name(safeFlushOnFinish, "safeFlushOnFinish");
8983
9040
  function serializeWorkflowStep(step, index) {
8984
9041
  const baseStep = {
8985
9042
  id: step.id,