agentlang 0.10.5 → 0.10.7

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.
Files changed (78) hide show
  1. package/out/cli/main.d.ts.map +1 -1
  2. package/out/cli/main.js +13 -3
  3. package/out/cli/main.js.map +1 -1
  4. package/out/language/error-reporter.d.ts.map +1 -1
  5. package/out/language/error-reporter.js +1 -0
  6. package/out/language/error-reporter.js.map +1 -1
  7. package/out/language/generated/ast.d.ts +1 -0
  8. package/out/language/generated/ast.d.ts.map +1 -1
  9. package/out/language/generated/ast.js +1 -0
  10. package/out/language/generated/ast.js.map +1 -1
  11. package/out/language/generated/grammar.d.ts.map +1 -1
  12. package/out/language/generated/grammar.js +20 -21
  13. package/out/language/generated/grammar.js.map +1 -1
  14. package/out/language/main.cjs +20 -21
  15. package/out/language/main.cjs.map +2 -2
  16. package/out/runtime/agents/common.d.ts +2 -2
  17. package/out/runtime/agents/common.d.ts.map +1 -1
  18. package/out/runtime/agents/common.js +4 -0
  19. package/out/runtime/agents/common.js.map +1 -1
  20. package/out/runtime/auth/cognito.d.ts +1 -0
  21. package/out/runtime/auth/cognito.d.ts.map +1 -1
  22. package/out/runtime/auth/cognito.js +31 -0
  23. package/out/runtime/auth/cognito.js.map +1 -1
  24. package/out/runtime/auth/interface.d.ts +2 -0
  25. package/out/runtime/auth/interface.d.ts.map +1 -1
  26. package/out/runtime/defs.d.ts +2 -0
  27. package/out/runtime/defs.d.ts.map +1 -1
  28. package/out/runtime/defs.js +9 -2
  29. package/out/runtime/defs.js.map +1 -1
  30. package/out/runtime/interpreter.d.ts.map +1 -1
  31. package/out/runtime/interpreter.js +50 -13
  32. package/out/runtime/interpreter.js.map +1 -1
  33. package/out/runtime/module.d.ts.map +1 -1
  34. package/out/runtime/module.js +33 -10
  35. package/out/runtime/module.js.map +1 -1
  36. package/out/runtime/modules/ai.d.ts +1 -1
  37. package/out/runtime/modules/ai.d.ts.map +1 -1
  38. package/out/runtime/modules/ai.js +1 -2
  39. package/out/runtime/modules/ai.js.map +1 -1
  40. package/out/runtime/modules/auth.d.ts.map +1 -1
  41. package/out/runtime/modules/auth.js +6 -1
  42. package/out/runtime/modules/auth.js.map +1 -1
  43. package/out/runtime/modules/core.d.ts.map +1 -1
  44. package/out/runtime/modules/core.js +1 -1
  45. package/out/runtime/modules/core.js.map +1 -1
  46. package/out/runtime/resolvers/sqldb/database.d.ts +10 -0
  47. package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
  48. package/out/runtime/resolvers/sqldb/database.js +104 -5
  49. package/out/runtime/resolvers/sqldb/database.js.map +1 -1
  50. package/out/runtime/resolvers/sqldb/dbutil.d.ts +6 -1
  51. package/out/runtime/resolvers/sqldb/dbutil.d.ts.map +1 -1
  52. package/out/runtime/resolvers/sqldb/dbutil.js +18 -13
  53. package/out/runtime/resolvers/sqldb/dbutil.js.map +1 -1
  54. package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
  55. package/out/runtime/resolvers/sqldb/impl.js +20 -4
  56. package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
  57. package/out/syntaxes/agentlang.monarch.d.ts.map +1 -1
  58. package/out/syntaxes/agentlang.monarch.js +1 -0
  59. package/out/syntaxes/agentlang.monarch.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/cli/main.ts +15 -2
  62. package/src/language/agentlang.langium +5 -1
  63. package/src/language/error-reporter.ts +1 -0
  64. package/src/language/generated/ast.ts +1 -0
  65. package/src/language/generated/grammar.ts +20 -21
  66. package/src/runtime/agents/common.ts +4 -0
  67. package/src/runtime/auth/cognito.ts +37 -0
  68. package/src/runtime/auth/interface.ts +2 -0
  69. package/src/runtime/defs.ts +9 -0
  70. package/src/runtime/interpreter.ts +43 -11
  71. package/src/runtime/module.ts +34 -9
  72. package/src/runtime/modules/ai.ts +2 -2
  73. package/src/runtime/modules/auth.ts +6 -1
  74. package/src/runtime/modules/core.ts +6 -2
  75. package/src/runtime/resolvers/sqldb/database.ts +122 -3
  76. package/src/runtime/resolvers/sqldb/dbutil.ts +18 -5
  77. package/src/runtime/resolvers/sqldb/impl.ts +27 -2
  78. package/src/syntaxes/agentlang.monarch.ts +1 -0
