Use the Reminder Plugin
Use this when you want the agent to receive automatic context messages after tool execution based on pattern matching — for example, reminding the agent to run cargo check after editing a .toml file, or warning about destructive commands.
Prerequisites
Section titled “Prerequisites”- A working awaken agent runtime (see Build an Agent)
- Feature
reminderenabled on theawakencrate
[dependencies]awaken = { git = "https://github.com/AwakenWorks/awaken", features = ["reminder"] }tokio = { version = "1", features = ["full"] }serde_json = "1"- Register the reminder plugin and configure agent-specific rules.
use std::sync::Arc;use serde_json::json;use awaken::engine::GenaiExecutor;use awaken::ext_reminder::ReminderPlugin;use awaken::registry_spec::ModelSpec;use awaken::registry_spec::AgentSpec;use awaken::{AgentRuntimeBuilder, Plugin};
let mut agent_spec = AgentSpec::new("my-agent") .with_model_id("claude-sonnet") .with_system_prompt("You are a helpful assistant.") .with_hook_filter("reminder") .with_section("reminder", json!({ "rules": [ { "tool": "Bash(command ~ 'rm *')", "output": { "status": "success" }, "message": { "target": "suffix_system", "content": "A deletion command just succeeded. Verify the result." } } ] }));agent_spec.plugin_ids.push("reminder".into());
let runtime = AgentRuntimeBuilder::new() .with_provider("anthropic", Arc::new(GenaiExecutor::new())) .with_model(ModelSpec::new("claude-sonnet", "anthropic", "claude-3-7-sonnet-latest")) .with_agent_spec(agent_spec) .with_plugin("reminder", Arc::new(ReminderPlugin::new(vec![])) as Arc<dyn Plugin>) .build() .expect("failed to build runtime");The plugin registers an AfterToolExecute phase hook. After every tool call, it evaluates each rule against the tool name, arguments, and result. When a rule matches, it schedules an AddContextMessage action that injects the configured message into the prompt.
plugin_ids loads the plugin for the agent. with_hook_filter("reminder")
keeps only the reminder plugin’s hooks active when multiple plugins are loaded.
The reminder section is validated through ReminderConfigKey, exposed in
/v1/capabilities, and saved by the admin console.
-
Define rules with tool patterns.
The
toolfield uses the same pattern DSL as the permission system:
| Pattern | Matches |
|---|---|
"Bash" | Exact tool name Bash |
"*" | Any tool |
"mcp__*" | Glob on tool name (all MCP tools) |
"Bash(command ~ 'rm *')" | Tool name + primary argument glob |
"Edit(file_path ~ '*.toml')" | Named field glob |
let json = r#"{ "rules": [ { "name": "toml-edit-reminder", "tool": "Edit(file_path ~ '*.toml')", "output": "any", "message": { "target": "system", "content": "You edited a TOML file. Run cargo check to verify.", "cooldown_turns": 3 } } ]}"#;The optional name field gives the rule a human-readable identifier. When omitted, one is auto-generated from the index and tool pattern (e.g. rule-0-Edit(file_path ~ '*.toml')).
-
Configure output matching.
The
outputfield controls whether the rule fires based on the tool result. Set it to"any"to match all outputs, or use a structured object withstatusand/orcontent:
// Match only errors containing "permission denied"let json = r#"{ "rules": [ { "tool": "*", "output": { "status": "error", "content": "*permission denied*" }, "message": { "target": "suffix_system", "content": "Permission denied. Consider using sudo or checking file ownership." } } ]}"#;Status values: "success", "error", "pending", "any".
Content matching supports two forms:
- Text glob (string shorthand):
"*permission denied*"— matches the stringified tool output against a glob pattern. - JSON fields (structured): matches specific fields in JSON output.
// JSON field matchinglet json = r#"{ "rules": [ { "tool": "*", "output": { "status": "error", "content": { "fields": [ { "path": "error.code", "op": "exact", "value": "403" } ] } }, "message": { "target": "suffix_system", "content": "HTTP 403 Forbidden. Check authentication credentials." } } ]}"#;Field match operations: "glob" (default), "exact", "regex", "not_glob", "not_exact", "not_regex". The path uses dot-separated JSON field names (e.g. "error.code"). When multiple fields are specified, all must match (AND logic).
When both status and content are present, both must match for the rule to fire.
-
Choose a message target.
The
targetfield determines where the injected message appears in the prompt:
| Target | Placement |
|---|---|
"system" | Prepended to the system prompt section |
"suffix_system" | Appended after the system prompt |
"session" | Inserted as a session-scoped system message |
"conversation" | Inserted as a conversation-scoped system message |
-
Use cooldown to avoid repetition.
Set
cooldown_turnson the message to suppress re-injection for a number of turns after firing:
let json = r#"{ "rules": [ { "tool": "*", "output": "any", "message": { "target": "system", "content": "Remember to be careful with file operations.", "cooldown_turns": 5 } } ]}"#;When cooldown_turns is 0 (default), the message is injected on every match.
- Load default rules from a file.
use awaken::ext_reminder::{ReminderPlugin, ReminderRulesConfig};
let config = ReminderRulesConfig::from_file("reminders.json") .expect("failed to load reminder config");let rules = config.into_rules().expect("invalid rules");let plugin = ReminderPlugin::new(rules);Rules passed to ReminderPlugin::new(...) are plugin defaults. Rules in the
agent’s reminder config section are appended at runtime, so one plugin
instance can serve many agents with different system reminders.
- Activate the plugin on an agent spec.
use awaken::registry_spec::AgentSpec;
let mut agent_spec = AgentSpec::new("my-agent") .with_model_id("claude-sonnet") .with_system_prompt("You are a helpful assistant.") .with_hook_filter("reminder");agent_spec.plugin_ids.push("reminder".into());plugin_ids controls plugin loading. with_hook_filter("reminder") only filters
hooks after the plugin has been loaded.
Verify
Section titled “Verify”- Configure a rule matching a tool you can easily trigger (e.g.
"*"with"any"output). - Run the agent and invoke a tool.
- Inspect the prompt or enable
tracingat debug level. You should see:reminder rule matched, scheduling context message - Confirm the context message appears in the agent’s next inference prompt at the expected target location.
Common Errors
Section titled “Common Errors”| Symptom | Cause | Fix |
|---|---|---|
InvalidPattern on startup | Malformed tool pattern string | Check syntax against the pattern table above; ensure quotes are escaped in JSON |
InvalidTarget on startup | Unknown message target | Use one of: system, suffix_system, session, conversation |
InvalidOutput on startup | Unrecognized output matcher string | Use "any" or a structured { "status": ..., "content": ... } object |
InvalidOp on startup | Unknown field match operation | Use one of: glob, exact, regex, not_glob, not_exact, not_regex |
| Rule never fires | Plugin not loaded or hook filtered out | Add "reminder" to plugin_ids and include with_hook_filter("reminder") when using hook filters |
| Rule fires too often | No cooldown configured | Set cooldown_turns to a positive value |
Related Example
Section titled “Related Example”crates/awaken-ext-reminder/src/config.rs— contains test cases with various rule configurations
Key Files
Section titled “Key Files”| Path | Purpose |
|---|---|
crates/awaken-ext-reminder/src/lib.rs | Module root and public re-exports |
crates/awaken-ext-reminder/src/config.rs | ReminderRulesConfig, JSON loading, ReminderConfigKey |
crates/awaken-ext-reminder/src/rule.rs | ReminderRule struct definition |
crates/awaken-ext-reminder/src/output_matcher.rs | OutputMatcher, ContentMatcher, status/content matching logic |
crates/awaken-ext-reminder/src/plugin/plugin.rs | ReminderPlugin registration (AfterToolExecute hook) |
crates/awaken-ext-reminder/src/plugin/hook.rs | ReminderHook — pattern + output evaluation per tool call |
crates/awaken-tool-pattern/ | Shared glob/regex pattern matching library |
Related
Section titled “Related”- Enable Tool Permission HITL — uses the same tool pattern DSL
- Add a Plugin
- Build an Agent