config: warn on unknown toolSets typo instead of dropping silently
unknownToolSetNames() + startup notice (stderr headless, NoticeBus interactive) flags gti vs git typos at boot, not as that set off.
This commit is contained in:
1 parent
dcede3c10a
commit
5028ea6973
4 files changed
+24
-3
No files matched your search
@@ -100,8 +100,6 @@ Not bugs exactly, but things that will bite someone.
|
|||||||
injected. `/memory` may merge them, or may keep both.
|
injected. `/memory` may merge them, or may keep both.
|
||||||
- **Windows `cmd /c` differs from `bash -lc`.** A command the model writes for one shell may
|
- **Windows `cmd /c` differs from `bash -lc`.** A command the model writes for one shell may
|
||||||
fail on the other. The prompt states the platform; it does not translate.
|
fail on the other. The prompt states the platform; it does not translate.
|
||||||
- **An unknown name in `toolSets` is dropped silently.** The header line shows which sets
|
|
||||||
actually loaded, but a typo reads as "that set is off" rather than as a mistake.
|
|
||||||
- **Permission rules gate the call, not what it does.** `bash` with `git *` allowed will run a
|
- **Permission rules gate the call, not what it does.** `bash` with `git *` allowed will run a
|
||||||
`git` alias that shells out to anything, and there is no sandbox around the shell. Codex solves
|
`git` alias that shells out to anything, and there is no sandbox around the shell. Codex solves
|
||||||
this with OS-level isolation — Seatbelt, Landlock, a Windows equivalent — which is three
|
this with OS-level isolation — Seatbelt, Landlock, a Windows equivalent — which is three
|
||||||
|
|||||||
+9
-1
@@ -4,7 +4,7 @@ import React from 'react';
|
|||||||
import type { LanguageModel, ModelMessage } from 'ai';
|
import type { LanguageModel, ModelMessage } from 'ai';
|
||||||
import { resolveAgent, VARIANTS, isThinkingLevel, type AgentVariant } from './agents';
|
import { resolveAgent, VARIANTS, isThinkingLevel, type AgentVariant } from './agents';
|
||||||
import { loadExternalPlugins, loadExternalTools } from './autoload';
|
import { loadExternalPlugins, loadExternalTools } from './autoload';
|
||||||
import { configPath, loadConfig, missingKeyMessage, resolveModel, writeConfigFile, type Config } from './config';
|
import { configPath, loadConfig, missingKeyMessage, readConfigFile, resolveModel, unknownToolSetNames, writeConfigFile, type Config } from './config';
|
||||||
import type { FallbackEvent } from './fallback';
|
import type { FallbackEvent } from './fallback';
|
||||||
import { farewell } from './farewell';
|
import { farewell } from './farewell';
|
||||||
import { readStdin, runHeadless } from './headless';
|
import { readStdin, runHeadless } from './headless';
|
||||||
@@ -107,6 +107,8 @@ if (modelFlag) process.env['SHIRO_MODEL'] = modelFlag;
|
|||||||
if (baseUrlFlag) process.env['SHIRO_BASE_URL'] = baseUrlFlag;
|
if (baseUrlFlag) process.env['SHIRO_BASE_URL'] = baseUrlFlag;
|
||||||
|
|
||||||
let cfg = await loadConfig();
|
let cfg = await loadConfig();
|
||||||
|
const rawToolSets = (await readConfigFile()).toolSets as unknown;
|
||||||
|
const unknownToolSets = unknownToolSetNames(rawToolSets);
|
||||||
const yolo = has('--yolo');
|
const yolo = has('--yolo');
|
||||||
const headless = flag('-p', '--print') !== undefined;
|
const headless = flag('-p', '--print') !== undefined;
|
||||||
|
|
||||||
@@ -121,6 +123,12 @@ const notices = createNoticeBus();
|
|||||||
const subagents = createSubagentBus();
|
const subagents = createSubagentBus();
|
||||||
const askBridge = createAskBridge();
|
const askBridge = createAskBridge();
|
||||||
|
|
||||||
|
if (unknownToolSets.length > 0) {
|
||||||
|
const msg = `unknown toolSets ignored: ${unknownToolSets.join(', ')} — valid: core, edit-plus, nav, extra, git, net`;
|
||||||
|
if (headless) process.stderr.write(`shiro: ${msg}\n`);
|
||||||
|
else notices.emit(msg);
|
||||||
|
}
|
||||||
|
|
||||||
function reportFallback(e: FallbackEvent): void {
|
function reportFallback(e: FallbackEvent): void {
|
||||||
const line = `endpoint fallback: ${e.from} rejected the request, retrying on ${e.to}\n ${e.reason}`;
|
const line = `endpoint fallback: ${e.from} rejected the request, retrying on ${e.to}\n ${e.reason}`;
|
||||||
if (headless) process.stderr.write(`shiro: ${line}\n`);
|
if (headless) process.stderr.write(`shiro: ${line}\n`);
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ export async function writeConfigFile(patch: Partial<Config>): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** File config, then env overrides. Env wins so `SHIRO_MODEL=x shiro` works. */
|
/** File config, then env overrides. Env wins so `SHIRO_MODEL=x shiro` works. */
|
||||||
|
export function unknownToolSetNames(raw: unknown): string[] {
|
||||||
|
if (!Array.isArray(raw)) return [];
|
||||||
|
return (raw as unknown[]).filter((v): v is string => typeof v === 'string' && !isToolSetName(v));
|
||||||
|
}
|
||||||
|
|
||||||
export async function loadConfig(): Promise<Config> {
|
export async function loadConfig(): Promise<Config> {
|
||||||
const file = await readConfigFile();
|
const file = await readConfigFile();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { expect, test } from 'bun:test';
|
||||||
|
import { unknownToolSetNames } from '../src/config';
|
||||||
|
|
||||||
|
test('unknownToolSetNames flags typos and ignores valid ones', () => {
|
||||||
|
expect(unknownToolSetNames(['edit-plus', 'gti', 'net', 'extra'])).toEqual(['gti']);
|
||||||
|
expect(unknownToolSetNames(['core', 'git'])).toEqual([]);
|
||||||
|
expect(unknownToolSetNames(undefined)).toEqual([]);
|
||||||
|
expect(unknownToolSetNames([])).toEqual([]);
|
||||||
|
expect(unknownToolSetNames(null)).toEqual([]);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user