@juspay/neurolink 12.18.1 → 12.19.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/README.md +277 -75
  3. package/dist/browser/neurolink.min.js +396 -396
  4. package/dist/cli/commands/setup.js +2 -1
  5. package/dist/cli/factories/commandFactory.d.ts +22 -2
  6. package/dist/cli/factories/commandFactory.js +14 -2
  7. package/dist/cli/loop/optionsSchema.js +4 -0
  8. package/dist/cli/utils/classifierRouterFlags.js +39 -1
  9. package/dist/constants/enums.d.ts +13 -0
  10. package/dist/constants/enums.js +14 -0
  11. package/dist/context/budgetChecker.d.ts +3 -1
  12. package/dist/context/budgetChecker.js +20 -4
  13. package/dist/context/contextCompactor.d.ts +43 -3
  14. package/dist/context/contextCompactor.js +157 -41
  15. package/dist/context/contextDecision.d.ts +56 -0
  16. package/dist/context/contextDecision.js +234 -0
  17. package/dist/core/baseProvider.d.ts +27 -1
  18. package/dist/core/baseProvider.js +39 -0
  19. package/dist/core/toolRouting.d.ts +8 -0
  20. package/dist/core/toolRouting.js +52 -1
  21. package/dist/core/toolRoutingDecision.d.ts +35 -0
  22. package/dist/core/toolRoutingDecision.js +194 -0
  23. package/dist/factories/providerDescriptors.d.ts +15 -0
  24. package/dist/factories/providerDescriptors.js +57 -1
  25. package/dist/factories/providerRegistry.js +9 -1
  26. package/dist/index.d.ts +57 -1
  27. package/dist/index.js +60 -1
  28. package/dist/models/manifestRegistry.js +2 -0
  29. package/dist/models/manifests/typesafe.d.ts +16 -0
  30. package/dist/models/manifests/typesafe.js +42 -0
  31. package/dist/neurolink.d.ts +57 -1
  32. package/dist/neurolink.js +254 -10
  33. package/dist/observability/exporters/laminarExporter.js +3 -0
  34. package/dist/observability/exporters/posthogExporter.js +3 -0
  35. package/dist/observability/utils/spanSerializer.js +4 -0
  36. package/dist/providers/catalog/schema.d.ts +1 -1
  37. package/dist/providers/typesafe.d.ts +112 -0
  38. package/dist/providers/typesafe.js +654 -0
  39. package/dist/rag/pipeline/RAGPipeline.d.ts +3 -1
  40. package/dist/rag/pipeline/RAGPipeline.js +65 -4
  41. package/dist/rag/retrieval/searchDecision.d.ts +27 -0
  42. package/dist/rag/retrieval/searchDecision.js +124 -0
  43. package/dist/routing/classifierRouter.d.ts +74 -2
  44. package/dist/routing/classifierRouter.js +232 -32
  45. package/dist/routing/classifierStrategies.d.ts +33 -1
  46. package/dist/routing/classifierStrategies.js +237 -0
  47. package/dist/routing/index.d.ts +1 -1
  48. package/dist/routing/index.js +1 -1
  49. package/dist/routing/modelCatalog.d.ts +79 -0
  50. package/dist/routing/modelCatalog.js +382 -0
  51. package/dist/telemetry/tracers.d.ts +1 -0
  52. package/dist/telemetry/tracers.js +1 -0
  53. package/dist/types/classifierRouter.d.ts +160 -4
  54. package/dist/types/cli.d.ts +17 -1
  55. package/dist/types/config.d.ts +12 -0
  56. package/dist/types/context.d.ts +55 -1
  57. package/dist/types/decision.d.ts +200 -0
  58. package/dist/types/decision.js +21 -0
  59. package/dist/types/generate.d.ts +20 -0
  60. package/dist/types/index.d.ts +1 -0
  61. package/dist/types/index.js +2 -0
  62. package/dist/types/providers.d.ts +59 -1
  63. package/dist/types/providers.js +5 -0
  64. package/dist/types/rag.d.ts +48 -0
  65. package/dist/types/span.d.ts +11 -0
  66. package/dist/types/span.js +11 -0
  67. package/dist/types/stream.d.ts +13 -0
  68. package/dist/types/toolRouting.d.ts +56 -0
  69. package/dist/utils/decisionAnswers.d.ts +56 -0
  70. package/dist/utils/decisionAnswers.js +95 -0
  71. package/dist/utils/modelChoices.js +13 -1
  72. package/dist/utils/pricing.js +11 -0
  73. package/dist/utils/providerConfig.d.ts +4 -0
  74. package/dist/utils/providerConfig.js +17 -0
  75. package/docs-site/static/search-index.json +1 -1
  76. package/package.json +16 -3
