zoe-agent 0.3.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/CHANGELOG.md +154 -0
- package/LICENSE +96 -0
- package/README.md +568 -0
- package/dist/adapters/cli/agent.d.ts +59 -0
- package/dist/adapters/cli/agent.js +232 -0
- package/dist/adapters/cli/bootstrap.d.ts +25 -0
- package/dist/adapters/cli/bootstrap.js +204 -0
- package/dist/adapters/cli/commands/build-registry.d.ts +14 -0
- package/dist/adapters/cli/commands/build-registry.js +88 -0
- package/dist/adapters/cli/commands/clear.d.ts +7 -0
- package/dist/adapters/cli/commands/clear.js +10 -0
- package/dist/adapters/cli/commands/compact.d.ts +13 -0
- package/dist/adapters/cli/commands/compact.js +96 -0
- package/dist/adapters/cli/commands/exit.d.ts +7 -0
- package/dist/adapters/cli/commands/exit.js +9 -0
- package/dist/adapters/cli/commands/gateway.d.ts +7 -0
- package/dist/adapters/cli/commands/gateway.js +152 -0
- package/dist/adapters/cli/commands/help.d.ts +9 -0
- package/dist/adapters/cli/commands/help.js +12 -0
- package/dist/adapters/cli/commands/models.d.ts +10 -0
- package/dist/adapters/cli/commands/models.js +32 -0
- package/dist/adapters/cli/commands/registry.d.ts +70 -0
- package/dist/adapters/cli/commands/registry.js +111 -0
- package/dist/adapters/cli/commands/settings-utils.d.ts +38 -0
- package/dist/adapters/cli/commands/settings-utils.js +182 -0
- package/dist/adapters/cli/commands/settings.d.ts +9 -0
- package/dist/adapters/cli/commands/settings.js +395 -0
- package/dist/adapters/cli/commands/skills.d.ts +7 -0
- package/dist/adapters/cli/commands/skills.js +21 -0
- package/dist/adapters/cli/config-loader.d.ts +27 -0
- package/dist/adapters/cli/config-loader.js +48 -0
- package/dist/adapters/cli/docker-utils.d.ts +37 -0
- package/dist/adapters/cli/docker-utils.js +90 -0
- package/dist/adapters/cli/index.d.ts +2 -0
- package/dist/adapters/cli/index.js +88 -0
- package/dist/adapters/cli/repl.d.ts +22 -0
- package/dist/adapters/cli/repl.js +256 -0
- package/dist/adapters/cli/setup.d.ts +19 -0
- package/dist/adapters/cli/setup.js +613 -0
- package/dist/adapters/cli/system-prompts.d.ts +56 -0
- package/dist/adapters/cli/system-prompts.js +131 -0
- package/dist/adapters/cli/tui/app.d.ts +58 -0
- package/dist/adapters/cli/tui/app.js +314 -0
- package/dist/adapters/cli/tui/components/assistant-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/assistant-message.js +9 -0
- package/dist/adapters/cli/tui/components/autocomplete.d.ts +19 -0
- package/dist/adapters/cli/tui/components/autocomplete.js +75 -0
- package/dist/adapters/cli/tui/components/command-palette.d.ts +15 -0
- package/dist/adapters/cli/tui/components/command-palette.js +50 -0
- package/dist/adapters/cli/tui/components/diff-viewer.d.ts +5 -0
- package/dist/adapters/cli/tui/components/diff-viewer.js +109 -0
- package/dist/adapters/cli/tui/components/error-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/error-message.js +8 -0
- package/dist/adapters/cli/tui/components/footer.d.ts +20 -0
- package/dist/adapters/cli/tui/components/footer.js +19 -0
- package/dist/adapters/cli/tui/components/goal-status.d.ts +12 -0
- package/dist/adapters/cli/tui/components/goal-status.js +22 -0
- package/dist/adapters/cli/tui/components/info-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/info-message.js +8 -0
- package/dist/adapters/cli/tui/components/logo-banner.d.ts +7 -0
- package/dist/adapters/cli/tui/components/logo-banner.js +33 -0
- package/dist/adapters/cli/tui/components/markdown.d.ts +9 -0
- package/dist/adapters/cli/tui/components/markdown.js +92 -0
- package/dist/adapters/cli/tui/components/message-area.d.ts +19 -0
- package/dist/adapters/cli/tui/components/message-area.js +55 -0
- package/dist/adapters/cli/tui/components/permission-prompt.d.ts +13 -0
- package/dist/adapters/cli/tui/components/permission-prompt.js +32 -0
- package/dist/adapters/cli/tui/components/prompt-area.d.ts +22 -0
- package/dist/adapters/cli/tui/components/prompt-area.js +68 -0
- package/dist/adapters/cli/tui/components/text-input.d.ts +27 -0
- package/dist/adapters/cli/tui/components/text-input.js +142 -0
- package/dist/adapters/cli/tui/components/tool-call-block.d.ts +11 -0
- package/dist/adapters/cli/tui/components/tool-call-block.js +68 -0
- package/dist/adapters/cli/tui/components/user-message.d.ts +5 -0
- package/dist/adapters/cli/tui/components/user-message.js +8 -0
- package/dist/adapters/cli/tui/diff/file-write-meta.d.ts +11 -0
- package/dist/adapters/cli/tui/diff/file-write-meta.js +11 -0
- package/dist/adapters/cli/tui/diff/line-diff.d.ts +17 -0
- package/dist/adapters/cli/tui/diff/line-diff.js +44 -0
- package/dist/adapters/cli/tui/feed-serializer.d.ts +29 -0
- package/dist/adapters/cli/tui/feed-serializer.js +70 -0
- package/dist/adapters/cli/tui/file-index.d.ts +8 -0
- package/dist/adapters/cli/tui/file-index.js +41 -0
- package/dist/adapters/cli/tui/hooks/use-agent.d.ts +54 -0
- package/dist/adapters/cli/tui/hooks/use-agent.js +177 -0
- package/dist/adapters/cli/tui/hooks/use-feed.d.ts +16 -0
- package/dist/adapters/cli/tui/hooks/use-feed.js +25 -0
- package/dist/adapters/cli/tui/hooks/use-file-watcher.d.ts +10 -0
- package/dist/adapters/cli/tui/hooks/use-file-watcher.js +43 -0
- package/dist/adapters/cli/tui/hooks/use-keybindings.d.ts +16 -0
- package/dist/adapters/cli/tui/hooks/use-keybindings.js +25 -0
- package/dist/adapters/cli/tui/hooks/use-theme.d.ts +8 -0
- package/dist/adapters/cli/tui/hooks/use-theme.js +12 -0
- package/dist/adapters/cli/tui/index.d.ts +19 -0
- package/dist/adapters/cli/tui/index.js +206 -0
- package/dist/adapters/cli/tui/ink-reset.d.ts +29 -0
- package/dist/adapters/cli/tui/ink-reset.js +57 -0
- package/dist/adapters/cli/tui/layout.d.ts +15 -0
- package/dist/adapters/cli/tui/layout.js +15 -0
- package/dist/adapters/cli/tui/logo/gradient.d.ts +11 -0
- package/dist/adapters/cli/tui/logo/gradient.js +31 -0
- package/dist/adapters/cli/tui/overlays/help-dialog.d.ts +4 -0
- package/dist/adapters/cli/tui/overlays/help-dialog.js +26 -0
- package/dist/adapters/cli/tui/overlays/model-selector.d.ts +14 -0
- package/dist/adapters/cli/tui/overlays/model-selector.js +43 -0
- package/dist/adapters/cli/tui/overlays/session-selector.d.ts +35 -0
- package/dist/adapters/cli/tui/overlays/session-selector.js +162 -0
- package/dist/adapters/cli/tui/overlays/settings-overlay.d.ts +24 -0
- package/dist/adapters/cli/tui/overlays/settings-overlay.js +126 -0
- package/dist/adapters/cli/tui/session-export.d.ts +21 -0
- package/dist/adapters/cli/tui/session-export.js +63 -0
- package/dist/adapters/cli/tui/theme.d.ts +23 -0
- package/dist/adapters/cli/tui/theme.js +22 -0
- package/dist/adapters/cli/tui/types.d.ts +52 -0
- package/dist/adapters/cli/tui/types.js +12 -0
- package/dist/adapters/sdk/agent.d.ts +20 -0
- package/dist/adapters/sdk/agent.js +356 -0
- package/dist/adapters/sdk/http.d.ts +43 -0
- package/dist/adapters/sdk/http.js +61 -0
- package/dist/adapters/sdk/index.d.ts +58 -0
- package/dist/adapters/sdk/index.js +209 -0
- package/dist/adapters/sdk/settings.d.ts +18 -0
- package/dist/adapters/sdk/settings.js +57 -0
- package/dist/adapters/sdk/tools.d.ts +7 -0
- package/dist/adapters/sdk/tools.js +13 -0
- package/dist/adapters/server/auth.d.ts +53 -0
- package/dist/adapters/server/auth.js +168 -0
- package/dist/adapters/server/index.d.ts +40 -0
- package/dist/adapters/server/index.js +255 -0
- package/dist/adapters/server/rest-gateway.d.ts +13 -0
- package/dist/adapters/server/rest-gateway.js +218 -0
- package/dist/adapters/server/rest.d.ts +37 -0
- package/dist/adapters/server/rest.js +341 -0
- package/dist/adapters/server/server-core.d.ts +55 -0
- package/dist/adapters/server/server-core.js +121 -0
- package/dist/adapters/server/session-store.d.ts +81 -0
- package/dist/adapters/server/session-store.js +272 -0
- package/dist/adapters/server/settings-handlers.d.ts +24 -0
- package/dist/adapters/server/settings-handlers.js +360 -0
- package/dist/adapters/server/standalone.d.ts +19 -0
- package/dist/adapters/server/standalone.js +113 -0
- package/dist/adapters/server/websocket.d.ts +26 -0
- package/dist/adapters/server/websocket.js +68 -0
- package/dist/adapters/server/ws-handlers.d.ts +32 -0
- package/dist/adapters/server/ws-handlers.js +523 -0
- package/dist/adapters/server/ws-types.d.ts +304 -0
- package/dist/adapters/server/ws-types.js +7 -0
- package/dist/core/agent-loop.d.ts +68 -0
- package/dist/core/agent-loop.js +423 -0
- package/dist/core/config.d.ts +115 -0
- package/dist/core/config.js +189 -0
- package/dist/core/errors.d.ts +58 -0
- package/dist/core/errors.js +88 -0
- package/dist/core/hooks.d.ts +35 -0
- package/dist/core/hooks.js +49 -0
- package/dist/core/index.d.ts +23 -0
- package/dist/core/index.js +29 -0
- package/dist/core/message-convert.d.ts +41 -0
- package/dist/core/message-convert.js +94 -0
- package/dist/core/middleware/auth.d.ts +24 -0
- package/dist/core/middleware/auth.js +28 -0
- package/dist/core/middleware/logging.d.ts +23 -0
- package/dist/core/middleware/logging.js +28 -0
- package/dist/core/middleware/rate-limit.d.ts +27 -0
- package/dist/core/middleware/rate-limit.js +38 -0
- package/dist/core/middleware/semantic-tools.d.ts +10 -0
- package/dist/core/middleware/semantic-tools.js +43 -0
- package/dist/core/middleware.d.ts +48 -0
- package/dist/core/middleware.js +38 -0
- package/dist/core/permission.d.ts +25 -0
- package/dist/core/permission.js +50 -0
- package/dist/core/provider-config.d.ts +129 -0
- package/dist/core/provider-config.js +273 -0
- package/dist/core/provider-env.d.ts +39 -0
- package/dist/core/provider-env.js +142 -0
- package/dist/core/provider-resolver.d.ts +12 -0
- package/dist/core/provider-resolver.js +12 -0
- package/dist/core/session-store.d.ts +75 -0
- package/dist/core/session-store.js +245 -0
- package/dist/core/settings-manager.d.ts +57 -0
- package/dist/core/settings-manager.js +359 -0
- package/dist/core/settings-schema.d.ts +38 -0
- package/dist/core/settings-schema.js +171 -0
- package/dist/core/skill-catalog.d.ts +6 -0
- package/dist/core/skill-catalog.js +17 -0
- package/dist/core/skill-invoker.d.ts +127 -0
- package/dist/core/skill-invoker.js +182 -0
- package/dist/core/stream-accumulator.d.ts +21 -0
- package/dist/core/stream-accumulator.js +51 -0
- package/dist/core/stream-manager.d.ts +58 -0
- package/dist/core/stream-manager.js +212 -0
- package/dist/core/tool-executor.d.ts +84 -0
- package/dist/core/tool-executor.js +256 -0
- package/dist/core/types.d.ts +259 -0
- package/dist/core/types.js +11 -0
- package/dist/gateway/gateway.d.ts +52 -0
- package/dist/gateway/gateway.js +537 -0
- package/dist/gateway/index.d.ts +21 -0
- package/dist/gateway/index.js +31 -0
- package/dist/gateway/openapi-importer.d.ts +15 -0
- package/dist/gateway/openapi-importer.js +66 -0
- package/dist/gateway/semantic-scorer.d.ts +7 -0
- package/dist/gateway/semantic-scorer.js +24 -0
- package/dist/gateway/settings-adapter.d.ts +49 -0
- package/dist/gateway/settings-adapter.js +137 -0
- package/dist/gateway/tool-factory.d.ts +9 -0
- package/dist/gateway/tool-factory.js +414 -0
- package/dist/gateway/types.d.ts +68 -0
- package/dist/gateway/types.js +7 -0
- package/dist/models-catalog.js +46 -0
- package/dist/providers/anthropic.d.ts +22 -0
- package/dist/providers/anthropic.js +148 -0
- package/dist/providers/factory.d.ts +10 -0
- package/dist/providers/factory.js +25 -0
- package/dist/providers/openai.d.ts +15 -0
- package/dist/providers/openai.js +71 -0
- package/dist/providers/types.d.ts +48 -0
- package/dist/providers/types.js +1 -0
- package/dist/skills/args.d.ts +37 -0
- package/dist/skills/args.js +99 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.js +23 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/skills/loader.js +59 -0
- package/dist/skills/parser.d.ts +7 -0
- package/dist/skills/parser.js +152 -0
- package/dist/skills/registry.d.ts +13 -0
- package/dist/skills/registry.js +74 -0
- package/dist/skills/resolver.d.ts +19 -0
- package/dist/skills/resolver.js +116 -0
- package/dist/skills/types.d.ts +74 -0
- package/dist/skills/types.js +50 -0
- package/dist/tools/browser.d.ts +2 -0
- package/dist/tools/browser.js +68 -0
- package/dist/tools/core.d.ts +20 -0
- package/dist/tools/core.js +244 -0
- package/dist/tools/email.d.ts +2 -0
- package/dist/tools/email.js +61 -0
- package/dist/tools/image.d.ts +2 -0
- package/dist/tools/image.js +257 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +88 -0
- package/dist/tools/interface.d.ts +22 -0
- package/dist/tools/interface.js +1 -0
- package/dist/tools/notify.d.ts +2 -0
- package/dist/tools/notify.js +100 -0
- package/dist/tools/prompt-optimizer.d.ts +2 -0
- package/dist/tools/prompt-optimizer.js +65 -0
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +184 -0
- package/dist/tools/search.d.ts +2 -0
- package/dist/tools/search.js +78 -0
- package/dist/tools/todos.d.ts +10 -0
- package/dist/tools/todos.js +50 -0
- package/package.json +119 -0
- package/skills/docker-ops/SKILL.md +329 -0
- package/skills/k8s-deploy/SKILL.md +397 -0
- package/skills/log-analyzer/SKILL.md +331 -0
- package/skills/speckit-analyze/SKILL.md +260 -0
- package/skills/speckit-checklist/SKILL.md +374 -0
- package/skills/speckit-clarify/SKILL.md +286 -0
- package/skills/speckit-constitution/SKILL.md +157 -0
- package/skills/speckit-implement/SKILL.md +224 -0
- package/skills/speckit-plan/SKILL.md +171 -0
- package/skills/speckit-specify/SKILL.md +346 -0
- package/skills/speckit-tasks/SKILL.md +215 -0
- package/skills/speckit-taskstoissues/SKILL.md +107 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Core — Settings Schema
|
|
3
|
+
*
|
|
4
|
+
* Static data structures mapping all user-visible settings to their
|
|
5
|
+
* AppConfig paths, validation rules, env var overrides, and metadata.
|
|
6
|
+
*/
|
|
7
|
+
export type SettingsCategory = 'providers' | 'permissions' | 'tools' | 'notifications' | 'skills' | 'gateway' | 'sessions';
|
|
8
|
+
export interface SettingsMapEntry {
|
|
9
|
+
dotKey: string;
|
|
10
|
+
configPath: string[];
|
|
11
|
+
category: SettingsCategory;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SettingsSchemaEntry {
|
|
15
|
+
type: 'string' | 'number' | 'boolean' | 'enum';
|
|
16
|
+
secret: boolean;
|
|
17
|
+
enumValues?: string[];
|
|
18
|
+
min?: number;
|
|
19
|
+
max?: number;
|
|
20
|
+
default?: string | number | boolean;
|
|
21
|
+
restartRequired: boolean;
|
|
22
|
+
envVar?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const SETTINGS_CATEGORIES: {
|
|
25
|
+
key: SettingsCategory;
|
|
26
|
+
label: string;
|
|
27
|
+
description: string;
|
|
28
|
+
}[];
|
|
29
|
+
export declare const SETTINGS_MAP: Map<string, SettingsMapEntry>;
|
|
30
|
+
export declare const CONFIG_PATH_TO_DOTKEY: Map<string, string>;
|
|
31
|
+
export declare const SETTINGS_SCHEMA: Map<string, SettingsSchemaEntry>;
|
|
32
|
+
export declare const ENV_VAR_MAP: Map<string, string>;
|
|
33
|
+
export declare function getSettingEntry(dotKey: string): SettingsMapEntry | undefined;
|
|
34
|
+
export declare function getSettingSchema(dotKey: string): SettingsSchemaEntry | undefined;
|
|
35
|
+
export declare function getDotKeyForConfigPath(path: string[]): string | undefined;
|
|
36
|
+
export declare function isSecretField(dotKey: string): boolean;
|
|
37
|
+
export declare function isRestartRequired(dotKey: string): boolean;
|
|
38
|
+
export declare function getSettingsByCategory(category: SettingsCategory): string[];
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoe Core — Settings Schema
|
|
3
|
+
*
|
|
4
|
+
* Static data structures mapping all user-visible settings to their
|
|
5
|
+
* AppConfig paths, validation rules, env var overrides, and metadata.
|
|
6
|
+
*/
|
|
7
|
+
import { DEFAULT_MODELS } from "../models-catalog.js";
|
|
8
|
+
// ── Categories ─────────────────────────────────────────────────────────
|
|
9
|
+
export const SETTINGS_CATEGORIES = [
|
|
10
|
+
{
|
|
11
|
+
key: 'providers',
|
|
12
|
+
label: 'Providers & Models',
|
|
13
|
+
description: 'LLM provider configuration (API keys, models, base URLs)',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
key: 'permissions',
|
|
17
|
+
label: 'Permissions & Safety',
|
|
18
|
+
description: 'Permission level and auto-confirm settings',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
key: 'tools',
|
|
22
|
+
label: 'Tools & Integrations',
|
|
23
|
+
description: 'Image generation, SMTP email, and web search settings',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: 'notifications',
|
|
27
|
+
label: 'Notifications',
|
|
28
|
+
description: 'Feishu, DingTalk, and WeCom webhook settings',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'skills',
|
|
32
|
+
label: 'Skills',
|
|
33
|
+
description: 'Skill system configuration (reserved for future use)',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
key: 'gateway',
|
|
37
|
+
label: 'Gateway',
|
|
38
|
+
description: 'MCP gateway, REST proxy, and OpenAPI adapter settings',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: 'sessions',
|
|
42
|
+
label: 'Sessions',
|
|
43
|
+
description: 'Session persistence and cleanup settings',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
// ── Settings Map ───────────────────────────────────────────────────────
|
|
47
|
+
const entries = [
|
|
48
|
+
// Providers
|
|
49
|
+
['providers.openai.apiKey', { dotKey: 'providers.openai.apiKey', configPath: ['models', 'openai', 'apiKey'], category: 'providers', label: 'OpenAI API Key' }],
|
|
50
|
+
['providers.openai.model', { dotKey: 'providers.openai.model', configPath: ['models', 'openai', 'model'], category: 'providers', label: 'OpenAI Model' }],
|
|
51
|
+
['providers.anthropic.apiKey', { dotKey: 'providers.anthropic.apiKey', configPath: ['models', 'anthropic', 'apiKey'], category: 'providers', label: 'Anthropic API Key' }],
|
|
52
|
+
['providers.anthropic.model', { dotKey: 'providers.anthropic.model', configPath: ['models', 'anthropic', 'model'], category: 'providers', label: 'Anthropic Model' }],
|
|
53
|
+
['providers.glm.apiKey', { dotKey: 'providers.glm.apiKey', configPath: ['models', 'glm', 'apiKey'], category: 'providers', label: 'GLM API Key' }],
|
|
54
|
+
['providers.glm.model', { dotKey: 'providers.glm.model', configPath: ['models', 'glm', 'model'], category: 'providers', label: 'GLM Model' }],
|
|
55
|
+
['providers.openai-compat.apiKey', { dotKey: 'providers.openai-compat.apiKey', configPath: ['models', 'openai-compatible', 'apiKey'], category: 'providers', label: 'OpenAI-Compatible API Key' }],
|
|
56
|
+
['providers.openai-compat.baseUrl', { dotKey: 'providers.openai-compat.baseUrl', configPath: ['models', 'openai-compatible', 'baseUrl'], category: 'providers', label: 'OpenAI-Compatible Base URL' }],
|
|
57
|
+
['providers.openai-compat.model', { dotKey: 'providers.openai-compat.model', configPath: ['models', 'openai-compatible', 'model'], category: 'providers', label: 'OpenAI-Compatible Model' }],
|
|
58
|
+
['provider', { dotKey: 'provider', configPath: ['provider'], category: 'providers', label: 'Active Provider' }],
|
|
59
|
+
// Image
|
|
60
|
+
['image.apiKey', { dotKey: 'image.apiKey', configPath: ['imageApiKey'], category: 'tools', label: 'Image Generation API Key' }],
|
|
61
|
+
['image.baseUrl', { dotKey: 'image.baseUrl', configPath: ['imageBaseUrl'], category: 'tools', label: 'Image Generation Base URL' }],
|
|
62
|
+
['image.model', { dotKey: 'image.model', configPath: ['imageModel'], category: 'tools', label: 'Image Generation Model' }],
|
|
63
|
+
['image.size', { dotKey: 'image.size', configPath: ['imageSize'], category: 'tools', label: 'Image Size' }],
|
|
64
|
+
['image.quality', { dotKey: 'image.quality', configPath: ['imageQuality'], category: 'tools', label: 'Image Quality' }],
|
|
65
|
+
['image.style', { dotKey: 'image.style', configPath: ['imageStyle'], category: 'tools', label: 'Image Style' }],
|
|
66
|
+
['image.n', { dotKey: 'image.n', configPath: ['imageN'], category: 'tools', label: 'Image Count' }],
|
|
67
|
+
// SMTP
|
|
68
|
+
['smtp.host', { dotKey: 'smtp.host', configPath: ['smtpHost'], category: 'tools', label: 'SMTP Host' }],
|
|
69
|
+
['smtp.port', { dotKey: 'smtp.port', configPath: ['smtpPort'], category: 'tools', label: 'SMTP Port' }],
|
|
70
|
+
['smtp.user', { dotKey: 'smtp.user', configPath: ['smtpUser'], category: 'tools', label: 'SMTP Username' }],
|
|
71
|
+
['smtp.pass', { dotKey: 'smtp.pass', configPath: ['smtpPass'], category: 'tools', label: 'SMTP Password' }],
|
|
72
|
+
['smtp.from', { dotKey: 'smtp.from', configPath: ['smtpFrom'], category: 'tools', label: 'SMTP From Address' }],
|
|
73
|
+
// Search
|
|
74
|
+
['search.tavilyApiKey', { dotKey: 'search.tavilyApiKey', configPath: ['tavilyApiKey'], category: 'tools', label: 'Tavily API Key' }],
|
|
75
|
+
// Notifications
|
|
76
|
+
['notifications.feishu.webhook', { dotKey: 'notifications.feishu.webhook', configPath: ['feishuWebhook'], category: 'notifications', label: 'Feishu Webhook URL' }],
|
|
77
|
+
['notifications.feishu.keyword', { dotKey: 'notifications.feishu.keyword', configPath: ['feishuKeyword'], category: 'notifications', label: 'Feishu Keyword' }],
|
|
78
|
+
['notifications.dingtalk.webhook', { dotKey: 'notifications.dingtalk.webhook', configPath: ['dingtalkWebhook'], category: 'notifications', label: 'DingTalk Webhook URL' }],
|
|
79
|
+
['notifications.dingtalk.keyword', { dotKey: 'notifications.dingtalk.keyword', configPath: ['dingtalkKeyword'], category: 'notifications', label: 'DingTalk Keyword' }],
|
|
80
|
+
['notifications.wecom.webhook', { dotKey: 'notifications.wecom.webhook', configPath: ['wecomWebhook'], category: 'notifications', label: 'WeCom Webhook URL' }],
|
|
81
|
+
['notifications.wecom.keyword', { dotKey: 'notifications.wecom.keyword', configPath: ['wecomKeyword'], category: 'notifications', label: 'WeCom Keyword' }],
|
|
82
|
+
// Permissions
|
|
83
|
+
['agent.permissionLevel', { dotKey: 'agent.permissionLevel', configPath: ['permissionLevel'], category: 'permissions', label: 'Permission Level' }],
|
|
84
|
+
['agent.autoConfirm', { dotKey: 'agent.autoConfirm', configPath: ['autoConfirm'], category: 'permissions', label: 'Auto-Confirm All Tools' }],
|
|
85
|
+
// Gateway
|
|
86
|
+
['gateway.enabled', { dotKey: 'gateway.enabled', configPath: ['gatewayEnabled'], category: 'gateway', label: 'Gateway Enabled' }],
|
|
87
|
+
['gateway.semanticTopK', { dotKey: 'gateway.semanticTopK', configPath: ['gatewaySemanticTopK'], category: 'gateway', label: 'Semantic Injection Top-K' }],
|
|
88
|
+
['gateway.defaultRateLimitPerMin', { dotKey: 'gateway.defaultRateLimitPerMin', configPath: ['gatewayRateLimit'], category: 'gateway', label: 'Gateway Rate Limit (per min)' }],
|
|
89
|
+
['gateway.maxAuditLogs', { dotKey: 'gateway.maxAuditLogs', configPath: ['gatewayMaxAuditLogs'], category: 'gateway', label: 'Max Audit Log Records' }],
|
|
90
|
+
// Sessions
|
|
91
|
+
['sessions.maxAgeDays', { dotKey: 'sessions.maxAgeDays', configPath: ['sessions', 'maxAgeDays'], category: 'sessions', label: 'Max Session Age (days)' }],
|
|
92
|
+
];
|
|
93
|
+
export const SETTINGS_MAP = new Map(entries);
|
|
94
|
+
// ── Reverse lookup ─────────────────────────────────────────────────────
|
|
95
|
+
export const CONFIG_PATH_TO_DOTKEY = new Map(entries.map(([, entry]) => [entry.configPath.join('.'), entry.dotKey]));
|
|
96
|
+
// ── Settings Schema ────────────────────────────────────────────────────
|
|
97
|
+
const schemaEntries = [
|
|
98
|
+
// Providers
|
|
99
|
+
['providers.openai.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'OPENAI_API_KEY' }],
|
|
100
|
+
['providers.openai.model', { type: 'string', secret: false, default: DEFAULT_MODELS.openai, restartRequired: false, envVar: 'OPENAI_MODEL' }],
|
|
101
|
+
['providers.anthropic.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'ANTHROPIC_API_KEY' }],
|
|
102
|
+
['providers.anthropic.model', { type: 'string', secret: false, default: DEFAULT_MODELS.anthropic, restartRequired: false, envVar: 'ANTHROPIC_MODEL' }],
|
|
103
|
+
['providers.glm.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'GLM_API_KEY' }],
|
|
104
|
+
['providers.glm.model', { type: 'string', secret: false, default: DEFAULT_MODELS.glm, restartRequired: false, envVar: 'GLM_MODEL' }],
|
|
105
|
+
['providers.openai-compat.apiKey', { type: 'string', secret: true, restartRequired: true, envVar: 'OPENAI_COMPAT_API_KEY' }],
|
|
106
|
+
['providers.openai-compat.baseUrl', { type: 'string', secret: false, restartRequired: true, envVar: 'OPENAI_COMPAT_BASE_URL' }],
|
|
107
|
+
['providers.openai-compat.model', { type: 'string', secret: false, default: DEFAULT_MODELS['openai-compatible'], restartRequired: false, envVar: 'OPENAI_MODEL' }],
|
|
108
|
+
['provider', { type: 'enum', secret: false, enumValues: ['openai', 'openai-compatible', 'anthropic', 'glm'], default: 'openai-compatible', restartRequired: true, envVar: 'LLM_PROVIDER' }],
|
|
109
|
+
// Image
|
|
110
|
+
['image.apiKey', { type: 'string', secret: true, restartRequired: false }],
|
|
111
|
+
['image.baseUrl', { type: 'string', secret: false, restartRequired: false }],
|
|
112
|
+
['image.model', { type: 'string', secret: false, default: 'dall-e-3', restartRequired: false }],
|
|
113
|
+
['image.size', { type: 'string', secret: false, default: '1024x1024', restartRequired: false }],
|
|
114
|
+
['image.quality', { type: 'enum', secret: false, enumValues: ['standard', 'hd'], default: 'standard', restartRequired: false }],
|
|
115
|
+
['image.style', { type: 'enum', secret: false, enumValues: ['vivid', 'natural'], default: 'vivid', restartRequired: false }],
|
|
116
|
+
['image.n', { type: 'number', secret: false, default: 1, min: 1, max: 10, restartRequired: false }],
|
|
117
|
+
// SMTP
|
|
118
|
+
['smtp.host', { type: 'string', secret: false, restartRequired: false, envVar: 'SMTP_HOST' }],
|
|
119
|
+
['smtp.port', { type: 'string', secret: false, restartRequired: false, envVar: 'SMTP_PORT' }],
|
|
120
|
+
['smtp.user', { type: 'string', secret: false, restartRequired: false, envVar: 'SMTP_USER' }],
|
|
121
|
+
['smtp.pass', { type: 'string', secret: true, restartRequired: false, envVar: 'SMTP_PASS' }],
|
|
122
|
+
['smtp.from', { type: 'string', secret: false, restartRequired: false }],
|
|
123
|
+
// Search
|
|
124
|
+
['search.tavilyApiKey', { type: 'string', secret: true, restartRequired: false, envVar: 'TAVILY_API_KEY' }],
|
|
125
|
+
// Notifications
|
|
126
|
+
['notifications.feishu.webhook', { type: 'string', secret: true, restartRequired: false, envVar: 'FEISHU_WEBHOOK' }],
|
|
127
|
+
['notifications.feishu.keyword', { type: 'string', secret: false, restartRequired: false, envVar: 'FEISHU_KEYWORD' }],
|
|
128
|
+
['notifications.dingtalk.webhook', { type: 'string', secret: true, restartRequired: false, envVar: 'DINGTALK_WEBHOOK' }],
|
|
129
|
+
['notifications.dingtalk.keyword', { type: 'string', secret: false, restartRequired: false, envVar: 'DINGTALK_KEYWORD' }],
|
|
130
|
+
['notifications.wecom.webhook', { type: 'string', secret: true, restartRequired: false, envVar: 'WECOM_WEBHOOK' }],
|
|
131
|
+
['notifications.wecom.keyword', { type: 'string', secret: false, restartRequired: false, envVar: 'WECOM_KEYWORD' }],
|
|
132
|
+
// Agent
|
|
133
|
+
['agent.permissionLevel', { type: 'enum', secret: false, enumValues: ['strict', 'moderate', 'permissive'], default: 'moderate', restartRequired: false, envVar: 'ZOE_PERMISSION' }],
|
|
134
|
+
['agent.autoConfirm', { type: 'boolean', secret: false, default: false, restartRequired: false }],
|
|
135
|
+
// Gateway
|
|
136
|
+
['gateway.enabled', { type: 'boolean', secret: false, default: true, restartRequired: true, envVar: 'ZOE_GATEWAY_ENABLED' }],
|
|
137
|
+
['gateway.semanticTopK', { type: 'number', secret: false, default: 3, min: 1, max: 10, restartRequired: false }],
|
|
138
|
+
['gateway.defaultRateLimitPerMin', { type: 'number', secret: false, default: 60, min: 0, restartRequired: false, envVar: 'ZOE_GATEWAY_RATE_LIMIT' }],
|
|
139
|
+
['gateway.maxAuditLogs', { type: 'number', secret: false, default: 1000, min: 10, max: 10000, restartRequired: false }],
|
|
140
|
+
// Sessions
|
|
141
|
+
['sessions.maxAgeDays', { type: 'number', secret: false, default: 30, min: 0, restartRequired: false }],
|
|
142
|
+
];
|
|
143
|
+
export const SETTINGS_SCHEMA = new Map(schemaEntries);
|
|
144
|
+
// ── Env Var Map ────────────────────────────────────────────────────────
|
|
145
|
+
export const ENV_VAR_MAP = new Map(schemaEntries
|
|
146
|
+
.filter(([, s]) => s.envVar !== undefined)
|
|
147
|
+
.map(([dotKey, s]) => [dotKey, s.envVar]));
|
|
148
|
+
// ── Helpers ────────────────────────────────────────────────────────────
|
|
149
|
+
export function getSettingEntry(dotKey) {
|
|
150
|
+
return SETTINGS_MAP.get(dotKey);
|
|
151
|
+
}
|
|
152
|
+
export function getSettingSchema(dotKey) {
|
|
153
|
+
return SETTINGS_SCHEMA.get(dotKey);
|
|
154
|
+
}
|
|
155
|
+
export function getDotKeyForConfigPath(path) {
|
|
156
|
+
return CONFIG_PATH_TO_DOTKEY.get(path.join('.'));
|
|
157
|
+
}
|
|
158
|
+
export function isSecretField(dotKey) {
|
|
159
|
+
return SETTINGS_SCHEMA.get(dotKey)?.secret ?? false;
|
|
160
|
+
}
|
|
161
|
+
export function isRestartRequired(dotKey) {
|
|
162
|
+
return SETTINGS_SCHEMA.get(dotKey)?.restartRequired ?? false;
|
|
163
|
+
}
|
|
164
|
+
export function getSettingsByCategory(category) {
|
|
165
|
+
const keys = [];
|
|
166
|
+
for (const entry of SETTINGS_MAP.values()) {
|
|
167
|
+
if (entry.category === category)
|
|
168
|
+
keys.push(entry.dotKey);
|
|
169
|
+
}
|
|
170
|
+
return keys;
|
|
171
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SkillMetadata } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build a skill catalog string suitable for appending to the system prompt.
|
|
4
|
+
* Returns an empty string when no skills are available.
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildSkillCatalog(metadata: SkillMetadata[]): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a skill catalog string suitable for appending to the system prompt.
|
|
3
|
+
* Returns an empty string when no skills are available.
|
|
4
|
+
*/
|
|
5
|
+
export function buildSkillCatalog(metadata) {
|
|
6
|
+
if (metadata.length === 0)
|
|
7
|
+
return '';
|
|
8
|
+
const lines = metadata.map(s => {
|
|
9
|
+
const tags = s.tags.length > 0 ? ` [${s.tags.join(', ')}]` : '';
|
|
10
|
+
return `- ${s.name}: ${s.description}${tags}`;
|
|
11
|
+
});
|
|
12
|
+
return [
|
|
13
|
+
'AVAILABLE SKILLS (activate with use_skill tool):',
|
|
14
|
+
...lines,
|
|
15
|
+
'When a user request matches a skill, call use_skill with the skill name.',
|
|
16
|
+
].join('\n');
|
|
17
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Invoker — Central orchestrator for skill invocation flow.
|
|
3
|
+
*
|
|
4
|
+
* This module consolidates the skill invocation logic that was previously
|
|
5
|
+
* scattered across the CLI (src/index.ts) and agent (src/agent.ts).
|
|
6
|
+
*
|
|
7
|
+
* Invocation flow:
|
|
8
|
+
* 1. Parse input → extract skill name and arguments
|
|
9
|
+
* 2. Registry lookup → resolve skill metadata
|
|
10
|
+
* 3. @path resolution → inline file references
|
|
11
|
+
* 4. Prompt construction → build the final prompt
|
|
12
|
+
* 5. Return result with provider switching metadata
|
|
13
|
+
*
|
|
14
|
+
* The invoker does NOT handle provider switching itself — it returns
|
|
15
|
+
* metadata so the adapter can decide whether to switch.
|
|
16
|
+
*/
|
|
17
|
+
import { type SkillRegistry } from '../skills/types.js';
|
|
18
|
+
import { type SkillMetadata } from './types.js';
|
|
19
|
+
import type { LLMProvider } from '../providers/types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Result of a skill invocation.
|
|
22
|
+
*
|
|
23
|
+
* Contains the constructed prompt along with metadata about
|
|
24
|
+
* whether the skill has a preferred provider/model configuration.
|
|
25
|
+
*/
|
|
26
|
+
export interface SkillInvocationResult {
|
|
27
|
+
/** The constructed prompt to send to the agent */
|
|
28
|
+
prompt: string;
|
|
29
|
+
/** Resolved skill metadata */
|
|
30
|
+
skill: SkillMetadata;
|
|
31
|
+
/** Whether the skill has a preferredProvider that needs switching */
|
|
32
|
+
providerSwitchNeeded: boolean;
|
|
33
|
+
/** The preferred provider type (if any) */
|
|
34
|
+
preferredProvider?: string;
|
|
35
|
+
/** The preferred model (if any) */
|
|
36
|
+
preferredModel?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Invoke a skill by name with the provided arguments.
|
|
40
|
+
*
|
|
41
|
+
* This function orchestrates the complete skill invocation flow:
|
|
42
|
+
* 1. Parses the input to extract skill name and arguments
|
|
43
|
+
* 2. Looks up the skill in the registry
|
|
44
|
+
* 3. Substitutes arguments into the skill body
|
|
45
|
+
* 4. Resolves @path references
|
|
46
|
+
* 5. Constructs the final prompt
|
|
47
|
+
* 6. Returns metadata about provider switching if needed
|
|
48
|
+
*
|
|
49
|
+
* @param options - Invocation options
|
|
50
|
+
* @param options.input - Raw "/skillname args" input from user
|
|
51
|
+
* @param options.registry - The skill registry to look up skills from
|
|
52
|
+
* @param options.skillsPath - Optional path for @path resolution (defaults to cwd)
|
|
53
|
+
* @returns SkillInvocationResult or null if no skill matches
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const result = await invokeSkill({
|
|
58
|
+
* input: '/code-review src/app.ts',
|
|
59
|
+
* registry: skillRegistry,
|
|
60
|
+
* });
|
|
61
|
+
*
|
|
62
|
+
* if (result) {
|
|
63
|
+
* if (result.providerSwitchNeeded) {
|
|
64
|
+
* await switchProvider(result.preferredProvider!, result.preferredModel!);
|
|
65
|
+
* }
|
|
66
|
+
* await agent.chat(result.prompt);
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function invokeSkill(options: {
|
|
71
|
+
input: string;
|
|
72
|
+
registry: SkillRegistry;
|
|
73
|
+
skillsPath?: string;
|
|
74
|
+
}): Promise<SkillInvocationResult | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Configuration for creating a skill provider switcher.
|
|
77
|
+
*/
|
|
78
|
+
export interface ProviderSwitcherConfig {
|
|
79
|
+
/** The current active provider */
|
|
80
|
+
provider: LLMProvider;
|
|
81
|
+
/** The current active model name */
|
|
82
|
+
model: string;
|
|
83
|
+
/** Available model configurations keyed by provider type */
|
|
84
|
+
models: Record<string, {
|
|
85
|
+
apiKey: string;
|
|
86
|
+
baseUrl?: string;
|
|
87
|
+
model: string;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A switcher that temporarily changes the active provider/model based on
|
|
92
|
+
* skill preferences and can restore the original when done.
|
|
93
|
+
*/
|
|
94
|
+
export interface SkillProviderSwitcher {
|
|
95
|
+
/** Switch provider if the skill requires it. Returns true if switched. */
|
|
96
|
+
switchIfNeeded(skillResult: SkillInvocationResult): Promise<boolean>;
|
|
97
|
+
/** Restore the original provider/model. */
|
|
98
|
+
restore(): void;
|
|
99
|
+
/** The current active provider. */
|
|
100
|
+
readonly activeProvider: LLMProvider;
|
|
101
|
+
/** The current active model name. */
|
|
102
|
+
readonly activeModel: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a skill provider switcher that captures the original provider/model
|
|
106
|
+
* state and can temporarily switch based on skill preferences.
|
|
107
|
+
*
|
|
108
|
+
* The switcher gracefully handles errors — if provider creation fails,
|
|
109
|
+
* `switchIfNeeded` returns `false` instead of throwing.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const switcher = createSkillProviderSwitcher({
|
|
114
|
+
* provider: currentProvider,
|
|
115
|
+
* model: 'gpt-4',
|
|
116
|
+
* models: config.models,
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* const switched = await switcher.switchIfNeeded(skillResult);
|
|
120
|
+
* try {
|
|
121
|
+
* await agent.chat(skillResult.prompt);
|
|
122
|
+
* } finally {
|
|
123
|
+
* if (switched) switcher.restore();
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function createSkillProviderSwitcher(config: ProviderSwitcherConfig): SkillProviderSwitcher;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Invoker — Central orchestrator for skill invocation flow.
|
|
3
|
+
*
|
|
4
|
+
* This module consolidates the skill invocation logic that was previously
|
|
5
|
+
* scattered across the CLI (src/index.ts) and agent (src/agent.ts).
|
|
6
|
+
*
|
|
7
|
+
* Invocation flow:
|
|
8
|
+
* 1. Parse input → extract skill name and arguments
|
|
9
|
+
* 2. Registry lookup → resolve skill metadata
|
|
10
|
+
* 3. @path resolution → inline file references
|
|
11
|
+
* 4. Prompt construction → build the final prompt
|
|
12
|
+
* 5. Return result with provider switching metadata
|
|
13
|
+
*
|
|
14
|
+
* The invoker does NOT handle provider switching itself — it returns
|
|
15
|
+
* metadata so the adapter can decide whether to switch.
|
|
16
|
+
*/
|
|
17
|
+
import { parseInvocation, substituteArgs } from '../skills/args.js';
|
|
18
|
+
import { limitSkillBody } from '../skills/types.js';
|
|
19
|
+
import { resolveReferences } from '../skills/resolver.js';
|
|
20
|
+
import { createProvider } from '../providers/factory.js';
|
|
21
|
+
/**
|
|
22
|
+
* Invoke a skill by name with the provided arguments.
|
|
23
|
+
*
|
|
24
|
+
* This function orchestrates the complete skill invocation flow:
|
|
25
|
+
* 1. Parses the input to extract skill name and arguments
|
|
26
|
+
* 2. Looks up the skill in the registry
|
|
27
|
+
* 3. Substitutes arguments into the skill body
|
|
28
|
+
* 4. Resolves @path references
|
|
29
|
+
* 5. Constructs the final prompt
|
|
30
|
+
* 6. Returns metadata about provider switching if needed
|
|
31
|
+
*
|
|
32
|
+
* @param options - Invocation options
|
|
33
|
+
* @param options.input - Raw "/skillname args" input from user
|
|
34
|
+
* @param options.registry - The skill registry to look up skills from
|
|
35
|
+
* @param options.skillsPath - Optional path for @path resolution (defaults to cwd)
|
|
36
|
+
* @returns SkillInvocationResult or null if no skill matches
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const result = await invokeSkill({
|
|
41
|
+
* input: '/code-review src/app.ts',
|
|
42
|
+
* registry: skillRegistry,
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* if (result) {
|
|
46
|
+
* if (result.providerSwitchNeeded) {
|
|
47
|
+
* await switchProvider(result.preferredProvider!, result.preferredModel!);
|
|
48
|
+
* }
|
|
49
|
+
* await agent.chat(result.prompt);
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export async function invokeSkill(options) {
|
|
54
|
+
const { input, registry, skillsPath } = options;
|
|
55
|
+
// Step 1: Parse the input to extract skill name and arguments
|
|
56
|
+
const parsed = parseInvocation(input);
|
|
57
|
+
if (!parsed) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const { skillName, args } = parsed;
|
|
61
|
+
// Step 2: Registry lookup
|
|
62
|
+
const skill = registry.get(skillName);
|
|
63
|
+
if (!skill) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
// Step 3: Get the skill body
|
|
67
|
+
const skillBody = await registry.getBody(skillName);
|
|
68
|
+
if (!skillBody) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// Step 4: Substitute arguments into skill body
|
|
72
|
+
const substitutedBody = substituteArgs(skillBody, args);
|
|
73
|
+
// Step 5: Resolve @path references in the query
|
|
74
|
+
let resolvedQuery = args.raw;
|
|
75
|
+
if (args.raw.includes('@')) {
|
|
76
|
+
try {
|
|
77
|
+
resolvedQuery = await resolveReferences(args.raw, skillsPath);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Resolver failed, use raw args
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Step 6: Enforce body size limits (truncate if necessary)
|
|
84
|
+
const { body: limitedBody, truncated, originalTokenEstimate, finalTokenEstimate } = limitSkillBody(substitutedBody);
|
|
85
|
+
if (truncated) {
|
|
86
|
+
console.warn(`[SKILLS] Skill "${skillName}" body truncated: ` +
|
|
87
|
+
`${originalTokenEstimate} -> ${finalTokenEstimate} estimated tokens.`);
|
|
88
|
+
}
|
|
89
|
+
// Step 7: Construct the final prompt
|
|
90
|
+
const prompt = resolvedQuery
|
|
91
|
+
? `[Skill: ${skill.name} activated]\n\n${limitedBody}\n\nUser request: ${resolvedQuery}`
|
|
92
|
+
: `[Skill: ${skill.name} activated]\n\n${limitedBody}\n\nSkill loaded. What would you like me to do?`;
|
|
93
|
+
// Step 8: Extract provider switching metadata
|
|
94
|
+
const extendedSkill = skill;
|
|
95
|
+
const modelConfig = extendedSkill.frontmatter?.model;
|
|
96
|
+
const providerSwitchNeeded = !!modelConfig?.provider;
|
|
97
|
+
return {
|
|
98
|
+
prompt,
|
|
99
|
+
skill: {
|
|
100
|
+
name: skill.name,
|
|
101
|
+
description: skill.description,
|
|
102
|
+
tags: skill.tags,
|
|
103
|
+
},
|
|
104
|
+
providerSwitchNeeded,
|
|
105
|
+
preferredProvider: modelConfig?.provider,
|
|
106
|
+
preferredModel: modelConfig?.model,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Create a skill provider switcher that captures the original provider/model
|
|
111
|
+
* state and can temporarily switch based on skill preferences.
|
|
112
|
+
*
|
|
113
|
+
* The switcher gracefully handles errors — if provider creation fails,
|
|
114
|
+
* `switchIfNeeded` returns `false` instead of throwing.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const switcher = createSkillProviderSwitcher({
|
|
119
|
+
* provider: currentProvider,
|
|
120
|
+
* model: 'gpt-4',
|
|
121
|
+
* models: config.models,
|
|
122
|
+
* });
|
|
123
|
+
*
|
|
124
|
+
* const switched = await switcher.switchIfNeeded(skillResult);
|
|
125
|
+
* try {
|
|
126
|
+
* await agent.chat(skillResult.prompt);
|
|
127
|
+
* } finally {
|
|
128
|
+
* if (switched) switcher.restore();
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export function createSkillProviderSwitcher(config) {
|
|
133
|
+
let activeProvider = config.provider;
|
|
134
|
+
let activeModel = config.model;
|
|
135
|
+
let originalProvider = null;
|
|
136
|
+
let originalModel = null;
|
|
137
|
+
return {
|
|
138
|
+
get activeProvider() {
|
|
139
|
+
return activeProvider;
|
|
140
|
+
},
|
|
141
|
+
get activeModel() {
|
|
142
|
+
return activeModel;
|
|
143
|
+
},
|
|
144
|
+
async switchIfNeeded(skillResult) {
|
|
145
|
+
if (!skillResult.providerSwitchNeeded)
|
|
146
|
+
return false;
|
|
147
|
+
if (!skillResult.preferredProvider || !skillResult.preferredModel)
|
|
148
|
+
return false;
|
|
149
|
+
const providerType = skillResult.preferredProvider;
|
|
150
|
+
const providerModelConfig = config.models[providerType];
|
|
151
|
+
if (!providerModelConfig?.apiKey)
|
|
152
|
+
return false;
|
|
153
|
+
try {
|
|
154
|
+
const newProvider = await createProvider({
|
|
155
|
+
type: providerType,
|
|
156
|
+
apiKey: providerModelConfig.apiKey,
|
|
157
|
+
model: skillResult.preferredModel,
|
|
158
|
+
baseUrl: providerModelConfig.baseUrl,
|
|
159
|
+
});
|
|
160
|
+
// Capture originals before overwriting (only on first switch)
|
|
161
|
+
if (!originalProvider) {
|
|
162
|
+
originalProvider = activeProvider;
|
|
163
|
+
originalModel = activeModel;
|
|
164
|
+
}
|
|
165
|
+
activeProvider = newProvider;
|
|
166
|
+
activeModel = skillResult.preferredModel;
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
restore() {
|
|
174
|
+
if (originalProvider) {
|
|
175
|
+
activeProvider = originalProvider;
|
|
176
|
+
activeModel = originalModel ?? activeModel;
|
|
177
|
+
originalProvider = null;
|
|
178
|
+
originalModel = null;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamingResponseAccumulator — reassembles a complete `ProviderResponse`
|
|
3
|
+
* ({ content, tool_calls }) from a stream of `StreamDelta`s.
|
|
4
|
+
*
|
|
5
|
+
* Provider streams frequently split a tool call's JSON `arguments` across many
|
|
6
|
+
* deltas; this buffers each tool call by index and concatenates the fragments,
|
|
7
|
+
* so `runAgentLoop` receives well-formed, complete tool calls to execute.
|
|
8
|
+
*/
|
|
9
|
+
import type { ProviderResponse } from '../providers/types.js';
|
|
10
|
+
import type { Usage } from './types.js';
|
|
11
|
+
export declare class StreamingResponseAccumulator {
|
|
12
|
+
private text;
|
|
13
|
+
private toolCalls;
|
|
14
|
+
private usage;
|
|
15
|
+
appendText(delta: string): void;
|
|
16
|
+
beginToolCall(index: number, id: string, name: string): void;
|
|
17
|
+
appendToolCallArgs(index: number, argsDelta: string): void;
|
|
18
|
+
setUsage(usage: Usage): void;
|
|
19
|
+
getUsage(): Usage | undefined;
|
|
20
|
+
toResponse(): ProviderResponse;
|
|
21
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StreamingResponseAccumulator — reassembles a complete `ProviderResponse`
|
|
3
|
+
* ({ content, tool_calls }) from a stream of `StreamDelta`s.
|
|
4
|
+
*
|
|
5
|
+
* Provider streams frequently split a tool call's JSON `arguments` across many
|
|
6
|
+
* deltas; this buffers each tool call by index and concatenates the fragments,
|
|
7
|
+
* so `runAgentLoop` receives well-formed, complete tool calls to execute.
|
|
8
|
+
*/
|
|
9
|
+
export class StreamingResponseAccumulator {
|
|
10
|
+
text = '';
|
|
11
|
+
toolCalls = new Map();
|
|
12
|
+
usage;
|
|
13
|
+
appendText(delta) {
|
|
14
|
+
this.text += delta;
|
|
15
|
+
}
|
|
16
|
+
beginToolCall(index, id, name) {
|
|
17
|
+
const existing = this.toolCalls.get(index);
|
|
18
|
+
if (existing) {
|
|
19
|
+
existing.id = id || existing.id;
|
|
20
|
+
existing.name = name || existing.name;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.toolCalls.set(index, { index, id, name, argumentsBuffer: '' });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
appendToolCallArgs(index, argsDelta) {
|
|
27
|
+
const tc = this.toolCalls.get(index);
|
|
28
|
+
if (tc) {
|
|
29
|
+
tc.argumentsBuffer += argsDelta;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// delta arrived before begin — create a placeholder at this index
|
|
33
|
+
this.toolCalls.set(index, { index, id: '', name: '', argumentsBuffer: argsDelta });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
setUsage(usage) {
|
|
37
|
+
this.usage = usage;
|
|
38
|
+
}
|
|
39
|
+
getUsage() {
|
|
40
|
+
return this.usage;
|
|
41
|
+
}
|
|
42
|
+
toResponse() {
|
|
43
|
+
const tool_calls = [...this.toolCalls.values()]
|
|
44
|
+
.sort((a, b) => a.index - b.index)
|
|
45
|
+
.map((tc) => ({ id: tc.id, name: tc.name, arguments: tc.argumentsBuffer }));
|
|
46
|
+
return {
|
|
47
|
+
content: this.text || undefined,
|
|
48
|
+
tool_calls: tool_calls.length > 0 ? tool_calls : undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|