@sayknow-cli/coding-agent 0.6.5 → 0.6.7
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 +29 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +79 -0
- package/dist/types/i18n/messages/en.d.ts +6 -29
- package/dist/types/modes/components/welcome.d.ts +34 -40
- package/dist/types/modes/interactive-mode.d.ts +7 -4
- package/dist/types/modes/types.d.ts +4 -0
- package/dist/types/sdk/broker/broker.d.ts +22 -0
- package/dist/types/sdk/broker/process-guard.d.ts +71 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/auth-storage-discovery.d.ts +15 -0
- package/dist/types/session/auto-fallback.d.ts +27 -0
- package/dist/types/session/fallback-chain-controller.d.ts +5 -0
- package/dist/types/session/response-language.d.ts +25 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +8 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/tools/debug.d.ts +2 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/locate-core.d.ts +97 -0
- package/dist/types/tools/locate.d.ts +38 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/src/cli/setup-cli.ts +7 -4
- package/src/commands/sdk.ts +3 -0
- package/src/commands/setup.ts +4 -1
- package/src/config/keybindings.ts +7 -0
- package/src/config/settings-schema.ts +82 -0
- package/src/decisions/typesafe-backend.ts +38 -4
- package/src/i18n/messages/de.settings.ts +26 -0
- package/src/i18n/messages/de.ts +4 -27
- package/src/i18n/messages/en.ts +6 -29
- package/src/i18n/messages/es.settings.ts +26 -0
- package/src/i18n/messages/es.ts +4 -27
- package/src/i18n/messages/fr.settings.ts +26 -0
- package/src/i18n/messages/fr.ts +4 -27
- package/src/i18n/messages/ja.settings.ts +26 -0
- package/src/i18n/messages/ja.ts +4 -27
- package/src/i18n/messages/ko.settings.ts +25 -0
- package/src/i18n/messages/ko.ts +5 -28
- package/src/i18n/messages/zh.settings.ts +23 -0
- package/src/i18n/messages/zh.ts +4 -27
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/modes/action-registry.ts +1 -0
- package/src/modes/components/welcome.ts +385 -387
- package/src/modes/controllers/input-controller.ts +15 -0
- package/src/modes/controllers/selector-controller.ts +13 -0
- package/src/modes/interactive-mode.ts +160 -98
- package/src/modes/types.ts +4 -0
- package/src/prompts/system/system-prompt.md +7 -2
- package/src/prompts/tools/locate.md +12 -0
- package/src/sdk/broker/broker.ts +75 -1
- package/src/sdk/broker/process-guard.ts +160 -0
- package/src/sdk/broker/transport.ts +15 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +13 -1
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session.ts +4 -1
- package/src/session/agent-session.ts +118 -2
- package/src/session/auth-storage-discovery.ts +22 -7
- package/src/session/auto-fallback.ts +59 -0
- package/src/session/fallback-chain-controller.ts +5 -0
- package/src/session/response-language.ts +71 -0
- package/src/setup/model-onboarding-guidance.ts +29 -14
- package/src/setup/provider-onboarding.ts +5 -0
- package/src/slash-commands/builtin-registry.ts +112 -2
- package/src/tools/index.ts +3 -0
- package/src/tools/locate-core.ts +720 -0
- package/src/tools/locate.ts +197 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which language to answer in, detected from the script of the user's message.
|
|
3
|
+
*
|
|
4
|
+
* Long tool-heavy turns drift into English: tool output, logs, skill text and context
|
|
5
|
+
* summaries are English, and the final report follows them. The system prompt states
|
|
6
|
+
* the rule once; this adds a one-line reminder at the start of each turn, naming the
|
|
7
|
+
* language, when the message is clearly written in a non-Latin script. Latin-script
|
|
8
|
+
* languages (English, Spanish, French, German, …) cannot be told apart by script alone,
|
|
9
|
+
* so they get no reminder and rely on the system prompt rule.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export interface DetectedLanguage {
|
|
13
|
+
/** BCP 47 code. */
|
|
14
|
+
code: string;
|
|
15
|
+
/** English name, as used in the reminder. */
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Scripts that name one language well enough for a reminder. Kana wins over Han: Japanese mixes both. */
|
|
20
|
+
const SCRIPTS: ReadonlyArray<{ pattern: RegExp; language: DetectedLanguage }> = [
|
|
21
|
+
{ pattern: /\p{Script=Hangul}/gu, language: { code: "ko", name: "Korean" } },
|
|
22
|
+
{ pattern: /[\p{Script=Hiragana}\p{Script=Katakana}]/gu, language: { code: "ja", name: "Japanese" } },
|
|
23
|
+
{ pattern: /\p{Script=Han}/gu, language: { code: "zh", name: "Chinese" } },
|
|
24
|
+
{ pattern: /\p{Script=Cyrillic}/gu, language: { code: "ru", name: "Russian" } },
|
|
25
|
+
{ pattern: /\p{Script=Arabic}/gu, language: { code: "ar", name: "Arabic" } },
|
|
26
|
+
{ pattern: /\p{Script=Hebrew}/gu, language: { code: "he", name: "Hebrew" } },
|
|
27
|
+
{ pattern: /\p{Script=Thai}/gu, language: { code: "th", name: "Thai" } },
|
|
28
|
+
{ pattern: /\p{Script=Devanagari}/gu, language: { code: "hi", name: "Hindi" } },
|
|
29
|
+
{ pattern: /\p{Script=Greek}/gu, language: { code: "el", name: "Greek" } },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/** Code, quoted output and links say nothing about the language the user writes in. */
|
|
33
|
+
function proseOnly(text: string): string {
|
|
34
|
+
return text
|
|
35
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
36
|
+
.replace(/`[^`\n]*`/g, " ")
|
|
37
|
+
.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, " ")
|
|
38
|
+
.replace(/https?:\/\/\S+/g, " ")
|
|
39
|
+
.split("\n")
|
|
40
|
+
.filter(line => !/^\s*>/.test(line))
|
|
41
|
+
.join("\n");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The language of `text` when its prose is clearly in a non-Latin script, else
|
|
46
|
+
* undefined. "Clearly": at least two characters of the script, and — since one CJK
|
|
47
|
+
* character carries about a word — at least a quarter as many as Latin letters, so a
|
|
48
|
+
* pasted English log with one Korean word does not count.
|
|
49
|
+
*/
|
|
50
|
+
export function detectResponseLanguage(text: string): DetectedLanguage | undefined {
|
|
51
|
+
const prose = proseOnly(text);
|
|
52
|
+
const latin = prose.match(/\p{Script=Latin}/gu)?.length ?? 0;
|
|
53
|
+
const kana = prose.match(SCRIPTS[1]!.pattern)?.length ?? 0;
|
|
54
|
+
for (const { pattern, language } of SCRIPTS) {
|
|
55
|
+
let count = prose.match(pattern)?.length ?? 0;
|
|
56
|
+
if (language.code === "zh" && kana > 0) continue;
|
|
57
|
+
// Japanese needs kana to be told from Chinese; with it, its kanji count too.
|
|
58
|
+
if (language.code === "ja" && kana > 0) count += prose.match(/\p{Script=Han}/gu)?.length ?? 0;
|
|
59
|
+
if (count >= 2 && count * 4 >= latin) return language;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The per-turn reminder for a detected language. */
|
|
65
|
+
export function buildResponseLanguageReminder(language: DetectedLanguage): string {
|
|
66
|
+
return [
|
|
67
|
+
"<system-reminder>",
|
|
68
|
+
`The user wrote in ${language.name}. Write every user-facing message this turn in ${language.name} — progress notes and the final report after tool work included — unless they asked for another language. Keep code, commands, paths and identifiers as they are.`,
|
|
69
|
+
"</system-reminder>",
|
|
70
|
+
].join("\n");
|
|
71
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { formatProviderCredentialHint } from "@sayknow-cli/ai/stream";
|
|
2
|
+
import { getOAuthProviders } from "@sayknow-cli/ai/utils/oauth";
|
|
3
|
+
import { formatProviderPresetChoices } from "./provider-onboarding";
|
|
2
4
|
|
|
3
5
|
export const MODEL_ONBOARDING_API_PROVIDER_COMMAND =
|
|
4
6
|
"/provider add --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <model>";
|
|
5
|
-
|
|
7
|
+
/** Built from the bundled preset catalog, so a new preset shows up here without editing copy. */
|
|
8
|
+
export const MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND = `/provider add --preset <${formatProviderPresetChoices()}>`;
|
|
6
9
|
|
|
7
10
|
export const MODEL_ONBOARDING_SETUP_COMMAND = "skc setup provider";
|
|
8
11
|
export const MODEL_ONBOARDING_OAUTH_COMMAND = "/provider login [provider-id] or /login [provider-id]";
|
|
@@ -11,40 +14,52 @@ export const MODEL_ONBOARDING_OAUTH_COMMAND = "/provider login [provider-id] or
|
|
|
11
14
|
* user learns it exists is from the surfaces where they go to add credentials.
|
|
12
15
|
*/
|
|
13
16
|
export const MODEL_ONBOARDING_TYPESAFE_COMMAND = "/provider typesafe";
|
|
17
|
+
/** Where the models tried after a blocked one are shown and edited. */
|
|
18
|
+
export const MODEL_ONBOARDING_FALLBACK_COMMAND = "/fallback";
|
|
14
19
|
|
|
15
20
|
export function formatModelOnboardingGuidance(): string {
|
|
16
21
|
return [
|
|
17
22
|
"Model selection only shows configured providers.",
|
|
18
23
|
"Assignment targets are DEFAULT plus the SKC role agents: EXECUTOR, ARCHITECT, PLANNER, and CRITIC.",
|
|
19
24
|
"Legacy model-role aliases are compatibility-only and are not shown as assignment targets.",
|
|
25
|
+
`OAuth/subscription providers: ${MODEL_ONBOARDING_OAUTH_COMMAND}.`,
|
|
20
26
|
`Provider presets: ${MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND} (or ${MODEL_ONBOARDING_SETUP_COMMAND} --preset <preset>).`,
|
|
21
27
|
`API-compatible custom providers: ${MODEL_ONBOARDING_API_PROVIDER_COMMAND}.`,
|
|
22
|
-
`OAuth/subscription providers: ${MODEL_ONBOARDING_OAUTH_COMMAND}.`,
|
|
23
28
|
`Typed decisions (not a chat model): ${MODEL_ONBOARDING_TYPESAFE_COMMAND} adds a TypeSafe key.`,
|
|
24
|
-
|
|
29
|
+
`Then run /model to select a configured model or assign it to a target, and ${MODEL_ONBOARDING_FALLBACK_COMMAND} to see what runs when it is blocked.`,
|
|
25
30
|
].join("\n");
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
export function formatModelOnboardingInlineHint(): string {
|
|
29
|
-
return `
|
|
34
|
+
return `Log in with ${MODEL_ONBOARDING_OAUTH_COMMAND}; presets with ${MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND}; custom API providers with ${MODEL_ONBOARDING_API_PROVIDER_COMMAND} (or ${MODEL_ONBOARDING_SETUP_COMMAND}); TypeSafe typed decisions with ${MODEL_ONBOARDING_TYPESAFE_COMMAND}; then run /model for DEFAULT, EXECUTOR, ARCHITECT, PLANNER, and CRITIC, and ${MODEL_ONBOARDING_FALLBACK_COMMAND} for what runs when a model is blocked.`;
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
export function formatNoModelOnboardingError(): string {
|
|
33
38
|
return `No model selected.\n\n${formatModelOnboardingGuidance()}`;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Missing credentials for the provider the session tried to call. The fix for that
|
|
43
|
+
* provider comes first; the ways to add a different provider follow as alternatives.
|
|
44
|
+
*/
|
|
36
45
|
export function formatNoCredentialOnboardingError(providerId: string): string {
|
|
37
|
-
const lines = [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
const lines = [`No credentials found for ${providerId}.`, ""];
|
|
47
|
+
const canLogIn = getOAuthProviders().some(provider => provider.id === providerId);
|
|
48
|
+
const credentialHint = formatProviderCredentialHint(providerId);
|
|
49
|
+
if (canLogIn) {
|
|
50
|
+
lines.push(`Log in to ${providerId}: /login ${providerId} (interactive; not available in headless/print mode).`);
|
|
51
|
+
}
|
|
52
|
+
if (credentialHint) lines.push(credentialHint);
|
|
53
|
+
if (!canLogIn && !credentialHint) {
|
|
54
|
+
lines.push(`Add an API key for ${providerId} (the env var or apiKey its models.yml entry names).`);
|
|
55
|
+
}
|
|
46
56
|
lines.push(
|
|
47
|
-
"
|
|
57
|
+
"",
|
|
58
|
+
"Or use another provider:",
|
|
59
|
+
`- OAuth/subscription: ${MODEL_ONBOARDING_OAUTH_COMMAND} (interactive; not available in headless/print mode).`,
|
|
60
|
+
`- Presets: ${MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND} (or ${MODEL_ONBOARDING_SETUP_COMMAND} --preset <preset>).`,
|
|
61
|
+
`- API-compatible: ${MODEL_ONBOARDING_API_PROVIDER_COMMAND}.`,
|
|
62
|
+
`Then run /model to select a configured model or assign it to DEFAULT, EXECUTOR, ARCHITECT, PLANNER, or CRITIC. With another provider logged in, a blocked model falls back automatically (${MODEL_ONBOARDING_FALLBACK_COMMAND}).`,
|
|
48
63
|
);
|
|
49
64
|
return lines.join("\n");
|
|
50
65
|
}
|
|
@@ -103,6 +103,11 @@ export function formatProviderPresetList(): string {
|
|
|
103
103
|
.join("\n");
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
/** `minimax|minimax-cn|…` — the preset ids for a `--preset <…>` usage line, in catalog order. */
|
|
107
|
+
export function formatProviderPresetChoices(): string {
|
|
108
|
+
return PROVIDER_PRESETS.map(preset => preset.id).join("|");
|
|
109
|
+
}
|
|
110
|
+
|
|
106
111
|
export function parseModelList(values: readonly string[]): string[] {
|
|
107
112
|
const models = values
|
|
108
113
|
.flatMap(value => value.split(","))
|
|
@@ -45,6 +45,7 @@ import { formatSessionImportSummary, runSessionImportCommand } from "../session-
|
|
|
45
45
|
import { formatModelOnboardingGuidance } from "../setup/model-onboarding-guidance";
|
|
46
46
|
import {
|
|
47
47
|
addApiCompatibleProvider,
|
|
48
|
+
formatProviderPresetChoices,
|
|
48
49
|
formatProviderPresetList,
|
|
49
50
|
formatProviderSetupResult,
|
|
50
51
|
parseProviderCompatibility,
|
|
@@ -207,6 +208,102 @@ async function updateSessionStar(
|
|
|
207
208
|
return commandConsumed();
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
const FALLBACK_USAGE =
|
|
212
|
+
"Usage: /fallback [add <model> | remove <number|model> | clear | auto on|off]\n" +
|
|
213
|
+
"Models listed here run, in order, after the default model when it is blocked (no login, quota, auth or server errors).";
|
|
214
|
+
|
|
215
|
+
/** Show or edit what runs when the default model is blocked. */
|
|
216
|
+
async function handleFallbackCommand(
|
|
217
|
+
command: ParsedSlashCommand,
|
|
218
|
+
runtime: SlashCommandRuntime,
|
|
219
|
+
): Promise<SlashCommandResult> {
|
|
220
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
221
|
+
const added = runtime.settings.get("fallback.models");
|
|
222
|
+
const save = async (models: string[], message: string): Promise<SlashCommandResult> => {
|
|
223
|
+
runtime.settings.set("fallback.models", models);
|
|
224
|
+
await runtime.notifyConfigChanged?.();
|
|
225
|
+
await runtime.output(`${message}\n\n${formatFallbackChain(runtime)}`);
|
|
226
|
+
return commandConsumed();
|
|
227
|
+
};
|
|
228
|
+
switch (verb) {
|
|
229
|
+
case "":
|
|
230
|
+
case "show":
|
|
231
|
+
case "list":
|
|
232
|
+
await runtime.output(formatFallbackChain(runtime));
|
|
233
|
+
return commandConsumed();
|
|
234
|
+
case "add": {
|
|
235
|
+
if (!rest) return usage(FALLBACK_USAGE, runtime);
|
|
236
|
+
const resolution = await resolveModelCommandSelection(runtime, rest);
|
|
237
|
+
if (!resolution.ok) return usage(resolution.failure.message, runtime);
|
|
238
|
+
const { model, thinkingLevel } = resolution.selection;
|
|
239
|
+
const selector = formatModelSelectorValue(`${model.provider}/${model.id}`, thinkingLevel);
|
|
240
|
+
if (added.includes(selector)) {
|
|
241
|
+
await runtime.output(`${selector} is already a fallback.\n\n${formatFallbackChain(runtime)}`);
|
|
242
|
+
return commandConsumed();
|
|
243
|
+
}
|
|
244
|
+
return save([...added, selector], `Added fallback: ${selector}`);
|
|
245
|
+
}
|
|
246
|
+
case "remove":
|
|
247
|
+
case "rm": {
|
|
248
|
+
const index = /^\d+$/.test(rest) ? Number(rest) - 1 : added.indexOf(rest);
|
|
249
|
+
const target = added[index];
|
|
250
|
+
if (!rest || target === undefined) {
|
|
251
|
+
return usage(
|
|
252
|
+
added.length === 0
|
|
253
|
+
? "No fallback models are set. Add one with /fallback add <model>."
|
|
254
|
+
: `Remove by number (1-${added.length}) or exact selector: ${added.join(", ")}`,
|
|
255
|
+
runtime,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return save(
|
|
259
|
+
added.filter((_, position) => position !== index),
|
|
260
|
+
`Removed fallback: ${target}`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
case "clear":
|
|
264
|
+
return save([], "Cleared fallback models.");
|
|
265
|
+
case "auto": {
|
|
266
|
+
const value = rest.toLowerCase();
|
|
267
|
+
if (value !== "on" && value !== "off") return usage("Usage: /fallback auto on|off", runtime);
|
|
268
|
+
runtime.settings.set("fallback.auto", value === "on");
|
|
269
|
+
await runtime.notifyConfigChanged?.();
|
|
270
|
+
await runtime.output(`Automatic fallback ${value}.\n\n${formatFallbackChain(runtime)}`);
|
|
271
|
+
return commandConsumed();
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
return usage(FALLBACK_USAGE, runtime);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function formatFallbackChain(runtime: SlashCommandRuntime): string {
|
|
279
|
+
const auto = runtime.settings.get("fallback.auto");
|
|
280
|
+
const added = new Set(runtime.settings.get("fallback.models"));
|
|
281
|
+
const { entries, activeIndex, appendedFrom } = runtime.session.getDefaultFallbackChain();
|
|
282
|
+
const width = Math.max(0, ...entries.map(entry => entry.length));
|
|
283
|
+
const lines = [`Model fallback (automatic: ${auto ? "on" : "off"})`];
|
|
284
|
+
if (entries.length === 0) lines.push(" (no default model)");
|
|
285
|
+
entries.forEach((entry, index) => {
|
|
286
|
+
const source = index < appendedFrom ? (index === 0 ? "default" : "chain") : added.has(entry) ? "added" : "auto";
|
|
287
|
+
const inUse = index === activeIndex ? " ← in use" : "";
|
|
288
|
+
lines.push(` ${index + 1}. ${entry.padEnd(width)} ${source}${inUse}`);
|
|
289
|
+
});
|
|
290
|
+
if (entries.length === 1) {
|
|
291
|
+
lines.push(
|
|
292
|
+
auto
|
|
293
|
+
? " Nothing to fall back to yet: log in to another provider (/login) or add one with /fallback add <model>."
|
|
294
|
+
: " Nothing to fall back to: add a model with /fallback add <model> or turn on /fallback auto on.",
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (auto && runtime.settings.get("enabledModels").length > 0) {
|
|
298
|
+
lines.push(" enabledModels restricts this session, so automatic picks are off; added models still apply.");
|
|
299
|
+
}
|
|
300
|
+
lines.push(
|
|
301
|
+
`Each model gets ${runtime.settings.get("fallback.maxAttempts")} tries; a blocked or exhausted account first hands over to the provider's next logged-in account (order: ${runtime.session.modelRegistry.authStorage.getCredentialRankingMode()}).`,
|
|
302
|
+
"/fallback add <model> · remove <n> · clear · auto on|off",
|
|
303
|
+
);
|
|
304
|
+
return lines.join("\n");
|
|
305
|
+
}
|
|
306
|
+
|
|
210
307
|
function parseProviderSetupSlashArgs(args: string): {
|
|
211
308
|
preset?: string;
|
|
212
309
|
compat?: string;
|
|
@@ -273,8 +370,7 @@ function parseProviderSetupSlashArgs(args: string): {
|
|
|
273
370
|
function providerSetupUsage(): string {
|
|
274
371
|
return [
|
|
275
372
|
"Provider onboarding",
|
|
276
|
-
|
|
277
|
-
"Aliases: /provider add minimax, /provider add minimax-cn, /provider add glm, /provider add zai (writes glm-proxy)",
|
|
373
|
+
`Presets: /provider add --preset <${formatProviderPresetChoices()}> [--force]`,
|
|
278
374
|
"API providers: /provider add --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <model> [--force]",
|
|
279
375
|
`Available presets:\n${formatProviderPresetList()}`,
|
|
280
376
|
"OAuth/subscription providers: /provider login [provider-id] or /login [provider-id]",
|
|
@@ -931,6 +1027,20 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
931
1027
|
runtime.ctx.editor.setText("");
|
|
932
1028
|
},
|
|
933
1029
|
},
|
|
1030
|
+
{
|
|
1031
|
+
name: "fallback",
|
|
1032
|
+
description: "Show or edit the models tried when the default model is blocked",
|
|
1033
|
+
inlineHint: "[add <model> | remove <n> | clear | auto on|off]",
|
|
1034
|
+
acpInputHint: "[add <model> | remove <n> | clear | auto on|off]",
|
|
1035
|
+
subcommands: [
|
|
1036
|
+
{ name: "add", description: "Add a fallback model after the default chain" },
|
|
1037
|
+
{ name: "remove", description: "Remove an added fallback by number or selector" },
|
|
1038
|
+
{ name: "clear", description: "Remove every added fallback" },
|
|
1039
|
+
{ name: "auto", description: "Turn automatic fallback to other logged-in providers on or off" },
|
|
1040
|
+
],
|
|
1041
|
+
allowArgs: true,
|
|
1042
|
+
handle: handleFallbackCommand,
|
|
1043
|
+
},
|
|
934
1044
|
{
|
|
935
1045
|
name: "effort",
|
|
936
1046
|
description: "Show or set model reasoning effort",
|
package/src/tools/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { FindTool } from "./find";
|
|
|
47
47
|
import { GithubTool } from "./gh";
|
|
48
48
|
import { IrcTool } from "./irc";
|
|
49
49
|
import { JobTool } from "./job";
|
|
50
|
+
import { LocateTool } from "./locate";
|
|
50
51
|
import { MonitorTool } from "./monitor";
|
|
51
52
|
import { wrapToolWithMetaNotice } from "./output-meta";
|
|
52
53
|
import { ReadTool } from "./read";
|
|
@@ -88,6 +89,7 @@ export * from "./gh";
|
|
|
88
89
|
export * from "./image-gen";
|
|
89
90
|
export * from "./irc";
|
|
90
91
|
export * from "./job";
|
|
92
|
+
export * from "./locate";
|
|
91
93
|
export * from "./monitor";
|
|
92
94
|
export * from "./read";
|
|
93
95
|
export * from "./recipe";
|
|
@@ -473,6 +475,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
473
475
|
github: GithubTool.createIf,
|
|
474
476
|
find: s => new FindTool(s),
|
|
475
477
|
search: s => new SearchTool(s),
|
|
478
|
+
locate: LocateTool.createIf,
|
|
476
479
|
lsp: LspTool.createIf,
|
|
477
480
|
browser: s => new BrowserTool(s),
|
|
478
481
|
...(isComputerLoadablePlatform() ? { computer: ComputerTool.createIf } : {}),
|