@@ -14,10 +14,29 @@
14
14
  * `src/lib/types/` (see CLAUDE.md rule 9).
15
15
  */
16
16
  import type { ValidationSchema } from "./aliases.js";
17
+ import type { DecisionCallerFn } from "./decision.js";
17
18
  /** Coarse difficulty buckets the classifier maps a request into. */
18
19
  export type ClassifierDifficulty = "trivial" | "simple" | "moderate" | "hard" | "expert";
19
- /** Which classification strategy to run. */
20
- export type ClassifierStrategyKind = "heuristic" | "llm";
20
+ /**
21
+ * Which classification strategy to run.
22
+ *
23
+ * - `heuristic` — keyword/length scoring. Deterministic, zero latency.
24
+ * - `llm` — a cheap classifier model via the injected `generate`.
25
+ * - `jev` — TypeSafe's System One model; one ~400ms round trip that returns a
26
+ * *calibrated* confidence rather than a self-reported one.
27
+ * - `auto` — `jev` when `TYPESAFE_API_KEY` is set, otherwise `heuristic`.
28
+ */
29
+ export type ClassifierStrategyKind = "heuristic" | "llm" | "jev" | "auto";
30
+ /**
31
+ * How much of the available context a request actually needs, ordered
32
+ * narrowest → widest. This is a *rubric*, not a token count: a decision model
33
+ * is reliable at placing a request on an ordered scale and unreliable at
34
+ * naming a number (it reads digits as text, not as quantities).
35
+ *
36
+ * The index is mapped onto a compaction threshold by the router, and only
37
+ * ever downward — see `ClassifierRouterDecision.compactionThreshold`.
38
+ */
39
+ export type ClassifierContextScope = "current-message" | "recent-turns" | "full-conversation" | "everything";
21
40
  /**
22
41
  * The classifier's verdict for a single request. Strategy-agnostic: produced
23
42
  * by both the heuristic and the LLM classifier.
@@ -36,6 +55,27 @@ export type ClassifierDecision = {
36
55
  * (matches a `ClassifierCandidate.id`). Ignored by the heuristic classifier.
37
56
  */
38
57
  selectedModelId?: string;
58
+ /**
59
+ * Confidence in `selectedModelId`, when the strategy reports one.
60
+ *
61
+ * Separate from `confidence`, which is about the DIFFICULTY verdict: a
62
+ * classifier can be certain a task is hard and unsure which model suits it.
63
+ * The router needs this one on its own, because whether a pick must clear
64
+ * the upgrade bar or the downgrade bar depends on the pick, not the tier.
65
+ *
66
+ * Absent means the strategy does not report one (the LLM classifier), in
67
+ * which case the pick is honoured as it always was.
68
+ */
69
+ selectedModelConfidence?: number;
70
+ /**
71
+ * How much context the request needs. Only the decision strategy produces
72
+ * this — the heuristic has no way to judge it and the LLM classifier is not
73
+ * asked, since for it every extra field costs output tokens. For a decision
74
+ * model the question is very nearly free.
75
+ */
76
+ contextScope?: ClassifierContextScope;
77
+ /** Confidence in `contextScope`, 0-1. Calibrated for the decide strategy. */
78
+ contextScopeConfidence?: number;
39
79
  /** Human-readable explanation, emitted at debug level. */
40
80
  reason?: string;
41
81
  };
