Synthetic Injectors
Synthetic injectors are guardrails that fire at specific points in the assistant loop, injecting messages into the conversation to guide LLM behavior. They are implemented as Go plugin hooks interpreted by Yaegi. Example injectors are available in the repository under .aura/plugins/injectors/ — they are not installed by aura init.
Timing
Each injector fires at one of nine points in the assistant loop:
| Timing | When |
|---|---|
| BeforeChat | Before sending messages to the LLM |
| AfterResponse | After receiving the LLM response |
| BeforeToolExecution | Before a tool call executes (can modify args or block) |
| AfterToolExecution | After a tool call completes (can modify output) |
| OnError | When the LLM provider returns an error (after built-in recovery exhausted). Can classify, retry, or suppress errors. |
| BeforeCompaction | Before context compaction begins. Can skip built-in compaction (plugin manages context itself). |
| AfterCompaction | After context compaction completes (success or failure). Read-only — observe but don’t modify results. |
| OnAgentSwitch | When the active agent changes. Read-only — observe but don’t modify the switch. Reason: user, failover, task, cycle, resume. |
| TransformMessages | Inside chat(), transforms message array before LLM call. Ephemeral — builder history untouched. |
Shipped Injector Plugins
| Plugin | Timing | Purpose |
|---|---|---|
todo-reminder | BeforeChat | Reminds about pending todos every N iterations |
max-steps | BeforeChat | Disables tools when iteration limit reached |
session-stats | BeforeChat | Shows session stats summary (turns, tool calls, context usage, top tools) as a Notice every 5 turns |
todo-not-finished | AfterResponse | Warns if todos incomplete and no tool calls |
empty-response | AfterResponse | Handles empty LLM responses |
done-reminder | AfterResponse | Reminds LLM to call Done tool when stopping |
loop-detection | AfterToolExecution | Detects repeated identical tool calls |
failure-circuit-breaker | AfterToolExecution | Stops after 3+ consecutive different tool failures |
repeated-patch | AfterToolExecution | Warns after 5+ patches to same file |
Example injectors are available in the repository under .aura/plugins/injectors/. Copy them to your own .aura/plugins/ directory, then edit the Go source to customize detection logic and message text. Disable any injector by setting disabled: true in its plugin.yaml.
Condition System
Injectors use a condition expression in plugin.yaml to control when they fire. The condition is evaluated before every hook call.
# plugin.yaml
condition: "auto and context_above:70"
once: false
Boolean Conditions
| Condition | True when |
|---|---|
auto | Auto mode is enabled |
todo_empty | No todo items exist |
todo_done | All todo items completed |
todo_pending | Pending or in-progress items exist |
Comparison Conditions
| Greater-than | Less-than | Compares |
|---|---|---|
context_above:<N> | context_below:<N> | Token usage percentage |
history_gt:<N> | history_lt:<N> | Message count |
tool_errors_gt:<N> | tool_errors_lt:<N> | Tool error count |
tool_calls_gt:<N> | tool_calls_lt:<N> | Tool call count |
turns_gt:<N> | turns_lt:<N> | LLM turn count |
compactions_gt:<N> | compactions_lt:<N> | Compaction count |
iteration_gt:<N> | iteration_lt:<N> | Current iteration |
tokens_total_gt:<N> | tokens_total_lt:<N> | Cumulative tokens (input + output) |
model_context_gt:<N> | model_context_lt:<N> | Model max context tokens |
model_params_gt:<N> | model_params_lt:<N> | Model parameters in billions (supports decimals like 0.5) |
Model parameter and context conditions return false when the model is unresolved (zero values), preventing false positives like model_params_lt:10 matching before model resolution.
Model Conditions
| Condition | True when |
|---|---|
model_has:vision | Model supports vision/image input |
model_has:tools | Model supports tool calling |
model_has:thinking | Model supports reasoning output |
model_has:thinking_levels | Model supports thinking effort levels (low/medium/high) |
model_has:embedding | Model supports text embeddings |
model_has:reranking | Model supports reranking |
model_has:context_override | Model supports context length override |
model_is:<name> | Model name matches (case-insensitive) |
Filesystem & Shell Conditions
| Condition | True when |
|---|---|
exists:<path> | File or directory exists (relative to CWD or absolute) |
bash:<cmd> | Shell command exits 0 (120s timeout) |
Negation
Prefix any condition with not to negate it:
condition: "not auto"
condition: "not todo_done"
Composition
Join conditions with and – all must be true:
condition: "auto and context_above:70"
condition: "todo_pending and not turns_gt:50"
condition: "model_has:tools and model_params_gt:10"
Trigger Control
Set once: true in plugin.yaml to fire the injector only on the first match. Once the condition evaluates to true and the hook fires, it will not fire again for the rest of the session. The once state is preserved across /reload.
condition: "context_above:90"
once: true
Diagnostic Plugins
Two example diagnostic plugins are available alongside the injectors. These are not injectors themselves but use the same plugin hook system:
| Plugin | Type | Purpose |
|---|---|---|
tool-logger | Hook | Logs every tool call and result to a file |
turn-tracker | Hook | Injects a status line every N turns with token and context info |
Example diagnostics are available in the repository under .aura/plugins/diagnostics/.
Intent-Based Query Methods
The Messages type exposes intent-based methods that filter by semantic role, not by type value. Consumers use these rather than filtering on Type directly:
| Method | What it returns |
|---|---|
ForLLM() | Messages sent to the primary LLM. Excludes DisplayOnly, Bookmark, Metadata. Includes Normal, Synthetic, Ephemeral. |
ForCompaction(maxLen) | Messages sent to the compaction LLM. Excludes Synthetic, Ephemeral, internal types, and all thinking content. Tool results truncated to maxLen. |
ForPreservation() | Messages retained after compaction. Normal only — all one-turn and internal types removed. |
ForSave() | Messages persisted in session files. Excludes Ephemeral. Includes Normal, Synthetic, DisplayOnly, Bookmark, Metadata. |
ForExport() | Messages for human-readable export (plaintext, markdown). Excludes Synthetic, Ephemeral, Metadata. |
ForExportStructured() | Messages for machine-readable export (JSON, JSONL). Excludes Synthetic, Ephemeral. Includes Metadata. |
ForDisplay() | Messages for UI rendering on session restore. Excludes System role, Tool role, Metadata. |
Customization
Users customize injectors by editing the plugin source in their .aura/plugins/injectors/ directory. Each injector is a standalone Go module with a main.go containing the hook function and a plugin.yaml with metadata and conditions.
To create a custom injector, add a new plugin directory under .aura/plugins/ with a BeforeChat, AfterResponse, BeforeToolExecution, AfterToolExecution, OnError, BeforeCompaction, AfterCompaction, OnAgentSwitch, or TransformMessages hook function. See Plugins for the full plugin authoring guide.