@objectstack/runtime 15.0.0 → 15.1.1

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.cts CHANGED
@@ -683,16 +683,19 @@ interface DispatcherPluginConfig {
683
683
  */
684
684
  enforceProjectMembership?: boolean;
685
685
  /**
686
- * Reject anonymous requests to `auth: true` service routes (AI, etc.) with
687
- * HTTP 401, mirroring the REST API's `requireAuth` gate. Must match the
688
- * REST plugin's `api.requireAuth` so `/ai` and `/meta` stay in lockstep
689
- * with `/data` otherwise the AI routes' declared `auth: true` contract is
690
- * never enforced and anonymous callers reach adapter/model status routes.
686
+ * Reject anonymous requests to `auth: true` service routes (AI, etc.), the
687
+ * `/graphql` endpoint and the `/meta` catch-all with HTTP 401, mirroring the
688
+ * REST API's `requireAuth` gate. Must match the REST plugin's
689
+ * `api.requireAuth` so `/ai`, `/graphql` and `/meta` stay in lockstep with
690
+ * `/data` otherwise the AI routes' declared `auth: true` contract is never
691
+ * enforced and anonymous callers reach adapter/model status routes or read
692
+ * object data over GraphQL that `/data/*` 401s (#2567).
691
693
  *
692
- * Defaults to `false` (backward-compatible: previously nothing enforced
693
- * `RouteDefinition.auth` here). Hosts pass their `api.requireAuth` through —
694
- * the framework `serve` command and the cloud apps do so from the same
695
- * stack `api` config the REST plugin reads.
694
+ * Defaults to `true` — secure-by-default, matching the REST plugin's
695
+ * `api.requireAuth` default (ADR-0056 D2). Hosts pass their `api.requireAuth`
696
+ * through (the framework `serve` command and the cloud apps do so from the
697
+ * same stack `api` config the REST plugin reads); a deployment that serves
698
+ * these surfaces publicly sets `requireAuth: false` explicitly.
696
699
  */
697
700
  requireAuth?: boolean;
698
701
  /**
@@ -1128,6 +1131,19 @@ declare class HttpDispatcher {
1128
1131
  * route and the MCP `run_action` bridge enforce the SAME declaration.
1129
1132
  */
1130
1133
  private actionPermissionError;
1134
+ /**
1135
+ * [#2849 / ADR-0011] AI-exposure gate for the MCP action surface. Returns a
1136
+ * human-readable error string unless the action's author explicitly opted it
1137
+ * into the AI surface with `ai.exposed: true`, or `null` when exposed.
1138
+ *
1139
+ * This gate is the REAL agent-facing boundary for actions: script/body
1140
+ * handlers execute as TRUSTED application code (the engine facade carries no
1141
+ * ExecutionContext — see {@link buildActionEngineFacade}), so once invoked, a
1142
+ * body's reads/writes are NOT bounded by the caller's RLS/FLS or an agent's
1143
+ * data ceiling (ADR-0090 D10). The author's explicit opt-in — not a data-layer
1144
+ * backstop — therefore decides what AI may trigger. Fail-closed by default.
1145
+ */
1146
+ private actionAiExposureError;
1131
1147
  /**
1132
1148
  * Whether an action has a headless invocation path (so MCP can run it).
1133
1149
  * Mirrors the supported-type set of the (now cloud-side) action-tools
@@ -1144,15 +1160,34 @@ declare class HttpDispatcher {
1144
1160
  private jsonTypeOf;
1145
1161
  /** Resolve an action's params into LLM-facing summaries (field-backed types resolved). */
1146
1162
  private summarizeActionParams;
1147
- /** Slim engine facade matching the ActionContext.engine shape handlers expect. */
1163
+ /**
1164
+ * Slim engine facade matching the ActionContext.engine shape handlers expect.
1165
+ *
1166
+ * ⚠️ TRUSTED (SECURITY-DEFINER-like) BY DESIGN (#2849): these calls carry NO
1167
+ * ExecutionContext, so the data engine's security middleware skips RLS / FLS /
1168
+ * CRUD / tenant scoping entirely. Action bodies are the app author's own code
1169
+ * and legitimately perform cross-object writes the invoking user could not
1170
+ * (convert-lead, cascade-close). The boundary is therefore enforced at INVOKE
1171
+ * time (`ai.exposed` + ADR-0066 D4 capability gate), and every dispatch is
1172
+ * audit-logged. Longer-term direction: an action-level `runAs: 'user'|'system'`
1173
+ * mirroring flows (ADR-0049) — tracked in #2849.
1174
+ */
1148
1175
  private buildActionEngineFacade;
1149
1176
  /**
1150
1177
  * Resolve + invoke a business action by its declarative name for the MCP
1151
- * `run_action` tool. Enforces the ADR-0066 D4 capability gate and RLS as the
1152
- * caller, loads the subject record under RLS for row-context actions, and
1153
- * dispatches through the framework's `engine.executeAction` (script/body) or
1154
- * automation flow runner (flow). Throws on denial / not-found / handler
1155
- * failure so the tool surfaces a clean tool-error. No service-ai dependency.
1178
+ * `run_action` tool. Enforces the AI-exposure gate (`ai.exposed`, #2849), the
1179
+ * ADR-0066 D4 capability gate, loads the subject record under the caller's
1180
+ * RLS for row-context actions, and dispatches through the framework's
1181
+ * `engine.executeAction` (script/body) or automation flow runner (flow).
1182
+ * Throws on denial / not-found / handler failure so the tool surfaces a
1183
+ * clean tool-error. No service-ai dependency.
1184
+ *
1185
+ * SECURITY MODEL (#2849): all gating happens at INVOKE time. A script/body
1186
+ * handler then runs as trusted code — its engine facade performs
1187
+ * context-less reads/writes that bypass RLS/FLS (SECURITY-DEFINER-like), so
1188
+ * the caller's permissions and an agent's ADR-0090 D10 data ceiling do NOT
1189
+ * bound what the body does internally. Flow actions differ: the flow engine
1190
+ * receives the caller's identity below and honours `runAs` (ADR-0049).
1156
1191
  */
1157
1192
  private invokeBusinessAction;
1158
1193
  /**
@@ -1162,6 +1197,34 @@ declare class HttpDispatcher {
1162
1197
  * and no `objectName` was supplied (so `run_action` can ask for one).
1163
1198
  */
1164
1199
  private resolveActionByName;
1200
+ /**
1201
+ * The MCP surface's single declaration source: every action declaration the
1202
+ * bridge may list or invoke, as `{ action, objectName, obj }` rows.
1203
+ *
1204
+ * Two shapes feed it (#3010):
1205
+ * 1. `object.actions` — bundle/artifact objects and authored object rows.
1206
+ * 2. Standalone `action` metadata items — Studio-authored rows that the
1207
+ * engine executes since #2608 (`resyncAuthoredActions`) but that never
1208
+ * appear inside any object definition. Their owning object follows the
1209
+ * same convention as the engine registration key (`objectName` field,
1210
+ * legacy `object` field, else the `'global'` wildcard).
1211
+ *
1212
+ * On a key clash (`objectName:name`) the object-embedded declaration wins,
1213
+ * mirroring the execution layer's artifact-wins rule — `resyncAuthoredActions`
1214
+ * refuses to clobber an artifact-registered handler, so the embedded
1215
+ * declaration is the one that matches what actually runs. All MCP gating
1216
+ * (`ai.exposed`, ADR-0066 D4, headless-invokability) applies downstream of
1217
+ * this collection, unchanged.
1218
+ */
1219
+ private collectActionDeclarations;
1220
+ /**
1221
+ * Owning object of a standalone `action` item — must stay in lockstep with
1222
+ * the ObjectQL plugin's `actionObjectKey` (the engine registration key), so
1223
+ * the declaration the MCP surface resolves is the one whose handler
1224
+ * `executeAction` will find: spec `objectName`, bundle-collector `object`,
1225
+ * else the `'global'` wildcard.
1226
+ */
1227
+ private standaloneActionObjectName;
1165
1228
  /**
1166
1229
  * Generate a `sys_api_key` and return the raw secret EXACTLY ONCE
1167
1230
  * (`POST /keys`). This is the only mint path — the raw key is never stored
@@ -1255,7 +1318,7 @@ declare class HttpDispatcher {
1255
1318
  analytics: string | undefined;
1256
1319
  automation: string | undefined;
1257
1320
  workflow: string | undefined;
1258
- realtime: string | undefined;
1321
+ realtime: undefined;
1259
1322
  notifications: string | undefined;
1260
1323
  ai: string | undefined;
1261
1324
  i18n: string | undefined;
@@ -1272,7 +1335,7 @@ declare class HttpDispatcher {
1272
1335
  analytics: string | undefined;
1273
1336
  automation: string | undefined;
1274
1337
  workflow: string | undefined;
1275
- realtime: string | undefined;
1338
+ realtime: undefined;
1276
1339
  notifications: string | undefined;
1277
1340
  ai: string | undefined;
1278
1341
  i18n: string | undefined;
@@ -1299,18 +1362,34 @@ declare class HttpDispatcher {
1299
1362
  message: string;
1300
1363
  };
1301
1364
  data: {
1365
+ enabled: boolean;
1366
+ status: "degraded" | "stub";
1367
+ handlerReady: boolean;
1368
+ route: string | undefined;
1369
+ provider: string | undefined;
1370
+ message: string | undefined;
1371
+ } | {
1302
1372
  enabled: boolean;
1303
1373
  status: "available";
1304
1374
  handlerReady: boolean;
1305
1375
  route: string | undefined;
1306
1376
  provider: string | undefined;
1377
+ message?: undefined;
1307
1378
  };
1308
1379
  auth: {
1380
+ enabled: boolean;
1381
+ status: "degraded" | "stub";
1382
+ handlerReady: boolean;
1383
+ route: string | undefined;
1384
+ provider: string | undefined;
1385
+ message: string | undefined;
1386
+ } | {
1309
1387
  enabled: boolean;
1310
1388
  status: "available";
1311
1389
  handlerReady: boolean;
1312
1390
  route: string | undefined;
1313
1391
  provider: string | undefined;
1392
+ message?: undefined;
1314
1393
  } | {
1315
1394
  enabled: boolean;
1316
1395
  status: "unavailable";
@@ -1318,11 +1397,19 @@ declare class HttpDispatcher {
1318
1397
  message: string;
1319
1398
  };
1320
1399
  automation: {
1400
+ enabled: boolean;
1401
+ status: "degraded" | "stub";
1402
+ handlerReady: boolean;
1403
+ route: string | undefined;
1404
+ provider: string | undefined;
1405
+ message: string | undefined;
1406
+ } | {
1321
1407
  enabled: boolean;
1322
1408
  status: "available";
1323
1409
  handlerReady: boolean;
1324
1410
  route: string | undefined;
1325
1411
  provider: string | undefined;
1412
+ message?: undefined;
1326
1413
  } | {
1327
1414
  enabled: boolean;
1328
1415
  status: "unavailable";
@@ -1330,11 +1417,19 @@ declare class HttpDispatcher {
1330
1417
  message: string;
1331
1418
  };
1332
1419
  analytics: {
1420
+ enabled: boolean;
1421
+ status: "degraded" | "stub";
1422
+ handlerReady: boolean;
1423
+ route: string | undefined;
1424
+ provider: string | undefined;
1425
+ message: string | undefined;
1426
+ } | {
1333
1427
  enabled: boolean;
1334
1428
  status: "available";
1335
1429
  handlerReady: boolean;
1336
1430
  route: string | undefined;
1337
1431
  provider: string | undefined;
1432
+ message?: undefined;
1338
1433
  } | {
1339
1434
  enabled: boolean;
1340
1435
  status: "unavailable";
@@ -1342,11 +1437,19 @@ declare class HttpDispatcher {
1342
1437
  message: string;
1343
1438
  };
1344
1439
  cache: {
1440
+ enabled: boolean;
1441
+ status: "degraded" | "stub";
1442
+ handlerReady: boolean;
1443
+ route: string | undefined;
1444
+ provider: string | undefined;
1445
+ message: string | undefined;
1446
+ } | {
1345
1447
  enabled: boolean;
1346
1448
  status: "available";
1347
1449
  handlerReady: boolean;
1348
1450
  route: string | undefined;
1349
1451
  provider: string | undefined;
1452
+ message?: undefined;
1350
1453
  } | {
1351
1454
  enabled: boolean;
1352
1455
  status: "unavailable";
@@ -1354,11 +1457,19 @@ declare class HttpDispatcher {
1354
1457
  message: string;
1355
1458
  };
1356
1459
  queue: {
1460
+ enabled: boolean;
1461
+ status: "degraded" | "stub";
1462
+ handlerReady: boolean;
1463
+ route: string | undefined;
1464
+ provider: string | undefined;
1465
+ message: string | undefined;
1466
+ } | {
1357
1467
  enabled: boolean;
1358
1468
  status: "available";
1359
1469
  handlerReady: boolean;
1360
1470
  route: string | undefined;
1361
1471
  provider: string | undefined;
1472
+ message?: undefined;
1362
1473
  } | {
1363
1474
  enabled: boolean;
1364
1475
  status: "unavailable";
@@ -1366,11 +1477,19 @@ declare class HttpDispatcher {
1366
1477
  message: string;
1367
1478
  };
1368
1479
  job: {
1480
+ enabled: boolean;
1481
+ status: "degraded" | "stub";
1482
+ handlerReady: boolean;
1483
+ route: string | undefined;
1484
+ provider: string | undefined;
1485
+ message: string | undefined;
1486
+ } | {
1369
1487
  enabled: boolean;
1370
1488
  status: "available";
1371
1489
  handlerReady: boolean;
1372
1490
  route: string | undefined;
1373
1491
  provider: string | undefined;
1492
+ message?: undefined;
1374
1493
  } | {
1375
1494
  enabled: boolean;
1376
1495
  status: "unavailable";
@@ -1378,11 +1497,19 @@ declare class HttpDispatcher {
1378
1497
  message: string;
1379
1498
  };
1380
1499
  ui: {
1500
+ enabled: boolean;
1501
+ status: "degraded" | "stub";
1502
+ handlerReady: boolean;
1503
+ route: string | undefined;
1504
+ provider: string | undefined;
1505
+ message: string | undefined;
1506
+ } | {
1381
1507
  enabled: boolean;
1382
1508
  status: "available";
1383
1509
  handlerReady: boolean;
1384
1510
  route: string | undefined;
1385
1511
  provider: string | undefined;
1512
+ message?: undefined;
1386
1513
  } | {
1387
1514
  enabled: boolean;
1388
1515
  status: "unavailable";
@@ -1390,11 +1517,19 @@ declare class HttpDispatcher {
1390
1517
  message: string;
1391
1518
  };
1392
1519
  workflow: {
1520
+ enabled: boolean;
1521
+ status: "degraded" | "stub";
1522
+ handlerReady: boolean;
1523
+ route: string | undefined;
1524
+ provider: string | undefined;
1525
+ message: string | undefined;
1526
+ } | {
1393
1527
  enabled: boolean;
1394
1528
  status: "available";
1395
1529
  handlerReady: boolean;
1396
1530
  route: string | undefined;
1397
1531
  provider: string | undefined;
1532
+ message?: undefined;
1398
1533
  } | {
1399
1534
  enabled: boolean;
1400
1535
  status: "unavailable";
@@ -1403,22 +1538,29 @@ declare class HttpDispatcher {
1403
1538
  };
1404
1539
  realtime: {
1405
1540
  enabled: boolean;
1406
- status: "available";
1541
+ status: "unavailable";
1407
1542
  handlerReady: boolean;
1408
- route: string | undefined;
1409
- provider: string | undefined;
1543
+ message: string;
1410
1544
  } | {
1411
1545
  enabled: boolean;
1412
- status: "unavailable";
1546
+ status: "degraded" | "stub";
1413
1547
  handlerReady: boolean;
1414
1548
  message: string;
1415
1549
  };
1416
1550
  notification: {
1551
+ enabled: boolean;
1552
+ status: "degraded" | "stub";
1553
+ handlerReady: boolean;
1554
+ route: string | undefined;
1555
+ provider: string | undefined;
1556
+ message: string | undefined;
1557
+ } | {
1417
1558
  enabled: boolean;
1418
1559
  status: "available";
1419
1560
  handlerReady: boolean;
1420
1561
  route: string | undefined;
1421
1562
  provider: string | undefined;
1563
+ message?: undefined;
1422
1564
  } | {
1423
1565
  enabled: boolean;
1424
1566
  status: "unavailable";
@@ -1426,11 +1568,19 @@ declare class HttpDispatcher {
1426
1568
  message: string;
1427
1569
  };
1428
1570
  ai: {
1571
+ enabled: boolean;
1572
+ status: "degraded" | "stub";
1573
+ handlerReady: boolean;
1574
+ route: string | undefined;
1575
+ provider: string | undefined;
1576
+ message: string | undefined;
1577
+ } | {
1429
1578
  enabled: boolean;
1430
1579
  status: "available";
1431
1580
  handlerReady: boolean;
1432
1581
  route: string | undefined;
1433
1582
  provider: string | undefined;
1583
+ message?: undefined;
1434
1584
  } | {
1435
1585
  enabled: boolean;
1436
1586
  status: "unavailable";
@@ -1438,11 +1588,19 @@ declare class HttpDispatcher {
1438
1588
  message: string;
1439
1589
  };
1440
1590
  i18n: {
1591
+ enabled: boolean;
1592
+ status: "degraded" | "stub";
1593
+ handlerReady: boolean;
1594
+ route: string | undefined;
1595
+ provider: string | undefined;
1596
+ message: string | undefined;
1597
+ } | {
1441
1598
  enabled: boolean;
1442
1599
  status: "available";
1443
1600
  handlerReady: boolean;
1444
1601
  route: string | undefined;
1445
1602
  provider: string | undefined;
1603
+ message?: undefined;
1446
1604
  } | {
1447
1605
  enabled: boolean;
1448
1606
  status: "unavailable";
@@ -1450,11 +1608,19 @@ declare class HttpDispatcher {
1450
1608
  message: string;
1451
1609
  };
1452
1610
  graphql: {
1611
+ enabled: boolean;
1612
+ status: "degraded" | "stub";
1613
+ handlerReady: boolean;
1614
+ route: string | undefined;
1615
+ provider: string | undefined;
1616
+ message: string | undefined;
1617
+ } | {
1453
1618
  enabled: boolean;
1454
1619
  status: "available";
1455
1620
  handlerReady: boolean;
1456
1621
  route: string | undefined;
1457
1622
  provider: string | undefined;
1623
+ message?: undefined;
1458
1624
  } | {
1459
1625
  enabled: boolean;
1460
1626
  status: "unavailable";
@@ -1462,11 +1628,19 @@ declare class HttpDispatcher {
1462
1628
  message: string;
1463
1629
  };
1464
1630
  'file-storage': {
1631
+ enabled: boolean;
1632
+ status: "degraded" | "stub";
1633
+ handlerReady: boolean;
1634
+ route: string | undefined;
1635
+ provider: string | undefined;
1636
+ message: string | undefined;
1637
+ } | {
1465
1638
  enabled: boolean;
1466
1639
  status: "available";
1467
1640
  handlerReady: boolean;
1468
1641
  route: string | undefined;
1469
1642
  provider: string | undefined;
1643
+ message?: undefined;
1470
1644
  } | {
1471
1645
  enabled: boolean;
1472
1646
  status: "unavailable";
@@ -1474,11 +1648,19 @@ declare class HttpDispatcher {
1474
1648
  message: string;
1475
1649
  };
1476
1650
  search: {
1651
+ enabled: boolean;
1652
+ status: "degraded" | "stub";
1653
+ handlerReady: boolean;
1654
+ route: string | undefined;
1655
+ provider: string | undefined;
1656
+ message: string | undefined;
1657
+ } | {
1477
1658
  enabled: boolean;
1478
1659
  status: "available";
1479
1660
  handlerReady: boolean;
1480
1661
  route: string | undefined;
1481
1662
  provider: string | undefined;
1663
+ message?: undefined;
1482
1664
  } | {
1483
1665
  enabled: boolean;
1484
1666
  status: "unavailable";
@@ -1499,6 +1681,18 @@ declare class HttpDispatcher {
1499
1681
  query: string;
1500
1682
  variables?: any;
1501
1683
  }, context: HttpProtocolContext): Promise<any>;
1684
+ /**
1685
+ * Resolve the RBAC/RLS execution context for a request that did NOT flow
1686
+ * through {@link dispatch} (which resolves and caches it on
1687
+ * `context.executionContext`). The dispatcher-plugin's direct `/graphql`
1688
+ * route is the current caller. Mirrors the identity resolution `dispatch()`
1689
+ * performs so an anonymous-deny gate can tell an authenticated caller from
1690
+ * an anonymous one, and so the caller's identity can be THREADED to the
1691
+ * engine (#2992 / ADR-0096 D1) instead of dropped. Best-effort: returns
1692
+ * `undefined` on failure (treated as anonymous, i.e. denied under
1693
+ * `requireAuth`).
1694
+ */
1695
+ private resolveRequestExecutionContext;
1502
1696
  /**
1503
1697
  * Handles Auth requests
1504
1698
  * path: sub-path after /auth/