@mindstudio-ai/agent 0.1.9 → 0.1.10

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
@@ -597,16 +597,26 @@ interface TableConfig {
597
597
  */
598
598
  columns: AppDatabaseColumnSchema[];
599
599
  /**
600
- * Execute a SQL query against the managed database. This is bound to
601
- * `agent.executeStep('queryAppDatabase', ...)` at creation time.
600
+ * Execute one or more SQL queries against the managed database in a
601
+ * single round trip. All queries run on the same SQLite connection,
602
+ * enabling RETURNING clauses and multi-statement batches.
602
603
  *
603
- * @param sql - The SQL query string (fully formed, no placeholders)
604
- * @returns The query result: rows for SELECT, changes count for writes
604
+ * Bound to the `POST /_internal/v2/db/query` endpoint at creation time.
605
+ *
606
+ * @param queries - Array of SQL queries with optional bind params
607
+ * @returns Array of results in the same order as the input queries
605
608
  */
606
- executeQuery: (sql: string) => Promise<{
607
- rows: unknown[];
608
- changes: number;
609
- }>;
609
+ executeBatch: (queries: SqlQuery[]) => Promise<SqlResult[]>;
610
+ }
611
+ /** A single SQL query with optional positional bind params. */
612
+ interface SqlQuery {
613
+ sql: string;
614
+ params?: unknown[];
615
+ }
616
+ /** Result of a single SQL query execution. */
617
+ interface SqlResult {
618
+ rows: unknown[];
619
+ changes: number;
610
620
  }
611
621
 
612
622
  /**
@@ -642,53 +652,14 @@ interface TableConfig {
642
652
  *
643
653
  * Both paths produce identical results. The SQL path is a transparent
644
654
  * performance optimization.
645
- *
646
- * ## Data flow
647
- *
648
- * ```
649
- * .filter(pred1).filter(pred2).sortBy(fn).reverse().take(10)
650
- * ↓
651
- * Query { predicates: [pred1, pred2], sortAccessor: fn, reversed: true, limit: 10 }
652
- * ↓ (on await)
653
- * compilePredicate(pred1) → SQL? compilePredicate(pred2) → SQL?
654
- * ↓
655
- * All SQL? → buildSelect('table', { where: 'p1 AND p2', orderBy, desc, limit })
656
- * → executeQuery(sql) → deserialize rows → return T[]
657
- *
658
- * Any JS? → buildSelect('table', {}) → executeQuery → deserialize all rows
659
- * → Array.filter(pred1).filter(pred2).sort(...).slice(0, 10) → return T[]
660
- * ```
661
655
  */
662
656
 
