@atlaskit/editor-plugin-autocomplete 8.0.0 → 9.0.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 (79) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/cjs/analytics/ufo.js +2 -1
  3. package/dist/cjs/pm-plugins/artifact-loader.js +135 -0
  4. package/dist/cjs/pm-plugins/artifacts-manifest.js +3 -0
  5. package/dist/cjs/pm-plugins/autocomplete-plugin.js +416 -129
  6. package/dist/cjs/pm-plugins/canonical-lm-scoring.js +371 -0
  7. package/dist/cjs/pm-plugins/debug-mode.js +90 -7
  8. package/dist/cjs/pm-plugins/ghost-text-decoration.js +12 -3
  9. package/dist/cjs/pm-plugins/local-slow-lane-client.js +1202 -453
  10. package/dist/cjs/pm-plugins/scoring-pipeline.js +164 -19
  11. package/dist/cjs/pm-plugins/slow-lane-client.js +100 -37
  12. package/dist/cjs/pm-plugins/text-predictor.js +1951 -273
  13. package/dist/es2019/analytics/ufo.js +2 -0
  14. package/dist/es2019/pm-plugins/artifact-loader.js +70 -0
  15. package/dist/es2019/pm-plugins/artifacts-manifest.js +3 -0
  16. package/dist/es2019/pm-plugins/autocomplete-plugin.js +425 -130
  17. package/dist/es2019/pm-plugins/canonical-lm-scoring.js +270 -0
  18. package/dist/es2019/pm-plugins/debug-mode.js +87 -7
  19. package/dist/es2019/pm-plugins/ghost-text-decoration.js +12 -3
  20. package/dist/es2019/pm-plugins/local-slow-lane-client.js +940 -326
  21. package/dist/es2019/pm-plugins/scoring-pipeline.js +137 -13
  22. package/dist/es2019/pm-plugins/slow-lane-client.js +94 -37
  23. package/dist/es2019/pm-plugins/text-predictor.js +1603 -199
  24. package/dist/esm/analytics/ufo.js +2 -1
  25. package/dist/esm/pm-plugins/artifact-loader.js +128 -0
  26. package/dist/esm/pm-plugins/artifacts-manifest.js +3 -0
  27. package/dist/esm/pm-plugins/autocomplete-plugin.js +419 -132
  28. package/dist/esm/pm-plugins/canonical-lm-scoring.js +364 -0
  29. package/dist/esm/pm-plugins/debug-mode.js +89 -6
  30. package/dist/esm/pm-plugins/ghost-text-decoration.js +12 -3
  31. package/dist/esm/pm-plugins/local-slow-lane-client.js +1200 -451
  32. package/dist/esm/pm-plugins/scoring-pipeline.js +163 -18
  33. package/dist/esm/pm-plugins/slow-lane-client.js +100 -37
  34. package/dist/esm/pm-plugins/text-predictor.js +1953 -277
  35. package/dist/types/analytics/ufo.d.ts +1 -0
  36. package/dist/types/pm-plugins/artifact-loader.d.ts +44 -0
  37. package/dist/types/pm-plugins/artifacts-manifest.d.ts +3 -0
  38. package/dist/types/pm-plugins/autocomplete-plugin.d.ts +39 -0
  39. package/dist/types/pm-plugins/canonical-lm-scoring.d.ts +158 -0
  40. package/dist/types/pm-plugins/debug-mode.d.ts +36 -1
  41. package/dist/types/pm-plugins/ghost-text-decoration.d.ts +5 -2
  42. package/dist/types/pm-plugins/local-slow-lane-client.d.ts +87 -54
  43. package/dist/types/pm-plugins/scoring-pipeline.d.ts +33 -1
  44. package/dist/types/pm-plugins/slow-lane-client.d.ts +51 -3
  45. package/dist/types/pm-plugins/text-predictor.d.ts +120 -2
  46. package/package.json +2 -2
  47. package/src/analytics/ufo.ts +5 -0
  48. package/src/pm-plugins/artifact-loader.ts +92 -0
  49. package/src/pm-plugins/artifacts-manifest.ts +3 -0
  50. package/src/pm-plugins/autocomplete-plugin.ts +517 -133
  51. package/src/pm-plugins/canonical-lm-scoring.ts +412 -0
  52. package/src/pm-plugins/debug-mode.ts +109 -7
  53. package/src/pm-plugins/ghost-text-decoration.ts +13 -4
  54. package/src/pm-plugins/local-slow-lane-client.ts +1302 -386
  55. package/src/pm-plugins/scoring-pipeline.ts +167 -17
  56. package/src/pm-plugins/slow-lane-client.ts +147 -62
  57. package/src/pm-plugins/text-predictor.ts +2006 -255
  58. package/CANONICAL_FIX__DO_NOT_USE_ME_A/package.json +0 -8
  59. package/CANONICAL_FIX__DO_NOT_USE_ME_B/package.json +0 -8
  60. package/CANONICAL_FIX__DO_NOT_USE_ME_C/package.json +0 -8
  61. package/dist/cjs/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -18
  62. package/dist/cjs/entry-points/src-pm-plugins-slow-lane-client.js +0 -36
  63. package/dist/cjs/entry-points/src-pm-plugins-text-predictor.js +0 -66
  64. package/dist/es2019/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  65. package/dist/es2019/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  66. package/dist/es2019/entry-points/src-pm-plugins-text-predictor.js +0 -2
  67. package/dist/esm/entry-points/src-pm-plugins-autocomplete-plugin.js +0 -2
  68. package/dist/esm/entry-points/src-pm-plugins-slow-lane-client.js +0 -2
  69. package/dist/esm/entry-points/src-pm-plugins-text-predictor.js +0 -2
  70. package/dist/types/entry-points/src-pm-plugins-autocomplete-plugin.d.ts +0 -2
  71. package/dist/types/entry-points/src-pm-plugins-slow-lane-client.d.ts +0 -2
  72. package/dist/types/entry-points/src-pm-plugins-text-predictor.d.ts +0 -2
  73. package/scripts/gen_first_token_to_words.py +0 -170
  74. package/src/entry-points/src-pm-plugins-autocomplete-plugin.ts +0 -7
  75. package/src/entry-points/src-pm-plugins-slow-lane-client.ts +0 -13
  76. package/src/entry-points/src-pm-plugins-text-predictor.ts +0 -14
  77. package/src/pm-plugins/autocomplete-plugin/package.json +0 -8
  78. package/src/pm-plugins/slow-lane-client/package.json +0 -8
  79. package/src/pm-plugins/text-predictor/package.json +0 -8
@@ -1,39 +1,39 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- var _typeof = require("@babel/runtime/helpers/typeof");
4
+ var _typeof3 = require("@babel/runtime/helpers/typeof");
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.wrapForArctic = exports.createLocalSlowLaneClient = exports.computeBePayload = exports.LOCAL_MLC_EMBEDDING_MODEL_ID = exports.LOCAL_MLC_CAUSAL_MODEL_ID = exports.BE_PARITY = void 0;
8
+ exports.wrapForArctic = exports.createLocalSlowLaneClient = exports.LOCAL_MLC_EMBEDDING_MODEL_ID = exports.LOCAL_MLC_CAUSAL_MODEL_ID = exports.LOCAL_INFERENCE = exports.CANONICAL_SCORING = void 0;
9
9
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
10
10
  var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
11
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
12
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
12
13
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
- var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
14
- var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
15
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
14
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
16
15
  var _ufo = require("../analytics/ufo");
16
+ var _artifactLoader = require("./artifact-loader");
17
17
  var _artifactsManifest = require("./artifacts-manifest");
18
+ var _canonicalLmScoring = require("./canonical-lm-scoring");
18
19
  var _debugMode = require("./debug-mode");
20
+ var _scoringPipeline = require("./scoring-pipeline");
19
21
  var _slowLaneClient = require("./slow-lane-client");
20
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
21
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
22
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t6 in e) "default" !== _t6 && {}.hasOwnProperty.call(e, _t6) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t6)) && (i.get || i.set) ? o(f, _t6, i) : f[_t6] = e[_t6]); return f; })(e, t); }
23
22
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
24
23
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
25
- function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } /**
24
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
25
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
26
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
27
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t8 in e) "default" !== _t8 && {}.hasOwnProperty.call(e, _t8) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t8)) && (i.get || i.set) ? o(f, _t8, i) : f[_t8] = e[_t8]); return f; })(e, t); } /**
26
28
  * Local Slow Lane Client: On-device inference via @mlc-ai/web-llm.
27
29
  *
28
30
  * Drop-in replacement for the network-based slow-lane-client. Instead of calling
29
31
  * a backend API, this client runs two models in the browser via WebGPU, in a
30
32
  * single MLCEngine, to reproduce the BE encoder's outputs on-device:
31
33
  *
32
- * - Causal LM (SmolLM2-135M-Instruct): one decode step per word boundary. A
33
- * registered LogitProcessor captures the raw next-token logits, which
34
- * `computeBePayload` turns into a whole-word `lm_logits` payload — a faithful
35
- * port of the BE `CausalLMEncoder._get_top_k_probs` (masked softmax over the
36
- * vocab's first-tokens, prefix expansion, L2 reservation, log-space pooling).
34
+ * - Causal LM (SmolLM2-135M-Instruct): context-keyed one-step boundary primes
35
+ * provide canonical first-token logits. A persistent token-prefix scheduler
36
+ * expands shared paths and exact-scores only plausible finalists.
37
37
  * - Semantic embedder (Snowflake Arctic Embed S): produces the real 384-d
38
38
  * `semantic_vector`. Inputs are wrapped as passages (see `wrapForArctic`) so
39
39
  * the runtime vector lands in the same space as the precomputed word bin.
@@ -43,7 +43,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
43
43
  * the main thread is viable:
44
44
  *
45
45
  * - WebGPU GPU compute is inherently async (doesn't block the main thread)
46
- * - CPU overhead (BE-parity post-processing) is a few ms
46
+ * - CPU overhead for named-token reads and cache bookkeeping is small
47
47
  * - Per-inference latency is well within autocomplete expectations
48
48
  * (~250 ms between word boundaries)
49
49
  *
@@ -54,10 +54,12 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
54
54
  * - Standard npm import — just works
55
55
  *
56
56
  * ── Interface ────────────────────────────────────────────────────────────
57
- * Same shape as createSlowLaneClient so text-predictor.ts needs zero changes.
58
- * The client exposes getContextVector() and getLmLogits() which are populated
59
- * asynchronously after each updateContext() call.
57
+ * Same base shape as createSlowLaneClient, plus on-device canonical-surface
58
+ * scoring. Semantic updates remain word-boundary timed; causal work is requested
59
+ * independently for exact pre-surface contexts.
60
60
  */
61
+ /** Which of the two causal-LM call shapes a measurement or log line describes. */
62
+
61
63
  // ─── Types ───────────────────────────────────────────────────────────────────
62
64
 
63
65
  // Same return type as createSlowLaneClient for drop-in compatibility
@@ -109,22 +111,7 @@ var LOCAL_MLC_EMBEDDING_MODEL_ID = exports.LOCAL_MLC_EMBEDDING_MODEL_ID = 'snowf
109
111
  var wrapForArctic = exports.wrapForArctic = function wrapForArctic(text) {
110
112
  return "[CLS] ".concat(text, " [SEP]");
111
113
  };
