@kortexya/reasoninglayer 1.28.0 → 2.1.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
@@ -7,7 +7,7 @@ var __export = (target, all) => {
7
7
  };
8
8
 
9
9
  // src/config.ts
10
- var SDK_VERSION = "1.28.0";
10
+ var SDK_VERSION = "2.1.0";
11
11
  function resolveConfig(config) {
12
12
  if (!config.baseUrl) {
13
13
  throw new Error("ClientConfig.baseUrl is required");
@@ -1164,11 +1164,11 @@ var Sorts = class {
1164
1164
  ...params
1165
1165
  });
1166
1166
  /**
1167
- * @description Per Definition IV.5 (Milanese & Pasi, IEEE TFS 2024 — CC-BY manuscript in reasoninglayer-sources/pdf_sources/), verbatim: ≺∼· ≝ ((≺∼ .− ∼) ⊍ ⪯)⊕ The combined chain preorder ≺∼ of Definition IV.1 with every DIRECTLY-similar pair deleted (`.−` zeroes the pair — it is not an arithmetic difference), the crisp order unioned back, and the result re-closed. A chain survives when its ENDPOINTS are not directly similar: slasher ⪯ horror ∼₀.₅ thriller keeps 0.5 while (horror, thriller) itself answers 0 — two similar sorts meet through their GLB instead (Fig. 4c: horror ⩏ thriller = slasher). The combined ≺∼ — where a direct ∼ edge IS a step; the coarse retrieval mode of Example V.4 — backs equivalence classes and term substitutability internally. (History: the differencing form here is ORIGINAL and faithful; #203 swapped in the combined semantics, and a 2026-08-23 pass re-documented that as correct from secondary sources. The accepted manuscript settled it the other way.)
1167
+ * @description Omitted, the answer is Def. IV.5 `≾̇` — the default because that is the relation the graded GLB and term substitutability are computed from. `granularity: "combined"` answers Def. IV.1 `≺∼`, where a similarity edge IS a step, so a directly-similar pair reads its similarity degree instead of the `0` the pair deletion gives it. A caller asking "how close are these two sorts" wants the second; a caller asking "does this sort substitute for that one" wants the first (#282). Per Definition IV.5 (Milanese & Pasi, IEEE TFS 2024 — CC-BY manuscript in reasoninglayer-sources/pdf_sources/), verbatim: ≺∼· ≝ ((≺∼ .− ∼) ⊍ ⪯)⊕ The combined chain preorder ≺∼ of Definition IV.1 with every DIRECTLY-similar pair deleted (`.−` zeroes the pair — it is not an arithmetic difference), the crisp order unioned back, and the result re-closed. A chain survives when its ENDPOINTS are not directly similar: slasher ⪯ horror ∼₀.₅ thriller keeps 0.5 while (horror, thriller) itself answers 0 — two similar sorts meet through their GLB instead (Fig. 4c: horror ⩏ thriller = slasher). The combined ≺∼ — where a direct ∼ edge IS a step; the coarse retrieval mode of Example V.4 — backs equivalence classes and term substitutability internally. (History: the differencing form here is ORIGINAL and faithful; #203 swapped in the combined semantics, and a 2026-08-23 pass re-documented that as correct from secondary sources. The accepted manuscript settled it the other way.)
1168
1168
  *
1169
1169
  * @tags sorts
1170
1170
  * @name GetPreorderDegree
1171
- * @summary Get preorder degree ≾̇(s₁, s₂) between two sorts
1171
+ * @summary Get the preorder degree between two sorts, in either of the paper's two readings — `granularity` selects which, exactly as `GET /api/v1/sorts/quotient-order` does, and the answer echoes it back.
1172
1172
  * @request POST:/api/v1/sorts/preorder-degree
1173
1173
  */
1174
1174
  getPreorderDegree = (data, params = {}) => this.http.request({
@@ -1869,7 +1869,7 @@ var Inference = class {
1869
1869
  ...params
1870
1870
  });
1871
1871
  /**
1872
- * @description # TRUE HOMOICONIC API Request contains a goal term and optional constraints. Response returns solutions with term-based substitutions. ## Temporal Reasoning Use constraints to filter by temporal relations: ```json { "goal": {"sort_name": "Employment", "features": {"valid_to": {"name": "?EndTime"}}}, "constraints": [{"type": "Guard", "left": "?EndTime", "op": "lt", "right": "1583020800000"}] } ``` ## Reading a solution `solutions[].proof.kind` says how the goal was established, and is always present: `proved` (a rule was applied — either it fired in this search, or the goal was a conclusion a forward chain had materialised and the engine recovered the rule from its recorded derivation, so `rule_term_id` and `rule_label` are filled), `fact` (a stored fact with no recorded derivation), `residuated` (an open-world leaf: unknown, never false), `unattributed` (a sound answer whose derivation the engine could not report). A client never has to infer this from the node's shape. A goal that carries `constraints` is a CONJUNCTION: its root node holds one subproof per clause and reports the strongest kind the clauses carry — `proved` when any clause was proved by a rule, else `fact`. `solutions[].substitution.bindings` carries one entry per query variable the engine resolved. A variable left unresolved is ABSENT; it is never bound to itself. # Authorization Requires X-Tenant-Id header. Traced: this is the path the zanzibar gateway hits for every permission check, so it is where an end-to-end trace either explains a slow request or does not. `skip_all` because the request body can be large and has no business in a span attribute.
1872
+ * @description # TRUE HOMOICONIC API Request contains a goal term and optional constraints. Response returns solutions with term-based substitutions. ## Temporal Reasoning Use constraints to filter by temporal relations: ```json { "goal": {"sort_name": "Employment", "features": {"valid_to": {"name": "?EndTime"}}}, "constraints": [{"type": "Guard", "left": "?EndTime", "op": "lt", "right": "1583020800000"}] } ``` ## Reading a solution `solutions[].proof.kind` says how the goal was established, and is always present: `proved` (a rule was applied — either it fired in this search, or the goal was a conclusion a forward chain had materialised and the engine recovered the rule from its recorded derivation, so `rule_term_id` and `rule_label` are filled), `fact` (a stored fact with no recorded derivation), `residuated` (an open-world leaf: unknown, never false), `unattributed` (a sound answer whose derivation the engine could not report). A client never has to infer this from the node's shape. A goal that carries `constraints` is a CONJUNCTION: its root node holds one subproof per clause and reports the strongest kind the clauses carry — `proved` when any clause was proved by a rule, else `fact`. `solutions[].substitution.bindings` carries one entry per query variable the engine resolved. A variable left unresolved is ABSENT; it is never bound to itself. ## Joining a proof to the thing it proves `proof.goal_term_id` is the TermId of the CONCLUSION the node proved. It joins to `POST /api/v1/query/by-sort` and `GET /api/v1/terms/{id}`, and two solutions of one goal share it only when they prove the same conclusion. It is ABSENT when there is no such id — a goal proved without a preceding `CHAIN` materialised no conclusion, and a derivation replayed from a persistent store cannot be attributed to one. A client falls back deliberately there rather than by accident. `proof.rule_head_term_id` names the rule's instantiated HEAD on a rule application, when it differs from the conclusion. ⛔ It is rule scaffolding: `GET /api/v1/terms/{id}` refuses it deliberately (#192). It is a grouping key, not a fetchable id. # Authorization Requires X-Tenant-Id header. Traced: this is the path the zanzibar gateway hits for every permission check, so it is where an end-to-end trace either explains a slow request or does not. `skip_all` because the request body can be large and has no business in a span attribute.
1873
1873
  *
1874
1874
  * @tags inference
1875
1875
  * @name BackwardChain
@@ -13670,19 +13670,56 @@ function BulkSetSimilaritiesResponseFromApiToFront(dto) {
13670
13670
  errors: dto.errors
13671
13671
  };
13672
13672
  }
13673
+ var SORT_PREORDER_GRANULARITIES = [
13674
+ "similarity_deleted",
13675
+ "combined"
13676
+ ];
13677
+ function toSortPreorderGranularity(value) {
13678
+ const known = SORT_PREORDER_GRANULARITIES.find((candidate) => candidate === value);
13679
+ if (known === void 0) {
13680
+ throw new ValidationError(
13681
+ `Unknown preorder granularity "${value}" \u2014 expected ${SORT_PREORDER_GRANULARITIES.join(" or ")}.`
13682
+ );
13683
+ }
13684
+ return known;
13685
+ }
13673
13686
  function GetPreorderDegreeRequestFromFrontToApi(model) {
13674
13687
  return {
13675
13688
  sort1_id: model.sort1Id,
13676
- sort2_id: model.sort2Id
13689
+ sort2_id: model.sort2Id,
13690
+ granularity: model.granularity
13677
13691
  };
13678
13692
  }
13679
13693
  function GetPreorderDegreeResponseFromApiToFront(dto) {
13680
13694
  return {
13681
13695
  sort1Id: dto.sort1_id,
13682
13696
  sort2Id: dto.sort2_id,
13697
+ degree: dto.degree,
13698
+ granularity: toSortPreorderGranularity(dto.granularity)
13699
+ };
13700
+ }
13701
+ function QuotientClassFromApiToFront(dto) {
13702
+ return {
13703
+ sortIds: dto.sort_ids,
13704
+ size: dto.size,
13705
+ alpha: dto.alpha
13706
+ };
13707
+ }
13708
+ function QuotientOrderEdgeFromApiToFront(dto) {
13709
+ return {
13710
+ from: dto.from,
13711
+ to: dto.to,
13683
13712
  degree: dto.degree
13684
13713
  };
13685
13714
  }
13715
+ function GetQuotientOrderResponseFromApiToFront(dto) {
13716
+ return {
13717
+ granularity: toSortPreorderGranularity(dto.granularity),
13718
+ classes: dto.classes.map(QuotientClassFromApiToFront),
13719
+ count: dto.count,
13720
+ orderEdges: dto.order_edges.map(QuotientOrderEdgeFromApiToFront)
13721
+ };
13722
+ }
13686
13723
  function EquivalenceClassFromApiToFront(dto) {
13687
13724
  return {
13688
13725
  sortIds: dto.sort_ids,
@@ -14529,13 +14566,32 @@ var SortsClient = class {
14529
14566
  /**
14530
14567
  * Compute the preorder degree between two sorts.
14531
14568
  *
14532
- * @param request - Sort pair to compute preorder degree for.
14533
- * @returns The preorder degree response including sort IDs and degree.
14534
- * @throws {@link ApiError} If the sorts do not exist.
14535
- *
14536
- * @remarks
14537
- * Per Definition IV.5 (Milanese and Pasi 2024), the combined preorder is:
14538
- * `preorder_dot = ((similarity - subsumption) union subsumption)^+`
14569
+ * @param request - Sort pair to compute preorder degree for, and optionally
14570
+ * which of the two preorders to read it from.
14571
+ * @returns The preorder degree response including sort IDs, degree, and the
14572
+ * granularity the degree was read from.
14573
+ * @throws {@link ApiError} If the sorts do not exist, or if `granularity`
14574
+ * carries a spelling the engine does not accept.
14575
+ * @throws {@link ValidationError} If the engine answers a granularity this
14576
+ * SDK version does not know.
14577
+ *
14578
+ * @remarks
14579
+ * Per Definition IV.5 (Milanese and Pasi, IEEE TFS 2024), the dotted preorder
14580
+ * is `preorder_dot = ((combined_preorder .- similarity) union subsumption)^+`,
14581
+ * where `.-` DELETES each directly-similar pair — it is NOT an arithmetic
14582
+ * difference. So a directly-similar pair answers `0` under the default
14583
+ * granularity: two similar sorts meet through their GLB, not through each
14584
+ * other.
14585
+ *
14586
+ * `granularity` selects the reading, with the same two spellings
14587
+ * `GET /api/v1/sorts/quotient-order` uses:
14588
+ * - omitted or `similarity_deleted` — Definition IV.5, the default, and the
14589
+ * relation the graded GLB and term substitutability are computed from.
14590
+ * - `combined` — Definition IV.1, where a similarity edge IS a step, so a
14591
+ * directly-similar pair answers its similarity degree.
14592
+ *
14593
+ * The response always echoes the granularity back, so a `0.0` is never
14594
+ * ambiguous between "no path" and "the pair deletion zeroed it".
14539
14595
  *
14540
14596
  * Degree interpretation:
14541
14597
  * - 1.0 = subsumption (sort1 <= sort2)
@@ -14546,17 +14602,78 @@ var SortsClient = class {
14546
14602
  *
14547
14603
  * @example
14548
14604
  * ```typescript
14549
- * const result = await client.sorts.getPreorderDegree({
14605
+ * const strict = await client.sorts.getPreorderDegree({
14606
+ * sort1Id: 'uuid-1',
14607
+ * sort2Id: 'uuid-2',
14608
+ * });
14609
+ * console.log(strict.degree, strict.granularity); // 0 'similarity_deleted'
14610
+ *
14611
+ * const coarse = await client.sorts.getPreorderDegree({
14550
14612
  * sort1Id: 'uuid-1',
14551
14613
  * sort2Id: 'uuid-2',
14614
+ * granularity: 'combined',
14552
14615
  * });
14553
- * console.log(result.degree); // 0.72
14616
+ * console.log(coarse.degree, coarse.granularity); // 0.5 'combined'
14554
14617
  * ```
14555
14618
  */
14556
14619
  async getPreorderDegree(request, requestOptions) {
14557
14620
  const response = await this.sorts.getPreorderDegree(GetPreorderDegreeRequestFromFrontToApi(request), toRequestParams(requestOptions));
14558
14621
  return GetPreorderDegreeResponseFromApiToFront(response.data);
14559
14622
  }
14623
+ /**
14624
+ * Get the Definition IV.9 quotient order over the caller's own lattice.
14625
+ *
14626
+ * @param options - Which of the two fuzzy preorders to quotient. Omitted
14627
+ * means `combined`, the engine's default HERE.
14628
+ * @returns The equivalence classes with their degrees, and the fuzzy partial
14629
+ * order between them.
14630
+ * @throws {@link ApiError} 400 when `granularity` carries a spelling the
14631
+ * engine does not accept.
14632
+ * @throws {@link ValidationError} If the engine answers a granularity this
14633
+ * SDK version does not know.
14634
+ *
14635
+ * @remarks
14636
+ * This is the tenant-scoped companion of
14637
+ * {@link SortsClient.getEquivalenceClasses}: the classes, degrees and order
14638
+ * describe exactly the sorts the caller can see, where the older
14639
+ * equivalence-classes route computes process-wide and then filters. Prefer
14640
+ * this one.
14641
+ *
14642
+ * ⚠️ The default granularity here is `combined`, NOT the
14643
+ * `similarity_deleted` default of {@link SortsClient.getPreorderDegree}. The
14644
+ * two routes take the same two spellings and disagree on which is the
14645
+ * default, so state it when it matters. The response echoes it back either
14646
+ * way.
14647
+ *
14648
+ * `orderEdges` is SPARSE and indexes into `classes`: a pair with no edge has
14649
+ * degree `0`. The order is a partial order — antisymmetric, unlike either
14650
+ * preorder it is built from.
14651
+ *
14652
+ * Uses tagged serialization format.
14653
+ *
14654
+ * @example
14655
+ * ```typescript
14656
+ * const quotient = await client.sorts.getQuotientOrder({
14657
+ * granularity: 'similarity_deleted',
14658
+ * });
14659
+ *
14660
+ * for (const cls of quotient.classes) {
14661
+ * console.log(`class of ${cls.size} sorts, degree ${cls.alpha}`);
14662
+ * }
14663
+ * for (const edge of quotient.orderEdges) {
14664
+ * const lower = quotient.classes[edge.from];
14665
+ * const upper = quotient.classes[edge.to];
14666
+ * console.log(`${lower.sortIds} <= ${upper.sortIds} at ${edge.degree}`);
14667
+ * }
14668
+ * ```
14669
+ */
14670
+ async getQuotientOrder(options, requestOptions) {
14671
+ const response = await this.sorts.getQuotientOrder(
14672
+ { granularity: options?.granularity },
14673
+ toRequestParams(requestOptions)
14674
+ );
14675
+ return GetQuotientOrderResponseFromApiToFront(response.data);
14676
+ }
14560
14677
  /**
14561
14678
  * Get equivalence classes based on the combined preorder.
14562
14679
  *
@@ -14564,8 +14681,16 @@ var SortsClient = class {
14564
14681
  * @throws {@link ApiError} If the lattice cannot be computed.
14565
14682
  *
14566
14683
  * @remarks
14567
- * Per Definition IV.9 (Milanese and Pasi 2024):
14568
- * s1 ~ s2 iff preorder_dot(s1, s2) > 0 AND preorder_dot(s2, s1) > 0.
14684
+ * Per Definition IV.9 (Milanese and Pasi 2024), two sorts are equivalent when
14685
+ * each reaches the other: `s1 ~ s2` iff `preorder(s1, s2) > 0` AND
14686
+ * `preorder(s2, s1) > 0`. The preorder here is the COMBINED one, where a
14687
+ * similarity edge is itself a step — not the `similarity_deleted` default of
14688
+ * {@link SortsClient.getPreorderDegree}.
14689
+ *
14690
+ * ⚠️ This route computes PROCESS-WIDE and then filters, so its classes can
14691
+ * be shaped by sorts the caller cannot see. {@link SortsClient.getQuotientOrder}
14692
+ * computes on the tenant-visible hierarchy instead, returns the same classes
14693
+ * with their degrees, and adds the partial order between them. Prefer it.
14569
14694
  *
14570
14695
  * Uses tagged serialization format.
14571
14696
  *
@@ -15746,31 +15871,42 @@ function convertFeatures(features) {
15746
15871
  }
15747
15872
 
15748
15873
  // src/normalizers/homoiconic.ts
15749
- function FeatureInputValueDtoFromFrontToApi(value) {
15874
+ function assertPresent(value, what) {
15875
+ if (value === null || value === void 0) {
15876
+ throw new ValidationError(
15877
+ `${what} is required \u2014 received ${value === null ? "null" : "undefined"}.`,
15878
+ what
15879
+ );
15880
+ }
15881
+ }
15882
+ function FeatureInputValueDtoFromFrontToApi(value, path = "feature value") {
15883
+ assertPresent(value, path);
15750
15884
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
15751
15885
  return value;
15752
15886
  }
15753
15887
  if (Array.isArray(value)) {
15754
- return value.map(FeatureInputValueDtoFromFrontToApi);
15888
+ return value.map(
15889
+ (element, index) => FeatureInputValueDtoFromFrontToApi(element, `${path}[${index}]`)
15890
+ );
15755
15891
  }
15756
15892
  if ("name" in value && "constraint" in value) {
15757
15893
  return {
15758
15894
  name: value.name,
15759
- constraint: TermInputDtoFromFrontToApi(value.constraint)
15895
+ constraint: TermInputDtoFromFrontToApi(value.constraint, `${path}.constraint`)
15760
15896
  };
15761
15897
  }
15762
15898
  if ("name" in value) {
15763
15899
  return { name: value.name };
15764
15900
  }
15765
15901
  if ("sortId" in value) {
15766
- const features = value.features ? featuresInputFromFrontToApi(value.features) : void 0;
15902
+ const features = value.features ? featuresInputFromFrontToApi(value.features, `${path}.features`) : void 0;
15767
15903
  return {
15768
15904
  sort_id: value.sortId,
15769
15905
  ...features ? { features } : {}
15770
15906
  };
15771
15907
  }
15772
15908
  if ("sortName" in value) {
15773
- const features = value.features ? featuresInputFromFrontToApi(value.features) : void 0;
15909
+ const features = value.features ? featuresInputFromFrontToApi(value.features, `${path}.features`) : void 0;
15774
15910
  return {
15775
15911
  sort_name: value.sortName,
15776
15912
  ...features ? { features } : {}
@@ -15781,14 +15917,15 @@ function FeatureInputValueDtoFromFrontToApi(value) {
15781
15917
  }
15782
15918
  return { term_id: value.termId };
15783
15919
  }
15784
- function featuresInputFromFrontToApi(features) {
15920
+ function featuresInputFromFrontToApi(features, path = "features") {
15785
15921
  const result = {};
15786
15922
  for (const [key, val] of Object.entries(features)) {
15787
- result[key] = FeatureInputValueDtoFromFrontToApi(val);
15923
+ result[key] = FeatureInputValueDtoFromFrontToApi(val, `${path}.${key}`);
15788
15924
  }
15789
15925
  return result;
15790
15926
  }
15791
- function TermInputDtoFromFrontToApi(model) {
15927
+ function TermInputDtoFromFrontToApi(model, path = "term") {
15928
+ assertPresent(model, path);
15792
15929
  if ("termId" in model) {
15793
15930
  return { term_id: model.termId };
15794
15931
  }
@@ -15803,14 +15940,14 @@ function TermInputDtoFromFrontToApi(model) {
15803
15940
  if ("sortId" in model) {
15804
15941
  const inline = {
15805
15942
  sort_id: model.sortId,
15806
- features: featuresInputFromFrontToApi(model.features)
15943
+ features: featuresInputFromFrontToApi(model.features, `${path}.features`)
15807
15944
  };
15808
15945
  if (model.binding !== void 0) inline.binding = model.binding;
15809
15946
  return inline;
15810
15947
  }
15811
15948
  const byName = {
15812
15949
  sort_name: model.sortName,
15813
- features: model.features ? featuresInputFromFrontToApi(model.features) : void 0
15950
+ features: model.features ? featuresInputFromFrontToApi(model.features, `${path}.features`) : void 0
15814
15951
  };
15815
15952
  if (model.binding !== void 0) byName.binding = model.binding;
15816
15953
  return byName;
@@ -15872,6 +16009,7 @@ function ProofDtoFromApiToFront(dto) {
15872
16009
  kind: dto.kind,
15873
16010
  goalTermId: dto.goal_term_id,
15874
16011
  ruleTermId: dto.rule_term_id,
16012
+ ruleHeadTermId: dto.rule_head_term_id,
15875
16013
  goalDisplay: dto.goal_display,
15876
16014
  ruleLabel: dto.rule_label,
15877
16015
  substitution: HomoiconicSubstitutionDtoFromApiToFront(dto.substitution),
@@ -16371,7 +16509,9 @@ function RuleAggregatorDtoFromFrontToApi(model) {
16371
16509
  function AddRuleRequestFromFrontToApi(model) {
16372
16510
  return {
16373
16511
  term: TermInputDtoFromFrontToApi(model.term),
16374
- antecedents: model.antecedents?.map(TermInputDtoFromFrontToApi),
16512
+ antecedents: model.antecedents?.map(
16513
+ (t, i) => TermInputDtoFromFrontToApi(t, `antecedents[${i}]`)
16514
+ ),
16375
16515
  certainty: model.certainty,
16376
16516
  aggregator: model.aggregator ? RuleAggregatorDtoFromFrontToApi(model.aggregator) : model.aggregator
16377
16517
  };
@@ -16388,7 +16528,7 @@ function BulkAddRulesRequestFromFrontToApi(model) {
16388
16528
  }
16389
16529
  function BulkAddFactsRequestFromFrontToApi(model) {
16390
16530
  return {
16391
- facts: model.facts.map(TermInputDtoFromFrontToApi)
16531
+ facts: model.facts.map((t, i) => TermInputDtoFromFrontToApi(t, `facts[${i}]`))
16392
16532
  };
16393
16533
  }
16394
16534
  function BackwardChainRequestFromFrontToApi(model) {
@@ -16406,12 +16546,15 @@ function BackwardChainRequestFromFrontToApi(model) {
16406
16546
  }
16407
16547
  function ForwardChainRequestFromFrontToApi(model) {
16408
16548
  return {
16409
- initial_facts: model.initialFacts?.map(TermInputDtoFromFrontToApi),
16549
+ initial_facts: model.initialFacts?.map(
16550
+ (t, i) => TermInputDtoFromFrontToApi(t, `initialFacts[${i}]`)
16551
+ ),
16410
16552
  keep_derived: model.keepDerived,
16411
16553
  enable_provenance_tags: model.enableProvenanceTags,
16412
16554
  max_iterations: model.maxIterations,
16413
16555
  max_facts: model.maxFacts,
16414
- emit_provenance: model.emitProvenance
16556
+ emit_provenance: model.emitProvenance,
16557
+ timeout_ms: model.timeoutMs ?? void 0
16415
16558
  };
16416
16559
  }
16417
16560
  function FuzzyProveRequestFromFrontToApi(model) {
@@ -16428,7 +16571,7 @@ function FuzzyProveRequestFromFrontToApi(model) {
16428
16571
  }
16429
16572
  function BulkFuzzyProveRequestFromFrontToApi(model) {
16430
16573
  return {
16431
- goals: model.goals?.map(TermInputDtoFromFrontToApi),
16574
+ goals: model.goals?.map((t, i) => TermInputDtoFromFrontToApi(t, `goals[${i}]`)),
16432
16575
  goal_ids: model.goalIds ?? void 0,
16433
16576
  max_depth: model.maxDepth,
16434
16577
  min_degree: model.minDegree,
@@ -16451,7 +16594,7 @@ function BayesianPredictRequestFromFrontToApi(model) {
16451
16594
  }
16452
16595
  function CreateGoalRequestFromFrontToApi(model) {
16453
16596
  return {
16454
- clauses: model.clauses.map(TermInputDtoFromFrontToApi),
16597
+ clauses: model.clauses.map((t, i) => TermInputDtoFromFrontToApi(t, `clauses[${i}]`)),
16455
16598
  constraints: model.constraints?.map(ConstraintInputDtoFromFrontToApi),
16456
16599
  min_degree: model.minDegree ?? void 0
16457
16600
  };
@@ -16751,6 +16894,14 @@ var InferenceClient = class {
16751
16894
  *
16752
16895
  * The `timeout_ms` field on the request is a wall-clock timeout for the search.
16753
16896
  * When it fires, the backend returns whatever solutions have been found so far.
16897
+ *
16898
+ * To join a proof node to the thing it proves, read `proof.goalTermId` — the
16899
+ * conclusion's term ID, which `client.terms.getTerm()` and
16900
+ * `client.query.findBySort()` both answer for. It is OPTIONAL: a goal proved
16901
+ * without a preceding forward chain materialises no conclusion, so the node
16902
+ * carries no id and `proof.goalDisplay` renders it instead. Do not read
16903
+ * `proof.ruleHeadTermId` as a fetchable id — it names the rule's instantiated
16904
+ * head, which the term routes refuse.
16754
16905
  */
16755
16906
  async backwardChain(request, requestOptions) {
16756
16907
  const wireRequest = {
@@ -16854,13 +17005,37 @@ var InferenceClient = class {
16854
17005
  /**
16855
17006
  * Run negation-as-failure (NAF) proof search.
16856
17007
  *
16857
- * @param request - NAF prove request.
17008
+ * @param request - NAF prove request. Each literal's `term` takes a
17009
+ * {@link psi} term or a wire {@link TermInputDto}, like every other
17010
+ * term-carrying method.
16858
17011
  * @returns NAF proof result.
16859
17012
  *
17013
+ * @remarks
17014
+ * Uses the untagged (homoiconic) serialization format: a literal's features
17015
+ * are plain scalars and `"?Var"` strings.
17016
+ *
17017
+ * @example
17018
+ * ```typescript
17019
+ * const result = await client.inference.nafProve({
17020
+ * literals: [
17021
+ * { term: psi('employee', { name: '?Name' }) },
17022
+ * { term: psi('senior_engineer', { name: '?Name' }), negated: true },
17023
+ * ],
17024
+ * maxSolutions: 10,
17025
+ * });
17026
+ * ```
17027
+ *
16860
17028
  * @see proveWithNegation — friendlier alias for this method.
16861
17029
  */
16862
17030
  async nafProve(request, requestOptions) {
16863
- const response = await this.api.nafProve(NafProveRequestFromFrontToApi(request), toRequestParams(requestOptions));
17031
+ const wireRequest = {
17032
+ ...request,
17033
+ literals: request.literals?.map((literal) => ({
17034
+ ...literal,
17035
+ term: convertTermArg(literal.term)
17036
+ }))
17037
+ };
17038
+ const response = await this.api.nafProve(NafProveRequestFromFrontToApi(wireRequest), toRequestParams(requestOptions));
16864
17039
  return NafProveResponseFromApiToFront(response.data);
16865
17040
  }
16866
17041
  /**
@@ -23787,7 +23962,7 @@ function EntailmentRequestFromFrontToApi(model) {
23787
23962
  return {
23788
23963
  antecedent: TermInputDtoFromFrontToApi(model.antecedent),
23789
23964
  consequent: TermInputDtoFromFrontToApi(model.consequent),
23790
- facts: model.facts?.map(TermInputDtoFromFrontToApi)
23965
+ facts: model.facts?.map((t, i) => TermInputDtoFromFrontToApi(t, `facts[${i}]`))
23791
23966
  };
23792
23967
  }
23793
23968
  function EntailmentResponseFromApiToFront(dto) {
@@ -23801,7 +23976,7 @@ function DisentailmentRequestFromFrontToApi(model) {
23801
23976
  return {
23802
23977
  antecedent: TermInputDtoFromFrontToApi(model.antecedent),
23803
23978
  consequent: TermInputDtoFromFrontToApi(model.consequent),
23804
- facts: model.facts?.map(TermInputDtoFromFrontToApi)
23979
+ facts: model.facts?.map((t, i) => TermInputDtoFromFrontToApi(t, `facts[${i}]`))
23805
23980
  };
23806
23981
  }
23807
23982
  function DisentailmentResponseFromApiToFront(dto) {
@@ -25392,12 +25567,25 @@ function SourceDetailResponseFromApiToFront(dto) {
25392
25567
  supportedModes: dto.supported_modes
25393
25568
  };
25394
25569
  }
25570
+ function ConfigFieldDtoFromApiToFront(dto) {
25571
+ return {
25572
+ // `name` is the factory's own JSON key and goes back on the wire
25573
+ // verbatim — never camelCased, in either direction (#14).
25574
+ name: dto.name,
25575
+ type: dto.type,
25576
+ required: dto.required
25577
+ };
25578
+ }
25395
25579
  function SourceTypeDtoFromApiToFront(dto) {
25396
25580
  return {
25397
25581
  sourceType: dto.source_type,
25398
25582
  aliases: dto.aliases,
25399
25583
  available: dto.available,
25400
- supportedModes: dto.supported_modes
25584
+ supportedModes: dto.supported_modes,
25585
+ // Defaulted rather than left absent: the field is required on the current
25586
+ // engine, and an empty list reads correctly on an older one — a form built
25587
+ // from no fields is visibly empty, where an absent list is a crash.
25588
+ configFields: (dto.config_fields ?? []).map(ConfigFieldDtoFromApiToFront)
25401
25589
  };
25402
25590
  }
25403
25591
  function ListSourceTypesResponseFromApiToFront(dto) {
@@ -25417,6 +25605,7 @@ function DiscoverableTypeDtoFromApiToFront(dto) {
25417
25605
  return {
25418
25606
  comment: dto.comment,
25419
25607
  estimatedRows: dto.estimated_rows,
25608
+ columnCount: dto.column_count,
25420
25609
  name: dto.name
25421
25610
  };
25422
25611
  }
@@ -25427,6 +25616,15 @@ function ListTablesResponseFromApiToFront(dto) {
25427
25616
  total: dto.total
25428
25617
  };
25429
25618
  }
25619
+ function KeyProvenanceDtoFromApiToFront(dto) {
25620
+ return {
25621
+ source: dto.source,
25622
+ chosenBy: dto.chosen_by,
25623
+ explanation: dto.explanation,
25624
+ guaranteedUnique: dto.guaranteed_unique,
25625
+ durability: dto.durability
25626
+ };
25627
+ }
25430
25628
  function DiscoveredFeatureDtoFromApiToFront(dto) {
25431
25629
  return {
25432
25630
  constraints: dto.constraints,
@@ -25434,10 +25632,16 @@ function DiscoveredFeatureDtoFromApiToFront(dto) {
25434
25632
  isIdentifier: dto.is_identifier,
25435
25633
  isRelation: dto.is_relation,
25436
25634
  name: dto.name,
25635
+ // The engine's own OSFQL-readable spelling. Defaulted to `name`, which it
25636
+ // is identical to, so an older engine that predates it stays usable.
25637
+ featureName: dto.feature_name ?? dto.name,
25437
25638
  relationTarget: dto.relation_target,
25438
25639
  required: dto.required,
25439
25640
  sourceName: dto.source_name,
25440
- typeHint: dto.type_hint
25641
+ typeHint: dto.type_hint,
25642
+ // The difference between a declared primary key and a guess. Carried
25643
+ // through so a mapping wizard can present the guess as a guess (#14).
25644
+ identifierProvenance: dto.identifier_provenance ? KeyProvenanceDtoFromApiToFront(dto.identifier_provenance) : dto.identifier_provenance
25441
25645
  };
25442
25646
  }
25443
25647
  function DiscoveredSortDtoFromApiToFront(dto) {
@@ -31287,7 +31491,9 @@ var OptimizeClient = class {
31287
31491
  }
31288
31492
  const ruleResponse = await this.inferenceApi.addRule({
31289
31493
  term: TermInputDtoFromFrontToApi(compiled.solutionTerm),
31290
- antecedents: compiled.antecedents.map(TermInputDtoFromFrontToApi),
31494
+ antecedents: compiled.antecedents.map(
31495
+ (t, i) => TermInputDtoFromFrontToApi(t, `antecedents[${i}]`)
31496
+ ),
31291
31497
  certainty: 1
31292
31498
  }, toRequestParams(requestOptions));
31293
31499
  ruleTermId = extractRuleTermId(ruleResponse.data);
@@ -31966,6 +32172,7 @@ function OsfqlResponseFromApiToFront(dto) {
31966
32172
  bindings,
31967
32173
  fetched,
31968
32174
  producedTermIds: dto.produced_term_ids,
32175
+ updatedTermIds: dto.updated_term_ids,
31969
32176
  definedSortIds: dto.defined_sort_ids,
31970
32177
  diagnostics: dto.diagnostics,
31971
32178
  statementCount: dto.statement_count
@@ -32037,11 +32244,25 @@ function OsfqlPreviewAffectedFromApiToFront(dto) {
32037
32244
  affected.exact = dto.exact;
32038
32245
  }
32039
32246
  if (dto.sample_rows !== void 0) {
32040
- const rows = OsfqlBindingsFromApiToFront(dto.sample_rows);
32041
- if (rows === void 0) {
32042
- return void 0;
32247
+ const rows = [];
32248
+ let unparsed = 0;
32249
+ const wireRows = dto.sample_rows;
32250
+ if (Array.isArray(wireRows)) {
32251
+ for (const row of wireRows) {
32252
+ const parsed = OsfqlBindingFromApiToFront(row);
32253
+ if (parsed === void 0) {
32254
+ unparsed += 1;
32255
+ } else {
32256
+ rows.push(parsed);
32257
+ }
32258
+ }
32259
+ } else {
32260
+ unparsed = 1;
32043
32261
  }
32044
32262
  affected.sampleRows = rows;
32263
+ if (unparsed > 0) {
32264
+ affected.unparsedSampleRows = unparsed;
32265
+ }
32045
32266
  }
32046
32267
  if (dto.by_sort !== void 0) {
32047
32268
  affected.bySort = dto.by_sort.map(OsfqlPreviewSortCountFromApiToFront);
@@ -32062,34 +32283,15 @@ function OsfqlPreviewStatementFromApiToFront(dto) {
32062
32283
  source: dto.source
32063
32284
  };
32064
32285
  if (dto.affected !== void 0) {
32065
- const affected = OsfqlPreviewAffectedFromApiToFront(dto.affected);
32066
- if (affected === void 0) {
32067
- return void 0;
32068
- }
32069
- statement.affected = affected;
32286
+ statement.affected = OsfqlPreviewAffectedFromApiToFront(dto.affected);
32070
32287
  }
32071
32288
  if (dto.nested !== void 0) {
32072
- const nested = [];
32073
- for (const child of dto.nested) {
32074
- const parsed = OsfqlPreviewStatementFromApiToFront(child);
32075
- if (parsed === void 0) {
32076
- return void 0;
32077
- }
32078
- nested.push(parsed);
32079
- }
32080
- statement.nested = nested;
32289
+ statement.nested = dto.nested.map(OsfqlPreviewStatementFromApiToFront);
32081
32290
  }
32082
32291
  return statement;
32083
32292
  }
32084
32293
  function OsfqlPreviewResponseFromApiToFront(dto) {
32085
- const statements = [];
32086
- for (const entry of dto.statements) {
32087
- const parsed = OsfqlPreviewStatementFromApiToFront(entry);
32088
- if (parsed === void 0) {
32089
- return void 0;
32090
- }
32091
- statements.push(parsed);
32092
- }
32294
+ const statements = dto.statements.map(OsfqlPreviewStatementFromApiToFront);
32093
32295
  const response = { mutates: dto.mutates, statements };
32094
32296
  if (dto.atomic_refusal !== void 0) {
32095
32297
  response.atomicRefusal = dto.atomic_refusal ? OsfqlAtomicRefusalFromApiToFront(dto.atomic_refusal) : dto.atomic_refusal;
@@ -32185,8 +32387,6 @@ var OsfqlClient = class {
32185
32387
  * atomic refusal when there is one.
32186
32388
  * @throws {ApiError} If the program does not parse (HTTP 400, `OsfqlErrorResponse`),
32187
32389
  * or the request otherwise fails.
32188
- * @throws {ReasoningLayerError} If a sample row carries a value that does not satisfy
32189
- * the published {@link OsfqlValue} contract.
32190
32390
  *
32191
32391
  * @remarks
32192
32392
  * **Why call this instead of classifying the program client-side.** Three
@@ -32221,9 +32421,16 @@ var OsfqlClient = class {
32221
32421
  * Two further measured facts. The top-level `mutates` DOES fold nested
32222
32422
  * statements now: the `IF` program above answers `true`, and so does an `IF`
32223
32423
  * whose only writing branch is the `ELSE` — the earlier defect where an `IF`
32224
- * with a writing branch answered `false` is fixed. And `affected` being absent
32225
- * is ambiguous: `RETRACT customer(tier: "bronze")` matching zero rows omits
32226
- * the field entirely rather than answering `count: 0`, exactly as a read does.
32424
+ * with a writing branch answered `false` is fixed. And `affected` is NOT
32425
+ * ambiguous: a destructive statement that matches zero rows answers
32426
+ * `{count: 0, exact: true}` rather than omitting the block. Measured
32427
+ * 2026-09-21 — `preview('RETRACT widget(name: "nobody");')` against an empty
32428
+ * `widget` answers `affected: {"count":0,"exact":true}`, while
32429
+ * `preview('MATCH widget(name: ?N);')` carries no `affected` key at all. So
32430
+ * an ABSENT block means the statement has no affected-row set; `count: 0`
32431
+ * means the rows were counted and there are none. A confirm dialog can tell
32432
+ * the two apart, which is what the block was separated for. See
32433
+ * {@link OsfqlPreviewAffected.count}.
32227
32434
  *
32228
32435
  * `statement.index` is a single counter over the whole program: an `IF` at
32229
32436
  * index 1 carries nested entries at index 2 and 3.
@@ -32264,13 +32471,7 @@ var OsfqlClient = class {
32264
32471
  OsfqlRequestFromFrontToApi({ ...options, query }),
32265
32472
  toRequestParams(requestOptions)
32266
32473
  );
32267
- const parsed = OsfqlPreviewResponseFromApiToFront(response.data);
32268
- if (parsed === void 0) {
32269
- throw new ReasoningLayerError(
32270
- "osfql/preview returned an affected sample row that does not match the published OsfqlValue contract (expected tagged values with a lowercase `type` discriminator)"
32271
- );
32272
- }
32273
- return parsed;
32474
+ return OsfqlPreviewResponseFromApiToFront(response.data);
32274
32475
  }
32275
32476
  /**
32276
32477
  * Diagnose an OSFQL program for contradictions and inconsistencies.
@@ -34203,6 +34404,27 @@ function AuditPageFromApiToFront(dto) {
34203
34404
  offset: dto.offset
34204
34405
  };
34205
34406
  }
34407
+ function RiskTotalsDtoFromApiToFront(dto) {
34408
+ return {
34409
+ nSafe: dto.n_safe,
34410
+ nWarning: dto.n_warning,
34411
+ nUnsafe: dto.n_unsafe,
34412
+ nUngraded: dto.n_ungraded,
34413
+ nGradedByVerification: dto.n_graded_by_verification,
34414
+ nVerified: dto.n_verified,
34415
+ nRefuted: dto.n_refuted,
34416
+ nAbstained: dto.n_abstained,
34417
+ nFabricationDetected: dto.n_fabrication_detected,
34418
+ nFabricationClean: dto.n_fabrication_clean,
34419
+ nFabricationUnknown: dto.n_fabrication_unknown,
34420
+ nClaimsTotal: dto.n_claims_total,
34421
+ nClaimsRefuted: dto.n_claims_refuted,
34422
+ // Carried through as `null`, never folded to `0`. `null` is "not
34423
+ // measured" — the denominator is checked responses only — and `0` would
34424
+ // report a clean fabrication rate for a ledger nobody checked (#11).
34425
+ fabricationRate: dto.fabrication_rate
34426
+ };
34427
+ }
34206
34428
  function SummaryResponseFromApiToFront(dto) {
34207
34429
  const out = {
34208
34430
  nRecords: dto.n_records,
@@ -34212,6 +34434,12 @@ function SummaryResponseFromApiToFront(dto) {
34212
34434
  };
34213
34435
  if (dto.ts_first) out.tsFirst = dto.ts_first;
34214
34436
  if (dto.ts_last) out.tsLast = dto.ts_last;
34437
+ if (dto.risk !== void 0 && dto.risk !== null) {
34438
+ out.risk = RiskTotalsDtoFromApiToFront(dto.risk);
34439
+ }
34440
+ if (dto.n_oversight_decisions !== void 0 && dto.n_oversight_decisions !== null) {
34441
+ out.nOversightDecisions = dto.n_oversight_decisions;
34442
+ }
34215
34443
  return out;
34216
34444
  }
34217
34445
  function VerifyResponseFromApiToFront(dto) {
@@ -36438,6 +36666,23 @@ function ImportOwlResponseFromApiToFront(dto) {
36438
36666
  sortsCreated: dto.sorts_created
36439
36667
  };
36440
36668
  }
36669
+ function ExecuteTranspiledRequestFromFrontToApi(model) {
36670
+ return {
36671
+ osfql: model.osfql
36672
+ };
36673
+ }
36674
+ function ExecuteTranspiledResponseFromApiToFront(dto) {
36675
+ return {
36676
+ // `rows` are the source's OWN records; their keys are its column names and
36677
+ // are data, carried verbatim. The generated type erases them to `any[]`,
36678
+ // so the shipped surface narrows them to one object per row (#12).
36679
+ rows: dto.rows,
36680
+ rowCount: dto.row_count,
36681
+ query: dto.query,
36682
+ dialect: dto.dialect,
36683
+ sourceId: dto.source_id
36684
+ };
36685
+ }
36441
36686
  function TranspileRequestFromFrontToApi(model) {
36442
36687
  return {
36443
36688
  dialect: model.dialect,
@@ -36696,6 +36941,46 @@ var OntologyBridgeClient = class {
36696
36941
  );
36697
36942
  return TranspileResponseFromApiToFront(response.data);
36698
36943
  }
36944
+ /**
36945
+ * Transpile an OSFQL query AND run it on the source it is bound to.
36946
+ *
36947
+ * @param request - The OSFQL query. No dialect: the binding names the
36948
+ * source, and the source's type decides the dialect.
36949
+ * @param requestOptions - Per-call request options.
36950
+ * @returns The rows the source returned, the native query that fetched them,
36951
+ * its dialect, and the source id.
36952
+ * @throws {ApiError} If the request fails.
36953
+ *
36954
+ * @remarks
36955
+ * Sent to `POST /api/v1/ontology/query`. The response uses the snake_case
36956
+ * wire format (`row_count`, `source_id`); this client normalizes at the
36957
+ * boundary. `rows` are the source's own records, one object per row, and
36958
+ * their KEYS are the source's column names — data, carried verbatim.
36959
+ *
36960
+ * **This is the second half of {@link OntologyBridgeClient.transpile}, and
36961
+ * for a `transpile`-mode source it is the only route that answers.** Such a
36962
+ * source deliberately ingests nothing: its bound sorts are live views over
36963
+ * the operator's own database, so every KB query over them reports no
36964
+ * results. `transpile` emits the native query and stops; this runs it.
36965
+ *
36966
+ * @example
36967
+ * ```typescript
36968
+ * const result = await client.ontologyBridge.executeTranspiled({
36969
+ * osfql: 'MATCH person(name: ?N);',
36970
+ * });
36971
+ * console.log(result.dialect); // "postgres"
36972
+ * console.log(result.query); // the SQL that ran
36973
+ * console.log(result.rowCount); // 12
36974
+ * for (const row of result.rows) console.log(row['name']);
36975
+ * ```
36976
+ */
36977
+ async executeTranspiled(request, requestOptions) {
36978
+ const response = await this.api.executeTranspiled(
36979
+ ExecuteTranspiledRequestFromFrontToApi(request),
36980
+ toRequestParams(requestOptions)
36981
+ );
36982
+ return ExecuteTranspiledResponseFromApiToFront(response.data);
36983
+ }
36699
36984
  /**
36700
36985
  * Remove a sort-table binding.
36701
36986
  *
@@ -38941,12 +39226,46 @@ function ConformanceRequestFromFrontToApi(model) {
38941
39226
  ontology: model.ontology
38942
39227
  };
38943
39228
  }
39229
+ function stringAt(source, key) {
39230
+ const value = Reflect.get(source, key);
39231
+ return typeof value === "string" ? value : void 0;
39232
+ }
39233
+ function suggestedFixesAt(source) {
39234
+ const value = Reflect.get(source, "suggested_fixes");
39235
+ if (!Array.isArray(value)) return void 0;
39236
+ const fixes = [];
39237
+ for (const entry of value) {
39238
+ if (typeof entry === "object" && entry !== null && !Array.isArray(entry)) {
39239
+ fixes.push({ ...entry });
39240
+ }
39241
+ }
39242
+ return fixes;
39243
+ }
39244
+ function ConformanceViolationDtoFromApiToFront(dto) {
39245
+ if (typeof dto !== "object" || dto === null) return {};
39246
+ const violation = {};
39247
+ const subject = stringAt(dto, "subject");
39248
+ if (subject !== void 0) violation.subject = subject;
39249
+ const object = stringAt(dto, "object");
39250
+ if (object !== void 0) violation.object = object;
39251
+ const feature = stringAt(dto, "feature");
39252
+ if (feature !== void 0) violation.feature = feature;
39253
+ const subjectSort = stringAt(dto, "subject_sort");
39254
+ if (subjectSort !== void 0) violation.subjectSort = subjectSort;
39255
+ const objectSort = stringAt(dto, "object_sort");
39256
+ if (objectSort !== void 0) violation.objectSort = objectSort;
39257
+ const kind = stringAt(dto, "kind");
39258
+ if (kind !== void 0) violation.kind = kind;
39259
+ const suggestedFixes = suggestedFixesAt(dto);
39260
+ if (suggestedFixes !== void 0) violation.suggestedFixes = suggestedFixes;
39261
+ return violation;
39262
+ }
38944
39263
  function ConformanceCheckResponseFromApiToFront(dto) {
38945
39264
  return {
38946
39265
  conformant: dto.conformant,
38947
39266
  totalViolations: dto.total_violations,
38948
39267
  byKind: ViolationCountsDtoFromApiToFront(dto.by_kind),
38949
- violations: dto.violations.map(ViolationDtoFromApiToFront)
39268
+ violations: dto.violations.map(ConformanceViolationDtoFromApiToFront)
38950
39269
  };
38951
39270
  }
38952
39271
  function ConformanceRepairResponseFromApiToFront(dto) {
@@ -38984,6 +39303,11 @@ var ConformanceClient = class {
38984
39303
  * The ontology travels as an opaque string in the declared RDF format; no
38985
39304
  * value serialization is applied to it.
38986
39305
  *
39306
+ * Each violation is a {@link ConformanceViolationDto}, NOT the shared
39307
+ * `ViolationDto`. The two share no key, and the route's OpenAPI slot holds
39308
+ * the wrong one — which is why the fields are narrowed from the wire in the
39309
+ * normalizer and why every one of them is optional (#13).
39310
+ *
38987
39311
  * @example
38988
39312
  * ```typescript
38989
39313
  * const result = await client.conformance.check({
@@ -38995,7 +39319,8 @@ var ConformanceClient = class {
38995
39319
  * console.log(result.totalViolations, 'violations');
38996
39320
  * console.log(result.byKind.v1Inversion, 'subject/object inversions');
38997
39321
  * for (const violation of result.violations) {
38998
- * console.log(violation.severity, violation.description);
39322
+ * console.log(violation.kind, violation.subject, violation.feature, violation.object);
39323
+ * console.log(violation.suggestedFixes); // [{ action: 'swap' }]
38999
39324
  * }
39000
39325
  * }
39001
39326
  * ```