@causyne/agent 1.10.18 → 1.10.20
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/docs/ARCHITECTURE.md +20 -0
- package/native-skills/media/creative-studio-pipeline/SKILL.md +52 -0
- package/native-skills/media/photo-studio-automation/SKILL.md +53 -0
- package/native-skills/media/video-timeline-assembly/SKILL.md +52 -0
- package/package.json +1 -1
- package/src/agentLoop.js +4 -0
- package/src/appChannel.js +7 -3
- package/src/cliMode.js +33 -67
- package/src/commands.js +56 -8
- package/src/compiledSkills.js +15 -0
- package/src/config.js +80 -11
- package/src/creative/apps.js +69 -0
- package/src/creative/apps.ts +58 -0
- package/src/creative/blenderBatch.js +233 -0
- package/src/creative/blenderBatch.ts +217 -0
- package/src/creative/pipeline.js +56 -0
- package/src/creative/pipeline.ts +82 -0
- package/src/creative/studioScripts.js +374 -0
- package/src/creative/studioScripts.ts +378 -0
- package/src/decisionPolicy.js +2 -0
- package/src/decisionPolicy.ts +1 -0
- package/src/dynamicCatalogue.js +270 -0
- package/src/dynamicCatalogue.ts +283 -0
- package/src/entrypoint.js +2 -1
- package/src/modelCatalogue.d.ts +5 -0
- package/src/modelCatalogue.js +49 -18
- package/src/providers/anthropic.js +4 -0
- package/src/sandbox.js +4 -0
- package/src/toolPacks.js +1 -0
- package/src/tools/creativeApps.js +106 -0
- package/src/tools/creativeApps.ts +99 -0
- package/src/tools/registry.js +2 -1
- package/src/tui/banner.js +391 -190
- package/src/tui/banner.ts +409 -0
- package/src/tui/chatRenderer.js +1 -1
- package/src/tui/dialogs.js +10 -0
- package/src/tui/home.js +29 -72
- package/src/tui/setup.js +104 -0
- package/src/tui/setup.ts +98 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// Sovereign Dynamic Model Architecture & Live Discovery
|
|
2
|
+
//
|
|
3
|
+
// WHY THIS EXISTS:
|
|
4
|
+
// AI providers release and update models continuously. Hardcoded static
|
|
5
|
+
// catalogues freeze models at build time. This module gives Causyne a dual-layer
|
|
6
|
+
// model architecture:
|
|
7
|
+
// 1. Curated baseline: Instant offline availability with zero latency and verified pricing/capability tiers.
|
|
8
|
+
// 2. Dynamic discovery: Live discovery via provider APIs (Ollama, OpenAI, Gemini, Anthropic, DeepSeek, etc.)
|
|
9
|
+
// with resilient local caching in ~/.causyne/models_cache.json.
|
|
10
|
+
//
|
|
11
|
+
// ZERO-MALWARE: Pure TypeScript/ESM. Strictly respects timeouts, no secret leakage, no unprompted background calls.
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { causyneHome } from './utils/paths.js';
|
|
16
|
+
import { MODEL_CATALOGUE, DEFAULT_MODEL_BY_PROVIDER } from './modelCatalogue.js';
|
|
17
|
+
|
|
18
|
+
export interface DynamicCatalogueCache {
|
|
19
|
+
version: 1;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
providers: Record<string, {
|
|
22
|
+
timestamp: number;
|
|
23
|
+
models: string[];
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
28
|
+
const REQUEST_TIMEOUT_MS = 3500; // Fast timeout to never block user interaction
|
|
29
|
+
|
|
30
|
+
export function cacheFilePath(): string {
|
|
31
|
+
return path.join(causyneHome(), 'models_cache.json');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function normalizeProviderKey(provider: string): string {
|
|
35
|
+
const p = (provider || '').toLowerCase().trim();
|
|
36
|
+
if (p === 'dashscope' || p === 'dashscope-byok' || p === 'alibaba') return 'dashscope-byok';
|
|
37
|
+
if (p === 'ollama') return 'ollama';
|
|
38
|
+
if (p === 'openai') return 'openai';
|
|
39
|
+
if (p === 'anthropic' || p === 'claude') return 'anthropic';
|
|
40
|
+
if (p === 'gemini' || p === 'google') return 'gemini';
|
|
41
|
+
if (p === 'deepseek') return 'deepseek';
|
|
42
|
+
if (p === 'xai' || p === 'grok') return 'xai';
|
|
43
|
+
if (p === 'moonshot' || p === 'kimi') return 'moonshot';
|
|
44
|
+
if (p === 'muse' || p === 'meta') return 'muse';
|
|
45
|
+
if (p === 'openai-compatible' || p === 'custom') return 'openai-compatible';
|
|
46
|
+
return p;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Get the static curated models for a provider id. */
|
|
50
|
+
export function getCuratedModels(provider: string): string[] {
|
|
51
|
+
const norm = normalizeProviderKey(provider);
|
|
52
|
+
const mapping: Record<string, keyof typeof MODEL_CATALOGUE> = {
|
|
53
|
+
'deepseek': 'DeepSeek',
|
|
54
|
+
'qwen': 'Qwen',
|
|
55
|
+
'dashscope-byok': 'Alibaba (DashScope)',
|
|
56
|
+
'dashscope': 'Alibaba (DashScope)',
|
|
57
|
+
'anthropic': 'Anthropic',
|
|
58
|
+
'openai': 'OpenAI',
|
|
59
|
+
'gemini': 'Gemini',
|
|
60
|
+
'muse': 'Meta (Muse)',
|
|
61
|
+
'ollama': 'Ollama (Local)',
|
|
62
|
+
'moonshot': 'Moonshot (Kimi)',
|
|
63
|
+
'xai': 'xAI (Grok)'
|
|
64
|
+
};
|
|
65
|
+
const key = mapping[norm];
|
|
66
|
+
if (key && MODEL_CATALOGUE[key]) {
|
|
67
|
+
return [...MODEL_CATALOGUE[key]];
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Read cache safely from ~/.causyne/models_cache.json */
|
|
73
|
+
export function readModelCache(): DynamicCatalogueCache | null {
|
|
74
|
+
try {
|
|
75
|
+
const file = cacheFilePath();
|
|
76
|
+
if (!fs.existsSync(file)) return null;
|
|
77
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
78
|
+
const parsed = JSON.parse(content) as DynamicCatalogueCache;
|
|
79
|
+
if (parsed && parsed.version === 1 && typeof parsed.providers === 'object') {
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Graceful fallback on corrupt or inaccessible cache
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Save cache safely to ~/.causyne/models_cache.json */
|
|
89
|
+
export function writeModelCache(cache: DynamicCatalogueCache): void {
|
|
90
|
+
try {
|
|
91
|
+
const dir = causyneHome();
|
|
92
|
+
if (!fs.existsSync(dir)) {
|
|
93
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
94
|
+
}
|
|
95
|
+
const file = cacheFilePath();
|
|
96
|
+
fs.writeFileSync(file, JSON.stringify(cache, null, 2), { mode: 0o600 });
|
|
97
|
+
} catch {
|
|
98
|
+
// Cache failures must never crash the CLI
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Get cached models for a provider if not expired */
|
|
103
|
+
export function getCachedModels(provider: string): string[] | null {
|
|
104
|
+
const cache = readModelCache();
|
|
105
|
+
if (!cache) return null;
|
|
106
|
+
const norm = normalizeProviderKey(provider);
|
|
107
|
+
const entry = cache.providers[norm];
|
|
108
|
+
if (!entry) return null;
|
|
109
|
+
if (Date.now() - entry.timestamp > CACHE_TTL_MS) return null;
|
|
110
|
+
return entry.models;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Save models for a provider into the local cache */
|
|
114
|
+
export function saveCachedModels(provider: string, models: string[]): void {
|
|
115
|
+
const norm = normalizeProviderKey(provider);
|
|
116
|
+
const cache: DynamicCatalogueCache = readModelCache() || {
|
|
117
|
+
version: 1,
|
|
118
|
+
updatedAt: new Date().toISOString(),
|
|
119
|
+
providers: {}
|
|
120
|
+
};
|
|
121
|
+
cache.providers[norm] = {
|
|
122
|
+
timestamp: Date.now(),
|
|
123
|
+
models: [...new Set(models)]
|
|
124
|
+
};
|
|
125
|
+
cache.updatedAt = new Date().toISOString();
|
|
126
|
+
writeModelCache(cache);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Live query provider API to discover available models */
|
|
130
|
+
export async function fetchLiveModels(
|
|
131
|
+
provider: string,
|
|
132
|
+
config: { apiKey?: string; baseUrl?: string } = {}
|
|
133
|
+
): Promise<string[]> {
|
|
134
|
+
const norm = normalizeProviderKey(provider);
|
|
135
|
+
const { apiKey, baseUrl } = config;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
if (norm === 'ollama') {
|
|
139
|
+
const host = (baseUrl || 'http://127.0.0.1:11434').replace(/\/+$/, '');
|
|
140
|
+
const res = await fetch(`${host}/api/tags`, {
|
|
141
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
142
|
+
});
|
|
143
|
+
if (!res.ok) return [];
|
|
144
|
+
const data = await res.json() as { models?: Array<{ name?: string }> };
|
|
145
|
+
if (Array.isArray(data.models)) {
|
|
146
|
+
return data.models
|
|
147
|
+
.map((m) => (m.name || '').replace(/:latest$/, ''))
|
|
148
|
+
.filter(Boolean);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (norm === 'openai') {
|
|
153
|
+
if (!apiKey) return [];
|
|
154
|
+
const host = (baseUrl || 'https://api.openai.com/v1').replace(/\/+$/, '');
|
|
155
|
+
const res = await fetch(`${host}/models`, {
|
|
156
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
157
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
158
|
+
});
|
|
159
|
+
if (!res.ok) return [];
|
|
160
|
+
const data = await res.json() as { data?: Array<{ id?: string }> };
|
|
161
|
+
if (Array.isArray(data.data)) {
|
|
162
|
+
return data.data
|
|
163
|
+
.map((m) => m.id || '')
|
|
164
|
+
.filter((id) => /^(gpt|o1|o3|chatgpt)/i.test(id))
|
|
165
|
+
.sort();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (norm === 'gemini') {
|
|
170
|
+
if (!apiKey) return [];
|
|
171
|
+
const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}`, {
|
|
172
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
173
|
+
});
|
|
174
|
+
if (!res.ok) return [];
|
|
175
|
+
const data = await res.json() as { models?: Array<{ name?: string; supportedGenerationMethods?: string[] }> };
|
|
176
|
+
if (Array.isArray(data.models)) {
|
|
177
|
+
return data.models
|
|
178
|
+
.filter((m) => m.supportedGenerationMethods?.includes('generateContent'))
|
|
179
|
+
.map((m) => (m.name || '').replace(/^models\//, ''))
|
|
180
|
+
.filter(Boolean)
|
|
181
|
+
.sort();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (norm === 'anthropic') {
|
|
186
|
+
if (!apiKey) return [];
|
|
187
|
+
const res = await fetch('https://api.anthropic.com/v1/models', {
|
|
188
|
+
headers: {
|
|
189
|
+
'x-api-key': apiKey,
|
|
190
|
+
'anthropic-version': '2023-06-01'
|
|
191
|
+
},
|
|
192
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
193
|
+
});
|
|
194
|
+
if (!res.ok) return [];
|
|
195
|
+
const data = await res.json() as { data?: Array<{ id?: string }> };
|
|
196
|
+
if (Array.isArray(data.data)) {
|
|
197
|
+
return data.data
|
|
198
|
+
.map((m) => m.id || '')
|
|
199
|
+
.filter(Boolean)
|
|
200
|
+
.sort();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (norm === 'deepseek') {
|
|
205
|
+
if (!apiKey) return [];
|
|
206
|
+
const res = await fetch('https://api.deepseek.com/models', {
|
|
207
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
208
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
209
|
+
});
|
|
210
|
+
if (!res.ok) return [];
|
|
211
|
+
const data = await res.json() as { data?: Array<{ id?: string }> };
|
|
212
|
+
if (Array.isArray(data.data)) {
|
|
213
|
+
return data.data.map((m) => m.id || '').filter(Boolean).sort();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (norm === 'openai-compatible') {
|
|
218
|
+
if (!baseUrl) return [];
|
|
219
|
+
const host = baseUrl.replace(/\/+$/, '');
|
|
220
|
+
const headers: Record<string, string> = {};
|
|
221
|
+
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
|
222
|
+
const res = await fetch(`${host}/models`, {
|
|
223
|
+
headers,
|
|
224
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
225
|
+
});
|
|
226
|
+
if (!res.ok) return [];
|
|
227
|
+
const data = await res.json() as { data?: Array<{ id?: string }> };
|
|
228
|
+
if (Array.isArray(data.data)) {
|
|
229
|
+
return data.data.map((m) => m.id || '').filter(Boolean).sort();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
// Network errors or timeouts silently fall back
|
|
234
|
+
}
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface ModelResolutionResult {
|
|
239
|
+
models: string[];
|
|
240
|
+
source: 'live' | 'cache' | 'catalogue';
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Resolve available models using dual-layer architecture:
|
|
245
|
+
* 1. Checks live query if forceRefresh is true.
|
|
246
|
+
* 2. Checks local cache if available and fresh.
|
|
247
|
+
* 3. Falls back to static curated catalogue merged with any cached items.
|
|
248
|
+
*/
|
|
249
|
+
export async function resolveAvailableModels(
|
|
250
|
+
provider: string,
|
|
251
|
+
config: { apiKey?: string; baseUrl?: string } = {},
|
|
252
|
+
forceRefresh = false
|
|
253
|
+
): Promise<ModelResolutionResult> {
|
|
254
|
+
const norm = normalizeProviderKey(provider);
|
|
255
|
+
const curated = getCuratedModels(norm);
|
|
256
|
+
|
|
257
|
+
if (forceRefresh) {
|
|
258
|
+
const live = await fetchLiveModels(norm, config);
|
|
259
|
+
if (live.length > 0) {
|
|
260
|
+
const combined = [...new Set([...live, ...curated])];
|
|
261
|
+
saveCachedModels(norm, combined);
|
|
262
|
+
return { models: combined, source: 'live' };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const cached = getCachedModels(norm);
|
|
267
|
+
if (cached && cached.length > 0) {
|
|
268
|
+
const combined = [...new Set([...cached, ...curated])];
|
|
269
|
+
return { models: combined, source: 'cache' };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Attempt lazy background discovery if credentials exist
|
|
273
|
+
if (config.apiKey || norm === 'ollama') {
|
|
274
|
+
const live = await fetchLiveModels(norm, config);
|
|
275
|
+
if (live.length > 0) {
|
|
276
|
+
const combined = [...new Set([...live, ...curated])];
|
|
277
|
+
saveCachedModels(norm, combined);
|
|
278
|
+
return { models: combined, source: 'live' };
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return { models: curated, source: 'catalogue' };
|
|
283
|
+
}
|
package/src/entrypoint.js
CHANGED
|
@@ -90,7 +90,8 @@ program
|
|
|
90
90
|
|
|
91
91
|
program
|
|
92
92
|
.command('config')
|
|
93
|
-
.
|
|
93
|
+
.alias('setup')
|
|
94
|
+
.description('Guided provider, endpoint, model and autonomy setup')
|
|
94
95
|
.option('-p, --provider <name>', 'Set LLM provider (openai, anthropic, gemini, muse, ollama, dashscope, deepseek)')
|
|
95
96
|
.option('-k, --apikey <key>', 'Set API key')
|
|
96
97
|
.option('-m, --model <name>', 'Set model name')
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const MODEL_CATALOGUE: Record<string, string[]>;
|
|
2
|
+
export const PROVIDER_LABELS: string[];
|
|
3
|
+
export const DEFAULT_MODEL_BY_PROVIDER: Record<string, string>;
|
|
4
|
+
export const UTILITY_MODEL_BY_PROVIDER: Record<string, string>;
|
|
5
|
+
export function allCatalogueModels(): string[];
|
package/src/modelCatalogue.js
CHANGED
|
@@ -34,22 +34,52 @@
|
|
|
34
34
|
* The label keys are a stable configuration format, not display sugar.
|
|
35
35
|
*/
|
|
36
36
|
export const MODEL_CATALOGUE = {
|
|
37
|
-
'DeepSeek': ['deepseek-v4-pro', 'deepseek-v4-flash'],
|
|
38
|
-
'Qwen': [
|
|
37
|
+
'DeepSeek': ['deepseek-chat', 'deepseek-reasoner', 'deepseek-v4-pro', 'deepseek-v4-flash'],
|
|
38
|
+
'Qwen': [
|
|
39
|
+
'qwen2.5-coder-32b-instruct',
|
|
40
|
+
'qwen2.5-72b-instruct',
|
|
41
|
+
'qwen-max',
|
|
42
|
+
'qwen-plus',
|
|
43
|
+
'qwen-turbo',
|
|
44
|
+
'qwen3.7-max',
|
|
45
|
+
'qwen3.7-plus',
|
|
46
|
+
'qwen3.6-flash'
|
|
47
|
+
],
|
|
39
48
|
// Anthropic ids use DASHES and carry no date suffix. A dotted id here is a
|
|
40
49
|
// bug, not a variant — see src/modelIds.js.
|
|
41
50
|
'Anthropic': [
|
|
51
|
+
'claude-3-7-sonnet',
|
|
52
|
+
'claude-3-5-sonnet',
|
|
53
|
+
'claude-3-5-haiku',
|
|
54
|
+
'claude-3-opus',
|
|
42
55
|
'claude-fable-5-1',
|
|
43
56
|
'claude-opus-5',
|
|
44
57
|
'claude-opus-4-8',
|
|
45
58
|
'claude-sonnet-5',
|
|
46
59
|
'claude-haiku-4-5'
|
|
47
60
|
],
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
'OpenAI': [
|
|
62
|
+
'gpt-4o',
|
|
63
|
+
'gpt-4o-mini',
|
|
64
|
+
'o3-mini',
|
|
65
|
+
'o1',
|
|
66
|
+
'gpt-6-astra',
|
|
67
|
+
'gpt-5.6-sol',
|
|
68
|
+
'gpt-5.6-terra',
|
|
69
|
+
'gpt-5.6-luna',
|
|
70
|
+
'gpt-5.4-mini'
|
|
71
|
+
],
|
|
72
|
+
'Gemini': [
|
|
73
|
+
'gemini-2.5-pro',
|
|
74
|
+
'gemini-2.5-flash',
|
|
75
|
+
'gemini-2.0-flash',
|
|
76
|
+
'gemini-1.5-pro',
|
|
77
|
+
'gemini-1.5-flash',
|
|
78
|
+
'gemini-3.6-flash',
|
|
79
|
+
'gemini-3.5-flash',
|
|
80
|
+
'gemini-3.1-pro',
|
|
81
|
+
'gemini-3.1-flash-lite'
|
|
82
|
+
],
|
|
53
83
|
// Standard first. The -contributor variants are cheaper because Meta trains
|
|
54
84
|
// on the user's prompts and completions, so they must be a choice made
|
|
55
85
|
// deliberately, never the one at the top of the list.
|
|
@@ -60,14 +90,15 @@ export const MODEL_CATALOGUE = {
|
|
|
60
90
|
'muse-spark-1.3-contributor',
|
|
61
91
|
'muse-spark-1.2-contributor'
|
|
62
92
|
],
|
|
63
|
-
'Ollama (Local)': ['llama3', 'mistral', 'gemma2', 'custom'],
|
|
93
|
+
'Ollama (Local)': ['llama3.3', 'qwen2.5-coder', 'deepseek-r1', 'llama3', 'mistral', 'gemma2', 'custom'],
|
|
64
94
|
'Alibaba (DashScope)': [
|
|
95
|
+
'qwen-max', 'qwen-plus', 'qwen-turbo', 'qwen2.5-coder-32b-instruct',
|
|
65
96
|
'qwen3.7-max', 'qwen3.7-plus', 'kimi-k2.7-code', 'kimi-k2.6',
|
|
66
97
|
'kimi-k2.5', 'kimi-k2-thinking', 'Moonshot-Kimi-K2-Instruct',
|
|
67
98
|
'deepseek-v4-flash', 'deepseek-v4-pro'
|
|
68
99
|
],
|
|
69
100
|
'Moonshot (Kimi)': ['kimi-k3', 'kimi-k2.7-code', 'kimi-k2.6'],
|
|
70
|
-
'xAI (Grok)': ['grok-4.5', 'grok-latest']
|
|
101
|
+
'xAI (Grok)': ['grok-3', 'grok-3-mini', 'grok-2-1212', 'grok-4.5', 'grok-latest']
|
|
71
102
|
};
|
|
72
103
|
|
|
73
104
|
/** Provider labels in picker order. */
|
|
@@ -75,17 +106,17 @@ export const PROVIDER_LABELS = Object.keys(MODEL_CATALOGUE);
|
|
|
75
106
|
|
|
76
107
|
|
|
77
108
|
export const DEFAULT_MODEL_BY_PROVIDER = {
|
|
78
|
-
deepseek: 'deepseek-
|
|
79
|
-
qwen: '
|
|
80
|
-
'dashscope-byok': '
|
|
81
|
-
dashscope: '
|
|
109
|
+
deepseek: 'deepseek-chat',
|
|
110
|
+
qwen: 'qwen2.5-coder-32b-instruct',
|
|
111
|
+
'dashscope-byok': 'qwen-plus',
|
|
112
|
+
dashscope: 'qwen-plus',
|
|
82
113
|
muse: 'muse-spark-1.3',
|
|
83
|
-
anthropic: 'claude-
|
|
84
|
-
openai: 'gpt-
|
|
85
|
-
gemini: 'gemini-
|
|
114
|
+
anthropic: 'claude-3-7-sonnet',
|
|
115
|
+
openai: 'gpt-4o',
|
|
116
|
+
gemini: 'gemini-2.5-pro',
|
|
86
117
|
moonshot: 'kimi-k3',
|
|
87
|
-
xai: 'grok-
|
|
88
|
-
ollama: 'llama3',
|
|
118
|
+
xai: 'grok-3',
|
|
119
|
+
ollama: 'llama3.3',
|
|
89
120
|
'openai-compatible': ''
|
|
90
121
|
};
|
|
91
122
|
|
|
@@ -8,6 +8,10 @@ import { LLMProvider } from './base.js';
|
|
|
8
8
|
// full capability, none of the token cost. Older models fall back to the
|
|
9
9
|
// client-side progressive disclosure in src/toolPacks.js.
|
|
10
10
|
const TOOL_SEARCH_MODELS = [
|
|
11
|
+
'claude-3-7-sonnet',
|
|
12
|
+
'claude-3-5-sonnet',
|
|
13
|
+
'claude-3-5-haiku',
|
|
14
|
+
'claude-3-opus',
|
|
11
15
|
'claude-fable-5',
|
|
12
16
|
'claude-mythos-5',
|
|
13
17
|
'claude-opus-5',
|
package/src/sandbox.js
CHANGED
|
@@ -298,6 +298,10 @@ export class Sandbox {
|
|
|
298
298
|
return { allowed: true, risk: 'safe' };
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
if (toolName === 'creative_apps') {
|
|
302
|
+
return { allowed: true, risk: args.action === 'status' ? 'safe' : 'dangerous' };
|
|
303
|
+
}
|
|
304
|
+
|
|
301
305
|
if (toolName === 'game_asset_fetch' && args.action === 'download_asset') {
|
|
302
306
|
if (!args.url || this.isInternalUrl(args.url)) {
|
|
303
307
|
return { allowed: false, reason: 'Cannot download from an internal or invalid URL', risk: 'forbidden' };
|
package/src/toolPacks.js
CHANGED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { runStudioEdit } from '../creative/studioScripts.js';
|
|
2
|
+
import { creativeAppStatus } from '../creative/apps.js';
|
|
3
|
+
import { runBlenderBatch } from '../creative/blenderBatch.js';
|
|
4
|
+
import { runStudioPipeline } from '../creative/pipeline.js';
|
|
5
|
+
export const creativeApps = {
|
|
6
|
+
schema: {
|
|
7
|
+
name: 'creative_apps',
|
|
8
|
+
description: 'Inspect installed creative applications, run bounded creative batches, or orchestrate multi-stage creative pipelines. Blender batch creates and edits 3D scenes (primitives, materials, lights, cameras, render) and saves a new .blend. studio_edit uses native scripting: Photo Studio (resize/rotate/layers/flatten -> PSD/PNG/JPG/WEBP), Video Studio (sequences/imports/timeline cuts/markers -> PRPROJ), Motion Studio (compositions/imports/text -> AEP). studio_pipeline executes sequential coordinated multi-host stages. Requires workspace scoping.',
|
|
9
|
+
parameters: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
action: { type: 'string', enum: ['status', 'blender_batch', 'studio_edit', 'studio_pipeline'] },
|
|
13
|
+
host: { type: 'string', enum: ['photoshop', 'premiere', 'after-effects', 'photo-studio', 'video-studio', 'motion-studio'] },
|
|
14
|
+
input: { type: 'string', description: 'Existing .blend, image/document, or project file inside the workspace.' },
|
|
15
|
+
output: { type: 'string', description: 'New .blend, PSD/PNG, PRPROJ, or AEP file inside the workspace; never overwrites.' },
|
|
16
|
+
stages: {
|
|
17
|
+
type: 'array',
|
|
18
|
+
description: 'Stages for studio_pipeline: array of { id, type: "3d_batch"|"studio_batch", config }',
|
|
19
|
+
items: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
id: { type: 'string', description: 'Stage identifier' },
|
|
23
|
+
type: { type: 'string', enum: ['3d_batch', 'studio_batch'], description: 'Stage batch execution type' },
|
|
24
|
+
config: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
description: 'Stage payload configuration',
|
|
27
|
+
properties: {
|
|
28
|
+
host: { type: 'string' },
|
|
29
|
+
input: { type: 'string' },
|
|
30
|
+
output: { type: 'string' },
|
|
31
|
+
actions: {
|
|
32
|
+
type: 'array',
|
|
33
|
+
items: {
|
|
34
|
+
type: 'object',
|
|
35
|
+
properties: {
|
|
36
|
+
type: { type: 'string' }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
required: ['type']
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
actions: { type: 'array', maxItems: 64, items: {
|
|
47
|
+
type: 'object', properties: {
|
|
48
|
+
type: { type: 'string', enum: [
|
|
49
|
+
'add_cube', 'add_sphere', 'add_cylinder', 'add_plane', 'add_camera', 'add_light',
|
|
50
|
+
'material', 'render', 'transform', 'resize', 'rotate', 'crop', 'layer', 'new_layer', 'flatten',
|
|
51
|
+
'composition', 'sequence', 'import', 'text', 'marker', 'cut', 'solid', 'adjust_color'
|
|
52
|
+
] },
|
|
53
|
+
name: { type: 'string' },
|
|
54
|
+
width: { type: 'number' }, height: { type: 'number' },
|
|
55
|
+
angle: { type: 'number' }, index: { type: 'integer' },
|
|
56
|
+
opacity: { type: 'number' }, visible: { type: 'boolean' },
|
|
57
|
+
duration: { type: 'number' }, fps: { type: 'number' },
|
|
58
|
+
path: { type: 'string' }, start: { type: 'number' }, text: { type: 'string' },
|
|
59
|
+
location: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3 },
|
|
60
|
+
scale: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3 },
|
|
61
|
+
rotation: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3 },
|
|
62
|
+
radius: { type: 'number' }, depth: { type: 'number' },
|
|
63
|
+
lightType: { type: 'string', enum: ['POINT', 'SUN', 'SPOT', 'AREA'] },
|
|
64
|
+
energy: { type: 'number' },
|
|
65
|
+
color: { type: 'array', items: { type: 'number' }, minItems: 4, maxItems: 4 },
|
|
66
|
+
outputPath: { type: 'string' },
|
|
67
|
+
resolution: { type: 'array', items: { type: 'number' }, minItems: 2, maxItems: 2 },
|
|
68
|
+
comment: { type: 'string' },
|
|
69
|
+
track: { type: 'integer' },
|
|
70
|
+
time: { type: 'number' },
|
|
71
|
+
left: { type: 'number' },
|
|
72
|
+
top: { type: 'number' },
|
|
73
|
+
blendMode: { type: 'string' },
|
|
74
|
+
size: { type: 'number' },
|
|
75
|
+
font: { type: 'string' },
|
|
76
|
+
x: { type: 'number' },
|
|
77
|
+
y: { type: 'number' },
|
|
78
|
+
brightness: { type: 'number' },
|
|
79
|
+
contrast: { type: 'number' },
|
|
80
|
+
roughness: { type: 'number' },
|
|
81
|
+
metallic: { type: 'number' }
|
|
82
|
+
}, required: ['type'], additionalProperties: false
|
|
83
|
+
} }
|
|
84
|
+
}, required: ['action'], additionalProperties: false
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
async execute(value, context = {}) {
|
|
88
|
+
try {
|
|
89
|
+
if (!value || typeof value !== 'object')
|
|
90
|
+
throw new Error('Expected creative app arguments');
|
|
91
|
+
const args = value;
|
|
92
|
+
if (args.action === 'status')
|
|
93
|
+
return { success: true, apps: await creativeAppStatus() };
|
|
94
|
+
if (args.action === 'studio_edit')
|
|
95
|
+
return await runStudioEdit(args, process.cwd(), context.signal);
|
|
96
|
+
if (args.action === 'blender_batch')
|
|
97
|
+
return await runBlenderBatch(args, process.cwd(), context.signal);
|
|
98
|
+
if (args.action === 'studio_pipeline')
|
|
99
|
+
return await runStudioPipeline(args, process.cwd(), context.signal);
|
|
100
|
+
throw new Error('Unknown creative app action');
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { runStudioEdit } from '../creative/studioScripts.js';
|
|
2
|
+
import { creativeAppStatus } from '../creative/apps.js';
|
|
3
|
+
import { runBlenderBatch } from '../creative/blenderBatch.js';
|
|
4
|
+
import { runStudioPipeline } from '../creative/pipeline.js';
|
|
5
|
+
|
|
6
|
+
export const creativeApps = {
|
|
7
|
+
schema: {
|
|
8
|
+
name: 'creative_apps',
|
|
9
|
+
description: 'Inspect installed creative applications, run bounded creative batches, or orchestrate multi-stage creative pipelines. Blender batch creates and edits 3D scenes (primitives, materials, lights, cameras, render) and saves a new .blend. studio_edit uses native scripting: Photo Studio (resize/rotate/layers/flatten -> PSD/PNG/JPG/WEBP), Video Studio (sequences/imports/timeline cuts/markers -> PRPROJ), Motion Studio (compositions/imports/text -> AEP). studio_pipeline executes sequential coordinated multi-host stages. Requires workspace scoping.',
|
|
10
|
+
parameters: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
action: { type: 'string', enum: ['status', 'blender_batch', 'studio_edit', 'studio_pipeline'] },
|
|
14
|
+
host: { type: 'string', enum: ['photoshop', 'premiere', 'after-effects', 'photo-studio', 'video-studio', 'motion-studio'] },
|
|
15
|
+
input: { type: 'string', description: 'Existing .blend, image/document, or project file inside the workspace.' },
|
|
16
|
+
output: { type: 'string', description: 'New .blend, PSD/PNG, PRPROJ, or AEP file inside the workspace; never overwrites.' },
|
|
17
|
+
stages: {
|
|
18
|
+
type: 'array',
|
|
19
|
+
description: 'Stages for studio_pipeline: array of { id, type: "3d_batch"|"studio_batch", config }',
|
|
20
|
+
items: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
id: { type: 'string', description: 'Stage identifier' },
|
|
24
|
+
type: { type: 'string', enum: ['3d_batch', 'studio_batch'], description: 'Stage batch execution type' },
|
|
25
|
+
config: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
description: 'Stage payload configuration',
|
|
28
|
+
properties: {
|
|
29
|
+
host: { type: 'string' },
|
|
30
|
+
input: { type: 'string' },
|
|
31
|
+
output: { type: 'string' },
|
|
32
|
+
actions: {
|
|
33
|
+
type: 'array',
|
|
34
|
+
items: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
type: { type: 'string' }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
required: ['type']
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
actions: { type: 'array', maxItems: 64, items: {
|
|
48
|
+
type: 'object', properties: {
|
|
49
|
+
type: { type: 'string', enum: [
|
|
50
|
+
'add_cube', 'add_sphere', 'add_cylinder', 'add_plane', 'add_camera', 'add_light',
|
|
51
|
+
'material', 'render', 'transform', 'resize', 'rotate', 'crop', 'layer', 'new_layer', 'flatten',
|
|
52
|
+
'composition', 'sequence', 'import', 'text', 'marker', 'cut', 'solid', 'adjust_color'
|
|
53
|
+
] },
|
|
54
|
+
name: { type: 'string' },
|
|
55
|
+
width: { type: 'number' }, height: { type: 'number' },
|
|
56
|
+
angle: { type: 'number' }, index: { type: 'integer' },
|
|
57
|
+
opacity: { type: 'number' }, visible: { type: 'boolean' },
|
|
58
|
+
duration: { type: 'number' }, fps: { type: 'number' },
|
|
59
|
+
path: { type: 'string' }, start: { type: 'number' }, text: { type: 'string' },
|
|
60
|
+
location: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3 },
|
|
61
|
+
scale: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3 },
|
|
62
|
+
rotation: { type: 'array', items: { type: 'number' }, minItems: 3, maxItems: 3 },
|
|
63
|
+
radius: { type: 'number' }, depth: { type: 'number' },
|
|
64
|
+
lightType: { type: 'string', enum: ['POINT', 'SUN', 'SPOT', 'AREA'] },
|
|
65
|
+
energy: { type: 'number' },
|
|
66
|
+
color: { type: 'array', items: { type: 'number' }, minItems: 4, maxItems: 4 },
|
|
67
|
+
outputPath: { type: 'string' },
|
|
68
|
+
resolution: { type: 'array', items: { type: 'number' }, minItems: 2, maxItems: 2 },
|
|
69
|
+
comment: { type: 'string' },
|
|
70
|
+
track: { type: 'integer' },
|
|
71
|
+
time: { type: 'number' },
|
|
72
|
+
left: { type: 'number' },
|
|
73
|
+
top: { type: 'number' },
|
|
74
|
+
blendMode: { type: 'string' },
|
|
75
|
+
size: { type: 'number' },
|
|
76
|
+
font: { type: 'string' },
|
|
77
|
+
x: { type: 'number' },
|
|
78
|
+
y: { type: 'number' },
|
|
79
|
+
brightness: { type: 'number' },
|
|
80
|
+
contrast: { type: 'number' },
|
|
81
|
+
roughness: { type: 'number' },
|
|
82
|
+
metallic: { type: 'number' }
|
|
83
|
+
}, required: ['type'], additionalProperties: false
|
|
84
|
+
} }
|
|
85
|
+
}, required: ['action'], additionalProperties: false
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
async execute(value: unknown, context: { signal?: AbortSignal } = {}) {
|
|
89
|
+
try {
|
|
90
|
+
if (!value || typeof value !== 'object') throw new Error('Expected creative app arguments');
|
|
91
|
+
const args = value as Record<string, unknown>;
|
|
92
|
+
if (args.action === 'status') return { success: true, apps: await creativeAppStatus() };
|
|
93
|
+
if (args.action === 'studio_edit') return await runStudioEdit(args, process.cwd(), context.signal);
|
|
94
|
+
if (args.action === 'blender_batch') return await runBlenderBatch(args, process.cwd(), context.signal);
|
|
95
|
+
if (args.action === 'studio_pipeline') return await runStudioPipeline(args, process.cwd(), context.signal);
|
|
96
|
+
throw new Error('Unknown creative app action');
|
|
97
|
+
} catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error) }; }
|
|
98
|
+
}
|
|
99
|
+
};
|
package/src/tools/registry.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { creativeApps } from './creativeApps.js';
|
|
1
2
|
import { sysRun, sysRunBg } from './sysRun.js';
|
|
2
3
|
import { fsRead } from './fsRead.js';
|
|
3
4
|
import { fsWrite } from './fsWrite.js';
|
|
@@ -119,7 +120,7 @@ const TOOL_MODULES = [
|
|
|
119
120
|
gameAssetHarvester, gameAudioSynth, gameVfxEmitterGen,
|
|
120
121
|
gameShaderForge, gameSceneBuilder, gameEntitySpawner,
|
|
121
122
|
gameTilemapGenerator, gameUiHudBuilder, gameEconomyBalancer,
|
|
122
|
-
gameDataForge, gameWebRuntimeInspector, gameCanvasAudit, readNativeSkill, createSkill
|
|
123
|
+
gameDataForge, gameWebRuntimeInspector, gameCanvasAudit, readNativeSkill, createSkill, creativeApps
|
|
123
124
|
];
|
|
124
125
|
|
|
125
126
|
// Base static definitions (backwards compatible). Order is preserved from the
|