@mindstudio-ai/agent 0.1.46 → 0.1.48

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.js CHANGED
@@ -42,9 +42,9 @@ async function requestWithRetry(config, method, url, body, attempt) {
42
42
  body: body != null ? JSON.stringify(body) : void 0
43
43
  });
44
44
  config.rateLimiter.updateFromHeaders(res.headers);
45
- if (res.status === 429 && attempt < config.maxRetries) {
45
+ if (attempt < config.maxRetries && (res.status === 429 || res.status === 502 || res.status === 503 || res.status === 504)) {
46
46
  const retryAfter = res.headers.get("retry-after");
47
- const waitMs = retryAfter ? parseFloat(retryAfter) * 1e3 : 1e3;
47
+ const waitMs = retryAfter ? parseFloat(retryAfter) * 1e3 : Math.min(1e3 * Math.pow(2, attempt), 1e4);
48
48
  await sleep(waitMs);
49
49
  return requestWithRetry(config, method, url, body, attempt + 1);
50
50
  }
@@ -1493,15 +1493,16 @@ var Table = class {
1493
1493
  // -------------------------------------------------------------------------
1494
1494
  // Internal helpers
1495
1495
  // -------------------------------------------------------------------------
1496
- /** @internal Throw if data includes a platform-managed email/phone column. */
1496
+ /** @internal Throw if data includes a platform-managed read-only column. */
1497
1497
  _checkManagedColumns(data) {
1498
1498
  const mc = this._config.managedColumns;
1499
1499
  if (!mc) return;
1500
1500
  const keys = Object.keys(data);
1501
1501
  for (const key of keys) {
1502
- if (mc.email && key === mc.email || mc.phone && key === mc.phone) {
1502
+ if (mc.email && key === mc.email || mc.phone && key === mc.phone || mc.apiKey && key === mc.apiKey) {
1503
+ const friendly = key === mc.email ? "email" : key === mc.phone ? "phone" : "API key";
1503
1504
  throw new MindStudioError(
1504
- `Cannot write to "${key}" \u2014 this column is managed by auth. Use the auth API to change a user's ${key === mc.email ? "email" : "phone"}.`,
1505
+ `Cannot write to "${key}" \u2014 this column is managed by auth. Use the auth API to change a user's ${friendly}.`,
1505
1506
  "managed_column_write",
1506
1507
  400
1507
1508
  );
@@ -2884,7 +2885,7 @@ function buildTaskRequestBody(options) {
2884
2885
  prompt: options.prompt,
2885
2886
  input: options.input,
2886
2887
  tools: mapTools(options.tools),
2887
- structuredOutputExample: options.structuredOutputExample,
2888
+ structuredOutputExample: typeof options.structuredOutputExample === "string" ? options.structuredOutputExample : JSON.stringify(options.structuredOutputExample),
2888
2889
  model: options.model,
2889
2890
  ...options.maxTurns != null && { maxTurns: options.maxTurns },
2890
2891
  ...options.appId != null && { appId: options.appId },
@@ -2894,6 +2895,16 @@ function buildTaskRequestBody(options) {
2894
2895
  function sleep2(ms) {
2895
2896
  return new Promise((resolve) => setTimeout(resolve, ms));
2896
2897
  }
2898
+ function isDevMode() {
2899
+ return !!(process.env.CALLBACK_TOKEN || getRequestContext()?.callbackToken);
2900
+ }
2901
+ function logTaskResult(result) {
2902
+ if (!isDevMode()) return;
2903
+ const toolSummary = result.toolCalls.map((tc) => `${tc.name} (${tc.durationMs}ms) ${tc.success ? "\u2713" : "\u2717"}`).join(", ");
2904
+ console.log(
2905
+ `[task] ${result.turns} turn${result.turns === 1 ? "" : "s"}` + (toolSummary ? `: ${toolSummary}` : "") + ` | ${result.parsedSuccessfully ? "output OK" : "\u26A0 output not valid JSON"} | cost: ${result.usage.totalBillingCost}`
2906
+ );
2907
+ }
2897
2908
  async function runTaskPoll(httpConfig, body) {
2898
2909
  const { data } = await request(
2899
2910
  httpConfig,
@@ -2909,6 +2920,7 @@ async function runTaskPoll(httpConfig, body) {
2909
2920
  const res = await fetch(pollUrl, {
2910
2921
  headers: { "User-Agent": "@mindstudio-ai/agent" }
2911
2922
  });
2923
+ if (res.status === 502 || res.status === 503 || res.status === 504) continue;
2912
2924
  if (res.status === 404) {
2913
2925
  throw new MindStudioError(
2914
2926
  "Task poll token not found or expired.",
@@ -2926,7 +2938,12 @@ async function runTaskPoll(httpConfig, body) {
2926
2938
  );
2927
2939
  }
2928
2940
  const poll = await res.json();
2929
- if (poll.status === "pending") continue;
2941
+ if (poll.status === "pending") {
2942
+ if (isDevMode() && poll.currentTurn != null) {
2943
+ console.log(`[task] running... turn ${poll.currentTurn}/${poll.maxTurns ?? "?"}`);
2944
+ }
2945
+ continue;
2946
+ }
2930
2947
  if (poll.status === "error") {
2931
2948
  throw new MindStudioError(
2932
2949
  poll.error ?? "Task execution failed.",
@@ -2934,11 +2951,16 @@ async function runTaskPoll(httpConfig, body) {
2934
2951
  500
2935
2952
  );
2936
2953
  }
2937
- return {
2954
+ const result = {
2938
2955
  output: poll.output,
2956
+ outputRaw: poll.outputRaw ?? "",
2957
+ parsedSuccessfully: poll.parsedSuccessfully ?? true,
2939
2958
  turns: poll.turns ?? 0,
2940
- usage: poll.usage ?? { inputTokens: 0, outputTokens: 0, totalBillingCost: 0 }
2959
+ usage: poll.usage ?? { inputTokens: 0, outputTokens: 0, totalBillingCost: 0 },
2960
+ toolCalls: poll.toolCalls ?? []
2941
2961
  };
2962
+ logTaskResult(result);
2963
+ return result;
2942
2964
  }
2943
2965
  }
2944
2966
  async function runTaskStream(httpConfig, body, onEvent) {
@@ -2998,12 +3020,15 @@ async function runTaskStream(httpConfig, body, onEvent) {
2998
3020
  if (event.type === "done") {
2999
3021
  result = {
3000
3022
  output: event.output,
3023
+ outputRaw: event.outputRaw ?? "",
3024
+ parsedSuccessfully: event.parsedSuccessfully ?? true,
3001
3025
  turns: event.turns ?? 0,
3002
3026
  usage: event.usage ?? {
3003
3027
  inputTokens: 0,
3004
3028
  outputTokens: 0,
3005
3029
  totalBillingCost: 0
3006
- }
3030
+ },
3031
+ toolCalls: event.toolCalls ?? []
3007
3032
  };
3008
3033
  }
3009
3034
  } catch (err) {
@@ -3025,12 +3050,15 @@ async function runTaskStream(httpConfig, body, onEvent) {
3025
3050
  if (event.type === "done") {
3026
3051
  result = {
3027
3052
  output: event.output,
3053
+ outputRaw: event.outputRaw ?? "",
3054
+ parsedSuccessfully: event.parsedSuccessfully ?? true,
3028
3055
  turns: event.turns ?? 0,
3029
3056
  usage: event.usage ?? {
3030
3057
  inputTokens: 0,
3031
3058
  outputTokens: 0,
3032
3059
  totalBillingCost: 0
3033
- }
3060
+ },
3061
+ toolCalls: event.toolCalls ?? []
3034
3062
  };
3035
3063
  }
3036
3064
  } catch (err) {
@@ -3044,6 +3072,7 @@ async function runTaskStream(httpConfig, body, onEvent) {
3044
3072
  500
3045
3073
  );
3046
3074
  }
3075
+ logTaskResult(result);
3047
3076
  return result;
3048
3077
  }
3049
3078
 
@@ -3644,48 +3673,80 @@ var MindStudioAgent = class {
3644
3673
  );
3645
3674
  }
3646
3675
  const threadId = options?.threadId ?? (this._reuseThreadId && !getRequestContext() ? this._threadId : void 0);
3647
- const { data, headers } = await request(this._httpConfig, "POST", `/steps/${stepType}/execute`, {
3676
+ const { data: asyncData, headers } = await request(this._currentHttpConfig, "POST", `/steps/${stepType}/execute-async`, {
3648
3677
  step,
3649
3678
  ...options?.appId != null && { appId: options.appId },
3650
3679
  ...threadId != null && { threadId },
3651
3680
  ...this._currentStreamId != null && { streamId: this._currentStreamId }
3652
3681
  });
3653
- let output;
3654
- if (data.output != null) {
3655
- output = data.output;
3656
- } else if (data.outputUrl) {
3657
- const res = await fetch(data.outputUrl);
3682
+ const remaining = headers.get("x-ratelimit-remaining");
3683
+ const returnedThreadId = asyncData.threadId ?? "";
3684
+ if (this._reuseThreadId && returnedThreadId && !getRequestContext()) {
3685
+ this._threadId = returnedThreadId;
3686
+ }
3687
+ if (!this._appId && asyncData.appId && !getRequestContext()) {
3688
+ this._appId = asyncData.appId;
3689
+ }
3690
+ const pollUrl = `${this._currentHttpConfig.baseUrl}/developer/v2/steps/${stepType}/execute-async/poll/${asyncData.executionToken}`;
3691
+ let pollDelay = 100;
3692
+ while (true) {
3693
+ await sleep3(pollDelay);
3694
+ pollDelay = Math.min(pollDelay * 2, 5e3);
3695
+ const res = await fetch(pollUrl, {
3696
+ headers: { "User-Agent": "@mindstudio-ai/agent" }
3697
+ });
3698
+ if (res.status === 502 || res.status === 503 || res.status === 504) continue;
3699
+ if (res.status === 404) {
3700
+ throw new MindStudioError(
3701
+ `[${stepType}] Execution token expired.`,
3702
+ "poll_token_expired",
3703
+ 404
3704
+ );
3705
+ }
3658
3706
  if (!res.ok) {
3707
+ const errorBody = await res.json().catch(() => ({}));
3659
3708
  throw new MindStudioError(
3660
- `Failed to fetch ${stepType} output from S3: ${res.status} ${res.statusText}`,
3661
- "output_fetch_error",
3662
- res.status
3709
+ errorBody.message ?? errorBody.error ?? `[${stepType}] Poll failed: ${res.status} ${res.statusText}`,
3710
+ errorBody.code ?? "poll_error",
3711
+ res.status,
3712
+ errorBody
3663
3713
  );
3664
3714
  }
3665
- const envelope = await res.json();
3666
- output = envelope.value;
3667
- } else {
3668
- output = void 0;
3669
- }
3670
- const returnedThreadId = headers.get("x-mindstudio-thread-id") ?? "";
3671
- if (this._reuseThreadId && returnedThreadId && !getRequestContext()) {
3672
- this._threadId = returnedThreadId;
3673
- }
3674
- const returnedAppId = headers.get("x-mindstudio-app-id");
3675
- if (!this._appId && returnedAppId && !getRequestContext()) {
3676
- this._appId = returnedAppId;
3715
+ const poll = await res.json();
3716
+ if (poll.status === "pending") continue;
3717
+ if (poll.status === "error") {
3718
+ throw new MindStudioError(
3719
+ `[${stepType}] ${poll.error ?? "Step execution failed."}`,
3720
+ "step_error",
3721
+ 500
3722
+ );
3723
+ }
3724
+ let output;
3725
+ if (poll.output != null) {
3726
+ output = poll.output;
3727
+ } else if (poll.outputUrl) {
3728
+ const s3Res = await fetch(poll.outputUrl);
3729
+ if (!s3Res.ok) {
3730
+ throw new MindStudioError(
3731
+ `Failed to fetch ${stepType} output from S3: ${s3Res.status} ${s3Res.statusText}`,
3732
+ "output_fetch_error",
3733
+ s3Res.status
3734
+ );
3735
+ }
3736
+ const envelope = await s3Res.json();
3737
+ output = envelope.value;
3738
+ } else {
3739
+ output = void 0;
3740
+ }
3741
+ return {
3742
+ ...output,
3743
+ $appId: poll.appId ?? asyncData.appId ?? "",
3744
+ $threadId: poll.threadId ?? returnedThreadId,
3745
+ $rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
3746
+ $billingCost: poll.billingCost,
3747
+ $billingEvents: poll.billingEvents
3748
+ };
3677
3749
  }
3678
- const remaining = headers.get("x-ratelimit-remaining");
3679
- const billingCost = headers.get("x-mindstudio-billing-cost");
3680
- const billingEvents = headers.get("x-mindstudio-billing-events");
3681
- return {
3682
- ...output,
3683
- $appId: headers.get("x-mindstudio-app-id") ?? "",
3684
- $threadId: returnedThreadId,
3685
- $rateLimitRemaining: remaining != null ? parseInt(remaining, 10) : void 0,
3686
- $billingCost: billingCost != null ? parseFloat(billingCost) : void 0,
3687
- $billingEvents: billingEvents != null ? JSON.parse(billingEvents) : void 0
3688
- };
3689
3750
  }
3690
3751
  /**
3691
3752
  * @internal Streaming step execution — sends `Accept: text/event-stream`
@@ -3884,6 +3945,7 @@ var MindStudioAgent = class {
3884
3945
  const res = await fetch(pollUrl, {
3885
3946
  headers: { "User-Agent": "@mindstudio-ai/agent" }
3886
3947
  });
3948
+ if (res.status === 502 || res.status === 503 || res.status === 504) continue;
3887
3949
  if (res.status === 404) {
3888
3950
  throw new MindStudioError(
3889
3951
  "Batch poll token not found or expired.",
@@ -4047,6 +4109,7 @@ var MindStudioAgent = class {
4047
4109
  const res = await fetch(pollUrl, {
4048
4110
  headers: { "User-Agent": "@mindstudio-ai/agent" }
4049
4111
  });
4112
+ if (res.status === 502 || res.status === 503 || res.status === 504) continue;
4050
4113
  if (res.status === 404) {
4051
4114
  throw new MindStudioError(
4052
4115
  "Poll token not found or expired.",