agentic-flow 3.1.0 → 3.3.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 (81) hide show
  1. package/.agents/skills/agentic-flow-ecosystem/SKILL.md +51 -0
  2. package/.claude/skills/agentic-flow-ecosystem/skill.md +59 -0
  3. package/.claude/skills/agentic-flow-quickstart/skill.md +6 -6
  4. package/.claude/skills/hooks-automation/skill.md +11 -11
  5. package/.claude/skills/memory-patterns/skill.md +7 -7
  6. package/.claude/skills/sparc-methodology/skill.md +13 -13
  7. package/.claude/skills/swarm-coordination/skill.md +5 -5
  8. package/.claude/skills/worker-benchmarks/skill.md +5 -5
  9. package/.claude/skills/worker-integration/skill.md +5 -5
  10. package/CHANGELOG.md +50 -0
  11. package/README.md +82 -67
  12. package/dist/.tsbuildinfo +1 -1
  13. package/dist/cli/commands/init.d.ts.map +1 -1
  14. package/dist/cli/commands/init.js +9 -0
  15. package/dist/cli/commands/init.js.map +1 -1
  16. package/dist/config/claudeFlow.d.ts +1 -1
  17. package/dist/core/agentdb-fast.d.ts +21 -11
  18. package/dist/core/agentdb-fast.d.ts.map +1 -1
  19. package/dist/core/agentdb-fast.js +39 -14
  20. package/dist/core/agentdb-fast.js.map +1 -1
  21. package/dist/integrations/agent-booster.d.ts +35 -0
  22. package/dist/integrations/agent-booster.d.ts.map +1 -0
  23. package/dist/integrations/agent-booster.js +45 -0
  24. package/dist/integrations/agent-booster.js.map +1 -0
  25. package/dist/integrations/index.d.ts +15 -0
  26. package/dist/integrations/index.d.ts.map +1 -0
  27. package/dist/integrations/index.js +11 -0
  28. package/dist/integrations/index.js.map +1 -0
  29. package/dist/integrations/onnx-wasm-embedder.d.ts +59 -0
  30. package/dist/integrations/onnx-wasm-embedder.d.ts.map +1 -0
  31. package/dist/integrations/onnx-wasm-embedder.js +145 -0
  32. package/dist/integrations/onnx-wasm-embedder.js.map +1 -0
  33. package/dist/integrations/optional.d.ts +32 -0
  34. package/dist/integrations/optional.d.ts.map +1 -0
  35. package/dist/integrations/optional.js +83 -0
  36. package/dist/integrations/optional.js.map +1 -0
  37. package/dist/integrations/prompt-injection.d.ts +52 -0
  38. package/dist/integrations/prompt-injection.d.ts.map +1 -0
  39. package/dist/integrations/prompt-injection.js +126 -0
  40. package/dist/integrations/prompt-injection.js.map +1 -0
  41. package/dist/integrations/sublinear.d.ts +99 -0
  42. package/dist/integrations/sublinear.d.ts.map +1 -0
  43. package/dist/integrations/sublinear.js +303 -0
  44. package/dist/integrations/sublinear.js.map +1 -0
  45. package/dist/integrations/task-partition.d.ts +40 -0
  46. package/dist/integrations/task-partition.d.ts.map +1 -0
  47. package/dist/integrations/task-partition.js +173 -0
  48. package/dist/integrations/task-partition.js.map +1 -0
  49. package/dist/intelligence/RuVectorIntelligence.d.ts.map +1 -1
  50. package/dist/intelligence/RuVectorIntelligence.js +4 -3
  51. package/dist/intelligence/RuVectorIntelligence.js.map +1 -1
  52. package/dist/mcp/mcp-policy.d.ts.map +1 -1
  53. package/dist/mcp/mcp-policy.js +2 -0
  54. package/dist/mcp/mcp-policy.js.map +1 -1
  55. package/dist/mcp/standalone-stdio.js +12 -26
  56. package/dist/mcp/standalone-stdio.js.map +1 -1
  57. package/dist/mcp/tools/ecosystem-tools.d.ts +49 -0
  58. package/dist/mcp/tools/ecosystem-tools.d.ts.map +1 -0
  59. package/dist/mcp/tools/ecosystem-tools.js +148 -0
  60. package/dist/mcp/tools/ecosystem-tools.js.map +1 -0
  61. package/dist/optimizations/configuration-tuning.d.ts +1 -1
  62. package/dist/routing/SemanticRouter.d.ts +25 -9
  63. package/dist/routing/SemanticRouter.d.ts.map +1 -1
  64. package/dist/routing/SemanticRouter.js +63 -12
  65. package/dist/routing/SemanticRouter.js.map +1 -1
  66. package/dist/services/sona-loader.d.ts +15 -1
  67. package/dist/services/sona-loader.d.ts.map +1 -1
  68. package/dist/services/sona-loader.js +54 -2
  69. package/dist/services/sona-loader.js.map +1 -1
  70. package/dist/utils/input-validator.d.ts +10 -2
  71. package/dist/utils/input-validator.d.ts.map +1 -1
  72. package/dist/utils/input-validator.js +30 -2
  73. package/dist/utils/input-validator.js.map +1 -1
  74. package/dist/utils/model-cache.d.ts.map +1 -1
  75. package/dist/utils/model-cache.js +6 -25
  76. package/dist/utils/model-cache.js.map +1 -1
  77. package/dist/wasm/onnx-embeddings-wasm.d.ts.map +1 -1
  78. package/dist/wasm/onnx-embeddings-wasm.js +21 -10
  79. package/dist/wasm/onnx-embeddings-wasm.js.map +1 -1
  80. package/docs/integrations/FASTMCP_CLI_INTEGRATION.md +1 -1
  81. package/package.json +13 -8
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Real text embeddings through `ruvector-onnx-embeddings-wasm` (all-MiniLM-L6-v2, 384-d).
3
+ *
4
+ * Packaging note: ruvector-onnx-embeddings-wasm 0.1.0-0.1.2 declares
5
+ * `"type": "module"` but ships wasm-bindgen's CommonJS (nodejs target) glue,
6
+ * which assigns `module.exports`. `import()` therefore throws
7
+ * "module is not defined in ES module scope", and `require()` fails the same
8
+ * way because Node treats the file as ESM. We compile the glue explicitly as
9
+ * CommonJS. If a future release ships real ESM, plain `import()` is used.
10
+ *
11
+ * Model: the WASM runtime cannot load the quantized ONNX graph agentic-flow's
12
+ * OptimizedEmbedder uses by default (it fails on an Unsqueeze node), so this
13
+ * uses the full-precision Xenova/all-MiniLM-L6-v2 model (~91 MB) plus its
14
+ * tokenizer.json. Both live in the same model directory OptimizedEmbedder uses
15
+ * (`~/.agentic-flow/models`, override with `AGENTIC_FLOW_MODEL_DIR`); the ONNX
16
+ * file name matches OptimizedEmbedder's `all-MiniLM-L6-v2-full` registry entry,
17
+ * so either component can reuse the other's download.
18
+ *
19
+ * Downloads happen on first use unless any of these is set:
20
+ * `AGENTIC_FLOW_OFFLINE=1`, `HF_HUB_OFFLINE=1`, `AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD=0`.
21
+ * Nothing here writes to stdout (the MCP stdio server owns it).
22
+ */
23
+ export declare const ONNX_WASM_PACKAGE = "ruvector-onnx-embeddings-wasm";
24
+ export declare const ONNX_WASM_DIMENSION = 384;
25
+ interface WasmEmbedderHandle {
26
+ embedOne(text: string): Float32Array;
27
+ dimension(): number;
28
+ }
29
+ export interface OnnxWasmModule {
30
+ WasmEmbedder: new (modelBytes: Uint8Array, tokenizerJson: string) => WasmEmbedderHandle;
31
+ cosineSimilarity(a: Float32Array, b: Float32Array): number;
32
+ version(): string;
33
+ }
34
+ /** Load the WASM glue (see the packaging note above), or null when absent. */
35
+ export declare function loadOnnxWasmModule(): Promise<OnnxWasmModule | null>;
36
+ export declare function onnxWasmModelDir(): string;
37
+ export declare function onnxWasmDownloadAllowed(): boolean;
38
+ /** Whether model + tokenizer are already on disk (no network needed). */
39
+ export declare function onnxWasmModelCached(dir?: string): boolean;
40
+ /** A loaded all-MiniLM-L6-v2 embedder. `embed` is async to match other embedders. */
41
+ export declare class OnnxWasmTextEmbedder {
42
+ private readonly handle;
43
+ readonly dimension: number;
44
+ readonly source: "onnx-wasm";
45
+ private constructor();
46
+ /** Build an embedder, or null when the package or the model is unavailable. */
47
+ static create(options?: {
48
+ modelDir?: string;
49
+ }): Promise<OnnxWasmTextEmbedder | null>;
50
+ embedSync(text: string): Float32Array;
51
+ embed(text: string): Promise<Float32Array>;
52
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
53
+ }
54
+ /** Process-wide embedder (loaded once), or null. */
55
+ export declare function getOnnxWasmEmbedder(): Promise<OnnxWasmTextEmbedder | null>;
56
+ /** Test hook. */
57
+ export declare function _resetOnnxWasmForTests(): void;
58
+ export {};
59
+ //# sourceMappingURL=onnx-wasm-embedder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onnx-wasm-embedder.d.ts","sourceRoot":"","sources":["../../src/integrations/onnx-wasm-embedder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,eAAO,MAAM,iBAAiB,kCAAkC,CAAC;AACjE,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAOvC,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAAC;IACrC,SAAS,IAAI,MAAM,CAAC;CACrB;AACD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,KAAK,kBAAkB,CAAC;IACxF,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC3D,OAAO,IAAI,MAAM,CAAC;CACnB;AAID,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAsBnE;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,uBAAuB,IAAI,OAAO,CAQjD;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,GAAG,SAAqB,GAAG,OAAO,CAErE;AAqBD,qFAAqF;AACrF,qBAAa,oBAAoB;IAIX,OAAO,CAAC,QAAQ,CAAC,MAAM;IAH3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAG,WAAW,CAAU;IAEvC,OAAO;IAIP,+EAA+E;WAClE,MAAM,CAAC,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAiB9F,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IAI/B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI1C,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CAG3D;AAID,oDAAoD;AACpD,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAG1E;AAED,iBAAiB;AACjB,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Real text embeddings through `ruvector-onnx-embeddings-wasm` (all-MiniLM-L6-v2, 384-d).
3
+ *
4
+ * Packaging note: ruvector-onnx-embeddings-wasm 0.1.0-0.1.2 declares
5
+ * `"type": "module"` but ships wasm-bindgen's CommonJS (nodejs target) glue,
6
+ * which assigns `module.exports`. `import()` therefore throws
7
+ * "module is not defined in ES module scope", and `require()` fails the same
8
+ * way because Node treats the file as ESM. We compile the glue explicitly as
9
+ * CommonJS. If a future release ships real ESM, plain `import()` is used.
10
+ *
11
+ * Model: the WASM runtime cannot load the quantized ONNX graph agentic-flow's
12
+ * OptimizedEmbedder uses by default (it fails on an Unsqueeze node), so this
13
+ * uses the full-precision Xenova/all-MiniLM-L6-v2 model (~91 MB) plus its
14
+ * tokenizer.json. Both live in the same model directory OptimizedEmbedder uses
15
+ * (`~/.agentic-flow/models`, override with `AGENTIC_FLOW_MODEL_DIR`); the ONNX
16
+ * file name matches OptimizedEmbedder's `all-MiniLM-L6-v2-full` registry entry,
17
+ * so either component can reuse the other's download.
18
+ *
19
+ * Downloads happen on first use unless any of these is set:
20
+ * `AGENTIC_FLOW_OFFLINE=1`, `HF_HUB_OFFLINE=1`, `AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD=0`.
21
+ * Nothing here writes to stdout (the MCP stdio server owns it).
22
+ */
23
+ import Module from 'node:module';
24
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
25
+ import { homedir } from 'node:os';
26
+ import { dirname, join } from 'node:path';
27
+ import { resolveOptional } from './optional.js';
28
+ export const ONNX_WASM_PACKAGE = 'ruvector-onnx-embeddings-wasm';
29
+ export const ONNX_WASM_DIMENSION = 384;
30
+ const MODEL_FILE = 'all-MiniLM-L6-v2-full.onnx';
31
+ const TOKENIZER_FILE = 'all-MiniLM-L6-v2.tokenizer.json';
32
+ const MODEL_URL = 'https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main/onnx/model.onnx';
33
+ const TOKENIZER_URL = 'https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main/tokenizer.json';
34
+ let modulePromise = null;
35
+ /** Load the WASM glue (see the packaging note above), or null when absent. */
36
+ export function loadOnnxWasmModule() {
37
+ if (!modulePromise) {
38
+ modulePromise = (async () => {
39
+ const entry = resolveOptional(ONNX_WASM_PACKAGE);
40
+ if (!entry)
41
+ return null;
42
+ try {
43
+ const source = readFileSync(entry, 'utf8');
44
+ if (/\bmodule\.exports\b/.test(source)) {
45
+ const M = Module;
46
+ const mod = new M(entry, null);
47
+ mod.filename = entry;
48
+ mod.paths = M._nodeModulePaths(dirname(entry));
49
+ mod._compile(source, entry);
50
+ return mod.exports;
51
+ }
52
+ return (await import(ONNX_WASM_PACKAGE));
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ })();
58
+ }
59
+ return modulePromise;
60
+ }
61
+ export function onnxWasmModelDir() {
62
+ return process.env.AGENTIC_FLOW_MODEL_DIR || join(homedir(), '.agentic-flow', 'models');
63
+ }
64
+ export function onnxWasmDownloadAllowed() {
65
+ const off = (v) => v === '1' || v === 'true';
66
+ return !(off(process.env.AGENTIC_FLOW_OFFLINE) ||
67
+ off(process.env.HF_HUB_OFFLINE) ||
68
+ process.env.AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD === '0' ||
69
+ process.env.AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD === 'false');
70
+ }
71
+ /** Whether model + tokenizer are already on disk (no network needed). */
72
+ export function onnxWasmModelCached(dir = onnxWasmModelDir()) {
73
+ return existsSync(join(dir, MODEL_FILE)) && existsSync(join(dir, TOKENIZER_FILE));
74
+ }
75
+ async function download(url, target) {
76
+ const res = await fetch(url);
77
+ if (!res.ok)
78
+ throw new Error(`download failed ${res.status} ${res.statusText}: ${url}`);
79
+ const bytes = new Uint8Array(await res.arrayBuffer());
80
+ const tmp = `${target}.${process.pid}.part`;
81
+ writeFileSync(tmp, bytes, { mode: 0o600 });
82
+ renameSync(tmp, target);
83
+ }
84
+ async function ensureModelFiles(dir) {
85
+ if (onnxWasmModelCached(dir))
86
+ return true;
87
+ if (!onnxWasmDownloadAllowed())
88
+ return false;
89
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
90
+ process.stderr.write(`[agentic-flow] downloading all-MiniLM-L6-v2 (~91 MB) to ${dir}\n`);
91
+ if (!existsSync(join(dir, TOKENIZER_FILE)))
92
+ await download(TOKENIZER_URL, join(dir, TOKENIZER_FILE));
93
+ if (!existsSync(join(dir, MODEL_FILE)))
94
+ await download(MODEL_URL, join(dir, MODEL_FILE));
95
+ return true;
96
+ }
97
+ /** A loaded all-MiniLM-L6-v2 embedder. `embed` is async to match other embedders. */
98
+ export class OnnxWasmTextEmbedder {
99
+ handle;
100
+ dimension;
101
+ source = 'onnx-wasm';
102
+ constructor(handle) {
103
+ this.handle = handle;
104
+ this.dimension = handle.dimension();
105
+ }
106
+ /** Build an embedder, or null when the package or the model is unavailable. */
107
+ static async create(options = {}) {
108
+ const mod = await loadOnnxWasmModule();
109
+ if (!mod)
110
+ return null;
111
+ const dir = options.modelDir ?? onnxWasmModelDir();
112
+ try {
113
+ if (!(await ensureModelFiles(dir)))
114
+ return null;
115
+ const handle = new mod.WasmEmbedder(new Uint8Array(readFileSync(join(dir, MODEL_FILE))), readFileSync(join(dir, TOKENIZER_FILE), 'utf8'));
116
+ return new OnnxWasmTextEmbedder(handle);
117
+ }
118
+ catch (err) {
119
+ process.stderr.write(`[agentic-flow] ONNX WASM embedder unavailable: ${err?.message ?? err}\n`);
120
+ return null;
121
+ }
122
+ }
123
+ embedSync(text) {
124
+ return this.handle.embedOne(text);
125
+ }
126
+ async embed(text) {
127
+ return this.handle.embedOne(text);
128
+ }
129
+ async embedBatch(texts) {
130
+ return texts.map((t) => this.handle.embedOne(t));
131
+ }
132
+ }
133
+ let shared = null;
134
+ /** Process-wide embedder (loaded once), or null. */
135
+ export function getOnnxWasmEmbedder() {
136
+ if (!shared)
137
+ shared = OnnxWasmTextEmbedder.create();
138
+ return shared;
139
+ }
140
+ /** Test hook. */
141
+ export function _resetOnnxWasmForTests() {
142
+ shared = null;
143
+ modulePromise = null;
144
+ }
145
+ //# sourceMappingURL=onnx-wasm-embedder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onnx-wasm-embedder.js","sourceRoot":"","sources":["../../src/integrations/onnx-wasm-embedder.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Real text embeddings through `ruvector-onnx-embeddings-wasm` (all-MiniLM-L6-v2, 384-d).\n *\n * Packaging note: ruvector-onnx-embeddings-wasm 0.1.0-0.1.2 declares\n * `\"type\": \"module\"` but ships wasm-bindgen's CommonJS (nodejs target) glue,\n * which assigns `module.exports`. `import()` therefore throws\n * \"module is not defined in ES module scope\", and `require()` fails the same\n * way because Node treats the file as ESM. We compile the glue explicitly as\n * CommonJS. If a future release ships real ESM, plain `import()` is used.\n *\n * Model: the WASM runtime cannot load the quantized ONNX graph agentic-flow's\n * OptimizedEmbedder uses by default (it fails on an Unsqueeze node), so this\n * uses the full-precision Xenova/all-MiniLM-L6-v2 model (~91 MB) plus its\n * tokenizer.json. Both live in the same model directory OptimizedEmbedder uses\n * (`~/.agentic-flow/models`, override with `AGENTIC_FLOW_MODEL_DIR`); the ONNX\n * file name matches OptimizedEmbedder's `all-MiniLM-L6-v2-full` registry entry,\n * so either component can reuse the other's download.\n *\n * Downloads happen on first use unless any of these is set:\n * `AGENTIC_FLOW_OFFLINE=1`, `HF_HUB_OFFLINE=1`, `AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD=0`.\n * Nothing here writes to stdout (the MCP stdio server owns it).\n */\n\nimport Module from 'node:module';\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { resolveOptional } from './optional.js';\n\nexport const ONNX_WASM_PACKAGE = 'ruvector-onnx-embeddings-wasm';\nexport const ONNX_WASM_DIMENSION = 384;\n\nconst MODEL_FILE = 'all-MiniLM-L6-v2-full.onnx';\nconst TOKENIZER_FILE = 'all-MiniLM-L6-v2.tokenizer.json';\nconst MODEL_URL = 'https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main/onnx/model.onnx';\nconst TOKENIZER_URL = 'https://huggingface.co/Xenova/all-MiniLM-L6-v2/resolve/main/tokenizer.json';\n\ninterface WasmEmbedderHandle {\n embedOne(text: string): Float32Array;\n dimension(): number;\n}\nexport interface OnnxWasmModule {\n WasmEmbedder: new (modelBytes: Uint8Array, tokenizerJson: string) => WasmEmbedderHandle;\n cosineSimilarity(a: Float32Array, b: Float32Array): number;\n version(): string;\n}\n\nlet modulePromise: Promise<OnnxWasmModule | null> | null = null;\n\n/** Load the WASM glue (see the packaging note above), or null when absent. */\nexport function loadOnnxWasmModule(): Promise<OnnxWasmModule | null> {\n if (!modulePromise) {\n modulePromise = (async () => {\n const entry = resolveOptional(ONNX_WASM_PACKAGE);\n if (!entry) return null;\n try {\n const source = readFileSync(entry, 'utf8');\n if (/\\bmodule\\.exports\\b/.test(source)) {\n const M = Module as any;\n const mod = new M(entry, null);\n mod.filename = entry;\n mod.paths = M._nodeModulePaths(dirname(entry));\n mod._compile(source, entry);\n return mod.exports as OnnxWasmModule;\n }\n return (await import(ONNX_WASM_PACKAGE)) as unknown as OnnxWasmModule;\n } catch {\n return null;\n }\n })();\n }\n return modulePromise;\n}\n\nexport function onnxWasmModelDir(): string {\n return process.env.AGENTIC_FLOW_MODEL_DIR || join(homedir(), '.agentic-flow', 'models');\n}\n\nexport function onnxWasmDownloadAllowed(): boolean {\n const off = (v: string | undefined) => v === '1' || v === 'true';\n return !(\n off(process.env.AGENTIC_FLOW_OFFLINE) ||\n off(process.env.HF_HUB_OFFLINE) ||\n process.env.AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD === '0' ||\n process.env.AGENTIC_FLOW_EMBEDDINGS_DOWNLOAD === 'false'\n );\n}\n\n/** Whether model + tokenizer are already on disk (no network needed). */\nexport function onnxWasmModelCached(dir = onnxWasmModelDir()): boolean {\n return existsSync(join(dir, MODEL_FILE)) && existsSync(join(dir, TOKENIZER_FILE));\n}\n\nasync function download(url: string, target: string): Promise<void> {\n const res = await fetch(url);\n if (!res.ok) throw new Error(`download failed ${res.status} ${res.statusText}: ${url}`);\n const bytes = new Uint8Array(await res.arrayBuffer());\n const tmp = `${target}.${process.pid}.part`;\n writeFileSync(tmp, bytes, { mode: 0o600 });\n renameSync(tmp, target);\n}\n\nasync function ensureModelFiles(dir: string): Promise<boolean> {\n if (onnxWasmModelCached(dir)) return true;\n if (!onnxWasmDownloadAllowed()) return false;\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n process.stderr.write(`[agentic-flow] downloading all-MiniLM-L6-v2 (~91 MB) to ${dir}\\n`);\n if (!existsSync(join(dir, TOKENIZER_FILE))) await download(TOKENIZER_URL, join(dir, TOKENIZER_FILE));\n if (!existsSync(join(dir, MODEL_FILE))) await download(MODEL_URL, join(dir, MODEL_FILE));\n return true;\n}\n\n/** A loaded all-MiniLM-L6-v2 embedder. `embed` is async to match other embedders. */\nexport class OnnxWasmTextEmbedder {\n readonly dimension: number;\n readonly source = 'onnx-wasm' as const;\n\n private constructor(private readonly handle: WasmEmbedderHandle) {\n this.dimension = handle.dimension();\n }\n\n /** Build an embedder, or null when the package or the model is unavailable. */\n static async create(options: { modelDir?: string } = {}): Promise<OnnxWasmTextEmbedder | null> {\n const mod = await loadOnnxWasmModule();\n if (!mod) return null;\n const dir = options.modelDir ?? onnxWasmModelDir();\n try {\n if (!(await ensureModelFiles(dir))) return null;\n const handle = new mod.WasmEmbedder(\n new Uint8Array(readFileSync(join(dir, MODEL_FILE))),\n readFileSync(join(dir, TOKENIZER_FILE), 'utf8'),\n );\n return new OnnxWasmTextEmbedder(handle);\n } catch (err: any) {\n process.stderr.write(`[agentic-flow] ONNX WASM embedder unavailable: ${err?.message ?? err}\\n`);\n return null;\n }\n }\n\n embedSync(text: string): Float32Array {\n return this.handle.embedOne(text);\n }\n\n async embed(text: string): Promise<Float32Array> {\n return this.handle.embedOne(text);\n }\n\n async embedBatch(texts: string[]): Promise<Float32Array[]> {\n return texts.map((t) => this.handle.embedOne(t));\n }\n}\n\nlet shared: Promise<OnnxWasmTextEmbedder | null> | null = null;\n\n/** Process-wide embedder (loaded once), or null. */\nexport function getOnnxWasmEmbedder(): Promise<OnnxWasmTextEmbedder | null> {\n if (!shared) shared = OnnxWasmTextEmbedder.create();\n return shared;\n}\n\n/** Test hook. */\nexport function _resetOnnxWasmForTests(): void {\n shared = null;\n modulePromise = null;\n}\n"]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Optional ecosystem packages — one place to load them.
3
+ *
4
+ * Every package loaded here is an `optionalDependency` of agentic-flow: npm
5
+ * installs it by default, but it may be missing (`--omit=optional`, an
6
+ * unsupported platform, a failed native build). Callers therefore always get
7
+ * either the module or `null`, never a throw, and keep a built-in fallback.
8
+ *
9
+ * Two loaders:
10
+ * - `requireOptional` is synchronous (createRequire) for CommonJS packages that
11
+ * must be usable from synchronous code paths (e.g. input validation).
12
+ * - `importOptional` is asynchronous for ESM packages.
13
+ *
14
+ * Results (including "not installed") are cached per specifier.
15
+ */
16
+ /** Synchronously load a CommonJS optional package, or return null. */
17
+ export declare function requireOptional<T = any>(specifier: string): T | null;
18
+ /**
19
+ * Whether a package is installed, without loading it. ESM-only packages whose
20
+ * exports map has no require/default condition cannot be require.resolve()d,
21
+ * but that error itself proves the package is there.
22
+ */
23
+ export declare function isOptionalInstalled(specifier: string): boolean;
24
+ /** Asynchronously import an optional package (ESM or CJS), or return null. */
25
+ export declare function importOptional<T = any>(specifier: string): Promise<T | null>;
26
+ /** Why the last load of `specifier` failed, if it did. */
27
+ export declare function optionalLoadError(specifier: string): string | undefined;
28
+ /** Resolve a package's entry file without loading it, or null if absent. */
29
+ export declare function resolveOptional(specifier: string): string | null;
30
+ /** Test hook: forget cached load results. */
31
+ export declare function _resetOptionalCache(): void;
32
+ //# sourceMappingURL=optional.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optional.d.ts","sourceRoot":"","sources":["../../src/integrations/optional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAUH,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,CAAC,GAAG,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAWpE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAO9D;AAED,8EAA8E;AAC9E,wBAAsB,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAalF;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhE;AAED,6CAA6C;AAC7C,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Optional ecosystem packages — one place to load them.
3
+ *
4
+ * Every package loaded here is an `optionalDependency` of agentic-flow: npm
5
+ * installs it by default, but it may be missing (`--omit=optional`, an
6
+ * unsupported platform, a failed native build). Callers therefore always get
7
+ * either the module or `null`, never a throw, and keep a built-in fallback.
8
+ *
9
+ * Two loaders:
10
+ * - `requireOptional` is synchronous (createRequire) for CommonJS packages that
11
+ * must be usable from synchronous code paths (e.g. input validation).
12
+ * - `importOptional` is asynchronous for ESM packages.
13
+ *
14
+ * Results (including "not installed") are cached per specifier.
15
+ */
16
+ import { createRequire } from 'node:module';
17
+ const nodeRequire = createRequire(import.meta.url);
18
+ const syncCache = new Map();
19
+ const asyncCache = new Map();
20
+ /** Synchronously load a CommonJS optional package, or return null. */
21
+ export function requireOptional(specifier) {
22
+ const cached = syncCache.get(specifier);
23
+ if (cached)
24
+ return cached.mod;
25
+ let entry;
26
+ try {
27
+ entry = { mod: nodeRequire(specifier) };
28
+ }
29
+ catch (err) {
30
+ entry = { mod: null, error: err?.message ?? String(err) };
31
+ }
32
+ syncCache.set(specifier, entry);
33
+ return entry.mod;
34
+ }
35
+ /**
36
+ * Whether a package is installed, without loading it. ESM-only packages whose
37
+ * exports map has no require/default condition cannot be require.resolve()d,
38
+ * but that error itself proves the package is there.
39
+ */
40
+ export function isOptionalInstalled(specifier) {
41
+ try {
42
+ nodeRequire.resolve(specifier);
43
+ return true;
44
+ }
45
+ catch (err) {
46
+ return err?.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED';
47
+ }
48
+ }
49
+ /** Asynchronously import an optional package (ESM or CJS), or return null. */
50
+ export async function importOptional(specifier) {
51
+ let pending = asyncCache.get(specifier);
52
+ if (!pending) {
53
+ pending = (async () => {
54
+ try {
55
+ return { mod: await import(specifier) };
56
+ }
57
+ catch (err) {
58
+ return { mod: null, error: err?.message ?? String(err) };
59
+ }
60
+ })();
61
+ asyncCache.set(specifier, pending);
62
+ }
63
+ return (await pending).mod;
64
+ }
65
+ /** Why the last load of `specifier` failed, if it did. */
66
+ export function optionalLoadError(specifier) {
67
+ return syncCache.get(specifier)?.error;
68
+ }
69
+ /** Resolve a package's entry file without loading it, or null if absent. */
70
+ export function resolveOptional(specifier) {
71
+ try {
72
+ return nodeRequire.resolve(specifier);
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ /** Test hook: forget cached load results. */
79
+ export function _resetOptionalCache() {
80
+ syncCache.clear();
81
+ asyncCache.clear();
82
+ }
83
+ //# sourceMappingURL=optional.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optional.js","sourceRoot":"","sources":["../../src/integrations/optional.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,WAAW,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAGnD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsB,CAAC;AAChD,MAAM,UAAU,GAAG,IAAI,GAAG,EAA+B,CAAC;AAE1D,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAU,SAAiB;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,GAAe,CAAC;IAC1C,IAAI,KAAiB,CAAC;IACtB,IAAI,CAAC;QACH,KAAK,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,KAAK,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,CAAC;IACD,SAAS,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAChC,OAAO,KAAK,CAAC,GAAe,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,IAAI,CAAC;QACH,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,GAAG,EAAE,IAAI,KAAK,+BAA+B,CAAC;IACvD,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAU,SAAiB;IAC7D,IAAI,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,KAAK,IAAyB,EAAE;YACzC,IAAI,CAAC;gBACH,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3D,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,MAAM,OAAO,CAAC,CAAC,GAAe,CAAC;AACzC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC;AACzC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,IAAI,CAAC;QACH,OAAO,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,mBAAmB;IACjC,SAAS,CAAC,KAAK,EAAE,CAAC;IAClB,UAAU,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC","sourcesContent":["/**\n * Optional ecosystem packages — one place to load them.\n *\n * Every package loaded here is an `optionalDependency` of agentic-flow: npm\n * installs it by default, but it may be missing (`--omit=optional`, an\n * unsupported platform, a failed native build). Callers therefore always get\n * either the module or `null`, never a throw, and keep a built-in fallback.\n *\n * Two loaders:\n * - `requireOptional` is synchronous (createRequire) for CommonJS packages that\n * must be usable from synchronous code paths (e.g. input validation).\n * - `importOptional` is asynchronous for ESM packages.\n *\n * Results (including \"not installed\") are cached per specifier.\n */\n\nimport { createRequire } from 'node:module';\n\nconst nodeRequire = createRequire(import.meta.url);\n\ntype CacheEntry = { mod: any | null; error?: string };\nconst syncCache = new Map<string, CacheEntry>();\nconst asyncCache = new Map<string, Promise<CacheEntry>>();\n\n/** Synchronously load a CommonJS optional package, or return null. */\nexport function requireOptional<T = any>(specifier: string): T | null {\n const cached = syncCache.get(specifier);\n if (cached) return cached.mod as T | null;\n let entry: CacheEntry;\n try {\n entry = { mod: nodeRequire(specifier) };\n } catch (err: any) {\n entry = { mod: null, error: err?.message ?? String(err) };\n }\n syncCache.set(specifier, entry);\n return entry.mod as T | null;\n}\n\n/**\n * Whether a package is installed, without loading it. ESM-only packages whose\n * exports map has no require/default condition cannot be require.resolve()d,\n * but that error itself proves the package is there.\n */\nexport function isOptionalInstalled(specifier: string): boolean {\n try {\n nodeRequire.resolve(specifier);\n return true;\n } catch (err: any) {\n return err?.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED';\n }\n}\n\n/** Asynchronously import an optional package (ESM or CJS), or return null. */\nexport async function importOptional<T = any>(specifier: string): Promise<T | null> {\n let pending = asyncCache.get(specifier);\n if (!pending) {\n pending = (async (): Promise<CacheEntry> => {\n try {\n return { mod: await import(specifier) };\n } catch (err: any) {\n return { mod: null, error: err?.message ?? String(err) };\n }\n })();\n asyncCache.set(specifier, pending);\n }\n return (await pending).mod as T | null;\n}\n\n/** Why the last load of `specifier` failed, if it did. */\nexport function optionalLoadError(specifier: string): string | undefined {\n return syncCache.get(specifier)?.error;\n}\n\n/** Resolve a package's entry file without loading it, or null if absent. */\nexport function resolveOptional(specifier: string): string | null {\n try {\n return nodeRequire.resolve(specifier);\n } catch {\n return null;\n }\n}\n\n/** Test hook: forget cached load results. */\nexport function _resetOptionalCache(): void {\n syncCache.clear();\n asyncCache.clear();\n}\n"]}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Prompt-injection screening for task text.
3
+ *
4
+ * Uses the optional `aidefence` package (53 curated patterns across encodings
5
+ * and obfuscation variants) when installed, and a small built-in pattern set
6
+ * otherwise. Only the `aidefence/dist/detection` subpath is loaded: the package
7
+ * root starts an Express server stack, the detection engine has no deps.
8
+ *
9
+ * Policy (env `AGENTIC_FLOW_PROMPT_INJECTION`):
10
+ * - `block` (default): reject `critical` findings, warn on the rest
11
+ * - `strict`: reject `critical` and `high`, warn on the rest
12
+ * - `warn`: never reject, warn on every finding
13
+ * - `off`: do not scan
14
+ *
15
+ * `high` is not rejected by default because legitimate agent prompts often
16
+ * trip it — "You are now a code reviewer…" matches aidefence's role-switch rule.
17
+ */
18
+ export type InjectionSeverity = 'low' | 'medium' | 'high' | 'critical';
19
+ export type InjectionPolicy = 'block' | 'strict' | 'warn' | 'off';
20
+ export interface InjectionThreat {
21
+ id: string;
22
+ type: string;
23
+ severity: InjectionSeverity;
24
+ confidence: number;
25
+ description: string;
26
+ match?: string;
27
+ }
28
+ export interface InjectionScan {
29
+ safe: boolean;
30
+ maxSeverity: InjectionSeverity | null;
31
+ threats: InjectionThreat[];
32
+ /** Which engine produced the result. */
33
+ engine: 'aidefence' | 'builtin' | 'disabled';
34
+ /** What the active policy decided. */
35
+ decision: 'allow' | 'warn' | 'block';
36
+ }
37
+ interface AidefenceDetector {
38
+ detect(text: string): {
39
+ safe: boolean;
40
+ maxSeverity: InjectionSeverity | null;
41
+ threats: Array<InjectionThreat & Record<string, unknown>>;
42
+ };
43
+ }
44
+ export declare function getInjectionPolicy(): InjectionPolicy;
45
+ /** Scan `text` for prompt-injection attempts under the active policy. */
46
+ export declare function scanPromptInjection(text: string, policy?: InjectionPolicy): InjectionScan;
47
+ /** Which engine `scanPromptInjection` will use. */
48
+ export declare function promptInjectionEngine(): 'aidefence' | 'builtin';
49
+ /** Test hook: `null` forces the built-in engine, `undefined` restores auto-detection. */
50
+ export declare function _setInjectionDetectorForTests(d: AidefenceDetector | null | undefined): void;
51
+ export {};
52
+ //# sourceMappingURL=prompt-injection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-injection.d.ts","sourceRoot":"","sources":["../../src/integrations/prompt-injection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AACvE,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,wCAAwC;IACxC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;IAC7C,sCAAsC;IACtC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;CACtC;AAED,UAAU,iBAAiB;IACzB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;QACtC,OAAO,EAAE,KAAK,CAAC,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KAC3D,CAAC;CACH;AA2CD,wBAAgB,kBAAkB,IAAI,eAAe,CAKpD;AA0BD,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,eAAsC,GAAG,aAAa,CAwB/G;AAED,mDAAmD;AACnD,wBAAgB,qBAAqB,IAAI,WAAW,GAAG,SAAS,CAE/D;AAED,yFAAyF;AACzF,wBAAgB,6BAA6B,CAAC,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAE3F"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Prompt-injection screening for task text.
3
+ *
4
+ * Uses the optional `aidefence` package (53 curated patterns across encodings
5
+ * and obfuscation variants) when installed, and a small built-in pattern set
6
+ * otherwise. Only the `aidefence/dist/detection` subpath is loaded: the package
7
+ * root starts an Express server stack, the detection engine has no deps.
8
+ *
9
+ * Policy (env `AGENTIC_FLOW_PROMPT_INJECTION`):
10
+ * - `block` (default): reject `critical` findings, warn on the rest
11
+ * - `strict`: reject `critical` and `high`, warn on the rest
12
+ * - `warn`: never reject, warn on every finding
13
+ * - `off`: do not scan
14
+ *
15
+ * `high` is not rejected by default because legitimate agent prompts often
16
+ * trip it — "You are now a code reviewer…" matches aidefence's role-switch rule.
17
+ */
18
+ import { requireOptional } from './optional.js';
19
+ const SEVERITY_RANK = { low: 1, medium: 2, high: 3, critical: 4 };
20
+ /** Built-in fallback: the highest-signal override/exfiltration/jailbreak shapes. */
21
+ const BUILTIN_RULES = [
22
+ {
23
+ id: 'AF-001', type: 'instruction_override', severity: 'critical', confidence: 0.95,
24
+ description: 'Attempt to override prior instructions',
25
+ pattern: /\b(ignore|disregard|forget|override)\s+(all\s+|any\s+|the\s+|your\s+)*(previous|prior|above|earlier|preceding|system)\s+(instructions?|rules?|prompts?|directions?|guidelines?)/i,
26
+ },
27
+ {
28
+ id: 'AF-002', type: 'prompt_exfiltration', severity: 'high', confidence: 0.9,
29
+ description: 'Attempt to reveal the system prompt or hidden instructions',
30
+ pattern: /\b(reveal|print|show|output|repeat|leak)\s+(me\s+)?(your|the)\s+(system\s+prompt|hidden\s+instructions?|initial\s+instructions?)/i,
31
+ },
32
+ {
33
+ id: 'AF-003', type: 'jailbreak', severity: 'high', confidence: 0.9,
34
+ description: 'Known jailbreak persona or mode switch',
35
+ pattern: /\b(do\s+anything\s+now|DAN\s+mode|developer\s+mode\s+(enabled|on)|jailbreak(ed)?\s+mode)\b/i,
36
+ },
37
+ {
38
+ id: 'AF-004', type: 'role_token_injection', severity: 'high', confidence: 0.9,
39
+ description: 'Chat-template role markers embedded in user text',
40
+ pattern: /<\|?(im_start|im_end|system|endoftext)\|?>|\[\/?INST\]/i,
41
+ },
42
+ ];
43
+ let detector;
44
+ let detectorOverride;
45
+ function getDetector() {
46
+ if (detectorOverride !== undefined)
47
+ return detectorOverride;
48
+ if (detector !== undefined)
49
+ return detector;
50
+ const mod = requireOptional('aidefence/dist/detection');
51
+ try {
52
+ detector = mod?.createInjectionDetector ? mod.createInjectionDetector() : null;
53
+ }
54
+ catch {
55
+ detector = null;
56
+ }
57
+ return detector;
58
+ }
59
+ export function getInjectionPolicy() {
60
+ const raw = (process.env.AGENTIC_FLOW_PROMPT_INJECTION ?? 'block').toLowerCase();
61
+ return ['block', 'strict', 'warn', 'off'].includes(raw)
62
+ ? raw
63
+ : 'block';
64
+ }
65
+ function builtinDetect(text) {
66
+ const threats = [];
67
+ for (const { pattern, ...rule } of BUILTIN_RULES) {
68
+ const m = pattern.exec(text);
69
+ if (m)
70
+ threats.push({ ...rule, match: m[0] });
71
+ }
72
+ return { threats, maxSeverity: highest(threats) };
73
+ }
74
+ function highest(threats) {
75
+ let best = null;
76
+ for (const t of threats) {
77
+ if (!best || SEVERITY_RANK[t.severity] > SEVERITY_RANK[best])
78
+ best = t.severity;
79
+ }
80
+ return best;
81
+ }
82
+ function decide(maxSeverity, policy) {
83
+ if (!maxSeverity || policy === 'off')
84
+ return 'allow';
85
+ if (policy === 'warn')
86
+ return 'warn';
87
+ const threshold = policy === 'strict' ? SEVERITY_RANK.high : SEVERITY_RANK.critical;
88
+ return SEVERITY_RANK[maxSeverity] >= threshold ? 'block' : 'warn';
89
+ }
90
+ /** Scan `text` for prompt-injection attempts under the active policy. */
91
+ export function scanPromptInjection(text, policy = getInjectionPolicy()) {
92
+ if (policy === 'off') {
93
+ return { safe: true, maxSeverity: null, threats: [], engine: 'disabled', decision: 'allow' };
94
+ }
95
+ const engine = getDetector();
96
+ let threats;
97
+ let maxSeverity;
98
+ let engineName = 'builtin';
99
+ if (engine) {
100
+ try {
101
+ const r = engine.detect(text);
102
+ threats = r.threats.map((t) => ({
103
+ id: t.id, type: t.type, severity: t.severity, confidence: t.confidence,
104
+ description: t.description, match: t.match,
105
+ }));
106
+ maxSeverity = r.maxSeverity ?? highest(threats);
107
+ engineName = 'aidefence';
108
+ }
109
+ catch {
110
+ ({ threats, maxSeverity } = builtinDetect(text));
111
+ }
112
+ }
113
+ else {
114
+ ({ threats, maxSeverity } = builtinDetect(text));
115
+ }
116
+ return { safe: threats.length === 0, maxSeverity, threats, engine: engineName, decision: decide(maxSeverity, policy) };
117
+ }
118
+ /** Which engine `scanPromptInjection` will use. */
119
+ export function promptInjectionEngine() {
120
+ return getDetector() ? 'aidefence' : 'builtin';
121
+ }
122
+ /** Test hook: `null` forces the built-in engine, `undefined` restores auto-detection. */
123
+ export function _setInjectionDetectorForTests(d) {
124
+ detectorOverride = d;
125
+ }
126
+ //# sourceMappingURL=prompt-injection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-injection.js","sourceRoot":"","sources":["../../src/integrations/prompt-injection.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Prompt-injection screening for task text.\n *\n * Uses the optional `aidefence` package (53 curated patterns across encodings\n * and obfuscation variants) when installed, and a small built-in pattern set\n * otherwise. Only the `aidefence/dist/detection` subpath is loaded: the package\n * root starts an Express server stack, the detection engine has no deps.\n *\n * Policy (env `AGENTIC_FLOW_PROMPT_INJECTION`):\n * - `block` (default): reject `critical` findings, warn on the rest\n * - `strict`: reject `critical` and `high`, warn on the rest\n * - `warn`: never reject, warn on every finding\n * - `off`: do not scan\n *\n * `high` is not rejected by default because legitimate agent prompts often\n * trip it — \"You are now a code reviewer…\" matches aidefence's role-switch rule.\n */\n\nimport { requireOptional } from './optional.js';\n\nexport type InjectionSeverity = 'low' | 'medium' | 'high' | 'critical';\nexport type InjectionPolicy = 'block' | 'strict' | 'warn' | 'off';\n\nexport interface InjectionThreat {\n id: string;\n type: string;\n severity: InjectionSeverity;\n confidence: number;\n description: string;\n match?: string;\n}\n\nexport interface InjectionScan {\n safe: boolean;\n maxSeverity: InjectionSeverity | null;\n threats: InjectionThreat[];\n /** Which engine produced the result. */\n engine: 'aidefence' | 'builtin' | 'disabled';\n /** What the active policy decided. */\n decision: 'allow' | 'warn' | 'block';\n}\n\ninterface AidefenceDetector {\n detect(text: string): {\n safe: boolean;\n maxSeverity: InjectionSeverity | null;\n threats: Array<InjectionThreat & Record<string, unknown>>;\n };\n}\n\nconst SEVERITY_RANK: Record<InjectionSeverity, number> = { low: 1, medium: 2, high: 3, critical: 4 };\n\n/** Built-in fallback: the highest-signal override/exfiltration/jailbreak shapes. */\nconst BUILTIN_RULES: Array<Omit<InjectionThreat, 'match'> & { pattern: RegExp }> = [\n {\n id: 'AF-001', type: 'instruction_override', severity: 'critical', confidence: 0.95,\n description: 'Attempt to override prior instructions',\n pattern: /\\b(ignore|disregard|forget|override)\\s+(all\\s+|any\\s+|the\\s+|your\\s+)*(previous|prior|above|earlier|preceding|system)\\s+(instructions?|rules?|prompts?|directions?|guidelines?)/i,\n },\n {\n id: 'AF-002', type: 'prompt_exfiltration', severity: 'high', confidence: 0.9,\n description: 'Attempt to reveal the system prompt or hidden instructions',\n pattern: /\\b(reveal|print|show|output|repeat|leak)\\s+(me\\s+)?(your|the)\\s+(system\\s+prompt|hidden\\s+instructions?|initial\\s+instructions?)/i,\n },\n {\n id: 'AF-003', type: 'jailbreak', severity: 'high', confidence: 0.9,\n description: 'Known jailbreak persona or mode switch',\n pattern: /\\b(do\\s+anything\\s+now|DAN\\s+mode|developer\\s+mode\\s+(enabled|on)|jailbreak(ed)?\\s+mode)\\b/i,\n },\n {\n id: 'AF-004', type: 'role_token_injection', severity: 'high', confidence: 0.9,\n description: 'Chat-template role markers embedded in user text',\n pattern: /<\\|?(im_start|im_end|system|endoftext)\\|?>|\\[\\/?INST\\]/i,\n },\n];\n\nlet detector: AidefenceDetector | null | undefined;\nlet detectorOverride: AidefenceDetector | null | undefined;\n\nfunction getDetector(): AidefenceDetector | null {\n if (detectorOverride !== undefined) return detectorOverride;\n if (detector !== undefined) return detector;\n const mod = requireOptional<{ createInjectionDetector?: () => AidefenceDetector }>('aidefence/dist/detection');\n try {\n detector = mod?.createInjectionDetector ? mod.createInjectionDetector() : null;\n } catch {\n detector = null;\n }\n return detector;\n}\n\nexport function getInjectionPolicy(): InjectionPolicy {\n const raw = (process.env.AGENTIC_FLOW_PROMPT_INJECTION ?? 'block').toLowerCase();\n return (['block', 'strict', 'warn', 'off'] as const).includes(raw as InjectionPolicy)\n ? (raw as InjectionPolicy)\n : 'block';\n}\n\nfunction builtinDetect(text: string): Pick<InjectionScan, 'threats' | 'maxSeverity'> {\n const threats: InjectionThreat[] = [];\n for (const { pattern, ...rule } of BUILTIN_RULES) {\n const m = pattern.exec(text);\n if (m) threats.push({ ...rule, match: m[0] });\n }\n return { threats, maxSeverity: highest(threats) };\n}\n\nfunction highest(threats: InjectionThreat[]): InjectionSeverity | null {\n let best: InjectionSeverity | null = null;\n for (const t of threats) {\n if (!best || SEVERITY_RANK[t.severity] > SEVERITY_RANK[best]) best = t.severity;\n }\n return best;\n}\n\nfunction decide(maxSeverity: InjectionSeverity | null, policy: InjectionPolicy): InjectionScan['decision'] {\n if (!maxSeverity || policy === 'off') return 'allow';\n if (policy === 'warn') return 'warn';\n const threshold = policy === 'strict' ? SEVERITY_RANK.high : SEVERITY_RANK.critical;\n return SEVERITY_RANK[maxSeverity] >= threshold ? 'block' : 'warn';\n}\n\n/** Scan `text` for prompt-injection attempts under the active policy. */\nexport function scanPromptInjection(text: string, policy: InjectionPolicy = getInjectionPolicy()): InjectionScan {\n if (policy === 'off') {\n return { safe: true, maxSeverity: null, threats: [], engine: 'disabled', decision: 'allow' };\n }\n const engine = getDetector();\n let threats: InjectionThreat[];\n let maxSeverity: InjectionSeverity | null;\n let engineName: InjectionScan['engine'] = 'builtin';\n if (engine) {\n try {\n const r = engine.detect(text);\n threats = r.threats.map((t) => ({\n id: t.id, type: t.type, severity: t.severity, confidence: t.confidence,\n description: t.description, match: t.match,\n }));\n maxSeverity = r.maxSeverity ?? highest(threats);\n engineName = 'aidefence';\n } catch {\n ({ threats, maxSeverity } = builtinDetect(text));\n }\n } else {\n ({ threats, maxSeverity } = builtinDetect(text));\n }\n return { safe: threats.length === 0, maxSeverity, threats, engine: engineName, decision: decide(maxSeverity, policy) };\n}\n\n/** Which engine `scanPromptInjection` will use. */\nexport function promptInjectionEngine(): 'aidefence' | 'builtin' {\n return getDetector() ? 'aidefence' : 'builtin';\n}\n\n/** Test hook: `null` forces the built-in engine, `undefined` restores auto-detection. */\nexport function _setInjectionDetectorForTests(d: AidefenceDetector | null | undefined): void {\n detectorOverride = d;\n}\n"]}