dsh-switchman 1.7.1 → 1.8.1
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/README.de.md +25 -4
- package/README.es.md +25 -4
- package/README.fr.md +25 -4
- package/README.it.md +25 -4
- package/README.ja.md +25 -4
- package/README.ko.md +25 -4
- package/README.md +25 -4
- package/README.pt.md +25 -4
- package/README.ru.md +25 -4
- package/README.zh-TW.md +25 -4
- package/README.zh.md +25 -4
- package/client.js +1916 -35
- package/host/config.js +93 -4
- package/host/context-watch.js +126 -58
- package/host/dispatch.js +132 -20
- package/host/intent-presets.js +163 -0
- package/host/intent.js +952 -0
- package/host/routes.js +158 -3
- package/host/vision.js +62 -26
- package/index.js +18 -2
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/** Preset intent categories for the "auto" pseudo-model's candidate list.
|
|
2
|
+
*
|
|
3
|
+
* A preset row ALWAYS exists in the merged candidate list: its note (the
|
|
4
|
+
* text the classifier reads) is code-owned and immutable by mandate —
|
|
5
|
+
* upgrades ship new wording without any settings migration — while the
|
|
6
|
+
* route fields (provider/model/effort) and the enabled toggle stay
|
|
7
|
+
* user-configured. Presets ship enabled with an empty model, so filling
|
|
8
|
+
* the model is the single step that activates one.
|
|
9
|
+
*
|
|
10
|
+
* Descriptions are deliberately written FOR the classifier, not for
|
|
11
|
+
* humans: each one states which user messages belong to the lane and
|
|
12
|
+
* carries a contrastive anchor against its neighbours, because a small
|
|
13
|
+
* local model needs explicit discrimination cues far more than prose
|
|
14
|
+
* polish does. English by mandate. */
|
|
15
|
+
|
|
16
|
+
export const INTENT_PRESETS = [
|
|
17
|
+
{
|
|
18
|
+
id: "quick-chat",
|
|
19
|
+
label: "Casual chat & quick Q&A",
|
|
20
|
+
note: "Casual conversation, greetings, quick factual or meta questions about the session itself, and follow-up chatter. No code changes and no deep reasoning required; speed and cost matter most.",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "mechanical-edit",
|
|
24
|
+
label: "Mechanical edits & chores",
|
|
25
|
+
note: "Small, fully specified changes: renames, typo and formatting fixes, config tweaks, license headers, running a known command, repetitive boilerplate edits. The plan is already given; execution is trivial.",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "codebase-exploration",
|
|
29
|
+
label: "Read-only codebase exploration",
|
|
30
|
+
note: "Read-only investigation of the repository: locating code, tracing call paths, grepping symbols, summarizing how a subsystem works, or answering where/how something is implemented. Output is an explanation, not a diff.",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "general-coding",
|
|
34
|
+
label: "Everyday implementation",
|
|
35
|
+
note: "Regular feature work and bug fixing: implementing a clear requirement across a few files, ordinary debugging, writing or updating tests and small scripts. The default lane for hands-on code changes.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "deep-reasoning",
|
|
39
|
+
label: "Architecture & hard problems",
|
|
40
|
+
note: "Decisions and problems that need careful, long thinking: architecture and design choices, multi-step orchestration, tricky cross-component bugs, algorithmic or concurrency issues, security-sensitive changes, anything with high rework cost if answered shallowly.",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "code-review",
|
|
44
|
+
label: "Review & verification",
|
|
45
|
+
note: "Reading and judging existing changes or output: diff and PR review, adversarial verification of another agent's work, spotting regressions or spec violations, explaining whether and why a change is correct.",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "docs-writing",
|
|
49
|
+
label: "Docs, translation & wording",
|
|
50
|
+
note: "Producing or polishing prose: documentation, READMEs, changelogs, commit messages, translation between languages, tone and clarity improvements. Language quality matters more than reasoning depth.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "vision-input",
|
|
54
|
+
label: "Image understanding",
|
|
55
|
+
note: "Turns whose input includes screenshots, photos, diagrams, charts, or UI mockups that must be visually interpreted to proceed.",
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const PRESET_BY_ID = new Map(INTENT_PRESETS.map((preset) => [preset.id, preset]));
|
|
60
|
+
|
|
61
|
+
function stringOf(value) {
|
|
62
|
+
return typeof value === "string" ? value.trim() : "";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Deterministic id for a stored entry that predates ids (legacy rows keep
|
|
66
|
+
* a stable identity across restarts even before the next settings save
|
|
67
|
+
* persists one). Route-key based, with a numeric suffix on collisions. */
|
|
68
|
+
function derivedId(provider, model, used) {
|
|
69
|
+
const base = `user:${provider}/${model}`;
|
|
70
|
+
if (!used.has(base)) return base;
|
|
71
|
+
let n = 2;
|
|
72
|
+
while (used.has(`${base}#${n}`)) n += 1;
|
|
73
|
+
return `${base}#${n}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Merge the stored candidate entries with the built-in presets into the
|
|
77
|
+
* unified list the settings UI renders and the router filters.
|
|
78
|
+
*
|
|
79
|
+
* Rules:
|
|
80
|
+
* - presets come first, in INTENT_PRESETS order, always all present;
|
|
81
|
+
* - a stored entry whose id names a preset overrides that preset's
|
|
82
|
+
* route/effort/enabled, but its note is ALWAYS the code value;
|
|
83
|
+
* - stored entries with unknown ids survive as user rows (graceful
|
|
84
|
+
* downgrade when a future preset id is removed again);
|
|
85
|
+
* - legacy entries without ids get deterministic derived ids;
|
|
86
|
+
* - fully-empty legacy junk (no id, no route, no note) is dropped, while
|
|
87
|
+
* any entry carrying an id survives as-is — a half-edited new row must
|
|
88
|
+
* not vanish from under the user between save and snapshot. */
|
|
89
|
+
export function mergeIntentCandidates(stored) {
|
|
90
|
+
const list = Array.isArray(stored) ? stored : [];
|
|
91
|
+
const presetOverrides = new Map();
|
|
92
|
+
const userRows = [];
|
|
93
|
+
// Pass 1 claims every explicit id BEFORE any derived id is minted, so a
|
|
94
|
+
// later explicit row can never collide with an earlier legacy row's
|
|
95
|
+
// derived `user:<provider>/<model>` name.
|
|
96
|
+
const usedIds = new Set();
|
|
97
|
+
for (const entry of list) {
|
|
98
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
99
|
+
const id = stringOf(entry.id);
|
|
100
|
+
if (id !== "") usedIds.add(id);
|
|
101
|
+
}
|
|
102
|
+
for (const entry of list) {
|
|
103
|
+
if (entry === null || typeof entry !== "object") continue;
|
|
104
|
+
const id = stringOf(entry.id);
|
|
105
|
+
const provider = stringOf(entry.provider);
|
|
106
|
+
const model = stringOf(entry.model);
|
|
107
|
+
const effort = stringOf(entry.effort);
|
|
108
|
+
const note = stringOf(entry.note);
|
|
109
|
+
const enabled = entry.enabled !== false;
|
|
110
|
+
const preset = id === "" ? null : PRESET_BY_ID.get(id);
|
|
111
|
+
if (preset !== null && preset !== undefined) {
|
|
112
|
+
presetOverrides.set(preset.id, { provider, model, effort, enabled });
|
|
113
|
+
usedIds.add(preset.id);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (id === "" && provider === "" && model === "" && note === "" && effort === "") continue;
|
|
117
|
+
const row = {
|
|
118
|
+
id: id !== "" ? id : derivedId(provider, model, usedIds),
|
|
119
|
+
preset: false,
|
|
120
|
+
enabled,
|
|
121
|
+
provider,
|
|
122
|
+
model,
|
|
123
|
+
};
|
|
124
|
+
if (effort !== "") row.effort = effort;
|
|
125
|
+
if (note !== "") row.note = note;
|
|
126
|
+
usedIds.add(row.id);
|
|
127
|
+
userRows.push(row);
|
|
128
|
+
}
|
|
129
|
+
const rows = [];
|
|
130
|
+
for (const preset of INTENT_PRESETS) {
|
|
131
|
+
const override = presetOverrides.get(preset.id);
|
|
132
|
+
const row = {
|
|
133
|
+
id: preset.id,
|
|
134
|
+
preset: true,
|
|
135
|
+
enabled: override ? override.enabled : true,
|
|
136
|
+
provider: override ? override.provider : "",
|
|
137
|
+
model: override ? override.model : "",
|
|
138
|
+
note: preset.note,
|
|
139
|
+
label: preset.label,
|
|
140
|
+
};
|
|
141
|
+
if (override && override.effort !== "") row.effort = override.effort;
|
|
142
|
+
rows.push(row);
|
|
143
|
+
}
|
|
144
|
+
for (const row of userRows) rows.push(row);
|
|
145
|
+
return rows;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Only rows that actually participate in routing: enabled AND a complete
|
|
149
|
+
* route. A preset still waiting for its model (or a freshly added custom
|
|
150
|
+
* row) stays visible in the UI but outside the classifier's list. */
|
|
151
|
+
export function effectiveIntentCandidates(rows) {
|
|
152
|
+
const list = Array.isArray(rows) ? rows : [];
|
|
153
|
+
return list.filter(
|
|
154
|
+
(entry) =>
|
|
155
|
+
entry !== null &&
|
|
156
|
+
typeof entry === "object" &&
|
|
157
|
+
entry.enabled !== false &&
|
|
158
|
+
typeof entry.provider === "string" &&
|
|
159
|
+
entry.provider.trim() !== "" &&
|
|
160
|
+
typeof entry.model === "string" &&
|
|
161
|
+
entry.model.trim() !== "",
|
|
162
|
+
);
|
|
163
|
+
}
|