factbag 0.0.0-alpha.1 → 0.0.0-alpha.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # factbag
2
2
 
3
+ ## 0.0.0-alpha.2
4
+
5
+ ### Major Changes
6
+
7
+ - e212a94: Remove bigint support from predicate schemas, numeric expressions, aggregates, and numeric proof ranges. Numeric values now use JavaScript number exclusively, and an empty sum returns 0. The DSL rejects BigInt schemas and bigint operands rather than coercing them.
8
+
9
+ Migrate BigInt fields to Number and bigint literals to numbers. Division now uses number semantics (fractional results rather than bigint truncation); values outside the safe integer range can lose precision. Date support is unchanged.
10
+
11
+ ### Patch Changes
12
+
13
+ - bc114ed: Add npm alpha version, CI status, and license badges to the README.
14
+ - a3583ab: Publish the full project README on npm, including the document-to-conclusion walkthrough.
15
+
3
16
  ## 0.0.0-alpha.1
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -1,44 +1,306 @@
1
1
  # Factbag
2
2
 
3
- A TypeScript library for combining evidence with explicit rules in a knowledgebase.
4
- Facts can be true, false, conflicted, or unknown. Query results keep track of the
5
- sources and rules behind a conclusion.
3
+ [![npm alpha](https://img.shields.io/npm/v/factbag/alpha)](https://www.npmjs.com/package/factbag)
4
+ [![CI](https://img.shields.io/github/actions/workflow/status/factbag/factbag/ci.yml?branch=main&event=push&label=CI)](https://github.com/factbag/factbag/actions/workflows/ci.yml?query=branch%3Amain)
5
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/factbag/factbag/blob/main/LICENSE)
6
6
 
7
- Factbag includes a document corpus, permission controls, and an integration with
8
- TypeSafe AI’s Jev model.
7
+ <img src="https://raw.githubusercontent.com/factbag/factbag/main/docs/assets/factbag.png" alt="An open canvas bag" align="right" width="180" />
9
8
 
10
- Read [Introducing Factbag](https://daniel.du-pless.is/introducing-factbag) for how and
11
- why to use it, or [Jev and the Art of Truth Maintenance](https://daniel.du-pless.is/jev-and-the-art-of-truth-maintenance)
12
- for the thinking behind it.
9
+ Turn documents into evidence and conclusions with [Jev](https://typesafe.ai/) and TypeScript.
10
+
11
+ Factbag uses TypeSafe AI’s Jev model to read passages and assess specific claims.
12
+ Those assessments become evidence in a knowledge base, alongside facts from your
13
+ application. You write the rules that connect them and query what follows.
14
+
15
+ Each piece of documentary evidence points back to its source passage. Conclusions
16
+ can be **true**, **false**, **conflicted**, or **unknown**. When documents disagree,
17
+ you can inspect the conflicting evidence, correct a particular fact, and query
18
+ the rules again.
19
+
20
+ > **Important:** Factbag is an early alpha release and still pretty rough - APIs will definitely change.
21
+
22
+ ## How it works
23
+
24
+ 1. **Define the claims** you want Jev to assess, with descriptions of what counts
25
+ as evidence.
26
+ 2. **Add documents** to a corpus and choose which predicates Jev may assert or deny.
27
+ 3. **Execute classification jobs.** Jev assesses whether each passage supports,
28
+ contradicts, or is irrelevant to each candidate claim.
29
+ 4. **Query the result.** Factbag combines admitted evidence with your existing
30
+ facts and rules, retaining the sources behind the conclusion.
13
31
 
14
32
  ## Install
15
33
 
16
34
  ```sh
17
- npm install factbag
35
+ npm install factbag@alpha @typesafe-ai/sdk@^0.6.0
18
36
  ```
19
37
 
20
38
  ESM only. Requires Node 22.18+ on the 22.x line, or Node 24 or 26.
21
- This is an early prerelease; APIs may change.
22
39
 
23
- ## Example
40
+ Set your TypeSafe API key in the environment:
41
+
42
+ ```sh
43
+ export TYPESAFE_API_KEY="your-api-key"
44
+ ```
45
+
46
+ ## From a document to a conclusion
47
+
48
+ ### 1. Decide on your ruleset
49
+
50
+ Start with the criteria your application needs to apply. These might be your own
51
+ business rules or requirements set out in legislation or regulations.
52
+
53
+ Here, we're translating the individual-income route in the SEC's
54
+ [accredited-investor definition, 17 CFR § 230.501(a)(6)](<https://www.ecfr.gov/current/title-17/chapter-II/part-230/section-230.501#p-230.501(a)(6)>),
55
+ into three conditions:
56
+
57
+ - Individual income exceeded $200,000 in the first of the two most recent years.
58
+ - Individual income exceeded $200,000 in the second year.
59
+ - The person reasonably expects to reach the same income level in the current year.
60
+
61
+ ### 2. Define the claims
62
+
63
+ Declare the facts we'll combine, and describe the claims Jev will assess.
24
64
 
25
65
  ```ts
26
- import { predicate, variable } from "factbag/dsl";
66
+ import { and, enumeration, predicate, variable } from "factbag/dsl";
27
67
  import { KnowledgeBase } from "factbag/kb";
28
68
  import { query } from "factbag/query";
69
+ import { Corpus } from "factbag/corpus";
70
+ import { guard } from "factbag/guards";
71
+ import { classifier } from "factbag/jev/classifier";
72
+
73
+ class Person {
74
+ constructor(
75
+ readonly id: string,
76
+ readonly name: string,
77
+ ) {}
78
+ }
79
+
80
+ const hasIncomeOver200k = predicate(
81
+ "has_income_over_200k",
82
+ { person: Person, year: enumeration("2024", "2025") },
83
+ {
84
+ description:
85
+ "The person's total individual annual income exceeded USD 200,000 in the " +
86
+ "specified year. Joint household income and forecasts do not count.",
87
+ },
88
+ );
89
+
90
+ const expectsSameIncome = predicate(
91
+ "expects_same_income",
92
+ { person: Person },
93
+ {
94
+ description:
95
+ "The person reasonably expects their individual income in 2026 to remain " +
96
+ "at least at the level earned in each of 2024 and 2025.",
97
+ },
98
+ );
99
+
100
+ const isQualifiedInvestor = predicate("is_qualified_investor", {
101
+ person: Person,
102
+ });
103
+ const hasVerifiedIdentity = predicate("has_verified_identity", {
104
+ person: Person,
105
+ });
106
+ const isReadyForReview = predicate("is_ready_for_review", { person: Person });
107
+ ```
29
108
 
30
- const emailVerified = predicate("email_verified", { user: String });
31
- const canSignIn = predicate("can_sign_in", { user: String });
32
- const user = variable("user");
109
+ ### 3. Encode the ruleset
110
+
111
+ All three income conditions must hold for this route to qualification. Our
112
+ application also requires verified identity before sending an investor for review.
113
+
114
+ ```ts
115
+ const person = variable("person");
33
116
  const kb = new KnowledgeBase();
34
117
 
35
- kb.rule(canSignIn({ user })).if(emailVerified({ user }));
36
- kb.asserts(emailVerified({ user: "alice" }), { source: "accounts" });
118
+ kb.rule(isQualifiedInvestor({ person })).if(
119
+ and(
120
+ hasIncomeOver200k({ person, year: "2024" }),
121
+ hasIncomeOver200k({ person, year: "2025" }),
122
+ expectsSameIncome({ person }),
123
+ ),
124
+ );
37
125
 
38
- query(kb, canSignIn({ user: "alice" })).value; // "true"
126
+ kb.rule(isReadyForReview({ person })).if(
127
+ and(isQualifiedInvestor({ person }), hasVerifiedIdentity({ person })),
128
+ );
39
129
  ```
40
130
 
41
- For Jev, also install `@typesafe-ai/sdk@^0.6.0`. Other parts of Factbag do not
42
- require it. Live Jev requests need a TypeSafe API key.
131
+ ### 4. Import a known fact and add documents
132
+
133
+ Identity verification comes directly from our onboarding system. Income evidence
134
+ goes into the corpus for Jev to assess.
135
+
136
+ ```ts
137
+ const alice = new Person("alice", "Alice");
138
+ kb.entities.add(alice);
139
+
140
+ kb.asserts(hasVerifiedIdentity({ person: alice }), {
141
+ source: "onboarding:alice",
142
+ });
143
+
144
+ const corpus = new Corpus();
145
+
146
+ // Tax returns provide evidence of past annual income.
147
+ corpus.add({ documentId: "alice-2024-federal-tax-return", text: "…" });
148
+ corpus.add({ documentId: "alice-2025-federal-tax-return", text: "…" });
149
+
150
+ // An employer letter supports the expectation of continued income.
151
+ corpus.add({
152
+ documentId: "alice-2026-employment-confirmation-letter",
153
+ text: "…",
154
+ });
155
+ ```
156
+
157
+ ### 5. Set Jev's permissions
158
+
159
+ The guard starts with no permissions. Permit Jev to assert or deny the two income
160
+ predicates.
161
+
162
+ ```ts
163
+ const incomeEvidence = guard(kb)
164
+ .permit("assert", hasIncomeOver200k)
165
+ .permit("deny", hasIncomeOver200k)
166
+ .permit("assert", expectsSameIncome)
167
+ .permit("deny", expectsSameIncome);
168
+ ```
169
+
170
+ ### 6. Ask Jev to assess the income evidence
171
+
172
+ ```ts
173
+ const run = classifier(corpus, incomeEvidence);
174
+
175
+ for (const job of run) {
176
+ await job.execute(); // Makes a live Jev request.
177
+ }
178
+ ```
179
+
180
+ ### 7. Query the conclusion
181
+
182
+ The output knowledge base combines the imported fact and rules with the admitted
183
+ evidence.
184
+
185
+ ```ts
186
+ const qualification = query(run.kb, isQualifiedInvestor({ person: alice }));
187
+ const reviewReadiness = query(run.kb, isReadyForReview({ person: alice }));
188
+
189
+ // Both are "true" if Jev admits supporting evidence for all three income claims.
190
+ qualification.value; // "true"
191
+ reviewReadiness.value; // "true"
192
+ ```
193
+
194
+ Results can be `true`, `false`, `conflicted`, or
195
+ `unknown`, depending on the evidence admitted.
196
+
197
+ ### 8. Inspect the proof tree
198
+
199
+ Use `explainJudgment()` to follow a conclusion through its rule to the supporting
200
+ evidence and source passages.
201
+
202
+ ```ts
203
+ import { explainJudgment } from "factbag/explain";
204
+
205
+ const proof = explainJudgment(qualification);
206
+ JSON.stringify(proof, null, 2); // JSON shown below.
207
+ ```
208
+
209
+ An abbreviated example with supporting evidence for all three claims:
210
+
211
+ ```json
212
+ {
213
+ "claim": "is_qualified_investor(alice) = true",
214
+ "because": "is_qualified_investor(person) if and(...), with person = alice",
215
+ "given": [
216
+ {
217
+ "claim": "and(...) = true",
218
+ "given": [
219
+ {
220
+ "claim": "has_income_over_200k(alice, 2024) = true",
221
+ "because": {
222
+ "claim": "has_income_over_200k(alice, 2024) = true",
223
+ "source": "alice-2024-federal-tax-return#0-1"
224
+ }
225
+ },
226
+ {
227
+ "claim": "has_income_over_200k(alice, 2025) = true",
228
+ "because": {
229
+ "claim": "has_income_over_200k(alice, 2025) = true",
230
+ "source": "alice-2025-federal-tax-return#0-1"
231
+ }
232
+ },
233
+ {
234
+ "claim": "expects_same_income(alice) = true",
235
+ "because": {
236
+ "claim": "expects_same_income(alice) = true",
237
+ "source": "alice-2026-employment-confirmation-letter#0-1"
238
+ }
239
+ }
240
+ ]
241
+ }
242
+ ]
243
+ }
244
+ ```
245
+
246
+ Source IDs identify document passages; their ranges depend on the document text.
247
+ Use `corpus.resolve(source)` to retrieve the passage behind a piece of evidence.
248
+
249
+ ```ts
250
+ const source = "alice-2024-federal-tax-return#0-1";
251
+ corpus.resolve(source); // Object shown below.
252
+ ```
253
+
254
+ The result includes the exact passage and its containing document:
255
+
256
+ ```json
257
+ {
258
+ "chunk": {
259
+ "id": "alice-2024-federal-tax-return#0-1",
260
+ "documentId": "alice-2024-federal-tax-return",
261
+ "range": { "start": 0, "end": 1 },
262
+ "text": "…"
263
+ },
264
+ "document": {
265
+ "documentId": "alice-2024-federal-tax-return",
266
+ "text": "…",
267
+ "metadata": {}
268
+ }
269
+ }
270
+ ```
271
+
272
+ ### 9. Review conflicting evidence (optional)
273
+
274
+ Suppose an original tax return puts Alice below the income threshold, but an
275
+ amended return puts her above it. Jev admits both accounts, leaving qualification
276
+ `conflicted`. In this example, the other income conditions have supporting evidence.
277
+
278
+ You could pass the conflicting evidence to a human for review, or ask Jev to
279
+ compare the sources using the `resolver`.
280
+
281
+ Evidence judged superseded is retracted from the resolver's output
282
+ knowledge base.
283
+
284
+ ```ts
285
+ import { resolver } from "factbag/jev/resolve";
286
+
287
+ query(run.kb, isQualifiedInvestor({ person: alice })).value; // "conflicted"
288
+
289
+ const review = resolver(corpus, run.kb);
290
+
291
+ for (const job of review) {
292
+ await job.execute();
293
+ }
294
+
295
+ // If the resolver retracts the superseded return's denial:
296
+ query(review.kb, isQualifiedInvestor({ person: alice })).value; // "true"
297
+ ```
298
+
299
+ ## Go further
300
+
301
+ - [Introducing Factbag](https://daniel.du-pless.is/introducing-factbag): how and why
302
+ to use it.
303
+ - [Jev and the Art of Truth Maintenance](https://daniel.du-pless.is/jev-and-the-art-of-truth-maintenance):
304
+ the thinking behind the project.
43
305
 
44
306
  Apache-2.0 licensed.
@@ -50,7 +50,7 @@ function fieldIdentityToken(value) {
50
50
  return ["entity", value.constructor.name, value.id];
51
51
  }
52
52
  if (typeof value === "bigint") {
53
- return ["bigint", value.toString()];
53
+ throw new TypeError("bigint is not supported; use number values");
54
54
  }
55
55
  return value;
56
56
  }
@@ -0,0 +1,2 @@
1
+ /** Reject removed scalar types before building rules or atoms. */
2
+ export declare function assertSupportedValue(value: unknown): void;
@@ -1,11 +1,18 @@
1
1
  import {
2
2
  assertAtomLike,
3
3
  assertGroundAtom
4
- } from "../chunk-4IP7CXNV.js";
4
+ } from "../chunk-5J3WGMN2.js";
5
5
  import {
6
6
  Var
7
7
  } from "../chunk-UVOCOH2I.js";
8
8
 
9
+ // src/dsl/assert-supported-value.ts
10
+ function assertSupportedValue(value) {
11
+ if (typeof value === "bigint" || value === BigInt) {
12
+ throw new TypeError("bigint is not supported; use number values and Number fields");
13
+ }
14
+ }
15
+
9
16
  // src/dsl/formula.ts
10
17
  function not(atom) {
11
18
  return { sign: "-", atom };
@@ -20,6 +27,7 @@ function exists(...args) {
20
27
  return { kind: "exists", args };
21
28
  }
22
29
  function letIn(variable2, value, body) {
30
+ assertSupportedValue(value);
23
31
  return { kind: "letIn", args: [variable2, value, body] };
24
32
  }
25
33
 
@@ -31,6 +39,7 @@ function count(value) {
31
39
  return aggregateBuilder("count", value);
32
40
  }
33
41
  function aggregateBuilder(kind, value) {
42
+ assertSupportedValue(value);
34
43
  const where = (body, options) => ({
35
44
  kind,
36
45
  args: options === void 0 ? [value, body] : [value, body, options]
@@ -40,37 +49,53 @@ function aggregateBuilder(kind, value) {
40
49
 
41
50
  // src/dsl/operators/arithmetic.ts
42
51
  function add(left, right) {
52
+ assertSupportedValue(left);
53
+ assertSupportedValue(right);
43
54
  return { kind: "add", args: [left, right] };
44
55
  }
45
56
  function sub(left, right) {
57
+ assertSupportedValue(left);
58
+ assertSupportedValue(right);
46
59
  return { kind: "sub", args: [left, right] };
47
60
  }
48
61
  function mul(left, right) {
62
+ assertSupportedValue(left);
63
+ assertSupportedValue(right);
49
64
  return { kind: "mul", args: [left, right] };
50
65
  }
51
66
  function div(left, right) {
67
+ assertSupportedValue(left);
68
+ assertSupportedValue(right);
52
69
  return { kind: "div", args: [left, right] };
53
70
  }
54
71
 
55
72
  // src/dsl/operators/comparison.ts
56
73
  function gt(left, right) {
74
+ assertSupportedValue(left);
75
+ assertSupportedValue(right);
57
76
  return { kind: "gt", args: [left, right] };
58
77
  }
59
78
  function gte(left, right) {
79
+ assertSupportedValue(left);
80
+ assertSupportedValue(right);
60
81
  return { kind: "gte", args: [left, right] };
61
82
  }
62
83
  function lte(left, right) {
84
+ assertSupportedValue(left);
85
+ assertSupportedValue(right);
63
86
  return { kind: "lte", args: [left, right] };
64
87
  }
65
88
 
66
89
  // src/dsl/predicate.ts
67
90
  function makePredicate(name, params, world, metadata) {
91
+ Object.values(params).forEach(assertSupportedValue);
68
92
  const holder = {};
69
93
  const construct = (fields) => {
70
94
  const current = holder.current;
71
95
  if (current === void 0) {
72
96
  throw new Error(`Predicate ${name} was called before initialization`);
73
97
  }
98
+ Object.values(fields).forEach(assertSupportedValue);
74
99
  return { predicate: current, fields };
75
100
  };
76
101
  const declared = construct;
@@ -21,7 +21,7 @@ import type { AggregateBuilder, NumericTerm, Term } from "../types.js";
21
21
  * );
22
22
  * ```
23
23
  */
24
- export declare function sum(value: NumericTerm): AggregateBuilder<number | bigint>;
24
+ export declare function sum(value: NumericTerm): AggregateBuilder;
25
25
  /**
26
26
  * Count the rows matched by the aggregate body. Runs through the same
27
27
  * aggregate machinery as {@link sum}, but thresholds the number of matching
@@ -1,6 +1,6 @@
1
1
  import type { DateTerm, Formula, NumericTerm } from "../types.js";
2
2
  /** Add two numeric terms, producing a numeric rule-body expression. */
3
- export declare function add(left: NumericTerm, right: NumericTerm): Formula<number | bigint, readonly [NumericTerm, NumericTerm]>;
3
+ export declare function add(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
4
4
  /**
5
5
  * Subtract a numeric duration in milliseconds from a date term, producing a
6
6
  * date expression.
@@ -20,8 +20,8 @@ export declare function add(left: NumericTerm, right: NumericTerm): Formula<numb
20
20
  */
21
21
  export declare function sub(left: DateTerm, right: NumericTerm): Formula<Date, readonly [DateTerm, NumericTerm]>;
22
22
  /** Subtract one numeric term from another. */
23
- export declare function sub(left: NumericTerm, right: NumericTerm): Formula<number | bigint, readonly [NumericTerm, NumericTerm]>;
23
+ export declare function sub(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
24
24
  /** Multiply two numeric terms, producing a numeric rule-body expression. */
25
- export declare function mul(left: NumericTerm, right: NumericTerm): Formula<number | bigint, readonly [NumericTerm, NumericTerm]>;
25
+ export declare function mul(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
26
26
  /** Divide one numeric term by another, producing a numeric rule-body expression. */
27
- export declare function div(left: NumericTerm, right: NumericTerm): Formula<number | bigint, readonly [NumericTerm, NumericTerm]>;
27
+ export declare function div(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
@@ -7,7 +7,7 @@ export type { Entity };
7
7
  /** Predicate over stored literal handles. */
8
8
  export type LiteralFilter = (literal: AnyLiteral) => boolean;
9
9
  /** A scalar value accepted in example expressions. */
10
- export type Scalar = string | number | bigint | boolean | Date;
10
+ export type Scalar = string | number | boolean | Date;
11
11
  /** A concrete value or logic variable used in formulas and examples. */
12
12
  export type Term<Value = Scalar | Entity> = Value | Var;
13
13
  /**
@@ -39,13 +39,13 @@ export type LiteralArgs<Args extends readonly unknown[]> = {
39
39
  readonly [Index in keyof Args]: LiteralInput<Args[Index]>;
40
40
  };
41
41
  /** Numeric term accepted by arithmetic, comparisons, and aggregate helpers. */
42
- export type NumericTerm = number | bigint | Var | Formula<number | bigint, readonly unknown[]>;
42
+ export type NumericTerm = number | Var | Formula<number, readonly unknown[]>;
43
43
  /** Date term accepted by date comparisons and date-offset arithmetic. */
44
44
  export type DateTerm = Date | Var | Formula<Date, readonly unknown[]>;
45
45
  /** Term accepted by comparison helpers. */
46
46
  export type ComparableTerm = NumericTerm | DateTerm;
47
47
  /** Aggregate builder returned by sum(...) and count(...). */
48
- export type AggregateBuilder<Value extends number | bigint = number> = Readonly<{
48
+ export type AggregateBuilder<Value extends number = number> = Readonly<{
49
49
  where: (<const Body>(body: Body & RuleBodyInput<Body>) => Formula<Value, readonly [Body]>) & (<const Body, const CompleteWhen>(body: Body & RuleBodyInput<Body>, options: Readonly<{
50
50
  completeWhen: CompleteWhen & RuleBodyInput<CompleteWhen>;
51
51
  }>) => Formula<Value, readonly [Body, Readonly<{
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isFormula
3
- } from "../chunk-YF4PP42K.js";
3
+ } from "../chunk-A6ZZJWIO.js";
4
4
  import {
5
5
  AssertionError,
6
6
  isVar
@@ -6,7 +6,7 @@ import {
6
6
  } from "../chunk-X3LSDJL4.js";
7
7
  import {
8
8
  isAtomLike
9
- } from "../chunk-4IP7CXNV.js";
9
+ } from "../chunk-5J3WGMN2.js";
10
10
  import "../chunk-UVOCOH2I.js";
11
11
 
12
12
  // src/knowledge-base/assertions/exclusive-definitions.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isFormula
3
- } from "../chunk-YF4PP42K.js";
3
+ } from "../chunk-A6ZZJWIO.js";
4
4
  import {
5
5
  PrimitiveArithmeticProgrammerError,
6
6
  UnconstructiveRecursionProgrammerError,
@@ -14,7 +14,7 @@ import {
14
14
  isAtomForPredicate,
15
15
  isAtomLike,
16
16
  isGroundAtom
17
- } from "../chunk-4IP7CXNV.js";
17
+ } from "../chunk-5J3WGMN2.js";
18
18
  import {
19
19
  AssertionError,
20
20
  Var,
@@ -1196,16 +1196,16 @@ function definedValue(value, message) {
1196
1196
  return value;
1197
1197
  }
1198
1198
  function isDefinedValue(value) {
1199
- return typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || typeof value === "object" && value !== null;
1199
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "object" && value !== null;
1200
1200
  }
1201
1201
  function isGoalExpression(value) {
1202
- return isVar(value) || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || value instanceof Date || typeof value === "object" && value !== null && "id" in value && typeof value.id === "string";
1202
+ return isVar(value) || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date || typeof value === "object" && value !== null && "id" in value && typeof value.id === "string";
1203
1203
  }
1204
1204
  function isScalarGoalExpression(value) {
1205
- return isVar(value) || typeof value === "string" || typeof value === "number" || typeof value === "bigint" || typeof value === "boolean" || value instanceof Date;
1205
+ return isVar(value) || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
1206
1206
  }
1207
1207
  function isNumericGoalExpression(value) {
1208
- return isVar(value) || typeof value === "number" || typeof value === "bigint";
1208
+ return isVar(value) || typeof value === "number";
1209
1209
  }
1210
1210
  function evaluateExpression(expression, bindings, context) {
1211
1211
  if (isVar(expression)) {
@@ -1462,10 +1462,10 @@ function aggregateValueIsAvailable(expression, bindings) {
1462
1462
  return valueForVariable(bindings, expression) !== UNBOUND;
1463
1463
  }
1464
1464
  function sumValues(values) {
1465
- if (values.every((value) => typeof value === "bigint")) {
1466
- return values.reduce((total, value) => total + value, 0n);
1467
- }
1468
- return values.reduce((total, value) => total + Number(value), 0);
1465
+ return values.reduce((total, value) => {
1466
+ assert(typeof value === "number", "Sum values must be numbers");
1467
+ return total + value;
1468
+ }, 0);
1469
1469
  }
1470
1470
  function aggregateWhereGoal(judgments) {
1471
1471
  if (judgments.length === 1) {
@@ -1639,7 +1639,7 @@ function primitiveUncertaintyForComparison(value, left, leftExpression, right, r
1639
1639
  return {};
1640
1640
  }
1641
1641
  function primitiveRangeForExact(value) {
1642
- if (typeof value !== "number" && typeof value !== "bigint") {
1642
+ if (typeof value !== "number") {
1643
1643
  return {};
1644
1644
  }
1645
1645
  return { range: { lower: value, upper: value } };
@@ -1689,20 +1689,6 @@ function calculateValue(kind, left, right, goal) {
1689
1689
  return assertNever(kind, "Unhandled arithmetic kind");
1690
1690
  }
1691
1691
  }
1692
- if (typeof left === "bigint" && typeof right === "bigint") {
1693
- switch (kind) {
1694
- case "add":
1695
- return left + right;
1696
- case "sub":
1697
- return left - right;
1698
- case "mul":
1699
- return left * right;
1700
- case "div":
1701
- return left / right;
1702
- default:
1703
- return assertNever(kind, "Unhandled arithmetic kind");
1704
- }
1705
- }
1706
1692
  throw new PrimitiveArithmeticProgrammerError(kind, [left, right], goal);
1707
1693
  }
1708
1694
  function compareValues(kind, left, right) {
@@ -1730,10 +1716,7 @@ function compareValues(kind, left, right) {
1730
1716
  }
1731
1717
  }
1732
1718
  function compareOrderedValues(kind, left, right) {
1733
- assert(
1734
- (typeof left === "number" || typeof left === "bigint") && (typeof right === "number" || typeof right === "bigint"),
1735
- "Values must be comparable"
1736
- );
1719
+ assert(typeof left === "number" && typeof right === "number", "Values must be comparable");
1737
1720
  switch (kind) {
1738
1721
  case "lt":
1739
1722
  return left < right ? "true" : "false";
@@ -84,7 +84,7 @@ export type ComparisonGoal<Kind extends "eq" | "neq" | "lt" | "lte" | "gt" | "gt
84
84
  export type SumGoal = Readonly<{
85
85
  readonly kind: "sum";
86
86
  readonly bind: Var;
87
- readonly value: GoalExpression<number | bigint>;
87
+ readonly value: GoalExpression<number>;
88
88
  readonly where: Goal;
89
89
  readonly completeWhen?: Goal;
90
90
  }>;
@@ -56,8 +56,8 @@ export type PrimitiveEvaluation = Readonly<{
56
56
  }>;
57
57
  /** Numeric lower/upper bound produced or consumed by a primitive operation. */
58
58
  export type NumericRange = Readonly<{
59
- readonly lower: number | bigint;
60
- readonly upper: number | bigint | "infinity";
59
+ readonly lower: number;
60
+ readonly upper: number | "infinity";
61
61
  }>;
62
62
  /** Why a primitive evaluation is not exact. */
63
63
  export type PrimitiveUncertainty = Readonly<{
@@ -17,9 +17,9 @@ export type RuntimeConstructor<T> = (abstract new (...args: never[]) => T) & Rea
17
17
  name: string;
18
18
  }>;
19
19
  /** Any runtime validator accepted in a predicate schema field. */
20
- export type RuntimeType<T> = EnumType | RuntimeConstructor<T & Entity> | BigIntConstructor | BooleanConstructor | DateConstructor | NumberConstructor | StringConstructor;
20
+ export type RuntimeType<T> = EnumType | RuntimeConstructor<T & Entity> | BooleanConstructor | DateConstructor | NumberConstructor | StringConstructor;
21
21
  /** A predicate schema maps field names to runtime validators/constructors. */
22
- export type PredicateSchema = Readonly<Record<string, BigIntConstructor | BooleanConstructor | DateConstructor | NumberConstructor | StringConstructor | EnumType | ((abstract new (...args: never[]) => Entity) & Readonly<{
22
+ export type PredicateSchema = Readonly<Record<string, BooleanConstructor | DateConstructor | NumberConstructor | StringConstructor | EnumType | ((abstract new (...args: never[]) => Entity) & Readonly<{
23
23
  prototype: Entity;
24
24
  name: string;
25
25
  }>)>>;
@@ -28,6 +28,6 @@ export type PredicateSchema = Readonly<Record<string, BigIntConstructor | Boolea
28
28
  * type. A schema-erased constructor (the bare `RuntimeConstructor<Entity>`)
29
29
  * infers `unknown`, so erased atoms keep permissive field types.
30
30
  */
31
- export type InferRuntime<T> = T extends EnumType<infer Value> ? Value : T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BooleanConstructor ? boolean : T extends BigIntConstructor ? bigint : T extends DateConstructor ? Date : T extends {
31
+ export type InferRuntime<T> = T extends EnumType<infer Value> ? Value : T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BooleanConstructor ? boolean : T extends DateConstructor ? Date : T extends {
32
32
  prototype: infer Value;
33
33
  } ? RuntimeConstructor<Entity> extends T ? unknown : Value : never;
@@ -65,8 +65,8 @@ function encodeField(value) {
65
65
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
66
66
  return value;
67
67
  }
68
- if (typeof value === "bigint" || value instanceof Date) {
69
- return value instanceof Date ? value.toISOString() : value.toString();
68
+ if (value instanceof Date) {
69
+ return value.toISOString();
70
70
  }
71
71
  if (isRecord(value)) {
72
72
  return JSON.parse(JSON.stringify({ ...value, type: value.constructor.name }));
@@ -5,7 +5,7 @@ import {
5
5
  admissionPolicy,
6
6
  createJob,
7
7
  jevClient
8
- } from "./chunk-2UXHML67.js";
8
+ } from "./chunk-PISXBJOT.js";
9
9
 
10
10
  // src/classifier.ts
11
11
  import { chunkers } from "../corpus/index.js";
@@ -81,7 +81,7 @@ function validatePredicate(predicate, kb) {
81
81
  function assertSupportedType(predicate) {
82
82
  for (const name of predicate.paramNames) {
83
83
  const type = predicate.params[name];
84
- if (type === String || type === Number || type === BigInt || type === Date || type === void 0) {
84
+ if (type === String || type === Number || type === Date || type === void 0) {
85
85
  throw new UnsupportedPredicateError(
86
86
  `Cannot classify predicate "${predicate.name}": field "${name}" has an unsupported open-ended domain. Use Boolean, enumeration(...), or registered entities.`,
87
87
  predicate,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  admissionPolicy,
3
3
  jevAdjudicator
4
- } from "./chunk-2UXHML67.js";
4
+ } from "./chunk-PISXBJOT.js";
5
5
 
6
6
  // src/resolve.ts
7
7
  function resolver(corpus, kb, config = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "factbag",
3
- "version": "0.0.0-alpha.1",
3
+ "version": "0.0.0-alpha.2",
4
4
  "keywords": [
5
5
  "logic",
6
6
  "rules-engine",
@@ -76,7 +76,7 @@
76
76
  "@factbag/corpus": "0.1.0-next.0",
77
77
  "@factbag/guards": "0.1.0-next.0",
78
78
  "@factbag/jev": "0.1.0-next.0",
79
- "@typesafe-ai/sdk": "0.6.0"
79
+ "@typesafe-ai/sdk": "0.7.0"
80
80
  },
81
81
  "peerDependencies": {
82
82
  "@typesafe-ai/sdk": "^0.6.0"