@@ -702,10 +702,14 @@ export async function saveMigration(
702
702
  .join(SqlSep)
703
703
  );
704
704
  }
705
- const inst: Instance = await parseAndEvaluateStatement(`{agentlang/Migration {
705
+ const inst: Instance = await parseAndEvaluateStatement(
706
+ `{agentlang/Migration {
706
707
  appVersion "${version}",
707
708
  ups "${ups_str}",
708
- downs "${downs_str}"}}`);
709
+ downs "${downs_str}"}}`,
710
+ undefined,
711
+ env
712
+ );
709
713
  if (isInstanceOfType(inst, 'agentlang/Migration') && inst.lookup('appVersion') === version) {
710
714
  await env.commitAllTransactions();
711
715
  return true;
@@ -10,6 +10,7 @@ import {
10
10
  import { logger } from '../../logger.js';
11
11
  import {
12
12
  asTableReference,
13
+ ColumnRef,
13
14
  DefaultVectorDimension,
14
15
  modulesAsOrmSchema,
15
16
  OwnersSuffix,
@@ -32,10 +33,12 @@ import {
32
33
  DeletedFlagAttributeName,
33
34
  ForceReadPermFlag,
34
35
  getUserTenantId,
36
+ isRuntimeMode_apply_migration,
35
37
  isRuntimeMode_dev,
36
38
  isRuntimeMode_generate_migration,
37
39
  isRuntimeMode_init_schema,
38
40
  isRuntimeMode_migration,
41
+ isRuntimeMode_prod,
39
42
  isRuntimeMode_test,
40
43
  isRuntimeMode_undo_migration,
41
44
  PathAttributeName,
@@ -332,14 +335,80 @@ async function execMigrationSql(dataSource: DataSource, sql: string[]) {
332
335
  await queryRunner.commitTransaction();
333
336
  }
334
337
 
338
+ export async function getSchemaDiff(dataSource: DataSource): Promise<string[]> {
339
+ const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
340
+ return sqlInMemory.upQueries.map((q: any) => q.query);
341
+ }
342
+
343
+ export type SimulateMigrationResult = {
344
+ success: boolean;
345
+ queries: string[];
346
+ errors: string[];
347
+ };
348
+
349
+ async function simulateMigrationPostgres(
350
+ dataSource: DataSource,
351
+ queries: string[]
352
+ ): Promise<SimulateMigrationResult> {
353
+ const queryRunner = dataSource.createQueryRunner();
354
+ const errors: string[] = [];
355
+ try {
356
+ await queryRunner.startTransaction();
357
+ for (const q of queries) {
358
+ await queryRunner.query(q);
359
+ }
360
+ } catch (err: any) {
361
+ errors.push(`Migration SQL failed: ${err.message}`);
362
+ } finally {
363
+ // Always rollback — this is a simulation
364
+ try {
365
+ await queryRunner.rollbackTransaction();
366
+ } catch {
367
+ // already rolled back or connection lost
368
+ }
369
+ await queryRunner.release();
370
+ }
371
+ return { success: errors.length === 0, queries, errors };
372
+ }
373
+
374
+ function simulateMigrationDryRun(queries: string[]): SimulateMigrationResult {
375
+ return { success: true, queries, errors: [] };
376
+ }
377
+
378
+ export async function simulateMigration(dataSource: DataSource): Promise<SimulateMigrationResult> {
379
+ const queries = await getSchemaDiff(dataSource);
380
+ if (queries.length === 0) {
381
+ return { success: true, queries: [], errors: [] };
382
+ }
383
+ if (dataSource.options.type === 'postgres') {
384
+ return simulateMigrationPostgres(dataSource, queries);
385
+ }
386
+ return simulateMigrationDryRun(queries);
387
+ }
388
+
389
+ async function validateSchemaInProd(dataSource: DataSource) {
390
+ if (!isRuntimeMode_prod()) return;
391
+ const pendingQueries = (await getSchemaDiff(dataSource)).filter(
392
+ q => !q.match(/DROP CONSTRAINT\s+"FK_/i)
393
+ );
394
+ if (pendingQueries.length > 0) {
395
+ const pending = pendingQueries.join('\n ');
396
+ throw new Error(
397
+ `Schema mismatch detected: the app model does not match the database schema. ` +
398
+ `Run migrations before starting in production mode.\n Pending changes:\n ${pending}`
399
+ );
400
+ }
401
+ }
402
+
335
403
  async function maybeHandleMigrations(dataSource: DataSource) {
336
404
  const is_migration = isRuntimeMode_migration();
405
+ const is_apply_migration = isRuntimeMode_apply_migration();
337
406
  const is_undo_migration = isRuntimeMode_undo_migration();
338
407
  const is_gen_migration = isRuntimeMode_generate_migration();
339
- if (is_migration || is_undo_migration || is_gen_migration) {
408
+ if (is_migration || is_apply_migration || is_undo_migration || is_gen_migration) {
340
409
  const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
341
410
  let ups: string[] | undefined;
342
- if (is_migration || is_gen_migration) {
411
+ if (is_migration || is_apply_migration || is_gen_migration) {
343
412
  ups = new Array<string>();
344
413
  sqlInMemory.upQueries.forEach(upQuery => {
345
414
  ups?.push(upQuery.query.replaceAll('`', '\\`'));
@@ -353,8 +422,37 @@ async function maybeHandleMigrations(dataSource: DataSource) {
353
422
  });
354
423
  }
355
424
  if (is_migration && ups?.length) {
425
+ // Review-only: display pending changes and simulation result, but do not apply
426
+ logger.info('Pending migration queries:');
427
+ ups.forEach((q, i) => {
428
+ logger.info(` [${i + 1}] ${q}`);
429
+ console.log(q + ';');
430
+ });
431
+ const simulation = await simulateMigration(dataSource);
432
+ if (!simulation.success) {
433
+ logger.error(`Migration simulation failed:\n ${simulation.errors.join('\n ')}`);
434
+ throw new Error(
435
+ `Migration aborted: simulation failed.\n ${simulation.errors.join('\n ')}`
436
+ );
437
+ }
438
+ logger.info('Migration simulation passed.');
439
+ logger.info('Run `applyMigration` to apply these changes.');
440
+ } else if (is_migration && (!ups || ups.length === 0)) {
441
+ logger.info('No pending migration changes detected.');
442
+ } else if (is_apply_migration && ups?.length) {
443
+ const simulation = await simulateMigration(dataSource);
444
+ if (!simulation.success) {
445
+ logger.error(`Migration simulation failed:\n ${simulation.errors.join('\n ')}`);
446
+ throw new Error(
447
+ `Migration aborted: simulation failed.\n ${simulation.errors.join('\n ')}`
448
+ );
449
+ }
450
+ logger.info('Migration simulation passed, applying changes...');
356
451
  await saveMigration(getAppSpec().version, ups, downs);
357
452
  await execMigrationSql(dataSource, ups);
453
+ logger.info('Migration applied successfully.');
454
+ } else if (is_apply_migration && (!ups || ups.length === 0)) {
455
+ logger.info('No pending migration changes to apply.');
358
456
  } else if (is_undo_migration && downs?.length) {
359
457
  await saveMigration(getAppSpec().version, ups, downs);
360
458
  await execMigrationSql(dataSource, downs);
@@ -444,6 +542,15 @@ export function isUsingSqljs(): boolean {
444
542
  return getDbType(AppConfig?.store) == 'sqljs';
445
543
  }
446
544
 
545
+ /** Resolved store DB type (uses the same cache as initDatabase). */
546
+ export function getStoreDbType(config?: DatabaseConfig | undefined): string {
547
+ return getDbType(config);
548
+ }
549
+
550
+ export function isPostgresStore(): boolean {
551
+ return getDbType(AppConfig?.store) === 'postgres';
552
+ }
553
+
447
554
  export async function isVectorStoreSupported(): Promise<boolean> {
448
555
  const dbType = getDbType(AppConfig?.store);
449
556
  if (dbType === 'postgres') return true;
@@ -458,7 +565,7 @@ export async function initDatabase(config: DatabaseConfig | undefined) {
458
565
  if (defaultDataSource === undefined) {
459
566
  const mkds = getDsFunction(config);
460
567
  if (mkds) {
461
- const ormScm = modulesAsOrmSchema();
568
+ const ormScm = modulesAsOrmSchema(getStoreDbType(config));
462
569
  defaultDataSource = mkds(ormScm.entities, config) as DataSource;
463
570
  await defaultDataSource.initialize();
464
571
  await maybeHandleMigrations(defaultDataSource);
@@ -480,6 +587,7 @@ export async function initDatabase(config: DatabaseConfig | undefined) {
480
587
  }
481
588
  }
482
589
  }
590
+ await validateSchemaInProd(defaultDataSource);
483
591
  const vectEnts = ormScm.vectorEntities.map((es: EntitySchema) => {
484
592
  return es.options.name;
485
593
  });
@@ -991,6 +1099,12 @@ function objectToWhereClause(queryObj: object, queryVals: any, tableName?: strin
991
1099
  Object.entries(queryObj).forEach((value: [string, any]) => {
992
1100
  let op: string = value[1] as string;
993
1101
  const k = value[0];
1102
+ if (queryVals[k] instanceof ColumnRef) {
1103
+ const colRef = (queryVals[k] as ColumnRef).toSql(tableName);
1104
+ clauses.push(tableName ? `"${tableName}"."${k}" ${op} ${colRef}` : `"${k}" ${op} ${colRef}`);
1105
+ delete queryVals[k];
1106
+ return;
1107
+ }
994
1108
  const isnullcheck = queryVals[k] === null;
995
1109
  if (isnullcheck) {
996
1110
  if (op === '=') {
@@ -1018,6 +1132,11 @@ function objectToRawWhereClause(queryObj: object, queryVals: any, tableName?: st
1018
1132
  Object.entries(queryObj).forEach((value: [string, any]) => {
1019
1133
  let op: string = value[1] as string;
1020
1134
  const k: string = value[0];
1135
+ if (queryVals[k] instanceof ColumnRef) {
1136
+ const colRef = (queryVals[k] as ColumnRef).toSql(tableName);
1137
+ clauses.push(tableName ? `"${tableName}"."${k}" ${op} ${colRef}` : `"${k}" ${op} ${colRef}`);
1138
+ return;
1139
+ }
1021
1140
  if (queryVals[k] === null) {
1022
1141
  if (op === '=') {
1023
1142
  op = 'IS';
@@ -72,6 +72,15 @@ export function asColumnReference(n: string, tableName: string, entityName: stri
72
72
  }
73
73
  }
74
74
 
75
+ export class ColumnRef {
76
+ constructor(public readonly ref: string) {}
77
+ toSql(tableName?: string): string {
78
+ const dotIdx = this.ref.lastIndexOf('.');
79
+ const field = dotIdx >= 0 ? this.ref.substring(dotIdx + 1) : this.ref;
80
+ return tableName ? `"${tableName}"."${field}"` : `"${field}"`;
81
+ }
82
+ }
83
+
75
84
  export function modulesAsDbSchema(): TableSchema[] {
76
85
  const result: TableSchema[] = new Array<TableSchema>();
77
86
  getModuleNames().forEach((n: string) => {
@@ -105,7 +114,7 @@ function isSqlDefault(d: any): boolean {
105
114
  return typeof d !== 'string' || !d.endsWith('()') || SqlDefaultFunctions.has(d);
106
115
  }
107
116
 
108
- export function modulesAsOrmSchema(): OrmSchema {
117
+ export function modulesAsOrmSchema(storeDbType: string): OrmSchema {
109
118
  const ents: EntitySchema[] = [];
110
119
  const vects: EntitySchema[] = [];
111
120
  const fkSpecs: FkSpec[] = [];
@@ -115,12 +124,12 @@ export function modulesAsOrmSchema(): OrmSchema {
115
124
  const entities: Record[] = mod.getEntityEntries();
116
125
  const rels: Record[] = mod.getBetweenRelationshipEntriesThatNeedStore();
117
126
  entities.concat(rels).forEach((entry: Record) => {
118
- ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, entry)));
127
+ ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, entry, storeDbType)));
119
128
  const ownerEntry = createOwnersEntity(entry);
120
- ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, ownerEntry, true)));
129
+ ents.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, ownerEntry, storeDbType, true)));
121
130
  if (entry.getFullTextSearchAttributes()) {
122
131
  const vectorEntry = createVectorEntity(entry);
123
- vects.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, vectorEntry, true)));
132
+ vects.push(new EntitySchema<any>(ormSchemaFromRecordSchema(n, vectorEntry, storeDbType, true)));
124
133
  }
125
134
  });
126
135
  entities.forEach((r: Record) => {
@@ -136,6 +145,7 @@ export function modulesAsOrmSchema(): OrmSchema {
136
145
  function ormSchemaFromRecordSchema(
137
146
  moduleName: string,
138
147
  entry: Record,
148
+ storeDbType: string,
139
149
  hasOwnPk?: boolean
140
150
  ): EntitySchemaOptions<any> {
141
151
  const entityName = entry.name;
@@ -159,13 +169,16 @@ function ormSchemaFromRecordSchema(
159
169
  else if (autoUuid) genStrat = 'uuid';
160
170
  const isuq: boolean = isUniqueAttribute(attrSpec);
161
171
  const ispk: boolean = chkforpk && isIdAttribute(attrSpec);
172
+ const isArr: boolean = isArrayAttribute(attrSpec);
173
+ const usePgArray: boolean = isArr && storeDbType === 'postgres';
162
174
  const colDef: EntitySchemaColumnOptions = {
163
- type: asSqlType(attrSpec.type),
175
+ type: usePgArray ? asSqlType(attrSpec.type) : isArr ? 'varchar' : asSqlType(attrSpec.type),
164
176
  generated: genStrat,
165
177
  default: d,
166
178
  unique: isuq,
167
179
  primary: ispk,
168
180
  nullable: isOptionalAttribute(attrSpec),
181
+ ...(usePgArray ? { array: true } : {}),
169
182
  };
170
183
  if (ispk) {
171
184
  needPath = false;
@@ -9,8 +9,10 @@ import {
9
9
  getEntityRbacRules,
10
10
  Instance,
11
11
  InstanceAttributes,
12
+ isArrayAttribute,
12
13
  isBetweenRelationship,
13
14
  newInstanceAttributes,
15
+ type RecordSchema,
14
16
  Relationship,
15
17
  } from '../../module.js';
16
18
  import {
@@ -35,6 +37,7 @@ import {
35
37
  hardDeleteRow,
36
38
  insertBetweenRow,
37
39
  insertRow,
40
+ isPostgresStore,
38
41
  isVectorStoreSupported,
39
42
  JoinClause,
40
43
  JoinOn,
@@ -47,6 +50,22 @@ import {
47
50
  vectorStoreSearchEntryExists,
48
51
  } from './database.js';
49
52
  import { AggregateFunctionCall, Environment } from '../../interpreter.js';
53
+
54
+ /** Postgres uses native array columns; other drivers store JSON text in varchar. */
55
+ function rowObjectForSqlPersistence(row: object, schema: RecordSchema): object {
56
+ if (isPostgresStore()) {
57
+ return row;
58
+ }
59
+ const o = { ...(row as Record<string, any>) };
60
+ schema.forEach((spec, name) => {
61
+ if (!isArrayAttribute(spec)) return;
62
+ const v = o[name];
63
+ if (Array.isArray(v)) {
64
+ o[name] = JSON.stringify(v);
65
+ }
66
+ });
67
+ return o;
68
+ }
50
69
  import {
51
70
  DeletedFlagAttributeName,
52
71
  ParentAttributeName,
@@ -230,7 +249,10 @@ export class SqlDbResolver extends Resolver {
230
249
  attrs.set(PathAttributeName, p);
231
250
  }
232
251
  const n: string = asTableReference(inst.moduleName, inst.name);
233
- const rowObj: object = inst.attributesWithStringifiedObjects();
252
+ const rowObj: object = rowObjectForSqlPersistence(
253
+ inst.attributesWithStringifiedObjects(),
254
+ inst.record.schema
255
+ );
234
256
  await insertRow(n, rowObj, ctx, orUpdate);
235
257
  if (inst.record.getEmbeddingConfig() || inst.record.getFullTextSearchAttributes()) {
236
258
  const path = attrs.get(PathAttributeName);
@@ -292,7 +314,10 @@ export class SqlDbResolver extends Resolver {
292
314
  const queryVals: object = Object.fromEntries(
293
315
  new Map<string, any>().set(PathAttributeName, inst.attributes.get(PathAttributeName))
294
316
  );
295
- const updateObj: object = Instance.stringifyObjects(newAttrs);
317
+ const updateObj: object = rowObjectForSqlPersistence(
318
+ Instance.stringifyObjects(newAttrs),
319
+ inst.record.schema
320
+ );
296
321
  await updateRow(
297
322
  asTableReference(inst.moduleName, inst.name),
298
323
  queryObj,
@@ -14,6 +14,7 @@ export default {
14
14
  { regex: /[_a-zA-Z][\w_]*/, action: { cases: { '@keywords': {"token":"keyword"}, '@default': {"token":"string"} }} },
15
15
  { regex: /"(\\(?:[\s\S])|(?:(?!(\\|"|\r|\n))[\s\S]*?)|\r?\n)*"/, action: {"token":"string"} },
16
16
  { regex: /`(\\(?:[\s\S])|(?:(?!(\\|`|\r|\n))[\s\S]*?)|\r?\n)*`/, action: {"token":"string"} },
17
+ { regex: /-?(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)/, action: {"token":"number"} },
17
18
  { regex: /-?[0-9]+/, action: {"token":"number"} },
18
19
  { include: '@whitespace' },
19
20
  { regex: /@symbols/, action: { cases: { '@operators': {"token":"operator"}, '@default': {"token":""} }} },