@subcortex-ai/sdk 0.2.0 → 0.3.0

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.cjs CHANGED
@@ -46,6 +46,8 @@ __export(index_exports, {
46
46
  RecallNamespace: () => RecallNamespace,
47
47
  RelationshipTypes: () => RelationshipTypes,
48
48
  RelationshipsNamespace: () => RelationshipsNamespace,
49
+ RulePredicates: () => RulePredicates,
50
+ RulesNamespace: () => RulesNamespace,
49
51
  SIGNAL_DECAY_CONFIG: () => SIGNAL_DECAY_CONFIG,
50
52
  SYSTEM_SIGNAL_TYPES: () => SYSTEM_SIGNAL_TYPES,
51
53
  SearchNamespace: () => SearchNamespace,
@@ -1137,6 +1139,242 @@ function enc2(s) {
1137
1139
  return encodeURIComponent(s);
1138
1140
  }
1139
1141
 
1142
+ // src/types/rules.ts
1143
+ var RulePredicates = {
1144
+ BANNED_PATTERN: "rule:banned_pattern",
1145
+ REQUIRED_PATTERN: "rule:required_pattern",
1146
+ FILE_CONSTRAINT: "rule:file_constraint",
1147
+ REQUIRES_PROPAGATION: "rule:requires_propagation"
1148
+ };
1149
+
1150
+ // src/rules.ts
1151
+ var RULE_PREDICATE_PREFIX = "rule:";
1152
+ var TYPE_TO_PREDICATE = {
1153
+ banned_pattern: RulePredicates.BANNED_PATTERN,
1154
+ required_pattern: RulePredicates.REQUIRED_PATTERN,
1155
+ file_constraint: RulePredicates.FILE_CONSTRAINT,
1156
+ requires_propagation: RulePredicates.REQUIRES_PROPAGATION
1157
+ };
1158
+ function defaultSeverity(confidence) {
1159
+ if (confidence >= 1) return "block";
1160
+ if (confidence >= 0.7) return "warn";
1161
+ return "info";
1162
+ }
1163
+ var RulesNamespace = class {
1164
+ http;
1165
+ tenantId;
1166
+ constructor(http, tenantId) {
1167
+ this.http = http;
1168
+ this.tenantId = tenantId;
1169
+ }
1170
+ /**
1171
+ * Create a new rule.
1172
+ *
1173
+ * Stores an assertion with a `rule:*` predicate and a typed value.
1174
+ * Defaults to confidence 1.0 (enforcement) if not specified.
1175
+ *
1176
+ * @example
1177
+ * ```typescript
1178
+ * await subcortex.rules.create({
1179
+ * subject: 'project:my-project',
1180
+ * type: 'banned_pattern',
1181
+ * pattern: 'as any',
1182
+ * description: 'No any casts — use proper types',
1183
+ * })
1184
+ * ```
1185
+ */
1186
+ async create(input) {
1187
+ const confidence = input.confidence ?? 1;
1188
+ const severity = input.severity ?? defaultSeverity(confidence);
1189
+ const ruleValue = {
1190
+ description: input.description,
1191
+ pattern: input.pattern,
1192
+ fileScope: input.fileScope,
1193
+ propagatesTo: input.propagatesTo
1194
+ };
1195
+ const assertion = await this.http.post(
1196
+ `/api/v1/assertions`,
1197
+ {
1198
+ subject: input.subject,
1199
+ predicate: TYPE_TO_PREDICATE[input.type],
1200
+ value: { ...ruleValue, severity },
1201
+ confidence,
1202
+ tenant_id: input.tenantId ?? this.tenantId
1203
+ },
1204
+ input.tenantId ?? this.tenantId
1205
+ );
1206
+ return parseRule(assertion);
1207
+ }
1208
+ /**
1209
+ * Get a rule by its assertion ID.
1210
+ */
1211
+ async get(ruleId, options) {
1212
+ const tenant = options?.tenantId ?? this.tenantId;
1213
+ const assertion = await this.http.get(
1214
+ `/api/v1/assertions/${enc3(tenant)}/${enc3(ruleId)}`
1215
+ );
1216
+ return parseRule(assertion);
1217
+ }
1218
+ /**
1219
+ * List all rules for this project, optionally filtered by type or severity.
1220
+ */
1221
+ async list(options) {
1222
+ const tenant = options?.tenantId ?? this.tenantId;
1223
+ const assertions = await this.http.get(
1224
+ `/api/v1/assertions/list/${enc3(tenant)}`
1225
+ );
1226
+ return assertions.filter((a) => isRuleAssertion(a)).filter((a) => options?.includeSuperseded || !a.isSuperseded).map(parseRule).filter((r) => !options?.type || r.type === options.type).filter((r) => !options?.severity || r.severity === options.severity);
1227
+ }
1228
+ /**
1229
+ * Get all rules whose fileScope matches the given file path.
1230
+ *
1231
+ * This is the primary query for the PreToolUse hook — "what rules apply
1232
+ * to the file I'm about to modify?"
1233
+ *
1234
+ * @example
1235
+ * ```typescript
1236
+ * const rules = await subcortex.rules.forFile('app/api/users/route.ts')
1237
+ * // Returns banned_pattern and required_pattern rules whose fileScope
1238
+ * // matches the path, plus any project-wide rules without a fileScope.
1239
+ * ```
1240
+ */
1241
+ async forFile(filePath, options) {
1242
+ const allRules = await this.list(options);
1243
+ return allRules.filter((rule) => {
1244
+ if (!rule.value.fileScope) return true;
1245
+ try {
1246
+ const regex = new RegExp(rule.value.fileScope);
1247
+ return regex.test(filePath);
1248
+ } catch {
1249
+ return false;
1250
+ }
1251
+ });
1252
+ }
1253
+ /**
1254
+ * Get all rules scoped to a specific intent subject.
1255
+ *
1256
+ * @example
1257
+ * ```typescript
1258
+ * const apiRules = await subcortex.rules.forIntent('api')
1259
+ * // Queries subject "intent:api" for all rule assertions
1260
+ * ```
1261
+ */
1262
+ async forIntent(intent, options) {
1263
+ const tenant = options?.tenantId ?? this.tenantId;
1264
+ const subject2 = intent.startsWith("intent:") ? intent : `intent:${intent}`;
1265
+ const assertions = await this.http.get(
1266
+ `/api/v1/assertions/query/${enc3(tenant)}/${enc3(subject2)}`
1267
+ );
1268
+ return assertions.filter((a) => isRuleAssertion(a)).filter((a) => options?.includeSuperseded || !a.isSuperseded).map(parseRule).filter((r) => !options?.type || r.type === options.type).filter((r) => !options?.severity || r.severity === options.severity);
1269
+ }
1270
+ /**
1271
+ * Get all rules scoped to a specific subject (project, file, intent, etc.).
1272
+ */
1273
+ async forSubject(subject2, options) {
1274
+ const tenant = options?.tenantId ?? this.tenantId;
1275
+ const assertions = await this.http.get(
1276
+ `/api/v1/assertions/query/${enc3(tenant)}/${enc3(subject2)}`
1277
+ );
1278
+ return assertions.filter((a) => isRuleAssertion(a)).filter((a) => options?.includeSuperseded || !a.isSuperseded).map(parseRule).filter((r) => !options?.type || r.type === options.type).filter((r) => !options?.severity || r.severity === options.severity);
1279
+ }
1280
+ /**
1281
+ * Update a rule by superseding it.
1282
+ *
1283
+ * The old rule is marked as superseded, a new one is created.
1284
+ * Full assertion history is preserved.
1285
+ */
1286
+ async update(input) {
1287
+ const tenant = input.tenantId ?? this.tenantId;
1288
+ const existing = await this.get(input.ruleId, { tenantId: tenant });
1289
+ const confidence = input.confidence ?? existing.confidence;
1290
+ const severity = input.severity ?? existing.severity;
1291
+ const ruleValue = {
1292
+ description: input.description ?? existing.value.description,
1293
+ pattern: input.pattern ?? existing.value.pattern,
1294
+ fileScope: input.fileScope ?? existing.value.fileScope,
1295
+ propagatesTo: input.propagatesTo ?? existing.value.propagatesTo
1296
+ };
1297
+ const assertion = await this.http.post(
1298
+ `/api/v1/assertions/supersede`,
1299
+ {
1300
+ original_assertion_id: input.ruleId,
1301
+ subject: existing.subject,
1302
+ predicate: existing.predicate,
1303
+ value: { ...ruleValue, severity },
1304
+ confidence,
1305
+ tenant_id: tenant
1306
+ },
1307
+ tenant
1308
+ );
1309
+ return parseRule(assertion);
1310
+ }
1311
+ /**
1312
+ * Retract (deactivate) a rule.
1313
+ *
1314
+ * The assertion's temporal bounds are closed. The rule stops matching
1315
+ * in future queries but its history is preserved.
1316
+ */
1317
+ async retract(ruleId, options) {
1318
+ const tenant = options?.tenantId ?? this.tenantId;
1319
+ await this.http.post(
1320
+ `/api/v1/assertions/retract/${enc3(tenant)}/${enc3(ruleId)}`,
1321
+ {},
1322
+ tenant
1323
+ );
1324
+ }
1325
+ };
1326
+ function isRuleAssertion(assertion) {
1327
+ return assertion.predicate?.startsWith(RULE_PREDICATE_PREFIX) ?? false;
1328
+ }
1329
+ function parseRule(assertion) {
1330
+ const predicate = assertion.predicate;
1331
+ const type = predicate.replace(RULE_PREDICATE_PREFIX, "");
1332
+ let ruleValue;
1333
+ let severity;
1334
+ if (typeof assertion.value === "object" && assertion.value !== null) {
1335
+ const val = assertion.value;
1336
+ ruleValue = {
1337
+ description: String(val.description ?? ""),
1338
+ pattern: val.pattern != null ? String(val.pattern) : void 0,
1339
+ fileScope: val.fileScope != null ? String(val.fileScope) : void 0,
1340
+ propagatesTo: Array.isArray(val.propagatesTo) ? val.propagatesTo.map(String) : void 0
1341
+ };
1342
+ severity = val.severity ?? defaultSeverity(assertion.confidence);
1343
+ } else if (typeof assertion.value === "string") {
1344
+ try {
1345
+ const parsed = JSON.parse(assertion.value);
1346
+ ruleValue = {
1347
+ description: String(parsed.description ?? ""),
1348
+ pattern: parsed.pattern != null ? String(parsed.pattern) : void 0,
1349
+ fileScope: parsed.filePattern != null ? String(parsed.filePattern) : void 0,
1350
+ propagatesTo: Array.isArray(parsed.propagatesTo) ? parsed.propagatesTo.map(String) : void 0
1351
+ };
1352
+ severity = parsed.severity ?? defaultSeverity(assertion.confidence);
1353
+ } catch {
1354
+ ruleValue = { description: String(assertion.value) };
1355
+ severity = defaultSeverity(assertion.confidence);
1356
+ }
1357
+ } else {
1358
+ ruleValue = { description: "" };
1359
+ severity = defaultSeverity(assertion.confidence);
1360
+ }
1361
+ return {
1362
+ id: assertion.id,
1363
+ tenantId: assertion.tenantId,
1364
+ subject: assertion.subject,
1365
+ type,
1366
+ predicate,
1367
+ value: ruleValue,
1368
+ severity,
1369
+ confidence: assertion.confidence,
1370
+ validFrom: assertion.validFrom,
1371
+ isSuperseded: assertion.isSuperseded
1372
+ };
1373
+ }
1374
+ function enc3(s) {
1375
+ return encodeURIComponent(s);
1376
+ }
1377
+
1140
1378
  // src/client.ts
1141
1379
  var AssertionsNamespace = class {
1142
1380
  constructor(http, tenantId) {
@@ -1158,21 +1396,21 @@ var AssertionsNamespace = class {
1158
1396
  async get(assertionId, options) {
1159
1397
  const tenantId = options?.tenantId || this.tenantId;
1160
1398
  return this.http.get(
1161
- `/api/v1/assertions/${enc3(tenantId)}/${enc3(assertionId)}`
1399
+ `/api/v1/assertions/${enc4(tenantId)}/${enc4(assertionId)}`
1162
1400
  );
1163
1401
  }
1164
1402
  /** Query assertions by subject. */
1165
1403
  async query(subject2, options) {
1166
1404
  const tenantId = options?.tenantId || this.tenantId;
1167
1405
  return this.http.get(
1168
- `/api/v1/assertions/query/${enc3(tenantId)}/${enc3(subject2)}`
1406
+ `/api/v1/assertions/query/${enc4(tenantId)}/${enc4(subject2)}`
1169
1407
  );
1170
1408
  }
1171
1409
  /** List all active assertions for the tenant. */
1172
1410
  async list(options) {
1173
1411
  const tenantId = options?.tenantId || this.tenantId;
1174
1412
  return this.http.get(
1175
- `/api/v1/assertions/list/${enc3(tenantId)}`
1413
+ `/api/v1/assertions/list/${enc4(tenantId)}`
1176
1414
  );
1177
1415
  }
1178
1416
  /** Supersede an existing assertion with updated values. */
@@ -1191,7 +1429,7 @@ var AssertionsNamespace = class {
1191
1429
  async retract(assertionId, options) {
1192
1430
  const tenantId = options?.tenantId || this.tenantId;
1193
1431
  return this.http.post(
1194
- `/api/v1/assertions/retract/${enc3(tenantId)}/${enc3(assertionId)}`,
1432
+ `/api/v1/assertions/retract/${enc4(tenantId)}/${enc4(assertionId)}`,
1195
1433
  {}
1196
1434
  );
1197
1435
  }
@@ -1218,14 +1456,14 @@ var RelationshipsNamespace = class {
1218
1456
  async query(subject2, options) {
1219
1457
  const tenantId = options?.tenantId || this.tenantId;
1220
1458
  return this.http.get(
1221
- `/api/v1/relationships/${enc3(tenantId)}/${enc3(subject2)}`
1459
+ `/api/v1/relationships/${enc4(tenantId)}/${enc4(subject2)}`
1222
1460
  );
1223
1461
  }
1224
1462
  /** List all relationships for the tenant. */
1225
1463
  async list(options) {
1226
1464
  const tenantId = options?.tenantId || this.tenantId;
1227
1465
  return this.http.get(
1228
- `/api/v1/relationships/list/${enc3(tenantId)}`
1466
+ `/api/v1/relationships/list/${enc4(tenantId)}`
1229
1467
  );
1230
1468
  }
1231
1469
  };
@@ -1252,14 +1490,14 @@ var AgentsNamespace = class {
1252
1490
  async get(agentId, options) {
1253
1491
  const tenantId = options?.tenantId || this.tenantId;
1254
1492
  return this.http.get(
1255
- `/api/v1/agents/${enc3(tenantId)}/${enc3(agentId)}`
1493
+ `/api/v1/agents/${enc4(tenantId)}/${enc4(agentId)}`
1256
1494
  );
1257
1495
  }
1258
1496
  /** Update an agent. */
1259
1497
  async update(agentId, input, options) {
1260
1498
  const tenantId = options?.tenantId || this.tenantId;
1261
1499
  return this.http.put(
1262
- `/api/v1/agents/${enc3(tenantId)}/${enc3(agentId)}`,
1500
+ `/api/v1/agents/${enc4(tenantId)}/${enc4(agentId)}`,
1263
1501
  {
1264
1502
  tenant_id: tenantId,
1265
1503
  name: input.name,
@@ -1276,20 +1514,20 @@ var AgentsNamespace = class {
1276
1514
  /** Delete an agent. */
1277
1515
  async delete(agentId, options) {
1278
1516
  const tenantId = options?.tenantId || this.tenantId;
1279
- await this.http.delete(`/api/v1/agents/${enc3(tenantId)}/${enc3(agentId)}`);
1517
+ await this.http.delete(`/api/v1/agents/${enc4(tenantId)}/${enc4(agentId)}`);
1280
1518
  }
1281
1519
  /** List all agents for the tenant. */
1282
1520
  async list(options) {
1283
1521
  const tenantId = options?.tenantId || this.tenantId;
1284
1522
  return this.http.get(
1285
- `/api/v1/agents/list/${enc3(tenantId)}`
1523
+ `/api/v1/agents/list/${enc4(tenantId)}`
1286
1524
  );
1287
1525
  }
1288
1526
  /** Get composed system instructions for an agent. */
1289
1527
  async getInstructions(agentId, options) {
1290
1528
  const tenantId = options?.tenantId || this.tenantId;
1291
1529
  return this.http.get(
1292
- `/api/v1/agents/${enc3(tenantId)}/${enc3(agentId)}/instructions`
1530
+ `/api/v1/agents/${enc4(tenantId)}/${enc4(agentId)}/instructions`
1293
1531
  );
1294
1532
  }
1295
1533
  };
@@ -1339,14 +1577,14 @@ var MemoryNamespace = class {
1339
1577
  async recall(subject2, options) {
1340
1578
  const tenantId = options?.tenantId || this.tenantId;
1341
1579
  return this.http.get(
1342
- `/api/v1/assertions/query/${enc3(tenantId)}/${enc3(subject2)}`
1580
+ `/api/v1/assertions/query/${enc4(tenantId)}/${enc4(subject2)}`
1343
1581
  );
1344
1582
  }
1345
1583
  /** Forget a specific memory (retract the assertion). */
1346
1584
  async forget(assertionId, options) {
1347
1585
  const tenantId = options?.tenantId || this.tenantId;
1348
1586
  return this.http.post(
1349
- `/api/v1/assertions/retract/${enc3(tenantId)}/${enc3(assertionId)}`,
1587
+ `/api/v1/assertions/retract/${enc4(tenantId)}/${enc4(assertionId)}`,
1350
1588
  {}
1351
1589
  );
1352
1590
  }
@@ -1378,7 +1616,7 @@ var IntakeNamespace = class {
1378
1616
  }
1379
1617
  /** Get pending intake results for an agent. */
1380
1618
  async pending(agentId, options) {
1381
- let path = `/api/v1/intake/pending/${enc3(agentId)}`;
1619
+ let path = `/api/v1/intake/pending/${enc4(agentId)}`;
1382
1620
  const params = [];
1383
1621
  if (options?.status) params.push(`status=${options.status.join(",")}`);
1384
1622
  if (options?.limit) params.push(`limit=${options.limit}`);
@@ -1387,18 +1625,18 @@ var IntakeNamespace = class {
1387
1625
  }
1388
1626
  /** Acknowledge an intake result (mark as consumed). */
1389
1627
  async acknowledge(itemId) {
1390
- await this.http.post(`/api/v1/intake/acknowledge/${enc3(itemId)}`, {});
1628
+ await this.http.post(`/api/v1/intake/acknowledge/${enc4(itemId)}`, {});
1391
1629
  }
1392
1630
  /** Acknowledge all pending items for an agent. */
1393
1631
  async acknowledgeAll(agentId) {
1394
1632
  return this.http.post(
1395
- `/api/v1/intake/acknowledge-all/${enc3(agentId)}`,
1633
+ `/api/v1/intake/acknowledge-all/${enc4(agentId)}`,
1396
1634
  {}
1397
1635
  );
1398
1636
  }
1399
1637
  /** Get intake processing stats for an agent. */
1400
1638
  async stats(agentId) {
1401
- return this.http.get(`/api/v1/intake/stats/${enc3(agentId)}`);
1639
+ return this.http.get(`/api/v1/intake/stats/${enc4(agentId)}`);
1402
1640
  }
1403
1641
  };
1404
1642
  var ExperiencesNamespace = class {
@@ -1414,14 +1652,14 @@ var ExperiencesNamespace = class {
1414
1652
  if (input?.limit) params.set("limit", String(input.limit));
1415
1653
  const qs = params.toString();
1416
1654
  return this.http.get(
1417
- `/api/v1/experiences/${enc3(tenantId)}${qs ? `?${qs}` : ""}`
1655
+ `/api/v1/experiences/${enc4(tenantId)}${qs ? `?${qs}` : ""}`
1418
1656
  );
1419
1657
  }
1420
1658
  /** Get a single experience by ID. */
1421
1659
  async get(experienceId, options) {
1422
1660
  const tenantId = options?.tenantId || this.tenantId;
1423
1661
  return this.http.get(
1424
- `/api/v1/experiences/${enc3(tenantId)}/${enc3(experienceId)}`
1662
+ `/api/v1/experiences/${enc4(tenantId)}/${enc4(experienceId)}`
1425
1663
  );
1426
1664
  }
1427
1665
  };
@@ -1436,7 +1674,7 @@ var RecallNamespace = class {
1436
1674
  const params = new URLSearchParams({ q: input.query });
1437
1675
  if (input.limit) params.set("limit", String(input.limit));
1438
1676
  return this.http.get(
1439
- `/api/v1/recall/${enc3(tenantId)}?${params.toString()}`
1677
+ `/api/v1/recall/${enc4(tenantId)}?${params.toString()}`
1440
1678
  );
1441
1679
  }
1442
1680
  };
@@ -1449,7 +1687,7 @@ var GraphNamespace = class {
1449
1687
  async findPaths(input) {
1450
1688
  const tenantId = input.tenantId || this.tenantId;
1451
1689
  return this.http.post(
1452
- `/api/v1/graph/${enc3(tenantId)}/paths`,
1690
+ `/api/v1/graph/${enc4(tenantId)}/paths`,
1453
1691
  {
1454
1692
  from: input.from,
1455
1693
  to: input.to,
@@ -1463,7 +1701,7 @@ var GraphNamespace = class {
1463
1701
  async traverse(input) {
1464
1702
  const tenantId = input.tenantId || this.tenantId;
1465
1703
  return this.http.post(
1466
- `/api/v1/graph/${enc3(tenantId)}/traverse`,
1704
+ `/api/v1/graph/${enc4(tenantId)}/traverse`,
1467
1705
  {
1468
1706
  start_subject: input.startSubject,
1469
1707
  max_depth: input.maxDepth,
@@ -1484,7 +1722,7 @@ var TemporalNamespace = class {
1484
1722
  const tenantId = options?.tenantId || this.tenantId;
1485
1723
  const pointStr = point instanceof Date ? point.toISOString() : point;
1486
1724
  return this.http.get(
1487
- `/api/v1/temporal/${enc3(tenantId)}/${enc3(subject2)}/at?point=${enc3(pointStr)}`
1725
+ `/api/v1/temporal/${enc4(tenantId)}/${enc4(subject2)}/at?point=${enc4(pointStr)}`
1488
1726
  );
1489
1727
  }
1490
1728
  /** Get assertions within a date range. */
@@ -1493,7 +1731,7 @@ var TemporalNamespace = class {
1493
1731
  const startStr = start instanceof Date ? start.toISOString() : start;
1494
1732
  const endStr = end instanceof Date ? end.toISOString() : end;
1495
1733
  return this.http.get(
1496
- `/api/v1/temporal/${enc3(tenantId)}/${enc3(subject2)}/range?start=${enc3(startStr)}&end=${enc3(endStr)}`
1734
+ `/api/v1/temporal/${enc4(tenantId)}/${enc4(subject2)}/range?start=${enc4(startStr)}&end=${enc4(endStr)}`
1497
1735
  );
1498
1736
  }
1499
1737
  };
@@ -1506,7 +1744,7 @@ var SearchNamespace = class {
1506
1744
  async semantic(input) {
1507
1745
  const tenantId = input.tenantId || this.tenantId;
1508
1746
  return this.http.post(
1509
- `/api/v1/search/${enc3(tenantId)}/semantic`,
1747
+ `/api/v1/search/${enc4(tenantId)}/semantic`,
1510
1748
  {
1511
1749
  embedding: input.embedding,
1512
1750
  top_k: input.topK
@@ -1596,10 +1834,10 @@ var EntitiesNamespace = class {
1596
1834
  const tenantId = options?.tenantId || this.tenantId;
1597
1835
  const subject2 = `entity:${entityName.toLowerCase().replace(/\s+/g, "-")}`;
1598
1836
  const assertions = await this.http.get(
1599
- `/api/v1/assertions/query/${enc3(tenantId)}/${enc3(subject2)}`
1837
+ `/api/v1/assertions/query/${enc4(tenantId)}/${enc4(subject2)}`
1600
1838
  );
1601
1839
  const relationships = await this.http.get(
1602
- `/api/v1/relationships/query/${enc3(tenantId)}/${enc3(subject2)}`
1840
+ `/api/v1/relationships/query/${enc4(tenantId)}/${enc4(subject2)}`
1603
1841
  );
1604
1842
  const typeAssertion = assertions.find((a) => a.predicate === "type" && !a.isSuperseded);
1605
1843
  const descAssertion = assertions.find((a) => a.predicate === "description" && !a.isSuperseded);
@@ -1607,7 +1845,7 @@ var EntitiesNamespace = class {
1607
1845
  const properties = await Promise.all(
1608
1846
  propertyRels.map(async (rel) => {
1609
1847
  const propAssertions = await this.http.get(
1610
- `/api/v1/assertions/query/${enc3(tenantId)}/${enc3(rel.toSubject)}`
1848
+ `/api/v1/assertions/query/${enc4(tenantId)}/${enc4(rel.toSubject)}`
1611
1849
  );
1612
1850
  const fieldType = propAssertions.find((a) => a.predicate === "field_type" && !a.isSuperseded);
1613
1851
  const required = propAssertions.find((a) => a.predicate === "required" && !a.isSuperseded);
@@ -1637,7 +1875,7 @@ var EntitiesNamespace = class {
1637
1875
  async list(options) {
1638
1876
  const tenantId = options?.tenantId || this.tenantId;
1639
1877
  const all = await this.http.get(
1640
- `/api/v1/assertions/list/${enc3(tenantId)}`
1878
+ `/api/v1/assertions/list/${enc4(tenantId)}`
1641
1879
  );
1642
1880
  return all.filter((a) => a.subject.startsWith("entity:") && a.predicate === "type" && !a.isSuperseded);
1643
1881
  }
@@ -1669,6 +1907,8 @@ var SubCortexClient = class {
1669
1907
  experiences;
1670
1908
  /** Entity schema management — create, describe, and manage data models */
1671
1909
  entities;
1910
+ /** Code guardrails — typed rules stored as assertions with file-scope matching */
1911
+ rules;
1672
1912
  http;
1673
1913
  constructor(options) {
1674
1914
  if (!options.apiKey && !options.token) {
@@ -1698,13 +1938,14 @@ var SubCortexClient = class {
1698
1938
  this.recall = new RecallNamespace(this.http, options.tenantId);
1699
1939
  this.experiences = new ExperiencesNamespace(this.http, options.tenantId);
1700
1940
  this.entities = new EntitiesNamespace(this.http, options.tenantId);
1941
+ this.rules = new RulesNamespace(this.http, options.tenantId);
1701
1942
  }
1702
1943
  /** Check SubCortex server health. */
1703
1944
  async health() {
1704
1945
  return this.http.get("/health");
1705
1946
  }
1706
1947
  };
1707
- function enc3(s) {
1948
+ function enc4(s) {
1708
1949
  return encodeURIComponent(s);
1709
1950
  }
1710
1951
 
@@ -1894,6 +2135,8 @@ function esc(s) {
1894
2135
  RecallNamespace,
1895
2136
  RelationshipTypes,
1896
2137
  RelationshipsNamespace,
2138
+ RulePredicates,
2139
+ RulesNamespace,
1897
2140
  SIGNAL_DECAY_CONFIG,
1898
2141
  SYSTEM_SIGNAL_TYPES,
1899
2142
  SearchNamespace,