@monoes/routing 1.0.0

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.
Files changed (77) hide show
  1. package/dist/capability-index.d.ts +15 -0
  2. package/dist/capability-index.d.ts.map +1 -0
  3. package/dist/capability-index.js +32 -0
  4. package/dist/capability-index.js.map +1 -0
  5. package/dist/cosine.d.ts +11 -0
  6. package/dist/cosine.d.ts.map +1 -0
  7. package/dist/cosine.js +41 -0
  8. package/dist/cosine.js.map +1 -0
  9. package/dist/encoder.d.ts +37 -0
  10. package/dist/encoder.d.ts.map +1 -0
  11. package/dist/encoder.js +75 -0
  12. package/dist/encoder.js.map +1 -0
  13. package/dist/index.d.ts +12 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +9 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/keyword-pre-filter.d.ts +44 -0
  18. package/dist/keyword-pre-filter.d.ts.map +1 -0
  19. package/dist/keyword-pre-filter.js +109 -0
  20. package/dist/keyword-pre-filter.js.map +1 -0
  21. package/dist/llm-fallback.d.ts +18 -0
  22. package/dist/llm-fallback.d.ts.map +1 -0
  23. package/dist/llm-fallback.js +76 -0
  24. package/dist/llm-fallback.js.map +1 -0
  25. package/dist/prompts/classify.d.ts +5 -0
  26. package/dist/prompts/classify.d.ts.map +1 -0
  27. package/dist/prompts/classify.js +25 -0
  28. package/dist/prompts/classify.js.map +1 -0
  29. package/dist/route-layer.d.ts +25 -0
  30. package/dist/route-layer.d.ts.map +1 -0
  31. package/dist/route-layer.js +129 -0
  32. package/dist/route-layer.js.map +1 -0
  33. package/dist/routes/core.route.d.ts +3 -0
  34. package/dist/routes/core.route.d.ts.map +1 -0
  35. package/dist/routes/core.route.js +106 -0
  36. package/dist/routes/core.route.js.map +1 -0
  37. package/dist/routes/design.route.d.ts +3 -0
  38. package/dist/routes/design.route.d.ts.map +1 -0
  39. package/dist/routes/design.route.js +94 -0
  40. package/dist/routes/design.route.js.map +1 -0
  41. package/dist/routes/engineering.route.d.ts +3 -0
  42. package/dist/routes/engineering.route.d.ts.map +1 -0
  43. package/dist/routes/engineering.route.js +191 -0
  44. package/dist/routes/engineering.route.js.map +1 -0
  45. package/dist/routes/game-dev.route.d.ts +3 -0
  46. package/dist/routes/game-dev.route.d.ts.map +1 -0
  47. package/dist/routes/game-dev.route.js +109 -0
  48. package/dist/routes/game-dev.route.js.map +1 -0
  49. package/dist/routes/index.d.ts +13 -0
  50. package/dist/routes/index.d.ts.map +1 -0
  51. package/dist/routes/index.js +31 -0
  52. package/dist/routes/index.js.map +1 -0
  53. package/dist/routes/marketing.route.d.ts +3 -0
  54. package/dist/routes/marketing.route.d.ts.map +1 -0
  55. package/dist/routes/marketing.route.js +94 -0
  56. package/dist/routes/marketing.route.js.map +1 -0
  57. package/dist/routes/product.route.d.ts +3 -0
  58. package/dist/routes/product.route.d.ts.map +1 -0
  59. package/dist/routes/product.route.js +75 -0
  60. package/dist/routes/product.route.js.map +1 -0
  61. package/dist/routes/security.route.d.ts +3 -0
  62. package/dist/routes/security.route.d.ts.map +1 -0
  63. package/dist/routes/security.route.js +101 -0
  64. package/dist/routes/security.route.js.map +1 -0
  65. package/dist/routes/specialized.route.d.ts +3 -0
  66. package/dist/routes/specialized.route.d.ts.map +1 -0
  67. package/dist/routes/specialized.route.js +107 -0
  68. package/dist/routes/specialized.route.js.map +1 -0
  69. package/dist/routes/testing.route.d.ts +3 -0
  70. package/dist/routes/testing.route.d.ts.map +1 -0
  71. package/dist/routes/testing.route.js +115 -0
  72. package/dist/routes/testing.route.js.map +1 -0
  73. package/dist/types.d.ts +86 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +2 -0
  76. package/dist/types.js.map +1 -0
  77. package/package.json +30 -0
