diff options
| author | Adam Malczewski <[email protected]> | 2026-05-22 15:24:13 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-22 15:24:13 +0900 |
| commit | 9ecaabd87c0e51b8a7408dabb0133a9344586859 (patch) | |
| tree | 4b5809ab23948a5e3f558f3aa34c52d5038f4e19 /packages/core/src/config | |
| parent | 8f1dd855f0c4c877bff8d3a4ba193432b268b1c2 (diff) | |
| download | dispatch-9ecaabd87c0e51b8a7408dabb0133a9344586859.tar.gz dispatch-9ecaabd87c0e51b8a7408dabb0133a9344586859.zip | |
feat: agent builder, CWD support, auto-save, UI polish, unavailable tool handling
- Agent Builder: full CRUD with card grid, drag-and-drop model reorder, edit/delete
- Auto-save on edit with 600ms debounce, AbortController for concurrency, fieldset disabled until name entered
- Agent definitions stored as TOML with cwd field, loaded from global/project dirs
- Working directory: per-tab CWD override in Chat Settings, agent default CWD, auto-create on first message
- CWD validation: check-dir endpoint with ~ expansion, real-time validity indicator
- Subagent CWD validated against parent's effective CWD using path.relative
- Unavailable tool calls: caught gracefully, shown as tool call with error badge, model retries
- UI: tab bar border radius, sidebar border removed, chat input ghost style, scroll-to-bottom rectangle
- Skills dir collapse uses CSS rotation, Model Choice renamed to Chat Settings, System Prompt view removed
- Reusable SkillsBrowser/ToolPermissions with external mode for Agent Builder
- ModelSelector: Agent/Manual toggle, agent list, Agent Settings link
- Page router, skills recursive scanning, bin/up gopass removed, docker volume mounts
Diffstat (limited to 'packages/core/src/config')
| -rw-r--r-- | packages/core/src/config/loader.ts | 4 | ||||
| -rw-r--r-- | packages/core/src/config/schema.ts | 25 | ||||
| -rw-r--r-- | packages/core/src/config/watcher.ts | 14 |
3 files changed, 29 insertions, 14 deletions
diff --git a/packages/core/src/config/loader.ts b/packages/core/src/config/loader.ts index 3b4d733..bccbb8f 100644 --- a/packages/core/src/config/loader.ts +++ b/packages/core/src/config/loader.ts @@ -28,7 +28,9 @@ export function loadConfig(dir: string): DispatchConfig { // File doesn't exist — return empty default return DEFAULT_CONFIG; } - console.warn(`dispatch: failed to parse dispatch.toml: ${err instanceof Error ? err.message : String(err)}`); + console.warn( + `dispatch: failed to parse dispatch.toml: ${err instanceof Error ? err.message : String(err)}`, + ); throw err; } diff --git a/packages/core/src/config/schema.ts b/packages/core/src/config/schema.ts index 2cd8d55..ef10b65 100644 --- a/packages/core/src/config/schema.ts +++ b/packages/core/src/config/schema.ts @@ -1,8 +1,4 @@ -import type { - ConfigError, - DispatchConfig, - KeyDefinition, -} from "../types/index.js"; +import type { ConfigError, DispatchConfig, KeyDefinition } from "../types/index.js"; function isRecord(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); @@ -33,19 +29,28 @@ function validatePermissions( const result: Record<string, string | Record<string, string>> = {}; for (const [key, value] of Object.entries(raw)) { if (!isPermissionsValue(value)) { - errors.push({ path: `${path}.${key}`, message: "must be a string or a flat string-keyed object" }); + errors.push({ + path: `${path}.${key}`, + message: "must be a string or a flat string-keyed object", + }); continue; } if (typeof value === "string") { if (!isValidAction(value)) { - errors.push({ path: `${path}.${key}`, message: `invalid action "${value}"; must be "allow", "deny", or "ask"` }); + errors.push({ + path: `${path}.${key}`, + message: `invalid action "${value}"; must be "allow", "deny", or "ask"`, + }); continue; } } else { let hasError = false; for (const [pattern, action] of Object.entries(value)) { if (!isValidAction(action)) { - errors.push({ path: `${path}.${key}.${pattern}`, message: `invalid action "${action}"; must be "allow", "deny", or "ask"` }); + errors.push({ + path: `${path}.${key}.${pattern}`, + message: `invalid action "${action}"; must be "allow", "deny", or "ask"`, + }); hasError = true; } } @@ -80,7 +85,9 @@ function validateKey(raw: unknown, path: string, errors: ConfigError[]): KeyDefi id: raw["id"] as string, provider: raw["provider"] as string, base_url: raw["base_url"] as string, - ...(typeof raw["credentials_file"] === "string" ? { credentials_file: raw["credentials_file"] } as Pick<KeyDefinition, "credentials_file"> : {}), + ...(typeof raw["credentials_file"] === "string" + ? ({ credentials_file: raw["credentials_file"] } as Pick<KeyDefinition, "credentials_file">) + : {}), }; } diff --git a/packages/core/src/config/watcher.ts b/packages/core/src/config/watcher.ts index 42b2f87..70821ed 100644 --- a/packages/core/src/config/watcher.ts +++ b/packages/core/src/config/watcher.ts @@ -1,5 +1,5 @@ -import { watch } from "chokidar"; import { join } from "node:path"; +import { watch } from "chokidar"; import type { DispatchConfig } from "../types/index.js"; import { loadConfig } from "./loader.js"; @@ -26,7 +26,9 @@ export function createConfigWatcher( const config = loadConfig(dir); onChange(config); } catch (err) { - console.warn(`dispatch: retaining last known config due to parse error: ${err instanceof Error ? err.message : String(err)}`); + console.warn( + `dispatch: retaining last known config due to parse error: ${err instanceof Error ? err.message : String(err)}`, + ); } }, 300); }; @@ -36,7 +38,9 @@ export function createConfigWatcher( watcher.on("unlink", handleChange); watcher.on("error", (err) => { - console.warn(`dispatch: config watcher error: ${err instanceof Error ? err.message : String(err)}`); + console.warn( + `dispatch: config watcher error: ${err instanceof Error ? err.message : String(err)}`, + ); }); return { @@ -46,7 +50,9 @@ export function createConfigWatcher( debounceTimer = null; } watcher.close().catch((err) => { - console.warn(`dispatch: error closing config watcher: ${err instanceof Error ? err.message : String(err)}`); + console.warn( + `dispatch: error closing config watcher: ${err instanceof Error ? err.message : String(err)}`, + ); }); }, }; |
