Sentinel Configuration Reference
This document is a complete reference for all sentinel configuration options. It's a lookup guide for developers who already understand the core concepts and need to configure specific behaviors.
To learn why sentinels work the way they do, start with the Sentinels conceptual guide first.
Who is this for? This page is for developers who understand sentinels and need a reference for specific configuration options.
Sentinel Pipeline Overview
Every sentinel follows the same flow: events from the event stream are filtered by a trigger, batched by an execution strategy, and then processed by an LLM to produce output.
Configuration File Structure
Sentinel configuration is defined in JSON files with the following top-level structure:
{
"id": "example-sentinel",
"name": "Example Sentinel",
"description": "Optional description of what this sentinel does",
"model": "anthropic/claude-haiku-4-5",
"trigger": { /* ... */ },
"execution": { /* ... */ },
"systemPromptFile": "./prompts/system.md",
"systemPromptText": "Alternative inline system prompt",
"userPromptFile": "./prompts/user.md",
"userPromptText": "Alternative inline user prompt",
"conversational": { /* ... */ },
"structuredOutput": { /* ... */ },
"llmParams": { /* ... */ },
"errorHandling": { /* ... */ },
"output": { /* ... */ },
"joinString": "\n---\n",
"reportToWebsocket": { /* ... */ }
}Required Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier. Lowercase letters, numbers, hyphens only. Pattern: /^[a-z0-9-]+$/ |
name | string | Human-readable name for display. |
model | string | Full model ID (e.g., "anthropic/claude-haiku-4-5") or a short name (e.g., "haiku"). See Model Specification for options. |
trigger | object | Defines when the sentinel fires. See Triggers. |
execution | object | Defines how triggered events become LLM calls. See Execution Strategies. |
You must also provide at least one of userPromptFile or userPromptText.
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
description | string | — | Optional description for documentation. |
systemPromptFile | string | string[] | — | Path(s) to system prompt file(s). |
systemPromptText | string | — | Inline system prompt text. |
userPromptFile | string | string[] | — | Path(s) to user prompt file(s). |
userPromptText | string | — | Inline user prompt text. |
conversational | object | — | Enable conversation history. See Conversational Mode. |
structuredOutput | object | — | Generate validated JSON. See Structured Output. |
llmParams | object | — | LLM generation parameters. See LLM Parameters. |
errorHandling | object | — | Error handling behavior. See Error Handling. |
output | object | — | Direct output configuration. See Output Configuration. |
joinString | string | "\n---\n" | Separator for log file entries. Only used for text output format. |
reportToWebsocket | object | — | Control WebSocket event emission. See Event Reporting. |
Triggers
Triggers define when a sentinel executes, either by matching single events or by detecting sequences of events.
Event Trigger
Fires when a specific event type occurs and meets all conditions.
{
"trigger": {
"type": "event",
"on": ["assistant.action", "tool.result"],
"conditions": [
{
"operator": "equals",
"path": "isError",
"value": true
}
]
}
}| Field | Type | Required | Description |
|---|---|---|---|
type | "event" | Yes | Discriminator field. |
on | string[] | Yes | Event types to match. Use "*" for a wildcard to match any event. |
conditions | Condition[] | No | Additional conditions that must all be met (AND logic). |
Available Event Types
| Event Type | Description |
|---|---|
assistant.action | Agent thinking, tool use, or messages. |
tool.result | Results from tool executions. |
file.updated | A file was created, modified, or deleted. |
filetree.updated | File tree structure has changed. |
codon.started | Codon execution begins. |
codon.completed | Codon execution ends. |
token.usage | Token consumption statistics are updated. |
error | A generic error event occurred. |
info | An informational message was emitted. |
server.idle | The server is waiting for commands. |
state.snapshot | The current execution state. |
state.transition | An internal state machine transition occurred. |
checkpoint.list | List of available checkpoints. |
rollback.started | A rollback operation has started. |
rollback.progress | Rollback progress update. |
rollback.codonCheckpoint | A checkpoint was applied during rollback. |
rollback.rigCleanup | Rig directory was cleaned up during rollback. |
rollback.completed | The rollback has finished. |
sentinel.loaded | A sentinel has started. |
sentinel.triggered | A sentinel's trigger has fired. |
sentinel.output | A sentinel has produced an LLM response. |
sentinel.error | A sentinel encountered an error. |
sentinel.unloaded | A sentinel has stopped. |
* | Wildcard—matches any event type. |
Sequence Trigger
Fires when a specific pattern of events occurs in the event stream. This is useful for detecting repeated failures, specific workflows, or complex state changes.
This example detects three consecutive tool errors:
{
"trigger": {
"type": "sequence",
"interestFilter": {
"on": ["tool.result"]
},
"pattern": [
{
"type": "tool.result",
"conditions": [{"operator": "equals", "path": "isError", "value": true}]
},
{
"type": "tool.result",
"conditions": [{"operator": "equals", "path": "isError", "value": true}]
},
{
"type": "tool.result",
"conditions": [{"operator": "equals", "path": "isError", "value": true}]
}
],
"options": {
"consecutive": true
}
}
}| Field | Type | Required | Description |
|---|---|---|---|
type | "sequence" | Yes | Discriminator field. |
interestFilter.on | string[] | Yes | Event types to track in the history buffer. |
pattern | PatternStep[] | Yes | The sequence of events to detect (minimum 1 step). |
options.consecutive | boolean | No | If true (default), the events must occur consecutively. If false, other events can occur between pattern steps. |
Pattern Step
A pattern step defines one event in the sequence to be matched.
{
"type": "tool.result",
"conditions": [
{"operator": "equals", "path": "isError", "value": true}
]
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | The event type to match. Use "*" for a wildcard. |
conditions | Condition[] | No | Conditions that must be met for this step. |
History Limit: Sequence triggers maintain a history of up to 1000 events in memory.
Conditions
Conditions provide fine-grained control for both event and sequence triggers. All conditions within a conditions array use AND logic—every condition must pass for the trigger to fire.
{
"conditions": [
{"operator": "equals", "path": "isError", "value": true},
{"operator": "greaterThan", "path": "duration", "value": 1000}
]
}Condition Operators
| Operator | Value Type | Description |
|---|---|---|
equals | string | number | boolean | null | Value is an exact match. |
notEquals | string | number | boolean | null | Value is not equal. |
in | (string | number)[] | Value is present in the array. |
notIn | (string | number)[] | Value is not present in the array. |
contains | string | String contains the specified substring. |
matches | string | String matches the specified regular expression. |
greaterThan | number | Value is numerically greater than. |
lessThan | number | Value is numerically less than. |
Path Syntax
Paths use dot notation to access nested fields within the data object of an event. For example, exitStatus.type accesses event.data.exitStatus.type.
{"operator": "equals", "path": "exitStatus.type", "value": "success"}Validation: Condition paths are validated against the event's schema when the configuration is loaded. An invalid path for the specified event type will cause a validation error.
Execution Strategies
The execution strategy determines how triggered events are batched into LLM calls. Sentinels can fire immediately on each trigger or batch events by time, quiet periods, or count.
Immediate
Fires an LLM call for every trigger match.
{
"execution": {
"strategy": "immediate"
}
}Debounce
Waits for a period of inactivity (a "quiet period") before firing an LLM call with all events accumulated during that time. Each new event resets the timer.
{
"execution": {
"strategy": "debounce",
"milliseconds": 3000
}
}| Field | Type | Constraints | Description |
|---|---|---|---|
milliseconds | number | 1–300000 | The quiet period in milliseconds (max 5 minutes). |
Count
Fires an LLM call after a specific number of trigger matches have occurred.
{
"execution": {
"strategy": "count",
"threshold": 10
}
}| Field | Type | Constraints | Description |
|---|---|---|---|
threshold | number | 1–1000 | The number of events to accumulate before firing. |
Time Window
Fires an LLM call on a fixed time interval, batching all events that occurred during that window.
{
"execution": {
"strategy": "timeWindow",
"milliseconds": 30000
}
}| Field | Type | Constraints | Description |
|---|---|---|---|
milliseconds | number | 1–3600000 | The window duration in milliseconds (max 1 hour). |
The timer for the next window starts only after the current one finishes processing. If an LLM call takes longer than the window duration, subsequent windows will be delayed. The schedule does not "catch up" to a fixed real-world clock.
Prompts
Every sentinel requires a user prompt, defined via either userPromptFile or userPromptText. System prompts are optional for single-shot sentinels but required for conversational mode.
Prompt Fields
| Field | Type | Description |
|---|---|---|
systemPromptFile | string | string[] | Path(s) to system prompt file(s). |
systemPromptText | string | Inline system prompt text. |
userPromptFile | string | string[] | Path(s) to user prompt file(s). |
userPromptText | string | Inline user prompt text. |
When you provide an array of file paths, the files are concatenated in order.
Template Syntax
Prompts use Eta (opens in a new tab) templates, giving you access to the triggering events and codon context via the it object. See Template Context for all available properties.
Summarize these <%= it.events.length %> events:
<%= JSON.stringify(it.events, null, 2) %>Template Context
| Property | Type | Description |
|---|---|---|
it.events | ServerEvent[] | Array of events that triggered this execution. |
it.codon.id | string | The ID of the current codon. |
it.codon.name | string | The name of the current codon. |
it.codon.description | string? | The optional description of the current codon. |
it.codon.startTime | Date | The start time of the codon execution. |
it.world.currentTime | Date | The timestamp when the trigger was queued. |
Queue Time vs. Execution Time: it.world.currentTime reflects when the trigger was queued, not when the template runs. This provides a consistent timestamp even if LLM execution is delayed.
Template Examples
Iterate over events:
<% for (const event of it.events) { %>
- <%= event.type %>: <%= event.data?.codonId || 'N/A' %>
<% } %>Conditional content:
<% if (it.events.length > 10) { %>
High activity detected: <%= it.events.length %> events
<% } else { %>
Normal activity: <%= it.events.length %> events
<% } %>Access the last event:
Last event: <%= JSON.stringify(it.events[it.events.length - 1]) %>Conversational Mode
For sentinels that need to maintain memory across invocations, conversational mode keeps a rolling history of previous user prompts and assistant responses. This allows the sentinel to build context over time.
{
"conversational": {
"trimmingStrategy": {
"type": "maxTurns",
"maxTurns": 5
},
"continueOnError": true
}
}System Prompt Required: Conversational sentinels must have systemPromptFile or systemPromptText defined. This is enforced by validation.
Conversational Fields
| Field | Type | Required | Description |
|---|---|---|---|
trimmingStrategy | object | Yes | The strategy for pruning the conversation history to prevent it from growing indefinitely. |
continueOnError | boolean | No | If true, the sentinel will continue with a potentially corrupted history after an LLM failure. Defaults to false. |
Trimming Strategies
You must choose a strategy to prune the conversation history.
maxTurns
Keeps the last N complete turns (a turn consists of one user message and one assistant response).
{
"trimmingStrategy": {
"type": "maxTurns",
"maxTurns": 5
}
}| Field | Type | Constraints | Description |
|---|---|---|---|
maxTurns | number | 1–100 | The number of turns to keep. |
maxTokens
Keeps the most recent messages that fit within a specified token limit.
{
"trimmingStrategy": {
"type": "maxTokens",
"maxTokens": 4000
}
}| Field | Type | Constraints | Description |
|---|---|---|---|
maxTokens | number | 1–100000 | The token budget for the history. |
Token counting uses exact counts from LLM responses when available, falling back to a text.length / 4 approximation for older messages.
History Persistence
Conversation history is persisted to .hankweave/sentinels/history/{sentinel-id}-codon-{codon-id}.json, allowing sentinels to resume their state after a server restart.
Structured Output
For machine-readable output, structured output mode generates validated JSON according to a Zod schema. The LLM's response is parsed and validated before being written to the output file.
{
"structuredOutput": {
"output": "object",
"schemaStr": "z.object({ score: z.number(), notes: z.string() })",
"schemaName": "Evaluation",
"schemaDescription": "Code evaluation result"
}
}Structured Output Fields
| Field | Type | Required | Description |
|---|---|---|---|
output | "object" | "array" | "enum" | Yes | The desired output mode. |
schemaStr | string | Conditional | An inline Zod schema definition as a string. |
schemaFile | string | Conditional | The path to a file containing a Zod schema. |
enumValues | string[] | Conditional | An array of allowed string values for enum mode. |
schemaName | string | No | A name for the schema, passed to the LLM. |
schemaDescription | string | No | A description for the schema, passed to the LLM. |
Schema Rules
Hankweave enforces these rules at load time:
- For
objectorarraymode, you must provide exactly one ofschemaStrorschemaFile. - For
enummode, you must provideenumValuesand must not provideschemaStrorschemaFile.
Output Modes
Object Mode
Generates a single JSON object that conforms to the schema.
{
"structuredOutput": {
"output": "object",
"schemaStr": "z.object({ category: z.string(), confidence: z.number() })"
}
}Array Mode
Generates a JSON array of objects that conform to the schema.
{
"structuredOutput": {
"output": "array",
"schemaStr": "z.array(z.object({ item: z.string(), count: z.number() }))"
}
}Enum Mode
Generates a single string value from a predefined list.
{
"structuredOutput": {
"output": "enum",
"enumValues": ["urgent", "normal", "low-priority", "ignore"]
}
}Schema Files
For complex schemas, define them in a separate file and export the Zod schema as the default expression. The z object is automatically available in the file's scope; no import is needed.
z.object({
score: z.number().min(0).max(100),
notes: z.string(),
issues: z.array(z.string()).optional()
})Model Requirement: Structured output requires a model that supports tool calls. Using an incompatible model will cause a validation error at load time.
No joinString: The joinString property is invalid when structuredOutput is configured. Structured output is always written in NDJSON format (one JSON object per line).
LLM Parameters
Fine-tune the LLM's generation process.
{
"llmParams": {
"temperature": 0.3,
"maxOutputTokens": 4096,
"maxRetries": 2
}
}LLM Parameter Fields
| Field | Type | Default | Constraints | Description |
|---|---|---|---|---|
temperature | number | 0 | 0–2 | Controls randomness. 0 is deterministic, higher values are more creative. |
maxOutputTokens | number | 8192 | 1–100000 | The maximum number of tokens to generate in the response. |
maxRetries | number | 2 | 0–5 | Number of retry attempts for transient API failures. |
Error Handling
Configure how the sentinel behaves when it encounters errors like network issues or API failures.
{
"errorHandling": {
"maxConsecutiveFailures": 5,
"unloadOnFatalError": false
}
}Error Handling Fields
| Field | Type | Default | Description |
|---|---|---|---|
maxConsecutiveFailures | number | 3 | The number of consecutive failures before the sentinel unloads itself (min: 1). |
unloadOnFatalError | boolean | true | If true, the sentinel unloads on non-recoverable errors (e.g., bad template syntax). |
Error Categories
Hankweave distinguishes between different error types:
- Template Errors: Syntax errors in prompts that are guaranteed to fail on every execution.
- Configuration Errors: Invalid settings caught at load time. The sentinel will not start.
- Corruption Errors: Invalid data in the conversation history. Behavior depends on
continueOnError. - Resource Errors: Network timeouts or API failures. These are typically transient and retried.
A single successful LLM call resets the consecutive failure counter.
Output Configuration
By default, sentinel output is written to an auto-generated file. You can customize the format and location.
{
"output": {
"format": "text",
"file": "narrator-output.md"
}
}Output Fields
| Field | Type | Default | Description |
|---|---|---|---|
format | "text" | "jsonl" | text | The output file format. |
file | string | (auto) | The output file path. |
Path Conventions
File paths are resolved based on their structure:
- A single filename (e.g.,
"output.md"): Written to.hankweave/sentinels/outputs/{sentinel-id}/{filename}. - A relative path (e.g.,
"logs/output.md"): Written relative to the current execution directory.
Auto-generated Paths
If output.file is not specified, Hankweave generates a path automatically:
- Text:
.hankweave/sentinels/outputs/{id}/{id}-{codon}-{timestamp}.md - Structured:
.hankweave/sentinels/outputs/{id}/{id}-{codon}-{timestamp}.ndjson
Join String
For text output, joinString defines the separator between consecutive entries in the log file.
{
"joinString": "\n\n---\n\n"
}Supported escape sequences include \n (newline), \t (tab), \r (carriage return), and \\ (backslash). The default is "\n---\n".
Event Reporting
By default, sentinels emit lifecycle, error, and output events to connected WebSocket clients. You can configure this to reduce noise.
{
"reportToWebsocket": {
"lifecycle": true,
"errors": true,
"outputs": true,
"triggers": false
}
}Event Reporting Fields
| Field | Type | Default | Events Reported |
|---|---|---|---|
lifecycle | boolean | true | sentinel.loaded, sentinel.unloaded |
errors | boolean | true | sentinel.error |
outputs | boolean | true | sentinel.output |
triggers | boolean | false | sentinel.triggered (can be very verbose) |
Attaching to Codons
To use a sentinel, attach it to a codon in your hank.json file. You can reference an external configuration file or define the entire configuration inline.
{
"hank": [
{
"id": "generate-code",
"sentinels": [
{
"sentinelConfig": "./sentinels/narrator.sentinel.json"
},
{
"sentinelConfig": {
"id": "inline-sentinel",
"name": "Inline Example",
"model": "anthropic/claude-haiku-4-5",
"trigger": { "type": "event", "on": ["*"] },
"execution": { "strategy": "debounce", "milliseconds": 5000 },
"userPromptText": "Summarize: <%= JSON.stringify(it.events) %>"
},
"settings": {
"failCodonIfNotLoaded": true,
"outputPaths": {
"logFile": "custom-log.md",
"lastValueFile": "current-state.md"
},
"reportToWebsocket": {
"triggers": true
}
}
}
]
}
]
}Sentinel Entry Structure
| Field | Type | Description |
|---|---|---|
sentinelConfig | string | SentinelConfig | Path to a .sentinel.json file or an inline configuration object. |
settings | object | Codon-specific overrides for the sentinel's behavior. |
Codon-Level Settings
These settings allow you to reuse a single sentinel configuration with different behaviors for different codons.
{
"settings": {
"failCodonIfNotLoaded": true,
"outputPaths": {
"logFile": "custom-output.md",
"lastValueFile": "current.md"
},
"reportToWebsocket": {
/* ... */
}
}
}| Field | Type | Default | Description |
|---|---|---|---|
failCodonIfNotLoaded | boolean | false | If true, the codon will fail if this sentinel cannot be loaded. |
outputPaths.logFile | string | (from config) | Overrides the output.file path. |
outputPaths.lastValueFile | string | — | An additional output file that is atomically replaced with the latest value on each output. |
reportToWebsocket | object | (from config) | Overrides the reportToWebsocket configuration. |
lastValueFile vs. logFile: The logFile is append-only, creating a history of all outputs. The lastValueFile is overwritten each time, making it useful for dashboards or integrations that only need the current state.
Complete Examples
These battle-tested configurations cover common sentinel patterns.
Narrator Sentinel
The most common pattern: watch agent activity and produce human-readable summaries. Debouncing prevents spam during bursts of activity.
{
"id": "narrator",
"name": "Activity Narrator",
"description": "Provides human-readable summaries of agent activities.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "event",
"on": ["assistant.action", "tool.result"]
},
"execution": {
"strategy": "debounce",
"milliseconds": 3000
},
"userPromptText": "Summarize what the agent just did:\n\n<%= JSON.stringify(it.events, null, 2) %>\n\nProvide a brief, clear summary.",
"llmParams": {
"temperature": 0.3,
"maxOutputTokens": 2048
},
"output": {
"format": "text",
"file": "narrator.md"
}
}Conversational Narrator
When a sentinel needs to remember its previous outputs, conversational mode maintains history. This narrator builds on its previous summaries rather than repeating context.
{
"id": "conversational-narrator",
"name": "Conversational Narrator",
"description": "Maintains context across events for coherent narration.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "event",
"on": ["assistant.action", "tool.result"]
},
"execution": {
"strategy": "debounce",
"milliseconds": 500
},
"systemPromptText": "You are a narrator maintaining context. Build on previous summaries without repeating yourself.",
"userPromptText": "Summarize these events: <%= JSON.stringify(it.events, null, 2) %>",
"conversational": {
"trimmingStrategy": {
"type": "maxTurns",
"maxTurns": 5
},
"continueOnError": true
},
"output": {
"format": "text",
"file": "conversational-narrator.log"
}
}Error Pattern Detector
Sequence triggers are ideal for detecting patterns. This sentinel fires after three consecutive tool errors, which often indicates a systematic problem.
{
"id": "error-detector",
"name": "Error Pattern Detector",
"description": "Detects when the agent encounters repeated errors.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "sequence",
"interestFilter": {
"on": ["tool.result"]
},
"pattern": [
{
"type": "tool.result",
"conditions": [{"operator": "equals", "path": "isError", "value": true}]
},
{
"type": "tool.result",
"conditions": [{"operator": "equals", "path": "isError", "value": true}]
},
{
"type": "tool.result",
"conditions": [{"operator": "equals", "path": "isError", "value": true}]
}
],
"options": {
"consecutive": true
}
},
"execution": {
"strategy": "immediate"
},
"userPromptText": "The agent encountered 3 consecutive errors. Analyze:\n\n<%= JSON.stringify(it.events, null, 2) %>\n\nProvide:\n1. Common cause analysis\n2. Suggested fixes\n3. Whether to continue or intervene",
"output": {
"format": "jsonl",
"file": "error-patterns.jsonl"
}
}Cost Tracker with Conditions
Conditions filter triggers based on event data. This sentinel fires only when spending exceeds a threshold, helping to catch runaway costs.
{
"id": "cost-tracker",
"name": "Cost and Token Tracker",
"description": "Monitors token usage and costs, alerting on high usage.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "event",
"on": ["token.usage"],
"conditions": [
{
"operator": "greaterThan",
"path": "totalCost",
"value": 0.5
}
]
},
"execution": {
"strategy": "immediate"
},
"userPromptText": "High cost detected in codon <%= it.codon.id %>:\n\nTotal cost: $<%= it.events[0].data.totalCost %>\n\nAnalyze if this cost is justified.",
"output": {
"format": "jsonl",
"file": "cost-alerts.jsonl"
}
}Code Review with Structured Output
Structured output enables programmatic use of a sentinel's results. This QA sentinel reviews TypeScript files and returns validated JSON that downstream tools can consume.
{
"id": "qa-review",
"name": "QA Code Review",
"description": "Reviews TypeScript files and provides structured feedback.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "event",
"on": ["file.updated"],
"conditions": [
{
"operator": "matches",
"path": "path",
"value": "\\.ts$"
}
]
},
"execution": {
"strategy": "debounce",
"milliseconds": 10000
},
"systemPromptText": "You are a senior TypeScript developer. Review code for correctness, style, and bugs.",
"userPromptText": "Review these file changes:\n\n<% for (const e of it.events) { %>- <%= e.data.path %>\n<% } %>",
"structuredOutput": {
"output": "object",
"schemaStr": "z.object({ issues: z.array(z.object({ severity: z.enum(['error', 'warning', 'info']), file: z.string(), line: z.number().optional(), message: z.string() })), summary: z.string(), overallScore: z.number().min(0).max(100) })",
"schemaName": "CodeReview",
"schemaDescription": "Structured code review feedback"
}
}Periodic Summary with Time Window
The timeWindow strategy fires on a fixed schedule regardless of event volume, making it ideal for periodic status updates.
{
"id": "periodic-summary",
"name": "30-Second Summary",
"description": "Creates summaries every 30 seconds.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "event",
"on": ["assistant.action", "tool.result", "codon.started", "codon.completed"]
},
"execution": {
"strategy": "timeWindow",
"milliseconds": 30000
},
"userPromptText": "Summarize the last 30 seconds of activity:\n\n<%= JSON.stringify(it.events, null, 2) %>",
"output": {
"format": "text",
"file": "periodic-summaries.log"
}
}Tool Usage Analyzer with Count Strategy
The count strategy waits to accumulate N events before firing. This sentinel analyzes tool usage patterns after every 10 tool calls.
{
"id": "tool-analyzer",
"name": "Tool Usage Pattern Analyzer",
"description": "Analyzes patterns in tool usage to identify inefficiencies.",
"model": "anthropic/claude-haiku-4-5",
"trigger": {
"type": "event",
"on": ["tool.result"]
},
"execution": {
"strategy": "count",
"threshold": 10
},
"userPromptText": "Analyze tool usage patterns from <%= it.events.length %> calls:\n\n<%= JSON.stringify(it.events, null, 2) %>\n\nIdentify:\n1. Most frequently used tools\n2. Success/failure rates\n3. Inefficient patterns\n4. Optimization suggestions",
"output": {
"format": "jsonl",
"file": "tool-patterns.jsonl"
}
}Validation Rules
Hankweave validates sentinel configurations when they are loaded. The sentinel will fail to load if any of these rules are violated:
- Required user prompt: At least one of
userPromptFileoruserPromptTextmust be provided. - Conversational requires system prompt: If
conversationalis configured,systemPromptFileorsystemPromptTextis required. joinStringcompatibility:joinStringis only valid fortextoutput and cannot be used whenstructuredOutputis configured.- Structured output schema rules:
objectorarraymode requires exactly one ofschemaStrorschemaFile.enummode requiresenumValuesand forbids schema definitions.
- ID format: The
idmust match the pattern/^[a-z0-9-]+$/. - Condition path validation: Paths in
conditionsare validated against the schemas of the specified event types.
Related Pages
- Sentinels — Conceptual overview and when to use sentinels.
- Codons — The execution units that sentinels attach to.
- Configuration Reference — Full
hank.jsonconfiguration options. - Execution Flow — How sentinels fit into the overall lifecycle.