@@ -50,6 +90,46 @@ export type ClassifierCandidate = {
50
90
  description?: string;
51
91
  tiers?: ClassifierDifficulty[];
52
92
  capabilities?: string[];
93
+ /**
94
+ * Maximum input window, in tokens. Read from the model registry when the
95
+ * pool is built from the catalogue. Nothing in routing consulted this
96
+ * before — a request was routed to a model without ever asking whether it
97
+ * could hold the request.
98
+ */
99
+ contextWindow?: number;
100
+ /** USD per 1K input tokens, for the cheapest-that-works judgement. */
101
+ inputCostPer1K?: number;
102
+ /** USD per 1K output tokens. */
103
+ outputCostPer1K?: number;
104
+ /** Registry speed bucket ("fast" | "medium" | "slow"). */
105
+ speed?: string;
106
+ /** Registry quality bucket ("high" | "medium" | "low"). */
107
+ quality?: string;
108
+ /**
109
+ * The registry's per-dimension suitability scores (1–10): coding, analysis,
110
+ * reasoning, conversation, creative, translation, summarization. Present
111
+ * only for registry-backed models.
112
+ */
113
+ useCases?: Readonly<Record<string, number>>;
114
+ /**
115
+ * Deterministic merit score for the difficulty this candidate was built
116
+ * for, higher is better. Computed by `enrichCandidate`; it is what the
117
+ * fallback ranker sorts on and is never sent to the model.
118
+ */
119
+ score?: number;
120
+ /**
121
+ * The host's own `cost` / `quality` from the pool member, if it declared
122
+ * them. These are RELATIVE scales, comparable only against other members
123
+ * of the same pool — never a currency and never a registry bucket.
124
+ *
125
+ * They are carried separately because they take precedence over anything
126
+ * the registry says. A host that writes `quality: 2` next to "cheap and
127
+ * fast; rote edits only" has made a statement about how it wants that
128
+ * model used, and the registry — which may rate the same model highly on
129
+ * its own general benchmarks — does not get to overrule it.
130
+ */
131
+ relativeCost?: number;
132
+ relativeQuality?: number;
53
133
  };
54
134
  /**
55
135
  * One candidate (provider, model, region) in the available base pool, with
@@ -102,12 +182,26 @@ export type ClassifierRouterConfig = {
102
182
  /** Master switch. When false/absent, the router is never built. */
103
183
  enabled: boolean;
104
184
  /**
105
- * Classification strategy. Default: "heuristic" (no LLM, zero added latency).
106
- * "llm" runs a cheap classifier model (see `classifierModel`).
185
+ * Classification strategy. Default: "auto" — which resolves to "jev" when
186
+ * `TYPESAFE_API_KEY` is set and "heuristic" otherwise, so configuring a key
187
+ * upgrades routing without any code change. Behaviour for callers with no
188
+ * key is unchanged.
107
189
  */
108
190
  classifier?: ClassifierStrategyKind;
109
191
  /** Model used by the "llm" strategy. Defaults to provider/model auto. */
110
192
  classifierModel?: ClassifierModelRef;
193
+ /**
194
+ * How sure the classifier must be to route a request UP to a more capable
195
+ * (costlier) model. Being wrong here costs money, so the bar is low.
196
+ * Only meaningful for "jev", whose confidence is calibrated. Default: 0.3.
197
+ */
198
+ minUpgradeConfidence?: number;
199
+ /**
200
+ * How sure it must be to route DOWN to a cheaper model. Being wrong here
201
+ * means a task handled by too small a model, so the bar is high.
202
+ * Default: 0.6.
203
+ */
204
+ minDowngradeConfidence?: number;
111
205
  /** The available base pool the router selects a model from. */
112
206
  pool: ClassifierRouterPoolMember[];
113
207
  /**
@@ -119,6 +213,46 @@ export type ClassifierRouterConfig = {
119
213
  toolDirectives?: Partial<Record<ClassifierDifficulty, ClassifierToolDirective>>;
120
214
  /** Hard timeout (ms) for the LLM classifier call. Default: 8000. */
121
215
  timeoutMs?: number;
216
+ /**
217
+ * Widen the pool with every model the registry knows about that this host
218
+ * actually has credentials for.
219
+ *
220
+ * Off by default, and deliberately so: the declared `pool` is a statement
221
+ * about which models a host is *willing* to be billed for, and NeuroLink
222
+ * cannot invent that. Turning this on says "anything I have a key for is
223
+ * fair game", which is exactly right for a CLI and exactly wrong for a
224
+ * service with a negotiated model list.
225
+ */
226
+ catalog?: ClassifierCatalogConfig;
227
+ /**
228
+ * Ask the classifier how much context the request needs and use the answer
229
+ * to lower the compaction threshold. Default: true when the strategy
230
+ * resolves to a decision model, since the question rides along in a batch
231
+ * that is already being sent. Ignored by the other strategies.
232
+ */
233
+ contextBudget?: boolean;
234
+ };
235
+ /** How the model catalogue widens the declared pool. */
236
+ export type ClassifierCatalogConfig = {
237
+ enabled: boolean;
238
+ /**
239
+ * Cap on catalogue-derived members. The binding constraint is the decision
240
+ * model's input ceiling — state plus the longest single question must stay
241
+ * under ~33K tokens, and the model question's `criteria` map is that
242
+ * question. At roughly 40 tokens per rendered model that ceiling is
243
+ * hundreds of models away.
244
+ *
245
+ * Default: 120. The registry currently holds 64 models, so the default
246
+ * never truncates today — it is a guard against a future registry that
247
+ * grows past what one question can carry, not a limit anyone is hitting.
248
+ */
249
+ maxModels?: number;
250
+ /** Restrict the catalogue to these provider names. Omit for all configured. */
251
+ providers?: string[];
252
+ /** Drop models whose context window is below this. Default: 0 (keep all). */
253
+ minContextWindow?: number;
254
+ /** Include models flagged deprecated in the registry. Default: false. */
255
+ includeDeprecated?: boolean;
122
256
  };
