shraga 0.1.61 → 0.1.63
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/dist/client/assets/{index-QguraA7L.js → index-6SI-XJ2-.js} +62 -62
- package/dist/client/assets/{index-BeVHw9x2.css → index-uvYJNXVj.css} +1 -1
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/src/client/components/ConfigPanel.tsx +21 -9
- package/src/server/boot.ts +1 -1
- package/src/server/claude-usage.ts +75 -13
- package/src/server/claude.ts +7 -8
- package/src/server/directives.ts +29 -8
- package/src/server/engine/index.ts +22 -0
package/src/server/directives.ts
CHANGED
|
@@ -27,11 +27,32 @@ const DIRECTIVE_KEYS = ['model', 'turns', 'thinking', 'think', 'effort', 'engine
|
|
|
27
27
|
|
|
28
28
|
/** MODEL_ALIASES only covers the bare Anthropic shorthands. A `provider/model` id
|
|
29
29
|
* (`cursor/composer-2.5`, `openai/gpt-5.6`) is already concrete — gating it on the alias table
|
|
30
|
-
* silently dropped it and ran the instance default instead.
|
|
31
|
-
*
|
|
32
|
-
* second bracket group
|
|
30
|
+
* silently dropped it and ran the instance default instead. As a bare positional it is honoured
|
|
31
|
+
* only when a REGISTERED engine actually advertises it (see the resolver below) — an arbitrary
|
|
32
|
+
* `[src/foo.ts]` stays prompt text, which is what a second bracket group must not swallow. */
|
|
33
33
|
const isQualifiedModel = (v: string) => /^[a-z0-9._-]+\/[a-z0-9./_-]+$/.test(v);
|
|
34
34
|
|
|
35
|
+
/** Resolves a model token the alias table doesn't know against the REGISTERED engines' own model
|
|
36
|
+
* lists, and reports which engine owns it. Without this, `[composer-2.5]` (an agentx model) was
|
|
37
|
+
* warned about and dropped, so the turn silently ran on the previous engine/model. Injected by
|
|
38
|
+
* `initEngines()` — directives.ts stays pure and dependency-free for CE and for tests. */
|
|
39
|
+
export type ModelResolver = (token: string) => { model: string; engine?: string } | null;
|
|
40
|
+
let modelResolver: ModelResolver | null = null;
|
|
41
|
+
export function setModelResolver(fn: ModelResolver | null): void { modelResolver = fn; }
|
|
42
|
+
|
|
43
|
+
/** Alias table first (canonical shorthands win), then the engine registry. */
|
|
44
|
+
function resolveModelToken(d: Directives, val: string): boolean {
|
|
45
|
+
if (MODEL_ALIASES[val]) { d.model = MODEL_ALIASES[val]; return true; }
|
|
46
|
+
const hit = modelResolver?.(val);
|
|
47
|
+
if (hit) {
|
|
48
|
+
d.model = hit.model;
|
|
49
|
+
// An engine-owned model implies its engine — but never override an explicit `[engine:x]`.
|
|
50
|
+
if (hit.engine && !d.engine) d.engine = hit.engine;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
35
56
|
/** Does a bracket group look like directives (vs. prompt text that happens to start with `[`)?
|
|
36
57
|
* Every token must be a known key:value or a known positional, else we leave the group alone. */
|
|
37
58
|
function isDirectiveGroup(raw: string): boolean {
|
|
@@ -41,7 +62,7 @@ function isDirectiveGroup(raw: string): boolean {
|
|
|
41
62
|
const colonIdx = t.indexOf(':');
|
|
42
63
|
if (colonIdx !== -1) return DIRECTIVE_KEYS.includes(t.slice(0, colonIdx).trim().toLowerCase());
|
|
43
64
|
const v = t.toLowerCase();
|
|
44
|
-
return !!MODEL_ALIASES[v] || /^\d+$/.test(v) || ['think', 'adaptive', 'nothink', 'nothinking'].includes(v);
|
|
65
|
+
return !!MODEL_ALIASES[v] || !!modelResolver?.(v) || /^\d+$/.test(v) || ['think', 'adaptive', 'nothink', 'nothinking'].includes(v);
|
|
45
66
|
});
|
|
46
67
|
}
|
|
47
68
|
|
|
@@ -82,8 +103,8 @@ export function parseDirectives(text: string): ParsedPrompt {
|
|
|
82
103
|
applyDirective(directives, key, val);
|
|
83
104
|
} else {
|
|
84
105
|
const val = t.toLowerCase();
|
|
85
|
-
if (positionalIndex === 0 &&
|
|
86
|
-
|
|
106
|
+
if (positionalIndex === 0 && resolveModelToken(directives, val)) {
|
|
107
|
+
// handled
|
|
87
108
|
} else if (positionalIndex <= 1 && /^\d+$/.test(val)) {
|
|
88
109
|
directives.turns = parseInt(val, 10);
|
|
89
110
|
} else if (['think', 'adaptive'].includes(val)) {
|
|
@@ -105,8 +126,8 @@ export function parseDirectives(text: string): ParsedPrompt {
|
|
|
105
126
|
function applyDirective(d: Directives, key: string, val: string) {
|
|
106
127
|
switch (key) {
|
|
107
128
|
case 'model':
|
|
108
|
-
if (
|
|
109
|
-
|
|
129
|
+
if (resolveModelToken(d, val)) break;
|
|
130
|
+
if (isQualifiedModel(val)) d.model = val;
|
|
110
131
|
else console.warn(`[directives] Unknown model alias: "${val}"`);
|
|
111
132
|
break;
|
|
112
133
|
case 'turns':
|
|
@@ -4,6 +4,7 @@ export { ClaudeCodeEngine } from './claude-code.ts';
|
|
|
4
4
|
|
|
5
5
|
import { registerEngine, getEngine, getAvailableEngines, hasEngine } from './registry.ts';
|
|
6
6
|
import { ClaudeCodeEngine } from './claude-code.ts';
|
|
7
|
+
import { setModelResolver } from '../directives.ts';
|
|
7
8
|
|
|
8
9
|
let _initialized = false;
|
|
9
10
|
|
|
@@ -17,6 +18,27 @@ export async function initEngines(): Promise<void> {
|
|
|
17
18
|
// only; a directive requesting an unregistered engine falls back to claude-code (resolveAndGetEngine).
|
|
18
19
|
registerEngine(new ClaudeCodeEngine());
|
|
19
20
|
|
|
21
|
+
// Let `[<model>]` name ANY registered engine's model (e.g. `[composer-2.5]`) and imply its engine.
|
|
22
|
+
// Resolved lazily on each parse so engines an add-on registers later are covered too.
|
|
23
|
+
// `[composer-2.5]` must reach the engine's real id (`cursor/composer-2.5`), so an exact match is
|
|
24
|
+
// tried first, then the bare suffix after the provider prefix — and ONLY when it is unambiguous
|
|
25
|
+
// across engines, so a name two engines share is never silently routed to whichever came first.
|
|
26
|
+
setModelResolver((token) => {
|
|
27
|
+
const t = token.toLowerCase();
|
|
28
|
+
const hits: { model: string; engine: string }[] = [];
|
|
29
|
+
for (const name of getAvailableEngines()) {
|
|
30
|
+
for (const m of getEngine(name).getModels()) {
|
|
31
|
+
if (!m.value) continue;
|
|
32
|
+
const v = m.value.toLowerCase();
|
|
33
|
+
if (v === t) return { model: m.value, engine: name };
|
|
34
|
+
if (v.slice(v.lastIndexOf('/') + 1) === t) hits.push({ model: m.value, engine: name });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (hits.length === 1) return hits[0];
|
|
38
|
+
if (hits.length > 1) console.warn(`[directives] Ambiguous model "${token}" (${hits.map((h) => `${h.engine}:${h.model}`).join(', ')}) — use the full id`);
|
|
39
|
+
return null;
|
|
40
|
+
});
|
|
41
|
+
|
|
20
42
|
console.log(`[engine] Available engines: ${getAvailableEngines().join(', ')}`);
|
|
21
43
|
}
|
|
22
44
|
|