112
-
113
- /**
114
- * BE-parity constants — must match `CausalLMEncoder` defaults in the Python
115
- * sidecar (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`) and
116
- * `SlowLaneEngine` (`typeahead_context_encoding.py`) so local payloads behave
117
- * identically to the server-client setup.
118
- */
119
- var BE_PARITY = exports.BE_PARITY = {
120
- /** Final payload size cap (BE: `top_k_words`). */
121
- TOP_K_WORDS: 2000,
122
- /** L2 (domain) words admitted unconditionally before pooling (BE: `reserved_l2_slots`). */
123
- RESERVED_L2_SLOTS: 500,
124
- /** Log-space additive bias favouring L2 over L3 in the pool (BE: `l2_bias`). */
125
- L2_BIAS: 1.0,
126
- /** Drop words below this probability from the final payload (BE: `> 0.00001`). */
127
- MIN_PROB: 0.00001,
114
+ var LOCAL_INFERENCE = exports.LOCAL_INFERENCE = {
128
115
  /**
129
116
  * Word-level approximation of the BE causal LM token limit.
130
117
  *
@@ -145,6 +132,72 @@ var BE_PARITY = exports.BE_PARITY = {
145
132
  */
146
133
  MAX_CONTEXT_WORDS: 100
147
134
  };
135
+ var CANONICAL_SCORING = exports.CANONICAL_SCORING = {
136
+ /**
137
+ * How many contexts keep their prefilled state before the oldest is dropped.
138
+ *
139
+ * Sizes every context-keyed cache together on purpose: an expansion needs
140
+ * both the boundary logits and the candidate list to still be resident, so
141
+ * bounding them separately would evict half of a context and strand the
142
+ * other half.
143
+ *
144
+ * The context of a keystroke is the text *before* the word being typed,
145
+ * which does not change while that word is typed, so one word should cost
146
+ * one prefill and then hit. Measured hit rate was 45% over the first 84s of
147
+ * a session and 24% over the following four minutes, well short of that, and
148
+ * prompt prefills per decision rose 0.80 → 1.25 across the same split while
149
+ * cost per prefill stayed flat. Contexts were being dropped while still live.
150
+ *
151
+ * A boundary entry holds a `Float32Array` over the 49,152-token vocabulary,
152
+ * so each one is ~192KB and this bound is the dominant term in the scorer's
153
+ * footprint: ~24MB resident here, against ~6MB at the 32 this replaced. That
154
+ * cost is what kept the bound low, not a hit rate anyone had measured.
155
+ */
156
+ BOUNDARY_CACHE_MAX: 128,
157
+ EXACT_MAX_TARGET_TOKENS: 8,
158
+ PREFIX_CACHE_MAX: 256,
159
+ /**
160
+ * How many candidates compete for expansion in one context.
161
+ *
162
+ * Each distinct token prefix among them is a separate branch, and a branch
163
+ * the engine is not already standing on costs a prompt prefill before its
164
+ * first decode. A wide field therefore spreads a decision's round trips
165
+ * across candidates and finishes none of them inside the budget.
166
+ */
167
+ PROGRESSIVE_CANDIDATES_MAX: 8,
168
+ /**
169
+ * Ceiling on distinct token prefixes explored per context.
170
+ *
171
+ * Raising this to 8 to give long surfaces more room did the opposite: model
172
+ * calls per decision went 2.35 → 4.32, the warm-KV extend share fell 40% →
173
+ * 31% as the extra branches displaced the live path, the prefix queue backed
174
+ * up to 29 deep, and acceptances per thousand decisions fell 9.3 → 6.7. No
175
+ * phrase was shown either way, so breadth was never the binding constraint.
176
+ *
177
+ * What blocked them then was normalisation against a short word's per-token
178
+ * mean, which has since been replaced by a posterior over sequence
179
+ * log-likelihoods. That removes the bias towards short surfaces but does not
180
+ * by itself make phrases reachable: a longer surface is strictly less likely
181
+ * than a shorter one, so a phrase sharing a shortlist with a unigram still
182
+ * holds little of its mass. Whether the remaining gap is the threshold or the
183
+ * comparison is still open.
184
+ */
185
+ PROGRESSIVE_EXPANSIONS_PER_CONTEXT_MAX: 4,
186
+ PROGRESSIVE_INPUT_MAX: 400,
187
+ SURFACE_CACHE_MAX: 128,
188
+ TIER_A_PRIMES_MAX: 3
189
+ };
190
+ /**
191
+ * How much optimistic score we give up to stay on the warm KV path.
192
+ *
193
+ * Expanding a prefix the cache already holds costs one decode step, while
194
+ * branching to any other prefix costs a full prompt prefill first. Measured,
195
+ * that is about 30ms against about 66ms, so continuing the live path is worth
196
+ * roughly half a round trip and the margin has to be wide enough to reflect
197
+ * that. At the previous 0.05 almost any ranking difference was enough to
198
+ * abandon the sequence, and 70% of expansions ended up re-prefilling.
199
+ */
200
+ var PROGRESSIVE_WARM_PATH_MARGIN = 0.25;
148
201
  var splitOnWhitespace = function splitOnWhitespace(text) {
149
202
  var trimmed = text.trim();
150
203
  if (trimmed === '') {
@@ -179,114 +232,60 @@ var truncateToLastNWords = function truncateToLastNWords(text, n) {
179
232
  return words.length <= n ? text : words.slice(-n).join(' ');
180
233
  };
181
234
 
182
- // ─── Logit capture ─────────────────────────────────────────────────────────
183
-
184
- /**
185
- * A LogitProcessor that captures the raw next-token logits and passes them
186
- * through unmodified.
187
- *
188
- * web-llm invokes `processLogits` on the CPU after the model's forward pass and
189
- * before sampling, handing us the full `Float32Array(vocab_size)` at the current
190
- * decode position. We copy it off web-llm's shared buffer (which it may reuse
191
- * across calls) and return the original untouched so sampling is unaffected.
192
- *
193
- * This is the raw-logit access the BE-parity algorithm needs (masked softmax +
194
- * prefix expansion, consumed in a later step). Registered for the causal LM
195
- * only — the embedder never decodes tokens, so it produces no logits.
196
- */
197
- var CapturingLogitProcessor = /*#__PURE__*/(0, _createClass2.default)(function CapturingLogitProcessor() {
198
- var _this = this;
199
- (0, _classCallCheck2.default)(this, CapturingLogitProcessor);
200
- (0, _defineProperty2.default)(this, "captured", null);
201
- (0, _defineProperty2.default)(this, "processLogits", function (logits) {
202
- // Copy off web-llm's shared buffer — it may reuse `logits` across calls.
203
- _this.captured = new Float32Array(logits);
204
- return logits;
205
- });
206
- (0, _defineProperty2.default)(this, "processSampledToken", function () {
207
- // No-op — we don't track sampled tokens.
208
- });
209
- (0, _defineProperty2.default)(this, "resetState", function () {
210
- _this.captured = null;
211
- });
212
- }); // ─── BE-parity data + algorithm ──────────────────────────────────────────────
213
235
  /**
214
- * Prefix-expansion map: first-token id → words whose space-prefixed SmolLM2
215
- * encoding starts with that token. Generated offline by
216
- * `scripts/gen_first_token_to_words.py`, which mirrors the BE's in-memory map
217
- * (`CausalLMEncoder._ensure_loaded`).
218
- *
219
- * Populated lazily by `loadBePayloadData()` from a dynamically-imported JSON so
220
- * the (large) payload is only fetched when the local client is actually
221
- * initialised — keeping it out of the editor's main chunk for the vast majority
222
- * of users (who run with `useLocalModel` off).
236
+ * Full canonical leading-space token sequence for every served surface. The
237
+ * producer keeps the existing `phrase-continuation-tokens.json` wire name while
238
+ * expanding its key set to the complete word/bigram/phrase union.
223
239
  */
224
- var firstTokenToWords = new Map();
225
-
226
- /**
227
- * L2 (Atlassian-domain) word set, derived from the keys of `vocabulary_10k.json`.
228
- * Used by `computeBePayload` for tier-aware ranking: any word in the prefix map
229
- * that is not in this set is treated as L3 (general English), matching the BE.
230
- * Populated lazily alongside `firstTokenToWords` — see `loadBePayloadData()`.
231
- */
232
- var l2Words = new Set();
233
-
234
- /**
235
- * Array of token IDs that appear as a first token for at least one vocabulary
236
- * word. Derived from `firstTokenToWords` when the data loads so `computeBePayload`
237
- * does not re-allocate this array on every word-boundary call.
238
- */
239
- var prefixMapTokenIds = [];
240
+ var surfaceTokenIds = new Map();
241
+ var surfaceTokenTrie = new _canonicalLmScoring.CanonicalSurfaceTokenTrie();
240
242
 
241
243
  /** De-dupes concurrent loads and lets repeated calls await the same payload. */
242
- var bePayloadDataPromise;
244
+ var surfaceTokenIdsPromise;
245
+ var isPhraseContinuationTokens = function isPhraseContinuationTokens(payload) {
246
+ if (payload == null || (0, _typeof2.default)(payload) !== 'object') {
247
+ return false;
248
+ }
249
+ return Object.values(payload).every(function (value) {
250
+ return Array.isArray(value) && value.every(function (entry) {
251
+ return typeof entry === 'number' && entry >= 0;
252
+ });
253
+ });
254
+ };
243
255
 
244
- /**
245
- * Lazily load and build the BE-parity lookup tables from their CDN payloads.
246
- * Both artifacts are fetched on demand so neither is downloaded unless local
247
- * inference is initialised.
248
- *
249
- * :returns:
250
- * A promise that resolves once `firstTokenToWords`, `l2Words` and
251
- * `prefixMapTokenIds` are populated.
252
- */
253
- var loadBePayloadData = function loadBePayloadData() {
254
- if (!bePayloadDataPromise) {
255
- bePayloadDataPromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
256
- var _yield$Promise$all, _yield$Promise$all2, firstTokenToWordsData, vocabularyData;
256
+ /** Lazily load the producer's full-union canonical surface token map. */
257
+ var loadCanonicalSurfaceTokens = function loadCanonicalSurfaceTokens() {
258
+ if (!surfaceTokenIdsPromise) {
259
+ surfaceTokenIdsPromise = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
260
+ var continuationTokensData;
257
261
  return _regenerator.default.wrap(function (_context) {
258
262
  while (1) switch (_context.prev = _context.next) {
259
263
  case 0:
260
264
  _context.next = 1;
261
- return Promise.all([(0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.FIRST_TOKEN_TO_WORDS), (0, _artifactsManifest.fetchArtifactJson)(_artifactsManifest.ARTIFACT_NAME.VOCABULARY)]);
265
+ return (0, _artifactLoader.fetchAutocompleteArtifactJson)(_artifactsManifest.ARTIFACT_NAME.PHRASE_CONTINUATION_TOKENS, {
266
+ summarize: function summarize(payload) {
267
+ return "".concat(Object.keys(payload).length, " surfaces");
268
+ },
269
+ validate: isPhraseContinuationTokens
270
+ }).catch(function () {
271
+ return null;
272
+ });
262
273
  case 1:
263
- _yield$Promise$all = _context.sent;
264
- _yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 2);
265
- firstTokenToWordsData = _yield$Promise$all2[0];
266
- vocabularyData = _yield$Promise$all2[1];
267
- if (!(firstTokenToWordsData == null || (vocabularyData === null || vocabularyData === void 0 ? void 0 : vocabularyData.words) == null)) {
268
- _context.next = 2;
269
- break;
270
- }
271
- throw new Error("[LocalSlowLane] unexpected artifact payload \u2014 " + "firstTokenToWordsData=".concat(firstTokenToWordsData == null ? 'null/undefined' : 'defined', ", ") + "vocabularyData=".concat(vocabularyData == null ? 'null/undefined' : vocabularyData.words == null ? 'defined but missing .words' : 'defined'));
272
- case 2:
273
- firstTokenToWords = new Map(Object.entries(firstTokenToWordsData).map(function (_ref2) {
274
+ continuationTokensData = _context.sent;
275
+ surfaceTokenIds = new Map(Object.entries(continuationTokensData !== null && continuationTokensData !== void 0 ? continuationTokensData : {}).map(function (_ref2) {
274
276
  var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
275
- tokenId = _ref3[0],
276
- words = _ref3[1];
277
- return [Number(tokenId), words];
277
+ surface = _ref3[0],
278
+ tokenIds = _ref3[1];
279
+ return [surface.toLowerCase(), tokenIds];
278
280
  }));
279
- l2Words = new Set(Object.keys(vocabularyData.words));
280
- prefixMapTokenIds = Array.from(firstTokenToWords.keys());
281
+ surfaceTokenTrie = new _canonicalLmScoring.CanonicalSurfaceTokenTrie(surfaceTokenIds.entries());
281
282
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
282
283
  // eslint-disable-next-line no-console
283
- console.log('%c[LocalSlowLane] %c✅ BE-parity payload data loaded:', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50; font-weight: bold;', {
284
- firstTokenToWordsEntries: firstTokenToWords.size,
285
- l2WordsCount: l2Words.size,
286
- prefixMapTokenIdsLength: prefixMapTokenIds.length
284
+ console.log("%c[CTC:model] %c".concat(surfaceTokenIds.size > 0 ? '✅ canonical surface tokens loaded:' : '⚠️ canonical surface tokens unavailable — LM evidence will remain absent:'), 'color: #9c27b0; font-weight: bold;', surfaceTokenIds.size > 0 ? 'color: #4caf50; font-weight: bold;' : 'color: #ff9800; font-weight: bold;', {
285
+ surfaces: surfaceTokenIds.size
287
286
  });
288
287
  }
289
- case 3:
288
+ case 2:
290
289
  case "end":
291
290
  return _context.stop();
292
291
  }
@@ -295,225 +294,11 @@ var loadBePayloadData = function loadBePayloadData() {
295
294
  // Don't cache a rejected promise — a transient import failure would
296
295
  // otherwise prevent the local model from ever initialising again this
297
296
  // session. Reset so the next init attempt retries.
298
- bePayloadDataPromise = undefined;
297
+ surfaceTokenIdsPromise = undefined;
299
298
  throw e;
300
299
  });
301
300
  }
302
- return bePayloadDataPromise;
303
- };
304
-
305
- /**
306
- * Convert a raw next-token logit vector into a whole-word probability payload,
307
- * faithfully porting the BE `CausalLMEncoder._get_top_k_probs`
308
- * (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`).
309
- *
310
- * Steps: (1) numerically-stable masked softmax over only the token ids present
311
- * in the prefix-expansion map; (2) spread each token's probability to every
312
- * whole word sharing that first token, taking the max; (3) reserve the top L2
313
- * words unconditionally; (4) rank the remainder in a log-space pool with an
314
- * additive L2 bias; (5) emit raw probabilities for the survivors, lowercased
315
- * and trimmed at `MIN_PROB`.
316
- *
317
- * :params:
318
- * rawLogits: Full-vocabulary logits from the LM's single decode step
319
- * prefixMap: Map of first-token id to the words starting with that token
320
- * domainWords: Set of L2 (domain) words, for tier-aware ranking
321
- * :returns:
322
- * A record of lowercase word to probability — the BE `lm_logits` payload
323
- */
324
- var computeBePayload = exports.computeBePayload = function computeBePayload(rawLogits, prefixMap, domainWords) {
325
- var validTokenIds = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : prefixMapTokenIds;
326
- // 1. Numerically-stable masked softmax over validTokenIds only.
327
- var maxLogit = -Infinity;
328
- var _iterator = _createForOfIteratorHelper(validTokenIds),
329
- _step;
330
- try {
331
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
332
- var id = _step.value;
333
- var v = rawLogits[id];
334
- if (v > maxLogit) {
335
- maxLogit = v;
336
- }
337
- }
338
- } catch (err) {
339
- _iterator.e(err);
340
- } finally {
341
- _iterator.f();
342
- }
343
- var sumExp = 0;
344
- var expByToken = new Map();
345
- var _iterator2 = _createForOfIteratorHelper(validTokenIds),
346
- _step2;
347
- try {
348
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
349
- var _id = _step2.value;
350
- var e = Math.exp(rawLogits[_id] - maxLogit);
351
- expByToken.set(_id, e);
352
- sumExp += e;
353
- }
354
-
355
- // 2. Prefix expansion with max aggregation (probabilities sum to 1 over the
356
- // masked subset, so divide each token's exp by sumExp on the fly).
357
- } catch (err) {
358
- _iterator2.e(err);
359
- } finally {
360
- _iterator2.f();
361
- }
362
- var wordProbs = new Map();
363
- var _iterator3 = _createForOfIteratorHelper(prefixMap),
364
- _step3;
365
- try {
366
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
367
- var _expByToken$get;
368
- var _step3$value = (0, _slicedToArray2.default)(_step3.value, 2),
369
- _id2 = _step3$value[0],
370
- words = _step3$value[1];
371
- var _p = sumExp > 0 ? ((_expByToken$get = expByToken.get(_id2)) !== null && _expByToken$get !== void 0 ? _expByToken$get : 0) / sumExp : 0;
372
- var _iterator8 = _createForOfIteratorHelper(words),
373
- _step8;
374
- try {
375
- for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
376
- var _wordProbs$get2;
377
- var _w = _step8.value;
378
- var prev = (_wordProbs$get2 = wordProbs.get(_w)) !== null && _wordProbs$get2 !== void 0 ? _wordProbs$get2 : 0;
379
- if (_p > prev) {
380
- wordProbs.set(_w, _p);
381
- }
382
- }
383
- } catch (err) {
384
- _iterator8.e(err);
385
- } finally {
386
- _iterator8.f();
387
- }
388
- }
389
-
390
- // 3. Split into L2 / L3 and reserve the top L2 slots unconditionally.
391
- } catch (err) {
392
- _iterator3.e(err);
393
- } finally {
394
- _iterator3.f();
395
- }
396
- var l2Matches = [];
397
- var l3Matches = [];
398
- var _iterator4 = _createForOfIteratorHelper(wordProbs),
399
- _step4;
400
- try {
401
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
402
- var _step4$value = (0, _slicedToArray2.default)(_step4.value, 2),
403
- _w2 = _step4$value[0],
404
- _p2 = _step4$value[1];
405
- if (domainWords.has(_w2)) {
406
- l2Matches.push([_w2, _p2]);
407
- } else {
408
- l3Matches.push([_w2, _p2]);
409
- }
410
- }
411
- } catch (err) {
412
- _iterator4.e(err);
413
- } finally {
414
- _iterator4.f();
415
- }
416
- l2Matches.sort(function (a, b) {
417
- return b[1] - a[1];
418
- });
419
- var reserved = l2Matches.slice(0, BE_PARITY.RESERVED_L2_SLOTS);
420
-
421
- // 4. Pool the leftovers in log space; the L2 bias only affects ranking here.
422
- // Words in l2Matches are unique and the array is sorted descending, so the
423
- // non-reserved entries are exactly the tail after the reserved prefix — slice
424
- // it directly rather than allocating a Set and scanning every entry on this
425
- // hot path (runs ~every word boundary while typing).
426
- var pool = [];
427
- var _iterator5 = _createForOfIteratorHelper(l2Matches.slice(BE_PARITY.RESERVED_L2_SLOTS)),
428
- _step5;
429
- try {
430
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
431
- var _step5$value = (0, _slicedToArray2.default)(_step5.value, 2),
432
- _w3 = _step5$value[0],
433
- _p3 = _step5$value[1];
434
- pool.push([_w3, Math.log(Math.max(_p3, 1e-10)) + BE_PARITY.L2_BIAS]);
435
- }
436
- } catch (err) {
437
- _iterator5.e(err);
438
- } finally {
439
- _iterator5.f();
440
- }
441
- for (var _i = 0, _l3Matches = l3Matches; _i < _l3Matches.length; _i++) {
442
- var _l3Matches$_i = (0, _slicedToArray2.default)(_l3Matches[_i], 2),
443
- w = _l3Matches$_i[0],
444
- p = _l3Matches$_i[1];
445
- pool.push([w, Math.log(Math.max(p, 1e-10))]);
446
- }
447
- pool.sort(function (a, b) {
448
- return b[1] - a[1];
449
- });
450
- var remainingSlots = Math.max(0, BE_PARITY.TOP_K_WORDS - reserved.length);
451
- var poolWinners = pool.slice(0, remainingSlots);
452
-
453
- // 5. Assemble payload: store RAW probabilities (the bias was ranking-only),
454
- // lowercase keys, trimmed at MIN_PROB. Reserved first, then pool winners.
455
- // Reserved entries are written first; pool-winner writes must NOT clobber a
456
- // reserved entry whose normalised key collides (two source words can
457
- // `.trim().toLowerCase()` to the same key — e.g. "Function" vs "function ").
458
- // Without the existence guard, a low-probability pool winner would silently
459
- // overwrite the (higher-probability) reserved entry, degrading top-K
460
- // quality in a way that's invisible from the debug summary.
461
- var result = {};
462
- var addEntry = function addEntry(word, prob, allowOverwrite) {
463
- if (prob <= BE_PARITY.MIN_PROB) {
464
- return;
465
- }
466
- var key = word.trim().toLowerCase();
467
- if (!allowOverwrite && key in result) {
468
- return;
469
- }
470
- result[key] = prob;
471
- };
472
- var _iterator6 = _createForOfIteratorHelper(reserved),
473
- _step6;
474
- try {
475
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
476
- var _step6$value = (0, _slicedToArray2.default)(_step6.value, 2),
477
- _w4 = _step6$value[0],
478
- _p4 = _step6$value[1];
479
- addEntry(_w4, _p4, true);
480
- }
481
- } catch (err) {
482
- _iterator6.e(err);
483
- } finally {
484
- _iterator6.f();
485
- }
486
- var _iterator7 = _createForOfIteratorHelper(poolWinners),
487
- _step7;
488
- try {
489
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
490
- var _wordProbs$get3;
491
- var _step7$value = (0, _slicedToArray2.default)(_step7.value, 1),
492
- _w5 = _step7$value[0];
493
- addEntry(_w5, (_wordProbs$get3 = wordProbs.get(_w5)) !== null && _wordProbs$get3 !== void 0 ? _wordProbs$get3 : 0, false);
494
- }
495
- } catch (err) {
496
- _iterator7.e(err);
497
- } finally {
498
- _iterator7.f();
499
- }
500
- if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
501
- var topReserved = reserved.slice(0, 5).map(function (_ref4) {
502
- var _ref5 = (0, _slicedToArray2.default)(_ref4, 2),
503
- w = _ref5[0],
504
- p = _ref5[1];
505
- return "".concat(w, ":").concat((p * 100).toFixed(2), "%");
506
- }).join(', ');
507
- var topPool = poolWinners.slice(0, 5).map(function (_ref6) {
508
- var _wordProbs$get;
509
- var _ref7 = (0, _slicedToArray2.default)(_ref6, 1),
510
- w = _ref7[0];
511
- return "".concat(w, ":").concat((((_wordProbs$get = wordProbs.get(w)) !== null && _wordProbs$get !== void 0 ? _wordProbs$get : 0) * 100).toFixed(2), "%");
512
- }).join(', ');
513
- // eslint-disable-next-line no-console
514
- console.log('%c[computeBePayload] %c%d valid tokens → %d words expanded | L2: %d / L3: %d | reserved: %d | pool winners: %d | final: %d words\n maxLogit(masked): %s | sumExp: %s\n top reserved L2: %s\n top pool: %s', 'color: #9c27b0; font-weight: bold;', 'color: inherit;', validTokenIds.length, wordProbs.size, l2Matches.length, l3Matches.length, reserved.length, poolWinners.length, Object.keys(result).length, maxLogit.toFixed(3), sumExp.toFixed(1), topReserved || '(none)', topPool || '(none)');
515
- }
516
- return result;
301
+ return surfaceTokenIdsPromise;
517
302
  };