123
257
  /**
124
258
  * The router's combined decision: a provider/model/region override plus an
@@ -135,6 +269,20 @@ export type ClassifierRouterDecision = {
135
269
  excludeTools?: string[];
136
270
  /** The difficulty this decision was made for (debug/telemetry). */
137
271
  difficulty?: ClassifierDifficulty;
272
+ /**
273
+ * Fraction of the model's window at which compaction should trigger for
274
+ * THIS request, replacing the fixed 0.8 default.
275
+ *
276
+ * **Only ever lower than the default, never higher.** Raising it would let
277
+ * a request through that the model then rejects with a context-window
278
+ * error — and `ModelPool` treats that as a permanent cooldown (10 years),
279
+ * so a single optimistic guess retires the model for the life of the
280
+ * process. Shrinking a budget wastes a little context; growing one is
281
+ * unrecoverable.
282
+ */
283
+ compactionThreshold?: number;
284
+ /** The scope reading `compactionThreshold` was derived from. */
285
+ contextScope?: ClassifierContextScope;
138
286
  /** Remaining ranked candidates, best-first, for downstream failover. */
139
287
  modelFallbacks?: ClassifierRouterPoolMember[];
140
288
  /** Human-readable explanation, emitted at debug level. */
@@ -178,6 +326,12 @@ export type ClassifierGenerateResult = {
178
326
  };
179
327
  /** Injected LLM caller — typically a bound `NeuroLink.generate`. */
180
328
  export type ClassifierGenerateFn = (options: ClassifierGenerateOptions) => Promise<ClassifierGenerateResult>;
329
+ /**
330
+ * Injected decision caller — typically a bound `NeuroLink.tryDecide`, which
331
+ * returns null on any failure. Keeps `ClassifierRouter` free of provider
332
+ * imports, exactly as `ClassifierGenerateFn` does.
333
+ */
334
+ export type ClassifierDecideFn = DecisionCallerFn;
181
335
  /** Minimal logger surface the router uses (debug/warn). */
