Plugins let applications observe or transform CortexDB lifecycle operations without forking the core library.
Use defineCortexPlugin() to validate hook names and preserve exact TypeScript types.
import { Cortex, defineCortexPlugin } from "cortexdb"
const plugin = defineCortexPlugin({
name: "tag-docs-memories",
version: "1.0.0",
priority: 10,
hooks: {
beforeRemember: (context) => ({
...context.input,
tags: [...(context.input.tags ?? []), "docs"],
}),
afterRemember: (context) => {
console.log(`stored ${context.record.id}`)
},
},
})
const db = await Cortex.memory({ plugins: [plugin] })Plugins execute by descending priority, then registration order. Hook errors are isolated: the registry records a PluginErrorEvent, notifies error listeners, and allows the host operation to continue.
const unsubscribe = db.plugins.onError((event) => {
console.error(event.pluginName, event.hook, event.message)
})
console.log(db.plugins.errors())
db.plugins.clearErrors()
unsubscribe()| Method | Description |
|---|---|
register(plugin) |
Adds a plugin. Duplicate names are rejected. |
unregister(pluginOrName) |
Removes a plugin by name or object; returns true when removed. |
list() |
Returns registered plugins in execution order. |
errors() |
Returns captured plugin hook errors. |
clearErrors() |
Clears captured hook errors. |
onError(listener) |
Subscribes to hook errors and returns an unsubscribe function. |
| Hook | Can transform? | Runs when |
|---|---|---|
beforeRemember |
Yes | Before a memory is normalized and stored. |
afterRemember |
Yes | After a memory is stored. |
beforeRecall |
Yes | Before memory recall. |
afterRecall |
Yes | After recall results are produced. |
beforePack |
Yes | Legacy alias before context packing. |
afterPack |
Yes | Legacy alias after context packing. |
beforeContextPack |
Yes | Before context packing. |
afterContextPack |
Yes | After context packing. |
beforeSearch |
Yes | Before semantic/text/hybrid/graph/temporal search. |
afterSearch |
Yes | After search results. |
onRecordWrite |
No | After storage set or batch set writes a valid Cortex record. |
onRiskDetected |
No | After security scan/find detects risks. |
onCompaction |
No | After db.compact() succeeds. |
db.plugins.register(defineCortexPlugin({
name: "recall-rewriter",
hooks: {
beforeRecall: (context) => ({
...(typeof context.input === "string" ? {} : context.input),
query: "normalized project vocabulary",
}),
afterRecall: (context) => context.results.slice(0, 5),
},
}))db.plugins.register(defineCortexPlugin({
name: "search-audit",
hooks: {
beforeSearch: (context) => {
console.log(`search mode=${context.mode}`)
},
afterSearch: (context) => {
console.log(`results=${context.results.length}`)
},
},
}))The built-in example plugin redacts secrets and PII-like text from packed context output.
import { createContextRedactionPlugin } from "cortexdb"
db.plugins.register(createContextRedactionPlugin({ marker: "[MASKED]" }))createOperationLoggerPlugin() appends lifecycle events to an array.
import { createOperationLoggerPlugin, type OperationLogEntry } from "cortexdb"
const log: OperationLogEntry[] = []
db.plugins.register(createOperationLoggerPlugin(log))
await db.remember({ content: "Log this memory" })
console.log(log)withPluginRecordWriteObserver(storage, plugins) wraps a storage adapter so onRecordWrite hooks observe successful set writes that decode to valid Cortex records. Cortex applies this wrapper automatically to its configured storage.
- Keep hooks deterministic and fast; hooks run inline with user operations.
- Treat hook context as user-controlled input unless it came from a trusted source.
- Return transformed values only when you intentionally want to replace the input/result.
- Do not throw for routine filtering; return a transformed value instead.
- Use unique plugin names and semantic versions.
- Prefer
onRiskDetectedandonRecordWritefor audit logging instead of duplicating storage writes in transform hooks.