Conversation
…l + CLI to use them Agent-Logs-Url: https://github.com/microsoft/TypeAgent/sessions/8766b4b5-3498-4828-8c69-9f701f721f2a Co-authored-by: curtisman <12101885+curtisman@users.noreply.github.com>
…prove comment clarity Agent-Logs-Url: https://github.com/microsoft/TypeAgent/sessions/8766b4b5-3498-4828-8c69-9f701f721f2a Co-authored-by: curtisman <12101885+curtisman@users.noreply.github.com>
… wrappers - Rename debug namespace from typeagent:shell:partial to typeagent:completion:partial - Narrow barrel export in completion/index.ts (exclude TSTNode) - Clarify needQuotes comment in SearchMenuItem - Delete shell re-export files (partialCompletionSession.ts, prefixTree.ts, searchMenuBase.ts) - Update shell imports to use agent-dispatcher/helpers/completion directly - Move 8 PartialCompletionSession test suites to dispatcher package - Fix pre-existing TS2304 in electronTypes.ts (split export type into import + re-export) - Add moduleNameMapper for subdirectory tests in dispatcher jest config
…ammar dev dep from shell
- Use undefined instead of null for session/menu state in enhancedConsole - Extract repeated position callback into shared getPos const - Remove redundant render() calls after session.update() (menu callback handles it) - Add session.resetToIdle() on Enter path for consistency with Tab - Add CompletionSource<T> type alias for union parameter - Trim shell test helpers to minimal subset needed by switchMode tests - Make jest moduleNameMapper depth-agnostic for src/ imports - Add SearchMenuBase adapter unit tests
Replace the dual getCompletions/completionDispatcher paths with a single CompletionController that wraps PartialCompletionSession + ISearchMenu. - Add CompletionController, CallbackSearchMenu, and createCompletionController factory in dispatcher/helpers/completion/controller.ts - Add getFilteredItems() to SearchMenuBase and ISearchMenu - Add CompletionState type and getCompletionState() to PartialCompletionSession - Refactor CLI: simplify questionWithCompletion to use CompletionController, remove ~150 lines of dead callback-mode code, delete cliSearchMenu.ts - Refactor Shell: partial.ts uses CompletionController instead of PartialCompletionSession directly - Narrow public API: remove BaseTSTData, TST, normalizeMatchText, PartialCompletionSession, and ICompletionDispatcher from public exports - Update switchMode integration tests to use CompletionController - Add 11 new controller tests (CLI path, Shell path, prefix extraction)
The backspace and printable character handlers updated the input buffer and called controller.update() but never called render(), so typed text was invisible. Every other key handler (arrows, Tab, Escape) already called render() correctly.
Add setOnUpdate() to CompletionController so the CLI can hook render() into the internal CallbackSearchMenu. Without this, the first keystroke triggered the async fetch but render() only ran synchronously before results arrived — completions only appeared on the next keystroke.
- Extract TST into trie.ts, introduce SearchMenuDataProvider interface - Replace SearchMenuBase with HeadlessSearchMenu (dispatcher) and slimmed SearchMenu (shell) - Make CompletionController implement SearchMenuDataProvider so it serves as the data source for SearchMenu - Minimize ISearchMenuControl to three command methods: updatePrefix, hide, invalidate — remove query methods (getFilteredItems, isActive) that round-tripped data PCS already owns - Move SearchMenuPosition out of dispatcher: position resolution is now purely a shell concern via getPosition callback in SearchMenu - PCS queries its own trie directly in getCompletionState and matchOrConsume instead of reading back through the menu
- Store lastInput in PartialCompletionSession on update()/explicitHide() - Make getCompletionState() parameterless (no longer takes input) - Compute and cache CompletionState at every mutation point (notifyUpdate) - getCompletionState() is now a simple getter returning cached state - Remove getCompletionPrefix() — callers use getCompletionState()?.prefix - Remove hidden flag — hide paths set completionState = undefined directly - Update controller, shell partial.ts, CLI enhancedConsole.ts callers - Update all test files to match new no-arg signatures
When the user types faster than the completion backend responds, the .then()/.catch() callbacks in startNewSession used stale captured input/direction values, causing missed re-fetches or inconsistent onUpdate callbacks. Changes: - Add lastDirection field to track the most recent direction - Add reconcileTypeAhead() helper that uses lastInput/lastDirection to evaluate the current input against the session (via reuseSession) after a fetch completes, issuing a new fetch only when needed - Apply reconcileTypeAhead in .then() (empty result, normal result) and .catch() (error recovery) paths - Fix explicitCloseAnchor suppression to account for type-ahead - Add typeAhead.spec.ts with 19 tests covering type-ahead behavior and verifying exact fetch counts (no extraneous re-fetches)
- Remove SearchMenuDataProvider interface; inline methods into
MutableSearchMenuDataProvider
- Remove unnecessary exports from completion index (SearchMenuDataProvider,
TSTSearchMenuDataProvider, SearchMenuPosition)
- Move SearchMenuPosition type to shell (electronTypes.ts)
- Remove filterItems/numChoices from PartialCompletionSession public API
- Replace test introspection (trieItems) with public getCompletionState()
and loadedItems helper
- Remove unused dataProvider parameter from TestableSearchMenu constructor
- Refactor createSearchMenu in templateEditor.ts to return {searchMenu,
dataProvider} object; store as single searchMenuData field on editUI
- Cast trieProvider to typed TSTSearchMenuDataProvider in test helpers
…rchMenuDataProvider to createSearchMenuIndex
…fix test helpers - Replace CompletionController class with an interface restricting the public API to consumer-facing methods (update, accept, dismiss, hide, getCompletionState, setOnUpdate). Internal methods like resetToIdle, explicitHide, and getLoadedItems are no longer exposed to consumers. - Add accept() and dismiss() aliases on PartialCompletionSession. - Add getLoadedItems() test-only method, removing unsafe 'as unknown as' cast in test helpers. - Add shared flushPromises() to dispatcher test helpers; remove duplicate from controller.spec.ts. - Document retained SearchMenu state fields (switchMode re-creation). - Add error→type-ahead→re-fetch round-trip test in typeAhead.spec.ts.
switchMode() no longer re-creates the UI internally. Instead it closes the old UI, flips the inline flag, and resets prefix. The caller (partial.ts) re-renders from the controller's current completion state. This removes lastPosition/lastPrefix/lastItems fields from SearchMenu, making it a stateless renderer. updateToggleLayout() now takes position as a parameter.
- Rename TSTSearchMenuDataProvider → TSTSearchMenuIndex - Rename setChoices/numChoices → setItems/numItems for consistency - Rename trieProvider field → searchMenuIndex - Inline resetToIdle into accept(), explicitHide into dismiss() - Rename searchMenuBase.spec.ts → searchMenuIndex.spec.ts - Update tests: explicitHide → dismiss, resetToIdle → accept - Update completion.md: fix section numbers (5a/5b/5c → 5/6/7), correct package location, rewrite stale §5c (SearchMenuBase removed), update CLI integration for backward direction support, clarify anchor terminology in Data flow section
SearchMenu.render() had a position-only shortcut that skipped item updates when the prefix didn't change, even though sep-level transitions loaded different items into the trie. - Add generation counter to CompletionState, incremented on every loadLevel() call in PartialCompletionSession - Split SearchMenu into render() (full update) and updatePosition() (position-only); remove the stale prefix/anchorIndex shortcut - Move render-vs-position decision to PartialCompletion (the caller), comparing both generation and prefix to detect item changes - Update TestableSearchMenu in switchMode.spec.ts to mirror changes - Add generation.spec.ts for generation counter behavior - Add renderDispatch.spec.ts for render vs updatePosition dispatch
- Remove computeCompletionState() and notifyUpdate() — the hot path (C3 ACTIVE) now inlines CompletionState construction since items are already computed by filterItems(). - Add filterForState() for cold paths (slideAnchor, dismiss level-shift) that still need trie filtering. - Add setCompletionState() to unify all state+callback sites, replacing the old notifyHide() + inline completionState/onUpdate pattern. - Fix slideAnchor: guard against returning items when menuSepLevel > 0 (deferred state requires separator before showing items).
…, enforce interface - Guard setCompletionState to skip undefined→undefined no-ops, eliminating redundant onUpdate/hide() calls when the menu is already hidden - Cache committedSepRe regex at module scope (hot path in matchOrConsume) - Route accept() through setCompletionState for consistent callback firing - Add implements CompletionController to PartialCompletionSession - Add debug log in reconcileTypeAhead when suppressing re-fetch - Improve getLoadedItems JSDoc with @internal tag - Update tests to assert the no-op optimization
…ranch # Conflicts: # ts/docs/architecture/completion.md # ts/packages/cli/src/commands/connect.ts # ts/packages/cli/src/commands/interactive.ts # ts/packages/cli/src/enhancedConsole.ts # ts/pnpm-lock.yaml
…s, robust flush - Convert startNewSession from .then()/.catch() to async/await with a single try/catch covering both fetch rejections and processing errors - Expand searchMenuIndex.spec.ts with tests for empty prefix, case insensitivity, diacritical normalization, duplicate handling, setItems replacement, and isUniquelySatisfied edge cases - Replace fragile hardcoded Promise.resolve() microtask flushing with setTimeout-based flushPromises that drains the full microtask queue - Update errorHandling test expectations for async/await microtask timing
- Add fetchNewSession() sync wrapper with terminal .catch() so unhandled rejections never escape from fire-and-forget async calls. reconcileTypeAhead now routes through fetchNewSession as well. - Add dispose() to CompletionController interface and PartialCompletionSession: clears state and detaches onUpdate callback. - Shell PartialCompletion.close() calls dispose() instead of hide() to prevent leaked closures holding DOM references. - Rename stale test names referencing removed getCompletionPrefix API to completionState.prefix.
…factor - Replace 6 stale computeCompletionState references with filterForState - Update 3 partialCompletionSession.ts references in actionGrammar.md to session.ts - Change shell-specific language to host-agnostic in session.ts comments - Rename explicitCloseAnchor to dismissAnchor to align with public API - Replace stale setChoices references with loadLevel in test comments - Fix misleading accept() test: split into two cases (visible vs hidden) - Change 'Hide the menu' to 'Hide completions' in host-agnostic code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extracts the completion infrastructure from the shell (Electron) package into a modular
dispatcher/helpers/completion/directory, introducing a cleanCompletionControllerinterface that both the shell and CLI consume as host adapters. The completion session state machine, trie index, and search menu logic are now fully DOM-free and independently unit-testable.Motivation
Previously,
PartialCompletionSession, the prefix trie (TST), andSearchMenuBaseall lived inside the shell renderer — tightly coupled to Electron/DOM and impossible to test without a browser environment. The CLI duplicated completion filtering logic instead of reusing the session. This refactor:CompletionControllerinterfaceArchitecture
New module structure (
dispatcher/src/helpers/completion/)index.tscontroller.tsCompletionControllerinterface (7 methods) +createCompletionControllerfactorysession.tsPartialCompletionSession— core IDLE→PENDING→ACTIVE state machinesearchMenu.tsSearchMenuItem,SearchMenuIndexinterface,TSTSearchMenuIndex(TST-backed prefix index)trie.tsnormalizeMatchText()Key abstractions
CompletionController— consumer-facing interface:update(),accept(),dismiss(),hide(),getCompletionState(),setOnUpdate(),dispose()SearchMenuIndex—filterItems(),numItems(),setItems(),hasExactMatch()backed by TSTCompletionState—{ items, prefix, anchorIndex, generation }wheregenerationis a monotonically-increasing counter for item-change detectionNoMatchPolicy— internal tri-state (accept | refetch | slide) replacing scattered boolean checksICompletionDispatcher— minimal interface the session needs from the dispatcherChanges
Dispatcher (
packages/dispatcher)src/helpers/completion.ts(old thin re-export shim)src/helpers/completion/directory with 5 modules (see table above)PartialCompletionSessionimplementsCompletionControllerdirectly (no wrapper class)generationcounter so hosts can distinguish "items changed" from "prefix-only update"lastInput/lastDirectionfields detect stale async results and re-fetch or re-filter as neededagent-dispatcher/helpers/completionpreserved (target changed fromdist/helpers/completion.js→dist/helpers/completion/index.js)Shell (
packages/shell)src/renderer/src/searchMenuBase.ts— replaced by dispatcher'sSearchMenuIndexSearchMenuno longer extends a base class; now a flat class with explicitrender(prefix, items)andupdatePosition(prefix)methodsgetPositioncallback) instead of per-call argumentsPartialCompletioncreates aCompletionControllervia factory and usessetOnUpdate()for async render wiringlastGenerationandlastPrefixto deciderender()vsupdatePosition()templateEditor.tsimports fromagent-dispatcher/helpers/completioninstead of shell re-export layeraction-grammardev dependency from shellCLI (
packages/cli)connect.tsimportscreateCompletionControllerfrom dispatcher and passes controller to enhanced consoleenhancedConsole.tsreceives aCompletionControllerinstead of managing its own completion stateallCompletions,completionPrefix, inline filtering) withcontroller.getCompletionState()in render looponUpdatecallback triggers re-render on async completion arrivalTests
Moved from shell → dispatcher (13 test files at
dispatcher/test/partialCompletion/):controller.spec.tssetOnUpdatecallback replacementgeneration.spec.tsCompletionState.generationcounter semanticssearchMenuIndex.spec.tssetItemsreplacementtypeAhead.spec.tshelpers.tsmakeSession,makeDispatcher,makeCompletionResult,flushPromises,loadedItemsdirection.spec.tserrorHandling.spec.tsgrammarE2E.spec.tspublicAPI.spec.tsresultProcessing.spec.tsseparatorMode.spec.tsstartIndexSeparatorContract.spec.tsstateTransitions.spec.tsRetained in shell (DOM-specific):
renderDispatch.spec.ts— New — testsrender()vsupdatePosition()dispatch based ongenerationcounterswitchMode.spec.ts—SearchMenu.switchMode()inline↔expanded toggleDocs
docs/architecture/completion.mdwith new module structure,CompletionControllerinterface, and state machine descriptionCleanup
needsSeparatorInAutoModere-export wrapper (session imports directly)moduleNameMapperfor../src/→dist/resolution