@@ -0,0 +1,15 @@
1
+ import { Route } from './types.js';
2
+ /**
3
+ * Build a compact text index of all agent capabilities for LLM classification.
4
+ * Each entry: "<agentSlug>: <description>"
5
+ * Total output stays under MAX_INDEX_CHARS to fit in a single LLM prompt.
6
+ */
7
+ export declare function buildCapabilityIndex(routes: Route[]): string;
8
+ /**
9
+ * Build a compact list of the top-N candidate agents by semantic score for hint injection.
10
+ */
11
+ export declare function buildCandidateHints(scores: Array<{
12
+ agentSlug: string;
13
+ score: number;
14
+ }>, topN?: number): string;
15
+ //# sourceMappingURL=capability-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-index.d.ts","sourceRoot":"","sources":["../src/capability-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAInC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAgB5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EACnD,IAAI,SAAI,GACP,MAAM,CAKR"}
@@ -0,0 +1,32 @@
1
+ const MAX_INDEX_CHARS = 8000;
2
+ /**
3
+ * Build a compact text index of all agent capabilities for LLM classification.
4
+ * Each entry: "<agentSlug>: <description>"
5
+ * Total output stays under MAX_INDEX_CHARS to fit in a single LLM prompt.
6
+ */
7
+ export function buildCapabilityIndex(routes) {
8
+ const lines = [];
9
+ for (const route of routes) {
10
+ const description = route.description ?? route.utterances[0] ?? route.name;
11
+ lines.push(`${route.agentSlug}: ${description}`);
12
+ }
13
+ let index = lines.join('\n');
14
+ if (index.length > MAX_INDEX_CHARS) {
15
+ const trimmedLines = routes.map(r => {
16
+ const desc = (r.description ?? r.utterances[0] ?? r.name).slice(0, 80);
17
+ return `${r.agentSlug}: ${desc}`;
18
+ });
19
+ index = trimmedLines.join('\n').slice(0, MAX_INDEX_CHARS);
20
+ }
21
+ return index;
22
+ }
23
+ /**
24
+ * Build a compact list of the top-N candidate agents by semantic score for hint injection.
25
+ */
26
+ export function buildCandidateHints(scores, topN = 3) {
27
+ return scores
28
+ .slice(0, topN)
29
+ .map(s => `- ${s.agentSlug} (similarity: ${s.score.toFixed(3)})`)
30
+ .join('\n');
31
+ }
32
+ //# sourceMappingURL=capability-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-index.js","sourceRoot":"","sources":["../src/capability-index.ts"],"names":[],"mappings":"AAEA,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAe;IAClD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC;QAC3E,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,WAAW,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,KAAK,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;QACnC,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YAClC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvE,OAAO,GAAG,CAAC,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAmD,EACnD,IAAI,GAAG,CAAC;IAER,OAAO,MAAM;SACV,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;SACd,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,SAAS,iBAAiB,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;SAChE,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Compute cosine similarity between two vectors.
3
+ * Returns a value in [-1, 1], where 1 = identical direction.
4
+ */
5
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
6
+ /**
7
+ * Compute the mean centroid of a list of vectors.
8
+ * All vectors must have the same dimensionality.
9
+ */
10
+ export declare function computeCentroid(vectors: number[][]): number[];
11
+ //# sourceMappingURL=cosine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cosine.d.ts","sourceRoot":"","sources":["../src/cosine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAejE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,CAa7D"}
package/dist/cosine.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Compute cosine similarity between two vectors.
3
+ * Returns a value in [-1, 1], where 1 = identical direction.
4
+ */
5
+ export function cosineSimilarity(a, b) {
6
+ if (a.length !== b.length) {
7
+ throw new Error(`Vector length mismatch: ${a.length} vs ${b.length}`);
8
+ }
9
+ let dot = 0;
10
+ let normA = 0;
11
+ let normB = 0;
12
+ for (let i = 0; i < a.length; i++) {
13
+ dot += a[i] * b[i];
14
+ normA += a[i] * a[i];
15
+ normB += b[i] * b[i];
16
+ }
17
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
18
+ if (denominator === 0)
19
+ return 0;
20
+ return dot / denominator;
21
+ }
22
+ /**
23
+ * Compute the mean centroid of a list of vectors.
24
+ * All vectors must have the same dimensionality.
25
+ */
26
+ export function computeCentroid(vectors) {
27
+ if (vectors.length === 0)
28
+ throw new Error('Cannot compute centroid of empty array');
29
+ const dim = vectors[0].length;
30
+ const centroid = new Array(dim).fill(0);
31
+ for (const vec of vectors) {
32
+ for (let i = 0; i < dim; i++) {
33
+ centroid[i] += vec[i];
34
+ }
35
+ }
36
+ for (let i = 0; i < dim; i++) {
37
+ centroid[i] /= vectors.length;
38
+ }
39
+ return centroid;
40
+ }
41
+ //# sourceMappingURL=cosine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cosine.js","sourceRoot":"","sources":["../src/cosine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAW,EAAE,CAAW;IACvD,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrB,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,WAAW,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,GAAG,GAAG,WAAW,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAmB;IACjD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACpF,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC9B,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC;IAChC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,37 @@
1
+ export interface Encoder {
2
+ encode(text: string): Promise<number[]>;
3
+ encodeAll(texts: string[]): Promise<number[][]>;
4
+ }
5
+ /**
6
+ * Local encoder that produces deterministic pseudo-embeddings from text.
7
+ * Uses n-gram hashing to spread tokens across a fixed-dimensional vector.
8
+ * NOTE: Real semantic routing requires a real embedding model.
9
+ * In production, replace with HNSWEncoder backed by a proper embedding model.
10
+ */
11
+ export declare class LocalEncoder implements Encoder {
12
+ private readonly DIM;
13
+ private cache;
14
+ encode(text: string): Promise<number[]>;
15
+ encodeAll(texts: string[]): Promise<number[][]>;
16
+ }
17
+ /**
18
+ * Production encoder backed by a real embedding model.
19
+ *
20
+ * The model is injected by the host (the CLI wires in a local
21
+ * `@huggingface/transformers` feature-extraction pipeline) so this package
22
+ * carries no model dependency. When no embedder is injected it degrades to the
23
+ * deterministic {@link LocalEncoder}.
24
+ */
25
+ export declare class HNSWEncoder implements Encoder {
26
+ private fallback;
27
+ private embed?;
28
+ /**
29
+ * @param embed Optional real embedding function. When provided it is used
30
+ * exclusively — the hash-based LocalEncoder is NOT mixed in, since combining
31
+ * vectors of different dimensionality would break cosine comparison.
32
+ */
33
+ constructor(embed?: (text: string) => Promise<number[]>);
34
+ encode(text: string): Promise<number[]>;
35
+ encodeAll(texts: string[]): Promise<number[][]>;
36
+ }
37
+ //# sourceMappingURL=encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../src/encoder.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;CACjD;AAED;;;;;GAKG;AACH,qBAAa,YAAa,YAAW,OAAO;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAO;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAEtC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA6B5C;IAEK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAEpD;CACF;AAED;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,OAAO;IACzC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,KAAK,CAAC,CAAsC;IAEpD;;;;OAIG;IACH,YAAY,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,EAEtD;IAEK,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE5C;IAEK,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAOpD;CACF"}
@@ -0,0 +1,75 @@
1
+ import { createHash } from 'crypto';
2
+ /**
3
+ * Local encoder that produces deterministic pseudo-embeddings from text.
4
+ * Uses n-gram hashing to spread tokens across a fixed-dimensional vector.
5
+ * NOTE: Real semantic routing requires a real embedding model.
6
+ * In production, replace with HNSWEncoder backed by a proper embedding model.
7
+ */
8
+ export class LocalEncoder {
9
+ DIM = 256;
10
+ cache = new Map();
11
+ async encode(text) {
12
+ const key = createHash('sha256').update(text.toLowerCase().trim()).digest('hex');
13
+ if (this.cache.has(key))
14
+ return this.cache.get(key);
15
+ const vector = new Array(this.DIM).fill(0);
16
+ const words = text.toLowerCase().split(/\s+/);
17
+ // Unigrams
18
+ for (const word of words) {
19
+ const hash = createHash('md5').update(word).digest();
20
+ for (let i = 0; i < Math.min(hash.length, this.DIM); i++) {
21
+ vector[i % this.DIM] += (hash[i] - 128) / 128;
22
+ }
23
+ }
24
+ // Bigrams for better phrase sensitivity
25
+ for (let i = 0; i < words.length - 1; i++) {
26
+ const bigram = `${words[i]} ${words[i + 1]}`;
27
+ const hash = createHash('md5').update(bigram).digest();
28
+ for (let j = 0; j < Math.min(hash.length, this.DIM); j++) {
29
+ vector[(j + this.DIM / 2) % this.DIM] += (hash[j] - 128) / 128 * 0.5;
30
+ }
31
+ }
32
+ // L2 normalize
33
+ const norm = Math.sqrt(vector.reduce((s, v) => s + v * v, 0)) || 1;
34
+ const normalized = vector.map(v => v / norm);
35
+ this.cache.set(key, normalized);
36
+ return normalized;
37
+ }
38
+ async encodeAll(texts) {
39
+ return Promise.all(texts.map(t => this.encode(t)));
40
+ }
41
+ }
42
+ /**
43
+ * Production encoder backed by a real embedding model.
44
+ *
45
+ * The model is injected by the host (the CLI wires in a local
46
+ * `@huggingface/transformers` feature-extraction pipeline) so this package
47
+ * carries no model dependency. When no embedder is injected it degrades to the
48
+ * deterministic {@link LocalEncoder}.
49
+ */
50
+ export class HNSWEncoder {
51
+ fallback = new LocalEncoder();
52
+ embed;
53
+ /**
54
+ * @param embed Optional real embedding function. When provided it is used
55
+ * exclusively — the hash-based LocalEncoder is NOT mixed in, since combining
56
+ * vectors of different dimensionality would break cosine comparison.
57
+ */
58
+ constructor(embed) {
59
+ this.embed = embed;
60
+ }
61
+ async encode(text) {
62
+ return this.embed ? this.embed(text) : this.fallback.encode(text);
63
+ }
64
+ async encodeAll(texts) {
65
+ if (!this.embed)
66
+ return this.fallback.encodeAll(texts);
67
+ // Sequential: the underlying transformers pipeline is single-threaded, so
68
+ // Promise.all would not parallelise — it would only inflate peak memory.
69
+ const out = [];
70
+ for (const t of texts)
71
+ out.push(await this.embed(t));
72
+ return out;
73
+ }
74
+ }
75
+ //# sourceMappingURL=encoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoder.js","sourceRoot":"","sources":["../src/encoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export { RouteLayer } from './route-layer.js';
2
+ export { cosineSimilarity, computeCentroid } from './cosine.js';
3
+ export { LocalEncoder, HNSWEncoder } from './encoder.js';
4
+ export type { Encoder } from './encoder.js';
5
+ export { LLMFallbackRouter } from './llm-fallback.js';
6
+ export { buildCapabilityIndex, buildCandidateHints } from './capability-index.js';
7
+ export { buildClassificationPrompt } from './prompts/classify.js';
8
+ export type { Route, RouteResult, RouteLayerConfig, AgentCapability, LLMFallbackConfig } from './types.js';
9
+ export { ALL_ROUTES } from './routes/index.js';
10
+ export { KeywordPreFilter, DEFAULT_KEYWORD_ROUTES } from './keyword-pre-filter.js';
11
+ export type { KeywordRule } from './keyword-pre-filter.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACzD,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC3G,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACnF,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { RouteLayer } from './route-layer.js';
2
+ export { cosineSimilarity, computeCentroid } from './cosine.js';
3
+ export { LocalEncoder, HNSWEncoder } from './encoder.js';
4
+ export { LLMFallbackRouter } from './llm-fallback.js';
5
+ export { buildCapabilityIndex, buildCandidateHints } from './capability-index.js';
6
+ export { buildClassificationPrompt } from './prompts/classify.js';
7
+ export { ALL_ROUTES } from './routes/index.js';
8
+ export { KeywordPreFilter, DEFAULT_KEYWORD_ROUTES } from './keyword-pre-filter.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { RouteResult } from './types.js';
2
+ export interface KeywordRule {
3
+ /** Regex pattern to match against task description */
4
+ pattern: RegExp;
5
+ /** Agent slug to route to when matched */
6
+ agentSlug: string;
7
+ /** Human-readable name for this route */
8
+ routeName: string;
9
+ /** Description of what this rule matches */
10
+ description: string;
11
+ }
12
+ /**
13
+ * 30+ default keyword rules for fast, deterministic routing.
14
+ * First-match wins — order matters.
15
+ */
16
+ export declare const DEFAULT_KEYWORD_ROUTES: KeywordRule[];
17
+ /**
18
+ * Fast keyword-based pre-filter for routing.
19
+ * Runs regex matches against task descriptions before falling through
20
+ * to semantic (embedding-based) routing. First match wins.
21
+ */
22
+ export declare class KeywordPreFilter {
23
+ private rules;
24
+ constructor(customRules?: Array<{
25
+ pattern: RegExp;
26
+ agentSlug: string;
27
+ routeName: string;
28
+ description: string;
29
+ }>);
30
+ /**
31
+ * Match a task description against keyword rules.
32
+ * Returns the first matching route or null if no keyword matches.
33
+ */
34
+ match(taskDescription: string): RouteResult | null;
35
+ /**
36
+ * Prepend a custom rule (highest priority).
37
+ */
38
+ addRule(rule: KeywordRule): void;
39
+ /**
40
+ * Return a copy of the current rules (immutable view).
41
+ */
42
+ getRules(): ReadonlyArray<KeywordRule>;
43
+ }
44
+ //# sourceMappingURL=keyword-pre-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyword-pre-filter.d.ts","sourceRoot":"","sources":["../src/keyword-pre-filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,EA2E/C,CAAC;AAEF;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAgB;IAE7B,YAAY,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,EAM9G;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAYjD;IAED;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAE/B;IAED;;OAEG;IACH,QAAQ,IAAI,aAAa,CAAC,WAAW,CAAC,CAErC;CACF"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * 30+ default keyword rules for fast, deterministic routing.
3
+ * First-match wins — order matters.
4
+ */
5
+ export const DEFAULT_KEYWORD_ROUTES = [
6
+ // Security / CVE
7
+ { pattern: /CVE-\d{4}-\d+/i, agentSlug: 'engineering-security-engineer', routeName: 'cve-remediation', description: 'CVE identifier detected' },
8
+ { pattern: /\bOWASP\b/i, agentSlug: 'engineering-security-engineer', routeName: 'owasp-security', description: 'OWASP security reference' },
9
+ { pattern: /\bthreat\s*model/i, agentSlug: 'engineering-security-engineer', routeName: 'threat-modeling', description: 'Threat modeling task' },
10
+ // Test files
11
+ { pattern: /\.(test|spec)\.(ts|js|tsx|jsx)\b/i, agentSlug: 'tdd-london-swarm', routeName: 'test-file', description: 'Test file detected' },
12
+ { pattern: /\b(write|create|add|fix)\s+(unit|integration|e2e)?\s*tests?\b/i, agentSlug: 'tdd-london-swarm', routeName: 'test-writing', description: 'Test writing task' },
13
+ // Docker / DevOps
14
+ { pattern: /\bDockerfile\b/i, agentSlug: 'engineering-devops-automator', routeName: 'dockerfile', description: 'Dockerfile detected' },
15
+ { pattern: /\bdocker[-.]?compose\b/i, agentSlug: 'engineering-devops-automator', routeName: 'docker-compose', description: 'Docker Compose detected' },
16
+ { pattern: /\bterraform\b/i, agentSlug: 'engineering-devops-automator', routeName: 'terraform', description: 'Terraform infrastructure' },
17
+ { pattern: /\bgithub\s*actions?\b/i, agentSlug: 'engineering-devops-automator', routeName: 'github-actions', description: 'GitHub Actions workflow' },
18
+ { pattern: /\b\.github\/workflows\b/i, agentSlug: 'engineering-devops-automator', routeName: 'github-workflows', description: 'GitHub workflow file' },
19
+ { pattern: /\bkubernetes\b|\bk8s\b|\bhelm\b(?=[\s\S]{0,60}\b(?:kubernetes|k8s|chart|deploy|helmfile)\b)|\b(?:kubernetes|k8s|chart|deploy|helmfile)\b(?=[\s\S]{0,60}\bhelm\b)/i, agentSlug: 'engineering-devops-automator', routeName: 'kubernetes', description: 'Kubernetes / Helm (bare "helm" requires nearby Kubernetes context — "take the helm" is not Kubernetes)' },
20
+ // Git operations
21
+ { pattern: /\bgit\s+(rebase|blame|bisect|cherry-pick|stash|reflog)\b/i, agentSlug: 'engineering-git-workflow-master', routeName: 'git-operations', description: 'Advanced git operation' },
22
+ { pattern: /\bgit\s+(merge|branch|tag|log|diff)\b/i, agentSlug: 'engineering-git-workflow-master', routeName: 'git-workflow', description: 'Git workflow operation' },
23
+ // Solidity / Smart contracts
24
+ { pattern: /\.sol\b/i, agentSlug: 'engineering-solidity-smart-contract-engineer', routeName: 'solidity-file', description: 'Solidity file detected' },
25
+ { pattern: /\bsolidity\b|\bsmart\s*contract\b/i, agentSlug: 'engineering-solidity-smart-contract-engineer', routeName: 'solidity', description: 'Solidity / smart contract' },
26
+ // ZK proofs
27
+ { pattern: /\bzkp\b|\bcircom\b|\bsnark\b|\bzk[-\s]?proof/i, agentSlug: 'zk-steward', routeName: 'zk-proofs', description: 'Zero-knowledge proof' },
28
+ // MCP
29
+ { pattern: /\bMCP\s*(server|builder|tool)\b/i, agentSlug: 'specialized-mcp-builder', routeName: 'mcp-builder', description: 'MCP server/tool building' },
30
+ // React Native / Mobile
31
+ { pattern: /\breact[-\s]?native\b/i, agentSlug: 'engineering-react-native-developer', routeName: 'react-native', description: 'React Native development' },
32
+ // iOS / Swift
33
+ // Bare "swift" is a common English word ("a swift fix") — only match it when
34
+ // iOS/Xcode/Apple context appears nearby (either word order). swiftui/xcode/
35
+ // "ios app" are already unambiguous domain qualifiers.
36
+ { pattern: /\bswiftui\b|\bxcode\b|\bios\s+app\b|\bswift\b(?=[\s\S]{0,60}\b(?:ios|xcode|apple|app)\b)|\b(?:ios|xcode|apple|app)\b(?=[\s\S]{0,60}\bswift\b)/i, agentSlug: 'engineering-ios-swift-developer', routeName: 'ios-swift', description: 'iOS / Swift development' },
37
+ // Android / Kotlin
38
+ { pattern: /\bkotlin\b|\bandroid\s+(app|dev)/i, agentSlug: 'engineering-android-kotlin-developer', routeName: 'android-kotlin', description: 'Android / Kotlin development' },
39
+ // Embedded / Firmware
40
+ // Bare "embedded" is a common English word ("the config embedded in the file")
41
+ // — only match it near hardware/firmware context (either word order).
42
+ { pattern: /\bfirmware\b|\brtos\b|\bmicrocontroller\b|\bembedded\b(?=[\s\S]{0,60}\b(?:firmware|hardware|mcu|rtos|chip|microcontroller|device|system)\b)|\b(?:firmware|hardware|mcu|rtos|chip|microcontroller|device|system)\b(?=[\s\S]{0,60}\bembedded\b)/i, agentSlug: 'engineering-embedded-firmware-engineer', routeName: 'embedded-firmware', description: 'Embedded / firmware development' },
43
+ // Salesforce
44
+ // Bare "apex" is a common English word ("the apex of the call graph") —
45
+ // only match it near Salesforce context (either word order).
46
+ { pattern: /\bsalesforce\b|\bsoql\b|\bapex\b(?=[\s\S]{0,60}\b(?:salesforce|soql|trigger|lwc|visualforce)\b)|\b(?:salesforce|soql|trigger|lwc|visualforce)\b(?=[\s\S]{0,60}\bapex\b)/i, agentSlug: 'specialized-salesforce-developer', routeName: 'salesforce', description: 'Salesforce / Apex / SOQL' },
47
+ // Game engines
48
+ // Bare "blender" is a common English word (kitchen appliance) — only match
49
+ // it near 3D/render context (either word order).
50
+ { pattern: /\bblender\b(?=[\s\S]{0,60}\b(?:3d|model|render|animation|mesh|scene|texture)\b)|\b(?:3d|model|render|animation|mesh|scene|texture)\b(?=[\s\S]{0,60}\bblender\b)/i, agentSlug: 'specialized-blender-3d-artist', routeName: 'blender', description: 'Blender 3D modeling' },
51
+ { pattern: /\bunreal\s*engine\b|\bUE[45]\b/i, agentSlug: 'specialized-unreal-engine-developer', routeName: 'unreal-engine', description: 'Unreal Engine development' },
52
+ // Bare "unity" is a common English word ("ensure unity between modules") —
53
+ // only match it near game/engine context (either word order).
54
+ { pattern: /\bC#\s*game\b|\bunity3d\b|\bunity\b(?=[\s\S]{0,60}\b(?:game|engine|3d|prefab|asset|c#)\b)|\b(?:game|engine|3d|prefab|asset|c#)\b(?=[\s\S]{0,60}\bunity\b)/i, agentSlug: 'specialized-unity-developer', routeName: 'unity', description: 'Unity game development' },
55
+ { pattern: /\bgodot\b|\bgdscript\b/i, agentSlug: 'specialized-godot-developer', routeName: 'godot', description: 'Godot game development' },
56
+ // SEO
57
+ { pattern: /\bSEO\b|\bsearch\s*engine\s*optim/i, agentSlug: 'specialized-seo-strategist', routeName: 'seo', description: 'SEO optimization' },
58
+ // Supply chain
59
+ { pattern: /\bsupply[-\s]?chain\b|\bSBOM\b/i, agentSlug: 'engineering-supply-chain-security', routeName: 'supply-chain', description: 'Supply chain security' },
60
+ // GraphQL
61
+ { pattern: /\bgraphql\b|\b\.graphql\b|\b\.gql\b/i, agentSlug: 'engineering-graphql-developer', routeName: 'graphql', description: 'GraphQL development' },
62
+ // Database / SQL
63
+ { pattern: /\bpostgres\b|\bmysql\b|\bmongodb\b|\bredis\b/i, agentSlug: 'engineering-database-engineer', routeName: 'database', description: 'Database engineering' },
64
+ ];
65
+ /**
66
+ * Fast keyword-based pre-filter for routing.
67
+ * Runs regex matches against task descriptions before falling through
68
+ * to semantic (embedding-based) routing. First match wins.
69
+ */
70
+ export class KeywordPreFilter {
71
+ rules;
72
+ constructor(customRules) {
73
+ this.rules = [...DEFAULT_KEYWORD_ROUTES];
74
+ if (customRules) {
75
+ // Prepend custom rules so they take priority
76
+ this.rules = [...customRules, ...this.rules];
77
+ }
78
+ }
79
+ /**
80
+ * Match a task description against keyword rules.
81
+ * Returns the first matching route or null if no keyword matches.
82
+ */
83
+ match(taskDescription) {
84
+ for (const rule of this.rules) {
85
+ if (rule.pattern.test(taskDescription)) {
86
+ return {
87
+ agentSlug: rule.agentSlug,
88
+ confidence: 1.0,
89
+ method: 'keyword',
90
+ routeName: rule.routeName,
91
+ };
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ /**
97
+ * Prepend a custom rule (highest priority).
98
+ */
99
+ addRule(rule) {
100
+ this.rules.unshift(rule);
101
+ }
102
+ /**
103
+ * Return a copy of the current rules (immutable view).
104
+ */
105
+ getRules() {
106
+ return [...this.rules];
107
+ }
108
+ }
109
+ //# sourceMappingURL=keyword-pre-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyword-pre-filter.js","sourceRoot":"","sources":["../src/keyword-pre-filter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { Route, RouteResult, LLMFallbackConfig } from './types.js';
2
+ export declare class LLMFallbackRouter {
3
+ private config;
4
+ /** Session-scoped fallback counter per route name */
5
+ private fallbackCounts;
6
+ constructor(config: LLMFallbackConfig);
7
+ /**
8
+ * Classify a task description using an LLM when semantic routing confidence is too low.
9
+ */
10
+ classify(taskDescription: string, routes: Route[], scores: Array<{
11
+ routeName: string;
12
+ agentSlug: string;
13
+ score: number;
14
+ }>): Promise<RouteResult>;
15
+ /** Returns a snapshot of fallback counts for this session */
16
+ getFallbackStats(): Record<string, number>;
17
+ }
18
+ //# sourceMappingURL=llm-fallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-fallback.d.ts","sourceRoot":"","sources":["../src/llm-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAOnE,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAoB;IAClC,qDAAqD;IACrD,OAAO,CAAC,cAAc,CAA6B;IAEnD,YAAY,MAAM,EAAE,iBAAiB,EAEpC;IAED;;OAEG;IACG,QAAQ,CACZ,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,KAAK,EAAE,EACf,MAAM,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,GACrE,OAAO,CAAC,WAAW,CAAC,CA4DtB;IAED,6DAA6D;IAC7D,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEzC;CACF"}
@@ -0,0 +1,76 @@
1
+ import { buildCapabilityIndex, buildCandidateHints } from './capability-index.js';
2
+ import { buildClassificationPrompt } from './prompts/classify.js';
3
+ /** Slug validation regex — must match ALLOWED_AGENT_TYPES pattern */
4
+ const SLUG_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;
5
+ export class LLMFallbackRouter {
6
+ config;
7
+ /** Session-scoped fallback counter per route name */
8
+ fallbackCounts = new Map();
9
+ constructor(config) {
10
+ this.config = config;
11
+ }
12
+ /**
13
+ * Classify a task description using an LLM when semantic routing confidence is too low.
14
+ */
15
+ async classify(taskDescription, routes, scores) {
16
+ const nearestRoute = scores[0];
17
+ // Log the fallback event
18
+ const onFallback = this.config.onFallback ?? defaultFallbackLogger;
19
+ onFallback(nearestRoute.routeName, taskDescription, nearestRoute.score);
20
+ this.fallbackCounts.set(nearestRoute.routeName, (this.fallbackCounts.get(nearestRoute.routeName) ?? 0) + 1);
21
+ // Build prompt
22
+ const capabilityIndex = buildCapabilityIndex(routes);
23
+ const candidateHints = buildCandidateHints(scores);
24
+ const prompt = buildClassificationPrompt(taskDescription, capabilityIndex, candidateHints);
25
+ // Call LLM
26
+ let rawResponse;
27
+ try {
28
+ rawResponse = await this.config.llmCaller(prompt);
29
+ }
30
+ catch (err) {
31
+ console.error('[LLMFallback] LLM call failed, using best semantic match:', err);
32
+ return {
33
+ agentSlug: nearestRoute.agentSlug,
34
+ confidence: nearestRoute.score,
35
+ method: 'llm_fallback',
36
+ routeName: nearestRoute.routeName,
37
+ };
38
+ }
39
+ // Parse and validate the slug
40
+ const slug = rawResponse.trim().replace(/[`'"]/g, '').toLowerCase();
41
+ if (!SLUG_PATTERN.test(slug)) {
42
+ console.error(`[LLMFallback] Invalid slug in LLM response: "${slug}"`);
43
+ return {
44
+ agentSlug: nearestRoute.agentSlug,
45
+ confidence: nearestRoute.score,
46
+ method: 'llm_fallback',
47
+ routeName: nearestRoute.routeName,
48
+ };
49
+ }
50
+ // Verify slug exists in routes — compare case-insensitively since slug is lowercased
51
+ const matchedRoute = routes.find(r => r.agentSlug.toLowerCase() === slug);
52
+ if (!matchedRoute) {
53
+ console.error(`[LLMFallback] LLM returned unknown slug: "${slug}"`);
54
+ return {
55
+ agentSlug: nearestRoute.agentSlug,
56
+ confidence: nearestRoute.score,
57
+ method: 'llm_fallback',
58
+ routeName: nearestRoute.routeName,
59
+ };
60
+ }
61
+ return {
62
+ agentSlug: matchedRoute.agentSlug, // use canonical casing from route definition
63
+ confidence: 0.85,
64
+ method: 'llm_fallback',
65
+ routeName: matchedRoute.name,
66
+ };
67
+ }
68
+ /** Returns a snapshot of fallback counts for this session */
69
+ getFallbackStats() {
70
+ return Object.fromEntries(this.fallbackCounts.entries());
71
+ }
72
+ }
73
+ function defaultFallbackLogger(routeName, task, confidence) {
74
+ console.warn(`[LLMFallback] Route "${routeName}" confidence ${confidence.toFixed(3)} below threshold for task: "${task.slice(0, 80)}..."`);
75
+ }
76
+ //# sourceMappingURL=llm-fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-fallback.js","sourceRoot":"","sources":["../src/llm-fallback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Build the classification prompt for LLM fallback routing.
3
+ */
4
+ export declare function buildClassificationPrompt(taskDescription: string, capabilityIndex: string, candidateHints: string): string;
5
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/prompts/classify.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,cAAc,EAAE,MAAM,GACrB,MAAM,CAoBR"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Build the classification prompt for LLM fallback routing.
3
+ */
4
+ export function buildClassificationPrompt(taskDescription, capabilityIndex, candidateHints) {
5
+ return `You are a task routing classifier. Your job is to select the single best agent slug for a given task.
6
+
7
+ ## Available Agents
8
+ ${capabilityIndex}
9
+
10
+ ## Semantic Pre-Candidates (top 3 by embedding similarity)
11
+ ${candidateHints}
12
+
13
+ ## Task to Route
14
+ "${taskDescription}"
15
+
16
+ ## Instructions
17
+ 1. Review the task description carefully.
18
+ 2. Consider the semantic pre-candidates — they may already be correct.
19
+ 3. Select the SINGLE best agent slug from the Available Agents list above.
20
+ 4. Output ONLY the agent slug — no explanation, no markdown, no punctuation.
21
+ 5. The slug must exactly match one of the slugs in the Available Agents list.
22
+
23
+ Agent slug:`;
24
+ }
25
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/prompts/classify.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,eAAuB,EACvB,eAAuB,EACvB,cAAsB;IAEtB,OAAO;;;EAGP,eAAe;;;EAGf,cAAc;;;GAGb,eAAe;;;;;;;;;YASN,CAAC;AACb,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { Route, RouteResult, RouteLayerConfig } from './types.js';
2
+ export declare class RouteLayer {
3
+ private centroids;
4
+ private encoder;
5
+ private config;
6
+ private initialized;
7
+ private llmFallback?;
8
+ private keywordFilter?;
9
+ constructor(config: RouteLayerConfig);
10
+ /**
11
+ * Pre-compute centroids for all routes.
12
+ * Idempotent — safe to call multiple times.
13
+ */
14
+ initialize(): Promise<void>;
15
+ /**
16
+ * Route a task description to the most appropriate agent slug.
17
+ * Auto-initializes on first call.
18
+ */
19
+ route(taskDescription: string): Promise<RouteResult>;
20
+ /**
21
+ * Register an additional route at runtime without re-initializing all centroids.
22
+ */
23
+ addRoute(route: Route): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=route-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-layer.d.ts","sourceRoot":"","sources":["../src/route-layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAWlE,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC,OAAO,CAAC,aAAa,CAAC,CAAmB;IAEzC,YAAY,MAAM,EAAE,gBAAgB,EAkBnC;IAED;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAuBhC;IAED;;;OAGG;IACG,KAAK,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgEzD;IAED;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAM1C;CACF"}