@mindstudio-ai/agent 0.1.133 → 0.1.134

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.ts CHANGED
@@ -380,7 +380,6 @@ interface UserInfoResult {
380
380
  userId: string;
381
381
  displayName: string;
382
382
  role: 'owner' | 'admin' | 'member' | 'guest' | 'agent';
383
- isAgent: boolean;
384
383
  }[];
385
384
  }
386
385
  /**
@@ -745,13 +744,21 @@ interface RunAgentResult {
745
744
  }
746
745
 
747
746
  type AuthType = 'internal' | 'apiKey';
747
+ /**
748
+ * Keeps an execution's calls within the server's concurrency limit, so a
749
+ * `Promise.all` over many steps queues here instead of being refused.
750
+ *
751
+ * The server's limits are per execution (per hook token), so the slots are
752
+ * too: each `runWithContext()` request gets its own, and a persistent sandbox
753
+ * worker's concurrent executions never wait on each other. Outside a request
754
+ * context (API-key use, or one execution per process) the instance holds one
755
+ * set. There is no call cap here: the server enforces it and answers 429 with
756
+ * `Retry-After`, which `http.ts` retries.
757
+ */
748
758
  declare class RateLimiter {
749
- readonly authType: AuthType;
750
- private inflight;
751
759
  private concurrencyLimit;
752
- private callCount;
753
- private callCap;
754
- private queue;
760
+ private perRequest;
761
+ private shared;
755
762
  constructor(authType: AuthType);
756
763
  /** Acquire a slot. Resolves when a concurrent slot is available. */
757
764
  acquire(): Promise<void>;
@@ -759,11 +766,7 @@ declare class RateLimiter {
759
766
  release(): void;
760
767
  /** Update limits from response headers. */
761
768
  updateFromHeaders(headers: Headers): void;
762
- /** Read current rate limit state from response headers. */
763
- static parseHeaders(headers: Headers): {
764
- remaining: number | undefined;
765
- concurrencyRemaining: number | undefined;
766
- };
769
+ private slots;
767
770
  }
768
771
 
769
772
  interface HttpClientConfig {
@@ -3980,9 +3983,9 @@ interface RunTaskResult<T = unknown> {
3980
3983
  * 5. `https://v1.mindstudio-api.com` (production default)
3981
3984
  *
3982
3985
  * Rate limiting is handled automatically:
3983
- * - Concurrent requests are queued to stay within server limits
3986
+ * - Concurrent requests are queued to stay within the server's concurrency
3987
+ * limit, per execution inside MindStudio
3984
3988
  * - 429 responses are retried automatically using the `Retry-After` header
3985
- * - Internal (hook) tokens are capped at 500 calls per execution
3986
3989
  */
3987
3990
  declare class MindStudioAgent$1 {
3988
3991
  /** @internal */
@@ -4055,6 +4058,11 @@ declare class MindStudioAgent$1 {
4055
4058
  * ```
4056
4059
  */
4057
4060
  executeStep<TOutput = unknown>(stepType: string, step: Record<string, unknown>, options?: StepExecutionOptions): Promise<StepExecutionResult<TOutput>>;
4061
+ /**
4062
+ * @internal Async step execution — POST, then poll until the step finishes.
4063
+ * Runs inside one concurrency slot (see `withSlot`).
4064
+ */
4065
+ private _executeStepPolled;
4058
4066
  /**
4059
4067
  * @internal Streaming step execution — sends `Accept: text/event-stream`
4060
4068
  * and parses SSE events for real-time debug logs.
@@ -4075,6 +4083,11 @@ declare class MindStudioAgent$1 {
4075
4083
  * ```
4076
4084
  */
4077
4085
  executeStepBatch(steps: BatchStepInput[], options?: ExecuteStepBatchOptions): Promise<ExecuteStepBatchResult>;
4086
+ /**
4087
+ * @internal Async batch execution — POST, then poll until the batch settles.
4088
+ * Runs inside one concurrency slot (see `withSlot`).
4089
+ */
4090
+ private _executeStepBatchPolled;
4078
4091
  /**
4079
4092
  * Run a task agent — a multi-step tool-use loop that composes SDK actions
4080
4093
  * to produce structured output. The model receives the prompt and tools,
package/dist/index.js CHANGED
@@ -23,17 +23,36 @@ var MindStudioError = class extends Error {
23
23
  };
24
24
  }
25
25
  };
26
+ function errorCodeFrom(body, fallback) {
27
+ if (body == null || typeof body !== "object") {
28
+ return fallback;
29
+ }
30
+ const { code, errorString } = body;
31
+ if (typeof code === "string" && code) {
32
+ return code;
33
+ }
34
+ if (typeof errorString === "string" && /^\S+$/.test(errorString)) {
35
+ return errorString;
36
+ }
37
+ return fallback;
38
+ }
26
39
 
27
40
  // src/http.ts
28
41
  async function request(config, method, path, body) {
29
- const url = `${config.baseUrl}/developer/v2${path}`;
42
+ return withSlot(config, () => sendRequest(config, method, path, body));
43
+ }
44
+ async function withSlot(config, fn) {
30
45
  await config.rateLimiter.acquire();
31
46
  try {
32
- return await requestWithRetry(config, method, url, body, 0);
47
+ return await fn();
33
48
  } finally {
34
49
  config.rateLimiter.release();
35
50
  }
36
51
  }
52
+ async function sendRequest(config, method, path, body) {
53
+ const url = `${config.baseUrl}/developer/v2${path}`;
54
+ return requestWithRetry(config, method, url, body, 0);
55
+ }
37
56
  async function requestWithRetry(config, method, url, body, attempt) {
38
57
  const res = await fetch(url, {
39
58
  method,
@@ -66,11 +85,7 @@ async function requestWithRetry(config, method, url, body, attempt) {
66
85
  } else if (body2.error || body2.message || body2.details) {
67
86
  message = JSON.stringify(body2.error ?? body2.message ?? body2.details);
68
87
  }
69
- if (body2.code) {
70
- code = typeof body2.code === "string" ? body2.code : JSON.stringify(body2.code);
71
- } else if (typeof body2.errorString === "string") {
72
- code = body2.errorString;
73
- }
88
+ code = errorCodeFrom(body2, code);
74
89
  } catch {
75
90
  if (text) {
76
91
  const stripped = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
@@ -393,48 +408,37 @@ async function executeDbBatchOverWs(url, token, databaseId, queries) {
393
408
  }
394
409
 
395
410
  // src/rate-limit.ts
396
- var DEFAULTS = {
397
- internal: { concurrency: 10, callCap: 500 },
398
- apiKey: { concurrency: 20, callCap: Infinity }
411
+ var DEFAULT_CONCURRENCY = {
412
+ internal: 10,
413
+ apiKey: 20
399
414
  };
400
415
  var RateLimiter = class {
416
+ concurrencyLimit;
417
+ // Keyed by the request's context object, so an execution's slots go when it does.
418
+ perRequest = /* @__PURE__ */ new WeakMap();
419
+ shared = { inflight: 0, queue: [] };
401
420
  constructor(authType) {
402
- this.authType = authType;
403
- this.concurrencyLimit = DEFAULTS[authType].concurrency;
404
- this.callCap = DEFAULTS[authType].callCap;
421
+ this.concurrencyLimit = DEFAULT_CONCURRENCY[authType];
405
422
  }
406
- authType;
407
- inflight = 0;
408
- concurrencyLimit;
409
- callCount = 0;
410
- callCap;
411
- queue = [];
412
423
  /** Acquire a slot. Resolves when a concurrent slot is available. */
413
424
  async acquire() {
414
- if (this.callCount >= this.callCap) {
415
- throw new MindStudioError(
416
- `Call cap exceeded (${this.callCap} calls per execution). Reduce the number of API calls or use executeStepBatch() to combine multiple steps.`,
417
- "call_cap_exceeded",
418
- 429
419
- );
420
- }
421
- if (this.inflight < this.concurrencyLimit) {
422
- this.inflight++;
423
- this.callCount++;
425
+ const slots = this.slots();
426
+ if (slots.inflight < this.concurrencyLimit) {
427
+ slots.inflight++;
424
428
  return;
425
429
  }
426
430
  return new Promise((resolve) => {
427
- this.queue.push(() => {
428
- this.inflight++;
429
- this.callCount++;
431
+ slots.queue.push(() => {
432
+ slots.inflight++;
430
433
  resolve();
431
434
  });
432
435
  });
433
436
  }
434
437
  /** Release a slot and let the next queued request proceed. */
435
438
  release() {
436
- this.inflight--;
437
- const next = this.queue.shift();
439
+ const slots = this.slots();
440
+ slots.inflight--;
441
+ const next = slots.queue.shift();
438
442
  if (next) {
439
443
  next();
440
444
  }
@@ -445,21 +449,19 @@ var RateLimiter = class {
445
449
  if (concurrency) {
446
450
  this.concurrencyLimit = parseInt(concurrency, 10);
447
451
  }
448
- const limit = headers.get("x-ratelimit-limit");
449
- if (limit && this.authType === "internal") {
450
- this.callCap = parseInt(limit, 10);
451
- }
452
452
  }
453
- /** Read current rate limit state from response headers. */
454
- static parseHeaders(headers) {
455
- const remaining = headers.get("x-ratelimit-remaining");
456
- const concurrencyRemaining = headers.get(
457
- "x-ratelimit-concurrency-remaining"
458
- );
459
- return {
460
- remaining: remaining != null ? parseInt(remaining, 10) : void 0,
461
- concurrencyRemaining: concurrencyRemaining != null ? parseInt(concurrencyRemaining, 10) : void 0
462
- };
453
+ // Release runs in the same async chain as its acquire, so both resolve the same request's slots.
454
+ slots() {
455
+ const ctx = getRequestContext();
456
+ if (!ctx) {
457
+ return this.shared;
458
+ }
459
+ let slots = this.perRequest.get(ctx);
460
+ if (!slots) {
461
+ slots = { inflight: 0, queue: [] };
462
+ this.perRequest.set(ctx, slots);
463
+ }
464
+ return slots;
463
465
  }
464
466
  };
465
467
 
@@ -5013,7 +5015,7 @@ async function runTaskPoll(httpConfig, body) {
5013
5015
  const errorBody = await res.json().catch(() => ({}));
5014
5016
  throw new MindStudioError(
5015
5017
  errorBody.message ?? errorBody.error ?? `Task poll failed: ${res.status} ${res.statusText}`,
5016
- errorBody.code ?? "poll_error",
5018
+ errorCodeFrom(errorBody, "poll_error"),
5017
5019
  res.status,
5018
5020
  errorBody
5019
5021
  );
@@ -5077,9 +5079,7 @@ async function runTaskStream(httpConfig, body, onEvent) {
5077
5079
  if (errMsg) {
5078
5080
  message = errMsg;
5079
5081
  }
5080
- if (errBody.code) {
5081
- code = errBody.code;
5082
- }
5082
+ code = errorCodeFrom(errBody, code);
5083
5083
  } catch {
5084
5084
  const stripped = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
5085
5085
  if (stripped) {
@@ -6294,8 +6294,18 @@ var MindStudioAgent = class {
6294
6294
  options
6295
6295
  );
6296
6296
  }
6297
+ return withSlot(
6298
+ this._httpConfig,
6299
+ () => this._executeStepPolled(stepType, step, options)
6300
+ );
6301
+ }
6302
+ /**
6303
+ * @internal Async step execution — POST, then poll until the step finishes.
6304
+ * Runs inside one concurrency slot (see `withSlot`).
6305
+ */
6306
+ async _executeStepPolled(stepType, step, options) {
6297
6307
  const threadId = options?.threadId ?? (this._reuseThreadId && !getRequestContext() ? this._threadId : void 0);
6298
- const { data: asyncData, headers } = await request(this._currentHttpConfig, "POST", `/steps/${stepType}/execute-async`, {
6308
+ const { data: asyncData, headers } = await sendRequest(this._currentHttpConfig, "POST", `/steps/${stepType}/execute-async`, {
6299
6309
  step,
6300
6310
  ...options?.appId != null && { appId: options.appId },
6301
6311
  ...threadId != null && { threadId },
@@ -6335,7 +6345,7 @@ var MindStudioAgent = class {
6335
6345
  const errorBody = await res.json().catch(() => ({}));
6336
6346
  throw new MindStudioError(
6337
6347
  errorBody.message ?? errorBody.error ?? `[${stepType}] Poll failed: ${res.status} ${res.statusText}`,
6338
- errorBody.code ?? "poll_error",
6348
+ errorCodeFrom(errorBody, "poll_error"),
6339
6349
  res.status,
6340
6350
  errorBody
6341
6351
  );
@@ -6431,9 +6441,7 @@ var MindStudioAgent = class {
6431
6441
  body2.error ?? body2.message ?? body2.details
6432
6442
  );
6433
6443
  }
6434
- if (body2.code) {
6435
- code = body2.code;
6436
- }
6444
+ code = errorCodeFrom(body2, code);
6437
6445
  } catch {
6438
6446
  const stripped = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
6439
6447
  if (stripped) {
@@ -6585,8 +6593,18 @@ var MindStudioAgent = class {
6585
6593
  * ```
6586
6594
  */
6587
6595
  async executeStepBatch(steps, options) {
6596
+ return withSlot(
6597
+ this._httpConfig,
6598
+ () => this._executeStepBatchPolled(steps, options)
6599
+ );
6600
+ }
6601
+ /**
6602
+ * @internal Async batch execution — POST, then poll until the batch settles.
6603
+ * Runs inside one concurrency slot (see `withSlot`).
6604
+ */
6605
+ async _executeStepBatchPolled(steps, options) {
6588
6606
  const threadId = options?.threadId ?? (this._reuseThreadId && !getRequestContext() ? this._threadId : void 0);
6589
- const { data: asyncData } = await request(this._currentHttpConfig, "POST", "/steps/execute-batch-async", {
6607
+ const { data: asyncData } = await sendRequest(this._currentHttpConfig, "POST", "/steps/execute-batch-async", {
6590
6608
  steps: steps.map((s) => ({
6591
6609
  ...s,
6592
6610
  stepType: resolveStepType2(s.stepType)
@@ -6620,7 +6638,7 @@ var MindStudioAgent = class {
6620
6638
  const errorBody = await res.json().catch(() => ({}));
6621
6639
  throw new MindStudioError(
6622
6640
  errorBody.message ?? errorBody.error ?? `Batch poll failed: ${res.status} ${res.statusText}`,
6623
- errorBody.code ?? "poll_error",
6641
+ errorCodeFrom(errorBody, "poll_error"),
6624
6642
  res.status,
6625
6643
  errorBody
6626
6644
  );
@@ -6916,7 +6934,7 @@ var MindStudioAgent = class {
6916
6934
  const errorBody = await res.json().catch(() => ({}));
6917
6935
  throw new MindStudioError(
6918
6936
  errorBody.message ?? errorBody.error ?? `Poll request failed: ${res.status} ${res.statusText}`,
6919
- errorBody.code ?? "poll_error",
6937
+ errorCodeFrom(errorBody, "poll_error"),
6920
6938
  res.status,
6921
6939
  errorBody
6922
6940
  );
@@ -7604,9 +7622,7 @@ var MindStudioAgent = class {
7604
7622
  body.error ?? body.message ?? body.details
7605
7623
  );
7606
7624
  }
7607
- if (body.code) {
7608
- code = body.code;
7609
- }
7625
+ code = errorCodeFrom(body, code);
7610
7626
  } catch {
7611
7627
  if (text && text.length < 500) {
7612
7628
  message = text;
@@ -8225,7 +8241,7 @@ function deletes(externalIds) {
8225
8241
  };
8226
8242
  }
8227
8243
  var DEFAULT_TIMEOUT_MS = 3e4;
8228
- var DEFAULT_CONCURRENCY = 4;
8244
+ var DEFAULT_CONCURRENCY2 = 4;
8229
8245
  function errorInfo2(e) {
8230
8246
  if (e instanceof Error) {
8231
8247
  return { message: e.message, stack: e.stack };
@@ -8297,7 +8313,7 @@ function defineMapper(source, config) {
8297
8313
  const run = async (params) => {
8298
8314
  const startedAt = Date.now();
8299
8315
  const timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8300
- const concurrency = Math.max(1, params.concurrency ?? DEFAULT_CONCURRENCY);
8316
+ const concurrency = Math.max(1, params.concurrency ?? DEFAULT_CONCURRENCY2);
8301
8317
  const objects = Array.isArray(params.objects) ? params.objects : [];
8302
8318
  const outcomes = new Array(objects.length);
8303
8319
  let next = 0;