From e378eb8ce3c73c312e13f40b4c1a63f63489f901 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 5 Mar 2026 08:23:28 -0800 Subject: [PATCH 01/51] completion --- ts/packages/actionGrammar/README.md | 2 +- .../actionGrammar/src/grammarMatcher.ts | 105 +- .../grammarCompletionNestedWildcard.spec.ts | 47 + .../grammarCompletionPrefixLength.spec.ts | 343 ++ ts/packages/actionGrammarCompiler/README.md | 2 +- ts/packages/agentSdk/src/command.ts | 8 + ts/packages/cache/src/cache/grammarStore.ts | 16 + .../src/constructions/constructionCache.ts | 19 + .../cache/test/mergeCompletionResults.spec.ts | 241 + ts/packages/cli/src/commands/interactive.ts | 42 +- .../dispatcher/src/command/completion.ts | 92 +- .../src/translation/requestCompletion.ts | 10 + .../dispatcher/test/completion.spec.ts | 247 + .../dispatcher/types/src/dispatcher.ts | 3 + ts/packages/shell/package.json | 6 + .../shell/src/preload/electronTypes.ts | 2 +- ts/packages/shell/src/renderer/src/partial.ts | 254 +- .../renderer/src/partialCompletionSession.ts | 317 ++ ts/packages/shell/src/tsconfig.json | 14 + .../test/partialCompletionSession.spec.ts | 694 +++ ts/packages/shell/test/tsconfig.json | 11 + ts/pnpm-lock.yaml | 4696 +++++++++++++++-- 22 files changed, 6294 insertions(+), 877 deletions(-) create mode 100644 ts/packages/actionGrammar/test/grammarCompletionNestedWildcard.spec.ts create mode 100644 ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts create mode 100644 ts/packages/cache/test/mergeCompletionResults.spec.ts create mode 100644 ts/packages/dispatcher/dispatcher/test/completion.spec.ts create mode 100644 ts/packages/shell/src/renderer/src/partialCompletionSession.ts create mode 100644 ts/packages/shell/src/tsconfig.json create mode 100644 ts/packages/shell/test/partialCompletionSession.spec.ts create mode 100644 ts/packages/shell/test/tsconfig.json diff --git a/ts/packages/actionGrammar/README.md b/ts/packages/actionGrammar/README.md index 8118b83217..f99738fc72 100644 --- a/ts/packages/actionGrammar/README.md +++ b/ts/packages/actionGrammar/README.md @@ -131,7 +131,7 @@ npm run test:integration | --------------------------- | ------------------------------------------------------------------------------------- | | `action-schema` (workspace) | Reading `.pas.json` schema files for checked-variable enrichment | | `debug` | Debug logging (`typeagent:grammar:*`, `typeagent:nfa:*`, `typeagent:actionGrammar:*`) | -| `regexp.escape` | Safe regex escaping for the legacy matcher | +| `regexp.escape` | Safe regex escaping for the recursive backtracking matcher matcher | ## Trademarks diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index f50d510ace..af61bf0a2e 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -21,7 +21,7 @@ const debugCompletion = registerDebug("typeagent:grammar:completion"); const separatorRegExpStr = "\\s\\p{P}"; const separatorRegExp = new RegExp(`[${separatorRegExpStr}]+`, "yu"); const wildcardTrimRegExp = new RegExp( - `[${separatorRegExpStr}]*(.+?)[${separatorRegExpStr}]*$`, + `[${separatorRegExpStr}]*([^${separatorRegExpStr}](?:.*[^${separatorRegExpStr}])?)[${separatorRegExpStr}]*$`, "yu", ); @@ -393,6 +393,13 @@ function createValue( } } +// Extract and trim a wildcard capture from `request[start..end)`. In the +// default spacing modes the result is stripped of leading/trailing separators +// (whitespace and punctuation). Returns `undefined` when the capture is empty +// or consists *entirely* of separator characters — e.g. a lone " " — so that +// the matcher rejects wildcard slots that contain no meaningful content. +// In "none" mode no trimming is performed; only a truly zero-length capture +// is rejected. function getWildcardStr( request: string, start: number, @@ -1022,6 +1029,17 @@ export type GrammarCompletionProperty = { export type GrammarCompletionResult = { completions: string[]; properties?: GrammarCompletionProperty[] | undefined; + // Number of characters from the input prefix that the grammar consumed + // before the completion point. The shell uses this to determine where + // to insert/filter completions (replacing the space-based heuristic). + matchedPrefixLength?: number | undefined; + // True when a separator (e.g. space) must be inserted between the + // already-typed prefix and the completion text. This happens when the + // grammar consumed the entire input and the spacing rules between the + // last typed character and the first completion character require a + // separator (e.g. Latin "play" → "music" needs a space, but CJK + // "再生" → "音楽" does not). + needsSeparator?: boolean | undefined; }; function getGrammarCompletionProperty( @@ -1029,6 +1047,8 @@ function getGrammarCompletionProperty( valueId: number, ): GrammarCompletionProperty | undefined { const temp = { ...state }; + + while (finalizeNestedRule(temp, undefined, true)) {} if (temp.valueIds === null) { // valueId would have been undefined throw new Error( @@ -1036,8 +1056,6 @@ function getGrammarCompletionProperty( ); } const wildcardPropertyNames: string[] = []; - - while (finalizeNestedRule(temp, undefined, true)) {} const match = createValue( temp.value, temp.valueIds, @@ -1107,6 +1125,12 @@ export function matchGrammarCompletion( const pending = initialMatchState(grammar); const completions: string[] = []; const properties: GrammarCompletionProperty[] = []; + // Track the furthest point the grammar consumed across all + // completion-producing states. This tells the caller where + // the "filter text" begins so it doesn't have to guess from + // whitespace (which breaks for CJK and other non-space scripts). + let maxPrefixLength: number | undefined; + let needsSeparator = false; while (pending.length > 0) { const state = pending.pop()!; debugMatch(state, `resume state`); @@ -1123,10 +1147,31 @@ export function matchGrammarCompletion( debugCompletion(`Completing ${nextPart.type} part ${state.name}`); if (nextPart.type === "string") { - debugCompletion( - `Adding completion text: "${nextPart.value.join(" ")}"`, - ); - completions.push(nextPart.value.join(" ")); + const completionText = nextPart.value.join(" "); + debugCompletion(`Adding completion text: "${completionText}"`); + completions.push(completionText); + // Grammar consumed up to state.index; completions start there. + maxPrefixLength = + maxPrefixLength === undefined + ? state.index + : Math.max(maxPrefixLength, state.index); + // When the grammar consumed the entire input, check + // whether a separator is needed between the last typed + // character and the first completion character. + if ( + state.index === prefix.length && + prefix.length > 0 && + completionText.length > 0 && + state.spacingMode !== "none" + ) { + needsSeparator = + needsSeparator || + requiresSeparator( + prefix[prefix.length - 1], + completionText[0], + state.spacingMode, + ); + } } } else { // We can't finalize the state because of empty pending wildcard @@ -1146,6 +1191,43 @@ export function matchGrammarCompletion( `Adding completion property: ${JSON.stringify(completionProperty)}`, ); properties.push(completionProperty); + // The wildcard starts at pendingWildcard.start; the + // grammar consumed everything before that. + maxPrefixLength = + maxPrefixLength === undefined + ? pendingWildcard.start + : Math.max(maxPrefixLength, pendingWildcard.start); + // Wildcard completions: the wildcard consumes up to + // pendingWildcard.start. If that position is at or + // before the end of the input (with only separators + // between the wildcard start and the cursor), we may + // need a separator before the property value. + // This covers two cases: + // 1. pendingWildcard.start === prefix.length — separator + // not typed yet (e.g. input "play", wildcard at 4). + // 2. pendingWildcard.start < prefix.length and only + // separators follow (e.g. input "play ", wildcard at + // 4, space at 4 is already typed). + // In both cases needsSeparator must be set so the caller + // strips the leading whitespace before filtering. + // (For wildcards the completion text varies, so we + // conservatively use a typical word char "a".) + if ( + pendingWildcard.start > 0 && + pendingWildcard.start <= prefix.length && + prefix.length > 0 && + state.spacingMode !== "none" && + nextNonSeparatorIndex(prefix, pendingWildcard.start) >= + prefix.length + ) { + needsSeparator = + needsSeparator || + requiresSeparator( + prefix[pendingWildcard.start - 1], + "a", + state.spacingMode, + ); + } } } else if (!matched) { // matchState failed on a string part and there's trailing text. @@ -1166,14 +1248,21 @@ export function matchGrammarCompletion( `Adding partial prefix completion: "${fullText}"`, ); completions.push(fullText); + // The partial text starts at state.index. + maxPrefixLength = + maxPrefixLength === undefined + ? state.index + : Math.max(maxPrefixLength, state.index); } } } } - const result = { + const result: GrammarCompletionResult = { completions, properties, + matchedPrefixLength: maxPrefixLength, + needsSeparator: needsSeparator || undefined, }; debugCompletion(`Completed. ${JSON.stringify(result)}`); return result; diff --git a/ts/packages/actionGrammar/test/grammarCompletionNestedWildcard.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionNestedWildcard.spec.ts new file mode 100644 index 0000000000..c71d5216a8 --- /dev/null +++ b/ts/packages/actionGrammar/test/grammarCompletionNestedWildcard.spec.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { loadGrammarRules } from "../src/grammarLoader.js"; +import { matchGrammarCompletion } from "../src/grammarMatcher.js"; + +describe("Grammar Completion - nested wildcard through rules", () => { + // Reproduces the bug where completing "play" returns "by" instead of + // a completionProperty for the wildcard . + // + // Grammar: + // = play $(trackName:) by $(artist:) + // -> { actionName: "playTrack", parameters: { trackName, artists: [artist] } } + // = $(trackName:) -> trackName + // = $(x:wildcard) + // = $(x:wildcard) + const g = [ + ` = play $(trackName:) by $(artist:) -> { actionName: "playTrack", parameters: { trackName, artists: [artist] } };`, + ` = $(trackName:) -> trackName;`, + ` = $(x:wildcard);`, + ` = $(x:wildcard);`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it('should return completionProperty for wildcard after "play"', () => { + const result = matchGrammarCompletion(grammar, "play"); + // After matching "play", the next part is $(trackName:) + // which ultimately resolves to a wildcard. The completion should + // include a property for that wildcard, not just "by". + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + }); + + it('should return completionProperty for wildcard after "play "', () => { + const result = matchGrammarCompletion(grammar, "play "); + // Same as above but with trailing space + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + }); + + it('should return "by" as completion after wildcard text', () => { + const result = matchGrammarCompletion(grammar, "play some song"); + // After the wildcard has captured text, "by" should appear as a + // completion for the next string part. + expect(result.completions).toContain("by"); + }); +}); diff --git a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts new file mode 100644 index 0000000000..e898bff6fa --- /dev/null +++ b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts @@ -0,0 +1,343 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { loadGrammarRules } from "../src/grammarLoader.js"; +import { matchGrammarCompletion } from "../src/grammarMatcher.js"; + +describe("Grammar Completion - matchedPrefixLength", () => { + describe("single string part", () => { + // All words in one string part — matchedPrefixLength is always 0 + // because the completion is the full string part text. + const g = ` = play music -> true;`; + const grammar = loadGrammarRules("test.grammar", g); + + it("returns full part as completion for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toEqual(["play music"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns full part as completion for partial prefix", () => { + const result = matchGrammarCompletion(grammar, "pl"); + expect(result.completions).toEqual(["play music"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns full part as completion for first word typed", () => { + const result = matchGrammarCompletion(grammar, "play "); + expect(result.completions).toEqual(["play music"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns undefined matchedPrefixLength when no completions", () => { + const result = matchGrammarCompletion(grammar, "xyz"); + expect(result.completions).toHaveLength(0); + expect(result.matchedPrefixLength).toBeUndefined(); + }); + + it("returns undefined matchedPrefixLength for exact match", () => { + const result = matchGrammarCompletion(grammar, "play music"); + expect(result.completions).toHaveLength(0); + expect(result.matchedPrefixLength).toBeUndefined(); + }); + }); + + describe("multi-part via nested rule", () => { + // Nested rule creates separate parts, so matchedPrefixLength + // reflects the position after the consumed nested rule. + const g = [ + ` = $(v:) music -> true;`, + ` = play -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("returns nested rule text for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toEqual(["play"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns second part after nested rule consumed", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toEqual(["music"]); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("returns second part after nested rule with trailing space", () => { + const result = matchGrammarCompletion(grammar, "play "); + expect(result.completions).toEqual(["music"]); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("returns second part for partial second word", () => { + const result = matchGrammarCompletion(grammar, "play m"); + expect(result.completions).toEqual(["music"]); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("returns undefined matchedPrefixLength for complete match", () => { + const result = matchGrammarCompletion(grammar, "play music"); + expect(result.completions).toHaveLength(0); + expect(result.matchedPrefixLength).toBeUndefined(); + }); + }); + + describe("multiple rules with shared prefix", () => { + // Multiple rules that share a prefix via nested rules + const g = [ + ` = $(v:) music -> "play_music";`, + ` = $(v:) video -> "play_video";`, + ` = play -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("returns both completions after shared prefix", () => { + const result = matchGrammarCompletion(grammar, "play "); + expect(result.completions.sort()).toEqual(["music", "video"]); + expect(result.matchedPrefixLength).toBe(4); + }); + }); + + describe("wildcard with terminator", () => { + // Wildcard between string parts: "play $(name) now" + const g = ` = play $(name) now -> { name: name };`; + const grammar = loadGrammarRules("test.grammar", g); + + it("returns wildcard property (not terminator) when only separator follows wildcard start", () => { + // "play " — the trailing space is only a separator, not valid + // wildcard content, so the wildcard can't finalize and we fall + // through to the property-completion path instead of offering + // the terminator string. + const result = matchGrammarCompletion(grammar, "play "); + expect(result.completions).toEqual([]); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("returns terminator with matchedPrefixLength tracking wildcard text", () => { + const result = matchGrammarCompletion(grammar, "play hello"); + expect(result.completions).toEqual(["now"]); + // Wildcard consumed "hello" — matchedPrefixLength includes it + expect(result.matchedPrefixLength).toBe(10); + }); + + it("returns terminator with matchedPrefixLength for trailing space", () => { + const result = matchGrammarCompletion(grammar, "play hello "); + expect(result.completions).toEqual(["now"]); + expect(result.matchedPrefixLength).toBe(11); + }); + }); + + describe("wildcard without terminator", () => { + const g = ` = play $(name) -> { name: name };`; + const grammar = loadGrammarRules("test.grammar", g); + + it("returns start rule for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toEqual(["play"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns property completion for separator-only trailing wildcard", () => { + // The trailing space is not valid wildcard content, so the + // wildcard can't finalize. The else-branch produces a + // property completion instead, setting matchedPrefixLength to + // the wildcard start position. + const result = matchGrammarCompletion(grammar, "play "); + expect(result.completions).toHaveLength(0); + expect(result.matchedPrefixLength).toBe(4); + }); + }); + + describe("CJK multi-part with nested rule", () => { + // CJK requires multi-part grammar for meaningful matchedPrefixLength + const g = [ + ` [spacing=auto] = $(v:) 音楽 -> true;`, + ` [spacing=auto] = 再生 -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("returns verb for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toEqual(["再生"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns noun completion after CJK verb typed", () => { + const result = matchGrammarCompletion(grammar, "再生"); + expect(result.completions).toEqual(["音楽"]); + // "再生" is 2 chars; matchedPrefixLength reflects position after verb + expect(result.matchedPrefixLength).toBe(2); + }); + + it("returns noun completion after CJK verb with space", () => { + const result = matchGrammarCompletion(grammar, "再生 "); + expect(result.completions).toEqual(["音楽"]); + expect(result.matchedPrefixLength).toBe(2); + }); + + it("returns no completions for exact match", () => { + const result = matchGrammarCompletion(grammar, "再生音楽"); + expect(result.completions).toHaveLength(0); + expect(result.matchedPrefixLength).toBeUndefined(); + }); + }); + + describe("CJK single string part", () => { + // Single string part — matchedPrefixLength is 0 (all text in one part) + const g = ` [spacing=auto] = 再生 音楽 -> true;`; + const grammar = loadGrammarRules("test.grammar", g); + + it("returns full part for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toEqual(["再生 音楽"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("returns full part for partial CJK prefix", () => { + const result = matchGrammarCompletion(grammar, "再生"); + expect(result.completions).toEqual(["再生 音楽"]); + expect(result.matchedPrefixLength).toBe(0); + }); + }); + + describe("CJK wildcard", () => { + const g = ` [spacing=auto] = 再生 $(name) 停止 -> { name: name };`; + const grammar = loadGrammarRules("test.grammar", g); + + it("returns property completion when only separator follows CJK wildcard start", () => { + // Same as the Latin case: trailing space is a separator, not + // valid wildcard content, so the terminator isn't offered. + const result = matchGrammarCompletion(grammar, "再生 "); + expect(result.completions).toEqual([]); + expect(result.matchedPrefixLength).toBe(2); + }); + + it("returns terminator after CJK prefix + wildcard text", () => { + const result = matchGrammarCompletion(grammar, "再生 hello"); + expect(result.completions).toEqual(["停止"]); + expect(result.matchedPrefixLength).toBe(8); + }); + }); + + describe("needsSeparator - Latin multi-part", () => { + // Latin grammar: "play" → "music" requires a space separator + const g = [ + ` = $(v:) music -> true;`, + ` = play -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("reports needsSeparator for Latin 'play' → 'music'", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toEqual(["music"]); + expect(result.needsSeparator).toBe(true); + }); + + it("does not report needsSeparator when trailing space exists", () => { + const result = matchGrammarCompletion(grammar, "play "); + expect(result.completions).toEqual(["music"]); + // state.index > prefix.length because space is consumed, + // so needsSeparator is not set (no adjacent chars to check) + expect(result.needsSeparator).toBeUndefined(); + }); + + it("does not report needsSeparator for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toEqual(["play"]); + expect(result.needsSeparator).toBeUndefined(); + }); + + it("does not report needsSeparator for partial prefix match", () => { + // "pl" matches partially → the completion replaces from state.index, + // so no separator needed (user is typing the keyword) + const result = matchGrammarCompletion(grammar, "pl"); + expect(result.completions).toEqual(["play"]); + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("needsSeparator - CJK multi-part", () => { + // CJK grammar: "再生" → "音楽" does NOT require a space separator + const g = [ + ` [spacing=auto] = $(v:) 音楽 -> true;`, + ` [spacing=auto] = 再生 -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator for CJK '再生' → '音楽'", () => { + const result = matchGrammarCompletion(grammar, "再生"); + expect(result.completions).toEqual(["音楽"]); + // CJK → CJK in auto mode: no separator needed + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("needsSeparator - mixed scripts", () => { + // Latin followed by CJK: no separator needed in auto mode + const g = [ + ` [spacing=auto] = $(v:) 音楽 -> true;`, + ` [spacing=auto] = play -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator for Latin 'play' → CJK '音楽'", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toEqual(["音楽"]); + // Latin → CJK in auto mode: different scripts, no separator needed + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("needsSeparator - spacing=required", () => { + const g = [ + ` [spacing=required] = $(v:) music -> true;`, + ` [spacing=required] = play -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("reports needsSeparator when spacing=required", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toEqual(["music"]); + expect(result.needsSeparator).toBe(true); + }); + }); + + describe("needsSeparator - spacing=optional", () => { + const g = [ + ` [spacing=optional] = $(v:) music -> true;`, + ` [spacing=optional] = play -> true;`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator when spacing=optional", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toEqual(["music"]); + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("needsSeparator - wildcard entity with trailing separator", () => { + // Grammar where the completion is a wildcard entity (not a static string). + // "play " (with trailing space) should still report needsSeparator=true + // so the caller knows to strip the leading separator before filtering. + const g = [ + `entity TrackName;`, + ` = play $(name:TrackName) -> { actionName: "play", parameters: { name } };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("reports needsSeparator for 'play' before wildcard", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.properties?.length).toBeGreaterThan(0); + expect(result.needsSeparator).toBe(true); + }); + + it("reports needsSeparator for 'play ' (separator already typed)", () => { + // pendingWildcard.start=4 < prefix.length=5; only separator between + // them → needsSeparator must still be true so the caller strips it. + const result = matchGrammarCompletion(grammar, "play "); + expect(result.properties?.length).toBeGreaterThan(0); + expect(result.needsSeparator).toBe(true); + }); + }); +}); diff --git a/ts/packages/actionGrammarCompiler/README.md b/ts/packages/actionGrammarCompiler/README.md index 7851dd3dcd..d0eff3bc72 100644 --- a/ts/packages/actionGrammarCompiler/README.md +++ b/ts/packages/actionGrammarCompiler/README.md @@ -9,7 +9,7 @@ The package provides two CLI entry points: - **`agc`** — production binary - **`agc-dev`** — development binary (with ts-node loader) -Both support a legacy invocation style: if the first argument isn't a recognized command name, the `compile` command is assumed automatically. +Both support a shortcut invocation style: if the first argument isn't a recognized command name, the `compile` command is assumed automatically. ## Commands diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index 68ea496dee..521e47d61e 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -56,6 +56,14 @@ export type CompletionGroup = { emojiChar?: string | undefined; // Optional icon for the completion category sorted?: boolean; // If true, the completions are already sorted. Default is false, and the completions sorted alphabetically. kind?: "literal" | "entity"; // Whether completions are fixed grammar tokens or entity values from agents + // Number of characters of the input consumed by the grammar/command parser + // before the completion point for this group. When present, the shell + // inserts completions at this offset, replacing space-based heuristics + // that fail for CJK and other non-space-delimited scripts. + prefixLength?: number | undefined; + // True when a separator (e.g. space) must be inserted between the + // already-typed prefix and the completion text. + needsSeparator?: boolean | undefined; }; export interface AppAgentCommandInterface { diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 41f2b2059e..2f3f0841a4 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -272,6 +272,8 @@ export class GrammarStoreImpl implements GrammarStore { } const completions: string[] = []; const properties: CompletionProperty[] = []; + let matchedPrefixLength: number | undefined; + let needsSeparator = false; const filter = new Set(namespaceKeys); for (const [name, entry] of this.grammars) { if (filter && !filter.has(name)) { @@ -353,6 +355,18 @@ export class GrammarStoreImpl implements GrammarStore { if (partial.completions.length > 0) { completions.push(...partial.completions); } + if (partial.matchedPrefixLength !== undefined) { + matchedPrefixLength = + matchedPrefixLength === undefined + ? partial.matchedPrefixLength + : Math.max( + matchedPrefixLength, + partial.matchedPrefixLength, + ); + } + if (partial.needsSeparator) { + needsSeparator = true; + } if ( partial.properties !== undefined && partial.properties.length > 0 @@ -378,6 +392,8 @@ export class GrammarStoreImpl implements GrammarStore { return { completions, properties, + matchedPrefixLength, + needsSeparator: needsSeparator || undefined, }; } } diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index be3781bc5c..2b46017d06 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -73,6 +73,11 @@ export type CompletionProperty = { export type CompletionResult = { completions: string[]; properties?: CompletionProperty[] | undefined; + // Characters consumed by the grammar before the completion point. + matchedPrefixLength?: number | undefined; + // True when a separator (e.g. space) must be inserted between the + // already-typed prefix and the completion text. + needsSeparator?: boolean | undefined; }; export function mergeCompletionResults( @@ -85,6 +90,17 @@ export function mergeCompletionResults( if (second === undefined) { return first; } + // Take the max matchedPrefixLength across both results. + let matchedPrefixLength: number | undefined; + if ( + first.matchedPrefixLength !== undefined || + second.matchedPrefixLength !== undefined + ) { + matchedPrefixLength = Math.max( + first.matchedPrefixLength ?? 0, + second.matchedPrefixLength ?? 0, + ); + } return { completions: [...first.completions, ...second.completions], properties: first.properties @@ -92,6 +108,9 @@ export function mergeCompletionResults( ? [...first.properties, ...second.properties] : first.properties : second.properties, + matchedPrefixLength, + needsSeparator: + first.needsSeparator || second.needsSeparator || undefined, }; } export class ConstructionCache { diff --git a/ts/packages/cache/test/mergeCompletionResults.spec.ts b/ts/packages/cache/test/mergeCompletionResults.spec.ts new file mode 100644 index 0000000000..553ddd4109 --- /dev/null +++ b/ts/packages/cache/test/mergeCompletionResults.spec.ts @@ -0,0 +1,241 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + CompletionResult, + mergeCompletionResults, +} from "../src/constructions/constructionCache.js"; + +describe("mergeCompletionResults", () => { + describe("matchedPrefixLength merging", () => { + it("returns undefined when both are undefined", () => { + const result = mergeCompletionResults(undefined, undefined); + expect(result).toBeUndefined(); + }); + + it("returns first when second is undefined", () => { + const first: CompletionResult = { + completions: ["a"], + matchedPrefixLength: 5, + }; + const result = mergeCompletionResults(first, undefined); + expect(result).toBe(first); + }); + + it("returns second when first is undefined", () => { + const second: CompletionResult = { + completions: ["b"], + matchedPrefixLength: 3, + }; + const result = mergeCompletionResults(undefined, second); + expect(result).toBe(second); + }); + + it("takes max of matchedPrefixLength when both are defined", () => { + const first: CompletionResult = { + completions: ["a"], + matchedPrefixLength: 5, + }; + const second: CompletionResult = { + completions: ["b"], + matchedPrefixLength: 10, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.matchedPrefixLength).toBe(10); + expect(result.completions).toEqual(["a", "b"]); + }); + + it("takes max of matchedPrefixLength (first is larger)", () => { + const first: CompletionResult = { + completions: ["a"], + matchedPrefixLength: 12, + }; + const second: CompletionResult = { + completions: ["b"], + matchedPrefixLength: 3, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.matchedPrefixLength).toBe(12); + }); + + it("returns undefined matchedPrefixLength when both are missing", () => { + const first: CompletionResult = { + completions: ["a"], + }; + const second: CompletionResult = { + completions: ["b"], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.matchedPrefixLength).toBeUndefined(); + expect(result.completions).toEqual(["a", "b"]); + }); + + it("uses the defined value when only first has matchedPrefixLength", () => { + const first: CompletionResult = { + completions: ["a"], + matchedPrefixLength: 7, + }; + const second: CompletionResult = { + completions: ["b"], + }; + const result = mergeCompletionResults(first, second)!; + // max(7, 0) = 7 + expect(result.matchedPrefixLength).toBe(7); + }); + + it("uses the defined value when only second has matchedPrefixLength", () => { + const first: CompletionResult = { + completions: [], + }; + const second: CompletionResult = { + completions: ["b"], + matchedPrefixLength: 4, + }; + const result = mergeCompletionResults(first, second)!; + // max(0, 4) = 4 + expect(result.matchedPrefixLength).toBe(4); + }); + }); + + describe("completions merging", () => { + it("merges completions from both results", () => { + const first: CompletionResult = { + completions: ["a", "b"], + }; + const second: CompletionResult = { + completions: ["c", "d"], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.completions).toEqual(["a", "b", "c", "d"]); + }); + + it("handles empty completions", () => { + const first: CompletionResult = { + completions: [], + }; + const second: CompletionResult = { + completions: ["c"], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.completions).toEqual(["c"]); + }); + }); + + describe("properties merging", () => { + it("merges properties from both results", () => { + const prop1 = { + actions: [], + names: ["name1"], + }; + const prop2 = { + actions: [], + names: ["name2"], + }; + const first: CompletionResult = { + completions: [], + properties: [prop1], + }; + const second: CompletionResult = { + completions: [], + properties: [prop2], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.properties).toEqual([prop1, prop2]); + }); + + it("returns first.properties when second has none", () => { + const prop1 = { + actions: [], + names: ["name1"], + }; + const first: CompletionResult = { + completions: [], + properties: [prop1], + }; + const second: CompletionResult = { + completions: [], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.properties).toBe(first.properties); + }); + + it("returns second.properties when first has none", () => { + const prop2 = { + actions: [], + names: ["name2"], + }; + const first: CompletionResult = { + completions: [], + }; + const second: CompletionResult = { + completions: [], + properties: [prop2], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.properties).toBe(second.properties); + }); + + it("returns undefined properties when neither has them", () => { + const first: CompletionResult = { + completions: [], + }; + const second: CompletionResult = { + completions: [], + }; + const result = mergeCompletionResults(first, second)!; + expect(result.properties).toBeUndefined(); + }); + }); + + describe("needsSeparator merging", () => { + it("returns undefined when neither has needsSeparator", () => { + const first: CompletionResult = { completions: ["a"] }; + const second: CompletionResult = { completions: ["b"] }; + const result = mergeCompletionResults(first, second)!; + expect(result.needsSeparator).toBeUndefined(); + }); + + it("returns true when first has needsSeparator", () => { + const first: CompletionResult = { + completions: ["a"], + needsSeparator: true, + }; + const second: CompletionResult = { completions: ["b"] }; + const result = mergeCompletionResults(first, second)!; + expect(result.needsSeparator).toBe(true); + }); + + it("returns true when second has needsSeparator", () => { + const first: CompletionResult = { completions: ["a"] }; + const second: CompletionResult = { + completions: ["b"], + needsSeparator: true, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.needsSeparator).toBe(true); + }); + + it("returns true when both have needsSeparator", () => { + const first: CompletionResult = { + completions: ["a"], + needsSeparator: true, + }; + const second: CompletionResult = { + completions: ["b"], + needsSeparator: true, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.needsSeparator).toBe(true); + }); + + it("preserves needsSeparator when first is undefined", () => { + const second: CompletionResult = { + completions: ["b"], + needsSeparator: true, + }; + const result = mergeCompletionResults(undefined, second); + expect(result).toBe(second); + expect(result!.needsSeparator).toBe(true); + }); + }); +}); diff --git a/ts/packages/cli/src/commands/interactive.ts b/ts/packages/cli/src/commands/interactive.ts index fc7b3b9121..5306940133 100644 --- a/ts/packages/cli/src/commands/interactive.ts +++ b/ts/packages/cli/src/commands/interactive.ts @@ -52,28 +52,11 @@ async function getCompletionsData( dispatcher: Dispatcher, ): Promise { try { - // Token-boundary logic: for non-@ input, only send complete tokens - // to the backend. The NFA can only match whole tokens, so sending a - // partial word like "p" fails. Instead, send up to the last token - // boundary and let the CLI filter locally by the partial word. - let queryLine = line; - const trimmed = line.trimStart(); - if ( - trimmed.length > 0 && - !trimmed.startsWith("@") && - !/\s$/.test(line) - ) { - const lastSpace = line.lastIndexOf(" "); - if (lastSpace === -1) { - // First word being typed: send empty to get start-state completions - queryLine = ""; - } else { - // Mid-word after spaces: send up to last token boundary - queryLine = line.substring(0, lastSpace + 1); - } - } - - const result = await dispatcher.getCommandCompletion(queryLine); + // Send the full input to the backend. The grammar matcher reports + // how much of the input it consumed (matchedPrefixLength → + // startIndex), so we no longer need space-based token-boundary + // heuristics here. + const result = await dispatcher.getCommandCompletion(line); if (!result || !result.completions || result.completions.length === 0) { return null; } @@ -86,19 +69,18 @@ async function getCompletionsData( } } - // When we truncated the query, compute filter position from original input - const filterStartIndex = - queryLine !== line - ? line.lastIndexOf(" ") === -1 - ? 0 - : line.lastIndexOf(" ") + 1 - : result.startIndex; + const filterStartIndex = result.startIndex; const prefix = line.substring(0, filterStartIndex); + // When the grammar reports a separator is needed between the + // typed prefix and the completion text, prepend a space so the + // readline display doesn't produce "playmusic" for "play" + "music". + const separator = result.needsSeparator ? " " : ""; + return { allCompletions, filterStartIndex, - prefix, + prefix: prefix + separator, }; } catch (e) { return null; diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 23a2d9c94a..1445d9e4d7 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -28,31 +28,6 @@ import { CommandCompletionResult } from "@typeagent/dispatcher-types"; const debug = registerDebug("typeagent:command:completion"); const debugError = registerDebug("typeagent:command:completion:error"); -// Return the index of the first character of the last partial token. -// This is where the shell's filter text begins. -// Examples: -// "play sh" → 5 (the "s" in "sh") -// "play " → 5 (= input.length, empty filter) -// "p" → 0 -// "@calen" → 1 (right after "@") -// "@cal foo" → 5 (the "f" in "foo") -function getFilterStart(input: string) { - const commandMatch = input.match(/^\s*@/); - if (commandMatch !== null) { - const afterAt = input.substring(commandMatch.length); - if (!/\s/.test(afterAt)) { - // No space after @command — filtering command name - return commandMatch.length; - } - } - if (/\s$/.test(input)) { - // Trailing whitespace — filter is empty, starts at end - return input.length; - } - const lastSpace = input.lastIndexOf(" "); - return lastSpace === -1 ? 0 : lastSpace + 1; -} - // Return the full flag name if we are waiting a flag value. Add boolean values for completions and return undefined if the flag is boolean. function getPendingFlag( params: ParsedCommandParams, @@ -130,11 +105,17 @@ function collectFlags( return flagCompletions; } +type ParameterCompletionResult = { + completions: CompletionGroup[]; + startIndex: number; +}; + async function getCommandParameterCompletion( descriptor: CommandDescriptor, context: CommandHandlerContext, result: ResolveCommandResult, -): Promise { + inputLength: number, +): Promise { const completions: CompletionGroup[] = []; if (typeof descriptor.parameters !== "object") { // No more completion, return undefined; @@ -184,17 +165,27 @@ async function getCommandParameterCompletion( const sessionContext = context.agents.getSessionContext( result.actualAppAgentName, ); - completions.push( - ...(await agent.getCommandCompletion( - result.commands, - params, - agentCommandCompletions, - sessionContext, - )), + const agentGroups = await agent.getCommandCompletion( + result.commands, + params, + agentCommandCompletions, + sessionContext, ); + completions.push(...agentGroups); } } - return completions; + + // Compute startIndex from the parse position. The filter text is the + // last incomplete token the user is typing (or empty at a boundary). + const trailingSpace = /\s$/.test(result.suffix); + const lastToken = + params.tokens.length > 0 + ? params.tokens[params.tokens.length - 1] + : undefined; + const filterLength = trailingSpace || !lastToken ? 0 : lastToken.length; + const startIndex = inputLength - filterLength; + + return { completions, startIndex }; } export async function getCommandCompletion( @@ -203,7 +194,6 @@ export async function getCommandCompletion( ): Promise { try { debug(`Command completion start: '${input}'`); - const filterStart = getFilterStart(input); // Always send the full input so the backend sees all typed text. const partialCommand = normalizeCommand(input, context); @@ -218,6 +208,11 @@ export async function getCommandCompletion( return undefined; } + // The parse-derived startIndex: command resolution consumed + // everything up to the suffix; within the suffix, parameter + // parsing determines the last incomplete token position. + let startIndex = input.length - result.suffix.length; + // Collect completions const completions: CompletionGroup[] = []; if (input.trim() === "") { @@ -244,6 +239,7 @@ export async function getCommandCompletion( descriptor, context, result, + input.length, ); if (parameterCompletions === undefined) { if (completions.length === 0) { @@ -251,7 +247,8 @@ export async function getCommandCompletion( return undefined; } } else { - completions.push(...parameterCompletions); + completions.push(...parameterCompletions.completions); + startIndex = parameterCompletions.startIndex; } } else { if (result.suffix.length !== 0) { @@ -279,9 +276,28 @@ export async function getCommandCompletion( } } - const completionResult = { - startIndex: filterStart, + // Allow grammar-reported prefixLength (from groups) to override + // the parse-derived startIndex. This handles CJK and other + // non-space-delimited scripts where the grammar matcher is the + // authoritative source for how far into the input it consumed. + const groupPrefixLength = completions.find( + (g) => g.prefixLength !== undefined, + )?.prefixLength; + if (groupPrefixLength !== undefined) { + startIndex = groupPrefixLength; + } + + // Extract needsSeparator from any group that reports it. + const needsSeparator = completions.some( + (g) => g.needsSeparator === true, + ) + ? true + : undefined; + + const completionResult: CommandCompletionResult = { + startIndex, completions, + needsSeparator, }; debug(`Command completion result:`, completionResult); diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 6407b3e76b..cf4b12f053 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -91,6 +91,8 @@ export async function requestCompletion( return []; } + const prefixLength = results.matchedPrefixLength; + const needsSeparator = results.needsSeparator; const completions: CompletionGroup[] = []; if (results.completions.length > 0) { completions.push({ @@ -98,6 +100,8 @@ export async function requestCompletion( completions: results.completions, needQuotes: false, // Request completions are partial, no quotes needed kind: "literal", + prefixLength, + needsSeparator, }); } @@ -117,6 +121,8 @@ export async function requestCompletion( completionProperty.actions, context, propertyCompletions, + prefixLength, + needsSeparator, ); } } @@ -130,6 +136,8 @@ async function collectActionCompletions( partialActions: ExecutableAction[], context: CommandHandlerContext, propertyCompletions: Map, + prefixLength?: number | undefined, + needsSeparator?: boolean | undefined, ) { for (const propertyName of properties) { const { action, parameterName } = getPropertyInfo( @@ -155,6 +163,8 @@ async function collectActionCompletions( needQuotes: false, // Request completions are partial, no quotes needed sorted: true, // REVIEW: assume property completions are already in desired order by the agent. kind: "entity", + prefixLength, + needsSeparator, }); } } diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts new file mode 100644 index 0000000000..70091b69f5 --- /dev/null +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + AppAgent, + AppAgentManifest, + CompletionGroup, +} from "@typeagent/agent-sdk"; +import { AppAgentProvider } from "../src/agentProvider/agentProvider.js"; +import { + type CommandHandlerContext, + closeCommandHandlerContext, + initializeCommandHandlerContext, +} from "../src/context/commandHandlerContext.js"; +import { getCommandInterface } from "@typeagent/agent-sdk/helpers/command"; +import { getCommandCompletion } from "../src/command/completion.js"; + +// --------------------------------------------------------------------------- +// Test agent with parameters for completion testing +// --------------------------------------------------------------------------- +const handlers = { + description: "Completion test agent", + defaultSubCommand: "run", + commands: { + run: { + description: "Run a task", + parameters: { + args: { + task: { + description: "Task name", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + _names: string[], + ): Promise => { + return [ + { + name: "Tasks", + completions: ["build", "test", "deploy"], + }, + ]; + }, + }, + nested: { + description: "Nested test", + commands: { + sub: { + description: "Nested sub", + parameters: { + args: { + value: { + description: "A value", + }, + }, + flags: { + verbose: { + description: "Enable verbose", + type: "boolean" as const, + char: "v", + }, + }, + }, + run: async () => {}, + }, + }, + }, + }, +} as const; + +const config: AppAgentManifest = { + emojiChar: "🧪", + description: "Completion test", +}; + +const agent: AppAgent = { + ...getCommandInterface(handlers), +}; + +const testCompletionAgentProvider: AppAgentProvider = { + getAppAgentNames: () => ["comptest"], + getAppAgentManifest: async (name: string) => { + if (name !== "comptest") throw new Error(`Unknown: ${name}`); + return config; + }, + loadAppAgent: async (name: string) => { + if (name !== "comptest") throw new Error(`Unknown: ${name}`); + return agent; + }, + unloadAppAgent: async (name: string) => { + if (name !== "comptest") throw new Error(`Unknown: ${name}`); + }, +}; + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- +describe("Command Completion - startIndex", () => { + let context: CommandHandlerContext; + + beforeAll(async () => { + context = await initializeCommandHandlerContext("test", { + agents: { + actions: false, + schemas: false, + }, + translation: { enabled: false }, + explainer: { enabled: false }, + cache: { enabled: false }, + appAgentProviders: [testCompletionAgentProvider], + }); + }); + afterAll(async () => { + if (context) { + await closeCommandHandlerContext(context); + } + }); + + describe("agent + subcommand resolution", () => { + it("returns startIndex at suffix boundary for '@comptest run '", async () => { + const result = await getCommandCompletion( + "@comptest run ", + context, + ); + expect(result).toBeDefined(); + // "@comptest run " → suffix is "" after command resolution, + // and parameter parsing has no tokens so + // startIndex = inputLength - 0 = 14 + expect(result!.startIndex).toBe(14); + }); + + it("returns startIndex accounting for partial param for '@comptest run bu'", async () => { + const result = await getCommandCompletion( + "@comptest run bu", + context, + ); + expect(result).toBeDefined(); + // "@comptest run bu" (16 chars) + // suffix is "bu", parameter parsing sees token "bu" (2 chars) + // startIndex = 16 - 2 = 14 + expect(result!.startIndex).toBe(14); + }); + + it("returns startIndex for nested command '@comptest nested sub '", async () => { + const result = await getCommandCompletion( + "@comptest nested sub ", + context, + ); + expect(result).toBeDefined(); + // "@comptest nested sub " (21 chars) + // suffix is "" after command resolution; + // parameter parsing has no tokens; startIndex = 21 - 0 = 21 + expect(result!.startIndex).toBe(21); + }); + + it("returns startIndex for partial flag '@comptest nested sub --ver'", async () => { + const result = await getCommandCompletion( + "@comptest nested sub --ver", + context, + ); + expect(result).toBeDefined(); + // "@comptest nested sub --ver" (26 chars) + // suffix is "--ver", parameter parsing sees token "--ver" (5 chars) + // startIndex = 26 - 5 = 21 + expect(result!.startIndex).toBe(21); + }); + }); + + describe("empty and minimal input", () => { + it("returns completions for empty input", async () => { + const result = await getCommandCompletion("", context); + expect(result).toBeDefined(); + expect(result!.completions.length).toBeGreaterThan(0); + // completions should include "@" + const prefixes = result!.completions.find( + (g) => g.name === "Command Prefixes", + ); + expect(prefixes).toBeDefined(); + expect(prefixes!.completions).toContain("@"); + }); + + it("returns startIndex 0 for empty input", async () => { + const result = await getCommandCompletion("", context); + expect(result).toBeDefined(); + expect(result!.startIndex).toBe(0); + }); + + it("returns startIndex at end for whitespace-only input", async () => { + const result = await getCommandCompletion(" ", context); + expect(result).toBeDefined(); + // " " normalizes to a command prefix with no suffix; + // startIndex = input.length - suffix.length = 2 + expect(result!.startIndex).toBe(2); + }); + }); + + describe("agent name level", () => { + it("returns subcommands at agent boundary '@comptest '", async () => { + const result = await getCommandCompletion("@comptest ", context); + expect(result).toBeDefined(); + const subcommands = result!.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommands).toBeDefined(); + expect(subcommands!.completions).toContain("run"); + expect(subcommands!.completions).toContain("nested"); + }); + + it("returns undefined for unknown agent", async () => { + const result = await getCommandCompletion( + "@unknownagent ", + context, + ); + expect(result).toBeUndefined(); + }); + }); + + describe("startIndex tracks last token position", () => { + it("startIndex at token boundary with trailing space", async () => { + const result = await getCommandCompletion( + "@comptest run build ", + context, + ); + // "@comptest run build " (20 chars) + // suffix is "build ", token "build" is complete, trailing space + // means filter length = 0, so startIndex = 20 + expect(result).toBeDefined(); + expect(result!.startIndex).toBe(20); + }); + }); + + describe("needsSeparator for command completions", () => { + it("returns undefined needsSeparator for @-command completions", async () => { + const result = await getCommandCompletion( + "@comptest run ", + context, + ); + expect(result).toBeDefined(); + // @-command completions go through command handler, not grammar, + // so needsSeparator should not be set. + expect(result!.needsSeparator).toBeUndefined(); + }); + }); +}); diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index cffea84da7..de4c7fe1af 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -54,6 +54,9 @@ export type CommandResult = { export type CommandCompletionResult = { startIndex: number; // index of first character of the filter text (after the last space) completions: CompletionGroup[]; // completions available at the current position + // True when a separator (e.g. space) must be inserted between the + // already-typed prefix and the completion text. + needsSeparator?: boolean | undefined; }; export type AppAgentStatus = { diff --git a/ts/packages/shell/package.json b/ts/packages/shell/package.json index 852f5c3a32..7de075d71e 100644 --- a/ts/packages/shell/package.json +++ b/ts/packages/shell/package.json @@ -48,6 +48,9 @@ "start:package:linux": "./dist/linux-unpacked/typeagentshell", "start:package:macos": "open ./dist/mac-arm64/TypeAgent\\ Shell.app", "start:package:win32": ".\\dist\\win-unpacked\\typeagentshell.exe", + "jest-esm": "node --no-warnings --experimental-vm-modules ./node_modules/jest/bin/jest.js", + "test:local": "pnpm run jest-esm --testPathPattern=\".*[.]spec[.]js\"", + "tsc:model": "tsc -b src test", "typecheck": "concurrently npm:typecheck:node npm:typecheck:web", "typecheck:node": "tsc -p tsconfig.node.json", "typecheck:web": "tsc -p tsconfig.web.json" @@ -85,12 +88,15 @@ "@fontsource/lato": "^5.2.5", "@playwright/test": "^1.55.0", "@types/debug": "^4.1.12", + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.7", "concurrently": "^9.1.2", "cross-env": "^7.0.3", "electron": "40.6.0", "electron-builder": "26.8.1", "electron-builder-squirrel-windows": "26.8.1", "electron-vite": "^4.0.1", + "jest": "^29.7.0", "less": "^4.2.0", "rimraf": "^6.0.1", "run-script-os": "^1.1.6", diff --git a/ts/packages/shell/src/preload/electronTypes.ts b/ts/packages/shell/src/preload/electronTypes.ts index 42c830bc79..8c28d5af3e 100644 --- a/ts/packages/shell/src/preload/electronTypes.ts +++ b/ts/packages/shell/src/preload/electronTypes.ts @@ -93,7 +93,7 @@ export interface Client { focusInput(): void; titleUpdated(title: string): void; - searchMenuCompletion(id: number, item: SearchMenuItem); + searchMenuCompletion(id: number, item: SearchMenuItem): void; continuousSpeechProcessed(userExpressions: UserExpression[]): void; tabRestoreStatus(count: number): void; systemNotification?(message: string, id: string, timestamp: number): void; diff --git a/ts/packages/shell/src/renderer/src/partial.ts b/ts/packages/shell/src/renderer/src/partial.ts index 85e23b9b8a..32c88adda6 100644 --- a/ts/packages/shell/src/renderer/src/partial.ts +++ b/ts/packages/shell/src/renderer/src/partial.ts @@ -1,9 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { CommandCompletionResult, Dispatcher } from "agent-dispatcher"; +import { Dispatcher } from "agent-dispatcher"; import { SearchMenu } from "./search"; import { SearchMenuItem } from "./searchMenuUI/searchMenuUI"; +import { + ICompletionDispatcher, + ISearchMenu, + PartialCompletionSession, +} from "./partialCompletionSession"; import registerDebug from "debug"; import { ExpandableTextArea } from "./chat/expandableTextArea"; @@ -45,18 +50,14 @@ function getLeafNode(node: Node, offset: number) { export class PartialCompletion { private readonly searchMenu: SearchMenu; - private current: string | undefined = undefined; - private noCompletion: boolean = false; - private completionP: - | Promise - | undefined; + private readonly session: PartialCompletionSession; public closed: boolean = false; private readonly cleanupEventListeners: () => void; constructor( private readonly container: HTMLDivElement, private readonly input: ExpandableTextArea, - private readonly dispatcher: Dispatcher, + dispatcher: Dispatcher, private readonly inline: boolean = true, ) { this.searchMenu = new SearchMenu( @@ -66,6 +67,17 @@ export class PartialCompletion { this.inline, this.input.getTextEntry(), ); + + // Wrap SearchMenu to implement ISearchMenu (same shape, just typed). + const menuAdapter: ISearchMenu = this.searchMenu; + // Wrap Dispatcher to implement ICompletionDispatcher. + const dispatcherAdapter: ICompletionDispatcher = dispatcher; + + this.session = new PartialCompletionSession( + menuAdapter, + dispatcherAdapter, + ); + const selectionChangeHandler = () => { debug("Partial completion update on selection changed"); this.update(false); @@ -93,88 +105,27 @@ export class PartialCompletion { this.input.getTextEntry().normalize(); } if (!this.isSelectionAtEnd(contentChanged)) { - this.cancelCompletionMenu(); + this.session.hide(); return; } const input = this.getCurrentInputForCompletion(); debug(`Partial completion input: '${input}'`); - // @ commands: use existing command completion path. - // Same token-boundary logic as grammar completions: only re-fetch - // at word boundaries so partial words (e.g. "@config c") don't hit - // the backend, which would fail to resolve "c" and poison noCompletion. - if (input.trimStart().startsWith("@")) { - if (this.reuseSearchMenu(input)) { - return; - } - // Re-fetch at the last word boundary so the backend sees only - // complete command tokens and returns proper subcommand completions. - const lastSpaceIdx = input.lastIndexOf(" "); - if (/\s$/.test(input)) { - this.updatePartialCompletion(input); - } else if (lastSpaceIdx >= 0) { - this.updatePartialCompletion( - input.substring(0, lastSpaceIdx + 1), - ); - } else { - this.updatePartialCompletion(input); - } - return; - } - - // Empty input: hide any open menu and stop — don't show completions on - // an empty box (e.g. after backspacing all the way). This check must - // come before reuseSearchMenu() because reuseSearchMenu("") would - // match current="" and show all items for the start-state request. - const trimmed = input.trimStart(); - if (trimmed.length === 0) { - this.cancelCompletionMenu(); - return; - } - - // Request completions: only request at token boundaries. - // Between boundaries, filter the existing menu locally. - if (this.reuseSearchMenu(input)) { - return; - } - - // Determine whether this is a token boundary: - // 1. Trailing space → complete tokens available, request with them - // 2. Non-empty with no spaces → first typing, request start state (tokens=[]) - // 3. Otherwise (mid-word after spaces, no menu) → wait for next space - const hasTrailingSpace = /\s$/.test(input); - const hasSpaces = /\s/.test(trimmed); - - if (hasTrailingSpace) { - // Token boundary: send full input (all tokens are complete) - this.updatePartialCompletion(input); - } else if (!hasSpaces) { - // Start state: request with "" so backend returns all initial completions. - // The typed characters (e.g. "p") become the local filter via current="". - this.updatePartialCompletion(""); - } else { - // Mid-word with spaces and no active menu (e.g. backspace removed - // a trailing space). Request at the last token boundary so the - // menu reappears with the partial word as the local filter. - const lastSpaceIdx = input.lastIndexOf(" "); - if (lastSpaceIdx >= 0) { - this.updatePartialCompletion( - input.substring(0, lastSpaceIdx + 1), - ); - } - } + this.session.update(input, (prefix) => + this.getSearchMenuPosition(prefix), + ); } public hide() { - this.completionP = undefined; - this.cancelCompletionMenu(); + this.session.hide(); } public close() { this.closed = true; - this.hide(); + this.session.hide(); this.cleanupEventListeners(); } + private getCurrentInput() { // Strip inline ghost text if present const textEntry = this.input.getTextEntry(); @@ -186,6 +137,7 @@ export class PartialCompletion { } return textEntry.textContent ?? ""; } + private getCurrentInputForCompletion() { return this.getCurrentInput().trimStart(); } @@ -258,136 +210,7 @@ export class PartialCompletion { return true; } - private getCompletionPrefix(input: string) { - const current = this.current; - if (current === undefined) { - return undefined; - } - if (!input.startsWith(current)) { - return undefined; - } - return input.substring(current.length); - } - - // Determine if the current search menu can still be reused, or if we need to update the completions. - // Returns true to reuse (skip re-fetch), false to trigger a new completion request. - private reuseSearchMenu(input: string) { - const current = this.current; - if (current === undefined) { - return false; - } - - if (this.completionP !== undefined) { - debug(`Partial completion pending: ${current}`); - return true; - } - - if (!input.startsWith(current)) { - // Input diverged (e.g. backspace past the anchor point). - return false; - } - - if (this.noCompletion) { - debug( - `Partial completion skipped: No completions for '${current}'`, - ); - return true; - } - - const prefix = this.getCompletionPrefix(input); - if (prefix === undefined) { - return false; - } - - const position = this.getSearchMenuPosition(prefix); - if (position !== undefined) { - debug( - `Partial completion update: '${prefix}' @ ${JSON.stringify(position)}`, - ); - this.searchMenu.updatePrefix(prefix, position); - } else { - this.searchMenu.hide(); - } - - // Reuse while menu has matches; re-fetch when all items are filtered out. - return this.searchMenu.isActive(); - } - - // Updating completions information with input - private updatePartialCompletion(input: string) { - debug(`Partial completion start: '${input}'`); - this.cancelCompletionMenu(); - this.current = input; - this.noCompletion = false; - // Clear the choices - this.searchMenu.setChoices([]); - const completionP = this.dispatcher.getCommandCompletion(input); - this.completionP = completionP; - completionP - .then((result) => { - if (this.completionP !== completionP) { - debug(`Partial completion canceled: '${input}'`); - return; - } - - this.completionP = undefined; - debug(`Partial completion result: `, result); - if (result === undefined) { - debug( - `Partial completion skipped: No completions for '${input}'`, - ); - this.noCompletion = true; - return; - } - - const partial = - result.startIndex >= 0 && result.startIndex <= input.length - ? input.substring(0, result.startIndex) - : input; - this.current = partial; - - // Build completions preserving backend group order so that - // grammar completions (e.g. "by") appear before entity - // completions (e.g. song titles), matching CLI behavior. - const completions: SearchMenuItem[] = []; - let currentIndex = 0; - for (const group of result.completions) { - const items = group.sorted - ? group.completions - : [...group.completions].sort(); - for (const choice of items) { - completions.push({ - matchText: choice, - selectedText: choice, - sortIndex: currentIndex++, - needQuotes: group.needQuotes, - emojiChar: group.emojiChar, - }); - } - } - - if (completions.length === 0) { - debug( - `Partial completion skipped: No current completions for '${partial}'`, - ); - return; - } - - this.searchMenu.setChoices(completions); - - debug( - `Partial completion selection updated: '${partial}' with ${completions.length} items`, - ); - this.update(false); - }) - .catch((e) => { - debugError(`Partial completion error: '${input}' ${e}`); - this.completionP = undefined; - }); - } - private getSearchMenuPosition(prefix: string) { - // The menu is not active or completion prefix is empty (i.e. need to move the menu). const textEntry = this.input.getTextEntry(); let x: number; if (textEntry.childNodes.length === 0) { @@ -416,21 +239,19 @@ export class PartialCompletion { return { left: x, bottom: window.innerHeight - top }; } - private cancelCompletionMenu() { - this.searchMenu.hide(); - } - private handleSelect(item: SearchMenuItem) { debug(`Partial completion selected: ${item.selectedText}`); - this.cancelCompletionMenu(); - const prefix = this.getCompletionPrefix( - this.getCurrentInputForCompletion(), - ); + this.searchMenu.hide(); + + // Compute the filter prefix relative to the current anchor. + // Must be read before resetToIdle() clears the session's anchor. + const currentInput = this.getCurrentInputForCompletion(); + const prefix = this.session.getCompletionPrefix(currentInput); if (prefix === undefined) { - // This should not happen. debugError(`Partial completion abort select: prefix not found`); return; } + const replaceText = item.needQuotes !== false && /\s/.test(item.selectedText) ? `"${item.selectedText.replaceAll('"', '\\"')}"` @@ -472,9 +293,8 @@ export class PartialCompletion { // Make sure the text entry remains focused after replacement. this.input.getTextEntry().focus(); - // Reset completion state so the next update requests fresh - // completions from the backend instead of reusing stale trie data. - this.current = undefined; + // Reset completion state so the next update requests fresh completions. + this.session.resetToIdle(); debug(`Partial completion replaced: ${replaceText}`); } @@ -484,7 +304,7 @@ export class PartialCompletion { return false; } if (event.key === "Escape") { - this.cancelCompletionMenu(); + this.searchMenu.hide(); event.preventDefault(); return true; } diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts new file mode 100644 index 0000000000..d483b5afe7 --- /dev/null +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -0,0 +1,317 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { CommandCompletionResult } from "agent-dispatcher"; +import { + SearchMenuItem, + SearchMenuPosition, +} from "../../preload/electronTypes.js"; +import registerDebug from "debug"; + +const debug = registerDebug("typeagent:shell:partial"); +const debugError = registerDebug("typeagent:shell:partial:error"); + +export interface ISearchMenu { + setChoices(choices: SearchMenuItem[]): void; + updatePrefix(prefix: string, position: SearchMenuPosition): void; + hide(): void; + isActive(): boolean; +} + +export interface ICompletionDispatcher { + getCommandCompletion( + input: string, + ): Promise; +} + +// PartialCompletionSession manages the state machine for command completion. +// +// States: +// IDLE current === undefined +// PENDING current !== undefined && completionP !== undefined +// ACTIVE current !== undefined && completionP === undefined && noCompletion === false +// EXHAUSTED current !== undefined && completionP === undefined && noCompletion === true +// +// Design principles: +// - Completion result fields (noCompletion, needsSeparator) are stored as-is +// from the backend response and never mutated as the user keeps typing. +// reuseSession() reads them to decide whether to show, hide, or re-fetch. +// - reuseSession() makes exactly three kinds of decisions: +// 1. Re-fetch — input has moved past what the current result covers +// 2. Show/update menu — input satisfies the result's constraints +// 3. Hide menu, keep session — input doesn't satisfy constraints yet +// (separator not typed, or no completions exist), but is still +// within the anchor so a re-fetch would return the same result. +// - The anchor (`current`) is never advanced after a result is received. +// When `needsSeparator` is true the separator is stripped from the raw +// prefix before being passed to the menu, so the trie still matches. +// +// This class has no DOM dependencies and is fully unit-testable with Jest. +export class PartialCompletionSession { + // The "anchor" prefix for the current session. Set to the full input + // when the request is issued, then narrowed to input[0..startIndex] when + // the backend reports how much the grammar consumed. `undefined` = IDLE. + private current: string | undefined = undefined; + + // True when the backend reported no completions for `current` (EXHAUSTED). + private noCompletion: boolean = false; + + // Saved as-is from the last completion result: whether a separator must + // appear in the input immediately after `current` before completions are + // valid. Used by reuseSession() and getCompletionPrefix() to interpret + // the raw prefix without mutating `current`. + private needsSeparator: boolean = false; + + // The in-flight completion request, or undefined when settled. + private completionP: + | Promise + | undefined; + + constructor( + private readonly menu: ISearchMenu, + private readonly dispatcher: ICompletionDispatcher, + ) {} + + // Main entry point. Called by PartialCompletion.update() after DOM checks pass. + // input: trimmed input text (ghost text stripped, leading whitespace stripped) + // getPosition: DOM callback that computes the menu anchor position; returns + // undefined when position cannot be determined (hides menu). + public update( + input: string, + getPosition: (prefix: string) => SearchMenuPosition | undefined, + ): void { + // @-commands: use word-boundary re-fetch logic so partial tokens + // (e.g. "@config c") don't poison noCompletion. + if (input.trimStart().startsWith("@")) { + if (this.reuseSession(input, getPosition)) { + return; + } + const lastSpaceIdx = input.lastIndexOf(" "); + if (/\s$/.test(input)) { + this.startNewSession(input, getPosition); + } else if (lastSpaceIdx >= 0) { + this.startNewSession( + input.substring(0, lastSpaceIdx + 1), + getPosition, + ); + } else { + this.startNewSession(input, getPosition); + } + return; + } + + // Empty input: hide without fetching. Must come before reuseSession() + // because reuseSession("") would match current="" and show stale items. + if (input.trimStart().length === 0) { + this.cancelMenu(); + return; + } + + if (this.reuseSession(input, getPosition)) { + return; + } + + this.startNewSession(input, getPosition); + } + + // Reset to IDLE and hide the menu. + public hide(): void { + this.completionP = undefined; + this.current = undefined; + this.noCompletion = false; + this.needsSeparator = false; + this.cancelMenu(); + } + + // Reset state to IDLE without hiding the menu (used after handleSelect inserts text). + public resetToIdle(): void { + this.current = undefined; + this.needsSeparator = false; + } + + // Returns the text typed after the anchor (`current`), or undefined when + // the input has diverged past the anchor or the separator is not yet present. + public getCompletionPrefix(input: string): string | undefined { + const current = this.current; + if (current === undefined) { + return undefined; + } + if (!input.startsWith(current)) { + return undefined; + } + const rawPrefix = input.substring(current.length); + if (this.needsSeparator) { + // The separator must be present and is not part of the replaceable prefix. + if (!/^\s/.test(rawPrefix)) { + return undefined; + } + return rawPrefix.trimStart(); + } + return rawPrefix; + } + + // Decides whether the current session can service `input` without a new + // backend fetch. Returns true to reuse, false to trigger a re-fetch. + // + // Decision order: + // PENDING — a fetch is in flight; wait for it (return true, no-op). + // RE-FETCH — input has moved outside the anchor; the saved result no + // longer applies (return false). + // HIDE+KEEP — input is within the anchor but the result's constraints + // aren't satisfied yet (no completions, or separator not + // typed); hide the menu but don't re-fetch (return true). + // SHOW — constraints satisfied; update/show the menu (return + // isActive() so callers can re-fetch when the trie is empty). + private reuseSession( + input: string, + getPosition: (prefix: string) => SearchMenuPosition | undefined, + ): boolean { + const current = this.current; + if (current === undefined) { + return false; + } + + // PENDING — a fetch is already in flight. + if (this.completionP !== undefined) { + debug(`Partial completion pending: ${current}`); + return true; + } + + // RE-FETCH — input moved past the anchor (e.g. backspace, new word). + if (!input.startsWith(current)) { + return false; + } + + // HIDE+KEEP — backend found no completions for this anchor. + if (this.noCompletion) { + debug( + `Partial completion skipped: No completions for '${current}'`, + ); + this.menu.hide(); + return true; + } + + // Separator handling: the character immediately after the anchor must be + // whitespace. Three sub-cases: + // "" — separator not typed yet: HIDE+KEEP (separator may still arrive) + // " …" — separator present: SHOW (fall through, strip it below) + // "x…" — non-separator typed right after anchor: RE-FETCH (the + // separator constraint can never be satisfied without + // backtracking, so treat this as a new input) + const rawPrefix = input.substring(current.length); + if (this.needsSeparator) { + if (rawPrefix === "") { + debug( + `Partial completion deferred: still waiting for separator`, + ); + this.menu.hide(); + return true; // HIDE+KEEP + } + if (!/^\s/.test(rawPrefix)) { + return false; // RE-FETCH + } + } + + // SHOW — strip the leading separator (if any) before passing to the + // menu trie, so completions like "music" match prefix "" not " ". + const prefix = this.needsSeparator ? rawPrefix.trimStart() : rawPrefix; + + const position = getPosition(prefix); + if (position !== undefined) { + debug( + `Partial completion update: '${prefix}' @ ${JSON.stringify(position)}`, + ); + this.menu.updatePrefix(prefix, position); + } else { + this.menu.hide(); + } + + // Always reuse: input is within the anchor and we have loaded + // completions. Even if the trie has no matches for the current + // prefix (menu hidden), backspacing may restore matches without + // needing a re-fetch. + return true; + } + + // Start a new completion session: issue backend request and process result. + private startNewSession( + input: string, + getPosition: (prefix: string) => SearchMenuPosition | undefined, + ): void { + debug(`Partial completion start: '${input}'`); + this.cancelMenu(); + this.current = input; + this.noCompletion = false; + this.needsSeparator = false; + this.menu.setChoices([]); + const completionP = this.dispatcher.getCommandCompletion(input); + this.completionP = completionP; + completionP + .then((result) => { + if (this.completionP !== completionP) { + debug(`Partial completion canceled: '${input}'`); + return; + } + + this.completionP = undefined; + debug(`Partial completion result: `, result); + if (result === undefined) { + debug( + `Partial completion skipped: No completions for '${input}'`, + ); + this.noCompletion = true; + return; + } + + const partial = + result.startIndex >= 0 && result.startIndex <= input.length + ? input.substring(0, result.startIndex) + : input; + this.current = partial; + this.needsSeparator = result.needsSeparator === true; + + // Build completions preserving backend group order. + const completions: SearchMenuItem[] = []; + let currentIndex = 0; + for (const group of result.completions) { + const items = group.sorted + ? group.completions + : [...group.completions].sort(); + for (const choice of items) { + completions.push({ + matchText: choice, + selectedText: choice, + sortIndex: currentIndex++, + ...(group.needQuotes !== undefined + ? { needQuotes: group.needQuotes } + : {}), + ...(group.emojiChar !== undefined + ? { emojiChar: group.emojiChar } + : {}), + }); + } + } + + if (completions.length === 0) { + debug( + `Partial completion skipped: No current completions for '${partial}'`, + ); + return; + } + + this.menu.setChoices(completions); + + // Re-run update with captured input to show the menu (or defer + // if the separator has not been typed yet). + this.reuseSession(input, getPosition); + }) + .catch((e) => { + debugError(`Partial completion error: '${input}' ${e}`); + this.completionP = undefined; + }); + } + + private cancelMenu(): void { + this.menu.hide(); + } +} diff --git a/ts/packages/shell/src/tsconfig.json b/ts/packages/shell/src/tsconfig.json new file mode 100644 index 0000000000..65363f749c --- /dev/null +++ b/ts/packages/shell/src/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "../dist/src", + "rootDir": ".", + "moduleResolution": "node16" + }, + "include": [ + "renderer/src/partialCompletionSession.ts", + "preload/electronTypes.ts", + "preload/shellSettingsType.ts" + ] +} diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts new file mode 100644 index 0000000000..35436168c3 --- /dev/null +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -0,0 +1,694 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { jest } from "@jest/globals"; +import { + ICompletionDispatcher, + ISearchMenu, + PartialCompletionSession, +} from "../src/renderer/src/partialCompletionSession.js"; +import { SearchMenuPosition } from "../src/preload/electronTypes.js"; +import { CompletionGroup } from "@typeagent/agent-sdk"; +import { CommandCompletionResult } from "agent-dispatcher"; + +// ── Helpers ────────────────────────────────────────────────────────────────── + +type MockMenu = { + setChoices: jest.MockedFunction; + updatePrefix: jest.MockedFunction; + hide: jest.MockedFunction; + isActive: jest.MockedFunction; +}; + +function makeMenu(): MockMenu { + return { + setChoices: jest.fn(), + updatePrefix: jest.fn(), + hide: jest.fn(), + isActive: jest.fn().mockReturnValue(false), + }; +} + +type MockDispatcher = { + getCommandCompletion: jest.MockedFunction< + ICompletionDispatcher["getCommandCompletion"] + >; +}; + +function makeDispatcher( + result: CommandCompletionResult | undefined = undefined, +): MockDispatcher { + return { + getCommandCompletion: jest + .fn() + .mockResolvedValue(result), + }; +} + +const anyPosition: SearchMenuPosition = { left: 0, bottom: 0 }; +const getPos = (_prefix: string) => anyPosition; + +function makeCompletionResult( + completions: string[], + startIndex: number = 0, + opts: Partial = {}, +): CommandCompletionResult { + const group: CompletionGroup = { name: "test", completions }; + return { startIndex, completions: [group], ...opts }; +} + +// ── State machine tests ─────────────────────────────────────────────────────── + +describe("PartialCompletionSession — state transitions", () => { + test("IDLE → PENDING: first update triggers a backend fetch", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith("play"); + }); + + test("PENDING: second update while promise is in-flight does not re-fetch", () => { + const menu = makeMenu(); + // Never-resolving promise keeps session in PENDING + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValue(new Promise(() => {})), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + session.update("play s", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("PENDING → ACTIVE: completions returned → setChoices + updatePrefix called", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["song", "shuffle"], 0); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // flush microtask + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "shuffle" }), + expect.objectContaining({ selectedText: "song" }), + ]), + ); + expect(menu.updatePrefix).toHaveBeenCalled(); + }); + + test("PENDING → EXHAUSTED: undefined result suppresses re-fetch while input has same prefix", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(undefined); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Should be EXHAUSTED — no new fetch even with extended input + session.update("play s", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("EXHAUSTED → IDLE: backspace past anchor triggers a new fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(undefined); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); // → EXHAUSTED with current="play" + + // Backspace past anchor + session.update("pla", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("pla"); + }); + + test("ACTIVE → hide+keep: when trie has no matches, session is preserved (no re-fetch)", async () => { + const menu = makeMenu(); + // isActive returns true on first call (after setChoices), false on second (all filtered) + menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); + const result = makeCompletionResult(["song"], 5); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play " + + // User types more; trie returns no matches but input is within anchor + session.update("play xyz", getPos); + + // No re-fetch: session is kept alive, menu is hidden + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(menu.hide).toHaveBeenCalled(); + }); + + test("ACTIVE → backspace restores menu after no-match without re-fetch", async () => { + const menu = makeMenu(); + // isActive: true after initial load, false for "xyz" prefix, true again for "so" + menu.isActive + .mockReturnValueOnce(true) // initial reuseSession after result + .mockReturnValueOnce(false) // "xyz" — trie no match + .mockReturnValueOnce(true); // "so" — trie matches "song" + const result = makeCompletionResult(["song", "shuffle"], 5); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play " + + // User types non-matching text + session.update("play xyz", getPos); + expect(menu.hide).toHaveBeenCalled(); + + // User backspaces to matching prefix — menu reappears without re-fetch + menu.updatePrefix.mockClear(); + session.update("play so", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(menu.updatePrefix).toHaveBeenCalledWith("so", anyPosition); + }); + + test("hide() resets to IDLE so next update starts fresh", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 0)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + session.hide(); + expect(menu.hide).toHaveBeenCalled(); + + // After hide, next update should fetch again + session.update("play", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("hide() cancels an in-flight request (stale result is ignored)", async () => { + const menu = makeMenu(); + let resolve!: (v: CommandCompletionResult) => void; + const pending = new Promise( + (r) => (resolve = r), + ); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValue(pending), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + session.hide(); // cancels the promise + + // Now resolve the stale promise — should be a no-op + resolve(makeCompletionResult(["song"], 0)); + await Promise.resolve(); + + expect(menu.setChoices).not.toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "song" }), + ]), + ); + }); + + test("empty input hides menu and does not fetch", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + session.update(" ", getPos); + + expect(dispatcher.getCommandCompletion).not.toHaveBeenCalled(); + expect(menu.hide).toHaveBeenCalled(); + }); +}); + +// ── Completion result processing ────────────────────────────────────────────── + +describe("PartialCompletionSession — result processing", () => { + test("startIndex narrows the anchor (current) to input[0..startIndex]", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + // startIndex=5 means grammar consumed "play " (5 chars) + const result = makeCompletionResult(["song", "shuffle"], 5); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); + + // prefix should be "song" (the text after anchor "play ") + expect(menu.updatePrefix).toHaveBeenCalledWith("song", anyPosition); + }); + + test("group order preserved: items appear in backend-provided group order", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const group1: CompletionGroup = { + name: "grammar", + completions: ["by"], + sorted: true, + }; + const group2: CompletionGroup = { + name: "entities", + completions: ["Bohemian Rhapsody"], + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 5, + completions: [group1, group2], + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as { + sortIndex: number; + selectedText: string; + }[]; + const byIndex = items.find((i) => i.selectedText === "by")!.sortIndex; + const bohIndex = items.find( + (i) => i.selectedText === "Bohemian Rhapsody", + )!.sortIndex; + expect(byIndex).toBeLessThan(bohIndex); + }); + + test("needQuotes propagated from group to each SearchMenuItem", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const group: CompletionGroup = { + name: "entities", + completions: ["Bohemian Rhapsody"], + needQuotes: true, + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + selectedText: "Bohemian Rhapsody", + needQuotes: true, + }), + ]), + ); + }); + + test("unsorted group items are sorted alphabetically", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const group: CompletionGroup = { + name: "test", + completions: ["zebra", "apple", "mango"], + sorted: false, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("x", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as { selectedText: string }[]; + const texts = items.map((i) => i.selectedText); + expect(texts).toEqual(["apple", "mango", "zebra"]); + }); + + test("needsSeparator defers menu display until trailing space is present", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // Input without trailing space: "play" — choices are loaded but menu is not shown + session.update("play", getPos); + await Promise.resolve(); + + // setChoices IS called with actual items (trie is populated for later) + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "music" }), + ]), + ); + // But updatePrefix is NOT called yet (menu not shown) + expect(menu.updatePrefix).not.toHaveBeenCalled(); + }); + + test("needsSeparator: typing separator shows menu without re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // First update: "play" — deferred (needsSeparator, no trailing space) + session.update("play", getPos); + await Promise.resolve(); + + // Second update: "play " — separator typed, menu should appear + session.update("play ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + // No re-fetch — same dispatcher call count + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("needsSeparator: menu shown after trailing space is typed", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["music"], 5, { + needsSeparator: false, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "music" }), + ]), + ); + }); + + test("empty completions list does not call setChoices with items", async () => { + const menu = makeMenu(); + const result: CommandCompletionResult = { + startIndex: 0, + completions: [{ name: "empty", completions: [] }], + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Only the initial setChoices([]) call (cancel) should have been made + expect(menu.setChoices).toHaveBeenCalledTimes(1); + expect(menu.setChoices).toHaveBeenCalledWith([]); + }); +}); + +// ── @-command routing ───────────────────────────────────────────────────────── + +describe("PartialCompletionSession — @command routing", () => { + test("@ command with trailing space fetches full input", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config ", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( + "@config ", + ); + }); + + test("@ command with partial word fetches up to last word boundary", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config c", getPos); + + // Should fetch "@config " (up to last space), not "@config c" + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( + "@config ", + ); + }); + + test("@ command with no space fetches full input", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith("@config"); + }); + + test("@ command in PENDING state does not re-fetch", () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValue(new Promise(() => {})), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config ", getPos); + session.update("@config c", getPos); // same anchor: "@config " — PENDING reuse + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); + +// ── getCompletionPrefix ─────────────────────────────────────────────────────── + +describe("PartialCompletionSession — getCompletionPrefix", () => { + test("returns undefined when session is IDLE", () => { + const session = new PartialCompletionSession( + makeMenu(), + makeDispatcher(), + ); + expect(session.getCompletionPrefix("anything")).toBeUndefined(); + }); + + test("returns suffix after anchor when input starts with anchor", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["song"], 5); // anchor = "play " + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play song", getPos); + await Promise.resolve(); + + expect(session.getCompletionPrefix("play song")).toBe("song"); + }); + + test("returns undefined when input diverges from anchor", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["song"], 5); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play song", getPos); + await Promise.resolve(); + + // Input no longer starts with anchor "play " + expect(session.getCompletionPrefix("stop")).toBeUndefined(); + }); + + test("needsSeparator: returns stripped prefix when separator is present", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play", getPos); + await Promise.resolve(); + + // Separator + typed text: prefix should be "mu" (space stripped) + expect(session.getCompletionPrefix("play mu")).toBe("mu"); + }); + + test("needsSeparator: returns undefined when separator is absent", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play", getPos); + await Promise.resolve(); + + // No separator yet — undefined means no replacement should happen + expect(session.getCompletionPrefix("play")).toBeUndefined(); + }); +}); + +// ── resetToIdle ─────────────────────────────────────────────────────────────── + +describe("PartialCompletionSession — resetToIdle", () => { + test("clears session so next update re-fetches", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 5)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); // → ACTIVE + + session.resetToIdle(); + + // After reset, next update should fetch fresh completions + session.update("play song", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("does not hide the menu (caller is responsible for that)", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 5)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); + + menu.hide.mockClear(); + session.resetToIdle(); + + expect(menu.hide).not.toHaveBeenCalled(); + }); +}); + +// ── needsSeparator edge cases ───────────────────────────────────────────────── + +describe("PartialCompletionSession — needsSeparator edge cases", () => { + test("re-update with same input before separator does not re-fetch", async () => { + // Regression: selectionchange can fire again with the same input while + // the session is waiting for a separator. Must not trigger a re-fetch. + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); // deferred — waiting for separator + + session.update("play", getPos); // same input again (e.g. selectionchange) + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("input diverges before separator arrives triggers re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); // deferred + + // User typed a non-space character instead of a separator + session.update("play2", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play2", + ); + }); + + test("separator already in input when result arrives shows menu immediately", async () => { + // User typed "play " fast enough that the promise resolves after the space. + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["music"], 4, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // Fetch was issued for "play" but by the time it resolves the user + // has already moved on; a second update for "play " is already active. + // Simulate by updating to "play " *before* awaiting. + session.update("play", getPos); + // (promise not yet resolved — we rely on the .then() calling reuseSession + // with the captured "play" input, which has no separator, so menu stays + // hidden. A subsequent update("play ", ...) then shows it.) + await Promise.resolve(); + + session.update("play ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); + +// ── miscellaneous ───────────────────────────────────────────────────────────── + +describe("PartialCompletionSession — miscellaneous", () => { + test("getPosition returning undefined hides the menu", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["song"], 5); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); + + menu.hide.mockClear(); + // getPosition returns undefined (e.g. caret not found) + session.update("play song", () => undefined); + + expect(menu.hide).toHaveBeenCalled(); + }); + + test("startIndex beyond input length falls back to full input as anchor", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + // startIndex=99 is beyond "play" (length 4) — anchor falls back to "play" + const result = makeCompletionResult(["song"], 99); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Anchor is "play" (full input). reuseSession is called with the captured + // input "play", so rawPrefix="" and updatePrefix is called with "". + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + }); +}); diff --git a/ts/packages/shell/test/tsconfig.json b/ts/packages/shell/test/tsconfig.json new file mode 100644 index 0000000000..315900cbbe --- /dev/null +++ b/ts/packages/shell/test/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "rootDir": ".", + "outDir": "../dist/test", + "types": ["node", "jest"] + }, + "include": ["partialCompletionSession.spec.ts"], + "references": [{ "path": "../src" }] +} diff --git a/ts/pnpm-lock.yaml b/ts/pnpm-lock.yaml index 9bbab2b631..5aacfcb1f4 100644 --- a/ts/pnpm-lock.yaml +++ b/ts/pnpm-lock.yaml @@ -3,6 +3,7 @@ lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false + injectWorkspacePackages: true importers: @@ -31,7 +32,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../../packages/agentSdk + version: file:packages/agentSdk devDependencies: copyfiles: specifier: ^2.4.1 @@ -50,13 +51,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../../packages/agentSdk + version: file:packages/agentSdk better-sqlite3: specifier: 12.6.2 version: 12.6.2 typeagent: specifier: workspace:* - version: link:../../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) devDependencies: '@types/better-sqlite3': specifier: 7.6.13 @@ -78,25 +79,25 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: link:../../packages/actionSchema + version: file:packages/actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../packages/agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../packages/agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../packages/utils/commonUtils agent-cache: specifier: workspace:* - version: link:../../packages/cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) agent-dispatcher: specifier: workspace:* - version: link:../../packages/dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) body-parser: specifier: 1.20.3 version: 1.20.3 @@ -108,7 +109,7 @@ importers: version: 4.4.3(supports-color@8.1.1) default-agent-provider: specifier: workspace:* - version: link:../../packages/defaultAgentProvider + version: file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5) dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -120,10 +121,10 @@ importers: version: 1.0.0 telemetry: specifier: workspace:* - version: link:../../packages/telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) devDependencies: '@types/debug': specifier: ^4.1.12 @@ -142,7 +143,7 @@ importers: version: 8.18.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + version: 29.7.0(@types/node@24.10.13)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5)) prettier: specifier: ^3.5.3 version: 3.5.3 @@ -163,7 +164,7 @@ importers: version: 12.1.0 aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -172,49 +173,49 @@ importers: version: 5.6.2 code-processor: specifier: workspace:* - version: link:../../packages/codeProcessor + version: file:packages/codeProcessor(socks@2.8.7)(zod@3.25.76) conversation-memory: specifier: workspace:* - version: link:../../packages/memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) dotenv: specifier: ^16.3.1 version: 16.5.0 examples-lib: specifier: workspace:* - version: link:../examplesLib + version: file:examples/examplesLib(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) exifreader: specifier: ^4.30.1 version: 4.30.1 image-memory: specifier: workspace:* - version: link:../../packages/memory/image + version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) interactive-app: specifier: workspace:* version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: link:../../packages/knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../packages/knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro-test: specifier: workspace:* - version: link:../../packages/knowProTest + version: file:packages/knowProTest(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) memory-providers: specifier: workspace:* - version: link:../memoryProviders + version: file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) memory-storage: specifier: workspace:* - version: link:../../packages/memory/storage + version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../packages/telemetry + version: file:packages/telemetry(socks@2.8.7) textpro: specifier: workspace:* - version: link:../../packages/textPro + version: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -251,7 +252,7 @@ importers: version: 16.5.0 typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -270,16 +271,16 @@ importers: version: link:../../packages/utils/commonUtils aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 code-processor: specifier: workspace:* - version: link:../../packages/codeProcessor + version: file:packages/codeProcessor(socks@2.8.7)(zod@3.25.76) conversation-memory: specifier: workspace:* - version: link:../../packages/memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -288,25 +289,25 @@ importers: version: 4.30.1 image-memory: specifier: workspace:* - version: link:../../packages/memory/image + version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) interactive-app: specifier: workspace:* version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: link:../../packages/knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../packages/knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) memory-providers: specifier: workspace:* - version: link:../memoryProviders + version: file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) mongodb: specifier: ^6.15.0 version: 6.16.0(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -341,7 +342,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -350,7 +351,7 @@ importers: version: 1.0.0-rc.12 conversation-memory: specifier: workspace:* - version: link:../../packages/memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) debug: specifier: ^4.4.0 version: 4.4.1 @@ -365,19 +366,19 @@ importers: version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: link:../../packages/knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../packages/knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) memory-providers: specifier: workspace:* - version: link:../memoryProviders + version: file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) proper-lockfile: specifier: ^4.1.2 version: 4.1.2 typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -405,7 +406,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -417,10 +418,10 @@ importers: version: link:../../packages/interactiveApp knowpro: specifier: workspace:* - version: link:../../packages/knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -445,22 +446,22 @@ importers: version: 1.26.0(zod@4.1.13) conversation-memory: specifier: workspace:* - version: link:../../packages/memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) dotenv: specifier: ^16.3.1 version: 16.5.0 examples-lib: specifier: workspace:* - version: link:../examplesLib + version: file:examples/examplesLib(typescript@5.4.5)(zod@4.1.13) interactive-app: specifier: workspace:* version: link:../../packages/interactiveApp knowpro: specifier: workspace:* - version: link:../../packages/knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@4.1.13) zod: specifier: ^4.1.13 version: 4.1.13 @@ -485,7 +486,7 @@ importers: version: 8.19.1 aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -494,10 +495,10 @@ importers: version: 4.4.1 knowledge-processor: specifier: workspace:* - version: link:../../packages/knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -571,7 +572,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) copyfiles: specifier: ^2.4.1 version: 2.4.1 @@ -583,10 +584,10 @@ importers: version: link:../../packages/interactiveApp telemetry: specifier: workspace:* - version: link:../../packages/telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -602,25 +603,25 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: link:../../packages/actionSchema + version: file:packages/actionSchema agent-cache: specifier: workspace:* - version: link:../../packages/cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) agent-dispatcher: specifier: workspace:* - version: link:../../packages/dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) azure-ai-foundry: specifier: workspace:* - version: link:../../packages/azure-ai-foundry + version: file:packages/azure-ai-foundry(typescript@5.4.5)(ws@8.19.0)(zod@3.25.76) debug: specifier: ^4.4.0 version: 4.4.1 default-agent-provider: specifier: workspace:* - version: link:../../packages/defaultAgentProvider + version: file:packages/defaultAgentProvider(typescript@5.4.5) dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -635,7 +636,7 @@ importers: version: link:../../packages/schemaAuthor typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -660,7 +661,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) copyfiles: specifier: ^2.4.1 version: 2.4.1 @@ -672,13 +673,13 @@ importers: version: link:../../packages/interactiveApp typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: link:../../packages/utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typescript: specifier: ~5.4.5 version: 5.4.5 @@ -691,7 +692,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -706,10 +707,10 @@ importers: version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: link:../../packages/knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -731,7 +732,7 @@ importers: dependencies: agent-dispatcher: specifier: workspace:* - version: link:../../packages/dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -777,10 +778,10 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: link:../../packages/actionSchema + version: file:packages/actionSchema aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -798,10 +799,10 @@ importers: version: 1.0.16 telemetry: specifier: workspace:* - version: link:../../packages/telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -829,10 +830,10 @@ importers: version: link:../../packages/utils/commonUtils aiclient: specifier: workspace:* - version: link:../../packages/aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) azure-ai-foundry: specifier: workspace:* - version: link:../../packages/azure-ai-foundry + version: file:packages/azure-ai-foundry(typescript@5.4.5)(ws@8.19.0)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -853,7 +854,7 @@ importers: version: 24.37.5(typescript@5.4.5) typeagent: specifier: workspace:* - version: link:../../packages/typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -921,7 +922,7 @@ importers: version: 0.2.12(zod@4.3.6) '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema chalk: specifier: ^5.4.1 version: 5.6.2 @@ -970,7 +971,7 @@ importers: version: 6.2.26 action-grammar: specifier: workspace:* - version: link:../actionGrammar + version: file:packages/actionGrammar(zod@4.3.6) devDependencies: rimraf: specifier: ^6.0.1 @@ -1011,7 +1012,7 @@ importers: version: 6.2.26 '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema devDependencies: rimraf: specifier: ^6.0.1 @@ -1024,7 +1025,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils @@ -1092,19 +1093,19 @@ importers: version: 1.26.0(zod@3.25.76) '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema action-grammar: specifier: workspace:* - version: link:../actionGrammar + version: file:packages/actionGrammar(zod@3.25.76) agent-cache: specifier: workspace:* - version: link:../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) coder-wrapper: specifier: workspace:* - version: link:../coderWrapper + version: file:packages/coderWrapper(ws@8.19.0)(zod@3.25.76) dotenv: specifier: ^16.4.5 version: 16.5.0 @@ -1144,7 +1145,7 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/agent-server-protocol': specifier: workspace:* version: link:../protocol @@ -1194,7 +1195,7 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/agent-server-protocol': specifier: workspace:* version: link:../protocol @@ -1206,22 +1207,22 @@ importers: version: link:../../dispatcher/rpc agent-dispatcher: specifier: workspace:* - version: link:../../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) default-agent-provider: specifier: workspace:* - version: link:../../defaultAgentProvider + version: file:packages/defaultAgentProvider(typescript@5.4.5) dispatcher-node-providers: specifier: workspace:* - version: link:../../dispatcher/nodeProviders + version: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.18.2) dotenv: specifier: ^16.3.1 version: 16.5.0 websocket-channel-server: specifier: workspace:* - version: link:../../utils/webSocketChannelServer + version: file:packages/utils/webSocketChannelServer ws: specifier: ^8.17.1 version: 8.18.2 @@ -1261,7 +1262,7 @@ importers: version: link:../../../utils/commonUtils aiclient: specifier: workspace:* - version: link:../../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -1291,7 +1292,7 @@ importers: version: 1.1.2 typeagent: specifier: workspace:* - version: link:../../../typeagent + version: file:packages/typeagent(zod@3.25.76) devDependencies: '@microsoft/microsoft-graph-types': specifier: ^2.40.0 @@ -1328,7 +1329,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk devDependencies: prettier: specifier: ^3.5.3 @@ -1350,13 +1351,13 @@ importers: version: 2.11.8 '@typeagent/action-schema': specifier: workspace:* - version: link:../../actionSchema + version: file:packages/actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/agent-server-protocol': specifier: workspace:* version: link:../../agentServer/protocol @@ -1368,10 +1369,10 @@ importers: version: link:../../dispatcher/rpc aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) azure-ai-foundry: specifier: workspace:* - version: link:../../azure-ai-foundry + version: file:packages/azure-ai-foundry(encoding@0.1.13)(typescript@5.4.5)(ws@8.18.2)(zod@3.25.76) bootstrap: specifier: ^5.3.3 version: 5.3.6(@popperjs/core@2.11.8) @@ -1386,7 +1387,7 @@ importers: version: 1.1.0 conversation-memory: specifier: workspace:* - version: link:../../memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) cytoscape: specifier: ^3.32.0 version: 3.32.0 @@ -1437,10 +1438,10 @@ importers: version: 1.2.1 knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) markdown-it: specifier: ^14.1.1 version: 14.1.1 @@ -1461,7 +1462,7 @@ importers: version: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) puppeteer-extra-plugin-stealth: specifier: ^2.11.2 - version: 2.11.2(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + version: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) readline: specifier: ^1.3.0 version: 1.3.0 @@ -1473,16 +1474,16 @@ importers: version: link:../../textPro typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) website-memory: specifier: workspace:* - version: link:../../memory/website + version: file:packages/memory/website(typescript@5.4.5)(zod@3.25.76) websocket-utils: specifier: workspace:* - version: link:../../utils/webSocketUtils + version: file:packages/utils/webSocketUtils ws: specifier: ^8.17.1 version: 8.18.2 @@ -1498,7 +1499,7 @@ importers: version: 0.57.0(@types/node@20.19.23) '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/chrome': specifier: ^0.0.256 version: 0.0.256 @@ -1546,7 +1547,7 @@ importers: version: 8.18.1 action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler(zod@3.25.76) archiver: specifier: ^7.0.1 version: 7.0.1 @@ -1597,7 +1598,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk chalk: specifier: ^5.4.1 version: 5.6.2 @@ -1609,20 +1610,20 @@ importers: version: 4.4.1 graph-utils: specifier: workspace:* - version: link:../agentUtils/graphUtils + version: file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: link:../../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler(zod@3.25.76) concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1649,19 +1650,19 @@ importers: version: 4.10.0 '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -1686,7 +1687,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -1698,17 +1699,17 @@ importers: version: 4.4.1 telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) websocket-utils: specifier: workspace:* - version: link:../../utils/webSocketUtils + version: file:packages/utils/webSocketUtils ws: specifier: ^8.17.1 version: 8.18.2 devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/better-sqlite3': specifier: 7.6.13 version: 7.6.13 @@ -1735,13 +1736,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk agent-cache: specifier: workspace:* - version: link:../../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) body-parser: specifier: ^1.20.3 version: 1.20.3 @@ -1762,20 +1763,20 @@ importers: version: 1.0.0 typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: link:../../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) ws: specifier: ^8.17.1 version: 8.18.2 devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/body-parser': specifier: ^1.19.5 version: 1.19.5 @@ -1796,7 +1797,7 @@ importers: version: 8.18.1 action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler(zod@3.25.76) concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1817,13 +1818,13 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.12 - version: 0.2.12(zod@4.3.6) + version: 0.2.12(zod@4.1.13) '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -1832,17 +1833,17 @@ importers: version: 4.4.1 graph-utils: specifier: workspace:* - version: link:../agentUtils/graphUtils + version: file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5) kp: specifier: workspace:* version: link:../../kp typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@4.1.13) devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -1863,31 +1864,31 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils agent-dispatcher: specifier: workspace:* - version: link:../../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chat-agent: specifier: workspace:* - version: link:../chat + version: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) debug: specifier: ^4.4.0 version: 4.4.1 knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -1912,20 +1913,20 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typechat-utils: specifier: workspace:* version: link:../../utils/typechatUtils devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1943,14 +1944,14 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1992,10 +1993,10 @@ importers: version: 7.13.1 '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) debug: specifier: ^4.3.4 version: 4.4.1 @@ -2037,7 +2038,7 @@ importers: version: 1.40.0 telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2110,13 +2111,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) body-parser: specifier: 1.20.3 version: 1.20.3 @@ -2137,13 +2138,13 @@ importers: version: 7.5.0(express@4.22.1) image-memory: specifier: workspace:* - version: link:../../memory/image + version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) koffi: specifier: ^2.10.1 version: 2.11.0 @@ -2152,7 +2153,7 @@ importers: version: 0.33.5 typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2207,11 +2208,11 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2229,7 +2230,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils @@ -2254,7 +2255,7 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -2266,7 +2267,7 @@ importers: version: 0.0.25 action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler(zod@3.25.76) concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2281,7 +2282,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils @@ -2297,7 +2298,7 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -2309,7 +2310,7 @@ importers: version: 22.15.18 action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2327,31 +2328,31 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils agent-dispatcher: specifier: workspace:* - version: link:../../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chat-agent: specifier: workspace:* - version: link:../chat + version: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2376,13 +2377,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -2394,7 +2395,7 @@ importers: version: 16.5.0 typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2422,7 +2423,7 @@ importers: version: 0.2.12(zod@4.3.6) '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/agent-server-client': specifier: workspace:* version: link:../../agentServer/client @@ -2435,13 +2436,13 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/html-to-text': specifier: ^9.0.4 version: 9.0.4 action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler(zod@4.3.6) concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2459,7 +2460,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk devDependencies: prettier: specifier: ^3.5.3 @@ -2475,7 +2476,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2485,7 +2486,7 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -2524,16 +2525,16 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.12 - version: 0.2.12(zod@4.3.6) + version: 0.2.12(zod@4.1.13) '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@types/html-to-text': specifier: ^9.0.4 version: 9.0.4 browser-typeagent: specifier: workspace:* - version: link:../browser + version: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5) html-to-text: specifier: ^9.0.5 version: 9.0.5 @@ -2545,14 +2546,14 @@ importers: version: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) puppeteer-extra-plugin-stealth: specifier: ^2.11.2 - version: 2.11.2(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + version: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2570,20 +2571,20 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typechat-utils: specifier: workspace:* version: link:../../utils/typechatUtils devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2601,14 +2602,14 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: link:../../actionSchemaCompiler + version: file:packages/actionSchemaCompiler action-grammar-compiler: specifier: workspace:* - version: link:../../actionGrammarCompiler + version: file:packages/actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2675,22 +2676,22 @@ importers: version: 12.27.0 '@typeagent/agent-rpc': specifier: workspace:* - version: link:../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/dispatcher-rpc': specifier: workspace:* version: link:../dispatcher/rpc agent-cache: specifier: workspace:* - version: link:../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2699,10 +2700,10 @@ importers: version: 4.4.1 default-agent-provider: specifier: workspace:* - version: link:../defaultAgentProvider + version: file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5) dispatcher-node-providers: specifier: workspace:* - version: link:../dispatcher/nodeProviders + version: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -2711,10 +2712,10 @@ importers: version: 1.0.0 telemetry: specifier: workspace:* - version: link:../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat-utils: specifier: workspace:* version: link:../utils/typechatUtils @@ -2760,10 +2761,10 @@ importers: version: 4.10.0 '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) async: specifier: ^3.2.5 version: 3.2.6 @@ -2772,10 +2773,10 @@ importers: version: 4.4.1 telemetry: specifier: workspace:* - version: link:../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2809,19 +2810,19 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils action-grammar: specifier: workspace:* - version: link:../actionGrammar + version: file:packages/actionGrammar(zod@4.1.13) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) async: specifier: ^3.2.5 version: 3.2.6 @@ -2836,13 +2837,13 @@ importers: version: 2.0.1 telemetry: specifier: workspace:* - version: link:../telemetry + version: file:packages/telemetry(socks@2.8.7) typechat: specifier: ^0.1.1 - version: 0.1.1(typescript@5.4.5)(zod@3.25.76) + version: 0.1.1(typescript@5.4.5)(zod@4.1.13) typechat-utils: specifier: workspace:* - version: link:../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) devDependencies: '@types/async': specifier: ^3.2.24 @@ -2867,7 +2868,7 @@ importers: version: 6.0.1 test-lib: specifier: workspace:* - version: link:../testLib + version: file:packages/testLib(typescript@5.4.5) typescript: specifier: ~5.4.5 version: 5.4.5 @@ -2876,10 +2877,10 @@ importers: dependencies: agent-cache: specifier: workspace:* - version: link:../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2925,7 +2926,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk ansi_up: specifier: ^6.0.2 version: 6.0.5 @@ -2953,10 +2954,10 @@ importers: version: 6.2.26 '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/agent-server-client': specifier: workspace:* version: link:../agentServer/client @@ -2965,16 +2966,16 @@ importers: version: link:../utils/commonUtils action-grammar: specifier: workspace:* - version: link:../actionGrammar + version: file:packages/actionGrammar(zod@4.1.13) agent-cache: specifier: workspace:* - version: link:../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2983,10 +2984,10 @@ importers: version: 4.4.1 default-agent-provider: specifier: workspace:* - version: link:../defaultAgentProvider + version: file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5) dispatcher-node-providers: specifier: workspace:* - version: link:../dispatcher/nodeProviders + version: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -3007,16 +3008,16 @@ importers: version: 10.1.2 telemetry: specifier: workspace:* - version: link:../telemetry + version: file:packages/telemetry(socks@2.8.7) ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@24.10.13)(typescript@5.4.5) typechat: specifier: ^0.1.1 - version: 0.1.1(typescript@5.4.5)(zod@3.25.76) + version: 0.1.1(typescript@5.4.5)(zod@4.1.13) typechat-utils: specifier: workspace:* - version: link:../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) devDependencies: '@types/debug': specifier: ^4.1.12 @@ -3108,13 +3109,13 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) knowledge-processor: specifier: workspace:* - version: link:../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3167,7 +3168,7 @@ importers: version: 1.26.0(zod@4.1.13) '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/agent-server-client': specifier: workspace:* version: link:../agentServer/client @@ -3225,58 +3226,58 @@ importers: version: 2025.8.21(zod@4.1.13) '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: link:../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils action-grammar: specifier: workspace:* - version: link:../actionGrammar + version: file:packages/actionGrammar(zod@4.1.13) agent-cache: specifier: workspace:* - version: link:../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) android-mobile-agent: specifier: workspace:* version: link:../agents/androidMobile browser-typeagent: specifier: workspace:* - version: link:../agents/browser + version: file:packages/agents/browser(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) calendar: specifier: workspace:* - version: link:../agents/calendar + version: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) chalk: specifier: ^5.4.1 version: 5.6.2 chat-agent: specifier: workspace:* - version: link:../agents/chat + version: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) code-agent: specifier: workspace:* - version: link:../agents/code + version: file:packages/agents/code(socks@2.8.7) debug: specifier: ^4.4.0 version: 4.4.1 desktop-automation: specifier: workspace:* - version: link:../agents/desktop + version: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) dispatcher-node-providers: specifier: workspace:* - version: link:../dispatcher/nodeProviders + version: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) email: specifier: workspace:* - version: link:../agents/email + version: file:packages/agents/email(typescript@5.4.5)(zod@4.1.13) exifreader: specifier: ^4.30.1 version: 4.30.1 @@ -3288,25 +3289,25 @@ importers: version: 13.0.6 greeting-agent: specifier: workspace:* - version: link:../agents/greeting + version: file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) image-agent: specifier: workspace:* - version: link:../agents/image + version: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) knowledge-processor: specifier: workspace:* - version: link:../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) list-agent: specifier: workspace:* version: link:../agents/list markdown-agent: specifier: workspace:* - version: link:../agents/markdown + version: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) montage-agent: specifier: workspace:* - version: link:../agents/montage + version: file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) music: specifier: workspace:* - version: link:../agents/player + version: file:packages/agents/player(typescript@5.4.5)(zod@4.1.13) music-local: specifier: workspace:* version: link:../agents/playerLocal @@ -3318,34 +3319,34 @@ importers: version: 4.1.2 settings-agent: specifier: workspace:* - version: link:../agents/settings + version: file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) spelunker-agent: specifier: workspace:* - version: link:../agents/spelunker + version: file:packages/agents/spelunker(socks@2.8.7)(zod@4.1.13) string-width: specifier: ^7.2.0 version: 7.2.0 taskflow-typeagent: specifier: workspace:* - version: link:../agents/taskflow + version: file:packages/agents/taskflow(ws@8.18.2)(zod@4.1.13) telemetry: specifier: workspace:* - version: link:../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@4.1.13) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@4.1.13) typechat-utils: specifier: workspace:* - version: link:../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) utility-typeagent: specifier: workspace:* - version: link:../agents/utility + version: file:packages/agents/utility(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) video-agent: specifier: workspace:* - version: link:../agents/video + version: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) weather-agent: specifier: workspace:* version: link:../agents/weather @@ -3415,13 +3416,13 @@ importers: version: 0.1.26 '@typeagent/action-schema': specifier: workspace:* - version: link:../../actionSchema + version: file:packages/actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils @@ -3430,22 +3431,22 @@ importers: version: link:../types action-grammar: specifier: workspace:* - version: link:../../actionGrammar + version: file:packages/actionGrammar(zod@4.1.13) agent-cache: specifier: workspace:* - version: link:../../cache + version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) azure-ai-foundry: specifier: workspace:* - version: link:../../azure-ai-foundry + version: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13) chalk: specifier: ^5.4.1 version: 5.6.2 conversation-memory: specifier: workspace:* - version: link:../../memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) @@ -3463,13 +3464,13 @@ importers: version: 9.0.5 image-memory: specifier: workspace:* - version: link:../../memory/image + version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) open: specifier: ^10.1.0 version: 10.1.2 @@ -3481,19 +3482,19 @@ importers: version: 7.2.0 telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@4.1.13) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@4.1.13) typechat-utils: specifier: workspace:* - version: link:../../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) website-memory: specifier: workspace:* - version: link:../../memory/website + version: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) zod: specifier: ^4.1.13 version: 4.1.13 @@ -3539,16 +3540,16 @@ importers: version: 1.5.13 '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils agent-dispatcher: specifier: workspace:* - version: link:../dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) @@ -3576,10 +3577,10 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk '@typeagent/dispatcher-types': specifier: workspace:* version: link:../types @@ -3598,7 +3599,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk devDependencies: prettier: specifier: ^3.5.3 @@ -3627,7 +3628,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) async: specifier: ^3.2.5 version: 3.2.6 @@ -3639,10 +3640,10 @@ importers: version: 3.0.0 knowledge-processor: specifier: workspace:* - version: link:../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3685,22 +3686,22 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) conversation-memory: specifier: workspace:* - version: link:../memory/conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) interactive-app: specifier: workspace:* version: link:../interactiveApp knowledge-processor: specifier: workspace:* - version: link:../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: 0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3728,7 +3729,7 @@ importers: version: 2.0.0-beta.3 aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) debug: specifier: ^4.4.0 version: 4.4.1 @@ -3740,13 +3741,13 @@ importers: version: 0.33.5 typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: link:../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) devDependencies: '@types/debug': specifier: ^4.1.12 @@ -3777,7 +3778,7 @@ importers: dependencies: agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -3832,7 +3833,7 @@ importers: version: 0.2.12(zod@4.1.13) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -3863,13 +3864,13 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.12 - version: 0.2.12(zod@4.3.6) + version: 0.2.12(zod@4.1.13) '@anthropic-ai/sdk': specifier: ^0.35.0 version: 0.35.0(encoding@0.1.13) aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.9.3) dotenv: specifier: ^16.4.5 version: 16.5.0 @@ -3891,7 +3892,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) async: specifier: ^3.2.5 version: 3.2.6 @@ -3900,22 +3901,22 @@ importers: version: 4.4.1 knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) mailparser: specifier: 3.9.0 version: 3.9.0 memory-storage: specifier: workspace:* - version: link:../storage + version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) textpro: specifier: workspace:* version: link:../../textPro typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3967,7 +3968,7 @@ importers: version: 2.0.0-beta.3 aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -3979,19 +3980,19 @@ importers: version: 5.0.0 knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) memory-storage: specifier: workspace:* - version: link:../storage + version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4028,7 +4029,7 @@ importers: version: 12.1.0 aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -4037,13 +4038,13 @@ importers: version: 4.4.1 knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) devDependencies: '@types/better-sqlite3': specifier: 7.6.13 @@ -4083,7 +4084,7 @@ importers: version: link:../../utils/commonUtils aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -4092,7 +4093,7 @@ importers: version: 1.1.0 conversation-memory: specifier: workspace:* - version: link:../conversation + version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) debug: specifier: ^4.4.0 version: 4.4.1 @@ -4119,19 +4120,19 @@ importers: version: 26.1.0 knowledge-processor: specifier: workspace:* - version: link:../../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) knowpro: specifier: workspace:* - version: link:../../knowPro + version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) memory-storage: specifier: workspace:* - version: link:../storage + version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typeagent: specifier: workspace:* - version: link:../../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4174,13 +4175,13 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: link:../actionSchema + version: file:packages/actionSchema aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4208,13 +4209,13 @@ importers: version: 3.0.2(electron@40.6.0) '@typeagent/agent-rpc': specifier: workspace:* - version: link:../agentRpc + version: file:packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: link:../agentSdk + version: file:packages/agentSdk '@typeagent/agent-server-client': specifier: workspace:* - version: link:../agentServer/client + version: file:packages/agentServer/client(ws@8.18.2) '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils @@ -4223,25 +4224,25 @@ importers: version: link:../dispatcher/rpc agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) ansi_up: specifier: ^6.0.2 version: 6.0.5 browser-typeagent: specifier: workspace:* - version: link:../agents/browser + version: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) debug: specifier: ^4.4.0 version: 4.4.1 default-agent-provider: specifier: workspace:* - version: link:../defaultAgentProvider + version: file:packages/defaultAgentProvider(puppeteer-core@24.37.5)(typescript@5.4.5) dispatcher-node-providers: specifier: workspace:* - version: link:../dispatcher/nodeProviders + version: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.18.2) dompurify: specifier: ^3.2.5 version: 3.2.5 @@ -4262,16 +4263,16 @@ importers: version: 1.43.1 typeagent: specifier: workspace:* - version: link:../typeagent + version: file:packages/typeagent(zod@4.3.6) typechat: specifier: ^0.1.1 - version: 0.1.1(typescript@5.4.5)(zod@3.25.76) + version: 0.1.1(typescript@5.4.5)(zod@4.3.6) typechat-utils: specifier: workspace:* - version: link:../utils/typechatUtils + version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) websocket-utils: specifier: workspace:* - version: link:../utils/webSocketUtils + version: file:packages/utils/webSocketUtils ws: specifier: ^8.17.1 version: 8.18.2 @@ -4282,12 +4283,18 @@ importers: '@fontsource/lato': specifier: ^5.2.5 version: 5.2.5 + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 '@playwright/test': specifier: ^1.55.0 version: 1.57.0 '@types/debug': specifier: ^4.1.12 version: 4.1.12 + '@types/jest': + specifier: ^29.5.7 + version: 29.5.14 concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -4306,6 +4313,9 @@ importers: electron-vite: specifier: ^4.0.1 version: 4.0.1(vite@6.4.1(@types/node@24.10.13)(jiti@2.5.1)(less@4.3.0)(terser@5.39.2)(yaml@2.8.2)) + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@24.10.13)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5)) less: specifier: ^4.2.0 version: 4.3.0 @@ -4369,7 +4379,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4403,7 +4413,7 @@ importers: version: 1.0.0-rc.12 knowledge-processor: specifier: workspace:* - version: link:../knowledgeProcessor + version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) marked: specifier: 16.0.0 version: 16.0.0 @@ -4422,7 +4432,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: link:../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) async: specifier: ^3.2.5 version: 3.2.6 @@ -4468,7 +4478,7 @@ importers: version: link:../agentServer/client agent-dispatcher: specifier: workspace:* - version: link:../dispatcher/dispatcher + version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) devDependencies: '@types/debug': specifier: ^4.1.12 @@ -4496,7 +4506,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk chalk: specifier: ^5.4.1 version: 5.6.2 @@ -4530,10 +4540,10 @@ importers: version: 2.0.0-beta.3 '@typeagent/agent-sdk': specifier: workspace:* - version: link:../../agentSdk + version: file:packages/agentSdk aiclient: specifier: workspace:* - version: link:../../aiclient + version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) chalk: specifier: ^5.4.1 version: 5.6.2 @@ -4548,7 +4558,7 @@ importers: version: 4.30.1 telemetry: specifier: workspace:* - version: link:../../telemetry + version: file:packages/telemetry(socks@2.8.7) typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4576,7 +4586,7 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: link:../../agentRpc + version: file:packages/agentRpc '@typeagent/common-utils': specifier: workspace:* version: link:../commonUtils @@ -7214,6 +7224,35 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + '@typeagent/action-schema-compiler@file:packages/actionSchemaCompiler': + resolution: {directory: packages/actionSchemaCompiler, type: directory} + hasBin: true + + '@typeagent/action-schema@file:packages/actionSchema': + resolution: {directory: packages/actionSchema, type: directory} + engines: {node: '>=20'} + + '@typeagent/agent-rpc@file:packages/agentRpc': + resolution: {directory: packages/agentRpc, type: directory} + + '@typeagent/agent-sdk@file:packages/agentSdk': + resolution: {directory: packages/agentSdk, type: directory} + + '@typeagent/agent-server-client@file:packages/agentServer/client': + resolution: {directory: packages/agentServer/client, type: directory} + + '@typeagent/agent-server-protocol@file:packages/agentServer/protocol': + resolution: {directory: packages/agentServer/protocol, type: directory} + + '@typeagent/common-utils@file:packages/utils/commonUtils': + resolution: {directory: packages/utils/commonUtils, type: directory} + + '@typeagent/dispatcher-rpc@file:packages/dispatcher/rpc': + resolution: {directory: packages/dispatcher/rpc, type: directory} + + '@typeagent/dispatcher-types@file:packages/dispatcher/types': + resolution: {directory: packages/dispatcher/types, type: directory} + '@types/async@3.2.24': resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} @@ -7912,6 +7951,14 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + action-grammar-compiler@file:packages/actionGrammarCompiler: + resolution: {directory: packages/actionGrammarCompiler, type: directory} + hasBin: true + + action-grammar@file:packages/actionGrammar: + resolution: {directory: packages/actionGrammar, type: directory} + hasBin: true + agent-base@5.1.1: resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} engines: {node: '>= 6.0.0'} @@ -7924,6 +7971,12 @@ packages: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} + agent-cache@file:packages/cache: + resolution: {directory: packages/cache, type: directory} + + agent-dispatcher@file:packages/dispatcher/dispatcher: + resolution: {directory: packages/dispatcher/dispatcher, type: directory} + agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -7932,6 +7985,10 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} + aiclient@file:packages/aiclient: + resolution: {directory: packages/aiclient, type: directory} + engines: {node: '>=20'} + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -7964,6 +8021,9 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + android-mobile-agent@file:packages/agents/androidMobile: + resolution: {directory: packages/agents/androidMobile, type: directory} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -8140,6 +8200,10 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + azure-ai-foundry@file:packages/azure-ai-foundry: + resolution: {directory: packages/azure-ai-foundry, type: directory} + engines: {node: '>=20'} + azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} @@ -8295,6 +8359,9 @@ packages: browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + browser-typeagent@file:packages/agents/browser: + resolution: {directory: packages/agents/browser, type: directory} + browserslist@4.24.5: resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -8391,6 +8458,9 @@ packages: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} engines: {node: '>=8'} + calendar@file:packages/agents/calendar: + resolution: {directory: packages/agents/calendar, type: directory} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -8459,6 +8529,12 @@ packages: chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + chat-agent@file:packages/agents/chat: + resolution: {directory: packages/agents/chat, type: directory} + + chat-ui@file:packages/chat-ui: + resolution: {directory: packages/chat-ui, type: directory} + cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -8599,13 +8675,23 @@ packages: resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} engines: {node: '>=16'} + code-agent@file:packages/agents/code: + resolution: {directory: packages/agents/code, type: directory} + code-excerpt@4.0.0: resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + code-processor@file:packages/codeProcessor: + resolution: {directory: packages/codeProcessor, type: directory} + codemirror@6.0.1: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} + coder-wrapper@file:packages/coderWrapper: + resolution: {directory: packages/coderWrapper, type: directory} + hasBin: true + collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -8728,6 +8814,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + conversation-memory@file:packages/memory/conversation: + resolution: {directory: packages/memory/conversation, type: directory} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -9137,6 +9226,9 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + default-agent-provider@file:packages/defaultAgentProvider: + resolution: {directory: packages/defaultAgentProvider, type: directory} + default-browser-id@5.0.0: resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} engines: {node: '>=18'} @@ -9192,6 +9284,9 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + desktop-automation@file:packages/agents/desktop: + resolution: {directory: packages/agents/desktop, type: directory} + destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -9244,6 +9339,9 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dispatcher-node-providers@file:packages/dispatcher/nodeProviders: + resolution: {directory: packages/dispatcher/nodeProviders, type: directory} + dmg-builder@26.8.1: resolution: {integrity: sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==} @@ -9366,6 +9464,9 @@ packages: engines: {node: '>= 12.20.55'} hasBin: true + email@file:packages/agents/email: + resolution: {directory: packages/agents/email, type: directory} + emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -9574,6 +9675,9 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} + examples-lib@file:examples/examplesLib: + resolution: {directory: examples/examplesLib, type: directory} + execa@1.0.0: resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} engines: {node: '>=6'} @@ -10034,6 +10138,10 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graph-utils@file:packages/agents/agentUtils/graphUtils: + resolution: {directory: packages/agents/agentUtils/graphUtils, type: directory} + engines: {node: '>=20'} + graphlib@2.1.8: resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} @@ -10085,6 +10193,9 @@ packages: peerDependencies: graphology-types: '>=0.24.0' + greeting-agent@file:packages/agents/greeting: + resolution: {directory: packages/agents/greeting, type: directory} + gtoken@7.1.0: resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} engines: {node: '>=14.0.0'} @@ -10301,6 +10412,12 @@ packages: resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} engines: {node: '>= 4'} + image-agent@file:packages/agents/image: + resolution: {directory: packages/agents/image, type: directory} + + image-memory@file:packages/memory/image: + resolution: {directory: packages/memory/image, type: directory} + image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} @@ -10367,6 +10484,10 @@ packages: react-devtools-core: optional: true + interactive-app@file:packages/interactiveApp: + resolution: {directory: packages/interactiveApp, type: directory} + engines: {node: '>=20'} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -11001,12 +11122,25 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + knowledge-processor@file:packages/knowledgeProcessor: + resolution: {directory: packages/knowledgeProcessor, type: directory} + + knowpro-test@file:packages/knowProTest: + resolution: {directory: packages/knowProTest, type: directory} + + knowpro@file:packages/knowPro: + resolution: {directory: packages/knowPro, type: directory} + koffi@2.11.0: resolution: {integrity: sha512-AJ6MHz9Z8OIftKu322jrKJFvy/rZTdCD4b7F457WrK71rxYV7O5PSdWsJDN0p3rY1BZaPeLHVwyt4i2Xyk8wJg==} kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kp@file:packages/kp: + resolution: {directory: packages/kp, type: directory} + hasBin: true + langium@3.3.1: resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} engines: {node: '>=16.0.0'} @@ -11132,6 +11266,9 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + list-agent@file:packages/agents/list: + resolution: {directory: packages/agents/list, type: directory} + loader-runner@4.3.1: resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} @@ -11286,6 +11423,9 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + markdown-agent@file:packages/agents/markdown: + resolution: {directory: packages/agents/markdown, type: directory} + markdown-it-texmath@1.0.0: resolution: {integrity: sha512-4hhkiX8/gus+6e53PLCUmUrsa6ZWGgJW2XCW6O0ASvZUiezIK900ZicinTDtG3kAO2kon7oUA/ReWmpW2FByxg==} @@ -11389,6 +11529,12 @@ packages: memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + memory-providers@file:examples/memoryProviders: + resolution: {directory: examples/memoryProviders, type: directory} + + memory-storage@file:packages/memory/storage: + resolution: {directory: packages/memory/storage, type: directory} + merge-deep@3.0.3: resolution: {integrity: sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==} engines: {node: '>=0.10.0'} @@ -11686,6 +11832,9 @@ packages: socks: optional: true + montage-agent@file:packages/agents/montage: + resolution: {directory: packages/agents/montage, type: directory} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -11700,6 +11849,12 @@ packages: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} + music-local@file:packages/agents/playerLocal: + resolution: {directory: packages/agents/playerLocal, type: directory} + + music@file:packages/agents/player: + resolution: {directory: packages/agents/player, type: directory} + mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -12172,6 +12327,9 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + photo-agent@file:packages/agents/photo: + resolution: {directory: packages/agents/photo, type: directory} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -12926,6 +13084,9 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + settings-agent@file:packages/agents/settings: + resolution: {directory: packages/agents/settings, type: directory} + shallow-clone@0.1.2: resolution: {integrity: sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==} engines: {node: '>=0.10.0'} @@ -13103,6 +13264,9 @@ packages: resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} engines: {node: '>=6.0.0'} + spelunker-agent@file:packages/agents/spelunker: + resolution: {directory: packages/agents/spelunker, type: directory} + sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -13306,6 +13470,12 @@ packages: engines: {node: '>=18'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + taskflow-typeagent@file:packages/agents/taskflow: + resolution: {directory: packages/agents/taskflow, type: directory} + + telemetry@file:packages/telemetry: + resolution: {directory: packages/telemetry, type: directory} + temp-file@3.4.0: resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} @@ -13351,6 +13521,9 @@ packages: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} + test-lib@file:packages/testLib: + resolution: {directory: packages/testLib, type: directory} + text-decoder@1.2.1: resolution: {integrity: sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==} @@ -13361,6 +13534,10 @@ packages: resolution: {integrity: sha512-7D/r3s6uPZyU//MCYrX6I14nzauDwJ5CxazouuRGNuvSCihW87ufN6VLoROLCrHg6FblLuJrT6N2BVaPVzqElw==} engines: {node: '>=0.8'} + textpro@file:packages/textPro: + resolution: {directory: packages/textPro, type: directory} + engines: {node: '>=20'} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -13579,6 +13756,13 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} + typeagent@file:packages/typeagent: + resolution: {directory: packages/typeagent, type: directory} + engines: {node: '>=20'} + + typechat-utils@file:packages/utils/typechatUtils: + resolution: {directory: packages/utils/typechatUtils, type: directory} + typechat@0.1.1: resolution: {integrity: sha512-Sw96vmkYqbAahqam7vCp8P/MjIGsR26Odz17UHpVGniYN5ir2B37nRRkoDuRpA5djwNQB+W5TB7w2xoF6kwbHQ==} engines: {node: '>=18'} @@ -13765,6 +13949,9 @@ packages: utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + utility-typeagent@file:packages/agents/utility: + resolution: {directory: packages/agents/utility, type: directory} + utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -13813,6 +14000,9 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + video-agent@file:packages/agents/video: + resolution: {directory: packages/agents/video, type: directory} + vite@6.4.1: resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -13909,6 +14099,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + weather-agent@file:packages/agents/weather: + resolution: {directory: packages/agents/weather, type: directory} + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -13984,6 +14177,12 @@ packages: webpack-cli: optional: true + website-memory@file:packages/memory/website: + resolution: {directory: packages/memory/website, type: directory} + + websocket-channel-server@file:packages/utils/webSocketChannelServer: + resolution: {directory: packages/utils/webSocketChannelServer, type: directory} + websocket-driver@0.7.4: resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} engines: {node: '>=0.8.0'} @@ -13992,6 +14191,9 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} + websocket-utils@file:packages/utils/webSocketUtils: + resolution: {directory: packages/utils/webSocketUtils, type: directory} + webvtt-parser@2.2.0: resolution: {integrity: sha512-FzmaED+jZyt8SCJPTKbSsimrrnQU8ELlViE1wuF3x1pgiQUM8Llj5XWj2j/s6Tlk71ucPfGSMFqZWBtKn/0uEA==} @@ -14940,6 +15142,105 @@ snapshots: transitivePeerDependencies: - supports-color + '@azure/ai-projects@1.0.0-beta.8(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76)': + dependencies: + '@azure-rest/ai-inference': 1.0.0-beta.6 + '@azure-rest/core-client': 2.4.0 + '@azure/abort-controller': 2.1.2 + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/core-auth': 1.9.0 + '@azure/core-lro': 3.2.0 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.20.0 + '@azure/core-sse': 2.2.0 + '@azure/core-tracing': 1.2.0 + '@azure/core-util': 1.11.0 + '@azure/logger': 1.2.0 + '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 + '@azure/storage-blob': 12.27.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.200.0 + '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) + openai: 4.103.0(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76) + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + + '@azure/ai-projects@1.0.0-beta.8(ws@8.18.2)(zod@4.1.13)': + dependencies: + '@azure-rest/ai-inference': 1.0.0-beta.6 + '@azure-rest/core-client': 2.4.0 + '@azure/abort-controller': 2.1.2 + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/core-auth': 1.9.0 + '@azure/core-lro': 3.2.0 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.20.0 + '@azure/core-sse': 2.2.0 + '@azure/core-tracing': 1.2.0 + '@azure/core-util': 1.11.0 + '@azure/logger': 1.2.0 + '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 + '@azure/storage-blob': 12.27.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.200.0 + '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) + openai: 4.103.0(ws@8.18.2)(zod@4.1.13) + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + + '@azure/ai-projects@1.0.0-beta.8(ws@8.18.2)(zod@4.3.6)': + dependencies: + '@azure-rest/ai-inference': 1.0.0-beta.6 + '@azure-rest/core-client': 2.4.0 + '@azure/abort-controller': 2.1.2 + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/core-auth': 1.9.0 + '@azure/core-lro': 3.2.0 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.20.0 + '@azure/core-sse': 2.2.0 + '@azure/core-tracing': 1.2.0 + '@azure/core-util': 1.11.0 + '@azure/logger': 1.2.0 + '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 + '@azure/storage-blob': 12.27.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.200.0 + '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) + openai: 4.103.0(ws@8.18.2)(zod@4.3.6) + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + '@azure/ai-projects@1.0.0-beta.8(ws@8.19.0)(zod@3.25.76)': dependencies: '@azure-rest/ai-inference': 1.0.0-beta.6 @@ -15006,6 +15307,39 @@ snapshots: - ws - zod + '@azure/ai-projects@1.0.0-beta.8(ws@8.19.0)(zod@4.3.6)': + dependencies: + '@azure-rest/ai-inference': 1.0.0-beta.6 + '@azure-rest/core-client': 2.4.0 + '@azure/abort-controller': 2.1.2 + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/core-auth': 1.9.0 + '@azure/core-lro': 3.2.0 + '@azure/core-paging': 1.6.2 + '@azure/core-rest-pipeline': 1.20.0 + '@azure/core-sse': 2.2.0 + '@azure/core-tracing': 1.2.0 + '@azure/core-util': 1.11.0 + '@azure/logger': 1.2.0 + '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 + '@azure/storage-blob': 12.27.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.200.0 + '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) + '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) + openai: 4.103.0(ws@8.19.0)(zod@4.3.6) + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + - supports-color + - ws + - zod + '@azure/arm-authorization@9.0.0': dependencies: '@azure/core-auth': 1.9.0 @@ -16512,7 +16846,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -16526,7 +16860,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -16547,7 +16881,7 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -16561,7 +16895,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -16582,44 +16916,9 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5))': + '@jest/environment@29.7.0': dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.19.25 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 + '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/node': 20.19.25 jest-mock: 29.7.0 @@ -17320,6 +17619,18 @@ snapshots: - supports-color - zod + '@modelcontextprotocol/server-filesystem@2025.8.21(zod@4.3.6)': + dependencies: + '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) + diff: 5.2.0 + glob: 10.5.0 + minimatch: 10.2.4 + zod-to-json-schema: 3.24.5(zod@4.3.6) + transitivePeerDependencies: + - '@cfworker/json-schema' + - supports-color + - zod + '@mongodb-js/saslprep@1.2.2': dependencies: sparse-bitfield: 3.0.3 @@ -18305,6 +18616,86 @@ snapshots: '@tsconfig/node16@1.0.4': {} + '@typeagent/action-schema-compiler@file:packages/actionSchemaCompiler': + dependencies: + '@oclif/core': 4.8.0 + '@oclif/plugin-help': 6.2.26 + '@typeagent/action-schema': file:packages/actionSchema + transitivePeerDependencies: + - supports-color + + '@typeagent/action-schema@file:packages/actionSchema': + dependencies: + debug: 4.4.3(supports-color@8.1.1) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typeagent/agent-rpc@file:packages/agentRpc': + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + '@typeagent/agent-sdk@file:packages/agentSdk': + dependencies: + debug: 4.4.3(supports-color@8.1.1) + type-fest: 4.41.0 + transitivePeerDependencies: + - supports-color + + '@typeagent/agent-server-client@file:packages/agentServer/client(ws@8.18.2)': + dependencies: + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + debug: 4.4.3(supports-color@8.1.1) + isomorphic-ws: 5.0.0(ws@8.18.2) + transitivePeerDependencies: + - supports-color + - ws + + '@typeagent/agent-server-client@file:packages/agentServer/client(ws@8.19.0)': + dependencies: + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + debug: 4.4.3(supports-color@8.1.1) + isomorphic-ws: 5.0.0(ws@8.19.0) + transitivePeerDependencies: + - supports-color + - ws + + '@typeagent/agent-server-protocol@file:packages/agentServer/protocol': + dependencies: + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + transitivePeerDependencies: + - supports-color + + '@typeagent/common-utils@file:packages/utils/commonUtils': + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + '@typeagent/dispatcher-rpc@file:packages/dispatcher/rpc': + dependencies: + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/dispatcher-types': file:packages/dispatcher/types + transitivePeerDependencies: + - supports-color + + '@typeagent/dispatcher-types@file:packages/dispatcher/types': + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + transitivePeerDependencies: + - supports-color + '@types/async@3.2.24': {} '@types/babel__core@7.20.5': @@ -19163,99 +19554,506 @@ snapshots: acorn@8.15.0: {} - agent-base@5.1.1: {} - - agent-base@6.0.2: + action-grammar-compiler@file:packages/actionGrammarCompiler: dependencies: - debug: 4.4.3(supports-color@8.1.1) + '@oclif/core': 4.8.0 + '@oclif/plugin-help': 6.2.26 + action-grammar: file:packages/actionGrammar(zod@4.1.13) transitivePeerDependencies: - supports-color + - zod - agent-base@7.1.3: {} - - agentkeepalive@4.6.0: + action-grammar-compiler@file:packages/actionGrammarCompiler(zod@3.25.76): dependencies: - humanize-ms: 1.2.1 + '@oclif/core': 4.8.0 + '@oclif/plugin-help': 6.2.26 + action-grammar: file:packages/actionGrammar(zod@3.25.76) + transitivePeerDependencies: + - supports-color + - zod - aggregate-error@3.1.0: + action-grammar-compiler@file:packages/actionGrammarCompiler(zod@4.3.6): dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - - ajv-formats@3.0.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 + '@oclif/core': 4.8.0 + '@oclif/plugin-help': 6.2.26 + action-grammar: file:packages/actionGrammar(zod@4.3.6) + transitivePeerDependencies: + - supports-color + - zod - ajv-keywords@3.5.2(ajv@6.14.0): + action-grammar@file:packages/actionGrammar(zod@3.25.76): dependencies: - ajv: 6.14.0 + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@3.25.76) + '@typeagent/action-schema': file:packages/actionSchema + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + regexp.escape: 2.0.1 + transitivePeerDependencies: + - supports-color + - zod - ajv-keywords@5.1.0(ajv@8.18.0): + action-grammar@file:packages/actionGrammar(zod@4.1.13): dependencies: - ajv: 8.18.0 - fast-deep-equal: 3.1.3 + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) + '@typeagent/action-schema': file:packages/actionSchema + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + regexp.escape: 2.0.1 + transitivePeerDependencies: + - supports-color + - zod - ajv@6.14.0: + action-grammar@file:packages/actionGrammar(zod@4.3.6): dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@typeagent/action-schema': file:packages/actionSchema + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + regexp.escape: 2.0.1 + transitivePeerDependencies: + - supports-color + - zod - ajv@8.18.0: + agent-base@5.1.1: {} + + agent-base@6.0.2: dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color - ansi-colors@4.1.3: {} + agent-base@7.1.3: {} - ansi-escapes@4.3.2: + agent-cache@file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): dependencies: - type-fest: 0.21.3 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + action-grammar: file:packages/actionGrammar(zod@3.25.76) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + async: 3.2.6 + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + regexp.escape: 2.0.1 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod - ansi-escapes@7.0.0: + agent-cache@file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): dependencies: - environment: 1.1.0 - - ansi-html-community@0.0.8: {} - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + action-grammar: file:packages/actionGrammar(zod@4.1.13) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + async: 3.2.6 + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + regexp.escape: 2.0.1 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod - ansi-styles@3.2.1: + agent-cache@file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): dependencies: - color-convert: 1.9.3 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + action-grammar: file:packages/actionGrammar(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + async: 3.2.6 + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + regexp.escape: 2.0.1 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod - ansi-styles@4.3.0: + agent-dispatcher@file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2): dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.3: {} - - ansi_up@6.0.5: {} - - ansis@3.17.0: {} + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.3.6) + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) + '@azure/identity': 4.10.0 + '@github/copilot-sdk': 0.1.26 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-types': file:packages/dispatcher/types + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.3.6) + chalk: 5.6.2 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + html-to-text: 9.0.5 + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-width: 7.2.0 + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws - any-promise@1.3.0: {} + agent-dispatcher@file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) + '@azure/identity': 4.10.0 + '@github/copilot-sdk': 0.1.26 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-types': file:packages/dispatcher/types + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + chalk: 5.6.2 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + html-to-text: 9.0.5 + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-width: 7.2.0 + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws - anymatch@3.1.3: + agent-dispatcher@file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2): dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.3.6) + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) + '@azure/identity': 4.10.0 + '@github/copilot-sdk': 0.1.26 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-types': file:packages/dispatcher/types + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.3.6) + chalk: 5.6.2 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + html-to-text: 9.0.5 + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-width: 7.2.0 + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws - apache-arrow@18.1.0: + agent-dispatcher@file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0): dependencies: - '@swc/helpers': 0.5.15 - '@types/command-line-args': 5.2.3 + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) + '@azure/core-client': 1.10.1 + '@azure/core-rest-pipeline': 1.22.2 + '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) + '@azure/identity': 4.10.0 + '@github/copilot-sdk': 0.1.26 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-types': file:packages/dispatcher/types + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + chalk: 5.6.2 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + html-to-text: 9.0.5 + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-width: 7.2.0 + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + aiclient@file:packages/aiclient(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure/identity': 4.10.0 + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - supports-color + - typescript + - zod + + aiclient@file:packages/aiclient(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@azure/identity': 4.10.0 + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - supports-color + - typescript + - zod + + aiclient@file:packages/aiclient(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@azure/identity': 4.10.0 + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - supports-color + - typescript + - zod + + aiclient@file:packages/aiclient(typescript@5.9.3): + dependencies: + '@azure/identity': 4.10.0 + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + - typescript + - zod + + ajv-formats@2.1.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv-keywords@3.5.2(ajv@6.14.0): + dependencies: + ajv: 6.14.0 + + ajv-keywords@5.1.0(ajv@8.18.0): + dependencies: + ajv: 8.18.0 + fast-deep-equal: 3.1.3 + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + android-mobile-agent@file:packages/agents/androidMobile: + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + transitivePeerDependencies: + - supports-color + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-html-community@0.0.8: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + ansi_up@6.0.5: {} + + ansis@3.17.0: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + apache-arrow@18.1.0: + dependencies: + '@swc/helpers': 0.5.15 + '@types/command-line-args': 5.2.3 '@types/command-line-usage': 5.0.4 '@types/node': 20.19.25 command-line-args: 5.2.1 @@ -19425,6 +20223,136 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + azure-ai-foundry@file:packages/azure-ai-foundry(encoding@0.1.13)(typescript@5.4.5)(ws@8.18.2)(zod@3.25.76): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + azure-ai-foundry@file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.3.6): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.3.6) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + azure-ai-foundry@file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.1.13) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + azure-ai-foundry@file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + azure-ai-foundry@file:packages/azure-ai-foundry(typescript@5.4.5)(ws@8.19.0)(zod@3.25.76): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@3.25.76) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + azure-devops-node-api@12.5.0: dependencies: tunnel: 0.0.6 @@ -19621,6 +20549,361 @@ snapshots: browser-stdout@1.3.1: {} + browser-typeagent@file:packages/agents/browser(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@mozilla/readability': 0.6.0 + '@popperjs/core': 2.11.8 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13) + bootstrap: 5.3.6(@popperjs/core@2.11.8) + chalk: 5.6.2 + chat-ui: file:packages/chat-ui + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + cytoscape: 3.33.1 + cytoscape-dagre: 2.5.0(cytoscape@3.33.1) + dagre: 0.8.5 + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-layout: 0.6.1(graphology-types@0.24.8) + graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) + graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) + graphology-types: 0.24.8 + html-to-text: 9.0.5 + jsdom: 26.1.0 + jsonpath: 1.2.1 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + markdown-it: 14.1.1 + pdfjs-dist: 5.3.31 + prismjs: 1.30.0 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-adblocker: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + readline: 1.3.0 + sanitize-filename: 1.6.3 + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + websocket-utils: file:packages/utils/webSocketUtils + ws: 8.19.0 + yaml: 2.8.2 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + browser-typeagent@file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5): + dependencies: + '@mozilla/readability': 0.6.0 + '@popperjs/core': 2.11.8 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13) + bootstrap: 5.3.6(@popperjs/core@2.11.8) + chalk: 5.6.2 + chat-ui: file:packages/chat-ui + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + cytoscape: 3.33.1 + cytoscape-dagre: 2.5.0(cytoscape@3.33.1) + dagre: 0.8.5 + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-layout: 0.6.1(graphology-types@0.24.8) + graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) + graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) + graphology-types: 0.24.8 + html-to-text: 9.0.5 + jsdom: 26.1.0 + jsonpath: 1.2.1 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + markdown-it: 14.1.1 + pdfjs-dist: 5.3.31 + prismjs: 1.30.0 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-adblocker: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + readline: 1.3.0 + sanitize-filename: 1.6.3 + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + website-memory: file:packages/memory/website(typescript@5.4.5) + websocket-utils: file:packages/utils/webSocketUtils + ws: 8.19.0 + yaml: 2.8.2 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + browser-typeagent@file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@mozilla/readability': 0.6.0 + '@popperjs/core': 2.11.8 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + bootstrap: 5.3.6(@popperjs/core@2.11.8) + chalk: 5.6.2 + chat-ui: file:packages/chat-ui + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + cytoscape: 3.33.1 + cytoscape-dagre: 2.5.0(cytoscape@3.33.1) + dagre: 0.8.5 + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-layout: 0.6.1(graphology-types@0.24.8) + graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) + graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) + graphology-types: 0.24.8 + html-to-text: 9.0.5 + jsdom: 26.1.0 + jsonpath: 1.2.1 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + markdown-it: 14.1.1 + pdfjs-dist: 5.3.31 + prismjs: 1.30.0 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-adblocker: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + readline: 1.3.0 + sanitize-filename: 1.6.3 + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) + websocket-utils: file:packages/utils/webSocketUtils + ws: 8.19.0 + yaml: 2.8.2 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + browser-typeagent@file:packages/agents/browser(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@mozilla/readability': 0.6.0 + '@popperjs/core': 2.11.8 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + bootstrap: 5.3.6(@popperjs/core@2.11.8) + chalk: 5.6.2 + chat-ui: file:packages/chat-ui + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + cytoscape: 3.33.1 + cytoscape-dagre: 2.5.0(cytoscape@3.33.1) + dagre: 0.8.5 + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-layout: 0.6.1(graphology-types@0.24.8) + graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) + graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) + graphology-types: 0.24.8 + html-to-text: 9.0.5 + jsdom: 26.1.0 + jsonpath: 1.2.1 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + markdown-it: 14.1.1 + pdfjs-dist: 5.3.31 + prismjs: 1.30.0 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-adblocker: 2.13.6(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + readline: 1.3.0 + sanitize-filename: 1.6.3 + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + websocket-utils: file:packages/utils/webSocketUtils + ws: 8.19.0 + yaml: 2.8.2 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + browser-typeagent@file:packages/agents/browser(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@mozilla/readability': 0.6.0 + '@popperjs/core': 2.11.8 + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-protocol': file:packages/agentServer/protocol + '@typeagent/common-utils': file:packages/utils/commonUtils + '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + bootstrap: 5.3.6(@popperjs/core@2.11.8) + chalk: 5.6.2 + chat-ui: file:packages/chat-ui + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + cytoscape: 3.33.1 + cytoscape-dagre: 2.5.0(cytoscape@3.33.1) + dagre: 0.8.5 + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-layout: 0.6.1(graphology-types@0.24.8) + graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) + graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) + graphology-types: 0.24.8 + html-to-text: 9.0.5 + jsdom: 26.1.0 + jsonpath: 1.2.1 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + markdown-it: 14.1.1 + pdfjs-dist: 5.3.31 + prismjs: 1.30.0 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-adblocker: 2.13.6(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + readline: 1.3.0 + sanitize-filename: 1.6.3 + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) + websocket-utils: file:packages/utils/webSocketUtils + ws: 8.19.0 + yaml: 2.8.2 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + browserslist@4.24.5: dependencies: caniuse-lite: 1.0.30001718 @@ -19769,6 +21052,54 @@ snapshots: normalize-url: 6.1.0 responselike: 2.0.1 + calendar@file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.1.13) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@azure/msal-browser' + - '@mongodb-js/zstd' + - buffer + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - stream-browserify + - supports-color + - typescript + - zod + + calendar@file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@azure/msal-browser' + - '@mongodb-js/zstd' + - buffer + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - stream-browserify + - supports-color + - typescript + - zod + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -19830,6 +21161,93 @@ snapshots: chardet@2.1.0: {} + chat-agent@file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.1.13) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + chat-agent@file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + chat-agent@file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure/ai-agents': 1.0.0-beta.3 + '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@3.25.76) + '@azure/identity': 4.10.0 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - ws + - zod + + chat-ui@file:packages/chat-ui: + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + ansi_up: 6.0.5 + dompurify: 3.2.6 + markdown-it: 14.1.1 + transitivePeerDependencies: + - supports-color + cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -20001,10 +21419,85 @@ snapshots: cockatiel@3.2.1: {} + code-agent@file:packages/agents/code(socks@2.8.7): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + better-sqlite3: 12.6.2 + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + telemetry: file:packages/telemetry(socks@2.8.7) + websocket-utils: file:packages/utils/webSocketUtils + ws: 8.19.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - utf-8-validate + code-excerpt@4.0.0: dependencies: convert-to-spaces: 2.0.1 + code-processor@file:packages/codeProcessor(socks@2.8.7)(zod@3.25.76): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + typescript: 5.4.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - zod + + code-processor@file:packages/codeProcessor(socks@2.8.7)(zod@4.1.13): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typescript: 5.4.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - zod + + code-processor@file:packages/codeProcessor(socks@2.8.7)(zod@4.3.6): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typescript: 5.4.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - zod + codemirror@6.0.1: dependencies: '@codemirror/autocomplete': 6.18.6 @@ -20015,6 +21508,18 @@ snapshots: '@codemirror/state': 6.5.2 '@codemirror/view': 6.37.2 + coder-wrapper@file:packages/coderWrapper(ws@8.19.0)(zod@3.25.76): + dependencies: + '@modelcontextprotocol/sdk': 1.26.0(zod@3.25.76) + '@typeagent/agent-server-client': file:packages/agentServer/client(ws@8.19.0) + '@typeagent/dispatcher-types': file:packages/dispatcher/types + node-pty: 1.1.0 + transitivePeerDependencies: + - '@cfworker/json-schema' + - supports-color + - ws + - zod + collect-v8-coverage@1.0.2: {} color-convert@1.9.3: @@ -20137,6 +21642,81 @@ snapshots: content-type@1.0.5: {} + conversation-memory@file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + mailparser: 3.9.0 + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + webvtt-parser: 2.2.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + conversation-memory@file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + mailparser: 3.9.0 + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + webvtt-parser: 2.2.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + conversation-memory@file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + mailparser: 3.9.0 + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + webvtt-parser: 2.2.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + convert-source-map@2.0.0: {} convert-to-spaces@2.0.1: {} @@ -20238,21 +21818,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-jest@29.7.0(@types/node@22.15.18)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@jest/types': 29.6.3 @@ -20358,6 +21923,11 @@ snapshots: cytoscape: 3.32.0 dagre: 0.8.5 + cytoscape-dagre@2.5.0(cytoscape@3.33.1): + dependencies: + cytoscape: 3.33.1 + dagre: 0.8.5 + cytoscape-fcose@2.2.0(cytoscape@3.33.1): dependencies: cose-base: 2.2.0 @@ -20623,6 +22193,219 @@ snapshots: deepmerge@4.3.1: {} + default-agent-provider@file:packages/defaultAgentProvider(puppeteer-core@24.37.5)(typescript@5.4.5): + dependencies: + '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) + '@modelcontextprotocol/server-filesystem': 2025.8.21(zod@4.3.6) + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + android-mobile-agent: file:packages/agents/androidMobile + browser-typeagent: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) + calendar: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + chalk: 5.6.2 + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + code-agent: file:packages/agents/code(socks@2.8.7) + debug: 4.4.3(supports-color@8.1.1) + desktop-automation: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + dispatcher-node-providers: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.19.0) + email: file:packages/agents/email(typescript@5.4.5)(zod@4.3.6) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + greeting-agent: file:packages/agents/greeting(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + image-agent: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + list-agent: file:packages/agents/list + markdown-agent: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + montage-agent: file:packages/agents/montage(typescript@5.4.5)(zod@4.3.6) + music: file:packages/agents/player(typescript@5.4.5)(zod@4.3.6) + music-local: file:packages/agents/playerLocal + photo-agent: file:packages/agents/photo + proper-lockfile: 4.1.2 + settings-agent: file:packages/agents/settings(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + spelunker-agent: file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6) + string-width: 7.2.0 + taskflow-typeagent: file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + utility-typeagent: file:packages/agents/utility(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) + video-agent: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + weather-agent: file:packages/agents/weather + ws: 8.19.0 + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@azure/msal-browser' + - '@cfworker/json-schema' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - stream-browserify + - supports-color + - typescript + - utf-8-validate + + default-agent-provider@file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5): + dependencies: + '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) + '@modelcontextprotocol/server-filesystem': 2025.8.21(zod@4.3.6) + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + android-mobile-agent: file:packages/agents/androidMobile + browser-typeagent: file:packages/agents/browser(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + calendar: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + chalk: 5.6.2 + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + code-agent: file:packages/agents/code(socks@2.8.7) + debug: 4.4.3(supports-color@8.1.1) + desktop-automation: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + dispatcher-node-providers: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + email: file:packages/agents/email(typescript@5.4.5)(zod@4.3.6) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + greeting-agent: file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + image-agent: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + list-agent: file:packages/agents/list + markdown-agent: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + montage-agent: file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + music: file:packages/agents/player(typescript@5.4.5)(zod@4.3.6) + music-local: file:packages/agents/playerLocal + photo-agent: file:packages/agents/photo + proper-lockfile: 4.1.2 + settings-agent: file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + spelunker-agent: file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6) + string-width: 7.2.0 + taskflow-typeagent: file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + utility-typeagent: file:packages/agents/utility(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + video-agent: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + weather-agent: file:packages/agents/weather + ws: 8.19.0 + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@azure/msal-browser' + - '@cfworker/json-schema' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - stream-browserify + - supports-color + - typescript + - utf-8-validate + + default-agent-provider@file:packages/defaultAgentProvider(typescript@5.4.5): + dependencies: + '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) + '@modelcontextprotocol/server-filesystem': 2025.8.21(zod@4.3.6) + '@typeagent/action-schema': file:packages/actionSchema + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + action-grammar: file:packages/actionGrammar(zod@4.3.6) + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + android-mobile-agent: file:packages/agents/androidMobile + browser-typeagent: file:packages/agents/browser(typescript@5.4.5)(zod@4.3.6) + calendar: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + chalk: 5.6.2 + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + code-agent: file:packages/agents/code(socks@2.8.7) + debug: 4.4.3(supports-color@8.1.1) + desktop-automation: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + dispatcher-node-providers: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.19.0) + email: file:packages/agents/email(typescript@5.4.5)(zod@4.3.6) + exifreader: 4.30.1 + file-size: 1.0.0 + glob: 13.0.6 + greeting-agent: file:packages/agents/greeting(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + image-agent: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + list-agent: file:packages/agents/list + markdown-agent: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + montage-agent: file:packages/agents/montage(typescript@5.4.5)(zod@4.3.6) + music: file:packages/agents/player(typescript@5.4.5)(zod@4.3.6) + music-local: file:packages/agents/playerLocal + photo-agent: file:packages/agents/photo + proper-lockfile: 4.1.2 + settings-agent: file:packages/agents/settings(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + spelunker-agent: file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6) + string-width: 7.2.0 + taskflow-typeagent: file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + utility-typeagent: file:packages/agents/utility(typescript@5.4.5)(zod@4.3.6) + video-agent: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + weather-agent: file:packages/agents/weather + ws: 8.19.0 + zod: 4.3.6 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@azure/msal-browser' + - '@cfworker/json-schema' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - stream-browserify + - supports-color + - typescript + - utf-8-validate + default-browser-id@5.0.0: {} default-browser@5.2.1: @@ -20674,6 +22457,64 @@ snapshots: dequal@2.0.3: {} + desktop-automation@file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + body-parser: 1.20.3 + chalk: 5.6.2 + cors: 2.8.5 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + ws: 8.19.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + desktop-automation@file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + body-parser: 1.20.3 + chalk: 5.6.2 + cors: 2.8.5 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + ws: 8.19.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + destroy@1.2.0: {} detect-indent@6.1.0: {} @@ -20709,6 +22550,102 @@ snapshots: dependencies: path-type: 4.0.0 + dispatcher-node-providers@file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2): + dependencies: + '@azure/msal-node-extensions': 1.5.13 + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + + dispatcher-node-providers@file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0): + dependencies: + '@azure/msal-node-extensions': 1.5.13 + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + + dispatcher-node-providers@file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.18.2): + dependencies: + '@azure/msal-node-extensions': 1.5.13 + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + + dispatcher-node-providers@file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.19.0): + dependencies: + '@azure/msal-node-extensions': 1.5.13 + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + dmg-builder@26.8.1(electron-builder-squirrel-windows@26.8.1): dependencies: app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) @@ -20910,6 +22847,44 @@ snapshots: transitivePeerDependencies: - supports-color + email@file:packages/agents/email(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.1.13) + kp: file:packages/kp(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + transitivePeerDependencies: + - '@azure/msal-browser' + - buffer + - encoding + - stream-browserify + - supports-color + - typescript + - zod + + email@file:packages/agents/email(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.3.6) + kp: file:packages/kp(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + transitivePeerDependencies: + - '@azure/msal-browser' + - buffer + - encoding + - stream-browserify + - supports-color + - typescript + - zod + emittery@0.13.1: {} emoji-regex@10.4.0: {} @@ -21176,6 +23151,48 @@ snapshots: dependencies: eventsource-parser: 3.0.6 + examples-lib@file:examples/examplesLib(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + chalk: 5.6.2 + dotenv: 16.5.0 + interactive-app: file:packages/interactiveApp + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + examples-lib@file:examples/examplesLib(typescript@5.4.5)(zod@4.1.13): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chalk: 5.6.2 + dotenv: 16.5.0 + interactive-app: file:packages/interactiveApp + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + execa@1.0.0: dependencies: cross-spawn: 6.0.6 @@ -21793,31 +23810,139 @@ snapshots: - encoding - supports-color - googleapis@144.0.0(encoding@0.1.13): + googleapis@144.0.0(encoding@0.1.13): + dependencies: + google-auth-library: 9.15.1(encoding@0.1.13) + googleapis-common: 7.2.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + + gopd@1.2.0: {} + + got@11.8.6: + dependencies: + '@sindresorhus/is': 4.6.0 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.3 + '@types/responselike': 1.0.3 + cacheable-lookup: 5.0.4 + cacheable-request: 7.0.4 + decompress-response: 6.0.0 + http2-wrapper: 1.0.3 + lowercase-keys: 2.0.0 + p-cancelable: 2.1.1 + responselike: 2.0.1 + + graceful-fs@4.2.11: {} + + graph-utils@file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5): + dependencies: + '@azure/identity': 4.10.0 + '@azure/identity-cache-persistence': 1.2.0 + '@azure/logger': 1.3.0 + '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + googleapis: 144.0.0(encoding@0.1.13) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-compare: 1.1.2 + typeagent: file:packages/typeagent(zod@4.1.13) + transitivePeerDependencies: + - '@azure/msal-browser' + - buffer + - encoding + - stream-browserify + - supports-color + - typescript + - zod + + graph-utils@file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure/identity': 4.10.0 + '@azure/identity-cache-persistence': 1.2.0 + '@azure/logger': 1.3.0 + '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + googleapis: 144.0.0(encoding@0.1.13) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-compare: 1.1.2 + typeagent: file:packages/typeagent(zod@3.25.76) + transitivePeerDependencies: + - '@azure/msal-browser' + - buffer + - encoding + - stream-browserify + - supports-color + - typescript + - zod + + graph-utils@file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@azure/identity': 4.10.0 + '@azure/identity-cache-persistence': 1.2.0 + '@azure/logger': 1.3.0 + '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + googleapis: 144.0.0(encoding@0.1.13) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-compare: 1.1.2 + typeagent: file:packages/typeagent(zod@4.1.13) + transitivePeerDependencies: + - '@azure/msal-browser' + - buffer + - encoding + - stream-browserify + - supports-color + - typescript + - zod + + graph-utils@file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.3.6): dependencies: - google-auth-library: 9.15.1(encoding@0.1.13) - googleapis-common: 7.2.0(encoding@0.1.13) + '@azure/identity': 4.10.0 + '@azure/identity-cache-persistence': 1.2.0 + '@azure/logger': 1.3.0 + '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + googleapis: 144.0.0(encoding@0.1.13) + open: 10.1.2 + proper-lockfile: 4.1.2 + string-compare: 1.1.2 + typeagent: file:packages/typeagent(zod@4.3.6) transitivePeerDependencies: + - '@azure/msal-browser' + - buffer - encoding + - stream-browserify - supports-color - - gopd@1.2.0: {} - - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - graceful-fs@4.2.11: {} + - typescript + - zod graphlib@2.1.8: dependencies: @@ -21882,6 +24007,93 @@ snapshots: graphology-types: 0.24.8 obliterator: 2.0.5 + greeting-agent@file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + - zod + + greeting-agent@file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + - zod + + greeting-agent@file:packages/agents/greeting(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + - zod + gtoken@7.1.0(encoding@0.1.13): dependencies: gaxios: 6.7.1(encoding@0.1.13) @@ -22132,6 +24344,120 @@ snapshots: ignore@7.0.4: {} + image-agent@file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + image-agent@file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + image-memory@file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + get-folder-size: 5.0.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + image-memory@file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + get-folder-size: 5.0.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + image-memory@file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + get-folder-size: 5.0.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + image-size@0.5.5: optional: true @@ -22212,6 +24538,10 @@ snapshots: - bufferutil - utf-8-validate + interactive-app@file:packages/interactiveApp: + dependencies: + string-width: 7.2.0 + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -22567,25 +24897,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-cli@29.7.0(@types/node@22.15.18)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)) @@ -22686,37 +24997,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): - dependencies: - '@babel/core': 7.28.4 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.4) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 20.19.25 - ts-node: 10.9.2(@types/node@20.19.25)(typescript@5.4.5) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-config@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@babel/core': 7.28.4 @@ -23089,18 +25369,6 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest@29.7.0(@types/node@22.15.18)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)) @@ -23326,10 +25594,184 @@ snapshots: kleur@3.0.3: {} + knowledge-processor@file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + sharp: 0.33.5 + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + knowledge-processor@file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + sharp: 0.33.5 + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + knowledge-processor@file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + sharp: 0.33.5 + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + knowpro-test@file:packages/knowProTest(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + interactive-app: file:packages/interactiveApp + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + knowpro@file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + fast-levenshtein: 3.0.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + knowpro@file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + fast-levenshtein: 3.0.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + knowpro@file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + async: 3.2.6 + debug: 4.4.3(supports-color@8.1.1) + fast-levenshtein: 3.0.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + koffi@2.11.0: {} kolorist@1.8.0: {} + kp@file:packages/kp(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - supports-color + - typescript + - zod + + kp@file:packages/kp(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - supports-color + - typescript + - zod + langium@3.3.1: dependencies: chevrotain: 11.0.3 @@ -23480,6 +25922,12 @@ snapshots: dependencies: uc.micro: 2.1.0 + list-agent@file:packages/agents/list: + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + transitivePeerDependencies: + - supports-color + loader-runner@4.3.1: {} local-pkg@1.1.1: @@ -23628,9 +26076,103 @@ snapshots: transitivePeerDependencies: - supports-color - makeerror@1.0.12: + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + markdown-agent@file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@milkdown/core': 7.13.1 + '@milkdown/crepe': 7.13.1(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(typescript@5.4.5) + '@milkdown/plugin-collab': 7.13.1(y-prosemirror@1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) + '@milkdown/plugin-history': 7.13.1 + '@milkdown/preset-commonmark': 7.13.1 + '@milkdown/preset-gfm': 7.13.1 + '@milkdown/theme-nord': 7.13.1 + '@milkdown/utils': 7.13.1 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + katex: 0.16.22 + lib0: 0.2.117 + markdown-it: 14.1.1 + markdown-it-texmath: 1.0.0 + mermaid: 11.10.0 + prosemirror-inputrules: 1.5.0 + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.0 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + unist-util-visit: 4.1.2 + ws: 8.19.0 + y-prosemirror: 1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) + y-protocols: 1.0.6(yjs@13.6.27) + y-websocket: 1.5.4(yjs@13.6.27) + yjs: 13.6.27 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + markdown-agent@file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): dependencies: - tmpl: 1.0.5 + '@milkdown/core': 7.13.1 + '@milkdown/crepe': 7.13.1(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(typescript@5.4.5) + '@milkdown/plugin-collab': 7.13.1(y-prosemirror@1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) + '@milkdown/plugin-history': 7.13.1 + '@milkdown/preset-commonmark': 7.13.1 + '@milkdown/preset-gfm': 7.13.1 + '@milkdown/theme-nord': 7.13.1 + '@milkdown/utils': 7.13.1 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + katex: 0.16.22 + lib0: 0.2.117 + markdown-it: 14.1.1 + markdown-it-texmath: 1.0.0 + mermaid: 11.10.0 + prosemirror-inputrules: 1.5.0 + prosemirror-model: 1.25.1 + prosemirror-state: 1.4.3 + prosemirror-view: 1.40.0 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + unist-util-visit: 4.1.2 + ws: 8.19.0 + y-prosemirror: 1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) + y-protocols: 1.0.6(yjs@13.6.27) + y-websocket: 1.5.4(yjs@13.6.27) + yjs: 13.6.27 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod markdown-it-texmath@1.0.0: {} @@ -23823,6 +26365,90 @@ snapshots: memory-pager@1.5.0: {} + memory-providers@file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@elastic/elasticsearch': 8.19.1 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + memory-storage@file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure/search-documents': 12.1.0 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + typeagent: file:packages/typeagent(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + memory-storage@file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@azure/search-documents': 12.1.0 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + typeagent: file:packages/typeagent(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + memory-storage@file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@azure/search-documents': 12.1.0 + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + better-sqlite3: 12.6.2 + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + typeagent: file:packages/typeagent(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + merge-deep@3.0.3: dependencies: arr-union: 3.1.0 @@ -24237,6 +26863,99 @@ snapshots: optionalDependencies: socks: 2.8.7 + montage-agent@file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + body-parser: 1.20.3 + d3: 7.9.0 + d3-cloud: 1.2.7 + debug: 4.4.3(supports-color@8.1.1) + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + koffi: 2.11.0 + sharp: 0.33.5 + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + winreg: 1.2.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + montage-agent@file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + body-parser: 1.20.3 + d3: 7.9.0 + d3-cloud: 1.2.7 + debug: 4.4.3(supports-color@8.1.1) + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + koffi: 2.11.0 + sharp: 0.33.5 + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + winreg: 1.2.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + montage-agent@file:packages/agents/montage(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + body-parser: 1.20.3 + d3: 7.9.0 + d3-cloud: 1.2.7 + debug: 4.4.3(supports-color@8.1.1) + express: 4.22.1 + express-rate-limit: 7.5.0(express@4.22.1) + image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + koffi: 2.11.0 + sharp: 0.33.5 + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + winreg: 1.2.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + ms@2.0.0: {} ms@2.1.3: {} @@ -24254,6 +26973,46 @@ snapshots: arrify: 2.0.1 minimatch: 3.1.5 + music-local@file:packages/agents/playerLocal: + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + play-sound: 1.1.6 + transitivePeerDependencies: + - supports-color + + music@file:packages/agents/player(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + express: 4.22.1 + open: 10.1.2 + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - supports-color + - typescript + - zod + + music@file:packages/agents/player(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + express: 4.22.1 + open: 10.1.2 + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - supports-color + - typescript + - zod + mute-stream@0.0.8: {} mute-stream@2.0.0: {} @@ -24473,6 +27232,21 @@ snapshots: is-inside-container: 1.0.0 is-wsl: 3.1.0 + openai@4.103.0(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + optionalDependencies: + ws: 8.18.2 + zod: 3.25.76 + transitivePeerDependencies: + - encoding + openai@4.103.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76): dependencies: '@types/node': 18.19.130 @@ -24488,6 +27262,36 @@ snapshots: transitivePeerDependencies: - encoding + openai@4.103.0(ws@8.18.2)(zod@4.1.13): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + optionalDependencies: + ws: 8.18.2 + zod: 4.1.13 + transitivePeerDependencies: + - encoding + + openai@4.103.0(ws@8.18.2)(zod@4.3.6): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + optionalDependencies: + ws: 8.18.2 + zod: 4.3.6 + transitivePeerDependencies: + - encoding + openai@4.103.0(ws@8.19.0)(zod@3.25.76): dependencies: '@types/node': 18.19.130 @@ -24518,6 +27322,21 @@ snapshots: transitivePeerDependencies: - encoding + openai@4.103.0(ws@8.19.0)(zod@4.3.6): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0(encoding@0.1.13) + optionalDependencies: + ws: 8.19.0 + zod: 4.3.6 + transitivePeerDependencies: + - encoding + ora@5.4.1: dependencies: bl: 4.1.0 @@ -24752,6 +27571,12 @@ snapshots: pend@1.2.0: {} + photo-agent@file:packages/agents/photo: + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + transitivePeerDependencies: + - supports-color + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -25039,7 +27864,7 @@ snapshots: '@cliqz/adblocker-puppeteer': 1.23.8(puppeteer@24.37.5(typescript@5.4.5)) debug: 4.4.3(supports-color@8.1.1) node-fetch: 2.7.0(encoding@0.1.13) - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) optionalDependencies: puppeteer: 24.37.5(typescript@5.4.5) puppeteer-core: 24.37.5 @@ -25049,42 +27874,56 @@ snapshots: - playwright-extra - supports-color - puppeteer-extra-plugin-stealth@2.11.2(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin-adblocker@2.13.6(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)): + dependencies: + '@cliqz/adblocker-puppeteer': 1.23.8(puppeteer@24.37.5(typescript@5.4.5)) + debug: 4.4.3(supports-color@8.1.1) + node-fetch: 2.7.0(encoding@0.1.13) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + optionalDependencies: + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + transitivePeerDependencies: + - encoding + - playwright-extra + - supports-color + + puppeteer-extra-plugin-stealth@2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): dependencies: debug: 4.4.3(supports-color@8.1.1) - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) - puppeteer-extra-plugin-user-preferences: 2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin-user-preferences: 2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) transitivePeerDependencies: - supports-color - puppeteer-extra-plugin-user-data-dir@2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin-user-data-dir@2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): dependencies: debug: 4.4.3(supports-color@8.1.1) fs-extra: 10.1.0 - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) rimraf: 3.0.2 optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) transitivePeerDependencies: - supports-color - puppeteer-extra-plugin-user-preferences@2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin-user-preferences@2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): dependencies: - debug: 4.4.1 + debug: 4.4.3(supports-color@8.1.1) deepmerge: 4.3.1 - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) - puppeteer-extra-plugin-user-data-dir: 2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin-user-data-dir: 2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) transitivePeerDependencies: - supports-color - puppeteer-extra-plugin@3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin@3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): dependencies: '@types/debug': 4.1.12 - debug: 4.4.1 + debug: 4.4.3(supports-color@8.1.1) merge-deep: 3.0.3 optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) @@ -25736,6 +28575,93 @@ snapshots: setprototypeof@1.2.0: {} + settings-agent@file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + - zod + + settings-agent@file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + - zod + + settings-agent@file:packages/agents/settings(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - ws + - zod + shallow-clone@0.1.2: dependencies: is-extendable: 0.1.1 @@ -25979,6 +28905,50 @@ snapshots: transitivePeerDependencies: - supports-color + spelunker-agent@file:packages/agents/spelunker(socks@2.8.7)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + better-sqlite3: 12.6.2 + code-processor: file:packages/codeProcessor(socks@2.8.7)(zod@4.1.13) + dotenv: 16.5.0 + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typescript: 5.4.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - zod + + spelunker-agent@file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + better-sqlite3: 12.6.2 + code-processor: file:packages/codeProcessor(socks@2.8.7)(zod@4.3.6) + dotenv: 16.5.0 + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typescript: 5.4.5 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - zod + sprintf-js@1.0.3: {} sprintf-js@1.1.3: @@ -26207,6 +29177,47 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 + taskflow-typeagent@file:packages/agents/taskflow(ws@8.18.2)(zod@4.1.13): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-client': file:packages/agentServer/client(ws@8.18.2) + '@typeagent/dispatcher-types': file:packages/dispatcher/types + html-to-text: 9.0.5 + transitivePeerDependencies: + - supports-color + - ws + - zod + + taskflow-typeagent@file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@typeagent/agent-sdk': file:packages/agentSdk + '@typeagent/agent-server-client': file:packages/agentServer/client(ws@8.19.0) + '@typeagent/dispatcher-types': file:packages/dispatcher/types + html-to-text: 9.0.5 + transitivePeerDependencies: + - supports-color + - ws + - zod + + telemetry@file:packages/telemetry(socks@2.8.7): + dependencies: + chalk: 5.6.2 + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + mongodb: 6.16.0(socks@2.8.7) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + temp-file@3.4.0: dependencies: async-exit-hook: 2.0.1 @@ -26268,12 +29279,72 @@ snapshots: glob: 10.5.0 minimatch: 9.0.9 + test-lib@file:packages/testLib(typescript@5.4.5): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - supports-color + - typescript + - zod + text-decoder@1.2.1: {} text-table@0.2.0: {} textextensions@5.16.0: {} + textpro@file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + cheerio: 1.0.0-rc.12 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + marked: 16.0.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + textpro@file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + cheerio: 1.0.0-rc.12 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + marked: 16.0.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + textpro@file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + cheerio: 1.0.0-rc.12 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + marked: 16.0.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -26471,25 +29542,6 @@ snapshots: yn: 3.1.1 optional: true - ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.19.25 - acorn: 8.11.1 - acorn-walk: 8.3.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.4.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -26561,6 +29613,111 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 + typeagent@file:packages/typeagent(zod@3.25.76): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + async: 3.2.6 + cheerio: 1.0.0-rc.12 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + - zod + + typeagent@file:packages/typeagent(zod@4.1.13): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + async: 3.2.6 + cheerio: 1.0.0-rc.12 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + - zod + + typeagent@file:packages/typeagent(zod@4.3.6): + dependencies: + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + async: 3.2.6 + cheerio: 1.0.0-rc.12 + debug: 4.4.3(supports-color@8.1.1) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + - zod + + typechat-utils@file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + typechat-utils@file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + typechat-utils@file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@azure-rest/maps-search': 2.0.0-beta.3 + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + chalk: 5.6.2 + date-fns: 4.1.0 + debug: 4.4.3(supports-color@8.1.1) + exifreader: 4.30.1 + telemetry: file:packages/telemetry(socks@2.8.7) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + typechat@0.1.1(typescript@5.4.5)(zod@3.25.76): optionalDependencies: typescript: 5.4.5 @@ -26571,6 +29728,15 @@ snapshots: typescript: 5.4.5 zod: 4.1.13 + typechat@0.1.1(typescript@5.4.5)(zod@4.3.6): + optionalDependencies: + typescript: 5.4.5 + zod: 4.3.6 + + typechat@0.1.1(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -26751,6 +29917,126 @@ snapshots: utila@0.4.0: {} + utility-typeagent@file:packages/agents/utility(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) + '@typeagent/agent-sdk': file:packages/agentSdk + '@types/html-to-text': 9.0.4 + browser-typeagent: file:packages/agents/browser(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + html-to-text: 9.0.5 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + utility-typeagent@file:packages/agents/utility(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@typeagent/agent-sdk': file:packages/agentSdk + '@types/html-to-text': 9.0.4 + browser-typeagent: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) + html-to-text: 9.0.5 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + utility-typeagent@file:packages/agents/utility(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@typeagent/agent-sdk': file:packages/agentSdk + '@types/html-to-text': 9.0.4 + browser-typeagent: file:packages/agents/browser(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + html-to-text: 9.0.5 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + utility-typeagent@file:packages/agents/utility(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) + '@typeagent/agent-sdk': file:packages/agentSdk + '@types/html-to-text': 9.0.4 + browser-typeagent: file:packages/agents/browser(typescript@5.4.5)(zod@4.3.6) + html-to-text: 9.0.5 + puppeteer: 24.37.5(typescript@5.4.5) + puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) + puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - '@types/puppeteer' + - bare-buffer + - bufferutil + - canvas + - encoding + - gcp-metadata + - kerberos + - mongodb-client-encryption + - playwright-extra + - puppeteer-core + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + utils-merge@1.0.1: {} uuid@11.1.0: {} @@ -26795,6 +30081,42 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 + video-agent@file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + + video-agent@file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - zod + vite@6.4.1(@types/node@20.19.23)(jiti@2.5.1)(less@4.3.0)(terser@5.39.2)(yaml@2.7.0): dependencies: esbuild: 0.25.11 @@ -26883,6 +30205,12 @@ snapshots: dependencies: defaults: 1.0.4 + weather-agent@file:packages/agents/weather: + dependencies: + '@typeagent/agent-sdk': file:packages/agentSdk + transitivePeerDependencies: + - supports-color + web-streams-polyfill@3.3.3: {} web-streams-polyfill@4.0.0-beta.3: {} @@ -27036,6 +30364,197 @@ snapshots: - esbuild - uglify-js + website-memory@file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): + dependencies: + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + better-sqlite3: 12.6.2 + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + get-folder-size: 5.0.0 + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-metrics: 2.4.0(graphology-types@0.24.8) + graphology-types: 0.24.8 + jsdom: 26.1.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + website-memory@file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + better-sqlite3: 12.6.2 + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + get-folder-size: 5.0.0 + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-metrics: 2.4.0(graphology-types@0.24.8) + graphology-types: 0.24.8 + jsdom: 26.1.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + website-memory@file:packages/memory/website(typescript@5.4.5): + dependencies: + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + better-sqlite3: 12.6.2 + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + get-folder-size: 5.0.0 + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-metrics: 2.4.0(graphology-types@0.24.8) + graphology-types: 0.24.8 + jsdom: 26.1.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.1.13) + typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + website-memory@file:packages/memory/website(typescript@5.4.5)(zod@3.25.76): + dependencies: + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + better-sqlite3: 12.6.2 + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + get-folder-size: 5.0.0 + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-metrics: 2.4.0(graphology-types@0.24.8) + graphology-types: 0.24.8 + jsdom: 26.1.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@3.25.76) + typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + website-memory@file:packages/memory/website(typescript@5.4.5)(zod@4.3.6): + dependencies: + '@typeagent/common-utils': file:packages/utils/commonUtils + aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + better-sqlite3: 12.6.2 + cheerio: 1.1.0 + conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + debug: 4.4.3(supports-color@8.1.1) + dompurify: 3.2.6 + get-folder-size: 5.0.0 + graphology: 0.25.4(graphology-types@0.24.8) + graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) + graphology-metrics: 2.4.0(graphology-types@0.24.8) + graphology-types: 0.24.8 + jsdom: 26.1.0 + knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + telemetry: file:packages/telemetry(socks@2.8.7) + typeagent: file:packages/typeagent(zod@4.3.6) + typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - bufferutil + - canvas + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + - typescript + - utf-8-validate + - zod + + websocket-channel-server@file:packages/utils/webSocketChannelServer: + dependencies: + '@typeagent/agent-rpc': file:packages/agentRpc + '@typeagent/common-utils': file:packages/utils/commonUtils + debug: 4.4.3(supports-color@8.1.1) + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + websocket-driver@0.7.4: dependencies: http-parser-js: 0.5.8 @@ -27044,6 +30563,18 @@ snapshots: websocket-extensions@0.1.4: {} + websocket-utils@file:packages/utils/webSocketUtils: + dependencies: + debug: 4.4.3(supports-color@8.1.1) + dotenv: 16.5.0 + find-config: 1.0.0 + isomorphic-ws: 5.0.0(ws@8.19.0) + ws: 8.19.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + webvtt-parser@2.2.0: {} whatwg-encoding@2.0.0: @@ -27266,8 +30797,7 @@ snapshots: yaml@2.7.0: {} - yaml@2.8.2: - optional: true + yaml@2.8.2: {} yargs-parser@20.2.9: {} @@ -27339,6 +30869,10 @@ snapshots: dependencies: zod: 4.1.13 + zod-to-json-schema@3.24.5(zod@4.3.6): + dependencies: + zod: 4.3.6 + zod-to-json-schema@3.25.1(zod@3.25.76): dependencies: zod: 3.25.76 From 468ab7a5f7aacbda8e3e81f50369681e81a376aa Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 5 Mar 2026 09:53:15 -0800 Subject: [PATCH 02/51] name --- ts/packages/actionGrammar/README.md | 2 +- ts/packages/cache/src/cache/grammarStore.ts | 6 +- ts/pnpm-lock.yaml | 4583 +++---------------- 3 files changed, 533 insertions(+), 4058 deletions(-) diff --git a/ts/packages/actionGrammar/README.md b/ts/packages/actionGrammar/README.md index f99738fc72..b47a8ebced 100644 --- a/ts/packages/actionGrammar/README.md +++ b/ts/packages/actionGrammar/README.md @@ -131,7 +131,7 @@ npm run test:integration | --------------------------- | ------------------------------------------------------------------------------------- | | `action-schema` (workspace) | Reading `.pas.json` schema files for checked-variable enrichment | | `debug` | Debug logging (`typeagent:grammar:*`, `typeagent:nfa:*`, `typeagent:actionGrammar:*`) | -| `regexp.escape` | Safe regex escaping for the recursive backtracking matcher matcher | +| `regexp.escape` | Safe regex escaping for the simple recursive backtracking matcher | ## Trademarks diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 2f3f0841a4..ef9383b02b 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -185,12 +185,12 @@ export class GrammarStoreImpl implements GrammarStore { ? "DFA" : this.useNFA && entry.nfa ? "NFA" - : "legacy"; + : "simple"; debug( `Matching "${request}" against ${schemaName} (${matchMode}) - NFA states: ${entry.nfa?.states.length || 0}, DFA states: ${entry.dfa?.states.length || 0}, rules: ${entry.grammar.rules.length}`, ); - // Choose matcher: DFA > NFA > legacy + // Choose matcher: DFA > NFA > simple let grammarMatches; if (this.useDFA && entry.dfa) { const tokens = tokenizeRequest(request); @@ -347,7 +347,7 @@ export class GrammarStoreImpl implements GrammarStore { } } } else { - // Legacy grammar-based completions + // simple grammar-based completions const partial = matchGrammarCompletion( entry.grammar, requestPrefix ?? "", diff --git a/ts/pnpm-lock.yaml b/ts/pnpm-lock.yaml index 5aacfcb1f4..e3ef30478b 100644 --- a/ts/pnpm-lock.yaml +++ b/ts/pnpm-lock.yaml @@ -3,7 +3,6 @@ lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false - injectWorkspacePackages: true importers: @@ -32,7 +31,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../../packages/agentSdk devDependencies: copyfiles: specifier: ^2.4.1 @@ -51,13 +50,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../../packages/agentSdk better-sqlite3: specifier: 12.6.2 version: 12.6.2 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../../packages/typeagent devDependencies: '@types/better-sqlite3': specifier: 7.6.13 @@ -79,25 +78,25 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../../packages/actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../packages/agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../packages/agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../packages/utils/commonUtils agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/cache agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../../packages/dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient body-parser: specifier: 1.20.3 version: 1.20.3 @@ -109,7 +108,7 @@ importers: version: 4.4.3(supports-color@8.1.1) default-agent-provider: specifier: workspace:* - version: file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5) + version: link:../../packages/defaultAgentProvider dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -121,10 +120,10 @@ importers: version: 1.0.0 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../packages/telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent devDependencies: '@types/debug': specifier: ^4.1.12 @@ -143,7 +142,7 @@ importers: version: 8.18.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.10.13)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.4.5)) + version: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) prettier: specifier: ^3.5.3 version: 3.5.3 @@ -164,7 +163,7 @@ importers: version: 12.1.0 aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -173,49 +172,49 @@ importers: version: 5.6.2 code-processor: specifier: workspace:* - version: file:packages/codeProcessor(socks@2.8.7)(zod@3.25.76) + version: link:../../packages/codeProcessor conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/memory/conversation dotenv: specifier: ^16.3.1 version: 16.5.0 examples-lib: specifier: workspace:* - version: file:examples/examplesLib(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../examplesLib exifreader: specifier: ^4.30.1 version: 4.30.1 image-memory: specifier: workspace:* - version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/memory/image interactive-app: specifier: workspace:* version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowPro knowpro-test: specifier: workspace:* - version: file:packages/knowProTest(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowProTest memory-providers: specifier: workspace:* - version: file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../memoryProviders memory-storage: specifier: workspace:* - version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/memory/storage telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../packages/telemetry textpro: specifier: workspace:* - version: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/textPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -252,7 +251,7 @@ importers: version: 16.5.0 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -271,16 +270,16 @@ importers: version: link:../../packages/utils/commonUtils aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient chalk: specifier: ^5.4.1 version: 5.6.2 code-processor: specifier: workspace:* - version: file:packages/codeProcessor(socks@2.8.7)(zod@3.25.76) + version: link:../../packages/codeProcessor conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/memory/conversation dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -289,25 +288,25 @@ importers: version: 4.30.1 image-memory: specifier: workspace:* - version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/memory/image interactive-app: specifier: workspace:* version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowPro memory-providers: specifier: workspace:* - version: file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../memoryProviders mongodb: specifier: ^6.15.0 version: 6.16.0(socks@2.8.7) typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -342,7 +341,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -351,7 +350,7 @@ importers: version: 1.0.0-rc.12 conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/memory/conversation debug: specifier: ^4.4.0 version: 4.4.1 @@ -366,19 +365,19 @@ importers: version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowPro memory-providers: specifier: workspace:* - version: file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../memoryProviders proper-lockfile: specifier: ^4.1.2 version: 4.1.2 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -406,7 +405,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -418,10 +417,10 @@ importers: version: link:../../packages/interactiveApp knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -446,22 +445,22 @@ importers: version: 1.26.0(zod@4.1.13) conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../packages/memory/conversation dotenv: specifier: ^16.3.1 version: 16.5.0 examples-lib: specifier: workspace:* - version: file:examples/examplesLib(typescript@5.4.5)(zod@4.1.13) + version: link:../examplesLib interactive-app: specifier: workspace:* version: link:../../packages/interactiveApp knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../packages/knowPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@4.1.13) + version: link:../../packages/typeagent zod: specifier: ^4.1.13 version: 4.1.13 @@ -486,7 +485,7 @@ importers: version: 8.19.1 aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -495,10 +494,10 @@ importers: version: 4.4.1 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowledgeProcessor typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -572,7 +571,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient copyfiles: specifier: ^2.4.1 version: 2.4.1 @@ -584,10 +583,10 @@ importers: version: link:../../packages/interactiveApp telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../packages/telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -603,25 +602,25 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../../packages/actionSchema agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/cache agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../../packages/dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient azure-ai-foundry: specifier: workspace:* - version: file:packages/azure-ai-foundry(typescript@5.4.5)(ws@8.19.0)(zod@3.25.76) + version: link:../../packages/azure-ai-foundry debug: specifier: ^4.4.0 version: 4.4.1 default-agent-provider: specifier: workspace:* - version: file:packages/defaultAgentProvider(typescript@5.4.5) + version: link:../../packages/defaultAgentProvider dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -636,7 +635,7 @@ importers: version: link:../../packages/schemaAuthor typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -661,7 +660,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient copyfiles: specifier: ^2.4.1 version: 2.4.1 @@ -673,13 +672,13 @@ importers: version: link:../../packages/interactiveApp typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/utils/typechatUtils typescript: specifier: ~5.4.5 version: 5.4.5 @@ -692,7 +691,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -707,10 +706,10 @@ importers: version: link:../../packages/interactiveApp knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/knowledgeProcessor typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -732,7 +731,7 @@ importers: dependencies: agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../../packages/dispatcher/dispatcher chalk: specifier: ^5.4.1 version: 5.6.2 @@ -778,10 +777,10 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../../packages/actionSchema aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -799,10 +798,10 @@ importers: version: 1.0.16 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../packages/telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -830,10 +829,10 @@ importers: version: link:../../packages/utils/commonUtils aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../packages/aiclient azure-ai-foundry: specifier: workspace:* - version: file:packages/azure-ai-foundry(typescript@5.4.5)(ws@8.19.0)(zod@3.25.76) + version: link:../../packages/azure-ai-foundry chalk: specifier: ^5.4.1 version: 5.6.2 @@ -854,7 +853,7 @@ importers: version: 24.37.5(typescript@5.4.5) typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../packages/typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -922,7 +921,7 @@ importers: version: 0.2.12(zod@4.3.6) '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema chalk: specifier: ^5.4.1 version: 5.6.2 @@ -971,7 +970,7 @@ importers: version: 6.2.26 action-grammar: specifier: workspace:* - version: file:packages/actionGrammar(zod@4.3.6) + version: link:../actionGrammar devDependencies: rimraf: specifier: ^6.0.1 @@ -1012,7 +1011,7 @@ importers: version: 6.2.26 '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema devDependencies: rimraf: specifier: ^6.0.1 @@ -1025,7 +1024,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils @@ -1093,19 +1092,19 @@ importers: version: 1.26.0(zod@3.25.76) '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema action-grammar: specifier: workspace:* - version: file:packages/actionGrammar(zod@3.25.76) + version: link:../actionGrammar agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../cache aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient coder-wrapper: specifier: workspace:* - version: file:packages/coderWrapper(ws@8.19.0)(zod@3.25.76) + version: link:../coderWrapper dotenv: specifier: ^16.4.5 version: 16.5.0 @@ -1145,7 +1144,7 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/agent-server-protocol': specifier: workspace:* version: link:../protocol @@ -1195,7 +1194,7 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/agent-server-protocol': specifier: workspace:* version: link:../protocol @@ -1207,22 +1206,22 @@ importers: version: link:../../dispatcher/rpc agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../../dispatcher/dispatcher debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) default-agent-provider: specifier: workspace:* - version: file:packages/defaultAgentProvider(typescript@5.4.5) + version: link:../../defaultAgentProvider dispatcher-node-providers: specifier: workspace:* - version: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.18.2) + version: link:../../dispatcher/nodeProviders dotenv: specifier: ^16.3.1 version: 16.5.0 websocket-channel-server: specifier: workspace:* - version: file:packages/utils/webSocketChannelServer + version: link:../../utils/webSocketChannelServer ws: specifier: ^8.17.1 version: 8.18.2 @@ -1262,7 +1261,7 @@ importers: version: link:../../../utils/commonUtils aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../../aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -1292,7 +1291,7 @@ importers: version: 1.1.2 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../../typeagent devDependencies: '@microsoft/microsoft-graph-types': specifier: ^2.40.0 @@ -1329,7 +1328,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk devDependencies: prettier: specifier: ^3.5.3 @@ -1351,13 +1350,13 @@ importers: version: 2.11.8 '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../../actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/agent-server-protocol': specifier: workspace:* version: link:../../agentServer/protocol @@ -1369,10 +1368,10 @@ importers: version: link:../../dispatcher/rpc aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient azure-ai-foundry: specifier: workspace:* - version: file:packages/azure-ai-foundry(encoding@0.1.13)(typescript@5.4.5)(ws@8.18.2)(zod@3.25.76) + version: link:../../azure-ai-foundry bootstrap: specifier: ^5.3.3 version: 5.3.6(@popperjs/core@2.11.8) @@ -1387,7 +1386,7 @@ importers: version: 1.1.0 conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../memory/conversation cytoscape: specifier: ^3.32.0 version: 3.32.0 @@ -1438,10 +1437,10 @@ importers: version: 1.2.1 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowPro markdown-it: specifier: ^14.1.1 version: 14.1.1 @@ -1462,7 +1461,7 @@ importers: version: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) puppeteer-extra-plugin-stealth: specifier: ^2.11.2 - version: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + version: 2.11.2(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) readline: specifier: ^1.3.0 version: 1.3.0 @@ -1474,16 +1473,16 @@ importers: version: link:../../textPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) website-memory: specifier: workspace:* - version: file:packages/memory/website(typescript@5.4.5)(zod@3.25.76) + version: link:../../memory/website websocket-utils: specifier: workspace:* - version: file:packages/utils/webSocketUtils + version: link:../../utils/webSocketUtils ws: specifier: ^8.17.1 version: 8.18.2 @@ -1499,7 +1498,7 @@ importers: version: 0.57.0(@types/node@20.19.23) '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/chrome': specifier: ^0.0.256 version: 0.0.256 @@ -1547,7 +1546,7 @@ importers: version: 8.18.1 action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler(zod@3.25.76) + version: link:../../actionGrammarCompiler archiver: specifier: ^7.0.1 version: 7.0.1 @@ -1598,7 +1597,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk chalk: specifier: ^5.4.1 version: 5.6.2 @@ -1610,20 +1609,20 @@ importers: version: 4.4.1 graph-utils: specifier: workspace:* - version: file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5)(zod@3.25.76) + version: link:../agentUtils/graphUtils typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../utils/typechatUtils devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler(zod@3.25.76) + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1650,19 +1649,19 @@ importers: version: 4.10.0 '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -1687,7 +1686,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -1699,17 +1698,17 @@ importers: version: 4.4.1 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry websocket-utils: specifier: workspace:* - version: file:packages/utils/webSocketUtils + version: link:../../utils/webSocketUtils ws: specifier: ^8.17.1 version: 8.18.2 devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/better-sqlite3': specifier: 7.6.13 version: 7.6.13 @@ -1736,13 +1735,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../cache aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient body-parser: specifier: ^1.20.3 version: 1.20.3 @@ -1763,20 +1762,20 @@ importers: version: 1.0.0 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../utils/typechatUtils ws: specifier: ^8.17.1 version: 8.18.2 devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/body-parser': specifier: ^1.19.5 version: 1.19.5 @@ -1797,7 +1796,7 @@ importers: version: 8.18.1 action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler(zod@3.25.76) + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1818,13 +1817,13 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.12 - version: 0.2.12(zod@4.1.13) + version: 0.2.12(zod@4.3.6) '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + version: link:../../aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -1833,17 +1832,17 @@ importers: version: 4.4.1 graph-utils: specifier: workspace:* - version: file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5) + version: link:../agentUtils/graphUtils kp: specifier: workspace:* version: link:../../kp typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@4.1.13) + version: link:../../typeagent devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -1864,31 +1863,31 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../../dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient chat-agent: specifier: workspace:* - version: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../chat debug: specifier: ^4.4.0 version: 4.4.1 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -1913,20 +1912,20 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typechat-utils: specifier: workspace:* version: link:../../utils/typechatUtils devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1944,14 +1943,14 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -1993,10 +1992,10 @@ importers: version: 7.13.1 '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient debug: specifier: ^4.3.4 version: 4.4.1 @@ -2038,7 +2037,7 @@ importers: version: 1.40.0 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2111,13 +2110,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient body-parser: specifier: 1.20.3 version: 1.20.3 @@ -2138,13 +2137,13 @@ importers: version: 7.5.0(express@4.22.1) image-memory: specifier: workspace:* - version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../memory/image knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowPro koffi: specifier: ^2.10.1 version: 2.11.0 @@ -2153,7 +2152,7 @@ importers: version: 0.33.5 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2208,11 +2207,11 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2230,7 +2229,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils @@ -2255,7 +2254,7 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -2267,7 +2266,7 @@ importers: version: 0.0.25 action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler(zod@3.25.76) + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2282,7 +2281,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils @@ -2298,7 +2297,7 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -2310,7 +2309,7 @@ importers: version: 22.15.18 action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2328,31 +2327,31 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../../dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient chat-agent: specifier: workspace:* - version: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../chat debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2377,13 +2376,13 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -2395,7 +2394,7 @@ importers: version: 16.5.0 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2423,7 +2422,7 @@ importers: version: 0.2.12(zod@4.3.6) '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/agent-server-client': specifier: workspace:* version: link:../../agentServer/client @@ -2436,13 +2435,13 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/html-to-text': specifier: ^9.0.4 version: 9.0.4 action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler(zod@4.3.6) + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2460,7 +2459,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk devDependencies: prettier: specifier: ^3.5.3 @@ -2476,7 +2475,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2486,7 +2485,7 @@ importers: devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -2525,16 +2524,16 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.12 - version: 0.2.12(zod@4.1.13) + version: 0.2.12(zod@4.3.6) '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@types/html-to-text': specifier: ^9.0.4 version: 9.0.4 browser-typeagent: specifier: workspace:* - version: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5) + version: link:../browser html-to-text: specifier: ^9.0.5 version: 9.0.5 @@ -2546,14 +2545,14 @@ importers: version: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) puppeteer-extra-plugin-stealth: specifier: ^2.11.2 - version: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + version: 2.11.2(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2571,20 +2570,20 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typechat-utils: specifier: workspace:* version: link:../../utils/typechatUtils devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2602,14 +2601,14 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk devDependencies: '@typeagent/action-schema-compiler': specifier: workspace:* - version: file:packages/actionSchemaCompiler + version: link:../../actionSchemaCompiler action-grammar-compiler: specifier: workspace:* - version: file:packages/actionGrammarCompiler + version: link:../../actionGrammarCompiler concurrently: specifier: ^9.1.2 version: 9.1.2 @@ -2676,22 +2675,22 @@ importers: version: 12.27.0 '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/dispatcher-rpc': specifier: workspace:* version: link:../dispatcher/rpc agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../cache agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2700,10 +2699,10 @@ importers: version: 4.4.1 default-agent-provider: specifier: workspace:* - version: file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5) + version: link:../defaultAgentProvider dispatcher-node-providers: specifier: workspace:* - version: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/nodeProviders dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -2712,10 +2711,10 @@ importers: version: 1.0.0 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat-utils: specifier: workspace:* version: link:../utils/typechatUtils @@ -2761,10 +2760,10 @@ importers: version: 4.10.0 '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient async: specifier: ^3.2.5 version: 3.2.6 @@ -2773,10 +2772,10 @@ importers: version: 4.4.1 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -2810,19 +2809,19 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils action-grammar: specifier: workspace:* - version: file:packages/actionGrammar(zod@4.1.13) + version: link:../actionGrammar aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + version: link:../aiclient async: specifier: ^3.2.5 version: 3.2.6 @@ -2837,13 +2836,13 @@ importers: version: 2.0.1 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../telemetry typechat: specifier: ^0.1.1 - version: 0.1.1(typescript@5.4.5)(zod@4.1.13) + version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../utils/typechatUtils devDependencies: '@types/async': specifier: ^3.2.24 @@ -2868,7 +2867,7 @@ importers: version: 6.0.1 test-lib: specifier: workspace:* - version: file:packages/testLib(typescript@5.4.5) + version: link:../testLib typescript: specifier: ~5.4.5 version: 5.4.5 @@ -2877,10 +2876,10 @@ importers: dependencies: agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../cache agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/dispatcher chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2926,7 +2925,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk ansi_up: specifier: ^6.0.2 version: 6.0.5 @@ -2954,10 +2953,10 @@ importers: version: 6.2.26 '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/agent-server-client': specifier: workspace:* version: link:../agentServer/client @@ -2966,16 +2965,16 @@ importers: version: link:../utils/commonUtils action-grammar: specifier: workspace:* - version: file:packages/actionGrammar(zod@4.1.13) + version: link:../actionGrammar agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../cache agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + version: link:../dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + version: link:../aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -2984,10 +2983,10 @@ importers: version: 4.4.1 default-agent-provider: specifier: workspace:* - version: file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5) + version: link:../defaultAgentProvider dispatcher-node-providers: specifier: workspace:* - version: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) + version: link:../dispatcher/nodeProviders dotenv: specifier: ^16.3.1 version: 16.5.0 @@ -3008,16 +3007,16 @@ importers: version: 10.1.2 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../telemetry ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@24.10.13)(typescript@5.4.5) typechat: specifier: ^0.1.1 - version: 0.1.1(typescript@5.4.5)(zod@4.1.13) + version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../utils/typechatUtils devDependencies: '@types/debug': specifier: ^4.1.12 @@ -3109,13 +3108,13 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../knowledgeProcessor typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3168,7 +3167,7 @@ importers: version: 1.26.0(zod@4.1.13) '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/agent-server-client': specifier: workspace:* version: link:../agentServer/client @@ -3226,58 +3225,58 @@ importers: version: 2025.8.21(zod@4.1.13) '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils action-grammar: specifier: workspace:* - version: file:packages/actionGrammar(zod@4.1.13) + version: link:../actionGrammar agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../cache agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + version: link:../aiclient android-mobile-agent: specifier: workspace:* version: link:../agents/androidMobile browser-typeagent: specifier: workspace:* - version: file:packages/agents/browser(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/browser calendar: specifier: workspace:* - version: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/calendar chalk: specifier: ^5.4.1 version: 5.6.2 chat-agent: specifier: workspace:* - version: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) + version: link:../agents/chat code-agent: specifier: workspace:* - version: file:packages/agents/code(socks@2.8.7) + version: link:../agents/code debug: specifier: ^4.4.0 version: 4.4.1 desktop-automation: specifier: workspace:* - version: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/desktop dispatcher-node-providers: specifier: workspace:* - version: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/nodeProviders email: specifier: workspace:* - version: file:packages/agents/email(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/email exifreader: specifier: ^4.30.1 version: 4.30.1 @@ -3289,25 +3288,25 @@ importers: version: 13.0.6 greeting-agent: specifier: workspace:* - version: file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) + version: link:../agents/greeting image-agent: specifier: workspace:* - version: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/image knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../knowledgeProcessor list-agent: specifier: workspace:* version: link:../agents/list markdown-agent: specifier: workspace:* - version: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/markdown montage-agent: specifier: workspace:* - version: file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/montage music: specifier: workspace:* - version: file:packages/agents/player(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/player music-local: specifier: workspace:* version: link:../agents/playerLocal @@ -3319,34 +3318,34 @@ importers: version: 4.1.2 settings-agent: specifier: workspace:* - version: file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) + version: link:../agents/settings spelunker-agent: specifier: workspace:* - version: file:packages/agents/spelunker(socks@2.8.7)(zod@4.1.13) + version: link:../agents/spelunker string-width: specifier: ^7.2.0 version: 7.2.0 taskflow-typeagent: specifier: workspace:* - version: file:packages/agents/taskflow(ws@8.18.2)(zod@4.1.13) + version: link:../agents/taskflow telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@4.1.13) + version: link:../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@4.1.13) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../utils/typechatUtils utility-typeagent: specifier: workspace:* - version: file:packages/agents/utility(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/utility video-agent: specifier: workspace:* - version: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../agents/video weather-agent: specifier: workspace:* version: link:../agents/weather @@ -3416,13 +3415,13 @@ importers: version: 0.1.26 '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../../actionSchema '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils @@ -3431,22 +3430,22 @@ importers: version: link:../types action-grammar: specifier: workspace:* - version: file:packages/actionGrammar(zod@4.1.13) + version: link:../../actionGrammar agent-cache: specifier: workspace:* - version: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../cache aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + version: link:../../aiclient azure-ai-foundry: specifier: workspace:* - version: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13) + version: link:../../azure-ai-foundry chalk: specifier: ^5.4.1 version: 5.6.2 conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../memory/conversation debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) @@ -3464,13 +3463,13 @@ importers: version: 9.0.5 image-memory: specifier: workspace:* - version: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../memory/image knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../knowPro open: specifier: ^10.1.0 version: 10.1.2 @@ -3482,19 +3481,19 @@ importers: version: 7.2.0 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@4.1.13) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@4.1.13) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../utils/typechatUtils website-memory: specifier: workspace:* - version: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) + version: link:../../memory/website zod: specifier: ^4.1.13 version: 4.1.13 @@ -3540,16 +3539,16 @@ importers: version: 1.5.13 '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/common-utils': specifier: workspace:* version: link:../../utils/commonUtils agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher debug: specifier: ^4.4.0 version: 4.4.3(supports-color@8.1.1) @@ -3577,10 +3576,10 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk '@typeagent/dispatcher-types': specifier: workspace:* version: link:../types @@ -3599,7 +3598,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk devDependencies: prettier: specifier: ^3.5.3 @@ -3628,7 +3627,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient async: specifier: ^3.2.5 version: 3.2.6 @@ -3640,10 +3639,10 @@ importers: version: 3.0.0 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../knowledgeProcessor typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3686,22 +3685,22 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../memory/conversation interactive-app: specifier: workspace:* version: link:../interactiveApp knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../knowPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat: specifier: 0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3729,7 +3728,7 @@ importers: version: 2.0.0-beta.3 aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient debug: specifier: ^4.4.0 version: 4.4.1 @@ -3741,13 +3740,13 @@ importers: version: 0.33.5 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../utils/typechatUtils devDependencies: '@types/debug': specifier: ^4.1.12 @@ -3778,7 +3777,7 @@ importers: dependencies: agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/dispatcher chalk: specifier: ^5.4.1 version: 5.6.2 @@ -3833,7 +3832,7 @@ importers: version: 0.2.12(zod@4.1.13) aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) + version: link:../aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -3864,13 +3863,13 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.2.12 - version: 0.2.12(zod@4.1.13) + version: 0.2.12(zod@4.3.6) '@anthropic-ai/sdk': specifier: ^0.35.0 version: 0.35.0(encoding@0.1.13) aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.9.3) + version: link:../../aiclient dotenv: specifier: ^16.4.5 version: 16.5.0 @@ -3892,7 +3891,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient async: specifier: ^3.2.5 version: 3.2.6 @@ -3901,22 +3900,22 @@ importers: version: 4.4.1 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowPro mailparser: specifier: 3.9.0 version: 3.9.0 memory-storage: specifier: workspace:* - version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../storage textpro: specifier: workspace:* version: link:../../textPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -3968,7 +3967,7 @@ importers: version: 2.0.0-beta.3 aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -3980,19 +3979,19 @@ importers: version: 5.0.0 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowPro memory-storage: specifier: workspace:* - version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../storage telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4029,7 +4028,7 @@ importers: version: 12.1.0 aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -4038,13 +4037,13 @@ importers: version: 4.4.1 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowPro typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent devDependencies: '@types/better-sqlite3': specifier: 7.6.13 @@ -4084,7 +4083,7 @@ importers: version: link:../../utils/commonUtils aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient better-sqlite3: specifier: 12.6.2 version: 12.6.2 @@ -4093,7 +4092,7 @@ importers: version: 1.1.0 conversation-memory: specifier: workspace:* - version: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../conversation debug: specifier: ^4.4.0 version: 4.4.1 @@ -4120,19 +4119,19 @@ importers: version: 26.1.0 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowledgeProcessor knowpro: specifier: workspace:* - version: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../../knowPro memory-storage: specifier: workspace:* - version: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../storage telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4175,13 +4174,13 @@ importers: dependencies: '@typeagent/action-schema': specifier: workspace:* - version: file:packages/actionSchema + version: link:../actionSchema aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@3.25.76) + version: link:../typeagent typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4209,13 +4208,13 @@ importers: version: 3.0.2(electron@40.6.0) '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../agentRpc '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../agentSdk '@typeagent/agent-server-client': specifier: workspace:* - version: file:packages/agentServer/client(ws@8.18.2) + version: link:../agentServer/client '@typeagent/common-utils': specifier: workspace:* version: link:../utils/commonUtils @@ -4224,25 +4223,25 @@ importers: version: link:../dispatcher/rpc agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/dispatcher aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) + version: link:../aiclient ansi_up: specifier: ^6.0.2 version: 6.0.5 browser-typeagent: specifier: workspace:* - version: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) + version: link:../agents/browser debug: specifier: ^4.4.0 version: 4.4.1 default-agent-provider: specifier: workspace:* - version: file:packages/defaultAgentProvider(puppeteer-core@24.37.5)(typescript@5.4.5) + version: link:../defaultAgentProvider dispatcher-node-providers: specifier: workspace:* - version: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.18.2) + version: link:../dispatcher/nodeProviders dompurify: specifier: ^3.2.5 version: 3.2.5 @@ -4263,16 +4262,16 @@ importers: version: 1.43.1 typeagent: specifier: workspace:* - version: file:packages/typeagent(zod@4.3.6) + version: link:../typeagent typechat: specifier: ^0.1.1 - version: 0.1.1(typescript@5.4.5)(zod@4.3.6) + version: 0.1.1(typescript@5.4.5)(zod@3.25.76) typechat-utils: specifier: workspace:* - version: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) + version: link:../utils/typechatUtils websocket-utils: specifier: workspace:* - version: file:packages/utils/webSocketUtils + version: link:../utils/webSocketUtils ws: specifier: ^8.17.1 version: 8.18.2 @@ -4379,7 +4378,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4413,7 +4412,7 @@ importers: version: 1.0.0-rc.12 knowledge-processor: specifier: workspace:* - version: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) + version: link:../knowledgeProcessor marked: specifier: 16.0.0 version: 16.0.0 @@ -4432,7 +4431,7 @@ importers: dependencies: aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../aiclient async: specifier: ^3.2.5 version: 3.2.6 @@ -4478,7 +4477,7 @@ importers: version: link:../agentServer/client agent-dispatcher: specifier: workspace:* - version: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) + version: link:../dispatcher/dispatcher devDependencies: '@types/debug': specifier: ^4.1.12 @@ -4506,7 +4505,7 @@ importers: dependencies: '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk chalk: specifier: ^5.4.1 version: 5.6.2 @@ -4540,10 +4539,10 @@ importers: version: 2.0.0-beta.3 '@typeagent/agent-sdk': specifier: workspace:* - version: file:packages/agentSdk + version: link:../../agentSdk aiclient: specifier: workspace:* - version: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) + version: link:../../aiclient chalk: specifier: ^5.4.1 version: 5.6.2 @@ -4558,7 +4557,7 @@ importers: version: 4.30.1 telemetry: specifier: workspace:* - version: file:packages/telemetry(socks@2.8.7) + version: link:../../telemetry typechat: specifier: ^0.1.1 version: 0.1.1(typescript@5.4.5)(zod@3.25.76) @@ -4586,7 +4585,7 @@ importers: dependencies: '@typeagent/agent-rpc': specifier: workspace:* - version: file:packages/agentRpc + version: link:../../agentRpc '@typeagent/common-utils': specifier: workspace:* version: link:../commonUtils @@ -7224,35 +7223,6 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@typeagent/action-schema-compiler@file:packages/actionSchemaCompiler': - resolution: {directory: packages/actionSchemaCompiler, type: directory} - hasBin: true - - '@typeagent/action-schema@file:packages/actionSchema': - resolution: {directory: packages/actionSchema, type: directory} - engines: {node: '>=20'} - - '@typeagent/agent-rpc@file:packages/agentRpc': - resolution: {directory: packages/agentRpc, type: directory} - - '@typeagent/agent-sdk@file:packages/agentSdk': - resolution: {directory: packages/agentSdk, type: directory} - - '@typeagent/agent-server-client@file:packages/agentServer/client': - resolution: {directory: packages/agentServer/client, type: directory} - - '@typeagent/agent-server-protocol@file:packages/agentServer/protocol': - resolution: {directory: packages/agentServer/protocol, type: directory} - - '@typeagent/common-utils@file:packages/utils/commonUtils': - resolution: {directory: packages/utils/commonUtils, type: directory} - - '@typeagent/dispatcher-rpc@file:packages/dispatcher/rpc': - resolution: {directory: packages/dispatcher/rpc, type: directory} - - '@typeagent/dispatcher-types@file:packages/dispatcher/types': - resolution: {directory: packages/dispatcher/types, type: directory} - '@types/async@3.2.24': resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} @@ -7951,14 +7921,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - action-grammar-compiler@file:packages/actionGrammarCompiler: - resolution: {directory: packages/actionGrammarCompiler, type: directory} - hasBin: true - - action-grammar@file:packages/actionGrammar: - resolution: {directory: packages/actionGrammar, type: directory} - hasBin: true - agent-base@5.1.1: resolution: {integrity: sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==} engines: {node: '>= 6.0.0'} @@ -7971,12 +7933,6 @@ packages: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} - agent-cache@file:packages/cache: - resolution: {directory: packages/cache, type: directory} - - agent-dispatcher@file:packages/dispatcher/dispatcher: - resolution: {directory: packages/dispatcher/dispatcher, type: directory} - agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -7985,10 +7941,6 @@ packages: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} - aiclient@file:packages/aiclient: - resolution: {directory: packages/aiclient, type: directory} - engines: {node: '>=20'} - ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -8021,9 +7973,6 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - android-mobile-agent@file:packages/agents/androidMobile: - resolution: {directory: packages/agents/androidMobile, type: directory} - ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -8200,10 +8149,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - azure-ai-foundry@file:packages/azure-ai-foundry: - resolution: {directory: packages/azure-ai-foundry, type: directory} - engines: {node: '>=20'} - azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} @@ -8359,9 +8304,6 @@ packages: browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - browser-typeagent@file:packages/agents/browser: - resolution: {directory: packages/agents/browser, type: directory} - browserslist@4.24.5: resolution: {integrity: sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -8458,9 +8400,6 @@ packages: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} engines: {node: '>=8'} - calendar@file:packages/agents/calendar: - resolution: {directory: packages/agents/calendar, type: directory} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -8529,12 +8468,6 @@ packages: chardet@2.1.0: resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} - chat-agent@file:packages/agents/chat: - resolution: {directory: packages/agents/chat, type: directory} - - chat-ui@file:packages/chat-ui: - resolution: {directory: packages/chat-ui, type: directory} - cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -8675,23 +8608,13 @@ packages: resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} engines: {node: '>=16'} - code-agent@file:packages/agents/code: - resolution: {directory: packages/agents/code, type: directory} - code-excerpt@4.0.0: resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - code-processor@file:packages/codeProcessor: - resolution: {directory: packages/codeProcessor, type: directory} - codemirror@6.0.1: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} - coder-wrapper@file:packages/coderWrapper: - resolution: {directory: packages/coderWrapper, type: directory} - hasBin: true - collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} @@ -8814,9 +8737,6 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - conversation-memory@file:packages/memory/conversation: - resolution: {directory: packages/memory/conversation, type: directory} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -9226,9 +9146,6 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - default-agent-provider@file:packages/defaultAgentProvider: - resolution: {directory: packages/defaultAgentProvider, type: directory} - default-browser-id@5.0.0: resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} engines: {node: '>=18'} @@ -9284,9 +9201,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - desktop-automation@file:packages/agents/desktop: - resolution: {directory: packages/agents/desktop, type: directory} - destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -9339,9 +9253,6 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dispatcher-node-providers@file:packages/dispatcher/nodeProviders: - resolution: {directory: packages/dispatcher/nodeProviders, type: directory} - dmg-builder@26.8.1: resolution: {integrity: sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg==} @@ -9464,9 +9375,6 @@ packages: engines: {node: '>= 12.20.55'} hasBin: true - email@file:packages/agents/email: - resolution: {directory: packages/agents/email, type: directory} - emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -9675,9 +9583,6 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - examples-lib@file:examples/examplesLib: - resolution: {directory: examples/examplesLib, type: directory} - execa@1.0.0: resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} engines: {node: '>=6'} @@ -10138,10 +10043,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graph-utils@file:packages/agents/agentUtils/graphUtils: - resolution: {directory: packages/agents/agentUtils/graphUtils, type: directory} - engines: {node: '>=20'} - graphlib@2.1.8: resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} @@ -10193,9 +10094,6 @@ packages: peerDependencies: graphology-types: '>=0.24.0' - greeting-agent@file:packages/agents/greeting: - resolution: {directory: packages/agents/greeting, type: directory} - gtoken@7.1.0: resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} engines: {node: '>=14.0.0'} @@ -10412,12 +10310,6 @@ packages: resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} engines: {node: '>= 4'} - image-agent@file:packages/agents/image: - resolution: {directory: packages/agents/image, type: directory} - - image-memory@file:packages/memory/image: - resolution: {directory: packages/memory/image, type: directory} - image-size@0.5.5: resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} engines: {node: '>=0.10.0'} @@ -10484,10 +10376,6 @@ packages: react-devtools-core: optional: true - interactive-app@file:packages/interactiveApp: - resolution: {directory: packages/interactiveApp, type: directory} - engines: {node: '>=20'} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -11122,25 +11010,12 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - knowledge-processor@file:packages/knowledgeProcessor: - resolution: {directory: packages/knowledgeProcessor, type: directory} - - knowpro-test@file:packages/knowProTest: - resolution: {directory: packages/knowProTest, type: directory} - - knowpro@file:packages/knowPro: - resolution: {directory: packages/knowPro, type: directory} - koffi@2.11.0: resolution: {integrity: sha512-AJ6MHz9Z8OIftKu322jrKJFvy/rZTdCD4b7F457WrK71rxYV7O5PSdWsJDN0p3rY1BZaPeLHVwyt4i2Xyk8wJg==} kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - kp@file:packages/kp: - resolution: {directory: packages/kp, type: directory} - hasBin: true - langium@3.3.1: resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} engines: {node: '>=16.0.0'} @@ -11266,9 +11141,6 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - list-agent@file:packages/agents/list: - resolution: {directory: packages/agents/list, type: directory} - loader-runner@4.3.1: resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} @@ -11423,9 +11295,6 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - markdown-agent@file:packages/agents/markdown: - resolution: {directory: packages/agents/markdown, type: directory} - markdown-it-texmath@1.0.0: resolution: {integrity: sha512-4hhkiX8/gus+6e53PLCUmUrsa6ZWGgJW2XCW6O0ASvZUiezIK900ZicinTDtG3kAO2kon7oUA/ReWmpW2FByxg==} @@ -11529,12 +11398,6 @@ packages: memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - memory-providers@file:examples/memoryProviders: - resolution: {directory: examples/memoryProviders, type: directory} - - memory-storage@file:packages/memory/storage: - resolution: {directory: packages/memory/storage, type: directory} - merge-deep@3.0.3: resolution: {integrity: sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==} engines: {node: '>=0.10.0'} @@ -11832,9 +11695,6 @@ packages: socks: optional: true - montage-agent@file:packages/agents/montage: - resolution: {directory: packages/agents/montage, type: directory} - ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -11849,12 +11709,6 @@ packages: resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} engines: {node: '>=10'} - music-local@file:packages/agents/playerLocal: - resolution: {directory: packages/agents/playerLocal, type: directory} - - music@file:packages/agents/player: - resolution: {directory: packages/agents/player, type: directory} - mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -12327,9 +12181,6 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - photo-agent@file:packages/agents/photo: - resolution: {directory: packages/agents/photo, type: directory} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -13084,9 +12935,6 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - settings-agent@file:packages/agents/settings: - resolution: {directory: packages/agents/settings, type: directory} - shallow-clone@0.1.2: resolution: {integrity: sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==} engines: {node: '>=0.10.0'} @@ -13264,9 +13112,6 @@ packages: resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} engines: {node: '>=6.0.0'} - spelunker-agent@file:packages/agents/spelunker: - resolution: {directory: packages/agents/spelunker, type: directory} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -13470,12 +13315,6 @@ packages: engines: {node: '>=18'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - taskflow-typeagent@file:packages/agents/taskflow: - resolution: {directory: packages/agents/taskflow, type: directory} - - telemetry@file:packages/telemetry: - resolution: {directory: packages/telemetry, type: directory} - temp-file@3.4.0: resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} @@ -13521,9 +13360,6 @@ packages: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} engines: {node: '>=18'} - test-lib@file:packages/testLib: - resolution: {directory: packages/testLib, type: directory} - text-decoder@1.2.1: resolution: {integrity: sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==} @@ -13534,10 +13370,6 @@ packages: resolution: {integrity: sha512-7D/r3s6uPZyU//MCYrX6I14nzauDwJ5CxazouuRGNuvSCihW87ufN6VLoROLCrHg6FblLuJrT6N2BVaPVzqElw==} engines: {node: '>=0.8'} - textpro@file:packages/textPro: - resolution: {directory: packages/textPro, type: directory} - engines: {node: '>=20'} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -13756,13 +13588,6 @@ packages: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} - typeagent@file:packages/typeagent: - resolution: {directory: packages/typeagent, type: directory} - engines: {node: '>=20'} - - typechat-utils@file:packages/utils/typechatUtils: - resolution: {directory: packages/utils/typechatUtils, type: directory} - typechat@0.1.1: resolution: {integrity: sha512-Sw96vmkYqbAahqam7vCp8P/MjIGsR26Odz17UHpVGniYN5ir2B37nRRkoDuRpA5djwNQB+W5TB7w2xoF6kwbHQ==} engines: {node: '>=18'} @@ -13949,9 +13774,6 @@ packages: utila@0.4.0: resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} - utility-typeagent@file:packages/agents/utility: - resolution: {directory: packages/agents/utility, type: directory} - utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -14000,9 +13822,6 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - video-agent@file:packages/agents/video: - resolution: {directory: packages/agents/video, type: directory} - vite@6.4.1: resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -14099,9 +13918,6 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - weather-agent@file:packages/agents/weather: - resolution: {directory: packages/agents/weather, type: directory} - web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -14177,12 +13993,6 @@ packages: webpack-cli: optional: true - website-memory@file:packages/memory/website: - resolution: {directory: packages/memory/website, type: directory} - - websocket-channel-server@file:packages/utils/webSocketChannelServer: - resolution: {directory: packages/utils/webSocketChannelServer, type: directory} - websocket-driver@0.7.4: resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} engines: {node: '>=0.8.0'} @@ -14191,9 +14001,6 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} - websocket-utils@file:packages/utils/webSocketUtils: - resolution: {directory: packages/utils/webSocketUtils, type: directory} - webvtt-parser@2.2.0: resolution: {integrity: sha512-FzmaED+jZyt8SCJPTKbSsimrrnQU8ELlViE1wuF3x1pgiQUM8Llj5XWj2j/s6Tlk71ucPfGSMFqZWBtKn/0uEA==} @@ -15142,105 +14949,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/ai-projects@1.0.0-beta.8(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76)': - dependencies: - '@azure-rest/ai-inference': 1.0.0-beta.6 - '@azure-rest/core-client': 2.4.0 - '@azure/abort-controller': 2.1.2 - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/core-auth': 1.9.0 - '@azure/core-lro': 3.2.0 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.20.0 - '@azure/core-sse': 2.2.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.2.0 - '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 - '@azure/storage-blob': 12.27.0 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.200.0 - '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) - openai: 4.103.0(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76) - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - supports-color - - ws - - zod - - '@azure/ai-projects@1.0.0-beta.8(ws@8.18.2)(zod@4.1.13)': - dependencies: - '@azure-rest/ai-inference': 1.0.0-beta.6 - '@azure-rest/core-client': 2.4.0 - '@azure/abort-controller': 2.1.2 - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/core-auth': 1.9.0 - '@azure/core-lro': 3.2.0 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.20.0 - '@azure/core-sse': 2.2.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.2.0 - '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 - '@azure/storage-blob': 12.27.0 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.200.0 - '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) - openai: 4.103.0(ws@8.18.2)(zod@4.1.13) - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - supports-color - - ws - - zod - - '@azure/ai-projects@1.0.0-beta.8(ws@8.18.2)(zod@4.3.6)': - dependencies: - '@azure-rest/ai-inference': 1.0.0-beta.6 - '@azure-rest/core-client': 2.4.0 - '@azure/abort-controller': 2.1.2 - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/core-auth': 1.9.0 - '@azure/core-lro': 3.2.0 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.20.0 - '@azure/core-sse': 2.2.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.2.0 - '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 - '@azure/storage-blob': 12.27.0 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.200.0 - '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) - openai: 4.103.0(ws@8.18.2)(zod@4.3.6) - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - supports-color - - ws - - zod - '@azure/ai-projects@1.0.0-beta.8(ws@8.19.0)(zod@3.25.76)': dependencies: '@azure-rest/ai-inference': 1.0.0-beta.6 @@ -15307,39 +15015,6 @@ snapshots: - ws - zod - '@azure/ai-projects@1.0.0-beta.8(ws@8.19.0)(zod@4.3.6)': - dependencies: - '@azure-rest/ai-inference': 1.0.0-beta.6 - '@azure-rest/core-client': 2.4.0 - '@azure/abort-controller': 2.1.2 - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/core-auth': 1.9.0 - '@azure/core-lro': 3.2.0 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.20.0 - '@azure/core-sse': 2.2.0 - '@azure/core-tracing': 1.2.0 - '@azure/core-util': 1.11.0 - '@azure/logger': 1.2.0 - '@azure/opentelemetry-instrumentation-azure-sdk': 1.0.0-beta.8 - '@azure/storage-blob': 12.27.0 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.200.0 - '@opentelemetry/exporter-logs-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/exporter-trace-otlp-grpc': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': 0.200.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-langchain': 0.13.0(@opentelemetry/api@1.9.0) - '@traceloop/instrumentation-openai': 0.13.0(@opentelemetry/api@1.9.0) - openai: 4.103.0(ws@8.19.0)(zod@4.3.6) - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - supports-color - - ws - - zod - '@azure/arm-authorization@9.0.0': dependencies: '@azure/core-auth': 1.9.0 @@ -16846,6 +16521,41 @@ snapshots: - supports-color - ts-node + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 20.19.25 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5))': dependencies: '@jest/console': 29.7.0 @@ -17619,19 +17329,7 @@ snapshots: - supports-color - zod - '@modelcontextprotocol/server-filesystem@2025.8.21(zod@4.3.6)': - dependencies: - '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - diff: 5.2.0 - glob: 10.5.0 - minimatch: 10.2.4 - zod-to-json-schema: 3.24.5(zod@4.3.6) - transitivePeerDependencies: - - '@cfworker/json-schema' - - supports-color - - zod - - '@mongodb-js/saslprep@1.2.2': + '@mongodb-js/saslprep@1.2.2': dependencies: sparse-bitfield: 3.0.3 @@ -18616,86 +18314,6 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@typeagent/action-schema-compiler@file:packages/actionSchemaCompiler': - dependencies: - '@oclif/core': 4.8.0 - '@oclif/plugin-help': 6.2.26 - '@typeagent/action-schema': file:packages/actionSchema - transitivePeerDependencies: - - supports-color - - '@typeagent/action-schema@file:packages/actionSchema': - dependencies: - debug: 4.4.3(supports-color@8.1.1) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - '@typeagent/agent-rpc@file:packages/agentRpc': - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - '@typeagent/agent-sdk@file:packages/agentSdk': - dependencies: - debug: 4.4.3(supports-color@8.1.1) - type-fest: 4.41.0 - transitivePeerDependencies: - - supports-color - - '@typeagent/agent-server-client@file:packages/agentServer/client(ws@8.18.2)': - dependencies: - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - debug: 4.4.3(supports-color@8.1.1) - isomorphic-ws: 5.0.0(ws@8.18.2) - transitivePeerDependencies: - - supports-color - - ws - - '@typeagent/agent-server-client@file:packages/agentServer/client(ws@8.19.0)': - dependencies: - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - debug: 4.4.3(supports-color@8.1.1) - isomorphic-ws: 5.0.0(ws@8.19.0) - transitivePeerDependencies: - - supports-color - - ws - - '@typeagent/agent-server-protocol@file:packages/agentServer/protocol': - dependencies: - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - transitivePeerDependencies: - - supports-color - - '@typeagent/common-utils@file:packages/utils/commonUtils': - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - '@typeagent/dispatcher-rpc@file:packages/dispatcher/rpc': - dependencies: - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/dispatcher-types': file:packages/dispatcher/types - transitivePeerDependencies: - - supports-color - - '@typeagent/dispatcher-types@file:packages/dispatcher/types': - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - transitivePeerDependencies: - - supports-color - '@types/async@3.2.24': {} '@types/babel__core@7.20.5': @@ -19554,69 +19172,6 @@ snapshots: acorn@8.15.0: {} - action-grammar-compiler@file:packages/actionGrammarCompiler: - dependencies: - '@oclif/core': 4.8.0 - '@oclif/plugin-help': 6.2.26 - action-grammar: file:packages/actionGrammar(zod@4.1.13) - transitivePeerDependencies: - - supports-color - - zod - - action-grammar-compiler@file:packages/actionGrammarCompiler(zod@3.25.76): - dependencies: - '@oclif/core': 4.8.0 - '@oclif/plugin-help': 6.2.26 - action-grammar: file:packages/actionGrammar(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - zod - - action-grammar-compiler@file:packages/actionGrammarCompiler(zod@4.3.6): - dependencies: - '@oclif/core': 4.8.0 - '@oclif/plugin-help': 6.2.26 - action-grammar: file:packages/actionGrammar(zod@4.3.6) - transitivePeerDependencies: - - supports-color - - zod - - action-grammar@file:packages/actionGrammar(zod@3.25.76): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@3.25.76) - '@typeagent/action-schema': file:packages/actionSchema - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - regexp.escape: 2.0.1 - transitivePeerDependencies: - - supports-color - - zod - - action-grammar@file:packages/actionGrammar(zod@4.1.13): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) - '@typeagent/action-schema': file:packages/actionSchema - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - regexp.escape: 2.0.1 - transitivePeerDependencies: - - supports-color - - zod - - action-grammar@file:packages/actionGrammar(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@typeagent/action-schema': file:packages/actionSchema - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - regexp.escape: 2.0.1 - transitivePeerDependencies: - - supports-color - - zod - agent-base@5.1.1: {} agent-base@6.0.2: @@ -19627,300 +19182,6 @@ snapshots: agent-base@7.1.3: {} - agent-cache@file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - action-grammar: file:packages/actionGrammar(zod@3.25.76) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - async: 3.2.6 - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - regexp.escape: 2.0.1 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - agent-cache@file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - action-grammar: file:packages/actionGrammar(zod@4.1.13) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - async: 3.2.6 - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - regexp.escape: 2.0.1 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - agent-cache@file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - action-grammar: file:packages/actionGrammar(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - async: 3.2.6 - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - regexp.escape: 2.0.1 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - agent-dispatcher@file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.3.6) - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) - '@azure/identity': 4.10.0 - '@github/copilot-sdk': 0.1.26 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-types': file:packages/dispatcher/types - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.3.6) - chalk: 5.6.2 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - html-to-text: 9.0.5 - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-width: 7.2.0 - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - agent-dispatcher@file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) - '@azure/identity': 4.10.0 - '@github/copilot-sdk': 0.1.26 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-types': file:packages/dispatcher/types - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - chalk: 5.6.2 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - html-to-text: 9.0.5 - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-width: 7.2.0 - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - agent-dispatcher@file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.3.6) - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) - '@azure/identity': 4.10.0 - '@github/copilot-sdk': 0.1.26 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-types': file:packages/dispatcher/types - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.3.6) - chalk: 5.6.2 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - html-to-text: 9.0.5 - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-width: 7.2.0 - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - agent-dispatcher@file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/cosmos': 4.9.1(@azure/core-client@1.10.1) - '@azure/identity': 4.10.0 - '@github/copilot-sdk': 0.1.26 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-types': file:packages/dispatcher/types - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - chalk: 5.6.2 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - html-to-text: 9.0.5 - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-width: 7.2.0 - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -19930,50 +19191,6 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - aiclient@file:packages/aiclient(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure/identity': 4.10.0 - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - typescript - - zod - - aiclient@file:packages/aiclient(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@azure/identity': 4.10.0 - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - supports-color - - typescript - - zod - - aiclient@file:packages/aiclient(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@azure/identity': 4.10.0 - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - supports-color - - typescript - - zod - - aiclient@file:packages/aiclient(typescript@5.9.3): - dependencies: - '@azure/identity': 4.10.0 - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.9.3) - transitivePeerDependencies: - - supports-color - - typescript - - zod - ajv-formats@2.1.1(ajv@8.18.0): optionalDependencies: ajv: 8.18.0 @@ -20005,12 +19222,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - android-mobile-agent@file:packages/agents/androidMobile: - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - transitivePeerDependencies: - - supports-color - ansi-colors@4.1.3: {} ansi-escapes@4.3.2: @@ -20223,137 +19434,7 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - azure-ai-foundry@file:packages/azure-ai-foundry(encoding@0.1.13)(typescript@5.4.5)(ws@8.18.2)(zod@3.25.76): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - azure-ai-foundry@file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.3.6): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.3.6) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - azure-ai-foundry@file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.1.13) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - azure-ai-foundry@file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - azure-ai-foundry@file:packages/azure-ai-foundry(typescript@5.4.5)(ws@8.19.0)(zod@3.25.76): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@3.25.76) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - azure-devops-node-api@12.5.0: + azure-devops-node-api@12.5.0: dependencies: tunnel: 0.0.6 typed-rest-client: 1.8.11 @@ -20549,361 +19630,6 @@ snapshots: browser-stdout@1.3.1: {} - browser-typeagent@file:packages/agents/browser(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@mozilla/readability': 0.6.0 - '@popperjs/core': 2.11.8 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13) - bootstrap: 5.3.6(@popperjs/core@2.11.8) - chalk: 5.6.2 - chat-ui: file:packages/chat-ui - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - cytoscape: 3.33.1 - cytoscape-dagre: 2.5.0(cytoscape@3.33.1) - dagre: 0.8.5 - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-layout: 0.6.1(graphology-types@0.24.8) - graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) - graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) - graphology-types: 0.24.8 - html-to-text: 9.0.5 - jsdom: 26.1.0 - jsonpath: 1.2.1 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - markdown-it: 14.1.1 - pdfjs-dist: 5.3.31 - prismjs: 1.30.0 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-adblocker: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - readline: 1.3.0 - sanitize-filename: 1.6.3 - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - websocket-utils: file:packages/utils/webSocketUtils - ws: 8.19.0 - yaml: 2.8.2 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - browser-typeagent@file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5): - dependencies: - '@mozilla/readability': 0.6.0 - '@popperjs/core': 2.11.8 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.1.13) - bootstrap: 5.3.6(@popperjs/core@2.11.8) - chalk: 5.6.2 - chat-ui: file:packages/chat-ui - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - cytoscape: 3.33.1 - cytoscape-dagre: 2.5.0(cytoscape@3.33.1) - dagre: 0.8.5 - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-layout: 0.6.1(graphology-types@0.24.8) - graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) - graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) - graphology-types: 0.24.8 - html-to-text: 9.0.5 - jsdom: 26.1.0 - jsonpath: 1.2.1 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - markdown-it: 14.1.1 - pdfjs-dist: 5.3.31 - prismjs: 1.30.0 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-adblocker: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - readline: 1.3.0 - sanitize-filename: 1.6.3 - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - website-memory: file:packages/memory/website(typescript@5.4.5) - websocket-utils: file:packages/utils/webSocketUtils - ws: 8.19.0 - yaml: 2.8.2 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - browser-typeagent@file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@mozilla/readability': 0.6.0 - '@popperjs/core': 2.11.8 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - bootstrap: 5.3.6(@popperjs/core@2.11.8) - chalk: 5.6.2 - chat-ui: file:packages/chat-ui - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - cytoscape: 3.33.1 - cytoscape-dagre: 2.5.0(cytoscape@3.33.1) - dagre: 0.8.5 - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-layout: 0.6.1(graphology-types@0.24.8) - graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) - graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) - graphology-types: 0.24.8 - html-to-text: 9.0.5 - jsdom: 26.1.0 - jsonpath: 1.2.1 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - markdown-it: 14.1.1 - pdfjs-dist: 5.3.31 - prismjs: 1.30.0 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-adblocker: 2.13.6(encoding@0.1.13)(puppeteer-core@24.37.5)(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - readline: 1.3.0 - sanitize-filename: 1.6.3 - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) - websocket-utils: file:packages/utils/webSocketUtils - ws: 8.19.0 - yaml: 2.8.2 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - browser-typeagent@file:packages/agents/browser(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@mozilla/readability': 0.6.0 - '@popperjs/core': 2.11.8 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - bootstrap: 5.3.6(@popperjs/core@2.11.8) - chalk: 5.6.2 - chat-ui: file:packages/chat-ui - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - cytoscape: 3.33.1 - cytoscape-dagre: 2.5.0(cytoscape@3.33.1) - dagre: 0.8.5 - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-layout: 0.6.1(graphology-types@0.24.8) - graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) - graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) - graphology-types: 0.24.8 - html-to-text: 9.0.5 - jsdom: 26.1.0 - jsonpath: 1.2.1 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - markdown-it: 14.1.1 - pdfjs-dist: 5.3.31 - prismjs: 1.30.0 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-adblocker: 2.13.6(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - readline: 1.3.0 - sanitize-filename: 1.6.3 - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - websocket-utils: file:packages/utils/webSocketUtils - ws: 8.19.0 - yaml: 2.8.2 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - browser-typeagent@file:packages/agents/browser(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@mozilla/readability': 0.6.0 - '@popperjs/core': 2.11.8 - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-protocol': file:packages/agentServer/protocol - '@typeagent/common-utils': file:packages/utils/commonUtils - '@typeagent/dispatcher-rpc': file:packages/dispatcher/rpc - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - azure-ai-foundry: file:packages/azure-ai-foundry(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - bootstrap: 5.3.6(@popperjs/core@2.11.8) - chalk: 5.6.2 - chat-ui: file:packages/chat-ui - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - cytoscape: 3.33.1 - cytoscape-dagre: 2.5.0(cytoscape@3.33.1) - dagre: 0.8.5 - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-layout: 0.6.1(graphology-types@0.24.8) - graphology-layout-forceatlas2: 0.10.1(graphology-types@0.24.8) - graphology-layout-noverlap: 0.4.2(graphology-types@0.24.8) - graphology-types: 0.24.8 - html-to-text: 9.0.5 - jsdom: 26.1.0 - jsonpath: 1.2.1 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - markdown-it: 14.1.1 - pdfjs-dist: 5.3.31 - prismjs: 1.30.0 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-adblocker: 2.13.6(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - readline: 1.3.0 - sanitize-filename: 1.6.3 - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - website-memory: file:packages/memory/website(typescript@5.4.5)(zod@4.3.6) - websocket-utils: file:packages/utils/webSocketUtils - ws: 8.19.0 - yaml: 2.8.2 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - browserslist@4.24.5: dependencies: caniuse-lite: 1.0.30001718 @@ -21052,54 +19778,6 @@ snapshots: normalize-url: 6.1.0 responselike: 2.0.1 - calendar@file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.1.13) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@azure/msal-browser' - - '@mongodb-js/zstd' - - buffer - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - stream-browserify - - supports-color - - typescript - - zod - - calendar@file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@azure/msal-browser' - - '@mongodb-js/zstd' - - buffer - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - stream-browserify - - supports-color - - typescript - - zod - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -21161,93 +19839,6 @@ snapshots: chardet@2.1.0: {} - chat-agent@file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.18.2)(zod@4.1.13) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - chat-agent@file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@4.3.6) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - chat-agent@file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure/ai-agents': 1.0.0-beta.3 - '@azure/ai-projects': 1.0.0-beta.8(ws@8.19.0)(zod@3.25.76) - '@azure/identity': 4.10.0 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - ws - - zod - - chat-ui@file:packages/chat-ui: - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - ansi_up: 6.0.5 - dompurify: 3.2.6 - markdown-it: 14.1.1 - transitivePeerDependencies: - - supports-color - cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -21419,85 +20010,10 @@ snapshots: cockatiel@3.2.1: {} - code-agent@file:packages/agents/code(socks@2.8.7): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - better-sqlite3: 12.6.2 - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - telemetry: file:packages/telemetry(socks@2.8.7) - websocket-utils: file:packages/utils/webSocketUtils - ws: 8.19.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - utf-8-validate - code-excerpt@4.0.0: dependencies: convert-to-spaces: 2.0.1 - code-processor@file:packages/codeProcessor(socks@2.8.7)(zod@3.25.76): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - typescript: 5.4.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - zod - - code-processor@file:packages/codeProcessor(socks@2.8.7)(zod@4.1.13): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typescript: 5.4.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - zod - - code-processor@file:packages/codeProcessor(socks@2.8.7)(zod@4.3.6): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typescript: 5.4.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - zod - codemirror@6.0.1: dependencies: '@codemirror/autocomplete': 6.18.6 @@ -21508,18 +20024,6 @@ snapshots: '@codemirror/state': 6.5.2 '@codemirror/view': 6.37.2 - coder-wrapper@file:packages/coderWrapper(ws@8.19.0)(zod@3.25.76): - dependencies: - '@modelcontextprotocol/sdk': 1.26.0(zod@3.25.76) - '@typeagent/agent-server-client': file:packages/agentServer/client(ws@8.19.0) - '@typeagent/dispatcher-types': file:packages/dispatcher/types - node-pty: 1.1.0 - transitivePeerDependencies: - - '@cfworker/json-schema' - - supports-color - - ws - - zod - collect-v8-coverage@1.0.2: {} color-convert@1.9.3: @@ -21642,81 +20146,6 @@ snapshots: content-type@1.0.5: {} - conversation-memory@file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - mailparser: 3.9.0 - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - webvtt-parser: 2.2.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - conversation-memory@file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - mailparser: 3.9.0 - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - webvtt-parser: 2.2.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - conversation-memory@file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - mailparser: 3.9.0 - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - textpro: file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - webvtt-parser: 2.2.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - convert-source-map@2.0.0: {} convert-to-spaces@2.0.1: {} @@ -21803,13 +20232,28 @@ snapshots: dependencies: buffer: 5.7.1 - create-jest@29.7.0(@types/node@20.19.23)(ts-node@10.9.2(@types/node@20.19.23)(typescript@5.4.5)): + create-jest@29.7.0(@types/node@20.19.23)(ts-node@10.9.2(@types/node@20.19.23)(typescript@5.4.5)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.19.23)(ts-node@10.9.2(@types/node@20.19.23)(typescript@5.4.5)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + create-jest@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.19.23)(ts-node@10.9.2(@types/node@20.19.23)(typescript@5.4.5)) + jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -21923,11 +20367,6 @@ snapshots: cytoscape: 3.32.0 dagre: 0.8.5 - cytoscape-dagre@2.5.0(cytoscape@3.33.1): - dependencies: - cytoscape: 3.33.1 - dagre: 0.8.5 - cytoscape-fcose@2.2.0(cytoscape@3.33.1): dependencies: cose-base: 2.2.0 @@ -22193,219 +20632,6 @@ snapshots: deepmerge@4.3.1: {} - default-agent-provider@file:packages/defaultAgentProvider(puppeteer-core@24.37.5)(typescript@5.4.5): - dependencies: - '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@modelcontextprotocol/server-filesystem': 2025.8.21(zod@4.3.6) - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - android-mobile-agent: file:packages/agents/androidMobile - browser-typeagent: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) - calendar: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - chalk: 5.6.2 - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - code-agent: file:packages/agents/code(socks@2.8.7) - debug: 4.4.3(supports-color@8.1.1) - desktop-automation: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - dispatcher-node-providers: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.19.0) - email: file:packages/agents/email(typescript@5.4.5)(zod@4.3.6) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - greeting-agent: file:packages/agents/greeting(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - image-agent: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - list-agent: file:packages/agents/list - markdown-agent: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - montage-agent: file:packages/agents/montage(typescript@5.4.5)(zod@4.3.6) - music: file:packages/agents/player(typescript@5.4.5)(zod@4.3.6) - music-local: file:packages/agents/playerLocal - photo-agent: file:packages/agents/photo - proper-lockfile: 4.1.2 - settings-agent: file:packages/agents/settings(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - spelunker-agent: file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6) - string-width: 7.2.0 - taskflow-typeagent: file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - utility-typeagent: file:packages/agents/utility(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) - video-agent: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - weather-agent: file:packages/agents/weather - ws: 8.19.0 - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@azure/msal-browser' - - '@cfworker/json-schema' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - stream-browserify - - supports-color - - typescript - - utf-8-validate - - default-agent-provider@file:packages/defaultAgentProvider(socks@2.8.7)(typescript@5.4.5): - dependencies: - '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@modelcontextprotocol/server-filesystem': 2025.8.21(zod@4.3.6) - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - android-mobile-agent: file:packages/agents/androidMobile - browser-typeagent: file:packages/agents/browser(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - calendar: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - chalk: 5.6.2 - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - code-agent: file:packages/agents/code(socks@2.8.7) - debug: 4.4.3(supports-color@8.1.1) - desktop-automation: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - dispatcher-node-providers: file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) - email: file:packages/agents/email(typescript@5.4.5)(zod@4.3.6) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - greeting-agent: file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - image-agent: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - list-agent: file:packages/agents/list - markdown-agent: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - montage-agent: file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - music: file:packages/agents/player(typescript@5.4.5)(zod@4.3.6) - music-local: file:packages/agents/playerLocal - photo-agent: file:packages/agents/photo - proper-lockfile: 4.1.2 - settings-agent: file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - spelunker-agent: file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6) - string-width: 7.2.0 - taskflow-typeagent: file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - utility-typeagent: file:packages/agents/utility(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - video-agent: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - weather-agent: file:packages/agents/weather - ws: 8.19.0 - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@azure/msal-browser' - - '@cfworker/json-schema' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - stream-browserify - - supports-color - - typescript - - utf-8-validate - - default-agent-provider@file:packages/defaultAgentProvider(typescript@5.4.5): - dependencies: - '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@modelcontextprotocol/server-filesystem': 2025.8.21(zod@4.3.6) - '@typeagent/action-schema': file:packages/actionSchema - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - action-grammar: file:packages/actionGrammar(zod@4.3.6) - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - android-mobile-agent: file:packages/agents/androidMobile - browser-typeagent: file:packages/agents/browser(typescript@5.4.5)(zod@4.3.6) - calendar: file:packages/agents/calendar(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - chalk: 5.6.2 - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - code-agent: file:packages/agents/code(socks@2.8.7) - debug: 4.4.3(supports-color@8.1.1) - desktop-automation: file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - dispatcher-node-providers: file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.19.0) - email: file:packages/agents/email(typescript@5.4.5)(zod@4.3.6) - exifreader: 4.30.1 - file-size: 1.0.0 - glob: 13.0.6 - greeting-agent: file:packages/agents/greeting(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - image-agent: file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - list-agent: file:packages/agents/list - markdown-agent: file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - montage-agent: file:packages/agents/montage(typescript@5.4.5)(zod@4.3.6) - music: file:packages/agents/player(typescript@5.4.5)(zod@4.3.6) - music-local: file:packages/agents/playerLocal - photo-agent: file:packages/agents/photo - proper-lockfile: 4.1.2 - settings-agent: file:packages/agents/settings(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - spelunker-agent: file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6) - string-width: 7.2.0 - taskflow-typeagent: file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - utility-typeagent: file:packages/agents/utility(typescript@5.4.5)(zod@4.3.6) - video-agent: file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - weather-agent: file:packages/agents/weather - ws: 8.19.0 - zod: 4.3.6 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@azure/msal-browser' - - '@cfworker/json-schema' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - stream-browserify - - supports-color - - typescript - - utf-8-validate - default-browser-id@5.0.0: {} default-browser@5.2.1: @@ -22457,64 +20683,6 @@ snapshots: dequal@2.0.3: {} - desktop-automation@file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - body-parser: 1.20.3 - chalk: 5.6.2 - cors: 2.8.5 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - ws: 8.19.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - desktop-automation@file:packages/agents/desktop(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - agent-cache: file:packages/cache(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - body-parser: 1.20.3 - chalk: 5.6.2 - cors: 2.8.5 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - ws: 8.19.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - destroy@1.2.0: {} detect-indent@6.1.0: {} @@ -22550,102 +20718,6 @@ snapshots: dependencies: path-type: 4.0.0 - dispatcher-node-providers@file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2): - dependencies: - '@azure/msal-node-extensions': 1.5.13 - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - dispatcher-node-providers@file:packages/dispatcher/nodeProviders(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0): - dependencies: - '@azure/msal-node-extensions': 1.5.13 - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - dispatcher-node-providers@file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.18.2): - dependencies: - '@azure/msal-node-extensions': 1.5.13 - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.18.2) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - dispatcher-node-providers@file:packages/dispatcher/nodeProviders(typescript@5.4.5)(ws@8.19.0): - dependencies: - '@azure/msal-node-extensions': 1.5.13 - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - dmg-builder@26.8.1(electron-builder-squirrel-windows@26.8.1): dependencies: app-builder-lib: 26.8.1(dmg-builder@26.8.1)(electron-builder-squirrel-windows@26.8.1) @@ -22847,44 +20919,6 @@ snapshots: transitivePeerDependencies: - supports-color - email@file:packages/agents/email(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.1.13) - kp: file:packages/kp(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - transitivePeerDependencies: - - '@azure/msal-browser' - - buffer - - encoding - - stream-browserify - - supports-color - - typescript - - zod - - email@file:packages/agents/email(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - graph-utils: file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.3.6) - kp: file:packages/kp(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - transitivePeerDependencies: - - '@azure/msal-browser' - - buffer - - encoding - - stream-browserify - - supports-color - - typescript - - zod - emittery@0.13.1: {} emoji-regex@10.4.0: {} @@ -23151,48 +21185,6 @@ snapshots: dependencies: eventsource-parser: 3.0.6 - examples-lib@file:examples/examplesLib(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - chalk: 5.6.2 - dotenv: 16.5.0 - interactive-app: file:packages/interactiveApp - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - examples-lib@file:examples/examplesLib(typescript@5.4.5)(zod@4.1.13): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chalk: 5.6.2 - dotenv: 16.5.0 - interactive-app: file:packages/interactiveApp - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - execa@1.0.0: dependencies: cross-spawn: 6.0.6 @@ -23836,114 +21828,6 @@ snapshots: graceful-fs@4.2.11: {} - graph-utils@file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5): - dependencies: - '@azure/identity': 4.10.0 - '@azure/identity-cache-persistence': 1.2.0 - '@azure/logger': 1.3.0 - '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - googleapis: 144.0.0(encoding@0.1.13) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-compare: 1.1.2 - typeagent: file:packages/typeagent(zod@4.1.13) - transitivePeerDependencies: - - '@azure/msal-browser' - - buffer - - encoding - - stream-browserify - - supports-color - - typescript - - zod - - graph-utils@file:packages/agents/agentUtils/graphUtils(encoding@0.1.13)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure/identity': 4.10.0 - '@azure/identity-cache-persistence': 1.2.0 - '@azure/logger': 1.3.0 - '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - googleapis: 144.0.0(encoding@0.1.13) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-compare: 1.1.2 - typeagent: file:packages/typeagent(zod@3.25.76) - transitivePeerDependencies: - - '@azure/msal-browser' - - buffer - - encoding - - stream-browserify - - supports-color - - typescript - - zod - - graph-utils@file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@azure/identity': 4.10.0 - '@azure/identity-cache-persistence': 1.2.0 - '@azure/logger': 1.3.0 - '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - googleapis: 144.0.0(encoding@0.1.13) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-compare: 1.1.2 - typeagent: file:packages/typeagent(zod@4.1.13) - transitivePeerDependencies: - - '@azure/msal-browser' - - buffer - - encoding - - stream-browserify - - supports-color - - typescript - - zod - - graph-utils@file:packages/agents/agentUtils/graphUtils(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@azure/identity': 4.10.0 - '@azure/identity-cache-persistence': 1.2.0 - '@azure/logger': 1.3.0 - '@microsoft/microsoft-graph-client': 3.0.7(@azure/identity@4.10.0) - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - googleapis: 144.0.0(encoding@0.1.13) - open: 10.1.2 - proper-lockfile: 4.1.2 - string-compare: 1.1.2 - typeagent: file:packages/typeagent(zod@4.3.6) - transitivePeerDependencies: - - '@azure/msal-browser' - - buffer - - encoding - - stream-browserify - - supports-color - - typescript - - zod - graphlib@2.1.8: dependencies: lodash: 4.17.23 @@ -23993,106 +21877,19 @@ snapshots: graphology-indices: 0.17.0(graphology-types@0.24.8) graphology-types: 0.24.8 graphology-utils: 2.5.2(graphology-types@0.24.8) - mnemonist: 0.39.8 - - graphology-types@0.24.8: {} - - graphology-utils@2.5.2(graphology-types@0.24.8): - dependencies: - graphology-types: 0.24.8 - - graphology@0.25.4(graphology-types@0.24.8): - dependencies: - events: 3.3.0 - graphology-types: 0.24.8 - obliterator: 2.0.5 - - greeting-agent@file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - zod - - greeting-agent@file:packages/agents/greeting(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - zod + mnemonist: 0.39.8 + + graphology-types@0.24.8: {} - greeting-agent@file:packages/agents/greeting(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): + graphology-utils@2.5.2(graphology-types@0.24.8): dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - zod + graphology-types: 0.24.8 + + graphology@0.25.4(graphology-types@0.24.8): + dependencies: + events: 3.3.0 + graphology-types: 0.24.8 + obliterator: 2.0.5 gtoken@7.1.0(encoding@0.1.13): dependencies: @@ -24344,120 +22141,6 @@ snapshots: ignore@7.0.4: {} - image-agent@file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - image-agent@file:packages/agents/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - image-memory@file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - get-folder-size: 5.0.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - image-memory@file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - get-folder-size: 5.0.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - image-memory@file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - get-folder-size: 5.0.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - image-size@0.5.5: optional: true @@ -24538,10 +22221,6 @@ snapshots: - bufferutil - utf-8-validate - interactive-app@file:packages/interactiveApp: - dependencies: - string-width: 7.2.0 - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -24897,6 +22576,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@29.7.0(@types/node@22.15.18)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)) @@ -24997,6 +22695,37 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): + dependencies: + '@babel/core': 7.28.4 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.28.4) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 20.19.25 + ts-node: 10.9.2(@types/node@20.19.25)(typescript@5.4.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@babel/core': 7.28.4 @@ -25369,6 +23098,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@20.19.25)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@29.7.0(@types/node@22.15.18)(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5)) @@ -25594,184 +23335,10 @@ snapshots: kleur@3.0.3: {} - knowledge-processor@file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - sharp: 0.33.5 - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - knowledge-processor@file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - sharp: 0.33.5 - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - knowledge-processor@file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - sharp: 0.33.5 - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - knowpro-test@file:packages/knowProTest(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - interactive-app: file:packages/interactiveApp - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - knowpro@file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - fast-levenshtein: 3.0.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - knowpro@file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - fast-levenshtein: 3.0.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - knowpro@file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - async: 3.2.6 - debug: 4.4.3(supports-color@8.1.1) - fast-levenshtein: 3.0.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - koffi@2.11.0: {} kolorist@1.8.0: {} - kp@file:packages/kp(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - supports-color - - typescript - - zod - - kp@file:packages/kp(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - supports-color - - typescript - - zod - langium@3.3.1: dependencies: chevrotain: 11.0.3 @@ -25922,12 +23489,6 @@ snapshots: dependencies: uc.micro: 2.1.0 - list-agent@file:packages/agents/list: - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - transitivePeerDependencies: - - supports-color - loader-runner@4.3.1: {} local-pkg@1.1.1: @@ -26052,127 +23613,33 @@ snapshots: dependencies: pify: 4.0.1 semver: 5.7.2 - optional: true - - make-dir@4.0.0: - dependencies: - semver: 7.7.3 - - make-error@1.3.6: {} - - make-fetch-happen@14.0.3: - dependencies: - '@npmcli/agent': 3.0.0 - cacache: 19.0.1 - http-cache-semantics: 4.2.0 - minipass: 7.1.2 - minipass-fetch: 4.0.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 1.0.0 - proc-log: 5.0.0 - promise-retry: 2.0.1 - ssri: 12.0.0 - transitivePeerDependencies: - - supports-color - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - - markdown-agent@file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@milkdown/core': 7.13.1 - '@milkdown/crepe': 7.13.1(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(typescript@5.4.5) - '@milkdown/plugin-collab': 7.13.1(y-prosemirror@1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) - '@milkdown/plugin-history': 7.13.1 - '@milkdown/preset-commonmark': 7.13.1 - '@milkdown/preset-gfm': 7.13.1 - '@milkdown/theme-nord': 7.13.1 - '@milkdown/utils': 7.13.1 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - katex: 0.16.22 - lib0: 0.2.117 - markdown-it: 14.1.1 - markdown-it-texmath: 1.0.0 - mermaid: 11.10.0 - prosemirror-inputrules: 1.5.0 - prosemirror-model: 1.25.1 - prosemirror-state: 1.4.3 - prosemirror-view: 1.40.0 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - unist-util-visit: 4.1.2 - ws: 8.19.0 - y-prosemirror: 1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) - y-protocols: 1.0.6(yjs@13.6.27) - y-websocket: 1.5.4(yjs@13.6.27) - yjs: 13.6.27 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - markdown-agent@file:packages/agents/markdown(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@milkdown/core': 7.13.1 - '@milkdown/crepe': 7.13.1(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(typescript@5.4.5) - '@milkdown/plugin-collab': 7.13.1(y-prosemirror@1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27))(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) - '@milkdown/plugin-history': 7.13.1 - '@milkdown/preset-commonmark': 7.13.1 - '@milkdown/preset-gfm': 7.13.1 - '@milkdown/theme-nord': 7.13.1 - '@milkdown/utils': 7.13.1 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - katex: 0.16.22 - lib0: 0.2.117 - markdown-it: 14.1.1 - markdown-it-texmath: 1.0.0 - mermaid: 11.10.0 - prosemirror-inputrules: 1.5.0 - prosemirror-model: 1.25.1 - prosemirror-state: 1.4.3 - prosemirror-view: 1.40.0 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - unist-util-visit: 4.1.2 - ws: 8.19.0 - y-prosemirror: 1.3.5(prosemirror-model@1.25.1)(prosemirror-state@1.4.3)(prosemirror-view@1.40.0)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) - y-protocols: 1.0.6(yjs@13.6.27) - y-websocket: 1.5.4(yjs@13.6.27) - yjs: 13.6.27 + optional: true + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + + make-error@1.3.6: {} + + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.2.0 + minipass: 7.1.2 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - supports-color - - typescript - - utf-8-validate - - zod + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 markdown-it-texmath@1.0.0: {} @@ -26365,90 +23832,6 @@ snapshots: memory-pager@1.5.0: {} - memory-providers@file:examples/memoryProviders(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@elastic/elasticsearch': 8.19.1 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - memory-storage@file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure/search-documents': 12.1.0 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - typeagent: file:packages/typeagent(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - memory-storage@file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@azure/search-documents': 12.1.0 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - typeagent: file:packages/typeagent(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - memory-storage@file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@azure/search-documents': 12.1.0 - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - better-sqlite3: 12.6.2 - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - typeagent: file:packages/typeagent(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - merge-deep@3.0.3: dependencies: arr-union: 3.1.0 @@ -26863,99 +24246,6 @@ snapshots: optionalDependencies: socks: 2.8.7 - montage-agent@file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - body-parser: 1.20.3 - d3: 7.9.0 - d3-cloud: 1.2.7 - debug: 4.4.3(supports-color@8.1.1) - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - koffi: 2.11.0 - sharp: 0.33.5 - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - winreg: 1.2.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - montage-agent@file:packages/agents/montage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - body-parser: 1.20.3 - d3: 7.9.0 - d3-cloud: 1.2.7 - debug: 4.4.3(supports-color@8.1.1) - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - koffi: 2.11.0 - sharp: 0.33.5 - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - winreg: 1.2.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - montage-agent@file:packages/agents/montage(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - body-parser: 1.20.3 - d3: 7.9.0 - d3-cloud: 1.2.7 - debug: 4.4.3(supports-color@8.1.1) - express: 4.22.1 - express-rate-limit: 7.5.0(express@4.22.1) - image-memory: file:packages/memory/image(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - koffi: 2.11.0 - sharp: 0.33.5 - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - winreg: 1.2.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - ms@2.0.0: {} ms@2.1.3: {} @@ -26973,46 +24263,6 @@ snapshots: arrify: 2.0.1 minimatch: 3.1.5 - music-local@file:packages/agents/playerLocal: - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - play-sound: 1.1.6 - transitivePeerDependencies: - - supports-color - - music@file:packages/agents/player(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - express: 4.22.1 - open: 10.1.2 - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - supports-color - - typescript - - zod - - music@file:packages/agents/player(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - express: 4.22.1 - open: 10.1.2 - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - supports-color - - typescript - - zod - mute-stream@0.0.8: {} mute-stream@2.0.0: {} @@ -27232,21 +24482,6 @@ snapshots: is-inside-container: 1.0.0 is-wsl: 3.1.0 - openai@4.103.0(encoding@0.1.13)(ws@8.18.2)(zod@3.25.76): - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) - optionalDependencies: - ws: 8.18.2 - zod: 3.25.76 - transitivePeerDependencies: - - encoding - openai@4.103.0(encoding@0.1.13)(ws@8.19.0)(zod@3.25.76): dependencies: '@types/node': 18.19.130 @@ -27262,36 +24497,6 @@ snapshots: transitivePeerDependencies: - encoding - openai@4.103.0(ws@8.18.2)(zod@4.1.13): - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) - optionalDependencies: - ws: 8.18.2 - zod: 4.1.13 - transitivePeerDependencies: - - encoding - - openai@4.103.0(ws@8.18.2)(zod@4.3.6): - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) - optionalDependencies: - ws: 8.18.2 - zod: 4.3.6 - transitivePeerDependencies: - - encoding - openai@4.103.0(ws@8.19.0)(zod@3.25.76): dependencies: '@types/node': 18.19.130 @@ -27322,21 +24527,6 @@ snapshots: transitivePeerDependencies: - encoding - openai@4.103.0(ws@8.19.0)(zod@4.3.6): - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) - optionalDependencies: - ws: 8.19.0 - zod: 4.3.6 - transitivePeerDependencies: - - encoding - ora@5.4.1: dependencies: bl: 4.1.0 @@ -27571,12 +24761,6 @@ snapshots: pend@1.2.0: {} - photo-agent@file:packages/agents/photo: - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - transitivePeerDependencies: - - supports-color - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -27864,7 +25048,7 @@ snapshots: '@cliqz/adblocker-puppeteer': 1.23.8(puppeteer@24.37.5(typescript@5.4.5)) debug: 4.4.3(supports-color@8.1.1) node-fetch: 2.7.0(encoding@0.1.13) - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) optionalDependencies: puppeteer: 24.37.5(typescript@5.4.5) puppeteer-core: 24.37.5 @@ -27874,53 +25058,39 @@ snapshots: - playwright-extra - supports-color - puppeteer-extra-plugin-adblocker@2.13.6(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5)))(puppeteer@24.37.5(typescript@5.4.5)): - dependencies: - '@cliqz/adblocker-puppeteer': 1.23.8(puppeteer@24.37.5(typescript@5.4.5)) - debug: 4.4.3(supports-color@8.1.1) - node-fetch: 2.7.0(encoding@0.1.13) - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - optionalDependencies: - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - transitivePeerDependencies: - - encoding - - playwright-extra - - supports-color - - puppeteer-extra-plugin-stealth@2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin-stealth@2.11.2(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): dependencies: debug: 4.4.3(supports-color@8.1.1) - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - puppeteer-extra-plugin-user-preferences: 2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin-user-preferences: 2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) transitivePeerDependencies: - supports-color - puppeteer-extra-plugin-user-data-dir@2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin-user-data-dir@2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): dependencies: debug: 4.4.3(supports-color@8.1.1) fs-extra: 10.1.0 - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) rimraf: 3.0.2 optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) transitivePeerDependencies: - supports-color - puppeteer-extra-plugin-user-preferences@2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin-user-preferences@2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): dependencies: debug: 4.4.3(supports-color@8.1.1) deepmerge: 4.3.1 - puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - puppeteer-extra-plugin-user-data-dir: 2.4.1(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) + puppeteer-extra-plugin-user-data-dir: 2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) transitivePeerDependencies: - supports-color - puppeteer-extra-plugin@3.2.3(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))): + puppeteer-extra-plugin@3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): dependencies: '@types/debug': 4.1.12 debug: 4.4.3(supports-color@8.1.1) @@ -28575,93 +25745,6 @@ snapshots: setprototypeof@1.2.0: {} - settings-agent@file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.18.2)(zod@4.1.13) - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - zod - - settings-agent@file:packages/agents/settings(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - zod - - settings-agent@file:packages/agents/settings(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - agent-dispatcher: file:packages/dispatcher/dispatcher(typescript@5.4.5)(ws@8.19.0) - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chat-agent: file:packages/agents/chat(socks@2.8.7)(typescript@5.4.5)(ws@8.19.0)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - ws - - zod - shallow-clone@0.1.2: dependencies: is-extendable: 0.1.1 @@ -28905,50 +25988,6 @@ snapshots: transitivePeerDependencies: - supports-color - spelunker-agent@file:packages/agents/spelunker(socks@2.8.7)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - better-sqlite3: 12.6.2 - code-processor: file:packages/codeProcessor(socks@2.8.7)(zod@4.1.13) - dotenv: 16.5.0 - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typescript: 5.4.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - zod - - spelunker-agent@file:packages/agents/spelunker(socks@2.8.7)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - better-sqlite3: 12.6.2 - code-processor: file:packages/codeProcessor(socks@2.8.7)(zod@4.3.6) - dotenv: 16.5.0 - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typescript: 5.4.5 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - zod - sprintf-js@1.0.3: {} sprintf-js@1.1.3: @@ -29173,50 +26212,9 @@ snapshots: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 - minipass: 7.1.2 - minizlib: 3.1.0 - yallist: 5.0.0 - - taskflow-typeagent@file:packages/agents/taskflow(ws@8.18.2)(zod@4.1.13): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-client': file:packages/agentServer/client(ws@8.18.2) - '@typeagent/dispatcher-types': file:packages/dispatcher/types - html-to-text: 9.0.5 - transitivePeerDependencies: - - supports-color - - ws - - zod - - taskflow-typeagent@file:packages/agents/taskflow(ws@8.19.0)(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@typeagent/agent-sdk': file:packages/agentSdk - '@typeagent/agent-server-client': file:packages/agentServer/client(ws@8.19.0) - '@typeagent/dispatcher-types': file:packages/dispatcher/types - html-to-text: 9.0.5 - transitivePeerDependencies: - - supports-color - - ws - - zod - - telemetry@file:packages/telemetry(socks@2.8.7): - dependencies: - chalk: 5.6.2 - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - mongodb: 6.16.0(socks@2.8.7) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 temp-file@3.4.0: dependencies: @@ -29279,72 +26277,12 @@ snapshots: glob: 10.5.0 minimatch: 9.0.9 - test-lib@file:packages/testLib(typescript@5.4.5): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - supports-color - - typescript - - zod - text-decoder@1.2.1: {} text-table@0.2.0: {} textextensions@5.16.0: {} - textpro@file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - cheerio: 1.0.0-rc.12 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - marked: 16.0.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - textpro@file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - cheerio: 1.0.0-rc.12 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - marked: 16.0.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - textpro@file:packages/textPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - cheerio: 1.0.0-rc.12 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - marked: 16.0.0 - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -29542,6 +26480,25 @@ snapshots: yn: 3.1.1 optional: true + ts-node@10.9.2(@types/node@20.19.25)(typescript@5.4.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.19.25 + acorn: 8.11.1 + acorn-walk: 8.3.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.4.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + ts-node@10.9.2(@types/node@22.15.18)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -29613,111 +26570,6 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typeagent@file:packages/typeagent(zod@3.25.76): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - async: 3.2.6 - cheerio: 1.0.0-rc.12 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - zod - - typeagent@file:packages/typeagent(zod@4.1.13): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - async: 3.2.6 - cheerio: 1.0.0-rc.12 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - zod - - typeagent@file:packages/typeagent(zod@4.3.6): - dependencies: - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - async: 3.2.6 - cheerio: 1.0.0-rc.12 - debug: 4.4.3(supports-color@8.1.1) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - typescript: 5.4.5 - transitivePeerDependencies: - - supports-color - - zod - - typechat-utils@file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - typechat-utils@file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - typechat-utils@file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@azure-rest/maps-search': 2.0.0-beta.3 - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - chalk: 5.6.2 - date-fns: 4.1.0 - debug: 4.4.3(supports-color@8.1.1) - exifreader: 4.30.1 - telemetry: file:packages/telemetry(socks@2.8.7) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - typechat@0.1.1(typescript@5.4.5)(zod@3.25.76): optionalDependencies: typescript: 5.4.5 @@ -29728,15 +26580,6 @@ snapshots: typescript: 5.4.5 zod: 4.1.13 - typechat@0.1.1(typescript@5.4.5)(zod@4.3.6): - optionalDependencies: - typescript: 5.4.5 - zod: 4.3.6 - - typechat@0.1.1(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -29917,126 +26760,6 @@ snapshots: utila@0.4.0: {} - utility-typeagent@file:packages/agents/utility(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.1.13) - '@typeagent/agent-sdk': file:packages/agentSdk - '@types/html-to-text': 9.0.4 - browser-typeagent: file:packages/agents/browser(puppeteer-core@24.37.5)(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - html-to-text: 9.0.5 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - utility-typeagent@file:packages/agents/utility(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@typeagent/agent-sdk': file:packages/agentSdk - '@types/html-to-text': 9.0.4 - browser-typeagent: file:packages/agents/browser(puppeteer-core@24.37.5)(typescript@5.4.5)(zod@4.3.6) - html-to-text: 9.0.5 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - utility-typeagent@file:packages/agents/utility(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@typeagent/agent-sdk': file:packages/agentSdk - '@types/html-to-text': 9.0.4 - browser-typeagent: file:packages/agents/browser(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - html-to-text: 9.0.5 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - utility-typeagent@file:packages/agents/utility(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@anthropic-ai/claude-agent-sdk': 0.2.12(zod@4.3.6) - '@typeagent/agent-sdk': file:packages/agentSdk - '@types/html-to-text': 9.0.4 - browser-typeagent: file:packages/agents/browser(typescript@5.4.5)(zod@4.3.6) - html-to-text: 9.0.5 - puppeteer: 24.37.5(typescript@5.4.5) - puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) - puppeteer-extra-plugin-stealth: 2.11.2(puppeteer-extra@3.3.6(puppeteer@24.37.5(typescript@5.4.5))) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - '@types/puppeteer' - - bare-buffer - - bufferutil - - canvas - - encoding - - gcp-metadata - - kerberos - - mongodb-client-encryption - - playwright-extra - - puppeteer-core - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - utils-merge@1.0.1: {} uuid@11.1.0: {} @@ -30081,42 +26804,6 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - video-agent@file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - - video-agent@file:packages/agents/video(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typechat-utils: file:packages/utils/typechatUtils(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - zod - vite@6.4.1(@types/node@20.19.23)(jiti@2.5.1)(less@4.3.0)(terser@5.39.2)(yaml@2.7.0): dependencies: esbuild: 0.25.11 @@ -30205,12 +26892,6 @@ snapshots: dependencies: defaults: 1.0.4 - weather-agent@file:packages/agents/weather: - dependencies: - '@typeagent/agent-sdk': file:packages/agentSdk - transitivePeerDependencies: - - supports-color - web-streams-polyfill@3.3.3: {} web-streams-polyfill@4.0.0-beta.3: {} @@ -30364,197 +27045,6 @@ snapshots: - esbuild - uglify-js - website-memory@file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13): - dependencies: - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - better-sqlite3: 12.6.2 - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - get-folder-size: 5.0.0 - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-metrics: 2.4.0(graphology-types@0.24.8) - graphology-types: 0.24.8 - jsdom: 26.1.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - website-memory@file:packages/memory/website(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - better-sqlite3: 12.6.2 - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - get-folder-size: 5.0.0 - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-metrics: 2.4.0(graphology-types@0.24.8) - graphology-types: 0.24.8 - jsdom: 26.1.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - website-memory@file:packages/memory/website(typescript@5.4.5): - dependencies: - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.1.13) - better-sqlite3: 12.6.2 - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - get-folder-size: 5.0.0 - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-metrics: 2.4.0(graphology-types@0.24.8) - graphology-types: 0.24.8 - jsdom: 26.1.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.1.13) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.1.13) - typechat: 0.1.1(typescript@5.4.5)(zod@4.1.13) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - website-memory@file:packages/memory/website(typescript@5.4.5)(zod@3.25.76): - dependencies: - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@3.25.76) - better-sqlite3: 12.6.2 - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - get-folder-size: 5.0.0 - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-metrics: 2.4.0(graphology-types@0.24.8) - graphology-types: 0.24.8 - jsdom: 26.1.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@3.25.76) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@3.25.76) - typechat: 0.1.1(typescript@5.4.5)(zod@3.25.76) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - website-memory@file:packages/memory/website(typescript@5.4.5)(zod@4.3.6): - dependencies: - '@typeagent/common-utils': file:packages/utils/commonUtils - aiclient: file:packages/aiclient(typescript@5.4.5)(zod@4.3.6) - better-sqlite3: 12.6.2 - cheerio: 1.1.0 - conversation-memory: file:packages/memory/conversation(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - debug: 4.4.3(supports-color@8.1.1) - dompurify: 3.2.6 - get-folder-size: 5.0.0 - graphology: 0.25.4(graphology-types@0.24.8) - graphology-communities-louvain: 2.0.2(graphology-types@0.24.8) - graphology-metrics: 2.4.0(graphology-types@0.24.8) - graphology-types: 0.24.8 - jsdom: 26.1.0 - knowledge-processor: file:packages/knowledgeProcessor(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - knowpro: file:packages/knowPro(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - memory-storage: file:packages/memory/storage(socks@2.8.7)(typescript@5.4.5)(zod@4.3.6) - telemetry: file:packages/telemetry(socks@2.8.7) - typeagent: file:packages/typeagent(zod@4.3.6) - typechat: 0.1.1(typescript@5.4.5)(zod@4.3.6) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - bufferutil - - canvas - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - - typescript - - utf-8-validate - - zod - - websocket-channel-server@file:packages/utils/webSocketChannelServer: - dependencies: - '@typeagent/agent-rpc': file:packages/agentRpc - '@typeagent/common-utils': file:packages/utils/commonUtils - debug: 4.4.3(supports-color@8.1.1) - ws: 8.19.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - websocket-driver@0.7.4: dependencies: http-parser-js: 0.5.8 @@ -30563,18 +27053,6 @@ snapshots: websocket-extensions@0.1.4: {} - websocket-utils@file:packages/utils/webSocketUtils: - dependencies: - debug: 4.4.3(supports-color@8.1.1) - dotenv: 16.5.0 - find-config: 1.0.0 - isomorphic-ws: 5.0.0(ws@8.19.0) - ws: 8.19.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - webvtt-parser@2.2.0: {} whatwg-encoding@2.0.0: @@ -30797,7 +27275,8 @@ snapshots: yaml@2.7.0: {} - yaml@2.8.2: {} + yaml@2.8.2: + optional: true yargs-parser@20.2.9: {} @@ -30869,10 +27348,6 @@ snapshots: dependencies: zod: 4.1.13 - zod-to-json-schema@3.24.5(zod@4.3.6): - dependencies: - zod: 4.3.6 - zod-to-json-schema@3.25.1(zod@3.25.76): dependencies: zod: 3.25.76 From 0dcf9347ddcdb552191934a1991363eefb8ae548 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 5 Mar 2026 11:55:27 -0800 Subject: [PATCH 03/51] fix dependency --- ts/packages/chat-ui/package.json | 1 + ts/pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ts/packages/chat-ui/package.json b/ts/packages/chat-ui/package.json index 1139fa6c1a..4169eaf4d6 100644 --- a/ts/packages/chat-ui/package.json +++ b/ts/packages/chat-ui/package.json @@ -28,6 +28,7 @@ }, "devDependencies": { "@types/markdown-it": "^14.1.2", + "rimraf": "^6.0.1", "typescript": "~5.4.5" } } diff --git a/ts/pnpm-lock.yaml b/ts/pnpm-lock.yaml index e3ef30478b..3f5dee5fa2 100644 --- a/ts/pnpm-lock.yaml +++ b/ts/pnpm-lock.yaml @@ -2939,6 +2939,9 @@ importers: '@types/markdown-it': specifier: ^14.1.2 version: 14.1.2 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 typescript: specifier: ~5.4.5 version: 5.4.5 From c83bac92b6f3c53b89fd74d797ccad8ffe8c3650 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 5 Mar 2026 15:25:45 -0800 Subject: [PATCH 04/51] Fix matchGrammarCompletion: candidate filtering, needsSeparator semantics, and tests - Refactor matchGrammarCompletion to use candidate-based approach with post-loop filtering by maxPrefixLength so exact matches dominate shorter partial completions. - Category 1 (exact match) now updates maxPrefixLength. - Remove isPartialPrefixOfStringPart; Category 3b reports all string parts unconditionally (caller filters by trailing text). - Fix needsSeparator to strictly describe the boundary at matchedPrefixLength, independent of any trailing user-typed content. - Simplify Category 3a wildcard needsSep guard to match Categories 2/3b. - Add grammarCompletionLongestMatch.spec.ts (43 tests). - Add grammarCompletionCategory3bLimitation.spec.ts (23 tests including 7 needsSeparator tests for Category 3b). - Update grammarCompletionPrefixLength.spec.ts for new behavior. --- .../actionGrammar/src/grammarMatcher.ts | 344 +++++++++---- ...mmarCompletionCategory3bLimitation.spec.ts | 291 +++++++++++ .../grammarCompletionLongestMatch.spec.ts | 480 ++++++++++++++++++ .../grammarCompletionPrefixLength.spec.ts | 43 +- 4 files changed, 1046 insertions(+), 112 deletions(-) create mode 100644 ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts create mode 100644 ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index af61bf0a2e..6c41a137c4 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1034,11 +1034,13 @@ export type GrammarCompletionResult = { // to insert/filter completions (replacing the space-based heuristic). matchedPrefixLength?: number | undefined; // True when a separator (e.g. space) must be inserted between the - // already-typed prefix and the completion text. This happens when the - // grammar consumed the entire input and the spacing rules between the - // last typed character and the first completion character require a - // separator (e.g. Latin "play" → "music" needs a space, but CJK - // "再生" → "音楽" does not). + // content at `matchedPrefixLength` and the completion text. The + // caller should check the spacing rules between the last character + // of the matched prefix and the first character of each completion. + // Example: prefix="play", matchedPrefixLength=4, completion="music" + // → needsSeparator=true (Latin "y" → "m" requires a space). + // Example: prefix="再生", matchedPrefixLength=2, completion="音楽" + // → needsSeparator=false (CJK scripts don't require spaces). needsSeparator?: boolean | undefined; }; @@ -1099,88 +1101,196 @@ export function matchGrammar(grammar: Grammar, request: string) { } /** - * Check if the remaining input text is a case-insensitive prefix of a rule's - * string part. Used for completions when the user has partially typed a keyword. - * For example, prefix "p" should match and complete "play". + * Given a grammar and a user-typed prefix string, determine what completions + * are available. The algorithm greedily matches as many grammar parts as + * possible against the prefix (the "longest completable prefix"), then + * reports completions from the *next* unmatched part. + * + * The function explores every alternative rule/state in the grammar (via the + * `pending` work-list). Each state is run through `matchState` which + * consumes as many parts as the prefix allows. The state then falls into + * one of three categories: + * + * 1. **Exact match** — the prefix satisfies every part in the rule. + * No completion is needed, but `maxPrefixLength` is updated to + * the full input length so that completion candidates from shorter + * partial matches are filtered out in the post-loop step. + * + * 2. **Partial match, finalized** — the prefix was consumed (possibly with + * trailing separators) but the rule still has remaining parts. + * `matchState` returns `false` (could not match the next part) and + * `finalizeState` returns `true` (no trailing non-separator junk). + * The next unmatched part produces a completion candidate: + * - String part → literal keyword completion (e.g. "music"). + * - Wildcard / number → property completion (handled elsewhere). + * + * 3. **Partial match, NOT finalized** — either: + * a. A pending wildcard could not be finalized (trailing text is only + * separators with no wildcard content) → emit a property completion + * for the wildcard's entity type. + * b. Trailing text remains that didn't match any part → emit the + * next string part as a completion candidate unconditionally. + * The caller uses `matchedPrefixLength` to filter by trailing + * text. + * + * After processing all states, only candidates whose `prefixLength` + * equals the overall `maxPrefixLength` are returned. This ensures + * completions from shorter partial matches are discarded when a longer + * (or exact) match consumed more input. + * + * `matchedPrefixLength` tracks the furthest point consumed across all + * states — including exact matches (via `Math.max`). This tells the + * caller where the completable portion of the input ends, so it can + * position the completion insertion point correctly (especially important + * for non-space-separated scripts like CJK). + * + * `needsSeparator` indicates whether a separator (e.g. a space) must be + * inserted between the content at `matchedPrefixLength` and the completion + * text. It is determined by the spacing rules between the last character + * of the matched prefix and the first character of the completion. */ -function isPartialPrefixOfStringPart( - prefix: string, - index: number, - part: StringPart, -): boolean { - // Get the remaining text after any leading separators - const remaining = prefix.slice(index).trimStart().toLowerCase(); - if (remaining.length === 0) { - return false; // No partial text - handled by the normal completion path - } - const partText = part.value.join(" ").toLowerCase(); - return partText.startsWith(remaining) && remaining.length < partText.length; -} - export function matchGrammarCompletion( grammar: Grammar, prefix: string, ): GrammarCompletionResult { debugCompletion(`Start completion for prefix: "${prefix}"`); + + // Seed the work-list with one MatchState per top-level grammar rule. + // matchState may push additional states (for nested rules, optional + // parts, wildcard extensions, repeat groups) during processing. const pending = initialMatchState(grammar); - const completions: string[] = []; - const properties: GrammarCompletionProperty[] = []; + + // Accumulate completion candidates with their associated prefix + // lengths. After the main loop we filter to keep only candidates + // whose prefix length equals the overall maximum — completions + // from shorter partial matches are irrelevant when a longer (or + // exact) match exists. + const completionCandidates: Array<{ + text: string; + prefixLength: number; + needsSep: boolean; + }> = []; + const propertyCandidates: Array<{ + property: GrammarCompletionProperty; + prefixLength: number; + needsSep: boolean; + }> = []; + // Track the furthest point the grammar consumed across all - // completion-producing states. This tells the caller where + // states (including exact matches). This tells the caller where // the "filter text" begins so it doesn't have to guess from // whitespace (which breaks for CJK and other non-space scripts). let maxPrefixLength: number | undefined; - let needsSeparator = false; + + // --- Main loop: process every pending state --- while (pending.length > 0) { const state = pending.pop()!; debugMatch(state, `resume state`); + + // Attempt to greedily match as many grammar parts as possible + // against the prefix. `matched` is true only when ALL parts in + // the rule (including nested rules) were satisfied. matchState + // may also push new derivative states onto `pending` (e.g. for + // alternative nested rules, optional-skip paths, wildcard + // extensions, repeat iterations). const matched = matchState(state, prefix, pending); + // finalizeState does two things: + // 1. If a wildcard is pending at the end, attempt to capture + // all remaining input as its value. + // 2. Reject states that leave trailing non-separator characters + // un-consumed (those states don't represent valid parses). + // It returns true when the state is "clean" — all input was + // consumed (or only trailing separators remain). if (finalizeState(state, prefix)) { + // --- Category 1: Exact match --- + // All parts matched AND prefix was fully consumed. + // Nothing left to complete; but record how far we got + // so that completions from shorter partial matches are + // filtered out in the post-loop step. if (matched) { debugCompletion("Matched. Nothing to complete."); - // Matched exactly, nothing to complete. + maxPrefixLength = + maxPrefixLength === undefined + ? state.index + : Math.max(maxPrefixLength, state.index); continue; } - // Completion with the current part + + // --- Category 2: Partial match (clean finalization) --- + // matchState stopped at state.partIndex because it couldn't + // match the next part against the (exhausted) prefix. + // That next part is what we offer as a completion. const nextPart = state.parts[state.partIndex]; debugCompletion(`Completing ${nextPart.type} part ${state.name}`); if (nextPart.type === "string") { + // The next expected part is a literal keyword string. + // Offer it as a completion (e.g. "music" after "play"). const completionText = nextPart.value.join(" "); debugCompletion(`Adding completion text: "${completionText}"`); - completions.push(completionText); - // Grammar consumed up to state.index; completions start there. - maxPrefixLength = - maxPrefixLength === undefined - ? state.index - : Math.max(maxPrefixLength, state.index); - // When the grammar consumed the entire input, check - // whether a separator is needed between the last typed - // character and the first completion character. + + // Determine whether a separator (e.g. space) is needed + // between the content at matchedPrefixLength and the + // completion text. Check the boundary between the last + // consumed character and the first character of the + // completion. This is purely a property of the + // boundary — it does not account for any unmatched + // trailing content the user may have typed beyond + // matchedPrefixLength (e.g. a trailing space). + // Example: prefix="play" completion="music" → true (Latin). + // Example: prefix="play " completion="music" → true (matched + // prefix is "play"; separator still needed at that boundary). + // Example: prefix="再生" completion="音楽" → false (CJK). + let candidateNeedsSep = false; if ( - state.index === prefix.length && - prefix.length > 0 && + state.index > 0 && completionText.length > 0 && state.spacingMode !== "none" ) { - needsSeparator = - needsSeparator || - requiresSeparator( - prefix[prefix.length - 1], - completionText[0], - state.spacingMode, - ); + candidateNeedsSep = requiresSeparator( + prefix[state.index - 1], + completionText[0], + state.spacingMode, + ); } + + completionCandidates.push({ + text: completionText, + prefixLength: state.index, + needsSep: candidateNeedsSep, + }); + + // Record how far into the prefix the grammar consumed + // before reaching this completion point. + maxPrefixLength = + maxPrefixLength === undefined + ? state.index + : Math.max(maxPrefixLength, state.index); } + // Note: non-string next parts (wildcard, number, rules) in + // Category 2 don't produce completions here — wildcards are + // handled by Category 3a (pending wildcard) and nested rules + // are expanded by matchState into separate pending states. } else { - // We can't finalize the state because of empty pending wildcard - // or because there's trailing unmatched text. + // --- Category 3: finalizeState failed --- + // Either (a) a pending wildcard couldn't capture meaningful + // content, or (b) trailing non-separator text remains that + // didn't match any grammar part. const pendingWildcard = state.pendingWildcard; + if ( pendingWildcard !== undefined && pendingWildcard.valueId !== undefined ) { + // --- Category 3a: Unfinalizable pending wildcard --- + // The grammar reached a wildcard slot but its capture + // region is empty or separator-only (e.g. prefix="play " + // with wildcard starting at index 4 — the space is not + // valid wildcard content). Instead of offering the + // *following* string part as a completion, we report a + // property completion describing the wildcard's type so + // the caller can provide entity-specific suggestions. debugCompletion("Completing wildcard part"); const completionProperty = getGrammarCompletionProperty( state, @@ -1190,65 +1300,88 @@ export function matchGrammarCompletion( debugCompletion( `Adding completion property: ${JSON.stringify(completionProperty)}`, ); - properties.push(completionProperty); - // The wildcard starts at pendingWildcard.start; the - // grammar consumed everything before that. - maxPrefixLength = - maxPrefixLength === undefined - ? pendingWildcard.start - : Math.max(maxPrefixLength, pendingWildcard.start); - // Wildcard completions: the wildcard consumes up to - // pendingWildcard.start. If that position is at or - // before the end of the input (with only separators - // between the wildcard start and the cursor), we may - // need a separator before the property value. - // This covers two cases: - // 1. pendingWildcard.start === prefix.length — separator - // not typed yet (e.g. input "play", wildcard at 4). - // 2. pendingWildcard.start < prefix.length and only - // separators follow (e.g. input "play ", wildcard at - // 4, space at 4 is already typed). - // In both cases needsSeparator must be set so the caller - // strips the leading whitespace before filtering. - // (For wildcards the completion text varies, so we - // conservatively use a typical word char "a".) + + const candidatePrefixLength = pendingWildcard.start; + + // Determine whether a separator is needed between + // the content at matchedPrefixLength and the + // completion (the wildcard entity value). Check + // the boundary between the last consumed character + // before the wildcard and the first character of the + // entity value. We use "a" as a representative word + // character since the actual value is unknown. + let candidateNeedsSep = false; if ( pendingWildcard.start > 0 && - pendingWildcard.start <= prefix.length && - prefix.length > 0 && - state.spacingMode !== "none" && - nextNonSeparatorIndex(prefix, pendingWildcard.start) >= - prefix.length + state.spacingMode !== "none" ) { - needsSeparator = - needsSeparator || - requiresSeparator( - prefix[pendingWildcard.start - 1], - "a", - state.spacingMode, - ); + candidateNeedsSep = requiresSeparator( + prefix[pendingWildcard.start - 1], + "a", + state.spacingMode, + ); } + + propertyCandidates.push({ + property: completionProperty, + prefixLength: candidatePrefixLength, + needsSep: candidateNeedsSep, + }); + + // The wildcard starts at pendingWildcard.start; the + // grammar consumed everything before that. + maxPrefixLength = + maxPrefixLength === undefined + ? candidatePrefixLength + : Math.max(maxPrefixLength, candidatePrefixLength); } } else if (!matched) { - // matchState failed on a string part and there's trailing text. - // Check if the remaining input is a partial prefix of the - // current string part (e.g. "p" is a prefix of "play"). + // --- Category 3b: Completion after consumed prefix --- + // The grammar stopped at a string part it could not + // match. Report the string part as a completion + // candidate regardless of any trailing text — the + // caller can use matchedPrefixLength to determine how + // much of the input was successfully consumed and + // filter completions by any trailing text beyond that + // point. Post-loop filtering ensures only candidates + // at the maximum prefix length are returned, so + // completions from shorter partial matches are + // automatically discarded when a longer match exists. const currentPart = state.parts[state.partIndex]; if ( currentPart !== undefined && - currentPart.type === "string" && - isPartialPrefixOfStringPart( - prefix, - state.index, - currentPart, - ) + currentPart.type === "string" ) { const fullText = currentPart.value.join(" "); debugCompletion( - `Adding partial prefix completion: "${fullText}"`, + `Adding completion: "${fullText}" (consumed ${state.index} chars)`, ); - completions.push(fullText); - // The partial text starts at state.index. + + // Determine whether a separator is needed between + // the matched prefix and the completion text. Check + // the boundary between the last consumed character + // and the first character of the completion. + let candidateNeedsSep = false; + if ( + state.index > 0 && + fullText.length > 0 && + state.spacingMode !== "none" + ) { + candidateNeedsSep = requiresSeparator( + prefix[state.index - 1], + fullText[0], + state.spacingMode, + ); + } + + completionCandidates.push({ + text: fullText, + prefixLength: state.index, + needsSep: candidateNeedsSep, + }); + + // state.index is where matching stopped (before the + // trailing text), so completions begin there. maxPrefixLength = maxPrefixLength === undefined ? state.index @@ -1258,6 +1391,29 @@ export function matchGrammarCompletion( } } + // --- Post-loop filtering --- + // Only keep candidates whose prefix length equals the overall + // maximum. Completions from shorter partial matches are + // irrelevant when a longer (or exact) match consumed more input. + const completions: string[] = []; + const properties: GrammarCompletionProperty[] = []; + let needsSeparator = false; + + if (maxPrefixLength !== undefined) { + for (const c of completionCandidates) { + if (c.prefixLength === maxPrefixLength) { + completions.push(c.text); + needsSeparator = needsSeparator || c.needsSep; + } + } + for (const p of propertyCandidates) { + if (p.prefixLength === maxPrefixLength) { + properties.push(p.property); + needsSeparator = needsSeparator || p.needsSep; + } + } + } + const result: GrammarCompletionResult = { completions, properties, diff --git a/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts new file mode 100644 index 0000000000..33b4ac2b48 --- /dev/null +++ b/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts @@ -0,0 +1,291 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { loadGrammarRules } from "../src/grammarLoader.js"; +import { matchGrammarCompletion } from "../src/grammarMatcher.js"; + +describe("Grammar Completion - all alternatives after longest match", () => { + // After the longest fully matched prefix, ALL valid next-part + // completions are reported regardless of trailing partial text. + // The caller is responsible for filtering by the trailing text. + + describe("alternatives preserved with partial trailing text", () => { + const g = [ + ` = $(a:) $(b:) $(c:) -> { a, b, c };`, + ` = play -> "play";`, + ` = rock -> "rock";`, + ` = music -> "music";`, + ` = hard -> "hard";`, + ` = loud -> "loud";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("without partial text: all alternatives are offered", () => { + const result = matchGrammarCompletion(grammar, "play rock"); + expect(result.completions.sort()).toEqual([ + "hard", + "loud", + "music", + ]); + expect(result.matchedPrefixLength).toBe(9); + }); + + it("with trailing space: all alternatives are still offered", () => { + const result = matchGrammarCompletion(grammar, "play rock "); + expect(result.completions.sort()).toEqual([ + "hard", + "loud", + "music", + ]); + }); + + it("partial text 'm': all alternatives still reported", () => { + const result = matchGrammarCompletion(grammar, "play rock m"); + // All three are reported; caller filters by "m". + expect(result.completions.sort()).toEqual([ + "hard", + "loud", + "music", + ]); + expect(result.matchedPrefixLength).toBe(9); + }); + + it("partial text 'h': all alternatives still reported", () => { + const result = matchGrammarCompletion(grammar, "play rock h"); + expect(result.completions.sort()).toEqual([ + "hard", + "loud", + "music", + ]); + expect(result.matchedPrefixLength).toBe(9); + }); + + it("non-matching text 'x': all alternatives still reported", () => { + const result = matchGrammarCompletion(grammar, "play rock x"); + // All alternatives are reported even though "x" doesn't + // prefix-match any of them; the caller filters. + expect(result.completions.sort()).toEqual([ + "hard", + "loud", + "music", + ]); + expect(result.matchedPrefixLength).toBe(9); + }); + }); + + describe("inline single-part alternatives preserved", () => { + const g = [` = go (north | south | east | west) -> true;`].join( + "\n", + ); + const grammar = loadGrammarRules("test.grammar", g); + + it("all directions offered without partial text", () => { + const result = matchGrammarCompletion(grammar, "go"); + expect(result.completions.sort()).toEqual([ + "east", + "north", + "south", + "west", + ]); + }); + + it("'n' trailing: all directions still offered", () => { + const result = matchGrammarCompletion(grammar, "go n"); + expect(result.completions.sort()).toEqual([ + "east", + "north", + "south", + "west", + ]); + expect(result.matchedPrefixLength).toBe(2); + }); + + it("'z' trailing: all directions still offered", () => { + const result = matchGrammarCompletion(grammar, "go z"); + expect(result.completions.sort()).toEqual([ + "east", + "north", + "south", + "west", + ]); + expect(result.matchedPrefixLength).toBe(2); + }); + }); + + describe("all alternatives after consumed prefix", () => { + const g = [ + ` = $(a:) $(b:) -> { a, b };`, + ` = open -> "open";`, + ` = file -> "file";`, + ` = folder -> "folder";`, + ` = finder -> "finder";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("'open f': all alternatives offered", () => { + const result = matchGrammarCompletion(grammar, "open f"); + expect(result.completions.sort()).toEqual([ + "file", + "finder", + "folder", + ]); + }); + + it("'open fi': all alternatives still offered", () => { + const result = matchGrammarCompletion(grammar, "open fi"); + // "folder" is now correctly reported alongside file/finder. + expect(result.completions.sort()).toEqual([ + "file", + "finder", + "folder", + ]); + expect(result.matchedPrefixLength).toBe(4); + }); + }); + + describe("no false completions when nothing consumed", () => { + // When state.index === 0 (no prefix consumed), we should NOT + // report unrelated string parts — only partial prefix matches. + const g = [ + ` = play $(g:) -> { genre: g };`, + ` = rock -> "rock";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("all first parts offered for unrelated input", () => { + const result = matchGrammarCompletion(grammar, "xyz"); + // Nothing consumed; the first string part is offered + // unconditionally. The caller filters by trailing text. + expect(result.completions).toEqual(["play"]); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("partial prefix at start still works", () => { + const result = matchGrammarCompletion(grammar, "pl"); + expect(result.completions).toContain("play"); + }); + }); + + describe("needsSeparator in Category 3b", () => { + // Category 3b: finalizeState failed, trailing non-separator text + // remains. needsSeparator indicates whether a separator is needed + // between matchedPrefixLength and the completion text. + + describe("Latin grammar (auto spacing)", () => { + const g = [ + ` = $(a:) $(b:) -> { a, b };`, + ` = play -> "a";`, + ` = music -> "b";`, + ` = midi -> "b2";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("reports needsSeparator after consumed Latin prefix with trailing text", () => { + // "play x" → consumed "play" (4 chars), trailing "x" fails. + // Completion "music"/"midi" at matchedPrefixLength=4. + // Last consumed char: "y" (Latin), first completion char: "m" (Latin) + // → separator needed. + const result = matchGrammarCompletion(grammar, "play x"); + expect(result.completions.sort()).toEqual(["midi", "music"]); + expect(result.matchedPrefixLength).toBe(4); + expect(result.needsSeparator).toBe(true); + }); + + it("reports needsSeparator with partial-match trailing text", () => { + // "play mu" → consumed "play" (4 chars), trailing "mu". + // Same boundary: "y" → "m" → separator needed. + const result = matchGrammarCompletion(grammar, "play mu"); + expect(result.completions.sort()).toEqual(["midi", "music"]); + expect(result.matchedPrefixLength).toBe(4); + expect(result.needsSeparator).toBe(true); + }); + }); + + describe("CJK grammar (auto spacing)", () => { + const g = [ + ` [spacing=auto] = $(a:) $(b:) -> { a, b };`, + ` [spacing=auto] = 再生 -> "a";`, + ` [spacing=auto] = 音楽 -> "b";`, + ` [spacing=auto] = 映画 -> "b2";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator for CJK → CJK", () => { + // "再生x" → consumed "再生" (2 chars), trailing "x" fails. + // Last consumed char: "生" (CJK), first completion: "音" (CJK) + // → no separator needed in auto mode. + const result = matchGrammarCompletion(grammar, "再生x"); + expect(result.completions.sort()).toEqual(["映画", "音楽"]); + expect(result.matchedPrefixLength).toBe(2); + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("nothing consumed (index=0)", () => { + const g = [ + ` = play $(g:) -> { genre: g };`, + ` = rock -> "rock";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator when nothing consumed", () => { + // "xyz" → consumed 0 chars, offers "play" at prefixLength=0. + // No last consumed char → no separator check. + const result = matchGrammarCompletion(grammar, "xyz"); + expect(result.completions).toEqual(["play"]); + expect(result.matchedPrefixLength).toBe(0); + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("spacing=required", () => { + const g = [ + ` [spacing=required] = $(a:) $(b:) -> { a, b };`, + ` [spacing=required] = play -> "a";`, + ` [spacing=required] = music -> "b";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("reports needsSeparator for spacing=required with trailing text", () => { + const result = matchGrammarCompletion(grammar, "play x"); + expect(result.completions).toEqual(["music"]); + expect(result.matchedPrefixLength).toBe(4); + expect(result.needsSeparator).toBe(true); + }); + }); + + describe("spacing=optional", () => { + const g = [ + ` [spacing=optional] = $(a:) $(b:) -> { a, b };`, + ` [spacing=optional] = play -> "a";`, + ` [spacing=optional] = music -> "b";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator for spacing=optional", () => { + const result = matchGrammarCompletion(grammar, "play x"); + expect(result.completions).toEqual(["music"]); + expect(result.matchedPrefixLength).toBe(4); + expect(result.needsSeparator).toBeUndefined(); + }); + }); + + describe("mixed scripts (Latin → CJK)", () => { + const g = [ + ` [spacing=auto] = $(a:) $(b:) -> { a, b };`, + ` [spacing=auto] = play -> "a";`, + ` [spacing=auto] = 音楽 -> "b";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("does not report needsSeparator for Latin → CJK", () => { + // Last consumed: "y" (Latin), completion: "音" (CJK) + // → different scripts, no separator needed in auto mode. + const result = matchGrammarCompletion(grammar, "play x"); + expect(result.completions).toEqual(["音楽"]); + expect(result.matchedPrefixLength).toBe(4); + expect(result.needsSeparator).toBeUndefined(); + }); + }); + }); +}); diff --git a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts new file mode 100644 index 0000000000..5e686523af --- /dev/null +++ b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts @@ -0,0 +1,480 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { loadGrammarRules } from "../src/grammarLoader.js"; +import { matchGrammarCompletion } from "../src/grammarMatcher.js"; + +describe("Grammar Completion - longest match property", () => { + describe("three sequential parts", () => { + // Verifies that after matching 2 of 3 parts, completion is the 3rd part. + const g = [ + ` = $(a:) $(b:) $(c:) -> { a, b, c };`, + ` = first -> "a";`, + ` = second -> "b";`, + ` = third -> "c";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("completes first part for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toContain("first"); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("completes second part after first matched", () => { + const result = matchGrammarCompletion(grammar, "first"); + expect(result.completions).toContain("second"); + expect(result.matchedPrefixLength).toBe(5); + }); + + it("completes second part after first matched with space", () => { + const result = matchGrammarCompletion(grammar, "first "); + expect(result.completions).toContain("second"); + expect(result.matchedPrefixLength).toBe(5); + }); + + it("completes third part after first two matched", () => { + const result = matchGrammarCompletion(grammar, "first second"); + expect(result.completions).toContain("third"); + expect(result.matchedPrefixLength).toBe(12); + }); + + it("completes third part after first two matched with space", () => { + const result = matchGrammarCompletion(grammar, "first second "); + expect(result.completions).toContain("third"); + expect(result.matchedPrefixLength).toBe(12); + }); + + it("no completion for exact full match", () => { + const result = matchGrammarCompletion( + grammar, + "first second third", + ); + expect(result.completions).toHaveLength(0); + // Exact match records the full consumed length. + expect(result.matchedPrefixLength).toBe(18); + }); + + it("partial prefix of third part completes correctly", () => { + const result = matchGrammarCompletion(grammar, "first second th"); + expect(result.completions).toContain("third"); + expect(result.matchedPrefixLength).toBe(12); + }); + }); + + describe("four sequential parts", () => { + const g = [ + ` = $(a:) $(b:) $(c:) $(d:) -> { a, b, c, d };`, + ` = alpha -> "a";`, + ` = bravo -> "b";`, + ` = charlie -> "c";`, + ` = delta -> "d";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("completes delta after three parts matched", () => { + const result = matchGrammarCompletion( + grammar, + "alpha bravo charlie", + ); + expect(result.completions).toContain("delta"); + expect(result.matchedPrefixLength).toBe(19); + }); + + it("completes charlie after two parts matched", () => { + const result = matchGrammarCompletion(grammar, "alpha bravo"); + expect(result.completions).toContain("charlie"); + expect(result.matchedPrefixLength).toBe(11); + }); + }); + + describe("competing rules - longer match wins", () => { + // Two rules: short (2 parts) and long (3 parts). + // When first two parts match, the short rule is a full match (no + // completion) and the long rule offers completion for the third part. + const g = [ + ` = $(a:) $(b:) -> { a, b };`, + ` = $(a:) $(b:) $(c:) -> { a, b, c };`, + ` = alpha -> "a";`, + ` = bravo -> "b";`, + ` = charlie -> "c";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("short rule is exact match; long rule offers completion", () => { + const result = matchGrammarCompletion(grammar, "alpha bravo"); + // Short rule matches exactly, no completion from it. + // Long rule matches alpha + bravo and offers "charlie". + expect(result.completions).toContain("charlie"); + expect(result.matchedPrefixLength).toBe(11); + }); + + it("both rules offer completions at same depth for first part", () => { + const result = matchGrammarCompletion(grammar, "alpha"); + // Both rules need "bravo" next. + expect(result.completions).toContain("bravo"); + expect(result.matchedPrefixLength).toBe(5); + }); + }); + + describe("competing rules - different next parts at same depth", () => { + // Two rules that share a prefix but diverge after. + const g = [ + ` = $(a:) suffix_x -> "rx";`, + ` = $(a:) suffix_y -> "ry";`, + ` = prefix -> "a";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("offers both alternatives after shared prefix", () => { + const result = matchGrammarCompletion(grammar, "prefix"); + expect(result.completions).toContain("suffix_x"); + expect(result.completions).toContain("suffix_y"); + expect(result.matchedPrefixLength).toBe(6); + }); + + it("alternative still offered even when one rule matches exactly", () => { + const result = matchGrammarCompletion(grammar, "prefix suffix_x"); + // Rule 1 (suffix_x) matches exactly at length 15. + // Rule 2's "suffix_y" at prefixLength 6 is filtered out + // because a longer match exists. + expect(result.completions).toHaveLength(0); + expect(result.matchedPrefixLength).toBe(15); + }); + }); + + describe("optional part followed by required part", () => { + const g = [ + ` = $(a:) $(b:)? $(c:) -> { a, c };`, + ` = begin -> "a";`, + ` = middle -> "b";`, + ` = finish -> "c";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("offers both optional and skip alternatives after first part", () => { + const result = matchGrammarCompletion(grammar, "begin"); + // Should offer "middle" (optional) and "finish" (skipping optional) + expect(result.completions).toContain("middle"); + expect(result.completions).toContain("finish"); + expect(result.matchedPrefixLength).toBe(5); + }); + + it("offers finish after optional part matched", () => { + const result = matchGrammarCompletion(grammar, "begin middle"); + expect(result.completions).toContain("finish"); + expect(result.matchedPrefixLength).toBe(12); + }); + + it("longest path wins for 'finish' completion when optional is matched", () => { + // When "begin middle" is typed: + // - Through-optional path: matches "begin" + "middle", + // offers "finish" at matchedPrefixLength=12. + // - Skip-optional path: matches "begin" (index=5), + // offers "finish" at matchedPrefixLength=5 — but this + // is filtered out because a longer match (12) exists. + const result = matchGrammarCompletion(grammar, "begin middle"); + expect(result.completions).toContain("finish"); + expect(result.matchedPrefixLength).toBe(12); + }); + }); + + describe("completion after number variable", () => { + const g = [ + ` = set volume $(n:number) percent -> { volume: n };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("completes 'percent' after number matched", () => { + const result = matchGrammarCompletion(grammar, "set volume 50"); + expect(result.completions).toContain("percent"); + expect(result.matchedPrefixLength).toBe(13); + }); + + it("completes 'percent' after number with space", () => { + const result = matchGrammarCompletion(grammar, "set volume 50 "); + expect(result.completions).toContain("percent"); + expect(result.matchedPrefixLength).toBe(13); + }); + + it("no completion for exact match", () => { + const result = matchGrammarCompletion( + grammar, + "set volume 50 percent", + ); + expect(result.completions).toHaveLength(0); + }); + }); + + describe("multiple alternatives in nested rule", () => { + // After matching a prefix, the nested rule has multiple alternatives + // for the next part, all at the same depth. + const g = [ + ` = play $(g:) -> { genre: g };`, + ` = rock -> "rock";`, + ` = pop -> "pop";`, + ` = jazz -> "jazz";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("offers all genre alternatives after 'play'", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toContain("rock"); + expect(result.completions).toContain("pop"); + expect(result.completions).toContain("jazz"); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("all alternatives offered even with partial trailing text", () => { + const result = matchGrammarCompletion(grammar, "play r"); + // All genre alternatives are reported after the longest + // complete match "play"; the caller filters by "r". + expect(result.completions).toContain("rock"); + expect(result.completions).toContain("pop"); + expect(result.completions).toContain("jazz"); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("all alternatives offered with 'p' trailing text", () => { + const result = matchGrammarCompletion(grammar, "play p"); + // All are reported; caller filters by "p". + expect(result.completions).toContain("pop"); + expect(result.completions).toContain("rock"); + expect(result.completions).toContain("jazz"); + }); + }); + + describe("wildcard between string parts - longest match", () => { + // Grammar: verb WILDCARD terminator + // Completion should offer terminator only after wildcard captures text. + const g = [ + ` = play $(name) by $(artist) -> { name, artist };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("offers wildcard property after 'play'", () => { + const result = matchGrammarCompletion(grammar, "play"); + // Wildcard is next, should have property completion + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + }); + + it("offers 'by' terminator after wildcard text", () => { + const result = matchGrammarCompletion(grammar, "play hello"); + expect(result.completions).toContain("by"); + }); + + it("offers artist wildcard property after 'by'", () => { + const result = matchGrammarCompletion(grammar, "play hello by"); + // After "by", the next part is the artist wildcard + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + }); + }); + + describe("deep nesting - three levels of rules", () => { + const g = [ + ` = $(x:) done -> { x };`, + ` = $(y:) -> y;`, + ` = deep -> "deep";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("completes 'deep' for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toContain("deep"); + expect(result.matchedPrefixLength).toBe(0); + }); + + it("completes 'done' after deeply nested match", () => { + const result = matchGrammarCompletion(grammar, "deep"); + expect(result.completions).toContain("done"); + expect(result.matchedPrefixLength).toBe(4); + }); + }); + + describe("repeat group (+) completion", () => { + // Grammar with ()+ repeat group using inline alternatives + const g = ` = hello (world | earth)+ done -> true;`; + const grammar = loadGrammarRules("test.grammar", g); + + it("offers 'hello' for empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toContain("hello"); + }); + + it("offers repeat alternatives after 'hello'", () => { + const result = matchGrammarCompletion(grammar, "hello"); + // After "hello", the ()+ group requires at least one match + expect(result.completions).toContain("world"); + expect(result.completions).toContain("earth"); + }); + + it("offers 'done' and repeat alternatives after first repeat match", () => { + const result = matchGrammarCompletion(grammar, "hello world"); + // After one repeat match, can repeat or proceed to "done" + expect(result.completions).toContain("done"); + // Also should offer repeat alternatives + expect( + result.completions.includes("world") || + result.completions.includes("earth"), + ).toBe(true); + }); + + it("offers 'done' after two repeat matches", () => { + const result = matchGrammarCompletion(grammar, "hello world earth"); + expect(result.completions).toContain("done"); + }); + }); + + describe("partial prefix vs longest match interaction", () => { + // Test that partial prefix matching from a shorter rule does not + // interfere with the longest match from a longer rule. + // + // Rule 1's string part "beta gamma" partially matches "beta" in the + // remaining text, while Rule 2 fully matches "beta" as a separate + // nested rule and offers "gamma" from the longer match. + // + // Both completions may appear because they're valid for different + // interpretations. The key property is that matchedPrefixLength + // reflects the longest consumed prefix. + const g = [ + ` = $(a:) beta gamma -> "r1";`, + ` = $(a:) $(b:) gamma -> "r2";`, + ` = alpha -> "a";`, + ` = beta -> "b";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("matchedPrefixLength reflects longest consumed prefix", () => { + const result = matchGrammarCompletion(grammar, "alpha beta"); + // Rule 2 matches alpha + beta = 10 chars. + // matchedPrefixLength should be at least 10 (from the longest match). + expect(result.matchedPrefixLength).toBeGreaterThanOrEqual(10); + // "gamma" must appear as completion from the longer match. + expect(result.completions).toContain("gamma"); + }); + + it("shorter partial prefix completion also appears (known behavior)", () => { + const result = matchGrammarCompletion(grammar, "alpha beta"); + // Rule 1 may produce "beta gamma" via isPartialPrefixOfStringPart + // since "beta" is a prefix of "beta gamma". This is expected: + // both rules produce valid completions for the input. + // We verify maxPrefixLength is from the longest match. + expect(result.matchedPrefixLength).toBe(10); + }); + }); + + describe("case insensitive matching for completions", () => { + const g = [ + ` = $(a:) World -> { a };`, + ` = Hello -> "hello";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("completes after case-insensitive match", () => { + const result = matchGrammarCompletion(grammar, "hello"); + expect(result.completions).toContain("World"); + expect(result.matchedPrefixLength).toBe(5); + }); + + it("completes after uppercase input", () => { + const result = matchGrammarCompletion(grammar, "HELLO"); + expect(result.completions).toContain("World"); + expect(result.matchedPrefixLength).toBe(5); + }); + + it("partial prefix is case insensitive", () => { + const result = matchGrammarCompletion(grammar, "hello WO"); + expect(result.completions).toContain("World"); + }); + }); + + describe("no spurious completions from unrelated rules", () => { + // Two completely different rules. Only the matching one should + // produce completions. + const g = [ + ` = play $(g:) -> { action: "play", genre: g };`, + ` = stop $(r:) -> { action: "stop", reason: r };`, + ` = rock -> "rock";`, + ` = now -> "now";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("only matching rule offers completions for 'play'", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toContain("rock"); + expect(result.completions).not.toContain("now"); + expect(result.completions).not.toContain("stop"); + }); + + it("only matching rule offers completions for 'stop'", () => { + const result = matchGrammarCompletion(grammar, "stop"); + expect(result.completions).toContain("now"); + expect(result.completions).not.toContain("rock"); + expect(result.completions).not.toContain("play"); + }); + + it("all first parts offered for unrelated input", () => { + const result = matchGrammarCompletion(grammar, "dance"); + // Nothing consumed; all first string parts from every rule + // are offered at prefixLength 0. The caller filters by + // the trailing text "dance". + expect(result.completions.sort()).toEqual(["play", "stop"]); + expect(result.matchedPrefixLength).toBe(0); + }); + }); + + describe("completion with entity wildcard", () => { + // Entity wildcards should produce property completions, not string + // completions, and matchedPrefixLength should indicate where the + // entity value begins. + const g = [ + `entity SongName;`, + ` = play $(song:SongName) next -> { song };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("entity wildcard produces property completion", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + expect(result.matchedPrefixLength).toBe(4); + }); + + it("string terminator after entity text", () => { + const result = matchGrammarCompletion(grammar, "play mysong"); + expect(result.completions).toContain("next"); + }); + }); + + describe("completion at boundary between consumed and remaining", () => { + // Verify that trailing separators (spaces) don't affect + // which part is offered for completion. + const g = [ + ` = $(a:) $(b:) -> { a, b };`, + ` = one -> "a";`, + ` = two -> "b";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("same completion with and without trailing space", () => { + const r1 = matchGrammarCompletion(grammar, "one"); + const r2 = matchGrammarCompletion(grammar, "one "); + const r3 = matchGrammarCompletion(grammar, "one "); + expect(r1.completions).toEqual(r2.completions); + expect(r2.completions).toEqual(r3.completions); + expect(r1.completions).toContain("two"); + }); + + it("matchedPrefixLength is stable regardless of trailing spaces", () => { + const r1 = matchGrammarCompletion(grammar, "one"); + const r2 = matchGrammarCompletion(grammar, "one "); + const r3 = matchGrammarCompletion(grammar, "one "); + // All should report the same matchedPrefixLength (end of + // consumed prefix, which is the "one" portion) + expect(r1.matchedPrefixLength).toBe(r2.matchedPrefixLength); + expect(r2.matchedPrefixLength).toBe(r3.matchedPrefixLength); + }); + }); +}); diff --git a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts index e898bff6fa..40867ddbb5 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts @@ -29,16 +29,19 @@ describe("Grammar Completion - matchedPrefixLength", () => { expect(result.matchedPrefixLength).toBe(0); }); - it("returns undefined matchedPrefixLength when no completions", () => { + it("returns all first parts for non-matching input", () => { const result = matchGrammarCompletion(grammar, "xyz"); - expect(result.completions).toHaveLength(0); - expect(result.matchedPrefixLength).toBeUndefined(); + // Nothing consumed; the first string part is offered + // unconditionally so the caller can filter by trailing text. + expect(result.completions).toEqual(["play music"]); + expect(result.matchedPrefixLength).toBe(0); }); - it("returns undefined matchedPrefixLength for exact match", () => { + it("returns matchedPrefixLength for exact match", () => { const result = matchGrammarCompletion(grammar, "play music"); expect(result.completions).toHaveLength(0); - expect(result.matchedPrefixLength).toBeUndefined(); + // Exact match now records the full consumed length. + expect(result.matchedPrefixLength).toBe(10); }); }); @@ -75,10 +78,10 @@ describe("Grammar Completion - matchedPrefixLength", () => { expect(result.matchedPrefixLength).toBe(4); }); - it("returns undefined matchedPrefixLength for complete match", () => { + it("returns matchedPrefixLength for complete match", () => { const result = matchGrammarCompletion(grammar, "play music"); expect(result.completions).toHaveLength(0); - expect(result.matchedPrefixLength).toBeUndefined(); + expect(result.matchedPrefixLength).toBe(10); }); }); @@ -178,7 +181,7 @@ describe("Grammar Completion - matchedPrefixLength", () => { it("returns no completions for exact match", () => { const result = matchGrammarCompletion(grammar, "再生音楽"); expect(result.completions).toHaveLength(0); - expect(result.matchedPrefixLength).toBeUndefined(); + expect(result.matchedPrefixLength).toBe(4); }); }); @@ -233,12 +236,15 @@ describe("Grammar Completion - matchedPrefixLength", () => { expect(result.needsSeparator).toBe(true); }); - it("does not report needsSeparator when trailing space exists", () => { + it("reports needsSeparator even when trailing space exists", () => { const result = matchGrammarCompletion(grammar, "play "); expect(result.completions).toEqual(["music"]); - // state.index > prefix.length because space is consumed, - // so needsSeparator is not set (no adjacent chars to check) - expect(result.needsSeparator).toBeUndefined(); + // matchedPrefixLength is 4 ("play"); the trailing space is + // unmatched content beyond that boundary. needsSeparator + // describes the boundary at matchedPrefixLength, so it is + // true (Latin "y" → "m" needs a separator). + expect(result.matchedPrefixLength).toBe(4); + expect(result.needsSeparator).toBe(true); }); it("does not report needsSeparator for empty input", () => { @@ -316,10 +322,9 @@ describe("Grammar Completion - matchedPrefixLength", () => { }); }); - describe("needsSeparator - wildcard entity with trailing separator", () => { + describe("needsSeparator - wildcard entity", () => { // Grammar where the completion is a wildcard entity (not a static string). - // "play " (with trailing space) should still report needsSeparator=true - // so the caller knows to strip the leading separator before filtering. + // needsSeparator describes the boundary at matchedPrefixLength. const g = [ `entity TrackName;`, ` = play $(name:TrackName) -> { actionName: "play", parameters: { name } };`, @@ -329,12 +334,14 @@ describe("Grammar Completion - matchedPrefixLength", () => { it("reports needsSeparator for 'play' before wildcard", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.properties?.length).toBeGreaterThan(0); + // matchedPrefixLength=4; boundary "y" → entity needs separator. expect(result.needsSeparator).toBe(true); }); - it("reports needsSeparator for 'play ' (separator already typed)", () => { - // pendingWildcard.start=4 < prefix.length=5; only separator between - // them → needsSeparator must still be true so the caller strips it. + it("reports needsSeparator for 'play ' before wildcard", () => { + // matchedPrefixLength=4 ("play"); the trailing space is + // beyond that boundary. needsSeparator describes the + // boundary at matchedPrefixLength: "y" → entity → true. const result = matchGrammarCompletion(grammar, "play "); expect(result.properties?.length).toBeGreaterThan(0); expect(result.needsSeparator).toBe(true); From cd6f2243aac8b2792b0a03a239d8d38dd84da74a Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Fri, 6 Mar 2026 15:18:17 -0800 Subject: [PATCH 05/51] Fix command completion to set needsSeparator like grammar matcher Command completion now sets needsSeparator on subcommand groups and backs startIndex past trailing whitespace, matching the grammar matcher's contract. This makes '@config ' (and similar inputs) show the completion menu through the generic path. - Add needsSeparator to Subcommands completion groups in completion.ts - Back up startIndex past whitespace when needsSeparator is set - Guard parameter startIndex with hasSubcommandCompletions flag - Change subcommand inclusion condition from suffix.length===0 to - Remove @-command special case from partialCompletionSession.ts - Update and expand tests for both backend and frontend --- .../dispatcher/src/command/completion.ts | 36 +++++++- .../dispatcher/test/completion.spec.ts | 55 ++++++++++-- .../renderer/src/partialCompletionSession.ts | 20 ----- .../test/partialCompletionSession.spec.ts | 87 ++++++++++++++++++- 4 files changed, 167 insertions(+), 31 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 1445d9e4d7..4f7beb9b35 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -223,17 +223,29 @@ export async function getCommandCompletion( } const descriptor = result.descriptor; + // Track whether subcommand alternatives are included alongside the + // default descriptor's parameters. When true, startIndex must stay + // at the command boundary (before the suffix) since needsSeparator + // strips the space and lets the trie match both subcommands and + // parameter values. + let hasSubcommandCompletions = false; if (descriptor !== undefined) { if ( - result.suffix.length === 0 && table !== undefined && + !result.matched && getDefaultSubCommandDescriptor(table) === result.descriptor ) { - // Match the default sub command. Includes additional subcommand names + // Resolved to the default sub command (not an explicit + // match). Include sibling subcommand names so the user + // can choose between them and the default's parameters. + // This covers both "@config " (suffix="") and "@config c" + // (suffix="c") — the trie filters by prefix either way. completions.push({ name: "Subcommands", completions: Object.keys(table.commands), + needsSeparator: true, }); + hasSubcommandCompletions = true; } const parameterCompletions = await getCommandParameterCompletion( descriptor, @@ -248,7 +260,9 @@ export async function getCommandCompletion( } } else { completions.push(...parameterCompletions.completions); - startIndex = parameterCompletions.startIndex; + if (!hasSubcommandCompletions) { + startIndex = parameterCompletions.startIndex; + } } } else { if (result.suffix.length !== 0) { @@ -259,6 +273,11 @@ export async function getCommandCompletion( completions.push({ name: "Subcommands", completions: Object.keys(table.commands), + needsSeparator: + result.parsedAppAgentName !== undefined || + result.commands.length > 0 + ? true + : undefined, }); if ( result.parsedAppAgentName === undefined && @@ -294,6 +313,17 @@ export async function getCommandCompletion( ? true : undefined; + // Like the grammar matcher, exclude trailing whitespace before + // startIndex when a separator is needed — the separator lives + // between the command anchor and the completion text, not inside + // the filter prefix. This handles both "@config " (trailing + // space) and "@config c" (space between command and partial token). + if (needsSeparator) { + while (startIndex > 0 && /\s/.test(input[startIndex - 1])) { + startIndex--; + } + } + const completionResult: CommandCompletionResult = { startIndex, completions, diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 70091b69f5..0f967b4d07 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -127,7 +127,8 @@ describe("Command Completion - startIndex", () => { ); expect(result).toBeDefined(); // "@comptest run " → suffix is "" after command resolution, - // and parameter parsing has no tokens so + // "run" is explicitly matched so no Subcommands group. + // parameter parsing has no tokens so // startIndex = inputLength - 0 = 14 expect(result!.startIndex).toBe(14); }); @@ -233,15 +234,59 @@ describe("Command Completion - startIndex", () => { }); describe("needsSeparator for command completions", () => { - it("returns undefined needsSeparator for @-command completions", async () => { + it("returns needsSeparator for subcommand completions at agent boundary", async () => { + const result = await getCommandCompletion("@comptest ", context); + expect(result).toBeDefined(); + // "run" is the default subcommand, so subcommand alternatives + // are included and the group has needsSeparator: true. + expect(result!.needsSeparator).toBe(true); + // startIndex excludes trailing whitespace (matching grammar + // matcher behaviour where prefixLength doesn't include the + // separator). + expect(result!.startIndex).toBe(9); + }); + + it("returns needsSeparator for resolved agent without trailing space", async () => { + const result = await getCommandCompletion("@comptest", context); + expect(result).toBeDefined(); + expect(result!.needsSeparator).toBe(true); + // No trailing whitespace to trim — startIndex stays at end + expect(result!.startIndex).toBe(9); + }); + + it("does not set needsSeparator at top level (@)", async () => { + const result = await getCommandCompletion("@", context); + expect(result).toBeDefined(); + // Top-level completions (agent names, system subcommands) + // follow '@' directly without a separator. + expect(result!.needsSeparator).toBeUndefined(); + }); + + it("does not set needsSeparator for parameter completions only", async () => { const result = await getCommandCompletion( - "@comptest run ", + "@comptest run bu", context, ); expect(result).toBeDefined(); - // @-command completions go through command handler, not grammar, - // so needsSeparator should not be set. + // Partial parameter token — only parameter completions returned, + // no subcommand group, so needsSeparator is not set. expect(result!.needsSeparator).toBeUndefined(); }); + + it("returns needsSeparator + subcommands for partial unmatched token", async () => { + const result = await getCommandCompletion("@comptest ne", context); + expect(result).toBeDefined(); + // "ne" doesn't match an explicit subcommand, so resolved to + // default — subcommand alternatives included. + expect(result!.needsSeparator).toBe(true); + const subcommands = result!.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommands).toBeDefined(); + expect(subcommands!.completions).toContain("nested"); + // startIndex backs up past the space to the agent boundary. + // "@comptest" = 9 chars. + expect(result!.startIndex).toBe(9); + }); }); }); diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index d483b5afe7..368de2b4c9 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -80,26 +80,6 @@ export class PartialCompletionSession { input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, ): void { - // @-commands: use word-boundary re-fetch logic so partial tokens - // (e.g. "@config c") don't poison noCompletion. - if (input.trimStart().startsWith("@")) { - if (this.reuseSession(input, getPosition)) { - return; - } - const lastSpaceIdx = input.lastIndexOf(" "); - if (/\s$/.test(input)) { - this.startNewSession(input, getPosition); - } else if (lastSpaceIdx >= 0) { - this.startNewSession( - input.substring(0, lastSpaceIdx + 1), - getPosition, - ); - } else { - this.startNewSession(input, getPosition); - } - return; - } - // Empty input: hide without fetching. Must come before reuseSession() // because reuseSession("") would match current="" and show stale items. if (input.trimStart().length === 0) { diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index 35436168c3..f85eb0e2de 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -437,16 +437,17 @@ describe("PartialCompletionSession — @command routing", () => { ); }); - test("@ command with partial word fetches up to last word boundary", () => { + test("@ command with partial word fetches full input (backend filters)", () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("@config c", getPos); - // Should fetch "@config " (up to last space), not "@config c" + // Backend receives full input and returns completions with the + // correct startIndex; no word-boundary truncation needed. expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( - "@config ", + "@config c", ); }); @@ -474,6 +475,86 @@ describe("PartialCompletionSession — @command routing", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); + + test("@ command: needsSeparator defers menu until space typed", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + // Backend returns subcommands with needsSeparator: true + // (anchor = "@config", subcommands follow after a space) + const result = makeCompletionResult(["clear", "theme"], 7, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // User types "@config" → completions loaded, menu deferred (no separator yet) + session.update("@config", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "clear" }), + ]), + ); + expect(menu.updatePrefix).not.toHaveBeenCalled(); + + // User types space → separator present, menu appears + session.update("@config ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + // No re-fetch — same session handles both states + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("@ command: typing after space filters within same session", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + // Backend: needsSeparator, anchor = "@config" + const result = makeCompletionResult(["clear", "theme"], 7, { + needsSeparator: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config", getPos); + await Promise.resolve(); + + // Type space + partial subcommand + session.update("@config cl", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("cl", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("@ command: undefined result enters EXHAUSTED (no re-fetch)", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(undefined); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@unknown", getPos); + await Promise.resolve(); // → EXHAUSTED + + // Still within anchor — no re-fetch + session.update("@unknownmore", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(menu.hide).toHaveBeenCalled(); + }); + + test("@ command: backspace past anchor after EXHAUSTED triggers re-fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(undefined); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@unknown", getPos); + await Promise.resolve(); // → EXHAUSTED with current="@unknown" + + // Backspace past anchor + session.update("@unknow", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@unknow"); + }); }); // ── getCompletionPrefix ─────────────────────────────────────────────────────── From 080000acc1699070bb9da8f9bcbfe99443e21b89 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Fri, 6 Mar 2026 16:41:40 -0800 Subject: [PATCH 06/51] Re-fetch completions when prefix uniquely satisfies a menu entry Allow empty input to fetch completions so clicking an empty input box shows the completion menu (e.g. '@'). When the user types text that exactly matches one trie entry and is not a prefix of any other, trigger a re-fetch to get the next level of completions. - Remove empty-input guard in PartialCompletionSession.update() - Change ISearchMenu.updatePrefix to return boolean (true = uniquely satisfied) - SearchMenu.updatePrefix returns true when exactly one trie match equals the prefix - reuseSession() uses the return value to trigger re-fetch - Update and expand tests for empty input and unique satisfaction --- .../renderer/src/partialCompletionSession.ts | 22 +++--- ts/packages/shell/src/renderer/src/search.ts | 16 +++-- .../test/partialCompletionSession.spec.ts | 72 +++++++++++++++++-- 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 368de2b4c9..a44ac2a37b 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -13,7 +13,9 @@ const debugError = registerDebug("typeagent:shell:partial:error"); export interface ISearchMenu { setChoices(choices: SearchMenuItem[]): void; - updatePrefix(prefix: string, position: SearchMenuPosition): void; + // Returns true when the prefix uniquely satisfies exactly one entry + // (exact match that is not a prefix of any other entry). + updatePrefix(prefix: string, position: SearchMenuPosition): boolean; hide(): void; isActive(): boolean; } @@ -80,13 +82,6 @@ export class PartialCompletionSession { input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, ): void { - // Empty input: hide without fetching. Must come before reuseSession() - // because reuseSession("") would match current="" and show stale items. - if (input.trimStart().length === 0) { - this.cancelMenu(); - return; - } - if (this.reuseSession(input, getPosition)) { return; } @@ -201,7 +196,16 @@ export class PartialCompletionSession { debug( `Partial completion update: '${prefix}' @ ${JSON.stringify(position)}`, ); - this.menu.updatePrefix(prefix, position); + const uniquelySatisfied = this.menu.updatePrefix( + prefix, + position, + ); + if (uniquelySatisfied) { + debug( + `Partial completion re-fetch: '${prefix}' uniquely satisfied`, + ); + return false; // RE-FETCH for next level of completions + } } else { this.menu.hide(); } diff --git a/ts/packages/shell/src/renderer/src/search.ts b/ts/packages/shell/src/renderer/src/search.ts index 214605813b..890a8d5332 100644 --- a/ts/packages/shell/src/renderer/src/search.ts +++ b/ts/packages/shell/src/renderer/src/search.ts @@ -49,22 +49,25 @@ export class SearchMenu { return this.trie.size(); } - public updatePrefix(prefix: string, position: SearchMenuPosition) { + public updatePrefix( + prefix: string, + position: SearchMenuPosition, + ): boolean { if (this.numChoices() === 0) { - return; + return false; } if (this.prefix === prefix && this.searchMenuUI !== undefined) { // No need to update existing searchMenuUI, just update the position. this.searchMenuUI.update({ position }); - return; + return false; } this.prefix = prefix; const items = this.trie.dataWithPrefix(normalizeMatchText(prefix)); - const showMenu = - items.length !== 0 && - (items.length !== 1 || items[0].matchText !== prefix); + const uniquelySatisfied = + items.length === 1 && items[0].matchText === prefix; + const showMenu = items.length !== 0 && !uniquelySatisfied; if (showMenu) { if (this.searchMenuUI === undefined) { @@ -76,6 +79,7 @@ export class SearchMenu { } else { this.hide(); } + return uniquelySatisfied; } public hide() { diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index f85eb0e2de..23cee10ec4 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -23,7 +23,9 @@ type MockMenu = { function makeMenu(): MockMenu { return { setChoices: jest.fn(), - updatePrefix: jest.fn(), + updatePrefix: jest + .fn() + .mockReturnValue(false), hide: jest.fn(), isActive: jest.fn().mockReturnValue(false), }; @@ -223,16 +225,74 @@ describe("PartialCompletionSession — state transitions", () => { ); }); - test("empty input hides menu and does not fetch", () => { + test("empty input fetches completions from backend", async () => { const menu = makeMenu(); - const dispatcher = makeDispatcher(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["@"], 0); + const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("", getPos); - session.update(" ", getPos); + await Promise.resolve(); - expect(dispatcher.getCommandCompletion).not.toHaveBeenCalled(); - expect(menu.hide).toHaveBeenCalled(); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith(""); + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "@" }), + ]), + ); + }); + + test("empty input: second update reuses session without re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + const result = makeCompletionResult(["@"], 0); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("empty input: unique match triggers re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + // updatePrefix returns true = uniquely satisfied + menu.updatePrefix.mockReturnValue(true); + const result = makeCompletionResult(["@"], 0); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("@", getPos); + + // "@" uniquely matches the only completion — triggers re-fetch + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); + }); + + test("empty input: ambiguous prefix does not re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + // updatePrefix returns false = not uniquely satisfied + menu.updatePrefix.mockReturnValue(false); + const result = makeCompletionResult(["@config", "@configure"], 0); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("@config", getPos); + + // "@config" is a prefix of "@configure" — reuse, no re-fetch + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); }); From 90923de236930cf8016a00623969f2064c20c541 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Fri, 6 Mar 2026 21:31:29 -0800 Subject: [PATCH 07/51] Improve needsSeparator docs and add clean command to CLAUDE.md - Clarify needsSeparator comment in CompletionGroup and CommandCompletionResult to describe the grammar relationship rather than implying the user hasn't typed a separator yet - Add 'pnpm run clean' to the CLAUDE.md build instructions - Fix minor whitespace alignment in CLAUDE.md --- ts/CLAUDE.md | 5 ++++- ts/packages/agentSdk/src/command.ts | 7 +++++-- ts/packages/dispatcher/types/src/dispatcher.ts | 7 +++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ts/CLAUDE.md b/ts/CLAUDE.md index a428dfc949..d34aa1e5bb 100644 --- a/ts/CLAUDE.md +++ b/ts/CLAUDE.md @@ -7,9 +7,12 @@ This is a **pnpm monorepo** rooted at `ts/`. All commands run from the `ts/` dir ```bash # Install & build pnpm i -pnpm run build # Uses fluid-build to build all packages +pnpm run build # Uses fluid-build to build all packages pnpm run build:shell # Build only the shell app and its dependencies +# Clean +pnpm run clean # works at the root and per package + # Test pnpm run test:local # All unit tests (*.spec.ts) across packages pnpm run test:live # Integration tests (*.test.ts) — requires API keys diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index 521e47d61e..2a045def19 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -61,8 +61,11 @@ export type CompletionGroup = { // inserts completions at this offset, replacing space-based heuristics // that fail for CJK and other non-space-delimited scripts. prefixLength?: number | undefined; - // True when a separator (e.g. space) must be inserted between the - // already-typed prefix and the completion text. + // True when the matched prefix and the completion text are + // structurally separated (e.g. a space between a command and its + // parameters). This describes the grammar relationship, not whether + // the user has already typed the separator — the frontend uses + // startIndex and this flag together to decide when to show the menu. needsSeparator?: boolean | undefined; }; diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index de4c7fe1af..4e01675172 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -54,8 +54,11 @@ export type CommandResult = { export type CommandCompletionResult = { startIndex: number; // index of first character of the filter text (after the last space) completions: CompletionGroup[]; // completions available at the current position - // True when a separator (e.g. space) must be inserted between the - // already-typed prefix and the completion text. + // True when the matched prefix and the completion text are + // structurally separated (e.g. a space between a command and its + // parameters). This describes the grammar relationship, not whether + // the user has already typed the separator — the frontend uses + // startIndex and this flag together to decide when to show the menu. needsSeparator?: boolean | undefined; }; From 143c4359bcce9daaa31b2e1f02cd4eba31abc9c7 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Sat, 7 Mar 2026 14:36:14 -0800 Subject: [PATCH 08/51] Refactor command completion: contract, bug fixes, and documentation Completion pipeline (dispatcher): - Add complete field to CommandCompletionResult for exhaustive sets - Eliminate undefined returns from getCommandCompletion (always return result) - Fix @com partial agent completion (suffix is filter text, not a gate) - Fix subcommand/startIndex: compute parameters first, conditionally add subcommands - Merge flat descriptor path into unified three-way if/else if/else - Add contract comments on getCommandCompletion, getCommandParameterCompletion, ParameterCompletionResult, and resolveCommand - 28 dispatcher completion tests (up from ~10) Shell (partialCompletionSession): - Fix uniquelySatisfied: always re-fetch for next-level completions regardless of complete flag (complete describes THIS level, not next) - Fix no-match fallthrough: re-fetch when complete=false and trie has zero matches (non-exhaustive set may have unlisted completions) - Update state machine documentation with four decision types - 43 shell tests (up from ~35) --- .../dispatcher/src/command/command.ts | 66 ++++ .../dispatcher/src/command/completion.ts | 189 ++++++++--- .../dispatcher/src/command/parameters.ts | 6 + .../dispatcher/test/completion.spec.ts | 304 +++++++++++++++++- .../dispatcher/rpc/src/dispatcherTypes.ts | 4 +- .../dispatcher/types/src/dispatcher.ts | 14 +- .../renderer/src/partialCompletionSession.ts | 99 ++++-- .../test/partialCompletionSession.spec.ts | 85 ++++- 8 files changed, 662 insertions(+), 105 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/command.ts b/ts/packages/dispatcher/dispatcher/src/command/command.ts index d18cfb132d..508d6afb7f 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/command.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/command.ts @@ -73,6 +73,72 @@ export function getDefaultSubCommandDescriptor( return table.defaultSubCommand; } +// +// ── resolveCommand contract ───────────────────────────────────────────────── +// +// Given a normalized input string (output of normalizeCommand — no leading +// "@", no leading whitespace), greedily resolves as many tokens as possible +// into an agent name, then a chain of subcommand names. Returns the point +// at which resolution stopped and the state accumulated so far. +// +// Resolution is greedy and exact-match only. Each token is consumed if and +// only if it matches a key in the current command table. As soon as a +// token doesn't match, resolution stops and that token (plus everything +// after it) becomes the suffix. If a token matches a leaf descriptor +// (not a sub-table), resolution also stops — the command is fully matched. +// +// When resolution stops at a table (not a leaf), the table's +// defaultSubCommand (if any) is used as the descriptor. This is the +// "resolved to default" case — matched is false. +// +// The first token is special: it is tested as an agent name first. +// - If it matches a known, enabled agent → that agent is selected +// and the token is consumed (not included in suffix). +// - Otherwise → the "system" agent is selected and the token is +// NOT consumed (rolled back into the input for subcommand matching). +// +// Return fields (see ResolveCommandResult): +// +// parsedAppAgentName +// The agent name parsed from input, or undefined. +// Set when the first token matched an agent name, OR +// when no subcommands were consumed (commands.length===0) +// — in this case the originally-matched agent name (which +// may be undefined) is returned. +// +// actualAppAgentName +// The agent that will handle the command. Either the +// parsed agent name or "system" as fallback. +// +// commands Ordered list of subcommand names that were successfully +// consumed. May be empty. +// +// suffix The remaining input after the last consumed token, +// with leading whitespace trimmed. This is the portion +// that was NOT resolved — it may be empty (fully resolved), +// a partial word (user is still typing), or multiple +// tokens (unrecognized input). Callers use it as +// parameter text (parseCommand) or filter text for +// completions (getCommandCompletion). +// +// table The CommandDescriptorTable at the point where resolution +// stopped. Undefined when the agent has no subcommands +// (flat agent with only a root descriptor). When defined, +// table.commands lists the valid subcommands that could +// have followed. +// +// descriptor The resolved CommandDescriptor, or undefined. It is +// defined when: +// (a) an explicit subcommand matched a leaf, or +// (b) we stopped at a table that has a defaultSubCommand. +// Undefined when the table has no default and the suffix +// didn't match any subcommand. +// +// matched true only when descriptor came from an explicit +// exact-match of the last consumed token against a leaf +// in the table. false when descriptor is the default +// subcommand or when descriptor is undefined. +// export async function resolveCommand( input: string, context: CommandHandlerContext, diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 4f7beb9b35..f01712c8b6 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -105,11 +105,23 @@ function collectFlags( return flagCompletions; } +// Internal result from parameter-level completion. +// +// `complete` uses a conservative heuristic: +// - false when the agent's getCommandCompletion was invoked (agents +// cannot yet signal whether their set is exhaustive). +// - false when a pending non-boolean flag accepts free-form input. +// - true when all positional args are filled and only enumerable +// flag names remain (a finite, known set). type ParameterCompletionResult = { completions: CompletionGroup[]; startIndex: number; + complete: boolean; }; +// Complete parameter values and flags for an already-resolved command +// descriptor. Returns undefined when the descriptor declares no +// parameters (the caller decides whether sibling subcommands suffice). async function getCommandParameterCompletion( descriptor: CommandDescriptor, context: CommandHandlerContext, @@ -146,6 +158,7 @@ async function getCommandParameterCompletion( agentCommandCompletions.push(pendingFlag); } + let agentInvoked = false; const agent = context.agents.getAppAgent(result.actualAppAgentName); if (agent.getCommandCompletion) { const { tokens, lastCompletableParam, lastParamImplicitQuotes } = @@ -172,6 +185,7 @@ async function getCommandParameterCompletion( sessionContext, ); completions.push(...agentGroups); + agentInvoked = true; } } @@ -185,13 +199,88 @@ async function getCommandParameterCompletion( const filterLength = trailingSpace || !lastToken ? 0 : lastToken.length; const startIndex = inputLength - filterLength; - return { completions, startIndex }; + // Determine whether the completion set is exhaustive. + // Agent-provided completions are conservatively treated as + // non-exhaustive since agents cannot yet signal exhaustiveness. + // When no agent is involved, completions are exhaustive if: + // - A pending boolean flag offers only ["true", "false"] + // - All positional args are filled and only enumerable flags remain + // Otherwise free-form text parameters make the set non-exhaustive. + let complete: boolean; + if (agentInvoked) { + complete = false; + } else if (pendingFlag !== undefined) { + // A non-boolean pending flag accepts free-form values. + // (Boolean flags are handled by getPendingFlag returning undefined + // and pushing ["true", "false"] into completions directly.) + complete = false; + } else { + // No agent, no pending flag. Exhaustive when there are no + // unfilled positional args (nextArgs is empty) and only + // flags remain — flag names are a finite, known set. + complete = params.nextArgs.length === 0; + } + + return { completions, startIndex, complete }; } +// +// ── getCommandCompletion contract ──────────────────────────────────────────── +// +// Given a partial user input string, returns the longest valid prefix, +// available completions from that point, and metadata about how they attach. +// +// Always returns a result — every input has a longest valid prefix +// (at minimum the empty string, startIndex=0). An empty completions +// array with complete=true means the command is fully specified and +// nothing further can follow. +// +// The portion of input after the prefix (the "suffix") is NOT a gate +// on whether completions are returned. The suffix is filter text: +// the caller feeds it to a trie to narrow the offered completions +// down to prefix-matches. Even if the suffix doesn't match anything +// today, the full set is still returned so the trie can decide. For +// example "@unknownagent " resolves only as far as "@" (startIndex=1); +// completions offer all agent names and system subcommands, and the +// trie filters "unknownagent " against them (yielding no matches, but +// that is the trie's job, not ours). +// +// Return fields (see CommandCompletionResult): +// +// startIndex Length of the longest resolved prefix. +// input[0..startIndex) was fully consumed by +// normalizeCommand → resolveCommand (→ parseParams); +// completions describe what can validly follow. +// May be overridden by a grammar-reported prefixLength +// from a CompletionGroup. Trailing whitespace before +// startIndex is stripped when needsSeparator is true. +// +// completions Array of CompletionGroups from up to three sources: +// (a) built-in command / subcommand / agent-name lists, +// (b) flag names from the descriptor's ParameterDefinitions, +// (c) agent-provided groups via the agent's +// getCommandCompletion callback. +// +// needsSeparator +// true when the prefix and completions are structurally +// separated (e.g. a space between a command and its +// parameters). Aggregated: true if *any* group reports +// needsSeparator. +// +// complete true when the returned completions are the *exhaustive* +// set of valid continuations after the prefix. When true +// and the user types something that doesn't prefix-match +// any completion, the caller can skip re-fetching because +// no other valid input exists. Subcommand and agent-name +// lists are always exhaustive. Parameter completions are +// exhaustive only when no agent was invoked and no +// free-form positional args remain unfilled — see +// ParameterCompletionResult for the heuristic. +// export async function getCommandCompletion( input: string, context: CommandHandlerContext, -): Promise { +): Promise { try { debug(`Command completion start: '${input}'`); @@ -202,11 +291,6 @@ export async function getCommandCompletion( const result = await resolveCommand(partialCommand, context); const table = result.table; - if (table === undefined) { - // Unknown app agent, or appAgent doesn't support commands - // Return undefined to indicate no more completions for this prefix. - return undefined; - } // The parse-derived startIndex: command resolution consumed // everything up to the suffix; within the suffix, parameter @@ -223,53 +307,66 @@ export async function getCommandCompletion( } const descriptor = result.descriptor; - // Track whether subcommand alternatives are included alongside the - // default descriptor's parameters. When true, startIndex must stay - // at the command boundary (before the suffix) since needsSeparator - // strips the space and lets the trie match both subcommands and - // parameter values. - let hasSubcommandCompletions = false; + let complete = true; if (descriptor !== undefined) { - if ( + // Get parameter completions first — we need to know + // whether parameters consumed past the command boundary + // before deciding if subcommand alternatives apply. + const parameterCompletions = await getCommandParameterCompletion( + descriptor, + context, + result, + input.length, + ); + + // Include sibling subcommand names when resolved to the + // default (not an explicit match), but only if parameter + // parsing hasn't consumed past the command boundary. + // Once the user has typed tokens that fill parameters + // (moving startIndex forward), they've committed to the + // default — subcommand names would be filtered against + // the wrong text at the wrong position. + const commandBoundary = input.length - result.suffix.length; + const addSubcommands = table !== undefined && !result.matched && - getDefaultSubCommandDescriptor(table) === result.descriptor - ) { - // Resolved to the default sub command (not an explicit - // match). Include sibling subcommand names so the user - // can choose between them and the default's parameters. - // This covers both "@config " (suffix="") and "@config c" - // (suffix="c") — the trie filters by prefix either way. + getDefaultSubCommandDescriptor(table) === descriptor && + (parameterCompletions === undefined || + parameterCompletions.startIndex <= commandBoundary); + + if (addSubcommands) { completions.push({ name: "Subcommands", completions: Object.keys(table.commands), needsSeparator: true, }); - hasSubcommandCompletions = true; } - const parameterCompletions = await getCommandParameterCompletion( - descriptor, - context, - result, - input.length, - ); + if (parameterCompletions === undefined) { - if (completions.length === 0) { - // No more completion, return undefined; - return undefined; - } + // Descriptor has no parameters. If subcommand + // alternatives were added above, they are the + // exhaustive set; otherwise the command is fully + // specified with nothing more to type. } else { completions.push(...parameterCompletions.completions); - if (!hasSubcommandCompletions) { + if (!addSubcommands) { startIndex = parameterCompletions.startIndex; } + complete = parameterCompletions.complete; } - } else { - if (result.suffix.length !== 0) { - // Unknown command - // Return undefined to indicate no more completions for this prefix. - return undefined; - } + } else if (table !== undefined) { + // descriptor is undefined: the suffix didn't resolve to any + // known command or subcommand. startIndex already points to + // where resolution stopped (the start of the suffix), so we + // offer every valid continuation from that point — subcommand + // names and (when at the root) agent names. The suffix is + // filter text for the caller's trie, not a reason to suppress + // completions. Examples: + // "@com" → suffix="com", completions include + // agent names + subcommands (trie + // narrows to "comptest", etc.) + // "@unknownagent " → suffix="unknownagent ", same set + // (trie finds no match — that's fine) completions.push({ name: "Subcommands", completions: Object.keys(table.commands), @@ -293,6 +390,10 @@ export async function getCommandCompletion( ), }); } + } else { + // Both table and descriptor are undefined — the agent + // returned no commands at all. Nothing to add; + // completions stays empty, complete stays true. } // Allow grammar-reported prefixLength (from groups) to override @@ -328,12 +429,20 @@ export async function getCommandCompletion( startIndex, completions, needsSeparator, + complete, }; debug(`Command completion result:`, completionResult); return completionResult; } catch (e: any) { debugError(`Command completion error: ${e}\n${e.stack}`); - return undefined; + // On error, return a safe default — don't claim exhaustiveness + // since we don't know what went wrong. + return { + startIndex: 0, + completions: [], + needsSeparator: undefined, + complete: false, + }; } } diff --git a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts index 9fae97e38a..4a9266e92b 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts @@ -136,6 +136,12 @@ function parseValueToken( } } +// Tokenizes and parses a parameter string into typed flags and positional +// arguments according to the given parameter definitions. Supports quoted +// values, boolean/number/string/json types, multi-value flags and arguments, +// implicit-quote (rest-of-line) arguments, and a `--` separator. When +// `partial` is true, parsing errors are silently ignored so the function can +// be used for live completions on incomplete input. export function parseParams( parameters: string, paramDefs: T, diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 0f967b4d07..f9c3dede45 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -68,6 +68,26 @@ const handlers = { }, }, }, + noop: { + description: "No-params command", + run: async () => {}, + }, + flagsonly: { + description: "Flags-only command", + parameters: { + flags: { + debug: { + description: "Enable debug", + type: "boolean" as const, + }, + level: { + description: "Log level", + type: "number" as const, + }, + }, + }, + run: async () => {}, + }, }, } as const; @@ -80,18 +100,65 @@ const agent: AppAgent = { ...getCommandInterface(handlers), }; -const testCompletionAgentProvider: AppAgentProvider = { - getAppAgentNames: () => ["comptest"], +// --------------------------------------------------------------------------- +// Flat agent — returns a single CommandDescriptor (no subcommand table) +// --------------------------------------------------------------------------- +const flatHandlers = { + description: "Flat agent with params but no subcommands", + parameters: { + args: { + target: { + description: "Build target", + }, + }, + flags: { + release: { + description: "Release build", + type: "boolean" as const, + }, + }, + }, + run: async () => {}, +} as const; + +const flatConfig: AppAgentManifest = { + emojiChar: "📦", + description: "Flat completion test", +}; + +const flatAgent: AppAgent = { + ...getCommandInterface(flatHandlers), +}; + +// --------------------------------------------------------------------------- +// No-commands agent — getCommands returns undefined +// --------------------------------------------------------------------------- +const noCommandsConfig: AppAgentManifest = { + emojiChar: "🚫", + description: "Agent with no commands", +}; + +const noCommandsAgent: AppAgent = { + // getCommands not defined → resolveCommand sees descriptors=undefined +}; + +const testCompletionAgentProviderMulti: AppAgentProvider = { + getAppAgentNames: () => ["comptest", "flattest", "nocmdtest"], getAppAgentManifest: async (name: string) => { - if (name !== "comptest") throw new Error(`Unknown: ${name}`); - return config; + if (name === "comptest") return config; + if (name === "flattest") return flatConfig; + if (name === "nocmdtest") return noCommandsConfig; + throw new Error(`Unknown: ${name}`); }, loadAppAgent: async (name: string) => { - if (name !== "comptest") throw new Error(`Unknown: ${name}`); - return agent; + if (name === "comptest") return agent; + if (name === "flattest") return flatAgent; + if (name === "nocmdtest") return noCommandsAgent; + throw new Error(`Unknown: ${name}`); }, unloadAppAgent: async (name: string) => { - if (name !== "comptest") throw new Error(`Unknown: ${name}`); + if (!["comptest", "flattest", "nocmdtest"].includes(name)) + throw new Error(`Unknown: ${name}`); }, }; @@ -110,7 +177,7 @@ describe("Command Completion - startIndex", () => { translation: { enabled: false }, explainer: { enabled: false }, cache: { enabled: false }, - appAgentProviders: [testCompletionAgentProvider], + appAgentProviders: [testCompletionAgentProviderMulti], }); }); afterAll(async () => { @@ -131,6 +198,9 @@ describe("Command Completion - startIndex", () => { // parameter parsing has no tokens so // startIndex = inputLength - 0 = 14 expect(result!.startIndex).toBe(14); + // Agent getCompletion is invoked for the "task" arg → + // completions are not exhaustive. + expect(result!.complete).toBe(false); }); it("returns startIndex accounting for partial param for '@comptest run bu'", async () => { @@ -143,6 +213,10 @@ describe("Command Completion - startIndex", () => { // suffix is "bu", parameter parsing sees token "bu" (2 chars) // startIndex = 16 - 2 = 14 expect(result!.startIndex).toBe(14); + // "bu" consumes the "task" arg → nextArgs is empty. + // Agent is not invoked (bare word, no implicit quotes). + // Only flags remain (none defined) → exhaustive. + expect(result!.complete).toBe(true); }); it("returns startIndex for nested command '@comptest nested sub '", async () => { @@ -155,6 +229,8 @@ describe("Command Completion - startIndex", () => { // suffix is "" after command resolution; // parameter parsing has no tokens; startIndex = 21 - 0 = 21 expect(result!.startIndex).toBe(21); + // Unfilled "value" arg (free-form) → not exhaustive. + expect(result!.complete).toBe(false); }); it("returns startIndex for partial flag '@comptest nested sub --ver'", async () => { @@ -167,6 +243,8 @@ describe("Command Completion - startIndex", () => { // suffix is "--ver", parameter parsing sees token "--ver" (5 chars) // startIndex = 26 - 5 = 21 expect(result!.startIndex).toBe(21); + // Unfilled "value" arg → not exhaustive. + expect(result!.complete).toBe(false); }); }); @@ -181,6 +259,9 @@ describe("Command Completion - startIndex", () => { ); expect(prefixes).toBeDefined(); expect(prefixes!.completions).toContain("@"); + // Empty input normalizes to "{requestHandler} request" which + // has open parameters → not exhaustive. + expect(result!.complete).toBe(false); }); it("returns startIndex 0 for empty input", async () => { @@ -208,14 +289,48 @@ describe("Command Completion - startIndex", () => { expect(subcommands).toBeDefined(); expect(subcommands!.completions).toContain("run"); expect(subcommands!.completions).toContain("nested"); + // Default subcommand "run" has agent completions → not exhaustive. + expect(result!.complete).toBe(false); }); - it("returns undefined for unknown agent", async () => { + it("returns matching agent names for partial prefix '@com'", async () => { + const result = await getCommandCompletion("@com", context); + // "@com" → normalizeCommand strips '@' → "com" + // resolveCommand: "com" isn't an agent name → system agent, + // system has no defaultSubCommand → descriptor=undefined, + // suffix="com". Completions include both system subcommands + // and agent names; the trie filters "com" against them. + expect(result.startIndex).toBe(1); + const agentGroup = result.completions.find( + (g) => g.name === "Agent Names", + ); + expect(agentGroup).toBeDefined(); + expect(agentGroup!.completions).toContain("comptest"); + const subcommandGroup = result.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommandGroup).toBeDefined(); + expect(result.complete).toBe(true); + }); + + it("returns completions for unknown agent with startIndex at '@'", async () => { const result = await getCommandCompletion( "@unknownagent ", context, ); - expect(result).toBeUndefined(); + // "@unknownagent " → longest valid prefix is "@" + // (startIndex = 1). Completions offer system subcommands + // and agent names so the user can correct the typo. + expect(result.startIndex).toBe(1); + const agentGroup = result.completions.find( + (g) => g.name === "Agent Names", + ); + expect(agentGroup).toBeDefined(); + const subcommandGroup = result.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommandGroup).toBeDefined(); + expect(result.complete).toBe(true); }); }); @@ -230,6 +345,10 @@ describe("Command Completion - startIndex", () => { // means filter length = 0, so startIndex = 20 expect(result).toBeDefined(); expect(result!.startIndex).toBe(20); + // All positional args filled ("task" consumed "build"), + // no flags, agent not invoked (agentCommandCompletions + // is empty) → exhaustive. + expect(result!.complete).toBe(true); }); }); @@ -252,6 +371,8 @@ describe("Command Completion - startIndex", () => { expect(result!.needsSeparator).toBe(true); // No trailing whitespace to trim — startIndex stays at end expect(result!.startIndex).toBe(9); + // Default subcommand has agent completions → not exhaustive. + expect(result!.complete).toBe(false); }); it("does not set needsSeparator at top level (@)", async () => { @@ -260,6 +381,8 @@ describe("Command Completion - startIndex", () => { // Top-level completions (agent names, system subcommands) // follow '@' directly without a separator. expect(result!.needsSeparator).toBeUndefined(); + // Subcommand + agent name sets are finite → exhaustive. + expect(result!.complete).toBe(true); }); it("does not set needsSeparator for parameter completions only", async () => { @@ -289,4 +412,165 @@ describe("Command Completion - startIndex", () => { expect(result!.startIndex).toBe(9); }); }); + + describe("complete flag", () => { + it("returns empty completions for command with no parameters", async () => { + const result = await getCommandCompletion( + "@comptest noop ", + context, + ); + // "noop" has no parameters at all → nothing more to type. + // getCommandParameterCompletion returns undefined and + // no subcommand alternatives exist (explicit match) → + // empty completions with complete=true. + expect(result.completions).toHaveLength(0); + expect(result.complete).toBe(true); + }); + + it("complete=true for flags-only command with no args unfilled", async () => { + const result = await getCommandCompletion( + "@comptest flagsonly ", + context, + ); + expect(result).toBeDefined(); + // No positional args, only flags. nextArgs is empty. + // No agent getCompletion. Flags are a finite set → exhaustive. + expect(result!.complete).toBe(true); + const flags = result!.completions.find( + (g) => g.name === "Command Flags", + ); + expect(flags).toBeDefined(); + expect(flags!.completions).toContain("--debug"); + expect(flags!.completions).toContain("--level"); + }); + + it("complete=true for boolean flag pending", async () => { + const result = await getCommandCompletion( + "@comptest nested sub --verbose ", + context, + ); + expect(result).toBeDefined(); + // --verbose is boolean; getPendingFlag pushed ["true", "false"] + // and returned undefined (not a pending non-boolean flag). + // nextArgs still has "value" unfilled → complete = false. + expect(result!.complete).toBe(false); + }); + + it("complete=false when agent completions are invoked", async () => { + const result = await getCommandCompletion( + "@comptest run ", + context, + ); + expect(result).toBeDefined(); + // Agent getCompletion is invoked → conservatively not exhaustive. + expect(result!.complete).toBe(false); + }); + + it("complete=false for unfilled positional args without agent", async () => { + const result = await getCommandCompletion( + "@comptest nested sub ", + context, + ); + expect(result).toBeDefined(); + // "value" arg is unfilled, no agent getCompletion → not exhaustive + // (free-form text). + expect(result!.complete).toBe(false); + }); + + it("complete=true for flags-only after one flag is set", async () => { + const result = await getCommandCompletion( + "@comptest flagsonly --debug true ", + context, + ); + expect(result).toBeDefined(); + // --debug is consumed; only --level remains. Still a finite set. + expect(result!.complete).toBe(true); + }); + }); + + describe("flat descriptor (no subcommand table)", () => { + it("returns parameter completions for flat agent", async () => { + const result = await getCommandCompletion("@flattest ", context); + // flattest has no subcommand table (table===undefined), + // but its descriptor has parameters (args + flags). + // Should return flag completions. + const flags = result.completions.find( + (g) => g.name === "Command Flags", + ); + expect(flags).toBeDefined(); + expect(flags!.completions).toContain("--release"); + // Unfilled "target" arg → not exhaustive. + expect(result.complete).toBe(false); + }); + + it("returns correct startIndex for flat agent with partial token", async () => { + const result = await getCommandCompletion( + "@flattest --rel", + context, + ); + // "@flattest --rel" (15 chars) + // startIndex = 15 - 5 ("--rel") = 10 + expect(result.startIndex).toBe(10); + expect(result.complete).toBe(false); + }); + + it("falls back to system for agent with no commands", async () => { + const result = await getCommandCompletion("@nocmdtest ", context); + // nocmdtest has no getCommands → not command-enabled → + // resolveCommand falls back to system agent. System has + // a subcommand table, so we get system subcommands. + const subcommands = result.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommands).toBeDefined(); + expect(subcommands!.completions.length).toBeGreaterThan(0); + }); + }); + + describe("subcommands dropped when parameters consume past boundary", () => { + it("drops subcommands when default command parameter is filled", async () => { + const result = await getCommandCompletion( + "@comptest build ", + context, + ); + // "@comptest build " (16 chars) + // Resolves to default "run" (not explicit match). + // "build" fills the "task" arg, trailing space moves + // startIndex to 16 — past the command boundary (10). + // Subcommand names are no longer relevant at this + // position; only parameter completions remain. + const subcommands = result.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommands).toBeUndefined(); + expect(result.startIndex).toBe(16); + // All positional args filled, no flags → exhaustive. + expect(result.complete).toBe(true); + }); + + it("keeps subcommands when at the command boundary", async () => { + const result = await getCommandCompletion("@comptest ", context); + // "@comptest " (10 chars) + // Resolves to default "run" — suffix is empty, parameter + // startIndex equals commandBoundary → subcommands included. + const subcommands = result.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommands).toBeDefined(); + expect(subcommands!.completions).toContain("run"); + expect(subcommands!.completions).toContain("nested"); + }); + + it("keeps subcommands when partial token is at the boundary", async () => { + const result = await getCommandCompletion("@comptest ne", context); + // "@comptest ne" — suffix is "ne", parameter parsing sees + // one partial token but startIndex = 10 (command boundary + // for needsSeparator stripping) → subcommands included. + const subcommands = result.completions.find( + (g) => g.name === "Subcommands", + ); + expect(subcommands).toBeDefined(); + expect(subcommands!.completions).toContain("nested"); + }); + }); }); diff --git a/ts/packages/dispatcher/rpc/src/dispatcherTypes.ts b/ts/packages/dispatcher/rpc/src/dispatcherTypes.ts index 3e8f30c6ba..20a45a0c9d 100644 --- a/ts/packages/dispatcher/rpc/src/dispatcherTypes.ts +++ b/ts/packages/dispatcher/rpc/src/dispatcherTypes.ts @@ -40,9 +40,7 @@ export type DispatcherInvokeFunctions = { propertyName: string, ): Promise; - getCommandCompletion( - prefix: string, - ): Promise; + getCommandCompletion(prefix: string): Promise; checkCache(request: string): Promise; diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index 4e01675172..3f72e11ede 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -52,7 +52,10 @@ export type CommandResult = { }; export type CommandCompletionResult = { - startIndex: number; // index of first character of the filter text (after the last space) + // Length of the longest valid (parsable) prefix of the input. + // input[0..startIndex) is fully resolved; completions describe + // what can follow after that prefix. + startIndex: number; completions: CompletionGroup[]; // completions available at the current position // True when the matched prefix and the completion text are // structurally separated (e.g. a space between a command and its @@ -60,6 +63,11 @@ export type CommandCompletionResult = { // the user has already typed the separator — the frontend uses // startIndex and this flag together to decide when to show the menu. needsSeparator?: boolean | undefined; + // True when the completions listed are the exhaustive set of valid + // continuations after the prefix. When true and the user types + // something that doesn't prefix-match any completion, the caller + // can skip refetching since no other valid input exists. + complete: boolean; }; export type AppAgentStatus = { @@ -164,9 +172,7 @@ export interface Dispatcher { ): Promise; // APIs to get command completion for intellisense like functionality. - getCommandCompletion( - prefix: string, - ): Promise; + getCommandCompletion(prefix: string): Promise; // Check if a request can be handled by cache without executing checkCache(request: string): Promise; diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index a44ac2a37b..764b5c8881 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -21,9 +21,7 @@ export interface ISearchMenu { } export interface ICompletionDispatcher { - getCommandCompletion( - input: string, - ): Promise; + getCommandCompletion(input: string): Promise; } // PartialCompletionSession manages the state machine for command completion. @@ -38,12 +36,22 @@ export interface ICompletionDispatcher { // - Completion result fields (noCompletion, needsSeparator) are stored as-is // from the backend response and never mutated as the user keeps typing. // reuseSession() reads them to decide whether to show, hide, or re-fetch. -// - reuseSession() makes exactly three kinds of decisions: +// - reuseSession() makes exactly four kinds of decisions: // 1. Re-fetch — input has moved past what the current result covers -// 2. Show/update menu — input satisfies the result's constraints -// 3. Hide menu, keep session — input doesn't satisfy constraints yet -// (separator not typed, or no completions exist), but is still -// within the anchor so a re-fetch would return the same result. +// 2. Show/update menu — input satisfies the result's constraints; +// trie filters the loaded completions against the typed prefix. +// 3. Hide menu, keep session — input is within the anchor but the +// result's constraints aren't satisfied yet (separator not typed, +// or no completions exist). A re-fetch would return the same result. +// 4. Uniquely satisfied — the user has exactly typed one completion +// entry (and it is not a prefix of any other). Always re-fetch to +// get the NEXT level's completions (e.g. agent name → subcommands). +// This re-fetch is unconditional: `complete` is irrelevant here +// because it describes THIS level's exhaustiveness, not the next's. +// - The `complete` flag controls the no-match fallthrough: when the trie +// has zero matches for the typed prefix: +// complete=true → reuse (exhaustive set, nothing else exists) +// complete=false → re-fetch (set is partial, backend may know more) // - The anchor (`current`) is never advanced after a result is received. // When `needsSeparator` is true the separator is stripped from the raw // prefix before being passed to the menu, so the trie still matches. @@ -64,6 +72,19 @@ export class PartialCompletionSession { // the raw prefix without mutating `current`. private needsSeparator: boolean = false; + // When true, the completion set returned by the backend is exhaustive + // for THIS level of the command hierarchy. This affects one decision + // in reuseSession(): + // + // No trie matches: complete=true → reuse (nothing else exists, no + // point re-fetching); complete=false → re-fetch (the backend may + // know about completions we haven't loaded). + // + // Notably, `complete` does NOT suppress the uniquelySatisfied re-fetch. + // uniquelySatisfied means the user needs the NEXT level's completions, + // which is a different question from whether THIS level is exhaustive. + private complete: boolean = false; + // The in-flight completion request, or undefined when settled. private completionP: | Promise @@ -95,6 +116,7 @@ export class PartialCompletionSession { this.current = undefined; this.noCompletion = false; this.needsSeparator = false; + this.complete = false; this.cancelMenu(); } @@ -102,6 +124,7 @@ export class PartialCompletionSession { public resetToIdle(): void { this.current = undefined; this.needsSeparator = false; + this.complete = false; } // Returns the text typed after the anchor (`current`), or undefined when @@ -135,8 +158,14 @@ export class PartialCompletionSession { // HIDE+KEEP — input is within the anchor but the result's constraints // aren't satisfied yet (no completions, or separator not // typed); hide the menu but don't re-fetch (return true). - // SHOW — constraints satisfied; update/show the menu (return - // isActive() so callers can re-fetch when the trie is empty). + // UNIQUE — prefix exactly matches one entry and is not a prefix of + // any other; re-fetch for the NEXT level (return false). + // Unconditional — `complete` is irrelevant here. + // SHOW — constraints satisfied; update the menu. The final + // return is `this.complete || this.menu.isActive()`: + // reuse when the trie still has matches, or when the set + // is exhaustive (nothing new to fetch). Re-fetch only + // when the trie is empty AND the set is non-exhaustive. private reuseSession( input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, @@ -196,11 +225,11 @@ export class PartialCompletionSession { debug( `Partial completion update: '${prefix}' @ ${JSON.stringify(position)}`, ); - const uniquelySatisfied = this.menu.updatePrefix( - prefix, - position, - ); + const uniquelySatisfied = this.menu.updatePrefix(prefix, position); if (uniquelySatisfied) { + // The user has typed text that exactly matches one completion + // and is not a prefix of any other. We need the NEXT level's + // completions (e.g. agent name → subcommands), so re-fetch. debug( `Partial completion re-fetch: '${prefix}' uniquely satisfied`, ); @@ -210,11 +239,15 @@ export class PartialCompletionSession { this.menu.hide(); } - // Always reuse: input is within the anchor and we have loaded - // completions. Even if the trie has no matches for the current - // prefix (menu hidden), backspacing may restore matches without - // needing a re-fetch. - return true; + // When the menu is still active (trie has matches) we always + // reuse — the loaded completions are still useful. When there are + // NO matches, the decision depends on `complete`: + // complete=true → the set is exhaustive; the user typed past all + // valid continuations, so re-fetching won't help. + // complete=false → the set is NOT exhaustive; the user may have + // typed something valid that wasn't loaded, so + // re-fetch with the longer input. + return this.complete || this.menu.isActive(); } // Start a new completion session: issue backend request and process result. @@ -227,6 +260,7 @@ export class PartialCompletionSession { this.current = input; this.noCompletion = false; this.needsSeparator = false; + this.complete = false; this.menu.setChoices([]); const completionP = this.dispatcher.getCommandCompletion(input); this.completionP = completionP; @@ -239,20 +273,9 @@ export class PartialCompletionSession { this.completionP = undefined; debug(`Partial completion result: `, result); - if (result === undefined) { - debug( - `Partial completion skipped: No completions for '${input}'`, - ); - this.noCompletion = true; - return; - } - const partial = - result.startIndex >= 0 && result.startIndex <= input.length - ? input.substring(0, result.startIndex) - : input; - this.current = partial; this.needsSeparator = result.needsSeparator === true; + this.complete = result.complete; // Build completions preserving backend group order. const completions: SearchMenuItem[] = []; @@ -278,11 +301,23 @@ export class PartialCompletionSession { if (completions.length === 0) { debug( - `Partial completion skipped: No current completions for '${partial}'`, + `Partial completion skipped: No completions for '${input}'`, ); + // Keep this.current at the value startNewSession set + // (the full input) so the EXHAUSTED anchor covers the + // entire typed text. + this.noCompletion = true; return; } + // Anchor the session at the resolved prefix so + // subsequent keystrokes filter within the trie. + const partial = + result.startIndex >= 0 && result.startIndex <= input.length + ? input.substring(0, result.startIndex) + : input; + this.current = partial; + this.menu.setChoices(completions); // Re-run update with captured input to show the menu (or defer diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index 23cee10ec4..73effd0d46 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -38,7 +38,12 @@ type MockDispatcher = { }; function makeDispatcher( - result: CommandCompletionResult | undefined = undefined, + result: CommandCompletionResult = { + startIndex: 0, + completions: [], + needsSeparator: undefined, + complete: true, + }, ): MockDispatcher { return { getCommandCompletion: jest @@ -56,7 +61,7 @@ function makeCompletionResult( opts: Partial = {}, ): CommandCompletionResult { const group: CompletionGroup = { name: "test", completions }; - return { startIndex, completions: [group], ...opts }; + return { startIndex, completions: [group], complete: false, ...opts }; } // ── State machine tests ─────────────────────────────────────────────────────── @@ -108,9 +113,9 @@ describe("PartialCompletionSession — state transitions", () => { expect(menu.updatePrefix).toHaveBeenCalled(); }); - test("PENDING → EXHAUSTED: undefined result suppresses re-fetch while input has same prefix", async () => { + test("PENDING → EXHAUSTED: empty result suppresses re-fetch while input has same prefix", async () => { const menu = makeMenu(); - const dispatcher = makeDispatcher(undefined); + const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("play", getPos); @@ -124,7 +129,7 @@ describe("PartialCompletionSession — state transitions", () => { test("EXHAUSTED → IDLE: backspace past anchor triggers a new fetch", async () => { const menu = makeMenu(); - const dispatcher = makeDispatcher(undefined); + const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("play", getPos); @@ -137,40 +142,63 @@ describe("PartialCompletionSession — state transitions", () => { expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("pla"); }); - test("ACTIVE → hide+keep: when trie has no matches, session is preserved (no re-fetch)", async () => { + test("ACTIVE → hide+keep: complete=true, trie has no matches — no re-fetch", async () => { const menu = makeMenu(); // isActive returns true on first call (after setChoices), false on second (all filtered) menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); - const result = makeCompletionResult(["song"], 5); + const result = makeCompletionResult(["song"], 5, { complete: true }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play ", getPos); await Promise.resolve(); // → ACTIVE, anchor = "play " - // User types more; trie returns no matches but input is within anchor + // User types more; trie returns no matches but input is within anchor. + // complete=true → exhaustive set, no point re-fetching. session.update("play xyz", getPos); - // No re-fetch: session is kept alive, menu is hidden expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); expect(menu.hide).toHaveBeenCalled(); }); - test("ACTIVE → backspace restores menu after no-match without re-fetch", async () => { + test("ACTIVE → re-fetch: complete=false, trie has no matches — re-fetches", async () => { + const menu = makeMenu(); + // isActive: true after initial load, false for "xyz" — non-exhaustive set + menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); + const result = makeCompletionResult(["song"], 5); // complete=false default + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play " + + // User types text with no trie match. complete=false → set is NOT + // exhaustive, so we should re-fetch in case the backend knows more. + session.update("play xyz", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play xyz", + ); + }); + + test("ACTIVE → backspace restores menu after no-match without re-fetch (complete=true)", async () => { const menu = makeMenu(); // isActive: true after initial load, false for "xyz" prefix, true again for "so" menu.isActive .mockReturnValueOnce(true) // initial reuseSession after result .mockReturnValueOnce(false) // "xyz" — trie no match .mockReturnValueOnce(true); // "so" — trie matches "song" - const result = makeCompletionResult(["song", "shuffle"], 5); + const result = makeCompletionResult(["song", "shuffle"], 5, { + complete: true, + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play ", getPos); await Promise.resolve(); // → ACTIVE, anchor = "play " - // User types non-matching text + // User types non-matching text. complete=true → no re-fetch. session.update("play xyz", getPos); expect(menu.hide).toHaveBeenCalled(); @@ -277,6 +305,25 @@ describe("PartialCompletionSession — state transitions", () => { expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); }); + test("empty input: unique match triggers re-fetch even when complete=true", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + menu.updatePrefix.mockReturnValue(true); + // complete=true means exhaustive at THIS level, but uniquelySatisfied + // means the user needs NEXT level completions — always re-fetch. + const result = makeCompletionResult(["@"], 0, { complete: true }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("@", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); + }); + test("empty input: ambiguous prefix does not re-fetch", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); @@ -330,6 +377,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 5, completions: [group1, group2], + complete: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -361,6 +409,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 0, completions: [group], + complete: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -389,6 +438,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 0, completions: [group], + complete: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -469,6 +519,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 0, completions: [{ name: "empty", completions: [] }], + complete: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -586,9 +637,9 @@ describe("PartialCompletionSession — @command routing", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("@ command: undefined result enters EXHAUSTED (no re-fetch)", async () => { + test("@ command: empty result enters EXHAUSTED (no re-fetch)", async () => { const menu = makeMenu(); - const dispatcher = makeDispatcher(undefined); + const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("@unknown", getPos); @@ -603,7 +654,7 @@ describe("PartialCompletionSession — @command routing", () => { test("@ command: backspace past anchor after EXHAUSTED triggers re-fetch", async () => { const menu = makeMenu(); - const dispatcher = makeDispatcher(undefined); + const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("@unknown", getPos); @@ -613,7 +664,9 @@ describe("PartialCompletionSession — @command routing", () => { session.update("@unknow", getPos); expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@unknow"); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "@unknow", + ); }); }); From 5ff8dfee05473cc33a60dd9087e11122b228713c Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Sat, 7 Mar 2026 15:32:36 -0800 Subject: [PATCH 09/51] Decouple agent name completions from fallback agent table Move the Agent Names completion group out of the else-if (table !== undefined) branch into an independent check after the three-way if/else if/else. The condition (parsedAppAgentName === undefined && commands.length === 0) is purely about resolution state, not about what the fallback agent provides. This ensures agent names are offered whenever the user hasn't typed a recognized agent, regardless of whether the fallback agent has a command table, a flat descriptor, or no commands at all. Tests: verify Agent Names presence/absence for @, @comptest, @nocmdtest. --- .../dispatcher/src/command/completion.ts | 42 +++++++++++-------- .../dispatcher/test/completion.spec.ts | 20 +++++++++ 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index f01712c8b6..051c0c0a1b 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -359,11 +359,12 @@ export async function getCommandCompletion( // known command or subcommand. startIndex already points to // where resolution stopped (the start of the suffix), so we // offer every valid continuation from that point — subcommand - // names and (when at the root) agent names. The suffix is - // filter text for the caller's trie, not a reason to suppress - // completions. Examples: + // names from the current table. Agent names are handled + // independently below. The suffix is filter text for the + // caller's trie, not a reason to suppress completions. + // Examples: // "@com" → suffix="com", completions include - // agent names + subcommands (trie + // subcommands + agent names (trie // narrows to "comptest", etc.) // "@unknownagent " → suffix="unknownagent ", same set // (trie finds no match — that's fine) @@ -376,26 +377,31 @@ export async function getCommandCompletion( ? true : undefined, }); - if ( - result.parsedAppAgentName === undefined && - result.commands.length === 0 - ) { - // Include the agent names - completions.push({ - name: "Agent Names", - completions: context.agents - .getAppAgentNames() - .filter((name) => - context.agents.isCommandEnabled(name), - ), - }); - } } else { // Both table and descriptor are undefined — the agent // returned no commands at all. Nothing to add; // completions stays empty, complete stays true. } + // Independently of which branch above ran, offer agent names + // when the user hasn't typed a recognized agent and hasn't + // navigated into a subcommand tree. This is decoupled from + // the three-way branch so it works regardless of whether the + // fallback agent has a command table. + if ( + result.parsedAppAgentName === undefined && + result.commands.length === 0 + ) { + completions.push({ + name: "Agent Names", + completions: context.agents + .getAppAgentNames() + .filter((name) => + context.agents.isCommandEnabled(name), + ), + }); + } + // Allow grammar-reported prefixLength (from groups) to override // the parse-derived startIndex. This handles CJK and other // non-space-delimited scripts where the grammar matcher is the diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index f9c3dede45..7052dc556f 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -291,6 +291,11 @@ describe("Command Completion - startIndex", () => { expect(subcommands!.completions).toContain("nested"); // Default subcommand "run" has agent completions → not exhaustive. expect(result!.complete).toBe(false); + // Agent was recognized → no agent names offered. + const agentGroup = result!.completions.find( + (g) => g.name === "Agent Names", + ); + expect(agentGroup).toBeUndefined(); }); it("returns matching agent names for partial prefix '@com'", async () => { @@ -381,6 +386,14 @@ describe("Command Completion - startIndex", () => { // Top-level completions (agent names, system subcommands) // follow '@' directly without a separator. expect(result!.needsSeparator).toBeUndefined(); + // Agent names are offered when no agent was recognized, + // independent of which branch (descriptor/table/neither) + // produced the subcommand completions. + const agentGroup = result!.completions.find( + (g) => g.name === "Agent Names", + ); + expect(agentGroup).toBeDefined(); + expect(agentGroup!.completions).toContain("comptest"); // Subcommand + agent name sets are finite → exhaustive. expect(result!.complete).toBe(true); }); @@ -524,6 +537,13 @@ describe("Command Completion - startIndex", () => { ); expect(subcommands).toBeDefined(); expect(subcommands!.completions.length).toBeGreaterThan(0); + // nocmdtest IS a recognized agent name (just not + // command-enabled), so parsedAppAgentName is set and + // agent names are NOT offered. + const agentGroup = result.completions.find( + (g) => g.name === "Agent Names", + ); + expect(agentGroup).toBeUndefined(); }); }); From 26a5f000b9f6367a44bc6df06704be8919210e0d Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Sun, 8 Mar 2026 08:02:13 -0700 Subject: [PATCH 10/51] Fix completion startIndex calculation using parse-position tracking - Add ParseParamsResult type extending ParsedCommandParams with remainderLength to report unconsumed parameter text - Track parser consumption position (lastGoodCurrLength) through flag name, flag+value, and argument parsing stages - Rewrite startIndex computation in getCommandParameterCompletion to use remainderLength instead of reverse-engineering from token lengths - Handle pending non-boolean flags correctly: consumed flag name keeps startIndex at full input length - Add tests for non-boolean flag without trailing space and unrecognized flag prefix as filter text --- .../dispatcher/src/command/completion.ts | 49 +++++++++++++------ .../dispatcher/src/command/parameters.ts | 21 +++++++- .../dispatcher/test/completion.spec.ts | 35 +++++++++++++ 3 files changed, 89 insertions(+), 16 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 051c0c0a1b..e04aaed9d0 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -15,7 +15,7 @@ import { getFlagType, resolveFlag, } from "@typeagent/agent-sdk/helpers/command"; -import { parseParams } from "./parameters.js"; +import { parseParams, ParseParamsResult } from "./parameters.js"; import { getDefaultSubCommandDescriptor, normalizeCommand, @@ -134,7 +134,11 @@ async function getCommandParameterCompletion( return undefined; } const flags = descriptor.parameters.flags; - const params = parseParams(result.suffix, descriptor.parameters, true); + const params: ParseParamsResult = parseParams( + result.suffix, + descriptor.parameters, + true, + ); const pendingFlag = getPendingFlag(params, flags, completions); const agentCommandCompletions: string[] = []; if (pendingFlag === undefined) { @@ -154,7 +158,8 @@ async function getCommandParameterCompletion( } } } else { - // get the potential values for the pending flag + // The last token is a recognized flag waiting for a value. + // Ask the agent for potential values for this flag. agentCommandCompletions.push(pendingFlag); } @@ -189,15 +194,31 @@ async function getCommandParameterCompletion( } } - // Compute startIndex from the parse position. The filter text is the - // last incomplete token the user is typing (or empty at a boundary). - const trailingSpace = /\s$/.test(result.suffix); - const lastToken = - params.tokens.length > 0 - ? params.tokens[params.tokens.length - 1] - : undefined; - const filterLength = trailingSpace || !lastToken ? 0 : lastToken.length; - const startIndex = inputLength - filterLength; + // Compute startIndex from how far parseParams consumed the suffix. + // remainderLength is the length of the (trimmed) parameter text + // that was NOT successfully parsed — everything before it is part + // of the longest valid prefix. + let startIndex = inputLength - params.remainderLength; + + // When everything was consumed and there is no trailing whitespace, + // the last *value* token may still be in progress — rewind to make + // it filter text. lastCompletableParam is only set when the final + // parsed entity was a parameter value (string arg or string flag + // value), so pending flags (name consumed, value missing) naturally + // keep startIndex at the fully-consumed position. + if ( + params.remainderLength === 0 && + !/\s$/.test(result.suffix) && + params.lastCompletableParam !== undefined + ) { + const lastToken = + params.tokens.length > 0 + ? params.tokens[params.tokens.length - 1] + : undefined; + if (lastToken !== undefined) { + startIndex -= lastToken.length; + } + } // Determine whether the completion set is exhaustive. // Agent-provided completions are conservatively treated as @@ -396,9 +417,7 @@ export async function getCommandCompletion( name: "Agent Names", completions: context.agents .getAppAgentNames() - .filter((name) => - context.agents.isCommandEnabled(name), - ), + .filter((name) => context.agents.isCommandEnabled(name)), }); } diff --git a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts index 4a9266e92b..9484111f8f 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts @@ -136,6 +136,16 @@ function parseValueToken( } } +// Extension of ParsedCommandParams that also reports how much of the +// parameter string was *not* consumed by the parser. Used internally by +// the completion layer to compute startIndex from parse position rather +// than by reverse-engineering filter text from token lengths. +export type ParseParamsResult = + ParsedCommandParams & { + /** Length of the (trimmed) parameter text left unconsumed. */ + remainderLength: number; + }; + // Tokenizes and parses a parameter string into typed flags and positional // arguments according to the given parameter definitions. Supports quoted // values, boolean/number/string/json types, multi-value flags and arguments, @@ -146,7 +156,7 @@ export function parseParams( parameters: string, paramDefs: T, partial: boolean = false, -): ParsedCommandParams { +): ParseParamsResult { // Use trimStart (not trim) so trailing whitespace is preserved for // completion: a trailing space signals that the last token is complete. let curr = partial ? parameters.trimStart() : parameters.trim(); @@ -198,6 +208,11 @@ export function parseParams( let lastCompletableParam: string | undefined = undefined; let lastParamImplicitQuotes: boolean = false; let advanceMultiple = false; + // Track how far the parser successfully consumed. Updated after + // each fully-resolved entity (flag name, flag+value, argument). + // On error in partial mode the value stays at the last-good + // position, giving the completion layer an accurate remainder. + let lastGoodCurrLength = curr.length; while (true) { try { // Save the rest for implicit quote arguments; @@ -216,6 +231,7 @@ export function parseParams( } const [name, flag] = flagInfo; const valueType = getFlagType(flag); + lastGoodCurrLength = curr.length; // flag name consumed let value: FlagValueTypes; const rollback = curr; const valueToken = nextToken(); @@ -256,6 +272,7 @@ export function parseParams( } parsedFlags[name] = value; } + lastGoodCurrLength = curr.length; // flag+value consumed if (valueType === "string") { lastCompletableParam = `--${name}`; lastParamImplicitQuotes = false; @@ -313,6 +330,7 @@ export function parseParams( parsedArgs[name].push(argValue); } } + lastGoodCurrLength = curr.length; // argument consumed if (argType === "string") { lastCompletableParam = name; lastParamImplicitQuotes = argDef.implicitQuotes ?? false; @@ -373,5 +391,6 @@ export function parseParams( lastCompletableParam, lastParamImplicitQuotes, nextArgs, + remainderLength: lastGoodCurrLength, }; } diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 7052dc556f..c083efad14 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -499,6 +499,41 @@ describe("Command Completion - startIndex", () => { // --debug is consumed; only --level remains. Still a finite set. expect(result!.complete).toBe(true); }); + + it("returns flag value completions for non-boolean flag without trailing space", async () => { + const result = await getCommandCompletion( + "@comptest flagsonly --level", + context, + ); + // "--level" is a recognized number flag — the entire input + // is the longest valid prefix. startIndex = input.length + // because the flag name is consumed, not filter text. + // Completions should offer the flag's values (if any), + // not flag names. + expect(result.startIndex).toBe(27); // full input consumed + const flags = result.completions.find( + (g) => g.name === "Command Flags", + ); + expect(flags).toBeUndefined(); // flag names not offered when pending + }); + + it("treats unrecognized flag prefix as filter text", async () => { + const result = await getCommandCompletion( + "@comptest flagsonly --lev", + context, + ); + // "--lev" doesn't resolve (exact match only), so parseParams + // leaves it unconsumed. startIndex points to where "--lev" + // starts — it is the filter text. + // "@comptest flagsonly " = 20 chars consumed. + expect(result.startIndex).toBe(20); + const flags = result.completions.find( + (g) => g.name === "Command Flags", + ); + expect(flags).toBeDefined(); + expect(flags!.completions).toContain("--debug"); + expect(flags!.completions).toContain("--level"); + }); }); describe("flat descriptor (no subcommand table)", () => { From c61b869f9f78433f98f5c25031e0669a3c7b6321 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Sun, 8 Mar 2026 10:50:50 -0700 Subject: [PATCH 11/51] Replace needsSeparator boolean with SeparatorMode enum Replace the boolean needsSeparator flag with a 4-value SeparatorMode enum across all completion types. The new type distinguishes: - "space": whitespace required (command subcommands) - "spacePunctuation": whitespace or punctuation (grammar boundaries) - "optional": separator accepted but not required (CJK, after @) - "none": no separator ([spacing=none] grammars) Default is "space" when omitted, so agents that forget to set the field get safe behavior (menu defers until whitespace is typed). Packages updated: - agentSdk: SeparatorMode type, CompletionGroup.separatorMode field - dispatcher/types: CommandCompletionResult.separatorMode - actionGrammar: GrammarSeparatorMode subset type, candidate/merge helpers - cache: CompletionResult.separatorMode, mergeSeparatorModes helper - dispatcher: aggregateSeparatorMode, @-level uses "optional" - shell: mode-aware separator detection with per-mode regex - cli: updated separator check - All corresponding tests updated --- .../actionGrammar/src/grammarMatcher.ts | 95 +++++++++++++++---- ts/packages/actionGrammar/src/index.ts | 2 + ...mmarCompletionCategory3bLimitation.spec.ts | 36 +++---- .../grammarCompletionPrefixLength.spec.ts | 66 ++++++------- ts/packages/agentSdk/src/command.ts | 27 ++++-- ts/packages/agentSdk/src/index.ts | 1 + ts/packages/cache/src/cache/grammarStore.ts | 34 ++++++- .../src/constructions/constructionCache.ts | 30 +++++- .../cache/test/mergeCompletionResults.spec.ts | 32 +++---- ts/packages/cli/src/commands/interactive.ts | 8 +- .../dispatcher/src/command/completion.ts | 65 +++++++++---- .../src/translation/requestCompletion.ts | 16 ++-- .../dispatcher/test/completion.spec.ts | 30 +++--- .../dispatcher/types/src/dispatcher.ts | 11 +-- .../renderer/src/partialCompletionSession.ts | 54 +++++++---- .../test/partialCompletionSession.spec.ts | 44 ++++----- 16 files changed, 364 insertions(+), 187 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 6c41a137c4..314a3e5a42 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -14,6 +14,11 @@ import { VarStringPart, } from "./grammarTypes.js"; +// Separator mode for grammar completion results. +// Structurally compatible with SeparatorMode from @typeagent/agent-sdk +// (actionGrammar does not depend on agentSdk). +export type GrammarSeparatorMode = "spacePunctuation" | "optional" | "none"; + const debugMatchRaw = registerDebug("typeagent:grammar:match"); const debugCompletion = registerDebug("typeagent:grammar:completion"); @@ -78,6 +83,49 @@ function requiresSeparator(a: string, b: string, mode: SpacingMode): boolean { } } +// Convert a per-candidate (needsSep, spacingMode) pair into a +// GrammarSeparatorMode value. When needsSep is true (separator +// required), the grammar always uses spacePunctuation separators. +// When needsSep is false, the mode tells us whether the boundary is +// "optional" (CJK/mixed, or SpacingMode "optional") or "none". +function candidateSeparatorMode( + needsSep: boolean, + spacingMode: SpacingMode, +): GrammarSeparatorMode { + if (needsSep) { + return "spacePunctuation"; + } + if (spacingMode === "none") { + return "none"; + } + return "optional"; +} + +// Merge a new candidate's separator mode into the running aggregate. +// The most restrictive mode wins: spacePunctuation > optional > none. +function mergeSeparatorMode( + current: GrammarSeparatorMode | undefined, + needsSep: boolean, + spacingMode: SpacingMode, +): GrammarSeparatorMode { + const candidateMode = candidateSeparatorMode(needsSep, spacingMode); + if (current === undefined) { + return candidateMode; + } + // "spacePunctuation" is the most restrictive — once set, stays. + if ( + current === "spacePunctuation" || + candidateMode === "spacePunctuation" + ) { + return "spacePunctuation"; + } + // "optional" is more restrictive than "none". + if (current === "optional" || candidateMode === "optional") { + return "optional"; + } + return "none"; +} + function isBoundarySatisfied( request: string, index: number, @@ -1033,15 +1081,15 @@ export type GrammarCompletionResult = { // before the completion point. The shell uses this to determine where // to insert/filter completions (replacing the space-based heuristic). matchedPrefixLength?: number | undefined; - // True when a separator (e.g. space) must be inserted between the - // content at `matchedPrefixLength` and the completion text. The - // caller should check the spacing rules between the last character - // of the matched prefix and the first character of each completion. - // Example: prefix="play", matchedPrefixLength=4, completion="music" - // → needsSeparator=true (Latin "y" → "m" requires a space). - // Example: prefix="再生", matchedPrefixLength=2, completion="音楽" - // → needsSeparator=false (CJK scripts don't require spaces). - needsSeparator?: boolean | undefined; + // What kind of separator is required between the content at + // `matchedPrefixLength` and the completion text. + // "spacePunctuation" — whitespace or punctuation required + // (Latin "y" → "m" requires a separator). + // "optional" — separator accepted but not required + // (CJK 再生 → 音楽 does not require a separator). + // "none" — no separator at all ([spacing=none] grammars). + // Omitted when no completions were generated. + separatorMode?: GrammarSeparatorMode | undefined; }; function getGrammarCompletionProperty( @@ -1144,10 +1192,10 @@ export function matchGrammar(grammar: Grammar, request: string) { * position the completion insertion point correctly (especially important * for non-space-separated scripts like CJK). * - * `needsSeparator` indicates whether a separator (e.g. a space) must be - * inserted between the content at `matchedPrefixLength` and the completion - * text. It is determined by the spacing rules between the last character - * of the matched prefix and the first character of the completion. + * `separatorMode` indicates what kind of separator is needed between the + * content at `matchedPrefixLength` and the completion text. It is + * determined by the spacing rules between the last character of the + * matched prefix and the first character of the completion. */ export function matchGrammarCompletion( grammar: Grammar, @@ -1169,11 +1217,13 @@ export function matchGrammarCompletion( text: string; prefixLength: number; needsSep: boolean; + spacingMode: SpacingMode; }> = []; const propertyCandidates: Array<{ property: GrammarCompletionProperty; prefixLength: number; needsSep: boolean; + spacingMode: SpacingMode; }> = []; // Track the furthest point the grammar consumed across all @@ -1259,6 +1309,7 @@ export function matchGrammarCompletion( text: completionText, prefixLength: state.index, needsSep: candidateNeedsSep, + spacingMode: state.spacingMode, }); // Record how far into the prefix the grammar consumed @@ -1326,6 +1377,7 @@ export function matchGrammarCompletion( property: completionProperty, prefixLength: candidatePrefixLength, needsSep: candidateNeedsSep, + spacingMode: state.spacingMode, }); // The wildcard starts at pendingWildcard.start; the @@ -1378,6 +1430,7 @@ export function matchGrammarCompletion( text: fullText, prefixLength: state.index, needsSep: candidateNeedsSep, + spacingMode: state.spacingMode, }); // state.index is where matching stopped (before the @@ -1397,19 +1450,27 @@ export function matchGrammarCompletion( // irrelevant when a longer (or exact) match consumed more input. const completions: string[] = []; const properties: GrammarCompletionProperty[] = []; - let needsSeparator = false; + let separatorMode: GrammarSeparatorMode | undefined; if (maxPrefixLength !== undefined) { for (const c of completionCandidates) { if (c.prefixLength === maxPrefixLength) { completions.push(c.text); - needsSeparator = needsSeparator || c.needsSep; + separatorMode = mergeSeparatorMode( + separatorMode, + c.needsSep, + c.spacingMode, + ); } } for (const p of propertyCandidates) { if (p.prefixLength === maxPrefixLength) { properties.push(p.property); - needsSeparator = needsSeparator || p.needsSep; + separatorMode = mergeSeparatorMode( + separatorMode, + p.needsSep, + p.spacingMode, + ); } } } @@ -1418,7 +1479,7 @@ export function matchGrammarCompletion( completions, properties, matchedPrefixLength: maxPrefixLength, - needsSeparator: needsSeparator || undefined, + separatorMode, }; debugCompletion(`Completed. ${JSON.stringify(result)}`); return result; diff --git a/ts/packages/actionGrammar/src/index.ts b/ts/packages/actionGrammar/src/index.ts index 1b3c1895e3..e8fad7423f 100644 --- a/ts/packages/actionGrammar/src/index.ts +++ b/ts/packages/actionGrammar/src/index.ts @@ -25,6 +25,8 @@ export { GrammarCompletionResult, } from "./grammarMatcher.js"; +export type { GrammarSeparatorMode } from "./grammarMatcher.js"; + // Entity system export type { EntityValidator, EntityConverter } from "./entityRegistry.js"; export { diff --git a/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts index 33b4ac2b48..0391e9c9f9 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionCategory3bLimitation.spec.ts @@ -166,9 +166,9 @@ describe("Grammar Completion - all alternatives after longest match", () => { }); }); - describe("needsSeparator in Category 3b", () => { + describe("separatorMode in Category 3b", () => { // Category 3b: finalizeState failed, trailing non-separator text - // remains. needsSeparator indicates whether a separator is needed + // remains. separatorMode indicates whether a separator is needed // between matchedPrefixLength and the completion text. describe("Latin grammar (auto spacing)", () => { @@ -180,7 +180,7 @@ describe("Grammar Completion - all alternatives after longest match", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("reports needsSeparator after consumed Latin prefix with trailing text", () => { + it("reports separatorMode after consumed Latin prefix with trailing text", () => { // "play x" → consumed "play" (4 chars), trailing "x" fails. // Completion "music"/"midi" at matchedPrefixLength=4. // Last consumed char: "y" (Latin), first completion char: "m" (Latin) @@ -188,16 +188,16 @@ describe("Grammar Completion - all alternatives after longest match", () => { const result = matchGrammarCompletion(grammar, "play x"); expect(result.completions.sort()).toEqual(["midi", "music"]); expect(result.matchedPrefixLength).toBe(4); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("reports needsSeparator with partial-match trailing text", () => { + it("reports separatorMode with partial-match trailing text", () => { // "play mu" → consumed "play" (4 chars), trailing "mu". // Same boundary: "y" → "m" → separator needed. const result = matchGrammarCompletion(grammar, "play mu"); expect(result.completions.sort()).toEqual(["midi", "music"]); expect(result.matchedPrefixLength).toBe(4); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); }); @@ -210,14 +210,14 @@ describe("Grammar Completion - all alternatives after longest match", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator for CJK → CJK", () => { + it("reports optional separatorMode for CJK → CJK", () => { // "再生x" → consumed "再生" (2 chars), trailing "x" fails. // Last consumed char: "生" (CJK), first completion: "音" (CJK) - // → no separator needed in auto mode. + // → separator optional in auto mode. const result = matchGrammarCompletion(grammar, "再生x"); expect(result.completions.sort()).toEqual(["映画", "音楽"]); expect(result.matchedPrefixLength).toBe(2); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); }); @@ -228,13 +228,13 @@ describe("Grammar Completion - all alternatives after longest match", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator when nothing consumed", () => { + it("reports optional separatorMode when nothing consumed", () => { // "xyz" → consumed 0 chars, offers "play" at prefixLength=0. // No last consumed char → no separator check. const result = matchGrammarCompletion(grammar, "xyz"); expect(result.completions).toEqual(["play"]); expect(result.matchedPrefixLength).toBe(0); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); }); @@ -246,11 +246,11 @@ describe("Grammar Completion - all alternatives after longest match", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("reports needsSeparator for spacing=required with trailing text", () => { + it("reports separatorMode for spacing=required with trailing text", () => { const result = matchGrammarCompletion(grammar, "play x"); expect(result.completions).toEqual(["music"]); expect(result.matchedPrefixLength).toBe(4); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); }); @@ -262,11 +262,11 @@ describe("Grammar Completion - all alternatives after longest match", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator for spacing=optional", () => { + it("reports optional separatorMode for spacing=optional", () => { const result = matchGrammarCompletion(grammar, "play x"); expect(result.completions).toEqual(["music"]); expect(result.matchedPrefixLength).toBe(4); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); }); @@ -278,13 +278,13 @@ describe("Grammar Completion - all alternatives after longest match", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator for Latin → CJK", () => { + it("reports optional separatorMode for Latin → CJK", () => { // Last consumed: "y" (Latin), completion: "音" (CJK) - // → different scripts, no separator needed in auto mode. + // → different scripts, separator optional in auto mode. const result = matchGrammarCompletion(grammar, "play x"); expect(result.completions).toEqual(["音楽"]); expect(result.matchedPrefixLength).toBe(4); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); }); }); diff --git a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts index 40867ddbb5..ef8394f000 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts @@ -222,7 +222,7 @@ describe("Grammar Completion - matchedPrefixLength", () => { }); }); - describe("needsSeparator - Latin multi-part", () => { + describe("separatorMode - Latin multi-part", () => { // Latin grammar: "play" → "music" requires a space separator const g = [ ` = $(v:) music -> true;`, @@ -230,39 +230,39 @@ describe("Grammar Completion - matchedPrefixLength", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("reports needsSeparator for Latin 'play' → 'music'", () => { + it("reports separatorMode for Latin 'play' → 'music'", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.completions).toEqual(["music"]); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("reports needsSeparator even when trailing space exists", () => { + it("reports separatorMode even when trailing space exists", () => { const result = matchGrammarCompletion(grammar, "play "); expect(result.completions).toEqual(["music"]); // matchedPrefixLength is 4 ("play"); the trailing space is - // unmatched content beyond that boundary. needsSeparator + // unmatched content beyond that boundary. separatorMode // describes the boundary at matchedPrefixLength, so it is - // true (Latin "y" → "m" needs a separator). + // "spacePunctuation" (Latin "y" → "m" needs a separator). expect(result.matchedPrefixLength).toBe(4); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("does not report needsSeparator for empty input", () => { + it("reports optional separatorMode for empty input", () => { const result = matchGrammarCompletion(grammar, ""); expect(result.completions).toEqual(["play"]); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); - it("does not report needsSeparator for partial prefix match", () => { + it("reports optional separatorMode for partial prefix match", () => { // "pl" matches partially → the completion replaces from state.index, // so no separator needed (user is typing the keyword) const result = matchGrammarCompletion(grammar, "pl"); expect(result.completions).toEqual(["play"]); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); }); - describe("needsSeparator - CJK multi-part", () => { + describe("separatorMode - CJK multi-part", () => { // CJK grammar: "再生" → "音楽" does NOT require a space separator const g = [ ` [spacing=auto] = $(v:) 音楽 -> true;`, @@ -270,15 +270,15 @@ describe("Grammar Completion - matchedPrefixLength", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator for CJK '再生' → '音楽'", () => { + it("reports optional separatorMode for CJK '再生' → '音楽'", () => { const result = matchGrammarCompletion(grammar, "再生"); expect(result.completions).toEqual(["音楽"]); - // CJK → CJK in auto mode: no separator needed - expect(result.needsSeparator).toBeUndefined(); + // CJK → CJK in auto mode: separator optional + expect(result.separatorMode).toBe("optional"); }); }); - describe("needsSeparator - mixed scripts", () => { + describe("separatorMode - mixed scripts", () => { // Latin followed by CJK: no separator needed in auto mode const g = [ ` [spacing=auto] = $(v:) 音楽 -> true;`, @@ -286,65 +286,65 @@ describe("Grammar Completion - matchedPrefixLength", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator for Latin 'play' → CJK '音楽'", () => { + it("reports optional separatorMode for Latin 'play' → CJK '音楽'", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.completions).toEqual(["音楽"]); - // Latin → CJK in auto mode: different scripts, no separator needed - expect(result.needsSeparator).toBeUndefined(); + // Latin → CJK in auto mode: different scripts, separator optional + expect(result.separatorMode).toBe("optional"); }); }); - describe("needsSeparator - spacing=required", () => { + describe("separatorMode - spacing=required", () => { const g = [ ` [spacing=required] = $(v:) music -> true;`, ` [spacing=required] = play -> true;`, ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("reports needsSeparator when spacing=required", () => { + it("reports separatorMode when spacing=required", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.completions).toEqual(["music"]); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); }); - describe("needsSeparator - spacing=optional", () => { + describe("separatorMode - spacing=optional", () => { const g = [ ` [spacing=optional] = $(v:) music -> true;`, ` [spacing=optional] = play -> true;`, ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("does not report needsSeparator when spacing=optional", () => { + it("reports optional separatorMode when spacing=optional", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.completions).toEqual(["music"]); - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBe("optional"); }); }); - describe("needsSeparator - wildcard entity", () => { + describe("separatorMode - wildcard entity", () => { // Grammar where the completion is a wildcard entity (not a static string). - // needsSeparator describes the boundary at matchedPrefixLength. + // separatorMode describes the boundary at matchedPrefixLength. const g = [ `entity TrackName;`, ` = play $(name:TrackName) -> { actionName: "play", parameters: { name } };`, ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("reports needsSeparator for 'play' before wildcard", () => { + it("reports separatorMode for 'play' before wildcard", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.properties?.length).toBeGreaterThan(0); // matchedPrefixLength=4; boundary "y" → entity needs separator. - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("reports needsSeparator for 'play ' before wildcard", () => { + it("reports separatorMode for 'play ' before wildcard", () => { // matchedPrefixLength=4 ("play"); the trailing space is - // beyond that boundary. needsSeparator describes the - // boundary at matchedPrefixLength: "y" → entity → true. + // beyond that boundary. separatorMode describes the + // boundary at matchedPrefixLength: "y" → entity → "spacePunctuation". const result = matchGrammarCompletion(grammar, "play "); expect(result.properties?.length).toBeGreaterThan(0); - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); }); }); diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index 2a045def19..6aa6df8005 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -49,6 +49,23 @@ export type CommandDescriptors = //=========================================== // Command APIs //=========================================== + +// Describes what kind of separator is required between the matched prefix +// and the completion text. The frontend uses this to decide when to show +// the completion menu. +// +// "space" — whitespace required (default when omitted). +// Used for commands, flags, agent names. +// "spacePunctuation" — whitespace or Unicode punctuation ([\s\p{P}]) +// required. Used by the grammar matcher for +// Latin-script completions. +// "optional" — separator accepted but not required; menu shown +// immediately. Used for CJK / mixed-script +// grammar completions. +// "none" — no separator at all; menu shown immediately. +// Used for [spacing=none] grammars. +export type SeparatorMode = "space" | "spacePunctuation" | "optional" | "none"; + export type CompletionGroup = { name: string; // The group name for the completion completions: string[]; // The list of completions in the group @@ -61,12 +78,10 @@ export type CompletionGroup = { // inserts completions at this offset, replacing space-based heuristics // that fail for CJK and other non-space-delimited scripts. prefixLength?: number | undefined; - // True when the matched prefix and the completion text are - // structurally separated (e.g. a space between a command and its - // parameters). This describes the grammar relationship, not whether - // the user has already typed the separator — the frontend uses - // startIndex and this flag together to decide when to show the menu. - needsSeparator?: boolean | undefined; + // What kind of separator is required between the matched prefix and + // the completion text. When omitted, defaults to "space" (whitespace + // required before completions are shown). See SeparatorMode. + separatorMode?: SeparatorMode | undefined; }; export interface AppAgentCommandInterface { diff --git a/ts/packages/agentSdk/src/index.ts b/ts/packages/agentSdk/src/index.ts index 70a0f5b821..43da8f4190 100644 --- a/ts/packages/agentSdk/src/index.ts +++ b/ts/packages/agentSdk/src/index.ts @@ -30,6 +30,7 @@ export { CommandDescriptorTable, AppAgentCommandInterface, CompletionGroup, + SeparatorMode, } from "./command.js"; export { diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index ef9383b02b..b6629e0455 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -6,6 +6,7 @@ import { Grammar, matchGrammar, matchGrammarCompletion, + GrammarSeparatorMode, NFA, compileGrammarToNFA, matchGrammarWithNFA, @@ -18,6 +19,7 @@ import { } from "action-grammar"; const debug = registerDebug("typeagent:cache:grammarStore"); +import { SeparatorMode } from "@typeagent/agent-sdk"; import { CompletionProperty, CompletionResult, @@ -32,6 +34,27 @@ import { } from "../explanation/requestAction.js"; import { sortMatches } from "./sortMatches.js"; +// Merge a GrammarSeparatorMode (from action-grammar) into an accumulator +// SeparatorMode. Most restrictive wins: spacePunctuation > optional > none. +// (Grammar results never produce "space" — that mode is for command dispatch.) +function mergeGrammarSeparatorMode( + current: SeparatorMode | undefined, + incoming: GrammarSeparatorMode, +): SeparatorMode { + // GrammarSeparatorMode is structurally compatible with SeparatorMode + // (it is a subset: "spacePunctuation" | "optional" | "none"). + if (current === undefined) { + return incoming; + } + if (current === "spacePunctuation" || incoming === "spacePunctuation") { + return "spacePunctuation"; + } + if (current === "optional" || incoming === "optional") { + return "optional"; + } + return "none"; +} + interface GrammarEntry { grammar: Grammar; nfa?: NFA; @@ -273,7 +296,7 @@ export class GrammarStoreImpl implements GrammarStore { const completions: string[] = []; const properties: CompletionProperty[] = []; let matchedPrefixLength: number | undefined; - let needsSeparator = false; + let separatorMode: SeparatorMode | undefined; const filter = new Set(namespaceKeys); for (const [name, entry] of this.grammars) { if (filter && !filter.has(name)) { @@ -364,8 +387,11 @@ export class GrammarStoreImpl implements GrammarStore { partial.matchedPrefixLength, ); } - if (partial.needsSeparator) { - needsSeparator = true; + if (partial.separatorMode !== undefined) { + separatorMode = mergeGrammarSeparatorMode( + separatorMode, + partial.separatorMode, + ); } if ( partial.properties !== undefined && @@ -393,7 +419,7 @@ export class GrammarStoreImpl implements GrammarStore { completions, properties, matchedPrefixLength, - needsSeparator: needsSeparator || undefined, + separatorMode, }; } } diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index 2b46017d06..b633dca8a5 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { SeparatorMode } from "@typeagent/agent-sdk"; import { ExecutableAction, HistoryContext, @@ -75,9 +76,9 @@ export type CompletionResult = { properties?: CompletionProperty[] | undefined; // Characters consumed by the grammar before the completion point. matchedPrefixLength?: number | undefined; - // True when a separator (e.g. space) must be inserted between the - // already-typed prefix and the completion text. - needsSeparator?: boolean | undefined; + // What kind of separator is required between the already-typed prefix + // and the completion text. See SeparatorMode in @typeagent/agent-sdk. + separatorMode?: SeparatorMode | undefined; }; export function mergeCompletionResults( @@ -109,10 +110,29 @@ export function mergeCompletionResults( : first.properties : second.properties, matchedPrefixLength, - needsSeparator: - first.needsSeparator || second.needsSeparator || undefined, + separatorMode: mergeSeparatorModes( + first.separatorMode, + second.separatorMode, + ), }; } + +// Merge two SeparatorMode values — most restrictive wins. +// Priority: "space" > "spacePunctuation" > "optional" > "none". +function mergeSeparatorModes( + a: SeparatorMode | undefined, + b: SeparatorMode | undefined, +): SeparatorMode | undefined { + if (a === undefined) return b; + if (b === undefined) return a; + const order: Record = { + space: 3, + spacePunctuation: 2, + optional: 1, + none: 0, + }; + return order[a] >= order[b] ? a : b; +} export class ConstructionCache { private readonly matchSetsByUid = new Map(); diff --git a/ts/packages/cache/test/mergeCompletionResults.spec.ts b/ts/packages/cache/test/mergeCompletionResults.spec.ts index 553ddd4109..d80741743a 100644 --- a/ts/packages/cache/test/mergeCompletionResults.spec.ts +++ b/ts/packages/cache/test/mergeCompletionResults.spec.ts @@ -187,55 +187,55 @@ describe("mergeCompletionResults", () => { }); }); - describe("needsSeparator merging", () => { - it("returns undefined when neither has needsSeparator", () => { + describe("separatorMode merging", () => { + it("returns undefined when neither has separatorMode", () => { const first: CompletionResult = { completions: ["a"] }; const second: CompletionResult = { completions: ["b"] }; const result = mergeCompletionResults(first, second)!; - expect(result.needsSeparator).toBeUndefined(); + expect(result.separatorMode).toBeUndefined(); }); - it("returns true when first has needsSeparator", () => { + it("returns first separatorMode when second is undefined", () => { const first: CompletionResult = { completions: ["a"], - needsSeparator: true, + separatorMode: "spacePunctuation", }; const second: CompletionResult = { completions: ["b"] }; const result = mergeCompletionResults(first, second)!; - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("returns true when second has needsSeparator", () => { + it("returns second separatorMode when first is undefined", () => { const first: CompletionResult = { completions: ["a"] }; const second: CompletionResult = { completions: ["b"], - needsSeparator: true, + separatorMode: "spacePunctuation", }; const result = mergeCompletionResults(first, second)!; - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("returns true when both have needsSeparator", () => { + it("returns most restrictive when both have separatorMode", () => { const first: CompletionResult = { completions: ["a"], - needsSeparator: true, + separatorMode: "spacePunctuation", }; const second: CompletionResult = { completions: ["b"], - needsSeparator: true, + separatorMode: "optional", }; const result = mergeCompletionResults(first, second)!; - expect(result.needsSeparator).toBe(true); + expect(result.separatorMode).toBe("spacePunctuation"); }); - it("preserves needsSeparator when first is undefined", () => { + it("preserves separatorMode when first is undefined result", () => { const second: CompletionResult = { completions: ["b"], - needsSeparator: true, + separatorMode: "spacePunctuation", }; const result = mergeCompletionResults(undefined, second); expect(result).toBe(second); - expect(result!.needsSeparator).toBe(true); + expect(result!.separatorMode).toBe("spacePunctuation"); }); }); }); diff --git a/ts/packages/cli/src/commands/interactive.ts b/ts/packages/cli/src/commands/interactive.ts index 5306940133..8353e5420e 100644 --- a/ts/packages/cli/src/commands/interactive.ts +++ b/ts/packages/cli/src/commands/interactive.ts @@ -72,10 +72,14 @@ async function getCompletionsData( const filterStartIndex = result.startIndex; const prefix = line.substring(0, filterStartIndex); - // When the grammar reports a separator is needed between the + // When the result reports a separator-requiring mode between the // typed prefix and the completion text, prepend a space so the // readline display doesn't produce "playmusic" for "play" + "music". - const separator = result.needsSeparator ? " " : ""; + const separator = + result.separatorMode === "space" || + result.separatorMode === "spacePunctuation" + ? " " + : ""; return { allCompletions, diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index e04aaed9d0..c3e0f39a7a 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -9,6 +9,7 @@ import { ParameterDefinitions, ParsedCommandParams, CompletionGroup, + SeparatorMode, } from "@typeagent/agent-sdk"; import { getFlagMultiple, @@ -28,6 +29,32 @@ import { CommandCompletionResult } from "@typeagent/dispatcher-types"; const debug = registerDebug("typeagent:command:completion"); const debugError = registerDebug("typeagent:command:completion:error"); +// Aggregate SeparatorMode from completion groups. +// The most restrictive separator-requiring mode wins. +// Priority: "space" > "spacePunctuation" > "optional" > "none" > undefined. +// Groups that omit separatorMode are ignored (they inherit the default). +function aggregateSeparatorMode( + groups: CompletionGroup[], +): SeparatorMode | undefined { + let result: SeparatorMode | undefined; + for (const g of groups) { + const mode = g.separatorMode; + if (mode === undefined) { + continue; + } + if (mode === "space") { + return "space"; // Most restrictive, short-circuit. + } + if (result === undefined) { + result = mode; + } else if (mode === "spacePunctuation") { + result = "spacePunctuation"; + } + // "optional" and "none" don't override "spacePunctuation" + } + return result; +} + // Return the full flag name if we are waiting a flag value. Add boolean values for completions and return undefined if the flag is boolean. function getPendingFlag( params: ParsedCommandParams, @@ -274,7 +301,7 @@ async function getCommandParameterCompletion( // completions describe what can validly follow. // May be overridden by a grammar-reported prefixLength // from a CompletionGroup. Trailing whitespace before -// startIndex is stripped when needsSeparator is true. +// startIndex is stripped when separatorMode requires it. // // completions Array of CompletionGroups from up to three sources: // (a) built-in command / subcommand / agent-name lists, @@ -282,11 +309,11 @@ async function getCommandParameterCompletion( // (c) agent-provided groups via the agent's // getCommandCompletion callback. // -// needsSeparator -// true when the prefix and completions are structurally -// separated (e.g. a space between a command and its -// parameters). Aggregated: true if *any* group reports -// needsSeparator. +// separatorMode +// Describes what kind of separator is required between +// the matched prefix and the completion text. +// Aggregated: most restrictive mode from any group wins. +// When omitted, consumers default to "space". // // complete true when the returned completions are the *exhaustive* // set of valid continuations after the prefix. When true @@ -359,7 +386,7 @@ export async function getCommandCompletion( completions.push({ name: "Subcommands", completions: Object.keys(table.commands), - needsSeparator: true, + separatorMode: "space", }); } @@ -392,11 +419,11 @@ export async function getCommandCompletion( completions.push({ name: "Subcommands", completions: Object.keys(table.commands), - needsSeparator: + separatorMode: result.parsedAppAgentName !== undefined || result.commands.length > 0 - ? true - : undefined, + ? "space" + : "optional", }); } else { // Both table and descriptor are undefined — the agent @@ -418,6 +445,7 @@ export async function getCommandCompletion( completions: context.agents .getAppAgentNames() .filter((name) => context.agents.isCommandEnabled(name)), + separatorMode: "optional", }); } @@ -432,19 +460,18 @@ export async function getCommandCompletion( startIndex = groupPrefixLength; } - // Extract needsSeparator from any group that reports it. - const needsSeparator = completions.some( - (g) => g.needsSeparator === true, - ) - ? true - : undefined; + // Extract separatorMode from groups. The most restrictive + // mode wins ("space" and "spacePunctuation" are both + // separator-requiring; among those, "space" is more + // restrictive because it only allows whitespace). + const separatorMode = aggregateSeparatorMode(completions); // Like the grammar matcher, exclude trailing whitespace before // startIndex when a separator is needed — the separator lives // between the command anchor and the completion text, not inside // the filter prefix. This handles both "@config " (trailing // space) and "@config c" (space between command and partial token). - if (needsSeparator) { + if (separatorMode === "space" || separatorMode === "spacePunctuation") { while (startIndex > 0 && /\s/.test(input[startIndex - 1])) { startIndex--; } @@ -453,7 +480,7 @@ export async function getCommandCompletion( const completionResult: CommandCompletionResult = { startIndex, completions, - needsSeparator, + separatorMode, complete, }; @@ -466,7 +493,7 @@ export async function getCommandCompletion( return { startIndex: 0, completions: [], - needsSeparator: undefined, + separatorMode: undefined, complete: false, }; } diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index cf4b12f053..ab05064e67 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -4,7 +4,11 @@ import { CommandHandlerContext } from "../context/commandHandlerContext.js"; import registerDebug from "debug"; import { ExecutableAction, getPropertyInfo, MatchOptions } from "agent-cache"; -import { CompletionGroup, TypeAgentAction } from "@typeagent/agent-sdk"; +import { + CompletionGroup, + SeparatorMode, + TypeAgentAction, +} from "@typeagent/agent-sdk"; import { DeepPartialUndefined } from "@typeagent/common-utils"; import { ActionParamType, @@ -92,7 +96,7 @@ export async function requestCompletion( } const prefixLength = results.matchedPrefixLength; - const needsSeparator = results.needsSeparator; + const separatorMode = results.separatorMode; const completions: CompletionGroup[] = []; if (results.completions.length > 0) { completions.push({ @@ -101,7 +105,7 @@ export async function requestCompletion( needQuotes: false, // Request completions are partial, no quotes needed kind: "literal", prefixLength, - needsSeparator, + separatorMode, }); } @@ -122,7 +126,7 @@ export async function requestCompletion( context, propertyCompletions, prefixLength, - needsSeparator, + separatorMode, ); } } @@ -137,7 +141,7 @@ async function collectActionCompletions( context: CommandHandlerContext, propertyCompletions: Map, prefixLength?: number | undefined, - needsSeparator?: boolean | undefined, + separatorMode?: SeparatorMode | undefined, ) { for (const propertyName of properties) { const { action, parameterName } = getPropertyInfo( @@ -164,7 +168,7 @@ async function collectActionCompletions( sorted: true, // REVIEW: assume property completions are already in desired order by the agent. kind: "entity", prefixLength, - needsSeparator, + separatorMode, }); } } diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index c083efad14..135b2e1084 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -357,35 +357,35 @@ describe("Command Completion - startIndex", () => { }); }); - describe("needsSeparator for command completions", () => { - it("returns needsSeparator for subcommand completions at agent boundary", async () => { + describe("separatorMode for command completions", () => { + it("returns separatorMode for subcommand completions at agent boundary", async () => { const result = await getCommandCompletion("@comptest ", context); expect(result).toBeDefined(); // "run" is the default subcommand, so subcommand alternatives - // are included and the group has needsSeparator: true. - expect(result!.needsSeparator).toBe(true); + // are included and the group has separatorMode: "space". + expect(result!.separatorMode).toBe("space"); // startIndex excludes trailing whitespace (matching grammar // matcher behaviour where prefixLength doesn't include the // separator). expect(result!.startIndex).toBe(9); }); - it("returns needsSeparator for resolved agent without trailing space", async () => { + it("returns separatorMode for resolved agent without trailing space", async () => { const result = await getCommandCompletion("@comptest", context); expect(result).toBeDefined(); - expect(result!.needsSeparator).toBe(true); + expect(result!.separatorMode).toBe("space"); // No trailing whitespace to trim — startIndex stays at end expect(result!.startIndex).toBe(9); // Default subcommand has agent completions → not exhaustive. expect(result!.complete).toBe(false); }); - it("does not set needsSeparator at top level (@)", async () => { + it("does not set separatorMode at top level (@)", async () => { const result = await getCommandCompletion("@", context); expect(result).toBeDefined(); // Top-level completions (agent names, system subcommands) - // follow '@' directly without a separator. - expect(result!.needsSeparator).toBeUndefined(); + // follow '@' — space is accepted but not required. + expect(result!.separatorMode).toBe("optional"); // Agent names are offered when no agent was recognized, // independent of which branch (descriptor/table/neither) // produced the subcommand completions. @@ -398,23 +398,23 @@ describe("Command Completion - startIndex", () => { expect(result!.complete).toBe(true); }); - it("does not set needsSeparator for parameter completions only", async () => { + it("does not set separatorMode for parameter completions only", async () => { const result = await getCommandCompletion( "@comptest run bu", context, ); expect(result).toBeDefined(); // Partial parameter token — only parameter completions returned, - // no subcommand group, so needsSeparator is not set. - expect(result!.needsSeparator).toBeUndefined(); + // no subcommand group, so separatorMode is not set. + expect(result!.separatorMode).toBeUndefined(); }); - it("returns needsSeparator + subcommands for partial unmatched token", async () => { + it("returns separatorMode + subcommands for partial unmatched token", async () => { const result = await getCommandCompletion("@comptest ne", context); expect(result).toBeDefined(); // "ne" doesn't match an explicit subcommand, so resolved to // default — subcommand alternatives included. - expect(result!.needsSeparator).toBe(true); + expect(result!.separatorMode).toBe("space"); const subcommands = result!.completions.find( (g) => g.name === "Subcommands", ); @@ -620,7 +620,7 @@ describe("Command Completion - startIndex", () => { const result = await getCommandCompletion("@comptest ne", context); // "@comptest ne" — suffix is "ne", parameter parsing sees // one partial token but startIndex = 10 (command boundary - // for needsSeparator stripping) → subcommands included. + // for separatorMode stripping) → subcommands included. const subcommands = result.completions.find( (g) => g.name === "Subcommands", ); diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index 3f72e11ede..c3b84d9c24 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -5,6 +5,7 @@ import { CompletionGroup, DisplayType, DynamicDisplay, + SeparatorMode, TemplateSchema, TypeAgentAction, } from "@typeagent/agent-sdk"; @@ -57,12 +58,10 @@ export type CommandCompletionResult = { // what can follow after that prefix. startIndex: number; completions: CompletionGroup[]; // completions available at the current position - // True when the matched prefix and the completion text are - // structurally separated (e.g. a space between a command and its - // parameters). This describes the grammar relationship, not whether - // the user has already typed the separator — the frontend uses - // startIndex and this flag together to decide when to show the menu. - needsSeparator?: boolean | undefined; + // What kind of separator is required between the matched prefix and + // the completion text. When omitted, defaults to "space". + // See SeparatorMode in @typeagent/agent-sdk. + separatorMode?: SeparatorMode | undefined; // True when the completions listed are the exhaustive set of valid // continuations after the prefix. When true and the user types // something that doesn't prefix-match any completion, the caller diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 764b5c8881..4941a2d2a0 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { CommandCompletionResult } from "agent-dispatcher"; +import { SeparatorMode } from "@typeagent/agent-sdk"; import { SearchMenuItem, SearchMenuPosition, @@ -33,7 +34,7 @@ export interface ICompletionDispatcher { // EXHAUSTED current !== undefined && completionP === undefined && noCompletion === true // // Design principles: -// - Completion result fields (noCompletion, needsSeparator) are stored as-is +// - Completion result fields (noCompletion, separatorMode) are stored as-is // from the backend response and never mutated as the user keeps typing. // reuseSession() reads them to decide whether to show, hide, or re-fetch. // - reuseSession() makes exactly four kinds of decisions: @@ -53,8 +54,9 @@ export interface ICompletionDispatcher { // complete=true → reuse (exhaustive set, nothing else exists) // complete=false → re-fetch (set is partial, backend may know more) // - The anchor (`current`) is never advanced after a result is received. -// When `needsSeparator` is true the separator is stripped from the raw -// prefix before being passed to the menu, so the trie still matches. +// When `separatorMode` requires a separator, the separator is stripped +// from the raw prefix before being passed to the menu, so the trie +// still matches. // // This class has no DOM dependencies and is fully unit-testable with Jest. export class PartialCompletionSession { @@ -66,11 +68,12 @@ export class PartialCompletionSession { // True when the backend reported no completions for `current` (EXHAUSTED). private noCompletion: boolean = false; - // Saved as-is from the last completion result: whether a separator must - // appear in the input immediately after `current` before completions are - // valid. Used by reuseSession() and getCompletionPrefix() to interpret + // Saved as-is from the last completion result: what kind of separator + // must appear in the input immediately after `current` before + // completions are valid. Defaults to "space" when omitted. + // Used by reuseSession() and getCompletionPrefix() to interpret // the raw prefix without mutating `current`. - private needsSeparator: boolean = false; + private separatorMode: SeparatorMode = "space"; // When true, the completion set returned by the backend is exhaustive // for THIS level of the command hierarchy. This affects one decision @@ -115,7 +118,7 @@ export class PartialCompletionSession { this.completionP = undefined; this.current = undefined; this.noCompletion = false; - this.needsSeparator = false; + this.separatorMode = "space"; this.complete = false; this.cancelMenu(); } @@ -123,7 +126,7 @@ export class PartialCompletionSession { // Reset state to IDLE without hiding the menu (used after handleSelect inserts text). public resetToIdle(): void { this.current = undefined; - this.needsSeparator = false; + this.separatorMode = "space"; this.complete = false; } @@ -138,9 +141,14 @@ export class PartialCompletionSession { return undefined; } const rawPrefix = input.substring(current.length); - if (this.needsSeparator) { + if ( + this.separatorMode === "space" || + this.separatorMode === "spacePunctuation" + ) { // The separator must be present and is not part of the replaceable prefix. - if (!/^\s/.test(rawPrefix)) { + const sepRe = + this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; + if (!sepRe.test(rawPrefix)) { return undefined; } return rawPrefix.trimStart(); @@ -195,15 +203,23 @@ export class PartialCompletionSession { return true; } - // Separator handling: the character immediately after the anchor must be - // whitespace. Three sub-cases: + // Separator handling: the character immediately after the anchor must + // satisfy the separatorMode constraint. + // "space": whitespace required + // "spacePunctuation": whitespace or Unicode punctuation required + // "optional"/"none": no separator needed, fall through to SHOW + // + // Three sub-cases when a separator IS required: // "" — separator not typed yet: HIDE+KEEP (separator may still arrive) // " …" — separator present: SHOW (fall through, strip it below) // "x…" — non-separator typed right after anchor: RE-FETCH (the // separator constraint can never be satisfied without // backtracking, so treat this as a new input) const rawPrefix = input.substring(current.length); - if (this.needsSeparator) { + const requiresSep = + this.separatorMode === "space" || + this.separatorMode === "spacePunctuation"; + if (requiresSep) { if (rawPrefix === "") { debug( `Partial completion deferred: still waiting for separator`, @@ -211,14 +227,16 @@ export class PartialCompletionSession { this.menu.hide(); return true; // HIDE+KEEP } - if (!/^\s/.test(rawPrefix)) { + const sepRe = + this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; + if (!sepRe.test(rawPrefix)) { return false; // RE-FETCH } } // SHOW — strip the leading separator (if any) before passing to the // menu trie, so completions like "music" match prefix "" not " ". - const prefix = this.needsSeparator ? rawPrefix.trimStart() : rawPrefix; + const prefix = requiresSep ? rawPrefix.trimStart() : rawPrefix; const position = getPosition(prefix); if (position !== undefined) { @@ -259,7 +277,7 @@ export class PartialCompletionSession { this.cancelMenu(); this.current = input; this.noCompletion = false; - this.needsSeparator = false; + this.separatorMode = "space"; this.complete = false; this.menu.setChoices([]); const completionP = this.dispatcher.getCommandCompletion(input); @@ -274,7 +292,7 @@ export class PartialCompletionSession { this.completionP = undefined; debug(`Partial completion result: `, result); - this.needsSeparator = result.needsSeparator === true; + this.separatorMode = result.separatorMode ?? "space"; this.complete = result.complete; // Build completions preserving backend group order. diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index 73effd0d46..08963a787e 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -41,7 +41,7 @@ function makeDispatcher( result: CommandCompletionResult = { startIndex: 0, completions: [], - needsSeparator: undefined, + separatorMode: undefined, complete: true, }, ): MockDispatcher { @@ -452,10 +452,10 @@ describe("PartialCompletionSession — result processing", () => { expect(texts).toEqual(["apple", "mango", "zebra"]); }); - test("needsSeparator defers menu display until trailing space is present", async () => { + test("separatorMode defers menu display until trailing space is present", async () => { const menu = makeMenu(); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -474,16 +474,16 @@ describe("PartialCompletionSession — result processing", () => { expect(menu.updatePrefix).not.toHaveBeenCalled(); }); - test("needsSeparator: typing separator shows menu without re-fetch", async () => { + test("separatorMode: typing separator shows menu without re-fetch", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); - // First update: "play" — deferred (needsSeparator, no trailing space) + // First update: "play" — deferred (separatorMode, no trailing space) session.update("play", getPos); await Promise.resolve(); @@ -495,11 +495,11 @@ describe("PartialCompletionSession — result processing", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("needsSeparator: menu shown after trailing space is typed", async () => { + test("separatorMode: menu shown after trailing space is typed", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 5, { - needsSeparator: false, + separatorMode: undefined, }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -587,13 +587,13 @@ describe("PartialCompletionSession — @command routing", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("@ command: needsSeparator defers menu until space typed", async () => { + test("@ command: separatorMode defers menu until space typed", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - // Backend returns subcommands with needsSeparator: true + // Backend returns subcommands with separatorMode: "space" // (anchor = "@config", subcommands follow after a space) const result = makeCompletionResult(["clear", "theme"], 7, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -620,9 +620,9 @@ describe("PartialCompletionSession — @command routing", () => { test("@ command: typing after space filters within same session", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - // Backend: needsSeparator, anchor = "@config" + // Backend: separatorMode, anchor = "@config" const result = makeCompletionResult(["clear", "theme"], 7, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -712,11 +712,11 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { expect(session.getCompletionPrefix("stop")).toBeUndefined(); }); - test("needsSeparator: returns stripped prefix when separator is present", async () => { + test("separatorMode: returns stripped prefix when separator is present", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const session = new PartialCompletionSession( menu, @@ -730,10 +730,10 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { expect(session.getCompletionPrefix("play mu")).toBe("mu"); }); - test("needsSeparator: returns undefined when separator is absent", async () => { + test("separatorMode: returns undefined when separator is absent", async () => { const menu = makeMenu(); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const session = new PartialCompletionSession( menu, @@ -783,15 +783,15 @@ describe("PartialCompletionSession — resetToIdle", () => { }); }); -// ── needsSeparator edge cases ───────────────────────────────────────────────── +// ── separatorMode edge cases ───────────────────────────────────────────────── -describe("PartialCompletionSession — needsSeparator edge cases", () => { +describe("PartialCompletionSession — separatorMode edge cases", () => { test("re-update with same input before separator does not re-fetch", async () => { // Regression: selectionchange can fire again with the same input while // the session is waiting for a separator. Must not trigger a re-fetch. const menu = makeMenu(); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -807,7 +807,7 @@ describe("PartialCompletionSession — needsSeparator edge cases", () => { test("input diverges before separator arrives triggers re-fetch", async () => { const menu = makeMenu(); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -829,7 +829,7 @@ describe("PartialCompletionSession — needsSeparator edge cases", () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { - needsSeparator: true, + separatorMode: "space", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); From 92398f8f7d5fce604176c5124f40c38b74a59625 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Sun, 8 Mar 2026 13:42:43 -0700 Subject: [PATCH 12/51] Add remainderLength tests and back startIndex over whitespace - Add remainder.spec.ts with 30 tests covering remainderLength in parseParams for non-partial, partial-consumed, and partial-unconsumed cases. - In completion.ts, universally back startIndex over inter-token whitespace between the last consumed token and the unconsumed remainder, replacing the old rewind-for-filter-text logic. - Update ParseParamsResult comments in parameters.ts to clarify that remainderLength excludes inter-token whitespace. - Add completion tests for whitespace backing with remainderLength > 0 (single and multiple spaces). - Update existing completion test expectations for the new startIndex behavior. --- .../dispatcher/src/command/completion.ts | 29 +- .../dispatcher/src/command/parameters.ts | 13 +- .../dispatcher/test/completion.spec.ts | 78 ++++-- .../dispatcher/test/remainder.spec.ts | 264 ++++++++++++++++++ 4 files changed, 341 insertions(+), 43 deletions(-) create mode 100644 ts/packages/dispatcher/dispatcher/test/remainder.spec.ts diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index c3e0f39a7a..655549a590 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -225,26 +225,19 @@ async function getCommandParameterCompletion( // remainderLength is the length of the (trimmed) parameter text // that was NOT successfully parsed — everything before it is part // of the longest valid prefix. + // + // Because remainderLength excludes inter-token whitespace (the + // tokenizer strips it), we back startIndex over any whitespace + // that sits between the last consumed token and the unconsumed + // remainder so that the separator space is not treated as part + // of the consumed prefix. let startIndex = inputLength - params.remainderLength; - // When everything was consumed and there is no trailing whitespace, - // the last *value* token may still be in progress — rewind to make - // it filter text. lastCompletableParam is only set when the final - // parsed entity was a parameter value (string arg or string flag - // value), so pending flags (name consumed, value missing) naturally - // keep startIndex at the fully-consumed position. - if ( - params.remainderLength === 0 && - !/\s$/.test(result.suffix) && - params.lastCompletableParam !== undefined - ) { - const lastToken = - params.tokens.length > 0 - ? params.tokens[params.tokens.length - 1] - : undefined; - if (lastToken !== undefined) { - startIndex -= lastToken.length; - } + const suffix = result.suffix; + let suffixPos = suffix.length - params.remainderLength; + while (suffixPos > 0 && /\s/.test(suffix[suffixPos - 1])) { + suffixPos--; + startIndex--; } // Determine whether the completion set is exhaustive. diff --git a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts index 9484111f8f..ddbb8600cf 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts @@ -140,9 +140,20 @@ function parseValueToken( // parameter string was *not* consumed by the parser. Used internally by // the completion layer to compute startIndex from parse position rather // than by reverse-engineering filter text from token lengths. +// +// NOTE: remainderLength measures only the unconsumed *token* text — it +// does NOT include any inter-token whitespace that the tokenizer +// stripped between the last consumed token and the unconsumed portion. +// For example, parsing "hello extra" with a single-arg definition +// yields remainderLength = 5 ("extra"), not 6 (" extra"). The +// completion layer in completion.ts accounts for this by backing +// startIndex over any preceding whitespace after the initial +// computation. export type ParseParamsResult = ParsedCommandParams & { - /** Length of the (trimmed) parameter text left unconsumed. */ + /** Length of the (trimmed) parameter text left unconsumed. + * Excludes inter-token whitespace between the last consumed + * token and the start of the unconsumed remainder. */ remainderLength: number; }; diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 135b2e1084..1b527b7e3b 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -210,9 +210,10 @@ describe("Command Completion - startIndex", () => { ); expect(result).toBeDefined(); // "@comptest run bu" (16 chars) - // suffix is "bu", parameter parsing sees token "bu" (2 chars) - // startIndex = 16 - 2 = 14 - expect(result!.startIndex).toBe(14); + // suffix is "bu", parameter parsing fully consumes "bu" + // remainderLength = 0 → startIndex = 16, then whitespace + // backing finds no space at suffix end → startIndex = 16. + expect(result!.startIndex).toBe(16); // "bu" consumes the "task" arg → nextArgs is empty. // Agent is not invoked (bare word, no implicit quotes). // Only flags remain (none defined) → exhaustive. @@ -346,15 +347,44 @@ describe("Command Completion - startIndex", () => { context, ); // "@comptest run build " (20 chars) - // suffix is "build ", token "build" is complete, trailing space - // means filter length = 0, so startIndex = 20 + // suffix is "build ", token "build" is fully consumed, + // remainderLength = 0 → startIndex = 20, then whitespace + // backing rewinds over the trailing space → startIndex = 19. expect(result).toBeDefined(); - expect(result!.startIndex).toBe(20); + expect(result!.startIndex).toBe(19); // All positional args filled ("task" consumed "build"), // no flags, agent not invoked (agentCommandCompletions // is empty) → exhaustive. expect(result!.complete).toBe(true); }); + + it("startIndex backs over whitespace before unconsumed remainder", async () => { + const result = await getCommandCompletion( + "@comptest run hello --unknown", + context, + ); + expect(result).toBeDefined(); + // "@comptest run hello --unknown" (29 chars) + // suffix is "hello --unknown", "hello" fills the "task" arg, + // "--unknown" is not a defined flag → remainderLength = 9. + // startIndex = 29 - 9 = 20, then whitespace backing rewinds + // over the space between "hello" and "--unknown" → 19. + expect(result!.startIndex).toBe(19); + }); + + it("startIndex backs over multiple spaces before unconsumed remainder", async () => { + const result = await getCommandCompletion( + "@comptest run hello --unknown", + context, + ); + expect(result).toBeDefined(); + // "@comptest run hello --unknown" (31 chars) + // suffix is "hello --unknown", "hello" fills "task", + // "--unknown" unconsumed → remainderLength = 9. + // startIndex = 31 - 9 = 22, then whitespace backing rewinds + // over three spaces → 19. + expect(result!.startIndex).toBe(19); + }); }); describe("separatorMode for command completions", () => { @@ -409,20 +439,19 @@ describe("Command Completion - startIndex", () => { expect(result!.separatorMode).toBeUndefined(); }); - it("returns separatorMode + subcommands for partial unmatched token", async () => { + it("returns no separatorMode for partial unmatched token consumed as param", async () => { const result = await getCommandCompletion("@comptest ne", context); expect(result).toBeDefined(); - // "ne" doesn't match an explicit subcommand, so resolved to - // default — subcommand alternatives included. - expect(result!.separatorMode).toBe("space"); + // "ne" is fully consumed as the "task" arg by parameter + // parsing → startIndex = 12 (past command boundary 10), + // so subcommands are not included and separatorMode is + // not set. + expect(result!.separatorMode).toBeUndefined(); const subcommands = result!.completions.find( (g) => g.name === "Subcommands", ); - expect(subcommands).toBeDefined(); - expect(subcommands!.completions).toContain("nested"); - // startIndex backs up past the space to the agent boundary. - // "@comptest" = 9 chars. - expect(result!.startIndex).toBe(9); + expect(subcommands).toBeUndefined(); + expect(result!.startIndex).toBe(12); }); }); @@ -590,15 +619,17 @@ describe("Command Completion - startIndex", () => { ); // "@comptest build " (16 chars) // Resolves to default "run" (not explicit match). - // "build" fills the "task" arg, trailing space moves - // startIndex to 16 — past the command boundary (10). + // "build" fills the "task" arg, trailing space present. + // remainderLength = 0 → startIndex = 16, then whitespace + // backing rewinds over the trailing space → startIndex = 15, + // past the command boundary (10). // Subcommand names are no longer relevant at this // position; only parameter completions remain. const subcommands = result.completions.find( (g) => g.name === "Subcommands", ); expect(subcommands).toBeUndefined(); - expect(result.startIndex).toBe(16); + expect(result.startIndex).toBe(15); // All positional args filled, no flags → exhaustive. expect(result.complete).toBe(true); }); @@ -616,16 +647,15 @@ describe("Command Completion - startIndex", () => { expect(subcommands!.completions).toContain("nested"); }); - it("keeps subcommands when partial token is at the boundary", async () => { + it("drops subcommands when partial token is consumed past boundary", async () => { const result = await getCommandCompletion("@comptest ne", context); - // "@comptest ne" — suffix is "ne", parameter parsing sees - // one partial token but startIndex = 10 (command boundary - // for separatorMode stripping) → subcommands included. + // "@comptest ne" — suffix is "ne", parameter parsing fully + // consumes it as the "task" arg → startIndex = 12, which + // exceeds commandBoundary (10) → subcommands dropped. const subcommands = result.completions.find( (g) => g.name === "Subcommands", ); - expect(subcommands).toBeDefined(); - expect(subcommands!.completions).toContain("nested"); + expect(subcommands).toBeUndefined(); }); }); }); diff --git a/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts b/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts new file mode 100644 index 0000000000..170ee50114 --- /dev/null +++ b/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { parseParams } from "../src/command/parameters.js"; + +describe("remainderLength", () => { + // ----- Parameter definitions reused across tests ----- + const singleArg = { + args: { + name: { description: "a name" }, + }, + } as const; + + const twoArgs = { + args: { + first: { description: "first" }, + second: { description: "second" }, + }, + } as const; + + const multipleArg = { + args: { + items: { description: "items", multiple: true }, + }, + } as const; + + const multipleAndSingle = { + args: { + items: { description: "items", multiple: true }, + last: { description: "last", optional: true }, + }, + } as const; + + const strFlag = { + flags: { + str: { description: "string flag", type: "string" }, + }, + } as const; + + const numFlag = { + flags: { + num: { description: "number flag", type: "number" }, + }, + } as const; + + const boolFlag = { + flags: { + bool: { description: "boolean flag", type: "boolean" }, + }, + } as const; + + const flagsAndArgs = { + flags: { + bool: { description: "boolean flag", type: "boolean" }, + }, + args: { + name: { description: "a name" }, + }, + } as const; + + const implicitQuoteArg = { + args: { + text: { description: "text", implicitQuotes: true }, + }, + }; + + const optionalArg = { + args: { + name: { description: "a name", optional: true }, + }, + } as const; + + // ---- Non-partial mode (entire input consumed → 0) ---- + + describe("non-partial", () => { + it("empty input", () => { + expect(parseParams("", optionalArg).remainderLength).toBe(0); + }); + + it("single argument consumed", () => { + expect(parseParams("hello", singleArg).remainderLength).toBe(0); + }); + + it("two arguments consumed", () => { + expect( + parseParams("hello world", twoArgs).remainderLength, + ).toBe(0); + }); + + it("multiple arguments consumed", () => { + expect(parseParams("a b c", multipleArg).remainderLength).toBe( + 0, + ); + }); + + it("flag with string value consumed", () => { + expect( + parseParams("--str value", strFlag).remainderLength, + ).toBe(0); + }); + + it("boolean flag consumed", () => { + expect(parseParams("--bool", boolFlag).remainderLength).toBe(0); + }); + + it("boolean flag with explicit true consumed", () => { + expect( + parseParams("--bool true", boolFlag).remainderLength, + ).toBe(0); + }); + + it("flags and args consumed", () => { + expect( + parseParams("--bool hello", flagsAndArgs).remainderLength, + ).toBe(0); + }); + + it("quoted argument consumed", () => { + expect( + parseParams("'hello world'", singleArg).remainderLength, + ).toBe(0); + }); + + it("whitespace-padded input trimmed and consumed", () => { + expect( + parseParams(" hello ", singleArg).remainderLength, + ).toBe(0); + }); + + it("implicit quote argument consumes rest of line", () => { + expect( + parseParams("hello world extra", implicitQuoteArg) + .remainderLength, + ).toBe(0); + }); + + it("separator between multiple and single arg", () => { + expect( + parseParams("a b -- c", multipleAndSingle).remainderLength, + ).toBe(0); + }); + + it("flag with value plus argument consumed", () => { + expect( + parseParams("--str value hello", { + flags: { str: { description: "s", type: "string" } }, + args: { name: { description: "n" } }, + } as const).remainderLength, + ).toBe(0); + }); + }); + + // ---- Partial mode — fully consumed ---- + + describe("partial - fully consumed", () => { + it("empty input", () => { + expect(parseParams("", optionalArg, true).remainderLength).toBe( + 0, + ); + }); + + it("single argument", () => { + expect( + parseParams("hello", singleArg, true).remainderLength, + ).toBe(0); + }); + + it("flag with value", () => { + expect( + parseParams("--str value", strFlag, true).remainderLength, + ).toBe(0); + }); + + it("boolean flag without value", () => { + expect( + parseParams("--bool", boolFlag, true).remainderLength, + ).toBe(0); + }); + + it("trailing whitespace after completed arg", () => { + expect( + parseParams("hello ", singleArg, true).remainderLength, + ).toBe(0); + }); + + it("multiple arguments", () => { + expect( + parseParams("a b c", multipleArg, true).remainderLength, + ).toBe(0); + }); + + it("flags and args", () => { + expect( + parseParams("--bool hello", flagsAndArgs, true) + .remainderLength, + ).toBe(0); + }); + }); + + // ---- Partial mode — partially consumed ---- + + describe("partial - partially consumed", () => { + it("too many arguments leaves remainder", () => { + const result = parseParams("hello extra", singleArg, true); + expect(result.remainderLength).toBe("extra".length); + }); + + it("invalid flag leaves full input as remainder", () => { + const result = parseParams("--unknown", strFlag, true); + expect(result.remainderLength).toBe("--unknown".length); + }); + + it("string flag missing value with next flag-like token", () => { + // --str consumed as flag name, but --other is not a valid value + // so curr is rolled back to "--other" + const result = parseParams("--str --other", strFlag, true); + expect(result.remainderLength).toBe("--other".length); + }); + + it("string flag missing value at end of input", () => { + // Flag name consumed, no more input → remainder is 0 + const result = parseParams("--str", strFlag, true); + expect(result.remainderLength).toBe(0); + }); + + it("number flag with non-numeric value", () => { + // --num consumed as flag name, "abc" fails number parse and + // is rolled back + const result = parseParams("--num abc", numFlag, true); + expect(result.remainderLength).toBe("abc".length); + }); + + it("boolean flag defaults true then extra has no arg def", () => { + // --bool consumed with default true, "extra" has no arg def + const result = parseParams("--bool extra", boolFlag, true); + expect(result.remainderLength).toBe("extra".length); + }); + + it("multiple arg terminated by invalid flag-like token", () => { + const result = parseParams("a b --bad", multipleArg, true); + expect(result.remainderLength).toBe("--bad".length); + }); + + it("two valid args then too many", () => { + const result = parseParams("hello world extra", twoArgs, true); + expect(result.remainderLength).toBe("extra".length); + }); + + it("valid flag+value then invalid flag", () => { + const result = parseParams( + "--str hello --unknown", + strFlag, + true, + ); + expect(result.remainderLength).toBe("--unknown".length); + }); + + it("leading whitespace is trimmed before measuring", () => { + // partial trims only the start; "extra" is 5 chars + const result = parseParams(" hello extra", singleArg, true); + expect(result.remainderLength).toBe("extra".length); + }); + }); +}); From ae7944ce8fe04a5636669d65109b37eedbcba668 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 10:41:09 -0700 Subject: [PATCH 13/51] Fix completion startIndex for lastCompletableParam and make whitespace backing unconditional - Make lastCompletableParam path exclusive: clear other completions and adjust startIndex to token start when inside an open-quoted or implicitQuotes token - Make outer whitespace backing unconditional so the anchor always sits at the last token boundary (consumers default separatorMode to "space" and expect the separator in rawPrefix) - Remove inner whitespace backing that broke flag filtering - Update contract and inline comments to describe the general consumer expectation - Add twoarg and search test commands; add 5 new lastCompletableParam tests; update expectations for unconditional backing --- .../dispatcher/src/command/completion.ts | 127 ++++++++----- .../dispatcher/test/completion.spec.ts | 179 ++++++++++++++++-- 2 files changed, 233 insertions(+), 73 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 655549a590..ed279d1c52 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -190,6 +190,15 @@ async function getCommandParameterCompletion( agentCommandCompletions.push(pendingFlag); } + // Compute startIndex from how far parseParams consumed the suffix. + // remainderLength is the length of the (trimmed) parameter text + // that was NOT successfully parsed — everything before it is part + // of the longest valid prefix. + let startIndex = inputLength - params.remainderLength; + debug( + `Command completion parameter consumed length: ${params.remainderLength}`, + ); + let agentInvoked = false; const agent = context.agents.getAppAgent(result.actualAppAgentName); if (agent.getCommandCompletion) { @@ -203,12 +212,24 @@ async function getCommandParameterCompletion( quoted === false || (quoted === undefined && lastParamImplicitQuotes) ) { + // The user is inside a token (open quote or + // implicitQuotes rest-of-line) — completions for + // other parameters or flags at the original + // startIndex are invalid because no new token can + // start. Make this path exclusive: clear earlier + // completions and adjust startIndex to the token + // start so the caller replaces the partial token. + agentCommandCompletions.length = 0; + completions.length = 0; agentCommandCompletions.push(lastCompletableParam); + startIndex -= valueToken.length; } } if (agentCommandCompletions.length > 0) { - const sessionContext = context.agents.getSessionContext( - result.actualAppAgentName, + const agentName = result.actualAppAgentName; + const sessionContext = context.agents.getSessionContext(agentName); + debug( + `Command completion parameter with agent: '${agentName}' with params ${JSON.stringify(agentCommandCompletions)}`, ); const agentGroups = await agent.getCommandCompletion( result.commands, @@ -216,30 +237,27 @@ async function getCommandParameterCompletion( agentCommandCompletions, sessionContext, ); + + // Allow grammar-reported prefixLength (from groups) to override + // the parse-derived startIndex. This handles CJK and other + // non-space-delimited scripts where the grammar matcher is the + // authoritative source for how far into the input it consumed. + const groupPrefixLength = agentGroups.find( + (g) => g.prefixLength !== undefined, + )?.prefixLength; + if (groupPrefixLength !== undefined && groupPrefixLength != 0) { + startIndex += groupPrefixLength; + // we have advanced the startIndex, so existing completions are no longer valid, clear them out. + completions.length = 0; + } completions.push(...agentGroups); agentInvoked = true; + debug( + `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}`, + ); } } - // Compute startIndex from how far parseParams consumed the suffix. - // remainderLength is the length of the (trimmed) parameter text - // that was NOT successfully parsed — everything before it is part - // of the longest valid prefix. - // - // Because remainderLength excludes inter-token whitespace (the - // tokenizer strips it), we back startIndex over any whitespace - // that sits between the last consumed token and the unconsumed - // remainder so that the separator space is not treated as part - // of the consumed prefix. - let startIndex = inputLength - params.remainderLength; - - const suffix = result.suffix; - let suffixPos = suffix.length - params.remainderLength; - while (suffixPos > 0 && /\s/.test(suffix[suffixPos - 1])) { - suffixPos--; - startIndex--; - } - // Determine whether the completion set is exhaustive. // Agent-provided completions are conservatively treated as // non-exhaustive since agents cannot yet signal exhaustiveness. @@ -289,12 +307,21 @@ async function getCommandParameterCompletion( // Return fields (see CommandCompletionResult): // // startIndex Length of the longest resolved prefix. -// input[0..startIndex) was fully consumed by -// normalizeCommand → resolveCommand (→ parseParams); -// completions describe what can validly follow. +// input[0..startIndex) is the "anchor" — the text +// that was fully consumed by normalizeCommand → +// resolveCommand (→ parseParams). Completions +// describe what can validly follow after the anchor. // May be overridden by a grammar-reported prefixLength -// from a CompletionGroup. Trailing whitespace before -// startIndex is stripped when separatorMode requires it. +// from a CompletionGroup. +// +// Trailing whitespace is always stripped from the +// anchor so that it sits at the last token boundary. +// Consumers treat the text after the anchor as +// "rawPrefix", expect it to begin with a separator +// (per separatorMode, which defaults to "space" +// when omitted), and strip the separator before +// filtering. Keeping whitespace inside the anchor +// would violate this contract. // // completions Array of CompletionGroups from up to three sources: // (a) built-in command / subcommand / agent-name lists, @@ -336,7 +363,11 @@ export async function getCommandCompletion( // The parse-derived startIndex: command resolution consumed // everything up to the suffix; within the suffix, parameter // parsing determines the last incomplete token position. - let startIndex = input.length - result.suffix.length; + const commandConsumedLength = input.length - result.suffix.length; + debug( + `Command completion command consumed length: ${commandConsumedLength}, suffix: '${result.suffix}'`, + ); + let startIndex = commandConsumedLength; // Collect completions const completions: CompletionGroup[] = []; @@ -367,13 +398,12 @@ export async function getCommandCompletion( // (moving startIndex forward), they've committed to the // default — subcommand names would be filtered against // the wrong text at the wrong position. - const commandBoundary = input.length - result.suffix.length; const addSubcommands = table !== undefined && !result.matched && getDefaultSubCommandDescriptor(table) === descriptor && (parameterCompletions === undefined || - parameterCompletions.startIndex <= commandBoundary); + parameterCompletions.startIndex <= commandConsumedLength); if (addSubcommands) { completions.push({ @@ -390,9 +420,7 @@ export async function getCommandCompletion( // specified with nothing more to type. } else { completions.push(...parameterCompletions.completions); - if (!addSubcommands) { - startIndex = parameterCompletions.startIndex; - } + startIndex = parameterCompletions.startIndex; complete = parameterCompletions.complete; } } else if (table !== undefined) { @@ -442,32 +470,27 @@ export async function getCommandCompletion( }); } - // Allow grammar-reported prefixLength (from groups) to override - // the parse-derived startIndex. This handles CJK and other - // non-space-delimited scripts where the grammar matcher is the - // authoritative source for how far into the input it consumed. - const groupPrefixLength = completions.find( - (g) => g.prefixLength !== undefined, - )?.prefixLength; - if (groupPrefixLength !== undefined) { - startIndex = groupPrefixLength; - } - // Extract separatorMode from groups. The most restrictive // mode wins ("space" and "spacePunctuation" are both // separator-requiring; among those, "space" is more // restrictive because it only allows whitespace). const separatorMode = aggregateSeparatorMode(completions); - // Like the grammar matcher, exclude trailing whitespace before - // startIndex when a separator is needed — the separator lives - // between the command anchor and the completion text, not inside - // the filter prefix. This handles both "@config " (trailing - // space) and "@config c" (space between command and partial token). - if (separatorMode === "space" || separatorMode === "spacePunctuation") { - while (startIndex > 0 && /\s/.test(input[startIndex - 1])) { - startIndex--; - } + // Always strip trailing whitespace from the anchor. + // + // Consumers default separatorMode to "space" when the result + // omits it, so they always expect the separator to live in + // rawPrefix (= input after the anchor), not inside the anchor + // itself. Keeping whitespace inside the anchor would cause + // separator validation to fail — rawPrefix would start with + // the next token instead of the expected space. + // + // This is unconditional because parseParams' remainderLength + // excludes inter-token whitespace, so the raw startIndex can + // land on whitespace even when the result has completions that + // don't set separatorMode. + while (startIndex > 0 && /\s/.test(input[startIndex - 1])) { + startIndex--; } const completionResult: CommandCompletionResult = { diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 1b527b7e3b..9532d9e9a0 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -88,6 +88,56 @@ const handlers = { }, run: async () => {}, }, + twoarg: { + description: "Two-arg command", + parameters: { + args: { + first: { + description: "First arg", + }, + second: { + description: "Second arg", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + names: string[], + ): Promise => { + return [ + { + name: "Values", + completions: ["alpha", "beta"], + }, + ]; + }, + }, + search: { + description: "Implicit-quotes command", + parameters: { + args: { + query: { + description: "Search query", + implicitQuotes: true, + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + _names: string[], + ): Promise => { + return [ + { + name: "Suggestions", + completions: ["hello world", "foo bar"], + }, + ]; + }, + }, }, } as const; @@ -196,8 +246,9 @@ describe("Command Completion - startIndex", () => { // "@comptest run " → suffix is "" after command resolution, // "run" is explicitly matched so no Subcommands group. // parameter parsing has no tokens so - // startIndex = inputLength - 0 = 14 - expect(result!.startIndex).toBe(14); + // startIndex = inputLength - 0 = 14, then unconditional + // whitespace backing rewinds over trailing space → 13. + expect(result!.startIndex).toBe(13); // Agent getCompletion is invoked for the "task" arg → // completions are not exhaustive. expect(result!.complete).toBe(false); @@ -228,8 +279,9 @@ describe("Command Completion - startIndex", () => { expect(result).toBeDefined(); // "@comptest nested sub " (21 chars) // suffix is "" after command resolution; - // parameter parsing has no tokens; startIndex = 21 - 0 = 21 - expect(result!.startIndex).toBe(21); + // parameter parsing has no tokens; startIndex = 21 - 0 = 21, + // then unconditional whitespace backing → 20. + expect(result!.startIndex).toBe(20); // Unfilled "value" arg (free-form) → not exhaustive. expect(result!.complete).toBe(false); }); @@ -242,8 +294,9 @@ describe("Command Completion - startIndex", () => { expect(result).toBeDefined(); // "@comptest nested sub --ver" (26 chars) // suffix is "--ver", parameter parsing sees token "--ver" (5 chars) - // startIndex = 26 - 5 = 21 - expect(result!.startIndex).toBe(21); + // startIndex = 26 - 5 = 21, then unconditional whitespace + // backing rewinds over the space before "--ver" → 20. + expect(result!.startIndex).toBe(20); // Unfilled "value" arg → not exhaustive. expect(result!.complete).toBe(false); }); @@ -275,8 +328,9 @@ describe("Command Completion - startIndex", () => { const result = await getCommandCompletion(" ", context); expect(result).toBeDefined(); // " " normalizes to a command prefix with no suffix; - // startIndex = input.length - suffix.length = 2 - expect(result!.startIndex).toBe(2); + // startIndex = input.length - suffix.length = 2, then + // unconditional whitespace backing rewinds to 0. + expect(result!.startIndex).toBe(0); }); }); @@ -348,8 +402,8 @@ describe("Command Completion - startIndex", () => { ); // "@comptest run build " (20 chars) // suffix is "build ", token "build" is fully consumed, - // remainderLength = 0 → startIndex = 20, then whitespace - // backing rewinds over the trailing space → startIndex = 19. + // remainderLength = 0 → startIndex = 20, then unconditional + // whitespace backing rewinds over trailing space → 19. expect(result).toBeDefined(); expect(result!.startIndex).toBe(19); // All positional args filled ("task" consumed "build"), @@ -367,8 +421,8 @@ describe("Command Completion - startIndex", () => { // "@comptest run hello --unknown" (29 chars) // suffix is "hello --unknown", "hello" fills the "task" arg, // "--unknown" is not a defined flag → remainderLength = 9. - // startIndex = 29 - 9 = 20, then whitespace backing rewinds - // over the space between "hello" and "--unknown" → 19. + // startIndex = 29 - 9 = 20, then unconditional whitespace + // backing rewinds over the space → 19. expect(result!.startIndex).toBe(19); }); @@ -381,8 +435,8 @@ describe("Command Completion - startIndex", () => { // "@comptest run hello --unknown" (31 chars) // suffix is "hello --unknown", "hello" fills "task", // "--unknown" unconsumed → remainderLength = 9. - // startIndex = 31 - 9 = 22, then whitespace backing rewinds - // over three spaces → 19. + // startIndex = 31 - 9 = 22, then unconditional whitespace + // backing rewinds over three spaces → 19. expect(result!.startIndex).toBe(19); }); }); @@ -554,8 +608,9 @@ describe("Command Completion - startIndex", () => { // "--lev" doesn't resolve (exact match only), so parseParams // leaves it unconsumed. startIndex points to where "--lev" // starts — it is the filter text. - // "@comptest flagsonly " = 20 chars consumed. - expect(result.startIndex).toBe(20); + // "@comptest flagsonly " = 20 chars consumed, then + // unconditional whitespace backing → 19. + expect(result.startIndex).toBe(19); const flags = result.completions.find( (g) => g.name === "Command Flags", ); @@ -586,8 +641,9 @@ describe("Command Completion - startIndex", () => { context, ); // "@flattest --rel" (15 chars) - // startIndex = 15 - 5 ("--rel") = 10 - expect(result.startIndex).toBe(10); + // startIndex = 15 - 5 ("--rel") = 10, then unconditional + // whitespace backing rewinds over space → 9. + expect(result.startIndex).toBe(9); expect(result.complete).toBe(false); }); @@ -620,9 +676,8 @@ describe("Command Completion - startIndex", () => { // "@comptest build " (16 chars) // Resolves to default "run" (not explicit match). // "build" fills the "task" arg, trailing space present. - // remainderLength = 0 → startIndex = 16, then whitespace - // backing rewinds over the trailing space → startIndex = 15, - // past the command boundary (10). + // remainderLength = 0 → startIndex = 16, then unconditional + // whitespace backing → 15, past the command boundary (10). // Subcommand names are no longer relevant at this // position; only parameter completions remain. const subcommands = result.completions.find( @@ -658,4 +713,86 @@ describe("Command Completion - startIndex", () => { expect(subcommands).toBeUndefined(); }); }); + + describe("lastCompletableParam adjusts startIndex", () => { + it("backs startIndex to open-quote token start for '@comptest run \"bu'", async () => { + const result = await getCommandCompletion( + '@comptest run "bu', + context, + ); + // '@comptest run "bu' (17 chars) + // suffix is '"bu', parseParams consumes the open-quoted + // token through EOF → remainderLength = 0. + // lastCompletableParam = "task", quoted = false (open quote). + // Exclusive path: startIndex = 17 - 3 = 14, then unconditional + // whitespace backing rewinds over the space before '"bu' → 13. + expect(result.startIndex).toBe(13); + // Agent was invoked → not exhaustive. + expect(result.complete).toBe(false); + // Flag groups and nextArgs completions should be cleared. + const flags = result.completions.find( + (g) => g.name === "Command Flags", + ); + expect(flags).toBeUndefined(); + }); + + it("backs startIndex for multi-arg open quote '@comptest twoarg \"partial'", async () => { + const result = await getCommandCompletion( + '@comptest twoarg "partial', + context, + ); + // '@comptest twoarg "partial' (25 chars) + // suffix is '"partial', parseParams consumes open quote + // through EOF → remainderLength = 0. + // lastCompletableParam = "first", quoted = false. + // Exclusive path: startIndex = 25 - 8 = 17, then unconditional + // whitespace backing rewinds over the space → 16. + // "second" from nextArgs should NOT be in agentCommandCompletions. + expect(result.startIndex).toBe(16); + expect(result.complete).toBe(false); + }); + + it("backs startIndex for implicitQuotes '@comptest search hello world'", async () => { + const result = await getCommandCompletion( + "@comptest search hello world", + context, + ); + // "@comptest search hello world" (28 chars) + // suffix is "hello world", implicitQuotes consumes rest + // of line → remainderLength = 0, token = "hello world". + // lastCompletableParam = "query", lastParamImplicitQuotes = true. + // Exclusive path: startIndex = 28 - 11 = 17, then unconditional + // whitespace backing rewinds over the space → 16. + expect(result.startIndex).toBe(16); + expect(result.complete).toBe(false); + }); + + it("does not adjust startIndex for fully-quoted token", async () => { + const result = await getCommandCompletion( + '@comptest run "build"', + context, + ); + // '@comptest run "build"' (21 chars) + // Token '"build"' is fully quoted → isFullyQuoted returns true. + // lastCompletableParam condition does NOT fire. + // remainderLength = 0 → startIndex = 21, unconditional + // whitespace backing finds no space at input[20]='"' → 21. + // "task" is filled, no flags → exhaustive. + expect(result.startIndex).toBe(21); + expect(result.complete).toBe(true); + }); + + it("does not adjust startIndex for bare unquoted token", async () => { + const result = await getCommandCompletion( + "@comptest run bu", + context, + ); + // "bu" is not quoted at all → isFullyQuoted returns undefined. + // lastParamImplicitQuotes is false for "task" arg. + // lastCompletableParam condition does NOT fire. + // startIndex stays at 16 (end of input). + expect(result.startIndex).toBe(16); + expect(result.complete).toBe(true); + }); + }); }); From 5c7b058a3926db86662cecc39321e45f7bb1b818 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 12:24:14 -0700 Subject: [PATCH 14/51] fix: compute grammar prefixLength from token start, not tokenBoundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Grammar-reported prefixLength is relative to the token content start (after the separator space), not to tokenBoundary (before it). Track tokenStartIndex separately so Site 4 adds prefixLength to the correct base. Refactor grammar/grammariq test mocks: - Extract shared grammarCompletion() helper with CJK and English branches - Return suffix completions (タワー/駅, Tower/Station) with realistic prefixLength and separatorMode instead of full-word completions - Validate names parameter in all mock getCompletion implementations Add 3 new tests (44→47): English prefix with space separator, completed CJK match returns no completions, no-text initial completions via grammariq. --- .../dispatcher/src/command/completion.ts | 65 ++-- .../dispatcher/test/completion.spec.ts | 305 +++++++++++++++++- 2 files changed, 342 insertions(+), 28 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index ed279d1c52..ad60543ad3 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -84,6 +84,16 @@ function getPendingFlag( return `--${resolvedFlag[0]}`; // use the full flag name in case it was a short flag } +// Rewind index past any trailing whitespace in `text` so it sits +// at the end of the preceding token. Returns `index` unchanged +// when the character before it is already non-whitespace. +function tokenBoundary(text: string, index: number): number { + while (index > 0 && /\s/.test(text[index - 1])) { + index--; + } + return index; +} + // True if surrounded by quotes at both ends (matching single or double quotes). // False if only start with a quote. // Undefined if no starting quote. @@ -153,7 +163,7 @@ async function getCommandParameterCompletion( descriptor: CommandDescriptor, context: CommandHandlerContext, result: ResolveCommandResult, - inputLength: number, + input: string, ): Promise { const completions: CompletionGroup[] = []; if (typeof descriptor.parameters !== "object") { @@ -193,8 +203,14 @@ async function getCommandParameterCompletion( // Compute startIndex from how far parseParams consumed the suffix. // remainderLength is the length of the (trimmed) parameter text // that was NOT successfully parsed — everything before it is part - // of the longest valid prefix. - let startIndex = inputLength - params.remainderLength; + // of the longest valid prefix. Since parseParams strips inter- + // token whitespace (trimStart), the raw arithmetic can land on + // the separator space — tokenBoundary rewinds to the preceding + // token edge. + let startIndex = tokenBoundary( + input, + input.length - params.remainderLength, + ); debug( `Command completion parameter consumed length: ${params.remainderLength}`, ); @@ -205,6 +221,7 @@ async function getCommandParameterCompletion( const { tokens, lastCompletableParam, lastParamImplicitQuotes } = params; + let tokenStartIndex: number | undefined; if (lastCompletableParam !== undefined && tokens.length > 0) { const valueToken = tokens[tokens.length - 1]; const quoted = isFullyQuoted(valueToken); @@ -222,7 +239,8 @@ async function getCommandParameterCompletion( agentCommandCompletions.length = 0; completions.length = 0; agentCommandCompletions.push(lastCompletableParam); - startIndex -= valueToken.length; + tokenStartIndex = startIndex - valueToken.length; + startIndex = tokenBoundary(input, tokenStartIndex); } } if (agentCommandCompletions.length > 0) { @@ -242,11 +260,15 @@ async function getCommandParameterCompletion( // the parse-derived startIndex. This handles CJK and other // non-space-delimited scripts where the grammar matcher is the // authoritative source for how far into the input it consumed. + // Grammar prefixLength is relative to the token content start + // (after the separator space), not to tokenBoundary (before + // it), so use tokenStartIndex when available. const groupPrefixLength = agentGroups.find( (g) => g.prefixLength !== undefined, )?.prefixLength; if (groupPrefixLength !== undefined && groupPrefixLength != 0) { - startIndex += groupPrefixLength; + startIndex = + (tokenStartIndex ?? startIndex) + groupPrefixLength; // we have advanced the startIndex, so existing completions are no longer valid, clear them out. completions.length = 0; } @@ -314,14 +336,22 @@ async function getCommandParameterCompletion( // May be overridden by a grammar-reported prefixLength // from a CompletionGroup. // -// Trailing whitespace is always stripped from the -// anchor so that it sits at the last token boundary. +// startIndex is always placed at a token boundary +// (not on separator whitespace). Each production +// site — resolveCommand consumed length, parseParams +// remainder, and the lastCompletableParam adjustment +// — applies tokenBoundary() to enforce this. // Consumers treat the text after the anchor as // "rawPrefix", expect it to begin with a separator // (per separatorMode, which defaults to "space" // when omitted), and strip the separator before // filtering. Keeping whitespace inside the anchor // would violate this contract. +// The grammar-reported prefixLength override (Site 4) +// is added to the token start position (before the +// separator space), not to tokenBoundary — the grammar +// reports how many characters of the token content it +// consumed, which is relative to the token start. // // completions Array of CompletionGroups from up to three sources: // (a) built-in command / subcommand / agent-name lists, @@ -367,7 +397,7 @@ export async function getCommandCompletion( debug( `Command completion command consumed length: ${commandConsumedLength}, suffix: '${result.suffix}'`, ); - let startIndex = commandConsumedLength; + let startIndex = tokenBoundary(input, commandConsumedLength); // Collect completions const completions: CompletionGroup[] = []; @@ -388,7 +418,7 @@ export async function getCommandCompletion( descriptor, context, result, - input.length, + input, ); // Include sibling subcommand names when resolved to the @@ -476,23 +506,6 @@ export async function getCommandCompletion( // restrictive because it only allows whitespace). const separatorMode = aggregateSeparatorMode(completions); - // Always strip trailing whitespace from the anchor. - // - // Consumers default separatorMode to "space" when the result - // omits it, so they always expect the separator to live in - // rawPrefix (= input after the anchor), not inside the anchor - // itself. Keeping whitespace inside the anchor would cause - // separator validation to fail — rawPrefix would start with - // the next token instead of the expected space. - // - // This is unconditional because parseParams' remainderLength - // excludes inter-token whitespace, so the raw startIndex can - // land on whitespace even when the result has completions that - // don't set separatorMode. - while (startIndex > 0 && /\s/.test(input[startIndex - 1])) { - startIndex--; - } - const completionResult: CommandCompletionResult = { startIndex, completions, diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 9532d9e9a0..1a2c5b3693 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -18,6 +18,55 @@ import { getCommandCompletion } from "../src/command/completion.js"; // --------------------------------------------------------------------------- // Test agent with parameters for completion testing // --------------------------------------------------------------------------- + +// Shared grammar completion mock. Simulates a grammar that recognises +// CJK ("東京" → "タワー"/"駅") and English ("Tokyo" → "Tower"/"Station") +// prefixes. `token` is the raw last token from parseParams — it may +// include a leading quote for open-quoted input. +function grammarCompletion(token: string): CompletionGroup[] { + // Strip a leading quote so grammar match logic operates on text only. + const text = token.startsWith('"') ? token.substring(1) : token; + const quoteOffset = token.length - text.length; // 0 or 1 + + if (text.startsWith("Tokyo")) { + const suffix = text.substring(5).trim(); + if (suffix.startsWith("Tower") || suffix.startsWith("Station")) { + return []; // completed match + } + return [ + { + name: "Grammar", + completions: ["Tower", "Station"], + prefixLength: quoteOffset + 5, + separatorMode: "space", + }, + ]; + } + if (text.startsWith("東京")) { + const suffix = text.substring(2); + if (suffix.startsWith("タワー") || suffix.startsWith("駅")) { + return []; // completed match + } + return [ + { + name: "Grammar", + completions: ["タワー", "駅"], + prefixLength: quoteOffset + 2, + separatorMode: "optional", + }, + ]; + } + // No prefix matched — offer initial completions. + return [ + { + name: "Grammar", + completions: ["Tokyo ", "東京"], + ...(token.length > 0 ? { prefixLength: 0 } : {}), + separatorMode: "space", + }, + ]; +} + const handlers = { description: "Completion test agent", defaultSubCommand: "run", @@ -35,8 +84,11 @@ const handlers = { getCompletion: async ( _context: unknown, _params: unknown, - _names: string[], + names: string[], ): Promise => { + if (!names.includes("task")) { + return []; + } return [ { name: "Tasks", @@ -106,6 +158,9 @@ const handlers = { _params: unknown, names: string[], ): Promise => { + if (!names.includes("first") && !names.includes("second")) { + return []; + } return [ { name: "Values", @@ -128,8 +183,11 @@ const handlers = { getCompletion: async ( _context: unknown, _params: unknown, - _names: string[], + names: string[], ): Promise => { + if (!names.includes("query")) { + return []; + } return [ { name: "Suggestions", @@ -138,6 +196,53 @@ const handlers = { ]; }, }, + grammar: { + description: "Grammar prefixLength command", + parameters: { + args: { + phrase: { + description: "A CJK phrase", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + params: unknown, + names: string[], + ): Promise => { + if (!names.includes("phrase")) { + return []; + } + const p = params as { tokens?: string[] }; + const lastToken = p.tokens?.[p.tokens.length - 1] ?? ""; + return grammarCompletion(lastToken); + }, + }, + grammariq: { + description: "Grammar with implicitQuotes", + parameters: { + args: { + query: { + description: "CJK search query", + implicitQuotes: true, + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + params: unknown, + names: string[], + ): Promise => { + if (!names.includes("query")) { + return []; + } + const p = params as { tokens?: string[] }; + const lastToken = p.tokens?.[p.tokens.length - 1] ?? ""; + return grammarCompletion(lastToken); + }, + }, }, } as const; @@ -795,4 +900,200 @@ describe("Command Completion - startIndex", () => { expect(result.complete).toBe(true); }); }); + + describe("groupPrefixLength overrides startIndex", () => { + it("open-quote CJK advances startIndex by prefixLength", async () => { + const result = await getCommandCompletion( + '@comptest grammar "東京タ', + context, + ); + // '@comptest grammar "東京タ' (22 chars) + // 0-8: @comptest 9: sp 10-16: grammar 17: sp + // 18: " 19: 東 20: 京 21: タ + // suffix = '"東京タ' (4 chars), open-quoted token. + // lastCompletableParam fires (quoted=false). + // tokenStartIndex = 22 - 4 = 18 (position of '"') + // startIndex = tokenBoundary(input, 18) = 17 + // Agent strips opening quote, matches "東京" (2 chars), + // returns prefixLength = 3 (1 quote + 2 CJK chars). + // startIndex = tokenStartIndex + 3 = 18 + 3 = 21. + // rawPrefix = "タ", "タワー".startsWith("タ") ✓ + expect(result.startIndex).toBe(21); + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeDefined(); + expect(grammar!.completions).toContain("タワー"); + expect(grammar!.completions).toContain("駅"); + expect(result.complete).toBe(false); + }); + + it("implicitQuotes CJK advances startIndex by prefixLength", async () => { + const result = await getCommandCompletion( + "@comptest grammariq 東京タ", + context, + ); + // "@comptest grammariq 東京タ" (23 chars) + // 0-8: @comptest 9: sp 10-18: grammariq 19: sp + // 20: 東 21: 京 22: タ + // suffix = "東京タ" (3 chars), implicitQuotes captures + // rest of line as token. + // lastCompletableParam fires (implicitQuotes). + // tokenStartIndex = 23 - 3 = 20 (position of "東") + // startIndex = tokenBoundary(input, 20) = 19 + // Agent matches "東京" (2 chars), returns prefixLength=2. + // startIndex = tokenStartIndex + 2 = 20 + 2 = 22. + // rawPrefix = "タ", "タワー".startsWith("タ") ✓ + expect(result.startIndex).toBe(22); + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeDefined(); + expect(grammar!.completions).toContain("タワー"); + expect(grammar!.completions).toContain("駅"); + expect(result.complete).toBe(false); + }); + + it("fully-quoted token does not invoke grammar", async () => { + const result = await getCommandCompletion( + '@comptest grammar "東京タ"', + context, + ); + // Token '"東京タ"' is fully quoted → isFullyQuoted = true. + // lastCompletableParam exclusive path does NOT fire. + // All args consumed → nextArgs empty → agent not called. + // prefixLength never applies. + expect(result.startIndex).toBe(23); + expect(result.complete).toBe(true); + // No Grammar group since agent wasn't invoked. + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeUndefined(); + }); + + it("bare unquoted token does not invoke grammar", async () => { + const result = await getCommandCompletion( + "@comptest grammar 東京タ", + context, + ); + // "東京タ" has no quotes, grammar command has no + // implicitQuotes → lastCompletableParam condition false. + // All args consumed → nextArgs empty → agent not called. + expect(result.startIndex).toBe(21); + expect(result.complete).toBe(true); + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeUndefined(); + }); + + it("trailing space without text offers initial completions", async () => { + const result = await getCommandCompletion( + "@comptest grammar ", + context, + ); + // "@comptest grammar " (18 chars) + // suffix is "", no tokens parsed → nextArgs = ["phrase"]. + // Agent called, mock sees empty token list → returns + // completions ["東京"] with no prefixLength. + // groupPrefixLength path does not fire. + // startIndex = tokenBoundary(input, 18) = 17. + expect(result.startIndex).toBe(17); + expect(result.complete).toBe(false); + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeDefined(); + expect(grammar!.completions).toContain("Tokyo "); + expect(grammar!.completions).toContain("東京"); + }); + + it("clears earlier completions when prefixLength is set", async () => { + const result = await getCommandCompletion( + '@comptest grammar "東京タ', + context, + ); + // When groupPrefixLength fires, parameter/flag + // completions from before the agent call are cleared. + const flags = result.completions.find( + (g) => g.name === "Command Flags", + ); + expect(flags).toBeUndefined(); + }); + + it("does not override startIndex when prefixLength is absent", async () => { + // "run" handler returns groups without prefixLength. + const result = await getCommandCompletion( + "@comptest run bu", + context, + ); + expect(result.startIndex).toBe(16); + }); + + it("English prefix with space separator", async () => { + const result = await getCommandCompletion( + "@comptest grammariq Tokyo T", + context, + ); + // "@comptest grammariq Tokyo T" (27 chars) + // 0-8: @comptest 9: sp 10-18: grammariq 19: sp + // 20-24: Tokyo 25: sp 26: T + // Token = "Tokyo T" (7 chars), implicitQuotes. + // lastCompletableParam fires. + // tokenStartIndex = 27 - 7 = 20 + // startIndex = tokenBoundary(input, 20) = 19 + // Mock matches "Tokyo" → prefixLength=5, separatorMode="space". + // startIndex = tokenStartIndex + 5 = 20 + 5 = 25. + // rawPrefix = " T", consumer strips space → filter "T". + // "Tower".startsWith("T") ✓ + expect(result.startIndex).toBe(25); + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeDefined(); + expect(grammar!.completions).toContain("Tower"); + expect(grammar!.completions).toContain("Station"); + expect(result.complete).toBe(false); + }); + + it("completed CJK match returns no completions", async () => { + const result = await getCommandCompletion( + "@comptest grammariq 東京タワー", + context, + ); + // Token = "東京タワー" (5 chars). Mock matches "東京" + // and finds suffix "タワー" starts with "タワー" → + // returns empty (completed match, no more to suggest). + // agentGroups is [], no prefixLength. + // startIndex = tokenBoundary from lastCompletableParam path. + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeUndefined(); + expect(result.complete).toBe(false); + }); + + it("no-text offers initial completions via grammariq", async () => { + const result = await getCommandCompletion( + "@comptest grammariq ", + context, + ); + // "@comptest grammariq " (20 chars) + // No tokens parsed → nextArgs = ["query"]. + // Mock sees empty token → falls to "no prefix matched" + // branch → completions: ["Tokyo ", "東京"], + // prefixLength: 0, separatorMode: "space". + // groupPrefixLength = 0 → condition false → skip. + // startIndex = tokenBoundary(input, 20) = 19. + expect(result.startIndex).toBe(19); + const grammar = result.completions.find( + (g) => g.name === "Grammar", + ); + expect(grammar).toBeDefined(); + expect(grammar!.completions).toContain("Tokyo "); + expect(grammar!.completions).toContain("東京"); + expect(result.complete).toBe(false); + }); + }); }); From 630f8c27d0b46c458507c561d6feb6c9a5c525d2 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 16:15:05 -0700 Subject: [PATCH 15/51] Refactor matchGrammarCompletion to eagerly filter candidates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace post-loop filtering of completion and property candidates with eager inline filtering during the main loop. Candidates are added directly to the output arrays, and whenever maxPrefixLength increases the arrays are cleared — eliminating the need for intermediate candidate arrays and a separate post-loop filtering pass. --- .../actionGrammar/src/grammarMatcher.ts | 149 +++++++----------- 1 file changed, 53 insertions(+), 96 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 314a3e5a42..22d364c2f5 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1208,23 +1208,13 @@ export function matchGrammarCompletion( // parts, wildcard extensions, repeat groups) during processing. const pending = initialMatchState(grammar); - // Accumulate completion candidates with their associated prefix - // lengths. After the main loop we filter to keep only candidates - // whose prefix length equals the overall maximum — completions - // from shorter partial matches are irrelevant when a longer (or - // exact) match exists. - const completionCandidates: Array<{ - text: string; - prefixLength: number; - needsSep: boolean; - spacingMode: SpacingMode; - }> = []; - const propertyCandidates: Array<{ - property: GrammarCompletionProperty; - prefixLength: number; - needsSep: boolean; - spacingMode: SpacingMode; - }> = []; + // Direct output arrays — candidates are added eagerly and cleared + // whenever maxPrefixLength increases, so no post-loop filtering is + // needed. Only candidates whose prefix length equals the current + // maximum are kept. + const completions: string[] = []; + const properties: GrammarCompletionProperty[] = []; + let separatorMode: GrammarSeparatorMode | undefined; // Track the furthest point the grammar consumed across all // states (including exact matches). This tells the caller where @@ -1232,6 +1222,18 @@ export function matchGrammarCompletion( // whitespace (which breaks for CJK and other non-space scripts). let maxPrefixLength: number | undefined; + // Helper: update maxPrefixLength. When it increases, all previously + // accumulated completions came from shorter matches and are + // irrelevant — clear them. + function updateMaxPrefixLength(prefixLength: number): void { + if (maxPrefixLength === undefined || prefixLength > maxPrefixLength) { + maxPrefixLength = prefixLength; + completions.length = 0; + properties.length = 0; + separatorMode = undefined; + } + } + // --- Main loop: process every pending state --- while (pending.length > 0) { const state = pending.pop()!; @@ -1260,10 +1262,7 @@ export function matchGrammarCompletion( // filtered out in the post-loop step. if (matched) { debugCompletion("Matched. Nothing to complete."); - maxPrefixLength = - maxPrefixLength === undefined - ? state.index - : Math.max(maxPrefixLength, state.index); + updateMaxPrefixLength(state.index); continue; } @@ -1278,7 +1277,9 @@ export function matchGrammarCompletion( // The next expected part is a literal keyword string. // Offer it as a completion (e.g. "music" after "play"). const completionText = nextPart.value.join(" "); - debugCompletion(`Adding completion text: "${completionText}"`); + debugCompletion( + `Adding completion candidate: "${completionText}" (consumed ${state.index} chars, spacing=${state.spacingMode ?? "auto"})`, + ); // Determine whether a separator (e.g. space) is needed // between the content at matchedPrefixLength and the @@ -1305,19 +1306,15 @@ export function matchGrammarCompletion( ); } - completionCandidates.push({ - text: completionText, - prefixLength: state.index, - needsSep: candidateNeedsSep, - spacingMode: state.spacingMode, - }); - - // Record how far into the prefix the grammar consumed - // before reaching this completion point. - maxPrefixLength = - maxPrefixLength === undefined - ? state.index - : Math.max(maxPrefixLength, state.index); + updateMaxPrefixLength(state.index); + if (state.index === maxPrefixLength) { + completions.push(completionText); + separatorMode = mergeSeparatorMode( + separatorMode, + candidateNeedsSep, + state.spacingMode, + ); + } } // Note: non-string next parts (wildcard, number, rules) in // Category 2 don't produce completions here — wildcards are @@ -1373,19 +1370,15 @@ export function matchGrammarCompletion( ); } - propertyCandidates.push({ - property: completionProperty, - prefixLength: candidatePrefixLength, - needsSep: candidateNeedsSep, - spacingMode: state.spacingMode, - }); - - // The wildcard starts at pendingWildcard.start; the - // grammar consumed everything before that. - maxPrefixLength = - maxPrefixLength === undefined - ? candidatePrefixLength - : Math.max(maxPrefixLength, candidatePrefixLength); + updateMaxPrefixLength(candidatePrefixLength); + if (candidatePrefixLength === maxPrefixLength) { + properties.push(completionProperty); + separatorMode = mergeSeparatorMode( + separatorMode, + candidateNeedsSep, + state.spacingMode, + ); + } } } else if (!matched) { // --- Category 3b: Completion after consumed prefix --- @@ -1395,10 +1388,9 @@ export function matchGrammarCompletion( // caller can use matchedPrefixLength to determine how // much of the input was successfully consumed and // filter completions by any trailing text beyond that - // point. Post-loop filtering ensures only candidates - // at the maximum prefix length are returned, so - // completions from shorter partial matches are - // automatically discarded when a longer match exists. + // point. Candidates from shorter partial matches are + // automatically discarded when a longer match updates + // maxPrefixLength. const currentPart = state.parts[state.partIndex]; if ( currentPart !== undefined && @@ -1426,55 +1418,20 @@ export function matchGrammarCompletion( ); } - completionCandidates.push({ - text: fullText, - prefixLength: state.index, - needsSep: candidateNeedsSep, - spacingMode: state.spacingMode, - }); - - // state.index is where matching stopped (before the - // trailing text), so completions begin there. - maxPrefixLength = - maxPrefixLength === undefined - ? state.index - : Math.max(maxPrefixLength, state.index); + updateMaxPrefixLength(state.index); + if (state.index === maxPrefixLength) { + completions.push(fullText); + separatorMode = mergeSeparatorMode( + separatorMode, + candidateNeedsSep, + state.spacingMode, + ); + } } } } } - // --- Post-loop filtering --- - // Only keep candidates whose prefix length equals the overall - // maximum. Completions from shorter partial matches are - // irrelevant when a longer (or exact) match consumed more input. - const completions: string[] = []; - const properties: GrammarCompletionProperty[] = []; - let separatorMode: GrammarSeparatorMode | undefined; - - if (maxPrefixLength !== undefined) { - for (const c of completionCandidates) { - if (c.prefixLength === maxPrefixLength) { - completions.push(c.text); - separatorMode = mergeSeparatorMode( - separatorMode, - c.needsSep, - c.spacingMode, - ); - } - } - for (const p of propertyCandidates) { - if (p.prefixLength === maxPrefixLength) { - properties.push(p.property); - separatorMode = mergeSeparatorMode( - separatorMode, - p.needsSep, - p.spacingMode, - ); - } - } - } - const result: GrammarCompletionResult = { completions, properties, From b76535f6e2d9b43cb57b5b282d317787ebe4984a Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 16:59:25 -0700 Subject: [PATCH 16/51] Add minPrefixLength to matchGrammarCompletion; enforce longest-prefix-wins in grammarStore --- .../actionGrammar/src/grammarMatcher.ts | 9 +-- ts/packages/cache/src/cache/grammarStore.ts | 72 ++++++++++--------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 22d364c2f5..816c1847b4 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1200,6 +1200,7 @@ export function matchGrammar(grammar: Grammar, request: string) { export function matchGrammarCompletion( grammar: Grammar, prefix: string, + minPrefixLength?: number, ): GrammarCompletionResult { debugCompletion(`Start completion for prefix: "${prefix}"`); @@ -1220,13 +1221,13 @@ export function matchGrammarCompletion( // states (including exact matches). This tells the caller where // the "filter text" begins so it doesn't have to guess from // whitespace (which breaks for CJK and other non-space scripts). - let maxPrefixLength: number | undefined; + let maxPrefixLength = minPrefixLength ?? 0; // Helper: update maxPrefixLength. When it increases, all previously - // accumulated completions came from shorter matches and are - // irrelevant — clear them. + // accumulated completions from shorter matches are irrelevant + // — clear them. function updateMaxPrefixLength(prefixLength: number): void { - if (maxPrefixLength === undefined || prefixLength > maxPrefixLength) { + if (prefixLength > maxPrefixLength) { maxPrefixLength = prefixLength; completions.length = 0; properties.length = 0; diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index b6629e0455..2cba0b7199 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -295,7 +295,7 @@ export class GrammarStoreImpl implements GrammarStore { } const completions: string[] = []; const properties: CompletionProperty[] = []; - let matchedPrefixLength: number | undefined; + let matchedPrefixLength = 0; let separatorMode: SeparatorMode | undefined; const filter = new Set(namespaceKeys); for (const [name, entry] of this.grammars) { @@ -374,42 +374,44 @@ export class GrammarStoreImpl implements GrammarStore { const partial = matchGrammarCompletion( entry.grammar, requestPrefix ?? "", + matchedPrefixLength, ); - if (partial.completions.length > 0) { - completions.push(...partial.completions); - } - if (partial.matchedPrefixLength !== undefined) { - matchedPrefixLength = - matchedPrefixLength === undefined - ? partial.matchedPrefixLength - : Math.max( - matchedPrefixLength, - partial.matchedPrefixLength, - ); - } - if (partial.separatorMode !== undefined) { - separatorMode = mergeGrammarSeparatorMode( - separatorMode, - partial.separatorMode, - ); + const partialPrefixLength = + partial.matchedPrefixLength ?? 0; + if (partialPrefixLength > matchedPrefixLength) { + // Longer prefix — discard shorter-match results. + matchedPrefixLength = partialPrefixLength; + completions.length = 0; + properties.length = 0; + separatorMode = undefined; } - if ( - partial.properties !== undefined && - partial.properties.length > 0 - ) { - const { schemaName } = splitSchemaNamespaceKey(name); - for (const p of partial.properties) { - const action: any = p.match; - properties.push({ - actions: [ - createExecutableAction( - schemaName, - action.actionName, - action.parameters, - ), - ], - names: p.propertyNames, - }); + if (partialPrefixLength === matchedPrefixLength) { + completions.push(...partial.completions); + if (partial.separatorMode !== undefined) { + separatorMode = mergeGrammarSeparatorMode( + separatorMode, + partial.separatorMode, + ); + } + if ( + partial.properties !== undefined && + partial.properties.length > 0 + ) { + const { schemaName } = + splitSchemaNamespaceKey(name); + for (const p of partial.properties) { + const action: any = p.match; + properties.push({ + actions: [ + createExecutableAction( + schemaName, + action.actionName, + action.parameters, + ), + ], + names: p.propertyNames, + }); + } } } } From 4f780d3168e29bc52c51ec69e8388365c62ea021 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 17:30:52 -0700 Subject: [PATCH 17/51] Fix case-sensitive uniquelySatisfied check in SearchMenu.updatePrefix --- ts/packages/shell/src/renderer/src/search.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ts/packages/shell/src/renderer/src/search.ts b/ts/packages/shell/src/renderer/src/search.ts index 890a8d5332..600698a263 100644 --- a/ts/packages/shell/src/renderer/src/search.ts +++ b/ts/packages/shell/src/renderer/src/search.ts @@ -66,7 +66,9 @@ export class SearchMenu { this.prefix = prefix; const items = this.trie.dataWithPrefix(normalizeMatchText(prefix)); const uniquelySatisfied = - items.length === 1 && items[0].matchText === prefix; + items.length === 1 && + normalizeMatchText(items[0].matchText) === + normalizeMatchText(prefix); const showMenu = items.length !== 0 && !uniquelySatisfied; if (showMenu) { From 409efeddc2d1d0fde9ed7a75b0321018f5b1e80a Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 17:38:52 -0700 Subject: [PATCH 18/51] lock file --- ts/pnpm-lock.yaml | 1592 ++++++++++++++++++++++----------------------- 1 file changed, 796 insertions(+), 796 deletions(-) diff --git a/ts/pnpm-lock.yaml b/ts/pnpm-lock.yaml index b1792c3943..14b3e95904 100644 --- a/ts/pnpm-lock.yaml +++ b/ts/pnpm-lock.yaml @@ -1410,7 +1410,7 @@ importers: version: 4.22.1 express-rate-limit: specifier: ^7.5.0 - version: 7.5.0(express@4.22.1) + version: 7.5.1(express@4.22.1) graphology: specifier: ^0.25.4 version: 0.25.4(graphology-types@0.24.8) @@ -1437,7 +1437,7 @@ importers: version: 26.1.0 jsonpath: specifier: ^1.2.0 - version: 1.2.1 + version: 1.3.0 knowledge-processor: specifier: workspace:* version: link:../../knowledgeProcessor @@ -2016,7 +2016,7 @@ importers: version: 4.22.1 express-rate-limit: specifier: ^7.5.0 - version: 7.5.0(express@4.22.1) + version: 7.5.1(express@4.22.1) katex: specifier: ^0.16.21 version: 0.16.22 @@ -2143,7 +2143,7 @@ importers: version: 4.22.1 express-rate-limit: specifier: ^7.5.0 - version: 7.5.0(express@4.22.1) + version: 7.5.1(express@4.22.1) image-memory: specifier: workspace:* version: link:../../memory/image @@ -2672,10 +2672,10 @@ importers: dependencies: '@aws-sdk/client-s3': specifier: ^3.726.0 - version: 3.810.0 + version: 3.1004.0 '@aws-sdk/lib-storage': specifier: ^3.726.0 - version: 3.810.0(@aws-sdk/client-s3@3.810.0) + version: 3.1004.0(@aws-sdk/client-s3@3.1004.0) '@azure/identity': specifier: ^4.9.1 version: 4.10.0 @@ -3424,7 +3424,7 @@ importers: version: 4.10.0 '@github/copilot-sdk': specifier: ^0.1.26 - version: 0.1.26 + version: 0.1.32 '@typeagent/action-schema': specifier: workspace:* version: link:../../actionSchema @@ -4742,138 +4742,146 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.810.0': - resolution: {integrity: sha512-wM8M0BrqRkbZ9fGaLmAl24CUgVmmLjiKuNTqGOHsdCIc7RV+IGv5CnGK7ciOltAttrFBxvDlhy2lg5F8gNw8Bg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/client-s3@3.1004.0': + resolution: {integrity: sha512-m0zNfpsona9jQdX1cHtHArOiuvSGZPsgp/KRZS2YjJhKah96G2UN3UNGZQ6aVjXIQjCY6UanCJo0uW9Xf2U41w==} + engines: {node: '>=20.0.0'} - '@aws-sdk/client-sso@3.810.0': - resolution: {integrity: sha512-Txp/3jHqkfA4BTklQEOGiZ1yTUxg+hITislfaWEzJ904vlDt4DvAljTlhfaz7pceCLA2+LhRlYZYSv7t5b0Ltw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/core@3.973.18': + resolution: {integrity: sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.810.0': - resolution: {integrity: sha512-s2IJk+qa/15YZcv3pbdQNATDR+YdYnHf94MrAeVAWubtRLnzD8JciC+gh4LSPp7JzrWSvVOg2Ut1S+0y89xqCg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/crc64-nvme@3.972.4': + resolution: {integrity: sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.810.0': - resolution: {integrity: sha512-iwHqF+KryKONfbdFk3iKhhPk4fHxh5QP5fXXR//jhYwmszaLOwc7CLCE9AxhgiMzAs+kV8nBFQZvdjFpPzVGOA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-env@3.972.16': + resolution: {integrity: sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.810.0': - resolution: {integrity: sha512-SKzjLd+8ugif7yy9sOAAdnPE1vCBHQe6jKgs2AadMpCmWm34DiHz/KuulHdvURUGMIi7CvmaC8aH77twDPYbtg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-http@3.972.18': + resolution: {integrity: sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.810.0': - resolution: {integrity: sha512-H2QCSnxWJ/mj8HTcyHmCmyQ5bO/+imRi4mlBIpUyKjiYKro52WD3gXlGgPIDo2q3UFIHq37kmYvS00i+qIY9tw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-ini@3.972.17': + resolution: {integrity: sha512-dFqh7nfX43B8dO1aPQHOcjC0SnCJ83H3F+1LoCh3X1P7E7N09I+0/taID0asU6GCddfDExqnEvQtDdkuMe5tKQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.810.0': - resolution: {integrity: sha512-9E3Chv3x+RBM3N1bwLCyvXxoiPAckCI74wG7ePN4F3b/7ieIkbEl/3Hd67j1fnt62Xa1cjUHRu2tz5pdEv5G1Q==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-login@3.972.17': + resolution: {integrity: sha512-gf2E5b7LpKb+JX2oQsRIDxdRZjBFZt2olCGlWCdb3vBERbXIPgm2t1R5mEnwd4j0UEO/Tbg5zN2KJbHXttJqwA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.810.0': - resolution: {integrity: sha512-42kE6MLdsmMGp1id3Gisal4MbMiF7PIc0tAznTeIuE8r7cIF8yeQWw/PBOIvjyI57DxbyKzLUAMEJuigUpApCw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-node@3.972.18': + resolution: {integrity: sha512-ZDJa2gd1xiPg/nBDGhUlat02O8obaDEnICBAVS8qieZ0+nDfaB0Z3ec6gjZj27OqFTjnB/Q5a0GwQwb7rMVViw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.810.0': - resolution: {integrity: sha512-8WjX6tz+FCvM93Y33gsr13p/HiiTJmVn5AK1O8PTkvHBclQDzmtAW5FdPqTpAJGswLW2FB0xRqdsSMN2dQEjNw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-process@3.972.16': + resolution: {integrity: sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.810.0': - resolution: {integrity: sha512-uKQJY0AcPyrvMmfGLo36semgjqJ4vmLTqOSW9u40qQDspRnG73/P09lAO2ntqKlhwvMBt3XfcNnOpyyhKRcOfA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-sso@3.972.17': + resolution: {integrity: sha512-wGtte+48xnhnhHMl/MsxzacBPs5A+7JJedjiP452IkHY7vsbYKcvQBqFye8LwdTJVeHtBHv+JFeTscnwepoWGg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/lib-storage@3.810.0': - resolution: {integrity: sha512-Ri3Kgkk9XK7wmgKaQUA4ZNgr4lTASFlt2pd4xo7sbpX7K984fYkWe7ecrgbRptIiIYUjeJiYCRb0FS34diSaZw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.17': + resolution: {integrity: sha512-8aiVJh6fTdl8gcyL+sVNcNwTtWpmoFa1Sh7xlj6Z7L/cZ/tYMEBHq44wTYG8Kt0z/PpGNopD89nbj3FHl9QmTA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/lib-storage@3.1004.0': + resolution: {integrity: sha512-4W6UkeLVd/1FyXFvD9PHMw5FSOY7tsf6+I52jmgdZwDZ9gJcJBx6wF9IhaVp1AXhScZGY9HqHiqYt0qlrSHrGw==} + engines: {node: '>=20.0.0'} peerDependencies: - '@aws-sdk/client-s3': ^3.810.0 + '@aws-sdk/client-s3': ^3.1004.0 - '@aws-sdk/middleware-bucket-endpoint@3.808.0': - resolution: {integrity: sha512-wEPlNcs8dir9lXbuviEGtSzYSxG/NRKQrJk5ybOc7OpPGHovsN+QhDOdY3lcjOFdwMTiMIG9foUkPz3zBpLB1A==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.972.7': + resolution: {integrity: sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-expect-continue@3.804.0': - resolution: {integrity: sha512-YW1hySBolALMII6C8y7Z0CRG2UX1dGJjLEBNFeefhO/xP7ZuE1dvnmfJGaEuBMnvc3wkRS63VZ3aqX6sevM1CA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-expect-continue@3.972.7': + resolution: {integrity: sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.810.0': - resolution: {integrity: sha512-lF5fse+26hluElOtDZMsi5EH50G13OEqglFgpSc6xWnqNhbDc+CnPQRMwTVlOJBDR1/YVbJ15LOKf4pkat46Eg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.973.4': + resolution: {integrity: sha512-7CH2jcGmkvkHc5Buz9IGbdjq1729AAlgYJiAvGq7qhCHqYleCsriWdSnmsqWTwdAfXHMT+pkxX3w6v5tJNcSug==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.804.0': - resolution: {integrity: sha512-bum1hLVBrn2lJCi423Z2fMUYtsbkGI2s4N+2RI2WSjvbaVyMSv/WcejIrjkqiiMR+2Y7m5exgoKeg4/TODLDPQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.972.7': + resolution: {integrity: sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-location-constraint@3.804.0': - resolution: {integrity: sha512-AMtKnllIWKgoo7hiJfphLYotEwTERfjVMO2+cKAncz9w1g+bnYhHxiVhJJoR94y047c06X4PU5MsTxvdQ73Znw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-location-constraint@3.972.7': + resolution: {integrity: sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.804.0': - resolution: {integrity: sha512-w/qLwL3iq0KOPQNat0Kb7sKndl9BtceigINwBU7SpkYWX9L/Lem6f8NPEKrC9Tl4wDBht3Yztub4oRTy/horJA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.972.7': + resolution: {integrity: sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.804.0': - resolution: {integrity: sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.7': + resolution: {integrity: sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.810.0': - resolution: {integrity: sha512-CmQHPVopJYDiGQOmqn5AcmhksQ9qNETF0VgU251Q4tsP9s3R9nBR1r2bZwLt5+dCtf9UCa7cBw4jXKHtH38JTg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-sdk-s3@3.972.18': + resolution: {integrity: sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-ssec@3.804.0': - resolution: {integrity: sha512-Tk8jK0gOIUBvEPTz/wwSlP1V70zVQ3QYqsLPAjQRMO6zfOK9ax31dln3MgKvFDJxBydS2tS3wsn53v+brxDxTA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-ssec@3.972.7': + resolution: {integrity: sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.810.0': - resolution: {integrity: sha512-gLMJcqgIq7k9skX8u0Yyi+jil4elbsmLf3TuDuqNdlqiZ44/AKdDFfU3mU5tRUtMfP42a3gvb2U3elP0BIeybQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-user-agent@3.972.19': + resolution: {integrity: sha512-Km90fcXt3W/iqujHzuM6IaDkYCj73gsYufcuWXApWdzoTy6KGk8fnchAjePMARU0xegIR3K4N3yIo1vy7OVe8A==} + engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.810.0': - resolution: {integrity: sha512-w+tGXFSQjzvJ3j2sQ4GJRdD+YXLTgwLd9eG/A+7pjrv2yLLV70M4HqRrFqH06JBjqT5rsOxonc/QSjROyxk+IA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/nested-clients@3.996.7': + resolution: {integrity: sha512-MlGWA8uPaOs5AiTZ5JLM4uuWDm9EEAnm9cqwvqQIc6kEgel/8s1BaOWm9QgUcfc9K8qd7KkC3n43yDbeXOA2tg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.808.0': - resolution: {integrity: sha512-9x2QWfphkARZY5OGkl9dJxZlSlYM2l5inFeo2bKntGuwg4A4YUe5h7d5yJ6sZbam9h43eBrkOdumx03DAkQF9A==} - engines: {node: '>=18.0.0'} + '@aws-sdk/region-config-resolver@3.972.7': + resolution: {integrity: sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.810.0': - resolution: {integrity: sha512-6F6evHRrA0OG/H67YuZBcI7EH4A0O5dIhczo2N0AK9z495uSIv+0xUrSrDhFTZxZjo6gkADIXroRjP1kwqK6ew==} - engines: {node: '>=18.0.0'} + '@aws-sdk/signature-v4-multi-region@3.996.6': + resolution: {integrity: sha512-NnsOQsVmJXy4+IdPFUjRCWPn9qNH1TzS/f7MiWgXeoHs903tJpAWQWQtoFvLccyPoBgomKP9L89RRr2YsT/L0g==} + engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.810.0': - resolution: {integrity: sha512-fdgHRCDpnzsD+0km7zuRbHRysJECfS8o9T9/pZ6XAr1z2FNV/UveHtnUYq0j6XpDMrIm0/suvXbshIjQU+a+sw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/token-providers@3.1004.0': + resolution: {integrity: sha512-j9BwZZId9sFp+4GPhf6KrwO8Tben2sXibZA8D1vv2I1zBdvkUHcBA2g4pkqIpTRalMTLC0NPkBPX0gERxfy/iA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.804.0': - resolution: {integrity: sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.973.5': + resolution: {integrity: sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-arn-parser@3.804.0': - resolution: {integrity: sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-arn-parser@3.972.3': + resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.808.0': - resolution: {integrity: sha512-N6Lic98uc4ADB7fLWlzx+1uVnq04VgVjngZvwHoujcRg9YDhIg9dUDiTzD5VZv13g1BrPYmvYP1HhsildpGV6w==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-endpoints@3.996.4': + resolution: {integrity: sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.723.0': - resolution: {integrity: sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.804.0': - resolution: {integrity: sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==} + '@aws-sdk/util-user-agent-browser@3.972.7': + resolution: {integrity: sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==} - '@aws-sdk/util-user-agent-node@3.810.0': - resolution: {integrity: sha512-T56/ANEGNuvhqVoWZdr+0ZY2hjV93cH2OfGHIlVTVSAMACWG54XehDPESEso1CJNhJGYZPsE+FE42HGCk/XDMg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-user-agent-node@3.973.4': + resolution: {integrity: sha512-uqKeLqZ9D3nQjH7HGIERNXK9qnSpUK08l4MlJ5/NZqSSdeJsVANYp437EM9sEzwU28c2xfj2V6qlkqzsgtKs6Q==} + engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' peerDependenciesMeta: aws-crt: optional: true - '@aws-sdk/xml-builder@3.804.0': - resolution: {integrity: sha512-JbGWp36IG9dgxtvC6+YXwt5WDZYfuamWFtVfK6fQpnmL96dx+GUPOXPKRWdw67WLKf2comHY28iX2d3z35I53Q==} + '@aws-sdk/xml-builder@3.972.10': + resolution: {integrity: sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} engines: {node: '>=18.0.0'} '@azu/format-text@1.0.2': @@ -5795,48 +5803,48 @@ packages: resolution: {integrity: sha512-Lm/ZLhDZcBECta3TmCQSngiQykFdfw+QtI1/GYMsZd4l3nG+P8WLB16XuS7WaBGLQ+9E+cOcWQsth9cayuGt8g==} engines: {node: ^20.17.0 || >=22.9.0} - '@github/copilot-darwin-arm64@0.0.414': - resolution: {integrity: sha512-PW4v89v41i4Mg/NYl4+gEhwnDaVz+olNL+RbqtiQI3IV89gZdS+RZQbUEJfOwMaFcT2GfiUK1OuB+YDv5GrkBg==} + '@github/copilot-darwin-arm64@1.0.2': + resolution: {integrity: sha512-dYoeaTidsphRXyMjvAgpjEbBV41ipICnXURrLFEiATcjC4IY6x2BqPOocrExBYW/Tz2VZvDw51iIZaf6GXrTmw==} cpu: [arm64] os: [darwin] hasBin: true - '@github/copilot-darwin-x64@0.0.414': - resolution: {integrity: sha512-NyPYm0NovQTwtuI42WJIi4cjYd2z0wBHEvWlUSczRsSuYEyImAClmZmBPegUU63e5JdZd1PdQkQ7FqrrfL2fZQ==} + '@github/copilot-darwin-x64@1.0.2': + resolution: {integrity: sha512-8+Z9dYigEfXf0wHl9c2tgFn8Cr6v4RAY8xTgHMI9mZInjQyxVeBXCxbE2VgzUtDUD3a705Ka2d8ZOz05aYtGsg==} cpu: [x64] os: [darwin] hasBin: true - '@github/copilot-linux-arm64@0.0.414': - resolution: {integrity: sha512-VgdRsvA1FiZ1lcU/AscSvyJWEUWZzoXv2tSZ6WV3NE0uUTuO1Qoq4xuqbKZ/+vKJmn1b8afe7sxAAOtCoWPBHQ==} + '@github/copilot-linux-arm64@1.0.2': + resolution: {integrity: sha512-ik0Y5aTXOFRPLFrNjZJdtfzkozYqYeJjVXGBAH3Pp1nFZRu/pxJnrnQ1HrqO/LEgQVbJzAjQmWEfMbXdQIxE4Q==} cpu: [arm64] os: [linux] hasBin: true - '@github/copilot-linux-x64@0.0.414': - resolution: {integrity: sha512-3HyZsbZqYTF5jcT7/e+nDIYBCQXo8UCVWjBI3raOE4lzAw9b2ucL290IhtA23s1+EiquMxJ4m3FnjwFmwlQ12A==} + '@github/copilot-linux-x64@1.0.2': + resolution: {integrity: sha512-mHSPZjH4nU9rwbfwLxYJ7CQ90jK/Qu1v2CmvBCUPfmuGdVwrpGPHB5FrB+f+b0NEXjmemDWstk2zG53F7ppHfw==} cpu: [x64] os: [linux] hasBin: true - '@github/copilot-sdk@0.1.26': - resolution: {integrity: sha512-5YsApwYa/k2VqaNGvB+ngvWIRxyBR+AY17wCX3ceo+0UcwR7RbW0Ld8l8c5wFOh8Qa/UN4/kXb10HRUTYelGUA==} + '@github/copilot-sdk@0.1.32': + resolution: {integrity: sha512-mPWM0fw1Gqc/SW8nl45K8abrFH+92fO7y6tRtRl5imjS5hGapLf/dkX5WDrgPtlsflD0c41lFXVUri5NVJwtoA==} engines: {node: '>=20.0.0'} - '@github/copilot-win32-arm64@0.0.414': - resolution: {integrity: sha512-8gdaoF4MPpeV0h8UnCZ8TKI5l274EP0fvAaV9BGjsdyEydDcEb+DHqQiXgitWVKKiHAAaPi12aH8P5OsEDUneQ==} + '@github/copilot-win32-arm64@1.0.2': + resolution: {integrity: sha512-tLW2CY/vg0fYLp8EuiFhWIHBVzbFCDDpohxT/F/XyMAdTVSZLnopCcxQHv2BOu0CVGrYjlf7YOIwPfAKYml1FA==} cpu: [arm64] os: [win32] hasBin: true - '@github/copilot-win32-x64@0.0.414': - resolution: {integrity: sha512-E1Oq1jXHaL1oWNsmmiCd4G30/CfgVdswg/T5oDFUxx3Ri+6uBekciIzdyCDelsP1kn2+fC1EYz2AerQ6F+huzg==} + '@github/copilot-win32-x64@1.0.2': + resolution: {integrity: sha512-cFlc3xMkKKFRIYR00EEJ2XlYAemeh5EZHsGA8Ir2G0AH+DOevJbomdP1yyCC5gaK/7IyPkHX3sGie5sER2yPvQ==} cpu: [x64] os: [win32] hasBin: true - '@github/copilot@0.0.414': - resolution: {integrity: sha512-jseJ2S02CLWrFks5QK22zzq7as2ErY5m1wMCFBOE6sro1uACq1kvqqM1LwM4qy58YSZFrM1ZAn1s7UOVd9zhIA==} + '@github/copilot@1.0.2': + resolution: {integrity: sha512-716SIZMYftldVcJay2uZOzsa9ROGGb2Mh2HnxbDxoisFsWNNgZlQXlV7A+PYoGsnAo2Zk/8e1i5SPTscGf2oww==} hasBin: true '@grpc/grpc-js@1.13.4': @@ -5848,8 +5856,8 @@ packages: engines: {node: '>=6'} hasBin: true - '@hono/node-server@1.19.9': - resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} + '@hono/node-server@1.19.11': + resolution: {integrity: sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -6977,216 +6985,220 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@smithy/abort-controller@4.0.2': - resolution: {integrity: sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==} + '@smithy/abort-controller@4.2.11': + resolution: {integrity: sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader-native@4.0.0': - resolution: {integrity: sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==} + '@smithy/chunked-blob-reader-native@4.2.3': + resolution: {integrity: sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@5.0.0': - resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==} + '@smithy/chunked-blob-reader@5.2.2': + resolution: {integrity: sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.1.2': - resolution: {integrity: sha512-7r6mZGwb5LmLJ+zPtkLoznf2EtwEuSWdtid10pjGl/7HefCE4mueOkrfki8JCUm99W6UfP47/r3tbxx9CfBN5A==} + '@smithy/config-resolver@4.4.10': + resolution: {integrity: sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.3.3': - resolution: {integrity: sha512-CiJNc0b/WdnttAfQ6uMkxPQ3Z8hG/ba8wF89x9KtBBLDdZk6CX52K4F8hbe94uNbc8LDUuZFtbqfdhM3T21naw==} + '@smithy/core@3.23.9': + resolution: {integrity: sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.0.4': - resolution: {integrity: sha512-jN6M6zaGVyB8FmNGG+xOPQB4N89M1x97MMdMnm1ESjljLS3Qju/IegQizKujaNcy2vXAvrz0en8bobe6E55FEA==} + '@smithy/credential-provider-imds@4.2.11': + resolution: {integrity: sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@4.0.2': - resolution: {integrity: sha512-p+f2kLSK7ZrXVfskU/f5dzksKTewZk8pJLPvER3aFHPt76C2MxD9vNatSfLzzQSQB4FNO96RK4PSXfhD1TTeMQ==} + '@smithy/eventstream-codec@4.2.11': + resolution: {integrity: sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@4.0.2': - resolution: {integrity: sha512-CepZCDs2xgVUtH7ZZ7oDdZFH8e6Y2zOv8iiX6RhndH69nlojCALSKK+OXwZUgOtUZEUaZ5e1hULVCHYbCn7pug==} + '@smithy/eventstream-serde-browser@4.2.11': + resolution: {integrity: sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@4.1.0': - resolution: {integrity: sha512-1PI+WPZ5TWXrfj3CIoKyUycYynYJgZjuQo8U+sphneOtjsgrttYybdqESFReQrdWJ+LKt6NEdbYzmmfDBmjX2A==} + '@smithy/eventstream-serde-config-resolver@4.3.11': + resolution: {integrity: sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@4.0.2': - resolution: {integrity: sha512-C5bJ/C6x9ENPMx2cFOirspnF9ZsBVnBMtP6BdPl/qYSuUawdGQ34Lq0dMcf42QTjUZgWGbUIZnz6+zLxJlb9aw==} + '@smithy/eventstream-serde-node@4.2.11': + resolution: {integrity: sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@4.0.2': - resolution: {integrity: sha512-St8h9JqzvnbB52FtckiHPN4U/cnXcarMniXRXTKn0r4b4XesZOGiAyUdj1aXbqqn1icSqBlzzUsCl6nPB018ng==} + '@smithy/eventstream-serde-universal@4.2.11': + resolution: {integrity: sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.0.2': - resolution: {integrity: sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==} + '@smithy/fetch-http-handler@5.3.13': + resolution: {integrity: sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ==} engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@4.0.2': - resolution: {integrity: sha512-3g188Z3DyhtzfBRxpZjU8R9PpOQuYsbNnyStc/ZVS+9nVX1f6XeNOa9IrAh35HwwIZg+XWk8bFVtNINVscBP+g==} + '@smithy/hash-blob-browser@4.2.12': + resolution: {integrity: sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.0.2': - resolution: {integrity: sha512-VnTpYPnRUE7yVhWozFdlxcYknv9UN7CeOqSrMH+V877v4oqtVYuoqhIhtSjmGPvYrYnAkaM61sLMKHvxL138yg==} + '@smithy/hash-node@4.2.11': + resolution: {integrity: sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A==} engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@4.0.2': - resolution: {integrity: sha512-POWDuTznzbIwlEXEvvXoPMS10y0WKXK790soe57tFRfvf4zBHyzE529HpZMqmDdwG9MfFflnyzndUQ8j78ZdSg==} + '@smithy/hash-stream-node@4.2.11': + resolution: {integrity: sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.0.2': - resolution: {integrity: sha512-GatB4+2DTpgWPday+mnUkoumP54u/MDM/5u44KF9hIu8jF0uafZtQLcdfIKkIcUNuF/fBojpLEHZS/56JqPeXQ==} + '@smithy/invalid-dependency@4.2.11': + resolution: {integrity: sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.0.0': - resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + '@smithy/is-array-buffer@4.2.2': + resolution: {integrity: sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.0.2': - resolution: {integrity: sha512-Hc0R8EiuVunUewCse2syVgA2AfSRco3LyAv07B/zCOMa+jpXI9ll+Q21Nc6FAlYPcpNcAXqBzMhNs1CD/pP2bA==} + '@smithy/md5-js@4.2.11': + resolution: {integrity: sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.0.2': - resolution: {integrity: sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==} + '@smithy/middleware-content-length@4.2.11': + resolution: {integrity: sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.1.6': - resolution: {integrity: sha512-Zdieg07c3ua3ap5ungdcyNnY1OsxmsXXtKDTk28+/YbwIPju0Z1ZX9X5AnkjmDE3+AbqgvhtC/ZuCMSr6VSfPw==} + '@smithy/middleware-endpoint@4.4.23': + resolution: {integrity: sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.1.7': - resolution: {integrity: sha512-lFIFUJ0E/4I0UaIDY5usNUzNKAghhxO0lDH4TZktXMmE+e4ActD9F154Si0Unc01aCPzcwd+NcOwQw6AfXXRRQ==} + '@smithy/middleware-retry@4.4.40': + resolution: {integrity: sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.0.5': - resolution: {integrity: sha512-yREC3q/HXqQigq29xX3hiy6tFi+kjPKXoYUQmwQdgPORLbQ0n6V2Z/Iw9Nnlu66da9fM/WhDtGvYvqwecrCljQ==} + '@smithy/middleware-serde@4.2.12': + resolution: {integrity: sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.0.2': - resolution: {integrity: sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==} + '@smithy/middleware-stack@4.2.11': + resolution: {integrity: sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.1.1': - resolution: {integrity: sha512-1slS5jf5icHETwl5hxEVBj+mh6B+LbVW4yRINsGtUKH+nxM5Pw2H59+qf+JqYFCHp9jssG4vX81f5WKnjMN3Vw==} + '@smithy/node-config-provider@4.3.11': + resolution: {integrity: sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.0.4': - resolution: {integrity: sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==} + '@smithy/node-http-handler@4.4.14': + resolution: {integrity: sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.0.2': - resolution: {integrity: sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==} + '@smithy/property-provider@4.2.11': + resolution: {integrity: sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.1.0': - resolution: {integrity: sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==} + '@smithy/protocol-http@5.3.11': + resolution: {integrity: sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.0.2': - resolution: {integrity: sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==} + '@smithy/querystring-builder@4.2.11': + resolution: {integrity: sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.0.2': - resolution: {integrity: sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==} + '@smithy/querystring-parser@4.2.11': + resolution: {integrity: sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.0.3': - resolution: {integrity: sha512-FTbcajmltovWMjj3tksDQdD23b2w6gH+A0DYA1Yz3iSpjDj8fmkwy62UnXcWMy4d5YoMoSyLFHMfkEVEzbiN8Q==} + '@smithy/service-error-classification@4.2.11': + resolution: {integrity: sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.0.2': - resolution: {integrity: sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==} + '@smithy/shared-ini-file-loader@4.4.6': + resolution: {integrity: sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.1.0': - resolution: {integrity: sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==} + '@smithy/signature-v4@5.3.11': + resolution: {integrity: sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.2.6': - resolution: {integrity: sha512-WEqP0wQ1N/lVS4pwNK1Vk+0i6QIr66cq/xbu1dVy1tM0A0qYwAYyz0JhbquzM5pMa8s89lyDBtoGKxo7iG74GA==} + '@smithy/smithy-client@4.12.3': + resolution: {integrity: sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw==} engines: {node: '>=18.0.0'} - '@smithy/types@4.2.0': - resolution: {integrity: sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==} + '@smithy/types@4.13.0': + resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.0.2': - resolution: {integrity: sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==} + '@smithy/url-parser@4.2.11': + resolution: {integrity: sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.0.0': - resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + '@smithy/util-base64@4.3.2': + resolution: {integrity: sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@4.0.0': - resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + '@smithy/util-body-length-browser@4.2.2': + resolution: {integrity: sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@4.0.0': - resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + '@smithy/util-body-length-node@4.2.3': + resolution: {integrity: sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.0.0': - resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + '@smithy/util-buffer-from@4.2.2': + resolution: {integrity: sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==} engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@4.0.0': - resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + '@smithy/util-config-provider@4.2.2': + resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.0.14': - resolution: {integrity: sha512-l7QnMX8VcDOH6n/fBRu4zqguSlOBZxFzWqp58dXFSARFBjNlmEDk5G/z4T7BMGr+rI0Pg8MkhmMUfEtHFgpy2g==} + '@smithy/util-defaults-mode-browser@4.3.39': + resolution: {integrity: sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.0.14': - resolution: {integrity: sha512-Ujs1gsWDo3m/T63VWBTBmHLTD2UlU6J6FEokLCEp7OZQv45jcjLHoxTwgWsi8ULpsYozvH4MTWkRP+bhwr0vDg==} + '@smithy/util-defaults-mode-node@4.2.42': + resolution: {integrity: sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.0.4': - resolution: {integrity: sha512-VfFATC1bmZLV2858B/O1NpMcL32wYo8DPPhHxYxDCodDl3f3mSZ5oJheW1IF91A0EeAADz2WsakM/hGGPGNKLg==} + '@smithy/util-endpoints@3.3.2': + resolution: {integrity: sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA==} engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@4.0.0': - resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + '@smithy/util-hex-encoding@4.2.2': + resolution: {integrity: sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.0.2': - resolution: {integrity: sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==} + '@smithy/util-middleware@4.2.11': + resolution: {integrity: sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.0.3': - resolution: {integrity: sha512-DPuYjZQDXmKr/sNvy9Spu8R/ESa2e22wXZzSAY6NkjOLj6spbIje/Aq8rT97iUMdDj0qHMRIe+bTxvlU74d9Ng==} + '@smithy/util-retry@4.2.11': + resolution: {integrity: sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.2.0': - resolution: {integrity: sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==} + '@smithy/util-stream@4.5.17': + resolution: {integrity: sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ==} engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@4.0.0': - resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + '@smithy/util-uri-escape@4.2.2': + resolution: {integrity: sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==} engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.0.0': - resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + '@smithy/util-utf8@4.2.2': + resolution: {integrity: sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.2.11': + resolution: {integrity: sha512-x7Rh2azQPs3XxbvCzcttRErKKvLnbZfqRf/gOjw2pb+ZscX88e5UkRPCB67bVnsFHxayvMvmePfKTqsRb+is1A==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@4.0.3': - resolution: {integrity: sha512-JtaY3FxmD+te+KSI2FJuEcfNC9T/DGGVf551babM7fAaXhjJUt7oSYurH1Devxd2+BOSUACCgt3buinx4UnmEA==} + '@smithy/uuid@1.1.2': + resolution: {integrity: sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==} engines: {node: '>=18.0.0'} '@swc/helpers@0.5.15': @@ -7593,8 +7605,8 @@ packages: '@types/proper-lockfile@4.1.4': resolution: {integrity: sha512-uo2ABllncSqg9F1D4nugVl9v93RmjxF6LJzQLMLDdPaXCUIDPeOJ21Gbqi43xNKzBi/WQ0Q0dICqufzQbMjipQ==} - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + '@types/qs@6.15.0': + resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} @@ -9260,12 +9272,12 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + diff@5.2.2: + resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} dir-compare@4.2.0: @@ -9629,14 +9641,14 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} engines: {node: '>= 16'} peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 + express: '>= 4.11' - express-rate-limit@8.2.1: - resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} + express-rate-limit@8.3.1: + resolution: {integrity: sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -9687,9 +9699,8 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-parser@4.4.1: - resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} - hasBin: true + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} fast-xml-parser@4.5.4: resolution: {integrity: sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==} @@ -9699,6 +9710,10 @@ packages: resolution: {integrity: sha512-53jIF4N6u/pxvaL1eb/hEZts/cFLWZ92eCfLrNyCI0k38lettCG/Bs40W9pPwoPXyHQlKu2OUbQtiEIZK/J6Vw==} hasBin: true + fast-xml-parser@5.4.1: + resolution: {integrity: sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==} + hasBin: true + fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} @@ -10162,8 +10177,8 @@ packages: highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - hono@4.12.3: - resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} + hono@4.12.5: + resolution: {integrity: sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg==} engines: {node: '>=16.9.0'} hosted-git-info@4.1.0: @@ -10307,10 +10322,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} - engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} @@ -10415,10 +10426,6 @@ packages: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} engines: {node: '>=10.13.0'} - ip-address@10.0.1: - resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} - engines: {node: '>= 12'} - ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} @@ -10983,8 +10990,8 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - jsonpath@1.2.1: - resolution: {integrity: sha512-Jl6Jhk0jG+kP3yk59SSeGq7LFPR4JQz1DU0K+kXTysUhMostbhU3qh5mjTuf0PqFcXpAT7kvmMt9WxV10NyIgQ==} + jsonpath@1.3.0: + resolution: {integrity: sha512-0kjkYHJBkAy50Z5QzArZ7udmvxrJzkpKYW27fiF//BrMY7TQibYLl+FYIXN2BiYmwMIVzSfD8aDRj6IzgBX2/w==} jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} @@ -12288,6 +12295,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prettier@3.5.3: @@ -13334,10 +13342,9 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - tar@7.5.7: - resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==} + tar@7.5.11: + resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==} engines: {node: '>=18'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me temp-file@3.4.0: resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} @@ -13677,6 +13684,9 @@ packages: underscore@1.13.6: resolution: {integrity: sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==} + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -14451,21 +14461,21 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.804.0 + '@aws-sdk/types': 3.973.5 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.804.0 + '@aws-sdk/types': 3.973.5 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-locate-window': 3.723.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -14474,15 +14484,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-locate-window': 3.723.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.804.0 + '@aws-sdk/types': 3.973.5 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -14491,432 +14501,417 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.804.0 + '@aws-sdk/types': 3.973.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.810.0': + '@aws-sdk/client-s3@3.1004.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.810.0 - '@aws-sdk/credential-provider-node': 3.810.0 - '@aws-sdk/middleware-bucket-endpoint': 3.808.0 - '@aws-sdk/middleware-expect-continue': 3.804.0 - '@aws-sdk/middleware-flexible-checksums': 3.810.0 - '@aws-sdk/middleware-host-header': 3.804.0 - '@aws-sdk/middleware-location-constraint': 3.804.0 - '@aws-sdk/middleware-logger': 3.804.0 - '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-sdk-s3': 3.810.0 - '@aws-sdk/middleware-ssec': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.810.0 - '@aws-sdk/region-config-resolver': 3.808.0 - '@aws-sdk/signature-v4-multi-region': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-endpoints': 3.808.0 - '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.810.0 - '@aws-sdk/xml-builder': 3.804.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/eventstream-serde-browser': 4.0.2 - '@smithy/eventstream-serde-config-resolver': 4.1.0 - '@smithy/eventstream-serde-node': 4.0.2 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-blob-browser': 4.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/hash-stream-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/md5-js': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 - '@smithy/util-stream': 4.2.0 - '@smithy/util-utf8': 4.0.0 - '@smithy/util-waiter': 4.0.3 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-node': 3.972.18 + '@aws-sdk/middleware-bucket-endpoint': 3.972.7 + '@aws-sdk/middleware-expect-continue': 3.972.7 + '@aws-sdk/middleware-flexible-checksums': 3.973.4 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-location-constraint': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-sdk-s3': 3.972.18 + '@aws-sdk/middleware-ssec': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/signature-v4-multi-region': 3.996.6 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.4 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.9 + '@smithy/eventstream-serde-browser': 4.2.11 + '@smithy/eventstream-serde-config-resolver': 4.3.11 + '@smithy/eventstream-serde-node': 4.2.11 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-blob-browser': 4.2.12 + '@smithy/hash-node': 4.2.11 + '@smithy/hash-stream-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/md5-js': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-retry': 4.4.40 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.39 + '@smithy/util-defaults-mode-node': 4.2.42 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/util-waiter': 4.2.11 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.810.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.810.0 - '@aws-sdk/middleware-host-header': 3.804.0 - '@aws-sdk/middleware-logger': 3.804.0 - '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.810.0 - '@aws-sdk/region-config-resolver': 3.808.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-endpoints': 3.808.0 - '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.810.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 - '@smithy/util-utf8': 4.0.0 + '@aws-sdk/core@3.973.18': + dependencies: + '@aws-sdk/types': 3.973.5 + '@aws-sdk/xml-builder': 3.972.10 + '@smithy/core': 3.23.9 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/core@3.810.0': - dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/core': 3.3.3 - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/signature-v4': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-middleware': 4.0.2 - fast-xml-parser: 4.4.1 + '@aws-sdk/crc64-nvme@3.972.4': + dependencies: + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.810.0': + '@aws-sdk/credential-provider-env@3.972.16': dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.810.0': - dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/node-http-handler': 4.0.4 - '@smithy/property-provider': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-stream': 4.2.0 + '@aws-sdk/credential-provider-http@3.972.18': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.810.0': - dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/credential-provider-env': 3.810.0 - '@aws-sdk/credential-provider-http': 3.810.0 - '@aws-sdk/credential-provider-process': 3.810.0 - '@aws-sdk/credential-provider-sso': 3.810.0 - '@aws-sdk/credential-provider-web-identity': 3.810.0 - '@aws-sdk/nested-clients': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/credential-provider-ini@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/credential-provider-env': 3.972.16 + '@aws-sdk/credential-provider-http': 3.972.18 + '@aws-sdk/credential-provider-login': 3.972.17 + '@aws-sdk/credential-provider-process': 3.972.16 + '@aws-sdk/credential-provider-sso': 3.972.17 + '@aws-sdk/credential-provider-web-identity': 3.972.17 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.810.0': - dependencies: - '@aws-sdk/credential-provider-env': 3.810.0 - '@aws-sdk/credential-provider-http': 3.810.0 - '@aws-sdk/credential-provider-ini': 3.810.0 - '@aws-sdk/credential-provider-process': 3.810.0 - '@aws-sdk/credential-provider-sso': 3.810.0 - '@aws-sdk/credential-provider-web-identity': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/credential-provider-login@3.972.17': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.810.0': + '@aws-sdk/credential-provider-node@3.972.18': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.16 + '@aws-sdk/credential-provider-http': 3.972.18 + '@aws-sdk/credential-provider-ini': 3.972.17 + '@aws-sdk/credential-provider-process': 3.972.16 + '@aws-sdk/credential-provider-sso': 3.972.17 + '@aws-sdk/credential-provider-web-identity': 3.972.17 + '@aws-sdk/types': 3.973.5 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.972.16': dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.810.0': + '@aws-sdk/credential-provider-sso@3.972.17': dependencies: - '@aws-sdk/client-sso': 3.810.0 - '@aws-sdk/core': 3.810.0 - '@aws-sdk/token-providers': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/token-providers': 3.1004.0 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.810.0': + '@aws-sdk/credential-provider-web-identity@3.972.17': dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/nested-clients': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/lib-storage@3.810.0(@aws-sdk/client-s3@3.810.0)': + '@aws-sdk/lib-storage@3.1004.0(@aws-sdk/client-s3@3.1004.0)': dependencies: - '@aws-sdk/client-s3': 3.810.0 - '@smithy/abort-controller': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/smithy-client': 4.2.6 + '@aws-sdk/client-s3': 3.1004.0 + '@smithy/abort-controller': 4.2.11 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/smithy-client': 4.12.3 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.808.0': + '@aws-sdk/middleware-bucket-endpoint@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-arn-parser': 3.804.0 - '@smithy/node-config-provider': 4.1.1 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-config-provider': 4.0.0 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.804.0': + '@aws-sdk/middleware-expect-continue@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.810.0': + '@aws-sdk/middleware-flexible-checksums@3.973.4': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/is-array-buffer': 4.0.0 - '@smithy/node-config-provider': 4.1.1 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-stream': 4.2.0 - '@smithy/util-utf8': 4.0.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/crc64-nvme': 3.972.4 + '@aws-sdk/types': 3.973.5 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.804.0': + '@aws-sdk/middleware-host-header@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.804.0': + '@aws-sdk/middleware-location-constraint@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.804.0': + '@aws-sdk/middleware-logger@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.804.0': + '@aws-sdk/middleware-recursion-detection@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.810.0': - dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-arn-parser': 3.804.0 - '@smithy/core': 3.3.3 - '@smithy/node-config-provider': 4.1.1 - '@smithy/protocol-http': 5.1.0 - '@smithy/signature-v4': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-config-provider': 4.0.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-stream': 4.2.0 - '@smithy/util-utf8': 4.0.0 + '@aws-sdk/middleware-sdk-s3@3.972.18': + dependencies: + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-arn-parser': 3.972.3 + '@smithy/core': 3.23.9 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.804.0': + '@aws-sdk/middleware-ssec@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.810.0': + '@aws-sdk/middleware-user-agent@3.972.19': dependencies: - '@aws-sdk/core': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-endpoints': 3.808.0 - '@smithy/core': 3.3.3 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@smithy/core': 3.23.9 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-retry': 4.2.11 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.810.0': + '@aws-sdk/nested-clients@3.996.7': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.810.0 - '@aws-sdk/middleware-host-header': 3.804.0 - '@aws-sdk/middleware-logger': 3.804.0 - '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.810.0 - '@aws-sdk/region-config-resolver': 3.808.0 - '@aws-sdk/types': 3.804.0 - '@aws-sdk/util-endpoints': 3.808.0 - '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.810.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 - '@smithy/util-base64': 4.0.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 - '@smithy/util-utf8': 4.0.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/middleware-host-header': 3.972.7 + '@aws-sdk/middleware-logger': 3.972.7 + '@aws-sdk/middleware-recursion-detection': 3.972.7 + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/region-config-resolver': 3.972.7 + '@aws-sdk/types': 3.973.5 + '@aws-sdk/util-endpoints': 3.996.4 + '@aws-sdk/util-user-agent-browser': 3.972.7 + '@aws-sdk/util-user-agent-node': 3.973.4 + '@smithy/config-resolver': 4.4.10 + '@smithy/core': 3.23.9 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/hash-node': 4.2.11 + '@smithy/invalid-dependency': 4.2.11 + '@smithy/middleware-content-length': 4.2.11 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-retry': 4.4.40 + '@smithy/middleware-serde': 4.2.12 + '@smithy/middleware-stack': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/node-http-handler': 4.4.14 + '@smithy/protocol-http': 5.3.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-body-length-node': 4.2.3 + '@smithy/util-defaults-mode-browser': 4.3.39 + '@smithy/util-defaults-mode-node': 4.2.42 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.808.0': + '@aws-sdk/region-config-resolver@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 - '@smithy/util-config-provider': 4.0.0 - '@smithy/util-middleware': 4.0.2 + '@aws-sdk/types': 3.973.5 + '@smithy/config-resolver': 4.4.10 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.810.0': + '@aws-sdk/signature-v4-multi-region@3.996.6': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/signature-v4': 5.1.0 - '@smithy/types': 4.2.0 + '@aws-sdk/middleware-sdk-s3': 3.972.18 + '@aws-sdk/types': 3.973.5 + '@smithy/protocol-http': 5.3.11 + '@smithy/signature-v4': 5.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.810.0': + '@aws-sdk/token-providers@3.1004.0': dependencies: - '@aws-sdk/nested-clients': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@aws-sdk/core': 3.973.18 + '@aws-sdk/nested-clients': 3.996.7 + '@aws-sdk/types': 3.973.5 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.804.0': + '@aws-sdk/types@3.973.5': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.804.0': + '@aws-sdk/util-arn-parser@3.972.3': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.808.0': + '@aws-sdk/util-endpoints@3.996.4': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 - '@smithy/util-endpoints': 3.0.4 + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-endpoints': 3.3.2 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.723.0': + '@aws-sdk/util-locate-window@3.965.5': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.804.0': + '@aws-sdk/util-user-agent-browser@3.972.7': dependencies: - '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 + '@aws-sdk/types': 3.973.5 + '@smithy/types': 4.13.0 bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.810.0': + '@aws-sdk/util-user-agent-node@3.973.4': dependencies: - '@aws-sdk/middleware-user-agent': 3.810.0 - '@aws-sdk/types': 3.804.0 - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 + '@aws-sdk/middleware-user-agent': 3.972.19 + '@aws-sdk/types': 3.973.5 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.804.0': + '@aws-sdk/xml-builder@3.972.10': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 + fast-xml-parser: 5.4.1 tslib: 2.8.1 + '@aws/lambda-invoke-store@0.2.3': {} + '@azu/format-text@1.0.2': {} '@azu/style-format@1.0.1': @@ -15973,7 +15968,7 @@ snapshots: ora: 5.4.1 read-binary-file-arch: 1.0.6 semver: 7.7.4 - tar: 7.5.7 + tar: 7.5.11 yargs: 17.7.2 transitivePeerDependencies: - supports-color @@ -16230,38 +16225,38 @@ snapshots: dependencies: retry: 0.13.1 - '@github/copilot-darwin-arm64@0.0.414': + '@github/copilot-darwin-arm64@1.0.2': optional: true - '@github/copilot-darwin-x64@0.0.414': + '@github/copilot-darwin-x64@1.0.2': optional: true - '@github/copilot-linux-arm64@0.0.414': + '@github/copilot-linux-arm64@1.0.2': optional: true - '@github/copilot-linux-x64@0.0.414': + '@github/copilot-linux-x64@1.0.2': optional: true - '@github/copilot-sdk@0.1.26': + '@github/copilot-sdk@0.1.32': dependencies: - '@github/copilot': 0.0.414 + '@github/copilot': 1.0.2 vscode-jsonrpc: 8.2.1 zod: 4.3.6 - '@github/copilot-win32-arm64@0.0.414': + '@github/copilot-win32-arm64@1.0.2': optional: true - '@github/copilot-win32-x64@0.0.414': + '@github/copilot-win32-x64@1.0.2': optional: true - '@github/copilot@0.0.414': + '@github/copilot@1.0.2': optionalDependencies: - '@github/copilot-darwin-arm64': 0.0.414 - '@github/copilot-darwin-x64': 0.0.414 - '@github/copilot-linux-arm64': 0.0.414 - '@github/copilot-linux-x64': 0.0.414 - '@github/copilot-win32-arm64': 0.0.414 - '@github/copilot-win32-x64': 0.0.414 + '@github/copilot-darwin-arm64': 1.0.2 + '@github/copilot-darwin-x64': 1.0.2 + '@github/copilot-linux-arm64': 1.0.2 + '@github/copilot-linux-x64': 1.0.2 + '@github/copilot-win32-arm64': 1.0.2 + '@github/copilot-win32-x64': 1.0.2 '@grpc/grpc-js@1.13.4': dependencies: @@ -16275,9 +16270,9 @@ snapshots: protobufjs: 7.4.0 yargs: 17.7.2 - '@hono/node-server@1.19.9(hono@4.12.3)': + '@hono/node-server@1.19.11(hono@4.12.5)': dependencies: - hono: 4.12.3 + hono: 4.12.5 '@iconify/types@2.0.0': {} @@ -16503,7 +16498,7 @@ snapshots: '@isaacs/fs-minipass@4.0.1': dependencies: - minipass: 7.1.2 + minipass: 7.1.3 '@istanbuljs/load-nyc-config@1.1.0': dependencies: @@ -17291,7 +17286,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.9(hono@4.12.3) + '@hono/node-server': 1.19.11(hono@4.12.5) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -17300,8 +17295,8 @@ snapshots: eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.12.3 + express-rate-limit: 8.3.1(express@5.2.1) + hono: 4.12.5 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -17313,7 +17308,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.1.13)': dependencies: - '@hono/node-server': 1.19.9(hono@4.12.3) + '@hono/node-server': 1.19.11(hono@4.12.5) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -17322,8 +17317,8 @@ snapshots: eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.12.3 + express-rate-limit: 8.3.1(express@5.2.1) + hono: 4.12.5 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -17335,7 +17330,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.9(hono@4.12.3) + '@hono/node-server': 1.19.11(hono@4.12.5) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -17344,8 +17339,8 @@ snapshots: eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.12.3 + express-rate-limit: 8.3.1(express@5.2.1) + hono: 4.12.5 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -17358,7 +17353,7 @@ snapshots: '@modelcontextprotocol/server-filesystem@2025.8.21(zod@4.1.13)': dependencies: '@modelcontextprotocol/sdk': 1.26.0(zod@4.1.13) - diff: 5.2.0 + diff: 5.2.2 glob: 10.5.0 minimatch: 10.2.4 zod-to-json-schema: 3.24.5(zod@4.1.13) @@ -17944,251 +17939,254 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.0 - '@smithy/abort-controller@4.0.2': + '@smithy/abort-controller@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/chunked-blob-reader-native@4.0.0': + '@smithy/chunked-blob-reader-native@4.2.3': dependencies: - '@smithy/util-base64': 4.0.0 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/chunked-blob-reader@5.0.0': + '@smithy/chunked-blob-reader@5.2.2': dependencies: tslib: 2.8.1 - '@smithy/config-resolver@4.1.2': + '@smithy/config-resolver@4.4.10': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 - '@smithy/util-config-provider': 4.0.0 - '@smithy/util-middleware': 4.0.2 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.2 + '@smithy/util-endpoints': 3.3.2 + '@smithy/util-middleware': 4.2.11 tslib: 2.8.1 - '@smithy/core@3.3.3': - dependencies: - '@smithy/middleware-serde': 4.0.5 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-stream': 4.2.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/core@3.23.9': + dependencies: + '@smithy/middleware-serde': 4.2.12 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-body-length-browser': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-stream': 4.5.17 + '@smithy/util-utf8': 4.2.2 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.0.4': + '@smithy/credential-provider-imds@4.2.11': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 tslib: 2.8.1 - '@smithy/eventstream-codec@4.0.2': + '@smithy/eventstream-codec@4.2.11': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.2.0 - '@smithy/util-hex-encoding': 4.0.0 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.0.2': + '@smithy/eventstream-serde-browser@4.2.11': dependencies: - '@smithy/eventstream-serde-universal': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.1.0': + '@smithy/eventstream-serde-config-resolver@4.3.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/eventstream-serde-node@4.0.2': + '@smithy/eventstream-serde-node@4.2.11': dependencies: - '@smithy/eventstream-serde-universal': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-serde-universal': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.0.2': + '@smithy/eventstream-serde-universal@4.2.11': dependencies: - '@smithy/eventstream-codec': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-codec': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.0.2': + '@smithy/fetch-http-handler@5.3.13': dependencies: - '@smithy/protocol-http': 5.1.0 - '@smithy/querystring-builder': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/util-base64': 4.0.0 + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 tslib: 2.8.1 - '@smithy/hash-blob-browser@4.0.2': + '@smithy/hash-blob-browser@4.2.12': dependencies: - '@smithy/chunked-blob-reader': 5.0.0 - '@smithy/chunked-blob-reader-native': 4.0.0 - '@smithy/types': 4.2.0 + '@smithy/chunked-blob-reader': 5.2.2 + '@smithy/chunked-blob-reader-native': 4.2.3 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/hash-node@4.0.2': + '@smithy/hash-node@4.2.11': dependencies: - '@smithy/types': 4.2.0 - '@smithy/util-buffer-from': 4.0.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/types': 4.13.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/hash-stream-node@4.0.2': + '@smithy/hash-stream-node@4.2.11': dependencies: - '@smithy/types': 4.2.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/invalid-dependency@4.0.2': + '@smithy/invalid-dependency@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.0.0': + '@smithy/is-array-buffer@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/md5-js@4.0.2': + '@smithy/md5-js@4.2.11': dependencies: - '@smithy/types': 4.2.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/middleware-content-length@4.0.2': + '@smithy/middleware-content-length@4.2.11': dependencies: - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.1.6': + '@smithy/middleware-endpoint@4.4.23': dependencies: - '@smithy/core': 3.3.3 - '@smithy/middleware-serde': 4.0.5 - '@smithy/node-config-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 - '@smithy/util-middleware': 4.0.2 + '@smithy/core': 3.23.9 + '@smithy/middleware-serde': 4.2.12 + '@smithy/node-config-provider': 4.3.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.11 + '@smithy/util-middleware': 4.2.11 tslib: 2.8.1 - '@smithy/middleware-retry@4.1.7': + '@smithy/middleware-retry@4.4.40': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/protocol-http': 5.1.0 - '@smithy/service-error-classification': 4.0.3 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 + '@smithy/node-config-provider': 4.3.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/service-error-classification': 4.2.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-retry': 4.2.11 + '@smithy/uuid': 1.1.2 tslib: 2.8.1 - uuid: 9.0.1 - '@smithy/middleware-serde@4.0.5': + '@smithy/middleware-serde@4.2.12': dependencies: - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/middleware-stack@4.0.2': + '@smithy/middleware-stack@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.1.1': + '@smithy/node-config-provider@4.3.11': dependencies: - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.2.11 + '@smithy/shared-ini-file-loader': 4.4.6 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.0.4': + '@smithy/node-http-handler@4.4.14': dependencies: - '@smithy/abort-controller': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/querystring-builder': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/abort-controller': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/querystring-builder': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/property-provider@4.0.2': + '@smithy/property-provider@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/protocol-http@5.1.0': + '@smithy/protocol-http@5.3.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/querystring-builder@4.0.2': + '@smithy/querystring-builder@4.2.11': dependencies: - '@smithy/types': 4.2.0 - '@smithy/util-uri-escape': 4.0.0 + '@smithy/types': 4.13.0 + '@smithy/util-uri-escape': 4.2.2 tslib: 2.8.1 - '@smithy/querystring-parser@4.0.2': + '@smithy/querystring-parser@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/service-error-classification@4.0.3': + '@smithy/service-error-classification@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 - '@smithy/shared-ini-file-loader@4.0.2': + '@smithy/shared-ini-file-loader@4.4.6': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/signature-v4@5.1.0': + '@smithy/signature-v4@5.3.11': dependencies: - '@smithy/is-array-buffer': 4.0.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-hex-encoding': 4.0.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-uri-escape': 4.0.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/is-array-buffer': 4.2.2 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-middleware': 4.2.11 + '@smithy/util-uri-escape': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.2.6': + '@smithy/smithy-client@4.12.3': dependencies: - '@smithy/core': 3.3.3 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-stack': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-stream': 4.2.0 + '@smithy/core': 3.23.9 + '@smithy/middleware-endpoint': 4.4.23 + '@smithy/middleware-stack': 4.2.11 + '@smithy/protocol-http': 5.3.11 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.17 tslib: 2.8.1 - '@smithy/types@4.2.0': + '@smithy/types@4.13.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.0.2': + '@smithy/url-parser@4.2.11': dependencies: - '@smithy/querystring-parser': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/querystring-parser': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-base64@4.0.0': + '@smithy/util-base64@4.3.2': dependencies: - '@smithy/util-buffer-from': 4.0.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-body-length-browser@4.0.0': + '@smithy/util-body-length-browser@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@4.0.0': + '@smithy/util-body-length-node@4.2.3': dependencies: tslib: 2.8.1 @@ -18197,66 +18195,65 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.0.0': + '@smithy/util-buffer-from@4.2.2': dependencies: - '@smithy/is-array-buffer': 4.0.0 + '@smithy/is-array-buffer': 4.2.2 tslib: 2.8.1 - '@smithy/util-config-provider@4.0.0': + '@smithy/util-config-provider@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.0.14': + '@smithy/util-defaults-mode-browser@4.3.39': dependencies: - '@smithy/property-provider': 4.0.2 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - bowser: 2.11.0 + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.0.14': + '@smithy/util-defaults-mode-node@4.2.42': dependencies: - '@smithy/config-resolver': 4.1.2 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 + '@smithy/config-resolver': 4.4.10 + '@smithy/credential-provider-imds': 4.2.11 + '@smithy/node-config-provider': 4.3.11 + '@smithy/property-provider': 4.2.11 + '@smithy/smithy-client': 4.12.3 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-endpoints@3.0.4': + '@smithy/util-endpoints@3.3.2': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 + '@smithy/node-config-provider': 4.3.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.0.0': + '@smithy/util-hex-encoding@4.2.2': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.0.2': + '@smithy/util-middleware@4.2.11': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-retry@4.0.3': + '@smithy/util-retry@4.2.11': dependencies: - '@smithy/service-error-classification': 4.0.3 - '@smithy/types': 4.2.0 + '@smithy/service-error-classification': 4.2.11 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-stream@4.2.0': + '@smithy/util-stream@4.5.17': dependencies: - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/node-http-handler': 4.0.4 - '@smithy/types': 4.2.0 - '@smithy/util-base64': 4.0.0 - '@smithy/util-buffer-from': 4.0.0 - '@smithy/util-hex-encoding': 4.0.0 - '@smithy/util-utf8': 4.0.0 + '@smithy/fetch-http-handler': 5.3.13 + '@smithy/node-http-handler': 4.4.14 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.2 + '@smithy/util-buffer-from': 4.2.2 + '@smithy/util-hex-encoding': 4.2.2 + '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/util-uri-escape@4.0.0': + '@smithy/util-uri-escape@4.2.2': dependencies: tslib: 2.8.1 @@ -18265,15 +18262,19 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.0.0': + '@smithy/util-utf8@4.2.2': dependencies: - '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-buffer-from': 4.2.2 tslib: 2.8.1 - '@smithy/util-waiter@4.0.3': + '@smithy/util-waiter@4.2.11': + dependencies: + '@smithy/abort-controller': 4.2.11 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@smithy/uuid@1.1.2': dependencies: - '@smithy/abort-controller': 4.0.2 - '@smithy/types': 4.2.0 tslib: 2.8.1 '@swc/helpers@0.5.15': @@ -18575,14 +18576,14 @@ snapshots: '@types/express-serve-static-core@4.17.41': dependencies: '@types/node': 20.19.25 - '@types/qs': 6.14.0 + '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 '@types/express-serve-static-core@4.19.6': dependencies: '@types/node': 20.19.25 - '@types/qs': 6.14.0 + '@types/qs': 6.15.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -18590,7 +18591,7 @@ snapshots: dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.17.41 - '@types/qs': 6.14.0 + '@types/qs': 6.15.0 '@types/serve-static': 1.15.5 '@types/fast-levenshtein@0.0.4': {} @@ -18783,7 +18784,7 @@ snapshots: dependencies: '@types/retry': 0.12.2 - '@types/qs@6.14.0': {} + '@types/qs@6.15.0': {} '@types/range-parser@1.2.7': {} @@ -19349,7 +19350,7 @@ snapshots: proper-lockfile: 4.1.2 resedit: 1.7.2 semver: 7.7.3 - tar: 7.5.7 + tar: 7.5.11 temp-file: 3.4.0 tiny-async-pool: 1.3.0 which: 5.0.0 @@ -19623,7 +19624,7 @@ snapshots: content-type: 1.0.5 debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 on-finished: 2.4.1 qs: 6.15.0 raw-body: 3.0.2 @@ -20744,9 +20745,9 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: {} + diff@4.0.4: {} - diff@5.2.0: {} + diff@5.2.2: {} dir-compare@4.2.0: dependencies: @@ -21260,14 +21261,14 @@ snapshots: exponential-backoff@3.1.3: {} - express-rate-limit@7.5.0(express@4.22.1): + express-rate-limit@7.5.1(express@4.22.1): dependencies: express: 4.22.1 - express-rate-limit@8.2.1(express@5.2.1): + express-rate-limit@8.3.1(express@5.2.1): dependencies: express: 5.2.1 - ip-address: 10.0.1 + ip-address: 10.1.0 express@4.22.1: dependencies: @@ -21383,9 +21384,7 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-parser@4.4.1: - dependencies: - strnum: 1.1.2 + fast-xml-builder@1.0.0: {} fast-xml-parser@4.5.4: dependencies: @@ -21395,6 +21394,11 @@ snapshots: dependencies: strnum: 2.2.0 + fast-xml-parser@5.4.1: + dependencies: + fast-xml-builder: 1.0.0 + strnum: 2.2.0 + fastest-levenshtein@1.0.16: {} fastq@1.15.0: @@ -21966,7 +21970,7 @@ snapshots: highlight.js@10.7.3: {} - hono@4.12.3: {} + hono@4.12.5: {} hosted-git-info@4.1.0: dependencies: @@ -22160,10 +22164,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -22270,8 +22270,6 @@ snapshots: interpret@3.1.1: {} - ip-address@10.0.1: {} - ip-address@10.1.0: {} ipaddr.js@1.9.1: {} @@ -23297,7 +23295,7 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonpath@1.2.1: + jsonpath@1.3.0: dependencies: esprima: 1.2.5 static-eval: 2.1.1 @@ -24215,7 +24213,7 @@ snapshots: minizlib@3.1.0: dependencies: - minipass: 7.1.2 + minipass: 7.1.3 mitt@3.0.1: {} @@ -24251,7 +24249,7 @@ snapshots: browser-stdout: 1.3.1 chokidar: 3.6.0 debug: 4.4.3(supports-color@8.1.1) - diff: 5.2.0 + diff: 5.2.2 escape-string-regexp: 4.0.0 find-up: 5.0.0 glob: 8.1.0 @@ -24404,7 +24402,7 @@ snapshots: nopt: 9.0.0 proc-log: 6.1.0 semver: 7.7.4 - tar: 7.5.7 + tar: 7.5.11 tinyglobby: 0.2.15 which: 6.0.1 transitivePeerDependencies: @@ -25118,7 +25116,7 @@ snapshots: puppeteer-extra-plugin-user-preferences@2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): dependencies: - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.1 deepmerge: 4.3.1 puppeteer-extra-plugin: 3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) puppeteer-extra-plugin-user-data-dir: 2.4.1(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))) @@ -25130,7 +25128,7 @@ snapshots: puppeteer-extra-plugin@3.2.3(puppeteer-extra@3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5))): dependencies: '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) + debug: 4.4.1 merge-deep: 3.0.3 optionalDependencies: puppeteer-extra: 3.3.6(puppeteer-core@24.37.5)(puppeteer@24.37.5(typescript@5.4.5)) @@ -25202,7 +25200,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 unpipe: 1.0.0 rc-config-loader@4.1.3: @@ -26245,11 +26243,11 @@ snapshots: fast-fifo: 1.3.2 streamx: 2.22.0 - tar@7.5.7: + tar@7.5.11: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 - minipass: 7.1.2 + minipass: 7.1.3 minizlib: 3.1.0 yallist: 5.0.0 @@ -26510,7 +26508,7 @@ snapshots: acorn-walk: 8.3.0 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.2 + diff: 4.0.4 make-error: 1.3.6 typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 @@ -26529,7 +26527,7 @@ snapshots: acorn-walk: 8.3.0 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.2 + diff: 4.0.4 make-error: 1.3.6 typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 @@ -26548,7 +26546,7 @@ snapshots: acorn-walk: 8.3.0 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.2 + diff: 4.0.4 make-error: 1.3.6 typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 @@ -26567,7 +26565,7 @@ snapshots: acorn-walk: 8.3.0 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.2 + diff: 4.0.4 make-error: 1.3.6 typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 @@ -26656,7 +26654,7 @@ snapshots: dependencies: qs: 6.15.0 tunnel: 0.0.6 - underscore: 1.13.6 + underscore: 1.13.8 typescript@5.4.5: {} @@ -26679,6 +26677,8 @@ snapshots: underscore@1.13.6: {} + underscore@1.13.8: {} + undici-types@5.26.5: {} undici-types@6.21.0: {} From db67aa9b68e6e5ea3b86b087a6567b519b1e861e Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 18:34:59 -0700 Subject: [PATCH 19/51] Remove noCompletion field from PartialCompletionSession MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Empty completions now flow through the SHOW path naturally: - complete=true (fully specified) → reuse (menu inactive, complete flag wins) - complete=false (error path) → re-fetch when user types past anchor Set separatorMode='none' on empty results so the separator check doesn't interfere with the SHOW path decision. --- .../renderer/src/partialCompletionSession.ts | 39 +++++++------------ .../test/partialCompletionSession.spec.ts | 17 ++++---- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 4941a2d2a0..421f9b1ea7 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -30,11 +30,10 @@ export interface ICompletionDispatcher { // States: // IDLE current === undefined // PENDING current !== undefined && completionP !== undefined -// ACTIVE current !== undefined && completionP === undefined && noCompletion === false -// EXHAUSTED current !== undefined && completionP === undefined && noCompletion === true +// ACTIVE current !== undefined && completionP === undefined // // Design principles: -// - Completion result fields (noCompletion, separatorMode) are stored as-is +// - Completion result fields (separatorMode, complete) are stored as-is // from the backend response and never mutated as the user keeps typing. // reuseSession() reads them to decide whether to show, hide, or re-fetch. // - reuseSession() makes exactly four kinds of decisions: @@ -65,9 +64,6 @@ export class PartialCompletionSession { // the backend reports how much the grammar consumed. `undefined` = IDLE. private current: string | undefined = undefined; - // True when the backend reported no completions for `current` (EXHAUSTED). - private noCompletion: boolean = false; - // Saved as-is from the last completion result: what kind of separator // must appear in the input immediately after `current` before // completions are valid. Defaults to "space" when omitted. @@ -117,7 +113,6 @@ export class PartialCompletionSession { public hide(): void { this.completionP = undefined; this.current = undefined; - this.noCompletion = false; this.separatorMode = "space"; this.complete = false; this.cancelMenu(); @@ -163,9 +158,9 @@ export class PartialCompletionSession { // PENDING — a fetch is in flight; wait for it (return true, no-op). // RE-FETCH — input has moved outside the anchor; the saved result no // longer applies (return false). - // HIDE+KEEP — input is within the anchor but the result's constraints - // aren't satisfied yet (no completions, or separator not - // typed); hide the menu but don't re-fetch (return true). + // HIDE+KEEP — input is within the anchor but the separator hasn't + // been typed yet; hide the menu but don't re-fetch + // (return true). // UNIQUE — prefix exactly matches one entry and is not a prefix of // any other; re-fetch for the NEXT level (return false). // Unconditional — `complete` is irrelevant here. @@ -194,15 +189,6 @@ export class PartialCompletionSession { return false; } - // HIDE+KEEP — backend found no completions for this anchor. - if (this.noCompletion) { - debug( - `Partial completion skipped: No completions for '${current}'`, - ); - this.menu.hide(); - return true; - } - // Separator handling: the character immediately after the anchor must // satisfy the separatorMode constraint. // "space": whitespace required @@ -276,7 +262,6 @@ export class PartialCompletionSession { debug(`Partial completion start: '${input}'`); this.cancelMenu(); this.current = input; - this.noCompletion = false; this.separatorMode = "space"; this.complete = false; this.menu.setChoices([]); @@ -321,10 +306,16 @@ export class PartialCompletionSession { debug( `Partial completion skipped: No completions for '${input}'`, ); - // Keep this.current at the value startNewSession set - // (the full input) so the EXHAUSTED anchor covers the - // entire typed text. - this.noCompletion = true; + // Keep this.current at the full input so the anchor + // covers the entire typed text. The menu stays empty, + // so reuseSession()'s SHOW path will use `complete` to + // decide: complete=true → reuse (nothing more exists); + // complete=false → re-fetch when new input arrives. + // + // Override separatorMode: with no completions, there is + // nothing to separate from, so the separator check in + // reuseSession() should not interfere. + this.separatorMode = "none"; return; } diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index 08963a787e..07fb2aa97a 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -113,7 +113,7 @@ describe("PartialCompletionSession — state transitions", () => { expect(menu.updatePrefix).toHaveBeenCalled(); }); - test("PENDING → EXHAUSTED: empty result suppresses re-fetch while input has same prefix", async () => { + test("PENDING → ACTIVE: empty result (complete=true) suppresses re-fetch while input has same prefix", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); @@ -121,19 +121,19 @@ describe("PartialCompletionSession — state transitions", () => { session.update("play", getPos); await Promise.resolve(); - // Should be EXHAUSTED — no new fetch even with extended input + // Empty completions + complete=true — no new fetch even with extended input session.update("play s", getPos); expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("EXHAUSTED → IDLE: backspace past anchor triggers a new fetch", async () => { + test("empty result: backspace past anchor triggers a new fetch", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("play", getPos); - await Promise.resolve(); // → EXHAUSTED with current="play" + await Promise.resolve(); // → ACTIVE (empty, complete=true) with current="play" // Backspace past anchor session.update("pla", getPos); @@ -637,28 +637,27 @@ describe("PartialCompletionSession — @command routing", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("@ command: empty result enters EXHAUSTED (no re-fetch)", async () => { + test("@ command: empty result (complete=true) suppresses re-fetch", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("@unknown", getPos); - await Promise.resolve(); // → EXHAUSTED + await Promise.resolve(); // → empty completions, complete=true // Still within anchor — no re-fetch session.update("@unknownmore", getPos); expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - expect(menu.hide).toHaveBeenCalled(); }); - test("@ command: backspace past anchor after EXHAUSTED triggers re-fetch", async () => { + test("@ command: backspace past anchor after empty result triggers re-fetch", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("@unknown", getPos); - await Promise.resolve(); // → EXHAUSTED with current="@unknown" + await Promise.resolve(); // → empty completions with current="@unknown" // Backspace past anchor session.update("@unknow", getPos); From 56a7c8699e1bf8e679890fcf7c55a51e6bf62d47 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 19:03:29 -0700 Subject: [PATCH 20/51] Fix type: use CompiledSpacingMode in candidateSeparatorMode and mergeSeparatorMode --- ts/packages/actionGrammar/src/grammarMatcher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index e332e3b0fd..7f7819170c 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -94,7 +94,7 @@ function requiresSeparator( // "optional" (CJK/mixed, or SpacingMode "optional") or "none". function candidateSeparatorMode( needsSep: boolean, - spacingMode: SpacingMode, + spacingMode: CompiledSpacingMode, ): GrammarSeparatorMode { if (needsSep) { return "spacePunctuation"; @@ -110,7 +110,7 @@ function candidateSeparatorMode( function mergeSeparatorMode( current: GrammarSeparatorMode | undefined, needsSep: boolean, - spacingMode: SpacingMode, + spacingMode: CompiledSpacingMode, ): GrammarSeparatorMode { const candidateMode = candidateSeparatorMode(needsSep, spacingMode); if (current === undefined) { From 5e8db8291ed140f277fd924e874fab6b1c142fc6 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 20:41:25 -0700 Subject: [PATCH 21/51] refactor: initialize tokenStartIndex to remainderIndex in completion Extract remainderIndex to avoid redundant computation and initialize tokenStartIndex unconditionally so the groupPrefixLength offset calculation no longer needs a nullish coalescing fallback. Add tokenStartIndex to the debug log for easier troubleshooting. --- .../dispatcher/src/command/completion.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index ad60543ad3..a392234c0b 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -207,10 +207,8 @@ async function getCommandParameterCompletion( // token whitespace (trimStart), the raw arithmetic can land on // the separator space — tokenBoundary rewinds to the preceding // token edge. - let startIndex = tokenBoundary( - input, - input.length - params.remainderLength, - ); + const remainderIndex = input.length - params.remainderLength; + let startIndex = tokenBoundary(input, remainderIndex); debug( `Command completion parameter consumed length: ${params.remainderLength}`, ); @@ -221,7 +219,7 @@ async function getCommandParameterCompletion( const { tokens, lastCompletableParam, lastParamImplicitQuotes } = params; - let tokenStartIndex: number | undefined; + let tokenStartIndex = remainderIndex; if (lastCompletableParam !== undefined && tokens.length > 0) { const valueToken = tokens[tokens.length - 1]; const quoted = isFullyQuoted(valueToken); @@ -239,7 +237,7 @@ async function getCommandParameterCompletion( agentCommandCompletions.length = 0; completions.length = 0; agentCommandCompletions.push(lastCompletableParam); - tokenStartIndex = startIndex - valueToken.length; + tokenStartIndex = remainderIndex - valueToken.length; startIndex = tokenBoundary(input, tokenStartIndex); } } @@ -267,15 +265,14 @@ async function getCommandParameterCompletion( (g) => g.prefixLength !== undefined, )?.prefixLength; if (groupPrefixLength !== undefined && groupPrefixLength != 0) { - startIndex = - (tokenStartIndex ?? startIndex) + groupPrefixLength; + startIndex = tokenStartIndex + groupPrefixLength; // we have advanced the startIndex, so existing completions are no longer valid, clear them out. completions.length = 0; } completions.push(...agentGroups); agentInvoked = true; debug( - `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}`, + `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${tokenStartIndex}`, ); } } From dfa48cfabba4d199803c968a250962c684a1cc01 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 20:52:58 -0700 Subject: [PATCH 22/51] perf: defer completion computation until maxPrefixLength check Move completion text construction, separator determination, and debug logging inside the maxPrefixLength guard in all three completion paths (string part, wildcard property, current string part). This avoids unnecessary work for candidates that will be discarded. --- .../actionGrammar/src/grammarMatcher.ts | 154 +++++++++--------- 1 file changed, 76 insertions(+), 78 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 7f7819170c..0ae0aebf31 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1279,40 +1279,40 @@ export function matchGrammarCompletion( debugCompletion(`Completing ${nextPart.type} part ${state.name}`); if (nextPart.type === "string") { - // The next expected part is a literal keyword string. - // Offer it as a completion (e.g. "music" after "play"). - const completionText = nextPart.value.join(" "); - debugCompletion( - `Adding completion candidate: "${completionText}" (consumed ${state.index} chars, spacing=${state.spacingMode ?? "auto"})`, - ); - - // Determine whether a separator (e.g. space) is needed - // between the content at matchedPrefixLength and the - // completion text. Check the boundary between the last - // consumed character and the first character of the - // completion. This is purely a property of the - // boundary — it does not account for any unmatched - // trailing content the user may have typed beyond - // matchedPrefixLength (e.g. a trailing space). - // Example: prefix="play" completion="music" → true (Latin). - // Example: prefix="play " completion="music" → true (matched - // prefix is "play"; separator still needed at that boundary). - // Example: prefix="再生" completion="音楽" → false (CJK). - let candidateNeedsSep = false; - if ( - state.index > 0 && - completionText.length > 0 && - state.spacingMode !== "none" - ) { - candidateNeedsSep = requiresSeparator( - prefix[state.index - 1], - completionText[0], - state.spacingMode, - ); - } - updateMaxPrefixLength(state.index); if (state.index === maxPrefixLength) { + // The next expected part is a literal keyword string. + // Offer it as a completion (e.g. "music" after "play"). + const completionText = nextPart.value.join(" "); + debugCompletion( + `Adding completion candidate: "${completionText}" (consumed ${state.index} chars, spacing=${state.spacingMode ?? "auto"})`, + ); + + // Determine whether a separator (e.g. space) is needed + // between the content at matchedPrefixLength and the + // completion text. Check the boundary between the last + // consumed character and the first character of the + // completion. This is purely a property of the + // boundary — it does not account for any unmatched + // trailing content the user may have typed beyond + // matchedPrefixLength (e.g. a trailing space). + // Example: prefix="play" completion="music" → true (Latin). + // Example: prefix="play " completion="music" → true (matched + // prefix is "play"; separator still needed at that boundary). + // Example: prefix="再生" completion="音楽" → false (CJK). + let candidateNeedsSep = false; + if ( + state.index > 0 && + completionText.length > 0 && + state.spacingMode !== "none" + ) { + candidateNeedsSep = requiresSeparator( + prefix[state.index - 1], + completionText[0], + state.spacingMode, + ); + } + completions.push(completionText); separatorMode = mergeSeparatorMode( separatorMode, @@ -1350,33 +1350,31 @@ export function matchGrammarCompletion( pendingWildcard.valueId, ); if (completionProperty !== undefined) { - debugCompletion( - `Adding completion property: ${JSON.stringify(completionProperty)}`, - ); - const candidatePrefixLength = pendingWildcard.start; - - // Determine whether a separator is needed between - // the content at matchedPrefixLength and the - // completion (the wildcard entity value). Check - // the boundary between the last consumed character - // before the wildcard and the first character of the - // entity value. We use "a" as a representative word - // character since the actual value is unknown. - let candidateNeedsSep = false; - if ( - pendingWildcard.start > 0 && - state.spacingMode !== "none" - ) { - candidateNeedsSep = requiresSeparator( - prefix[pendingWildcard.start - 1], - "a", - state.spacingMode, - ); - } - updateMaxPrefixLength(candidatePrefixLength); if (candidatePrefixLength === maxPrefixLength) { + debugCompletion( + `Adding completion property: ${JSON.stringify(completionProperty)}`, + ); + // Determine whether a separator is needed between + // the content at matchedPrefixLength and the + // completion (the wildcard entity value). Check + // the boundary between the last consumed character + // before the wildcard and the first character of the + // entity value. We use "a" as a representative word + // character since the actual value is unknown. + let candidateNeedsSep = false; + if ( + pendingWildcard.start > 0 && + state.spacingMode !== "none" + ) { + candidateNeedsSep = requiresSeparator( + prefix[pendingWildcard.start - 1], + "a", + state.spacingMode, + ); + } + properties.push(completionProperty); separatorMode = mergeSeparatorMode( separatorMode, @@ -1401,30 +1399,30 @@ export function matchGrammarCompletion( currentPart !== undefined && currentPart.type === "string" ) { - const fullText = currentPart.value.join(" "); - debugCompletion( - `Adding completion: "${fullText}" (consumed ${state.index} chars)`, - ); - - // Determine whether a separator is needed between - // the matched prefix and the completion text. Check - // the boundary between the last consumed character - // and the first character of the completion. - let candidateNeedsSep = false; - if ( - state.index > 0 && - fullText.length > 0 && - state.spacingMode !== "none" - ) { - candidateNeedsSep = requiresSeparator( - prefix[state.index - 1], - fullText[0], - state.spacingMode, - ); - } - updateMaxPrefixLength(state.index); if (state.index === maxPrefixLength) { + const fullText = currentPart.value.join(" "); + debugCompletion( + `Adding completion: "${fullText}" (consumed ${state.index} chars)`, + ); + + // Determine whether a separator is needed between + // the matched prefix and the completion text. Check + // the boundary between the last consumed character + // and the first character of the completion. + let candidateNeedsSep = false; + if ( + state.index > 0 && + fullText.length > 0 && + state.spacingMode !== "none" + ) { + candidateNeedsSep = requiresSeparator( + prefix[state.index - 1], + fullText[0], + state.spacingMode, + ); + } + completions.push(fullText); separatorMode = mergeSeparatorMode( separatorMode, From e128a2d2bdfaf08bc464a47680190687e70aecd7 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 21:10:33 -0700 Subject: [PATCH 23/51] Fix partial completion not refetching after search menu selection After handleSelect replaced text via Range.insertNode() + collapse(), the selection's endContainer pointed at the parent element rather than the deepest text node. This caused isSelectionAtEnd() to fail, so the selectionchange-triggered update bailed out without fetching new completions. Fix: normalize the text entry to merge adjacent text nodes, place the cursor at the end of the last text node (matching isSelectionAtEnd's expectation), and explicitly call update() instead of relying solely on the async selectionchange event. --- ts/packages/shell/src/renderer/src/partial.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partial.ts b/ts/packages/shell/src/renderer/src/partial.ts index 32c88adda6..7475864846 100644 --- a/ts/packages/shell/src/renderer/src/partial.ts +++ b/ts/packages/shell/src/renderer/src/partial.ts @@ -283,20 +283,34 @@ export class PartialCompletion { r.deleteContents(); r.insertNode(newNode); - r.collapse(false); + // Normalize merges adjacent text nodes so getSelectionEndNode() + // returns a single text node. Then place the cursor at its end + // so isSelectionAtEnd() passes. Without this, r.collapse(false) + // leaves endContainer pointing at the parent element, which does + // not match the deepest-last-child that isSelectionAtEnd() expects. + const textEntry = this.input.getTextEntry(); + textEntry.normalize(); + const endNode = this.input.getSelectionEndNode(); + const cursorRange = document.createRange(); + cursorRange.setStart(endNode, endNode.textContent?.length ?? 0); + cursorRange.collapse(true); const s = document.getSelection(); if (s) { s.removeAllRanges(); - s.addRange(r); + s.addRange(cursorRange); } // Make sure the text entry remains focused after replacement. - this.input.getTextEntry().focus(); + textEntry.focus(); // Reset completion state so the next update requests fresh completions. this.session.resetToIdle(); debug(`Partial completion replaced: ${replaceText}`); + + // Explicitly trigger a completion update. The selectionchange event + // alone is unreliable after programmatic DOM manipulation. + this.update(false); } public handleSpecialKeys(event: KeyboardEvent) { From c79165d204144009877e34ac2b982a7ca74163a3 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 21:12:55 -0700 Subject: [PATCH 24/51] Fix debug log label in grammar completion matching --- ts/packages/actionGrammar/src/grammarMatcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 0ae0aebf31..d6b425c26a 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1285,7 +1285,7 @@ export function matchGrammarCompletion( // Offer it as a completion (e.g. "music" after "play"). const completionText = nextPart.value.join(" "); debugCompletion( - `Adding completion candidate: "${completionText}" (consumed ${state.index} chars, spacing=${state.spacingMode ?? "auto"})`, + `Adding completion text: "${completionText}" (consumed ${state.index} chars, spacing=${state.spacingMode ?? "auto"})`, ); // Determine whether a separator (e.g. space) is needed From 90743c1bbdb226e5cebe53418502ce50741fc2ba Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Mon, 9 Mar 2026 23:10:46 -0700 Subject: [PATCH 25/51] Refactor: move prefixLength and separatorMode from CompletionGroup to new CompletionGroups wrapper type These two fields are always identical across all CompletionGroup objects in a single completion response. Introduce a CompletionGroups wrapper type that holds the shared prefixLength and separatorMode alongside the array of groups, eliminating per-group duplication. Updated across the entire monorepo: - agentSdk: new CompletionGroups type, updated AppAgentCommandInterface - agentRpc: updated RPC invoke/server types - dispatcher: completion engine, command handlers, config handlers - shell: ShellSetSettingCommandHandler - tests: completion.spec.ts --- ts/packages/agentRpc/src/server.ts | 4 +- ts/packages/agentRpc/src/types.ts | 4 +- ts/packages/agentSdk/src/command.ts | 14 +- .../agentSdk/src/helpers/commandHelpers.ts | 10 +- ts/packages/agentSdk/src/index.ts | 1 + .../dispatcher/src/command/completion.ts | 95 +++++++------- .../handlers/matchCommandHandler.ts | 19 +-- .../handlers/requestCommandHandler.ts | 19 +-- .../handlers/translateCommandHandler.ts | 19 +-- .../system/handlers/actionCommandHandler.ts | 5 +- .../system/handlers/configCommandHandlers.ts | 21 +-- .../src/translation/requestCompletion.ts | 18 +-- .../dispatcher/test/completion.spec.ts | 122 ++++++++++-------- ts/packages/shell/src/main/agent.ts | 10 +- 14 files changed, 187 insertions(+), 174 deletions(-) diff --git a/ts/packages/agentRpc/src/server.ts b/ts/packages/agentRpc/src/server.ts index cfb26b5b18..9b5b0e1555 100644 --- a/ts/packages/agentRpc/src/server.ts +++ b/ts/packages/agentRpc/src/server.ts @@ -17,7 +17,7 @@ import { AppAgentManifest, TypeAgentAction, AppAgentInitSettings, - CompletionGroup, + CompletionGroups, } from "@typeagent/agent-sdk"; import { @@ -170,7 +170,7 @@ export function createAgentRpcServer( } return agent.getCommands(getSessionContextShim(param)); }, - async getCommandCompletion(param): Promise { + async getCommandCompletion(param): Promise { if (agent.getCommandCompletion === undefined) { throw new Error("Invalid invocation of getCommandCompletion"); } diff --git a/ts/packages/agentRpc/src/types.ts b/ts/packages/agentRpc/src/types.ts index 72e8492586..9a73f19cc2 100644 --- a/ts/packages/agentRpc/src/types.ts +++ b/ts/packages/agentRpc/src/types.ts @@ -20,7 +20,7 @@ import { StorageListOptions, TemplateSchema, TypeAgentAction, - CompletionGroup, + CompletionGroups, ResolveEntityResult, } from "@typeagent/agent-sdk"; import { AgentInterfaceFunctionName } from "./server.js"; @@ -182,7 +182,7 @@ export type AgentInvokeFunctions = { params: ParsedCommandParams; names: string[]; }, - ): Promise; + ): Promise; executeCommand( param: Partial & { commands: string[]; diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index 6aa6df8005..cc27152309 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -73,10 +73,16 @@ export type CompletionGroup = { emojiChar?: string | undefined; // Optional icon for the completion category sorted?: boolean; // If true, the completions are already sorted. Default is false, and the completions sorted alphabetically. kind?: "literal" | "entity"; // Whether completions are fixed grammar tokens or entity values from agents +}; + +// Wraps an array of CompletionGroups with shared metadata that applies +// uniformly to all groups in the response. +export type CompletionGroups = { + groups: CompletionGroup[]; // Number of characters of the input consumed by the grammar/command parser - // before the completion point for this group. When present, the shell - // inserts completions at this offset, replacing space-based heuristics - // that fail for CJK and other non-space-delimited scripts. + // before the completion point. When present, the shell inserts + // completions at this offset, replacing space-based heuristics that fail + // for CJK and other non-space-delimited scripts. prefixLength?: number | undefined; // What kind of separator is required between the matched prefix and // the completion text. When omitted, defaults to "space" (whitespace @@ -94,7 +100,7 @@ export interface AppAgentCommandInterface { params: ParsedCommandParams | undefined, names: string[], // array of or -- or -- for completion context: SessionContext, - ): Promise; + ): Promise; // Execute a resolved command. Exception from the execution are treated as errors and displayed to the user. executeCommand( diff --git a/ts/packages/agentSdk/src/helpers/commandHelpers.ts b/ts/packages/agentSdk/src/helpers/commandHelpers.ts index 0ed54947b5..9b44aa8fde 100644 --- a/ts/packages/agentSdk/src/helpers/commandHelpers.ts +++ b/ts/packages/agentSdk/src/helpers/commandHelpers.ts @@ -7,7 +7,7 @@ import { CommandDescriptor, CommandDescriptors, CommandDescriptorTable, - CompletionGroup, + CompletionGroups, } from "../command.js"; import { ParameterDefinitions, @@ -42,7 +42,7 @@ export type CommandHandler = CommandDescriptor & { context: SessionContext, params: PartialParsedCommandParams, names: string[], - ): Promise; + ): Promise; }; type CommandHandlerTypes = CommandHandlerNoParams | CommandHandler; @@ -171,7 +171,11 @@ export function getCommandInterface( context: SessionContext, ) => { const handler = getCommandHandler(handlers, commands); - return handler.getCompletion?.(context, params, names) ?? []; + return ( + handler.getCompletion?.(context, params, names) ?? { + groups: [], + } + ); }; } return commandInterface; diff --git a/ts/packages/agentSdk/src/index.ts b/ts/packages/agentSdk/src/index.ts index 43da8f4190..d2e6ac4b0f 100644 --- a/ts/packages/agentSdk/src/index.ts +++ b/ts/packages/agentSdk/src/index.ts @@ -30,6 +30,7 @@ export { CommandDescriptorTable, AppAgentCommandInterface, CompletionGroup, + CompletionGroups, SeparatorMode, } from "./command.js"; diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index a392234c0b..f38b5886a9 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -9,6 +9,7 @@ import { ParameterDefinitions, ParsedCommandParams, CompletionGroup, + CompletionGroups, SeparatorMode, } from "@typeagent/agent-sdk"; import { @@ -29,30 +30,19 @@ import { CommandCompletionResult } from "@typeagent/dispatcher-types"; const debug = registerDebug("typeagent:command:completion"); const debugError = registerDebug("typeagent:command:completion:error"); -// Aggregate SeparatorMode from completion groups. -// The most restrictive separator-requiring mode wins. +// Merge two SeparatorMode values — most restrictive wins. // Priority: "space" > "spacePunctuation" > "optional" > "none" > undefined. -// Groups that omit separatorMode are ignored (they inherit the default). -function aggregateSeparatorMode( - groups: CompletionGroup[], +function mergeSeparatorMode( + a: SeparatorMode | undefined, + b: SeparatorMode | undefined, ): SeparatorMode | undefined { - let result: SeparatorMode | undefined; - for (const g of groups) { - const mode = g.separatorMode; - if (mode === undefined) { - continue; - } - if (mode === "space") { - return "space"; // Most restrictive, short-circuit. - } - if (result === undefined) { - result = mode; - } else if (mode === "spacePunctuation") { - result = "spacePunctuation"; - } - // "optional" and "none" don't override "spacePunctuation" - } - return result; + if (a === undefined) return b; + if (b === undefined) return a; + if (a === "space" || b === "space") return "space"; + if (a === "spacePunctuation" || b === "spacePunctuation") + return "spacePunctuation"; + if (a === "optional" || b === "optional") return "optional"; + return "none"; } // Return the full flag name if we are waiting a flag value. Add boolean values for completions and return undefined if the flag is boolean. @@ -153,6 +143,7 @@ function collectFlags( type ParameterCompletionResult = { completions: CompletionGroup[]; startIndex: number; + separatorMode: SeparatorMode | undefined; complete: boolean; }; @@ -214,6 +205,7 @@ async function getCommandParameterCompletion( ); let agentInvoked = false; + let separatorMode: SeparatorMode | undefined; const agent = context.agents.getAppAgent(result.actualAppAgentName); if (agent.getCommandCompletion) { const { tokens, lastCompletableParam, lastParamImplicitQuotes } = @@ -247,29 +239,29 @@ async function getCommandParameterCompletion( debug( `Command completion parameter with agent: '${agentName}' with params ${JSON.stringify(agentCommandCompletions)}`, ); - const agentGroups = await agent.getCommandCompletion( - result.commands, - params, - agentCommandCompletions, - sessionContext, - ); - - // Allow grammar-reported prefixLength (from groups) to override + const agentResult: CompletionGroups = + await agent.getCommandCompletion( + result.commands, + params, + agentCommandCompletions, + sessionContext, + ); + + // Allow grammar-reported prefixLength to override // the parse-derived startIndex. This handles CJK and other // non-space-delimited scripts where the grammar matcher is the // authoritative source for how far into the input it consumed. // Grammar prefixLength is relative to the token content start // (after the separator space), not to tokenBoundary (before // it), so use tokenStartIndex when available. - const groupPrefixLength = agentGroups.find( - (g) => g.prefixLength !== undefined, - )?.prefixLength; + const groupPrefixLength = agentResult.prefixLength; if (groupPrefixLength !== undefined && groupPrefixLength != 0) { startIndex = tokenStartIndex + groupPrefixLength; // we have advanced the startIndex, so existing completions are no longer valid, clear them out. completions.length = 0; } - completions.push(...agentGroups); + completions.push(...agentResult.groups); + separatorMode = agentResult.separatorMode; agentInvoked = true; debug( `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${tokenStartIndex}`, @@ -299,7 +291,7 @@ async function getCommandParameterCompletion( complete = params.nextArgs.length === 0; } - return { completions, startIndex, complete }; + return { completions, startIndex, separatorMode, complete }; } // @@ -331,7 +323,7 @@ async function getCommandParameterCompletion( // resolveCommand (→ parseParams). Completions // describe what can validly follow after the anchor. // May be overridden by a grammar-reported prefixLength -// from a CompletionGroup. +// from a CompletionGroups result. // // startIndex is always placed at a token boundary // (not on separator whitespace). Each production @@ -359,7 +351,7 @@ async function getCommandParameterCompletion( // separatorMode // Describes what kind of separator is required between // the matched prefix and the completion text. -// Aggregated: most restrictive mode from any group wins. +// Merged: most restrictive mode from any source wins. // When omitted, consumers default to "space". // // complete true when the returned completions are the *exhaustive* @@ -396,8 +388,9 @@ export async function getCommandCompletion( ); let startIndex = tokenBoundary(input, commandConsumedLength); - // Collect completions + // Collect completions and track separatorMode across all sources. const completions: CompletionGroup[] = []; + let separatorMode: SeparatorMode | undefined; if (input.trim() === "") { completions.push({ name: "Command Prefixes", @@ -436,8 +429,8 @@ export async function getCommandCompletion( completions.push({ name: "Subcommands", completions: Object.keys(table.commands), - separatorMode: "space", }); + separatorMode = mergeSeparatorMode(separatorMode, "space"); } if (parameterCompletions === undefined) { @@ -448,6 +441,10 @@ export async function getCommandCompletion( } else { completions.push(...parameterCompletions.completions); startIndex = parameterCompletions.startIndex; + separatorMode = mergeSeparatorMode( + separatorMode, + parameterCompletions.separatorMode, + ); complete = parameterCompletions.complete; } } else if (table !== undefined) { @@ -467,12 +464,14 @@ export async function getCommandCompletion( completions.push({ name: "Subcommands", completions: Object.keys(table.commands), - separatorMode: - result.parsedAppAgentName !== undefined || - result.commands.length > 0 - ? "space" - : "optional", }); + separatorMode = mergeSeparatorMode( + separatorMode, + result.parsedAppAgentName !== undefined || + result.commands.length > 0 + ? "space" + : "optional", + ); } else { // Both table and descriptor are undefined — the agent // returned no commands at all. Nothing to add; @@ -493,16 +492,10 @@ export async function getCommandCompletion( completions: context.agents .getAppAgentNames() .filter((name) => context.agents.isCommandEnabled(name)), - separatorMode: "optional", }); + separatorMode = mergeSeparatorMode(separatorMode, "optional"); } - // Extract separatorMode from groups. The most restrictive - // mode wins ("space" and "spacePunctuation" are both - // separator-requiring; among those, "space" is more - // restrictive because it only allows whitespace). - const separatorMode = aggregateSeparatorMode(completions); - const completionResult: CommandCompletionResult = { startIndex, completions, diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts index 05855042f2..d2deb6dd24 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts @@ -4,7 +4,7 @@ import { CommandHandlerContext } from "../../commandHandlerContext.js"; import { ActionContext, - CompletionGroup, + CompletionGroups, ParsedCommandParams, SessionContext, } from "@typeagent/agent-sdk"; @@ -51,19 +51,20 @@ export class MatchCommandHandler implements CommandHandler { context: SessionContext, params: ParsedCommandParams, names: string[], - ): Promise { - const completions: CompletionGroup[] = []; + ): Promise { + const result: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "request") { const requestPrefix = params.args.request; - completions.push( - ...(await requestCompletion( - requestPrefix, - context.agentContext, - )), + const requestResult = await requestCompletion( + requestPrefix, + context.agentContext, ); + result.groups.push(...requestResult.groups); + result.prefixLength = requestResult.prefixLength; + result.separatorMode = requestResult.separatorMode; } } - return completions; + return result; } } diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts index 82c8b0656f..aac6ff91a4 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts @@ -35,7 +35,7 @@ import { ActionContext, ParsedCommandParams, SessionContext, - CompletionGroup, + CompletionGroups, } from "@typeagent/agent-sdk"; import { CommandHandler } from "@typeagent/agent-sdk/helpers/command"; import { @@ -474,19 +474,20 @@ export class RequestCommandHandler implements CommandHandler { context: SessionContext, params: ParsedCommandParams, names: string[], - ): Promise { - const completions: CompletionGroup[] = []; + ): Promise { + const result: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "request") { const requestPrefix = params.args.request; - completions.push( - ...(await requestCompletion( - requestPrefix, - context.agentContext, - )), + const requestResult = await requestCompletion( + requestPrefix, + context.agentContext, ); + result.groups.push(...requestResult.groups); + result.prefixLength = requestResult.prefixLength; + result.separatorMode = requestResult.separatorMode; } } - return completions; + return result; } } diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts index 12362c3acf..eec0b95bd1 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts @@ -5,7 +5,7 @@ import { CommandHandlerContext } from "../../commandHandlerContext.js"; import { openai as ai } from "aiclient"; import { ActionContext, - CompletionGroup, + CompletionGroups, ParsedCommandParams, SessionContext, } from "@typeagent/agent-sdk"; @@ -76,19 +76,20 @@ export class TranslateCommandHandler implements CommandHandler { context: SessionContext, params: ParsedCommandParams, names: string[], - ): Promise { - const completions: CompletionGroup[] = []; + ): Promise { + const result: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "request") { const requestPrefix = params.args.request; - completions.push( - ...(await requestCompletion( - requestPrefix, - context.agentContext, - )), + const requestResult = await requestCompletion( + requestPrefix, + context.agentContext, ); + result.groups.push(...requestResult.groups); + result.prefixLength = requestResult.prefixLength; + result.separatorMode = requestResult.separatorMode; } } - return completions; + return result; } } diff --git a/ts/packages/dispatcher/dispatcher/src/context/system/handlers/actionCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/system/handlers/actionCommandHandler.ts index b3591b0c77..f5dd269007 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/system/handlers/actionCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/system/handlers/actionCommandHandler.ts @@ -5,6 +5,7 @@ import { ActionContext, AppAction, CompletionGroup, + CompletionGroups, ParsedCommandParams, PartialParsedCommandParams, SessionContext, @@ -343,7 +344,7 @@ export class ActionCommandHandler implements CommandHandler { context: SessionContext, params: PartialParsedCommandParams, names: string[], - ): Promise { + ): Promise { const systemContext = context.agentContext; const completions: CompletionGroup[] = []; for (const name of names) { @@ -430,6 +431,6 @@ export class ActionCommandHandler implements CommandHandler { continue; } } - return completions; + return { groups: completions }; } } diff --git a/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts b/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts index 1175271444..2f3ad22824 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/system/handlers/configCommandHandlers.ts @@ -18,6 +18,7 @@ import chalk from "chalk"; import { ActionContext, CompletionGroup, + CompletionGroups, ParameterDefinitions, ParsedCommandParams, PartialParsedCommandParams, @@ -502,7 +503,7 @@ class AgentToggleCommandHandler implements CommandHandler { } } - return completions; + return { groups: completions }; } } @@ -557,7 +558,7 @@ class ExplainerCommandHandler implements CommandHandler { }); } } - return completions; + return { groups: completions }; } } @@ -626,7 +627,7 @@ class ConfigModelSetCommandHandler implements CommandHandler { context: SessionContext, params: PartialParsedCommandParams, names: string[], - ): Promise { + ): Promise { const completions: CompletionGroup[] = []; for (const name of names) { if (name === "model") { @@ -637,7 +638,7 @@ class ConfigModelSetCommandHandler implements CommandHandler { } } - return completions; + return { groups: completions }; } } @@ -746,7 +747,7 @@ class FixedSchemaCommandHandler implements CommandHandler { context: SessionContext, params: PartialParsedCommandParams, names: string[], - ): Promise { + ): Promise { const completions: CompletionGroup[] = []; const systemContext = context.agentContext; for (const name of names) { @@ -757,7 +758,7 @@ class FixedSchemaCommandHandler implements CommandHandler { }); } } - return completions; + return { groups: completions }; } } @@ -839,7 +840,7 @@ class GrammarSystemCommandHandler implements CommandHandler { }); } } - return completions; + return { groups: completions }; } } class GrammarUseDFACommandHandler implements CommandHandler { @@ -882,7 +883,7 @@ class GrammarUseDFACommandHandler implements CommandHandler { completions.push({ name, completions: ["true", "false"] }); } } - return completions; + return { groups: completions }; } } @@ -1219,7 +1220,7 @@ class ConfigRequestCommandHandler implements CommandHandler { context: SessionContext, params: PartialParsedCommandParams, names: string[], - ): Promise { + ): Promise { const completions: CompletionGroup[] = []; const systemContext = context.agentContext; for (const name of names) { @@ -1244,7 +1245,7 @@ class ConfigRequestCommandHandler implements CommandHandler { } } } - return completions; + return { groups: completions }; } } diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 229c7e4d55..78c45ad9a0 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -6,7 +6,7 @@ import registerDebug from "debug"; import { ExecutableAction, getPropertyInfo, MatchOptions } from "agent-cache"; import { CompletionGroup, - SeparatorMode, + CompletionGroups, TypeAgentAction, } from "@typeagent/agent-sdk"; import { DeepPartialUndefined } from "@typeagent/common-utils"; @@ -77,7 +77,7 @@ function getCompletionNamespaceKeys(context: CommandHandlerContext): string[] { export async function requestCompletion( requestPrefix: string | undefined, context: CommandHandlerContext, -): Promise { +): Promise { debugCompletion(`Request completion for prefix: '${requestPrefix}'`); const namespaceKeys = getCompletionNamespaceKeys(context); debugCompletion(`Request completion namespace keys`, namespaceKeys); @@ -92,7 +92,7 @@ export async function requestCompletion( const results = context.agentCache.completion(requestPrefix, options); if (results === undefined) { - return []; + return { groups: [] }; } const prefixLength = results.matchedPrefixLength; @@ -104,13 +104,11 @@ export async function requestCompletion( completions: results.completions, needQuotes: false, // Request completions are partial, no quotes needed kind: "literal", - prefixLength, - separatorMode, }); } if (results.properties === undefined) { - return completions; + return { groups: completions, prefixLength, separatorMode }; } const propertyCompletions = new Map(); @@ -125,14 +123,12 @@ export async function requestCompletion( completionProperty.actions, context, propertyCompletions, - prefixLength, - separatorMode, ); } } completions.push(...propertyCompletions.values()); - return completions; + return { groups: completions, prefixLength, separatorMode }; } async function collectActionCompletions( @@ -140,8 +136,6 @@ async function collectActionCompletions( partialActions: ExecutableAction[], context: CommandHandlerContext, propertyCompletions: Map, - prefixLength?: number | undefined, - separatorMode?: SeparatorMode | undefined, ) { for (const propertyName of properties) { const { action, parameterName } = getPropertyInfo( @@ -167,8 +161,6 @@ async function collectActionCompletions( needQuotes: false, // Request completions are partial, no quotes needed sorted: true, // REVIEW: assume property completions are already in desired order by the agent. kind: "entity", - prefixLength, - separatorMode, }); } } diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 1a2c5b3693..66309e765f 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -4,7 +4,7 @@ import { AppAgent, AppAgentManifest, - CompletionGroup, + CompletionGroups, } from "@typeagent/agent-sdk"; import { AppAgentProvider } from "../src/agentProvider/agentProvider.js"; import { @@ -23,7 +23,7 @@ import { getCommandCompletion } from "../src/command/completion.js"; // CJK ("東京" → "タワー"/"駅") and English ("Tokyo" → "Tower"/"Station") // prefixes. `token` is the raw last token from parseParams — it may // include a leading quote for open-quoted input. -function grammarCompletion(token: string): CompletionGroup[] { +function grammarCompletion(token: string): CompletionGroups { // Strip a leading quote so grammar match logic operates on text only. const text = token.startsWith('"') ? token.substring(1) : token; const quoteOffset = token.length - text.length; // 0 or 1 @@ -31,40 +31,46 @@ function grammarCompletion(token: string): CompletionGroup[] { if (text.startsWith("Tokyo")) { const suffix = text.substring(5).trim(); if (suffix.startsWith("Tower") || suffix.startsWith("Station")) { - return []; // completed match + return { groups: [] }; // completed match } - return [ - { - name: "Grammar", - completions: ["Tower", "Station"], - prefixLength: quoteOffset + 5, - separatorMode: "space", - }, - ]; + return { + groups: [ + { + name: "Grammar", + completions: ["Tower", "Station"], + }, + ], + prefixLength: quoteOffset + 5, + separatorMode: "space", + }; } if (text.startsWith("東京")) { const suffix = text.substring(2); if (suffix.startsWith("タワー") || suffix.startsWith("駅")) { - return []; // completed match + return { groups: [] }; // completed match } - return [ + return { + groups: [ + { + name: "Grammar", + completions: ["タワー", "駅"], + }, + ], + prefixLength: quoteOffset + 2, + separatorMode: "optional", + }; + } + // No prefix matched — offer initial completions. + return { + groups: [ { name: "Grammar", - completions: ["タワー", "駅"], - prefixLength: quoteOffset + 2, - separatorMode: "optional", + completions: ["Tokyo ", "東京"], }, - ]; - } - // No prefix matched — offer initial completions. - return [ - { - name: "Grammar", - completions: ["Tokyo ", "東京"], - ...(token.length > 0 ? { prefixLength: 0 } : {}), - separatorMode: "space", - }, - ]; + ], + ...(token.length > 0 ? { prefixLength: 0 } : {}), + separatorMode: "space", + }; } const handlers = { @@ -85,16 +91,18 @@ const handlers = { _context: unknown, _params: unknown, names: string[], - ): Promise => { + ): Promise => { if (!names.includes("task")) { - return []; + return { groups: [] }; } - return [ - { - name: "Tasks", - completions: ["build", "test", "deploy"], - }, - ]; + return { + groups: [ + { + name: "Tasks", + completions: ["build", "test", "deploy"], + }, + ], + }; }, }, nested: { @@ -157,16 +165,18 @@ const handlers = { _context: unknown, _params: unknown, names: string[], - ): Promise => { + ): Promise => { if (!names.includes("first") && !names.includes("second")) { - return []; + return { groups: [] }; } - return [ - { - name: "Values", - completions: ["alpha", "beta"], - }, - ]; + return { + groups: [ + { + name: "Values", + completions: ["alpha", "beta"], + }, + ], + }; }, }, search: { @@ -184,16 +194,18 @@ const handlers = { _context: unknown, _params: unknown, names: string[], - ): Promise => { + ): Promise => { if (!names.includes("query")) { - return []; + return { groups: [] }; } - return [ - { - name: "Suggestions", - completions: ["hello world", "foo bar"], - }, - ]; + return { + groups: [ + { + name: "Suggestions", + completions: ["hello world", "foo bar"], + }, + ], + }; }, }, grammar: { @@ -210,9 +222,9 @@ const handlers = { _context: unknown, params: unknown, names: string[], - ): Promise => { + ): Promise => { if (!names.includes("phrase")) { - return []; + return { groups: [] }; } const p = params as { tokens?: string[] }; const lastToken = p.tokens?.[p.tokens.length - 1] ?? ""; @@ -234,9 +246,9 @@ const handlers = { _context: unknown, params: unknown, names: string[], - ): Promise => { + ): Promise => { if (!names.includes("query")) { - return []; + return { groups: [] }; } const p = params as { tokens?: string[] }; const lastToken = p.tokens?.[p.tokens.length - 1] ?? ""; diff --git a/ts/packages/shell/src/main/agent.ts b/ts/packages/shell/src/main/agent.ts index a9b2fd83e5..3bcc5fc1fb 100644 --- a/ts/packages/shell/src/main/agent.ts +++ b/ts/packages/shell/src/main/agent.ts @@ -6,7 +6,7 @@ import { AppAgent, AppAgentInitSettings, AppAgentManifest, - CompletionGroup, + CompletionGroups, ParsedCommandParams, PartialParsedCommandParams, SessionContext, @@ -209,11 +209,11 @@ class ShellSetSettingCommandHandler implements CommandHandler { context: SessionContext, params: PartialParsedCommandParams, names: string[], - ): Promise { - const completions: CompletionGroup[] = []; + ): Promise { + const completions: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "name") { - completions.push({ + completions.groups.push({ name, completions: getObjectPropertyNames( context.agentContext.shellWindow.getUserSettings(), @@ -228,7 +228,7 @@ class ShellSetSettingCommandHandler implements CommandHandler { context.agentContext.shellWindow.getUserSettings(); const value = getObjectProperty(settings, settingName); if (typeof value === "boolean") { - completions.push({ + completions.groups.push({ name, completions: ["true", "false"], }); From e5cd8f71a9a6b7594240e7e11acd9a501806dbc4 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Tue, 10 Mar 2026 14:00:58 -0700 Subject: [PATCH 26/51] Move completion fields from SDK ParsedCommandParams to dispatcher-internal ParseParamsResult The tokens, lastCompletableParam, lastParamImplicitQuotes, and nextArgs fields on ParsedCommandParams were only used internally by the dispatcher's command completion engine. Move them into the existing dispatcher-internal ParseParamsResult type, keeping the public SDK surface to just args and flags. - Remove four completion fields from agentSdk ParsedCommandParams - Add them to dispatcher's ParseParamsResult alongside remainderLength - Update completion.ts getPendingFlag to use ParseParamsResult type - Simplify browser agent's manually-constructed ParsedCommandParams - Refactor MCP agent provider to extract values from params.args instead of the removed params.tokens --- ts/packages/agentSdk/src/parameters.ts | 6 ------ .../browser/src/agent/browserActionHandler.mts | 4 ---- .../src/mcpAgentProvider.ts | 17 ++++++++++++++--- .../dispatcher/src/command/completion.ts | 3 +-- .../dispatcher/src/command/parameters.ts | 6 ++++++ 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/ts/packages/agentSdk/src/parameters.ts b/ts/packages/agentSdk/src/parameters.ts index 7d21e7fa52..ec817a1b57 100644 --- a/ts/packages/agentSdk/src/parameters.ts +++ b/ts/packages/agentSdk/src/parameters.ts @@ -140,12 +140,6 @@ type ArgsOutput = T extends ArgDefinitions export type ParsedCommandParams = { args: ArgsOutput; flags: FlagsOutput; - - // Information for partial command completion. - tokens: string[]; // The list of tokens parsed from the command. - lastCompletableParam: string | undefined; // The last parameter that was parsed that can be completed. - lastParamImplicitQuotes: boolean; // If the last parameter is implicitly quoted. - nextArgs: string[]; // A list of potential arguments next. }; export type PartialParsedCommandParams = diff --git a/ts/packages/agents/browser/src/agent/browserActionHandler.mts b/ts/packages/agents/browser/src/agent/browserActionHandler.mts index 1c90b99590..06fafd748c 100644 --- a/ts/packages/agents/browser/src/agent/browserActionHandler.mts +++ b/ts/packages/agents/browser/src/agent/browserActionHandler.mts @@ -1761,10 +1761,6 @@ async function changeSearchProvider( const params: ParsedCommandParams = { args: { provider: `${action.parameters.name}` }, flags: {}, - tokens: [], - lastCompletableParam: undefined, - lastParamImplicitQuotes: false, - nextArgs: [], }; await cmd.run(context, params); diff --git a/ts/packages/defaultAgentProvider/src/mcpAgentProvider.ts b/ts/packages/defaultAgentProvider/src/mcpAgentProvider.ts index ceead98165..c8625e5521 100644 --- a/ts/packages/defaultAgentProvider/src/mcpAgentProvider.ts +++ b/ts/packages/defaultAgentProvider/src/mcpAgentProvider.ts @@ -12,6 +12,7 @@ import { import { AppAgentProvider } from "agent-dispatcher"; import { ArgDefinitions, + ParameterDefinitions, ParsedCommandParams, ActionContext, } from "@typeagent/agent-sdk"; @@ -109,8 +110,18 @@ function getMcpCommandHandlerTable( }, run: async ( context: ActionContext, - params: ParsedCommandParams<{}>, + params: ParsedCommandParams, ) => { + const serverArgs: string[] = []; + if (params.args) { + for (const value of Object.values(params.args)) { + if (Array.isArray(value)) { + serverArgs.push(...value.map(String)); + } else if (value !== undefined) { + serverArgs.push(String(value)); + } + } + } const instanceConfig: InstanceConfig = structuredClone( configs.getInstanceConfig(), ); @@ -118,11 +129,11 @@ function getMcpCommandHandlerTable( instanceConfig.mcpServers = {}; } instanceConfig.mcpServers[appAgentName] = { - serverScriptArgs: params.tokens, + serverScriptArgs: serverArgs, }; configs.setInstanceConfig(instanceConfig); context.actionIO.appendDisplay( - `Server arguments set to ${params.tokens.join(" ")}. Please restart TypeAgent to reflect the change.`, + `Server arguments set to ${serverArgs.join(" ")}. Please restart TypeAgent to reflect the change.`, ); }, }, diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index f38b5886a9..32dbb33c64 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -7,7 +7,6 @@ import { CommandDescriptor, FlagDefinitions, ParameterDefinitions, - ParsedCommandParams, CompletionGroup, CompletionGroups, SeparatorMode, @@ -47,7 +46,7 @@ function mergeSeparatorMode( // Return the full flag name if we are waiting a flag value. Add boolean values for completions and return undefined if the flag is boolean. function getPendingFlag( - params: ParsedCommandParams, + params: ParseParamsResult, flags: FlagDefinitions | undefined, completions: CompletionGroup[], ) { diff --git a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts index ddbb8600cf..266cc3f817 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/parameters.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/parameters.ts @@ -151,6 +151,12 @@ function parseValueToken( // computation. export type ParseParamsResult = ParsedCommandParams & { + // Information for partial command completion. + tokens: string[]; // The list of tokens parsed from the command. + lastCompletableParam: string | undefined; // The last parameter that was parsed that can be completed. + lastParamImplicitQuotes: boolean; // If the last parameter is implicitly quoted. + nextArgs: string[]; // A list of potential arguments next. + /** Length of the (trimmed) parameter text left unconsumed. * Excludes inter-token whitespace between the last consumed * token and the start of the unconsumed remainder. */ From 5be29a8c78a963ab13c28e5ef36b43a96a23bfde Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Tue, 10 Mar 2026 15:08:07 -0700 Subject: [PATCH 27/51] Add complete flag for exhaustive completion signaling --- .../actionGrammar/src/grammarMatcher.ts | 18 ++ .../grammarCompletionLongestMatch.spec.ts | 178 ++++++++++++++++++ ts/packages/agentSdk/src/command.ts | 6 + ts/packages/cache/src/cache/grammarStore.ts | 17 +- .../src/constructions/constructionCache.ts | 9 + .../cache/test/mergeCompletionResults.spec.ts | 107 +++++++++++ .../dispatcher/src/command/completion.ts | 9 +- .../src/translation/requestCompletion.ts | 5 +- .../dispatcher/test/completion.spec.ts | 134 ++++++++++++- 9 files changed, 472 insertions(+), 11 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index d6b425c26a..8b61a071de 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1094,6 +1094,12 @@ export type GrammarCompletionResult = { // "none" — no separator at all ([spacing=none] grammars). // Omitted when no completions were generated. separatorMode?: GrammarSeparatorMode | undefined; + // True when `completions` is the exhaustive set of valid + // continuations after the matched prefix. False or undefined + // means additional valid inputs may exist that are not listed + // (e.g. wildcard/entity slots whose values are external to the + // grammar). + complete?: boolean | undefined; }; function getGrammarCompletionProperty( @@ -1221,6 +1227,13 @@ export function matchGrammarCompletion( const properties: GrammarCompletionProperty[] = []; let separatorMode: GrammarSeparatorMode | undefined; + // Whether the accumulated completions are the exhaustive set of valid + // continuations. Starts true and is set to false when property/wildcard + // completions are emitted (entity values are external to the grammar). + // Reset to true whenever maxPrefixLength advances (old candidates are + // discarded, new batch starts fresh). + let complete: boolean = true; + // Track the furthest point the grammar consumed across all // states (including exact matches). This tells the caller where // the "filter text" begins so it doesn't have to guess from @@ -1236,6 +1249,7 @@ export function matchGrammarCompletion( completions.length = 0; properties.length = 0; separatorMode = undefined; + complete = true; } } @@ -1381,6 +1395,9 @@ export function matchGrammarCompletion( candidateNeedsSep, state.spacingMode, ); + // Property/wildcard completions are not exhaustive + // — entity values are external to the grammar. + complete = false; } } } else if (!matched) { @@ -1440,6 +1457,7 @@ export function matchGrammarCompletion( properties, matchedPrefixLength: maxPrefixLength, separatorMode, + complete, }; debugCompletion(`Completed. ${JSON.stringify(result)}`); return result; diff --git a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts index 5e686523af..b9a6551dc5 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts @@ -477,4 +477,182 @@ describe("Grammar Completion - longest match property", () => { expect(r2.matchedPrefixLength).toBe(r3.matchedPrefixLength); }); }); + + describe("complete flag - exhaustiveness", () => { + describe("string-only completions are exhaustive", () => { + const g = [ + ` = play $(g:) -> { genre: g };`, + ` = rock -> "rock";`, + ` = pop -> "pop";`, + ` = jazz -> "jazz";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete=true for first string part on empty input", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toContain("play"); + expect(result.complete).toBe(true); + }); + + it("complete=true for alternatives after prefix", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.completions).toContain("rock"); + expect(result.completions).toContain("pop"); + expect(result.completions).toContain("jazz"); + expect(result.complete).toBe(true); + }); + + it("complete=true for exact match (no completions)", () => { + const result = matchGrammarCompletion(grammar, "play rock"); + expect(result.completions).toHaveLength(0); + expect(result.complete).toBe(true); + }); + }); + + describe("wildcard/entity completions are not exhaustive", () => { + const g = [ + `entity SongName;`, + ` = play $(song:SongName) next -> { song };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete=false for entity wildcard property completion", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + expect(result.complete).toBe(false); + }); + + it("complete=true for string completion after wildcard", () => { + const result = matchGrammarCompletion(grammar, "play mysong"); + expect(result.completions).toContain("next"); + // The "next" keyword is the only valid continuation + // from the grammar — no property completions at this + // point — so completions are exhaustive. + expect(result.complete).toBe(true); + }); + }); + + describe("untyped wildcard produces property completion → not exhaustive", () => { + const g = [ + ` = play $(name) by $(artist) -> { name, artist };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete=false for untyped wildcard property", () => { + const result = matchGrammarCompletion(grammar, "play"); + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + expect(result.complete).toBe(false); + }); + + it("complete=true for 'by' keyword after wildcard captured", () => { + const result = matchGrammarCompletion(grammar, "play hello"); + expect(result.completions).toContain("by"); + expect(result.complete).toBe(true); + }); + }); + + describe("mixed string and entity at same prefix length", () => { + // Two rules: one leads to string completion, one to entity. + // After matching "play", Rule 2 offers "shuffle" (string) + // and Rule 1 opens an entity wildcard (property). + // Both are at the same prefix length, so both should be + // present and complete should be false due to the entity. + const g = [ + `entity SongName;`, + ` = play shuffle -> { action: "shuffle" };`, + ` = play $(song:SongName) -> { action: "search", song };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete=false when entity property is offered", () => { + const result = matchGrammarCompletion(grammar, "play"); + // The string rule offers "shuffle" and the entity rule + // offers a property completion. Both are at prefix=4. + expect(result.matchedPrefixLength).toBe(4); + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + // Property completions make complete=false. + expect(result.complete).toBe(false); + }); + }); + + describe("competing rules - longer match resets complete", () => { + const g = [ + `entity SongName;`, + ` = $(a:) $(song:SongName) -> { a, song };`, + ` = $(a:) $(b:) finish -> { a, b };`, + ` = alpha -> "a";`, + ` = bravo -> "b";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete reflects longest prefix length result", () => { + const result = matchGrammarCompletion(grammar, "alpha bravo"); + // Rule 2 matches alpha+bravo (11 chars) and offers "finish" + // (string → complete=true). + // Rule 1 matches alpha (5 chars) and offers entity + // (complete=false) — but this is at a shorter prefix + // length so it's discarded. + expect(result.completions).toContain("finish"); + expect(result.matchedPrefixLength).toBe(11); + expect(result.complete).toBe(true); + }); + }); + + describe("sequential parts - complete stays true throughout", () => { + const g = [ + ` = $(a:) $(b:) $(c:) -> { a, b, c };`, + ` = first -> "a";`, + ` = second -> "b";`, + ` = third -> "c";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete=true for first part", () => { + const result = matchGrammarCompletion(grammar, ""); + expect(result.completions).toContain("first"); + expect(result.complete).toBe(true); + }); + + it("complete=true for second part", () => { + const result = matchGrammarCompletion(grammar, "first"); + expect(result.completions).toContain("second"); + expect(result.complete).toBe(true); + }); + + it("complete=true for third part", () => { + const result = matchGrammarCompletion(grammar, "first second"); + expect(result.completions).toContain("third"); + expect(result.complete).toBe(true); + }); + + it("complete=true for exact full match", () => { + const result = matchGrammarCompletion( + grammar, + "first second third", + ); + expect(result.completions).toHaveLength(0); + expect(result.complete).toBe(true); + }); + }); + + describe("optional parts", () => { + const g = [ + ` = $(a:) $(b:)? $(c:) -> { a, c };`, + ` = begin -> "a";`, + ` = middle -> "b";`, + ` = finish -> "c";`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + + it("complete=true for alternatives after optional", () => { + const result = matchGrammarCompletion(grammar, "begin"); + expect(result.completions).toContain("middle"); + expect(result.completions).toContain("finish"); + expect(result.complete).toBe(true); + }); + }); + }); }); diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index cc27152309..f2fb55428f 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -88,6 +88,12 @@ export type CompletionGroups = { // the completion text. When omitted, defaults to "space" (whitespace // required before completions are shown). See SeparatorMode. separatorMode?: SeparatorMode | undefined; + // True when the completions are the exhaustive set of valid + // continuations after the matched prefix. When true and the user + // types something that doesn't prefix-match any completion, the + // caller can skip re-fetching. False or undefined means additional + // valid inputs may exist beyond what is listed. + complete?: boolean | undefined; }; export interface AppAgentCommandInterface { diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 2cba0b7199..43d0567f9c 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -297,6 +297,7 @@ export class GrammarStoreImpl implements GrammarStore { const properties: CompletionProperty[] = []; let matchedPrefixLength = 0; let separatorMode: SeparatorMode | undefined; + let complete: boolean | undefined; const filter = new Set(namespaceKeys); for (const [name, entry] of this.grammars) { if (filter && !filter.has(name)) { @@ -376,14 +377,14 @@ export class GrammarStoreImpl implements GrammarStore { requestPrefix ?? "", matchedPrefixLength, ); - const partialPrefixLength = - partial.matchedPrefixLength ?? 0; + const partialPrefixLength = partial.matchedPrefixLength ?? 0; if (partialPrefixLength > matchedPrefixLength) { // Longer prefix — discard shorter-match results. matchedPrefixLength = partialPrefixLength; completions.length = 0; properties.length = 0; separatorMode = undefined; + complete = undefined; } if (partialPrefixLength === matchedPrefixLength) { completions.push(...partial.completions); @@ -393,12 +394,19 @@ export class GrammarStoreImpl implements GrammarStore { partial.separatorMode, ); } + // AND-merge: exhaustive only when all grammar + // results at this prefix length are exhaustive. + if (partial.complete !== undefined) { + complete = + complete === undefined + ? partial.complete + : complete && partial.complete; + } if ( partial.properties !== undefined && partial.properties.length > 0 ) { - const { schemaName } = - splitSchemaNamespaceKey(name); + const { schemaName } = splitSchemaNamespaceKey(name); for (const p of partial.properties) { const action: any = p.match; properties.push({ @@ -422,6 +430,7 @@ export class GrammarStoreImpl implements GrammarStore { properties, matchedPrefixLength, separatorMode, + complete, }; } } diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index b633dca8a5..7cea790d52 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -79,6 +79,10 @@ export type CompletionResult = { // What kind of separator is required between the already-typed prefix // and the completion text. See SeparatorMode in @typeagent/agent-sdk. separatorMode?: SeparatorMode | undefined; + // True when the completions are the exhaustive set of valid + // continuations. False or undefined means additional valid inputs + // may exist beyond what is listed. + complete?: boolean | undefined; }; export function mergeCompletionResults( @@ -114,6 +118,11 @@ export function mergeCompletionResults( first.separatorMode, second.separatorMode, ), + // Exhaustive only when both sources are exhaustive. + complete: + first.complete !== undefined || second.complete !== undefined + ? (first.complete ?? false) && (second.complete ?? false) + : undefined, }; } diff --git a/ts/packages/cache/test/mergeCompletionResults.spec.ts b/ts/packages/cache/test/mergeCompletionResults.spec.ts index d80741743a..ce393f9286 100644 --- a/ts/packages/cache/test/mergeCompletionResults.spec.ts +++ b/ts/packages/cache/test/mergeCompletionResults.spec.ts @@ -238,4 +238,111 @@ describe("mergeCompletionResults", () => { expect(result!.separatorMode).toBe("spacePunctuation"); }); }); + + describe("complete merging", () => { + it("returns undefined when neither has complete", () => { + const first: CompletionResult = { completions: ["a"] }; + const second: CompletionResult = { completions: ["b"] }; + const result = mergeCompletionResults(first, second)!; + expect(result.complete).toBeUndefined(); + }); + + it("returns true only when both are true", () => { + const first: CompletionResult = { + completions: ["a"], + complete: true, + }; + const second: CompletionResult = { + completions: ["b"], + complete: true, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.complete).toBe(true); + }); + + it("returns false when first is true and second is false", () => { + const first: CompletionResult = { + completions: ["a"], + complete: true, + }; + const second: CompletionResult = { + completions: ["b"], + complete: false, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.complete).toBe(false); + }); + + it("returns false when first is false and second is true", () => { + const first: CompletionResult = { + completions: ["a"], + complete: false, + }; + const second: CompletionResult = { + completions: ["b"], + complete: true, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.complete).toBe(false); + }); + + it("returns false when both are false", () => { + const first: CompletionResult = { + completions: ["a"], + complete: false, + }; + const second: CompletionResult = { + completions: ["b"], + complete: false, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.complete).toBe(false); + }); + + it("returns false when only first has complete=true and second is undefined", () => { + const first: CompletionResult = { + completions: ["a"], + complete: true, + }; + const second: CompletionResult = { + completions: ["b"], + }; + const result = mergeCompletionResults(first, second)!; + // undefined treated as false → true && false = false + expect(result.complete).toBe(false); + }); + + it("returns false when only second has complete=true and first is undefined", () => { + const first: CompletionResult = { + completions: ["a"], + }; + const second: CompletionResult = { + completions: ["b"], + complete: true, + }; + const result = mergeCompletionResults(first, second)!; + // undefined treated as false → false && true = false + expect(result.complete).toBe(false); + }); + + it("preserves complete when first result is undefined", () => { + const second: CompletionResult = { + completions: ["b"], + complete: true, + }; + const result = mergeCompletionResults(undefined, second); + expect(result).toBe(second); + expect(result!.complete).toBe(true); + }); + + it("preserves complete when second result is undefined", () => { + const first: CompletionResult = { + completions: ["a"], + complete: false, + }; + const result = mergeCompletionResults(first, undefined); + expect(result).toBe(first); + expect(result!.complete).toBe(false); + }); + }); }); diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 32dbb33c64..32e2993669 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -204,6 +204,7 @@ async function getCommandParameterCompletion( ); let agentInvoked = false; + let agentComplete: boolean | undefined; let separatorMode: SeparatorMode | undefined; const agent = context.agents.getAppAgent(result.actualAppAgentName); if (agent.getCommandCompletion) { @@ -262,6 +263,7 @@ async function getCommandParameterCompletion( completions.push(...agentResult.groups); separatorMode = agentResult.separatorMode; agentInvoked = true; + agentComplete = agentResult.complete; debug( `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${tokenStartIndex}`, ); @@ -269,15 +271,16 @@ async function getCommandParameterCompletion( } // Determine whether the completion set is exhaustive. - // Agent-provided completions are conservatively treated as - // non-exhaustive since agents cannot yet signal exhaustiveness. + // Agent-provided completions use the agent's self-reported + // exhaustiveness (via CompletionGroups.complete), defaulting to + // false when the agent doesn't set it. // When no agent is involved, completions are exhaustive if: // - A pending boolean flag offers only ["true", "false"] // - All positional args are filled and only enumerable flags remain // Otherwise free-form text parameters make the set non-exhaustive. let complete: boolean; if (agentInvoked) { - complete = false; + complete = agentComplete ?? false; } else if (pendingFlag !== undefined) { // A non-boolean pending flag accepts free-form values. // (Boolean flags are handled by getPendingFlag returning undefined diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 78c45ad9a0..10b9bb7721 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -97,6 +97,7 @@ export async function requestCompletion( const prefixLength = results.matchedPrefixLength; const separatorMode = results.separatorMode; + const complete = results.complete; const completions: CompletionGroup[] = []; if (results.completions.length > 0) { completions.push({ @@ -108,7 +109,7 @@ export async function requestCompletion( } if (results.properties === undefined) { - return { groups: completions, prefixLength, separatorMode }; + return { groups: completions, prefixLength, separatorMode, complete }; } const propertyCompletions = new Map(); @@ -128,7 +129,7 @@ export async function requestCompletion( } completions.push(...propertyCompletions.values()); - return { groups: completions, prefixLength, separatorMode }; + return { groups: completions, prefixLength, separatorMode, complete }; } async function collectActionCompletions( diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 66309e765f..d788e5d006 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -255,6 +255,92 @@ const handlers = { return grammarCompletion(lastToken); }, }, + exhaustive: { + description: "Agent returns complete=true", + parameters: { + args: { + color: { + description: "A color", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + names: string[], + ): Promise => { + if (!names.includes("color")) { + return { groups: [] }; + } + return { + groups: [ + { + name: "Colors", + completions: ["red", "green", "blue"], + }, + ], + complete: true, + }; + }, + }, + nonexhaustive: { + description: "Agent returns complete=false", + parameters: { + args: { + item: { + description: "An item", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + names: string[], + ): Promise => { + if (!names.includes("item")) { + return { groups: [] }; + } + return { + groups: [ + { + name: "Items", + completions: ["apple", "banana"], + }, + ], + complete: false, + }; + }, + }, + nocompletefield: { + description: "Agent does not set complete", + parameters: { + args: { + thing: { + description: "A thing", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + names: string[], + ): Promise => { + if (!names.includes("thing")) { + return { groups: [] }; + } + return { + groups: [ + { + name: "Things", + completions: ["widget", "gadget"], + }, + ], + }; + }, + }, }, } as const; @@ -669,13 +755,57 @@ describe("Command Completion - startIndex", () => { expect(result!.complete).toBe(false); }); - it("complete=false when agent completions are invoked", async () => { + it("complete=false when agent completions are invoked without complete flag", async () => { const result = await getCommandCompletion( "@comptest run ", context, ); expect(result).toBeDefined(); - // Agent getCompletion is invoked → conservatively not exhaustive. + // Agent getCompletion is invoked but does not set complete → + // defaults to false. + expect(result!.complete).toBe(false); + }); + + it("complete=true when agent returns complete=true", async () => { + const result = await getCommandCompletion( + "@comptest exhaustive ", + context, + ); + expect(result).toBeDefined(); + const colors = result!.completions.find((g) => g.name === "Colors"); + expect(colors).toBeDefined(); + expect(colors!.completions).toContain("red"); + expect(colors!.completions).toContain("green"); + expect(colors!.completions).toContain("blue"); + // Agent explicitly signals exhaustive completions. + expect(result!.complete).toBe(true); + }); + + it("complete=false when agent returns complete=false", async () => { + const result = await getCommandCompletion( + "@comptest nonexhaustive ", + context, + ); + expect(result).toBeDefined(); + const items = result!.completions.find((g) => g.name === "Items"); + expect(items).toBeDefined(); + expect(items!.completions).toContain("apple"); + expect(items!.completions).toContain("banana"); + // Agent explicitly signals non-exhaustive. + expect(result!.complete).toBe(false); + }); + + it("complete=false when agent does not set complete field", async () => { + const result = await getCommandCompletion( + "@comptest nocompletefield ", + context, + ); + expect(result).toBeDefined(); + const things = result!.completions.find((g) => g.name === "Things"); + expect(things).toBeDefined(); + expect(things!.completions).toContain("widget"); + expect(things!.completions).toContain("gadget"); + // Agent omits complete → defaults to false. expect(result!.complete).toBe(false); }); From 3159471e956059a61fe4484ffea4489ee09139e6 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Tue, 10 Mar 2026 16:45:46 -0700 Subject: [PATCH 28/51] Consolidate one-word-at-a-time completion into tryPartialStringMatch tryPartialStringMatch now handles single-word parts (returns the word directly instead of undefined), so Category 2 and Category 3b both use the helper consistently with no fallback logic. --- .../actionGrammar/src/grammarMatcher.ts | 162 +++++++++++++----- .../grammarCompletionLongestMatch.spec.ts | 41 +++-- .../grammarCompletionPrefixLength.spec.ts | 45 +++-- 3 files changed, 169 insertions(+), 79 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 8b61a071de..a52a1802cf 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1207,6 +1207,60 @@ export function matchGrammar(grammar: Grammar, request: string) { * determined by the spacing rules between the last character of the * matched prefix and the first character of the completion. */ +/** + * Try to partially match leading words of a multi-word string part + * against the prefix starting at `startIndex`. Returns the consumed + * length and the remaining (unmatched) words as the completion text. + * + * - All words matched → returns undefined (caller should treat as + * a full match, not a completion candidate). + * - Some words matched → returns consumed length + next word. + * - No words matched → returns startIndex + first word. + * + * Always returns exactly one word as the completion text, + * providing one-word-at-a-time progression. + */ +function tryPartialStringMatch( + part: StringPart, + prefix: string, + startIndex: number, + spacingMode: CompiledSpacingMode, +): { consumedLength: number; remainingText: string } | undefined { + const words = part.value; + let index = startIndex; + let matchedWords = 0; + + for (const word of words) { + const escaped = escapeMatch(word); + const regExpStr = + spacingMode === "none" + ? escaped + : `[${separatorRegExpStr}]*?${escaped}`; + const re = new RegExp(regExpStr, "iuy"); + re.lastIndex = index; + const m = re.exec(prefix); + if (m === null) { + break; + } + const newIndex = m.index + m[0].length; + if (!isBoundarySatisfied(prefix, newIndex, spacingMode)) { + break; + } + index = newIndex; + matchedWords++; + } + + // No partial match found — either zero or all words matched + if (matchedWords >= words.length) { + return undefined; + } + + return { + consumedLength: index, + remainingText: words[matchedWords], + }; +} + export function matchGrammarCompletion( grammar: Grammar, prefix: string, @@ -1293,46 +1347,48 @@ export function matchGrammarCompletion( debugCompletion(`Completing ${nextPart.type} part ${state.name}`); if (nextPart.type === "string") { - updateMaxPrefixLength(state.index); - if (state.index === maxPrefixLength) { - // The next expected part is a literal keyword string. - // Offer it as a completion (e.g. "music" after "play"). - const completionText = nextPart.value.join(" "); - debugCompletion( - `Adding completion text: "${completionText}" (consumed ${state.index} chars, spacing=${state.spacingMode ?? "auto"})`, - ); + // Use tryPartialStringMatch for one-word-at-a-time + // progression through string parts. + const partial = tryPartialStringMatch( + nextPart, + prefix, + state.index, + state.spacingMode, + ); + if (partial !== undefined) { + const candidatePrefixLength = partial.consumedLength; + const completionText = partial.remainingText; + updateMaxPrefixLength(candidatePrefixLength); + if (candidatePrefixLength === maxPrefixLength) { + debugCompletion( + `Adding completion text: "${completionText}" (consumed ${candidatePrefixLength} chars, spacing=${state.spacingMode ?? "auto"})`, + ); - // Determine whether a separator (e.g. space) is needed - // between the content at matchedPrefixLength and the - // completion text. Check the boundary between the last - // consumed character and the first character of the - // completion. This is purely a property of the - // boundary — it does not account for any unmatched - // trailing content the user may have typed beyond - // matchedPrefixLength (e.g. a trailing space). - // Example: prefix="play" completion="music" → true (Latin). - // Example: prefix="play " completion="music" → true (matched - // prefix is "play"; separator still needed at that boundary). - // Example: prefix="再生" completion="音楽" → false (CJK). - let candidateNeedsSep = false; - if ( - state.index > 0 && - completionText.length > 0 && - state.spacingMode !== "none" - ) { - candidateNeedsSep = requiresSeparator( - prefix[state.index - 1], - completionText[0], + // Determine whether a separator (e.g. space) is needed + // between the content at matchedPrefixLength and the + // completion text. Check the boundary between the last + // consumed character and the first character of the + // completion. + let candidateNeedsSep = false; + if ( + candidatePrefixLength > 0 && + completionText.length > 0 && + state.spacingMode !== "none" + ) { + candidateNeedsSep = requiresSeparator( + prefix[candidatePrefixLength - 1], + completionText[0], + state.spacingMode, + ); + } + + completions.push(completionText); + separatorMode = mergeSeparatorMode( + separatorMode, + candidateNeedsSep, state.spacingMode, ); } - - completions.push(completionText); - separatorMode = mergeSeparatorMode( - separatorMode, - candidateNeedsSep, - state.spacingMode, - ); } } // Note: non-string next parts (wildcard, number, rules) in @@ -1416,11 +1472,27 @@ export function matchGrammarCompletion( currentPart !== undefined && currentPart.type === "string" ) { - updateMaxPrefixLength(state.index); - if (state.index === maxPrefixLength) { - const fullText = currentPart.value.join(" "); + // For multi-word string parts (e.g. ["play", "shuffle"]), + // the all-at-once regex may have failed even though some + // leading words DO match the prefix. Try word-by-word + // to recover the partial match and offer only the next + // unmatched word as the completion (one word at a time). + const partial = tryPartialStringMatch( + currentPart, + prefix, + state.index, + state.spacingMode, + ); + if (partial === undefined) { + continue; + } + const candidatePrefixLength = partial.consumedLength; + const completionText = partial.remainingText; + + updateMaxPrefixLength(candidatePrefixLength); + if (candidatePrefixLength === maxPrefixLength) { debugCompletion( - `Adding completion: "${fullText}" (consumed ${state.index} chars)`, + `Adding completion: "${completionText}" (consumed ${candidatePrefixLength} chars)`, ); // Determine whether a separator is needed between @@ -1429,18 +1501,18 @@ export function matchGrammarCompletion( // and the first character of the completion. let candidateNeedsSep = false; if ( - state.index > 0 && - fullText.length > 0 && + candidatePrefixLength > 0 && + completionText.length > 0 && state.spacingMode !== "none" ) { candidateNeedsSep = requiresSeparator( - prefix[state.index - 1], - fullText[0], + prefix[candidatePrefixLength - 1], + completionText[0], state.spacingMode, ); } - completions.push(fullText); + completions.push(completionText); separatorMode = mergeSeparatorMode( separatorMode, candidateNeedsSep, diff --git a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts index b9a6551dc5..9a4692ac8b 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts @@ -554,26 +554,37 @@ describe("Grammar Completion - longest match property", () => { }); describe("mixed string and entity at same prefix length", () => { - // Two rules: one leads to string completion, one to entity. - // After matching "play", Rule 2 offers "shuffle" (string) - // and Rule 1 opens an entity wildcard (property). - // Both are at the same prefix length, so both should be - // present and complete should be false due to the entity. - const g = [ - `entity SongName;`, - ` = play shuffle -> { action: "shuffle" };`, - ` = play $(song:SongName) -> { action: "search", song };`, - ].join("\n"); - const grammar = loadGrammarRules("test.grammar", g); + // Two rules sharing "play" prefix: one leads to string + // completion ("shuffle"), one to entity property. + // Both completions should be present regardless of rule order. + + it("entity rule first, string rule second", () => { + const g = [ + `entity SongName;`, + ` = play $(song:SongName) -> { action: "search", song };`, + ` = play shuffle -> { action: "shuffle" };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); + const result = matchGrammarCompletion(grammar, "play"); + expect(result.matchedPrefixLength).toBe(4); + expect(result.completions).toContain("shuffle"); + expect(result.properties).toBeDefined(); + expect(result.properties!.length).toBeGreaterThan(0); + expect(result.complete).toBe(false); + }); - it("complete=false when entity property is offered", () => { + it("string rule first, entity rule second", () => { + const g = [ + `entity SongName;`, + ` = play shuffle -> { action: "shuffle" };`, + ` = play $(song:SongName) -> { action: "search", song };`, + ].join("\n"); + const grammar = loadGrammarRules("test.grammar", g); const result = matchGrammarCompletion(grammar, "play"); - // The string rule offers "shuffle" and the entity rule - // offers a property completion. Both are at prefix=4. expect(result.matchedPrefixLength).toBe(4); + expect(result.completions).toContain("shuffle"); expect(result.properties).toBeDefined(); expect(result.properties!.length).toBeGreaterThan(0); - // Property completions make complete=false. expect(result.complete).toBe(false); }); }); diff --git a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts index ef8394f000..615d22597d 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionPrefixLength.spec.ts @@ -6,34 +6,38 @@ import { matchGrammarCompletion } from "../src/grammarMatcher.js"; describe("Grammar Completion - matchedPrefixLength", () => { describe("single string part", () => { - // All words in one string part — matchedPrefixLength is always 0 - // because the completion is the full string part text. + // All words in one string part — when no leading words match, + // matchedPrefixLength is 0 and only the first word is offered + // as a completion. When leading words match, matchedPrefixLength + // advances and only the remaining words are offered. const g = ` = play music -> true;`; const grammar = loadGrammarRules("test.grammar", g); - it("returns full part as completion for empty input", () => { + it("returns first word as completion for empty input", () => { const result = matchGrammarCompletion(grammar, ""); - expect(result.completions).toEqual(["play music"]); + expect(result.completions).toEqual(["play"]); expect(result.matchedPrefixLength).toBe(0); }); - it("returns full part as completion for partial prefix", () => { + it("returns first word as completion for partial prefix", () => { const result = matchGrammarCompletion(grammar, "pl"); - expect(result.completions).toEqual(["play music"]); + expect(result.completions).toEqual(["play"]); expect(result.matchedPrefixLength).toBe(0); }); - it("returns full part as completion for first word typed", () => { + it("returns remaining words as completion for first word typed", () => { const result = matchGrammarCompletion(grammar, "play "); - expect(result.completions).toEqual(["play music"]); - expect(result.matchedPrefixLength).toBe(0); + // tryPartialStringMatch splits the multi-word part: "play" + // is consumed (4 chars), "music" remains as the completion. + expect(result.completions).toEqual(["music"]); + expect(result.matchedPrefixLength).toBe(4); }); - it("returns all first parts for non-matching input", () => { + it("returns first word for non-matching input", () => { const result = matchGrammarCompletion(grammar, "xyz"); - // Nothing consumed; the first string part is offered - // unconditionally so the caller can filter by trailing text. - expect(result.completions).toEqual(["play music"]); + // Nothing consumed; only the first word of the string part is + // offered so the caller can filter by trailing text. + expect(result.completions).toEqual(["play"]); expect(result.matchedPrefixLength).toBe(0); }); @@ -186,20 +190,23 @@ describe("Grammar Completion - matchedPrefixLength", () => { }); describe("CJK single string part", () => { - // Single string part — matchedPrefixLength is 0 (all text in one part) + // Single string part — only the first word is offered initially. + // After the first word matches, the remaining words are offered. const g = ` [spacing=auto] = 再生 音楽 -> true;`; const grammar = loadGrammarRules("test.grammar", g); - it("returns full part for empty input", () => { + it("returns first word for empty input", () => { const result = matchGrammarCompletion(grammar, ""); - expect(result.completions).toEqual(["再生 音楽"]); + expect(result.completions).toEqual(["再生"]); expect(result.matchedPrefixLength).toBe(0); }); - it("returns full part for partial CJK prefix", () => { + it("returns remaining words for partial CJK prefix", () => { const result = matchGrammarCompletion(grammar, "再生"); - expect(result.completions).toEqual(["再生 音楽"]); - expect(result.matchedPrefixLength).toBe(0); + // tryPartialStringMatch splits the multi-word part: "再生" + // is consumed (2 chars), "音楽" remains as the completion. + expect(result.completions).toEqual(["音楽"]); + expect(result.matchedPrefixLength).toBe(2); }); }); From 6ec17470e97cf4f4ac1d1da12b92d2e90dc2b345 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Tue, 10 Mar 2026 17:23:34 -0700 Subject: [PATCH 29/51] Rename complete property to closedSet across completion pipeline Rename the 'complete' property to 'closedSet' in all type definitions, producers, consumers, and tests. The new name better captures the semantics: closedSet=true means 'if the user types something not in this list, no further completions can exist beyond it', without implying that all possible values are present. Affected types: GrammarCompletionResult, CompletionResult, CompletionGroups, CommandCompletionResult, ParameterCompletionResult. Files changed: 8 source files, 4 test files. --- .../actionGrammar/src/grammarMatcher.ts | 30 ++--- .../grammarCompletionLongestMatch.spec.ts | 66 +++++------ ts/packages/agentSdk/src/command.ts | 13 ++- ts/packages/cache/src/cache/grammarStore.ts | 20 ++-- .../src/constructions/constructionCache.ts | 17 +-- .../cache/test/mergeCompletionResults.spec.ts | 54 ++++----- .../dispatcher/src/command/completion.ts | 52 ++++----- .../src/translation/requestCompletion.ts | 6 +- .../dispatcher/test/completion.spec.ts | 108 +++++++++--------- .../dispatcher/types/src/dispatcher.ts | 11 +- .../renderer/src/partialCompletionSession.ts | 65 +++++------ .../test/partialCompletionSession.spec.ts | 46 ++++---- 12 files changed, 247 insertions(+), 241 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index a52a1802cf..a5a8ab2a8d 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -1094,12 +1094,13 @@ export type GrammarCompletionResult = { // "none" — no separator at all ([spacing=none] grammars). // Omitted when no completions were generated. separatorMode?: GrammarSeparatorMode | undefined; - // True when `completions` is the exhaustive set of valid - // continuations after the matched prefix. False or undefined - // means additional valid inputs may exist that are not listed - // (e.g. wildcard/entity slots whose values are external to the - // grammar). - complete?: boolean | undefined; + // True when `completions` is the closed set of valid + // continuations after the matched prefix — if the user types + // something not in the list, no further completions can exist + // beyond it. False or undefined means the parser can continue + // past unrecognized input and find more completions (e.g. + // wildcard/entity slots whose values are external to the grammar). + closedSet?: boolean | undefined; }; function getGrammarCompletionProperty( @@ -1281,12 +1282,13 @@ export function matchGrammarCompletion( const properties: GrammarCompletionProperty[] = []; let separatorMode: GrammarSeparatorMode | undefined; - // Whether the accumulated completions are the exhaustive set of valid - // continuations. Starts true and is set to false when property/wildcard + // Whether the accumulated completions form a closed set — if the + // user types something not listed, no further completions can exist + // beyond it. Starts true and is set to false when property/wildcard // completions are emitted (entity values are external to the grammar). // Reset to true whenever maxPrefixLength advances (old candidates are // discarded, new batch starts fresh). - let complete: boolean = true; + let closedSet: boolean = true; // Track the furthest point the grammar consumed across all // states (including exact matches). This tells the caller where @@ -1303,7 +1305,7 @@ export function matchGrammarCompletion( completions.length = 0; properties.length = 0; separatorMode = undefined; - complete = true; + closedSet = true; } } @@ -1451,9 +1453,9 @@ export function matchGrammarCompletion( candidateNeedsSep, state.spacingMode, ); - // Property/wildcard completions are not exhaustive - // — entity values are external to the grammar. - complete = false; + // Property/wildcard completions are not a closed + // set — entity values are external to the grammar. + closedSet = false; } } } else if (!matched) { @@ -1529,7 +1531,7 @@ export function matchGrammarCompletion( properties, matchedPrefixLength: maxPrefixLength, separatorMode, - complete, + closedSet, }; debugCompletion(`Completed. ${JSON.stringify(result)}`); return result; diff --git a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts index 9a4692ac8b..195f419d11 100644 --- a/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts +++ b/ts/packages/actionGrammar/test/grammarCompletionLongestMatch.spec.ts @@ -478,7 +478,7 @@ describe("Grammar Completion - longest match property", () => { }); }); - describe("complete flag - exhaustiveness", () => { + describe("closedSet flag - exhaustiveness", () => { describe("string-only completions are exhaustive", () => { const g = [ ` = play $(g:) -> { genre: g };`, @@ -488,24 +488,24 @@ describe("Grammar Completion - longest match property", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("complete=true for first string part on empty input", () => { + it("closedSet=true for first string part on empty input", () => { const result = matchGrammarCompletion(grammar, ""); expect(result.completions).toContain("play"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); - it("complete=true for alternatives after prefix", () => { + it("closedSet=true for alternatives after prefix", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.completions).toContain("rock"); expect(result.completions).toContain("pop"); expect(result.completions).toContain("jazz"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); - it("complete=true for exact match (no completions)", () => { + it("closedSet=true for exact match (no completions)", () => { const result = matchGrammarCompletion(grammar, "play rock"); expect(result.completions).toHaveLength(0); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); @@ -516,20 +516,20 @@ describe("Grammar Completion - longest match property", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("complete=false for entity wildcard property completion", () => { + it("closedSet=false for entity wildcard property completion", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.properties).toBeDefined(); expect(result.properties!.length).toBeGreaterThan(0); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); - it("complete=true for string completion after wildcard", () => { + it("closedSet=true for string completion after wildcard", () => { const result = matchGrammarCompletion(grammar, "play mysong"); expect(result.completions).toContain("next"); // The "next" keyword is the only valid continuation // from the grammar — no property completions at this // point — so completions are exhaustive. - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); @@ -539,17 +539,17 @@ describe("Grammar Completion - longest match property", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("complete=false for untyped wildcard property", () => { + it("closedSet=false for untyped wildcard property", () => { const result = matchGrammarCompletion(grammar, "play"); expect(result.properties).toBeDefined(); expect(result.properties!.length).toBeGreaterThan(0); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); - it("complete=true for 'by' keyword after wildcard captured", () => { + it("closedSet=true for 'by' keyword after wildcard captured", () => { const result = matchGrammarCompletion(grammar, "play hello"); expect(result.completions).toContain("by"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); @@ -570,7 +570,7 @@ describe("Grammar Completion - longest match property", () => { expect(result.completions).toContain("shuffle"); expect(result.properties).toBeDefined(); expect(result.properties!.length).toBeGreaterThan(0); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("string rule first, entity rule second", () => { @@ -585,11 +585,11 @@ describe("Grammar Completion - longest match property", () => { expect(result.completions).toContain("shuffle"); expect(result.properties).toBeDefined(); expect(result.properties!.length).toBeGreaterThan(0); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); }); - describe("competing rules - longer match resets complete", () => { + describe("competing rules - longer match resets closedSet", () => { const g = [ `entity SongName;`, ` = $(a:) $(song:SongName) -> { a, song };`, @@ -599,20 +599,20 @@ describe("Grammar Completion - longest match property", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("complete reflects longest prefix length result", () => { + it("closedSet reflects longest prefix length result", () => { const result = matchGrammarCompletion(grammar, "alpha bravo"); // Rule 2 matches alpha+bravo (11 chars) and offers "finish" - // (string → complete=true). + // (string → closedSet=true). // Rule 1 matches alpha (5 chars) and offers entity - // (complete=false) — but this is at a shorter prefix + // (closedSet=false) — but this is at a shorter prefix // length so it's discarded. expect(result.completions).toContain("finish"); expect(result.matchedPrefixLength).toBe(11); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); - describe("sequential parts - complete stays true throughout", () => { + describe("sequential parts - closedSet stays true throughout", () => { const g = [ ` = $(a:) $(b:) $(c:) -> { a, b, c };`, ` = first -> "a";`, @@ -621,31 +621,31 @@ describe("Grammar Completion - longest match property", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("complete=true for first part", () => { + it("closedSet=true for first part", () => { const result = matchGrammarCompletion(grammar, ""); expect(result.completions).toContain("first"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); - it("complete=true for second part", () => { + it("closedSet=true for second part", () => { const result = matchGrammarCompletion(grammar, "first"); expect(result.completions).toContain("second"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); - it("complete=true for third part", () => { + it("closedSet=true for third part", () => { const result = matchGrammarCompletion(grammar, "first second"); expect(result.completions).toContain("third"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); - it("complete=true for exact full match", () => { + it("closedSet=true for exact full match", () => { const result = matchGrammarCompletion( grammar, "first second third", ); expect(result.completions).toHaveLength(0); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); @@ -658,11 +658,11 @@ describe("Grammar Completion - longest match property", () => { ].join("\n"); const grammar = loadGrammarRules("test.grammar", g); - it("complete=true for alternatives after optional", () => { + it("closedSet=true for alternatives after optional", () => { const result = matchGrammarCompletion(grammar, "begin"); expect(result.completions).toContain("middle"); expect(result.completions).toContain("finish"); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); }); diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index f2fb55428f..5181ac9bae 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -88,12 +88,13 @@ export type CompletionGroups = { // the completion text. When omitted, defaults to "space" (whitespace // required before completions are shown). See SeparatorMode. separatorMode?: SeparatorMode | undefined; - // True when the completions are the exhaustive set of valid - // continuations after the matched prefix. When true and the user - // types something that doesn't prefix-match any completion, the - // caller can skip re-fetching. False or undefined means additional - // valid inputs may exist beyond what is listed. - complete?: boolean | undefined; + // True when the completions form a closed set — if the user types + // something not in the list, no further completions can exist + // beyond it. When true and the user types something that doesn't + // prefix-match any completion, the caller can skip re-fetching. + // False or undefined means the parser can continue past + // unrecognized input and find more completions. + closedSet?: boolean | undefined; }; export interface AppAgentCommandInterface { diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 43d0567f9c..2d47016601 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -297,7 +297,7 @@ export class GrammarStoreImpl implements GrammarStore { const properties: CompletionProperty[] = []; let matchedPrefixLength = 0; let separatorMode: SeparatorMode | undefined; - let complete: boolean | undefined; + let closedSet: boolean | undefined; const filter = new Set(namespaceKeys); for (const [name, entry] of this.grammars) { if (filter && !filter.has(name)) { @@ -384,7 +384,7 @@ export class GrammarStoreImpl implements GrammarStore { completions.length = 0; properties.length = 0; separatorMode = undefined; - complete = undefined; + closedSet = undefined; } if (partialPrefixLength === matchedPrefixLength) { completions.push(...partial.completions); @@ -394,13 +394,13 @@ export class GrammarStoreImpl implements GrammarStore { partial.separatorMode, ); } - // AND-merge: exhaustive only when all grammar - // results at this prefix length are exhaustive. - if (partial.complete !== undefined) { - complete = - complete === undefined - ? partial.complete - : complete && partial.complete; + // AND-merge: closed set only when all grammar + // results at this prefix length are closed sets. + if (partial.closedSet !== undefined) { + closedSet = + closedSet === undefined + ? partial.closedSet + : closedSet && partial.closedSet; } if ( partial.properties !== undefined && @@ -430,7 +430,7 @@ export class GrammarStoreImpl implements GrammarStore { properties, matchedPrefixLength, separatorMode, - complete, + closedSet, }; } } diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index 7cea790d52..a9850a6f5d 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -79,10 +79,11 @@ export type CompletionResult = { // What kind of separator is required between the already-typed prefix // and the completion text. See SeparatorMode in @typeagent/agent-sdk. separatorMode?: SeparatorMode | undefined; - // True when the completions are the exhaustive set of valid - // continuations. False or undefined means additional valid inputs - // may exist beyond what is listed. - complete?: boolean | undefined; + // True when the completions form a closed set — if the user types + // something not in the list, no further completions can exist + // beyond it. False or undefined means the parser can continue + // past unrecognized input and find more completions. + closedSet?: boolean | undefined; }; export function mergeCompletionResults( @@ -118,10 +119,10 @@ export function mergeCompletionResults( first.separatorMode, second.separatorMode, ), - // Exhaustive only when both sources are exhaustive. - complete: - first.complete !== undefined || second.complete !== undefined - ? (first.complete ?? false) && (second.complete ?? false) + // Closed set only when both sources are closed sets. + closedSet: + first.closedSet !== undefined || second.closedSet !== undefined + ? (first.closedSet ?? false) && (second.closedSet ?? false) : undefined, }; } diff --git a/ts/packages/cache/test/mergeCompletionResults.spec.ts b/ts/packages/cache/test/mergeCompletionResults.spec.ts index ce393f9286..c747340c4b 100644 --- a/ts/packages/cache/test/mergeCompletionResults.spec.ts +++ b/ts/packages/cache/test/mergeCompletionResults.spec.ts @@ -239,110 +239,110 @@ describe("mergeCompletionResults", () => { }); }); - describe("complete merging", () => { - it("returns undefined when neither has complete", () => { + describe("closedSet merging", () => { + it("returns undefined when neither has closedSet", () => { const first: CompletionResult = { completions: ["a"] }; const second: CompletionResult = { completions: ["b"] }; const result = mergeCompletionResults(first, second)!; - expect(result.complete).toBeUndefined(); + expect(result.closedSet).toBeUndefined(); }); it("returns true only when both are true", () => { const first: CompletionResult = { completions: ["a"], - complete: true, + closedSet: true, }; const second: CompletionResult = { completions: ["b"], - complete: true, + closedSet: true, }; const result = mergeCompletionResults(first, second)!; - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); it("returns false when first is true and second is false", () => { const first: CompletionResult = { completions: ["a"], - complete: true, + closedSet: true, }; const second: CompletionResult = { completions: ["b"], - complete: false, + closedSet: false, }; const result = mergeCompletionResults(first, second)!; - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("returns false when first is false and second is true", () => { const first: CompletionResult = { completions: ["a"], - complete: false, + closedSet: false, }; const second: CompletionResult = { completions: ["b"], - complete: true, + closedSet: true, }; const result = mergeCompletionResults(first, second)!; - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("returns false when both are false", () => { const first: CompletionResult = { completions: ["a"], - complete: false, + closedSet: false, }; const second: CompletionResult = { completions: ["b"], - complete: false, + closedSet: false, }; const result = mergeCompletionResults(first, second)!; - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); - it("returns false when only first has complete=true and second is undefined", () => { + it("returns false when only first has closedSet=true and second is undefined", () => { const first: CompletionResult = { completions: ["a"], - complete: true, + closedSet: true, }; const second: CompletionResult = { completions: ["b"], }; const result = mergeCompletionResults(first, second)!; // undefined treated as false → true && false = false - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); - it("returns false when only second has complete=true and first is undefined", () => { + it("returns false when only second has closedSet=true and first is undefined", () => { const first: CompletionResult = { completions: ["a"], }; const second: CompletionResult = { completions: ["b"], - complete: true, + closedSet: true, }; const result = mergeCompletionResults(first, second)!; // undefined treated as false → false && true = false - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); - it("preserves complete when first result is undefined", () => { + it("preserves closedSet when first result is undefined", () => { const second: CompletionResult = { completions: ["b"], - complete: true, + closedSet: true, }; const result = mergeCompletionResults(undefined, second); expect(result).toBe(second); - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); }); - it("preserves complete when second result is undefined", () => { + it("preserves closedSet when second result is undefined", () => { const first: CompletionResult = { completions: ["a"], - complete: false, + closedSet: false, }; const result = mergeCompletionResults(first, undefined); expect(result).toBe(first); - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); }); }); diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 32e2993669..5c5bf77374 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -133,9 +133,9 @@ function collectFlags( // Internal result from parameter-level completion. // -// `complete` uses a conservative heuristic: +// `closedSet` uses a conservative heuristic: // - false when the agent's getCommandCompletion was invoked (agents -// cannot yet signal whether their set is exhaustive). +// cannot yet signal whether their set is closed). // - false when a pending non-boolean flag accepts free-form input. // - true when all positional args are filled and only enumerable // flag names remain (a finite, known set). @@ -143,7 +143,7 @@ type ParameterCompletionResult = { completions: CompletionGroup[]; startIndex: number; separatorMode: SeparatorMode | undefined; - complete: boolean; + closedSet: boolean; }; // Complete parameter values and flags for an already-resolved command @@ -204,7 +204,7 @@ async function getCommandParameterCompletion( ); let agentInvoked = false; - let agentComplete: boolean | undefined; + let agentClosedSet: boolean | undefined; let separatorMode: SeparatorMode | undefined; const agent = context.agents.getAppAgent(result.actualAppAgentName); if (agent.getCommandCompletion) { @@ -263,37 +263,37 @@ async function getCommandParameterCompletion( completions.push(...agentResult.groups); separatorMode = agentResult.separatorMode; agentInvoked = true; - agentComplete = agentResult.complete; + agentClosedSet = agentResult.closedSet; debug( `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${tokenStartIndex}`, ); } } - // Determine whether the completion set is exhaustive. + // Determine whether the completion set is a closed set. // Agent-provided completions use the agent's self-reported - // exhaustiveness (via CompletionGroups.complete), defaulting to + // closedSet flag (via CompletionGroups.closedSet), defaulting to // false when the agent doesn't set it. - // When no agent is involved, completions are exhaustive if: + // When no agent is involved, the set is closed if: // - A pending boolean flag offers only ["true", "false"] // - All positional args are filled and only enumerable flags remain - // Otherwise free-form text parameters make the set non-exhaustive. - let complete: boolean; + // Otherwise free-form text parameters make the set open. + let closedSet: boolean; if (agentInvoked) { - complete = agentComplete ?? false; + closedSet = agentClosedSet ?? false; } else if (pendingFlag !== undefined) { // A non-boolean pending flag accepts free-form values. // (Boolean flags are handled by getPendingFlag returning undefined // and pushing ["true", "false"] into completions directly.) - complete = false; + closedSet = false; } else { - // No agent, no pending flag. Exhaustive when there are no + // No agent, no pending flag. Closed set when there are no // unfilled positional args (nextArgs is empty) and only // flags remain — flag names are a finite, known set. - complete = params.nextArgs.length === 0; + closedSet = params.nextArgs.length === 0; } - return { completions, startIndex, separatorMode, complete }; + return { completions, startIndex, separatorMode, closedSet }; } // @@ -304,7 +304,7 @@ async function getCommandParameterCompletion( // // Always returns a result — every input has a longest valid prefix // (at minimum the empty string, startIndex=0). An empty completions -// array with complete=true means the command is fully specified and +// array with closedSet=true means the command is fully specified and // nothing further can follow. // // The portion of input after the prefix (the "suffix") is NOT a gate @@ -356,13 +356,13 @@ async function getCommandParameterCompletion( // Merged: most restrictive mode from any source wins. // When omitted, consumers default to "space". // -// complete true when the returned completions are the *exhaustive* -// set of valid continuations after the prefix. When true +// closedSet true when the returned completions form a *closed set* +// of valid continuations after the prefix. When true // and the user types something that doesn't prefix-match // any completion, the caller can skip re-fetching because // no other valid input exists. Subcommand and agent-name -// lists are always exhaustive. Parameter completions are -// exhaustive only when no agent was invoked and no +// lists are always closed sets. Parameter completions are +// closed only when no agent was invoked and no // free-form positional args remain unfilled — see // ParameterCompletionResult for the heuristic. // @@ -401,7 +401,7 @@ export async function getCommandCompletion( } const descriptor = result.descriptor; - let complete = true; + let closedSet = true; if (descriptor !== undefined) { // Get parameter completions first — we need to know // whether parameters consumed past the command boundary @@ -447,7 +447,7 @@ export async function getCommandCompletion( separatorMode, parameterCompletions.separatorMode, ); - complete = parameterCompletions.complete; + closedSet = parameterCompletions.closedSet; } } else if (table !== undefined) { // descriptor is undefined: the suffix didn't resolve to any @@ -477,7 +477,7 @@ export async function getCommandCompletion( } else { // Both table and descriptor are undefined — the agent // returned no commands at all. Nothing to add; - // completions stays empty, complete stays true. + // completions stays empty, closedSet stays true. } // Independently of which branch above ran, offer agent names @@ -502,20 +502,20 @@ export async function getCommandCompletion( startIndex, completions, separatorMode, - complete, + closedSet, }; debug(`Command completion result:`, completionResult); return completionResult; } catch (e: any) { debugError(`Command completion error: ${e}\n${e.stack}`); - // On error, return a safe default — don't claim exhaustiveness + // On error, return a safe default — don't claim closedSet // since we don't know what went wrong. return { startIndex: 0, completions: [], separatorMode: undefined, - complete: false, + closedSet: false, }; } } diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 10b9bb7721..765a3c69b6 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -97,7 +97,7 @@ export async function requestCompletion( const prefixLength = results.matchedPrefixLength; const separatorMode = results.separatorMode; - const complete = results.complete; + const closedSet = results.closedSet; const completions: CompletionGroup[] = []; if (results.completions.length > 0) { completions.push({ @@ -109,7 +109,7 @@ export async function requestCompletion( } if (results.properties === undefined) { - return { groups: completions, prefixLength, separatorMode, complete }; + return { groups: completions, prefixLength, separatorMode, closedSet }; } const propertyCompletions = new Map(); @@ -129,7 +129,7 @@ export async function requestCompletion( } completions.push(...propertyCompletions.values()); - return { groups: completions, prefixLength, separatorMode, complete }; + return { groups: completions, prefixLength, separatorMode, closedSet }; } async function collectActionCompletions( diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index d788e5d006..733d310e24 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -256,7 +256,7 @@ const handlers = { }, }, exhaustive: { - description: "Agent returns complete=true", + description: "Agent returns closedSet=true", parameters: { args: { color: { @@ -280,12 +280,12 @@ const handlers = { completions: ["red", "green", "blue"], }, ], - complete: true, + closedSet: true, }; }, }, nonexhaustive: { - description: "Agent returns complete=false", + description: "Agent returns closedSet=false", parameters: { args: { item: { @@ -309,12 +309,12 @@ const handlers = { completions: ["apple", "banana"], }, ], - complete: false, + closedSet: false, }; }, }, nocompletefield: { - description: "Agent does not set complete", + description: "Agent does not set closedSet", parameters: { args: { thing: { @@ -454,7 +454,7 @@ describe("Command Completion - startIndex", () => { expect(result!.startIndex).toBe(13); // Agent getCompletion is invoked for the "task" arg → // completions are not exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); it("returns startIndex accounting for partial param for '@comptest run bu'", async () => { @@ -471,7 +471,7 @@ describe("Command Completion - startIndex", () => { // "bu" consumes the "task" arg → nextArgs is empty. // Agent is not invoked (bare word, no implicit quotes). // Only flags remain (none defined) → exhaustive. - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); }); it("returns startIndex for nested command '@comptest nested sub '", async () => { @@ -486,7 +486,7 @@ describe("Command Completion - startIndex", () => { // then unconditional whitespace backing → 20. expect(result!.startIndex).toBe(20); // Unfilled "value" arg (free-form) → not exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); it("returns startIndex for partial flag '@comptest nested sub --ver'", async () => { @@ -501,7 +501,7 @@ describe("Command Completion - startIndex", () => { // backing rewinds over the space before "--ver" → 20. expect(result!.startIndex).toBe(20); // Unfilled "value" arg → not exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); }); @@ -518,7 +518,7 @@ describe("Command Completion - startIndex", () => { expect(prefixes!.completions).toContain("@"); // Empty input normalizes to "{requestHandler} request" which // has open parameters → not exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); it("returns startIndex 0 for empty input", async () => { @@ -548,7 +548,7 @@ describe("Command Completion - startIndex", () => { expect(subcommands!.completions).toContain("run"); expect(subcommands!.completions).toContain("nested"); // Default subcommand "run" has agent completions → not exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); // Agent was recognized → no agent names offered. const agentGroup = result!.completions.find( (g) => g.name === "Agent Names", @@ -573,7 +573,7 @@ describe("Command Completion - startIndex", () => { (g) => g.name === "Subcommands", ); expect(subcommandGroup).toBeDefined(); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); it("returns completions for unknown agent with startIndex at '@'", async () => { @@ -593,7 +593,7 @@ describe("Command Completion - startIndex", () => { (g) => g.name === "Subcommands", ); expect(subcommandGroup).toBeDefined(); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); @@ -612,7 +612,7 @@ describe("Command Completion - startIndex", () => { // All positional args filled ("task" consumed "build"), // no flags, agent not invoked (agentCommandCompletions // is empty) → exhaustive. - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); }); it("startIndex backs over whitespace before unconsumed remainder", async () => { @@ -664,7 +664,7 @@ describe("Command Completion - startIndex", () => { // No trailing whitespace to trim — startIndex stays at end expect(result!.startIndex).toBe(9); // Default subcommand has agent completions → not exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); it("does not set separatorMode at top level (@)", async () => { @@ -682,7 +682,7 @@ describe("Command Completion - startIndex", () => { expect(agentGroup).toBeDefined(); expect(agentGroup!.completions).toContain("comptest"); // Subcommand + agent name sets are finite → exhaustive. - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); }); it("does not set separatorMode for parameter completions only", async () => { @@ -712,7 +712,7 @@ describe("Command Completion - startIndex", () => { }); }); - describe("complete flag", () => { + describe("closedSet flag", () => { it("returns empty completions for command with no parameters", async () => { const result = await getCommandCompletion( "@comptest noop ", @@ -721,12 +721,12 @@ describe("Command Completion - startIndex", () => { // "noop" has no parameters at all → nothing more to type. // getCommandParameterCompletion returns undefined and // no subcommand alternatives exist (explicit match) → - // empty completions with complete=true. + // empty completions with closedSet=true. expect(result.completions).toHaveLength(0); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); - it("complete=true for flags-only command with no args unfilled", async () => { + it("closedSet=true for flags-only command with no args unfilled", async () => { const result = await getCommandCompletion( "@comptest flagsonly ", context, @@ -734,7 +734,7 @@ describe("Command Completion - startIndex", () => { expect(result).toBeDefined(); // No positional args, only flags. nextArgs is empty. // No agent getCompletion. Flags are a finite set → exhaustive. - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); const flags = result!.completions.find( (g) => g.name === "Command Flags", ); @@ -743,7 +743,7 @@ describe("Command Completion - startIndex", () => { expect(flags!.completions).toContain("--level"); }); - it("complete=true for boolean flag pending", async () => { + it("closedSet=true for boolean flag pending", async () => { const result = await getCommandCompletion( "@comptest nested sub --verbose ", context, @@ -751,22 +751,22 @@ describe("Command Completion - startIndex", () => { expect(result).toBeDefined(); // --verbose is boolean; getPendingFlag pushed ["true", "false"] // and returned undefined (not a pending non-boolean flag). - // nextArgs still has "value" unfilled → complete = false. - expect(result!.complete).toBe(false); + // nextArgs still has "value" unfilled → closedSet = false. + expect(result!.closedSet).toBe(false); }); - it("complete=false when agent completions are invoked without complete flag", async () => { + it("closedSet=false when agent completions are invoked without closedSet flag", async () => { const result = await getCommandCompletion( "@comptest run ", context, ); expect(result).toBeDefined(); - // Agent getCompletion is invoked but does not set complete → + // Agent getCompletion is invoked but does not set closedSet → // defaults to false. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); - it("complete=true when agent returns complete=true", async () => { + it("closedSet=true when agent returns closedSet=true", async () => { const result = await getCommandCompletion( "@comptest exhaustive ", context, @@ -778,10 +778,10 @@ describe("Command Completion - startIndex", () => { expect(colors!.completions).toContain("green"); expect(colors!.completions).toContain("blue"); // Agent explicitly signals exhaustive completions. - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); }); - it("complete=false when agent returns complete=false", async () => { + it("closedSet=false when agent returns closedSet=false", async () => { const result = await getCommandCompletion( "@comptest nonexhaustive ", context, @@ -792,10 +792,10 @@ describe("Command Completion - startIndex", () => { expect(items!.completions).toContain("apple"); expect(items!.completions).toContain("banana"); // Agent explicitly signals non-exhaustive. - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); - it("complete=false when agent does not set complete field", async () => { + it("closedSet=false when agent does not set closedSet field", async () => { const result = await getCommandCompletion( "@comptest nocompletefield ", context, @@ -805,11 +805,11 @@ describe("Command Completion - startIndex", () => { expect(things).toBeDefined(); expect(things!.completions).toContain("widget"); expect(things!.completions).toContain("gadget"); - // Agent omits complete → defaults to false. - expect(result!.complete).toBe(false); + // Agent omits closedSet → defaults to false. + expect(result!.closedSet).toBe(false); }); - it("complete=false for unfilled positional args without agent", async () => { + it("closedSet=false for unfilled positional args without agent", async () => { const result = await getCommandCompletion( "@comptest nested sub ", context, @@ -817,17 +817,17 @@ describe("Command Completion - startIndex", () => { expect(result).toBeDefined(); // "value" arg is unfilled, no agent getCompletion → not exhaustive // (free-form text). - expect(result!.complete).toBe(false); + expect(result!.closedSet).toBe(false); }); - it("complete=true for flags-only after one flag is set", async () => { + it("closedSet=true for flags-only after one flag is set", async () => { const result = await getCommandCompletion( "@comptest flagsonly --debug true ", context, ); expect(result).toBeDefined(); // --debug is consumed; only --level remains. Still a finite set. - expect(result!.complete).toBe(true); + expect(result!.closedSet).toBe(true); }); it("returns flag value completions for non-boolean flag without trailing space", async () => { @@ -879,7 +879,7 @@ describe("Command Completion - startIndex", () => { expect(flags).toBeDefined(); expect(flags!.completions).toContain("--release"); // Unfilled "target" arg → not exhaustive. - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("returns correct startIndex for flat agent with partial token", async () => { @@ -891,7 +891,7 @@ describe("Command Completion - startIndex", () => { // startIndex = 15 - 5 ("--rel") = 10, then unconditional // whitespace backing rewinds over space → 9. expect(result.startIndex).toBe(9); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("falls back to system for agent with no commands", async () => { @@ -933,7 +933,7 @@ describe("Command Completion - startIndex", () => { expect(subcommands).toBeUndefined(); expect(result.startIndex).toBe(15); // All positional args filled, no flags → exhaustive. - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); it("keeps subcommands when at the command boundary", async () => { @@ -975,7 +975,7 @@ describe("Command Completion - startIndex", () => { // whitespace backing rewinds over the space before '"bu' → 13. expect(result.startIndex).toBe(13); // Agent was invoked → not exhaustive. - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); // Flag groups and nextArgs completions should be cleared. const flags = result.completions.find( (g) => g.name === "Command Flags", @@ -996,7 +996,7 @@ describe("Command Completion - startIndex", () => { // whitespace backing rewinds over the space → 16. // "second" from nextArgs should NOT be in agentCommandCompletions. expect(result.startIndex).toBe(16); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("backs startIndex for implicitQuotes '@comptest search hello world'", async () => { @@ -1011,7 +1011,7 @@ describe("Command Completion - startIndex", () => { // Exclusive path: startIndex = 28 - 11 = 17, then unconditional // whitespace backing rewinds over the space → 16. expect(result.startIndex).toBe(16); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("does not adjust startIndex for fully-quoted token", async () => { @@ -1026,7 +1026,7 @@ describe("Command Completion - startIndex", () => { // whitespace backing finds no space at input[20]='"' → 21. // "task" is filled, no flags → exhaustive. expect(result.startIndex).toBe(21); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); it("does not adjust startIndex for bare unquoted token", async () => { @@ -1039,7 +1039,7 @@ describe("Command Completion - startIndex", () => { // lastCompletableParam condition does NOT fire. // startIndex stays at 16 (end of input). expect(result.startIndex).toBe(16); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); }); }); @@ -1067,7 +1067,7 @@ describe("Command Completion - startIndex", () => { expect(grammar).toBeDefined(); expect(grammar!.completions).toContain("タワー"); expect(grammar!.completions).toContain("駅"); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("implicitQuotes CJK advances startIndex by prefixLength", async () => { @@ -1093,7 +1093,7 @@ describe("Command Completion - startIndex", () => { expect(grammar).toBeDefined(); expect(grammar!.completions).toContain("タワー"); expect(grammar!.completions).toContain("駅"); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("fully-quoted token does not invoke grammar", async () => { @@ -1106,7 +1106,7 @@ describe("Command Completion - startIndex", () => { // All args consumed → nextArgs empty → agent not called. // prefixLength never applies. expect(result.startIndex).toBe(23); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); // No Grammar group since agent wasn't invoked. const grammar = result.completions.find( (g) => g.name === "Grammar", @@ -1123,7 +1123,7 @@ describe("Command Completion - startIndex", () => { // implicitQuotes → lastCompletableParam condition false. // All args consumed → nextArgs empty → agent not called. expect(result.startIndex).toBe(21); - expect(result.complete).toBe(true); + expect(result.closedSet).toBe(true); const grammar = result.completions.find( (g) => g.name === "Grammar", ); @@ -1142,7 +1142,7 @@ describe("Command Completion - startIndex", () => { // groupPrefixLength path does not fire. // startIndex = tokenBoundary(input, 18) = 17. expect(result.startIndex).toBe(17); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); const grammar = result.completions.find( (g) => g.name === "Grammar", ); @@ -1196,7 +1196,7 @@ describe("Command Completion - startIndex", () => { expect(grammar).toBeDefined(); expect(grammar!.completions).toContain("Tower"); expect(grammar!.completions).toContain("Station"); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("completed CJK match returns no completions", async () => { @@ -1213,7 +1213,7 @@ describe("Command Completion - startIndex", () => { (g) => g.name === "Grammar", ); expect(grammar).toBeUndefined(); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); it("no-text offers initial completions via grammariq", async () => { @@ -1235,7 +1235,7 @@ describe("Command Completion - startIndex", () => { expect(grammar).toBeDefined(); expect(grammar!.completions).toContain("Tokyo "); expect(grammar!.completions).toContain("東京"); - expect(result.complete).toBe(false); + expect(result.closedSet).toBe(false); }); }); }); diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index eafabb9f86..a7c5e82eb0 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -79,11 +79,12 @@ export type CommandCompletionResult = { // the completion text. When omitted, defaults to "space". // See SeparatorMode in @typeagent/agent-sdk. separatorMode?: SeparatorMode | undefined; - // True when the completions listed are the exhaustive set of valid - // continuations after the prefix. When true and the user types - // something that doesn't prefix-match any completion, the caller - // can skip refetching since no other valid input exists. - complete: boolean; + // True when the completions form a closed set — if the user types + // something not in the list, no further completions can exist + // beyond it. When true and the user types something that doesn't + // prefix-match any completion, the caller can skip refetching since + // no other valid input exists. + closedSet: boolean; }; export type AppAgentStatus = { diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 421f9b1ea7..4abe953be7 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -33,7 +33,7 @@ export interface ICompletionDispatcher { // ACTIVE current !== undefined && completionP === undefined // // Design principles: -// - Completion result fields (separatorMode, complete) are stored as-is +// - Completion result fields (separatorMode, closedSet) are stored as-is // from the backend response and never mutated as the user keeps typing. // reuseSession() reads them to decide whether to show, hide, or re-fetch. // - reuseSession() makes exactly four kinds of decisions: @@ -46,12 +46,12 @@ export interface ICompletionDispatcher { // 4. Uniquely satisfied — the user has exactly typed one completion // entry (and it is not a prefix of any other). Always re-fetch to // get the NEXT level's completions (e.g. agent name → subcommands). -// This re-fetch is unconditional: `complete` is irrelevant here -// because it describes THIS level's exhaustiveness, not the next's. -// - The `complete` flag controls the no-match fallthrough: when the trie +// This re-fetch is unconditional: `closedSet` is irrelevant here +// because it describes THIS level, not the next's. +// - The `closedSet` flag controls the no-match fallthrough: when the trie // has zero matches for the typed prefix: -// complete=true → reuse (exhaustive set, nothing else exists) -// complete=false → re-fetch (set is partial, backend may know more) +// closedSet=true → reuse (closed set, nothing else exists) +// closedSet=false → re-fetch (set is open, backend may know more) // - The anchor (`current`) is never advanced after a result is received. // When `separatorMode` requires a separator, the separator is stripped // from the raw prefix before being passed to the menu, so the trie @@ -71,18 +71,19 @@ export class PartialCompletionSession { // the raw prefix without mutating `current`. private separatorMode: SeparatorMode = "space"; - // When true, the completion set returned by the backend is exhaustive - // for THIS level of the command hierarchy. This affects one decision - // in reuseSession(): + // When true, the completion set returned by the backend is a closed + // set for THIS level of the command hierarchy — if the user types + // something not in the list, no further completions can exist beyond + // it. This affects one decision in reuseSession(): // - // No trie matches: complete=true → reuse (nothing else exists, no - // point re-fetching); complete=false → re-fetch (the backend may + // No trie matches: closedSet=true → reuse (nothing else exists, no + // point re-fetching); closedSet=false → re-fetch (the backend may // know about completions we haven't loaded). // - // Notably, `complete` does NOT suppress the uniquelySatisfied re-fetch. + // Notably, `closedSet` does NOT suppress the uniquelySatisfied re-fetch. // uniquelySatisfied means the user needs the NEXT level's completions, - // which is a different question from whether THIS level is exhaustive. - private complete: boolean = false; + // which is a different question from whether THIS level is a closed set. + private closedSet: boolean = false; // The in-flight completion request, or undefined when settled. private completionP: @@ -114,7 +115,7 @@ export class PartialCompletionSession { this.completionP = undefined; this.current = undefined; this.separatorMode = "space"; - this.complete = false; + this.closedSet = false; this.cancelMenu(); } @@ -122,7 +123,7 @@ export class PartialCompletionSession { public resetToIdle(): void { this.current = undefined; this.separatorMode = "space"; - this.complete = false; + this.closedSet = false; } // Returns the text typed after the anchor (`current`), or undefined when @@ -163,12 +164,12 @@ export class PartialCompletionSession { // (return true). // UNIQUE — prefix exactly matches one entry and is not a prefix of // any other; re-fetch for the NEXT level (return false). - // Unconditional — `complete` is irrelevant here. + // Unconditional — `closedSet` is irrelevant here. // SHOW — constraints satisfied; update the menu. The final - // return is `this.complete || this.menu.isActive()`: + // return is `this.closedSet || this.menu.isActive()`: // reuse when the trie still has matches, or when the set - // is exhaustive (nothing new to fetch). Re-fetch only - // when the trie is empty AND the set is non-exhaustive. + // is closed (nothing new to fetch). Re-fetch only + // when the trie is empty AND the set is open. private reuseSession( input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, @@ -245,13 +246,13 @@ export class PartialCompletionSession { // When the menu is still active (trie has matches) we always // reuse — the loaded completions are still useful. When there are - // NO matches, the decision depends on `complete`: - // complete=true → the set is exhaustive; the user typed past all - // valid continuations, so re-fetching won't help. - // complete=false → the set is NOT exhaustive; the user may have - // typed something valid that wasn't loaded, so - // re-fetch with the longer input. - return this.complete || this.menu.isActive(); + // NO matches, the decision depends on `closedSet`: + // closedSet=true → the set is closed; the user typed past all + // valid continuations, so re-fetching won't help. + // closedSet=false → the set is NOT closed; the user may have + // typed something valid that wasn't loaded, so + // re-fetch with the longer input. + return this.closedSet || this.menu.isActive(); } // Start a new completion session: issue backend request and process result. @@ -263,7 +264,7 @@ export class PartialCompletionSession { this.cancelMenu(); this.current = input; this.separatorMode = "space"; - this.complete = false; + this.closedSet = false; this.menu.setChoices([]); const completionP = this.dispatcher.getCommandCompletion(input); this.completionP = completionP; @@ -278,7 +279,7 @@ export class PartialCompletionSession { debug(`Partial completion result: `, result); this.separatorMode = result.separatorMode ?? "space"; - this.complete = result.complete; + this.closedSet = result.closedSet; // Build completions preserving backend group order. const completions: SearchMenuItem[] = []; @@ -308,9 +309,9 @@ export class PartialCompletionSession { ); // Keep this.current at the full input so the anchor // covers the entire typed text. The menu stays empty, - // so reuseSession()'s SHOW path will use `complete` to - // decide: complete=true → reuse (nothing more exists); - // complete=false → re-fetch when new input arrives. + // so reuseSession()'s SHOW path will use `closedSet` to + // decide: closedSet=true → reuse (nothing more exists); + // closedSet=false → re-fetch when new input arrives. // // Override separatorMode: with no completions, there is // nothing to separate from, so the separator check in diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index 07fb2aa97a..42c4d0cc4e 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -42,7 +42,7 @@ function makeDispatcher( startIndex: 0, completions: [], separatorMode: undefined, - complete: true, + closedSet: true, }, ): MockDispatcher { return { @@ -61,7 +61,7 @@ function makeCompletionResult( opts: Partial = {}, ): CommandCompletionResult { const group: CompletionGroup = { name: "test", completions }; - return { startIndex, completions: [group], complete: false, ...opts }; + return { startIndex, completions: [group], closedSet: false, ...opts }; } // ── State machine tests ─────────────────────────────────────────────────────── @@ -113,7 +113,7 @@ describe("PartialCompletionSession — state transitions", () => { expect(menu.updatePrefix).toHaveBeenCalled(); }); - test("PENDING → ACTIVE: empty result (complete=true) suppresses re-fetch while input has same prefix", async () => { + test("PENDING → ACTIVE: empty result (closedSet=true) suppresses re-fetch while input has same prefix", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); @@ -121,7 +121,7 @@ describe("PartialCompletionSession — state transitions", () => { session.update("play", getPos); await Promise.resolve(); - // Empty completions + complete=true — no new fetch even with extended input + // Empty completions + closedSet=true — no new fetch even with extended input session.update("play s", getPos); expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); @@ -133,7 +133,7 @@ describe("PartialCompletionSession — state transitions", () => { const session = new PartialCompletionSession(menu, dispatcher); session.update("play", getPos); - await Promise.resolve(); // → ACTIVE (empty, complete=true) with current="play" + await Promise.resolve(); // → ACTIVE (empty, closedSet=true) with current="play" // Backspace past anchor session.update("pla", getPos); @@ -142,11 +142,11 @@ describe("PartialCompletionSession — state transitions", () => { expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("pla"); }); - test("ACTIVE → hide+keep: complete=true, trie has no matches — no re-fetch", async () => { + test("ACTIVE → hide+keep: closedSet=true, trie has no matches — no re-fetch", async () => { const menu = makeMenu(); // isActive returns true on first call (after setChoices), false on second (all filtered) menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); - const result = makeCompletionResult(["song"], 5, { complete: true }); + const result = makeCompletionResult(["song"], 5, { closedSet: true }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -154,25 +154,25 @@ describe("PartialCompletionSession — state transitions", () => { await Promise.resolve(); // → ACTIVE, anchor = "play " // User types more; trie returns no matches but input is within anchor. - // complete=true → exhaustive set, no point re-fetching. + // closedSet=true → exhaustive set, no point re-fetching. session.update("play xyz", getPos); expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); expect(menu.hide).toHaveBeenCalled(); }); - test("ACTIVE → re-fetch: complete=false, trie has no matches — re-fetches", async () => { + test("ACTIVE → re-fetch: closedSet=false, trie has no matches — re-fetches", async () => { const menu = makeMenu(); // isActive: true after initial load, false for "xyz" — non-exhaustive set menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); - const result = makeCompletionResult(["song"], 5); // complete=false default + const result = makeCompletionResult(["song"], 5); // closedSet=false default const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play ", getPos); await Promise.resolve(); // → ACTIVE, anchor = "play " - // User types text with no trie match. complete=false → set is NOT + // User types text with no trie match. closedSet=false → set is NOT // exhaustive, so we should re-fetch in case the backend knows more. session.update("play xyz", getPos); @@ -182,7 +182,7 @@ describe("PartialCompletionSession — state transitions", () => { ); }); - test("ACTIVE → backspace restores menu after no-match without re-fetch (complete=true)", async () => { + test("ACTIVE → backspace restores menu after no-match without re-fetch (closedSet=true)", async () => { const menu = makeMenu(); // isActive: true after initial load, false for "xyz" prefix, true again for "so" menu.isActive @@ -190,7 +190,7 @@ describe("PartialCompletionSession — state transitions", () => { .mockReturnValueOnce(false) // "xyz" — trie no match .mockReturnValueOnce(true); // "so" — trie matches "song" const result = makeCompletionResult(["song", "shuffle"], 5, { - complete: true, + closedSet: true, }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -198,7 +198,7 @@ describe("PartialCompletionSession — state transitions", () => { session.update("play ", getPos); await Promise.resolve(); // → ACTIVE, anchor = "play " - // User types non-matching text. complete=true → no re-fetch. + // User types non-matching text. closedSet=true → no re-fetch. session.update("play xyz", getPos); expect(menu.hide).toHaveBeenCalled(); @@ -305,13 +305,13 @@ describe("PartialCompletionSession — state transitions", () => { expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); }); - test("empty input: unique match triggers re-fetch even when complete=true", async () => { + test("empty input: unique match triggers re-fetch even when closedSet=true", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); menu.updatePrefix.mockReturnValue(true); - // complete=true means exhaustive at THIS level, but uniquelySatisfied + // closedSet=true means exhaustive at THIS level, but uniquelySatisfied // means the user needs NEXT level completions — always re-fetch. - const result = makeCompletionResult(["@"], 0, { complete: true }); + const result = makeCompletionResult(["@"], 0, { closedSet: true }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -377,7 +377,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 5, completions: [group1, group2], - complete: false, + closedSet: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -409,7 +409,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 0, completions: [group], - complete: false, + closedSet: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -438,7 +438,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 0, completions: [group], - complete: false, + closedSet: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -519,7 +519,7 @@ describe("PartialCompletionSession — result processing", () => { const result: CommandCompletionResult = { startIndex: 0, completions: [{ name: "empty", completions: [] }], - complete: false, + closedSet: false, }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -637,13 +637,13 @@ describe("PartialCompletionSession — @command routing", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("@ command: empty result (complete=true) suppresses re-fetch", async () => { + test("@ command: empty result (closedSet=true) suppresses re-fetch", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(); const session = new PartialCompletionSession(menu, dispatcher); session.update("@unknown", getPos); - await Promise.resolve(); // → empty completions, complete=true + await Promise.resolve(); // → empty completions, closedSet=true // Still within anchor — no re-fetch session.update("@unknownmore", getPos); From a1ba7a7313e75271dbd07f2b797f562cee5dddb9 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Tue, 10 Mar 2026 18:40:58 -0700 Subject: [PATCH 30/51] Fix comment/implementation inconsistencies and unclear terminology in grammar matcher --- .../actionGrammar/src/grammarMatcher.ts | 149 ++++++++++-------- ts/packages/cache/src/cache/grammarStore.ts | 9 +- .../src/constructions/constructionCache.ts | 7 +- .../dispatcher/src/command/completion.ts | 3 +- 4 files changed, 94 insertions(+), 74 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index a5a8ab2a8d..e1e4f4c9d4 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -15,8 +15,10 @@ import { } from "./grammarTypes.js"; // Separator mode for grammar completion results. -// Structurally compatible with SeparatorMode from @typeagent/agent-sdk -// (actionGrammar does not depend on agentSdk). +// A subset of SeparatorMode from @typeagent/agent-sdk (which also +// includes "space"). actionGrammar does not depend on agentSdk, so +// the type is redefined here; values are directly assignable to +// SeparatorMode. export type GrammarSeparatorMode = "spacePunctuation" | "optional" | "none"; const debugMatchRaw = registerDebug("typeagent:grammar:match"); @@ -50,8 +52,9 @@ const digitRe = /[0-9]/; // while still requiring them when both sides use word spaces // (e.g. Latin followed by Latin). function isWordBoundaryScript(c: string): boolean { - // Fast path: all ASCII letters are Latin-script (boundary required). - // ASCII digits and punctuation/space fall through to return false here + // Fast path: all ASCII characters are handled here without the regex. + // ASCII letters are Latin-script (boundary required). + // ASCII digits, punctuation, and space return false // (digits are handled separately by digitRe, punctuation/space never need a boundary). const code = c.charCodeAt(0); if (code < 128) { @@ -90,8 +93,8 @@ function requiresSeparator( // Convert a per-candidate (needsSep, spacingMode) pair into a // GrammarSeparatorMode value. When needsSep is true (separator // required), the grammar always uses spacePunctuation separators. -// When needsSep is false, the mode tells us whether the boundary is -// "optional" (CJK/mixed, or SpacingMode "optional") or "none". +// When needsSep is false: "none" spacingMode → "none", otherwise +// → "optional" (covers auto mode/CJK/mixed and explicit "optional"). function candidateSeparatorMode( needsSep: boolean, spacingMode: CompiledSpacingMode, @@ -106,8 +109,9 @@ function candidateSeparatorMode( } // Merge a new candidate's separator mode into the running aggregate. -// The most restrictive mode wins: spacePunctuation > optional > none. -function mergeSeparatorMode( +// The mode requiring the strongest separator wins (i.e. the mode that +// demands the most from the user): spacePunctuation > optional > none. +function mergeGrammarSeparatorMode( current: GrammarSeparatorMode | undefined, needsSep: boolean, spacingMode: CompiledSpacingMode, @@ -116,14 +120,14 @@ function mergeSeparatorMode( if (current === undefined) { return candidateMode; } - // "spacePunctuation" is the most restrictive — once set, stays. + // "spacePunctuation" requires a separator — strongest requirement. if ( current === "spacePunctuation" || candidateMode === "spacePunctuation" ) { return "spacePunctuation"; } - // "optional" is more restrictive than "none". + // "optional" is a stronger requirement than "none". if (current === "optional" || candidateMode === "optional") { return "optional"; } @@ -1085,8 +1089,11 @@ export type GrammarCompletionResult = { // before the completion point. The shell uses this to determine where // to insert/filter completions (replacing the space-based heuristic). matchedPrefixLength?: number | undefined; - // What kind of separator is required between the content at - // `matchedPrefixLength` and the completion text. + // What kind of separator is expected between the content at + // `matchedPrefixLength` and the completion text. This is a + // *completion-result* concept (GrammarSeparatorMode), derived from the + // per-rule *match-time* spacing rules (CompiledSpacingMode / + // spacingMode) but distinct from them. // "spacePunctuation" — whitespace or punctuation required // (Latin "y" → "m" requires a separator). // "optional" — separator accepted but not required @@ -1159,55 +1166,6 @@ export function matchGrammar(grammar: Grammar, request: string) { return results; } -/** - * Given a grammar and a user-typed prefix string, determine what completions - * are available. The algorithm greedily matches as many grammar parts as - * possible against the prefix (the "longest completable prefix"), then - * reports completions from the *next* unmatched part. - * - * The function explores every alternative rule/state in the grammar (via the - * `pending` work-list). Each state is run through `matchState` which - * consumes as many parts as the prefix allows. The state then falls into - * one of three categories: - * - * 1. **Exact match** — the prefix satisfies every part in the rule. - * No completion is needed, but `maxPrefixLength` is updated to - * the full input length so that completion candidates from shorter - * partial matches are filtered out in the post-loop step. - * - * 2. **Partial match, finalized** — the prefix was consumed (possibly with - * trailing separators) but the rule still has remaining parts. - * `matchState` returns `false` (could not match the next part) and - * `finalizeState` returns `true` (no trailing non-separator junk). - * The next unmatched part produces a completion candidate: - * - String part → literal keyword completion (e.g. "music"). - * - Wildcard / number → property completion (handled elsewhere). - * - * 3. **Partial match, NOT finalized** — either: - * a. A pending wildcard could not be finalized (trailing text is only - * separators with no wildcard content) → emit a property completion - * for the wildcard's entity type. - * b. Trailing text remains that didn't match any part → emit the - * next string part as a completion candidate unconditionally. - * The caller uses `matchedPrefixLength` to filter by trailing - * text. - * - * After processing all states, only candidates whose `prefixLength` - * equals the overall `maxPrefixLength` are returned. This ensures - * completions from shorter partial matches are discarded when a longer - * (or exact) match consumed more input. - * - * `matchedPrefixLength` tracks the furthest point consumed across all - * states — including exact matches (via `Math.max`). This tells the - * caller where the completable portion of the input ends, so it can - * position the completion insertion point correctly (especially important - * for non-space-separated scripts like CJK). - * - * `separatorMode` indicates what kind of separator is needed between the - * content at `matchedPrefixLength` and the completion text. It is - * determined by the spacing rules between the last character of the - * matched prefix and the first character of the completion. - */ /** * Try to partially match leading words of a multi-word string part * against the prefix starting at `startIndex`. Returns the consumed @@ -1218,8 +1176,9 @@ export function matchGrammar(grammar: Grammar, request: string) { * - Some words matched → returns consumed length + next word. * - No words matched → returns startIndex + first word. * - * Always returns exactly one word as the completion text, - * providing one-word-at-a-time progression. + * When returning a non-undefined result, it contains exactly one + * word as the completion text, providing one-word-at-a-time + * progression. */ function tryPartialStringMatch( part: StringPart, @@ -1262,6 +1221,60 @@ function tryPartialStringMatch( }; } +/** + * Given a grammar and a user-typed prefix string, determine what completions + * are available. The algorithm greedily matches as many grammar parts as + * possible against the prefix (the "longest completable prefix"), then + * reports completions from the *next* unmatched part. + * + * The function explores every alternative rule/state in the grammar (via the + * `pending` work-list). Each state is run through `matchState` which + * consumes as many parts as the prefix allows. The state then falls into + * one of three categories: + * + * 1. **Exact match** — the prefix satisfies every part in the rule. + * No completion is needed, but `maxPrefixLength` is updated to + * the full input length so that completion candidates from shorter + * partial matches are eagerly discarded (via `updateMaxPrefixLength`). + * + * 2. **Partial match, finalized** — the prefix was consumed (possibly with + * trailing separators) but the rule still has remaining parts. + * `matchState` returns `false` (could not match the next part) and + * `finalizeState` returns `true` (no trailing non-separator junk). + * The next unmatched part produces a completion candidate: + * - String part → literal keyword completion (e.g. "music"). + * - Wildcard / number → property completion (handled elsewhere). + * + * 3. **Partial match, NOT finalized** — either: + * a. A pending wildcard could not be finalized (trailing text is only + * separators with no wildcard content) → emit a property completion + * for the wildcard's entity type. + * b. Trailing text remains that didn't match any part → + * attempt word-by-word matching of the current string part + * against that text (via `tryPartialStringMatch`). If some + * leading words match they advance the consumed prefix; the + * next unmatched word is emitted as a completion candidate. + * Candidates from shorter partial matches are automatically + * discarded when a longer match updates `maxPrefixLength`. + * + * During processing, whenever `maxPrefixLength` advances, all + * previously accumulated candidates are cleared. Only candidates + * whose prefix length equals the current maximum are kept. This + * ensures completions from shorter partial matches are discarded + * when a longer (or exact) match consumed more input. + * + * `matchedPrefixLength` tracks the furthest point consumed across all + * states — including exact matches (via `Math.max`). This tells the + * caller where the completable portion of the input ends, so it can + * position the completion insertion point correctly (especially important + * for non-space-separated scripts like CJK). + * + * `separatorMode` (a {@link GrammarSeparatorMode}) indicates what kind of + * separator is needed between the content at `matchedPrefixLength` and the + * completion text. It is determined by the spacing rules (the per-rule + * {@link CompiledSpacingMode}) between the last character of the matched + * prefix and the first character of the completion. + */ export function matchGrammarCompletion( grammar: Grammar, prefix: string, @@ -1333,8 +1346,8 @@ export function matchGrammarCompletion( // --- Category 1: Exact match --- // All parts matched AND prefix was fully consumed. // Nothing left to complete; but record how far we got - // so that completions from shorter partial matches are - // filtered out in the post-loop step. + // so that completion candidates from shorter partial + // matches are eagerly discarded. if (matched) { debugCompletion("Matched. Nothing to complete."); updateMaxPrefixLength(state.index); @@ -1385,7 +1398,7 @@ export function matchGrammarCompletion( } completions.push(completionText); - separatorMode = mergeSeparatorMode( + separatorMode = mergeGrammarSeparatorMode( separatorMode, candidateNeedsSep, state.spacingMode, @@ -1448,7 +1461,7 @@ export function matchGrammarCompletion( } properties.push(completionProperty); - separatorMode = mergeSeparatorMode( + separatorMode = mergeGrammarSeparatorMode( separatorMode, candidateNeedsSep, state.spacingMode, @@ -1515,7 +1528,7 @@ export function matchGrammarCompletion( } completions.push(completionText); - separatorMode = mergeSeparatorMode( + separatorMode = mergeGrammarSeparatorMode( separatorMode, candidateNeedsSep, state.spacingMode, diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 2d47016601..63c6f547b7 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -35,14 +35,17 @@ import { import { sortMatches } from "./sortMatches.js"; // Merge a GrammarSeparatorMode (from action-grammar) into an accumulator -// SeparatorMode. Most restrictive wins: spacePunctuation > optional > none. +// SeparatorMode. The mode requiring the strongest separator wins +// (i.e. the mode that demands the most from the user): +// spacePunctuation > optional > none. // (Grammar results never produce "space" — that mode is for command dispatch.) function mergeGrammarSeparatorMode( current: SeparatorMode | undefined, incoming: GrammarSeparatorMode, ): SeparatorMode { - // GrammarSeparatorMode is structurally compatible with SeparatorMode - // (it is a subset: "spacePunctuation" | "optional" | "none"). + // GrammarSeparatorMode is a subset of SeparatorMode + // ("spacePunctuation" | "optional" | "none") — values are directly + // assignable. if (current === undefined) { return incoming; } diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index a9850a6f5d..ad1b5dc36c 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -96,7 +96,9 @@ export function mergeCompletionResults( if (second === undefined) { return first; } - // Take the max matchedPrefixLength across both results. + // TODO: Be consistent with grammarMatcher and eagerly discard + // shorter-prefix completions instead of retaining them and + // relying on the caller to filter. let matchedPrefixLength: number | undefined; if ( first.matchedPrefixLength !== undefined || @@ -127,7 +129,8 @@ export function mergeCompletionResults( }; } -// Merge two SeparatorMode values — most restrictive wins. +// Merge two SeparatorMode values — the mode requiring the strongest +// separator wins (i.e. the mode that demands the most from the user). // Priority: "space" > "spacePunctuation" > "optional" > "none". function mergeSeparatorModes( a: SeparatorMode | undefined, diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 5c5bf77374..cd7358cbaf 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -29,7 +29,8 @@ import { CommandCompletionResult } from "@typeagent/dispatcher-types"; const debug = registerDebug("typeagent:command:completion"); const debugError = registerDebug("typeagent:command:completion:error"); -// Merge two SeparatorMode values — most restrictive wins. +// Merge two SeparatorMode values — the mode requiring the strongest +// separator wins (i.e. the mode that demands the most from the user). // Priority: "space" > "spacePunctuation" > "optional" > "none" > undefined. function mergeSeparatorMode( a: SeparatorMode | undefined, From 4853f80c32a2f5646ce8f6ccf7e5e0316472c5f4 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Tue, 10 Mar 2026 21:23:46 -0700 Subject: [PATCH 31/51] Fix test mocks to match documented getCommandCompletion contract - Place startIndex at token boundaries (not on separator whitespace) - Add separatorMode: 'none' for agent-name completions from empty input - Add hasExactMatch mock and committed-past-boundary re-fetch tests --- .../renderer/src/partialCompletionSession.ts | 17 ++ ts/packages/shell/src/renderer/src/search.ts | 9 +- .../test/partialCompletionSession.spec.ts | 177 +++++++++++++++--- 3 files changed, 171 insertions(+), 32 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 4abe953be7..0b96e13ac8 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -17,6 +17,8 @@ export interface ISearchMenu { // Returns true when the prefix uniquely satisfies exactly one entry // (exact match that is not a prefix of any other entry). updatePrefix(prefix: string, position: SearchMenuPosition): boolean; + // Returns true when text is an exact match for a completion entry. + hasExactMatch(text: string): boolean; hide(): void; isActive(): boolean; } @@ -240,6 +242,21 @@ export class PartialCompletionSession { ); return false; // RE-FETCH for next level of completions } + + // Committed-past-boundary: the prefix contains whitespace or + // punctuation, meaning the user typed past a completion entry. + // If the text before the first separator exactly matches a + // completion, re-fetch for the next level. This handles the + // case where an entry (e.g. "set") is also a prefix of other + // entries ("setWindowState") so uniquelySatisfied is false, + // but the user committed by typing a separator. + const sepMatch = prefix.match(/^(.+?)[\s\p{P}]/u); + if (sepMatch !== null && this.menu.hasExactMatch(sepMatch[1])) { + debug( + `Partial completion re-fetch: '${sepMatch[1]}' committed with separator`, + ); + return false; // RE-FETCH for next level of completions + } } else { this.menu.hide(); } diff --git a/ts/packages/shell/src/renderer/src/search.ts b/ts/packages/shell/src/renderer/src/search.ts index 600698a263..d1315b6d86 100644 --- a/ts/packages/shell/src/renderer/src/search.ts +++ b/ts/packages/shell/src/renderer/src/search.ts @@ -49,10 +49,11 @@ export class SearchMenu { return this.trie.size(); } - public updatePrefix( - prefix: string, - position: SearchMenuPosition, - ): boolean { + public hasExactMatch(text: string): boolean { + return this.trie.contains(normalizeMatchText(text)); + } + + public updatePrefix(prefix: string, position: SearchMenuPosition): boolean { if (this.numChoices() === 0) { return false; } diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index 42c4d0cc4e..bd3872d16f 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -16,6 +16,7 @@ import { CommandCompletionResult } from "agent-dispatcher"; type MockMenu = { setChoices: jest.MockedFunction; updatePrefix: jest.MockedFunction; + hasExactMatch: jest.MockedFunction; hide: jest.MockedFunction; isActive: jest.MockedFunction; }; @@ -26,6 +27,9 @@ function makeMenu(): MockMenu { updatePrefix: jest .fn() .mockReturnValue(false), + hasExactMatch: jest + .fn() + .mockReturnValue(false), hide: jest.fn(), isActive: jest.fn().mockReturnValue(false), }; @@ -61,7 +65,12 @@ function makeCompletionResult( opts: Partial = {}, ): CommandCompletionResult { const group: CompletionGroup = { name: "test", completions }; - return { startIndex, completions: [group], closedSet: false, ...opts }; + return { + startIndex, + completions: [group], + closedSet: false, + ...opts, + }; } // ── State machine tests ─────────────────────────────────────────────────────── @@ -97,7 +106,7 @@ describe("PartialCompletionSession — state transitions", () => { test("PENDING → ACTIVE: completions returned → setChoices + updatePrefix called", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["song", "shuffle"], 0); + const result = makeCompletionResult(["song", "shuffle"], 4); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -146,12 +155,14 @@ describe("PartialCompletionSession — state transitions", () => { const menu = makeMenu(); // isActive returns true on first call (after setChoices), false on second (all filtered) menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); - const result = makeCompletionResult(["song"], 5, { closedSet: true }); + const result = makeCompletionResult(["song"], 4, { + closedSet: true, + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play " + await Promise.resolve(); // → ACTIVE, anchor = "play" // User types more; trie returns no matches but input is within anchor. // closedSet=true → exhaustive set, no point re-fetching. @@ -165,12 +176,12 @@ describe("PartialCompletionSession — state transitions", () => { const menu = makeMenu(); // isActive: true after initial load, false for "xyz" — non-exhaustive set menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); - const result = makeCompletionResult(["song"], 5); // closedSet=false default + const result = makeCompletionResult(["song"], 4); // closedSet=false default const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play " + await Promise.resolve(); // → ACTIVE, anchor = "play" // User types text with no trie match. closedSet=false → set is NOT // exhaustive, so we should re-fetch in case the backend knows more. @@ -189,14 +200,14 @@ describe("PartialCompletionSession — state transitions", () => { .mockReturnValueOnce(true) // initial reuseSession after result .mockReturnValueOnce(false) // "xyz" — trie no match .mockReturnValueOnce(true); // "so" — trie matches "song" - const result = makeCompletionResult(["song", "shuffle"], 5, { + const result = makeCompletionResult(["song", "shuffle"], 4, { closedSet: true, }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play " + await Promise.resolve(); // → ACTIVE, anchor = "play" // User types non-matching text. closedSet=true → no re-fetch. session.update("play xyz", getPos); @@ -212,7 +223,7 @@ describe("PartialCompletionSession — state transitions", () => { test("hide() resets to IDLE so next update starts fresh", async () => { const menu = makeMenu(); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 0)); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); const session = new PartialCompletionSession(menu, dispatcher); session.update("play", getPos); @@ -243,7 +254,7 @@ describe("PartialCompletionSession — state transitions", () => { session.hide(); // cancels the promise // Now resolve the stale promise — should be a no-op - resolve(makeCompletionResult(["song"], 0)); + resolve(makeCompletionResult(["song"], 4)); await Promise.resolve(); expect(menu.setChoices).not.toHaveBeenCalledWith( @@ -256,7 +267,9 @@ describe("PartialCompletionSession — state transitions", () => { test("empty input fetches completions from backend", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["@"], 0); + const result = makeCompletionResult(["@"], 0, { + separatorMode: "none", + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -274,7 +287,9 @@ describe("PartialCompletionSession — state transitions", () => { test("empty input: second update reuses session without re-fetch", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["@"], 0); + const result = makeCompletionResult(["@"], 0, { + separatorMode: "none", + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -291,7 +306,9 @@ describe("PartialCompletionSession — state transitions", () => { menu.isActive.mockReturnValue(true); // updatePrefix returns true = uniquely satisfied menu.updatePrefix.mockReturnValue(true); - const result = makeCompletionResult(["@"], 0); + const result = makeCompletionResult(["@"], 0, { + separatorMode: "none", + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -311,7 +328,10 @@ describe("PartialCompletionSession — state transitions", () => { menu.updatePrefix.mockReturnValue(true); // closedSet=true means exhaustive at THIS level, but uniquelySatisfied // means the user needs NEXT level completions — always re-fetch. - const result = makeCompletionResult(["@"], 0, { closedSet: true }); + const result = makeCompletionResult(["@"], 0, { + closedSet: true, + separatorMode: "none", + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -329,7 +349,9 @@ describe("PartialCompletionSession — state transitions", () => { menu.isActive.mockReturnValue(true); // updatePrefix returns false = not uniquely satisfied menu.updatePrefix.mockReturnValue(false); - const result = makeCompletionResult(["@config", "@configure"], 0); + const result = makeCompletionResult(["@config", "@configure"], 0, { + separatorMode: "none", + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -349,15 +371,16 @@ describe("PartialCompletionSession — result processing", () => { test("startIndex narrows the anchor (current) to input[0..startIndex]", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - // startIndex=5 means grammar consumed "play " (5 chars) - const result = makeCompletionResult(["song", "shuffle"], 5); + // startIndex=4 means grammar consumed "play" (4 chars); the + // trailing space is the separator between anchor and completions. + const result = makeCompletionResult(["song", "shuffle"], 4); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); session.update("play song", getPos); await Promise.resolve(); - // prefix should be "song" (the text after anchor "play ") + // prefix should be "song" (the text after anchor "play" + separator " ") expect(menu.updatePrefix).toHaveBeenCalledWith("song", anyPosition); }); @@ -375,7 +398,7 @@ describe("PartialCompletionSession — result processing", () => { sorted: true, }; const result: CommandCompletionResult = { - startIndex: 5, + startIndex: 4, completions: [group1, group2], closedSet: false, }; @@ -407,7 +430,7 @@ describe("PartialCompletionSession — result processing", () => { sorted: true, }; const result: CommandCompletionResult = { - startIndex: 0, + startIndex: 4, completions: [group], closedSet: false, }; @@ -439,6 +462,7 @@ describe("PartialCompletionSession — result processing", () => { startIndex: 0, completions: [group], closedSet: false, + separatorMode: "none", }; const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -498,7 +522,7 @@ describe("PartialCompletionSession — result processing", () => { test("separatorMode: menu shown after trailing space is typed", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["music"], 5, { + const result = makeCompletionResult(["music"], 4, { separatorMode: undefined, }); const dispatcher = makeDispatcher(result); @@ -683,7 +707,7 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { test("returns suffix after anchor when input starts with anchor", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["song"], 5); // anchor = "play " + const result = makeCompletionResult(["song"], 4); const session = new PartialCompletionSession( menu, makeDispatcher(result), @@ -698,7 +722,7 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { test("returns undefined when input diverges from anchor", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["song"], 5); + const result = makeCompletionResult(["song"], 4); const session = new PartialCompletionSession( menu, makeDispatcher(result), @@ -707,7 +731,7 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { session.update("play song", getPos); await Promise.resolve(); - // Input no longer starts with anchor "play " + // Input no longer starts with anchor "play" expect(session.getCompletionPrefix("stop")).toBeUndefined(); }); @@ -753,7 +777,7 @@ describe("PartialCompletionSession — resetToIdle", () => { test("clears session so next update re-fetches", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 5)); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); const session = new PartialCompletionSession(menu, dispatcher); session.update("play song", getPos); @@ -769,7 +793,7 @@ describe("PartialCompletionSession — resetToIdle", () => { test("does not hide the menu (caller is responsible for that)", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 5)); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); const session = new PartialCompletionSession(menu, dispatcher); session.update("play song", getPos); @@ -855,7 +879,7 @@ describe("PartialCompletionSession — miscellaneous", () => { test("getPosition returning undefined hides the menu", async () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); - const result = makeCompletionResult(["song"], 5); + const result = makeCompletionResult(["song"], 4); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -873,7 +897,9 @@ describe("PartialCompletionSession — miscellaneous", () => { const menu = makeMenu(); menu.isActive.mockReturnValue(true); // startIndex=99 is beyond "play" (length 4) — anchor falls back to "play" - const result = makeCompletionResult(["song"], 99); + const result = makeCompletionResult(["song"], 99, { + separatorMode: "none", + }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -885,3 +911,98 @@ describe("PartialCompletionSession — miscellaneous", () => { expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); }); }); + +// ── committed-past-boundary (hasExactMatch) ─────────────────────────────────── + +describe("PartialCompletionSession — committed-past-boundary re-fetch", () => { + test("closedSet=true: typing space after exact match triggers re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + menu.hasExactMatch.mockImplementation((text) => text === "set"); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // User types "set " — prefix is "set ", exact match "set" + separator + session.update("play set ", getPos); + + expect(menu.hasExactMatch).toHaveBeenCalledWith("set"); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play set ", + ); + }); + + test("closedSet=true: typing multiple spaces after exact match triggers re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + menu.hasExactMatch.mockImplementation((text) => text === "set"); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // Double space after "set" + session.update("play set ", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("closedSet=true: typing punctuation after exact match triggers re-fetch", async () => { + const menu = makeMenu(); + menu.isActive.mockReturnValue(true); + menu.hasExactMatch.mockImplementation((text) => text === "set"); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // Punctuation after "set" + session.update("play set.", getPos); + + expect(menu.hasExactMatch).toHaveBeenCalledWith("set"); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("closedSet=true: typing separator after non-matching text does NOT re-fetch", async () => { + const menu = makeMenu(); + // isActive: true after initial load, false after "xyz " (no trie match) + menu.isActive.mockReturnValueOnce(true).mockReturnValue(false); + menu.hasExactMatch.mockReturnValue(false); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "xyz" is not a known completion — closedSet=true should suppress re-fetch + session.update("play xyz ", getPos); + + expect(menu.hasExactMatch).toHaveBeenCalledWith("xyz"); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); From b191d75202b93e3d7e44b4de450459501b755c8c Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 08:07:55 -0700 Subject: [PATCH 32/51] Rename 'current' to 'anchor' and 'prefix' to 'completionPrefix' for clarity --- ts/packages/shell/src/renderer/src/partial.ts | 8 +- .../renderer/src/partialCompletionSession.ts | 107 +++++++++--------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partial.ts b/ts/packages/shell/src/renderer/src/partial.ts index 7475864846..f9c0af6024 100644 --- a/ts/packages/shell/src/renderer/src/partial.ts +++ b/ts/packages/shell/src/renderer/src/partial.ts @@ -246,8 +246,8 @@ export class PartialCompletion { // Compute the filter prefix relative to the current anchor. // Must be read before resetToIdle() clears the session's anchor. const currentInput = this.getCurrentInputForCompletion(); - const prefix = this.session.getCompletionPrefix(currentInput); - if (prefix === undefined) { + const completionPrefix = this.session.getCompletionPrefix(currentInput); + if (completionPrefix === undefined) { debugError(`Partial completion abort select: prefix not found`); return; } @@ -257,7 +257,7 @@ export class PartialCompletion { ? `"${item.selectedText.replaceAll('"', '\\"')}"` : item.selectedText; - const offset = this.getCurrentInput().length - prefix.length; + const offset = this.getCurrentInput().length - completionPrefix.length; const leafNode = getLeafNode(this.input.getTextEntry(), offset); if (leafNode === undefined) { debugError( @@ -267,7 +267,7 @@ export class PartialCompletion { } const endLeafNode = getLeafNode( this.input.getTextEntry(), - offset + prefix.length, + offset + completionPrefix.length, ); if (endLeafNode === undefined) { debugError( diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 0b96e13ac8..6ada16f7a8 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -30,9 +30,9 @@ export interface ICompletionDispatcher { // PartialCompletionSession manages the state machine for command completion. // // States: -// IDLE current === undefined -// PENDING current !== undefined && completionP !== undefined -// ACTIVE current !== undefined && completionP === undefined +// IDLE anchor === undefined +// PENDING anchor !== undefined && completionP !== undefined +// ACTIVE anchor !== undefined && completionP === undefined // // Design principles: // - Completion result fields (separatorMode, closedSet) are stored as-is @@ -54,7 +54,7 @@ export interface ICompletionDispatcher { // has zero matches for the typed prefix: // closedSet=true → reuse (closed set, nothing else exists) // closedSet=false → re-fetch (set is open, backend may know more) -// - The anchor (`current`) is never advanced after a result is received. +// - The anchor is never advanced after a result is received. // When `separatorMode` requires a separator, the separator is stripped // from the raw prefix before being passed to the menu, so the trie // still matches. @@ -64,13 +64,13 @@ export class PartialCompletionSession { // The "anchor" prefix for the current session. Set to the full input // when the request is issued, then narrowed to input[0..startIndex] when // the backend reports how much the grammar consumed. `undefined` = IDLE. - private current: string | undefined = undefined; + private anchor: string | undefined = undefined; // Saved as-is from the last completion result: what kind of separator - // must appear in the input immediately after `current` before + // must appear in the input immediately after `anchor` before // completions are valid. Defaults to "space" when omitted. // Used by reuseSession() and getCompletionPrefix() to interpret - // the raw prefix without mutating `current`. + // the raw prefix without mutating `anchor`. private separatorMode: SeparatorMode = "space"; // When true, the completion set returned by the backend is a closed @@ -115,38 +115,26 @@ export class PartialCompletionSession { // Reset to IDLE and hide the menu. public hide(): void { this.completionP = undefined; - this.current = undefined; - this.separatorMode = "space"; - this.closedSet = false; - this.cancelMenu(); + this.resetSessionFields(); + this.menu.hide(); } // Reset state to IDLE without hiding the menu (used after handleSelect inserts text). public resetToIdle(): void { - this.current = undefined; - this.separatorMode = "space"; - this.closedSet = false; + this.resetSessionFields(); } - // Returns the text typed after the anchor (`current`), or undefined when + // Returns the text typed after the anchor, or undefined when // the input has diverged past the anchor or the separator is not yet present. public getCompletionPrefix(input: string): string | undefined { - const current = this.current; - if (current === undefined) { - return undefined; - } - if (!input.startsWith(current)) { + const anchor = this.anchor; + if (anchor === undefined || !input.startsWith(anchor)) { return undefined; } - const rawPrefix = input.substring(current.length); - if ( - this.separatorMode === "space" || - this.separatorMode === "spacePunctuation" - ) { + const rawPrefix = input.substring(anchor.length); + if (this.requiresSeparator()) { // The separator must be present and is not part of the replaceable prefix. - const sepRe = - this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; - if (!sepRe.test(rawPrefix)) { + if (!this.separatorRegex().test(rawPrefix)) { return undefined; } return rawPrefix.trimStart(); @@ -176,19 +164,19 @@ export class PartialCompletionSession { input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, ): boolean { - const current = this.current; - if (current === undefined) { + const anchor = this.anchor; + if (anchor === undefined) { return false; } // PENDING — a fetch is already in flight. if (this.completionP !== undefined) { - debug(`Partial completion pending: ${current}`); + debug(`Partial completion pending: ${anchor}`); return true; } // RE-FETCH — input moved past the anchor (e.g. backspace, new word). - if (!input.startsWith(current)) { + if (!input.startsWith(anchor)) { return false; } @@ -204,10 +192,8 @@ export class PartialCompletionSession { // "x…" — non-separator typed right after anchor: RE-FETCH (the // separator constraint can never be satisfied without // backtracking, so treat this as a new input) - const rawPrefix = input.substring(current.length); - const requiresSep = - this.separatorMode === "space" || - this.separatorMode === "spacePunctuation"; + const rawPrefix = input.substring(anchor.length); + const requiresSep = this.requiresSeparator(); if (requiresSep) { if (rawPrefix === "") { debug( @@ -216,29 +202,32 @@ export class PartialCompletionSession { this.menu.hide(); return true; // HIDE+KEEP } - const sepRe = - this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; - if (!sepRe.test(rawPrefix)) { + if (!this.separatorRegex().test(rawPrefix)) { return false; // RE-FETCH } } // SHOW — strip the leading separator (if any) before passing to the // menu trie, so completions like "music" match prefix "" not " ". - const prefix = requiresSep ? rawPrefix.trimStart() : rawPrefix; + const completionPrefix = requiresSep + ? rawPrefix.trimStart() + : rawPrefix; - const position = getPosition(prefix); + const position = getPosition(completionPrefix); if (position !== undefined) { debug( - `Partial completion update: '${prefix}' @ ${JSON.stringify(position)}`, + `Partial completion update: '${completionPrefix}' @ ${JSON.stringify(position)}`, + ); + const uniquelySatisfied = this.menu.updatePrefix( + completionPrefix, + position, ); - const uniquelySatisfied = this.menu.updatePrefix(prefix, position); if (uniquelySatisfied) { // The user has typed text that exactly matches one completion // and is not a prefix of any other. We need the NEXT level's // completions (e.g. agent name → subcommands), so re-fetch. debug( - `Partial completion re-fetch: '${prefix}' uniquely satisfied`, + `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied`, ); return false; // RE-FETCH for next level of completions } @@ -250,7 +239,7 @@ export class PartialCompletionSession { // case where an entry (e.g. "set") is also a prefix of other // entries ("setWindowState") so uniquelySatisfied is false, // but the user committed by typing a separator. - const sepMatch = prefix.match(/^(.+?)[\s\p{P}]/u); + const sepMatch = completionPrefix.match(/^(.+?)[\s\p{P}]/u); if (sepMatch !== null && this.menu.hasExactMatch(sepMatch[1])) { debug( `Partial completion re-fetch: '${sepMatch[1]}' committed with separator`, @@ -278,10 +267,9 @@ export class PartialCompletionSession { getPosition: (prefix: string) => SearchMenuPosition | undefined, ): void { debug(`Partial completion start: '${input}'`); - this.cancelMenu(); - this.current = input; - this.separatorMode = "space"; - this.closedSet = false; + this.menu.hide(); + this.resetSessionFields(); + this.anchor = input; this.menu.setChoices([]); const completionP = this.dispatcher.getCommandCompletion(input); this.completionP = completionP; @@ -324,7 +312,7 @@ export class PartialCompletionSession { debug( `Partial completion skipped: No completions for '${input}'`, ); - // Keep this.current at the full input so the anchor + // Keep this.anchor at the full input so the anchor // covers the entire typed text. The menu stays empty, // so reuseSession()'s SHOW path will use `closedSet` to // decide: closedSet=true → reuse (nothing more exists); @@ -343,7 +331,7 @@ export class PartialCompletionSession { result.startIndex >= 0 && result.startIndex <= input.length ? input.substring(0, result.startIndex) : input; - this.current = partial; + this.anchor = partial; this.menu.setChoices(completions); @@ -357,7 +345,20 @@ export class PartialCompletionSession { }); } - private cancelMenu(): void { - this.menu.hide(); + private resetSessionFields(): void { + this.anchor = undefined; + this.separatorMode = "space"; + this.closedSet = false; + } + + private requiresSeparator(): boolean { + return ( + this.separatorMode === "space" || + this.separatorMode === "spacePunctuation" + ); + } + + private separatorRegex(): RegExp { + return this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; } } From 6ff503a675a625e36a000d32f167228ce69b05a6 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 11:32:38 -0700 Subject: [PATCH 33/51] Categorize re-fetch triggers into invalidation, navigation, and discovery --- .../renderer/src/partialCompletionSession.ts | 58 +++++++++++++++---- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 6ada16f7a8..4e59ce9af8 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -160,10 +160,41 @@ export class PartialCompletionSession { // reuse when the trie still has matches, or when the set // is closed (nothing new to fetch). Re-fetch only // when the trie is empty AND the set is open. + // + // Re-fetch triggers (returns false → startNewSession): + // + // A. Session invalidation — anchor is stale; backend result was computed + // for a prefix that no longer matches the input. Unconditional. + // 1. No session — anchor is undefined (IDLE state). + // 2. Anchor diverged — input no longer starts with the saved anchor + // (e.g. backspace deleted into the anchor region). + // 3. Bad separator — separatorMode requires whitespace (or punctuation) + // immediately after anchor, but a non-separator + // character was typed instead. The constraint can + // never be satisfied, so treat as new input. + // + // B. Hierarchical navigation — user completed this level; re-fetch for + // the NEXT level's completions. Unconditional (closedSet describes + // THIS level, not the next). + // 4. Uniquely satisfied — typed prefix exactly matches one completion and + // is not a prefix of any other. Re-fetch for the + // NEXT level (e.g. agent name → subcommands). + // 5. Committed past boundary — prefix contains a separator after a valid + // completion match (e.g. "set " where "set" matches + // but so does "setWindowState"). The user committed + // by typing a separator; re-fetch for next level. + // + // C. Open-set discovery — trie has zero matches and the set is not + // exhaustive; the backend may know about completions not yet loaded. + // Gated by closedSet === false. + // 6. Open set, no matches — trie has zero matches for the typed prefix + // AND closedSet is false. The backend may know about + // completions not yet loaded. private reuseSession( input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, ): boolean { + // [A1] No session — IDLE state, must fetch. const anchor = this.anchor; if (anchor === undefined) { return false; @@ -175,7 +206,7 @@ export class PartialCompletionSession { return true; } - // RE-FETCH — input moved past the anchor (e.g. backspace, new word). + // [A2] RE-FETCH — input moved past the anchor (e.g. backspace, new word). if (!input.startsWith(anchor)) { return false; } @@ -203,7 +234,11 @@ export class PartialCompletionSession { return true; // HIDE+KEEP } if (!this.separatorRegex().test(rawPrefix)) { - return false; // RE-FETCH + // [A3] closedSet is not consulted here: it describes whether + // the completion *entries* are exhaustive, not whether + // the anchor token can extend. The grammar may parse + // the longer input on a completely different path. + return false; // RE-FETCH (session invalidation) } } @@ -223,17 +258,18 @@ export class PartialCompletionSession { position, ); if (uniquelySatisfied) { - // The user has typed text that exactly matches one completion - // and is not a prefix of any other. We need the NEXT level's - // completions (e.g. agent name → subcommands), so re-fetch. + // [B4] The user has typed text that exactly matches one + // completion and is not a prefix of any other. We need the + // NEXT level's completions (e.g. agent name → subcommands), + // so re-fetch. debug( `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied`, ); - return false; // RE-FETCH for next level of completions + return false; // RE-FETCH (hierarchical navigation) } - // Committed-past-boundary: the prefix contains whitespace or - // punctuation, meaning the user typed past a completion entry. + // [B5] Committed-past-boundary: the prefix contains whitespace + // or punctuation, meaning the user typed past a completion entry. // If the text before the first separator exactly matches a // completion, re-fetch for the next level. This handles the // case where an entry (e.g. "set") is also a prefix of other @@ -244,20 +280,20 @@ export class PartialCompletionSession { debug( `Partial completion re-fetch: '${sepMatch[1]}' committed with separator`, ); - return false; // RE-FETCH for next level of completions + return false; // RE-FETCH (hierarchical navigation) } } else { this.menu.hide(); } - // When the menu is still active (trie has matches) we always + // [C6] When the menu is still active (trie has matches) we always // reuse — the loaded completions are still useful. When there are // NO matches, the decision depends on `closedSet`: // closedSet=true → the set is closed; the user typed past all // valid continuations, so re-fetching won't help. // closedSet=false → the set is NOT closed; the user may have // typed something valid that wasn't loaded, so - // re-fetch with the longer input. + // re-fetch with the longer input (open-set discovery). return this.closedSet || this.menu.isActive(); } From 58422730341b6e9611c935be1ab7eb150fde9db8 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 15:54:00 -0700 Subject: [PATCH 34/51] Replace mock SearchMenu with real trie-backed TestSearchMenu in tests - Add TestSearchMenu class using real TST prefix tree instead of manually orchestrated jest.fn() mocks for isActive/updatePrefix/hasExactMatch - Add prefixTree.ts to shell src tsconfig for test imports - Fix TSTNode property types (optional -> explicit undefined) for exactOptionalPropertyTypes compatibility - Remove 49 mock orchestration lines across all tests - Add closedSet: true to explicit-mode tests where unique satisfaction makes isActive naturally false (prevents C6 interference) --- ts/packages/agentSdk/src/command.ts | 14 + ts/packages/agentSdk/src/index.ts | 1 + .../dispatcher/src/command/completion.ts | 80 +++++- .../src/translation/requestCompletion.ts | 16 +- .../dispatcher/test/completion.spec.ts | 107 +++---- .../dispatcher/types/src/dispatcher.ts | 7 + .../renderer/src/partialCompletionSession.ts | 55 +++- .../shell/src/renderer/src/prefixTree.ts | 6 +- ts/packages/shell/src/tsconfig.json | 1 + .../test/partialCompletionSession.spec.ts | 262 +++++++++++++----- 10 files changed, 405 insertions(+), 144 deletions(-) diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index 5181ac9bae..acad8ecb89 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -66,6 +66,15 @@ export type CommandDescriptors = // Used for [spacing=none] grammars. export type SeparatorMode = "space" | "spacePunctuation" | "optional" | "none"; +// Controls when the session considers a typed completion "committed" and +// triggers a re-fetch for the next hierarchical level. +// "explicit" — the user must type an explicit delimiter (e.g. space or +// punctuation) after the matched token to commit it. +// Suppresses eager re-fetch on unique match. +// "eager" — commit as soon as the typed prefix uniquely satisfies a +// completion. Re-fetches immediately for the next level. +export type CommitMode = "explicit" | "eager"; + export type CompletionGroup = { name: string; // The group name for the completion completions: string[]; // The list of completions in the group @@ -95,6 +104,11 @@ export type CompletionGroups = { // False or undefined means the parser can continue past // unrecognized input and find more completions. closedSet?: boolean | undefined; + // Controls when a uniquely-satisfied completion triggers a re-fetch + // for the next hierarchical level. See CommitMode. + // When omitted, the dispatcher decides (typically "explicit" for + // command/parameter completions). + commitMode?: CommitMode | undefined; }; export interface AppAgentCommandInterface { diff --git a/ts/packages/agentSdk/src/index.ts b/ts/packages/agentSdk/src/index.ts index d2e6ac4b0f..0de4c9b7ae 100644 --- a/ts/packages/agentSdk/src/index.ts +++ b/ts/packages/agentSdk/src/index.ts @@ -29,6 +29,7 @@ export { CommandDescriptors, CommandDescriptorTable, AppAgentCommandInterface, + CommitMode, CompletionGroup, CompletionGroups, SeparatorMode, diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index cd7358cbaf..482ee3fde6 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -4,6 +4,7 @@ import { CommandHandlerContext } from "../context/commandHandlerContext.js"; import { + CommitMode, CommandDescriptor, FlagDefinitions, ParameterDefinitions, @@ -145,6 +146,7 @@ type ParameterCompletionResult = { startIndex: number; separatorMode: SeparatorMode | undefined; closedSet: boolean; + commitMode: CommitMode | undefined; }; // Complete parameter values and flags for an already-resolved command @@ -155,6 +157,7 @@ async function getCommandParameterCompletion( context: CommandHandlerContext, result: ResolveCommandResult, input: string, + hasTrailingSpace: boolean, ): Promise { const completions: CompletionGroup[] = []; if (typeof descriptor.parameters !== "object") { @@ -169,8 +172,13 @@ async function getCommandParameterCompletion( ); const pendingFlag = getPendingFlag(params, flags, completions); const agentCommandCompletions: string[] = []; - if (pendingFlag === undefined) { - // TODO: auto inject boolean value for boolean args. + if (pendingFlag !== undefined && hasTrailingSpace) { + // The last token is a recognized flag and the user committed + // it with a trailing space. Ask the agent for flag values. + agentCommandCompletions.push(pendingFlag); + } else { + // Either no pending flag, or the flag isn't committed yet + // (no trailing space). Offer positional args and flag names. agentCommandCompletions.push(...params.nextArgs); if (flags !== undefined) { const flagCompletions = collectFlags( @@ -185,10 +193,6 @@ async function getCommandParameterCompletion( }); } } - } else { - // The last token is a recognized flag waiting for a value. - // Ask the agent for potential values for this flag. - agentCommandCompletions.push(pendingFlag); } // Compute startIndex from how far parseParams consumed the suffix. @@ -206,6 +210,7 @@ async function getCommandParameterCompletion( let agentInvoked = false; let agentClosedSet: boolean | undefined; + let agentCommitMode: CommitMode | undefined; let separatorMode: SeparatorMode | undefined; const agent = context.agents.getAppAgent(result.actualAppAgentName); if (agent.getCommandCompletion) { @@ -218,7 +223,10 @@ async function getCommandParameterCompletion( const quoted = isFullyQuoted(valueToken); if ( quoted === false || - (quoted === undefined && lastParamImplicitQuotes) + (quoted === undefined && lastParamImplicitQuotes) || + (quoted === undefined && + !hasTrailingSpace && + pendingFlag === undefined) ) { // The user is inside a token (open quote or // implicitQuotes rest-of-line) — completions for @@ -265,12 +273,33 @@ async function getCommandParameterCompletion( separatorMode = agentResult.separatorMode; agentInvoked = true; agentClosedSet = agentResult.closedSet; + agentCommitMode = agentResult.commitMode; debug( `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${tokenStartIndex}`, ); } } + // When there is no trailing space, the last consumed token + // hasn't been committed. If no earlier path already adjusted + // startIndex (lastCompletableParam / grammar-prefixLength), + // back up to the start of that token so the caller's trie can + // filter completions against it. + // Exception: fully-quoted tokens (e.g. "build") are committed + // by their closing quote — no back-up needed. + const unadjustedStartIndex = tokenBoundary(input, remainderIndex); + if ( + !hasTrailingSpace && + params.remainderLength === 0 && + params.tokens.length > 0 && + startIndex === unadjustedStartIndex + ) { + const lastToken = params.tokens[params.tokens.length - 1]; + if (isFullyQuoted(lastToken) !== true) { + startIndex = unadjustedStartIndex - lastToken.length; + } + } + // Determine whether the completion set is a closed set. // Agent-provided completions use the agent's self-reported // closedSet flag (via CompletionGroups.closedSet), defaulting to @@ -294,7 +323,11 @@ async function getCommandParameterCompletion( closedSet = params.nextArgs.length === 0; } - return { completions, startIndex, separatorMode, closedSet }; + // Propagate agent-provided commitMode. When the agent doesn't + // specify one, leave undefined so the caller can apply its default. + const commitMode: CommitMode | undefined = agentCommitMode; + + return { completions, startIndex, separatorMode, closedSet, commitMode }; } // @@ -390,6 +423,7 @@ export async function getCommandCompletion( `Command completion command consumed length: ${commandConsumedLength}, suffix: '${result.suffix}'`, ); let startIndex = tokenBoundary(input, commandConsumedLength); + const hasTrailingSpace = /\s$/.test(partialCommand); // Collect completions and track separatorMode across all sources. const completions: CompletionGroup[] = []; @@ -403,7 +437,30 @@ export async function getCommandCompletion( const descriptor = result.descriptor; let closedSet = true; - if (descriptor !== undefined) { + let commitMode: "explicit" | "eager" = "explicit"; + + // When the last command token was exactly matched but the + // user hasn't typed a trailing space, they haven't committed + // it yet. Offer subcommand alternatives at that token's + // position instead of jumping to parameter completions. + const uncommittedCommand = + descriptor !== undefined && + result.matched && + !hasTrailingSpace && + result.suffix === "" && + table !== undefined; + + if (uncommittedCommand) { + const lastCmd = result.commands[result.commands.length - 1]; + startIndex = + tokenBoundary(input, commandConsumedLength) - lastCmd.length; + completions.push({ + name: "Subcommands", + completions: Object.keys(table!.commands), + }); + separatorMode = mergeSeparatorMode(separatorMode, "none"); + // closedSet stays true: subcommand names are exhaustive. + } else if (descriptor !== undefined) { // Get parameter completions first — we need to know // whether parameters consumed past the command boundary // before deciding if subcommand alternatives apply. @@ -412,6 +469,7 @@ export async function getCommandCompletion( context, result, input, + hasTrailingSpace, ); // Include sibling subcommand names when resolved to the @@ -449,6 +507,9 @@ export async function getCommandCompletion( parameterCompletions.separatorMode, ); closedSet = parameterCompletions.closedSet; + if (parameterCompletions.commitMode === "eager") { + commitMode = "eager"; + } } } else if (table !== undefined) { // descriptor is undefined: the suffix didn't resolve to any @@ -504,6 +565,7 @@ export async function getCommandCompletion( completions, separatorMode, closedSet, + commitMode, }; debug(`Command completion result:`, completionResult); diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 765a3c69b6..1872f34de1 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -109,7 +109,13 @@ export async function requestCompletion( } if (results.properties === undefined) { - return { groups: completions, prefixLength, separatorMode, closedSet }; + return { + groups: completions, + prefixLength, + separatorMode, + closedSet, + commitMode: "eager", + }; } const propertyCompletions = new Map(); @@ -129,7 +135,13 @@ export async function requestCompletion( } completions.push(...propertyCompletions.values()); - return { groups: completions, prefixLength, separatorMode, closedSet }; + return { + groups: completions, + prefixLength, + separatorMode, + closedSet, + commitMode: "eager", + }; } async function collectActionCompletions( diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index 733d310e24..e556c4259c 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -464,14 +464,15 @@ describe("Command Completion - startIndex", () => { ); expect(result).toBeDefined(); // "@comptest run bu" (16 chars) - // suffix is "bu", parameter parsing fully consumes "bu" - // remainderLength = 0 → startIndex = 16, then whitespace - // backing finds no space at suffix end → startIndex = 16. - expect(result!.startIndex).toBe(16); - // "bu" consumes the "task" arg → nextArgs is empty. - // Agent is not invoked (bare word, no implicit quotes). - // Only flags remain (none defined) → exhaustive. - expect(result!.closedSet).toBe(true); + // No trailing space → hasTrailingSpace=false. + // suffix is "bu", parameter parsing fully consumes "bu". + // lastCompletableParam="task", bare unquoted token, + // !hasTrailingSpace → exclusive path fires: backs up + // startIndex to the start of "bu" → 13. + expect(result!.startIndex).toBe(13); + // Agent IS invoked ("task" in agentCommandCompletions). + // Agent does not set closedSet → defaults to false. + expect(result!.closedSet).toBe(false); }); it("returns startIndex for nested command '@comptest nested sub '", async () => { @@ -700,15 +701,16 @@ describe("Command Completion - startIndex", () => { const result = await getCommandCompletion("@comptest ne", context); expect(result).toBeDefined(); // "ne" is fully consumed as the "task" arg by parameter - // parsing → startIndex = 12 (past command boundary 10), - // so subcommands are not included and separatorMode is - // not set. - expect(result!.separatorMode).toBeUndefined(); + // parsing. No trailing space → backs up to the start + // of "ne" → parameterCompletions.startIndex = 9. Since + // startIndex (9) ≤ commandConsumedLength (10), sibling + // subcommands are included with separatorMode="space". + expect(result!.separatorMode).toBe("space"); const subcommands = result!.completions.find( (g) => g.name === "Subcommands", ); - expect(subcommands).toBeUndefined(); - expect(result!.startIndex).toBe(12); + expect(subcommands).toBeDefined(); + expect(result!.startIndex).toBe(9); }); }); @@ -830,21 +832,21 @@ describe("Command Completion - startIndex", () => { expect(result!.closedSet).toBe(true); }); - it("returns flag value completions for non-boolean flag without trailing space", async () => { + it("returns flag names for non-boolean flag without trailing space", async () => { const result = await getCommandCompletion( "@comptest flagsonly --level", context, ); - // "--level" is a recognized number flag — the entire input - // is the longest valid prefix. startIndex = input.length - // because the flag name is consumed, not filter text. - // Completions should offer the flag's values (if any), - // not flag names. - expect(result.startIndex).toBe(27); // full input consumed + // "--level" is a recognized number flag, but no trailing + // space → user hasn't committed. Offer flag names at the + // start of "--level" (position 20) instead of flag values. + expect(result.startIndex).toBe(20); const flags = result.completions.find( (g) => g.name === "Command Flags", ); - expect(flags).toBeUndefined(); // flag names not offered when pending + expect(flags).toBeDefined(); + expect(flags!.completions).toContain("--debug"); + expect(flags!.completions).toContain("--level"); }); it("treats unrecognized flag prefix as filter text", async () => { @@ -949,15 +951,17 @@ describe("Command Completion - startIndex", () => { expect(subcommands!.completions).toContain("nested"); }); - it("drops subcommands when partial token is consumed past boundary", async () => { + it("includes subcommands when no trailing space at default command", async () => { const result = await getCommandCompletion("@comptest ne", context); - // "@comptest ne" — suffix is "ne", parameter parsing fully - // consumes it as the "task" arg → startIndex = 12, which - // exceeds commandBoundary (10) → subcommands dropped. + // "@comptest ne" — suffix is "ne", parameter parsing + // fully consumes it as the "task" arg. No trailing space + // backs up startIndex to 9, which is ≤ commandBoundary + // (10), so subcommands ARE included. const subcommands = result.completions.find( (g) => g.name === "Subcommands", ); - expect(subcommands).toBeUndefined(); + expect(subcommands).toBeDefined(); + expect(subcommands!.completions).toContain("nested"); }); }); @@ -1021,25 +1025,24 @@ describe("Command Completion - startIndex", () => { ); // '@comptest run "build"' (21 chars) // Token '"build"' is fully quoted → isFullyQuoted returns true. - // lastCompletableParam condition does NOT fire. - // remainderLength = 0 → startIndex = 21, unconditional - // whitespace backing finds no space at input[20]='"' → 21. - // "task" is filled, no flags → exhaustive. + // Fully-quoted tokens are committed by their closing quote; + // neither lastCompletableParam nor the fallback back-up fires. + // startIndex stays at 21. expect(result.startIndex).toBe(21); expect(result.closedSet).toBe(true); }); - it("does not adjust startIndex for bare unquoted token", async () => { + it("adjusts startIndex for bare unquoted token without trailing space", async () => { const result = await getCommandCompletion( "@comptest run bu", context, ); - // "bu" is not quoted at all → isFullyQuoted returns undefined. - // lastParamImplicitQuotes is false for "task" arg. - // lastCompletableParam condition does NOT fire. - // startIndex stays at 16 (end of input). - expect(result.startIndex).toBe(16); - expect(result.closedSet).toBe(true); + // "bu" is not quoted → isFullyQuoted returns undefined. + // No trailing space → lastCompletableParam exclusive path + // fires: backs up startIndex to the start of "bu" → 13. + // Agent IS invoked for "task" completions. + expect(result.startIndex).toBe(13); + expect(result.closedSet).toBe(false); }); }); @@ -1102,9 +1105,9 @@ describe("Command Completion - startIndex", () => { context, ); // Token '"東京タ"' is fully quoted → isFullyQuoted = true. - // lastCompletableParam exclusive path does NOT fire. - // All args consumed → nextArgs empty → agent not called. - // prefixLength never applies. + // Fully-quoted tokens are committed; neither + // lastCompletableParam nor the fallback back-up fires. + // startIndex stays at 23. expect(result.startIndex).toBe(23); expect(result.closedSet).toBe(true); // No Grammar group since agent wasn't invoked. @@ -1114,20 +1117,25 @@ describe("Command Completion - startIndex", () => { expect(grammar).toBeUndefined(); }); - it("bare unquoted token does not invoke grammar", async () => { + it("bare unquoted token invokes grammar without trailing space", async () => { const result = await getCommandCompletion( "@comptest grammar 東京タ", context, ); - // "東京タ" has no quotes, grammar command has no - // implicitQuotes → lastCompletableParam condition false. - // All args consumed → nextArgs empty → agent not called. - expect(result.startIndex).toBe(21); - expect(result.closedSet).toBe(true); + // "東京タ" has no quotes and no trailing space. + // lastCompletableParam exclusive path fires + // (!hasTrailingSpace && pendingFlag === undefined). + // Agent is invoked with grammar mock → matches "東京" → + // returns prefixLength=2. tokenStartIndex = 21-3 = 18, + // startIndex = 18 + 2 = 20. + expect(result.startIndex).toBe(20); + expect(result.closedSet).toBe(false); const grammar = result.completions.find( (g) => g.name === "Grammar", ); - expect(grammar).toBeUndefined(); + expect(grammar).toBeDefined(); + expect(grammar!.completions).toContain("タワー"); + expect(grammar!.completions).toContain("駅"); }); it("trailing space without text offers initial completions", async () => { @@ -1166,11 +1174,12 @@ describe("Command Completion - startIndex", () => { it("does not override startIndex when prefixLength is absent", async () => { // "run" handler returns groups without prefixLength. + // No trailing space → backs up to start of "bu". const result = await getCommandCompletion( "@comptest run bu", context, ); - expect(result.startIndex).toBe(16); + expect(result.startIndex).toBe(13); }); it("English prefix with space separator", async () => { diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index a7c5e82eb0..c23793af75 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -85,6 +85,13 @@ export type CommandCompletionResult = { // prefix-match any completion, the caller can skip refetching since // no other valid input exists. closedSet: boolean; + // Controls when a uniquely-satisfied completion triggers a re-fetch + // for the next hierarchical level. + // "explicit" — user must type a delimiter to commit; suppresses + // eager re-fetch on unique match. + // "eager" — re-fetch immediately on unique satisfaction. + // When omitted, defaults to "explicit". + commitMode?: "explicit" | "eager"; }; export type AppAgentStatus = { diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 4e59ce9af8..b51f445b90 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { CommandCompletionResult } from "agent-dispatcher"; -import { SeparatorMode } from "@typeagent/agent-sdk"; +import { CommitMode, SeparatorMode } from "@typeagent/agent-sdk"; import { SearchMenuItem, SearchMenuPosition, @@ -46,10 +46,16 @@ export interface ICompletionDispatcher { // result's constraints aren't satisfied yet (separator not typed, // or no completions exist). A re-fetch would return the same result. // 4. Uniquely satisfied — the user has exactly typed one completion -// entry (and it is not a prefix of any other). Always re-fetch to -// get the NEXT level's completions (e.g. agent name → subcommands). -// This re-fetch is unconditional: `closedSet` is irrelevant here -// because it describes THIS level, not the next's. +// entry (and it is not a prefix of any other). Gated by +// `commitMode`: +// commitMode="eager" → re-fetch immediately for the NEXT +// level's completions (e.g. variable-space grammar where +// tokens can abut without whitespace). +// commitMode="explicit" → suppress; the user hasn't committed +// yet (must type an explicit delimiter). B5 handles the +// separator arrival. +// `closedSet` is irrelevant here because it describes THIS +// level, not the next's. // - The `closedSet` flag controls the no-match fallthrough: when the trie // has zero matches for the typed prefix: // closedSet=true → reuse (closed set, nothing else exists) @@ -87,6 +93,19 @@ export class PartialCompletionSession { // which is a different question from whether THIS level is a closed set. private closedSet: boolean = false; + // Controls when "uniquely satisfied" triggers a re-fetch for the next + // hierarchical level. Defaults to "explicit" when omitted. + // + // "explicit" — tokens require an explicit delimiter (e.g. space) + // to commit. The user must type a separator after + // the matched completion to commit it. This suppresses + // B4 (uniquely satisfied) and lets B5 (committed-past- + // boundary) handle it when the separator actually arrives. + // "eager" — commit immediately on unique satisfaction + // (e.g. variable-space grammar where tokens can abut + // without whitespace). Re-fetches eagerly. + private commitMode: CommitMode = "explicit"; + // The in-flight completion request, or undefined when settled. private completionP: | Promise @@ -154,7 +173,8 @@ export class PartialCompletionSession { // (return true). // UNIQUE — prefix exactly matches one entry and is not a prefix of // any other; re-fetch for the NEXT level (return false). - // Unconditional — `closedSet` is irrelevant here. + // Gated by commitMode: "eager" re-fetches immediately; + // "explicit" defers to B5 (committed-past-boundary). // SHOW — constraints satisfied; update the menu. The final // return is `this.closedSet || this.menu.isActive()`: // reuse when the trie still has matches, or when the set @@ -173,12 +193,13 @@ export class PartialCompletionSession { // character was typed instead. The constraint can // never be satisfied, so treat as new input. // - // B. Hierarchical navigation — user completed this level; re-fetch for - // the NEXT level's completions. Unconditional (closedSet describes - // THIS level, not the next). + // B. Hierarchical navigation — user completed this level; re-fetch for\n // the NEXT level's completions. closedSet describes THIS level,\n // not the next.", "oldString": " // B. Hierarchical navigation — user completed this level; re-fetch for\n // the NEXT level's completions. Unconditional (closedSet describes\n // THIS level, not the next)." // 4. Uniquely satisfied — typed prefix exactly matches one completion and // is not a prefix of any other. Re-fetch for the // NEXT level (e.g. agent name → subcommands). + // Gated by commitMode: when "explicit", this is + // suppressed (B5 handles it once the user types a + // separator). When "eager", fires immediately. // 5. Committed past boundary — prefix contains a separator after a valid // completion match (e.g. "set " where "set" matches // but so does "setWindowState"). The user committed @@ -257,13 +278,15 @@ export class PartialCompletionSession { completionPrefix, position, ); - if (uniquelySatisfied) { - // [B4] The user has typed text that exactly matches one - // completion and is not a prefix of any other. We need the - // NEXT level's completions (e.g. agent name → subcommands), - // so re-fetch. + + // [B4] The user has typed text that exactly matches one + // completion and is not a prefix of any other. + // Only re-fetch when commitMode="eager" (tokens can abut + // without whitespace). When "explicit", B5 handles it + // once the user types a separator. + if (uniquelySatisfied && this.commitMode === "eager") { debug( - `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied`, + `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied (eager commit)`, ); return false; // RE-FETCH (hierarchical navigation) } @@ -321,6 +344,7 @@ export class PartialCompletionSession { this.separatorMode = result.separatorMode ?? "space"; this.closedSet = result.closedSet; + this.commitMode = result.commitMode ?? "explicit"; // Build completions preserving backend group order. const completions: SearchMenuItem[] = []; @@ -385,6 +409,7 @@ export class PartialCompletionSession { this.anchor = undefined; this.separatorMode = "space"; this.closedSet = false; + this.commitMode = "explicit"; } private requiresSeparator(): boolean { diff --git a/ts/packages/shell/src/renderer/src/prefixTree.ts b/ts/packages/shell/src/renderer/src/prefixTree.ts index 410e4b5df4..42f6df81a6 100644 --- a/ts/packages/shell/src/renderer/src/prefixTree.ts +++ b/ts/packages/shell/src/renderer/src/prefixTree.ts @@ -6,9 +6,9 @@ export class TSTNode { this.count = 0; } count: number; - left?: TSTNode; - middle?: TSTNode; - right?: TSTNode; + left: TSTNode | undefined; + middle: TSTNode | undefined; + right: TSTNode | undefined; data: TData | undefined; } diff --git a/ts/packages/shell/src/tsconfig.json b/ts/packages/shell/src/tsconfig.json index 65363f749c..16d00fb278 100644 --- a/ts/packages/shell/src/tsconfig.json +++ b/ts/packages/shell/src/tsconfig.json @@ -8,6 +8,7 @@ }, "include": [ "renderer/src/partialCompletionSession.ts", + "renderer/src/prefixTree.ts", "preload/electronTypes.ts", "preload/shellSettingsType.ts" ] diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index bd3872d16f..d896913e91 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -10,29 +10,77 @@ import { import { SearchMenuPosition } from "../src/preload/electronTypes.js"; import { CompletionGroup } from "@typeagent/agent-sdk"; import { CommandCompletionResult } from "agent-dispatcher"; +import { TST, BaseTSTData } from "../src/renderer/src/prefixTree.js"; // ── Helpers ────────────────────────────────────────────────────────────────── -type MockMenu = { - setChoices: jest.MockedFunction; - updatePrefix: jest.MockedFunction; - hasExactMatch: jest.MockedFunction; - hide: jest.MockedFunction; - isActive: jest.MockedFunction; -}; +// Duplicated from search.ts (not exported, avoids DOM imports). +function normalizeMatchText(text: string): string { + return text + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/\s/g, " ") + .toLowerCase(); +} -function makeMenu(): MockMenu { - return { - setChoices: jest.fn(), - updatePrefix: jest - .fn() - .mockReturnValue(false), - hasExactMatch: jest - .fn() - .mockReturnValue(false), - hide: jest.fn(), - isActive: jest.fn().mockReturnValue(false), - }; +interface TrieItem extends BaseTSTData { + matchText: string; + selectedText: string; + needQuotes?: boolean; + emojiChar?: string; +} + +// Real trie-backed ISearchMenu — same logic as SearchMenu but without DOM UI. +// Every method is a jest.fn() wrapping the real implementation so tests can +// assert on call counts and arguments. +class TestSearchMenu implements ISearchMenu { + private trie: TST = new TST(); + private _isActive: boolean = false; + + setChoices = jest.fn((choices) => { + this.trie.init(); + this._isActive = false; + for (const choice of choices) { + const normText = normalizeMatchText(choice.matchText); + if (!this.trie.get(normText)) { + this.trie.insert(normText, choice as TrieItem); + } + } + }); + + updatePrefix = jest.fn( + (prefix: string, _position: SearchMenuPosition): boolean => { + if (this.trie.size() === 0) { + return false; + } + const items = this.trie.dataWithPrefix(normalizeMatchText(prefix)); + const uniquelySatisfied = + items.length === 1 && + normalizeMatchText(items[0].matchText) === + normalizeMatchText(prefix); + const showMenu = items.length !== 0 && !uniquelySatisfied; + this._isActive = showMenu; + return uniquelySatisfied; + }, + ); + + hasExactMatch = jest.fn( + (text: string): boolean => { + return this.trie.contains(normalizeMatchText(text)); + }, + ); + + hide = jest.fn(() => { + this._isActive = false; + }); + + isActive = jest.fn(() => { + return this._isActive; + }); +} + +function makeMenu(): TestSearchMenu { + return new TestSearchMenu(); } type MockDispatcher = { @@ -105,7 +153,6 @@ describe("PartialCompletionSession — state transitions", () => { test("PENDING → ACTIVE: completions returned → setChoices + updatePrefix called", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["song", "shuffle"], 4); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -153,8 +200,6 @@ describe("PartialCompletionSession — state transitions", () => { test("ACTIVE → hide+keep: closedSet=true, trie has no matches — no re-fetch", async () => { const menu = makeMenu(); - // isActive returns true on first call (after setChoices), false on second (all filtered) - menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); const result = makeCompletionResult(["song"], 4, { closedSet: true, }); @@ -174,8 +219,6 @@ describe("PartialCompletionSession — state transitions", () => { test("ACTIVE → re-fetch: closedSet=false, trie has no matches — re-fetches", async () => { const menu = makeMenu(); - // isActive: true after initial load, false for "xyz" — non-exhaustive set - menu.isActive.mockReturnValueOnce(true).mockReturnValueOnce(false); const result = makeCompletionResult(["song"], 4); // closedSet=false default const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -195,11 +238,6 @@ describe("PartialCompletionSession — state transitions", () => { test("ACTIVE → backspace restores menu after no-match without re-fetch (closedSet=true)", async () => { const menu = makeMenu(); - // isActive: true after initial load, false for "xyz" prefix, true again for "so" - menu.isActive - .mockReturnValueOnce(true) // initial reuseSession after result - .mockReturnValueOnce(false) // "xyz" — trie no match - .mockReturnValueOnce(true); // "so" — trie matches "song" const result = makeCompletionResult(["song", "shuffle"], 4, { closedSet: true, }); @@ -266,7 +304,6 @@ describe("PartialCompletionSession — state transitions", () => { test("empty input fetches completions from backend", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["@"], 0, { separatorMode: "none", }); @@ -286,7 +323,6 @@ describe("PartialCompletionSession — state transitions", () => { test("empty input: second update reuses session without re-fetch", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["@"], 0, { separatorMode: "none", }); @@ -301,13 +337,11 @@ describe("PartialCompletionSession — state transitions", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); - test("empty input: unique match triggers re-fetch", async () => { + test("empty input: unique match triggers re-fetch (commitMode=eager)", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); - // updatePrefix returns true = uniquely satisfied - menu.updatePrefix.mockReturnValue(true); const result = makeCompletionResult(["@"], 0, { separatorMode: "none", + commitMode: "eager", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -322,15 +356,14 @@ describe("PartialCompletionSession — state transitions", () => { expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); }); - test("empty input: unique match triggers re-fetch even when closedSet=true", async () => { + test("empty input: unique match triggers re-fetch even when closedSet=true (commitMode=eager)", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); - menu.updatePrefix.mockReturnValue(true); // closedSet=true means exhaustive at THIS level, but uniquelySatisfied // means the user needs NEXT level completions — always re-fetch. const result = makeCompletionResult(["@"], 0, { closedSet: true, separatorMode: "none", + commitMode: "eager", }); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -346,9 +379,6 @@ describe("PartialCompletionSession — state transitions", () => { test("empty input: ambiguous prefix does not re-fetch", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); - // updatePrefix returns false = not uniquely satisfied - menu.updatePrefix.mockReturnValue(false); const result = makeCompletionResult(["@config", "@configure"], 0, { separatorMode: "none", }); @@ -370,7 +400,6 @@ describe("PartialCompletionSession — state transitions", () => { describe("PartialCompletionSession — result processing", () => { test("startIndex narrows the anchor (current) to input[0..startIndex]", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); // startIndex=4 means grammar consumed "play" (4 chars); the // trailing space is the separator between anchor and completions. const result = makeCompletionResult(["song", "shuffle"], 4); @@ -386,7 +415,6 @@ describe("PartialCompletionSession — result processing", () => { test("group order preserved: items appear in backend-provided group order", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const group1: CompletionGroup = { name: "grammar", completions: ["by"], @@ -422,7 +450,6 @@ describe("PartialCompletionSession — result processing", () => { test("needQuotes propagated from group to each SearchMenuItem", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const group: CompletionGroup = { name: "entities", completions: ["Bohemian Rhapsody"], @@ -452,7 +479,6 @@ describe("PartialCompletionSession — result processing", () => { test("unsorted group items are sorted alphabetically", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const group: CompletionGroup = { name: "test", completions: ["zebra", "apple", "mango"], @@ -500,7 +526,6 @@ describe("PartialCompletionSession — result processing", () => { test("separatorMode: typing separator shows menu without re-fetch", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { separatorMode: "space", }); @@ -521,7 +546,6 @@ describe("PartialCompletionSession — result processing", () => { test("separatorMode: menu shown after trailing space is typed", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { separatorMode: undefined, }); @@ -613,7 +637,6 @@ describe("PartialCompletionSession — @command routing", () => { test("@ command: separatorMode defers menu until space typed", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); // Backend returns subcommands with separatorMode: "space" // (anchor = "@config", subcommands follow after a space) const result = makeCompletionResult(["clear", "theme"], 7, { @@ -643,7 +666,6 @@ describe("PartialCompletionSession — @command routing", () => { test("@ command: typing after space filters within same session", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); // Backend: separatorMode, anchor = "@config" const result = makeCompletionResult(["clear", "theme"], 7, { separatorMode: "space", @@ -706,7 +728,6 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { test("returns suffix after anchor when input starts with anchor", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["song"], 4); const session = new PartialCompletionSession( menu, @@ -721,7 +742,6 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { test("returns undefined when input diverges from anchor", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["song"], 4); const session = new PartialCompletionSession( menu, @@ -737,7 +757,6 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { test("separatorMode: returns stripped prefix when separator is present", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { separatorMode: "space", }); @@ -776,7 +795,6 @@ describe("PartialCompletionSession — getCompletionPrefix", () => { describe("PartialCompletionSession — resetToIdle", () => { test("clears session so next update re-fetches", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); const session = new PartialCompletionSession(menu, dispatcher); @@ -792,7 +810,6 @@ describe("PartialCompletionSession — resetToIdle", () => { test("does not hide the menu (caller is responsible for that)", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); const session = new PartialCompletionSession(menu, dispatcher); @@ -850,7 +867,6 @@ describe("PartialCompletionSession — separatorMode edge cases", () => { test("separator already in input when result arrives shows menu immediately", async () => { // User typed "play " fast enough that the promise resolves after the space. const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["music"], 4, { separatorMode: "space", }); @@ -878,7 +894,6 @@ describe("PartialCompletionSession — separatorMode edge cases", () => { describe("PartialCompletionSession — miscellaneous", () => { test("getPosition returning undefined hides the menu", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); const result = makeCompletionResult(["song"], 4); const dispatcher = makeDispatcher(result); const session = new PartialCompletionSession(menu, dispatcher); @@ -895,7 +910,6 @@ describe("PartialCompletionSession — miscellaneous", () => { test("startIndex beyond input length falls back to full input as anchor", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); // startIndex=99 is beyond "play" (length 4) — anchor falls back to "play" const result = makeCompletionResult(["song"], 99, { separatorMode: "none", @@ -917,8 +931,6 @@ describe("PartialCompletionSession — miscellaneous", () => { describe("PartialCompletionSession — committed-past-boundary re-fetch", () => { test("closedSet=true: typing space after exact match triggers re-fetch", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); - menu.hasExactMatch.mockImplementation((text) => text === "set"); const result = makeCompletionResult( ["set", "setWindowState", "setWindowZoomLevel"], 4, @@ -942,8 +954,6 @@ describe("PartialCompletionSession — committed-past-boundary re-fetch", () => test("closedSet=true: typing multiple spaces after exact match triggers re-fetch", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); - menu.hasExactMatch.mockImplementation((text) => text === "set"); const result = makeCompletionResult( ["set", "setWindowState", "setWindowZoomLevel"], 4, @@ -963,8 +973,6 @@ describe("PartialCompletionSession — committed-past-boundary re-fetch", () => test("closedSet=true: typing punctuation after exact match triggers re-fetch", async () => { const menu = makeMenu(); - menu.isActive.mockReturnValue(true); - menu.hasExactMatch.mockImplementation((text) => text === "set"); const result = makeCompletionResult( ["set", "setWindowState", "setWindowZoomLevel"], 4, @@ -985,9 +993,6 @@ describe("PartialCompletionSession — committed-past-boundary re-fetch", () => test("closedSet=true: typing separator after non-matching text does NOT re-fetch", async () => { const menu = makeMenu(); - // isActive: true after initial load, false after "xyz " (no trie match) - menu.isActive.mockReturnValueOnce(true).mockReturnValue(false); - menu.hasExactMatch.mockReturnValue(false); const result = makeCompletionResult( ["set", "setWindowState", "setWindowZoomLevel"], 4, @@ -1006,3 +1011,128 @@ describe("PartialCompletionSession — committed-past-boundary re-fetch", () => expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); }); + +// ── commitMode ──────────────────────────────────────────────────────────────── + +describe("PartialCompletionSession — commitMode", () => { + test("commitMode=explicit (default): uniquely satisfied does NOT re-fetch", async () => { + const menu = makeMenu(); + // Default commitMode (omitted → "explicit") + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + session.update("play song", getPos); + + // "song" uniquely matched, but commitMode="explicit" — B4 suppressed + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("commitMode=explicit: uniquely satisfied + trailing space triggers re-fetch via B5", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // First: "play song" — uniquely satisfied but suppressed (no trailing space) + session.update("play song", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + + // Second: "play song " — user typed space → B5 fires (committed past boundary) + session.update("play song ", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play song ", + ); + }); + + test("commitMode=eager: uniquely satisfied triggers immediate re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + commitMode: "eager", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + session.update("play song", getPos); + + // commitMode="eager" — B4 fires immediately + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play song", + ); + }); + + test("commitMode=explicit: B5 committed-past-boundary still fires", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["set", "setWindowState"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "set " — contains separator after exact match → B5 fires + session.update("play set ", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play set ", + ); + }); + + test("commitMode=explicit: open-set no-matches still triggers re-fetch (C6 unaffected)", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: false, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "xyz" — no trie match, closedSet=false → C6 re-fetch + session.update("play xyz", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("commitMode defaults to explicit when omitted from result", async () => { + const menu = makeMenu(); + // No commitMode in result — defaults to "explicit" + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + session.update("play song", getPos); + + // Default commitMode="explicit" — B4 suppressed + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); From e479c54ee489fca51ff75ba868ab967eecac8a72 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 16:11:08 -0700 Subject: [PATCH 35/51] Extract SearchMenuBase to unify SearchMenu and TestSearchMenu --- ts/packages/shell/src/renderer/src/search.ts | 87 ++++-------------- .../shell/src/renderer/src/searchMenuBase.ts | 91 +++++++++++++++++++ ts/packages/shell/src/tsconfig.json | 1 + .../test/partialCompletionSession.spec.ts | 65 +++---------- 4 files changed, 122 insertions(+), 122 deletions(-) create mode 100644 ts/packages/shell/src/renderer/src/searchMenuBase.ts diff --git a/ts/packages/shell/src/renderer/src/search.ts b/ts/packages/shell/src/renderer/src/search.ts index d1315b6d86..91e02f50cb 100644 --- a/ts/packages/shell/src/renderer/src/search.ts +++ b/ts/packages/shell/src/renderer/src/search.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { TST } from "./prefixTree"; +import { SearchMenuBase } from "./searchMenuBase"; import { InlineSearchMenuUI } from "./searchMenuUI/inlineSearchMenuUI"; import { LocalSearchMenuUI } from "./searchMenuUI/localSearchMenuUI"; import { @@ -10,87 +10,38 @@ import { SearchMenuUI, } from "./searchMenuUI/searchMenuUI"; -function normalizeMatchText(text: string): string { - // Remove diacritical marks, and case replace any space characters with the normalized ' '. - return text - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, "") // Remove combining diacritical marks - .replace(/\s/g, " ") - .toLowerCase(); -} - -export class SearchMenu { +export class SearchMenu extends SearchMenuBase { private searchMenuUI: SearchMenuUI | undefined; - private trie: TST = new TST(); - private prefix: string | undefined; constructor( private readonly onCompletion: (item: SearchMenuItem) => void, private readonly inline: boolean, private readonly textEntry?: HTMLSpanElement, - ) {} - - public isActive() { - return this.searchMenuUI !== undefined; + ) { + super(); } - public setChoices(choices: SearchMenuItem[]) { - this.prefix = undefined; - this.trie.init(); - for (const choice of choices) { - // choices are sorted in priority order so prefer first norm text - const normText = normalizeMatchText(choice.matchText); - if (!this.trie.get(normText)) { - this.trie.insert(normText, choice); - } + protected override onShow( + position: SearchMenuPosition, + prefix: string, + items: SearchMenuItem[], + ): void { + if (this.searchMenuUI === undefined) { + this.searchMenuUI = this.inline + ? new InlineSearchMenuUI(this.onCompletion, this.textEntry!) + : new LocalSearchMenuUI(this.onCompletion); } + this.searchMenuUI.update({ position, prefix, items }); } - public numChoices() { - return this.trie.size(); + protected override onUpdatePosition(position: SearchMenuPosition): void { + this.searchMenuUI!.update({ position }); } - public hasExactMatch(text: string): boolean { - return this.trie.contains(normalizeMatchText(text)); - } - - public updatePrefix(prefix: string, position: SearchMenuPosition): boolean { - if (this.numChoices() === 0) { - return false; - } - - if (this.prefix === prefix && this.searchMenuUI !== undefined) { - // No need to update existing searchMenuUI, just update the position. - this.searchMenuUI.update({ position }); - return false; - } - - this.prefix = prefix; - const items = this.trie.dataWithPrefix(normalizeMatchText(prefix)); - const uniquelySatisfied = - items.length === 1 && - normalizeMatchText(items[0].matchText) === - normalizeMatchText(prefix); - const showMenu = items.length !== 0 && !uniquelySatisfied; - - if (showMenu) { - if (this.searchMenuUI === undefined) { - this.searchMenuUI = this.inline - ? new InlineSearchMenuUI(this.onCompletion, this.textEntry!) - : new LocalSearchMenuUI(this.onCompletion); - } - this.searchMenuUI.update({ position, prefix, items }); - } else { - this.hide(); - } - return uniquelySatisfied; + protected override onHide(): void { + this.searchMenuUI!.close(); + this.searchMenuUI = undefined; } - public hide() { - if (this.searchMenuUI) { - this.searchMenuUI.close(); - this.searchMenuUI = undefined; - } - } public handleMouseWheel(deltaY: number) { this.searchMenuUI?.adjustSelection(deltaY); } diff --git a/ts/packages/shell/src/renderer/src/searchMenuBase.ts b/ts/packages/shell/src/renderer/src/searchMenuBase.ts new file mode 100644 index 0000000000..82060bff72 --- /dev/null +++ b/ts/packages/shell/src/renderer/src/searchMenuBase.ts @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { TST } from "./prefixTree.js"; +import { + SearchMenuItem, + SearchMenuPosition, +} from "../../preload/electronTypes.js"; + +export function normalizeMatchText(text: string): string { + // Remove diacritical marks, and replace any space characters with normalized ' '. + return text + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") // Remove combining diacritical marks + .replace(/\s/g, " ") + .toLowerCase(); +} + +export class SearchMenuBase { + private trie: TST = new TST(); + private prefix: string | undefined; + private _active: boolean = false; + + public setChoices(choices: SearchMenuItem[]): void { + this.prefix = undefined; + this.trie.init(); + for (const choice of choices) { + // choices are sorted in priority order so prefer first norm text + const normText = normalizeMatchText(choice.matchText); + if (!this.trie.get(normText)) { + this.trie.insert(normText, choice); + } + } + } + + public numChoices(): number { + return this.trie.size(); + } + + public hasExactMatch(text: string): boolean { + return this.trie.contains(normalizeMatchText(text)); + } + + public updatePrefix(prefix: string, position: SearchMenuPosition): boolean { + if (this.trie.size() === 0) { + return false; + } + + if (this.prefix === prefix && this._active) { + this.onUpdatePosition(position); + return false; + } + + this.prefix = prefix; + const items = this.trie.dataWithPrefix(normalizeMatchText(prefix)); + const uniquelySatisfied = + items.length === 1 && + normalizeMatchText(items[0].matchText) === + normalizeMatchText(prefix); + const showMenu = items.length !== 0 && !uniquelySatisfied; + + if (showMenu) { + this._active = true; + this.onShow(position, prefix, items); + } else { + this.hide(); + } + return uniquelySatisfied; + } + + public hide(): void { + if (this._active) { + this._active = false; + this.onHide(); + } + } + + public isActive(): boolean { + return this._active; + } + + protected onShow( + _position: SearchMenuPosition, + _prefix: string, + _items: SearchMenuItem[], + ): void {} + + protected onUpdatePosition(_position: SearchMenuPosition): void {} + + protected onHide(): void {} +} diff --git a/ts/packages/shell/src/tsconfig.json b/ts/packages/shell/src/tsconfig.json index 16d00fb278..d4adab848c 100644 --- a/ts/packages/shell/src/tsconfig.json +++ b/ts/packages/shell/src/tsconfig.json @@ -9,6 +9,7 @@ "include": [ "renderer/src/partialCompletionSession.ts", "renderer/src/prefixTree.ts", + "renderer/src/searchMenuBase.ts", "preload/electronTypes.ts", "preload/shellSettingsType.ts" ] diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index d896913e91..aa674d8eec 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -10,73 +10,30 @@ import { import { SearchMenuPosition } from "../src/preload/electronTypes.js"; import { CompletionGroup } from "@typeagent/agent-sdk"; import { CommandCompletionResult } from "agent-dispatcher"; -import { TST, BaseTSTData } from "../src/renderer/src/prefixTree.js"; +import { SearchMenuBase } from "../src/renderer/src/searchMenuBase.js"; // ── Helpers ────────────────────────────────────────────────────────────────── -// Duplicated from search.ts (not exported, avoids DOM imports). -function normalizeMatchText(text: string): string { - return text - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, "") - .replace(/\s/g, " ") - .toLowerCase(); -} - -interface TrieItem extends BaseTSTData { - matchText: string; - selectedText: string; - needQuotes?: boolean; - emojiChar?: string; -} - -// Real trie-backed ISearchMenu — same logic as SearchMenu but without DOM UI. +// Real trie-backed ISearchMenu backed by SearchMenuBase. // Every method is a jest.fn() wrapping the real implementation so tests can // assert on call counts and arguments. -class TestSearchMenu implements ISearchMenu { - private trie: TST = new TST(); - private _isActive: boolean = false; - - setChoices = jest.fn((choices) => { - this.trie.init(); - this._isActive = false; - for (const choice of choices) { - const normText = normalizeMatchText(choice.matchText); - if (!this.trie.get(normText)) { - this.trie.insert(normText, choice as TrieItem); - } - } - }); +class TestSearchMenu extends SearchMenuBase { + setChoices = jest.fn((choices) => + super.setChoices(choices), + ); updatePrefix = jest.fn( - (prefix: string, _position: SearchMenuPosition): boolean => { - if (this.trie.size() === 0) { - return false; - } - const items = this.trie.dataWithPrefix(normalizeMatchText(prefix)); - const uniquelySatisfied = - items.length === 1 && - normalizeMatchText(items[0].matchText) === - normalizeMatchText(prefix); - const showMenu = items.length !== 0 && !uniquelySatisfied; - this._isActive = showMenu; - return uniquelySatisfied; - }, + (prefix: string, position: SearchMenuPosition): boolean => + super.updatePrefix(prefix, position), ); hasExactMatch = jest.fn( - (text: string): boolean => { - return this.trie.contains(normalizeMatchText(text)); - }, + (text: string): boolean => super.hasExactMatch(text), ); - hide = jest.fn(() => { - this._isActive = false; - }); + hide = jest.fn(() => super.hide()); - isActive = jest.fn(() => { - return this._isActive; - }); + isActive = jest.fn(() => super.isActive()); } function makeMenu(): TestSearchMenu { From 4aaf278571a0213d8d73edc00a6c870d1176fed6 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 16:53:52 -0700 Subject: [PATCH 36/51] Add reuseSession tracing and preserve session state on hide() - Add debug tracing to all decision points in reuseSession() (A1-A3, B4 explicit-deferred, no-position, C6 final decision) - Restore original hide() behavior: only cancel in-flight fetch and hide menu, preserving anchor/separatorMode/closedSet/commitMode so the session can be reused if update() is called again - Fix corrupted comment on line 196 (JSON replacement artifacts) --- .../renderer/src/partialCompletionSession.ts | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index b51f445b90..b560394164 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -131,10 +131,11 @@ export class PartialCompletionSession { this.startNewSession(input, getPosition); } - // Reset to IDLE and hide the menu. + // Hide the menu and cancel any in-flight fetch, but preserve session + // state so reuseSession() can still match the anchor if the user + // returns (e.g. cursor moved away then back without typing). public hide(): void { this.completionP = undefined; - this.resetSessionFields(); this.menu.hide(); } @@ -193,7 +194,9 @@ export class PartialCompletionSession { // character was typed instead. The constraint can // never be satisfied, so treat as new input. // - // B. Hierarchical navigation — user completed this level; re-fetch for\n // the NEXT level's completions. closedSet describes THIS level,\n // not the next.", "oldString": " // B. Hierarchical navigation — user completed this level; re-fetch for\n // the NEXT level's completions. Unconditional (closedSet describes\n // THIS level, not the next)." + // B. Hierarchical navigation — user completed this level; re-fetch for + // the NEXT level's completions. closedSet describes THIS level, + // not the next. // 4. Uniquely satisfied — typed prefix exactly matches one completion and // is not a prefix of any other. Re-fetch for the // NEXT level (e.g. agent name → subcommands). @@ -215,20 +218,24 @@ export class PartialCompletionSession { input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, ): boolean { + // PENDING — a fetch is already in flight. + if (this.completionP !== undefined) { + debug(`Partial completion pending: ${this.anchor}`); + return true; + } + // [A1] No session — IDLE state, must fetch. const anchor = this.anchor; if (anchor === undefined) { + debug(`Partial completion re-fetch: no active session (IDLE)`); return false; } - // PENDING — a fetch is already in flight. - if (this.completionP !== undefined) { - debug(`Partial completion pending: ${anchor}`); - return true; - } - // [A2] RE-FETCH — input moved past the anchor (e.g. backspace, new word). if (!input.startsWith(anchor)) { + debug( + `Partial completion re-fetch: anchor diverged (anchor='${anchor}', input='${input}')`, + ); return false; } @@ -259,6 +266,9 @@ export class PartialCompletionSession { // the completion *entries* are exhaustive, not whether // the anchor token can extend. The grammar may parse // the longer input on a completely different path. + debug( + `Partial completion re-fetch: non-separator after anchor (mode='${this.separatorMode}', rawPrefix='${rawPrefix}')`, + ); return false; // RE-FETCH (session invalidation) } } @@ -284,11 +294,16 @@ export class PartialCompletionSession { // Only re-fetch when commitMode="eager" (tokens can abut // without whitespace). When "explicit", B5 handles it // once the user types a separator. - if (uniquelySatisfied && this.commitMode === "eager") { + if (uniquelySatisfied) { + if (this.commitMode === "eager") { + debug( + `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied (eager commit)`, + ); + return false; // RE-FETCH (hierarchical navigation) + } debug( - `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied (eager commit)`, + `Partial completion: '${completionPrefix}' uniquely satisfied but commitMode='${this.commitMode}', deferring to separator`, ); - return false; // RE-FETCH (hierarchical navigation) } // [B5] Committed-past-boundary: the prefix contains whitespace @@ -306,6 +321,9 @@ export class PartialCompletionSession { return false; // RE-FETCH (hierarchical navigation) } } else { + debug( + `Partial completion: no position for prefix '${completionPrefix}', hiding menu`, + ); this.menu.hide(); } @@ -317,7 +335,12 @@ export class PartialCompletionSession { // closedSet=false → the set is NOT closed; the user may have // typed something valid that wasn't loaded, so // re-fetch with the longer input (open-set discovery). - return this.closedSet || this.menu.isActive(); + const active = this.menu.isActive(); + const reuse = this.closedSet || active; + debug( + `Partial completion ${reuse ? "reuse" : "re-fetch"}: closedSet=${this.closedSet}, menuActive=${active}`, + ); + return reuse; } // Start a new completion session: issue backend request and process result. From 01fc440430afbdaafb45b86233bbc02b801fbc16 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 17:09:43 -0700 Subject: [PATCH 37/51] Fix commitMode=explicit bypass when closedSet=false on unique match When uniquelySatisfied=true and commitMode=explicit, reuseSession() fell through to C6 where closedSet=false incorrectly triggered a re-fetch. Return true at B4 to hold the session until an explicit separator arrives. Add test for commitMode=explicit + closedSet=false + unique match. --- .../renderer/src/partialCompletionSession.ts | 1 + .../test/partialCompletionSession.spec.ts | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index b560394164..e1ff46602e 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -304,6 +304,7 @@ export class PartialCompletionSession { debug( `Partial completion: '${completionPrefix}' uniquely satisfied but commitMode='${this.commitMode}', deferring to separator`, ); + return true; // REUSE — wait for explicit separator before re-fetching } // [B5] Committed-past-boundary: the prefix contains whitespace diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index aa674d8eec..dc39382476 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -1092,4 +1092,26 @@ describe("PartialCompletionSession — commitMode", () => { // Default commitMode="explicit" — B4 suppressed expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); }); + + test("commitMode=explicit + closedSet=false: uniquely satisfied does NOT re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: false, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "song" uniquely matches — commitMode="explicit" must suppress re-fetch + // even though closedSet=false (closedSet describes THIS level, not next) + session.update("play song", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + + // Only after typing a separator should B5 trigger a re-fetch + session.update("play song ", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); }); From 52d4452c3c9a0b7dd63ece89be3da5270a1b94f8 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 17:33:03 -0700 Subject: [PATCH 38/51] Fix spacePunctuation separator stripping and add test coverage - Fix bug: stripLeadingSeparator() properly strips punctuation in spacePunctuation mode instead of only whitespace via trimStart() - Add tests for separatorMode 'spacePunctuation' and 'optional' - Add tests for emojiChar propagation from groups to menu items - Add tests for backend error handling (rejected promises) - Add tests for startIndex edge cases (negative, zero) - Add tests for mixed sorted/unsorted group ordering - Fix outdated hide() test to match anchor-preserving semantics - Add tsc:model to shell build script for test compilation --- ts/packages/shell/package.json | 2 +- .../renderer/src/partialCompletionSession.ts | 13 +- .../test/partialCompletionSession.spec.ts | 376 +++++++++++++++++- 3 files changed, 386 insertions(+), 5 deletions(-) diff --git a/ts/packages/shell/package.json b/ts/packages/shell/package.json index eeea633638..3743fa6120 100644 --- a/ts/packages/shell/package.json +++ b/ts/packages/shell/package.json @@ -19,7 +19,7 @@ "src/**/*ActionSchema*" ], "scripts": { - "build": "concurrently npm:prepare-vite npm:build:electron:esm", + "build": "concurrently npm:prepare-vite npm:build:electron:esm npm:tsc:model", "build:electron:cjs": "electron-vite build src/preload --config electron.vite.preload-cjs.config.mts --logLevel error", "build:electron:esm": "electron-vite build", "clean": "rimraf --glob out dist deploy *.tsbuildinfo *.done.build.log", diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index e1ff46602e..67fad83714 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -157,7 +157,7 @@ export class PartialCompletionSession { if (!this.separatorRegex().test(rawPrefix)) { return undefined; } - return rawPrefix.trimStart(); + return this.stripLeadingSeparator(rawPrefix); } return rawPrefix; } @@ -276,7 +276,7 @@ export class PartialCompletionSession { // SHOW — strip the leading separator (if any) before passing to the // menu trie, so completions like "music" match prefix "" not " ". const completionPrefix = requiresSep - ? rawPrefix.trimStart() + ? this.stripLeadingSeparator(rawPrefix) : rawPrefix; const position = getPosition(completionPrefix); @@ -446,4 +446,13 @@ export class PartialCompletionSession { private separatorRegex(): RegExp { return this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; } + + // Strip leading separator characters from rawPrefix. + // For "space" mode, only whitespace is stripped. + // For "spacePunctuation" mode, leading whitespace and punctuation are stripped. + private stripLeadingSeparator(rawPrefix: string): string { + return this.separatorMode === "space" + ? rawPrefix.trimStart() + : rawPrefix.replace(/^[\s\p{P}]+/u, ""); + } } diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts index dc39382476..dcf1760bd0 100644 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ b/ts/packages/shell/test/partialCompletionSession.spec.ts @@ -216,7 +216,7 @@ describe("PartialCompletionSession — state transitions", () => { expect(menu.updatePrefix).toHaveBeenCalledWith("so", anyPosition); }); - test("hide() resets to IDLE so next update starts fresh", async () => { + test("hide() preserves anchor so same input reuses session", async () => { const menu = makeMenu(); const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); const session = new PartialCompletionSession(menu, dispatcher); @@ -227,9 +227,27 @@ describe("PartialCompletionSession — state transitions", () => { session.hide(); expect(menu.hide).toHaveBeenCalled(); - // After hide, next update should fetch again + // After hide, same input within anchor reuses session — no re-fetch session.update("play", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("hide() preserves anchor: diverged input triggers re-fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + session.hide(); + + // Input that diverges from anchor triggers a new fetch + session.update("stop", getPos); expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "stop", + ); }); test("hide() cancels an in-flight request (stale result is ignored)", async () => { @@ -1115,3 +1133,357 @@ describe("PartialCompletionSession — commitMode", () => { expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); }); }); + +// ── separatorMode: "spacePunctuation" ───────────────────────────────────────── + +describe("PartialCompletionSession — separatorMode: spacePunctuation", () => { + test("space satisfies spacePunctuation separator", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Space satisfies spacePunctuation + session.update("play ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("punctuation satisfies spacePunctuation separator", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Punctuation mark satisfies spacePunctuation. + // The leading punctuation separator is stripped, just like whitespace. + session.update("play.mu", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("mu", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("letter after anchor triggers re-fetch under spacePunctuation", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // A letter is neither space nor punctuation — triggers re-fetch (A3) + session.update("playx", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "playx", + ); + }); + + test("no separator yet hides menu under spacePunctuation", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Exact anchor, no separator — menu hidden but session kept + menu.hide.mockClear(); + session.update("play", getPos); + + expect(menu.hide).toHaveBeenCalled(); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); + +// ── separatorMode: "optional" ───────────────────────────────────────────────── + +describe("PartialCompletionSession — separatorMode: optional", () => { + test("completions shown immediately without separator", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "optional", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // "optional" does not require a separator — menu shown immediately + // rawPrefix="" → updatePrefix("", ...) + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + }); + + test("typing after anchor filters within session", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music", "movie"], 4, { + separatorMode: "optional", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + session.update("playmu", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("mu", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); + +// ── emojiChar propagation ───────────────────────────────────────────────────── + +describe("PartialCompletionSession — emojiChar propagation", () => { + test("emojiChar from group is propagated to each SearchMenuItem", async () => { + const menu = makeMenu(); + const group: CompletionGroup = { + name: "agents", + completions: ["player", "calendar"], + emojiChar: "\uD83C\uDFB5", + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + selectedText: "player", + emojiChar: "\uD83C\uDFB5", + }), + expect.objectContaining({ + selectedText: "calendar", + emojiChar: "\uD83C\uDFB5", + }), + ]), + ); + }); + + test("emojiChar absent from group means no emojiChar on items", async () => { + const menu = makeMenu(); + const group: CompletionGroup = { + name: "plain", + completions: ["alpha"], + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as Record[]; + expect(items[0]).not.toHaveProperty("emojiChar"); + }); +}); + +// ── backend error handling ──────────────────────────────────────────────────── + +describe("PartialCompletionSession — backend error handling", () => { + test("rejected promise clears PENDING state so next update can proceed", async () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockRejectedValueOnce(new Error("network error")) + .mockResolvedValue(makeCompletionResult(["song"], 4)), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + // Flush rejected promise + catch handler + await Promise.resolve(); + await Promise.resolve(); + + // After rejection, anchor is still "play" with separatorMode="space". + // Diverged input triggers a re-fetch (anchor no longer matches). + session.update("stop", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "stop", + ); + }); + + test("rejected promise: same input within anchor does not re-fetch", async () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockRejectedValueOnce(new Error("network error")) + .mockResolvedValue(makeCompletionResult(["song"], 4)), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + await Promise.resolve(); + + // Same input — anchor still matches, reuse session (no re-fetch) + session.update("play", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("rejected promise does not leave session stuck in PENDING", async () => { + const menu = makeMenu(); + let rejectFn!: (e: Error) => void; + const rejecting = new Promise( + (_, reject) => (rejectFn = reject), + ); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValueOnce(rejecting) + .mockResolvedValue(makeCompletionResult(["song"], 4)), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + + // While PENDING, second update is suppressed + session.update("play more", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + + // Now reject + rejectFn(new Error("timeout")); + await Promise.resolve(); + await Promise.resolve(); + + // Session is no longer PENDING — diverged input triggers re-fetch + session.update("stop", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("rejected promise does not populate menu", async () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockRejectedValue(new Error("timeout")), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + await Promise.resolve(); + + // setChoices should only have the initial empty-array call, not real items + expect(menu.setChoices).toHaveBeenCalledTimes(1); + expect(menu.setChoices).toHaveBeenCalledWith([]); + }); +}); + +// ── startIndex edge cases ───────────────────────────────────────────────────── + +describe("PartialCompletionSession — startIndex edge cases", () => { + test("negative startIndex falls back to full input as anchor", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], -1, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Anchor is "play" (full input). rawPrefix="" → updatePrefix("", ...) + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + }); + + test("startIndex=0 sets empty anchor", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["play"], 0, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Anchor is "" (empty). rawPrefix="play" → updatePrefix("play", ...) + expect(menu.updatePrefix).toHaveBeenCalledWith("play", anyPosition); + }); +}); + +// ── mixed sorted/unsorted groups ────────────────────────────────────────────── + +describe("PartialCompletionSession — mixed sorted/unsorted groups", () => { + test("sorted group preserves order while unsorted group is alphabetized", async () => { + const menu = makeMenu(); + const sortedGroup: CompletionGroup = { + name: "grammar", + completions: ["zebra", "apple"], + sorted: true, + }; + const unsortedGroup: CompletionGroup = { + name: "entities", + completions: ["cherry", "banana"], + sorted: false, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [sortedGroup, unsortedGroup], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("x", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as { + selectedText: string; + sortIndex: number; + }[]; + const texts = items.map((i) => i.selectedText); + + // Sorted group: order preserved (zebra before apple) + // Unsorted group: alphabetized (banana before cherry) + // Cross-group: sorted group first + expect(texts).toEqual(["zebra", "apple", "banana", "cherry"]); + + // sortIndex is sequential across both groups + expect(items.map((i) => i.sortIndex)).toEqual([0, 1, 2, 3]); + }); +}); From 4e40064fa583d72e2030fb924941615b7bed5bcb Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 17:51:57 -0700 Subject: [PATCH 39/51] Reorganize partialCompletion tests into themed files Split monolithic partialCompletionSession.spec.ts (70 tests) into 6 themed spec files under test/partialCompletion/: - stateTransitions.spec.ts: IDLE/PENDING/ACTIVE transitions, closedSet, hide() - resultProcessing.spec.ts: startIndex, groups, needQuotes, emoji, edge cases - separatorMode.spec.ts: space, spacePunctuation, optional modes - commitMode.spec.ts: explicit vs eager, B5 boundary, hasExactMatch - publicAPI.spec.ts: getCompletionPrefix, resetToIdle, @command routing - errorHandling.spec.ts: rejected promises, recovery Shared test infrastructure extracted to helpers.ts. --- .../test/partialCompletion/commitMode.spec.ts | 243 +++ .../partialCompletion/errorHandling.spec.ts | 107 ++ .../shell/test/partialCompletion/helpers.ts | 91 + .../test/partialCompletion/publicAPI.spec.ts | 275 +++ .../resultProcessing.spec.ts | 282 ++++ .../partialCompletion/separatorMode.spec.ts | 258 +++ .../stateTransitions.spec.ts | 304 ++++ .../test/partialCompletionSession.spec.ts | 1489 ----------------- ts/packages/shell/test/tsconfig.json | 2 +- 9 files changed, 1561 insertions(+), 1490 deletions(-) create mode 100644 ts/packages/shell/test/partialCompletion/commitMode.spec.ts create mode 100644 ts/packages/shell/test/partialCompletion/errorHandling.spec.ts create mode 100644 ts/packages/shell/test/partialCompletion/helpers.ts create mode 100644 ts/packages/shell/test/partialCompletion/publicAPI.spec.ts create mode 100644 ts/packages/shell/test/partialCompletion/resultProcessing.spec.ts create mode 100644 ts/packages/shell/test/partialCompletion/separatorMode.spec.ts create mode 100644 ts/packages/shell/test/partialCompletion/stateTransitions.spec.ts delete mode 100644 ts/packages/shell/test/partialCompletionSession.spec.ts diff --git a/ts/packages/shell/test/partialCompletion/commitMode.spec.ts b/ts/packages/shell/test/partialCompletion/commitMode.spec.ts new file mode 100644 index 0000000000..acffed51b5 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/commitMode.spec.ts @@ -0,0 +1,243 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + PartialCompletionSession, + makeMenu, + makeDispatcher, + makeCompletionResult, + getPos, +} from "./helpers.js"; + +// ── commitMode ──────────────────────────────────────────────────────────────── + +describe("PartialCompletionSession — commitMode", () => { + test("commitMode=explicit (default): uniquely satisfied does NOT re-fetch", async () => { + const menu = makeMenu(); + // Default commitMode (omitted → "explicit") + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + session.update("play song", getPos); + + // "song" uniquely matched, but commitMode="explicit" — B4 suppressed + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("commitMode=explicit: uniquely satisfied + trailing space triggers re-fetch via B5", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // First: "play song" — uniquely satisfied but suppressed (no trailing space) + session.update("play song", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + + // Second: "play song " — user typed space → B5 fires (committed past boundary) + session.update("play song ", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play song ", + ); + }); + + test("commitMode=eager: uniquely satisfied triggers immediate re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + commitMode: "eager", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + session.update("play song", getPos); + + // commitMode="eager" — B4 fires immediately + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play song", + ); + }); + + test("commitMode=explicit: B5 committed-past-boundary still fires", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["set", "setWindowState"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "set " — contains separator after exact match → B5 fires + session.update("play set ", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play set ", + ); + }); + + test("commitMode=explicit: open-set no-matches still triggers re-fetch (C6 unaffected)", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: false, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "xyz" — no trie match, closedSet=false → C6 re-fetch + session.update("play xyz", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("commitMode defaults to explicit when omitted from result", async () => { + const menu = makeMenu(); + // No commitMode in result — defaults to "explicit" + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + session.update("play song", getPos); + + // Default commitMode="explicit" — B4 suppressed + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("commitMode=explicit + closedSet=false: uniquely satisfied does NOT re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + separatorMode: "space", + closedSet: false, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "song" uniquely matches — commitMode="explicit" must suppress re-fetch + // even though closedSet=false (closedSet describes THIS level, not next) + session.update("play song", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + + // Only after typing a separator should B5 trigger a re-fetch + session.update("play song ", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); +}); + +// ── committed-past-boundary (hasExactMatch) ─────────────────────────────────── + +describe("PartialCompletionSession — committed-past-boundary re-fetch", () => { + test("closedSet=true: typing space after exact match triggers re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // User types "set " — prefix is "set ", exact match "set" + separator + session.update("play set ", getPos); + + expect(menu.hasExactMatch).toHaveBeenCalledWith("set"); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play set ", + ); + }); + + test("closedSet=true: typing multiple spaces after exact match triggers re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // Double space after "set" + session.update("play set ", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("closedSet=true: typing punctuation after exact match triggers re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // Punctuation after "set" + session.update("play set.", getPos); + + expect(menu.hasExactMatch).toHaveBeenCalledWith("set"); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("closedSet=true: typing separator after non-matching text does NOT re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult( + ["set", "setWindowState", "setWindowZoomLevel"], + 4, + { separatorMode: "space", closedSet: true }, + ); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + // "xyz" is not a known completion — closedSet=true should suppress re-fetch + session.update("play xyz ", getPos); + + expect(menu.hasExactMatch).toHaveBeenCalledWith("xyz"); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ts/packages/shell/test/partialCompletion/errorHandling.spec.ts b/ts/packages/shell/test/partialCompletion/errorHandling.spec.ts new file mode 100644 index 0000000000..8e357f6101 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/errorHandling.spec.ts @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { jest } from "@jest/globals"; +import { + PartialCompletionSession, + ICompletionDispatcher, + CommandCompletionResult, + makeMenu, + makeCompletionResult, + getPos, +} from "./helpers.js"; + +describe("PartialCompletionSession — backend error handling", () => { + test("rejected promise clears PENDING state so next update can proceed", async () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockRejectedValueOnce(new Error("network error")) + .mockResolvedValue(makeCompletionResult(["song"], 4)), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + // Flush rejected promise + catch handler + await Promise.resolve(); + await Promise.resolve(); + + // After rejection, anchor is still "play" with separatorMode="space". + // Diverged input triggers a re-fetch (anchor no longer matches). + session.update("stop", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "stop", + ); + }); + + test("rejected promise: same input within anchor does not re-fetch", async () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockRejectedValueOnce(new Error("network error")) + .mockResolvedValue(makeCompletionResult(["song"], 4)), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + await Promise.resolve(); + + // Same input — anchor still matches, reuse session (no re-fetch) + session.update("play", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("rejected promise does not leave session stuck in PENDING", async () => { + const menu = makeMenu(); + let rejectFn!: (e: Error) => void; + const rejecting = new Promise( + (_, reject) => (rejectFn = reject), + ); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValueOnce(rejecting) + .mockResolvedValue(makeCompletionResult(["song"], 4)), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + + // While PENDING, second update is suppressed + session.update("play more", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + + // Now reject + rejectFn(new Error("timeout")); + await Promise.resolve(); + await Promise.resolve(); + + // Session is no longer PENDING — diverged input triggers re-fetch + session.update("stop", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("rejected promise does not populate menu", async () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockRejectedValue(new Error("timeout")), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + await Promise.resolve(); + + // setChoices should only have the initial empty-array call, not real items + expect(menu.setChoices).toHaveBeenCalledTimes(1); + expect(menu.setChoices).toHaveBeenCalledWith([]); + }); +}); diff --git a/ts/packages/shell/test/partialCompletion/helpers.ts b/ts/packages/shell/test/partialCompletion/helpers.ts new file mode 100644 index 0000000000..1b61fcad89 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/helpers.ts @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { jest, type jest as JestTypes } from "@jest/globals"; +import { + ICompletionDispatcher, + ISearchMenu, + PartialCompletionSession, +} from "../../src/renderer/src/partialCompletionSession.js"; +import { SearchMenuPosition } from "../../src/preload/electronTypes.js"; +import { CompletionGroup } from "@typeagent/agent-sdk"; +import { CommandCompletionResult } from "agent-dispatcher"; +import { SearchMenuBase } from "../../src/renderer/src/searchMenuBase.js"; + +export { PartialCompletionSession }; +export type { ICompletionDispatcher, ISearchMenu }; +export type { CompletionGroup }; +export type { CommandCompletionResult }; +export type { SearchMenuPosition }; + +type Mocked any> = T & + JestTypes.MockedFunction; + +// Real trie-backed ISearchMenu backed by SearchMenuBase. +// Every method is a jest.fn() wrapping the real implementation so tests can +// assert on call counts and arguments. +export class TestSearchMenu extends SearchMenuBase { + override setChoices: Mocked = jest.fn( + (...args: Parameters) => + super.setChoices(...args), + ) as any; + + override updatePrefix: Mocked = jest.fn( + (prefix: string, position: SearchMenuPosition): boolean => + super.updatePrefix(prefix, position), + ) as any; + + override hasExactMatch: Mocked = jest.fn( + (text: string): boolean => super.hasExactMatch(text), + ) as any; + + override hide: Mocked = jest.fn(() => + super.hide(), + ) as any; + + override isActive: Mocked = jest.fn(() => + super.isActive(), + ) as any; +} + +export function makeMenu(): TestSearchMenu { + return new TestSearchMenu(); +} + +export type MockDispatcher = { + getCommandCompletion: jest.MockedFunction< + ICompletionDispatcher["getCommandCompletion"] + >; +}; + +export function makeDispatcher( + result: CommandCompletionResult = { + startIndex: 0, + completions: [], + separatorMode: undefined, + closedSet: true, + }, +): MockDispatcher { + return { + getCommandCompletion: jest + .fn() + .mockResolvedValue(result), + }; +} + +export const anyPosition: SearchMenuPosition = { left: 0, bottom: 0 }; +export const getPos = (_prefix: string) => anyPosition; + +export function makeCompletionResult( + completions: string[], + startIndex: number = 0, + opts: Partial = {}, +): CommandCompletionResult { + const group: CompletionGroup = { name: "test", completions }; + return { + startIndex, + completions: [group], + closedSet: false, + ...opts, + }; +} diff --git a/ts/packages/shell/test/partialCompletion/publicAPI.spec.ts b/ts/packages/shell/test/partialCompletion/publicAPI.spec.ts new file mode 100644 index 0000000000..63a547df79 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/publicAPI.spec.ts @@ -0,0 +1,275 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { jest } from "@jest/globals"; +import { + PartialCompletionSession, + ICompletionDispatcher, + makeMenu, + makeDispatcher, + makeCompletionResult, + getPos, + anyPosition, +} from "./helpers.js"; + +// ── getCompletionPrefix ─────────────────────────────────────────────────────── + +describe("PartialCompletionSession — getCompletionPrefix", () => { + test("returns undefined when session is IDLE", () => { + const session = new PartialCompletionSession( + makeMenu(), + makeDispatcher(), + ); + expect(session.getCompletionPrefix("anything")).toBeUndefined(); + }); + + test("returns suffix after anchor when input starts with anchor", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play song", getPos); + await Promise.resolve(); + + expect(session.getCompletionPrefix("play song")).toBe("song"); + }); + + test("returns undefined when input diverges from anchor", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play song", getPos); + await Promise.resolve(); + + // Input no longer starts with anchor "play" + expect(session.getCompletionPrefix("stop")).toBeUndefined(); + }); + + test("separatorMode: returns stripped prefix when separator is present", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play", getPos); + await Promise.resolve(); + + // Separator + typed text: prefix should be "mu" (space stripped) + expect(session.getCompletionPrefix("play mu")).toBe("mu"); + }); + + test("separatorMode: returns undefined when separator is absent", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const session = new PartialCompletionSession( + menu, + makeDispatcher(result), + ); + + session.update("play", getPos); + await Promise.resolve(); + + // No separator yet — undefined means no replacement should happen + expect(session.getCompletionPrefix("play")).toBeUndefined(); + }); +}); + +// ── resetToIdle ─────────────────────────────────────────────────────────────── + +describe("PartialCompletionSession — resetToIdle", () => { + test("clears session so next update re-fetches", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); // → ACTIVE + + session.resetToIdle(); + + // After reset, next update should fetch fresh completions + session.update("play song", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + }); + + test("does not hide the menu (caller is responsible for that)", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); + + menu.hide.mockClear(); + session.resetToIdle(); + + expect(menu.hide).not.toHaveBeenCalled(); + }); +}); + +// ── @-command routing ───────────────────────────────────────────────────────── + +describe("PartialCompletionSession — @command routing", () => { + test("@ command with trailing space fetches full input", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config ", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( + "@config ", + ); + }); + + test("@ command with partial word fetches full input (backend filters)", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config c", getPos); + + // Backend receives full input and returns completions with the + // correct startIndex; no word-boundary truncation needed. + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( + "@config c", + ); + }); + + test("@ command with no space fetches full input", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith("@config"); + }); + + test("@ command in PENDING state does not re-fetch", () => { + const menu = makeMenu(); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValue(new Promise(() => {})), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config ", getPos); + session.update("@config c", getPos); // same anchor: "@config " — PENDING reuse + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("@ command: separatorMode defers menu until space typed", async () => { + const menu = makeMenu(); + // Backend returns subcommands with separatorMode: "space" + // (anchor = "@config", subcommands follow after a space) + const result = makeCompletionResult(["clear", "theme"], 7, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // User types "@config" → completions loaded, menu deferred (no separator yet) + session.update("@config", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "clear" }), + ]), + ); + expect(menu.updatePrefix).not.toHaveBeenCalled(); + + // User types space → separator present, menu appears + session.update("@config ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + // No re-fetch — same session handles both states + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("@ command: typing after space filters within same session", async () => { + const menu = makeMenu(); + // Backend: separatorMode, anchor = "@config" + const result = makeCompletionResult(["clear", "theme"], 7, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@config", getPos); + await Promise.resolve(); + + // Type space + partial subcommand + session.update("@config cl", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("cl", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("@ command: empty result (closedSet=true) suppresses re-fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@unknown", getPos); + await Promise.resolve(); // → empty completions, closedSet=true + + // Still within anchor — no re-fetch + session.update("@unknownmore", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("@ command: backspace past anchor after empty result triggers re-fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("@unknown", getPos); + await Promise.resolve(); // → empty completions with current="@unknown" + + // Backspace past anchor + session.update("@unknow", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "@unknow", + ); + }); +}); + +// ── miscellaneous ───────────────────────────────────────────────────────────── + +describe("PartialCompletionSession — miscellaneous", () => { + test("getPosition returning undefined hides the menu", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); + + menu.hide.mockClear(); + // getPosition returns undefined (e.g. caret not found) + session.update("play song", () => undefined); + + expect(menu.hide).toHaveBeenCalled(); + }); +}); diff --git a/ts/packages/shell/test/partialCompletion/resultProcessing.spec.ts b/ts/packages/shell/test/partialCompletion/resultProcessing.spec.ts new file mode 100644 index 0000000000..8cd3c30001 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/resultProcessing.spec.ts @@ -0,0 +1,282 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + PartialCompletionSession, + CommandCompletionResult, + CompletionGroup, + makeMenu, + makeDispatcher, + makeCompletionResult, + getPos, + anyPosition, +} from "./helpers.js"; + +describe("PartialCompletionSession — result processing", () => { + test("startIndex narrows the anchor (current) to input[0..startIndex]", async () => { + const menu = makeMenu(); + // startIndex=4 means grammar consumed "play" (4 chars); the + // trailing space is the separator between anchor and completions. + const result = makeCompletionResult(["song", "shuffle"], 4); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play song", getPos); + await Promise.resolve(); + + // prefix should be "song" (the text after anchor "play" + separator " ") + expect(menu.updatePrefix).toHaveBeenCalledWith("song", anyPosition); + }); + + test("group order preserved: items appear in backend-provided group order", async () => { + const menu = makeMenu(); + const group1: CompletionGroup = { + name: "grammar", + completions: ["by"], + sorted: true, + }; + const group2: CompletionGroup = { + name: "entities", + completions: ["Bohemian Rhapsody"], + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 4, + completions: [group1, group2], + closedSet: false, + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as { + sortIndex: number; + selectedText: string; + }[]; + const byIndex = items.find((i) => i.selectedText === "by")!.sortIndex; + const bohIndex = items.find( + (i) => i.selectedText === "Bohemian Rhapsody", + )!.sortIndex; + expect(byIndex).toBeLessThan(bohIndex); + }); + + test("needQuotes propagated from group to each SearchMenuItem", async () => { + const menu = makeMenu(); + const group: CompletionGroup = { + name: "entities", + completions: ["Bohemian Rhapsody"], + needQuotes: true, + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 4, + completions: [group], + closedSet: false, + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + selectedText: "Bohemian Rhapsody", + needQuotes: true, + }), + ]), + ); + }); + + test("unsorted group items are sorted alphabetically", async () => { + const menu = makeMenu(); + const group: CompletionGroup = { + name: "test", + completions: ["zebra", "apple", "mango"], + sorted: false, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("x", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as { selectedText: string }[]; + const texts = items.map((i) => i.selectedText); + expect(texts).toEqual(["apple", "mango", "zebra"]); + }); + + test("empty completions list does not call setChoices with items", async () => { + const menu = makeMenu(); + const result: CommandCompletionResult = { + startIndex: 0, + completions: [{ name: "empty", completions: [] }], + closedSet: false, + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Only the initial setChoices([]) call (cancel) should have been made + expect(menu.setChoices).toHaveBeenCalledTimes(1); + expect(menu.setChoices).toHaveBeenCalledWith([]); + }); + + test("emojiChar from group is propagated to each SearchMenuItem", async () => { + const menu = makeMenu(); + const group: CompletionGroup = { + name: "agents", + completions: ["player", "calendar"], + emojiChar: "\uD83C\uDFB5", + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ + selectedText: "player", + emojiChar: "\uD83C\uDFB5", + }), + expect.objectContaining({ + selectedText: "calendar", + emojiChar: "\uD83C\uDFB5", + }), + ]), + ); + }); + + test("emojiChar absent from group means no emojiChar on items", async () => { + const menu = makeMenu(); + const group: CompletionGroup = { + name: "plain", + completions: ["alpha"], + sorted: true, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [group], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as Record[]; + expect(items[0]).not.toHaveProperty("emojiChar"); + }); + + test("sorted group preserves order while unsorted group is alphabetized", async () => { + const menu = makeMenu(); + const sortedGroup: CompletionGroup = { + name: "grammar", + completions: ["zebra", "apple"], + sorted: true, + }; + const unsortedGroup: CompletionGroup = { + name: "entities", + completions: ["cherry", "banana"], + sorted: false, + }; + const result: CommandCompletionResult = { + startIndex: 0, + completions: [sortedGroup, unsortedGroup], + closedSet: false, + separatorMode: "none", + }; + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("x", getPos); + await Promise.resolve(); + + const calls = menu.setChoices.mock.calls; + const items = calls[calls.length - 1][0] as { + selectedText: string; + sortIndex: number; + }[]; + const texts = items.map((i) => i.selectedText); + + // Sorted group: order preserved (zebra before apple) + // Unsorted group: alphabetized (banana before cherry) + // Cross-group: sorted group first + expect(texts).toEqual(["zebra", "apple", "banana", "cherry"]); + + // sortIndex is sequential across both groups + expect(items.map((i) => i.sortIndex)).toEqual([0, 1, 2, 3]); + }); + + test("negative startIndex falls back to full input as anchor", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], -1, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Anchor is "play" (full input). rawPrefix="" → updatePrefix("", ...) + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + }); + + test("startIndex=0 sets empty anchor", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["play"], 0, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Anchor is "" (empty). rawPrefix="play" → updatePrefix("play", ...) + expect(menu.updatePrefix).toHaveBeenCalledWith("play", anyPosition); + }); + + test("startIndex beyond input length falls back to full input as anchor", async () => { + const menu = makeMenu(); + // startIndex=99 is beyond "play" (length 4) — anchor falls back to "play" + const result = makeCompletionResult(["song"], 99, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Anchor is "play" (full input). reuseSession is called with the captured + // input "play", so rawPrefix="" and updatePrefix is called with "". + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + }); +}); diff --git a/ts/packages/shell/test/partialCompletion/separatorMode.spec.ts b/ts/packages/shell/test/partialCompletion/separatorMode.spec.ts new file mode 100644 index 0000000000..d859e273f6 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/separatorMode.spec.ts @@ -0,0 +1,258 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + PartialCompletionSession, + makeMenu, + makeDispatcher, + makeCompletionResult, + getPos, + anyPosition, +} from "./helpers.js"; + +// ── separatorMode: "space" ──────────────────────────────────────────────────── + +describe("PartialCompletionSession — separatorMode: space", () => { + test("defers menu display until trailing space is present", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // Input without trailing space: "play" — choices are loaded but menu is not shown + session.update("play", getPos); + await Promise.resolve(); + + // setChoices IS called with actual items (trie is populated for later) + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "music" }), + ]), + ); + // But updatePrefix is NOT called yet (menu not shown) + expect(menu.updatePrefix).not.toHaveBeenCalled(); + }); + + test("typing separator shows menu without re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // First update: "play" — deferred (separatorMode, no trailing space) + session.update("play", getPos); + await Promise.resolve(); + + // Second update: "play " — separator typed, menu should appear + session.update("play ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + // No re-fetch — same dispatcher call count + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("menu shown after trailing space is typed", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: undefined, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "music" }), + ]), + ); + }); +}); + +// ── separatorMode: "spacePunctuation" ───────────────────────────────────────── + +describe("PartialCompletionSession — separatorMode: spacePunctuation", () => { + test("space satisfies spacePunctuation separator", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Space satisfies spacePunctuation + session.update("play ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("punctuation satisfies spacePunctuation separator", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Punctuation mark satisfies spacePunctuation. + // The leading punctuation separator is stripped, just like whitespace. + session.update("play.mu", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("mu", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("letter after anchor triggers re-fetch under spacePunctuation", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // A letter is neither space nor punctuation — triggers re-fetch (A3) + session.update("playx", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "playx", + ); + }); + + test("no separator yet hides menu under spacePunctuation", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "spacePunctuation", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Exact anchor, no separator — menu hidden but session kept + menu.hide.mockClear(); + session.update("play", getPos); + + expect(menu.hide).toHaveBeenCalled(); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); + +// ── separatorMode: "optional" ───────────────────────────────────────────────── + +describe("PartialCompletionSession — separatorMode: optional", () => { + test("completions shown immediately without separator", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "optional", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // "optional" does not require a separator — menu shown immediately + // rawPrefix="" → updatePrefix("", ...) + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + }); + + test("typing after anchor filters within session", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music", "movie"], 4, { + separatorMode: "optional", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + session.update("playmu", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("mu", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); + +// ── separatorMode edge cases ───────────────────────────────────────────────── + +describe("PartialCompletionSession — separatorMode edge cases", () => { + test("re-update with same input before separator does not re-fetch", async () => { + // Regression: selectionchange can fire again with the same input while + // the session is waiting for a separator. Must not trigger a re-fetch. + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); // deferred — waiting for separator + + session.update("play", getPos); // same input again (e.g. selectionchange) + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("input diverges before separator arrives triggers re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); // deferred + + // User typed a non-space character instead of a separator + session.update("play2", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play2", + ); + }); + + test("separator already in input when result arrives shows menu immediately", async () => { + // User typed "play " fast enough that the promise resolves after the space. + const menu = makeMenu(); + const result = makeCompletionResult(["music"], 4, { + separatorMode: "space", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + // Fetch was issued for "play" but by the time it resolves the user + // has already moved on; a second update for "play " is already active. + // Simulate by updating to "play " *before* awaiting. + session.update("play", getPos); + // (promise not yet resolved — we rely on the .then() calling reuseSession + // with the captured "play" input, which has no separator, so menu stays + // hidden. A subsequent update("play ", ...) then shows it.) + await Promise.resolve(); + + session.update("play ", getPos); + + expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ts/packages/shell/test/partialCompletion/stateTransitions.spec.ts b/ts/packages/shell/test/partialCompletion/stateTransitions.spec.ts new file mode 100644 index 0000000000..33eadc8634 --- /dev/null +++ b/ts/packages/shell/test/partialCompletion/stateTransitions.spec.ts @@ -0,0 +1,304 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { jest } from "@jest/globals"; +import { + PartialCompletionSession, + ICompletionDispatcher, + CommandCompletionResult, + makeMenu, + makeDispatcher, + makeCompletionResult, + getPos, + anyPosition, +} from "./helpers.js"; + +describe("PartialCompletionSession — state transitions", () => { + test("IDLE → PENDING: first update triggers a backend fetch", () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith("play"); + }); + + test("PENDING: second update while promise is in-flight does not re-fetch", () => { + const menu = makeMenu(); + // Never-resolving promise keeps session in PENDING + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValue(new Promise(() => {})), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + session.update("play s", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("PENDING → ACTIVE: completions returned → setChoices + updatePrefix called", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song", "shuffle"], 4); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // flush microtask + + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "shuffle" }), + expect.objectContaining({ selectedText: "song" }), + ]), + ); + expect(menu.updatePrefix).toHaveBeenCalled(); + }); + + test("PENDING → ACTIVE: empty result (closedSet=true) suppresses re-fetch while input has same prefix", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + // Empty completions + closedSet=true — no new fetch even with extended input + session.update("play s", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("empty result: backspace past anchor triggers a new fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); // → ACTIVE (empty, closedSet=true) with current="play" + + // Backspace past anchor + session.update("pla", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("pla"); + }); + + test("ACTIVE → hide+keep: closedSet=true, trie has no matches — no re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4, { + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // User types more; trie returns no matches but input is within anchor. + // closedSet=true → exhaustive set, no point re-fetching. + session.update("play xyz", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(menu.hide).toHaveBeenCalled(); + }); + + test("ACTIVE → re-fetch: closedSet=false, trie has no matches — re-fetches", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song"], 4); // closedSet=false default + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // User types text with no trie match. closedSet=false → set is NOT + // exhaustive, so we should re-fetch in case the backend knows more. + session.update("play xyz", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "play xyz", + ); + }); + + test("ACTIVE → backspace restores menu after no-match without re-fetch (closedSet=true)", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["song", "shuffle"], 4, { + closedSet: true, + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play ", getPos); + await Promise.resolve(); // → ACTIVE, anchor = "play" + + // User types non-matching text. closedSet=true → no re-fetch. + session.update("play xyz", getPos); + expect(menu.hide).toHaveBeenCalled(); + + // User backspaces to matching prefix — menu reappears without re-fetch + menu.updatePrefix.mockClear(); + session.update("play so", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + expect(menu.updatePrefix).toHaveBeenCalledWith("so", anyPosition); + }); + + test("hide() preserves anchor so same input reuses session", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + session.hide(); + expect(menu.hide).toHaveBeenCalled(); + + // After hide, same input within anchor reuses session — no re-fetch + session.update("play", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("hide() preserves anchor: diverged input triggers re-fetch", async () => { + const menu = makeMenu(); + const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + await Promise.resolve(); + + session.hide(); + + // Input that diverges from anchor triggers a new fetch + session.update("stop", getPos); + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( + "stop", + ); + }); + + test("hide() cancels an in-flight request (stale result is ignored)", async () => { + const menu = makeMenu(); + let resolve!: (v: CommandCompletionResult) => void; + const pending = new Promise( + (r) => (resolve = r), + ); + const dispatcher: ICompletionDispatcher = { + getCommandCompletion: jest + .fn() + .mockReturnValue(pending), + }; + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("play", getPos); + session.hide(); // cancels the promise + + // Now resolve the stale promise — should be a no-op + resolve(makeCompletionResult(["song"], 4)); + await Promise.resolve(); + + expect(menu.setChoices).not.toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "song" }), + ]), + ); + }); + + test("empty input fetches completions from backend", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["@"], 0, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith(""); + expect(menu.setChoices).toHaveBeenCalledWith( + expect.arrayContaining([ + expect.objectContaining({ selectedText: "@" }), + ]), + ); + }); + + test("empty input: second update reuses session without re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["@"], 0, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); + + test("empty input: unique match triggers re-fetch (commitMode=eager)", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["@"], 0, { + separatorMode: "none", + commitMode: "eager", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("@", getPos); + + // "@" uniquely matches the only completion — triggers re-fetch + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); + }); + + test("empty input: unique match triggers re-fetch even when closedSet=true (commitMode=eager)", async () => { + const menu = makeMenu(); + // closedSet=true means exhaustive at THIS level, but uniquelySatisfied + // means the user needs NEXT level completions — always re-fetch. + const result = makeCompletionResult(["@"], 0, { + closedSet: true, + separatorMode: "none", + commitMode: "eager", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("@", getPos); + + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); + expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); + }); + + test("empty input: ambiguous prefix does not re-fetch", async () => { + const menu = makeMenu(); + const result = makeCompletionResult(["@config", "@configure"], 0, { + separatorMode: "none", + }); + const dispatcher = makeDispatcher(result); + const session = new PartialCompletionSession(menu, dispatcher); + + session.update("", getPos); + await Promise.resolve(); + + session.update("@config", getPos); + + // "@config" is a prefix of "@configure" — reuse, no re-fetch + expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ts/packages/shell/test/partialCompletionSession.spec.ts b/ts/packages/shell/test/partialCompletionSession.spec.ts deleted file mode 100644 index dcf1760bd0..0000000000 --- a/ts/packages/shell/test/partialCompletionSession.spec.ts +++ /dev/null @@ -1,1489 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { jest } from "@jest/globals"; -import { - ICompletionDispatcher, - ISearchMenu, - PartialCompletionSession, -} from "../src/renderer/src/partialCompletionSession.js"; -import { SearchMenuPosition } from "../src/preload/electronTypes.js"; -import { CompletionGroup } from "@typeagent/agent-sdk"; -import { CommandCompletionResult } from "agent-dispatcher"; -import { SearchMenuBase } from "../src/renderer/src/searchMenuBase.js"; - -// ── Helpers ────────────────────────────────────────────────────────────────── - -// Real trie-backed ISearchMenu backed by SearchMenuBase. -// Every method is a jest.fn() wrapping the real implementation so tests can -// assert on call counts and arguments. -class TestSearchMenu extends SearchMenuBase { - setChoices = jest.fn((choices) => - super.setChoices(choices), - ); - - updatePrefix = jest.fn( - (prefix: string, position: SearchMenuPosition): boolean => - super.updatePrefix(prefix, position), - ); - - hasExactMatch = jest.fn( - (text: string): boolean => super.hasExactMatch(text), - ); - - hide = jest.fn(() => super.hide()); - - isActive = jest.fn(() => super.isActive()); -} - -function makeMenu(): TestSearchMenu { - return new TestSearchMenu(); -} - -type MockDispatcher = { - getCommandCompletion: jest.MockedFunction< - ICompletionDispatcher["getCommandCompletion"] - >; -}; - -function makeDispatcher( - result: CommandCompletionResult = { - startIndex: 0, - completions: [], - separatorMode: undefined, - closedSet: true, - }, -): MockDispatcher { - return { - getCommandCompletion: jest - .fn() - .mockResolvedValue(result), - }; -} - -const anyPosition: SearchMenuPosition = { left: 0, bottom: 0 }; -const getPos = (_prefix: string) => anyPosition; - -function makeCompletionResult( - completions: string[], - startIndex: number = 0, - opts: Partial = {}, -): CommandCompletionResult { - const group: CompletionGroup = { name: "test", completions }; - return { - startIndex, - completions: [group], - closedSet: false, - ...opts, - }; -} - -// ── State machine tests ─────────────────────────────────────────────────────── - -describe("PartialCompletionSession — state transitions", () => { - test("IDLE → PENDING: first update triggers a backend fetch", () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith("play"); - }); - - test("PENDING: second update while promise is in-flight does not re-fetch", () => { - const menu = makeMenu(); - // Never-resolving promise keeps session in PENDING - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockReturnValue(new Promise(() => {})), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - session.update("play s", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("PENDING → ACTIVE: completions returned → setChoices + updatePrefix called", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song", "shuffle"], 4); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // flush microtask - - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ selectedText: "shuffle" }), - expect.objectContaining({ selectedText: "song" }), - ]), - ); - expect(menu.updatePrefix).toHaveBeenCalled(); - }); - - test("PENDING → ACTIVE: empty result (closedSet=true) suppresses re-fetch while input has same prefix", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Empty completions + closedSet=true — no new fetch even with extended input - session.update("play s", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("empty result: backspace past anchor triggers a new fetch", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); // → ACTIVE (empty, closedSet=true) with current="play" - - // Backspace past anchor - session.update("pla", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("pla"); - }); - - test("ACTIVE → hide+keep: closedSet=true, trie has no matches — no re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4, { - closedSet: true, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - // User types more; trie returns no matches but input is within anchor. - // closedSet=true → exhaustive set, no point re-fetching. - session.update("play xyz", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - expect(menu.hide).toHaveBeenCalled(); - }); - - test("ACTIVE → re-fetch: closedSet=false, trie has no matches — re-fetches", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4); // closedSet=false default - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - // User types text with no trie match. closedSet=false → set is NOT - // exhaustive, so we should re-fetch in case the backend knows more. - session.update("play xyz", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "play xyz", - ); - }); - - test("ACTIVE → backspace restores menu after no-match without re-fetch (closedSet=true)", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song", "shuffle"], 4, { - closedSet: true, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - // User types non-matching text. closedSet=true → no re-fetch. - session.update("play xyz", getPos); - expect(menu.hide).toHaveBeenCalled(); - - // User backspaces to matching prefix — menu reappears without re-fetch - menu.updatePrefix.mockClear(); - session.update("play so", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - expect(menu.updatePrefix).toHaveBeenCalledWith("so", anyPosition); - }); - - test("hide() preserves anchor so same input reuses session", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - session.hide(); - expect(menu.hide).toHaveBeenCalled(); - - // After hide, same input within anchor reuses session — no re-fetch - session.update("play", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("hide() preserves anchor: diverged input triggers re-fetch", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - session.hide(); - - // Input that diverges from anchor triggers a new fetch - session.update("stop", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "stop", - ); - }); - - test("hide() cancels an in-flight request (stale result is ignored)", async () => { - const menu = makeMenu(); - let resolve!: (v: CommandCompletionResult) => void; - const pending = new Promise( - (r) => (resolve = r), - ); - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockReturnValue(pending), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - session.hide(); // cancels the promise - - // Now resolve the stale promise — should be a no-op - resolve(makeCompletionResult(["song"], 4)); - await Promise.resolve(); - - expect(menu.setChoices).not.toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ selectedText: "song" }), - ]), - ); - }); - - test("empty input fetches completions from backend", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["@"], 0, { - separatorMode: "none", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith(""); - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ selectedText: "@" }), - ]), - ); - }); - - test("empty input: second update reuses session without re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["@"], 0, { - separatorMode: "none", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - session.update("", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("empty input: unique match triggers re-fetch (commitMode=eager)", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["@"], 0, { - separatorMode: "none", - commitMode: "eager", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - session.update("@", getPos); - - // "@" uniquely matches the only completion — triggers re-fetch - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); - }); - - test("empty input: unique match triggers re-fetch even when closedSet=true (commitMode=eager)", async () => { - const menu = makeMenu(); - // closedSet=true means exhaustive at THIS level, but uniquelySatisfied - // means the user needs NEXT level completions — always re-fetch. - const result = makeCompletionResult(["@"], 0, { - closedSet: true, - separatorMode: "none", - commitMode: "eager", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - session.update("@", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith("@"); - }); - - test("empty input: ambiguous prefix does not re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["@config", "@configure"], 0, { - separatorMode: "none", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - session.update("@config", getPos); - - // "@config" is a prefix of "@configure" — reuse, no re-fetch - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); -}); - -// ── Completion result processing ────────────────────────────────────────────── - -describe("PartialCompletionSession — result processing", () => { - test("startIndex narrows the anchor (current) to input[0..startIndex]", async () => { - const menu = makeMenu(); - // startIndex=4 means grammar consumed "play" (4 chars); the - // trailing space is the separator between anchor and completions. - const result = makeCompletionResult(["song", "shuffle"], 4); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play song", getPos); - await Promise.resolve(); - - // prefix should be "song" (the text after anchor "play" + separator " ") - expect(menu.updatePrefix).toHaveBeenCalledWith("song", anyPosition); - }); - - test("group order preserved: items appear in backend-provided group order", async () => { - const menu = makeMenu(); - const group1: CompletionGroup = { - name: "grammar", - completions: ["by"], - sorted: true, - }; - const group2: CompletionGroup = { - name: "entities", - completions: ["Bohemian Rhapsody"], - sorted: true, - }; - const result: CommandCompletionResult = { - startIndex: 4, - completions: [group1, group2], - closedSet: false, - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - const calls = menu.setChoices.mock.calls; - const items = calls[calls.length - 1][0] as { - sortIndex: number; - selectedText: string; - }[]; - const byIndex = items.find((i) => i.selectedText === "by")!.sortIndex; - const bohIndex = items.find( - (i) => i.selectedText === "Bohemian Rhapsody", - )!.sortIndex; - expect(byIndex).toBeLessThan(bohIndex); - }); - - test("needQuotes propagated from group to each SearchMenuItem", async () => { - const menu = makeMenu(); - const group: CompletionGroup = { - name: "entities", - completions: ["Bohemian Rhapsody"], - needQuotes: true, - sorted: true, - }; - const result: CommandCompletionResult = { - startIndex: 4, - completions: [group], - closedSet: false, - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - selectedText: "Bohemian Rhapsody", - needQuotes: true, - }), - ]), - ); - }); - - test("unsorted group items are sorted alphabetically", async () => { - const menu = makeMenu(); - const group: CompletionGroup = { - name: "test", - completions: ["zebra", "apple", "mango"], - sorted: false, - }; - const result: CommandCompletionResult = { - startIndex: 0, - completions: [group], - closedSet: false, - separatorMode: "none", - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("x", getPos); - await Promise.resolve(); - - const calls = menu.setChoices.mock.calls; - const items = calls[calls.length - 1][0] as { selectedText: string }[]; - const texts = items.map((i) => i.selectedText); - expect(texts).toEqual(["apple", "mango", "zebra"]); - }); - - test("separatorMode defers menu display until trailing space is present", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - // Input without trailing space: "play" — choices are loaded but menu is not shown - session.update("play", getPos); - await Promise.resolve(); - - // setChoices IS called with actual items (trie is populated for later) - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ selectedText: "music" }), - ]), - ); - // But updatePrefix is NOT called yet (menu not shown) - expect(menu.updatePrefix).not.toHaveBeenCalled(); - }); - - test("separatorMode: typing separator shows menu without re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - // First update: "play" — deferred (separatorMode, no trailing space) - session.update("play", getPos); - await Promise.resolve(); - - // Second update: "play " — separator typed, menu should appear - session.update("play ", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - // No re-fetch — same dispatcher call count - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("separatorMode: menu shown after trailing space is typed", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: undefined, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ selectedText: "music" }), - ]), - ); - }); - - test("empty completions list does not call setChoices with items", async () => { - const menu = makeMenu(); - const result: CommandCompletionResult = { - startIndex: 0, - completions: [{ name: "empty", completions: [] }], - closedSet: false, - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Only the initial setChoices([]) call (cancel) should have been made - expect(menu.setChoices).toHaveBeenCalledTimes(1); - expect(menu.setChoices).toHaveBeenCalledWith([]); - }); -}); - -// ── @-command routing ───────────────────────────────────────────────────────── - -describe("PartialCompletionSession — @command routing", () => { - test("@ command with trailing space fetches full input", () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@config ", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( - "@config ", - ); - }); - - test("@ command with partial word fetches full input (backend filters)", () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@config c", getPos); - - // Backend receives full input and returns completions with the - // correct startIndex; no word-boundary truncation needed. - expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith( - "@config c", - ); - }); - - test("@ command with no space fetches full input", () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@config", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledWith("@config"); - }); - - test("@ command in PENDING state does not re-fetch", () => { - const menu = makeMenu(); - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockReturnValue(new Promise(() => {})), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@config ", getPos); - session.update("@config c", getPos); // same anchor: "@config " — PENDING reuse - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("@ command: separatorMode defers menu until space typed", async () => { - const menu = makeMenu(); - // Backend returns subcommands with separatorMode: "space" - // (anchor = "@config", subcommands follow after a space) - const result = makeCompletionResult(["clear", "theme"], 7, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - // User types "@config" → completions loaded, menu deferred (no separator yet) - session.update("@config", getPos); - await Promise.resolve(); - - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ selectedText: "clear" }), - ]), - ); - expect(menu.updatePrefix).not.toHaveBeenCalled(); - - // User types space → separator present, menu appears - session.update("@config ", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - // No re-fetch — same session handles both states - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("@ command: typing after space filters within same session", async () => { - const menu = makeMenu(); - // Backend: separatorMode, anchor = "@config" - const result = makeCompletionResult(["clear", "theme"], 7, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@config", getPos); - await Promise.resolve(); - - // Type space + partial subcommand - session.update("@config cl", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("cl", anyPosition); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("@ command: empty result (closedSet=true) suppresses re-fetch", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@unknown", getPos); - await Promise.resolve(); // → empty completions, closedSet=true - - // Still within anchor — no re-fetch - session.update("@unknownmore", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("@ command: backspace past anchor after empty result triggers re-fetch", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("@unknown", getPos); - await Promise.resolve(); // → empty completions with current="@unknown" - - // Backspace past anchor - session.update("@unknow", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "@unknow", - ); - }); -}); - -// ── getCompletionPrefix ─────────────────────────────────────────────────────── - -describe("PartialCompletionSession — getCompletionPrefix", () => { - test("returns undefined when session is IDLE", () => { - const session = new PartialCompletionSession( - makeMenu(), - makeDispatcher(), - ); - expect(session.getCompletionPrefix("anything")).toBeUndefined(); - }); - - test("returns suffix after anchor when input starts with anchor", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4); - const session = new PartialCompletionSession( - menu, - makeDispatcher(result), - ); - - session.update("play song", getPos); - await Promise.resolve(); - - expect(session.getCompletionPrefix("play song")).toBe("song"); - }); - - test("returns undefined when input diverges from anchor", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4); - const session = new PartialCompletionSession( - menu, - makeDispatcher(result), - ); - - session.update("play song", getPos); - await Promise.resolve(); - - // Input no longer starts with anchor "play" - expect(session.getCompletionPrefix("stop")).toBeUndefined(); - }); - - test("separatorMode: returns stripped prefix when separator is present", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const session = new PartialCompletionSession( - menu, - makeDispatcher(result), - ); - - session.update("play", getPos); - await Promise.resolve(); - - // Separator + typed text: prefix should be "mu" (space stripped) - expect(session.getCompletionPrefix("play mu")).toBe("mu"); - }); - - test("separatorMode: returns undefined when separator is absent", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const session = new PartialCompletionSession( - menu, - makeDispatcher(result), - ); - - session.update("play", getPos); - await Promise.resolve(); - - // No separator yet — undefined means no replacement should happen - expect(session.getCompletionPrefix("play")).toBeUndefined(); - }); -}); - -// ── resetToIdle ─────────────────────────────────────────────────────────────── - -describe("PartialCompletionSession — resetToIdle", () => { - test("clears session so next update re-fetches", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play song", getPos); - await Promise.resolve(); // → ACTIVE - - session.resetToIdle(); - - // After reset, next update should fetch fresh completions - session.update("play song", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - }); - - test("does not hide the menu (caller is responsible for that)", async () => { - const menu = makeMenu(); - const dispatcher = makeDispatcher(makeCompletionResult(["song"], 4)); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play song", getPos); - await Promise.resolve(); - - menu.hide.mockClear(); - session.resetToIdle(); - - expect(menu.hide).not.toHaveBeenCalled(); - }); -}); - -// ── separatorMode edge cases ───────────────────────────────────────────────── - -describe("PartialCompletionSession — separatorMode edge cases", () => { - test("re-update with same input before separator does not re-fetch", async () => { - // Regression: selectionchange can fire again with the same input while - // the session is waiting for a separator. Must not trigger a re-fetch. - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); // deferred — waiting for separator - - session.update("play", getPos); // same input again (e.g. selectionchange) - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("input diverges before separator arrives triggers re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); // deferred - - // User typed a non-space character instead of a separator - session.update("play2", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "play2", - ); - }); - - test("separator already in input when result arrives shows menu immediately", async () => { - // User typed "play " fast enough that the promise resolves after the space. - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - // Fetch was issued for "play" but by the time it resolves the user - // has already moved on; a second update for "play " is already active. - // Simulate by updating to "play " *before* awaiting. - session.update("play", getPos); - // (promise not yet resolved — we rely on the .then() calling reuseSession - // with the captured "play" input, which has no separator, so menu stays - // hidden. A subsequent update("play ", ...) then shows it.) - await Promise.resolve(); - - session.update("play ", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); -}); - -// ── miscellaneous ───────────────────────────────────────────────────────────── - -describe("PartialCompletionSession — miscellaneous", () => { - test("getPosition returning undefined hides the menu", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play song", getPos); - await Promise.resolve(); - - menu.hide.mockClear(); - // getPosition returns undefined (e.g. caret not found) - session.update("play song", () => undefined); - - expect(menu.hide).toHaveBeenCalled(); - }); - - test("startIndex beyond input length falls back to full input as anchor", async () => { - const menu = makeMenu(); - // startIndex=99 is beyond "play" (length 4) — anchor falls back to "play" - const result = makeCompletionResult(["song"], 99, { - separatorMode: "none", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Anchor is "play" (full input). reuseSession is called with the captured - // input "play", so rawPrefix="" and updatePrefix is called with "". - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - }); -}); - -// ── committed-past-boundary (hasExactMatch) ─────────────────────────────────── - -describe("PartialCompletionSession — committed-past-boundary re-fetch", () => { - test("closedSet=true: typing space after exact match triggers re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult( - ["set", "setWindowState", "setWindowZoomLevel"], - 4, - { separatorMode: "space", closedSet: true }, - ); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - // User types "set " — prefix is "set ", exact match "set" + separator - session.update("play set ", getPos); - - expect(menu.hasExactMatch).toHaveBeenCalledWith("set"); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "play set ", - ); - }); - - test("closedSet=true: typing multiple spaces after exact match triggers re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult( - ["set", "setWindowState", "setWindowZoomLevel"], - 4, - { separatorMode: "space", closedSet: true }, - ); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - // Double space after "set" - session.update("play set ", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - }); - - test("closedSet=true: typing punctuation after exact match triggers re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult( - ["set", "setWindowState", "setWindowZoomLevel"], - 4, - { separatorMode: "space", closedSet: true }, - ); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - // Punctuation after "set" - session.update("play set.", getPos); - - expect(menu.hasExactMatch).toHaveBeenCalledWith("set"); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - }); - - test("closedSet=true: typing separator after non-matching text does NOT re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult( - ["set", "setWindowState", "setWindowZoomLevel"], - 4, - { separatorMode: "space", closedSet: true }, - ); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - // "xyz" is not a known completion — closedSet=true should suppress re-fetch - session.update("play xyz ", getPos); - - expect(menu.hasExactMatch).toHaveBeenCalledWith("xyz"); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); -}); - -// ── commitMode ──────────────────────────────────────────────────────────────── - -describe("PartialCompletionSession — commitMode", () => { - test("commitMode=explicit (default): uniquely satisfied does NOT re-fetch", async () => { - const menu = makeMenu(); - // Default commitMode (omitted → "explicit") - const result = makeCompletionResult(["song"], 4, { - separatorMode: "space", - closedSet: true, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - session.update("play song", getPos); - - // "song" uniquely matched, but commitMode="explicit" — B4 suppressed - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("commitMode=explicit: uniquely satisfied + trailing space triggers re-fetch via B5", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4, { - separatorMode: "space", - closedSet: true, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - // First: "play song" — uniquely satisfied but suppressed (no trailing space) - session.update("play song", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - - // Second: "play song " — user typed space → B5 fires (committed past boundary) - session.update("play song ", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "play song ", - ); - }); - - test("commitMode=eager: uniquely satisfied triggers immediate re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4, { - separatorMode: "space", - commitMode: "eager", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); // → ACTIVE, anchor = "play" - - session.update("play song", getPos); - - // commitMode="eager" — B4 fires immediately - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "play song", - ); - }); - - test("commitMode=explicit: B5 committed-past-boundary still fires", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["set", "setWindowState"], 4, { - separatorMode: "space", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - // "set " — contains separator after exact match → B5 fires - session.update("play set ", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "play set ", - ); - }); - - test("commitMode=explicit: open-set no-matches still triggers re-fetch (C6 unaffected)", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4, { - separatorMode: "space", - closedSet: false, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - // "xyz" — no trie match, closedSet=false → C6 re-fetch - session.update("play xyz", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - }); - - test("commitMode defaults to explicit when omitted from result", async () => { - const menu = makeMenu(); - // No commitMode in result — defaults to "explicit" - const result = makeCompletionResult(["song"], 4, { - separatorMode: "space", - closedSet: true, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - session.update("play song", getPos); - - // Default commitMode="explicit" — B4 suppressed - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("commitMode=explicit + closedSet=false: uniquely satisfied does NOT re-fetch", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], 4, { - separatorMode: "space", - closedSet: false, - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play ", getPos); - await Promise.resolve(); - - // "song" uniquely matches — commitMode="explicit" must suppress re-fetch - // even though closedSet=false (closedSet describes THIS level, not next) - session.update("play song", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - - // Only after typing a separator should B5 trigger a re-fetch - session.update("play song ", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - }); -}); - -// ── separatorMode: "spacePunctuation" ───────────────────────────────────────── - -describe("PartialCompletionSession — separatorMode: spacePunctuation", () => { - test("space satisfies spacePunctuation separator", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "spacePunctuation", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Space satisfies spacePunctuation - session.update("play ", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("punctuation satisfies spacePunctuation separator", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "spacePunctuation", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Punctuation mark satisfies spacePunctuation. - // The leading punctuation separator is stripped, just like whitespace. - session.update("play.mu", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("mu", anyPosition); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("letter after anchor triggers re-fetch under spacePunctuation", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "spacePunctuation", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // A letter is neither space nor punctuation — triggers re-fetch (A3) - session.update("playx", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "playx", - ); - }); - - test("no separator yet hides menu under spacePunctuation", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "spacePunctuation", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Exact anchor, no separator — menu hidden but session kept - menu.hide.mockClear(); - session.update("play", getPos); - - expect(menu.hide).toHaveBeenCalled(); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); -}); - -// ── separatorMode: "optional" ───────────────────────────────────────────────── - -describe("PartialCompletionSession — separatorMode: optional", () => { - test("completions shown immediately without separator", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music"], 4, { - separatorMode: "optional", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // "optional" does not require a separator — menu shown immediately - // rawPrefix="" → updatePrefix("", ...) - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - }); - - test("typing after anchor filters within session", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["music", "movie"], 4, { - separatorMode: "optional", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - session.update("playmu", getPos); - - expect(menu.updatePrefix).toHaveBeenCalledWith("mu", anyPosition); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); -}); - -// ── emojiChar propagation ───────────────────────────────────────────────────── - -describe("PartialCompletionSession — emojiChar propagation", () => { - test("emojiChar from group is propagated to each SearchMenuItem", async () => { - const menu = makeMenu(); - const group: CompletionGroup = { - name: "agents", - completions: ["player", "calendar"], - emojiChar: "\uD83C\uDFB5", - sorted: true, - }; - const result: CommandCompletionResult = { - startIndex: 0, - completions: [group], - closedSet: false, - separatorMode: "none", - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - expect(menu.setChoices).toHaveBeenCalledWith( - expect.arrayContaining([ - expect.objectContaining({ - selectedText: "player", - emojiChar: "\uD83C\uDFB5", - }), - expect.objectContaining({ - selectedText: "calendar", - emojiChar: "\uD83C\uDFB5", - }), - ]), - ); - }); - - test("emojiChar absent from group means no emojiChar on items", async () => { - const menu = makeMenu(); - const group: CompletionGroup = { - name: "plain", - completions: ["alpha"], - sorted: true, - }; - const result: CommandCompletionResult = { - startIndex: 0, - completions: [group], - closedSet: false, - separatorMode: "none", - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("", getPos); - await Promise.resolve(); - - const calls = menu.setChoices.mock.calls; - const items = calls[calls.length - 1][0] as Record[]; - expect(items[0]).not.toHaveProperty("emojiChar"); - }); -}); - -// ── backend error handling ──────────────────────────────────────────────────── - -describe("PartialCompletionSession — backend error handling", () => { - test("rejected promise clears PENDING state so next update can proceed", async () => { - const menu = makeMenu(); - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockRejectedValueOnce(new Error("network error")) - .mockResolvedValue(makeCompletionResult(["song"], 4)), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - // Flush rejected promise + catch handler - await Promise.resolve(); - await Promise.resolve(); - - // After rejection, anchor is still "play" with separatorMode="space". - // Diverged input triggers a re-fetch (anchor no longer matches). - session.update("stop", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - expect(dispatcher.getCommandCompletion).toHaveBeenLastCalledWith( - "stop", - ); - }); - - test("rejected promise: same input within anchor does not re-fetch", async () => { - const menu = makeMenu(); - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockRejectedValueOnce(new Error("network error")) - .mockResolvedValue(makeCompletionResult(["song"], 4)), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - await Promise.resolve(); - - // Same input — anchor still matches, reuse session (no re-fetch) - session.update("play", getPos); - - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - }); - - test("rejected promise does not leave session stuck in PENDING", async () => { - const menu = makeMenu(); - let rejectFn!: (e: Error) => void; - const rejecting = new Promise( - (_, reject) => (rejectFn = reject), - ); - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockReturnValueOnce(rejecting) - .mockResolvedValue(makeCompletionResult(["song"], 4)), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - - // While PENDING, second update is suppressed - session.update("play more", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(1); - - // Now reject - rejectFn(new Error("timeout")); - await Promise.resolve(); - await Promise.resolve(); - - // Session is no longer PENDING — diverged input triggers re-fetch - session.update("stop", getPos); - expect(dispatcher.getCommandCompletion).toHaveBeenCalledTimes(2); - }); - - test("rejected promise does not populate menu", async () => { - const menu = makeMenu(); - const dispatcher: ICompletionDispatcher = { - getCommandCompletion: jest - .fn() - .mockRejectedValue(new Error("timeout")), - }; - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - await Promise.resolve(); - - // setChoices should only have the initial empty-array call, not real items - expect(menu.setChoices).toHaveBeenCalledTimes(1); - expect(menu.setChoices).toHaveBeenCalledWith([]); - }); -}); - -// ── startIndex edge cases ───────────────────────────────────────────────────── - -describe("PartialCompletionSession — startIndex edge cases", () => { - test("negative startIndex falls back to full input as anchor", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["song"], -1, { - separatorMode: "none", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Anchor is "play" (full input). rawPrefix="" → updatePrefix("", ...) - expect(menu.updatePrefix).toHaveBeenCalledWith("", anyPosition); - }); - - test("startIndex=0 sets empty anchor", async () => { - const menu = makeMenu(); - const result = makeCompletionResult(["play"], 0, { - separatorMode: "none", - }); - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("play", getPos); - await Promise.resolve(); - - // Anchor is "" (empty). rawPrefix="play" → updatePrefix("play", ...) - expect(menu.updatePrefix).toHaveBeenCalledWith("play", anyPosition); - }); -}); - -// ── mixed sorted/unsorted groups ────────────────────────────────────────────── - -describe("PartialCompletionSession — mixed sorted/unsorted groups", () => { - test("sorted group preserves order while unsorted group is alphabetized", async () => { - const menu = makeMenu(); - const sortedGroup: CompletionGroup = { - name: "grammar", - completions: ["zebra", "apple"], - sorted: true, - }; - const unsortedGroup: CompletionGroup = { - name: "entities", - completions: ["cherry", "banana"], - sorted: false, - }; - const result: CommandCompletionResult = { - startIndex: 0, - completions: [sortedGroup, unsortedGroup], - closedSet: false, - separatorMode: "none", - }; - const dispatcher = makeDispatcher(result); - const session = new PartialCompletionSession(menu, dispatcher); - - session.update("x", getPos); - await Promise.resolve(); - - const calls = menu.setChoices.mock.calls; - const items = calls[calls.length - 1][0] as { - selectedText: string; - sortIndex: number; - }[]; - const texts = items.map((i) => i.selectedText); - - // Sorted group: order preserved (zebra before apple) - // Unsorted group: alphabetized (banana before cherry) - // Cross-group: sorted group first - expect(texts).toEqual(["zebra", "apple", "banana", "cherry"]); - - // sortIndex is sequential across both groups - expect(items.map((i) => i.sortIndex)).toEqual([0, 1, 2, 3]); - }); -}); diff --git a/ts/packages/shell/test/tsconfig.json b/ts/packages/shell/test/tsconfig.json index 315900cbbe..d22e8be3f2 100644 --- a/ts/packages/shell/test/tsconfig.json +++ b/ts/packages/shell/test/tsconfig.json @@ -6,6 +6,6 @@ "outDir": "../dist/test", "types": ["node", "jest"] }, - "include": ["partialCompletionSession.spec.ts"], + "include": ["partialCompletion/**/*.ts"], "references": [{ "path": "../src" }] } From 54635b0bf2c4d1bd7094cfc494bd89f3b4a895c4 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 19:42:59 -0700 Subject: [PATCH 40/51] Move mergeSeparatorMode and getContentForType to SDK helper modules Move function implementations out of the root SDK type files (command.ts, display.ts) into their respective helper modules (commandHelpers.ts, displayHelpers.ts), following the convention that root SDK exports are type-only and functionality lives in helpers. Update all consumers to import from @typeagent/agent-sdk/helpers/command and @typeagent/agent-sdk/helpers/display respectively. --- ts/packages/agentSdk/src/display.ts | 22 --------------- .../agentSdk/src/helpers/commandHelpers.ts | 19 +++++++++++++ .../agentSdk/src/helpers/displayHelpers.ts | 24 ++++++++++++++++ ts/packages/agentSdk/src/index.ts | 1 - .../agents/taskflow/src/taskFlowRunner.mts | 2 +- ts/packages/cache/src/cache/grammarStore.ts | 28 ++----------------- .../src/constructions/constructionCache.ts | 20 ++----------- ts/packages/chat-ui/src/setContent.ts | 2 +- ts/packages/cli/src/commands/interactive.ts | 2 +- ts/packages/cli/src/enhancedConsole.ts | 2 +- .../dispatcher/src/command/completion.ts | 17 +---------- .../handlers/matchCommandHandler.ts | 2 ++ .../handlers/requestCommandHandler.ts | 2 ++ .../handlers/translateCommandHandler.ts | 2 ++ .../dispatcher/src/helpers/console.ts | 2 +- .../dispatcher/types/src/dispatcher.ts | 9 +++--- .../renderer/src/partialCompletionSession.ts | 4 +-- .../shell/src/renderer/src/setContent.ts | 2 +- 18 files changed, 66 insertions(+), 96 deletions(-) diff --git a/ts/packages/agentSdk/src/display.ts b/ts/packages/agentSdk/src/display.ts index d1917e2b8e..ed818e0806 100644 --- a/ts/packages/agentSdk/src/display.ts +++ b/ts/packages/agentSdk/src/display.ts @@ -58,28 +58,6 @@ export type ClientAction = | "automate-phone-ui" | "open-folder"; -/** - * Given a TypedDisplayContent, find the content for a preferred type. - * Checks alternates first, then falls back to the primary content if it matches. - * Returns undefined if the preferred type is not available. - */ -export function getContentForType( - content: TypedDisplayContent, - preferredType: DisplayType, -): MessageContent | undefined { - if (content.alternates) { - for (const alt of content.alternates) { - if (alt.type === preferredType) { - return alt.content; - } - } - } - if (content.type === preferredType) { - return content.content; - } - return undefined; -} - export interface ActionIO { // Set the display to the content provided setDisplay(content: DisplayContent): void; diff --git a/ts/packages/agentSdk/src/helpers/commandHelpers.ts b/ts/packages/agentSdk/src/helpers/commandHelpers.ts index 9b44aa8fde..0f45a1b48f 100644 --- a/ts/packages/agentSdk/src/helpers/commandHelpers.ts +++ b/ts/packages/agentSdk/src/helpers/commandHelpers.ts @@ -8,7 +8,26 @@ import { CommandDescriptors, CommandDescriptorTable, CompletionGroups, + SeparatorMode, } from "../command.js"; + +// Merge two SeparatorMode values — the mode requiring the strongest +// separator wins (i.e. the mode that demands the most from the user). +// Priority: "space" > "spacePunctuation" > "optional" > "none" > undefined. +export function mergeSeparatorMode( + a: SeparatorMode | undefined, + b: SeparatorMode | undefined, +): SeparatorMode | undefined { + if (a === undefined) return b; + if (b === undefined) return a; + const order: Record = { + space: 3, + spacePunctuation: 2, + optional: 1, + none: 0, + }; + return order[a] >= order[b] ? a : b; +} import { ParameterDefinitions, ParsedCommandParams, diff --git a/ts/packages/agentSdk/src/helpers/displayHelpers.ts b/ts/packages/agentSdk/src/helpers/displayHelpers.ts index accca2fb7f..b84f86df2f 100644 --- a/ts/packages/agentSdk/src/helpers/displayHelpers.ts +++ b/ts/packages/agentSdk/src/helpers/displayHelpers.ts @@ -6,9 +6,33 @@ import { DisplayAppendMode, DisplayContent, DisplayMessageKind, + DisplayType, MessageContent, + TypedDisplayContent, } from "../display.js"; +/** + * Given a TypedDisplayContent, find the content for a preferred type. + * Checks alternates first, then falls back to the primary content if it matches. + * Returns undefined if the preferred type is not available. + */ +export function getContentForType( + content: TypedDisplayContent, + preferredType: DisplayType, +): MessageContent | undefined { + if (content.alternates) { + for (const alt of content.alternates) { + if (alt.type === preferredType) { + return alt.content; + } + } + } + if (content.type === preferredType) { + return content.content; + } + return undefined; +} + function gatherMessages(callback: (log: (message?: string) => void) => void) { const messages: (string | undefined)[] = []; callback((message?: string) => { diff --git a/ts/packages/agentSdk/src/index.ts b/ts/packages/agentSdk/src/index.ts index 0de4c9b7ae..bed6aa1793 100644 --- a/ts/packages/agentSdk/src/index.ts +++ b/ts/packages/agentSdk/src/index.ts @@ -54,7 +54,6 @@ export { TypedDisplayContent, DisplayAppendMode, DisplayMessageKind, - getContentForType, } from "./display.js"; export { diff --git a/ts/packages/agents/taskflow/src/taskFlowRunner.mts b/ts/packages/agents/taskflow/src/taskFlowRunner.mts index 999b7f8e04..8044053263 100644 --- a/ts/packages/agents/taskflow/src/taskFlowRunner.mts +++ b/ts/packages/agents/taskflow/src/taskFlowRunner.mts @@ -10,11 +10,11 @@ import type { } from "@typeagent/dispatcher-types"; import { DisplayAppendMode, - getContentForType, type DisplayContent, type MessageContent, type TypedDisplayContent, } from "@typeagent/agent-sdk"; +import { getContentForType } from "@typeagent/agent-sdk/helpers/display"; import { convert } from "html-to-text"; // ── Text utilities ─────────────────────────────────────────────────────────── diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 63c6f547b7..97f7abd513 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -6,7 +6,6 @@ import { Grammar, matchGrammar, matchGrammarCompletion, - GrammarSeparatorMode, NFA, compileGrammarToNFA, matchGrammarWithNFA, @@ -20,6 +19,7 @@ import { const debug = registerDebug("typeagent:cache:grammarStore"); import { SeparatorMode } from "@typeagent/agent-sdk"; +import { mergeSeparatorMode } from "@typeagent/agent-sdk/helpers/command"; import { CompletionProperty, CompletionResult, @@ -34,30 +34,6 @@ import { } from "../explanation/requestAction.js"; import { sortMatches } from "./sortMatches.js"; -// Merge a GrammarSeparatorMode (from action-grammar) into an accumulator -// SeparatorMode. The mode requiring the strongest separator wins -// (i.e. the mode that demands the most from the user): -// spacePunctuation > optional > none. -// (Grammar results never produce "space" — that mode is for command dispatch.) -function mergeGrammarSeparatorMode( - current: SeparatorMode | undefined, - incoming: GrammarSeparatorMode, -): SeparatorMode { - // GrammarSeparatorMode is a subset of SeparatorMode - // ("spacePunctuation" | "optional" | "none") — values are directly - // assignable. - if (current === undefined) { - return incoming; - } - if (current === "spacePunctuation" || incoming === "spacePunctuation") { - return "spacePunctuation"; - } - if (current === "optional" || incoming === "optional") { - return "optional"; - } - return "none"; -} - interface GrammarEntry { grammar: Grammar; nfa?: NFA; @@ -392,7 +368,7 @@ export class GrammarStoreImpl implements GrammarStore { if (partialPrefixLength === matchedPrefixLength) { completions.push(...partial.completions); if (partial.separatorMode !== undefined) { - separatorMode = mergeGrammarSeparatorMode( + separatorMode = mergeSeparatorMode( separatorMode, partial.separatorMode, ); diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index ad1b5dc36c..eafa2fb374 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { SeparatorMode } from "@typeagent/agent-sdk"; +import { mergeSeparatorMode } from "@typeagent/agent-sdk/helpers/command"; import { ExecutableAction, HistoryContext, @@ -117,7 +118,7 @@ export function mergeCompletionResults( : first.properties : second.properties, matchedPrefixLength, - separatorMode: mergeSeparatorModes( + separatorMode: mergeSeparatorMode( first.separatorMode, second.separatorMode, ), @@ -129,23 +130,6 @@ export function mergeCompletionResults( }; } -// Merge two SeparatorMode values — the mode requiring the strongest -// separator wins (i.e. the mode that demands the most from the user). -// Priority: "space" > "spacePunctuation" > "optional" > "none". -function mergeSeparatorModes( - a: SeparatorMode | undefined, - b: SeparatorMode | undefined, -): SeparatorMode | undefined { - if (a === undefined) return b; - if (b === undefined) return a; - const order: Record = { - space: 3, - spacePunctuation: 2, - optional: 1, - none: 0, - }; - return order[a] >= order[b] ? a : b; -} export class ConstructionCache { private readonly matchSetsByUid = new Map(); diff --git a/ts/packages/chat-ui/src/setContent.ts b/ts/packages/chat-ui/src/setContent.ts index 7198235a8e..7016b3d730 100644 --- a/ts/packages/chat-ui/src/setContent.ts +++ b/ts/packages/chat-ui/src/setContent.ts @@ -8,8 +8,8 @@ import { DisplayType, DisplayMessageKind, MessageContent, - getContentForType, } from "@typeagent/agent-sdk"; +import { getContentForType } from "@typeagent/agent-sdk/helpers/display"; import DOMPurify from "dompurify"; import MarkdownIt from "markdown-it"; import { PlatformAdapter, ChatSettingsView } from "./platformAdapter.js"; diff --git a/ts/packages/cli/src/commands/interactive.ts b/ts/packages/cli/src/commands/interactive.ts index 8353e5420e..514bb0f698 100644 --- a/ts/packages/cli/src/commands/interactive.ts +++ b/ts/packages/cli/src/commands/interactive.ts @@ -57,7 +57,7 @@ async function getCompletionsData( // startIndex), so we no longer need space-based token-boundary // heuristics here. const result = await dispatcher.getCommandCompletion(line); - if (!result || !result.completions || result.completions.length === 0) { + if (result.completions.length === 0) { return null; } diff --git a/ts/packages/cli/src/enhancedConsole.ts b/ts/packages/cli/src/enhancedConsole.ts index d2003eabed..aafe372d31 100644 --- a/ts/packages/cli/src/enhancedConsole.ts +++ b/ts/packages/cli/src/enhancedConsole.ts @@ -17,8 +17,8 @@ import { DisplayAppendMode, DisplayContent, MessageContent, - getContentForType, } from "@typeagent/agent-sdk"; +import { getContentForType } from "@typeagent/agent-sdk/helpers/display"; import type { RequestId, ClientIO, diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 482ee3fde6..b0baf196de 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -15,6 +15,7 @@ import { import { getFlagMultiple, getFlagType, + mergeSeparatorMode, resolveFlag, } from "@typeagent/agent-sdk/helpers/command"; import { parseParams, ParseParamsResult } from "./parameters.js"; @@ -30,22 +31,6 @@ import { CommandCompletionResult } from "@typeagent/dispatcher-types"; const debug = registerDebug("typeagent:command:completion"); const debugError = registerDebug("typeagent:command:completion:error"); -// Merge two SeparatorMode values — the mode requiring the strongest -// separator wins (i.e. the mode that demands the most from the user). -// Priority: "space" > "spacePunctuation" > "optional" > "none" > undefined. -function mergeSeparatorMode( - a: SeparatorMode | undefined, - b: SeparatorMode | undefined, -): SeparatorMode | undefined { - if (a === undefined) return b; - if (b === undefined) return a; - if (a === "space" || b === "space") return "space"; - if (a === "spacePunctuation" || b === "spacePunctuation") - return "spacePunctuation"; - if (a === "optional" || b === "optional") return "optional"; - return "none"; -} - // Return the full flag name if we are waiting a flag value. Add boolean values for completions and return undefined if the flag is boolean. function getPendingFlag( params: ParseParamsResult, diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts index d2deb6dd24..3a1ac16679 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts @@ -63,6 +63,8 @@ export class MatchCommandHandler implements CommandHandler { result.groups.push(...requestResult.groups); result.prefixLength = requestResult.prefixLength; result.separatorMode = requestResult.separatorMode; + result.closedSet = requestResult.closedSet; + result.commitMode = requestResult.commitMode; } } return result; diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts index aac6ff91a4..fcfb381712 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts @@ -486,6 +486,8 @@ export class RequestCommandHandler implements CommandHandler { result.groups.push(...requestResult.groups); result.prefixLength = requestResult.prefixLength; result.separatorMode = requestResult.separatorMode; + result.closedSet = requestResult.closedSet; + result.commitMode = requestResult.commitMode; } } return result; diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts index eec0b95bd1..b4ed16817a 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts @@ -88,6 +88,8 @@ export class TranslateCommandHandler implements CommandHandler { result.groups.push(...requestResult.groups); result.prefixLength = requestResult.prefixLength; result.separatorMode = requestResult.separatorMode; + result.closedSet = requestResult.closedSet; + result.commitMode = requestResult.commitMode; } } return result; diff --git a/ts/packages/dispatcher/dispatcher/src/helpers/console.ts b/ts/packages/dispatcher/dispatcher/src/helpers/console.ts index 8316089df0..c69dc0b9d9 100644 --- a/ts/packages/dispatcher/dispatcher/src/helpers/console.ts +++ b/ts/packages/dispatcher/dispatcher/src/helpers/console.ts @@ -6,8 +6,8 @@ import { DisplayAppendMode, DisplayContent, MessageContent, - getContentForType, } from "@typeagent/agent-sdk"; +import { getContentForType } from "@typeagent/agent-sdk/helpers/display"; import type { RequestId, ClientIO, diff --git a/ts/packages/dispatcher/types/src/dispatcher.ts b/ts/packages/dispatcher/types/src/dispatcher.ts index c23793af75..8512cdb29f 100644 --- a/ts/packages/dispatcher/types/src/dispatcher.ts +++ b/ts/packages/dispatcher/types/src/dispatcher.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. import { + CommitMode, CompletionGroup, DisplayType, DynamicDisplay, @@ -70,9 +71,9 @@ export type CommandResult = { }; export type CommandCompletionResult = { - // Length of the longest valid (parsable) prefix of the input. - // input[0..startIndex) is fully resolved; completions describe - // what can follow after that prefix. + // Index into the input where the resolved prefix ends and the + // filter/completion region begins. input[0..startIndex) is fully + // resolved; completions describe what can follow after that prefix. startIndex: number; completions: CompletionGroup[]; // completions available at the current position // What kind of separator is required between the matched prefix and @@ -91,7 +92,7 @@ export type CommandCompletionResult = { // eager re-fetch on unique match. // "eager" — re-fetch immediately on unique satisfaction. // When omitted, defaults to "explicit". - commitMode?: "explicit" | "eager"; + commitMode?: CommitMode; }; export type AppAgentStatus = { diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 67fad83714..278ce77b10 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -107,9 +107,7 @@ export class PartialCompletionSession { private commitMode: CommitMode = "explicit"; // The in-flight completion request, or undefined when settled. - private completionP: - | Promise - | undefined; + private completionP: Promise | undefined; constructor( private readonly menu: ISearchMenu, diff --git a/ts/packages/shell/src/renderer/src/setContent.ts b/ts/packages/shell/src/renderer/src/setContent.ts index 21441dea59..baa95b2516 100644 --- a/ts/packages/shell/src/renderer/src/setContent.ts +++ b/ts/packages/shell/src/renderer/src/setContent.ts @@ -8,8 +8,8 @@ import { DisplayType, DisplayMessageKind, MessageContent, - getContentForType, } from "@typeagent/agent-sdk"; +import { getContentForType } from "@typeagent/agent-sdk/helpers/display"; import DOMPurify from "dompurify"; import { SettingsView } from "./settingsView"; import MarkdownIt from "markdown-it"; From 10563b047a78ce98bb655d55166a61b4be89e3ee Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Wed, 11 Mar 2026 21:31:32 -0700 Subject: [PATCH 41/51] Simplify completion pipeline: unify SeparatorMode, rename prefixLength, clean up session Option C: Replace exported GrammarSeparatorMode with local SeparatorMode in actionGrammar (includes 'space'; independently defined to avoid agentSdk dep). Option D: Rename prefixLength to matchedPrefixLength in CompletionGroups and all consumers (dispatcher, command handlers, tests) for clarity. partialCompletionSession: extract separator helpers to module-level functions, inline resetSessionFields(), trim redundant field JSDoc, fix error handler to preserve anchor on rejection so same-input calls reuse the session. --- .../actionGrammar/src/grammarMatcher.ts | 36 +++-- ts/packages/actionGrammar/src/index.ts | 2 - ts/packages/agentSdk/src/command.ts | 2 +- .../dispatcher/src/command/completion.ts | 16 +-- .../handlers/matchCommandHandler.ts | 2 +- .../handlers/requestCommandHandler.ts | 2 +- .../handlers/translateCommandHandler.ts | 2 +- .../src/translation/requestCompletion.ts | 6 +- .../dispatcher/test/completion.spec.ts | 34 ++--- .../renderer/src/partialCompletionSession.ts | 126 +++++++----------- 10 files changed, 99 insertions(+), 129 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index e1e4f4c9d4..42b8d5212b 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -14,12 +14,10 @@ import { VarStringPart, } from "./grammarTypes.js"; -// Separator mode for grammar completion results. -// A subset of SeparatorMode from @typeagent/agent-sdk (which also -// includes "space"). actionGrammar does not depend on agentSdk, so -// the type is redefined here; values are directly assignable to -// SeparatorMode. -export type GrammarSeparatorMode = "spacePunctuation" | "optional" | "none"; +// Separator mode for completion results. Identical to SeparatorMode +// from @typeagent/agent-sdk; independently defined here so +// actionGrammar does not depend on agentSdk. +type SeparatorMode = "space" | "spacePunctuation" | "optional" | "none"; const debugMatchRaw = registerDebug("typeagent:grammar:match"); const debugCompletion = registerDebug("typeagent:grammar:completion"); @@ -91,14 +89,14 @@ function requiresSeparator( } // Convert a per-candidate (needsSep, spacingMode) pair into a -// GrammarSeparatorMode value. When needsSep is true (separator -// required), the grammar always uses spacePunctuation separators. +// SeparatorMode value. When needsSep is true (separator required), +// the grammar always uses spacePunctuation separators. // When needsSep is false: "none" spacingMode → "none", otherwise // → "optional" (covers auto mode/CJK/mixed and explicit "optional"). function candidateSeparatorMode( needsSep: boolean, spacingMode: CompiledSpacingMode, -): GrammarSeparatorMode { +): SeparatorMode { if (needsSep) { return "spacePunctuation"; } @@ -111,11 +109,11 @@ function candidateSeparatorMode( // Merge a new candidate's separator mode into the running aggregate. // The mode requiring the strongest separator wins (i.e. the mode that // demands the most from the user): spacePunctuation > optional > none. -function mergeGrammarSeparatorMode( - current: GrammarSeparatorMode | undefined, +function mergeSeparatorMode( + current: SeparatorMode | undefined, needsSep: boolean, spacingMode: CompiledSpacingMode, -): GrammarSeparatorMode { +): SeparatorMode { const candidateMode = candidateSeparatorMode(needsSep, spacingMode); if (current === undefined) { return candidateMode; @@ -1091,7 +1089,7 @@ export type GrammarCompletionResult = { matchedPrefixLength?: number | undefined; // What kind of separator is expected between the content at // `matchedPrefixLength` and the completion text. This is a - // *completion-result* concept (GrammarSeparatorMode), derived from the + // *completion-result* concept (SeparatorMode), derived from the // per-rule *match-time* spacing rules (CompiledSpacingMode / // spacingMode) but distinct from them. // "spacePunctuation" — whitespace or punctuation required @@ -1100,7 +1098,7 @@ export type GrammarCompletionResult = { // (CJK 再生 → 音楽 does not require a separator). // "none" — no separator at all ([spacing=none] grammars). // Omitted when no completions were generated. - separatorMode?: GrammarSeparatorMode | undefined; + separatorMode?: SeparatorMode | undefined; // True when `completions` is the closed set of valid // continuations after the matched prefix — if the user types // something not in the list, no further completions can exist @@ -1269,7 +1267,7 @@ function tryPartialStringMatch( * position the completion insertion point correctly (especially important * for non-space-separated scripts like CJK). * - * `separatorMode` (a {@link GrammarSeparatorMode}) indicates what kind of + * `separatorMode` (a {@link SeparatorMode}) indicates what kind of * separator is needed between the content at `matchedPrefixLength` and the * completion text. It is determined by the spacing rules (the per-rule * {@link CompiledSpacingMode}) between the last character of the matched @@ -1293,7 +1291,7 @@ export function matchGrammarCompletion( // maximum are kept. const completions: string[] = []; const properties: GrammarCompletionProperty[] = []; - let separatorMode: GrammarSeparatorMode | undefined; + let separatorMode: SeparatorMode | undefined; // Whether the accumulated completions form a closed set — if the // user types something not listed, no further completions can exist @@ -1398,7 +1396,7 @@ export function matchGrammarCompletion( } completions.push(completionText); - separatorMode = mergeGrammarSeparatorMode( + separatorMode = mergeSeparatorMode( separatorMode, candidateNeedsSep, state.spacingMode, @@ -1461,7 +1459,7 @@ export function matchGrammarCompletion( } properties.push(completionProperty); - separatorMode = mergeGrammarSeparatorMode( + separatorMode = mergeSeparatorMode( separatorMode, candidateNeedsSep, state.spacingMode, @@ -1528,7 +1526,7 @@ export function matchGrammarCompletion( } completions.push(completionText); - separatorMode = mergeGrammarSeparatorMode( + separatorMode = mergeSeparatorMode( separatorMode, candidateNeedsSep, state.spacingMode, diff --git a/ts/packages/actionGrammar/src/index.ts b/ts/packages/actionGrammar/src/index.ts index 00c8c3267f..6ca548c447 100644 --- a/ts/packages/actionGrammar/src/index.ts +++ b/ts/packages/actionGrammar/src/index.ts @@ -28,8 +28,6 @@ export { GrammarCompletionResult, } from "./grammarMatcher.js"; -export type { GrammarSeparatorMode } from "./grammarMatcher.js"; - // Entity system export type { EntityValidator, EntityConverter } from "./entityRegistry.js"; export { diff --git a/ts/packages/agentSdk/src/command.ts b/ts/packages/agentSdk/src/command.ts index acad8ecb89..970309219c 100644 --- a/ts/packages/agentSdk/src/command.ts +++ b/ts/packages/agentSdk/src/command.ts @@ -92,7 +92,7 @@ export type CompletionGroups = { // before the completion point. When present, the shell inserts // completions at this offset, replacing space-based heuristics that fail // for CJK and other non-space-delimited scripts. - prefixLength?: number | undefined; + matchedPrefixLength?: number | undefined; // What kind of separator is required between the matched prefix and // the completion text. When omitted, defaults to "space" (whitespace // required before completions are shown). See SeparatorMode. diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index b0baf196de..0e01e7cf23 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -241,14 +241,14 @@ async function getCommandParameterCompletion( sessionContext, ); - // Allow grammar-reported prefixLength to override + // Allow grammar-reported matchedPrefixLength to override // the parse-derived startIndex. This handles CJK and other // non-space-delimited scripts where the grammar matcher is the // authoritative source for how far into the input it consumed. - // Grammar prefixLength is relative to the token content start - // (after the separator space), not to tokenBoundary (before - // it), so use tokenStartIndex when available. - const groupPrefixLength = agentResult.prefixLength; + // Grammar matchedPrefixLength is relative to the token content + // start (after the separator space), not to tokenBoundary + // (before it), so use tokenStartIndex when available. + const groupPrefixLength = agentResult.matchedPrefixLength; if (groupPrefixLength !== undefined && groupPrefixLength != 0) { startIndex = tokenStartIndex + groupPrefixLength; // we have advanced the startIndex, so existing completions are no longer valid, clear them out. @@ -267,7 +267,7 @@ async function getCommandParameterCompletion( // When there is no trailing space, the last consumed token // hasn't been committed. If no earlier path already adjusted - // startIndex (lastCompletableParam / grammar-prefixLength), + // startIndex (lastCompletableParam / grammar-matchedPrefixLength), // back up to the start of that token so the caller's trie can // filter completions against it. // Exception: fully-quoted tokens (e.g. "build") are committed @@ -343,7 +343,7 @@ async function getCommandParameterCompletion( // that was fully consumed by normalizeCommand → // resolveCommand (→ parseParams). Completions // describe what can validly follow after the anchor. -// May be overridden by a grammar-reported prefixLength +// May be overridden by a grammar-reported matchedPrefixLength // from a CompletionGroups result. // // startIndex is always placed at a token boundary @@ -357,7 +357,7 @@ async function getCommandParameterCompletion( // when omitted), and strip the separator before // filtering. Keeping whitespace inside the anchor // would violate this contract. -// The grammar-reported prefixLength override (Site 4) +// The grammar-reported matchedPrefixLength override (Site 4) // is added to the token start position (before the // separator space), not to tokenBoundary — the grammar // reports how many characters of the token content it diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts index 3a1ac16679..2f2b32a820 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts @@ -61,7 +61,7 @@ export class MatchCommandHandler implements CommandHandler { context.agentContext, ); result.groups.push(...requestResult.groups); - result.prefixLength = requestResult.prefixLength; + result.matchedPrefixLength = requestResult.matchedPrefixLength; result.separatorMode = requestResult.separatorMode; result.closedSet = requestResult.closedSet; result.commitMode = requestResult.commitMode; diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts index fcfb381712..505cc7f5ed 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts @@ -484,7 +484,7 @@ export class RequestCommandHandler implements CommandHandler { context.agentContext, ); result.groups.push(...requestResult.groups); - result.prefixLength = requestResult.prefixLength; + result.matchedPrefixLength = requestResult.matchedPrefixLength; result.separatorMode = requestResult.separatorMode; result.closedSet = requestResult.closedSet; result.commitMode = requestResult.commitMode; diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts index b4ed16817a..10d38c2ea1 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts @@ -86,7 +86,7 @@ export class TranslateCommandHandler implements CommandHandler { context.agentContext, ); result.groups.push(...requestResult.groups); - result.prefixLength = requestResult.prefixLength; + result.matchedPrefixLength = requestResult.matchedPrefixLength; result.separatorMode = requestResult.separatorMode; result.closedSet = requestResult.closedSet; result.commitMode = requestResult.commitMode; diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 1872f34de1..d451bea4f6 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -95,7 +95,7 @@ export async function requestCompletion( return { groups: [] }; } - const prefixLength = results.matchedPrefixLength; + const matchedPrefixLength = results.matchedPrefixLength; const separatorMode = results.separatorMode; const closedSet = results.closedSet; const completions: CompletionGroup[] = []; @@ -111,7 +111,7 @@ export async function requestCompletion( if (results.properties === undefined) { return { groups: completions, - prefixLength, + matchedPrefixLength, separatorMode, closedSet, commitMode: "eager", @@ -137,7 +137,7 @@ export async function requestCompletion( completions.push(...propertyCompletions.values()); return { groups: completions, - prefixLength, + matchedPrefixLength, separatorMode, closedSet, commitMode: "eager", diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index e556c4259c..daf7d8187c 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -40,7 +40,7 @@ function grammarCompletion(token: string): CompletionGroups { completions: ["Tower", "Station"], }, ], - prefixLength: quoteOffset + 5, + matchedPrefixLength: quoteOffset + 5, separatorMode: "space", }; } @@ -56,7 +56,7 @@ function grammarCompletion(token: string): CompletionGroups { completions: ["タワー", "駅"], }, ], - prefixLength: quoteOffset + 2, + matchedPrefixLength: quoteOffset + 2, separatorMode: "optional", }; } @@ -68,7 +68,7 @@ function grammarCompletion(token: string): CompletionGroups { completions: ["Tokyo ", "東京"], }, ], - ...(token.length > 0 ? { prefixLength: 0 } : {}), + ...(token.length > 0 ? { matchedPrefixLength: 0 } : {}), separatorMode: "space", }; } @@ -209,7 +209,7 @@ const handlers = { }, }, grammar: { - description: "Grammar prefixLength command", + description: "Grammar matchedPrefixLength command", parameters: { args: { phrase: { @@ -653,7 +653,7 @@ describe("Command Completion - startIndex", () => { // are included and the group has separatorMode: "space". expect(result!.separatorMode).toBe("space"); // startIndex excludes trailing whitespace (matching grammar - // matcher behaviour where prefixLength doesn't include the + // matcher behaviour where matchedPrefixLength doesn't include the // separator). expect(result!.startIndex).toBe(9); }); @@ -1047,7 +1047,7 @@ describe("Command Completion - startIndex", () => { }); describe("groupPrefixLength overrides startIndex", () => { - it("open-quote CJK advances startIndex by prefixLength", async () => { + it("open-quote CJK advances startIndex by matchedPrefixLength", async () => { const result = await getCommandCompletion( '@comptest grammar "東京タ', context, @@ -1060,7 +1060,7 @@ describe("Command Completion - startIndex", () => { // tokenStartIndex = 22 - 4 = 18 (position of '"') // startIndex = tokenBoundary(input, 18) = 17 // Agent strips opening quote, matches "東京" (2 chars), - // returns prefixLength = 3 (1 quote + 2 CJK chars). + // returns matchedPrefixLength = 3 (1 quote + 2 CJK chars). // startIndex = tokenStartIndex + 3 = 18 + 3 = 21. // rawPrefix = "タ", "タワー".startsWith("タ") ✓ expect(result.startIndex).toBe(21); @@ -1073,7 +1073,7 @@ describe("Command Completion - startIndex", () => { expect(result.closedSet).toBe(false); }); - it("implicitQuotes CJK advances startIndex by prefixLength", async () => { + it("implicitQuotes CJK advances startIndex by matchedPrefixLength", async () => { const result = await getCommandCompletion( "@comptest grammariq 東京タ", context, @@ -1086,7 +1086,7 @@ describe("Command Completion - startIndex", () => { // lastCompletableParam fires (implicitQuotes). // tokenStartIndex = 23 - 3 = 20 (position of "東") // startIndex = tokenBoundary(input, 20) = 19 - // Agent matches "東京" (2 chars), returns prefixLength=2. + // Agent matches "東京" (2 chars), returns matchedPrefixLength=2. // startIndex = tokenStartIndex + 2 = 20 + 2 = 22. // rawPrefix = "タ", "タワー".startsWith("タ") ✓ expect(result.startIndex).toBe(22); @@ -1126,7 +1126,7 @@ describe("Command Completion - startIndex", () => { // lastCompletableParam exclusive path fires // (!hasTrailingSpace && pendingFlag === undefined). // Agent is invoked with grammar mock → matches "東京" → - // returns prefixLength=2. tokenStartIndex = 21-3 = 18, + // returns matchedPrefixLength=2. tokenStartIndex = 21-3 = 18, // startIndex = 18 + 2 = 20. expect(result.startIndex).toBe(20); expect(result.closedSet).toBe(false); @@ -1146,7 +1146,7 @@ describe("Command Completion - startIndex", () => { // "@comptest grammar " (18 chars) // suffix is "", no tokens parsed → nextArgs = ["phrase"]. // Agent called, mock sees empty token list → returns - // completions ["東京"] with no prefixLength. + // completions ["東京"] with no matchedPrefixLength. // groupPrefixLength path does not fire. // startIndex = tokenBoundary(input, 18) = 17. expect(result.startIndex).toBe(17); @@ -1159,7 +1159,7 @@ describe("Command Completion - startIndex", () => { expect(grammar!.completions).toContain("東京"); }); - it("clears earlier completions when prefixLength is set", async () => { + it("clears earlier completions when matchedPrefixLength is set", async () => { const result = await getCommandCompletion( '@comptest grammar "東京タ', context, @@ -1172,8 +1172,8 @@ describe("Command Completion - startIndex", () => { expect(flags).toBeUndefined(); }); - it("does not override startIndex when prefixLength is absent", async () => { - // "run" handler returns groups without prefixLength. + it("does not override startIndex when matchedPrefixLength is absent", async () => { + // "run" handler returns groups without matchedPrefixLength. // No trailing space → backs up to start of "bu". const result = await getCommandCompletion( "@comptest run bu", @@ -1194,7 +1194,7 @@ describe("Command Completion - startIndex", () => { // lastCompletableParam fires. // tokenStartIndex = 27 - 7 = 20 // startIndex = tokenBoundary(input, 20) = 19 - // Mock matches "Tokyo" → prefixLength=5, separatorMode="space". + // Mock matches "Tokyo" → matchedPrefixLength=5, separatorMode="space". // startIndex = tokenStartIndex + 5 = 20 + 5 = 25. // rawPrefix = " T", consumer strips space → filter "T". // "Tower".startsWith("T") ✓ @@ -1216,7 +1216,7 @@ describe("Command Completion - startIndex", () => { // Token = "東京タワー" (5 chars). Mock matches "東京" // and finds suffix "タワー" starts with "タワー" → // returns empty (completed match, no more to suggest). - // agentGroups is [], no prefixLength. + // agentGroups is [], no matchedPrefixLength. // startIndex = tokenBoundary from lastCompletableParam path. const grammar = result.completions.find( (g) => g.name === "Grammar", @@ -1234,7 +1234,7 @@ describe("Command Completion - startIndex", () => { // No tokens parsed → nextArgs = ["query"]. // Mock sees empty token → falls to "no prefix matched" // branch → completions: ["Tokyo ", "東京"], - // prefixLength: 0, separatorMode: "space". + // matchedPrefixLength: 0, separatorMode: "space". // groupPrefixLength = 0 → condition false → skip. // startIndex = tokenBoundary(input, 20) = 19. expect(result.startIndex).toBe(19); diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 278ce77b10..10df45ef1d 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -72,38 +72,9 @@ export class PartialCompletionSession { // the backend reports how much the grammar consumed. `undefined` = IDLE. private anchor: string | undefined = undefined; - // Saved as-is from the last completion result: what kind of separator - // must appear in the input immediately after `anchor` before - // completions are valid. Defaults to "space" when omitted. - // Used by reuseSession() and getCompletionPrefix() to interpret - // the raw prefix without mutating `anchor`. + // Saved as-is from the last completion result. private separatorMode: SeparatorMode = "space"; - - // When true, the completion set returned by the backend is a closed - // set for THIS level of the command hierarchy — if the user types - // something not in the list, no further completions can exist beyond - // it. This affects one decision in reuseSession(): - // - // No trie matches: closedSet=true → reuse (nothing else exists, no - // point re-fetching); closedSet=false → re-fetch (the backend may - // know about completions we haven't loaded). - // - // Notably, `closedSet` does NOT suppress the uniquelySatisfied re-fetch. - // uniquelySatisfied means the user needs the NEXT level's completions, - // which is a different question from whether THIS level is a closed set. private closedSet: boolean = false; - - // Controls when "uniquely satisfied" triggers a re-fetch for the next - // hierarchical level. Defaults to "explicit" when omitted. - // - // "explicit" — tokens require an explicit delimiter (e.g. space) - // to commit. The user must type a separator after - // the matched completion to commit it. This suppresses - // B4 (uniquely satisfied) and lets B5 (committed-past- - // boundary) handle it when the separator actually arrives. - // "eager" — commit immediately on unique satisfaction - // (e.g. variable-space grammar where tokens can abut - // without whitespace). Re-fetches eagerly. private commitMode: CommitMode = "explicit"; // The in-flight completion request, or undefined when settled. @@ -133,13 +104,16 @@ export class PartialCompletionSession { // state so reuseSession() can still match the anchor if the user // returns (e.g. cursor moved away then back without typing). public hide(): void { + // Cancel any in-flight request but preserve anchor and config + // so reuseSession() can still match on re-focus. this.completionP = undefined; this.menu.hide(); } // Reset state to IDLE without hiding the menu (used after handleSelect inserts text). public resetToIdle(): void { - this.resetSessionFields(); + this.anchor = undefined; + this.completionP = undefined; } // Returns the text typed after the anchor, or undefined when @@ -150,12 +124,13 @@ export class PartialCompletionSession { return undefined; } const rawPrefix = input.substring(anchor.length); - if (this.requiresSeparator()) { + const sepMode = this.separatorMode; + if (requiresSeparator(sepMode)) { // The separator must be present and is not part of the replaceable prefix. - if (!this.separatorRegex().test(rawPrefix)) { + if (!separatorRegex(sepMode).test(rawPrefix)) { return undefined; } - return this.stripLeadingSeparator(rawPrefix); + return stripLeadingSeparator(rawPrefix, sepMode); } return rawPrefix; } @@ -216,18 +191,20 @@ export class PartialCompletionSession { input: string, getPosition: (prefix: string) => SearchMenuPosition | undefined, ): boolean { + // [A1] No session — IDLE state, must fetch. + if (this.anchor === undefined) { + debug(`Partial completion re-fetch: no active session (IDLE)`); + return false; + } + // PENDING — a fetch is already in flight. if (this.completionP !== undefined) { debug(`Partial completion pending: ${this.anchor}`); return true; } - // [A1] No session — IDLE state, must fetch. - const anchor = this.anchor; - if (anchor === undefined) { - debug(`Partial completion re-fetch: no active session (IDLE)`); - return false; - } + // ACTIVE from here. + const { anchor, separatorMode: sepMode, closedSet, commitMode } = this; // [A2] RE-FETCH — input moved past the anchor (e.g. backspace, new word). if (!input.startsWith(anchor)) { @@ -250,8 +227,8 @@ export class PartialCompletionSession { // separator constraint can never be satisfied without // backtracking, so treat this as a new input) const rawPrefix = input.substring(anchor.length); - const requiresSep = this.requiresSeparator(); - if (requiresSep) { + const needsSep = requiresSeparator(sepMode); + if (needsSep) { if (rawPrefix === "") { debug( `Partial completion deferred: still waiting for separator`, @@ -259,13 +236,13 @@ export class PartialCompletionSession { this.menu.hide(); return true; // HIDE+KEEP } - if (!this.separatorRegex().test(rawPrefix)) { + if (!separatorRegex(sepMode).test(rawPrefix)) { // [A3] closedSet is not consulted here: it describes whether // the completion *entries* are exhaustive, not whether // the anchor token can extend. The grammar may parse // the longer input on a completely different path. debug( - `Partial completion re-fetch: non-separator after anchor (mode='${this.separatorMode}', rawPrefix='${rawPrefix}')`, + `Partial completion re-fetch: non-separator after anchor (mode='${sepMode}', rawPrefix='${rawPrefix}')`, ); return false; // RE-FETCH (session invalidation) } @@ -273,8 +250,8 @@ export class PartialCompletionSession { // SHOW — strip the leading separator (if any) before passing to the // menu trie, so completions like "music" match prefix "" not " ". - const completionPrefix = requiresSep - ? this.stripLeadingSeparator(rawPrefix) + const completionPrefix = needsSep + ? stripLeadingSeparator(rawPrefix, sepMode) : rawPrefix; const position = getPosition(completionPrefix); @@ -293,14 +270,14 @@ export class PartialCompletionSession { // without whitespace). When "explicit", B5 handles it // once the user types a separator. if (uniquelySatisfied) { - if (this.commitMode === "eager") { + if (commitMode === "eager") { debug( `Partial completion re-fetch: '${completionPrefix}' uniquely satisfied (eager commit)`, ); return false; // RE-FETCH (hierarchical navigation) } debug( - `Partial completion: '${completionPrefix}' uniquely satisfied but commitMode='${this.commitMode}', deferring to separator`, + `Partial completion: '${completionPrefix}' uniquely satisfied but commitMode='${commitMode}', deferring to separator`, ); return true; // REUSE — wait for explicit separator before re-fetching } @@ -335,9 +312,9 @@ export class PartialCompletionSession { // typed something valid that wasn't loaded, so // re-fetch with the longer input (open-set discovery). const active = this.menu.isActive(); - const reuse = this.closedSet || active; + const reuse = closedSet || active; debug( - `Partial completion ${reuse ? "reuse" : "re-fetch"}: closedSet=${this.closedSet}, menuActive=${active}`, + `Partial completion ${reuse ? "reuse" : "re-fetch"}: closedSet=${closedSet}, menuActive=${active}`, ); return reuse; } @@ -349,9 +326,11 @@ export class PartialCompletionSession { ): void { debug(`Partial completion start: '${input}'`); this.menu.hide(); - this.resetSessionFields(); - this.anchor = input; this.menu.setChoices([]); + this.anchor = input; + this.separatorMode = "space"; + this.closedSet = false; + this.commitMode = "explicit"; const completionP = this.dispatcher.getCommandCompletion(input); this.completionP = completionP; completionP @@ -394,7 +373,7 @@ export class PartialCompletionSession { debug( `Partial completion skipped: No completions for '${input}'`, ); - // Keep this.anchor at the full input so the anchor + // Keep anchor at the full input so the anchor // covers the entire typed text. The menu stays empty, // so reuseSession()'s SHOW path will use `closedSet` to // decide: closedSet=true → reuse (nothing more exists); @@ -423,34 +402,29 @@ export class PartialCompletionSession { }) .catch((e) => { debugError(`Partial completion error: '${input}' ${e}`); + // On error, clear the in-flight promise but preserve the + // anchor so that identical input reuses the session (no + // re-fetch) while diverged input still triggers a new fetch. this.completionP = undefined; }); } +} - private resetSessionFields(): void { - this.anchor = undefined; - this.separatorMode = "space"; - this.closedSet = false; - this.commitMode = "explicit"; - } +// ── Separator helpers ──────────────────────────────────────────────────────── - private requiresSeparator(): boolean { - return ( - this.separatorMode === "space" || - this.separatorMode === "spacePunctuation" - ); - } +function requiresSeparator(mode: SeparatorMode): boolean { + return mode === "space" || mode === "spacePunctuation"; +} - private separatorRegex(): RegExp { - return this.separatorMode === "space" ? /^\s/ : /^[\s\p{P}]/u; - } +function separatorRegex(mode: SeparatorMode): RegExp { + return mode === "space" ? /^\s/ : /^[\s\p{P}]/u; +} - // Strip leading separator characters from rawPrefix. - // For "space" mode, only whitespace is stripped. - // For "spacePunctuation" mode, leading whitespace and punctuation are stripped. - private stripLeadingSeparator(rawPrefix: string): string { - return this.separatorMode === "space" - ? rawPrefix.trimStart() - : rawPrefix.replace(/^[\s\p{P}]+/u, ""); - } +// Strip leading separator characters from rawPrefix. +// For "space" mode, only whitespace is stripped. +// For "spacePunctuation" mode, leading whitespace and punctuation are stripped. +function stripLeadingSeparator(rawPrefix: string, mode: SeparatorMode): string { + return mode === "space" + ? rawPrefix.trimStart() + : rawPrefix.replace(/^[\s\p{P}]+/u, ""); } From 5bd9656dc69370e16292084b89ef94b6964425da Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 08:01:38 -0700 Subject: [PATCH 42/51] Fix fallback startIndex bugs in getCommandParameterCompletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1: Apply tokenBoundary() in the fallback back-up so startIndex lands at the end of the preceding token (before separator whitespace), matching the convention every other code path follows. startIndex when the agent was already invoked for next-param completions — prevents mismatch between startIndex and completions when the last consumed parameter is a non-string type (e.g. number). Add numstrtest agent (number+string args) and 5 new test cases covering both bugs. --- .../dispatcher/src/command/completion.ts | 35 +++- .../dispatcher/test/completion.spec.ts | 169 +++++++++++++++++- 2 files changed, 199 insertions(+), 5 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 0e01e7cf23..8ec9c90510 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -137,6 +137,32 @@ type ParameterCompletionResult = { // Complete parameter values and flags for an already-resolved command // descriptor. Returns undefined when the descriptor declares no // parameters (the caller decides whether sibling subcommands suffice). +// +// ── Spec ────────────────────────────────────────────────────────────────── +// +// 1. Parse parameters partially up to the longest valid index. +// +// 2. If parsing did NOT consume all input (remainderLength > 0): +// → startIndex = position after the longest valid prefix. +// → Offer completions for whatever can validly follow that prefix +// (next positional args, flag names). +// +// 3. If parsing consumed all input (remainderLength === 0): +// +// a. If any of the following are true, the user is still typing the +// last token — return startIndex at the *beginning* of that token +// and offer completions for it: +// • No trailing space (cursor is at the end of the last token). +// • The last parameter uses implicitQuotes (rest-of-line capture, +// never "committed" by whitespace). +// • The last token is partially quoted (open quote without a +// matching close quote). +// +// b. Otherwise (trailing space present, last token is bare or fully +// quoted) — the last token has been committed. Return startIndex +// at the *end* of the last token (excluding the trailing space) +// and offer completions for the next parameters. +// async function getCommandParameterCompletion( descriptor: CommandDescriptor, context: CommandHandlerContext, @@ -272,16 +298,23 @@ async function getCommandParameterCompletion( // filter completions against it. // Exception: fully-quoted tokens (e.g. "build") are committed // by their closing quote — no back-up needed. + // Exception: when the agent was already invoked (for nextArgs), + // its completions describe the *next* position — backing up + // would create a mismatch between startIndex and completions. const unadjustedStartIndex = tokenBoundary(input, remainderIndex); if ( !hasTrailingSpace && + !agentInvoked && params.remainderLength === 0 && params.tokens.length > 0 && startIndex === unadjustedStartIndex ) { const lastToken = params.tokens[params.tokens.length - 1]; if (isFullyQuoted(lastToken) !== true) { - startIndex = unadjustedStartIndex - lastToken.length; + startIndex = tokenBoundary( + input, + unadjustedStartIndex - lastToken.length, + ); } } diff --git a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts index daf7d8187c..a21a8d0d71 100644 --- a/ts/packages/dispatcher/dispatcher/test/completion.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/completion.spec.ts @@ -395,22 +395,75 @@ const noCommandsAgent: AppAgent = { // getCommands not defined → resolveCommand sees descriptors=undefined }; +// --------------------------------------------------------------------------- +// numstr agent — number arg followed by string arg (with getCompletion) +// --------------------------------------------------------------------------- +const numstrHandlers = { + description: "Agent with number then string arg", + defaultSubCommand: "numstr", + commands: { + numstr: { + description: "Number then string command", + parameters: { + args: { + count: { + description: "A count", + type: "number" as const, + }, + name: { + description: "A name", + }, + }, + }, + run: async () => {}, + getCompletion: async ( + _context: unknown, + _params: unknown, + names: string[], + ): Promise => { + if (!names.includes("name")) { + return { groups: [] }; + } + return { + groups: [ + { + name: "Names", + completions: ["alice", "bob"], + }, + ], + }; + }, + }, + }, +} as const; + +const numstrConfig: AppAgentManifest = { + emojiChar: "🔢", + description: "Numstr completion test", +}; + +const numstrAgent: AppAgent = { + ...getCommandInterface(numstrHandlers), +}; + const testCompletionAgentProviderMulti: AppAgentProvider = { - getAppAgentNames: () => ["comptest", "flattest", "nocmdtest"], + getAppAgentNames: () => ["comptest", "flattest", "nocmdtest", "numstrtest"], getAppAgentManifest: async (name: string) => { if (name === "comptest") return config; if (name === "flattest") return flatConfig; if (name === "nocmdtest") return noCommandsConfig; + if (name === "numstrtest") return numstrConfig; throw new Error(`Unknown: ${name}`); }, loadAppAgent: async (name: string) => { if (name === "comptest") return agent; if (name === "flattest") return flatAgent; if (name === "nocmdtest") return noCommandsAgent; + if (name === "numstrtest") return numstrAgent; throw new Error(`Unknown: ${name}`); }, unloadAppAgent: async (name: string) => { - if (!["comptest", "flattest", "nocmdtest"].includes(name)) + if (!["comptest", "flattest", "nocmdtest", "numstrtest"].includes(name)) throw new Error(`Unknown: ${name}`); }, }; @@ -839,8 +892,9 @@ describe("Command Completion - startIndex", () => { ); // "--level" is a recognized number flag, but no trailing // space → user hasn't committed. Offer flag names at the - // start of "--level" (position 20) instead of flag values. - expect(result.startIndex).toBe(20); + // tokenBoundary before "--level" (position 19, end of + // "flagsonly") instead of flag values. + expect(result.startIndex).toBe(19); const flags = result.completions.find( (g) => g.name === "Command Flags", ); @@ -1247,4 +1301,111 @@ describe("Command Completion - startIndex", () => { expect(result.closedSet).toBe(false); }); }); + + describe("Bug 1: fallback startIndex uses tokenBoundary", () => { + // When the agent has no getCommandCompletion, the fallback + // back-up path handles no-trailing-space. It must apply + // tokenBoundary() so startIndex lands at the end of the + // preceding token (before separator whitespace), matching + // the convention every other code path follows. + it("startIndex at tokenBoundary for '@comptest nested sub val' (no agent getCommandCompletion)", async () => { + const result = await getCommandCompletion( + "@comptest nested sub val", + context, + ); + // "@comptest nested sub val" (24 chars) + // 0-8: @comptest 9: sp 10-15: nested 16: sp + // 17-19: sub 20: sp 21-23: val + // "nested sub" has no getCommandCompletion, so the + // exclusive path inside `if (agent.getCommandCompletion)` + // is skipped. The fallback back-up fires because + // !hasTrailingSpace, remainderLength=0, tokens=["val"]. + // It should apply tokenBoundary to land at 20 (end of + // "sub"), not 21 (raw token start of "val"). + expect(result.startIndex).toBe(20); + }); + + it("startIndex at tokenBoundary for '@comptest nested sub --verbose val' (no agent getCommandCompletion)", async () => { + const result = await getCommandCompletion( + "@comptest nested sub --verbose val", + context, + ); + // "@comptest nested sub --verbose val" (33 chars) + // 0-8: @comptest 9: sp 10-15: nested 16: sp + // 17-19: sub 20: sp 21-29: --verbose 30: sp + // 31-33: val + // --verbose is parsed as boolean flag (defaults true), + // then "val" fills the "value" arg. No trailing space. + // Fallback should land at tokenBoundary before "val" → 30 + // (end of "--verbose"), not 31. + expect(result.startIndex).toBe(30); + }); + }); + + describe("Bug 2: fallback does not fire when agent was invoked", () => { + // When the last consumed parameter is non-string (e.g. number), + // lastCompletableParam is undefined so the exclusive path + // doesn't fire. But the agent IS invoked for nextArgs. + // The fallback must NOT back up startIndex because the + // completions describe the NEXT position, not the current token. + it("does not back up over number arg for '@numstrtest numstr 42' (no trailing space)", async () => { + const result = await getCommandCompletion( + "@numstrtest numstr 42", + context, + ); + // "@numstrtest numstr 42" (21 chars) + // 0-10: @numstrtest 11: sp 12-17: numstr 18: sp + // 19-20: 42 + // suffix = "42", parseParams consumes 42 as number arg + // "count". remainderLength=0, lastCompletableParam=undefined + // (number type). nextArgs=["name"], agent invoked for + // "name" completions. Fallback should NOT fire because + // the agent was already invoked — startIndex should stay + // at the end of consumed text (21), not back up to 19. + expect(result.startIndex).toBe(21); + // Agent was invoked for "name" completions. + const names = result.completions.find((g) => g.name === "Names"); + expect(names).toBeDefined(); + expect(names!.completions).toContain("alice"); + expect(names!.completions).toContain("bob"); + expect(result.closedSet).toBe(false); + }); + + it("baseline: '@numstrtest numstr 42 ' with trailing space works correctly", async () => { + const result = await getCommandCompletion( + "@numstrtest numstr 42 ", + context, + ); + // "@numstrtest numstr 42 " (22 chars) + // Trailing space → hasTrailingSpace=true, fallback never + // fires. startIndex = tokenBoundary(input, 22) = 21 + // (rewinds over trailing space to end of "42"). + // Agent invoked for "name" completions. + expect(result.startIndex).toBe(21); + const names = result.completions.find((g) => g.name === "Names"); + expect(names).toBeDefined(); + expect(names!.completions).toContain("alice"); + expect(names!.completions).toContain("bob"); + expect(result.closedSet).toBe(false); + }); + + it("does not back up over number arg for '@numstrtest numstr 42 al' (partial second arg)", async () => { + const result = await getCommandCompletion( + "@numstrtest numstr 42 al", + context, + ); + // "@numstrtest numstr 42 al" (24 chars) + // suffix = "42 al", parseParams: 42 → count, "al" → name. + // lastCompletableParam = "name" (string), no trailing space. + // Exclusive path fires (bare token, !hasTrailingSpace): + // backs up to before "al" → tokenBoundary(input, 22) = 21. + // Agent invoked for "name". + expect(result.startIndex).toBe(21); + const names = result.completions.find((g) => g.name === "Names"); + expect(names).toBeDefined(); + expect(names!.completions).toContain("alice"); + expect(names!.completions).toContain("bob"); + expect(result.closedSet).toBe(false); + }); + }); }); From d82b4ccf097fc48b79b84951fa91d46af13bb1bd Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 09:27:15 -0700 Subject: [PATCH 43/51] Refactor: extract resolveCompletionTarget helper from getCommandParameterCompletion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract pure decision logic into resolveCompletionTarget that determines what to complete and where, with no agent I/O. The main function becomes a simple pipeline: parse → resolve target → invoke agent → compute closedSet. Restructure spec cases so 3a covers both 'still editing' sub-cases: 3a-i: free-form parameter value (isPartialValue: true) 3a-ii: uncommitted flag name (isPartialValue: false) 3b: last token committed, complete next Rename isCurrentToken to isPartialValue to clarify its semantic: it indicates free-form text (driving closedSet=false), not just whether startIndex points at the current token. --- .../dispatcher/src/command/completion.ts | 397 +++++++++++------- 1 file changed, 239 insertions(+), 158 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 8ec9c90510..7e3259c6f2 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -134,6 +134,175 @@ type ParameterCompletionResult = { commitMode: CommitMode | undefined; }; +// ── resolveCompletionTarget ────────────────────────────────────────────── +// +// Pure decision logic: given the parse result, determine *what* to +// complete and *where* the completion attaches. No agent I/O. +// +// Returns: +// completionNames — parameter/flag names to ask the agent about. +// completions — built-in completions (flag names, boolean values). +// startIndex — index into `input` where the completion region +// begins (before any grammar matchedPrefixLength +// override). +// tokenStartIndex — raw position of the last token's first character, +// used by the caller to apply matchedPrefixLength +// arithmetic. Equal to startIndex when no token is +// being edited (next-param / remainder modes). +// isPartialValue — true when the user is mid-edit on a free-form +// parameter value (string arg or string flag value). +// When true and no agent is invoked, closedSet is +// false because any text is valid. False for +// enumerable completions (flag names, nextArgs) +// even when startIndex points at the current token. +type CompletionTarget = { + completionNames: string[]; + completions: CompletionGroup[]; + startIndex: number; + tokenStartIndex: number; + isPartialValue: boolean; +}; + +function resolveCompletionTarget( + params: ParseParamsResult, + flags: FlagDefinitions | undefined, + input: string, + hasTrailingSpace: boolean, +): CompletionTarget { + const completions: CompletionGroup[] = []; + const remainderIndex = input.length - params.remainderLength; + + // ── Pending flag detection ─────────────────────────────────────── + // If the last parsed token is a recognized flag name, it may be + // awaiting a value. Boolean flags get inline completions + // (["true","false"]) and are NOT considered pending. + const pendingFlag = getPendingFlag(params, flags, completions); + + // ── Spec case 2: partial parse (remainderLength > 0) ──────────── + // Parsing stopped partway. Offer what can follow the longest + // valid prefix. + if (params.remainderLength > 0) { + const completionNames = [...params.nextArgs]; + if (flags !== undefined) { + const flagCompletions = collectFlags( + completionNames, + flags, + params.flags, + ); + if (flagCompletions.length > 0) { + completions.push({ + name: "Command Flags", + completions: flagCompletions, + }); + } + } + const startIndex = tokenBoundary(input, remainderIndex); + return { + completionNames, + completions, + startIndex, + tokenStartIndex: startIndex, + isPartialValue: false, + }; + } + + // ── Spec case 3: full parse (remainderLength === 0) ───────────── + const { tokens, lastCompletableParam, lastParamImplicitQuotes } = params; + + // ── Spec case 3a: user is still editing the last token ────── + // The last token is uncommitted when there is no trailing space + // (or when quoting / implicitQuotes prevent commitment). Two + // sub-cases: editing a free-form parameter value, or editing a + // flag name. + + // 3a-i: free-form parameter value. lastCompletableParam is set + // only for string-type params — the only type whose partial text + // is meaningful for prefix-match completion. + if (lastCompletableParam !== undefined && tokens.length > 0) { + const valueToken = tokens[tokens.length - 1]; + const quoted = isFullyQuoted(valueToken); + if ( + quoted === false || + (quoted === undefined && lastParamImplicitQuotes) || + (quoted === undefined && + !hasTrailingSpace && + pendingFlag === undefined) + ) { + const tokenStartIndex = remainderIndex - valueToken.length; + const startIndex = tokenBoundary(input, tokenStartIndex); + return { + completionNames: [lastCompletableParam], + completions: [], // flags/nextArgs are irrelevant here + startIndex, + tokenStartIndex, + isPartialValue: true, + }; + } + } + + // 3a-ii: uncommitted flag name. A recognized flag was consumed + // but the user hasn't typed a trailing space — they might still + // change their mind (e.g. replace "--level" with "--debug"). + // Back up to the flag token's start and offer flag names. + // isPartialValue is false: flag names are an enumerable set. + if (pendingFlag !== undefined && !hasTrailingSpace) { + const flagToken = tokens[tokens.length - 1]; + const flagTokenStart = remainderIndex - flagToken.length; + const startIndex = tokenBoundary(input, flagTokenStart); + const completionNames: string[] = []; + if (flags !== undefined) { + const flagCompletions = collectFlags( + completionNames, + flags, + params.flags, + ); + if (flagCompletions.length > 0) { + completions.push({ + name: "Command Flags", + completions: flagCompletions, + }); + } + } + return { + completionNames, + completions, + startIndex, + tokenStartIndex: startIndex, + isPartialValue: false, + }; + } + + // ── Spec case 3b: last token committed, complete next ─────── + const startIndex = tokenBoundary(input, remainderIndex); + const completionNames: string[] = []; + if (pendingFlag !== undefined && hasTrailingSpace) { + // Flag awaiting a value and the user committed with a space. + completionNames.push(pendingFlag); + } else { + completionNames.push(...params.nextArgs); + if (flags !== undefined) { + const flagCompletions = collectFlags( + completionNames, + flags, + params.flags, + ); + if (flagCompletions.length > 0) { + completions.push({ + name: "Command Flags", + completions: flagCompletions, + }); + } + } + } + return { + completionNames, + completions, + startIndex, + tokenStartIndex: startIndex, + isPartialValue: false, + }; +} + // Complete parameter values and flags for an already-resolved command // descriptor. Returns undefined when the descriptor declares no // parameters (the caller decides whether sibling subcommands suffice). @@ -149,19 +318,24 @@ type ParameterCompletionResult = { // // 3. If parsing consumed all input (remainderLength === 0): // -// a. If any of the following are true, the user is still typing the -// last token — return startIndex at the *beginning* of that token -// and offer completions for it: -// • No trailing space (cursor is at the end of the last token). -// • The last parameter uses implicitQuotes (rest-of-line capture, -// never "committed" by whitespace). -// • The last token is partially quoted (open quote without a -// matching close quote). +// a. The user is still editing the last token — return startIndex +// at the *beginning* of that token: +// +// i. Free-form parameter value (lastCompletableParam is set): +// triggered when the token is partially quoted, uses +// implicitQuotes, or is a bare unquoted token with no +// trailing space. Completions come from the agent for +// that parameter. // -// b. Otherwise (trailing space present, last token is bare or fully -// quoted) — the last token has been committed. Return startIndex -// at the *end* of the last token (excluding the trailing space) -// and offer completions for the next parameters. +// ii. Uncommitted flag name (pendingFlag with no trailing +// space): the flag was recognized but the user hasn't +// committed it. Offer flag names so the user can change +// their choice. +// +// b. Otherwise — the last token has been committed (trailing space +// present, or fully quoted). Return startIndex at the *end* of +// the last token (excluding trailing space) and offer completions +// for the next parameters. // async function getCommandParameterCompletion( descriptor: CommandDescriptor, @@ -170,182 +344,89 @@ async function getCommandParameterCompletion( input: string, hasTrailingSpace: boolean, ): Promise { - const completions: CompletionGroup[] = []; if (typeof descriptor.parameters !== "object") { - // No more completion, return undefined; return undefined; } - const flags = descriptor.parameters.flags; const params: ParseParamsResult = parseParams( result.suffix, descriptor.parameters, true, ); - const pendingFlag = getPendingFlag(params, flags, completions); - const agentCommandCompletions: string[] = []; - if (pendingFlag !== undefined && hasTrailingSpace) { - // The last token is a recognized flag and the user committed - // it with a trailing space. Ask the agent for flag values. - agentCommandCompletions.push(pendingFlag); - } else { - // Either no pending flag, or the flag isn't committed yet - // (no trailing space). Offer positional args and flag names. - agentCommandCompletions.push(...params.nextArgs); - if (flags !== undefined) { - const flagCompletions = collectFlags( - agentCommandCompletions, - flags, - params.flags, - ); - if (flagCompletions.length > 0) { - completions.push({ - name: "Command Flags", - completions: flagCompletions, - }); - } - } - } - // Compute startIndex from how far parseParams consumed the suffix. - // remainderLength is the length of the (trimmed) parameter text - // that was NOT successfully parsed — everything before it is part - // of the longest valid prefix. Since parseParams strips inter- - // token whitespace (trimStart), the raw arithmetic can land on - // the separator space — tokenBoundary rewinds to the preceding - // token edge. - const remainderIndex = input.length - params.remainderLength; - let startIndex = tokenBoundary(input, remainderIndex); + // ── 1. Decide what to complete and where ───────────────────────── + const target = resolveCompletionTarget( + params, + descriptor.parameters.flags, + input, + hasTrailingSpace, + ); + let { startIndex } = target; + const completions = [...target.completions]; debug( `Command completion parameter consumed length: ${params.remainderLength}`, ); + // ── 2. Invoke agent (if available) ─────────────────────────────── let agentInvoked = false; let agentClosedSet: boolean | undefined; let agentCommitMode: CommitMode | undefined; let separatorMode: SeparatorMode | undefined; - const agent = context.agents.getAppAgent(result.actualAppAgentName); - if (agent.getCommandCompletion) { - const { tokens, lastCompletableParam, lastParamImplicitQuotes } = - params; - - let tokenStartIndex = remainderIndex; - if (lastCompletableParam !== undefined && tokens.length > 0) { - const valueToken = tokens[tokens.length - 1]; - const quoted = isFullyQuoted(valueToken); - if ( - quoted === false || - (quoted === undefined && lastParamImplicitQuotes) || - (quoted === undefined && - !hasTrailingSpace && - pendingFlag === undefined) - ) { - // The user is inside a token (open quote or - // implicitQuotes rest-of-line) — completions for - // other parameters or flags at the original - // startIndex are invalid because no new token can - // start. Make this path exclusive: clear earlier - // completions and adjust startIndex to the token - // start so the caller replaces the partial token. - agentCommandCompletions.length = 0; - completions.length = 0; - agentCommandCompletions.push(lastCompletableParam); - tokenStartIndex = remainderIndex - valueToken.length; - startIndex = tokenBoundary(input, tokenStartIndex); - } - } - if (agentCommandCompletions.length > 0) { - const agentName = result.actualAppAgentName; - const sessionContext = context.agents.getSessionContext(agentName); - debug( - `Command completion parameter with agent: '${agentName}' with params ${JSON.stringify(agentCommandCompletions)}`, - ); - const agentResult: CompletionGroups = - await agent.getCommandCompletion( - result.commands, - params, - agentCommandCompletions, - sessionContext, - ); - // Allow grammar-reported matchedPrefixLength to override - // the parse-derived startIndex. This handles CJK and other - // non-space-delimited scripts where the grammar matcher is the - // authoritative source for how far into the input it consumed. - // Grammar matchedPrefixLength is relative to the token content - // start (after the separator space), not to tokenBoundary - // (before it), so use tokenStartIndex when available. - const groupPrefixLength = agentResult.matchedPrefixLength; - if (groupPrefixLength !== undefined && groupPrefixLength != 0) { - startIndex = tokenStartIndex + groupPrefixLength; - // we have advanced the startIndex, so existing completions are no longer valid, clear them out. - completions.length = 0; - } - completions.push(...agentResult.groups); - separatorMode = agentResult.separatorMode; - agentInvoked = true; - agentClosedSet = agentResult.closedSet; - agentCommitMode = agentResult.commitMode; - debug( - `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${tokenStartIndex}`, - ); - } - } + const agent = context.agents.getAppAgent(result.actualAppAgentName); + if (agent.getCommandCompletion && target.completionNames.length > 0) { + const agentName = result.actualAppAgentName; + const sessionContext = context.agents.getSessionContext(agentName); + debug( + `Command completion parameter with agent: '${agentName}' with params ${JSON.stringify(target.completionNames)}`, + ); + const agentResult: CompletionGroups = await agent.getCommandCompletion( + result.commands, + params, + target.completionNames, + sessionContext, + ); - // When there is no trailing space, the last consumed token - // hasn't been committed. If no earlier path already adjusted - // startIndex (lastCompletableParam / grammar-matchedPrefixLength), - // back up to the start of that token so the caller's trie can - // filter completions against it. - // Exception: fully-quoted tokens (e.g. "build") are committed - // by their closing quote — no back-up needed. - // Exception: when the agent was already invoked (for nextArgs), - // its completions describe the *next* position — backing up - // would create a mismatch between startIndex and completions. - const unadjustedStartIndex = tokenBoundary(input, remainderIndex); - if ( - !hasTrailingSpace && - !agentInvoked && - params.remainderLength === 0 && - params.tokens.length > 0 && - startIndex === unadjustedStartIndex - ) { - const lastToken = params.tokens[params.tokens.length - 1]; - if (isFullyQuoted(lastToken) !== true) { - startIndex = tokenBoundary( - input, - unadjustedStartIndex - lastToken.length, - ); + // Allow grammar-reported matchedPrefixLength to override + // the parse-derived startIndex. This handles CJK and other + // non-space-delimited scripts where the grammar matcher is + // the authoritative source for how far into the input it + // consumed. matchedPrefixLength is relative to the token + // content start, so add it to tokenStartIndex. + const groupPrefixLength = agentResult.matchedPrefixLength; + if (groupPrefixLength !== undefined && groupPrefixLength !== 0) { + startIndex = target.tokenStartIndex + groupPrefixLength; + completions.length = 0; // grammar overrides built-in completions } + completions.push(...agentResult.groups); + separatorMode = agentResult.separatorMode; + agentInvoked = true; + agentClosedSet = agentResult.closedSet; + agentCommitMode = agentResult.commitMode; + debug( + `Command completion parameter with agent: groupPrefixLength=${groupPrefixLength}, startIndex=${startIndex}, tokenStartIndex=${target.tokenStartIndex}`, + ); } - // Determine whether the completion set is a closed set. - // Agent-provided completions use the agent's self-reported - // closedSet flag (via CompletionGroups.closedSet), defaulting to - // false when the agent doesn't set it. - // When no agent is involved, the set is closed if: - // - A pending boolean flag offers only ["true", "false"] - // - All positional args are filled and only enumerable flags remain - // Otherwise free-form text parameters make the set open. + // ── 3. Determine closedSet ─────────────────────────────────────── let closedSet: boolean; if (agentInvoked) { closedSet = agentClosedSet ?? false; - } else if (pendingFlag !== undefined) { - // A non-boolean pending flag accepts free-form values. - // (Boolean flags are handled by getPendingFlag returning undefined - // and pushing ["true", "false"] into completions directly.) + } else if (target.isPartialValue) { + // Editing a free-form value with no agent → open set. closedSet = false; } else { - // No agent, no pending flag. Closed set when there are no - // unfilled positional args (nextArgs is empty) and only - // flags remain — flag names are a finite, known set. + // No agent. Closed when all positional args are filled and + // only flags (a finite set) remain. closedSet = params.nextArgs.length === 0; } - // Propagate agent-provided commitMode. When the agent doesn't - // specify one, leave undefined so the caller can apply its default. - const commitMode: CommitMode | undefined = agentCommitMode; - - return { completions, startIndex, separatorMode, closedSet, commitMode }; + return { + completions, + startIndex, + separatorMode, + closedSet, + commitMode: agentCommitMode, + }; } // From 462dd5f213d6975d1eff6292ad66404ef5f9ea8c Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 09:34:43 -0700 Subject: [PATCH 44/51] Document spec exceptions for non-string params without trailing space Case 3a depends on lastCompletableParam (string-type only) and pendingFlag. Number, boolean, and json params leave lastCompletableParam undefined, so they fall through to 3b even without trailing space. This is acceptable because non-string values are not meaningful targets for prefix-match completion. --- .../dispatcher/src/command/completion.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 7e3259c6f2..69c4486d75 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -337,6 +337,27 @@ function resolveCompletionTarget( // the last token (excluding trailing space) and offer completions // for the next parameters. // +// ── Exceptions to case 3a ──────────────────────────────────────────────── +// +// Case 3a depends on lastCompletableParam (for 3a-i) and pendingFlag +// (for 3a-ii). parseParams only sets lastCompletableParam for +// *string*-type parameters: number, boolean, and json params leave it +// undefined. This means the following scenarios fall through to 3b +// even though the user has not typed a trailing space: +// +// • A number arg without trailing space (e.g. "cmd 42") +// • A boolean arg without trailing space (e.g. "cmd true") +// • A number flag value without trailing space (e.g. "cmd --level 5") +// +// In these cases startIndex stays at the end of the last token and +// completions describe what comes *next* rather than the current +// token. This is acceptable because non-string values are not +// meaningful targets for prefix-match completion — there is no useful +// set of candidates to filter against a partial "42" or "tru". The +// caller's trie will see an empty suffix (startIndex == input.length) +// and present the next-parameter completions unfiltered, which is the +// most useful behavior for the user. +// async function getCommandParameterCompletion( descriptor: CommandDescriptor, context: CommandHandlerContext, From 5ca59f93be7fac182a11447d0a4c98a2618a0100 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 09:54:08 -0700 Subject: [PATCH 45/51] Make resolveCompletionTarget purely decisional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace getPendingFlag (which pushed boolean completions as a side effect) with detectPendingFlag returning {pendingFlag, booleanFlagName}. Remove completions array from CompletionTarget; add includeFlags and booleanFlagName metadata fields instead. resolveCompletionTarget now returns only decisions — no collectFlags calls, no completion building. The caller (getCommandParameterCompletion) materialises completions from the target metadata in a new step 2, before invoking the agent. --- .../dispatcher/src/command/completion.ts | 165 +++++++++--------- 1 file changed, 84 insertions(+), 81 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 69c4486d75..6ae4792672 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -31,33 +31,47 @@ import { CommandCompletionResult } from "@typeagent/dispatcher-types"; const debug = registerDebug("typeagent:command:completion"); const debugError = registerDebug("typeagent:command:completion:error"); -// Return the full flag name if we are waiting a flag value. Add boolean values for completions and return undefined if the flag is boolean. -function getPendingFlag( +// Detect whether the last parsed token is a recognized flag name +// awaiting a value. Pure: returns metadata only, no side effects. +// +// pendingFlag — for non-boolean flags: the canonical name +// (e.g. "--level") to pass to the agent for +// value completions. undefined for boolean +// flags (they default to true and don't pend). +// booleanFlagName — for boolean flags: the canonical display name +// (e.g. "--debug") so the caller can offer +// ["true", "false"]. undefined otherwise. +type PendingFlagInfo = { + pendingFlag: string | undefined; + booleanFlagName: string | undefined; +}; + +function detectPendingFlag( params: ParseParamsResult, flags: FlagDefinitions | undefined, - completions: CompletionGroup[], -) { +): PendingFlagInfo { if (params.tokens.length === 0 || flags === undefined) { - return undefined; + return { pendingFlag: undefined, booleanFlagName: undefined }; } const lastToken = params.tokens[params.tokens.length - 1]; const resolvedFlag = resolveFlag(flags, lastToken); if (resolvedFlag === undefined) { - return undefined; + return { pendingFlag: undefined, booleanFlagName: undefined }; } const type = getFlagType(resolvedFlag[1]); if (type === "boolean") { - completions.push({ - name: `--${resolvedFlag[0]}`, - completions: ["true", "false"], - }); - return undefined; // doesn't require a value. + return { + pendingFlag: undefined, + booleanFlagName: `--${resolvedFlag[0]}`, + }; } if (type === "json") { - return lastToken; + return { pendingFlag: lastToken, booleanFlagName: undefined }; } - - return `--${resolvedFlag[0]}`; // use the full flag name in case it was a short flag + return { + pendingFlag: `--${resolvedFlag[0]}`, + booleanFlagName: undefined, + }; } // Rewind index past any trailing whitespace in `text` so it sits @@ -137,11 +151,11 @@ type ParameterCompletionResult = { // ── resolveCompletionTarget ────────────────────────────────────────────── // // Pure decision logic: given the parse result, determine *what* to -// complete and *where* the completion attaches. No agent I/O. +// complete and *where* the completion attaches. No I/O, no completion +// building — the caller materialises completions from the target. // // Returns: // completionNames — parameter/flag names to ask the agent about. -// completions — built-in completions (flag names, boolean values). // startIndex — index into `input` where the completion region // begins (before any grammar matchedPrefixLength // override). @@ -155,12 +169,17 @@ type ParameterCompletionResult = { // false because any text is valid. False for // enumerable completions (flag names, nextArgs) // even when startIndex points at the current token. +// includeFlags — true when the caller should add flag-name +// completions via collectFlags. +// booleanFlagName — when non-undefined, the caller should add +// ["true","false"] completions for this flag. type CompletionTarget = { completionNames: string[]; - completions: CompletionGroup[]; startIndex: number; tokenStartIndex: number; isPartialValue: boolean; + includeFlags: boolean; + booleanFlagName: string | undefined; }; function resolveCompletionTarget( @@ -169,40 +188,23 @@ function resolveCompletionTarget( input: string, hasTrailingSpace: boolean, ): CompletionTarget { - const completions: CompletionGroup[] = []; const remainderIndex = input.length - params.remainderLength; // ── Pending flag detection ─────────────────────────────────────── - // If the last parsed token is a recognized flag name, it may be - // awaiting a value. Boolean flags get inline completions - // (["true","false"]) and are NOT considered pending. - const pendingFlag = getPendingFlag(params, flags, completions); + const { pendingFlag, booleanFlagName } = detectPendingFlag(params, flags); // ── Spec case 2: partial parse (remainderLength > 0) ──────────── // Parsing stopped partway. Offer what can follow the longest // valid prefix. if (params.remainderLength > 0) { - const completionNames = [...params.nextArgs]; - if (flags !== undefined) { - const flagCompletions = collectFlags( - completionNames, - flags, - params.flags, - ); - if (flagCompletions.length > 0) { - completions.push({ - name: "Command Flags", - completions: flagCompletions, - }); - } - } const startIndex = tokenBoundary(input, remainderIndex); return { - completionNames, - completions, + completionNames: [...params.nextArgs], startIndex, tokenStartIndex: startIndex, isPartialValue: false, + includeFlags: true, + booleanFlagName, }; } @@ -210,10 +212,6 @@ function resolveCompletionTarget( const { tokens, lastCompletableParam, lastParamImplicitQuotes } = params; // ── Spec case 3a: user is still editing the last token ────── - // The last token is uncommitted when there is no trailing space - // (or when quoting / implicitQuotes prevent commitment). Two - // sub-cases: editing a free-form parameter value, or editing a - // flag name. // 3a-i: free-form parameter value. lastCompletableParam is set // only for string-type params — the only type whose partial text @@ -232,10 +230,11 @@ function resolveCompletionTarget( const startIndex = tokenBoundary(input, tokenStartIndex); return { completionNames: [lastCompletableParam], - completions: [], // flags/nextArgs are irrelevant here startIndex, tokenStartIndex, isPartialValue: true, + includeFlags: false, + booleanFlagName: undefined, }; } } @@ -249,57 +248,36 @@ function resolveCompletionTarget( const flagToken = tokens[tokens.length - 1]; const flagTokenStart = remainderIndex - flagToken.length; const startIndex = tokenBoundary(input, flagTokenStart); - const completionNames: string[] = []; - if (flags !== undefined) { - const flagCompletions = collectFlags( - completionNames, - flags, - params.flags, - ); - if (flagCompletions.length > 0) { - completions.push({ - name: "Command Flags", - completions: flagCompletions, - }); - } - } return { - completionNames, - completions, + completionNames: [], startIndex, tokenStartIndex: startIndex, isPartialValue: false, + includeFlags: true, + booleanFlagName, }; } // ── Spec case 3b: last token committed, complete next ─────── const startIndex = tokenBoundary(input, remainderIndex); - const completionNames: string[] = []; if (pendingFlag !== undefined && hasTrailingSpace) { // Flag awaiting a value and the user committed with a space. - completionNames.push(pendingFlag); - } else { - completionNames.push(...params.nextArgs); - if (flags !== undefined) { - const flagCompletions = collectFlags( - completionNames, - flags, - params.flags, - ); - if (flagCompletions.length > 0) { - completions.push({ - name: "Command Flags", - completions: flagCompletions, - }); - } - } + return { + completionNames: [pendingFlag], + startIndex, + tokenStartIndex: startIndex, + isPartialValue: false, + includeFlags: false, + booleanFlagName: undefined, + }; } return { - completionNames, - completions, + completionNames: [...params.nextArgs], startIndex, tokenStartIndex: startIndex, isPartialValue: false, + includeFlags: true, + booleanFlagName, }; } @@ -382,12 +360,37 @@ async function getCommandParameterCompletion( hasTrailingSpace, ); let { startIndex } = target; - const completions = [...target.completions]; debug( `Command completion parameter consumed length: ${params.remainderLength}`, ); - // ── 2. Invoke agent (if available) ─────────────────────────────── + // ── 2. Materialise built-in completions from the target ────────── + const completions: CompletionGroup[] = []; + if (target.booleanFlagName !== undefined) { + completions.push({ + name: target.booleanFlagName, + completions: ["true", "false"], + }); + } + if (target.includeFlags && descriptor.parameters.flags !== undefined) { + const flagCompletions = collectFlags( + target.completionNames, + descriptor.parameters.flags, + params.flags, + ); + if (flagCompletions.length > 0) { + completions.push({ + name: "Command Flags", + completions: flagCompletions, + }); + } + } + + // ── 3. Invoke agent (if available) ─────────────────────────────── + // Note: collectFlags (above) mutates target.completionNames as a + // side effect, appending "--key." entries for JSON property flags. + // This must happen before the agent call so the agent sees the + // full list of names to complete. let agentInvoked = false; let agentClosedSet: boolean | undefined; let agentCommitMode: CommitMode | undefined; @@ -428,7 +431,7 @@ async function getCommandParameterCompletion( ); } - // ── 3. Determine closedSet ─────────────────────────────────────── + // ── 4. Determine closedSet ─────────────────────────────────────── let closedSet: boolean; if (agentInvoked) { closedSet = agentClosedSet ?? false; From ca0ab0ce4b61a0e93634c56b255df9840d5f9f48 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 19:12:56 -0700 Subject: [PATCH 46/51] Improve construction cache completion: track prefix length, separator mode, and closed set - Narrow requestPrefix from string|undefined to string throughout the completion pipeline (cache, grammarStore, constructionStore, dispatcher). Callers pass empty string instead of undefined. - Rework ConstructionCache.completion() to track maxPrefixLength across all matching constructions, discarding shorter-match candidates. Report separatorMode and closedSet for downstream UI use. - Change mergeCompletionResults to eagerly discard completions from the shorter-prefix source instead of always merging both. - Propagate matchedCurrent character offset from partial matching so the completion layer knows exactly how far matching consumed. Accept partial matches even when remaining text has non-separator characters (the UI filters by remaining text). - Filter referential phrases from captured parts in completion candidates. - Move command prefix "@" to only appear when startIndex is 0 (first token), with separatorMode optional and commitMode eager. - Add comprehensive ConstructionCache.completion() tests and update mergeCompletionResults tests for the new discard behavior. - Remove unused getPrefix() method. - Fix typo: restor -> restore. --- ts/.vscode/settings.json | 1 + ts/packages/cache/src/cache/cache.ts | 14 +- .../cache/src/cache/constructionStore.ts | 5 +- ts/packages/cache/src/cache/grammarStore.ts | 20 +- ts/packages/cache/src/cache/types.ts | 1 + .../src/constructions/constructionCache.ts | 183 ++++-- .../src/constructions/constructionMatch.ts | 12 +- .../src/constructions/constructionValue.ts | 1 + .../cache/src/constructions/constructions.ts | 1 + ts/packages/cache/test/completion.spec.ts | 553 ++++++++++++++++++ .../cache/test/mergeCompletionResults.spec.ts | 29 +- .../dispatcher/src/command/completion.ts | 22 +- .../handlers/requestCommandHandler.ts | 2 +- .../src/translation/requestCompletion.ts | 2 +- 14 files changed, 749 insertions(+), 97 deletions(-) create mode 100644 ts/packages/cache/test/completion.spec.ts diff --git a/ts/.vscode/settings.json b/ts/.vscode/settings.json index dec86f76d6..2ff2419e62 100644 --- a/ts/.vscode/settings.json +++ b/ts/.vscode/settings.json @@ -3,6 +3,7 @@ "cSpell.words": [ "aiclient", "AUTOINCREMENT", + "behaviour", "Chunker", "Exif", "exifreader", diff --git a/ts/packages/cache/src/cache/cache.ts b/ts/packages/cache/src/cache/cache.ts index 3046c7e91c..be93c2a52b 100644 --- a/ts/packages/cache/src/cache/cache.ts +++ b/ts/packages/cache/src/cache/cache.ts @@ -589,6 +589,7 @@ export class AgentCache { // Otherwise use completion-based construction store debug(`match: Using completion-based construction store`); const store = this._constructionStore; + const grammarStore = this._grammarStore; if (store.isEnabled()) { const constructionMatches = store.match(request, options); if (constructionMatches.length > 0) { @@ -598,18 +599,19 @@ export class AgentCache { return rest; }); } + if (!grammarStore.isEnabled()) { + return []; + } + } else if (!grammarStore.isEnabled()) { + throw new Error("AgentCache is disabled"); } // Fallback to grammar store if construction store has no matches - const grammarStore = this._grammarStore; - if (grammarStore.isEnabled()) { - return this._grammarStore.match(request, options); - } - throw new Error("AgentCache is disabled"); + return grammarStore.match(request, options); } public completion( - requestPrefix: string | undefined, + requestPrefix: string, options?: MatchOptions, ): CompletionResult | undefined { // If NFA grammar system is configured, only use grammar store diff --git a/ts/packages/cache/src/cache/constructionStore.ts b/ts/packages/cache/src/cache/constructionStore.ts index 4c48be78ad..be13021d95 100644 --- a/ts/packages/cache/src/cache/constructionStore.ts +++ b/ts/packages/cache/src/cache/constructionStore.ts @@ -395,10 +395,7 @@ export class ConstructionStoreImpl implements ConstructionStore { return sortedMatches; } - public completion( - requestPrefix: string | undefined, - options?: MatchOptions, - ) { + public completion(requestPrefix: string, options?: MatchOptions) { const cacheCompletion = this.cache?.completion(requestPrefix, options); const builtInCompletion = this.builtInCache?.completion( requestPrefix, diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index 97f7abd513..d6eb084cae 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -262,7 +262,7 @@ export class GrammarStoreImpl implements GrammarStore { } public completion( - requestPrefix: string | undefined, + requestPrefix: string, options?: MatchOptions, ): CompletionResult | undefined { if (!this.enabled) { @@ -285,11 +285,10 @@ export class GrammarStoreImpl implements GrammarStore { if (this.useDFA && entry.dfa) { // DFA-based completions const tokens = requestPrefix - ? requestPrefix - .trim() - .split(/\s+/) - .filter((t) => t.length > 0) - : []; + .trim() + .split(/\s+/) + .filter((t) => t.length > 0); + const dfaCompResult = getDFACompletions(entry.dfa, tokens); if ( dfaCompResult.completions && @@ -318,11 +317,10 @@ export class GrammarStoreImpl implements GrammarStore { } else if (this.useNFA && entry.nfa) { // NFA-based completions: tokenize into complete whole tokens const tokens = requestPrefix - ? requestPrefix - .trim() - .split(/\s+/) - .filter((t) => t.length > 0) - : []; + .trim() + .split(/\s+/) + .filter((t) => t.length > 0); + const nfaResult = computeNFACompletions(entry.nfa, tokens); if (nfaResult.completions.length > 0) { completions.push(...nfaResult.completions); diff --git a/ts/packages/cache/src/cache/types.ts b/ts/packages/cache/src/cache/types.ts index ef5b1b863d..739086b0f6 100644 --- a/ts/packages/cache/src/cache/types.ts +++ b/ts/packages/cache/src/cache/types.ts @@ -13,6 +13,7 @@ export type MatchResult = { entityWildcardPropertyNames: string[]; conflictValues?: [string, ParamValueType[]][] | undefined; partialPartCount?: number | undefined; // Only used for partial match + partialMatchedCurrent?: number | undefined; // Character offset where partial matching stopped }; export interface GrammarStore { diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index eafa2fb374..6218587c1a 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -25,9 +25,33 @@ import { ConstructionCacheJSON, constructionCacheJSONVersion, } from "./constructionJSONTypes.js"; +import { getLanguageTools } from "../utils/language.js"; const debugConst = registerDebug("typeagent:const"); const debugConstMatchStat = registerDebug("typeagent:const:match:stat"); const debugCompletion = registerDebug("typeagent:const:completion"); + +// Separator heuristic for "auto" spacing mode. A separator is required +// between two adjacent characters only when both belong to a word-boundary +// script (e.g. Latin + Latin) or both are digits. This mirrors the +// grammar matcher's needsSeparatorInAutoMode without adding a cross-package +// dependency. +const wordBoundaryScriptRe = + /\p{Script=Latin}|\p{Script=Cyrillic}|\p{Script=Greek}|\p{Script=Armenian}|\p{Script=Georgian}|\p{Script=Hangul}|\p{Script=Arabic}|\p{Script=Hebrew}|\p{Script=Devanagari}|\p{Script=Bengali}|\p{Script=Tamil}|\p{Script=Telugu}|\p{Script=Kannada}|\p{Script=Malayalam}|\p{Script=Gujarati}|\p{Script=Gurmukhi}|\p{Script=Oriya}|\p{Script=Sinhala}|\p{Script=Ethiopic}|\p{Script=Mongolian}/u; +const digitRe = /[0-9]/; +function isWordBoundaryScript(c: string): boolean { + const code = c.charCodeAt(0); + if (code < 128) { + return (code >= 65 && code <= 90) || (code >= 97 && code <= 122); + } + return wordBoundaryScriptRe.test(c); +} +function needsSeparatorInAutoMode(a: string, b: string): boolean { + if (digitRe.test(a) && digitRe.test(b)) { + return true; + } + return isWordBoundaryScript(a) && isWordBoundaryScript(b); +} + // Agent Cache define the namespace policy. At the cache, it just combine the keys into a string for lookup. function getConstructionNamespace(namespaceKeys: string[]) { // Combine the namespace keys into a string using | as the separator. Use to filter easily when @@ -97,19 +121,23 @@ export function mergeCompletionResults( if (second === undefined) { return first; } - // TODO: Be consistent with grammarMatcher and eagerly discard - // shorter-prefix completions instead of retaining them and - // relying on the caller to filter. - let matchedPrefixLength: number | undefined; - if ( + // Eagerly discard shorter-prefix completions — consistent with the + // grammar matcher's approach. Only the source(s) with the longest + // matchedPrefixLength contribute completions. + const firstLen = first.matchedPrefixLength ?? 0; + const secondLen = second.matchedPrefixLength ?? 0; + if (firstLen > secondLen) { + return first; + } + if (secondLen > firstLen) { + return second; + } + // Same prefix length — merge completions from both sources. + const matchedPrefixLength = first.matchedPrefixLength !== undefined || second.matchedPrefixLength !== undefined - ) { - matchedPrefixLength = Math.max( - first.matchedPrefixLength ?? 0, - second.matchedPrefixLength ?? 0, - ); - } + ? firstLen + : undefined; return { completions: [...first.completions, ...second.completions], properties: first.properties @@ -321,40 +349,6 @@ export class ConstructionCache { return count; } - // For completion - private getPrefix(namespaceKeys?: string[]): string[] { - if (namespaceKeys?.length === 0) { - return []; - } - const prefix = new Set(); - const filter = namespaceKeys ? new Set(namespaceKeys) : undefined; - for (const [ - name, - constructionNamespace, - ] of this.constructionNamespaces.entries()) { - const keys = getNamespaceKeys(name); - if (filter && keys.some((key) => !filter.has(key))) { - continue; - } - - for (const construction of constructionNamespace.constructions) { - for (const part of construction.parts) { - if (part.optional) { - continue; - } - if (isMatchPart(part) && part.matchSet) { - // For match parts, we can use the match set name as the prefix - for (const match of part.matchSet.matches.values()) { - prefix.add(match); - } - } - break; - } - } - } - return [...prefix.values()]; - } - // For matching public match( request: string, @@ -397,21 +391,12 @@ export class ConstructionCache { } public completion( - requestPrefix: string | undefined, + requestPrefix: string, options?: MatchOptions, ): CompletionResult | undefined { debugCompletion(`Request completion for prefix: '${requestPrefix}'`); const namespaceKeys = options?.namespaceKeys; debugCompletion(`Request completion namespace keys`, namespaceKeys); - if (!requestPrefix) { - const completions = this.getPrefix(namespaceKeys); - - return completions.length > 0 - ? { - completions, - } - : undefined; - } const results = this.match(requestPrefix, options, true); @@ -419,10 +404,37 @@ export class ConstructionCache { `Request completion construction match: ${results.length}`, ); + if (results.length === 0) { + return undefined; + } + + // Track the furthest character position consumed across all + // matching constructions. When a longer match is found, all + // previously accumulated completions from shorter matches are + // discarded — mirroring the grammar matcher's approach. + let maxPrefixLength = 0; const completionProperty: CompletionProperty[] = []; const requestText: string[] = []; + let separatorMode: SeparatorMode | undefined; + // Whether the accumulated completions form a closed set. + // Starts true; set to false when property/wildcard completions + // are added (entity values are external). Reset to true when + // maxPrefixLength advances (old candidates discarded). + let closedSet: boolean = true; + + function updateMaxPrefixLength(prefixLength: number): void { + if (prefixLength > maxPrefixLength) { + maxPrefixLength = prefixLength; + requestText.length = 0; + completionProperty.length = 0; + separatorMode = undefined; + closedSet = true; + } + } + for (const result of results) { - const { construction, partialPartCount } = result; + const { construction, partialPartCount, partialMatchedCurrent } = + result; if (partialPartCount === undefined) { throw new Error( "Internal Error: Partial part count is undefined", @@ -430,7 +442,17 @@ export class ConstructionCache { } if (partialPartCount === construction.parts.length) { - continue; // No more parts to complete + // Exact match — all parts matched. Nothing to complete, + // but advance maxPrefixLength so shorter candidates are + // discarded. + updateMaxPrefixLength(requestPrefix.length); + continue; + } + + const candidatePrefixLength = partialMatchedCurrent ?? 0; + updateMaxPrefixLength(candidatePrefixLength); + if (candidatePrefixLength !== maxPrefixLength) { + continue; // Shorter than the best match — skip } const nextPart = construction.parts[partialPartCount]; @@ -438,7 +460,33 @@ export class ConstructionCache { if (nextPart.wildcardMode <= WildcardMode.Enabled) { const partCompletions = nextPart.getCompletion(); if (partCompletions) { - requestText.push(...partCompletions); + const langTools = getLanguageTools("en"); + const rejectReferences = options?.rejectReferences ?? true; + for (const completionText of partCompletions) { + // We would have rejected the value if this part is captured. + if ( + nextPart.capture && + rejectReferences && + langTools?.possibleReferentialPhrase(completionText) + ) { + continue; + } + requestText.push(completionText); + // Determine separator mode for this candidate. + if ( + candidatePrefixLength > 0 && + completionText.length > 0 + ) { + const needsSep = needsSeparatorInAutoMode( + requestPrefix[candidatePrefixLength - 1], + completionText[0], + ); + separatorMode = mergeSeparatorMode( + separatorMode, + needsSep ? "spacePunctuation" : "optional", + ); + } + } } } @@ -471,12 +519,31 @@ export class ConstructionCache { actions: result.match.actions, names: queryPropertyNames, }); + // Determine separator mode for the property/entity slot. + // Use "a" as a representative word character since the + // actual entity value is unknown. + if (candidatePrefixLength > 0) { + const needsSep = needsSeparatorInAutoMode( + requestPrefix[candidatePrefixLength - 1], + "a", + ); + separatorMode = mergeSeparatorMode( + separatorMode, + needsSep ? "spacePunctuation" : "optional", + ); + } + // Property/wildcard completions are not a closed set — + // entity values are external. + closedSet = false; } } return { completions: requestText, properties: completionProperty, + matchedPrefixLength: maxPrefixLength, + separatorMode, + closedSet, }; } diff --git a/ts/packages/cache/src/constructions/constructionMatch.ts b/ts/packages/cache/src/constructions/constructionMatch.ts index 83c445b7a9..09bbbcb2ff 100644 --- a/ts/packages/cache/src/constructions/constructionMatch.ts +++ b/ts/packages/cache/src/constructions/constructionMatch.ts @@ -64,6 +64,7 @@ export function matchParts( if (values !== undefined) { if (config.partial) { values.partialPartCount = state.matchedStart.length; + values.matchedCurrent = state.matchedCurrent; } return values; } @@ -201,6 +202,15 @@ function finishMatchParts( } if (state.pendingWildcard === -1) { + if (config.partial && state.matchedStart.length < parts.length) { + // Partial mode broke out of the loop before matching all + // parts. Accept the partial match even when the remaining + // text contains non-separator characters — the completion + // layer will return the next part's candidates and the + // caller (UI) filters by the remaining text, matching the + // grammar matcher's behaviour. + return true; + } // The tail should only be space or punctuation return ( state.matchedCurrent === request.length || @@ -367,7 +377,7 @@ function backtrack( // Check if it is optional, backtrack to before the optional and resume the search if (backtrackPart.optional) { // REVIEW: the constructor enforced that parts before and after a wildcard can't be optional. - // Otherwise, we need to restor pendingWildcard state here. + // Otherwise, we need to restore pendingWildcard state here. state.matchedStart.push(-1); return true; } diff --git a/ts/packages/cache/src/constructions/constructionValue.ts b/ts/packages/cache/src/constructions/constructionValue.ts index fa8f89955f..0a79147860 100644 --- a/ts/packages/cache/src/constructions/constructionValue.ts +++ b/ts/packages/cache/src/constructions/constructionValue.ts @@ -37,6 +37,7 @@ export type MatchedValues = { matchedCount: number; wildcardCharCount: number; partialPartCount?: number; // Only used for partial match + matchedCurrent?: number; // Character offset where matching stopped (partial only) }; export function matchedValues( diff --git a/ts/packages/cache/src/constructions/constructions.ts b/ts/packages/cache/src/constructions/constructions.ts index eec6926558..f5ea1e159a 100644 --- a/ts/packages/cache/src/constructions/constructions.ts +++ b/ts/packages/cache/src/constructions/constructions.ts @@ -160,6 +160,7 @@ export class Construction { nonOptionalCount: this.parts.filter((p) => !p.optional).length, implicitParameterCount: this.implicitParameterCount, partialPartCount: matchedValues.partialPartCount, + partialMatchedCurrent: matchedValues.matchedCurrent, }, ]; } diff --git a/ts/packages/cache/test/completion.spec.ts b/ts/packages/cache/test/completion.spec.ts new file mode 100644 index 0000000000..faf48ae7ec --- /dev/null +++ b/ts/packages/cache/test/completion.spec.ts @@ -0,0 +1,553 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + Construction, + WildcardMode, +} from "../src/constructions/constructions.js"; +import { + ConstructionCache, + MatchOptions, +} from "../src/constructions/constructionCache.js"; +import { + createMatchPart, + MatchPart, + MatchSet, + TransformInfo, +} from "../src/constructions/matchPart.js"; + +function makeTransformInfo(name: string): TransformInfo { + return { + namespace: "test", + transformName: name, + partCount: 1, + }; +} + +function createEntityPart(name: string, transformName: string): MatchPart { + return new MatchPart(undefined, false, WildcardMode.Entity, [ + makeTransformInfo(transformName), + ]); +} + +function createWildcardEnabledPartWithMatches( + matches: string[], + name: string, + transformName: string, +): MatchPart { + const matchSet = new MatchSet(matches, name, true, undefined); + return new MatchPart(matchSet, false, WildcardMode.Enabled, [ + makeTransformInfo(transformName), + ]); +} + +function makeCache( + constructions: Construction[], + namespace: string[] = ["test"], +): ConstructionCache { + const cache = new ConstructionCache("test"); + for (const c of constructions) { + cache.addConstruction(namespace, c, true); + } + return cache; +} + +const defaultOptions: MatchOptions = { namespaceKeys: ["test"] }; + +describe("ConstructionCache.completion()", () => { + describe("empty prefix", () => { + it("returns first non-optional parts from all constructions", () => { + const c1 = Construction.create( + [createMatchPart(["play"], "verb")], + new Map(), + ); + const c2 = Construction.create( + [createMatchPart(["stop"], "verb")], + new Map(), + ); + const cache = makeCache([c1, c2]); + const result = cache.completion("", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual(["play", "stop"]); + expect(result!.matchedPrefixLength).toBe(0); + expect(result!.closedSet).toBe(true); + }); + it("returns empty string prefix as undefined", () => { + const cache = new ConstructionCache("test"); + const result = cache.completion("", defaultOptions); + expect(result).toBeUndefined(); + }); + + it("skips optional leading parts", () => { + const optionalPart = createMatchPart(["please"], "polite", { + optional: true, + }); + const verbPart = createMatchPart(["play"], "verb"); + const c = Construction.create([optionalPart, verbPart], new Map()); + const cache = makeCache([c]); + const result = cache.completion("", defaultOptions); + expect(result).toBeDefined(); + // Should return the first non-optional part's completions + expect(result!.completions).toEqual(["play"]); + }); + }); + + describe("matchedPrefixLength", () => { + it("returns matchedPrefixLength matching the consumed prefix", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toContain("song"); + // The matcher consumes "play" (4 chars); the trailing space + // is a separator and not part of any match part. + expect(result!.matchedPrefixLength).toBe(4); + }); + + it("returns matchedPrefixLength for partial single-part match", () => { + const c = Construction.create( + [createMatchPart(["play"], "verb")], + new Map(), + ); + const cache = makeCache([c]); + // "pl" partially matches "play" + const result = cache.completion("pl", defaultOptions); + expect(result).toBeDefined(); + if (result!.completions.length > 0) { + expect(result!.matchedPrefixLength).toBeGreaterThanOrEqual(0); + } + }); + + it("discards shorter-prefix completions when longer exists", () => { + // Construction 1: "play song" (two parts) + const c1 = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song", "track"], "noun"), + ], + new Map(), + ); + // Construction 2: "play" (one part only, so exact match) + const c2 = Construction.create( + [createMatchPart(["play"], "verb2")], + new Map(), + ); + const cache = makeCache([c1, c2]); + // "play " is an exact match for c2, but partial for c1 + // c2 is exact → matchedPrefixLength = "play ".length = 5 + // c1 partial on second part → matchedPrefixLength = 5 + // Both should have the same prefix length + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.matchedPrefixLength).toBe(5); + }); + }); + + describe("closedSet", () => { + it("is true when all completions are from literal match parts", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song", "album"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.closedSet).toBe(true); + }); + + it("is false when entity wildcard properties are involved", () => { + const verbPart = createMatchPart(["play"], "verb"); + const entityPart = createEntityPart("entity", "songName"); + const c = Construction.create([verbPart, entityPart], new Map()); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.closedSet).toBe(false); + }); + + it("is false when wildcard-enabled part with property names is next", () => { + const verbPart = createMatchPart(["play"], "verb"); + const wildcardPart = createWildcardEnabledPartWithMatches( + ["rock", "pop"], + "genre", + "genreName", + ); + const c = Construction.create([verbPart, wildcardPart], new Map()); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + // The wildcard-enabled part has both completions AND property names + // Property names → closedSet becomes false + expect(result!.closedSet).toBe(false); + // Should still include the literal completions from the matchSet + expect(result!.completions.sort()).toEqual(["pop", "rock"]); + }); + }); + + describe("separatorMode", () => { + it("returns spacePunctuation when prefix ends with word char and completion starts with word char", () => { + // "play" ends with 'y' (Latin), "song" starts with 's' (Latin) + // → both are word-boundary scripts → needs separator + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + // The matcher consumes "play" (4 chars). The character at + // position 3 is 'y' (Latin) and "song" starts with 's' (Latin). + // Both are word-boundary scripts → spacePunctuation. + expect(result!.separatorMode).toBe("spacePunctuation"); + }); + + it("returns spacePunctuation between adjacent word characters", () => { + // Use a two-word single match part to get adjacent word chars + // "play" followed by completions starting with 's' + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + // "play" is 4 chars, no trailing space. + // If partial matching consumes "play" (4 chars), the next part "song" starts + // with 's'. Last prefix char is 'y' — both Latin → spacePunctuation + const result = cache.completion("play", defaultOptions); + expect(result).toBeDefined(); + if ( + result!.completions.length > 0 && + result!.matchedPrefixLength === 4 + ) { + // 'y' and 's' are both Latin word-boundary — needs separator + expect(result!.separatorMode).toBe("spacePunctuation"); + } + }); + + it("returns optional between non-word chars", () => { + // Prefix ends with punctuation, completions start with letter + // "!" is not a word-boundary script + const c = Construction.create( + [ + createMatchPart(["hey!"], "exclaim"), + createMatchPart(["world"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("hey! ", defaultOptions); + if (result && result.completions.length > 0) { + // ' ' is not a word char → optional + expect(result!.separatorMode).toBe("optional"); + } + }); + + it("returns spacePunctuation between adjacent digits", () => { + // Both '3' and '4' are digits → needsSeparatorInAutoMode + const c = Construction.create( + [ + createMatchPart(["item3"], "first"), + createMatchPart(["4ever"], "second"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("item3", defaultOptions); + if ( + result && + result.completions.length > 0 && + result.matchedPrefixLength === 5 + ) { + // '3' and '4' are digits → needs separator + expect(result!.separatorMode).toBe("spacePunctuation"); + } + }); + }); + + describe("completions content", () => { + it("returns next part completions for partial match", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song", "album", "track"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual([ + "album", + "song", + "track", + ]); + }); + + it("returns empty completions for exact match with no remaining parts", () => { + const c = Construction.create( + [createMatchPart(["play"], "verb")], + new Map(), + ); + const cache = makeCache([c]); + // Exact match — nothing left to complete + const result = cache.completion("play", defaultOptions); + expect(result).toBeDefined(); + // Exact match advances maxPrefixLength to requestPrefix.length + expect(result!.matchedPrefixLength).toBe(4); + expect(result!.completions).toEqual([]); + }); + + it("returns completions from multiple constructions with same prefix length", () => { + const c1 = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song"], "noun"), + ], + new Map(), + ); + const c2 = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["album"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c1, c2]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual(["album", "song"]); + }); + }); + + describe("properties", () => { + it("returns property names for entity wildcard parts", () => { + const verbPart = createMatchPart(["play"], "verb"); + const entityPart = createEntityPart("entity", "songName"); + const c = Construction.create([verbPart, entityPart], new Map()); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.properties).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("songName"); + }); + }); + + describe("namespace filtering", () => { + it("filters completions by namespace keys", () => { + // Use distinct match-set names to prevent merging across + // namespaces (same name + canBeMerged → shared MatchSet). + const c1 = Construction.create( + [createMatchPart(["play"], "verb1", { canBeMerged: false })], + new Map(), + ); + const c2 = Construction.create( + [createMatchPart(["stop"], "verb2", { canBeMerged: false })], + new Map(), + ); + const cache = new ConstructionCache("test"); + cache.addConstruction(["ns1"], c1, true); + cache.addConstruction(["ns2"], c2, true); + + const r1 = cache.completion("", { namespaceKeys: ["ns1"] }); + expect(r1).toBeDefined(); + expect(r1!.completions).toEqual(["play"]); + + const r2 = cache.completion("", { namespaceKeys: ["ns2"] }); + expect(r2).toBeDefined(); + expect(r2!.completions).toEqual(["stop"]); + }); + + it("returns completions from all namespaces when no filter", () => { + const c1 = Construction.create( + [createMatchPart(["play"], "verb1", { canBeMerged: false })], + new Map(), + ); + const c2 = Construction.create( + [createMatchPart(["stop"], "verb2", { canBeMerged: false })], + new Map(), + ); + const cache = new ConstructionCache("test"); + cache.addConstruction(["ns1"], c1, true); + cache.addConstruction(["ns2"], c2, true); + + const result = cache.completion("", {}); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual(["play", "stop"]); + }); + + it("returns no completions for empty namespace keys", () => { + const c = Construction.create( + [createMatchPart(["play"], "verb")], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("", { + namespaceKeys: [], + }); + // Empty namespace keys → no constructions match → no completions. + expect(result).toBeUndefined(); + }); + }); + + describe("progressive prefix matching", () => { + // Tests for progressive prefix lengths against a "play" + "song" + // construction. The match engine supports intra-part partial + // matching — a prefix like "p" returns completions from the + // first unmatched part (matchedPrefixLength=0) and the caller + // (UI) filters by the remaining text, matching the grammar + // matcher's behaviour. + + let cache: ConstructionCache; + beforeEach(() => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["song"], "noun"), + ], + new Map(), + ); + cache = makeCache([c]); + }); + + it("prefix 'p' — partial prefix returns first part completions", () => { + const result = cache.completion("p", defaultOptions); + expect(result).toBeDefined(); + // "p" doesn't fully match "play" but the partial match + // succeeds and returns the first part's candidates. + // The caller filters by the remaining text ("p"). + expect(result!.completions).toContain("play"); + expect(result!.matchedPrefixLength).toBe(0); + expect(result!.closedSet).toBe(true); + }); + + it("prefix 'pl' — partial prefix returns first part completions", () => { + const result = cache.completion("pl", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toContain("play"); + expect(result!.matchedPrefixLength).toBe(0); + }); + + it("prefix 'play' — first part fully matched, offers second part", () => { + const result = cache.completion("play", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toEqual(["song"]); + expect(result!.matchedPrefixLength).toBe(4); + expect(result!.separatorMode).toBe("spacePunctuation"); + expect(result!.closedSet).toBe(true); + }); + + it("prefix 'play ' — trailing space ignored, still offers second part", () => { + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toEqual(["song"]); + // matchedPrefixLength stays at 4 (the space is a separator, + // not consumed by any part) + expect(result!.matchedPrefixLength).toBe(4); + expect(result!.separatorMode).toBe("spacePunctuation"); + }); + + it("prefix 'play s' — partial intra-part on second part, returns completions", () => { + const result = cache.completion("play s", defaultOptions); + expect(result).toBeDefined(); + // "play" is fully matched (4 chars), " s" remains as + // partial prefix for the second part. + expect(result!.completions).toContain("song"); + expect(result!.matchedPrefixLength).toBe(4); + }); + + it("prefix 'play song' — exact full match, empty completions", () => { + const result = cache.completion("play song", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toEqual([]); + expect(result!.matchedPrefixLength).toBe(9); + expect(result!.closedSet).toBe(true); + }); + }); + + describe("multiple alternatives in a single part", () => { + it("offers all alternatives when first part is fully matched", () => { + const c = Construction.create( + [ + createMatchPart(["play", "start"], "verb"), + createMatchPart(["song", "track", "album"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + + const r1 = cache.completion("play", defaultOptions); + expect(r1).toBeDefined(); + expect(r1!.completions.sort()).toEqual(["album", "song", "track"]); + + const r2 = cache.completion("start", defaultOptions); + expect(r2).toBeDefined(); + expect(r2!.completions.sort()).toEqual(["album", "song", "track"]); + }); + }); + + describe("case insensitivity", () => { + it("matches prefix case-insensitively", () => { + const c = Construction.create( + [ + createMatchPart(["Play"], "verb"), + createMatchPart(["song"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("PLAY", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toEqual(["song"]); + expect(result!.matchedPrefixLength).toBe(4); + }); + }); + + describe("multi-part constructions", () => { + it("completes third part after matching first two", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createMatchPart(["the"], "article"), + createMatchPart(["song", "album"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play the ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual(["album", "song"]); + }); + + it("returns merged match set completions after merge", () => { + // Two constructions with same structure merge their match sets + const c1 = Construction.create( + [createMatchPart(["play"], "verb")], + new Map(), + ); + const c2 = Construction.create( + [createMatchPart(["stop"], "verb")], + new Map(), + ); + const cache = makeCache([c1, c2]); + // After merge, the match set should contain both "play" and "stop" + const result = cache.completion("", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual(["play", "stop"]); + }); + }); +}); diff --git a/ts/packages/cache/test/mergeCompletionResults.spec.ts b/ts/packages/cache/test/mergeCompletionResults.spec.ts index c747340c4b..f541208868 100644 --- a/ts/packages/cache/test/mergeCompletionResults.spec.ts +++ b/ts/packages/cache/test/mergeCompletionResults.spec.ts @@ -31,7 +31,7 @@ describe("mergeCompletionResults", () => { expect(result).toBe(second); }); - it("takes max of matchedPrefixLength when both are defined", () => { + it("discards shorter-prefix completions when second is longer", () => { const first: CompletionResult = { completions: ["a"], matchedPrefixLength: 5, @@ -42,10 +42,10 @@ describe("mergeCompletionResults", () => { }; const result = mergeCompletionResults(first, second)!; expect(result.matchedPrefixLength).toBe(10); - expect(result.completions).toEqual(["a", "b"]); + expect(result.completions).toEqual(["b"]); }); - it("takes max of matchedPrefixLength (first is larger)", () => { + it("discards shorter-prefix completions when first is longer", () => { const first: CompletionResult = { completions: ["a"], matchedPrefixLength: 12, @@ -56,6 +56,21 @@ describe("mergeCompletionResults", () => { }; const result = mergeCompletionResults(first, second)!; expect(result.matchedPrefixLength).toBe(12); + expect(result.completions).toEqual(["a"]); + }); + + it("merges completions when both have equal matchedPrefixLength", () => { + const first: CompletionResult = { + completions: ["a"], + matchedPrefixLength: 5, + }; + const second: CompletionResult = { + completions: ["b"], + matchedPrefixLength: 5, + }; + const result = mergeCompletionResults(first, second)!; + expect(result.matchedPrefixLength).toBe(5); + expect(result.completions).toEqual(["a", "b"]); }); it("returns undefined matchedPrefixLength when both are missing", () => { @@ -70,7 +85,7 @@ describe("mergeCompletionResults", () => { expect(result.completions).toEqual(["a", "b"]); }); - it("uses the defined value when only first has matchedPrefixLength", () => { + it("discards second when only first has matchedPrefixLength", () => { const first: CompletionResult = { completions: ["a"], matchedPrefixLength: 7, @@ -79,11 +94,11 @@ describe("mergeCompletionResults", () => { completions: ["b"], }; const result = mergeCompletionResults(first, second)!; - // max(7, 0) = 7 expect(result.matchedPrefixLength).toBe(7); + expect(result.completions).toEqual(["a"]); }); - it("uses the defined value when only second has matchedPrefixLength", () => { + it("discards first when only second has matchedPrefixLength", () => { const first: CompletionResult = { completions: [], }; @@ -92,8 +107,8 @@ describe("mergeCompletionResults", () => { matchedPrefixLength: 4, }; const result = mergeCompletionResults(first, second)!; - // max(0, 4) = 4 expect(result.matchedPrefixLength).toBe(4); + expect(result.completions).toEqual(["b"]); }); }); diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index 6ae4792672..bcca397dba 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -550,17 +550,11 @@ export async function getCommandCompletion( // Collect completions and track separatorMode across all sources. const completions: CompletionGroup[] = []; + let commitMode: "explicit" | "eager" = "explicit"; let separatorMode: SeparatorMode | undefined; - if (input.trim() === "") { - completions.push({ - name: "Command Prefixes", - completions: ["@"], - }); - } + let closedSet = true; const descriptor = result.descriptor; - let closedSet = true; - let commitMode: "explicit" | "eager" = "explicit"; // When the last command token was exactly matched but the // user hasn't typed a trailing space, they haven't committed @@ -683,6 +677,18 @@ export async function getCommandCompletion( separatorMode = mergeSeparatorMode(separatorMode, "optional"); } + if (startIndex === 0) { + // It is the first token, add "@" for the command prefix + completions.push({ + name: "Command Prefixes", + completions: ["@"], + }); + + // The first token doesn't require separator before it (separatorMode to optional) + // and it doesn't require space after it (commitMode to eager) + separatorMode = "optional"; + commitMode = "eager"; + } const completionResult: CommandCompletionResult = { startIndex, completions, diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts index 505cc7f5ed..cfe2a3adb5 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/requestCommandHandler.ts @@ -478,7 +478,7 @@ export class RequestCommandHandler implements CommandHandler { const result: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "request") { - const requestPrefix = params.args.request; + const requestPrefix = params.args.request ?? ""; const requestResult = await requestCompletion( requestPrefix, context.agentContext, diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index d451bea4f6..19a09132b4 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -75,7 +75,7 @@ function getCompletionNamespaceKeys(context: CommandHandlerContext): string[] { } export async function requestCompletion( - requestPrefix: string | undefined, + requestPrefix: string, context: CommandHandlerContext, ): Promise { debugCompletion(`Request completion for prefix: '${requestPrefix}'`); From 48f50c48bfbd4f8d79fe51828ba1d5b8bf6669ea Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 20:39:57 -0700 Subject: [PATCH 47/51] Fix wildcard completion in construction matching - Fix end-with-wildcard path in finishMatchParts to use isRejectReference() instead of config.rejectReferences directly, consistent with the middle-wildcard path. This prevents Entity wildcards from incorrectly rejecting referential phrases. - Add partial-mode wildcard advancement in finishMatchParts so completion can look past wildcard parts for subsequent literal parts, matching grammar matcher behaviour. - Add comprehensive wildcard completion tests covering entity wildcards, wildcards in middle of constructions, wildcard-enabled parts with literal matches, and constructions starting with wildcards. --- .../src/constructions/constructionMatch.ts | 26 +- ts/packages/cache/test/completion.spec.ts | 228 ++++++++++++++++++ 2 files changed, 253 insertions(+), 1 deletion(-) diff --git a/ts/packages/cache/src/constructions/constructionMatch.ts b/ts/packages/cache/src/constructions/constructionMatch.ts index 09bbbcb2ff..191a24923e 100644 --- a/ts/packages/cache/src/constructions/constructionMatch.ts +++ b/ts/packages/cache/src/constructions/constructionMatch.ts @@ -168,6 +168,29 @@ function finishMatchParts( } if (config.partial) { + // If this is a wildcard-enabled part and there is + // non-separator text remaining, try to advance past + // the wildcard by looking for the next literal part. + // This mirrors the grammar matcher's behaviour where + // wildcards consume text and the following literal + // part is offered as a completion. + if ( + isWildcardEnabled(config, part.wildcardMode) && + state.matchedCurrent < request.length && + !isSpaceOrPunctuationRange( + request, + state.matchedCurrent, + request.length, + ) + ) { + state.matchedStart.push(state.matchedCurrent); + state.pendingWildcard = findPendingWildcard( + request, + state.matchedCurrent, + ); + continue; + } + // For partial, act as if we have matched all the parts, and breaking out of the loop to finish the match. break; } @@ -227,11 +250,12 @@ function finishMatchParts( const wildcardMatch = wildcardRegex.exec(wildcardRange); if (wildcardMatch !== null) { // Update the state in case we need to backtrack because value translation failed. + const wildcardPart = parts[state.matchedStart.length - 1]; if ( !captureWildcardMatch( state, wildcardMatch[1], - config.rejectReferences, + isRejectReference(config, wildcardPart.wildcardMode), ) ) { return false; diff --git a/ts/packages/cache/test/completion.spec.ts b/ts/packages/cache/test/completion.spec.ts index faf48ae7ec..da7866045d 100644 --- a/ts/packages/cache/test/completion.spec.ts +++ b/ts/packages/cache/test/completion.spec.ts @@ -550,4 +550,232 @@ describe("ConstructionCache.completion()", () => { expect(result!.completions.sort()).toEqual(["play", "stop"]); }); }); + + describe("wildcard completions", () => { + describe("entity wildcard after literal", () => { + it("returns property completion for entity wildcard", () => { + const verbPart = createMatchPart(["play"], "verb"); + const entityPart = createEntityPart("entity", "songName"); + const c = Construction.create( + [verbPart, entityPart], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play", defaultOptions); + expect(result).toBeDefined(); + expect(result!.properties).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("songName"); + expect(result!.closedSet).toBe(false); + expect(result!.matchedPrefixLength).toBe(4); + }); + + it("returns property completion with trailing space", () => { + const verbPart = createMatchPart(["play"], "verb"); + const entityPart = createEntityPart("entity", "songName"); + const c = Construction.create( + [verbPart, entityPart], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("songName"); + }); + + it("consumes trailing wildcard text as exact match", () => { + const verbPart = createMatchPart(["play"], "verb"); + const entityPart = createEntityPart("entity", "songName"); + const c = Construction.create( + [verbPart, entityPart], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("play my song", defaultOptions); + expect(result).toBeDefined(); + // Wildcard consumes "my song" → exact match, no completions. + expect(result!.completions).toEqual([]); + expect(result!.matchedPrefixLength).toBe(12); + }); + }); + + describe("wildcard in middle of construction", () => { + // Mirrors the grammar test: + // play $(trackName:wildcard) by $(artist:wildcard) + let cache: ConstructionCache; + beforeEach(() => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createEntityPart("track", "trackName"), + createMatchPart(["by"], "prep"), + createEntityPart("artist", "artist"), + ], + new Map(), + ); + cache = makeCache([c]); + }); + + it("after prefix, returns property completion for first wildcard", () => { + const result = cache.completion("play", defaultOptions); + expect(result).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("trackName"); + expect(result!.closedSet).toBe(false); + expect(result!.matchedPrefixLength).toBe(4); + }); + + it("after prefix with space, returns property for wildcard", () => { + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("trackName"); + }); + + it("after wildcard text, returns next literal as completion", () => { + // Grammar behavior: after "play some song", the + // wildcard consumed "some song" and "by" is the next + // completion. + const result = cache.completion( + "play some song", + defaultOptions, + ); + expect(result).toBeDefined(); + expect(result!.completions).toContain("by"); + expect(result!.matchedPrefixLength).toBe(14); + }); + + it("after wildcard text and literal, returns property for second wildcard", () => { + const result = cache.completion( + "play some song by", + defaultOptions, + ); + expect(result).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("artist"); + expect(result!.closedSet).toBe(false); + }); + + it("complete input is an exact match", () => { + const result = cache.completion( + "play some song by john", + defaultOptions, + ); + expect(result).toBeDefined(); + expect(result!.completions).toEqual([]); + expect(result!.matchedPrefixLength).toBe(22); + }); + + it("multi-word wildcard text is consumed", () => { + const result = cache.completion( + "play a really long track name by", + defaultOptions, + ); + expect(result).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("artist"); + }); + }); + + describe("wildcard-enabled with matches in middle", () => { + it("advances past wildcard-enabled part when literal matches", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createWildcardEnabledPartWithMatches( + ["rock", "pop"], + "genre", + "genreName", + ), + createMatchPart(["music"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + // "rock" matches the wildcard-enabled part literally, + // so the matcher advances past it to offer "music". + const result = cache.completion("play rock ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toContain("music"); + }); + + it("offers wildcard-enabled part completions when literal doesn't match", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createWildcardEnabledPartWithMatches( + ["rock", "pop"], + "genre", + "genreName", + ), + createMatchPart(["music"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + // "play " — second part (wildcard-enabled) offers its + // literal matches and property names. + const result = cache.completion("play ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions.sort()).toEqual(["pop", "rock"]); + expect(result!.closedSet).toBe(false); + }); + + it("advances past wildcard-enabled part with non-matching text to offer next literal", () => { + const c = Construction.create( + [ + createMatchPart(["play"], "verb"), + createWildcardEnabledPartWithMatches( + ["rock", "pop"], + "genre", + "genreName", + ), + createMatchPart(["music"], "noun"), + ], + new Map(), + ); + const cache = makeCache([c]); + // "jazz" doesn't match "rock"/"pop" literally, but + // the wildcard-enabled part can consume it as wildcard + // text. The next literal "music" is offered. + const result = cache.completion("play jazz ", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toContain("music"); + }); + }); + + describe("construction starting with wildcard", () => { + it("returns property completion for leading wildcard on empty prefix", () => { + const c = Construction.create( + [ + createEntityPart("track", "trackName"), + createMatchPart(["by"], "prep"), + createEntityPart("artist", "artist"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("", defaultOptions); + expect(result).toBeDefined(); + expect(result!.properties!.length).toBeGreaterThan(0); + expect(result!.properties![0].names).toContain("trackName"); + }); + + it("after wildcard text, returns next literal as completion", () => { + const c = Construction.create( + [ + createEntityPart("track", "trackName"), + createMatchPart(["by"], "prep"), + createEntityPart("artist", "artist"), + ], + new Map(), + ); + const cache = makeCache([c]); + const result = cache.completion("some song", defaultOptions); + expect(result).toBeDefined(); + expect(result!.completions).toContain("by"); + }); + }); + }); }); From 21591fe47a5666b214da6a95e9a7382b7d2f16e9 Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 21:04:58 -0700 Subject: [PATCH 48/51] Fix prettier formatting in remainder.spec.ts --- .../dispatcher/test/remainder.spec.ts | 53 +++++++------------ 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts b/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts index 170ee50114..e7cad0dfdc 100644 --- a/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts +++ b/ts/packages/dispatcher/dispatcher/test/remainder.spec.ts @@ -82,21 +82,15 @@ describe("remainderLength", () => { }); it("two arguments consumed", () => { - expect( - parseParams("hello world", twoArgs).remainderLength, - ).toBe(0); + expect(parseParams("hello world", twoArgs).remainderLength).toBe(0); }); it("multiple arguments consumed", () => { - expect(parseParams("a b c", multipleArg).remainderLength).toBe( - 0, - ); + expect(parseParams("a b c", multipleArg).remainderLength).toBe(0); }); it("flag with string value consumed", () => { - expect( - parseParams("--str value", strFlag).remainderLength, - ).toBe(0); + expect(parseParams("--str value", strFlag).remainderLength).toBe(0); }); it("boolean flag consumed", () => { @@ -104,9 +98,9 @@ describe("remainderLength", () => { }); it("boolean flag with explicit true consumed", () => { - expect( - parseParams("--bool true", boolFlag).remainderLength, - ).toBe(0); + expect(parseParams("--bool true", boolFlag).remainderLength).toBe( + 0, + ); }); it("flags and args consumed", () => { @@ -122,9 +116,7 @@ describe("remainderLength", () => { }); it("whitespace-padded input trimmed and consumed", () => { - expect( - parseParams(" hello ", singleArg).remainderLength, - ).toBe(0); + expect(parseParams(" hello ", singleArg).remainderLength).toBe(0); }); it("implicit quote argument consumes rest of line", () => { @@ -154,15 +146,13 @@ describe("remainderLength", () => { describe("partial - fully consumed", () => { it("empty input", () => { - expect(parseParams("", optionalArg, true).remainderLength).toBe( - 0, - ); + expect(parseParams("", optionalArg, true).remainderLength).toBe(0); }); it("single argument", () => { - expect( - parseParams("hello", singleArg, true).remainderLength, - ).toBe(0); + expect(parseParams("hello", singleArg, true).remainderLength).toBe( + 0, + ); }); it("flag with value", () => { @@ -172,15 +162,15 @@ describe("remainderLength", () => { }); it("boolean flag without value", () => { - expect( - parseParams("--bool", boolFlag, true).remainderLength, - ).toBe(0); + expect(parseParams("--bool", boolFlag, true).remainderLength).toBe( + 0, + ); }); it("trailing whitespace after completed arg", () => { - expect( - parseParams("hello ", singleArg, true).remainderLength, - ).toBe(0); + expect(parseParams("hello ", singleArg, true).remainderLength).toBe( + 0, + ); }); it("multiple arguments", () => { @@ -191,8 +181,7 @@ describe("remainderLength", () => { it("flags and args", () => { expect( - parseParams("--bool hello", flagsAndArgs, true) - .remainderLength, + parseParams("--bool hello", flagsAndArgs, true).remainderLength, ).toBe(0); }); }); @@ -247,11 +236,7 @@ describe("remainderLength", () => { }); it("valid flag+value then invalid flag", () => { - const result = parseParams( - "--str hello --unknown", - strFlag, - true, - ); + const result = parseParams("--str hello --unknown", strFlag, true); expect(result.remainderLength).toBe("--unknown".length); }); From db24cb438410d3ab88554b9f8296efa4c4abf19a Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 21:28:17 -0700 Subject: [PATCH 49/51] Fix naming, terminology, and comment/implementation inconsistencies - Update ParameterCompletionResult.closedSet comment: agents CAN now signal closedSet via CompletionGroups (no longer "cannot yet signal") - Add ?? "" fallback in translateCommandHandler and matchCommandHandler for requestPrefix passed to requestCompletion(string) (matches requestCommandHandler) - Remove dead requestPrefix ?? "" in grammarStore (param is now string) - Clarify "Array of CompletionGroup items" in getCommandCompletion contract to avoid collision with CompletionGroups wrapper type name - Add "space" case to grammarMatcher's local mergeSeparatorMode so the priority chain handles all four SeparatorMode values - Document that grammarMatcher only produces spacePunctuation/optional/ none, never "space" --- ts/packages/actionGrammar/src/grammarMatcher.ts | 16 +++++++++++----- ts/packages/cache/src/cache/grammarStore.ts | 2 +- .../dispatcher/src/command/completion.ts | 9 +++++---- .../dispatcher/handlers/matchCommandHandler.ts | 2 +- .../handlers/translateCommandHandler.ts | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index 42b8d5212b..c8ada3ba79 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -14,9 +14,11 @@ import { VarStringPart, } from "./grammarTypes.js"; -// Separator mode for completion results. Identical to SeparatorMode -// from @typeagent/agent-sdk; independently defined here so -// actionGrammar does not depend on agentSdk. +// Separator mode for completion results. Structurally identical to +// SeparatorMode from @typeagent/agent-sdk; independently defined here so +// actionGrammar does not depend on agentSdk. The grammar matcher only +// produces "spacePunctuation", "optional", and "none" — never "space" +// (which is strictly command/flag-level). type SeparatorMode = "space" | "spacePunctuation" | "optional" | "none"; const debugMatchRaw = registerDebug("typeagent:grammar:match"); @@ -108,7 +110,7 @@ function candidateSeparatorMode( // Merge a new candidate's separator mode into the running aggregate. // The mode requiring the strongest separator wins (i.e. the mode that -// demands the most from the user): spacePunctuation > optional > none. +// demands the most from the user): space > spacePunctuation > optional > none. function mergeSeparatorMode( current: SeparatorMode | undefined, needsSep: boolean, @@ -118,7 +120,11 @@ function mergeSeparatorMode( if (current === undefined) { return candidateMode; } - // "spacePunctuation" requires a separator — strongest requirement. + // "space" requires strict whitespace — strongest requirement. + if (current === "space" || candidateMode === "space") { + return "space"; + } + // "spacePunctuation" requires a separator — next strongest. if ( current === "spacePunctuation" || candidateMode === "spacePunctuation" diff --git a/ts/packages/cache/src/cache/grammarStore.ts b/ts/packages/cache/src/cache/grammarStore.ts index d6eb084cae..758da8cf23 100644 --- a/ts/packages/cache/src/cache/grammarStore.ts +++ b/ts/packages/cache/src/cache/grammarStore.ts @@ -351,7 +351,7 @@ export class GrammarStoreImpl implements GrammarStore { // simple grammar-based completions const partial = matchGrammarCompletion( entry.grammar, - requestPrefix ?? "", + requestPrefix, matchedPrefixLength, ); const partialPrefixLength = partial.matchedPrefixLength ?? 0; diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index bcca397dba..e7336a65ec 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -135,9 +135,10 @@ function collectFlags( // Internal result from parameter-level completion. // // `closedSet` uses a conservative heuristic: -// - false when the agent's getCommandCompletion was invoked (agents -// cannot yet signal whether their set is closed). -// - false when a pending non-boolean flag accepts free-form input. +// - When the agent's getCommandCompletion was invoked, closedSet +// comes from the agent's response (defaults to false when the +// agent omits it). +// - false when editing a free-form parameter value with no agent. // - true when all positional args are filled and only enumerable // flag names remain (a finite, known set). type ParameterCompletionResult = { @@ -501,7 +502,7 @@ async function getCommandParameterCompletion( // reports how many characters of the token content it // consumed, which is relative to the token start. // -// completions Array of CompletionGroups from up to three sources: +// completions Array of CompletionGroup items from up to three sources: // (a) built-in command / subcommand / agent-name lists, // (b) flag names from the descriptor's ParameterDefinitions, // (c) agent-provided groups via the agent's diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts index 2f2b32a820..238458429b 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/matchCommandHandler.ts @@ -55,7 +55,7 @@ export class MatchCommandHandler implements CommandHandler { const result: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "request") { - const requestPrefix = params.args.request; + const requestPrefix = params.args.request ?? ""; const requestResult = await requestCompletion( requestPrefix, context.agentContext, diff --git a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts index 10d38c2ea1..807b716122 100644 --- a/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts +++ b/ts/packages/dispatcher/dispatcher/src/context/dispatcher/handlers/translateCommandHandler.ts @@ -80,7 +80,7 @@ export class TranslateCommandHandler implements CommandHandler { const result: CompletionGroups = { groups: [] }; for (const name of names) { if (name === "request") { - const requestPrefix = params.args.request; + const requestPrefix = params.args.request ?? ""; const requestResult = await requestCompletion( requestPrefix, context.agentContext, From 3bc592ad412712dfd72ea7cf3a56abd42c01c48e Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 21:40:03 -0700 Subject: [PATCH 50/51] Add jest config --- ts/packages/shell/jest.config.cjs | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ts/packages/shell/jest.config.cjs diff --git a/ts/packages/shell/jest.config.cjs b/ts/packages/shell/jest.config.cjs new file mode 100644 index 0000000000..25456e93bb --- /dev/null +++ b/ts/packages/shell/jest.config.cjs @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +module.exports = require("../../jest.config.js"); From eee1e614b08856319dc89ed338925d98498b137f Mon Sep 17 00:00:00 2001 From: Curtis Man Date: Thu, 12 Mar 2026 23:08:04 -0700 Subject: [PATCH 51/51] Simplify completion e2e: extract helpers, deduplicate, clarify types - Extract isEditingFreeFormValue() predicate for dense 3-way condition - Export needsSeparatorInAutoMode from actionGrammar, remove duplicate in cache - Extract completeDescriptor() from getCommandCompletion branch logic - Define ParameterCompletionResult as Omit - Extract toMenuItems() in partialCompletionSession - Extract computeClosedSet() with per-branch comments - Add commitMode: 'eager' rationale comments in requestCompletion --- .../actionGrammar/src/grammarMatcher.ts | 2 +- ts/packages/actionGrammar/src/index.ts | 1 + .../src/constructions/constructionCache.ts | 23 +- .../dispatcher/src/command/completion.ts | 203 +++++++++++------- .../src/translation/requestCompletion.ts | 5 + .../renderer/src/partialCompletionSession.ts | 54 +++-- 6 files changed, 169 insertions(+), 119 deletions(-) diff --git a/ts/packages/actionGrammar/src/grammarMatcher.ts b/ts/packages/actionGrammar/src/grammarMatcher.ts index c8ada3ba79..6d0fae2073 100644 --- a/ts/packages/actionGrammar/src/grammarMatcher.ts +++ b/ts/packages/actionGrammar/src/grammarMatcher.ts @@ -62,7 +62,7 @@ function isWordBoundaryScript(c: string): boolean { } return wordBoundaryScriptRe.test(c); } -function needsSeparatorInAutoMode(a: string, b: string): boolean { +export function needsSeparatorInAutoMode(a: string, b: string): boolean { if (digitRe.test(a) && digitRe.test(b)) { return true; } diff --git a/ts/packages/actionGrammar/src/index.ts b/ts/packages/actionGrammar/src/index.ts index 6ca548c447..0ab577db18 100644 --- a/ts/packages/actionGrammar/src/index.ts +++ b/ts/packages/actionGrammar/src/index.ts @@ -26,6 +26,7 @@ export { GrammarMatchResult, matchGrammarCompletion, GrammarCompletionResult, + needsSeparatorInAutoMode, } from "./grammarMatcher.js"; // Entity system diff --git a/ts/packages/cache/src/constructions/constructionCache.ts b/ts/packages/cache/src/constructions/constructionCache.ts index 6218587c1a..0d35d3d67d 100644 --- a/ts/packages/cache/src/constructions/constructionCache.ts +++ b/ts/packages/cache/src/constructions/constructionCache.ts @@ -3,6 +3,7 @@ import { SeparatorMode } from "@typeagent/agent-sdk"; import { mergeSeparatorMode } from "@typeagent/agent-sdk/helpers/command"; +import { needsSeparatorInAutoMode } from "action-grammar"; import { ExecutableAction, HistoryContext, @@ -30,28 +31,6 @@ const debugConst = registerDebug("typeagent:const"); const debugConstMatchStat = registerDebug("typeagent:const:match:stat"); const debugCompletion = registerDebug("typeagent:const:completion"); -// Separator heuristic for "auto" spacing mode. A separator is required -// between two adjacent characters only when both belong to a word-boundary -// script (e.g. Latin + Latin) or both are digits. This mirrors the -// grammar matcher's needsSeparatorInAutoMode without adding a cross-package -// dependency. -const wordBoundaryScriptRe = - /\p{Script=Latin}|\p{Script=Cyrillic}|\p{Script=Greek}|\p{Script=Armenian}|\p{Script=Georgian}|\p{Script=Hangul}|\p{Script=Arabic}|\p{Script=Hebrew}|\p{Script=Devanagari}|\p{Script=Bengali}|\p{Script=Tamil}|\p{Script=Telugu}|\p{Script=Kannada}|\p{Script=Malayalam}|\p{Script=Gujarati}|\p{Script=Gurmukhi}|\p{Script=Oriya}|\p{Script=Sinhala}|\p{Script=Ethiopic}|\p{Script=Mongolian}/u; -const digitRe = /[0-9]/; -function isWordBoundaryScript(c: string): boolean { - const code = c.charCodeAt(0); - if (code < 128) { - return (code >= 65 && code <= 90) || (code >= 97 && code <= 122); - } - return wordBoundaryScriptRe.test(c); -} -function needsSeparatorInAutoMode(a: string, b: string): boolean { - if (digitRe.test(a) && digitRe.test(b)) { - return true; - } - return isWordBoundaryScript(a) && isWordBoundaryScript(b); -} - // Agent Cache define the namespace policy. At the cache, it just combine the keys into a string for lookup. function getConstructionNamespace(namespaceKeys: string[]) { // Combine the namespace keys into a string using | as the separator. Use to filter easily when diff --git a/ts/packages/dispatcher/dispatcher/src/command/completion.ts b/ts/packages/dispatcher/dispatcher/src/command/completion.ts index e7336a65ec..71652aa9e7 100644 --- a/ts/packages/dispatcher/dispatcher/src/command/completion.ts +++ b/ts/packages/dispatcher/dispatcher/src/command/completion.ts @@ -105,6 +105,36 @@ function isFullyQuoted(value: string) { ); } +// True when the user is mid-edit on a free-form parameter value: +// - partially quoted (opening quote, no closing) +// - implicitQuotes parameter (rest-of-line) +// - bare unquoted token with no trailing space and no pending flag +function isEditingFreeFormValue( + quoted: boolean | undefined, + implicitQuotes: boolean, + hasTrailingSpace: boolean, + pendingFlag: string | undefined, +): boolean { + if (quoted === false) return true; // partially quoted + if (quoted !== undefined) return false; // fully quoted → committed + return implicitQuotes || (!hasTrailingSpace && pendingFlag === undefined); +} + +// Determine closedSet for parameter completion: +// - Agent is authoritative when invoked. +// - Free-form text with no agent → open set (anything is valid). +// - No agent and all positional args filled → only flags remain (finite set). +function computeClosedSet( + agentInvoked: boolean, + agentClosedSet: boolean | undefined, + isPartialValue: boolean, + hasRemainingArgs: boolean, +): boolean { + if (agentInvoked) return agentClosedSet ?? false; + if (isPartialValue) return false; + return !hasRemainingArgs; +} + function collectFlags( agentCommandCompletions: string[], flags: FlagDefinitions, @@ -133,19 +163,9 @@ function collectFlags( } // Internal result from parameter-level completion. -// -// `closedSet` uses a conservative heuristic: -// - When the agent's getCommandCompletion was invoked, closedSet -// comes from the agent's response (defaults to false when the -// agent omits it). -// - false when editing a free-form parameter value with no agent. -// - true when all positional args are filled and only enumerable -// flag names remain (a finite, known set). -type ParameterCompletionResult = { - completions: CompletionGroup[]; - startIndex: number; - separatorMode: SeparatorMode | undefined; - closedSet: boolean; +// Mirrors CommandCompletionResult but allows commitMode to be undefined +// (the caller decides the default). +type ParameterCompletionResult = Omit & { commitMode: CommitMode | undefined; }; @@ -221,11 +241,12 @@ function resolveCompletionTarget( const valueToken = tokens[tokens.length - 1]; const quoted = isFullyQuoted(valueToken); if ( - quoted === false || - (quoted === undefined && lastParamImplicitQuotes) || - (quoted === undefined && - !hasTrailingSpace && - pendingFlag === undefined) + isEditingFreeFormValue( + quoted, + lastParamImplicitQuotes, + hasTrailingSpace, + pendingFlag, + ) ) { const tokenStartIndex = remainderIndex - valueToken.length; const startIndex = tokenBoundary(input, tokenStartIndex); @@ -432,28 +453,92 @@ async function getCommandParameterCompletion( ); } - // ── 4. Determine closedSet ─────────────────────────────────────── - let closedSet: boolean; - if (agentInvoked) { - closedSet = agentClosedSet ?? false; - } else if (target.isPartialValue) { - // Editing a free-form value with no agent → open set. - closedSet = false; - } else { - // No agent. Closed when all positional args are filled and - // only flags (a finite set) remain. - closedSet = params.nextArgs.length === 0; - } - return { completions, startIndex, separatorMode, - closedSet, + closedSet: computeClosedSet( + agentInvoked, + agentClosedSet, + target.isPartialValue, + params.nextArgs.length > 0, + ), commitMode: agentCommitMode, }; } +// Complete a resolved command descriptor: parameter completions plus +// optional sibling subcommand names from the parent table. +async function completeDescriptor( + descriptor: CommandDescriptor, + context: CommandHandlerContext, + result: ResolveCommandResult, + input: string, + hasTrailingSpace: boolean, + commandConsumedLength: number, +): Promise<{ + completions: CompletionGroup[]; + startIndex: number | undefined; + separatorMode: SeparatorMode | undefined; + closedSet: boolean; + commitMode: CommitMode | undefined; +}> { + const completions: CompletionGroup[] = []; + let separatorMode: SeparatorMode | undefined; + + const parameterCompletions = await getCommandParameterCompletion( + descriptor, + context, + result, + input, + hasTrailingSpace, + ); + + // Include sibling subcommand names when resolved to the default + // (not an explicit match), but only if parameter parsing hasn't + // consumed past the command boundary. Once the user has typed + // tokens that fill parameters (moving startIndex forward), + // they've committed to the default — subcommand names would be + // filtered against the wrong text at the wrong position. + const table = result.table; + const addSubcommands = + table !== undefined && + !result.matched && + getDefaultSubCommandDescriptor(table) === descriptor && + (parameterCompletions === undefined || + parameterCompletions.startIndex <= commandConsumedLength); + + if (addSubcommands) { + completions.push({ + name: "Subcommands", + completions: Object.keys(table!.commands), + }); + separatorMode = mergeSeparatorMode(separatorMode, "space"); + } + + if (parameterCompletions === undefined) { + return { + completions, + startIndex: undefined, + separatorMode, + closedSet: true, + commitMode: undefined, + }; + } + + completions.push(...parameterCompletions.completions); + return { + completions, + startIndex: parameterCompletions.startIndex, + separatorMode: mergeSeparatorMode( + separatorMode, + parameterCompletions.separatorMode, + ), + closedSet: parameterCompletions.closedSet, + commitMode: parameterCompletions.commitMode, + }; +} + // // ── getCommandCompletion contract ──────────────────────────────────────────── // @@ -579,55 +664,25 @@ export async function getCommandCompletion( separatorMode = mergeSeparatorMode(separatorMode, "none"); // closedSet stays true: subcommand names are exhaustive. } else if (descriptor !== undefined) { - // Get parameter completions first — we need to know - // whether parameters consumed past the command boundary - // before deciding if subcommand alternatives apply. - const parameterCompletions = await getCommandParameterCompletion( + const desc = await completeDescriptor( descriptor, context, result, input, hasTrailingSpace, + commandConsumedLength, ); - - // Include sibling subcommand names when resolved to the - // default (not an explicit match), but only if parameter - // parsing hasn't consumed past the command boundary. - // Once the user has typed tokens that fill parameters - // (moving startIndex forward), they've committed to the - // default — subcommand names would be filtered against - // the wrong text at the wrong position. - const addSubcommands = - table !== undefined && - !result.matched && - getDefaultSubCommandDescriptor(table) === descriptor && - (parameterCompletions === undefined || - parameterCompletions.startIndex <= commandConsumedLength); - - if (addSubcommands) { - completions.push({ - name: "Subcommands", - completions: Object.keys(table.commands), - }); - separatorMode = mergeSeparatorMode(separatorMode, "space"); + completions.push(...desc.completions); + if (desc.startIndex !== undefined) { + startIndex = desc.startIndex; } - - if (parameterCompletions === undefined) { - // Descriptor has no parameters. If subcommand - // alternatives were added above, they are the - // exhaustive set; otherwise the command is fully - // specified with nothing more to type. - } else { - completions.push(...parameterCompletions.completions); - startIndex = parameterCompletions.startIndex; - separatorMode = mergeSeparatorMode( - separatorMode, - parameterCompletions.separatorMode, - ); - closedSet = parameterCompletions.closedSet; - if (parameterCompletions.commitMode === "eager") { - commitMode = "eager"; - } + separatorMode = mergeSeparatorMode( + separatorMode, + desc.separatorMode, + ); + closedSet = desc.closedSet; + if (desc.commitMode === "eager") { + commitMode = "eager"; } } else if (table !== undefined) { // descriptor is undefined: the suffix didn't resolve to any diff --git a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts index 19a09132b4..9022434970 100644 --- a/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts +++ b/ts/packages/dispatcher/dispatcher/src/translation/requestCompletion.ts @@ -114,6 +114,10 @@ export async function requestCompletion( matchedPrefixLength, separatorMode, closedSet, + // Grammar completions use eager commit: tokens can abut + // without an explicit delimiter (e.g. CJK characters), + // so the session should re-fetch immediately when a + // completion is uniquely satisfied. commitMode: "eager", }; } @@ -140,6 +144,7 @@ export async function requestCompletion( matchedPrefixLength, separatorMode, closedSet, + // Grammar completions use eager commit (see note above). commitMode: "eager", }; } diff --git a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts index 10df45ef1d..5b252aed0b 100644 --- a/ts/packages/shell/src/renderer/src/partialCompletionSession.ts +++ b/ts/packages/shell/src/renderer/src/partialCompletionSession.ts @@ -2,7 +2,11 @@ // Licensed under the MIT License. import { CommandCompletionResult } from "agent-dispatcher"; -import { CommitMode, SeparatorMode } from "@typeagent/agent-sdk"; +import { + CommitMode, + CompletionGroup, + SeparatorMode, +} from "@typeagent/agent-sdk"; import { SearchMenuItem, SearchMenuPosition, @@ -347,27 +351,7 @@ export class PartialCompletionSession { this.closedSet = result.closedSet; this.commitMode = result.commitMode ?? "explicit"; - // Build completions preserving backend group order. - const completions: SearchMenuItem[] = []; - let currentIndex = 0; - for (const group of result.completions) { - const items = group.sorted - ? group.completions - : [...group.completions].sort(); - for (const choice of items) { - completions.push({ - matchText: choice, - selectedText: choice, - sortIndex: currentIndex++, - ...(group.needQuotes !== undefined - ? { needQuotes: group.needQuotes } - : {}), - ...(group.emojiChar !== undefined - ? { emojiChar: group.emojiChar } - : {}), - }); - } - } + const completions = toMenuItems(result.completions); if (completions.length === 0) { debug( @@ -428,3 +412,29 @@ function stripLeadingSeparator(rawPrefix: string, mode: SeparatorMode): string { ? rawPrefix.trimStart() : rawPrefix.replace(/^[\s\p{P}]+/u, ""); } + +// Convert backend CompletionGroups into flat SearchMenuItems, +// preserving group order and sorting within each group. +function toMenuItems(groups: CompletionGroup[]): SearchMenuItem[] { + const items: SearchMenuItem[] = []; + let sortIndex = 0; + for (const group of groups) { + const sorted = group.sorted + ? group.completions + : [...group.completions].sort(); + for (const choice of sorted) { + items.push({ + matchText: choice, + selectedText: choice, + sortIndex: sortIndex++, + ...(group.needQuotes !== undefined + ? { needQuotes: group.needQuotes } + : {}), + ...(group.emojiChar !== undefined + ? { emojiChar: group.emojiChar } + : {}), + }); + } + } + return items; +}