@mindstudio-ai/agent 0.1.34 → 0.1.36

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/cli.js CHANGED
@@ -1225,13 +1225,26 @@ 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 = (typeof body2.error === "string" ? body2.error : void 0) ?? (typeof body2.message === "string" ? body2.message : void 0) ?? (typeof body2.details === "string" ? body2.details : void 0);
1237
+ if (errMsg) message = errMsg;
1238
+ else if (body2.error || body2.message || body2.details) {
1239
+ message = JSON.stringify(body2.error ?? body2.message ?? body2.details);
1240
+ }
1241
+ if (body2.code) code = body2.code;
1242
+ } catch {
1243
+ if (text && text.length < 500) message = text;
1244
+ }
1245
+ } catch {
1246
+ }
1247
+ throw new MindStudioError(message, code, res.status, details);
1235
1248
  }
1236
1249
  const data = await res.json();
1237
1250
  return { data, headers: res.headers };
@@ -1464,6 +1477,7 @@ function escapeValue(val) {
1464
1477
  return `'${json.replace(/'/g, "''")}'`;
1465
1478
  }
1466
1479
  function deserializeRow(row, columns) {
1480
+ if (row == null) return row;
1467
1481
  const result = {};
1468
1482
  for (const [key, value] of Object.entries(row)) {
1469
1483
  const col = columns.find((c) => c.name === key);
@@ -2085,6 +2099,8 @@ var init_query = __esm({
2085
2099
  _limit;
2086
2100
  _offset;
2087
2101
  _config;
2102
+ /** @internal Post-process transform applied after row deserialization. */
2103
+ _postProcess;
2088
2104
  constructor(config, options) {
2089
2105
  this._config = config;
2090
2106
  this._predicates = options?.predicates ?? [];
@@ -2092,6 +2108,7 @@ var init_query = __esm({
2092
2108
  this._reversed = options?.reversed ?? false;
2093
2109
  this._limit = options?.limit;
2094
2110
  this._offset = options?.offset;
2111
+ this._postProcess = options?.postProcess;
2095
2112
  }
2096
2113
  _clone(overrides) {
2097
2114
  return new _Query(this._config, {
@@ -2099,7 +2116,8 @@ var init_query = __esm({
2099
2116
  sortAccessor: overrides.sortAccessor ?? this._sortAccessor,
2100
2117
  reversed: overrides.reversed ?? this._reversed,
2101
2118
  limit: overrides.limit ?? this._limit,
2102
- offset: overrides.offset ?? this._offset
2119
+ offset: overrides.offset ?? this._offset,
2120
+ postProcess: overrides.postProcess
2103
2121
  });
2104
2122
  }
2105
2123
  // -------------------------------------------------------------------------
@@ -2123,13 +2141,18 @@ var init_query = __esm({
2123
2141
  // -------------------------------------------------------------------------
2124
2142
  // Terminal methods
2125
2143
  // -------------------------------------------------------------------------
2126
- async first() {
2127
- const rows = await this._clone({ limit: 1 })._execute();
2128
- return rows[0] ?? null;
2144
+ first() {
2145
+ return this._clone({
2146
+ limit: 1,
2147
+ postProcess: (rows) => rows[0] ?? null
2148
+ });
2129
2149
  }
2130
- async last() {
2131
- const rows = await this._clone({ limit: 1, reversed: !this._reversed })._execute();
2132
- return rows[0] ?? null;
2150
+ last() {
2151
+ return this._clone({
2152
+ limit: 1,
2153
+ reversed: !this._reversed,
2154
+ postProcess: (rows) => rows[0] ?? null
2155
+ });
2133
2156
  }
2134
2157
  async count() {
2135
2158
  const compiled = this._compilePredicates();
@@ -2178,10 +2201,10 @@ var init_query = __esm({
2178
2201
  (row) => this._predicates.every((pred) => pred(row))
2179
2202
  );
2180
2203
  }
2181
- async min(accessor) {
2204
+ min(accessor) {
2182
2205
  return this.sortBy(accessor).first();
2183
2206
  }
2184
- async max(accessor) {
2207
+ max(accessor) {
2185
2208
  return this.sortBy(accessor).reverse().first();
2186
2209
  }
2187
2210
  async groupBy(accessor) {
@@ -2220,7 +2243,7 @@ var init_query = __esm({
2220
2243
  limit: this._limit,
2221
2244
  offset: this._offset
2222
2245
  });
2223
- return { type: "query", query, fallbackQuery: null, config: this._config };
2246
+ return { type: "query", query, fallbackQuery: null, config: this._config, postProcess: this._postProcess };
2224
2247
  }
2225
2248
  const fallbackQuery = buildSelect(this._config.tableName);
2226
2249
  return {
@@ -2232,7 +2255,8 @@ var init_query = __esm({
2232
2255
  sortAccessor: this._sortAccessor,
2233
2256
  reversed: this._reversed,
2234
2257
  limit: this._limit,
2235
- offset: this._offset
2258
+ offset: this._offset,
2259
+ postProcess: this._postProcess
2236
2260
  };
2237
2261
  }
2238
2262
  /**
@@ -2249,7 +2273,9 @@ var init_query = __esm({
2249
2273
  compiled.config.columns
2250
2274
  )
2251
2275
  );
2252
- if (compiled.query) return rows;
2276
+ if (compiled.query) {
2277
+ return compiled.postProcess ? compiled.postProcess(rows) : rows;
2278
+ }
2253
2279
  let filtered = compiled.predicates ? rows.filter((row) => compiled.predicates.every((pred) => pred(row))) : rows;
2254
2280
  if (compiled.sortAccessor) {
2255
2281
  const accessor = compiled.sortAccessor;
@@ -2267,16 +2293,19 @@ var init_query = __esm({
2267
2293
  const end = compiled.limit != null ? start + compiled.limit : void 0;
2268
2294
  filtered = filtered.slice(start, end);
2269
2295
  }
2270
- return filtered;
2296
+ return compiled.postProcess ? compiled.postProcess(filtered) : filtered;
2271
2297
  }
2272
2298
  // -------------------------------------------------------------------------
2273
2299
  // PromiseLike
2274
2300
  // -------------------------------------------------------------------------
2275
2301
  then(onfulfilled, onrejected) {
2276
- return this._execute().then(onfulfilled, onrejected);
2302
+ const promise = this._execute().then(
2303
+ (rows) => this._postProcess ? this._postProcess(rows) : rows
2304
+ );
2305
+ return promise.then(onfulfilled, onrejected);
2277
2306
  }
2278
2307
  catch(onrejected) {
2279
- return this._execute().catch(onrejected);
2308
+ return this.then(void 0, onrejected);
2280
2309
  }
2281
2310
  // -------------------------------------------------------------------------
2282
2311
  // Execution internals
@@ -2515,6 +2544,9 @@ var init_table = __esm({
2515
2544
  const items = (isArray ? data : [data]).map(
2516
2545
  (item) => this._config.defaults ? { ...this._config.defaults, ...item } : item
2517
2546
  );
2547
+ for (const item of items) {
2548
+ this._checkManagedColumns(item);
2549
+ }
2518
2550
  const queries = items.map(
2519
2551
  (item) => buildInsert(
2520
2552
  this._config.tableName,
@@ -2532,7 +2564,13 @@ var init_table = __esm({
2532
2564
  }
2533
2565
  return void 0;
2534
2566
  });
2535
- return isArray ? rows : rows[0];
2567
+ const result = isArray ? rows : rows[0];
2568
+ this._syncRolesIfNeeded(
2569
+ items,
2570
+ result,
2571
+ isArray
2572
+ );
2573
+ return result;
2536
2574
  });
2537
2575
  }
2538
2576
  /**
@@ -2540,20 +2578,25 @@ var init_table = __esm({
2540
2578
  * Returns the updated row via `UPDATE ... RETURNING *`.
2541
2579
  */
2542
2580
  update(id, data) {
2581
+ this._checkManagedColumns(data);
2543
2582
  const query = buildUpdate(
2544
2583
  this._config.tableName,
2545
2584
  id,
2546
2585
  data,
2547
2586
  this._config.columns
2548
2587
  );
2549
- return new Mutation(
2550
- this._config,
2551
- [query],
2552
- (results) => deserializeRow(
2588
+ return new Mutation(this._config, [query], (results) => {
2589
+ const result = deserializeRow(
2553
2590
  results[0].rows[0],
2554
2591
  this._config.columns
2555
- )
2556
- );
2592
+ );
2593
+ this._syncRolesIfNeeded(
2594
+ [data],
2595
+ result,
2596
+ false
2597
+ );
2598
+ return result;
2599
+ });
2557
2600
  }
2558
2601
  remove(id) {
2559
2602
  const query = buildDelete(this._config.tableName, `id = ?`, [id]);
@@ -2608,24 +2651,65 @@ var init_table = __esm({
2608
2651
  const conflictColumns = Array.isArray(conflictKey) ? conflictKey : [conflictKey];
2609
2652
  this._validateUniqueConstraint(conflictColumns);
2610
2653
  const withDefaults = this._config.defaults ? { ...this._config.defaults, ...data } : data;
2654
+ this._checkManagedColumns(withDefaults);
2611
2655
  const query = buildUpsert(
2612
2656
  this._config.tableName,
2613
2657
  withDefaults,
2614
2658
  conflictColumns,
2615
2659
  this._config.columns
2616
2660
  );
2617
- return new Mutation(
2618
- this._config,
2619
- [query],
2620
- (results) => deserializeRow(
2661
+ return new Mutation(this._config, [query], (results) => {
2662
+ const result = deserializeRow(
2621
2663
  results[0].rows[0],
2622
2664
  this._config.columns
2623
- )
2624
- );
2665
+ );
2666
+ this._syncRolesIfNeeded([withDefaults], result, false);
2667
+ return result;
2668
+ });
2625
2669
  }
2626
2670
  // -------------------------------------------------------------------------
2627
2671
  // Internal helpers
2628
2672
  // -------------------------------------------------------------------------
2673
+ /** @internal Throw if data includes a platform-managed email/phone column. */
2674
+ _checkManagedColumns(data) {
2675
+ const mc = this._config.managedColumns;
2676
+ if (!mc) return;
2677
+ const keys = Object.keys(data);
2678
+ for (const key of keys) {
2679
+ if (mc.email && key === mc.email || mc.phone && key === mc.phone) {
2680
+ throw new MindStudioError(
2681
+ `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"}.`,
2682
+ "managed_column_write",
2683
+ 400
2684
+ );
2685
+ }
2686
+ }
2687
+ }
2688
+ /**
2689
+ * @internal Fire role sync for rows that wrote to the roles column.
2690
+ * Called inside processResult (runs after SQL execution in both
2691
+ * standalone and batch paths). Fire-and-forget.
2692
+ */
2693
+ _syncRolesIfNeeded(inputItems, result, isArray) {
2694
+ const rolesCol = this._config.managedColumns?.roles;
2695
+ const syncRoles = this._config.syncRoles;
2696
+ if (!rolesCol || !syncRoles) return;
2697
+ if (!inputItems.some((item) => rolesCol in item)) return;
2698
+ if (isArray) {
2699
+ for (const row of result) {
2700
+ if (row?.id) {
2701
+ syncRoles(row.id, row[rolesCol]).catch(() => {
2702
+ });
2703
+ }
2704
+ }
2705
+ } else {
2706
+ const row = result;
2707
+ if (row?.id) {
2708
+ syncRoles(row.id, row[rolesCol]).catch(() => {
2709
+ });
2710
+ }
2711
+ }
2712
+ }
2629
2713
  /** @internal Validate that the given columns match a declared unique constraint. */
2630
2714
  _validateUniqueConstraint(columns) {
2631
2715
  if (!this._config.unique?.length) {
@@ -2652,7 +2736,7 @@ var init_table = __esm({
2652
2736
  });
2653
2737
 
2654
2738
  // src/db/index.ts
2655
- function createDb(databases, executeBatch) {
2739
+ function createDb(databases, executeBatch, authConfig, syncRoles) {
2656
2740
  return {
2657
2741
  defineTable(name, options) {
2658
2742
  const resolved = resolveTable(databases, name, options?.database);
@@ -2662,6 +2746,8 @@ function createDb(databases, executeBatch) {
2662
2746
  columns: resolved.columns,
2663
2747
  unique: options?.unique,
2664
2748
  defaults: options?.defaults,
2749
+ managedColumns: authConfig?.table === name ? authConfig.columns : void 0,
2750
+ syncRoles: authConfig?.table === name && authConfig.columns.roles ? syncRoles : void 0,
2665
2751
  executeBatch: (queries) => executeBatch(resolved.databaseId, queries)
2666
2752
  };
2667
2753
  return new Table(config);
@@ -3923,6 +4009,12 @@ var init_client = __esm({
3923
4009
  * ```
3924
4010
  */
3925
4011
  get auth() {
4012
+ if (this._authType === "internal") {
4013
+ const ai = globalThis.ai;
4014
+ if (ai?.auth) {
4015
+ return new AuthContext(ai.auth);
4016
+ }
4017
+ }
3926
4018
  if (!this._auth) {
3927
4019
  this._trySandboxHydration();
3928
4020
  }
@@ -4011,7 +4103,9 @@ var init_client = __esm({
4011
4103
  this._auth = new AuthContext(context.auth);
4012
4104
  this._db = createDb(
4013
4105
  context.databases,
4014
- this._executeDbBatch.bind(this)
4106
+ this._executeDbBatch.bind(this),
4107
+ context.authConfig,
4108
+ this._syncRoles.bind(this)
4015
4109
  );
4016
4110
  }
4017
4111
  /**
@@ -4027,7 +4121,8 @@ var init_client = __esm({
4027
4121
  if (ai?.auth && ai?.databases) {
4028
4122
  this._applyContext({
4029
4123
  auth: ai.auth,
4030
- databases: ai.databases
4124
+ databases: ai.databases,
4125
+ authConfig: ai.authConfig
4031
4126
  });
4032
4127
  }
4033
4128
  }
@@ -4056,8 +4151,11 @@ var init_client = __esm({
4056
4151
  const text = await res.text();
4057
4152
  try {
4058
4153
  const body = JSON.parse(text);
4059
- const errMsg = body.error ?? body.message ?? body.details;
4154
+ const errMsg = (typeof body.error === "string" ? body.error : void 0) ?? (typeof body.message === "string" ? body.message : void 0) ?? (typeof body.details === "string" ? body.details : void 0);
4060
4155
  if (errMsg) message = errMsg;
4156
+ else if (body.error || body.message || body.details) {
4157
+ message = JSON.stringify(body.error ?? body.message ?? body.details);
4158
+ }
4061
4159
  if (body.code) code = body.code;
4062
4160
  } catch {
4063
4161
  if (text && text.length < 500) message = text;
@@ -4073,6 +4171,39 @@ var init_client = __esm({
4073
4171
  const data = await res.json();
4074
4172
  return data.results;
4075
4173
  }
4174
+ /**
4175
+ * @internal Sync a user's roles to the platform after a successful
4176
+ * auth table write. Calls POST /_internal/v2/auth/sync-user.
4177
+ * Fire-and-forget: errors are caught and logged, never propagated.
4178
+ */
4179
+ async _syncRoles(userId, roles) {
4180
+ try {
4181
+ const url = `${this._httpConfig.baseUrl}/_internal/v2/auth/sync-user`;
4182
+ const res = await fetch(url, {
4183
+ method: "POST",
4184
+ headers: {
4185
+ "Content-Type": "application/json",
4186
+ Authorization: this._token
4187
+ },
4188
+ body: JSON.stringify({
4189
+ appId: this._appId,
4190
+ userId,
4191
+ roles
4192
+ })
4193
+ });
4194
+ if (!res.ok) {
4195
+ const text = await res.text().catch(() => "");
4196
+ console.warn(
4197
+ `[mindstudio] Failed to sync roles for user ${userId}: ${res.status} ${text}`
4198
+ );
4199
+ }
4200
+ } catch (err) {
4201
+ console.warn(
4202
+ `[mindstudio] Failed to sync roles for user ${userId}:`,
4203
+ err
4204
+ );
4205
+ }
4206
+ }
4076
4207
  /**
4077
4208
  * @internal Create a lazy Db proxy that auto-hydrates context.
4078
4209
  *
@@ -4085,7 +4216,7 @@ var init_client = __esm({
4085
4216
  return {
4086
4217
  defineTable(name, options) {
4087
4218
  const databaseHint = options?.database;
4088
- return new Table({
4219
+ const tableConfig = {
4089
4220
  databaseId: "",
4090
4221
  tableName: name,
4091
4222
  columns: [],
@@ -4093,6 +4224,13 @@ var init_client = __esm({
4093
4224
  defaults: options?.defaults,
4094
4225
  executeBatch: async (queries) => {
4095
4226
  await agent.ensureContext();
4227
+ const ac = agent._context.authConfig;
4228
+ if (ac && ac.table === name && !tableConfig.managedColumns) {
4229
+ tableConfig.managedColumns = ac.columns;
4230
+ if (ac.columns.roles) {
4231
+ tableConfig.syncRoles = agent._syncRoles.bind(agent);
4232
+ }
4233
+ }
4096
4234
  const databases = agent._context.databases;
4097
4235
  let targetDb;
4098
4236
  if (databaseHint) {
@@ -4107,7 +4245,8 @@ var init_client = __esm({
4107
4245
  const databaseId = targetDb?.id ?? databases[0]?.id ?? "";
4108
4246
  return agent._executeDbBatch(databaseId, queries);
4109
4247
  }
4110
- });
4248
+ };
4249
+ return new Table(tableConfig);
4111
4250
  },
4112
4251
  // Time helpers work without context
4113
4252
  now: () => Date.now(),
@@ -4566,6 +4705,8 @@ var init_instructions = __esm({
4566
4705
  - **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
4706
  - **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
4707
  - **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.
4708
+ - Do not recommend using thread persistence. This is an advanced feature - if someone needs this they will discover it on their own.
4709
+ - 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
4710
  </anti_patterns>
4570
4711
 
4571
4712
  <model_guidance>
@@ -4885,7 +5026,7 @@ async function startMcpServer(options) {
4885
5026
  capabilities: { tools: {} },
4886
5027
  serverInfo: {
4887
5028
  name: "mindstudio-agent",
4888
- version: "0.1.34"
5029
+ version: "0.1.36"
4889
5030
  },
4890
5031
  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
5032
  });
@@ -5841,7 +5982,7 @@ function isNewerVersion(current, latest) {
5841
5982
  return false;
5842
5983
  }
5843
5984
  async function checkForUpdate() {
5844
- const currentVersion = "0.1.34";
5985
+ const currentVersion = "0.1.36";
5845
5986
  if (!currentVersion) return null;
5846
5987
  try {
5847
5988
  const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -5870,7 +6011,7 @@ async function checkForUpdate() {
5870
6011
  }
5871
6012
  }
5872
6013
  function printUpdateNotice(latestVersion) {
5873
- const currentVersion = "0.1.34";
6014
+ const currentVersion = "0.1.36";
5874
6015
  process.stderr.write(
5875
6016
  `
5876
6017
  ${ansi2.cyanBright("Update available")} ${ansi2.gray(currentVersion + " \u2192")} ${ansi2.cyanBold(latestVersion)}
@@ -5883,7 +6024,7 @@ function isStandaloneBinary() {
5883
6024
  return !argv1.includes("node_modules");
5884
6025
  }
5885
6026
  async function cmdUpdate() {
5886
- const currentVersion = "0.1.34";
6027
+ const currentVersion = "0.1.36";
5887
6028
  process.stderr.write(
5888
6029
  ` ${ansi2.gray("Current version:")} ${currentVersion}
5889
6030
  `
@@ -6018,7 +6159,7 @@ async function cmdLogin(options) {
6018
6159
  process.stderr.write("\n");
6019
6160
  printLogo();
6020
6161
  process.stderr.write("\n");
6021
- const ver = "0.1.34";
6162
+ const ver = "0.1.36";
6022
6163
  process.stderr.write(
6023
6164
  ` ${ansi2.bold("MindStudio Agent")} ${ver ? " " + ansi2.gray("v" + ver) : ""}
6024
6165
  `
@@ -6345,7 +6486,7 @@ async function main() {
6345
6486
  try {
6346
6487
  if (command === "version" || command === "-v") {
6347
6488
  process.stdout.write(
6348
- "0.1.34\n"
6489
+ "0.1.36\n"
6349
6490
  );
6350
6491
  return;
6351
6492
  }