pi-ghost-autocomplete 0.3.2 → 0.5.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 (170) hide show
  1. package/LICENSE +561 -0
  2. package/README.md +186 -4
  3. package/dist/bench-cli.js +1 -123
  4. package/dist/bench.d.ts +7 -3
  5. package/dist/bench.js +1 -522
  6. package/dist/config.d.ts +46 -3
  7. package/dist/context-aggregator.d.ts +4 -1
  8. package/dist/cycle-manager.d.ts +2 -21
  9. package/dist/entropy-stop.d.ts +4 -1
  10. package/dist/eval-dataset.d.ts +2 -13
  11. package/dist/fuzzy-matcher.d.ts +4 -1
  12. package/dist/ghost-editor.d.ts +11 -2
  13. package/dist/index.d.ts +7 -2
  14. package/dist/index.js +1 -289
  15. package/dist/intent.d.ts +179 -0
  16. package/dist/metrics.d.ts +4 -1
  17. package/dist/path-index.d.ts +4 -1
  18. package/dist/path-matcher.d.ts +4 -1
  19. package/dist/profile-store.d.ts +4 -1
  20. package/dist/profile.d.ts +2 -20
  21. package/dist/providers/cloud-provider.d.ts +4 -1
  22. package/dist/providers/local-provider.d.ts +4 -1
  23. package/dist/providers/openai-compat-chat-provider.d.ts +31 -0
  24. package/dist/providers/openai-compat-provider.d.ts +24 -1
  25. package/dist/providers/race.d.ts +4 -1
  26. package/dist/providers/router.d.ts +59 -0
  27. package/dist/providers/types.d.ts +13 -1
  28. package/dist/recent-paths.d.ts +4 -1
  29. package/dist/replay-cli.js +1 -166
  30. package/dist/replay.d.ts +2 -7
  31. package/package.json +10 -6
  32. package/dist/bench-cli.d.ts +0 -3
  33. package/dist/bench-cli.d.ts.map +0 -1
  34. package/dist/bench-cli.js.map +0 -1
  35. package/dist/bench.d.ts.map +0 -1
  36. package/dist/bench.js.map +0 -1
  37. package/dist/cache.d.ts +0 -124
  38. package/dist/cache.d.ts.map +0 -1
  39. package/dist/cache.js +0 -173
  40. package/dist/cache.js.map +0 -1
  41. package/dist/candidate-rerank.d.ts +0 -57
  42. package/dist/candidate-rerank.d.ts.map +0 -1
  43. package/dist/candidate-rerank.js +0 -78
  44. package/dist/candidate-rerank.js.map +0 -1
  45. package/dist/config.d.ts.map +0 -1
  46. package/dist/config.js +0 -317
  47. package/dist/config.js.map +0 -1
  48. package/dist/context-aggregator.d.ts.map +0 -1
  49. package/dist/context-aggregator.js +0 -87
  50. package/dist/context-aggregator.js.map +0 -1
  51. package/dist/context-selector.d.ts +0 -128
  52. package/dist/context-selector.d.ts.map +0 -1
  53. package/dist/context-selector.js +0 -300
  54. package/dist/context-selector.js.map +0 -1
  55. package/dist/cycle-manager.d.ts.map +0 -1
  56. package/dist/cycle-manager.js +0 -168
  57. package/dist/cycle-manager.js.map +0 -1
  58. package/dist/edit-history-ring.d.ts +0 -148
  59. package/dist/edit-history-ring.d.ts.map +0 -1
  60. package/dist/edit-history-ring.js +0 -262
  61. package/dist/edit-history-ring.js.map +0 -1
  62. package/dist/entropy-stop.d.ts.map +0 -1
  63. package/dist/entropy-stop.js +0 -141
  64. package/dist/entropy-stop.js.map +0 -1
  65. package/dist/eval-dataset.d.ts.map +0 -1
  66. package/dist/eval-dataset.js +0 -127
  67. package/dist/eval-dataset.js.map +0 -1
  68. package/dist/fast-path-engine.d.ts +0 -286
  69. package/dist/fast-path-engine.d.ts.map +0 -1
  70. package/dist/fast-path-engine.js +0 -341
  71. package/dist/fast-path-engine.js.map +0 -1
  72. package/dist/fuzzy-matcher.d.ts.map +0 -1
  73. package/dist/fuzzy-matcher.js +0 -162
  74. package/dist/fuzzy-matcher.js.map +0 -1
  75. package/dist/ghost-editor.d.ts.map +0 -1
  76. package/dist/ghost-editor.js +0 -1491
  77. package/dist/ghost-editor.js.map +0 -1
  78. package/dist/index.d.ts.map +0 -1
  79. package/dist/index.js.map +0 -1
  80. package/dist/keystroke-timer.d.ts +0 -59
  81. package/dist/keystroke-timer.d.ts.map +0 -1
  82. package/dist/keystroke-timer.js +0 -94
  83. package/dist/keystroke-timer.js.map +0 -1
  84. package/dist/metrics-builder.d.ts +0 -71
  85. package/dist/metrics-builder.d.ts.map +0 -1
  86. package/dist/metrics-builder.js +0 -71
  87. package/dist/metrics-builder.js.map +0 -1
  88. package/dist/metrics.d.ts.map +0 -1
  89. package/dist/metrics.js +0 -212
  90. package/dist/metrics.js.map +0 -1
  91. package/dist/path-index.d.ts.map +0 -1
  92. package/dist/path-index.js +0 -148
  93. package/dist/path-index.js.map +0 -1
  94. package/dist/path-matcher.d.ts.map +0 -1
  95. package/dist/path-matcher.js +0 -88
  96. package/dist/path-matcher.js.map +0 -1
  97. package/dist/profile-store.d.ts.map +0 -1
  98. package/dist/profile-store.js +0 -199
  99. package/dist/profile-store.js.map +0 -1
  100. package/dist/profile.d.ts.map +0 -1
  101. package/dist/profile.js +0 -434
  102. package/dist/profile.js.map +0 -1
  103. package/dist/providers/cloud-provider.d.ts.map +0 -1
  104. package/dist/providers/cloud-provider.js +0 -109
  105. package/dist/providers/cloud-provider.js.map +0 -1
  106. package/dist/providers/local-provider.d.ts.map +0 -1
  107. package/dist/providers/local-provider.js +0 -48
  108. package/dist/providers/local-provider.js.map +0 -1
  109. package/dist/providers/openai-compat-provider.d.ts.map +0 -1
  110. package/dist/providers/openai-compat-provider.js +0 -215
  111. package/dist/providers/openai-compat-provider.js.map +0 -1
  112. package/dist/providers/openai-completions-runner.d.ts +0 -52
  113. package/dist/providers/openai-completions-runner.d.ts.map +0 -1
  114. package/dist/providers/openai-completions-runner.js +0 -141
  115. package/dist/providers/openai-completions-runner.js.map +0 -1
  116. package/dist/providers/race.d.ts.map +0 -1
  117. package/dist/providers/race.js +0 -441
  118. package/dist/providers/race.js.map +0 -1
  119. package/dist/providers/truncate.d.ts +0 -7
  120. package/dist/providers/truncate.d.ts.map +0 -1
  121. package/dist/providers/truncate.js +0 -57
  122. package/dist/providers/truncate.js.map +0 -1
  123. package/dist/providers/types.d.ts.map +0 -1
  124. package/dist/providers/types.js +0 -60
  125. package/dist/providers/types.js.map +0 -1
  126. package/dist/provisional-manager.d.ts +0 -93
  127. package/dist/provisional-manager.d.ts.map +0 -1
  128. package/dist/provisional-manager.js +0 -98
  129. package/dist/provisional-manager.js.map +0 -1
  130. package/dist/provisional.d.ts +0 -150
  131. package/dist/provisional.d.ts.map +0 -1
  132. package/dist/provisional.js +0 -238
  133. package/dist/provisional.js.map +0 -1
  134. package/dist/recent-paths.d.ts.map +0 -1
  135. package/dist/recent-paths.js +0 -114
  136. package/dist/recent-paths.js.map +0 -1
  137. package/dist/regret-tracker.d.ts +0 -84
  138. package/dist/regret-tracker.d.ts.map +0 -1
  139. package/dist/regret-tracker.js +0 -145
  140. package/dist/regret-tracker.js.map +0 -1
  141. package/dist/replay-cli.d.ts +0 -13
  142. package/dist/replay-cli.d.ts.map +0 -1
  143. package/dist/replay-cli.js.map +0 -1
  144. package/dist/replay.d.ts.map +0 -1
  145. package/dist/replay.js +0 -225
  146. package/dist/replay.js.map +0 -1
  147. package/dist/reranker.d.ts +0 -181
  148. package/dist/reranker.d.ts.map +0 -1
  149. package/dist/reranker.js +0 -324
  150. package/dist/reranker.js.map +0 -1
  151. package/dist/session-trie.d.ts +0 -103
  152. package/dist/session-trie.d.ts.map +0 -1
  153. package/dist/session-trie.js +0 -280
  154. package/dist/session-trie.js.map +0 -1
  155. package/dist/speculative.d.ts +0 -74
  156. package/dist/speculative.d.ts.map +0 -1
  157. package/dist/speculative.js +0 -107
  158. package/dist/speculative.js.map +0 -1
  159. package/dist/tool-summarize.d.ts +0 -40
  160. package/dist/tool-summarize.d.ts.map +0 -1
  161. package/dist/tool-summarize.js +0 -245
  162. package/dist/tool-summarize.js.map +0 -1
  163. package/dist/trie-sanitize.d.ts +0 -22
  164. package/dist/trie-sanitize.d.ts.map +0 -1
  165. package/dist/trie-sanitize.js +0 -142
  166. package/dist/trie-sanitize.js.map +0 -1
  167. package/dist/versions.d.ts +0 -23
  168. package/dist/versions.d.ts.map +0 -1
  169. package/dist/versions.js +0 -31
  170. package/dist/versions.js.map +0 -1
