Skip to content

Event guards ​

These events fire on a round count, a truncation size, a call budget, or a memory cap. The two token events, auto-compact and fold, stay on Reactive LLM events.

Round-count events ​

The same {name, on, run} shape also covers the loop's stuck-loop and task-budget guards -- these fire on a count of rounds, not a token measurement, so their on: clause uses rounds: instead of tokens::

yaml
# ~/.kdeps/events/identical-tool-calls.yaml
name: identical-tool-calls
on:
  rounds: 3   # how many times in a row the model may issue the exact same tool call
run: force_answer
EventFires afterAction
identical-tool-callsthe model issues the exact same tool call and gets the exact same result this many times in a row. Tools with a per-turn budget (web, bash, file, code) ignore this number and use their allocated budget instead (/model tool set <category>-limit <n>; 0 = unlimited = never stops)ends the turn as a stuck loop
convergence-blockthis many consecutive rounds end in a convergence-blocked tool call (the model kept trying once a web/bash/file/code budget ran out)strips every tool and forces a text answer
task-round-budgeta single goal-directed task spends this many tool roundsforce-closes the task
unproductive-roundsthis many consecutive rounds produce no new tool result or state changeforce-closes the task and fails it forward
handshake-timeouta session-handshake grounding challenge (see session-integrity handshake) spends this many tool rounds without resolvingfails the handshake
judge-max-roundsa single judge's review spends this many tool roundsends that judge's review and forces its verdict
judge-iterationsthe revise-and-rejudge loop retries this many times after a judge rejectionaccepts the last response regardless of outcome -- a judge panel must never block a turn indefinitely

A sensible floor of 1 ​

Every one of these has a sensible floor of 1 built in: an override file that omits rounds:, or a corrupted one, never accidentally disables the guard by resolving to 0 (which would otherwise fire on the very first round).

Truncation events ​

The same shape covers the loop's token-economy limits -- these fire on a measured byte length, so their on: clause uses bytes: instead:

yaml
# ~/.kdeps/events/tool-result-truncate.yaml
name: tool-result-truncate
on:
  bytes: 16384   # any single tool result past this size is truncated
run: truncate
EventFires whenAction
tool-result-truncatea single tool result fed back to the LLM exceeds this sizetruncates on a line boundary, appends a marker
tool-error-truncatea tool's failure text exceeds this sizetruncates before display and before feeding it back to the LLM
force-answer-digestthe gathered-output digest inlined into a forced answer exceeds this sizetruncates the digest
history-window-trimthe in-flight tool-loop message array exceeds this sizedrops the oldest complete tool round-trips
file-read-limita file a builtin tool is about to read (or write_file's own content) exceeds this sizerejects the call with an error instead of reading/writing it

Like the round-count cluster, each has its own sensible fallback (matching its built-in default) if the event registry is ever unavailable -- an override that omits bytes: never resolves to "truncate to nothing" or "reject every file."

Call-budget events ​

The convergence caps on web_search/web_scraper, bash_exec, read_file/list_files, and search_local/code_search (see tools) are also events -- these fire on a distinct-call count per turn, so their on: clause uses distinctCalls::

yaml
# ~/.kdeps/events/web-call-budget.yaml
name: web-call-budget
on:
  distinctCalls: 20   # DISTINCT web_search + web_scraper calls this turn -- a repeat never counts twice
run: block
EventCapsDefault
web-call-budgetdistinct web_search/web_scraper calls per turn (they share one budget)20
bash-call-budgetdistinct bash_exec commands per turn50
file-call-budgetdistinct read_file/list_files paths per turn80
code-call-budgetdistinct search_local/code_search queries per turn30

These four already had a per-session override path before events existed -- /model tool set web-limit <n> (and bash-limit/file-limit/code-limit) still work exactly as before, on top of whatever the event's own default is, the same relationship /fold threshold has with the fold event. Setting one to 0 removes the cap entirely (/model tool set web-limit 0): the budget never blocks, and the system prompt stops telling the model to synthesize after a fixed number of searches - it states the limit actually enforced, or says there is no cap. Identical repeated calls (same call, same result) stop after as many rounds as the tool's allocated budget; only tools without a budget use the identical-tool-calls threshold.

Memory events ​

The persistent memory subsystem's injection/traversal caps are events too. memory-prompt-limit fires on a token measurement like auto-compact/fold; the rest are plain counts with no trigger condition at all -- they always apply, so they set items: directly instead of an on: clause:

yaml
# ~/.kdeps/events/memory-focus-max.yaml
name: memory-focus-max
items: 5   # how many prompt-relevant entries are force-kept when memory is truncated
run: cap
EventCapsDefault
memory-prompt-limittokens of memory content injected into the system preamble each turn500
memory-keys-limitkey names listed in the <memory-keys> preamble block100
memory-focus-maxprompt-relevant entries force-kept when memory is truncated5
memory-chain-maxentries the active task chain force-keeps (its nearest ancestors)8
rel-memory-limitbase memory rows fed into a memory_query join, bounding its worst case500

Released under the Apache 2.0 License.