182
336
  export type ClassifierLogger = {
183
337
  debug: (message: string, meta?: unknown) => void;
@@ -190,5 +344,7 @@ export type ClassifierLogger = {
190
344
  export type ClassifierRouterDeps = {
191
345
  /** LLM caller for the "llm" strategy. Omit to disable LLM classification. */
192
346
  generate?: ClassifierGenerateFn;
347
+ /** Decision caller for the "jev" strategy. Omit to disable it. */
348
+ decide?: ClassifierDecideFn;
193
349
  logger?: ClassifierLogger;
194
350
  };
@@ -1681,8 +1681,24 @@ export type CliToolRoutingFlags = {
1681
1681
  export type CliClassifierRouterFlags = {
1682
1682
  /** Master enable switch (--classifier-router). */
1683
1683
  classifierRouter?: boolean;
1684
- /** Strategy: "heuristic" (default) or "llm" (--classifier-strategy). */
1684
+ /**
1685
+ * Strategy: "auto" (default), "heuristic", "llm" or "jev"
1686
+ * (--classifier-strategy). "auto" resolves to "jev" when TYPESAFE_API_KEY
1687
+ * is set and "heuristic" otherwise.
1688
+ */
1685
1689
  classifierStrategy?: string;
1690
+ /**
1691
+ * Minimum confidence required to route UP to a costlier model
1692
+ * (--classifier-min-upgrade-confidence). Only meaningful for "jev", whose
1693
+ * confidence is calibrated. Default: 0.3.
1694
+ */
1695
+ classifierMinUpgradeConfidence?: number;
1696
+ /**
1697
+ * Minimum confidence required to route DOWN to a cheaper model
1698
+ * (--classifier-min-downgrade-confidence). Higher than the upgrade bar
1699
+ * because the mistakes cost differently. Default: 0.6.
1700
+ */
1701
+ classifierMinDowngradeConfidence?: number;
1686
1702
  /** LLM-classifier provider override (--classifier-model-provider). */
1687
1703
  classifierModelProvider?: string;
1688
1704
  /** LLM-classifier model override (--classifier-model-name). */
@@ -13,6 +13,7 @@ import type { NeurolinkCredentials } from "./providers.js";
13
13
  import type { ModelPoolConfig } from "./modelPool.js";
14
14
  import type { RequestRouter } from "./requestRouter.js";
15
15
  import type { ClassifierRouterConfig } from "./classifierRouter.js";
16
+ import type { ContextRelevanceOptions } from "./context.js";
16
17
  import type { SkillsConfig } from "./skills.js";
17
18
  import type { KnowledgeGroundingConfig } from "./knowledge.js";
18
19
  /**
@@ -143,6 +144,17 @@ export type NeurolinkConstructorConfig = {
143
144
  * caller pinned both `provider` and `model`. See {@link ClassifierRouterConfig}.
144
145
  */
145
146
  classifierRouter?: ClassifierRouterConfig;
147
+ /**
148
+ * Relevance-driven context compaction. When a decision provider is
149
+ * configured, compaction gains a stage-zero pass that drops the earlier
150
+ * messages the current request provably does not need, before the
151
+ * positional stages (which protect by recency alone) get to choose.
152
+ *
153
+ * This object only TUNES that stage; it does not enable it. The stage runs
154
+ * whenever a decision provider is configured, and does nothing otherwise,
155
+ * which is the same activation rule every other decision consumer uses.
156
+ */
157
+ contextRelevance?: ContextRelevanceOptions;
146
158
  /**
147
159
  * Native skills: versioned, discoverable instruction packs (SOPs,
148
160
  * playbooks) with progressive disclosure. When enabled, each
@@ -5,6 +5,7 @@
5
5
  import type { ExecutionContext } from "../types/tools.js";
6
6
  import type { JsonObject } from "./common.js";
7
7
  import type { ChatMessage, ConversationMemoryConfig } from "./conversation.js";
8
+ import type { DecisionCallerFn } from "./decision.js";
8
9
  /**
9
10
  * Base context type for all AI operations
10
11
  */
@@ -173,7 +174,7 @@ export declare class ContextConverter {
173
174
  private static extractCustomData;
174
175
  }
175
176
  /** Stages available in the compaction pipeline. */
176
- export type CompactionStage = "prune" | "deduplicate" | "summarize" | "truncate";
177
+ export type CompactionStage = "relevance" | "prune" | "deduplicate" | "summarize" | "truncate";
177
178
  /** Result of multi-stage context compaction. */
178
179
  export type CompactionResult = {
179
180
  compacted: boolean;
@@ -577,4 +578,57 @@ export type NativeGenerateGuardConfig = {
577
578
  availableInputTokens: number;
578
579
  getFixedOverheadTokens: () => number;
579
580
  };
581
+ /** Tuning for the relevance stage of context compaction. */
582
+ export type ContextRelevanceOptions = {
583
+ /** Trailing messages never eligible for dropping. Default 6. */
584
+ protectRecent?: number;
585
+ /** Confidence required to drop a message. Default 0.6. */
586
+ minDropConfidence?: number;
587
+ /** Ceiling on the share of eligible messages one pass may drop. Default 0.5. */
588
+ maxDropRatio?: number;
589
+ /** Per-call timeout override for the decision request. */
590
+ timeoutMs?: number;
591
+ };
592
+ /** What the relevance stage concluded. */
593
+ export type ContextRelevanceResult = {
594
+ /** The surviving messages, in their original order. */
595
+ messages: ChatMessage[];
596
+ /** Indices into the ORIGINAL array that were dropped. */
597
+ droppedIndices: number[];
598
+ /** How many messages were asked about. */
599
+ askedCount: number;
600
+ /** How many came back with a usable, confident answer. */
601
+ answeredCount: number;
602
+ /** Resolved decision model id, for telemetry. */
603
+ model: string;
604
+ /** Round trip in milliseconds. */
605
+ latencyMs: number;
606
+ };
607
+ /**
608
+ * Injected dependencies for {@link ContextCompactor}.
609
+ *
610
+ * Deliberately separate from `CompactionConfig`: that type is spread into a
611
+ * `Required<CompactionConfig>` default, so a function member would need a
612
+ * default implementation, and the compactor must stay fully usable with no
613
+ * decision provider configured.
614
+ */
615
+ export type ContextCompactorDeps = {
616
+ /** Fail-open decision caller. Omitted = the previous pipeline, exactly. */
617
+ decide?: DecisionCallerFn;
618
+ /** Tuning for the relevance stage. */
619
+ relevance?: ContextRelevanceOptions;
620
+ /** Tuning for the post-summarization quality gate. */
621
+ summaryGate?: {
622
+ timeoutMs?: number;
623
+ minConfidence?: number;
624
+ };
625
+ };
626
+ /** Per-call context handed to `ContextCompactor.compact`. */
627
+ export type ContextCompactRequest = {
628
+ /**
629
+ * The request being prepared. Without it the relevance stage cannot run —
630
+ * "is this message still needed?" is meaningless without knowing what for.
631
+ */
632
+ currentRequest?: string;
633
+ };
580
634
  export {};
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Decision inference types — typed, calibrated judgements from a model that
3
+ * emits no text.
4
+ *
5
+ * `decide` is the third inference type, alongside `generate` and `stream`. A
6
+ * decision model takes one `state` plus a map of named typed questions and
7
+ * returns one typed answer per question, all evaluated in a single parallel
8
+ * pass. TypeSafe's Jev is the first such model; Vercel's AI SDK models the
9
+ * same class separately from `LanguageModelV4`, for the same reason.
10
+ *
11
+ * The vocabulary here is deliberately **provider-neutral**. TypeSafe calls a
12
+ * yes/no question a `noul` and answers it in a field of the same name; both
13
+ * the Vercel AI SDK and Pydantic AI independently renamed that to `boolean` /
14
+ * `probability` when exposing it. This file follows them, and the mapping to
15
+ * a vendor's wire format belongs in that vendor's provider.
16
+ *
17
+ * Names are domain-prefixed `Decision*` to stay globally unique across
18
+ * `src/lib/types/` (CLAUDE.md rule 9). Note `evaluate`/`Evaluation*` is a
19
+ * different, unrelated feature — scoring a generated response with RAGAS.
20
+ */
21
+ import type { NeurolinkCredentials } from "./providers.js";
22
+ /**
23
+ * Anything a decision model accepts as free-form content. `state` may be a
24
+ * plain string or structured JSON (chat logs, records); `instructions` and
25
+ * `criteria` values likewise.
26
+ */
27
+ export type DecisionInput = string | number | boolean | readonly DecisionInput[] | {
28
+ readonly [key: string]: DecisionInput;
29
+ };
30
+ /** The content being judged. One shared state per request. */
31
+ export type DecisionState = DecisionInput;
32
+ /**
33
+ * A yes/no question. Answered with a probability and **no confidence of its
34
+ * own** — gate it on distance from 0.5 via {@link decisionBooleanConfidence}.
35
+ */
36
+ export type DecisionBooleanQuestion = {
37
+ type: "boolean";
38
+ instructions: DecisionInput;
39
+ /** Optional descriptions of what a yes and a no mean. */
40
+ criteria?: {
41
+ true?: DecisionInput | null;
42
+ false?: DecisionInput | null;
43
+ };
44
+ };
45
+ /**
46
+ * Pick one option. `criteria` maps option name → rubric description.
47
+ *
48
+ * The answer carries the full probability distribution, not just the winner,
49
+ * so a single choice question over N options also **ranks** all N.
50
+ */
51
+ export type DecisionChoiceQuestion = {
52
+ type: "choice";
53
+ instructions: DecisionInput;
54
+ criteria: Readonly<Record<string, DecisionInput | null>>;
55
+ };
56
+ /**
57
+ * Rate against an ordered rubric. The answer is the **0-based index**, so a
58
+ * four-level rubric scores 0–3. At least two levels are required.
59
+ */
60
+ export type DecisionScoreQuestion = {
61
+ type: "score";
62
+ instructions: DecisionInput;
63
+ criteria: readonly (DecisionInput | null)[];
64
+ };
65
+ export type DecisionQuestion = DecisionBooleanQuestion | DecisionChoiceQuestion | DecisionScoreQuestion;
66
+ /** Questions keyed by an id you choose; answers return under the same ids. */
67
+ export type DecisionQuestionMap = Readonly<Record<string, DecisionQuestion>>;
68
+ export type DecisionBooleanAnswer = {
69
+ type: "boolean";
70
+ /** Probability the statement is true, 0–1. */
71
+ probability: number;
72
+ };
73
+ export type DecisionChoiceAnswer = {
74
+ type: "choice";
75
+ /** The highest-probability option name. */
76
+ choice: string;
77
+ /** Every option mapped to its probability. Key order is not stable. */
78
+ probabilities: Readonly<Record<string, number>>;
79
+ /** Calibrated certainty, 0–1, derived from the distribution. */
80
+ confidence: number;
81
+ };
82
+ export type DecisionScoreAnswer = {
83
+ type: "score";
84
+ /** Probability-weighted level index; lands between levels. */
85
+ score: number;
86
+ /** Level index (as a string key) → the description supplied. */
87
+ legend: Readonly<Record<string, string>>;
88
+ /** Level index (as a string key) → probability. */
89
+ probabilities: Readonly<Record<string, number>>;
90
+ confidence: number;
91
+ };
92
+ export type DecisionAnswer = DecisionBooleanAnswer | DecisionChoiceAnswer | DecisionScoreAnswer;
93
+ export type DecisionAnswerMap = Readonly<Record<string, DecisionAnswer>>;
94
+ export type DecisionUsage = {
95
+ inputTokens: number;
96
+ outputTokens: number;
97
+ };
98
+ export type DecisionRequest = {
99
+ state: DecisionState;
100
+ questions: DecisionQuestionMap;
101
+ /** Overrides the provider's configured model for this call only. */
102
+ model?: string;
103
+ signal?: AbortSignal;
104
+ /** Overrides the configured timeout for this call only. */
105
+ timeoutMs?: number;
106
+ };
107
+ export type DecisionResult = {
108
+ /** The RESOLVED model id (e.g. "jev-1.13.0"), not the alias sent. */
109
+ model: string;
110
+ provider: string;
111
+ answers: DecisionAnswerMap;
112
+ usage: DecisionUsage;
113
+ /** Vendor request id, for support escalation. */
114
+ requestId?: string;
115
+ /** Wall-clock round trip measured client-side. */
116
+ latencyMs: number;
117
+ /** Server-side time behind the edge; isolates model time from network. */
118
+ upstreamMs?: number;
119
+ };
120
+ /**
121
+ * Why a decision call failed, normalised across vendors. TypeSafe alone
122
+ * returns two different error envelopes, so a provider must flatten them.
123
+ */
124
+ export type DecisionErrorKind = "authentication" | "invalid_request" | "max_tokens_exceeded" | "rate_limit" | "overloaded" | "server" | "timeout" | "network";
125
+ export type DecisionError = {
126
+ kind: DecisionErrorKind;
127
+ message: string;
128
+ status?: number;
129
+ requestId?: string;
130
+ /** True when a retry could plausibly succeed. */
131
+ retryable: boolean;
132
+ };
133
+ /** One entry from a decision provider's model listing. */
134
+ export type DecisionModelCard = {
135
+ name: string;
136
+ description: string;
137
+ releaseDate: string;
138
+ };
139
+ /**
140
+ * Narrowed view of a choice answer, safe to consume without re-narrowing.
141
+ * Returned by the reader helpers so call sites need no type assertion.
142
+ */
143
+ export type DecisionChoiceReading = {
144
+ choice: string;
145
+ confidence: number;
146
+ probabilities: Readonly<Record<string, number>>;
147
+ /**
148
+ * Every option ordered by probability, highest first. A choice question is
149
+ * therefore also a ranking — the basis for catalogue selection.
150
+ */
151
+ ranked: readonly {
152
+ name: string;
153
+ probability: number;
154
+ }[];
155
+ };
156
+ export type DecisionScoreReading = {
157
+ score: number;
158
+ confidence: number;
159
+ legend: Readonly<Record<string, string>>;
160
+ probabilities: Readonly<Record<string, number>>;
161
+ };
162
+ /**
163
+ * What `NeuroLink.decide()` accepts: a decision request plus the usual
164
+ * provider/credential selection every inference type shares.
165
+ */
166
+ export type DecisionOptions = DecisionRequest & {
167
+ /**
168
+ * Provider name or alias. Defaults to the first registered provider whose
169
+ * descriptor declares `"decide"` in `inferenceKinds`.
170
+ */
171
+ provider?: string;
172
+ /** Per-call credential overrides, as `generate()` takes. */
173
+ credentials?: NeurolinkCredentials;
174
+ };
175
+ /**
176
+ * Injected fail-open decision caller — typically a bound `NeuroLink.tryDecide`,
177
+ * which returns `null` on any failure rather than throwing.
178
+ *
179
+ * Every internal consumer of the `decide` inference type takes one of these
180
+ * instead of importing a provider, exactly as the tool router takes a
181
+ * `generateFn`. It is what keeps the consumers provider-import-free, and what
182
+ * makes "no decision provider configured" indistinguishable from "the call
183
+ * failed" at every call site: both are `null`, and both mean *carry on as
184
+ * before*.
185
+ */
186
+ export type DecisionCallerFn = (options: DecisionOptions) => Promise<DecisionResult | null>;
187
+ /**
188
+ * Shared gate for acting on a yes/no answer.
189
+ *
190
+ * A `boolean` answer carries no confidence of its own, so two separate bars
191
+ * apply: which side of the coin flip it fell on, and how far from the flip it
192
+ * landed. Both must be cleared, which is why "probability 0.55" never counts
193
+ * as a yes.
194
+ */
195
+ export type DecisionBooleanGate = {
196
+ /** Minimum probability to read the answer as "yes". Default 0.5. */
197
+ minProbability?: number;
198
+ /** Minimum {@link decisionBooleanConfidence} to act at all. Default 0.4. */
199
+ minConfidence?: number;
200
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Decision inference types — typed, calibrated judgements from a model that
3
+ * emits no text.
4
+ *
5
+ * `decide` is the third inference type, alongside `generate` and `stream`. A
6
+ * decision model takes one `state` plus a map of named typed questions and
7
+ * returns one typed answer per question, all evaluated in a single parallel
8
+ * pass. TypeSafe's Jev is the first such model; Vercel's AI SDK models the
9
+ * same class separately from `LanguageModelV4`, for the same reason.
10
+ *
11
+ * The vocabulary here is deliberately **provider-neutral**. TypeSafe calls a
12
+ * yes/no question a `noul` and answers it in a field of the same name; both
13
+ * the Vercel AI SDK and Pydantic AI independently renamed that to `boolean` /
14
+ * `probability` when exposing it. This file follows them, and the mapping to
15
+ * a vendor's wire format belongs in that vendor's provider.
16
+ *
17
+ * Names are domain-prefixed `Decision*` to stay globally unique across
18
+ * `src/lib/types/` (CLAUDE.md rule 9). Note `evaluate`/`Evaluation*` is a
19
+ * different, unrelated feature — scoring a generated response with RAGAS.
20
+ */
21
+ export {};
@@ -273,6 +273,13 @@ export type GenerateOptions = {
273
273
  region?: string;
274
274
  temperature?: number;
275
275
  maxTokens?: number;
276
+ /**
277
+ * Fraction of the model's context window at which history is compacted for
278
+ * this request, replacing the 0.8 default. Filled in per request by the
279
+ * classifier router when a decision provider is configured; always at or
280
+ * below the default, never above. See `TextGenerationOptions`.
281
+ */
282
+ compactionThreshold?: number;
276
283
  /** Top-p (nucleus) sampling parameter. Controls diversity of generated tokens. */
277
284
  topP?: number;
278
285
  /** Top-k sampling parameter. Limits the number of tokens considered. (Google/Gemini models only) */
@@ -1113,6 +1120,19 @@ export type FactoryEnhancedProvider = EnhancedProvider & {
1113
1120
  */
1114
1121
  export type TextGenerationOptions = {
1115
1122
  prompt?: string;
1123
+ /**
1124
+ * Fraction of the model's context window at which compaction runs for this
1125
+ * request, replacing the 0.8 default.
1126
+ *
1127
+ * A request that needs only the current message does not need the whole
1128
+ * window kept warm for it, and compacting earlier is free; a request that
1129
+ * depends on the entire conversation should compact as late as possible.
1130
+ * When the classifier router runs with a decision model it fills this in
1131
+ * per request — always at or below the default, never above, because a
1132
+ * request that overflows the window fails hard and `ModelPool` records that
1133
+ * failure as a permanent cooldown.
1134
+ */
1135
+ compactionThreshold?: number;
1116
1136
  /**
1117
1137
  * Alternative input format for multimodal SDK operations.
1118
1138
  *
@@ -98,3 +98,4 @@ export * from "./localUsage.js";
98
98
  export * from "./dispatch.js";
99
99
  export * from "./proxyRestart.js";
100
100
  export * from "./proxyBudget.js";
101
+ export * from "./decision.js";
@@ -113,3 +113,5 @@ export * from "./localUsage.js";
113
113
  export * from "./dispatch.js";
114
114
  export * from "./proxyRestart.js";
115
115
  export * from "./proxyBudget.js";
116
+ // `decide` inference-type contracts (TypeSafe Jev is the first provider)
117
+ export * from "./decision.js";