@@ -0,0 +1,179 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
5
+ /**
6
+ * Intent detection for ghost autocompletion.
7
+ *
8
+ * Centralises all prose / path / filename heuristics so they can be shared
9
+ * between the deterministic fast-path engine and the LLM provider layer
10
+ * without circular dependencies.
11
+ *
12
+ * This module is the single source of truth for:
13
+ * - `PromptIntent` — discriminated union for intent classification
14
+ * - `classifyPromptIntent()` — regex-based intent classifier
15
+ * - `ACTION_VERBS` — canonical action-verb list (used by isPathContext,
16
+ * draftLooksLikePathContext, and fuzzy-matcher)
17
+ * - `looksLikeProse()` — single-line prose detection
18
+ * - `looksLikeProseContext()` — multi-line prose detection
19
+ * - `isPathContext()` — path-prefix context validation
20
+ * - `looksLikeFilename()` — completion output shape detection
21
+ * - `draftLooksLikePathContext()` — draft context shape detection
22
+ *
23
+ * Consumers import from here; legacy re-exports live in `fast-path-engine.ts`
24
+ * and `providers/truncate.ts` for backward compatibility with existing tests.
25
+ */
26
+ /**
27
+ * Coarse-grained intent of the current prompt line. Used to route the
28
+ * autocompletion pipeline:
29
+ *
30
+ * - `command` — shell-like command (starts with `/`, `$`, `!`, `@`)
31
+ * → deterministic + LLM
32
+ * - `question` — user asks a question (starts with how/what/why/...)
33
+ * → LLM only (skip deterministic)
34
+ * - `instruction` — imperative sentence (starts with a verb)
35
+ * → deterministic + LLM
36
+ * - `path-ref` — user is typing a file path or referencing files
37
+ * → deterministic only
38
+ * - `prose` — natural-language sentence (none of the above)
39
+ * → LLM only (skip deterministic)
40
+ */
41
+ export type PromptIntent = "command" | "question" | "instruction" | "path-ref" | "prose";
42
+ /**
43
+ * Action verbs that strongly signal the user is about to reference a file
44
+ * path. Used by `isPathContext`, `draftLooksLikePathContext`, and the
45
+ * fuzzy matcher's `hasActionTrigger` gate.
46
+ *
47
+ * Single canonical source — do NOT duplicate elsewhere.
48
+ * Covers: file manipulation (open/edit/read/write/save), navigation (cd/ls),
49
+ * search (find/check/look), shell commands (cat/less/head/tail/touch/mkdir/
50
+ * rm/cp/mv), loading (load/import/include), and general (show/view/see/run/add).
51
+ */
52
+ export declare const ACTION_VERBS: Set<string>;
53
+ /** Alternation group for use in regex character classes. Kept in sync
54
+ * with `ACTION_VERBS` — rebuild with `[...ACTION_VERBS].join("|")`.
55
+ * Exported so consumers (fast-path-engine, etc.) can build verb-aware
56
+ * regexes without duplicating the list. */
57
+ export declare const ACTION_VERBS_ALT: string;
58
+ /**
59
+ * Heuristic: does the text before the cursor look like natural-language prose
60
+ * rather than a command / path context? Returns true when there are 4+ word
61
+ * tokens (\w{2,}) — a strong signal the user is writing a sentence, not a
62
+ * filename.
63
+ *
64
+ * This prevents the fuzzy matcher from proposing `config.ts` when the user
65
+ * types "please add a con|" ("con" fuzzy-matches config.ts at score ~75).
66
+ */
67
+ export declare function looksLikeProse(lineBeforeCursor: string): boolean;
68
+ /** Reason a multi-line prose context fired. Used for observability so
69
+ * callers can log *why* the deterministic path was suppressed. */
70
+ export type ProseContextReason = "density" | "cohesion" | "punctuation";
71
+ /** Detailed result of multi-line prose detection. `reason` is set iff
72
+ * `isProse` is true, naming the rule that triggered. */
73
+ export interface ProseContextResult {
74
+ isProse: boolean;
75
+ reason?: ProseContextReason;
76
+ }
77
+ /**
78
+ * Heuristic: do the surrounding lines indicate a prose context (the user
79
+ * is writing natural-language text across multiple lines)? Returns the
80
+ * verdict together with the matching reason for observability.
81
+ *
82
+ * Rules (first match wins):
83
+ *
84
+ * 1. **High word density**: the previous lines contain 8+ word tokens total
85
+ * (\w{2,}) — strong signal of paragraph-level prose.
86
+ * 2. **Lexical cohesion**: the previous lines share ≥2 word tokens with the
87
+ * current line — inspired by Hearst's TextTiling algorithm for
88
+ * detecting topic continuity.
89
+ * 3. **Sentence punctuation**: the previous non-empty line ends with a
90
+ * period or comma AND carries at least PUNCTUATION_MIN_PREV_TOKENS
91
+ * alpha word tokens. The token-count guard kills shell one-liners
92
+ * like `echo hello,` that would otherwise be misrouted.
93
+ * Semicolons are excluded (common in code).
94
+ */
95
+ export declare function classifyProseContext(previousLines: string[], currentLineBeforeCursor: string): ProseContextResult;
96
+ /**
97
+ * Boolean wrapper kept for back-compat with existing tests / call sites
98
+ * that don't need the reason. Prefer `classifyProseContext` when the
99
+ * caller wants to log why prose mode triggered.
100
+ *
101
+ * @param previousLines — lines before the current line
102
+ * @param currentLineBeforeCursor — text on the current line before the cursor
103
+ */
104
+ export declare function looksLikeProseContext(previousLines: string[], currentLineBeforeCursor: string): boolean;
105
+ /**
106
+ * Heuristic: is the path-prefix token in a context that suggests the user
107
+ * intends to type a filename? Returns true when:
108
+ * - The token is inside quotes
109
+ * - The token is preceded by an action verb + whitespace ("read src/|")
110
+ * - The token starts at column 0 or after only whitespace
111
+ * - The preceding character is a non-letter boundary (`=`, `:`, `,`, etc.)
112
+ *
113
+ * Returns false when the token is embedded in prose (e.g. "and then src/|").
114
+ */
115
+ export declare function isPathContext(lineBeforeCursor: string, _token: {
116
+ token: string;
117
+ insideQuote: boolean;
118
+ }): boolean;
119
+ /**
120
+ * Short human-readable description for each intent, suitable for injection
121
+ * into LLM system prompts or FIM comment headers. Single canonical source —
122
+ * both chat-style (`truncate.ts`) and FIM (`openai-compat-provider.ts`)
123
+ * providers use these so the wording stays consistent.
124
+ */
125
+ export declare const INTENT_DESCRIPTION: Record<PromptIntent, string>;
126
+ /**
127
+ * Classify the user's current prompt intent. Uses simple regex / keyword
128
+ * heuristics to decide which completion pipeline to activate.
129
+ *
130
+ * The classifier examines only the current line before the cursor (fast)
131
+ * plus optional previous lines for multi-line prose detection.
132
+ *
133
+ * Returns one of: `command`, `question`, `instruction`, `path-ref`, `prose`.
134
+ *
135
+ * Classification order (first match wins):
136
+ * 1. `command` — line starts with `/`, `!`, `$`, `@`
137
+ * 2. `path-ref` — line ends with a path-like prefix (contains `/`) or is
138
+ * inside quotes after an action verb
139
+ * 3. `question` — line starts with a question word (how/what/why/...)
140
+ * 4. `instruction` — line starts with an action verb
141
+ * 5. `prose` — multi-line prose context detected, or single-line prose
142
+ * (4+ word tokens)
143
+ */
144
+ export declare function classifyPromptIntent(lineBeforeCursor: string, previousLines?: string[]): PromptIntent;
145
+ /** Reason a prompt was classified as prose. `single-line` corresponds to
146
+ * `looksLikeProse` (4+ word tokens on the current line); the others
147
+ * mirror `ProseContextReason`. */
148
+ export type ProseReason = ProseContextReason | "single-line";
149
+ /** Detailed intent classification including the prose-rule reason when
150
+ * applicable. Lets the editor log *why* the deterministic path was
151
+ * suppressed without re-running the heuristic. */
152
+ export interface IntentClassification {
153
+ intent: PromptIntent;
154
+ proseReason?: ProseReason;
155
+ }
156
+ /**
157
+ * Detailed variant of `classifyPromptIntent`. Same classification order,
158
+ * but returns the prose-rule reason when intent === "prose" so callers
159
+ * can emit observability records.
160
+ */
161
+ export declare function classifyPromptIntentDetailed(lineBeforeCursor: string, previousLines?: string[]): IntentClassification;
162
+ /**
163
+ * Heuristic: does the completion text look like a raw filename or path?
164
+ * Matches patterns like "src/foo/bar.ts", "config.json", "./path/to/file",
165
+ * and extensions like .ts, .js, .py, .json, .md, etc.
166
+ *
167
+ * Intentionally conservative: requires at least a 2-char extension with
168
+ * no trailing dot, and rejects common false positives like "hello.there"
169
+ * by requiring the extension part to look like a real file extension
170
+ * (starts with a letter, contains only letters/digits).
171
+ */
172
+ export declare function looksLikeFilename(text: string): boolean;
173
+ /**
174
+ * Heuristic: does the user's draft text look like they're in a path-typing
175
+ * context? Returns true when the draft ends with a path-like prefix
176
+ * (contains `/`, optionally preceded by `./` or `~/`) or when the last
177
+ * few words include an action verb ("read", "open", "edit", etc.).
178
+ */
179
+ export declare function draftLooksLikePathContext(draft: string): boolean;
package/dist/metrics.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  /**
2
6
  * Source of truth for all completion modes. Both the type union (compile-time)
3
7
  * and the parser whitelist (runtime, in `bench.ts`) derive from this array so
@@ -150,4 +154,3 @@ export declare class MetricsLogger {
150
154
  * sees that *something* was there, without leaking content. SEC-27.
151
155
  */