518
303
 
519
304
  // ─── Factory ─────────────────────────────────────────────────────────────────
@@ -529,9 +314,8 @@ var computeBePayload = exports.computeBePayload = function computeBePayload(rawL
529
314
  * const client = createLocalSlowLaneClient({ debounceMs: 300 });
530
315
  * // On word boundaries:
531
316
  * client.updateContext(docText);
532
- * // In scoring pipeline:
533
- * const vec = client.getContextVector();
534
- * const logits = client.getLmLogits();
317
+ * // Candidate scoring independently calls
318
+ * // primeBoundaryLm/requestProgressiveSurfaceScores.
535
319
  * // On plugin teardown:
536
320
  * client.destroy();
537
321
  * ```
@@ -541,6 +325,8 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
541
325
  var _config$debounceMs = config.debounceMs,
542
326
  debounceMs = _config$debounceMs === void 0 ? DEFAULT_DEBOUNCE_MS : _config$debounceMs,
543
327
  onUpdate = config.onUpdate,
328
+ onBoundaryLmUpdate = config.onBoundaryLmUpdate,
329
+ onSurfaceScoreUpdate = config.onSurfaceScoreUpdate,
544
330
  onStatus = config.onStatus,
545
331
  onLoadError = config.onLoadError,
546
332
  onLoadSuccess = config.onLoadSuccess,
@@ -550,8 +336,54 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
550
336
  surface = config.surface;
551
337
 
552
338
  // ── State ──────────────────────────────────────────────────────────────
339
+ var storedContextInput = null;
553
340
  var storedContextVector = null;
554
341
  var storedLmLogits = null;
342
+ var boundaryCache = new Map();
343
+ var prefixExpansionCache = new Map();
344
+ var surfaceScoreCache = new Map();
345
+ var causalInFlight = false;
346
+ var inFlightBoundaryContextKey = null;
347
+ var latestCausalFamilyKey = '';
348
+ var causalRequestsThisFamily = 0;
349
+ var causalGeneratedTokensThisFamily = 0;
350
+ var tierAPrimeCacheHitsThisFamily = 0;
351
+ var tierAPrimeCacheMissesThisFamily = 0;
352
+ var causalInferenceOrdinal = 0;
353
+ var causalInferenceByContext = new Map();
354
+ var causalInferenceByFamily = new Map();
355
+ var exactEvidenceCountByContext = new Map();
356
+ var pendingBoundaryPrimes = new Map();
357
+ var pendingPrefixExpansions = new Map();
358
+ var inFlightPrefixExpansions = new Set();
359
+ // The engine holds exactly one linear KV sequence. WebLLM lets us extend it
360
+ // (`forwardTokensAndSample`) or drop it (a text completion always resets
361
+ // first), but never fork or rewind it. Tracking what is currently
362
+ // materialised is what makes decoding continuous: expanding a token prefix
363
+ // that extends `kvPath` costs a single decode step, while any other prefix
364
+ // costs a fresh prompt prefill.
365
+ var kvContextKey = null;
366
+ var kvPath = [];
367
+ var isPrefixOf = function isPrefixOf(prefix, path) {
368
+ return prefix.length <= path.length && prefix.every(function (token, index) {
369
+ return token === path[index];
370
+ });
371
+ };
372
+ /**
373
+ * Whether running this expansion would extend the sequence the engine is
374
+ * already holding rather than discarding it for a fresh prompt prefill.
375
+ *
376
+ * `planProgressiveExpansion` also prefers a warm group, but it decides when
377
+ * the work is queued and the queue is drained later. A boundary prime or
378
+ * another context's expansion running in between moves the path out from
379
+ * under that choice, so the preference has to be re-checked at the moment
380
+ * something is picked up.
381
+ */
382
+ var extendsLiveKvPath = function extendsLiveKvPath(request) {
383
+ return kvContextKey === request.contextKey && isPrefixOf(kvPath, request.tokenPrefix);
384
+ };
385
+ var progressiveRequests = new Map();
386
+ var progressivePrefixesByContext = new Map();
555
387
  var debounceTimer = null;
556
388
  var lastRequestedText = '';
557
389
  var requestCounter = 0;
@@ -569,14 +401,12 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
569
401
  var initFailed = false;
570
402
  var engine = null;
571
403
  var engineInitPromise = null;
572
- // Captures raw next-token logits from the LM's single decode step. Registered
573
- // with the engine below; `lmLogitsCapture.captured` is consumed in a later step.
574
- var lmLogitsCapture = new CapturingLogitProcessor();
404
+ var causalLogitProcessor = new _canonicalLmScoring.CanonicalLogitProcessor();
575
405
  var unloadEngine = function unloadEngine(engineToUnload) {
576
406
  engineToUnload.unload().catch(function (error) {
577
407
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
578
408
  // eslint-disable-next-line no-console
579
- console.log('%c[LocalSlowLane] %cFailed to unload engine', 'color: #9c27b0; font-weight: bold;', 'color: inherit;', error);
409
+ console.log('%c[CTC:model] %cFailed to unload engine', 'color: #9c27b0; font-weight: bold;', 'color: inherit;', error);
580
410
  }
581
411
  });
582
412
  };
@@ -587,7 +417,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
587
417
  var message = "[".concat((progress.progress * 100).toFixed(0), "%] ").concat(progress.text);
588
418
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
589
419
  // eslint-disable-next-line no-console
590
- console.log("%c[LocalSlowLane] %c\uD83D\uDD04 ".concat(message), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
420
+ console.log("%c[CTC:model] %c\uD83D\uDD04 ".concat(message), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
591
421
  }
592
422
  onStatus === null || onStatus === void 0 || onStatus(message);
593
423
  };
@@ -600,8 +430,8 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
600
430
  * to a concrete hardware/browser limitation rather than a generic error.
601
431
  */
602
432
  var probeWebGpuCapabilities = /*#__PURE__*/function () {
603
- var _ref8 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
604
- var gpu, _adapter$limits, _adapter$limits2, adapter, vendor, architecture, _info, _requestAdapterInfo, _ref9, info, _t, _t2, _t3;
433
+ var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
434
+ var gpu, _adapter$limits, _adapter$limits2, adapter, vendor, architecture, _info, _requestAdapterInfo, _ref5, info, _t, _t2, _t3;
605
435
  return _regenerator.default.wrap(function (_context2) {
606
436
  while (1) switch (_context2.prev = _context2.next) {
607
437
  case 0:
@@ -638,7 +468,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
638
468
  break;
639
469
  case 4:
640
470
  _context2.next = 5;
641
- return (_requestAdapterInfo = (_ref9 = adapter).requestAdapterInfo) === null || _requestAdapterInfo === void 0 ? void 0 : _requestAdapterInfo.call(_ref9);
471
+ return (_requestAdapterInfo = (_ref5 = adapter).requestAdapterInfo) === null || _requestAdapterInfo === void 0 ? void 0 : _requestAdapterInfo.call(_ref5);
642
472
  case 5:
643
473
  _t = _context2.sent;
644
474
  case 6:
@@ -674,7 +504,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
674
504
  }, _callee2, null, [[1, 9], [3, 7]]);
675
505
  }));
676
506
  return function probeWebGpuCapabilities() {
677
- return _ref8.apply(this, arguments);
507
+ return _ref4.apply(this, arguments);
678
508
  };
679
509
  }();
680
510
 
@@ -716,7 +546,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
716
546
  var message = LOAD_FAILURE_MESSAGE[reason];
717
547
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
718
548
  // eslint-disable-next-line no-console
719
- console.log("[LocalSlowLane] Engine initialisation failed (".concat(reason, "): ").concat(debugDetail !== null && debugDetail !== void 0 ? debugDetail : message));
549
+ console.log("[CTC:model] Engine initialisation failed (".concat(reason, "): ").concat(debugDetail !== null && debugDetail !== void 0 ? debugDetail : message));
720
550
  }
721
551
  onStatus === null || onStatus === void 0 || onStatus("Engine initialisation failed: ".concat(message));
722
552
  onLoadError === null || onLoadError === void 0 || onLoadError({
@@ -730,8 +560,8 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
730
560
  initFailed = true;
731
561
  };
732
562
  var initEngine = /*#__PURE__*/function () {
733
- var _ref0 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
734
- var capabilities, startTime, _yield$Promise$all3, _yield$Promise$all4, _yield$Promise$all4$, MLCEngineCtor, prebuiltAppConfig, customModelRecord, appConfig, newEngine, loadDurationMs, errorMsg, _t4;
563
+ var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
564
+ var capabilities, startTime, _yield$Promise$all, _yield$Promise$all2, _yield$Promise$all2$, MLCEngineCtor, prebuiltAppConfig, customModelRecord, appConfig, newEngine, loadDurationMs, errorMsg, _t4;
735
565
  return _regenerator.default.wrap(function (_context3) {
736
566
  while (1) switch (_context3.prev = _context3.next) {
737
567
  case 0:
@@ -764,22 +594,22 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
764
594
  _context3.prev = 5;
765
595
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
766
596
  // eslint-disable-next-line no-console
767
- console.log("%c[LocalSlowLane] %c\uD83D\uDE80 Initialising MLC engine with models: ".concat(modelId, " (LM) + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, " (embedder)"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
597
+ console.log("%c[CTC:model] %c\uD83D\uDE80 Initialising MLC engine with models: ".concat(modelId, " (LM) + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, " (embedder)"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
768
598
  }
769
599
  onStatus === null || onStatus === void 0 || onStatus("Initialising models: ".concat(modelId, " + ").concat(LOCAL_MLC_EMBEDDING_MODEL_ID, "\u2026"));
770
600
 
771
- // Fetch the web-llm runtime and the BE-parity lookup tables in parallel;
601
+ // Fetch the web-llm runtime and canonical token artifact in parallel;
772
602
  // both are dynamically imported so they stay out of the main editor chunk.
773
603
  _context3.next = 6;
774
604
  return Promise.all([Promise.resolve().then(function () {
775
605
  return _interopRequireWildcard(require( /* webpackChunkName: "@atlaskit-internal_editor-plugin-autocomplete-mlc-web-llm" */'@mlc-ai/web-llm'));
776
- }), loadBePayloadData()]);
606
+ }), loadCanonicalSurfaceTokens()]);
777
607
  case 6:
778
- _yield$Promise$all3 = _context3.sent;
779
- _yield$Promise$all4 = (0, _slicedToArray2.default)(_yield$Promise$all3, 1);
780
- _yield$Promise$all4$ = _yield$Promise$all4[0];
781
- MLCEngineCtor = _yield$Promise$all4$.MLCEngine;
782
- prebuiltAppConfig = _yield$Promise$all4$.prebuiltAppConfig;
608
+ _yield$Promise$all = _context3.sent;
609
+ _yield$Promise$all2 = (0, _slicedToArray2.default)(_yield$Promise$all, 1);
610
+ _yield$Promise$all2$ = _yield$Promise$all2[0];
611
+ MLCEngineCtor = _yield$Promise$all2$.MLCEngine;
612
+ prebuiltAppConfig = _yield$Promise$all2$.prebuiltAppConfig;
783
613
  customModelRecord = customModelConfig ? _objectSpread(_objectSpread({
784
614
  model: customModelConfig.model,
785
615
  model_id: modelId,
@@ -801,7 +631,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
801
631
  newEngine = new MLCEngineCtor({
802
632
  appConfig: appConfig,
803
633
  initProgressCallback: initProgressCallback,
804
- logitProcessorRegistry: new Map([[modelId, lmLogitsCapture]])
634
+ logitProcessorRegistry: new Map([[modelId, causalLogitProcessor]])
805
635
  });
806
636
  _context3.next = 7;
807
637
  return newEngine.reload([modelId, LOCAL_MLC_EMBEDDING_MODEL_ID]);
@@ -819,13 +649,13 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
819
649
  loadDurationMs = Math.round(performance.now() - startTime);
820
650
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
821
651
  // eslint-disable-next-line no-console
822
- console.log('%c[LocalSlowLane] %c✅ Both models loaded and ready', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50;');
652
+ console.log('%c[CTC:model] %c✅ Both models loaded and ready', 'color: #9c27b0; font-weight: bold;', 'color: #4caf50;');
823
653
  // One-time identity summary so you can confirm which models are active
824
654
  // without digging through the init-progress scroll.
825
655
  // eslint-disable-next-line no-console
826
- console.log('%c[LocalSlowLane] %c🧠 Causal LM →', 'color: #9c27b0; font-weight: bold;', 'color: #2196f3; font-weight: bold;', modelId);
656
+ console.log('%c[CTC:model] %c🧠 Causal LM →', 'color: #9c27b0; font-weight: bold;', 'color: #2196f3; font-weight: bold;', modelId);
827
657
  // eslint-disable-next-line no-console
828
- console.log('%c[LocalSlowLane] %c🔢 Embedder →', 'color: #9c27b0; font-weight: bold;', 'color: #009688; font-weight: bold;', LOCAL_MLC_EMBEDDING_MODEL_ID);
658
+ console.log('%c[CTC:model] %c🔢 Embedder →', 'color: #9c27b0; font-weight: bold;', 'color: #009688; font-weight: bold;', LOCAL_MLC_EMBEDDING_MODEL_ID);
829
659
  }
830
660
  onStatus === null || onStatus === void 0 || onStatus('Model loaded and ready.');
831
661
  onLoadSuccess === null || onLoadSuccess === void 0 || onLoadSuccess({
@@ -848,7 +678,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
848
678
  }, _callee3, null, [[5, 9]]);
849
679
  }));
850
680
  return function initEngine() {
851
- return _ref0.apply(this, arguments);
681
+ return _ref6.apply(this, arguments);
852
682
  };
853
683
  }();
854
684
  var ensureEngineInitialized = function ensureEngineInitialized() {
@@ -863,53 +693,25 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
863
693
 
864
694
  // ── Inference ──────────────────────────────────────────────────────────
865
695
 
866
- /**
867
- * Run a single forward pass to produce the BE-parity slow-lane outputs.
868
- *
869
- * Two calls run in parallel on the shared engine:
870
- * - `completions.create({ max_tokens: 1 })` runs the causal LM for exactly
871
- * one decode step. We ignore the generated text; the LogitProcessor
872
- * captures the raw next-token logits during that step, which we turn into
873
- * a whole-word payload via `computeBePayload`.
874
- * - `embeddings.create(...)` runs the Arctic embedder to produce the real
875
- * 384-d semantic vector (passage-encoded; see `wrapForArctic`).
876
- */
696
+ /** Run the boundary-timed Arctic semantic inference independently of the LM. */
877
697
  var runInference = /*#__PURE__*/function () {
878
- var _ref1 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(text, requestId) {
879
- var experienceId, lmText, semanticText, arcticInput, captureCompletionTime, _data, tStart, tLmDone, tEmbDone, _yield$Promise$all5, _yield$Promise$all6, embeddingResponse, rawLogits, payload, embedding, sumSq, i, topTokens, errorMsg, _t5;
698
+ var _ref7 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(text, requestId) {
699
+ var experienceId, semanticText, arcticInput, _data, tStart, embeddingResponse, tEmbDone, embedding, sumSq, i, errorMsg, _t5;
880
700
  return _regenerator.default.wrap(function (_context4) {
881
701
  while (1) switch (_context4.prev = _context4.next) {
882
702
  case 0:
883
- captureCompletionTime = function _captureCompletionTim(promise, onResolved) {
884
- return promise.then(function (value) {
885
- onResolved(performance.now());
886
- return value;
887
- });
888
- };
889
703
  if (!(!engine || destroyed)) {
890
704
  _context4.next = 1;
891
705
  break;
892
706
  }
893
707
  return _context4.abrupt("return");
894
708
  case 1:
895
- experienceId = String(requestId); // Clear the capture buffer so we read only this pass's logits. The engine
896
- // serialises per-model requests and updateContext is debounced, so the
897
- // latest request's decode step is the last to populate `captured` before
898
- // we read it below; stale requests bail on the latestRequestId guard.
899
- lmLogitsCapture.resetState();
900
-
901
- // Apply BE-parity rolling-window truncation before both encoders.
902
- // BE semantic: last max_context_words words (typeahead_context_encoding.py:36)
903
- // BE causal LM: last max_context_tokens BPE tokens (causal_lm_encoder.py:194–198),
904
- // approximated here with word count (no tokenizer available on FE).
905
- lmText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_TOKENS);
906
- semanticText = truncateToLastNWords(text, BE_PARITY.MAX_CONTEXT_WORDS);
709
+ experienceId = String(requestId);
710
+ semanticText = truncateToLastNWords(text, LOCAL_INFERENCE.MAX_CONTEXT_WORDS);
907
711
  arcticInput = wrapForArctic(semanticText);
908
- if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
909
- // eslint-disable-next-line no-console
910
- console.log("%c[LocalSlowLane] %c\uD83D\uDD22 Arctic input (".concat(arcticInput.length, " chars, ").concat(splitOnWhitespace(semanticText).length, " words): \"").concat(arcticInput.length > 100 ? "".concat(arcticInput.slice(0, 100), "\u2026") : arcticInput, "\""), 'color: #9c27b0; font-weight: bold;', 'color: #009688;');
712
+ if ((0, _debugMode.isAutocompleteDebugVerbose)()) {
911
713
  // eslint-disable-next-line no-console
912
- console.log("%c[LocalSlowLane] %c\uD83E\uDDE0 LM input (".concat(lmText.length, " chars, ").concat(splitOnWhitespace(lmText).length, " words): \"").concat(lmText.length > 100 ? "".concat(lmText.slice(0, 100), "\u2026") : lmText, "\""), 'color: #9c27b0; font-weight: bold;', 'color: #2196f3;');
714
+ console.log("%c[CTC:model] %c\uD83D\uDD22 Arctic input (".concat(arcticInput.length, " chars, ").concat(splitOnWhitespace(semanticText).length, " words): \"").concat(arcticInput.length > 100 ? "".concat(arcticInput.slice(0, 100), "\u2026") : arcticInput, "\""), 'color: #9c27b0; font-weight: bold;', 'color: #009688;');
913
715
  }
914
716
  _context4.prev = 2;
915
717
  // Reuse the network slow-lane-fetch UFO experience (tagged isLocalLLM:true,
@@ -925,30 +727,17 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
925
727
  surface: surface
926
728
  } : {}));
927
729
  tStart = performance.now();
928
- tLmDone = 0;
929
- tEmbDone = 0;
930
730
  _context4.next = 3;
931
- return Promise.all([captureCompletionTime(engine.completions.create({
932
- model: modelId,
933
- prompt: lmText,
934
- max_tokens: 1,
935
- temperature: 0,
936
- logprobs: false
937
- }), function (resolvedAt) {
938
- tLmDone = resolvedAt;
939
- }), captureCompletionTime(engine.embeddings.create({
731
+ return engine.embeddings.create({
940
732
  model: LOCAL_MLC_EMBEDDING_MODEL_ID,
941
733
  input: arcticInput
942
- }), function (resolvedAt) {
943
- tEmbDone = resolvedAt;
944
- })]);
734
+ });
945
735
  case 3:
946
- _yield$Promise$all5 = _context4.sent;
947
- _yield$Promise$all6 = (0, _slicedToArray2.default)(_yield$Promise$all5, 2);
948
- embeddingResponse = _yield$Promise$all6[1];
736
+ embeddingResponse = _context4.sent;
737
+ tEmbDone = performance.now();
949
738
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
950
739
  // eslint-disable-next-line no-console
951
- console.log("%c[LocalSlowLane] %c\u23F1 LM: ".concat((tLmDone - tStart).toFixed(0), "ms | Embedder: ").concat((tEmbDone - tStart).toFixed(0), "ms | Total: ").concat((Math.max(tLmDone, tEmbDone) - tStart).toFixed(0), "ms"), 'color: #9c27b0; font-weight: bold;', 'color: #ff9800;');
740
+ console.log("%c[CTC:model] %c\u23F1 Embedder: ".concat((tEmbDone - tStart).toFixed(0), "ms"), 'color: #9c27b0; font-weight: bold;', 'color: #ff9800;');
952
741
  }
953
742
 
954
743
  // Discard stale results
@@ -963,24 +752,16 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
963
752
  } : {}));
964
753
  return _context4.abrupt("return");
965
754
  case 4:
966
- // ── LM logits: whole-word BE-parity payload ──────────────────
967
- rawLogits = lmLogitsCapture.captured;
968
- if (rawLogits) {
969
- payload = computeBePayload(rawLogits, firstTokenToWords, l2Words);
970
- storedLmLogits = Object.keys(payload).length > 0 ? payload : null;
971
- } else {
972
- storedLmLogits = null;
973
- }
974
-
975
755
  // ── Semantic vector: real 384-d Arctic embedding ─────────────
976
756
  // Guard against base64-encoded responses (encoding_format: 'base64' would
977
757
  // yield a string, and new Float32Array(string) silently produces an empty
978
758
  // array, corrupting downstream cosine-similarity scoring).
979
759
  embedding = (_data = embeddingResponse.data) === null || _data === void 0 || (_data = _data[0]) === null || _data === void 0 ? void 0 : _data.embedding;
980
760
  storedContextVector = Array.isArray(embedding) && embedding.length > 0 ? new Float32Array(embedding) : null;
761
+ storedContextInput = storedContextVector ? semanticText : null;
981
762
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
982
763
  // eslint-disable-next-line no-console
983
- console.groupCollapsed("%c[LocalSlowLane] %c\uD83D\uDCE5 Inference result (request #".concat(requestId, ")"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
764
+ console.groupCollapsed("%c[CTC:model] %c\uD83D\uDCE5 Inference result (request #".concat(requestId, ")"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
984
765
  if (storedContextVector) {
985
766
  sumSq = 0;
986
767
  for (i = 0; i < storedContextVector.length; i++) {
@@ -993,29 +774,14 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
993
774
  console.log('❌ No vector');
994
775
  }
995
776
  // eslint-disable-next-line no-console
996
- console.log(storedLmLogits ? "\u2705 lm_logits: ".concat(Object.keys(storedLmLogits).length, " words") : '❌ No lm_logits');
997
- if (storedLmLogits) {
998
- topTokens = Object.entries(storedLmLogits).sort(function (_ref10, _ref11) {
999
- var _ref12 = (0, _slicedToArray2.default)(_ref10, 2),
1000
- a = _ref12[1];
1001
- var _ref13 = (0, _slicedToArray2.default)(_ref11, 2),
1002
- b = _ref13[1];
1003
- return b - a;
1004
- }).slice(0, 10); // eslint-disable-next-line no-console
1005
- console.log('Top 10 predictions:', topTokens.map(function (_ref14) {
1006
- var _ref15 = (0, _slicedToArray2.default)(_ref14, 2),
1007
- t = _ref15[0],
1008
- p = _ref15[1];
1009
- return "".concat(t, ": ").concat((p * 100).toFixed(1), "%");
1010
- }).join(', '));
1011
- }
777
+ console.log('🧠 causal LM: independently primed by exact candidate contexts');
1012
778
  // eslint-disable-next-line no-console
1013
779
  console.groupEnd();
1014
780
  }
1015
781
  (0, _ufo.succeedExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, _objectSpread({
1016
782
  textLength: text.length,
1017
783
  hasVector: storedContextVector !== null,
1018
- hasLmLogits: storedLmLogits !== null,
784
+ hasLmLogits: false,
1019
785
  isLocalLLM: true
1020
786
  }, surface ? {
1021
787
  surface: surface
@@ -1023,7 +789,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1023
789
  onUpdate === null || onUpdate === void 0 || onUpdate({
1024
790
  textLength: text.length,
1025
791
  hasVector: storedContextVector !== null,
1026
- hasLmLogits: storedLmLogits !== null
792
+ hasLmLogits: false
1027
793
  });
1028
794
  _context4.next = 7;
1029
795
  break;
@@ -1041,6 +807,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1041
807
  } : {}));
1042
808
  return _context4.abrupt("return");
1043
809
  case 6:
810
+ storedContextInput = null;
1044
811
  storedContextVector = null;
1045
812
  storedLmLogits = null;
1046
813
  (0, _ufo.failExp)(_ufo.EXPERIENCE_NAME.SLOW_LANE_FETCH, experienceId, _objectSpread({
@@ -1057,7 +824,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1057
824
  errorMsg = _t5 instanceof Error ? _t5.message : String(_t5);
1058
825
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
1059
826
  // eslint-disable-next-line no-console
1060
- console.log("%c[LocalSlowLane] %c\u274C Inference error (request #".concat(requestId, "): ").concat(errorMsg), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
827
+ console.log("%c[CTC:model] %c\u274C Inference error (request #".concat(requestId, "): ").concat(errorMsg), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
1061
828
  }
1062
829
  case 7:
1063
830
  case "end":
@@ -1066,9 +833,859 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1066
833
  }, _callee4, null, [[2, 5]]);
1067
834
  }));
1068
835
  return function runInference(_x, _x2) {
836
+ return _ref7.apply(this, arguments);
837
+ };
838
+ }();
839
+
840
+ // ── Canonical causal scorer ─────────────────────────────────────────────
841
+
842
+ var surfaceCacheKey = function surfaceCacheKey(contextKey, surface) {
843
+ var _surfaceTokenIds$get;
844
+ var tokenIds = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : (_surfaceTokenIds$get = surfaceTokenIds.get(surface.toLowerCase())) !== null && _surfaceTokenIds$get !== void 0 ? _surfaceTokenIds$get : [];
845
+ return "".concat(contextKey, "\0").concat(surface.toLowerCase(), "\0").concat(tokenIds.join(','));
846
+ };
847
+
848
+ // Declared as functions rather than generic arrows: this file carries a `webpackChunkName`
849
+ // comment, which opts it into a build-time parse that reads `<T>(` as a JSX tag.
850
+ function getLru(cache, key) {
851
+ var value = cache.get(key);
852
+ if (value === undefined) {
853
+ return null;
854
+ }
855
+ cache.delete(key);
856
+ cache.set(key, value);
857
+ return value;
858
+ }
859
+ function setLru(cache, key, value, maxSize) {
860
+ cache.delete(key);
861
+ cache.set(key, value);
862
+ while (cache.size > maxSize) {
863
+ var oldest = cache.keys().next().value;
864
+ if (oldest === undefined) {
865
+ break;
866
+ }
867
+ cache.delete(oldest);
868
+ }
869
+ }
870
+ var prefixCacheKey = function prefixCacheKey(contextKey, tokenPrefix) {
871
+ return "".concat(contextKey, "\0").concat(tokenPrefix.join(','));
872
+ };
873
+ var logProgressiveState = function logProgressiveState(state, detail) {
874
+ if (!(0, _debugMode.isAutocompleteDebugEnabled)()) {
875
+ return;
876
+ }
877
+ // eslint-disable-next-line no-console
878
+ console.log("%c[CTC:model] %c\uD83D\uDD00 grouped ".concat(state, " \xB7 ").concat(detail), 'color: #9c27b0; font-weight: bold;', state === 'failed' ? 'color: #f44336;' : state === 'completed' || state === 'cached' ? 'color: #4caf50;' : 'color: #2196f3;');
879
+ };
880
+ var createCausalInferenceAggregate = function createCausalInferenceAggregate(startedAt) {
881
+ return {
882
+ e2eLatencyMs: 0,
883
+ failedRequests: 0,
884
+ firstStartedAt: startedAt,
885
+ promptTokens: 0,
886
+ promptUsageSamples: 0,
887
+ requestedCompletionTokens: 0,
888
+ requests: 0,
889
+ sampledOutputTokens: 0,
890
+ timeToFirstTokenMs: 0,
891
+ wallClockMs: 0,
892
+ webLlmDecodeSteps: 0,
893
+ webLlmUsageSamples: 0
894
+ };
895
+ };
896
+ var updateCausalInferenceAggregate = function updateCausalInferenceAggregate(cache, key, measurement) {
897
+ var _getLru;
898
+ var aggregate = (_getLru = getLru(cache, key)) !== null && _getLru !== void 0 ? _getLru : createCausalInferenceAggregate(performance.now() - measurement.wallClockMs);
899
+ aggregate.requests++;
900
+ aggregate.requestedCompletionTokens += measurement.requestedCompletionTokens;
901
+ aggregate.wallClockMs += measurement.wallClockMs;
902
+ if (measurement.outcome === 'failed') {
903
+ aggregate.failedRequests++;
904
+ }
905
+ if (measurement.promptTokens !== null) {
906
+ aggregate.promptTokens += measurement.promptTokens;
907
+ aggregate.promptUsageSamples++;
908
+ }
909
+ if (measurement.sampledOutputTokens !== null && measurement.webLlmDecodeSteps !== null) {
910
+ aggregate.sampledOutputTokens += measurement.sampledOutputTokens;
911
+ aggregate.webLlmDecodeSteps += measurement.webLlmDecodeSteps;
912
+ aggregate.webLlmUsageSamples++;
913
+ }
914
+ if (measurement.timeToFirstTokenMs !== null) {
915
+ aggregate.timeToFirstTokenMs += measurement.timeToFirstTokenMs;
916
+ }
917
+ if (measurement.e2eLatencyMs !== null) {
918
+ aggregate.e2eLatencyMs += measurement.e2eLatencyMs;
919
+ }
920
+ setLru(cache, key, aggregate, CANONICAL_SCORING.BOUNDARY_CACHE_MAX);
921
+ return aggregate;
922
+ };
923
+ var causalAggregateForLog = function causalAggregateForLog(aggregate) {
924
+ return {
925
+ calls: aggregate.requests,
926
+ elapsedMs: Number((performance.now() - aggregate.firstStartedAt).toFixed(1)),
927
+ failedCalls: aggregate.failedRequests,
928
+ inferenceWallMs: Number(aggregate.wallClockMs.toFixed(1)),
929
+ promptTokens: aggregate.promptUsageSamples > 0 ? aggregate.promptTokens : 'unreported',
930
+ requestedOutputTokens: aggregate.requestedCompletionTokens,
931
+ sampledOutputTokens: aggregate.webLlmUsageSamples > 0 ? aggregate.sampledOutputTokens : 'unreported',
932
+ timeToFirstTokenMs: aggregate.timeToFirstTokenMs > 0 ? Number(aggregate.timeToFirstTokenMs.toFixed(1)) : 'unreported',
933
+ webLlmE2eMs: aggregate.e2eLatencyMs > 0 ? Number(aggregate.e2eLatencyMs.toFixed(1)) : 'unreported',
934
+ webLlmDecodeSteps: aggregate.webLlmUsageSamples > 0 ? aggregate.webLlmDecodeSteps : 'unreported'
935
+ };
936
+ };
937
+ var recordCausalInference = function recordCausalInference(measurement) {
938
+ var _measurement$promptTo, _measurement$sampledO, _measurement$webLlmDe, _measurement$timeToFi, _measurement$timeToFi2;
939
+ if (!(0, _debugMode.isAutocompleteDebugEnabled)()) {
940
+ return;
941
+ }
942
+ var familyAggregate = updateCausalInferenceAggregate(causalInferenceByFamily, measurement.familyKey, measurement);
943
+ var contextAggregate = updateCausalInferenceAggregate(causalInferenceByContext, measurement.contextKey, measurement);
944
+ // One line plus two nested objects per LM call is heavy enough to distort
945
+ // the latencies it reports, so keep the per-call breakdown behind verbose.
946
+ if (!(0, _debugMode.isAutocompleteDebugVerbose)()) {
947
+ return;
948
+ }
949
+ // eslint-disable-next-line no-console
950
+ console.log("%c[CTC:model-cost]%c ".concat(measurement.kind, " ").concat(measurement.outcome, " \xB7 ").concat(measurement.wallClockMs.toFixed(1), "ms \xB7 prompt ").concat((_measurement$promptTo = measurement.promptTokens) !== null && _measurement$promptTo !== void 0 ? _measurement$promptTo : '?', " tok/").concat(measurement.promptWords, " words \xB7 output ").concat((_measurement$sampledO = measurement.sampledOutputTokens) !== null && _measurement$sampledO !== void 0 ? _measurement$sampledO : '?', " sampled/").concat(measurement.requestedCompletionTokens, " requested \xB7 decode ").concat((_measurement$webLlmDe = measurement.webLlmDecodeSteps) !== null && _measurement$webLlmDe !== void 0 ? _measurement$webLlmDe : '?', " step").concat(measurement.webLlmDecodeSteps === 1 ? '' : 's', " \xB7 TTFT ").concat((_measurement$timeToFi = (_measurement$timeToFi2 = measurement.timeToFirstTokenMs) === null || _measurement$timeToFi2 === void 0 ? void 0 : _measurement$timeToFi2.toFixed(1)) !== null && _measurement$timeToFi !== void 0 ? _measurement$timeToFi : '?', "ms \xB7 ").concat(measurement.warmState), _debugMode.CTC_STYLES.section, _debugMode.CTC_STYLES.body, {
951
+ request: {
952
+ contextKey: measurement.contextKey,
953
+ familyKey: measurement.familyKey,
954
+ kind: measurement.kind,
955
+ outcome: measurement.outcome,
956
+ warmState: measurement.warmState
957
+ },
958
+ actual: {
959
+ decodeTokensPerSecond: measurement.decodeTokensPerSecond,
960
+ e2eLatencyMs: measurement.e2eLatencyMs,
961
+ prefillTokensPerSecond: measurement.prefillTokensPerSecond,
962
+ promptTokens: measurement.promptTokens,
963
+ promptWords: measurement.promptWords,
964
+ sampledOutputTokens: measurement.sampledOutputTokens,
965
+ timePerDecodeTokenMs: measurement.timePerDecodeTokenMs,
966
+ timeToFirstTokenMs: measurement.timeToFirstTokenMs,
967
+ wallClockMs: measurement.wallClockMs,
968
+ webLlmDecodeSteps: measurement.webLlmDecodeSteps
969
+ },
970
+ context: causalAggregateForLog(contextAggregate),
971
+ family: causalAggregateForLog(familyAggregate)
972
+ });
973
+ };
974
+
975
+ /**
976
+ * Pull the captured distribution out of the processor, timing the handover.
977
+ *
978
+ * Every call yields one array the width of the vocabulary. If that width is
979
+ * large and the handover is slow, thousands of calls per session turn into
980
+ * allocation churn that shows up as latency without any model work behind it.
981
+ */
982
+ var captureLogits = function captureLogits() {
983
+ return causalLogitProcessor.getCapturedLogits();
984
+ };
985
+ var createMeasuredCausalCompletion = /*#__PURE__*/function () {
986
+ var _ref8 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(activeEngine, input) {
987
+ var prompt, promptWords, startedAt, warmState, _usage$completion_tok, _usage$extra$decode_t, _usage$extra, _usage$extra2, _usage$extra$prefill_, _usage$extra3, _usage$prompt_tokens, _usage$extra4, _usage$extra5, completion, latencyMs, usage, webLlmDecodeSteps, sampledOutputTokens, _latencyMs, _t6;
988
+ return _regenerator.default.wrap(function (_context5) {
989
+ while (1) switch (_context5.prev = _context5.next) {
990
+ case 0:
991
+ prompt = truncateToLastNWords(input.prompt, LOCAL_INFERENCE.MAX_CONTEXT_TOKENS);
992
+ promptWords = splitOnWhitespace(prompt).length;
993
+ startedAt = performance.now();
994
+ warmState = ++causalInferenceOrdinal === 1 ? 'cold-first-call' : 'warm';
995
+ _context5.prev = 1;
996
+ _context5.next = 2;
997
+ return activeEngine.completions.create({
998
+ model: modelId,
999
+ prompt: prompt,
1000
+ max_tokens: input.requestedCompletionTokens,
1001
+ temperature: 0,
1002
+ logprobs: false,
1003
+ ignore_eos: true
1004
+ });
1005
+ case 2:
1006
+ completion = _context5.sent;
1007
+ latencyMs = performance.now() - startedAt;
1008
+ usage = completion.usage;
1009
+ webLlmDecodeSteps = (_usage$completion_tok = usage === null || usage === void 0 ? void 0 : usage.completion_tokens) !== null && _usage$completion_tok !== void 0 ? _usage$completion_tok : null; // WebLLM samples the first output token during prefill, but its
1010
+ // completion_tokens usage counter increments only in decodeStep().
1011
+ // Add that prefill-sampled token back without exceeding max_tokens.
1012
+ sampledOutputTokens = webLlmDecodeSteps === null ? null : Math.min(input.requestedCompletionTokens, webLlmDecodeSteps + 1);
1013
+ recordCausalInference({
1014
+ contextKey: input.contextKey,
1015
+ decodeTokensPerSecond: (_usage$extra$decode_t = usage === null || usage === void 0 || (_usage$extra = usage.extra) === null || _usage$extra === void 0 ? void 0 : _usage$extra.decode_tokens_per_s) !== null && _usage$extra$decode_t !== void 0 ? _usage$extra$decode_t : null,
1016
+ e2eLatencyMs: (usage === null || usage === void 0 || (_usage$extra2 = usage.extra) === null || _usage$extra2 === void 0 ? void 0 : _usage$extra2.e2e_latency_s) !== undefined ? usage.extra.e2e_latency_s * 1000 : null,
1017
+ familyKey: input.familyKey,
1018
+ kind: input.kind,
1019
+ outcome: 'completed',
1020
+ prefillTokensPerSecond: (_usage$extra$prefill_ = usage === null || usage === void 0 || (_usage$extra3 = usage.extra) === null || _usage$extra3 === void 0 ? void 0 : _usage$extra3.prefill_tokens_per_s) !== null && _usage$extra$prefill_ !== void 0 ? _usage$extra$prefill_ : null,
1021
+ promptTokens: (_usage$prompt_tokens = usage === null || usage === void 0 ? void 0 : usage.prompt_tokens) !== null && _usage$prompt_tokens !== void 0 ? _usage$prompt_tokens : null,
1022
+ promptWords: promptWords,
1023
+ requestedCompletionTokens: input.requestedCompletionTokens,
1024
+ sampledOutputTokens: sampledOutputTokens,
1025
+ timePerDecodeTokenMs: (usage === null || usage === void 0 || (_usage$extra4 = usage.extra) === null || _usage$extra4 === void 0 ? void 0 : _usage$extra4.time_per_output_token_s) !== undefined ? usage.extra.time_per_output_token_s * 1000 : null,
1026
+ timeToFirstTokenMs: (usage === null || usage === void 0 || (_usage$extra5 = usage.extra) === null || _usage$extra5 === void 0 ? void 0 : _usage$extra5.time_to_first_token_s) !== undefined ? usage.extra.time_to_first_token_s * 1000 : null,
1027
+ wallClockMs: latencyMs,
1028
+ warmState: warmState,
1029
+ webLlmDecodeSteps: webLlmDecodeSteps
1030
+ });
1031
+ return _context5.abrupt("return", {
1032
+ latencyMs: latencyMs
1033
+ });
1034
+ case 3:
1035
+ _context5.prev = 3;
1036
+ _t6 = _context5["catch"](1);
1037
+ _latencyMs = performance.now() - startedAt;
1038
+ recordCausalInference({
1039
+ contextKey: input.contextKey,
1040
+ decodeTokensPerSecond: null,
1041
+ e2eLatencyMs: null,
1042
+ familyKey: input.familyKey,
1043
+ kind: input.kind,
1044
+ outcome: 'failed',
1045
+ prefillTokensPerSecond: null,
1046
+ promptTokens: null,
1047
+ promptWords: promptWords,
1048
+ requestedCompletionTokens: input.requestedCompletionTokens,
1049
+ sampledOutputTokens: null,
1050
+ timePerDecodeTokenMs: null,
1051
+ timeToFirstTokenMs: null,
1052
+ wallClockMs: _latencyMs,
1053
+ warmState: warmState,
1054
+ webLlmDecodeSteps: null
1055
+ });
1056
+ throw _t6;
1057
+ case 4:
1058
+ case "end":
1059
+ return _context5.stop();
1060
+ }
1061
+ }, _callee5, null, [[1, 3]]);
1062
+ }));
1063
+ return function createMeasuredCausalCompletion(_x3, _x4) {
1064
+ return _ref8.apply(this, arguments);
1065
+ };
1066
+ }();
1067
+ var logExactEvidenceReadiness = function logExactEvidenceReadiness(contextKey, familyKey, exactSurfaceCount, source) {
1068
+ var _exactEvidenceCountBy;
1069
+ if (!(0, _debugMode.isAutocompleteDebugEnabled)()) {
1070
+ return;
1071
+ }
1072
+ var previousCount = (_exactEvidenceCountBy = exactEvidenceCountByContext.get(contextKey)) !== null && _exactEvidenceCountBy !== void 0 ? _exactEvidenceCountBy : 0;
1073
+ if (exactSurfaceCount <= previousCount) {
1074
+ return;
1075
+ }
1076
+ setLru(exactEvidenceCountByContext, contextKey, exactSurfaceCount, CANONICAL_SCORING.BOUNDARY_CACHE_MAX);
1077
+ var contextAggregate = getLru(causalInferenceByContext, contextKey);
1078
+ var familyAggregate = getLru(causalInferenceByFamily, familyKey);
1079
+ // eslint-disable-next-line no-console
1080
+ console.log("%c[CTC:readiness]%c exact evidence \xB7 ".concat(exactSurfaceCount, " surface").concat(exactSurfaceCount === 1 ? '' : 's', " \xB7 source=").concat(source, " \xB7 ctx=").concat(contextKey.slice(0, 64)), _debugMode.CTC_STYLES.good, _debugMode.CTC_STYLES.body, {
1081
+ context: contextAggregate ? causalAggregateForLog(contextAggregate) : null,
1082
+ contextKey: contextKey,
1083
+ exactSurfaceCount: exactSurfaceCount,
1084
+ family: familyAggregate ? causalAggregateForLog(familyAggregate) : null,
1085
+ familyKey: familyKey,
1086
+ source: source
1087
+ });
1088
+ };
1089
+ var getProgressiveEvidence = function getProgressiveEvidence(contextKey, candidateSurface) {
1090
+ var _surfaceTokenTrie$get;
1091
+ var tokenIds = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : (_surfaceTokenTrie$get = surfaceTokenTrie.getTokenIds(candidateSurface)) !== null && _surfaceTokenTrie$get !== void 0 ? _surfaceTokenTrie$get : [];
1092
+ if (tokenIds.length === 0) {
1093
+ return null;
1094
+ }
1095
+ var exactKey = surfaceCacheKey(contextKey, candidateSurface, tokenIds);
1096
+ var exact = getLru(surfaceScoreCache, exactKey);
1097
+ if (exact) {
1098
+ return {
1099
+ meanTokenLogProbabilityUpperBound: exact.meanTokenLogProbability,
1100
+ scoredTokenCount: exact.tokenCount,
1101
+ totalLogProbability: exact.totalLogProbability,
1102
+ totalTokenCount: exact.tokenCount
1103
+ };
1104
+ }
1105
+ var boundary = getLru(boundaryCache, contextKey);
1106
+ var firstToken = tokenIds[0];
1107
+ if (!boundary || firstToken === undefined) {
1108
+ return null;
1109
+ }
1110
+ var totalLogProbability = (0, _canonicalLmScoring.logSoftmaxAt)(boundary.rawLogits, firstToken);
1111
+ if (!Number.isFinite(totalLogProbability)) {
1112
+ return null;
1113
+ }
1114
+ var scoredTokenCount = 1;
1115
+ while (scoredTokenCount < tokenIds.length) {
1116
+ var prefix = tokenIds.slice(0, scoredTokenCount);
1117
+ var expansion = getLru(prefixExpansionCache, prefixCacheKey(contextKey, prefix));
1118
+ if (!expansion) {
1119
+ break;
1120
+ }
1121
+ var nextTokenLogProbability = (0, _canonicalLmScoring.logSoftmaxAt)(expansion.rawNextTokenLogits, tokenIds[scoredTokenCount]);
1122
+ if (!Number.isFinite(nextTokenLogProbability)) {
1123
+ break;
1124
+ }
1125
+ totalLogProbability = expansion.totalLogProbability + nextTokenLogProbability;
1126
+ scoredTokenCount++;
1127
+ }
1128
+ if (scoredTokenCount === tokenIds.length) {
1129
+ var exactScore = {
1130
+ contextKey: contextKey,
1131
+ surface: candidateSurface,
1132
+ totalLogProbability: totalLogProbability,
1133
+ meanTokenLogProbability: totalLogProbability / tokenIds.length,
1134
+ tokenCount: tokenIds.length
1135
+ };
1136
+ setLru(surfaceScoreCache, exactKey, exactScore, CANONICAL_SCORING.SURFACE_CACHE_MAX);
1137
+ }
1138
+ return {
1139
+ // Every unscored future token has log probability <= 0. Dividing the
1140
+ // scored total by the final token count is therefore a safe optimistic
1141
+ // bound on the eventual mean.
1142
+ meanTokenLogProbabilityUpperBound: totalLogProbability / tokenIds.length,
1143
+ scoredTokenCount: scoredTokenCount,
1144
+ totalLogProbability: totalLogProbability,
1145
+ totalTokenCount: tokenIds.length
1146
+ };
1147
+ };
1148
+ var _planProgressiveExpansion = function planProgressiveExpansion(contextKey) {
1149
+ var _progressivePrefixesB;
1150
+ var request = progressiveRequests.get(contextKey);
1151
+ if (!request || destroyed || !boundaryCache.has(contextKey)) {
1152
+ return;
1153
+ }
1154
+ var eligible = request.candidates.filter(function (candidate) {
1155
+ return candidate.tokenIds.length > 0 && candidate.tokenIds.length <= CANONICAL_SCORING.EXACT_MAX_TARGET_TOKENS;
1156
+ }).map(function (candidate) {
1157
+ return _objectSpread(_objectSpread({}, candidate), {}, {
1158
+ evidence: getProgressiveEvidence(contextKey, candidate.surface, candidate.tokenIds)
1159
+ });
1160
+ }).filter(function (candidate) {
1161
+ return candidate.evidence !== null;
1162
+ });
1163
+
1164
+ // Score candidates the way arbitration will: a posterior over sequence
1165
+ // log-likelihoods, blended with the corpus prior at the shipped weights.
1166
+ //
1167
+ // Sharing the rule is the point. Ranking on `exp(per-token mean)` instead
1168
+ // answers a different question — it favours short surfaces, because
1169
+ // dividing by fewer tokens flatters them — so this scheduler used to stop
1170
+ // reading on a margin the decision layer did not recognise, and then
1171
+ // abstain for want of the very tokens it declined to read. Nothing about
1172
+ // that was visible from either side.
1173
+ //
1174
+ // The normaliser spans every eligible candidate rather than only the
1175
+ // scheduled ones. That can only make each posterior smaller and each margin
1176
+ // narrower, so the error is always towards reading another token instead of
1177
+ // stopping early — the safe direction ahead of a precision-first gate.
1178
+ var normalizer = (0, _canonicalLmScoring.logSumExp)(eligible.map(function (candidate) {
1179
+ return candidate.evidence.totalLogProbability;
1180
+ }));
1181
+ var confidenceScore = function confidenceScore(candidate) {
1182
+ var _candidate$rankHint;
1183
+ return _scoringPipeline.STAGE1_WEIGHT * ((_candidate$rankHint = candidate.rankHint) !== null && _candidate$rankHint !== void 0 ? _candidate$rankHint : 0) + _scoringPipeline.STAGE2_WEIGHT * (Number.isFinite(normalizer) ? Math.exp(candidate.evidence.totalLogProbability - normalizer) : 0);
1184
+ };
1185
+ var candidates = eligible.sort(function (a, b) {
1186
+ return confidenceScore(b) - confidenceScore(a);
1187
+ }).slice(0, CANONICAL_SCORING.PROGRESSIVE_CANDIDATES_MAX);
1188
+ var unresolved = candidates.filter(function (candidate) {
1189
+ return candidate.evidence.scoredTokenCount < candidate.tokenIds.length;
1190
+ });
1191
+ if (unresolved.length === 0) {
1192
+ logProgressiveState('completed', "ctx=".concat(contextKey.slice(0, 48), " \xB7 exact=").concat(candidates.length, "/").concat(candidates.length));
1193
+ return;
1194
+ }
1195
+ var bestExactScore = Math.max.apply(Math, (0, _toConsumableArray2.default)(candidates.filter(function (candidate) {
1196
+ return candidate.evidence.scoredTokenCount === candidate.tokenIds.length;
1197
+ }).map(confidenceScore)).concat([-Infinity]));
1198
+ var bestUnresolvedScore = Math.max.apply(Math, (0, _toConsumableArray2.default)(unresolved.map(confidenceScore)).concat([-Infinity]));
1199
+ if (bestExactScore - bestUnresolvedScore >= _scoringPipeline.MIN_WINNER_MARGIN) {
1200
+ logProgressiveState('completed', "safe bound \xB7 ctx=".concat(contextKey.slice(0, 40), " \xB7 margin=").concat((bestExactScore - bestUnresolvedScore).toFixed(2), " \xB7 unresolved=").concat(unresolved.length));
1201
+ return;
1202
+ }
1203
+ var groups = surfaceTokenTrie.groupByScoredPrefix(unresolved.map(function (candidate) {
1204
+ return {
1205
+ surface: candidate.surface,
1206
+ tokenIds: candidate.tokenIds,
1207
+ scoredTokenCount: candidate.evidence.scoredTokenCount
1208
+ };
1209
+ }));
1210
+ var bySurface = new Map(candidates.map(function (candidate) {
1211
+ return [candidate.surface, candidate];
1212
+ }));
1213
+ var rankedGroups = groups.map(function (group) {
1214
+ var members = group.surfaces.map(function (candidateSurface) {
1215
+ return bySurface.get(candidateSurface);
1216
+ }).filter(function (candidate) {
1217
+ return Boolean(candidate);
1218
+ });
1219
+ var optimisticScore = Math.max.apply(Math, (0, _toConsumableArray2.default)(members.map(confidenceScore)));
1220
+ return _objectSpread(_objectSpread({}, group), {}, {
1221
+ optimisticScore: optimisticScore
1222
+ });
1223
+ }).sort(function (a, b) {
1224
+ return b.optimisticScore - a.optimisticScore;
1225
+ });
1226
+ var bestGroup = rankedGroups[0];
1227
+ if (!bestGroup) {
1228
+ return;
1229
+ }
1230
+ var warmGroup = kvContextKey === contextKey ? rankedGroups.find(function (group) {
1231
+ return isPrefixOf(kvPath, group.tokenPrefix);
1232
+ }) : undefined;
1233
+ var nextGroup = warmGroup && warmGroup.optimisticScore >= bestGroup.optimisticScore - PROGRESSIVE_WARM_PATH_MARGIN ? warmGroup : bestGroup;
1234
+ var contextPrefixes = (_progressivePrefixesB = progressivePrefixesByContext.get(contextKey)) !== null && _progressivePrefixesB !== void 0 ? _progressivePrefixesB : new Set();
1235
+ progressivePrefixesByContext.set(contextKey, contextPrefixes);
1236
+ var key = prefixCacheKey(contextKey, nextGroup.tokenPrefix);
1237
+ if (getLru(prefixExpansionCache, key)) {
1238
+ logProgressiveState('cached', "ctx=".concat(contextKey.slice(0, 40), " \xB7 prefix=[").concat(nextGroup.tokenPrefix.join(','), "]"));
1239
+ _planProgressiveExpansion(contextKey);
1240
+ return;
1241
+ }
1242
+ if (pendingPrefixExpansions.has(key) || inFlightPrefixExpansions.has(key)) {
1243
+ logProgressiveState('deduplicated', "ctx=".concat(contextKey.slice(0, 40), " \xB7 prefix=[").concat(nextGroup.tokenPrefix.join(','), "]"));
1244
+ return;
1245
+ }
1246
+ if (contextPrefixes.size >= CANONICAL_SCORING.PROGRESSIVE_EXPANSIONS_PER_CONTEXT_MAX) {
1247
+ logProgressiveState('completed', "ctx=".concat(contextKey.slice(0, 48), " \xB7 expansion cap=").concat(contextPrefixes.size, " \xB7 unresolved=").concat(unresolved.length));
1248
+ return;
1249
+ }
1250
+ contextPrefixes.add(key);
1251
+ pendingPrefixExpansions.set(key, {
1252
+ contextKey: contextKey,
1253
+ familyKey: request.familyKey,
1254
+ prompt: request.prompt,
1255
+ tokenPrefix: nextGroup.tokenPrefix,
1256
+ surfaces: nextGroup.surfaces
1257
+ });
1258
+ logProgressiveState('queued', "ctx=".concat(contextKey.slice(0, 40), " \xB7 prefix=[").concat(nextGroup.tokenPrefix.join(','), "] \xB7 surfaces=").concat(nextGroup.surfaces.length));
1259
+ _drainCausalQueue();
1260
+ };
1261
+
1262
+ /**
1263
+ * Prefill `prompt` and cache the boundary distribution that follows it.
1264
+ *
1265
+ * A text completion resets the KV cache before prefilling, so asking for a
1266
+ * single token leaves the cache holding exactly the prompt. That is the
1267
+ * anchor every later decode step extends.
1268
+ */
1269
+ var prefillBoundary = /*#__PURE__*/function () {
1270
+ var _ref9 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6(activeEngine, request) {
1271
+ var _yield$createMeasured, latencyMs, rawLogits;
1272
+ return _regenerator.default.wrap(function (_context6) {
1273
+ while (1) switch (_context6.prev = _context6.next) {
1274
+ case 0:
1275
+ kvContextKey = null;
1276
+ kvPath = [];
1277
+ causalRequestsThisFamily++;
1278
+ causalGeneratedTokensThisFamily++;
1279
+ causalLogitProcessor.startCapture();
1280
+ _context6.next = 1;
1281
+ return createMeasuredCausalCompletion(activeEngine, {
1282
+ contextKey: request.contextKey,
1283
+ familyKey: request.familyKey,
1284
+ kind: 'boundary',
1285
+ prompt: request.prompt,
1286
+ requestedCompletionTokens: 1
1287
+ });
1288
+ case 1:
1289
+ _yield$createMeasured = _context6.sent;
1290
+ latencyMs = _yield$createMeasured.latencyMs;
1291
+ rawLogits = captureLogits();
1292
+ if (rawLogits) {
1293
+ _context6.next = 2;
1294
+ break;
1295
+ }
1296
+ return _context6.abrupt("return", null);
1297
+ case 2:
1298
+ setLru(boundaryCache, request.contextKey, {
1299
+ contextKey: request.contextKey,
1300
+ prompt: request.prompt,
1301
+ rawLogits: rawLogits
1302
+ }, CANONICAL_SCORING.BOUNDARY_CACHE_MAX);
1303
+ kvContextKey = request.contextKey;
1304
+ kvPath = [];
1305
+ return _context6.abrupt("return", {
1306
+ latencyMs: latencyMs,
1307
+ rawLogits: rawLogits
1308
+ });
1309
+ case 3:
1310
+ case "end":
1311
+ return _context6.stop();
1312
+ }
1313
+ }, _callee6);
1314
+ }));
1315
+ return function prefillBoundary(_x5, _x6) {
1316
+ return _ref9.apply(this, arguments);
1317
+ };
1318
+ }();
1319
+
1320
+ /**
1321
+ * Append one token to the KV cache and return the distribution that follows.
1322
+ *
1323
+ * This deliberately bypasses the completion API: a completion would reset the
1324
+ * cache and re-prefill the whole prompt, whereas this forwards a single token
1325
+ * on top of the work already done.
1326
+ */
1327
+ var decodeOneToken = /*#__PURE__*/function () {
1328
+ var _ref0 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee7(activeEngine, token) {
1329
+ var _t7;
1330
+ return _regenerator.default.wrap(function (_context7) {
1331
+ while (1) switch (_context7.prev = _context7.next) {
1332
+ case 0:
1333
+ causalLogitProcessor.startCapture();
1334
+ _context7.prev = 1;
1335
+ _context7.next = 2;
1336
+ return activeEngine.forwardTokensAndSample([token], false, modelId);
1337
+ case 2:
1338
+ _context7.next = 4;
1339
+ break;
1340
+ case 3:
1341
+ _context7.prev = 3;
1342
+ _t7 = _context7["catch"](1);
1343
+ kvContextKey = null;
1344
+ kvPath = [];
1345
+ throw _t7;
1346
+ case 4:
1347
+ causalRequestsThisFamily++;
1348
+ causalGeneratedTokensThisFamily++;
1349
+ return _context7.abrupt("return", captureLogits());
1350
+ case 5:
1351
+ case "end":
1352
+ return _context7.stop();
1353
+ }
1354
+ }, _callee7, null, [[1, 3]]);
1355
+ }));
1356
+ return function decodeOneToken(_x7, _x8) {
1357
+ return _ref0.apply(this, arguments);
1358
+ };
1359
+ }();
1360
+ var runBoundaryPrime = /*#__PURE__*/function () {
1361
+ var _ref1 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee8(request) {
1362
+ var _progressiveRequest$c, _progressiveRequest$f, _progressiveRequests$, _progressiveRequests$2;
1363
+ var primed, latencyMs, rawLogits, progressiveRequest, exactSurfaceCount, callbackFamilyKey;
1364
+ return _regenerator.default.wrap(function (_context8) {
1365
+ while (1) switch (_context8.prev = _context8.next) {
1366
+ case 0:
1367
+ if (!(!engine || destroyed)) {
1368
+ _context8.next = 1;
1369
+ break;
1370
+ }
1371
+ return _context8.abrupt("return");
1372
+ case 1:
1373
+ _context8.next = 2;
1374
+ return prefillBoundary(engine, request);
1375
+ case 2:
1376
+ primed = _context8.sent;
1377
+ if (primed) {
1378
+ _context8.next = 3;
1379
+ break;
1380
+ }
1381
+ return _context8.abrupt("return");
1382
+ case 3:
1383
+ latencyMs = primed.latencyMs, rawLogits = primed.rawLogits;
1384
+ _planProgressiveExpansion(request.contextKey);
1385
+ progressiveRequest = progressiveRequests.get(request.contextKey);
1386
+ exactSurfaceCount = (_progressiveRequest$c = progressiveRequest === null || progressiveRequest === void 0 ? void 0 : progressiveRequest.candidates.filter(function (candidate) {
1387
+ var evidence = getProgressiveEvidence(request.contextKey, candidate.surface, candidate.tokenIds);
1388
+ return evidence !== null && evidence.scoredTokenCount === evidence.totalTokenCount;
1389
+ }).length) !== null && _progressiveRequest$c !== void 0 ? _progressiveRequest$c : 0;
1390
+ logExactEvidenceReadiness(request.contextKey, (_progressiveRequest$f = progressiveRequest === null || progressiveRequest === void 0 ? void 0 : progressiveRequest.familyKey) !== null && _progressiveRequest$f !== void 0 ? _progressiveRequest$f : request.familyKey, exactSurfaceCount, 'boundary');
1391
+ if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
1392
+ // eslint-disable-next-line no-console
1393
+ console.log("%c[CTC:model] %c\uD83E\uDDE0 Tier A prime #".concat(request.priority + 1, " \xB7 ctx=").concat(request.contextKey.slice(0, 48), " \xB7 ").concat(rawLogits.length, " logits \xB7 ").concat(latencyMs.toFixed(0), "ms \xB7 family req:").concat(causalRequestsThisFamily, " tok:").concat(causalGeneratedTokensThisFamily, " A hit/miss:").concat(tierAPrimeCacheHitsThisFamily, "/").concat(tierAPrimeCacheMissesThisFamily), 'color: #9c27b0; font-weight: bold;', 'color: #2196f3;');
1394
+ }
1395
+ callbackFamilyKey = (_progressiveRequests$ = (_progressiveRequests$2 = progressiveRequests.get(request.contextKey)) === null || _progressiveRequests$2 === void 0 ? void 0 : _progressiveRequests$2.familyKey) !== null && _progressiveRequests$ !== void 0 ? _progressiveRequests$ : request.familyKey;
1396
+ if (callbackFamilyKey === latestCausalFamilyKey && !destroyed) {
1397
+ onBoundaryLmUpdate === null || onBoundaryLmUpdate === void 0 || onBoundaryLmUpdate({
1398
+ contextKey: request.contextKey,
1399
+ familyKey: callbackFamilyKey,
1400
+ latencyMs: latencyMs
1401
+ });
1402
+ }
1403
+ case 4:
1404
+ case "end":
1405
+ return _context8.stop();
1406
+ }
1407
+ }, _callee8);
1408
+ }));
1409
+ return function runBoundaryPrime(_x9) {
1069
1410
  return _ref1.apply(this, arguments);
1070
1411
  };
1071
1412
  }();
1413
+ var runPrefixExpansion = /*#__PURE__*/function () {
1414
+ var _ref10 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee9(request) {
1415
+ var _progressivePrefixesB2, _progressiveRequest$f2, _progressiveRequests$3, _progressiveRequests$4;
1416
+ var activeEngine, startedAt, logitsAtPath, totalAtPath, pathIsLive, _getLru$rawLogits, _getLru2, _resume$rawNextTokenL, _resume$totalLogProba, resume, primed, contextPrefixes, index, token, stepLogProbability, nextLogits, stepKey, expansion, latencyMs, progressiveRequest, newlyExact, exactSurfaceCount, _iterator, _step, candidate, exactKey, wasExact, evidence, callbackFamilyKey;
1417
+ return _regenerator.default.wrap(function (_context9) {
1418
+ while (1) switch (_context9.prev = _context9.next) {
1419
+ case 0:
1420
+ if (!(!engine || destroyed)) {
1421
+ _context9.next = 1;
1422
+ break;
1423
+ }
1424
+ return _context9.abrupt("return");
1425
+ case 1:
1426
+ activeEngine = engine;
1427
+ startedAt = performance.now();
1428
+ logProgressiveState('started', "ctx=".concat(request.contextKey.slice(0, 40), " \xB7 prefix=[").concat(request.tokenPrefix.join(','), "] \xB7 surfaces=").concat(request.surfaces.length));
1429
+
1430
+ // Resume from whatever the KV cache already holds for this context, and
1431
+ // fall back to a prompt prefill only when the requested prefix branches
1432
+ // away from it.
1433
+ logitsAtPath = null;
1434
+ totalAtPath = 0;
1435
+ pathIsLive = extendsLiveKvPath(request);
1436
+ if (pathIsLive) {
1437
+ if (kvPath.length === 0) {
1438
+ logitsAtPath = (_getLru$rawLogits = (_getLru2 = getLru(boundaryCache, request.contextKey)) === null || _getLru2 === void 0 ? void 0 : _getLru2.rawLogits) !== null && _getLru$rawLogits !== void 0 ? _getLru$rawLogits : null;
1439
+ } else {
1440
+ resume = getLru(prefixExpansionCache, prefixCacheKey(request.contextKey, kvPath));
1441
+ logitsAtPath = (_resume$rawNextTokenL = resume === null || resume === void 0 ? void 0 : resume.rawNextTokenLogits) !== null && _resume$rawNextTokenL !== void 0 ? _resume$rawNextTokenL : null;
1442
+ totalAtPath = (_resume$totalLogProba = resume === null || resume === void 0 ? void 0 : resume.totalLogProbability) !== null && _resume$totalLogProba !== void 0 ? _resume$totalLogProba : 0;
1443
+ }
1444
+ }
1445
+ if (logitsAtPath) {
1446
+ _context9.next = 4;
1447
+ break;
1448
+ }
1449
+ _context9.next = 2;
1450
+ return prefillBoundary(activeEngine, request);
1451
+ case 2:
1452
+ primed = _context9.sent;
1453
+ if (primed) {
1454
+ _context9.next = 3;
1455
+ break;
1456
+ }
1457
+ logProgressiveState('failed', "ctx=".concat(request.contextKey.slice(0, 40), " \xB7 prefix=[").concat(request.tokenPrefix.join(','), "] \xB7 missing boundary logits"));
1458
+ return _context9.abrupt("return");
1459
+ case 3:
1460
+ logitsAtPath = primed.rawLogits;
1461
+ totalAtPath = 0;
1462
+ case 4:
1463
+ // Walking the path caches every depth along it, not just the requested
1464
+ // one, so a later expansion that shares this prefix costs nothing.
1465
+ contextPrefixes = (_progressivePrefixesB2 = progressivePrefixesByContext.get(request.contextKey)) !== null && _progressivePrefixesB2 !== void 0 ? _progressivePrefixesB2 : new Set();
1466
+ progressivePrefixesByContext.set(request.contextKey, contextPrefixes);
1467
+ index = kvPath.length;
1468
+ case 5:
1469
+ if (!(index < request.tokenPrefix.length)) {
1470
+ _context9.next = 11;
1471
+ break;
1472
+ }
1473
+ token = request.tokenPrefix[index];
1474
+ if (!(token === undefined)) {
1475
+ _context9.next = 6;
1476
+ break;
1477
+ }
1478
+ return _context9.abrupt("continue", 11);
1479
+ case 6:
1480
+ stepLogProbability = (0, _canonicalLmScoring.logSoftmaxAt)(logitsAtPath, token);
1481
+ if (Number.isFinite(stepLogProbability)) {
1482
+ _context9.next = 7;
1483
+ break;
1484
+ }
1485
+ logProgressiveState('failed', "ctx=".concat(request.contextKey.slice(0, 40), " \xB7 prefix=[").concat(request.tokenPrefix.join(','), "] \xB7 token ").concat(token, " unscoreable"));
1486
+ return _context9.abrupt("return");
1487
+ case 7:
1488
+ _context9.next = 8;
1489
+ return decodeOneToken(activeEngine, token);
1490
+ case 8:
1491
+ nextLogits = _context9.sent;
1492
+ if (!(!nextLogits || destroyed)) {
1493
+ _context9.next = 9;
1494
+ break;
1495
+ }
1496
+ kvContextKey = null;
1497
+ kvPath = [];
1498
+ logProgressiveState('failed', "ctx=".concat(request.contextKey.slice(0, 40), " \xB7 prefix=[").concat(request.tokenPrefix.join(','), "] \xB7 missing logits"));
1499
+ return _context9.abrupt("return");
1500
+ case 9:
1501
+ kvContextKey = request.contextKey;
1502
+ kvPath = [].concat((0, _toConsumableArray2.default)(kvPath), [token]);
1503
+ totalAtPath += stepLogProbability;
1504
+ logitsAtPath = nextLogits;
1505
+ stepKey = prefixCacheKey(request.contextKey, kvPath);
1506
+ expansion = {
1507
+ contextKey: request.contextKey,
1508
+ tokenPrefix: (0, _toConsumableArray2.default)(kvPath),
1509
+ totalLogProbability: totalAtPath,
1510
+ rawNextTokenLogits: nextLogits
1511
+ };
1512
+ setLru(prefixExpansionCache, stepKey, expansion, CANONICAL_SCORING.PREFIX_CACHE_MAX);
1513
+ contextPrefixes.add(stepKey);
1514
+ case 10:
1515
+ index++;
1516
+ _context9.next = 5;
1517
+ break;
1518
+ case 11:
1519
+ latencyMs = performance.now() - startedAt;
1520
+ progressiveRequest = progressiveRequests.get(request.contextKey);
1521
+ newlyExact = 0;
1522
+ exactSurfaceCount = 0;
1523
+ if (progressiveRequest) {
1524
+ _iterator = _createForOfIteratorHelper(progressiveRequest.candidates);
1525
+ try {
1526
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
1527
+ candidate = _step.value;
1528
+ exactKey = surfaceCacheKey(request.contextKey, candidate.surface, candidate.tokenIds);
1529
+ wasExact = surfaceScoreCache.has(exactKey);
1530
+ evidence = getProgressiveEvidence(request.contextKey, candidate.surface, candidate.tokenIds);
1531
+ if (!wasExact && evidence !== null && evidence.scoredTokenCount === evidence.totalTokenCount && surfaceScoreCache.has(exactKey)) {
1532
+ newlyExact++;
1533
+ }
1534
+ if (evidence !== null && evidence.scoredTokenCount === evidence.totalTokenCount) {
1535
+ exactSurfaceCount++;
1536
+ }
1537
+ }
1538
+ } catch (err) {
1539
+ _iterator.e(err);
1540
+ } finally {
1541
+ _iterator.f();
1542
+ }
1543
+ }
1544
+ logExactEvidenceReadiness(request.contextKey, (_progressiveRequest$f2 = progressiveRequest === null || progressiveRequest === void 0 ? void 0 : progressiveRequest.familyKey) !== null && _progressiveRequest$f2 !== void 0 ? _progressiveRequest$f2 : request.familyKey, exactSurfaceCount, 'prefix');
1545
+ logProgressiveState('expanded', "ctx=".concat(request.contextKey.slice(0, 40), " \xB7 prefix=[").concat(request.tokenPrefix.join(','), "] \xB7 exact+").concat(newlyExact, " \xB7 ").concat(latencyMs.toFixed(0), "ms"));
1546
+ _planProgressiveExpansion(request.contextKey);
1547
+ callbackFamilyKey = (_progressiveRequests$3 = (_progressiveRequests$4 = progressiveRequests.get(request.contextKey)) === null || _progressiveRequests$4 === void 0 ? void 0 : _progressiveRequests$4.familyKey) !== null && _progressiveRequests$3 !== void 0 ? _progressiveRequests$3 : request.familyKey;
1548
+ if (callbackFamilyKey === latestCausalFamilyKey && !destroyed) {
1549
+ onSurfaceScoreUpdate === null || onSurfaceScoreUpdate === void 0 || onSurfaceScoreUpdate({
1550
+ contextKey: request.contextKey,
1551
+ count: newlyExact,
1552
+ familyKey: callbackFamilyKey,
1553
+ latencyMs: latencyMs
1554
+ });
1555
+ } else {
1556
+ logProgressiveState('stale', "cached only \xB7 ctx=".concat(request.contextKey.slice(0, 40), " \xB7 family=").concat(request.familyKey.slice(0, 32)));
1557
+ }
1558
+ case 12:
1559
+ case "end":
1560
+ return _context9.stop();
1561
+ }
1562
+ }, _callee9);
1563
+ }));
1564
+ return function runPrefixExpansion(_x0) {
1565
+ return _ref10.apply(this, arguments);
1566
+ };
1567
+ }();
1568
+
1569
+ /**
1570
+ * Whether any live decision would still take this request's result.
1571
+ *
1572
+ * Ranking on the family a request was created under, as this used to, misses
1573
+ * in both directions. Work queued a keystroke ago for a context still under
1574
+ * the cursor sorts as stale even though the callbacks resolve delivery
1575
+ * through `progressiveRequests` and would hand it over. Work for a context
1576
+ * nothing asks about any more sorts as runnable even though the same
1577
+ * resolution drops it on arrival — and that one is expensive, because an
1578
+ * abandoned context is never the one the KV cache holds, so running it pays
1579
+ * a prompt prefill and leaves the live context evicted, charging the next
1580
+ * live request a second prefill. Two prefills for a discarded result.
1581
+ *
1582
+ * Either signal alone is enough to keep the work, which matters because a
1583
+ * boundary prime is queued before its context's surfaces are requested: at
1584
+ * that moment the stored family is still the previous decision's, and only
1585
+ * the request's own family says it is current.
1586
+ *
1587
+ * Agreeing with the layer being fed is the correction the expansion planner
1588
+ * already carries for arbitration's scoring rule — a scheduler deciding on
1589
+ * its own rule stops on margins the consumer does not recognise.
1590
+ */
1591
+ var stillWanted = function stillWanted(request) {
1592
+ var _progressiveRequests$5;
1593
+ return request.familyKey === latestCausalFamilyKey || ((_progressiveRequests$5 = progressiveRequests.get(request.contextKey)) === null || _progressiveRequests$5 === void 0 ? void 0 : _progressiveRequests$5.familyKey) === latestCausalFamilyKey;
1594
+ };
1595
+ var _drainCausalQueue = function drainCausalQueue() {
1596
+ if (causalInFlight || destroyed) {
1597
+ return;
1598
+ }
1599
+ // Drop abandoned work rather than leaving it to be picked up whenever the
1600
+ // live family happens to have nothing queued. Its cached side effects are
1601
+ // speculative — they only pay off if the user deletes back into exactly
1602
+ // this context and prefix — and the prefill pair above is certain.
1603
+ var _iterator2 = _createForOfIteratorHelper(pendingPrefixExpansions),
1604
+ _step2;
1605
+ try {
1606
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
1607
+ var _step2$value = (0, _slicedToArray2.default)(_step2.value, 2),
1608
+ key = _step2$value[0],
1609
+ request = _step2$value[1];
1610
+ if (!stillWanted(request)) {
1611
+ pendingPrefixExpansions.delete(key);
1612
+ }
1613
+ }
1614
+ } catch (err) {
1615
+ _iterator2.e(err);
1616
+ } finally {
1617
+ _iterator2.f();
1618
+ }
1619
+ var _iterator3 = _createForOfIteratorHelper(pendingBoundaryPrimes),
1620
+ _step3;
1621
+ try {
1622
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
1623
+ var _step3$value = (0, _slicedToArray2.default)(_step3.value, 2),
1624
+ contextKey = _step3$value[0],
1625
+ _request = _step3$value[1];
1626
+ if (!stillWanted(_request)) {
1627
+ pendingBoundaryPrimes.delete(contextKey);
1628
+ }
1629
+ }
1630
+ } catch (err) {
1631
+ _iterator3.e(err);
1632
+ } finally {
1633
+ _iterator3.f();
1634
+ }
1635
+ var sortedPrimes = Array.from(pendingBoundaryPrimes.values()).sort(function (a, b) {
1636
+ return a.priority - b.priority;
1637
+ });
1638
+ // Everything left is wanted, so relevance no longer needs a sort key.
1639
+ // Take whichever request continues the live sequence: that expansion costs
1640
+ // one decode step where any other costs a full prompt prefill, and nothing
1641
+ // is skipped, only reordered.
1642
+ //
1643
+ // Staying inside the live context when nothing continues it was tried and
1644
+ // reverted. It moved re-primes from `reprime-context` to `reprime-branch`
1645
+ // and left the total flat, because both pay for a prompt prefill: a
1646
+ // context whose remaining prefixes diverge at the first token re-primes as
1647
+ // a branch instead of as a context. Ordering cannot recover that; only
1648
+ // queueing fewer divergent branches can, which is what the prune above
1649
+ // does.
1650
+ var sortedPrefixes = Array.from(pendingPrefixExpansions.values()).sort(function (a, b) {
1651
+ return Number(extendsLiveKvPath(b)) - Number(extendsLiveKvPath(a));
1652
+ });
1653
+ var nextPrefix = sortedPrefixes[0];
1654
+ var nextPrime = nextPrefix ? undefined : sortedPrimes[0];
1655
+ if (!nextPrefix && !nextPrime) {
1656
+ return;
1657
+ }
1658
+ causalInFlight = true;
1659
+ var prefixKey = null;
1660
+ if (nextPrefix) {
1661
+ prefixKey = prefixCacheKey(nextPrefix.contextKey, nextPrefix.tokenPrefix);
1662
+ pendingPrefixExpansions.delete(prefixKey);
1663
+ inFlightPrefixExpansions.add(prefixKey);
1664
+ } else if (nextPrime) {
1665
+ pendingBoundaryPrimes.delete(nextPrime.contextKey);
1666
+ inFlightBoundaryContextKey = nextPrime.contextKey;
1667
+ }
1668
+ void ensureEngineInitialized().then(function () {
1669
+ return nextPrefix ? runPrefixExpansion(nextPrefix) : nextPrime ? runBoundaryPrime(nextPrime) : undefined;
1670
+ }).catch(function (error) {
1671
+ if (nextPrefix) {
1672
+ logProgressiveState('failed', "ctx=".concat(nextPrefix.contextKey.slice(0, 40), " \xB7 prefix=[").concat(nextPrefix.tokenPrefix.join(','), "] \xB7 ").concat(error instanceof Error ? error.message : String(error)));
1673
+ }
1674
+ if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
1675
+ // eslint-disable-next-line no-console
1676
+ console.log("%c[CTC:model] %c\u274C canonical causal request failed: ".concat(error instanceof Error ? error.message : String(error)), 'color: #9c27b0; font-weight: bold;', 'color: #f44336;');
1677
+ }
1678
+ }).finally(function () {
1679
+ if (prefixKey) {
1680
+ inFlightPrefixExpansions.delete(prefixKey);
1681
+ }
1682
+ if (nextPrime) {
1683
+ inFlightBoundaryContextKey = null;
1684
+ }
1685
+ causalInFlight = false;
1686
+ _drainCausalQueue();
1687
+ });
1688
+ };
1072
1689
 
1073
1690
  // ── Context update (debounced) ─────────────────────────────────────────
1074
1691
 
@@ -1118,7 +1735,7 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1118
1735
  latestRequestId = requestId;
1119
1736
  if ((0, _debugMode.isAutocompleteDebugEnabled)()) {
1120
1737
  // eslint-disable-next-line no-console
1121
- console.groupCollapsed("%c[LocalSlowLane] %c\uD83D\uDCE4 Context update (request #".concat(requestId, ") | ").concat(text.length, " chars"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
1738
+ console.groupCollapsed("%c[CTC:model] %c\uD83D\uDCE4 Context update (request #".concat(requestId, ") | ").concat(text.length, " chars"), 'color: #9c27b0; font-weight: bold;', 'color: inherit;');
1122
1739
  var lines = text.split('\n');
1123
1740
  lines.forEach(function (line, i) {
1124
1741
  // eslint-disable-next-line no-console
@@ -1154,22 +1771,136 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1154
1771
  doUpdateContext(lastRequestedText);
1155
1772
  }, debounceMs);
1156
1773
  };
1774
+ var setLatestCausalFamily = function setLatestCausalFamily(familyKey) {
1775
+ if (familyKey === latestCausalFamilyKey) {
1776
+ return;
1777
+ }
1778
+ latestCausalFamilyKey = familyKey;
1779
+ causalRequestsThisFamily = 0;
1780
+ causalGeneratedTokensThisFamily = 0;
1781
+ tierAPrimeCacheHitsThisFamily = 0;
1782
+ tierAPrimeCacheMissesThisFamily = 0;
1783
+ };
1784
+ var primeBoundaryLm = function primeBoundaryLm(input) {
1785
+ if (destroyed) {
1786
+ return;
1787
+ }
1788
+ setLatestCausalFamily(input.familyKey);
1789
+ if (getLru(boundaryCache, input.contextKey)) {
1790
+ tierAPrimeCacheHitsThisFamily++;
1791
+ if ((0, _debugMode.isAutocompleteDebugVerbose)()) {
1792
+ // eslint-disable-next-line no-console
1793
+ console.log("%c[CTC:model] %c\u26A1 Tier A cache hit \xB7 ctx=".concat(input.contextKey.slice(0, 48)), 'color: #9c27b0; font-weight: bold;', 'color: #2196f3;');
1794
+ }
1795
+ return;
1796
+ }
1797
+ if (pendingBoundaryPrimes.has(input.contextKey) || inFlightBoundaryContextKey === input.contextKey) {
1798
+ logProgressiveState('deduplicated', "boundary \xB7 ctx=".concat(input.contextKey.slice(0, 48)));
1799
+ return;
1800
+ }
1801
+ if (pendingBoundaryPrimes.size >= CANONICAL_SCORING.TIER_A_PRIMES_MAX) {
1802
+ var stalePending = Array.from(pendingBoundaryPrimes.entries()).find(function (_ref11) {
1803
+ var _ref12 = (0, _slicedToArray2.default)(_ref11, 2),
1804
+ request = _ref12[1];
1805
+ return request.familyKey !== latestCausalFamilyKey;
1806
+ });
1807
+ if (stalePending) {
1808
+ pendingBoundaryPrimes.delete(stalePending[0]);
1809
+ logProgressiveState('stale', "dropped unstarted boundary \xB7 ctx=".concat(stalePending[0].slice(0, 48)));
1810
+ }
1811
+ }
1812
+ if (pendingBoundaryPrimes.size < CANONICAL_SCORING.TIER_A_PRIMES_MAX) {
1813
+ tierAPrimeCacheMissesThisFamily++;
1814
+ pendingBoundaryPrimes.set(input.contextKey, input);
1815
+ }
1816
+ _drainCausalQueue();
1817
+ };
1818
+ var requestProgressiveSurfaceScores = function requestProgressiveSurfaceScores(input) {
1819
+ if (destroyed) {
1820
+ return;
1821
+ }
1822
+ setLatestCausalFamily(input.familyKey);
1823
+ var candidates = input.candidates.filter(function (candidate) {
1824
+ return candidate.tokenIds.length > 0 && candidate.tokenIds.length <= CANONICAL_SCORING.EXACT_MAX_TARGET_TOKENS;
1825
+ }).slice(0, CANONICAL_SCORING.PROGRESSIVE_INPUT_MAX);
1826
+ if (candidates.length === 0) {
1827
+ return;
1828
+ }
1829
+ var previous = progressiveRequests.get(input.contextKey);
1830
+ var candidateSignature = candidates.map(function (candidate) {
1831
+ return "".concat(candidate.surface, ":").concat(candidate.tokenIds.join(','));
1832
+ }).join("\x01");
1833
+ var previousSignature = previous === null || previous === void 0 ? void 0 : previous.candidates.map(function (candidate) {
1834
+ return "".concat(candidate.surface, ":").concat(candidate.tokenIds.join(','));
1835
+ }).join("\x01");
1836
+ setLru(progressiveRequests, input.contextKey, _objectSpread(_objectSpread({}, input), {}, {
1837
+ candidates: candidates
1838
+ }), CANONICAL_SCORING.BOUNDARY_CACHE_MAX);
1839
+ var _iterator4 = _createForOfIteratorHelper(progressivePrefixesByContext.keys()),
1840
+ _step4;
1841
+ try {
1842
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
1843
+ var cachedContextKey = _step4.value;
1844
+ if (!progressiveRequests.has(cachedContextKey)) {
1845
+ progressivePrefixesByContext.delete(cachedContextKey);
1846
+ }
1847
+ }
1848
+ } catch (err) {
1849
+ _iterator4.e(err);
1850
+ } finally {
1851
+ _iterator4.f();
1852
+ }
1853
+ var cachedProgressCount = candidates.filter(function (candidate) {
1854
+ var evidence = getProgressiveEvidence(input.contextKey, candidate.surface, candidate.tokenIds);
1855
+ return evidence !== null && (evidence.scoredTokenCount > 1 || evidence.scoredTokenCount === evidence.totalTokenCount);
1856
+ }).length;
1857
+ if (cachedProgressCount > 0) {
1858
+ logProgressiveState('cached', "ctx=".concat(input.contextKey.slice(0, 40), " \xB7 surfaces=").concat(cachedProgressCount, "/").concat(candidates.length));
1859
+ }
1860
+ if (candidateSignature === previousSignature) {
1861
+ logProgressiveState('deduplicated', "candidate set \xB7 ctx=".concat(input.contextKey.slice(0, 40), " \xB7 surfaces=").concat(candidates.length));
1862
+ } else {
1863
+ logProgressiveState('queued', "candidate set \xB7 ctx=".concat(input.contextKey.slice(0, 40), " \xB7 surfaces=").concat(candidates.length));
1864
+ }
1865
+ _planProgressiveExpansion(input.contextKey);
1866
+ };
1157
1867
 
1158
1868
  // ── Public API (same shape as createSlowLaneClient) ────────────────────
1159
1869
  return {
1160
1870
  updateContext: updateContextDebounced,
1871
+ getBoundaryLmState: function getBoundaryLmState(contextKey) {
1872
+ return getLru(boundaryCache, contextKey);
1873
+ },
1874
+ getCanonicalSurfaceTokenIds: function getCanonicalSurfaceTokenIds(candidateSurface) {
1875
+ return surfaceTokenTrie.getTokenIds(candidateSurface);
1876
+ },
1877
+ getCanonicalSurfaceCount: function getCanonicalSurfaceCount() {
1878
+ return surfaceTokenIds.size;
1879
+ },
1880
+ getContextInput: function getContextInput() {
1881
+ return storedContextInput;
1882
+ },
1161
1883
  getContextVector: function getContextVector() {
1162
1884
  return storedContextVector;
1163
1885
  },
1164
1886
  getLmLogits: function getLmLogits() {
1165
1887
  return storedLmLogits;
1166
1888
  },
1889
+ getProgressiveSurfaceEvidence: function getProgressiveSurfaceEvidence(contextKey, candidateSurface) {
1890
+ return getProgressiveEvidence(contextKey, candidateSurface);
1891
+ },
1892
+ getSurfaceScore: function getSurfaceScore(contextKey, candidateSurface) {
1893
+ return getLru(surfaceScoreCache, surfaceCacheKey(contextKey, candidateSurface));
1894
+ },
1167
1895
  setContextVector: function setContextVector(vector) {
1896
+ storedContextInput = null;
1168
1897
  storedContextVector = vector;
1169
1898
  },
1170
1899
  setLmLogits: function setLmLogits(logits) {
1171
1900
  storedLmLogits = logits;
1172
1901
  },
1902
+ primeBoundaryLm: primeBoundaryLm,
1903
+ requestProgressiveSurfaceScores: requestProgressiveSurfaceScores,
1173
1904
  isWordBoundary: _slowLaneClient.isWordBoundary,
1174
1905
  isReady: function isReady() {
1175
1906
  return ready;
@@ -1190,8 +1921,26 @@ var createLocalSlowLaneClient = exports.createLocalSlowLaneClient = function cre
1190
1921
  activeInferenceText = null;
1191
1922
  activeInferenceRequestId = -1;
1192
1923
  pendingInference = null;
1924
+ storedContextInput = null;
1193
1925
  storedContextVector = null;
1194
1926
  storedLmLogits = null;
1927
+ causalInFlight = false;
1928
+ inFlightBoundaryContextKey = null;
1929
+ kvContextKey = null;
1930
+ kvPath = [];
1931
+ latestCausalFamilyKey = '';
1932
+ pendingBoundaryPrimes.clear();
1933
+ pendingPrefixExpansions.clear();
1934
+ inFlightPrefixExpansions.clear();
1935
+ progressiveRequests.clear();
1936
+ progressivePrefixesByContext.clear();
1937
+ boundaryCache.clear();
1938
+ causalInferenceByContext.clear();
1939
+ causalInferenceByFamily.clear();
1940
+ exactEvidenceCountByContext.clear();
1941
+ prefixExpansionCache.clear();
1942
+ surfaceScoreCache.clear();
1943
+ causalLogitProcessor.resetState();
1195
1944
  }
1196
1945
  };
1197
1946
  };