@mindstudio-ai/agent 0.1.34 → 0.1.35

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.
@@ -1225,13 +1225,23 @@ async function requestWithRetry(config, method, url, body, attempt) {
1225
1225
  return requestWithRetry(config, method, url, body, attempt + 1);
1226
1226
  }
1227
1227
  if (!res.ok) {
1228
- const errorBody = await res.json().catch(() => ({}));
1229
- throw new MindStudioError(
1230
- errorBody.message || `${res.status} ${res.statusText}`,
1231
- errorBody.code || "api_error",
1232
- res.status,
1233
- errorBody
1234
- );
1228
+ let message = `${res.status} ${res.statusText}`;
1229
+ let code = "api_error";
1230
+ let details;
1231
+ try {
1232
+ const text = await res.text();
1233
+ try {
1234
+ const body2 = JSON.parse(text);
1235
+ details = body2;
1236
+ const errMsg = body2.error ?? body2.message ?? body2.details;
1237
+ if (errMsg) message = errMsg;
1238
+ if (body2.code) code = body2.code;
1239
+ } catch {
1240
+ if (text && text.length < 500) message = text;
1241
+ }
1242
+ } catch {
1243
+ }
1244
+ throw new MindStudioError(message, code, res.status, details);
1235
1245
  }
1236
1246
  const data = await res.json();
1237
1247
  return { data, headers: res.headers };
@@ -2515,6 +2525,9 @@ var init_table = __esm({
2515
2525
  const items = (isArray ? data : [data]).map(
2516
2526
  (item) => this._config.defaults ? { ...this._config.defaults, ...item } : item
2517
2527
  );
2528
+ for (const item of items) {
2529
+ this._checkManagedColumns(item);
2530
+ }
2518
2531
  const queries = items.map(
2519
2532
  (item) => buildInsert(
2520
2533
  this._config.tableName,
@@ -2532,7 +2545,13 @@ var init_table = __esm({
2532
2545
  }
2533
2546
  return void 0;
2534
2547
  });
2535
- return isArray ? rows : rows[0];
2548
+ const result = isArray ? rows : rows[0];
2549
+ this._syncRolesIfNeeded(
2550
+ items,
2551
+ result,
2552
+ isArray
2553
+ );
2554
+ return result;
2536
2555
  });
2537
2556
  }
2538
2557
  /**
@@ -2540,20 +2559,25 @@ var init_table = __esm({
2540
2559
  * Returns the updated row via `UPDATE ... RETURNING *`.
2541
2560
  */
2542
2561
  update(id, data) {
2562
+ this._checkManagedColumns(data);
2543
2563
  const query = buildUpdate(
2544
2564
  this._config.tableName,
2545
2565
  id,
2546
2566
  data,
2547
2567
  this._config.columns
2548
2568
  );
2549
- return new Mutation(
2550
- this._config,
2551
- [query],
2552
- (results) => deserializeRow(
2569
+ return new Mutation(this._config, [query], (results) => {
2570
+ const result = deserializeRow(
2553
2571
  results[0].rows[0],
2554
2572
  this._config.columns
2555
- )
2556
- );
2573
+ );
2574
+ this._syncRolesIfNeeded(
2575
+ [data],
2576
+ result,
2577
+ false
2578
+ );
2579
+ return result;
2580
+ });
2557
2581
  }