152
156
  export declare function redactDebug(rec: DebugRecord): DebugRecord;
153
- //# sourceMappingURL=metrics.d.ts.map
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  export interface PathIndexOptions {
2
6
  cap?: number;
3
7
  signal?: AbortSignal;
@@ -17,4 +21,3 @@ export interface PathIndex {
17
21
  * path completion this session".
18
22
  */
19
23
  export declare function buildPathIndex(cwd: string, opts?: PathIndexOptions): Promise<PathIndex | null>;
20
- //# sourceMappingURL=path-index.d.ts.map
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { PathIndex } from "./path-index.js";
2
6
  export interface PathToken {
3
7
  token: string;
@@ -38,4 +42,3 @@ export declare function extractPathPrefixToken(before: string): PathToken | null
38
42
  * (nothing to insert).
39
43
  */
40
44
  export declare function findPathPrefix(index: PathIndex, token: string, opts?: FindPathPrefixOptions | number): PathPrefixMatch | null;
41
- //# sourceMappingURL=path-matcher.d.ts.map
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  /**
2
6
  * Stateful wrapper around `profile.ts` for the editor. Holds the in-memory
3
7
  * `ProfileIndex`, serializes writes, triggers periodic compaction, and
@@ -79,4 +83,3 @@ export declare class ProfileStore {
79
83
  }
80
84
  /** Exported for tests — surface compactor knob without re-exporting `profile.ts`. */
81
85
  export { DEFAULT_DECAY_DAYS };
82
- //# sourceMappingURL=profile-store.d.ts.map
package/dist/profile.d.ts CHANGED
@@ -1,23 +1,6 @@
1
1
  /**
2
- * User profile — long-lived, cross-session frequency store. PRD §M4.
3
- *
4
- * Storage: `.pi/ghost-autocomplete/profile.jsonl` (append-only). One JSON
5
- * record per line, written via the in-process writer.
6
- *
7
- * The profile feeds three local-only signals:
8
- * - ngram trigrams → reranker `profileBias` (PRD §M3c hook)
9
- * - path frequency → bounded boost to deterministic path/fuzzy ranking
10
- * - slash-command frequency → editor fast-path for `/cmd…` prefixes
11
- *
12
- * Privacy: raw full prefixes and full completions are NEVER persisted. We
13
- * store sanitized n-grams (≤4), absolute paths, slash commands, and SHA-256
14
- * hashes of prefix keys (`prefixHash`) for exact-prefix lookup without
15
- * writing prompt text. The sanitizer is shared with `trie-sanitize.ts` so
16
- * the entropy + key-prefix rules drop the same tokens.
17
- *
18
- * The on-disk format is intentionally append-only + line-delimited so a
19
- * crash mid-write never corrupts the existing index; partial trailing lines
20
- * are skipped on load. Rotation compacts when the file exceeds 2 MB.
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
21
4
  */
22
5
  export declare const PROFILE_REL = ".pi/ghost-autocomplete/profile.jsonl";
23
6
  export declare const DEFAULT_MAX_BYTES: number;
@@ -182,4 +165,3 @@ export declare function compactRecords(records: ProfileRecord[], opts?: {
182
165
  }): ProfileRecord[];
183
166
  /** Parse `PI_GHOST_PROFILE_DECAY_DAYS`. Falls back to default when invalid. */
184
167
  export declare function parseDecayDaysEnv(raw: string | undefined): number;
185
- //# sourceMappingURL=profile.d.ts.map
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { CloudProviderConfig } from "../config.js";
2
6
  import type { GhostProvider, GhostRequest, StreamDelta } from "./types.js";
3
7
  export declare class CloudProvider implements GhostProvider {
@@ -16,4 +20,3 @@ export declare class CloudProvider implements GhostProvider {
16
20
  */
17
21
  predictStream(req: GhostRequest): AsyncIterable<StreamDelta>;
18
22
  }
19
- //# sourceMappingURL=cloud-provider.d.ts.map
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { LocalProviderConfig } from "../config.js";
2
6
  import type { GhostProvider, GhostRequest, StreamDelta } from "./types.js";
3
7
  export declare class LocalProvider implements GhostProvider {
@@ -15,4 +19,3 @@ export declare class LocalProvider implements GhostProvider {
15
19
  predictStream(req: GhostRequest): AsyncIterable<StreamDelta>;
16
20
  private callOpts;
17
21
  }
18
- //# sourceMappingURL=local-provider.d.ts.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
5
+ import type { OpenAICompatChatProviderConfig } from "../config.js";
6
+ import type { PreflightResult } from "./openai-compat-provider.js";
7
+ import type { GhostProvider, GhostRequest } from "./types.js";
8
+ /**
9
+ * Chat-register provider for OpenAI-compatible `/chat/completions` backends
10
+ * (e.g. Inception Mercury's conversational model). Sibling of
11
+ * {@link OpenAICompatProvider}, which uses `/fim/completions` for code.
12
+ *
13
+ * This is the natural-language leg of {@link RouterProvider}: instead of
14
+ * completing code, it completes the developer's *message to the AI coding
15
+ * agent*. The persona that frames the model — "continue the developer's
16
+ * request, don't perform the task" — lives in {@link buildPromptMessages}'s
17
+ * system prompt, shared with the cloud/local chat providers so the wording
18
+ * can't drift across backends.
19
+ */
20
+ export declare class OpenAICompatChatProvider implements GhostProvider {
21
+ private readonly config;
22
+ readonly name: string;
23
+ constructor(config: OpenAICompatChatProviderConfig);
24
+ get debounceMs(): number;
25
+ /**
26
+ * Validate the API key with a minimal chat request. A successful response
27
+ * (even an empty completion) proves auth + connectivity.
28
+ */
29
+ preflight(): Promise<PreflightResult>;
30
+ predict(req: GhostRequest): Promise<string | null>;
31
+ }
@@ -1,5 +1,10 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { OpenAICompatProviderConfig } from "../config.js";
2
6
  import type { CandidateSet, GhostProvider, GhostRequest } from "./types.js";
7
+ import { type PromptIntent } from "../intent.js";
3
8
  export interface PreflightResult {
4
9
  ok: boolean;
5
10
  status?: number;
@@ -36,4 +41,22 @@ export declare class OpenAICompatProvider implements GhostProvider {
36
41
  private effectiveParallelN;
37
42
  private fireOne;
38
43
  }
39
- //# sourceMappingURL=openai-compat-provider.d.ts.map
44
+ /**
45
+ * Whether the conversation context should be included in the FIM prompt for a
46
+ * given intent. Pure + exported for direct testing. Set
47
+ * `PI_GHOST_FIM_CONTEXT_ALL_INTENTS=1` to force-include context for every
48
+ * intent (A/B against the empty-rate fix).
49
+ */
50
+ export declare function fimContextIncluded(intent: PromptIntent | undefined, allIntentsRaw: string | undefined): boolean;
51
+ /**
52
+ * Build the FIM prompt/suffix pair from the ghost request.
53
+ *
54
+ * Recent conversation messages are prepended as comments in the prompt so the
55
+ * model has context about what the user is working on — except for `prose` /
56
+ * `question` intents, where the context is dropped to avoid the empty-rate
57
+ * spike described on {@link FIM_CONTEXT_SUPPRESSED_INTENTS}.
58
+ */
59
+ export declare function buildFIMPrompt(req: GhostRequest, maxRecent: number): {
60
+ prompt: string;
61
+ suffix: string;
62
+ };
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { ProviderLatency } from "../metrics.js";
2
6
  import { type GhostProvider, type GhostRequest, type StreamDelta } from "./types.js";
3
7
  export interface RaceConfig {
@@ -94,4 +98,3 @@ export declare class RaceProvider implements GhostProvider {
94
98
  * all promises produce null (or throw). Never rejects.
95
99
  */
96
100
  export declare function firstNonNull<T>(promises: Array<Promise<T | null>>): Promise<T | null>;
97
- //# sourceMappingURL=race.d.ts.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
5
+ /**
6
+ * Register-routing provider (PRD §M3h). Holds two legs and dispatches each
7
+ * request to the one whose *register* fits the user's intent:
8
+ *
9
+ * - **FIM leg** (code register) — `command`, `path-ref`, and unclassified
10
+ * requests. A code fill-in-the-middle model (Mercury Edit) completes
11
+ * commands, paths, and code-shaped drafts.
12
+ * - **chat leg** (instruction register) — `instruction`, `question`, `prose`.
13
+ * A conversational model completes the developer's *message to the AI coding
14
+ * agent* (their request/instruction), instead of writing the solution. This
15
+ * is the fix for "the autocomplete keeps suggesting solutions": a code-FIM
16
+ * model, fed a natural-language prompt, completes it like code.
17
+ *
18
+ * When `chat` is null the router degrades to FIM-only — byte-for-byte the
19
+ * default behavior before routing existed, so enabling router mode without a
20
+ * reachable chat backend never regresses.
21
+ */
22
+ import type { PromptIntent } from "../intent.js";
23
+ import type { PreflightResult } from "./openai-compat-provider.js";
24
+ import { type CandidateSet, type GhostProvider, type GhostRequest } from "./types.js";
25
+ /** Pure routing predicate — exported for direct testing. */
26
+ export declare function routeToChat(intent: PromptIntent | undefined): boolean;
27
+ export declare class RouterProvider implements GhostProvider {
28
+ private readonly fim;
29
+ private readonly chat;
30
+ readonly name = "ghost-router";
31
+ constructor(fim: GhostProvider, chat: GhostProvider | null);
32
+ get debounceMs(): number;
33
+ /**
34
+ * Which leg an intent resolves to — `"chat"` only when a chat leg is
35
+ * present AND the intent is natural-language; otherwise `"fim"` (including
36
+ * the chat-absent fallback). Pure; used by the editor to tag metrics with
37
+ * the served register so bench can split FIM-vs-chat quality.
38
+ */
39
+ legFor(intent: PromptIntent | undefined): "fim" | "chat";
40
+ /** Choose the leg for this request. Chat leg only when present AND the
41
+ * intent is natural-language; otherwise FIM. */
42
+ private leg;
43
+ predict(req: GhostRequest): Promise<string | null>;
44
+ /**
45
+ * Route, then adapt the chosen leg to the multi-candidate surface. The
46
+ * editor calls this (router exposes no `predictStream`, so it takes the
47
+ * `wrapAsMulti` branch). `wrapAsMulti` uses the leg's own `predictMulti`
48
+ * when present (FIM parallel-fire) and otherwise wraps `predict`.
49
+ */
50
+ predictMulti(req: GhostRequest): Promise<CandidateSet | null>;
51
+ /**
52
+ * Preflight the FIM leg — it carries the primary key/endpoint and is the
53
+ * always-present leg. The chat leg shares the same vendor/key by default;
54
+ * its own failures surface as silently-dropped NL completions (which fall
55
+ * back to nothing rather than a wrong answer), so we don't block startup
56
+ * on it.
57
+ */
58
+ preflight(): Promise<boolean | PreflightResult>;
59
+ }
@@ -1,4 +1,9 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { PreflightResult } from "./openai-compat-provider.js";
6
+ import type { PromptIntent } from "../intent.js";
2
7
  export interface GhostRequest {
3
8
  editorText: string;
4
9
  editorTextBeforeCursor: string;
@@ -15,6 +20,14 @@ export interface GhostRequest {
15
20
  }>;
16
21
  cwd: string;
17
22
  signal: AbortSignal;
23
+ /**
24
+ * Classified intent of the current prompt line. When set, providers use
25
+ * it to tailor the system prompt / FIM prompt so the model receives
26
+ * explicit signals about what kind of completion to produce.
27
+ * Undefined for backward compatibility (providers built before intent
28
+ * classification was added).
29
+ */
30
+ intent?: PromptIntent;
18
31
  }
19
32
  /**
20
33
  * One candidate completion. `avgLogprob` is the average per-token logprob if
@@ -110,4 +123,3 @@ export declare function wrapAsStream(provider: GhostProvider, req: GhostRequest)
110
123
  export declare function streamToText(stream: AsyncIterable<StreamDelta>, startedAt: number, out?: {
111
124
  ttftMs?: number;
112
125
  }): Promise<string | null>;
113
- //# sourceMappingURL=types.d.ts.map
@@ -1,3 +1,7 @@
1
+ /**
2
+ * SPDX-License-Identifier: SSPL-1.0
3
+ * SPDX-FileCopyrightText: 2025 Alessandro Pungitore <alessandro@pungitore.ch>
4
+ */
1
5
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
6
  import { type RecentMessage } from "./context-aggregator.js";
3
7
  /**
@@ -22,4 +26,3 @@ export declare function recentBoost(matchPath: string, recent: Set<string> | nul
22
26
  * assistant messages. Oldest-first.
23
27
  */
24
28
  export declare function getRoleWeightedRecent(ctx: ExtensionContext, userMax: number, assistantMax: number): RecentMessage[];
25
- //# sourceMappingURL=recent-paths.d.ts.map