@livx.cc/agentx 0.99.65 → 0.99.67

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.
@@ -0,0 +1,74 @@
1
+ interface Directives {
2
+ model?: string;
3
+ turns?: number;
4
+ thinking?: 'adaptive' | 'enabled' | 'disabled';
5
+ effort?: 'low' | 'medium' | 'high' | 'max';
6
+ engine?: string;
7
+ /** Session resume for this conversation (host-defined). */
8
+ resume?: boolean;
9
+ }
10
+ interface ParsedPrompt {
11
+ prompt: string;
12
+ directives: Directives;
13
+ /** An EXPLICIT model selection nothing could resolve. The caller must fail the turn, never run the old model. */
14
+ unresolvedModel?: string;
15
+ /** When `unresolvedModel` matched several catalog ids: the candidates (use the full id). */
16
+ ambiguous?: string[];
17
+ }
18
+ type ModelHit = {
19
+ model: string;
20
+ engine?: string;
21
+ };
22
+ /** Resolves a model token against a host catalog. `{ ambiguous }` = several ids match; never guess. */
23
+ type ModelResolver = (token: string) => ModelHit | {
24
+ ambiguous: string[];
25
+ } | null;
26
+ /** Vendor shorthand → provider id: `[cc-opus]` / `[cc/opus]` = `claude-code/opus`. Extend per host via options. */
27
+ declare const VENDOR_SHORTHAND: Record<string, string>;
28
+ declare const DIRECTIVE_KEYS: string[];
29
+ interface DirectiveOptions {
30
+ /** Host catalog lookup. Default: the one set by `setModelResolver` (none). */
31
+ resolveModel: ModelResolver | null;
32
+ /** Try the catalog before the alias table (Bare: `[opus]` = the catalog's `claude-code/opus`). */
33
+ resolverFirst: boolean;
34
+ aliases: Record<string, string>;
35
+ vendors: Record<string, string>;
36
+ /** Keys this host honours; positional turns/thinking count only when `turns`/`thinking` are listed. */
37
+ keys: string[];
38
+ /** Every group, the first included, must look like directives (else it stays prompt text). */
39
+ strict: boolean;
40
+ /** Resolve to catalog ids ONLY (Bare): no alias-table fallback, a full id the catalog lacks is refused, and a
41
+ * bare word matches an id's tail only when it is a known alias or carries a digit (`[auto]` stays prose). */
42
+ catalogOnly: boolean;
43
+ warn: (msg: string) => void;
44
+ }
45
+ /** Process-wide default resolver (Shraga's engine registry injects it once). Per-call options override. */
46
+ declare function setModelResolver(fn: ModelResolver | null): void;
47
+ /** `cc-opus` / `cc/opus` → `claude-code/opus`; a full `claude-code/opus` is returned as is. Else null. */
48
+ declare function expandVendor(token: string, vendors?: Record<string, string>): string | null;
49
+ /** Resolve one model token: vendor shorthand → catalog exact id; else alias table + catalog in the configured order. */
50
+ declare function resolveModelToken(token: string, o?: Partial<DirectiveOptions>): ModelHit | {
51
+ ambiguous: string[];
52
+ } | null;
53
+ /** Does a bracket group look like directives (vs. prompt text that happens to start with `[`)? */
54
+ declare function isDirectiveGroup(raw: string, o?: Partial<DirectiveOptions>): boolean;
55
+ /** Splits the leading `[..]` directive groups off a prompt — EVERY consecutive leading group. */
56
+ declare function splitDirectiveGroups(text: string, o?: Partial<DirectiveOptions>): {
57
+ groups: string[];
58
+ body: string;
59
+ };
60
+ declare function parseDirectives(text: string, o?: Partial<DirectiveOptions>): ParsedPrompt;
61
+ /** Engine/provider → the model ids it advertises. */
62
+ interface EngineModels {
63
+ name: string;
64
+ models: {
65
+ value: string;
66
+ }[];
67
+ }
68
+ /**
69
+ * A resolver over host catalogs: exact id first, then the bare suffix after the provider prefix —
70
+ * ONLY when unambiguous across engines (`{ ambiguous }` otherwise, never the first match).
71
+ */
72
+ declare function makeModelResolver(engines: () => EngineModels[]): ModelResolver;
73
+
74
+ export { DIRECTIVE_KEYS, type DirectiveOptions, type Directives, type EngineModels, type ModelHit, type ModelResolver, type ParsedPrompt, VENDOR_SHORTHAND, expandVendor, isDirectiveGroup, makeModelResolver, parseDirectives, resolveModelToken, setModelResolver, splitDirectiveGroups };
@@ -0,0 +1,192 @@
1
+ // src/models.ts
2
+ var MODEL_ALIASES = {
3
+ fable: "claude-fable-5",
4
+ "fable-5": "claude-fable-5",
5
+ opus: "claude-opus-5-5",
6
+ "opus-5-5": "claude-opus-5-5",
7
+ "opus-5": "claude-opus-5",
8
+ "opus-4-8": "claude-opus-4-8",
9
+ "opus-4-7": "claude-opus-4-7",
10
+ "opus-4-6": "claude-opus-4-6",
11
+ sonnet: "claude-sonnet-4-6",
12
+ haiku: "claude-haiku-4-5-20251001"
13
+ };
14
+
15
+ // src/directives.ts
16
+ var VENDOR_SHORTHAND = { cc: "claude-code", cur: "cursor", oa: "openai" };
17
+ var DIRECTIVE_KEYS = ["model", "turns", "thinking", "think", "effort", "engine", "resume"];
18
+ var defaultResolver = null;
19
+ function setModelResolver(fn) {
20
+ defaultResolver = fn;
21
+ }
22
+ var opt = (o) => ({
23
+ resolveModel: defaultResolver,
24
+ resolverFirst: false,
25
+ aliases: MODEL_ALIASES,
26
+ vendors: VENDOR_SHORTHAND,
27
+ keys: DIRECTIVE_KEYS,
28
+ strict: false,
29
+ catalogOnly: false,
30
+ warn: (m) => console.warn(m),
31
+ ...o
32
+ });
33
+ var DIRECTIVE_RE = /^\s*\[([^\]]*)\](?=\s|$)\s*([\s\S]*)/;
34
+ var isQualifiedModel = (v) => /^[a-z0-9._-]+\/[a-z0-9./_-]+$/.test(v);
35
+ function expandVendor(token, vendors = VENDOR_SHORTHAND) {
36
+ const v = token.trim().toLowerCase();
37
+ const m = v.match(/^([a-z0-9]+)[-/](.+)$/);
38
+ if (m && vendors[m[1]]) return `${vendors[m[1]]}/${m[2]}`;
39
+ const slash = v.indexOf("/");
40
+ return slash > 0 && Object.values(vendors).includes(v.slice(0, slash)) ? v : null;
41
+ }
42
+ function resolveModelToken(token, o) {
43
+ const p = opt(o);
44
+ const v = token.trim().toLowerCase();
45
+ const vendored = expandVendor(v, p.vendors);
46
+ if (vendored) return p.resolveModel ? p.resolveModel(vendored) : p.catalogOnly ? null : { model: vendored };
47
+ if (p.catalogOnly) return v.includes("/") || p.aliases[v] || /\d/.test(v) ? p.resolveModel?.(v) ?? null : null;
48
+ const alias = p.aliases[v] ? { model: p.aliases[v] } : null;
49
+ if (!p.resolverFirst) return alias ?? p.resolveModel?.(v) ?? null;
50
+ const hit = p.resolveModel?.(v) ?? null;
51
+ return hit && "model" in hit ? hit : alias ?? hit;
52
+ }
53
+ var THINK = ["think", "adaptive", "nothink", "nothinking"];
54
+ function isDirectiveGroup(raw, o) {
55
+ const p = opt(o);
56
+ const tokens = raw.split(",").map((t) => t.trim()).filter(Boolean);
57
+ if (!tokens.length) return false;
58
+ return tokens.every((t) => {
59
+ const i = t.indexOf(":");
60
+ if (i !== -1) return p.keys.includes(t.slice(0, i).trim().toLowerCase());
61
+ const v = t.toLowerCase();
62
+ return !!resolveModelToken(v, p) || p.catalogOnly && !!p.aliases[v] || p.keys.includes("turns") && /^\d+$/.test(v) || p.keys.includes("thinking") && THINK.includes(v);
63
+ });
64
+ }
65
+ function splitDirectiveGroups(text, o) {
66
+ const p = opt(o);
67
+ let rest = text;
68
+ const groups = [];
69
+ for (; ; ) {
70
+ const m = rest.match(DIRECTIVE_RE);
71
+ if (!m) break;
72
+ const g = m[1].trim();
73
+ if ((groups.length || p.strict) && g && !isDirectiveGroup(g, p)) break;
74
+ groups.push(g);
75
+ rest = m[2];
76
+ }
77
+ return { groups, body: rest };
78
+ }
79
+ function parseDirectives(text, o) {
80
+ const p = opt(o);
81
+ const { groups, body } = splitDirectiveGroups(text, p);
82
+ if (!groups.length) return { prompt: text, directives: {} };
83
+ const prompt = body.trim();
84
+ const raw = groups.filter(Boolean).join(",");
85
+ if (!raw) return { prompt, directives: {} };
86
+ const d = {};
87
+ const out = { prompt, directives: d };
88
+ const tokens = raw.split(",");
89
+ const proven = tokens.some((t) => {
90
+ const i = t.indexOf(":");
91
+ return i !== -1 && p.keys.includes(t.slice(0, i).trim().toLowerCase());
92
+ });
93
+ const fail = (tok, hit) => {
94
+ out.unresolvedModel ??= tok;
95
+ if (hit && "ambiguous" in hit) out.ambiguous ??= hit.ambiguous;
96
+ };
97
+ const setModel = (val) => {
98
+ const hit = resolveModelToken(val, p);
99
+ if (!hit || !("model" in hit)) return hit;
100
+ d.model = hit.model;
101
+ if (hit.engine && !d.engine) d.engine = hit.engine;
102
+ return true;
103
+ };
104
+ let pos = 0;
105
+ for (const token of tokens) {
106
+ const t = token.trim();
107
+ if (!t) continue;
108
+ const i = t.indexOf(":");
109
+ if (i !== -1) {
110
+ const key = t.slice(0, i).trim().toLowerCase(), val2 = t.slice(i + 1).trim().toLowerCase();
111
+ if (!p.keys.includes(key)) {
112
+ p.warn(`[directives] Unknown directive key: "${key}"`);
113
+ continue;
114
+ }
115
+ if (key === "model") {
116
+ const r2 = setModel(val2);
117
+ if (r2 === true) continue;
118
+ if (!r2 && isQualifiedModel(val2) && !p.catalogOnly) {
119
+ d.model = val2;
120
+ continue;
121
+ }
122
+ fail(val2, r2);
123
+ } else applyKey(d, key, val2, p.warn);
124
+ continue;
125
+ }
126
+ const val = t.toLowerCase();
127
+ const r = pos === 0 ? setModel(val) : null;
128
+ if (r === true) {
129
+ } else if (pos <= 1 && /^\d+$/.test(val) && p.keys.includes("turns")) d.turns = parseInt(val, 10);
130
+ else if (["think", "adaptive"].includes(val) && p.keys.includes("thinking")) d.thinking = "adaptive";
131
+ else if (["nothink", "nothinking"].includes(val) && p.keys.includes("thinking")) d.thinking = "disabled";
132
+ else if (pos === 0) {
133
+ if (r || p.catalogOnly && p.aliases[val] || proven && !isQualifiedModel(val)) fail(t, r);
134
+ p.warn(`[directives] Unknown model: "${t}"`);
135
+ }
136
+ pos++;
137
+ }
138
+ return out;
139
+ }
140
+ function applyKey(d, key, val, warn) {
141
+ switch (key) {
142
+ case "turns":
143
+ if (/^\d+$/.test(val)) d.turns = parseInt(val, 10);
144
+ else warn(`[directives] Invalid turns value: "${val}"`);
145
+ break;
146
+ case "thinking":
147
+ case "think":
148
+ if (["adaptive", "enabled", "disabled"].includes(val)) d.thinking = val;
149
+ else warn(`[directives] Invalid thinking value: "${val}"`);
150
+ break;
151
+ case "effort":
152
+ if (["low", "medium", "high", "max"].includes(val)) d.effort = val;
153
+ else warn(`[directives] Invalid effort value: "${val}"`);
154
+ break;
155
+ case "engine":
156
+ d.engine = val;
157
+ break;
158
+ case "resume":
159
+ if (["on", "true", "1"].includes(val)) d.resume = true;
160
+ else if (["off", "false", "0"].includes(val)) d.resume = false;
161
+ else warn(`[directives] Invalid resume value: "${val}"`);
162
+ break;
163
+ }
164
+ }
165
+ function makeModelResolver(engines) {
166
+ return (token) => {
167
+ const t = token.toLowerCase();
168
+ const hits = [];
169
+ for (const { name, models } of engines()) {
170
+ for (const m of models) {
171
+ if (!m.value) continue;
172
+ const v = m.value.toLowerCase();
173
+ if (v === t) return { model: m.value, engine: name };
174
+ if (v.slice(v.lastIndexOf("/") + 1) === t && !hits.some((h) => h.model === m.value)) hits.push({ model: m.value, engine: name });
175
+ }
176
+ }
177
+ if (hits.length === 1) return hits[0];
178
+ return hits.length > 1 ? { ambiguous: hits.map((h) => h.model) } : null;
179
+ };
180
+ }
181
+ export {
182
+ DIRECTIVE_KEYS,
183
+ VENDOR_SHORTHAND,
184
+ expandVendor,
185
+ isDirectiveGroup,
186
+ makeModelResolver,
187
+ parseDirectives,
188
+ resolveModelToken,
189
+ setModelResolver,
190
+ splitDirectiveGroups
191
+ };
192
+ //# sourceMappingURL=directives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/models.ts","../src/directives.ts"],"sourcesContent":["/**\n * Model aliasing + mid-conversation switch resolution — pure, dependency-free.\n *\n * Harness-level so every agentx consumer (unclaw, para-chat, …) resolves `[opus]`-style\n * aliases, gates by an allow-list, and announces a switch the same way. Policy (which models\n * a caller may use) and session state (the prior model) stay with the consumer; this module\n * is the shared mechanism.\n */\n\n/** Canonical short aliases → concrete Anthropic model ids. */\nexport const MODEL_ALIASES: Record<string, string> = {\n fable: 'claude-fable-5',\n 'fable-5': 'claude-fable-5',\n opus: 'claude-opus-5-5',\n 'opus-5-5': 'claude-opus-5-5',\n 'opus-5': 'claude-opus-5',\n 'opus-4-8': 'claude-opus-4-8',\n 'opus-4-7': 'claude-opus-4-7',\n 'opus-4-6': 'claude-opus-4-6',\n sonnet: 'claude-sonnet-4-6',\n haiku: 'claude-haiku-4-5-20251001',\n};\n\n/** Resolve an alias to a concrete id. A provider prefix (\"anthropic/\") is preserved; unknown ids pass through. */\nexport function resolveModelAlias(input: string): string {\n const slash = input.indexOf('/');\n const prefix = slash === -1 ? '' : input.slice(0, slash + 1);\n const rest = slash === -1 ? input : input.slice(slash + 1);\n // claude-code's catalogue IS the aliases (claude-code/opus); expanding them yields an unknown id.\n if (prefix === 'claude-code/') return input;\n return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);\n}\n\n/** The provider of a model id (\"cursor/x\" -> \"cursor\"). Provider-specific `providerOptions` are only\n * portable within the same provider — leaking them across is a hard 400.\n *\n * A BARE id is the common case, not the exception: `resolveModelAlias('haiku')` yields\n * `claude-haiku-4-5-20251001` with no prefix (ai.libx.js resolves it fuzzily downstream), so reading\n * only the literal \"/\" made every aliased child look like a provider switch and silently dropped the\n * parent's options. The fix belongs HERE — the alias table must keep emitting the ids ai.libx.js\n * expects, and the comparison is provider identity, not string identity.\n *\n * A bare id we can't attribute is UNKNOWN, not a shared provider: it gets a per-id marker so two\n * different unknown ids never compare equal (returning \"\" for both made them look like one provider\n * and would have leaked one host's options onto the other's model). An unknown id still compares\n * equal to ITSELF, so a child staying on the parent's exact model keeps the parent's options. */\nexport function modelProvider(model: string): string {\n const i = model.indexOf('/');\n if (i !== -1) return model.slice(0, i);\n if (/^claude-/i.test(model)) return 'anthropic';\n return `?${model}`; // unknown: identity-scoped, never equal to a different unknown id\n}\n\n/** Short, human-friendly name for a resolved model id (e.g. \"anthropic/claude-opus-4-8\" → \"opus\"). */\nexport function modelShortLabel(model?: string): string {\n if (!model) return 'default';\n const id = model.split('/').pop()!;\n return id.replace(/^claude-/, '').replace(/-\\d.*$/, '');\n}\n\nexport interface ResolveModelSwitchOpts {\n /** Model requested for this turn (e.g. from an inline directive). Undefined = keep `current`. */\n requested?: string;\n /** The turn's default/current model when nothing is requested. */\n current: string;\n /** The session's prior resolved model — drives change detection + the notice. */\n prior?: string;\n /** If set, `requested` must match one of these (by short label) or it's denied and `current` is kept. */\n allowed?: string[];\n /** Announcement formatter. Default: `_[switching from x to y]_\\n\\n`. */\n format?: (from: string, to: string) => string;\n}\n\nexport interface ModelSwitch {\n /** The model to run this turn. */\n model: string;\n /** True when `requested` was rejected by `allowed` and `model` fell back to `current`. */\n denied: boolean;\n /** Inline notice to surface when the model actually changed vs `prior`, else undefined. */\n notice?: string;\n}\n\n/**\n * Resolve which model a turn runs, given an optional request, an allow-list, and the prior model.\n * Comparisons are by short label so provider-prefixed and dated ids unify\n * (\"anthropic/claude-opus-4-8\" ≡ \"claude-opus-4-8\" ≡ \"opus\").\n */\nexport function resolveModelSwitch(opts: ResolveModelSwitchOpts): ModelSwitch {\n const { requested, current, prior, allowed } = opts;\n if (!requested) return { model: current, denied: false };\n if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {\n return { model: current, denied: true };\n }\n const fmt = opts.format ?? ((from: string, to: string) => `_[switching from ${from} to ${to}]_\\n\\n`);\n const changed = prior !== undefined && modelShortLabel(prior) !== modelShortLabel(requested);\n return {\n model: requested,\n denied: false,\n notice: changed ? fmt(modelShortLabel(prior!), modelShortLabel(requested)) : undefined,\n };\n}\n","/**\n * Inline prompt directives — `[opus] fix x`, `[cc-opus, 50] …`, `[model:openai/gpt-5.5] …` — pure,\n * dependency-free. A message may open with one or more `[..]` groups that pick the model (and, for\n * hosts that honour them, turns/thinking/effort/engine/resume); the rest is the prompt.\n *\n * Shared by every agentx host (unclaw/Shraga, Bare) so the grammar and its edge cases live once:\n * multi-group, strict mode (bracketed prose stays prose), and an unresolvable EXPLICIT model is\n * reported (`unresolvedModel`) rather than dropped — dropping it ran the turn on the previous model.\n *\n * Model tokens resolve through the alias table and an injected `ModelResolver` (the host's catalog,\n * see `makeModelResolver`). `<vendor>-<model>` / `<vendor>/<model>` shorthands (`cc-opus` →\n * `claude-code/opus`, see `VENDOR_SHORTHAND`) select a model only when they resolve (`[cc-by] …` stays prose).\n */\nimport { MODEL_ALIASES } from './models';\n\nexport interface Directives {\n model?: string;\n turns?: number;\n thinking?: 'adaptive' | 'enabled' | 'disabled';\n effort?: 'low' | 'medium' | 'high' | 'max';\n engine?: string;\n /** Session resume for this conversation (host-defined). */\n resume?: boolean;\n}\n\nexport interface ParsedPrompt {\n prompt: string;\n directives: Directives;\n /** An EXPLICIT model selection nothing could resolve. The caller must fail the turn, never run the old model. */\n unresolvedModel?: string;\n /** When `unresolvedModel` matched several catalog ids: the candidates (use the full id). */\n ambiguous?: string[];\n}\n\nexport type ModelHit = { model: string; engine?: string };\n/** Resolves a model token against a host catalog. `{ ambiguous }` = several ids match; never guess. */\nexport type ModelResolver = (token: string) => ModelHit | { ambiguous: string[] } | null;\n\n/** Vendor shorthand → provider id: `[cc-opus]` / `[cc/opus]` = `claude-code/opus`. Extend per host via options. */\nexport const VENDOR_SHORTHAND: Record<string, string> = { cc: 'claude-code', cur: 'cursor', oa: 'openai' };\n\nexport const DIRECTIVE_KEYS = ['model', 'turns', 'thinking', 'think', 'effort', 'engine', 'resume'];\n\nexport interface DirectiveOptions {\n /** Host catalog lookup. Default: the one set by `setModelResolver` (none). */\n resolveModel: ModelResolver | null;\n /** Try the catalog before the alias table (Bare: `[opus]` = the catalog's `claude-code/opus`). */\n resolverFirst: boolean;\n aliases: Record<string, string>;\n vendors: Record<string, string>;\n /** Keys this host honours; positional turns/thinking count only when `turns`/`thinking` are listed. */\n keys: string[];\n /** Every group, the first included, must look like directives (else it stays prompt text). */\n strict: boolean;\n /** Resolve to catalog ids ONLY (Bare): no alias-table fallback, a full id the catalog lacks is refused, and a\n * bare word matches an id's tail only when it is a known alias or carries a digit (`[auto]` stays prose). */\n catalogOnly: boolean;\n warn: (msg: string) => void;\n}\n\nlet defaultResolver: ModelResolver | null = null;\n/** Process-wide default resolver (Shraga's engine registry injects it once). Per-call options override. */\nexport function setModelResolver(fn: ModelResolver | null): void { defaultResolver = fn; }\n\nconst opt = (o?: Partial<DirectiveOptions>): DirectiveOptions => ({\n resolveModel: defaultResolver, resolverFirst: false, aliases: MODEL_ALIASES, vendors: VENDOR_SHORTHAND,\n keys: DIRECTIVE_KEYS, strict: false, catalogOnly: false, warn: (m) => console.warn(m), ...o,\n});\n\n// A group ends at whitespace or the end: `[opus](http://x)` is a markdown link, not a directive.\nconst DIRECTIVE_RE = /^\\s*\\[([^\\]]*)\\](?=\\s|$)\\s*([\\s\\S]*)/;\nconst isQualifiedModel = (v: string) => /^[a-z0-9._-]+\\/[a-z0-9./_-]+$/.test(v);\n\n/** `cc-opus` / `cc/opus` → `claude-code/opus`; a full `claude-code/opus` is returned as is. Else null. */\nexport function expandVendor(token: string, vendors: Record<string, string> = VENDOR_SHORTHAND): string | null {\n const v = token.trim().toLowerCase();\n const m = v.match(/^([a-z0-9]+)[-/](.+)$/);\n if (m && vendors[m[1]]) return `${vendors[m[1]]}/${m[2]}`;\n const slash = v.indexOf('/');\n return slash > 0 && Object.values(vendors).includes(v.slice(0, slash)) ? v : null;\n}\n\n/** Resolve one model token: vendor shorthand → catalog exact id; else alias table + catalog in the configured order. */\nexport function resolveModelToken(token: string, o?: Partial<DirectiveOptions>): ModelHit | { ambiguous: string[] } | null {\n const p = opt(o);\n const v = token.trim().toLowerCase();\n const vendored = expandVendor(v, p.vendors);\n if (vendored) return p.resolveModel ? p.resolveModel(vendored) : p.catalogOnly ? null : { model: vendored }; // no catalog: trust the explicit id\n if (p.catalogOnly) return v.includes('/') || p.aliases[v] || /\\d/.test(v) ? p.resolveModel?.(v) ?? null : null;\n const alias = p.aliases[v] ? { model: p.aliases[v] } : null;\n if (!p.resolverFirst) return alias ?? p.resolveModel?.(v) ?? null;\n const hit = p.resolveModel?.(v) ?? null;\n return hit && 'model' in hit ? hit : alias ?? hit;\n}\n\nconst THINK = ['think', 'adaptive', 'nothink', 'nothinking'];\n\n/** Does a bracket group look like directives (vs. prompt text that happens to start with `[`)? */\nexport function isDirectiveGroup(raw: string, o?: Partial<DirectiveOptions>): boolean {\n const p = opt(o);\n const tokens = raw.split(',').map((t) => t.trim()).filter(Boolean);\n if (!tokens.length) return false;\n return tokens.every((t) => {\n const i = t.indexOf(':');\n if (i !== -1) return p.keys.includes(t.slice(0, i).trim().toLowerCase());\n const v = t.toLowerCase();\n return !!resolveModelToken(v, p) || (p.catalogOnly && !!p.aliases[v])\n || (p.keys.includes('turns') && /^\\d+$/.test(v)) || (p.keys.includes('thinking') && THINK.includes(v));\n });\n}\n\n/** Splits the leading `[..]` directive groups off a prompt — EVERY consecutive leading group. */\nexport function splitDirectiveGroups(text: string, o?: Partial<DirectiveOptions>): { groups: string[]; body: string } {\n const p = opt(o);\n let rest = text;\n const groups: string[] = [];\n for (;;) {\n const m = rest.match(DIRECTIVE_RE);\n if (!m) break;\n const g = m[1].trim();\n if ((groups.length || p.strict) && g && !isDirectiveGroup(g, p)) break;\n groups.push(g);\n rest = m[2];\n }\n return { groups, body: rest };\n}\n\nexport function parseDirectives(text: string, o?: Partial<DirectiveOptions>): ParsedPrompt {\n const p = opt(o);\n const { groups, body } = splitDirectiveGroups(text, p);\n if (!groups.length) return { prompt: text, directives: {} };\n const prompt = body.trim();\n const raw = groups.filter(Boolean).join(',');\n if (!raw) return { prompt, directives: {} };\n\n const d: Directives = {};\n const out: ParsedPrompt = { prompt, directives: d };\n const tokens = raw.split(',');\n // A keyed sibling proves the group is directives, so an unresolvable leading model there IS a dropped selection.\n const proven = tokens.some((t) => { const i = t.indexOf(':'); return i !== -1 && p.keys.includes(t.slice(0, i).trim().toLowerCase()); });\n const fail = (tok: string, hit: ReturnType<typeof resolveModelToken>) => {\n out.unresolvedModel ??= tok;\n if (hit && 'ambiguous' in hit) out.ambiguous ??= hit.ambiguous;\n };\n const setModel = (val: string): boolean | ReturnType<typeof resolveModelToken> => {\n const hit = resolveModelToken(val, p);\n if (!hit || !('model' in hit)) return hit;\n d.model = hit.model;\n if (hit.engine && !d.engine) d.engine = hit.engine; // an engine-owned model implies its engine, never over `[engine:x]`\n return true;\n };\n\n let pos = 0;\n for (const token of tokens) {\n const t = token.trim();\n if (!t) continue;\n const i = t.indexOf(':');\n if (i !== -1) {\n const key = t.slice(0, i).trim().toLowerCase(), val = t.slice(i + 1).trim().toLowerCase();\n if (!p.keys.includes(key)) { p.warn(`[directives] Unknown directive key: \"${key}\"`); continue; }\n if (key === 'model') {\n const r = setModel(val);\n if (r === true) continue;\n // A plain `provider/model` with no catalog to check is concrete — the engine owns the verdict.\n if (!r && isQualifiedModel(val) && !p.catalogOnly) { d.model = val; continue; }\n fail(val, r as any);\n } else applyKey(d, key, val, p.warn);\n continue;\n }\n const val = t.toLowerCase();\n const r = pos === 0 ? setModel(val) : null;\n if (r === true) { /* model */ }\n else if (pos <= 1 && /^\\d+$/.test(val) && p.keys.includes('turns')) d.turns = parseInt(val, 10);\n else if (['think', 'adaptive'].includes(val) && p.keys.includes('thinking')) d.thinking = 'adaptive';\n else if (['nothink', 'nothinking'].includes(val) && p.keys.includes('thinking')) d.thinking = 'disabled';\n else if (pos === 0) {\n // Ambiguous tokens (and, catalog-only, a known alias the catalog lacks) are model selections; a plain unknown one\n // only with a keyed sibling. An unresolved vendor form (`[cc-by]`) is not a selection.\n if (r || (p.catalogOnly && p.aliases[val]) || (proven && !isQualifiedModel(val))) fail(t, r as any);\n p.warn(`[directives] Unknown model: \"${t}\"`);\n }\n pos++;\n }\n return out;\n}\n\nfunction applyKey(d: Directives, key: string, val: string, warn: (m: string) => void): void {\n switch (key) {\n case 'turns': if (/^\\d+$/.test(val)) d.turns = parseInt(val, 10); else warn(`[directives] Invalid turns value: \"${val}\"`); break;\n case 'thinking':\n case 'think': if (['adaptive', 'enabled', 'disabled'].includes(val)) d.thinking = val as Directives['thinking']; else warn(`[directives] Invalid thinking value: \"${val}\"`); break;\n case 'effort': if (['low', 'medium', 'high', 'max'].includes(val)) d.effort = val as Directives['effort']; else warn(`[directives] Invalid effort value: \"${val}\"`); break;\n case 'engine': d.engine = val; break;\n case 'resume':\n if (['on', 'true', '1'].includes(val)) d.resume = true;\n else if (['off', 'false', '0'].includes(val)) d.resume = false;\n else warn(`[directives] Invalid resume value: \"${val}\"`);\n break;\n }\n}\n\n/** Engine/provider → the model ids it advertises. */\nexport interface EngineModels { name: string; models: { value: string }[] }\n\n/**\n * A resolver over host catalogs: exact id first, then the bare suffix after the provider prefix —\n * ONLY when unambiguous across engines (`{ ambiguous }` otherwise, never the first match).\n */\nexport function makeModelResolver(engines: () => EngineModels[]): ModelResolver {\n return (token) => {\n const t = token.toLowerCase();\n const hits: ModelHit[] = [];\n for (const { name, models } of engines()) {\n for (const m of models) {\n if (!m.value) continue;\n const v = m.value.toLowerCase();\n if (v === t) return { model: m.value, engine: name };\n if (v.slice(v.lastIndexOf('/') + 1) === t && !hits.some((h) => h.model === m.value)) hits.push({ model: m.value, engine: name });\n }\n }\n if (hits.length === 1) return hits[0];\n return hits.length > 1 ? { ambiguous: hits.map((h) => h.model) } : null;\n };\n}\n"],"mappings":"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","names":["val","r"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livx.cc/agentx",
3
- "version": "0.99.65",
3
+ "version": "0.99.67",
4
4
  "description": "Edge-native AI agent runtime — drives a virtual filesystem via any LLM (ai.libx.js). Same bytes run in node, browser, or edge.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,6 +22,10 @@
22
22
  "./models": {
23
23
  "types": "./dist/models.d.ts",
24
24
  "default": "./dist/models.js"
25
+ },
26
+ "./directives": {
27
+ "types": "./dist/directives.d.ts",
28
+ "default": "./dist/directives.js"
25
29
  }
26
30
  },
27
31
  "license": "MIT",