@selesai/code 0.13.19 → 0.13.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to `@selesai/code` will be documented in this file.
4
4
 
5
+ ## [0.13.20] - 2026-09-10
6
+
7
+ ### Added
8
+ - **DeepSeek V4.1 Flash in default catalog.** Added `deepseek-v4.1-flash` (text+image, 512K context, 128K max tokens, deepseek thinking format).
9
+ - **Vision for DeepSeek V4 Pro.** `deepseek-v4-pro` now accepts text+image input with 128K max tokens.
10
+ - **Typo-tolerant capability routing.** The capability gateway now scores one-character-off name/alias matches as exact, so a misspelled tool name (e.g. `inercom` → `intercom`) auto-activates when uniquely identifiable.
11
+
5
12
  ## [0.13.19] - 2026-09-09
6
13
 
7
14
  ### Changed
@@ -105,16 +105,35 @@
105
105
  "requiresReasoningContentOnAssistantMessages": true
106
106
  }
107
107
  },
108
+ {
109
+ "id": "deepseek-v4.1-flash",
110
+ "name": "DeepSeek V4.1 Flash",
111
+ "reasoning": true,
112
+ "input": ["text", "image"],
113
+ "contextWindow": 512000,
114
+ "maxTokens": 128000,
115
+ "thinkingLevelMap": {
116
+ "minimal": null,
117
+ "low": null,
118
+ "medium": null,
119
+ "high": "high",
120
+ "xhigh": null,
121
+ "max": "max"
122
+ },
123
+ "compat": {
124
+ "requiresReasoningContentOnAssistantMessages": true
125
+ }
126
+ },
108
127
  {
109
128
  "id": "deepseek-v4-pro",
110
129
  "name": "DeepSeek V4 pro",
111
130
  "reasoning": true,
112
- "input": ["text"],
131
+ "input": ["text", "image"],
113
132
  "contextWindow": 512000,
114
- "maxTokens": 64000,
133
+ "maxTokens": 128000,
115
134
  "thinkingLevelMap": {
116
135
  "minimal": null,
117
- "low": "low",
136
+ "low": null,
118
137
  "medium": null,
119
138
  "high": "high",
120
139
  "xhigh": null,
@@ -86,6 +86,29 @@ export function tokenize(text: string): string[] {
86
86
  return normalizeQuery(text).split(/\s+/).filter(Boolean);
87
87
  }
88
88
 
89
+ /** True when two words differ by at most one insertion, deletion, or substitution. */
90
+ function differsByOne(left: string, right: string): boolean {
91
+ if (Math.abs(left.length - right.length) > 1) return false;
92
+ let leftIndex = 0;
93
+ let rightIndex = 0;
94
+ let differences = 0;
95
+ while (leftIndex < left.length && rightIndex < right.length) {
96
+ if (left[leftIndex] === right[rightIndex]) {
97
+ leftIndex++;
98
+ rightIndex++;
99
+ continue;
100
+ }
101
+ if (++differences > 1) return false;
102
+ if (left.length > right.length) leftIndex++;
103
+ else if (right.length > left.length) rightIndex++;
104
+ else {
105
+ leftIndex++;
106
+ rightIndex++;
107
+ }
108
+ }
109
+ return true;
110
+ }
111
+
89
112
  /** Words that carry no routing signal. */
90
113
  const STOPWORDS = new Set([
91
114
  "the", "a", "an", "to", "of", "for", "with", "and", "or", "use", "using", "me", "my", "i",
@@ -103,8 +126,8 @@ export interface RouteResult {
103
126
  /**
104
127
  * Deterministic routing over the compact catalog.
105
128
  *
106
- * - Exact name/alias match scores 3; prefix match scores 2; each content token
107
- * shared with the name/alias or summary adds 1.
129
+ * - Exact or one-character-off name/alias matches score 3; prefix matches score 2;
130
+ * each content token shared with the name/alias or summary adds 1.
108
131
  * - A unique tool with score >= 3 is auto-activated.
109
132
  * - A unique skill with score >= 3 is recommended (never auto-loaded).
110
133
  * - Score 2 or a tie at the top produces a catalog-discovery hint.
@@ -127,6 +150,7 @@ export function route(query: string, catalog: CatalogEntry[]): RouteResult {
127
150
  }
128
151
  for (const token of contentTokens) {
129
152
  if (nameTokens.has(token)) score += 3;
153
+ else if (token.length >= 5 && [...nameTokens].some((nameToken) => nameToken.length >= 5 && differsByOne(token, nameToken))) score += 3;
130
154
  else if (token.length >= 3 && [...nameTokens].some((nameToken) => nameToken.startsWith(token))) score += 2;
131
155
  if (summaryTokens.has(token)) score += 1;
132
156
  }
@@ -102,6 +102,15 @@ describe("deterministic routing", () => {
102
102
  expect(result.entry?.name).toBe("grep_app_search");
103
103
  });
104
104
 
105
+ it("auto-activates a uniquely identifiable one-character tool typo", () => {
106
+ const result = route("activate inercom", [
107
+ ...catalog,
108
+ { name: "intercom", kind: "tool", summary: "Coordinate with other local sessions", aliases: [], category: "coordination" },
109
+ ]);
110
+ expect(result.action).toBe("activate");
111
+ expect(result.entry?.name).toBe("intercom");
112
+ });
113
+
105
114
  it("recommends a unique high-confidence skill without auto-loading", () => {
106
115
  const result = route("do research on this topic", catalog);
107
116
  expect(result.action).toBe("recommend");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selesai/code",
3
- "version": "0.13.19",
3
+ "version": "0.13.20",
4
4
  "description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
5
5
  "type": "module",
6
6
  "repository": {