@atlaskit/editor-plugin-autocomplete 9.1.0 → 9.2.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.
@@ -30,6 +30,17 @@ export interface CommittedSuggestion {
30
30
  posterior: number;
31
31
  /** Final confidence-v2 ranking score. */
32
32
  rankScore: number;
33
+ /**
34
+ * Characters of already-typed prefix the insertion overwrites, for surfaces
35
+ * that carry their own casing.
36
+ *
37
+ * The scored path appends its tail and leaves what the user typed alone,
38
+ * which is right for prose. A harvested identifier is matched
39
+ * case-insensitively but is only correct in the casing it was written in, so
40
+ * accepting `ml-s` against `ML-Studio` has to replace rather than append.
41
+ * Absent on every other path.
42
+ */
43
+ replacesTypedPrefixLength?: number;
33
44
  /** Monotonic editor decision revision that owns this suggestion. */
34
45
  revision: number;
35
46
  /** How many scored candidates this surface's normaliser divided between. */
@@ -58,6 +69,21 @@ export interface AutocompletePluginState {
58
69
  * priority boost.
59
70
  */
60
71
  export interface AutocompleteContext {
72
+ /**
73
+ * Identifies what this context belongs to, for hosts whose editor outlives
74
+ * the thing it is writing about — a chat input that stays mounted across
75
+ * conversations and pages, say.
76
+ *
77
+ * When it changes, everything learned for the previous scope is dropped:
78
+ * both the L1 boosts and the harvested inline-code set are claims about what
79
+ * is being discussed, and neither transfers. The context reported alongside
80
+ * the new key is then ingested as if it were the first, so anything still
81
+ * current — an ongoing transcript, for instance — is primed again.
82
+ *
83
+ * Omit it and nothing resets; the editor learns for its own lifetime, which
84
+ * is right for a host mounted per comment or per page.
85
+ */
86
+ contextScopeKey?: string;
61
87
  /** Full page content as a string (e.g. markdown). */
62
88
  fullPageContent?: string;
63
89
  /** The currently selected text on the page, if any. */
@@ -73,6 +99,17 @@ export interface AutocompletePluginOptions {
73
99
  * word-frequency boosting. Called lazily so the preset can remain synchronous.
74
100
  */
75
101
  getContext?: () => Promise<AutocompleteContext | undefined>;
102
+ /**
103
+ * Opt in to suggesting inline-code identifiers seen in this session
104
+ * (`ml-studio` and friends), which no vocabulary holds and the scored path
105
+ * therefore cannot reach.
106
+ *
107
+ * Off by default and passed only by hosts whose own experiment enrolment
108
+ * covers it, so a surface that shares this plugin under a different gate is
109
+ * unaffected until it opts in too. The harvester is a separate chunk and is
110
+ * not requested at all while this is false.
111
+ */
112
+ harvestInlineCode?: boolean;
76
113
  /**
77
114
  * User locale used to determine whether autocomplete should run.
78
115
  * Defaults to browser locale when omitted.
@@ -25,6 +25,16 @@ declare global {
25
25
  * `__atlCtcDebug__.enable('verbose')`.
26
26
  */
27
27
  enable: (level?: 'verbose') => void;
28
+ /**
29
+ * Snapshot of the inline-code surfaces harvested this session, or what a
30
+ * typed prefix would be offered: `__atlCtcDebug__.harvest('ml-s')`.
31
+ *
32
+ * Installed by the harvester rather than declared with the rest of the
33
+ * API, and typed as `unknown` so the return shape can live with the module
34
+ * that owns it instead of creating a cycle back to this one. Undefined
35
+ * until the harvester chunk has loaded.
36
+ */
37
+ harvest?: (typedPrefix?: string) => unknown;
28
38
  isEnabled: () => boolean;
29
39
  /** Whether verbose logging (candidate tables + extra detail) is on. */
30
40
  isVerbose: () => boolean;
@@ -33,9 +43,9 @@ declare global {
33
43
  * has seen and how often — or one family of them:
34
44
  * `__atlCtcDebug__.session('poll')`.
35
45
  *
36
- * Installed by the predictor rather than declared with the rest of the
37
- * API, and typed as `unknown` so the return shape can live with the module
38
- * that owns it instead of creating a cycle back to this one.
46
+ * Installed by the predictor for the same reasons as `harvest` above, and
47
+ * distinct from it: this reports known words whose frequency the session
48
+ * raised, while `harvest` holds surfaces the vocabulary does not have.
39
49
  */
40
50
  session?: (prefix?: string) => unknown;
41
51
  };
@@ -71,11 +81,16 @@ export declare const ctcTag: (tag: string, body: string, tagStyle?: string) => v
71
81
  export declare const isAutocompleteDebugEnabled: () => boolean;
72
82
  export declare const isAutocompleteDebugVerbose: () => boolean;
73
83
  /**
74
- * Hang the L1 session-boost snapshot off the console API.
84
+ * Hang the harvest snapshot off the console API.
75
85
  *
76
86
  * Unlike the log helpers this is available whether or not debug is enabled:
77
87
  * inspecting state on demand is not logging, and asking someone to turn on
78
88
  * logging and retype to find out what the session already holds defeats the
79
89
  * point of being able to ask.
80
90
  */
91
+ export declare const registerCtcHarvestInspector: (inspect: (typedPrefix?: string) => unknown) => void;
92
+ /**
93
+ * Hang the L1 session-boost snapshot off the console API, on the same terms as
94
+ * `registerCtcHarvestInspector`: available whether or not logging is on.
95
+ */
81
96
  export declare const registerCtcSessionInspector: (inspect: (prefix?: string) => unknown) => void;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Inline-code harvester — the Class B candidate source.
3
+ *
4
+ * Class A terms (Atlassian, Confluence) already carry tenant frequencies, a word
5
+ * vector and canonical token ids, so they compete inside the scored pool. Class B
6
+ * terms — `ml-studio` and friends — exist only in the session and cannot be
7
+ * suggested at all today: `incrementSessionFreq` only touches trie nodes that
8
+ * already exist, so an unknown session word is silently discarded.
9
+ *
10
+ * Two sources carry the signal. Assistant replies arrive as markdown strings and
11
+ * keep their backticks; the live document has had its backticks consumed by the
12
+ * text-formatting input rule, so its spans are found through the `code` mark
13
+ * instead. Human chat messages arrive as ADF and are flattened to bare text, so
14
+ * they contribute no spans — which is why no author check is needed here.
15
+ *
16
+ * A harvested surface cannot be scored: there is no frequency to rank it, no
17
+ * vector to place it in context and no canonical token ids to price it under the
18
+ * model. Being marked as code is the whole of the evidence, so what stands in for
19
+ * a score is where the surface is allowed to speak — only on a prefix no
20
+ * vocabulary reached, and only once the scored path has finished with it.
21
+ *
22
+ * Sightings are still counted, per source and by different rules, but they do
23
+ * not admit a surface. They order the set under eviction, and they pick between
24
+ * two surfaces that complete the same typed prefix — see `HarvestedTerm` and
25
+ * `findHarvestedCompletion`.
26
+ *
27
+ * What the session is holding is visible at any time from the console:
28
+ * `__atlCtcDebug__.harvest()`, or `__atlCtcDebug__.harvest('ml-s')` to ask what
29
+ * a prefix would be offered — see `inspectInlineCodeHarvest`.
30
+ */
31
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
32
+ /** Why this surface was chosen over the others that completed the prefix. */
33
+ export type HarvestCollapseRule = 'alphabetical' | 'sightings' | 'sole-match';
34
+ export interface HarvestMatch {
35
+ /** Which rule settled the prefix; `sole-match` when there was no rival. */
36
+ collapsedBy: HarvestCollapseRule;
37
+ /** Code-marked spans in the document, as of the last walk. */
38
+ documentSpans: number;
39
+ /** Characters of the surface the user has not typed yet. */
40
+ ghostText: string;
41
+ /** Mentions across ingested reply and page text. */
42
+ replyOccurrences: number;
43
+ /** Surfaces that also completed the prefix and lost, in the losing order. */
44
+ rivalSurfaces: string[];
45
+ /** Full surface in its original casing, which replaces the typed prefix. */
46
+ surface: string;
47
+ /** Length of the typed prefix the surface replaces on accept. */
48
+ typedPrefixLength: number;
49
+ }
50
+ interface HarvestFunnel {
51
+ accepted: number;
52
+ rejectedInFence: number;
53
+ rejectedKnownL2: number;
54
+ rejectedKnownL3: number;
55
+ rejectedNonAlphaStart: number;
56
+ rejectedTooLong: number;
57
+ rejectedWhitespace: number;
58
+ spansFound: number;
59
+ }
60
+ /**
61
+ * Harvest single-backtick spans from a markdown-ish string. Fenced regions are
62
+ * skipped: they hold commands and file paths, and their contents are whitespace
63
+ * separated anyway.
64
+ */
65
+ export declare const harvestInlineCodeFromText: (text: string | undefined) => void;
66
+ /**
67
+ * Harvest code-marked text from the live document. The backtick input rule fires
68
+ * on the closing tick and removes both delimiters, so a finished span is only
69
+ * findable through its mark. Code blocks are skipped for the same reason fenced
70
+ * regions are.
71
+ *
72
+ * The whole document is re-read, and what it finds replaces the previous
73
+ * document counts rather than adding to them.
74
+ */
75
+ export declare const harvestInlineCodeFromDoc: (doc: PMNode) => void;
76
+ /**
77
+ * The harvested surface to offer for `typedPrefix`, or null.
78
+ *
79
+ * Rivals are collapsed rather than treated as a reason to stay quiet. Silence
80
+ * was the original rule, on the grounds that two session surfaces sharing a
81
+ * prefix have nothing to separate them, and it was wrong for the case this path
82
+ * exists to serve: a reply that answers "what are our services" names ten of
83
+ * them, several share a stem, and abstaining meant the harvester went quiet on
84
+ * exactly the reply it was built for. Being wrong here costs one keystroke —
85
+ * the ghost is ignored and typing continues — and staying silent costs the
86
+ * feature.
87
+ *
88
+ * `collapsedBy` records which rule settled it so a surprising ghost can be
89
+ * explained after the fact rather than guessed at.
90
+ */
91
+ export declare const findHarvestedCompletion: (typedPrefix: string) => HarvestMatch | null;
92
+ interface HarvestTermSnapshot {
93
+ /** Code-marked spans in the document as of the last walk. */
94
+ documentSpans: number;
95
+ /** Whether the surface is long enough to ever produce a ghost. */
96
+ longEnough: boolean;
97
+ /** Whether a sighting still stands behind it. */
98
+ offerable: boolean;
99
+ /** Mentions across ingested reply and page text. */
100
+ replyOccurrences: number;
101
+ /** Sum of both sources, which is what collapses rivals. */
102
+ sightings: number;
103
+ surface: string;
104
+ }
105
+ export interface HarvestSnapshot {
106
+ /** Size the set is held to; beyond it the weakest surfaces are dropped. */
107
+ cap: number;
108
+ /** How many surfaces have been dropped to hold the cap this session. */
109
+ evicted: number;
110
+ /**
111
+ * Intake accounting per source. The context funnel accumulates over the
112
+ * session; the live-document funnel describes the most recent walk only.
113
+ */
114
+ funnels: {
115
+ context: HarvestFunnel;
116
+ liveDocument: HarvestFunnel;
117
+ };
118
+ /**
119
+ * What a typed prefix would be offered, when one was passed.
120
+ *
121
+ * The harvester's own answer, not a prediction of what will appear on screen:
122
+ * the plugin still has to find the scored path finished and empty on that
123
+ * prefix, and still applies the accept cooldown and the repetition check.
124
+ */
125
+ match?: HarvestMatch | null;
126
+ /** The whole set in collapse order, so the winner for any prefix is above its rivals. */
127
+ terms: HarvestTermSnapshot[];
128
+ }
129
+ /**
130
+ * Read the session's harvest, optionally asking what `typedPrefix` would get.
131
+ *
132
+ * Installed as `__atlCtcDebug__.harvest()` and returned rather than logged, so
133
+ * the console renders it as an inspectable object and a caller can assert on it.
134
+ */
135
+ export declare const inspectInlineCodeHarvest: (typedPrefix?: string) => HarvestSnapshot;
136
+ export declare const resetInlineCodeHarvest: () => void;
137
+ /**
138
+ * Print the funnel when it has moved since the last print.
139
+ *
140
+ * Deliberately reports zero-span and all-rejected passes too. Logging only on a
141
+ * successful harvest makes "the replies held no inline code", "every span was
142
+ * filtered out" and "this never ran" indistinguishable, which are the three
143
+ * things worth telling apart.
144
+ */
145
+ export declare const logInlineCodeHarvest: (trigger: string) => void;
146
+ export {};
@@ -18,6 +18,35 @@
18
18
  * or `__atlCtcDebug__.session('poll')` for one family — see inspectSessionBoosts.
19
19
  */
20
20
  import type { TermType } from './scoring-pipeline';
21
+ /**
22
+ * Which constraint stopped an evaluation from putting a ghost on screen.
23
+ *
24
+ * Each one implies different work: `below-posterior-gate` is a threshold to
25
+ * calibrate, `winner-margin` is two candidates the model cannot separate,
26
+ * `cold-competitor` and `unresolved-rival` are scheduling, and `no-candidate`
27
+ * is vocabulary coverage.
28
+ */
29
+ export type CtcAbstainReason = 'below-posterior-gate' | 'cold-competitor' | 'empty-completion' | 'implausible-surface' | 'lone-candidate' | 'missing-artifact' | 'no-candidate' | 'no-evidence' | 'no-surface-token' | 'not-initialized' | 'prefetch' | 'short-completion' | 'unresolved-rival' | 'winner-margin';
30
+ /**
31
+ * What the scored path concluded, recorded on every evaluation whether or not
32
+ * debug is on.
33
+ *
34
+ * This exists for the inline-code harvester, which may only offer a harvested
35
+ * surface once the scored path has finished and come away empty. The reason is
36
+ * the load-bearing part: `no-candidate` means no vocabulary reaches this prefix
37
+ * at all, while `winner-margin` means two known words the model cannot yet
38
+ * separate — the first is a gap worth filling and the second is a prefix
39
+ * ambiguous enough that filling it would be a guess.
40
+ */
41
+ export interface PredictionOutcome {
42
+ abstainReason: CtcAbstainReason | null;
43
+ /** True while a pending async signal could still change the verdict. */
44
+ awaitingAsyncEvidence: boolean;
45
+ /** How many vocabulary candidates were scored for this prefix. */
46
+ scoredCandidateCount: number;
47
+ /** The exact string passed to `predict`, so a caller can confirm identity. */
48
+ textBefore: string;
49
+ }
21
50
  export interface WeightedTerm {
22
51
  authorFreq: number;
23
52
  docFreq: number;
@@ -37,12 +66,24 @@ interface VectorStore {
37
66
  * expects a simple array of strings: ["about", "above", "actually", ...]
38
67
  */
39
68
  export declare const initL3Vocabulary: (l3Words: string[]) => void;
69
+ /** The verdict from the most recent `predict` call. Always populated. */
70
+ export declare const getLastPredictionOutcome: () => PredictionOutcome | null;
40
71
  /**
41
72
  * Start a short cooldown for the exact surface the editor inserted. The caller
42
73
  * passes the committed snapshot's surface so background re-ranking can never
43
74
  * move cooldown bookkeeping away from what the user actually accepted.
44
75
  */
45
76
  export declare const noteSuggestionAccepted: (surface: string) => void;
77
+ /**
78
+ * Whether `surface` is the one the user just accepted and is still inside its
79
+ * cooldown window.
80
+ *
81
+ * Read-only, unlike the advance inside `predict`: the cooldown is measured in
82
+ * predictions, and a caller asking whether it is active must not consume one of
83
+ * them. Exported for the harvest path, which displays without going through
84
+ * arbitration and so would otherwise re-offer what was just accepted.
85
+ */
86
+ export declare const isSurfaceInAcceptCooldown: (surface: string) => boolean;
46
87
  /**
47
88
  * Get predictor status for debugging.
48
89
  * vectorsLoaded: true when semantic scoring is active
@@ -103,6 +144,24 @@ export declare const initPhrases: (artifact: PhraseArtifactJson, termType: TermT
103
144
  * Called from the plugin on word boundaries for efficient incremental boosting.
104
145
  */
105
146
  export declare const incrementSessionFreq: (word: string) => void;
147
+ /**
148
+ * Drop every L1 boost this session has accumulated.
149
+ *
150
+ * The vocabulary itself is left alone: only `sessionFreq` is cleared, so the
151
+ * tenant and generic frequencies a boost was sitting on top of survive. Called
152
+ * when the plugin decides the session it was learning for has ended — a new
153
+ * conversation, or a different page — since a boost is a claim about what is
154
+ * being discussed and that claim does not carry over.
155
+ */
156
+ export declare const resetSessionBoosts: () => void;
157
+ /**
158
+ * Which vocabulary already holds this surface, if any.
159
+ *
160
+ * Used by the inline-code harvester to drop terms the scored path can already
161
+ * serve, so that harvesting stays limited to words with no route to a
162
+ * suggestion today.
163
+ */
164
+ export declare const lookupVocabularySource: (word: string) => 'l2' | 'l3' | null;
106
165
  /**
107
166
  * Prime session frequencies from a document page string.
108
167
  *
@@ -143,7 +202,8 @@ export interface SessionSnapshot {
143
202
  * Only words the vocabulary already holds can carry a boost, because both writers
144
203
  * go through `incrementSessionFreq` and it only finds existing nodes. An ingested
145
204
  * word absent from the vocabulary is therefore missing from here and always will
146
- * be.
205
+ * be — that gap is what the inline-code harvester covers, and those surfaces show
206
+ * up under `__atlCtcDebug__.harvest()` instead.
147
207
  */
148
208
  export declare const inspectSessionBoosts: (prefix?: string) => SessionSnapshot;
149
209
  /**
@@ -158,8 +218,15 @@ export interface PredictionResult {
158
218
  verifiedChars: number;
159
219
  verifiedTokens: number;
160
220
  };
161
- /** Evidence tier that authorized display. Tier A is never display-eligible. */
162
- evidenceTier: 'canonical-full-surface' | 'network-logit';
221
+ /**
222
+ * Evidence tier that authorized display. Tier A is never display-eligible.
223
+ *
224
+ * `session-harvest` never passes through this module: an inline-code surface
225
+ * harvested from the session has no frequencies, vector or canonical token
226
+ * ids, so it is authorized by being marked as code on a prefix the scored
227
+ * path left unclaimed rather than by model evidence.
228
+ */
229
+ evidenceTier: 'canonical-full-surface' | 'network-logit' | 'session-harvest';
163
230
  /**
164
231
  * Mean per-token log-probability of the verified prefix.
165
232
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "9.1.0",
3
+ "version": "9.2.0",
4
4
  "description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "wink-nlp": "^2.4.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@atlaskit/editor-common": "^119.9.0",
30
+ "@atlaskit/editor-common": "^119.13.0",
31
31
  "@atlaskit/editor-plugin-analytics": "^15.0.0",
32
32
  "react": "^18.2.0 || ^19.2.0"
33
33
  },