min-agent 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -18
- package/dist/cli.js +538 -12
- package/docs/API.md +25 -8
- package/package.json +6 -6
- package/skills/self-config/SKILL.md +7 -3
- package/skills/self-config/reference.md +8 -4
- package/dist/agent.js +0 -1342
- package/dist/assistant-stream.js +0 -131
- package/dist/cli/commands/chat.js +0 -13
- package/dist/cli/commands/exec.js +0 -35
- package/dist/cli/commands/history.js +0 -58
- package/dist/cli/commands/index.js +0 -241
- package/dist/cli/commands/init.js +0 -18
- package/dist/cli/commands/mcp.js +0 -173
- package/dist/cli/commands/memory.js +0 -87
- package/dist/cli/commands/models.js +0 -21
- package/dist/cli/commands/permission.js +0 -12
- package/dist/cli/commands/rules.js +0 -33
- package/dist/cli/commands/sandbox.js +0 -13
- package/dist/cli/commands/serve.js +0 -9
- package/dist/cli/commands/setup.js +0 -4
- package/dist/cli/commands/shared.js +0 -16
- package/dist/cli/commands/skills.js +0 -119
- package/dist/cli/commands/think.js +0 -12
- package/dist/cli/commands/update.js +0 -7
- package/dist/cli/commands/write-config.js +0 -52
- package/dist/cli/errors.js +0 -36
- package/dist/cli/exec-prompt.js +0 -26
- package/dist/cli/option-helpers.js +0 -65
- package/dist/cli/program.js +0 -217
- package/dist/clipboard.js +0 -142
- package/dist/code-mode.js +0 -184
- package/dist/compaction.js +0 -644
- package/dist/config.js +0 -470
- package/dist/confirm.js +0 -172
- package/dist/context-window.js +0 -246
- package/dist/doom-loop.js +0 -278
- package/dist/fetch-timeout.js +0 -152
- package/dist/http-approvals.js +0 -60
- package/dist/http.js +0 -119
- package/dist/instructions.js +0 -196
- package/dist/logger.js +0 -95
- package/dist/markdown.js +0 -259
- package/dist/mcp.js +0 -1036
- package/dist/memory-cli.js +0 -33
- package/dist/memory.js +0 -295
- package/dist/model-catalog.js +0 -285
- package/dist/output.js +0 -76
- package/dist/paste-handler.js +0 -41
- package/dist/permission-cli.js +0 -40
- package/dist/plugins.js +0 -131
- package/dist/pricing.js +0 -119
- package/dist/provider.js +0 -63
- package/dist/question-format.js +0 -60
- package/dist/reasoning-stream.js +0 -158
- package/dist/sandbox-cli.js +0 -79
- package/dist/sandbox.js +0 -403
- package/dist/save-throttle.js +0 -45
- package/dist/scope.js +0 -23
- package/dist/serve/common.js +0 -425
- package/dist/serve/routes-chat.js +0 -367
- package/dist/serve/routes-mcp.js +0 -212
- package/dist/serve/routes-memory.js +0 -95
- package/dist/serve/routes-meta.js +0 -233
- package/dist/serve/routes-sessions.js +0 -61
- package/dist/serve/routes-skills.js +0 -70
- package/dist/serve.js +0 -138
- package/dist/sessions.js +0 -265
- package/dist/skills.js +0 -599
- package/dist/structured-output.js +0 -29
- package/dist/synthetic.js +0 -7
- package/dist/think-cli.js +0 -36
- package/dist/thinking-wire.js +0 -228
- package/dist/thinking.js +0 -142
- package/dist/title-gen.js +0 -55
- package/dist/token-display.js +0 -39
- package/dist/tool-display.js +0 -178
- package/dist/tool-output.js +0 -126
- package/dist/tools/apply_patch.js +0 -265
- package/dist/tools/atomic-file.js +0 -35
- package/dist/tools/backend.js +0 -61
- package/dist/tools/bash.js +0 -231
- package/dist/tools/code_search.js +0 -98
- package/dist/tools/edit.js +0 -68
- package/dist/tools/explore.js +0 -232
- package/dist/tools/glob.js +0 -40
- package/dist/tools/grep.js +0 -174
- package/dist/tools/index.js +0 -34
- package/dist/tools/question.js +0 -54
- package/dist/tools/read.js +0 -109
- package/dist/tools/search-searxng.js +0 -223
- package/dist/tools/search-serper.js +0 -189
- package/dist/tools/task.js +0 -165
- package/dist/tools/todo.js +0 -213
- package/dist/tools/web_fetch.js +0 -195
- package/dist/tools/web_search.js +0 -228
- package/dist/tools/write.js +0 -55
- package/dist/tui/App.js +0 -126
- package/dist/tui/ConfirmBar.js +0 -66
- package/dist/tui/InputBar.js +0 -616
- package/dist/tui/MessageList.js +0 -718
- package/dist/tui/ModelPicker.js +0 -113
- package/dist/tui/QuestionBar.js +0 -136
- package/dist/tui/SessionPicker.js +0 -79
- package/dist/tui/Spinner.js +0 -6
- package/dist/tui/StatusBar.js +0 -22
- package/dist/tui/ThinkPicker.js +0 -77
- package/dist/tui/agent-runner.js +0 -223
- package/dist/tui/bracketed-paste.js +0 -37
- package/dist/tui/caret-pos.js +0 -179
- package/dist/tui/caret.js +0 -69
- package/dist/tui/click-count.js +0 -13
- package/dist/tui/diff-view.js +0 -61
- package/dist/tui/drag-state.js +0 -49
- package/dist/tui/hydrate.js +0 -129
- package/dist/tui/index.js +0 -258
- package/dist/tui/input-history.js +0 -125
- package/dist/tui/layout.js +0 -105
- package/dist/tui/mouse.js +0 -46
- package/dist/tui/overlay-input.js +0 -12
- package/dist/tui/paste-draft.js +0 -173
- package/dist/tui/prompt-queue.js +0 -24
- package/dist/tui/selection.js +0 -232
- package/dist/tui/session-switch.js +0 -28
- package/dist/tui/slash-commands.js +0 -123
- package/dist/tui/slash-handler.js +0 -589
- package/dist/tui/text-width.js +0 -113
- package/dist/tui/theme.js +0 -12
- package/dist/tui/token-info.js +0 -7
- package/dist/tui/tool-children.js +0 -19
- package/dist/tui/types.js +0 -1
- package/dist/tui/undo-stack.js +0 -14
- package/dist/tui/use-sgr-mouse.js +0 -29
- package/dist/tui-chat.js +0 -456
- package/dist/updater.js +0 -116
- package/dist/xml-search.js +0 -194
- package/docs/superpowers/plans/2026-08-16-batch1-tui-improvements.md +0 -1510
- package/docs/superpowers/plans/2026-08-16-batch2-cli-tools-api.md +0 -2105
- package/docs/superpowers/plans/2026-08-16-batch3-config-engineering.md +0 -1595
- package/docs/superpowers/plans/2026-08-16-input-caret.md +0 -782
- package/docs/superpowers/plans/2026-08-20-tui-completeness.md +0 -873
- package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +0 -631
- package/docs/superpowers/plans/2026-08-23-input-paste-attachments.md +0 -475
- package/docs/superpowers/plans/2026-08-23-thinking-wire-profile.md +0 -450
- package/docs/superpowers/specs/2026-08-16-batch1-tui-improvements-design.md +0 -183
- package/docs/superpowers/specs/2026-08-16-batch2-cli-tools-api-design.md +0 -220
- package/docs/superpowers/specs/2026-08-16-batch3-config-engineering-design.md +0 -196
- package/docs/superpowers/specs/2026-08-16-input-caret-design.md +0 -63
- package/docs/superpowers/specs/2026-08-17-mouse-selection-design.md +0 -116
- package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +0 -47
- package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +0 -37
- package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +0 -68
- package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +0 -273
- package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +0 -165
- package/docs/superpowers/specs/2026-08-23-input-paste-attachments-design.md +0 -174
- package/docs/superpowers/specs/2026-08-23-thinking-wire-profile-design.md +0 -140
package/dist/thinking-wire.js
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
2
|
-
import { readFileSync, mkdirSync, existsSync } from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { getActiveProvider, getConfigDir, getEffectiveConfig } from "./config.js";
|
|
5
|
-
import { atomicWriteFileSync } from "./tools/atomic-file.js";
|
|
6
|
-
export const thinkingRequestStore = new AsyncLocalStorage();
|
|
7
|
-
export const THINKING_WIRE_VERSION = 1;
|
|
8
|
-
export const THINKING_WIRE_TTL = 7 * 24 * 60 * 60 * 1000;
|
|
9
|
-
export const DEFAULT_THINKING_WIRE = {
|
|
10
|
-
version: THINKING_WIRE_VERSION,
|
|
11
|
-
thinkingType: "enabled-disabled",
|
|
12
|
-
effort: "reasoning_effort",
|
|
13
|
-
};
|
|
14
|
-
const memoryCache = new Map();
|
|
15
|
-
function isRecord(value) {
|
|
16
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
17
|
-
}
|
|
18
|
-
function isThinkingTypeMode(value) {
|
|
19
|
-
return value === "enabled-disabled" || value === "adaptive-disabled" || value === "omit";
|
|
20
|
-
}
|
|
21
|
-
function isEffortMode(value) {
|
|
22
|
-
return value === "reasoning_effort" || value === "omit";
|
|
23
|
-
}
|
|
24
|
-
function isProfile(value) {
|
|
25
|
-
if (!isRecord(value))
|
|
26
|
-
return false;
|
|
27
|
-
return (value.version === THINKING_WIRE_VERSION &&
|
|
28
|
-
isThinkingTypeMode(value.thinkingType) &&
|
|
29
|
-
isEffortMode(value.effort) &&
|
|
30
|
-
typeof value.timestamp === "number" &&
|
|
31
|
-
Number.isFinite(value.timestamp));
|
|
32
|
-
}
|
|
33
|
-
function cacheFile() {
|
|
34
|
-
return path.join(getConfigDir(), "thinking-wire-cache.json");
|
|
35
|
-
}
|
|
36
|
-
function loadDiskCache() {
|
|
37
|
-
const file = cacheFile();
|
|
38
|
-
if (!existsSync(file))
|
|
39
|
-
return {};
|
|
40
|
-
try {
|
|
41
|
-
const parsed = JSON.parse(readFileSync(file, "utf-8"));
|
|
42
|
-
if (!isRecord(parsed))
|
|
43
|
-
return {};
|
|
44
|
-
const out = {};
|
|
45
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
46
|
-
if (isProfile(value))
|
|
47
|
-
out[key] = value;
|
|
48
|
-
}
|
|
49
|
-
return out;
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
return {};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function saveDiskCache(cache) {
|
|
56
|
-
const file = cacheFile();
|
|
57
|
-
mkdirSync(path.dirname(file), { recursive: true });
|
|
58
|
-
atomicWriteFileSync(file, JSON.stringify(cache));
|
|
59
|
-
}
|
|
60
|
-
function liveProfile(entry, now) {
|
|
61
|
-
if (entry.version !== THINKING_WIRE_VERSION)
|
|
62
|
-
return null;
|
|
63
|
-
if (now - entry.timestamp > THINKING_WIRE_TTL)
|
|
64
|
-
return null;
|
|
65
|
-
return entry;
|
|
66
|
-
}
|
|
67
|
-
export function thinkingWireCacheKey(host, modelId) {
|
|
68
|
-
const h = host.trim().toLowerCase();
|
|
69
|
-
const m = modelId.trim().toLowerCase();
|
|
70
|
-
if (!h)
|
|
71
|
-
return m;
|
|
72
|
-
if (!m)
|
|
73
|
-
return h;
|
|
74
|
-
return `${h}::${m}`;
|
|
75
|
-
}
|
|
76
|
-
export function inferThinkingWire(modelId, host, providerType) {
|
|
77
|
-
const haystack = `${modelId} ${host}`.toLowerCase();
|
|
78
|
-
if (haystack.includes("minimax")) {
|
|
79
|
-
return { thinkingType: "adaptive-disabled", effort: "reasoning_effort" };
|
|
80
|
-
}
|
|
81
|
-
if (providerType === "openai") {
|
|
82
|
-
return { thinkingType: "omit", effort: "reasoning_effort" };
|
|
83
|
-
}
|
|
84
|
-
return { thinkingType: DEFAULT_THINKING_WIRE.thinkingType, effort: DEFAULT_THINKING_WIRE.effort };
|
|
85
|
-
}
|
|
86
|
-
function readCachedProfile(key, now) {
|
|
87
|
-
const memory = memoryCache.get(key);
|
|
88
|
-
if (memory) {
|
|
89
|
-
const live = liveProfile(memory, now);
|
|
90
|
-
if (live)
|
|
91
|
-
return live;
|
|
92
|
-
memoryCache.delete(key);
|
|
93
|
-
}
|
|
94
|
-
const disk = loadDiskCache()[key];
|
|
95
|
-
if (!disk)
|
|
96
|
-
return null;
|
|
97
|
-
const live = liveProfile(disk, now);
|
|
98
|
-
if (!live)
|
|
99
|
-
return null;
|
|
100
|
-
memoryCache.set(key, live);
|
|
101
|
-
return live;
|
|
102
|
-
}
|
|
103
|
-
export function resolveThinkingWireProfile(host, modelId, providerType, now = Date.now()) {
|
|
104
|
-
const cached = readCachedProfile(thinkingWireCacheKey(host, modelId), now);
|
|
105
|
-
if (cached)
|
|
106
|
-
return cached;
|
|
107
|
-
return {
|
|
108
|
-
version: THINKING_WIRE_VERSION,
|
|
109
|
-
...inferThinkingWire(modelId, host, providerType),
|
|
110
|
-
timestamp: now,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
export function saveLearnedThinkingWire(host, modelId, profile, now = Date.now()) {
|
|
114
|
-
const entry = {
|
|
115
|
-
version: THINKING_WIRE_VERSION,
|
|
116
|
-
thinkingType: profile.thinkingType,
|
|
117
|
-
effort: profile.effort,
|
|
118
|
-
timestamp: profile.timestamp ?? now,
|
|
119
|
-
};
|
|
120
|
-
const key = thinkingWireCacheKey(host, modelId);
|
|
121
|
-
memoryCache.set(key, entry);
|
|
122
|
-
const cache = loadDiskCache();
|
|
123
|
-
cache[key] = entry;
|
|
124
|
-
saveDiskCache(cache);
|
|
125
|
-
return entry;
|
|
126
|
-
}
|
|
127
|
-
export function clearThinkingWireCache() {
|
|
128
|
-
memoryCache.clear();
|
|
129
|
-
}
|
|
130
|
-
export function applyThinkingToChatBody(body, spec, profile = DEFAULT_THINKING_WIRE) {
|
|
131
|
-
const next = { ...body };
|
|
132
|
-
if (profile.effort === "omit")
|
|
133
|
-
delete next.reasoning_effort;
|
|
134
|
-
else
|
|
135
|
-
next.reasoning_effort = spec.reasoningEffort;
|
|
136
|
-
if (profile.thinkingType === "omit")
|
|
137
|
-
delete next.thinking;
|
|
138
|
-
else {
|
|
139
|
-
const on = profile.thinkingType === "adaptive-disabled" ? "adaptive" : "enabled";
|
|
140
|
-
next.thinking = { type: spec.thinkingEnabled ? on : "disabled" };
|
|
141
|
-
}
|
|
142
|
-
return next;
|
|
143
|
-
}
|
|
144
|
-
function splitAllowed(raw) {
|
|
145
|
-
return new Set(raw
|
|
146
|
-
.split(",")
|
|
147
|
-
.map((part) => part
|
|
148
|
-
.trim()
|
|
149
|
-
.replace(/^['"]|['"]$/g, "")
|
|
150
|
-
.toLowerCase())
|
|
151
|
-
.filter(Boolean));
|
|
152
|
-
}
|
|
153
|
-
export function parseThinkingWireHint(text) {
|
|
154
|
-
const allowedMatch = text.match(/thinking\.type[\s\S]*?allowed:\s*([^)]+)/i);
|
|
155
|
-
if (allowedMatch?.[1]) {
|
|
156
|
-
const allowed = splitAllowed(allowedMatch[1]);
|
|
157
|
-
if (allowed.has("adaptive") && allowed.has("disabled"))
|
|
158
|
-
return { thinkingType: "adaptive-disabled" };
|
|
159
|
-
if (allowed.has("enabled") && allowed.has("disabled"))
|
|
160
|
-
return { thinkingType: "enabled-disabled" };
|
|
161
|
-
}
|
|
162
|
-
if (/unknown parameter[:\s]+['"`]?thinking['"`]?(?![\w.])/i.test(text))
|
|
163
|
-
return { thinkingType: "omit" };
|
|
164
|
-
if (/unknown parameter[:\s]+['"`]?reasoning_effort['"`]?(?![\w.])/i.test(text))
|
|
165
|
-
return { effort: "omit" };
|
|
166
|
-
return null;
|
|
167
|
-
}
|
|
168
|
-
function hostnameFromInput(input) {
|
|
169
|
-
try {
|
|
170
|
-
const raw = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
171
|
-
return new URL(raw).hostname;
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
return "";
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function rebuild400(response, text) {
|
|
178
|
-
return new Response(text, {
|
|
179
|
-
status: 400,
|
|
180
|
-
statusText: response.statusText,
|
|
181
|
-
headers: response.headers,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
function profileUnchanged(current, next) {
|
|
185
|
-
return current.thinkingType === next.thinkingType && current.effort === next.effort;
|
|
186
|
-
}
|
|
187
|
-
export async function fetchWithThinkingWire(inner, input, init, opts) {
|
|
188
|
-
const spec = thinkingRequestStore.getStore();
|
|
189
|
-
if (!spec || !init || typeof init.body !== "string")
|
|
190
|
-
return inner(input, init);
|
|
191
|
-
let parsed;
|
|
192
|
-
try {
|
|
193
|
-
parsed = JSON.parse(init.body);
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
return inner(input, init);
|
|
197
|
-
}
|
|
198
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
199
|
-
return inner(input, init);
|
|
200
|
-
const body = parsed;
|
|
201
|
-
const host = hostnameFromInput(input);
|
|
202
|
-
const modelId = typeof body.model === "string" ? body.model : "";
|
|
203
|
-
const providerType = opts?.providerType ?? getActiveProvider(getEffectiveConfig())?.type;
|
|
204
|
-
const profile = resolveThinkingWireProfile(host, modelId, providerType);
|
|
205
|
-
const first = await inner(input, {
|
|
206
|
-
...init,
|
|
207
|
-
body: JSON.stringify(applyThinkingToChatBody(body, spec, profile)),
|
|
208
|
-
});
|
|
209
|
-
if (first.status !== 400)
|
|
210
|
-
return first;
|
|
211
|
-
const text = await first.text();
|
|
212
|
-
const hint = parseThinkingWireHint(text);
|
|
213
|
-
if (!hint)
|
|
214
|
-
return rebuild400(first, text);
|
|
215
|
-
const next = {
|
|
216
|
-
version: THINKING_WIRE_VERSION,
|
|
217
|
-
thinkingType: hint.thinkingType ?? profile.thinkingType,
|
|
218
|
-
effort: hint.effort ?? profile.effort,
|
|
219
|
-
timestamp: Date.now(),
|
|
220
|
-
};
|
|
221
|
-
if (profileUnchanged(profile, next))
|
|
222
|
-
return rebuild400(first, text);
|
|
223
|
-
saveLearnedThinkingWire(host, modelId, next);
|
|
224
|
-
return inner(input, {
|
|
225
|
-
...init,
|
|
226
|
-
body: JSON.stringify(applyThinkingToChatBody(body, spec, next)),
|
|
227
|
-
});
|
|
228
|
-
}
|
package/dist/thinking.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { getEffectiveConfig, loadConfig, loadProjectConfig, saveConfig, saveProjectConfig, getActiveProvider, parseThinkingEffort, } from "./config.js";
|
|
2
|
-
import { getCachedModelCatalog } from "./model-catalog.js";
|
|
3
|
-
import { applyThinkingToChatBody, thinkingRequestStore } from "./thinking-wire.js";
|
|
4
|
-
export { parseThinkingEffort, applyThinkingToChatBody, thinkingRequestStore };
|
|
5
|
-
export const THINKING_EFFORTS = ["off", "low", "medium", "high", "max"];
|
|
6
|
-
export const DEFAULT_THINKING_EFFORT = "medium";
|
|
7
|
-
let thinkingOverride;
|
|
8
|
-
export function setThinkingOverride(effort) {
|
|
9
|
-
thinkingOverride = effort;
|
|
10
|
-
}
|
|
11
|
-
export function getThinkingOverride() {
|
|
12
|
-
return thinkingOverride;
|
|
13
|
-
}
|
|
14
|
-
export function thinkingEffortLabel(effort) {
|
|
15
|
-
return effort;
|
|
16
|
-
}
|
|
17
|
-
export function thinkingChoiceIndex(effort, choices = THINKING_EFFORTS) {
|
|
18
|
-
const list = choices.length > 0 ? choices : THINKING_EFFORTS;
|
|
19
|
-
const clamped = clampThinkingEffort(effort, list);
|
|
20
|
-
const i = list.indexOf(clamped);
|
|
21
|
-
return i >= 0 ? i : 0;
|
|
22
|
-
}
|
|
23
|
-
export function thinkingChoicesFromReasoning(info) {
|
|
24
|
-
const set = new Set();
|
|
25
|
-
if (info.toggle || info.efforts.includes("none"))
|
|
26
|
-
set.add("off");
|
|
27
|
-
for (const raw of info.efforts) {
|
|
28
|
-
const mapped = parseThinkingEffort(raw);
|
|
29
|
-
if (mapped)
|
|
30
|
-
set.add(mapped);
|
|
31
|
-
}
|
|
32
|
-
const enabled = THINKING_EFFORTS.filter((effort) => effort !== "off" && set.has(effort));
|
|
33
|
-
// Toggle-only catalog rows must not collapse the picker to off.
|
|
34
|
-
if (enabled.length === 0)
|
|
35
|
-
return [...THINKING_EFFORTS];
|
|
36
|
-
return THINKING_EFFORTS.filter((effort) => set.has(effort));
|
|
37
|
-
}
|
|
38
|
-
export function thinkingChoicesForModel(modelId) {
|
|
39
|
-
if (!modelId)
|
|
40
|
-
return [...THINKING_EFFORTS];
|
|
41
|
-
const entry = getCachedModelCatalog(modelId);
|
|
42
|
-
if (!entry)
|
|
43
|
-
return [...THINKING_EFFORTS];
|
|
44
|
-
return thinkingChoicesFromReasoning(entry.reasoning);
|
|
45
|
-
}
|
|
46
|
-
export function clampThinkingEffort(effort, choices = THINKING_EFFORTS) {
|
|
47
|
-
if (choices.length === 0 || choices.includes(effort))
|
|
48
|
-
return effort;
|
|
49
|
-
if (choices.includes("medium"))
|
|
50
|
-
return "medium";
|
|
51
|
-
if (choices.includes("high"))
|
|
52
|
-
return "high";
|
|
53
|
-
const enabled = choices.filter((item) => item !== "off");
|
|
54
|
-
if (enabled.length > 0)
|
|
55
|
-
return enabled[Math.floor((enabled.length - 1) / 2)];
|
|
56
|
-
return choices.find((item) => item !== "off") ?? DEFAULT_THINKING_EFFORT;
|
|
57
|
-
}
|
|
58
|
-
export function thinkingSourceLabel(source) {
|
|
59
|
-
if (source === "cli")
|
|
60
|
-
return "本次启动参数";
|
|
61
|
-
if (source === "project")
|
|
62
|
-
return "项目";
|
|
63
|
-
if (source === "global")
|
|
64
|
-
return "全局";
|
|
65
|
-
return "默认";
|
|
66
|
-
}
|
|
67
|
-
export function resolveThinkingEffort(runOverride, modelId) {
|
|
68
|
-
let thinking;
|
|
69
|
-
let source;
|
|
70
|
-
if (runOverride) {
|
|
71
|
-
thinking = runOverride;
|
|
72
|
-
source = "cli";
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
const override = getThinkingOverride();
|
|
76
|
-
if (override) {
|
|
77
|
-
thinking = override;
|
|
78
|
-
source = "cli";
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
const snap = getThinkingSnapshot();
|
|
82
|
-
thinking = snap.thinking ?? DEFAULT_THINKING_EFFORT;
|
|
83
|
-
source = snap.source;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
const id = modelId ?? getActiveProvider(getEffectiveConfig())?.defaultModel;
|
|
87
|
-
return { thinking: clampThinkingEffort(thinking, thinkingChoicesForModel(id)), source };
|
|
88
|
-
}
|
|
89
|
-
export function getThinkingSnapshot() {
|
|
90
|
-
const project = parseThinkingEffort(loadProjectConfig().thinking);
|
|
91
|
-
if (project !== undefined)
|
|
92
|
-
return { thinking: project, source: "project" };
|
|
93
|
-
const global = parseThinkingEffort(loadConfig().thinking);
|
|
94
|
-
if (global !== undefined)
|
|
95
|
-
return { thinking: global, source: "global" };
|
|
96
|
-
return { thinking: undefined, source: null };
|
|
97
|
-
}
|
|
98
|
-
function clearProjectThinking() {
|
|
99
|
-
const project = loadProjectConfig();
|
|
100
|
-
if (project.thinking === undefined)
|
|
101
|
-
return;
|
|
102
|
-
const { thinking: _removed, ...rest } = project;
|
|
103
|
-
saveProjectConfig(rest);
|
|
104
|
-
}
|
|
105
|
-
export function setThinkingEffort(effort, scope) {
|
|
106
|
-
if (scope === "project") {
|
|
107
|
-
saveProjectConfig({ ...loadProjectConfig(), thinking: effort });
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
saveConfig({ ...loadConfig(), thinking: effort });
|
|
111
|
-
// A leftover project value (including off) would keep shadowing the global file.
|
|
112
|
-
clearProjectThinking();
|
|
113
|
-
}
|
|
114
|
-
export function thinkingPayload() {
|
|
115
|
-
const { thinking, source } = resolveThinkingEffort();
|
|
116
|
-
return {
|
|
117
|
-
thinking,
|
|
118
|
-
source,
|
|
119
|
-
label: thinkingEffortLabel(thinking),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
export function toWireEffort(effort, providerType, modelId) {
|
|
123
|
-
if (effort === "off")
|
|
124
|
-
return "none";
|
|
125
|
-
if (effort !== "max")
|
|
126
|
-
return effort;
|
|
127
|
-
const catalog = modelId ? getCachedModelCatalog(modelId) : undefined;
|
|
128
|
-
const supported = catalog?.reasoning.efforts ?? [];
|
|
129
|
-
if (supported.includes("max"))
|
|
130
|
-
return "max";
|
|
131
|
-
if (supported.includes("xhigh") || providerType === "openai")
|
|
132
|
-
return "xhigh";
|
|
133
|
-
return "max";
|
|
134
|
-
}
|
|
135
|
-
export function resolveThinkingRequest(opts) {
|
|
136
|
-
const { thinking } = resolveThinkingEffort(opts?.runOverride, opts?.modelId);
|
|
137
|
-
const providerType = opts?.providerType ?? getActiveProvider(getEffectiveConfig())?.type;
|
|
138
|
-
return {
|
|
139
|
-
reasoningEffort: toWireEffort(thinking, providerType, opts?.modelId),
|
|
140
|
-
thinkingEnabled: thinking !== "off",
|
|
141
|
-
};
|
|
142
|
-
}
|
package/dist/title-gen.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { generateText } from "ai";
|
|
2
|
-
/**
|
|
3
|
-
* Auto-generate a short title for a conversation session.
|
|
4
|
-
*
|
|
5
|
-
* Based on opencode's title agent — uses the LLM to produce a concise
|
|
6
|
-
* title from the first user message and assistant response.
|
|
7
|
-
*/
|
|
8
|
-
const TITLE_PROMPT = `Generate a very short title (max 50 chars) for this conversation.
|
|
9
|
-
Output ONLY the title text, nothing else. No quotes, no prefix.
|
|
10
|
-
The title should capture the main topic or intent.`;
|
|
11
|
-
export async function generateTitle(model, messages) {
|
|
12
|
-
// Need at least one user message
|
|
13
|
-
const userMsg = messages.find((m) => m.role === "user");
|
|
14
|
-
if (!userMsg)
|
|
15
|
-
return null;
|
|
16
|
-
const userContent = typeof userMsg.content === "string"
|
|
17
|
-
? userMsg.content
|
|
18
|
-
: Array.isArray(userMsg.content)
|
|
19
|
-
? userMsg.content
|
|
20
|
-
.filter((p) => "text" in p)
|
|
21
|
-
.map((p) => p.text ?? "")
|
|
22
|
-
.join(" ")
|
|
23
|
-
: "";
|
|
24
|
-
if (!userContent.trim())
|
|
25
|
-
return null;
|
|
26
|
-
// Include first assistant response if available for better context
|
|
27
|
-
const assistantMsg = messages.find((m) => m.role === "assistant");
|
|
28
|
-
const assistantContent = assistantMsg
|
|
29
|
-
? typeof assistantMsg.content === "string"
|
|
30
|
-
? assistantMsg.content.slice(0, 200)
|
|
31
|
-
: ""
|
|
32
|
-
: "";
|
|
33
|
-
const context = assistantContent
|
|
34
|
-
? `User: ${userContent.slice(0, 300)}\nAssistant: ${assistantContent}`
|
|
35
|
-
: `User: ${userContent.slice(0, 300)}`;
|
|
36
|
-
try {
|
|
37
|
-
const result = await generateText({
|
|
38
|
-
model,
|
|
39
|
-
messages: [
|
|
40
|
-
{ role: "system", content: TITLE_PROMPT },
|
|
41
|
-
{ role: "user", content: context },
|
|
42
|
-
],
|
|
43
|
-
temperature: 0.5,
|
|
44
|
-
maxOutputTokens: 30,
|
|
45
|
-
});
|
|
46
|
-
const title = result.text
|
|
47
|
-
.trim()
|
|
48
|
-
.replace(/^["']|["']$/g, "")
|
|
49
|
-
.slice(0, 60);
|
|
50
|
-
return title || null;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
}
|
package/dist/token-display.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
export function formatTokenCount(n) {
|
|
2
|
-
const v = Math.max(0, Math.round(n));
|
|
3
|
-
if (v < 1000)
|
|
4
|
-
return String(v);
|
|
5
|
-
if (v < 100_000)
|
|
6
|
-
return `${(v / 1000).toFixed(1).replace(/\.0$/, "")}k`;
|
|
7
|
-
if (v < 1_000_000)
|
|
8
|
-
return `${Math.round(v / 1000)}k`;
|
|
9
|
-
if (v < 10_000_000)
|
|
10
|
-
return `${(v / 1_000_000).toFixed(1).replace(/\.0$/, "")}M`;
|
|
11
|
-
return `${Math.round(v / 1_000_000)}M`;
|
|
12
|
-
}
|
|
13
|
-
export function contextUsagePercent(context, contextWindow) {
|
|
14
|
-
if (context <= 0 || contextWindow <= 0)
|
|
15
|
-
return null;
|
|
16
|
-
return Math.round((context / contextWindow) * 100);
|
|
17
|
-
}
|
|
18
|
-
export function tokenInfoFromTracker(tracker, contextWindow, estimatedContext = 0) {
|
|
19
|
-
return {
|
|
20
|
-
context: tracker.lastInputTokens > 0 ? tracker.lastInputTokens : estimatedContext,
|
|
21
|
-
input: tracker.totalInputTokens,
|
|
22
|
-
output: tracker.totalOutputTokens,
|
|
23
|
-
contextWindow,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
export function formatTokenStatusLabel(info) {
|
|
27
|
-
const pct = contextUsagePercent(info.context, info.contextWindow);
|
|
28
|
-
if (pct == null)
|
|
29
|
-
return "";
|
|
30
|
-
return `${formatTokenCount(info.context)}/${formatTokenCount(info.contextWindow)} ${pct}%`;
|
|
31
|
-
}
|
|
32
|
-
export function formatModelStatusMeta(thinkingLabel, contextWindow, memoryOn = false) {
|
|
33
|
-
const parts = [`思考 ${thinkingLabel}`];
|
|
34
|
-
if (memoryOn)
|
|
35
|
-
parts.push("记忆 开");
|
|
36
|
-
if (contextWindow && contextWindow > 0)
|
|
37
|
-
parts.push(`窗口 ${formatTokenCount(contextWindow)}`);
|
|
38
|
-
return parts.join(" · ");
|
|
39
|
-
}
|
package/dist/tool-display.js
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One-line summaries of tool calls and their results, shared by the TUI
|
|
3
|
-
* (`⚡ search_web "query" (news)`) and the plain CLI output. Keeping both
|
|
4
|
-
* surfaces on this module means a tool only needs a summary rule once.
|
|
5
|
-
*
|
|
6
|
-
* Summaries are neutral/English because tool names, arguments and paths are.
|
|
7
|
-
*/
|
|
8
|
-
/** Truncate by code point (not UTF-16 unit) so surrogate pairs never split. */
|
|
9
|
-
export function truncateDisplay(text, max) {
|
|
10
|
-
const chars = Array.from(text);
|
|
11
|
-
if (chars.length <= max)
|
|
12
|
-
return text;
|
|
13
|
-
return chars.slice(0, Math.max(1, max - 1)).join("") + "…";
|
|
14
|
-
}
|
|
15
|
-
/** Collapse whitespace so a multi-line value stays on one row. */
|
|
16
|
-
function oneLine(text) {
|
|
17
|
-
return text.replace(/\s+/g, " ").trim();
|
|
18
|
-
}
|
|
19
|
-
function asRecord(input) {
|
|
20
|
-
return typeof input === "object" && input !== null && !Array.isArray(input)
|
|
21
|
-
? input
|
|
22
|
-
: null;
|
|
23
|
-
}
|
|
24
|
-
function str(rec, key) {
|
|
25
|
-
const v = rec[key];
|
|
26
|
-
return typeof v === "string" ? oneLine(v) : "";
|
|
27
|
-
}
|
|
28
|
-
/** Shorten a path to its last two segments (`src/tools/web_search.ts` → `tools/web_search.ts`). */
|
|
29
|
-
function shortPath(p) {
|
|
30
|
-
const parts = p.split("/").filter(Boolean);
|
|
31
|
-
return parts.length <= 2 ? p : `…/${parts.slice(-2).join("/")}`;
|
|
32
|
-
}
|
|
33
|
-
/** Generic `key=value` rendering, used for tools without a specific rule. */
|
|
34
|
-
export function formatToolArgs(input, maxValueLen = 60) {
|
|
35
|
-
const rec = asRecord(input);
|
|
36
|
-
if (!rec)
|
|
37
|
-
return "";
|
|
38
|
-
const parts = [];
|
|
39
|
-
for (const [k, v] of Object.entries(rec)) {
|
|
40
|
-
if (v === undefined || v === null)
|
|
41
|
-
continue;
|
|
42
|
-
const raw = typeof v === "string" ? oneLine(v) : JSON.stringify(v);
|
|
43
|
-
if (raw === undefined)
|
|
44
|
-
continue;
|
|
45
|
-
parts.push(`${k}=${truncateDisplay(raw, maxValueLen)}`);
|
|
46
|
-
}
|
|
47
|
-
return parts.join(" ");
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Summarize a tool call for a single row. `maxLen` bounds the whole summary;
|
|
51
|
-
* the caller still truncates to the terminal width.
|
|
52
|
-
*/
|
|
53
|
-
export function summarizeToolCall(toolName, input, maxLen = 120) {
|
|
54
|
-
const rec = asRecord(input);
|
|
55
|
-
if (!rec)
|
|
56
|
-
return "";
|
|
57
|
-
const summary = specificCallSummary(toolName, rec);
|
|
58
|
-
return truncateDisplay(summary || formatToolArgs(rec, 60), maxLen);
|
|
59
|
-
}
|
|
60
|
-
function specificCallSummary(toolName, rec) {
|
|
61
|
-
switch (toolName) {
|
|
62
|
-
case "search_web": {
|
|
63
|
-
const query = str(rec, "query");
|
|
64
|
-
if (!query)
|
|
65
|
-
return "";
|
|
66
|
-
const facets = [];
|
|
67
|
-
const categories = str(rec, "categories");
|
|
68
|
-
if (categories && categories !== "general")
|
|
69
|
-
facets.push(categories);
|
|
70
|
-
const language = str(rec, "language");
|
|
71
|
-
if (language)
|
|
72
|
-
facets.push(language);
|
|
73
|
-
const range = str(rec, "time_range");
|
|
74
|
-
if (range)
|
|
75
|
-
facets.push(`past ${range}`);
|
|
76
|
-
const engines = str(rec, "engines");
|
|
77
|
-
if (engines)
|
|
78
|
-
facets.push(`via ${engines}`);
|
|
79
|
-
return `"${query}"${facets.length > 0 ? ` (${facets.join(", ")})` : ""}`;
|
|
80
|
-
}
|
|
81
|
-
case "web_fetch": {
|
|
82
|
-
const url = str(rec, "url");
|
|
83
|
-
const method = str(rec, "method");
|
|
84
|
-
return url ? `${method && method.toUpperCase() !== "GET" ? `${method.toUpperCase()} ` : ""}${url}` : "";
|
|
85
|
-
}
|
|
86
|
-
case "read": {
|
|
87
|
-
const p = str(rec, "filePath");
|
|
88
|
-
if (!p)
|
|
89
|
-
return "";
|
|
90
|
-
const start = rec.startLine;
|
|
91
|
-
const end = rec.endLine;
|
|
92
|
-
const range = typeof start === "number" || typeof end === "number" ? `:${start ?? 1}-${end ?? ""}` : "";
|
|
93
|
-
return `${shortPath(p)}${range}`;
|
|
94
|
-
}
|
|
95
|
-
case "write":
|
|
96
|
-
case "edit":
|
|
97
|
-
case "apply_patch": {
|
|
98
|
-
const p = str(rec, "filePath") || str(rec, "path");
|
|
99
|
-
return p ? shortPath(p) : "";
|
|
100
|
-
}
|
|
101
|
-
case "bash":
|
|
102
|
-
return str(rec, "command");
|
|
103
|
-
case "grep": {
|
|
104
|
-
const pattern = str(rec, "pattern");
|
|
105
|
-
if (!pattern)
|
|
106
|
-
return "";
|
|
107
|
-
const where = str(rec, "path");
|
|
108
|
-
const include = str(rec, "include");
|
|
109
|
-
const scope = [where, include].filter(Boolean).join(" ");
|
|
110
|
-
return `${pattern}${scope ? ` in ${scope}` : ""}`;
|
|
111
|
-
}
|
|
112
|
-
case "glob":
|
|
113
|
-
return str(rec, "pattern");
|
|
114
|
-
case "todo": {
|
|
115
|
-
const todos = rec.todos;
|
|
116
|
-
return Array.isArray(todos) ? `${todos.length} item${todos.length === 1 ? "" : "s"}` : "";
|
|
117
|
-
}
|
|
118
|
-
case "skill":
|
|
119
|
-
return str(rec, "name");
|
|
120
|
-
case "question":
|
|
121
|
-
return str(rec, "question");
|
|
122
|
-
case "task":
|
|
123
|
-
return str(rec, "description");
|
|
124
|
-
case "explore":
|
|
125
|
-
case "codesearch":
|
|
126
|
-
return str(rec, "query");
|
|
127
|
-
default:
|
|
128
|
-
return "";
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
/** Text form of a tool result, for the expanded view. */
|
|
132
|
-
export function toolResultText(output) {
|
|
133
|
-
if (typeof output === "string")
|
|
134
|
-
return output;
|
|
135
|
-
if (output === undefined || output === null)
|
|
136
|
-
return "";
|
|
137
|
-
try {
|
|
138
|
-
return JSON.stringify(output, null, 2);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return String(output);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* One-line result summary. `isError` comes from the tool-error stream event;
|
|
146
|
-
* error strings produced by tools themselves are also recognized.
|
|
147
|
-
*/
|
|
148
|
-
export function summarizeToolResult(toolName, output, isError = false, maxLen = 100) {
|
|
149
|
-
const text = toolResultText(output).trim();
|
|
150
|
-
if (text === "")
|
|
151
|
-
return isError ? "error" : "(empty)";
|
|
152
|
-
const firstLine = oneLine(text.split("\n", 1)[0] ?? "");
|
|
153
|
-
if (isError || /^(error|search error)\b/i.test(firstLine)) {
|
|
154
|
-
return truncateDisplay(firstLine.replace(/^Error:\s*/i, ""), maxLen);
|
|
155
|
-
}
|
|
156
|
-
if (toolName === "search_web") {
|
|
157
|
-
const found = /^Found (\d+) results? for /m.exec(text);
|
|
158
|
-
if (found)
|
|
159
|
-
return `${found[1]} results`;
|
|
160
|
-
if (/^No search results found/m.test(text))
|
|
161
|
-
return "no results";
|
|
162
|
-
const titles = text.match(/^\d+\.\s+Title\s*:/gm);
|
|
163
|
-
if (titles && titles.length > 0)
|
|
164
|
-
return `${titles.length} results`;
|
|
165
|
-
if (/^Search results for /m.test(text))
|
|
166
|
-
return "results";
|
|
167
|
-
}
|
|
168
|
-
if (toolName === "skill") {
|
|
169
|
-
const loaded = /^<skill_content name="([^"]*)"/m.exec(text);
|
|
170
|
-
if (loaded)
|
|
171
|
-
return truncateDisplay(`loaded ${loaded[1]} (${text.split("\n").length} lines)`, maxLen);
|
|
172
|
-
return truncateDisplay(firstLine, maxLen);
|
|
173
|
-
}
|
|
174
|
-
const lineCount = text.split("\n").length;
|
|
175
|
-
if (lineCount === 1)
|
|
176
|
-
return truncateDisplay(firstLine, maxLen);
|
|
177
|
-
return truncateDisplay(`${firstLine} (${lineCount} lines)`, maxLen);
|
|
178
|
-
}
|