@framers/agentos-ext-topicality 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # @framers/agentos-ext-topicality
2
+
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`15065c9`](https://github.com/framersai/agentos-extensions/commit/15065c949ea5d25f4408ffab2079ad3e600ddded) Thanks [@jddunn](https://github.com/jddunn)! - Fix npm publish: add missing repository.url field for sigstore provenance verification
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`c35afe8`](https://github.com/framersai/agentos-extensions/commit/c35afe8c16fdf51df6ce2d0bb83de6cd702e3a8b) Thanks [@jddunn](https://github.com/jddunn)! - Implement all 5 guardrail extension packs with full detection logic:
14
+ - PII Redaction: 4-tier detection (regex + keyword + NER + LLM)
15
+ - Code Safety: OWASP regex patterns for SQL injection, XSS, command injection
16
+ - ML Classifiers: toxicity/injection/NSFW via ONNX or LLM fallback
17
+ - Topicality: embedding-based topic enforcement with LLM fallback
18
+ - Grounding Guard: NLI-based hallucination detection against RAG sources
@@ -1,196 +1,111 @@
1
1
  /**
2
- * @fileoverview IGuardrailService implementation for topicality enforcement.
2
+ * @file TopicalityGuardrail.ts
3
+ * @description Guardrail service that enforces on/off-topic boundaries for
4
+ * agent conversations using a three-tier evaluation strategy:
3
5
  *
4
- * `TopicalityGuardrail` evaluates user input (and optionally agent output)
5
- * against configured allowed and forbidden topic sets using semantic
6
- * embedding similarity. It enforces three independent policy checks:
6
+ * 1. **Embedding similarity** (primary) cosine similarity between input
7
+ * and topic embeddings via `@huggingface/transformers`.
8
+ * 2. **LLM-as-judge** (fallback) structured JSON classification prompt
9
+ * sent to the configured LLM invoker.
10
+ * 3. **Keyword matching** (last resort) — simple substring search against
11
+ * topic strings when neither embeddings nor LLM are available.
7
12
  *
8
- * 1. **Forbidden topics** Messages that score above `forbiddenThreshold`
9
- * against any forbidden topic are blocked (or flagged).
10
- * 2. **Off-topic detection** Messages that score below `allowedThreshold`
11
- * against *all* allowed topics are flagged (or blocked/redirected).
12
- * 3. **Session drift** — An EMA-based tracker flags sustained drift away
13
- * from allowed topics across consecutive messages.
13
+ * The guardrail only evaluates input (user messages). Output evaluation
14
+ * returns `null` (pass-through) because topic drift in agent responses is
15
+ * best handled at the input gate.
14
16
  *
15
- * ### Lazy initialisation
16
- * Embedding indices are built on the **first evaluation call**, not at
17
- * construction time. This keeps instantiation cheap and defers the
18
- * potentially expensive batch embedding call until the agent actually
19
- * receives its first message.
17
+ * ### Guardrail pipeline phase
20
18
  *
21
- * ### Fail-open semantics
22
- * All evaluation methods wrap their logic in try/catch. If the embedding
23
- * function throws, or any other unexpected error occurs, the guardrail
24
- * logs a warning and returns `null` (pass) to avoid blocking legitimate
25
- * traffic due to infrastructure failures.
19
+ * This guardrail sets `canSanitize: false` and `evaluateStreamingChunks: false`,
20
+ * placing it in Phase 2 (parallel) of the guardrail dispatcher. It never
21
+ * modifies content it only FLAGs or BLOCKs.
26
22
  *
27
- * @module topicality/TopicalityGuardrail
23
+ * @module agentos/extensions/packs/topicality/TopicalityGuardrail
28
24
  */
29
- import type { GuardrailConfig, GuardrailEvaluationResult, GuardrailInputPayload, GuardrailOutputPayload, IGuardrailService } from '@framers/agentos';
30
- import type { ISharedServiceRegistry } from '@framers/agentos';
31
- import type { TopicalityPackOptions } from './types';
25
+ import type { IGuardrailService, GuardrailConfig, GuardrailInputPayload, GuardrailOutputPayload, GuardrailEvaluationResult } from '@framers/agentos';
26
+ import type { TopicalityOptions } from './types';
32
27
  /**
33
- * Guardrail that enforces topicality constraints via semantic embeddings.
28
+ * Guardrail that enforces topic boundaries on user input.
34
29
  *
35
- * Implements {@link IGuardrailService} with Phase 2 (parallel) semantics:
36
- * `evaluateStreamingChunks: false` and `canSanitize: false`. The guardrail
37
- * never modifies content — it only blocks or flags.
38
- *
39
- * @example
40
- * ```ts
41
- * const guardrail = new TopicalityGuardrail(registry, {
42
- * allowedTopics: TOPIC_PRESETS.customerSupport,
43
- * forbiddenTopics: TOPIC_PRESETS.commonUnsafe,
44
- * forbiddenAction: 'block',
45
- * offTopicAction: 'flag',
46
- * }, embeddingFn);
47
- *
48
- * const result = await guardrail.evaluateInput(payload);
49
- * if (result?.action === GuardrailAction.BLOCK) {
50
- * // Reject the message
51
- * }
52
- * ```
30
+ * Implements {@link IGuardrailService} with input-only evaluation.
31
+ * Runs in Phase 2 (parallel, non-sanitizing) of the guardrail dispatcher.
53
32
  */
54
33
  export declare class TopicalityGuardrail implements IGuardrailService {
55
34
  /**
56
- * Guardrail pipeline configuration.
57
- *
58
- * - `evaluateStreamingChunks: false` — topicality evaluation requires
59
- * complete text, not partial deltas.
60
- * - `canSanitize: false` — this guardrail only blocks or flags; it never
61
- * modifies content, so it runs in Phase 2 (parallel) of the pipeline.
35
+ * Guardrail configuration — Phase 2 parallel (no sanitization, no streaming).
62
36
  */
63
37
  readonly config: GuardrailConfig;
64
- /** Shared service registry provided by the extension manager. */
65
- private readonly services;
66
- /** Resolved pack options with caller overrides. */
67
- private readonly options;
68
- /** Caller-supplied or registry-backed embedding function. */
69
- private readonly embeddingFn;
70
- /**
71
- * Embedding index for allowed topics. Lazily built on the first
72
- * evaluation call. `null` until built or if no allowed topics are
73
- * configured.
74
- */
75
- private allowedIndex;
76
- /**
77
- * Embedding index for forbidden topics. Lazily built on the first
78
- * evaluation call. `null` until built or if no forbidden topics are
79
- * configured.
80
- */
81
- private forbiddenIndex;
82
- /**
83
- * Session-level EMA drift tracker. Only instantiated when
84
- * `enableDriftDetection` is `true` (default). `null` otherwise.
85
- */
86
- private driftTracker;
87
- /**
88
- * Which side of the conversation to evaluate.
89
- * - `'input'` — only user messages
90
- * - `'output'` — only agent responses
91
- * - `'both'` — both directions
92
- */
93
- private readonly scope;
38
+ /** Resolved options with defaults applied. */
39
+ private readonly opts;
94
40
  /**
95
- * Minimum similarity to any allowed topic for the message to be
96
- * considered on-topic.
41
+ * @param options - Topicality configuration provided by the pack factory.
97
42
  */
98
- private readonly allowedThreshold;
43
+ constructor(options: TopicalityOptions);
99
44
  /**
100
- * Similarity above which a forbidden topic match triggers action.
101
- */
102
- private readonly forbiddenThreshold;
103
- /**
104
- * Whether the lazy initialisation of embedding indices has been
105
- * performed. Prevents redundant build calls.
106
- */
107
- private indicesBuilt;
108
- /**
109
- * Creates a new `TopicalityGuardrail`.
45
+ * Evaluate user input for topic relevance.
46
+ *
47
+ * Runs the three-tier evaluation strategy:
48
+ * 1. Embedding similarity (if `@huggingface/transformers` available)
49
+ * 2. LLM-as-judge (if `llmInvoker` configured)
50
+ * 3. Keyword matching (always available)
110
51
  *
111
- * @param services - Shared service registry for heavyweight resource sharing.
112
- * @param options - Pack-level configuration (topics, thresholds, actions).
113
- * @param embeddingFn - Optional explicit embedding function. When omitted,
114
- * the guardrail falls back to requesting an EmbeddingManager from the
115
- * shared service registry at evaluation time.
52
+ * @param payload - The input payload containing user text and context.
53
+ * @returns A guardrail result (FLAG/BLOCK) or `null` to allow.
116
54
  */
117
- constructor(services: ISharedServiceRegistry, options: TopicalityPackOptions, embeddingFn?: (texts: string[]) => Promise<number[][]>);
55
+ evaluateInput(payload: GuardrailInputPayload): Promise<GuardrailEvaluationResult | null>;
118
56
  /**
119
- * Clears any session-level drift-tracking state held by this guardrail.
57
+ * Output evaluation returns `null` (pass-through).
120
58
  *
121
- * Called by the topicality pack's `onDeactivate` hook so long-lived agents
122
- * do not retain per-session EMA state after the pack is removed or the
123
- * agent shuts down.
59
+ * Topic enforcement is applied at the input gate only. Agent responses
60
+ * are not evaluated for topicality.
124
61
  */
125
- clearSessionState(): void;
62
+ evaluateOutput(_payload: GuardrailOutputPayload): Promise<GuardrailEvaluationResult | null>;
126
63
  /**
127
- * Evaluates a user input message against configured topic constraints.
64
+ * Evaluate input via cosine similarity between embeddings.
128
65
  *
129
- * When `scope` is `'output'`, this method immediately returns `null`
130
- * because input evaluation is disabled.
66
+ * Embeds the input text and each topic string, then compares similarities
67
+ * against the configured thresholds.
131
68
  *
132
- * @param payload - The input payload containing the user message text and
133
- * session context.
134
- * @returns A guardrail evaluation result (BLOCK or FLAG), or `null` if
135
- * the message passes all topic checks. Returns `null` on any error
136
- * (fail-open).
69
+ * @param text - The user input text.
70
+ * @returns A {@link TopicMatchResult} or `null` if embeddings are unavailable.
137
71
  */
138
- evaluateInput(payload: GuardrailInputPayload): Promise<GuardrailEvaluationResult | null>;
72
+ private evaluateViaEmbeddings;
139
73
  /**
140
- * Evaluates an agent output chunk against configured topic constraints.
141
- *
142
- * When `scope` is `'input'`, this method immediately returns `null`
143
- * because output evaluation is disabled.
74
+ * Evaluate input via LLM classification prompt.
144
75
  *
145
- * For output evaluation, the guardrail extracts text from the response
146
- * chunk's `finalResponseText` field (since `evaluateStreamingChunks` is
147
- * `false`, only FINAL_RESPONSE chunks are seen).
76
+ * Sends a structured prompt to the configured LLM invoker asking it to
77
+ * classify the input as on-topic or off-topic and return JSON.
148
78
  *
149
- * @param payload - The output payload containing the response chunk and
150
- * session context.
151
- * @returns A guardrail evaluation result (BLOCK or FLAG), or `null` if
152
- * the output passes all topic checks. Returns `null` on any error
153
- * (fail-open).
79
+ * @param text - The user input text.
80
+ * @returns A {@link TopicMatchResult} or `null` if no LLM invoker is configured.
154
81
  */
155
- evaluateOutput(payload: GuardrailOutputPayload): Promise<GuardrailEvaluationResult | null>;
82
+ private evaluateViaLlm;
156
83
  /**
157
- * Runs the three-stage topicality evaluation pipeline on a pre-computed
158
- * embedding vector.
159
- *
160
- * Evaluation order:
161
- * 1. Forbidden topic check (highest priority — immediate block/flag)
162
- * 2. Off-topic check against allowed topics
163
- * 3. Session drift check (only if drift detection is enabled and allowed
164
- * topics are configured)
84
+ * Evaluate input via simple case-insensitive substring matching against
85
+ * topic strings.
165
86
  *
166
- * @param embedding - Pre-computed embedding vector for the text.
167
- * @param sessionId - Session identifier for drift tracking.
168
- * @returns A {@link GuardrailEvaluationResult} if any check triggers, or
169
- * `null` if all checks pass.
87
+ * This is the fallback of last resort when neither embeddings nor LLM
88
+ * are available. It checks whether any topic string appears as a
89
+ * substring of the input (or vice versa).
170
90
  *
171
- * @internal
91
+ * @param text - The user input text.
92
+ * @returns A {@link TopicMatchResult}.
172
93
  */
173
- private evaluateEmbedding;
94
+ private evaluateViaKeywords;
174
95
  /**
175
- * Ensures that the allowed and forbidden embedding indices have been built.
96
+ * Convert a {@link TopicMatchResult} into a {@link GuardrailEvaluationResult}.
176
97
  *
177
- * Called once before the first evaluation. Subsequent calls are no-ops
178
- * (guarded by the `indicesBuilt` flag).
98
+ * - On-topic results return `null` (allow).
99
+ * - Blocked-topic matches return `BLOCK`.
100
+ * - Off-topic (below allowed threshold) returns `FLAG`.
179
101
  *
180
- * @internal
102
+ * @param result - The topic match result to convert.
103
+ * @returns A guardrail evaluation result, or `null` to allow.
181
104
  */
182
- private ensureIndicesBuilt;
105
+ private toGuardrailResult;
183
106
  /**
184
- * Creates an embedding function that retrieves an EmbeddingManager from
185
- * the shared service registry at call time.
186
- *
187
- * This fallback is used when no explicit `embeddingFn` is provided to
188
- * the constructor. It throws if the EmbeddingManager service is not
189
- * available in the registry.
190
- *
191
- * @returns An async embedding function.
192
- * @internal
107
+ * Clear cached embeddings. Called during pack deactivation.
193
108
  */
194
- private createRegistryEmbeddingFn;
109
+ clearCache(): void;
195
110
  }
196
111
  //# sourceMappingURL=TopicalityGuardrail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TopicalityGuardrail.d.ts","sourceRoot":"","sources":["../src/TopicalityGuardrail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AA+BrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,mBAAoB,YAAW,iBAAiB;IAK3D;;;;;;;OAOG;IACH,SAAgB,MAAM,EAAE,eAAe,CAGrC;IAMF,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAElD,mDAAmD;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2C;IAEvE;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAAoC;IAExD;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAoC;IAE1D;;;OAGG;IACH,OAAO,CAAC,YAAY,CAAkC;IAEtD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA8B;IAEpD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAE1C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAE5C;;;OAGG;IACH,OAAO,CAAC,YAAY,CAAS;IAM7B;;;;;;;;OAQG;gBAED,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IAsBxD;;;;;;OAMG;IACH,iBAAiB,IAAI,IAAI;IAQzB;;;;;;;;;;;OAWG;IACG,aAAa,CACjB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAoC5C;;;;;;;;;;;;;;;OAeG;IACG,cAAc,CAClB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAyC5C;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,iBAAiB;IAkHzB;;;;;;;OAOG;YACW,kBAAkB;IAwBhC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,yBAAyB;CAiBlC"}
1
+ {"version":3,"file":"TopicalityGuardrail.d.ts","sourceRoot":"","sources":["../src/TopicalityGuardrail.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EAC1B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,SAAS,CAAC;AA+DnE;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,eAAe,CAG9B;IAEF,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAMmB;IAExC;;OAEG;gBACS,OAAO,EAAE,iBAAiB;IActC;;;;;;;;;;OAUG;IACG,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAwB9F;;;;;OAKG;IACG,cAAc,CAClB,QAAQ,EAAE,sBAAsB,GAC/B,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC;IAQ5C;;;;;;;;OAQG;YACW,qBAAqB;IA+CnC;;;;;;;;OAQG;YACW,cAAc;IA0C5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,mBAAmB;IA+B3B;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IA6BzB;;OAEG;IACH,UAAU,IAAI,IAAI;CAGnB"}