663
- /**
664
- * A lazy, chainable database query. Implements PromiseLike<T[]> so it
665
- * can be awaited directly to get the result rows.
666
- *
667
- * @example
668
- * ```ts
669
- * // Chain operations (nothing executes yet)
670
- * const query = Orders
671
- * .filter(o => o.status === 'active')
672
- * .sortBy(o => o.createdAt)
673
- * .reverse()
674
- * .take(10);
675
- *
676
- * // Execute the query by awaiting it
677
- * const rows = await query;
678
- * ```
679
- */
680
657
  declare class Query<T> implements PromiseLike<T[]> {
681
- /** @internal Accumulated predicate functions to filter by. */
682
658
  private readonly _predicates;
683
- /** @internal The field accessor for sorting, if set. */
684
659
  private readonly _sortAccessor;
685
- /** @internal Whether the sort order is reversed (DESC). */
686
660
  private readonly _reversed;
687
- /** @internal Maximum number of results (SQL LIMIT). */
688
661
  private readonly _limit;
689
- /** @internal Number of results to skip (SQL OFFSET). */
690
662
  private readonly _offset;
691
- /** @internal Binding to the database execution layer. */
692
663
  private readonly _config;
693
664
  constructor(config: TableConfig, options?: {
694
665
  predicates?: Predicate<T>[];
@@ -697,356 +668,76 @@ declare class Query<T> implements PromiseLike<T[]> {
697
668
  limit?: number;
698
669
  offset?: number;
699
670
  });
700
- /**
701
- * Create a clone of this query with some options overridden.
702
- * Used internally by chain methods to maintain immutability.
703
- */
704
671
  private _clone;
705
- /**
706
- * Add a filter predicate. Multiple filters are ANDed together.
707
- *
708
- * @example
709
- * ```ts
710
- * const active = Orders.filter(o => o.status === 'active');
711
- * const expensive = active.filter(o => o.amount > 5000);
712
- * // WHERE status = 'active' AND amount > 5000
713
- * ```
714
- */
715
672
  filter(predicate: Predicate<T>): Query<T>;
716
- /**
717
- * Sort results by a field (ascending by default).
718
- * Use `.reverse()` after `.sortBy()` for descending order.
719
- *
720
- * @example
721
- * ```ts
722
- * const newest = Orders.sortBy(o => o.createdAt).reverse();
723
- * ```
724
- */
725
673
  sortBy(accessor: Accessor<T>): Query<T>;
726
- /**
727
- * Reverse the current sort order. If no sort is set, this has no effect.
728
- */
729
674
  reverse(): Query<T>;
730
- /**
731
- * Limit the number of results returned.
732
- *
733
- * @example
734
- * ```ts
735
- * const top10 = Orders.sortBy(o => o.amount).reverse().take(10);
736
- * ```
737
- */
738
675
  take(n: number): Query<T>;
739
- /**
740
- * Skip the first n results. Use with `.take()` for pagination.
741
- *
742
- * @example
743
- * ```ts
744
- * const page2 = Orders.sortBy(o => o.createdAt).skip(50).take(50);
745
- * ```
746
- */
747
676
  skip(n: number): Query<T>;
748
- /**
749
- * Return the first matching row, or null if no rows match.
750
- * Applies the current sort order before taking the first result.
751
- */
752
677
  first(): Promise<T | null>;
753
- /**
754
- * Return the last matching row (per current sort), or null.
755
- * Flips the sort direction and takes 1 row.
756
- */
757
678
  last(): Promise<T | null>;
758
- /**
759
- * Count matching rows. Returns a number, not the rows themselves.
760
- * Executes as `SELECT COUNT(*)` when predicates compile to SQL.
761
- */
762
679
  count(): Promise<number>;
763
- /**
764
- * Check if any row matches the current filters. Short-circuits —
765
- * doesn't load all rows when using SQL.
766
- */
767
680
  some(): Promise<boolean>;
768
- /**
769
- * Check if all rows match the current filters. Short-circuits on false.
770
- *
771
- * Implemented as NOT EXISTS(... WHERE NOT predicate) — returns true
772
- * if no rows fail the predicate.
773
- */
774
681
  every(): Promise<boolean>;
775
- /**
776
- * Return the row with the minimum value for the given field.
777
- * Executes as `ORDER BY field ASC LIMIT 1` in SQL.
778
- */
779
682
  min(accessor: Accessor<T, number>): Promise<T | null>;
780
- /**
781
- * Return the row with the maximum value for the given field.
782
- * Executes as `ORDER BY field DESC LIMIT 1` in SQL.
783
- */
784
683
  max(accessor: Accessor<T, number>): Promise<T | null>;
785
- /**
786
- * Group rows by a field value. Returns a Map.
787
- * Always executes in JS (no SQL equivalent for grouping into a Map).
788
- */
789
684
  groupBy<K extends string | number>(accessor: Accessor<T, K>): Promise<Map<K, T[]>>;
790
- /**
791
- * PromiseLike.then() — executes the query and pipes the result.
792
- * This is what makes `const rows = await query` work.
793
- */
794
685
  then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
795
- /**
796
- * Execute the query and return typed result rows.
797
- *
798
- * This is the core execution method. It:
799
- * 1. Tries to compile all predicates to SQL
800
- * 2. If all compile → builds and executes a single SQL query
801
- * 3. If any fail → fetches all rows and processes in JS
802
- * 4. Deserializes rows (user prefix stripping, JSON parsing)
803
- */
804
686
  private _execute;
805
- /**
806
- * Compile all accumulated predicates and determine the execution strategy.
807
- *
808
- * Returns an object with:
809
- * - `allSql`: whether all predicates compiled to SQL
810
- * - `sqlWhere`: combined WHERE clause (ANDed) if all compiled
811
- * - `compiled`: individual compilation results
812
- */
813
687
  private _compilePredicates;
814
- /**
815
- * Fetch all rows from the table and apply JS predicates.
816
- * This is the fallback path when SQL compilation fails.
817
- *
818
- * Logs a warning to stderr so developers know they're on the slow path.
819
- */
820
688
  private _fetchAndFilterInJs;
821
- /**
822
- * Fetch all rows from the table (SELECT * with no WHERE).
823
- * Used by the JS fallback path.
824
- */
825
689
  private _fetchAllRows;
826
690
  }
827
691
 
828
692
  /**
829
693
  * Table<T> — a typed persistent collection backed by SQLite.
830
694
  *
831
- * Created via `db.defineTable<T>(name)`. The returned object is the full
832
- * API for interacting with that table. Every method either returns a
695
+ * Created via `db.defineTable<T>(name)`. Every method either returns a
833
696
  * chainable Query<T> (for lazy reads) or a Promise (for terminal reads
834
697
  * and writes).
835
698
  *
836
- * ## Read vs Write operations
837
- *
838
- * **Reads** come in two flavors:
839
- * - **Direct reads**: `get(id)`, `findOne()`, `count()`, `some()`, `every()`,
840
- * `isEmpty()`, `min()`, `max()`, `groupBy()` — return Promises directly.
841
- * - **Chainable reads**: `filter()`, `sortBy()` — return a Query<T> that
842
- * accumulates operations lazily. The query executes when awaited.
699
+ * ## Write operations use RETURNING
843
700
  *
844
- * **Writes** are always immediate (return Promises):
845
- * - `push()` INSERT + SELECT to return the created row with system fields
846
- * - `update()` UPDATE + SELECT to return the updated row
847
- * - `remove()` — DELETE by ID
848
- * - `removeAll()` — DELETE with compiled WHERE clause
849
- * - `clear()` — DELETE all rows
850
- *
851
- * ## System columns
852
- *
853
- * Every row has platform-managed columns: `id`, `createdAt`, `updatedAt`,
854
- * `lastUpdatedBy`. These are:
855
- * - Included in read results (populated by the platform)
856
- * - Excluded from `push()` and `update()` inputs (TypeScript enforces this
857
- * via PushInput<T> and UpdateInput<T>; runtime also strips them)
858
- *
859
- * ## User type handling
860
- *
861
- * Columns with schema type `'user'` store values with a `@@user@@` prefix
862
- * in SQLite. This is handled transparently:
863
- * - On read: prefix is stripped, application code gets clean UUID strings
864
- * - On write: prefix is added before sending to the database
701
+ * INSERT and UPDATE use `RETURNING *` to get the created/updated row
702
+ * back in a single round trip no separate SELECT needed. This is
703
+ * executed via the batch endpoint which runs all queries on a single
704
+ * SQLite connection.
865
705
  */
866
706
 
867
707
  declare class Table<T> {
868
- /** @internal Runtime config binding this table to the execution layer. */
708
+ /** @internal */
869
709
  private readonly _config;
870
710
  constructor(config: TableConfig);
871
- /**
872
- * Get a single row by ID. Returns null if not found.
873
- *
874
- * @example
875
- * ```ts
876
- * const order = await Orders.get('abc-123');
877
- * if (order) console.log(order.status);
878
- * ```
879
- */
880
711
  get(id: string): Promise<T | null>;
881
- /**
882
- * Find the first row matching a predicate. Returns null if none match.
883
- *
884
- * @example
885
- * ```ts
886
- * const activeOrder = await Orders.findOne(o => o.status === 'active');
887
- * ```
888
- */
889
712
  findOne(predicate: Predicate<T>): Promise<T | null>;
890
- /**
891
- * Count rows, optionally filtered by a predicate.
892
- *
893
- * @example
894
- * ```ts
895
- * const total = await Orders.count();
896
- * const pending = await Orders.count(o => o.status === 'pending');
897
- * ```
898
- */
899
713
  count(predicate?: Predicate<T>): Promise<number>;
900
- /**
901
- * Check if any row matches a predicate. Short-circuits.
902
- *
903
- * @example
904
- * ```ts
905
- * const hasActive = await Orders.some(o => o.status === 'active');
906
- * ```
907
- */
908
714
  some(predicate: Predicate<T>): Promise<boolean>;
909
- /**
910
- * Check if all rows match a predicate.
911
- *
912
- * @example
913
- * ```ts
914
- * const allComplete = await Orders.every(o => o.status === 'completed');
915
- * ```
916
- */
917
715
  every(predicate: Predicate<T>): Promise<boolean>;
918
- /**
919
- * Check if the table has zero rows.
920
- *
921
- * @example
922
- * ```ts
923
- * if (await Orders.isEmpty()) console.log('No orders yet');
924
- * ```
925
- */
926
716
  isEmpty(): Promise<boolean>;
927
- /**
928
- * Return the row with the minimum value for a field.
929
- * Executes as `ORDER BY field ASC LIMIT 1`.
930
- *
931
- * @example
932
- * ```ts
933
- * const cheapest = await Orders.min(o => o.amount);
934
- * ```
935
- */
936
717
  min(accessor: Accessor<T, number>): Promise<T | null>;
937
- /**
938
- * Return the row with the maximum value for a field.
939
- * Executes as `ORDER BY field DESC LIMIT 1`.
940
- *
941
- * @example
942
- * ```ts
943
- * const mostExpensive = await Orders.max(o => o.amount);
944
- * ```
945
- */
946
718
  max(accessor: Accessor<T, number>): Promise<T | null>;
947
- /**
948
- * Group all rows by a field value. Returns a Map.
949
- *
950
- * @example
951
- * ```ts
952
- * const byStatus = await Orders.groupBy(o => o.status);
953
- * // Map { 'pending' => [...], 'approved' => [...] }
954
- * ```
955
- */
956
719
  groupBy<K extends string | number>(accessor: Accessor<T, K>): Promise<Map<K, T[]>>;
957
- /**
958
- * Filter rows by a predicate. Returns a chainable Query.
959
- *
960
- * The predicate is compiled to SQL when possible. If compilation fails,
961
- * the query falls back to fetching all rows and filtering in JS.
962
- *
963
- * @example
964
- * ```ts
965
- * const active = await Orders.filter(o => o.status === 'active');
966
- * const recentActive = await Orders
967
- * .filter(o => o.status === 'active')
968
- * .sortBy(o => o.createdAt)
969
- * .reverse()
970
- * .take(10);
971
- * ```
972
- */
973
720
  filter(predicate: Predicate<T>): Query<T>;
974
- /**
975
- * Sort all rows by a field. Returns a chainable Query.
976
- *
977
- * @example
978
- * ```ts
979
- * const newest = await Orders.sortBy(o => o.createdAt).reverse().take(5);
980
- * ```
981
- */
982
721
  sortBy(accessor: Accessor<T>): Query<T>;
983
722
  /**
984
723
  * Insert one or more rows. Returns the created row(s) with system fields
985
724
  * populated (id, createdAt, updatedAt, lastUpdatedBy).
986
725
  *
987
- * System columns are stripped from the input automatically you don't
988
- * need to (and can't) set id, createdAt, etc.
989
- *
990
- * @example
991
- * ```ts
992
- * // Single row
993
- * const order = await Orders.push({ item: 'Laptop', amount: 999, status: 'pending' });
994
- * console.log(order.id, order.createdAt); // system fields populated
995
- *
996
- * // Multiple rows
997
- * const orders = await Orders.push([
998
- * { item: 'Monitor', amount: 300, status: 'pending' },
999
- * { item: 'Keyboard', amount: 50, status: 'pending' },
1000
- * ]);
1001
- * ```
726
+ * Uses `INSERT ... RETURNING *` so the created row comes back in a
727
+ * single round trip no separate SELECT needed.
1002
728
  */
1003
729
  push(data: PushInput<T>): Promise<T>;
1004
730
  push(data: PushInput<T>[]): Promise<T[]>;
1005
731
  /**
1006
732
  * Update a row by ID. Only the provided fields are changed.
1007
- * Returns the updated row.
1008
- *
1009
- * System columns cannot be updated — they're stripped automatically.
1010
- * `updatedAt` and `lastUpdatedBy` are set by the platform.
1011
- *
1012
- * @example
1013
- * ```ts
1014
- * const updated = await Orders.update(order.id, { status: 'approved' });
1015
- * console.log(updated.updatedAt); // freshly updated
1016
- * ```
733
+ * Returns the updated row via `UPDATE ... RETURNING *`.
1017
734
  */
1018
735
  update(id: string, data: UpdateInput<T>): Promise<T>;
1019
- /**
1020
- * Remove a row by ID.
1021
- *
1022
- * @example
1023
- * ```ts
1024
- * await Orders.remove('abc-123');
1025
- * ```
1026
- */
1027
736
  remove(id: string): Promise<void>;
1028
737
  /**
1029
738
  * Remove all rows matching a predicate. Returns the count removed.
1030
- *
1031
- * The predicate is compiled to SQL when possible. If compilation fails,
1032
- * the function fetches all matching rows, collects their IDs, and
1033
- * deletes them individually.
1034
- *
1035
- * @example
1036
- * ```ts
1037
- * const removed = await Orders.removeAll(o => o.status === 'rejected');
1038
- * console.log(`Removed ${removed} orders`);
1039
- * ```
1040
739
  */
1041
740
  removeAll(predicate: Predicate<T>): Promise<number>;
1042
- /**
1043
- * Remove all rows from the table.
1044
- *
1045
- * @example
1046
- * ```ts
1047
- * await Orders.clear();
1048
- * ```
1049
- */
1050
741
  clear(): Promise<void>;
1051
742
  }
1052
743
 
@@ -1408,18 +1099,19 @@ declare class MindStudioAgent$1 {
1408
1099
  */
1409
1100
  private _trySandboxHydration;
1410
1101
  /**
1411
- * @internal Execute a SQL query against a managed database.
1412
- * Used as the `executeQuery` callback for Table instances.
1102
+ * @internal Execute a batch of SQL queries against a managed database.
1103
+ * Used as the `executeBatch` callback for Table/Query instances.
1413
1104
  *
1414
- * Calls the `queryAppDatabase` step with `parameterize: false`
1415
- * (the SDK builds fully-formed SQL with escaped inline values).
1105
+ * Calls `POST /_internal/v2/db/query` directly with the hook token
1106
+ * (raw, no Bearer prefix). All queries run on a single SQLite connection,
1107
+ * enabling RETURNING clauses and multi-statement batches.
1416
1108
  */
1417
- private _executeDbQuery;
1109
+ private _executeDbBatch;
1418
1110
  /**
1419
1111
  * @internal Create a lazy Db proxy that auto-hydrates context.
1420
1112
  *
1421
1113
  * defineTable() returns Table instances immediately (no async needed).
1422
- * But the Table's executeQuery callback is wrapped to call ensureContext()
1114
+ * But the Table's executeBatch callback is wrapped to call ensureContext()
1423
1115
  * before the first query, so context is fetched lazily.
1424
1116
  */
1425
1117
  private _createLazyDb;