2558
2582
  remove(id) {
2559
2583
  const query = buildDelete(this._config.tableName, `id = ?`, [id]);
@@ -2608,24 +2632,65 @@ var init_table = __esm({
2608
2632
  const conflictColumns = Array.isArray(conflictKey) ? conflictKey : [conflictKey];
2609
2633
  this._validateUniqueConstraint(conflictColumns);
2610
2634
  const withDefaults = this._config.defaults ? { ...this._config.defaults, ...data } : data;
2635
+ this._checkManagedColumns(withDefaults);
2611
2636
  const query = buildUpsert(
2612
2637
  this._config.tableName,
2613
2638
  withDefaults,
2614
2639
  conflictColumns,
2615
2640
  this._config.columns
2616
2641
  );
2617
- return new Mutation(
2618
- this._config,
2619
- [query],
2620
- (results) => deserializeRow(
2642
+ return new Mutation(this._config, [query], (results) => {
2643
+ const result = deserializeRow(
2621
2644
  results[0].rows[0],
2622
2645
  this._config.columns
2623
- )
2624
- );
2646
+ );
2647
+ this._syncRolesIfNeeded([withDefaults], result, false);
2648
+ return result;
2649
+ });
2625
2650
  }
2626
2651
  // -------------------------------------------------------------------------
2627
2652
  // Internal helpers
2628
2653
  // -------------------------------------------------------------------------
2654
+ /** @internal Throw if data includes a platform-managed email/phone column. */
2655
+ _checkManagedColumns(data) {
2656
+ const mc = this._config.managedColumns;
2657
+ if (!mc) return;
2658
+ const keys = Object.keys(data);
2659
+ for (const key of keys) {
2660
+ if (mc.email && key === mc.email || mc.phone && key === mc.phone) {
2661
+ throw new MindStudioError(
2662
+ `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"}.`,
2663
+ "managed_column_write",
2664
+ 400
2665
+ );
2666
+ }
2667
+ }
2668
+ }
2669
+ /**
2670
+ * @internal Fire role sync for rows that wrote to the roles column.
2671
+ * Called inside processResult (runs after SQL execution in both
2672
+ * standalone and batch paths). Fire-and-forget.
2673
+ */
2674
+ _syncRolesIfNeeded(inputItems, result, isArray) {
2675
+ const rolesCol = this._config.managedColumns?.roles;
2676
+ const syncRoles = this._config.syncRoles;
2677
+ if (!rolesCol || !syncRoles) return;
2678
+ if (!inputItems.some((item) => rolesCol in item)) return;
2679
+ if (isArray) {
2680
+ for (const row of result) {
2681
+ if (row?.id) {
2682
+ syncRoles(row.id, row[rolesCol]).catch(() => {
2683
+ });
2684
+ }
2685
+ }
2686
+ } else {
2687
+ const row = result;
2688
+ if (row?.id) {
2689
+ syncRoles(row.id, row[rolesCol]).catch(() => {
2690
+ });
2691
+ }
2692
+ }
2693
+ }
2629
2694
  /** @internal Validate that the given columns match a declared unique constraint. */
2630
2695
  _validateUniqueConstraint(columns) {
2631
2696
  if (!this._config.unique?.length) {
@@ -2652,7 +2717,7 @@ var init_table = __esm({
2652
2717
  });
2653
2718
 
2654
2719
  // src/db/index.ts
2655
- function createDb(databases, executeBatch) {
2720
+ function createDb(databases, executeBatch, authConfig, syncRoles) {
2656
2721
  return {
2657
2722
  defineTable(name, options) {
2658
2723
  const resolved = resolveTable(databases, name, options?.database);
@@ -2662,6 +2727,8 @@ function createDb(databases, executeBatch) {
2662
2727
  columns: resolved.columns,
2663
2728
  unique: options?.unique,
2664
2729
  defaults: options?.defaults,
2730
+ managedColumns: authConfig?.table === name ? authConfig.columns : void 0,
2731
+ syncRoles: authConfig?.table === name && authConfig.columns.roles ? syncRoles : void 0,
2665
2732
  executeBatch: (queries) => executeBatch(resolved.databaseId, queries)
2666
2733
  };
2667
2734
  return new Table(config);
@@ -4011,7 +4078,9 @@ var init_client = __esm({
4011
4078
  this._auth = new AuthContext(context.auth);
4012
4079
  this._db = createDb(
4013
4080
  context.databases,
4014
- this._executeDbBatch.bind(this)
4081
+ this._executeDbBatch.bind(this),
4082
+ context.authConfig,
4083
+ this._syncRoles.bind(this)
4015
4084
  );
4016
4085
  }
4017
4086
  /**
@@ -4027,7 +4096,8 @@ var init_client = __esm({
4027
4096
  if (ai?.auth && ai?.databases) {
4028
4097
  this._applyContext({
4029
4098
  auth: ai.auth,
4030
- databases: ai.databases
4099
+ databases: ai.databases,
4100
+ authConfig: ai.authConfig
4031
4101
  });
4032
4102
  }
4033
4103
  }
@@ -4073,6 +4143,39 @@ var init_client = __esm({
4073
4143
  const data = await res.json();
4074
4144
  return data.results;
4075
4145
  }
4146
+ /**
4147
+ * @internal Sync a user's roles to the platform after a successful
4148
+ * auth table write. Calls POST /_internal/v2/auth/sync-user.
4149
+ * Fire-and-forget: errors are caught and logged, never propagated.
4150
+ */
4151
+ async _syncRoles(userId, roles) {
4152
+ try {
4153
+ const url = `${this._httpConfig.baseUrl}/_internal/v2/auth/sync-user`;
4154
+ const res = await fetch(url, {
4155
+ method: "POST",
4156
+ headers: {
4157
+ "Content-Type": "application/json",
4158
+ Authorization: this._token
4159
+ },
4160
+ body: JSON.stringify({
4161
+ appId: this._appId,
4162
+ userId,
4163
+ roles
4164
+ })
4165
+ });
4166
+ if (!res.ok) {
4167
+ const text = await res.text().catch(() => "");
4168
+ console.warn(
4169
+ `[mindstudio] Failed to sync roles for user ${userId}: ${res.status} ${text}`
4170
+ );
4171
+ }
4172
+ } catch (err) {
4173
+ console.warn(
4174
+ `[mindstudio] Failed to sync roles for user ${userId}:`,
4175
+ err
4176
+ );
4177
+ }
4178
+ }
4076
4179
  /**
4077
4180
  * @internal Create a lazy Db proxy that auto-hydrates context.
4078
4181
  *
@@ -4085,7 +4188,7 @@ var init_client = __esm({
4085
4188
  return {
4086
4189
  defineTable(name, options) {
4087
4190
  const databaseHint = options?.database;
4088
- return new Table({
4191
+ const tableConfig = {
4089
4192
  databaseId: "",
4090
4193
  tableName: name,
4091
4194
  columns: [],
@@ -4093,6 +4196,13 @@ var init_client = __esm({
4093
4196
  defaults: options?.defaults,
4094
4197
  executeBatch: async (queries) => {
4095
4198
  await agent.ensureContext();
4199
+ const ac = agent._context.authConfig;
4200
+ if (ac && ac.table === name && !tableConfig.managedColumns) {
4201
+ tableConfig.managedColumns = ac.columns;
4202
+ if (ac.columns.roles) {
4203
+ tableConfig.syncRoles = agent._syncRoles.bind(agent);
4204
+ }
4205
+ }
4096
4206
  const databases = agent._context.databases;
4097
4207
  let targetDb;
4098
4208
  if (databaseHint) {
@@ -4107,7 +4217,8 @@ var init_client = __esm({
4107
4217
  const databaseId = targetDb?.id ?? databases[0]?.id ?? "";
4108
4218
  return agent._executeDbBatch(databaseId, queries);
4109
4219
  }
4110
- });
4220
+ };
4221
+ return new Table(tableConfig);
4111
4222
  },
4112
4223
  // Time helpers work without context
4113
4224
  now: () => Date.now(),
@@ -4566,6 +4677,8 @@ var init_instructions = __esm({
4566
4677
  - **Missing MindStudioError handling** \u2014 the SDK has structured errors with \`code\`, \`status\`, \`details\`. Catching generic \`Error\` loses actionable information. Always include \`MindStudioError\` handling in code examples.
4567
4678
  - **One-at-a-time db writes when batch exists** \u2014 N sequential \`update()\` or \`push()\` calls should be a single \`db.batch()\` call. One round trip instead of N.
4568
4679
  - **Hardcoded model IDs without context** \u2014 model IDs can change. When writing code with a specific model, include a comment noting which model it is and why it was chosen, so the caller can swap it later.
4680
+ - Do not recommend using thread persistence. This is an advanced feature - if someone needs this they will discover it on their own.
4681
+ - Generate text is great for one-off text generation requests to models. For sustained conversations, conversations that require history, persistence, or multiple threads or tool use, recommend that the developer add an Agent interface to their MindStudio app and use that directly. They will understand what you mean by this.
4569
4682
  </anti_patterns>
4570
4683
 
4571
4684
  <model_guidance>
@@ -4885,7 +4998,7 @@ async function startMcpServer(options) {
4885
4998
  capabilities: { tools: {} },
4886
4999
  serverInfo: {
4887
5000
  name: "mindstudio-agent",
4888
- version: "0.1.34"
5001
+ version: "0.1.35"
4889
5002
  },
4890
5003
  instructions: 'Welcome to MindStudio \u2014 a platform with 200+ AI models, 850+ third-party integrations, and pre-built agents.\n\nGetting started:\n1. Call `ask` with any question about the SDK \u2014 it knows every action, model, and connector and returns working code with real model IDs and config options. Examples: ask("generate an image with FLUX"), ask("what models support vision?"), ask("how do I send a Slack message?").\n2. Call `changeName` to set your display name \u2014 use your name or whatever your user calls you. This is how you\'ll appear in MindStudio request logs.\n3. If you have a profile picture or icon, call `uploadFile` to upload it, then `changeProfilePicture` with the returned URL.\n4. For manual browsing, call `listActions` to discover all available actions.\n\nThen use the tools to generate text, images, video, audio, search the web, work with data sources, run agents, and more.\n\nImportant:\n- AI-powered actions (text generation, image generation, video, audio, etc.) cost money. Before running these, call `estimateActionCost` and confirm with the user before proceeding \u2014 unless they\'ve explicitly told you to go ahead.\n- Not all agents from `listAgents` are configured for API use. Do not try to run an agent just because it appears in the list \u2014 it will likely fail. Only run agents the user specifically asks you to run.'
4891
5004
  });
@@ -5745,7 +5858,7 @@ function isNewerVersion(current, latest) {
5745
5858
  return false;
5746
5859
  }
5747
5860
  async function checkForUpdate() {
5748
- const currentVersion = "0.1.34";
5861
+ const currentVersion = "0.1.35";
5749
5862
  if (!currentVersion) return null;
5750
5863
  try {
5751
5864
  const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -5774,7 +5887,7 @@ async function checkForUpdate() {
5774
5887
  }
5775
5888
  }
5776
5889
  function printUpdateNotice(latestVersion) {
5777
- const currentVersion = "0.1.34";
5890
+ const currentVersion = "0.1.35";
5778
5891
  process.stderr.write(
5779
5892
  `
5780
5893
  ${ansi2.cyanBright("Update available")} ${ansi2.gray(currentVersion + " \u2192")} ${ansi2.cyanBold(latestVersion)}
@@ -5787,7 +5900,7 @@ function isStandaloneBinary() {
5787
5900
  return !argv1.includes("node_modules");
5788
5901
  }
5789
5902
  async function cmdUpdate() {
5790
- const currentVersion = "0.1.34";
5903
+ const currentVersion = "0.1.35";
5791
5904
  process.stderr.write(
5792
5905
  ` ${ansi2.gray("Current version:")} ${currentVersion}
5793
5906
  `
@@ -5902,7 +6015,7 @@ async function cmdLogin(options) {
5902
6015
  process.stderr.write("\n");
5903
6016
  printLogo();
5904
6017
  process.stderr.write("\n");
5905
- const ver = "0.1.34";
6018
+ const ver = "0.1.35";
5906
6019
  process.stderr.write(
5907
6020
  ` ${ansi2.bold("MindStudio Agent")} ${ver ? " " + ansi2.gray("v" + ver) : ""}
5908
6021
  `
@@ -6213,7 +6326,7 @@ async function main() {
6213
6326
  try {
6214
6327
  if (command === "version" || command === "-v") {
6215
6328
  process.stdout.write(
6216
- "0.1.34\n"
6329
+ "0.1.35\n"
6217
6330
  );
6218
6331
  return;
6219
6332
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/agent",
3
- "version": "0.1.34",
3
+ "version": "0.1.35",
4
4
  "description": "TypeScript SDK for MindStudio direct step execution",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",