Skip to content

LoopConfig

chimera.core.loop_config.LoopConfig is the single dataclass that funnels every cross-cutting loop concern — permissions, detection, events, audit, checkpoints, git workflow, cancellation, message queues, file tracking, hooks, learning, discipline, condensation — into one optional argument. Each loop variant (ReAct, AgentLoop, RetryLoop, PlanActLoop, LintFeedbackLoop, PlanAndExecute, Reflexion, TreeOfThought, AutonomousLoop) accepts a LoopConfig and behaves identically when none is supplied.

from chimera.core.loop import ReAct
from chimera.core.loop_config import LoopConfig
from chimera.permissions import PermissionRuleset, Rule, PermissionAction
from chimera.detection.actions import LoopDetector
from chimera.events.base import EventBus
config = LoopConfig(
permissions=PermissionRuleset(
rules=[
Rule(tool_pattern="bash", action=PermissionAction.ASK),
Rule(tool_pattern="read_file", action=PermissionAction.ALLOW),
],
default=PermissionAction.ASK,
),
detector=LoopDetector(),
event_bus=EventBus(),
)
loop = ReAct(max_steps=50, config=config)

LoopConfig.__post_init__ installs a safe Interactive() permission policy whenever permissions is None and yolo_mode is False. It also auto-attaches a RedactionMiddleware to any provided event_bus so secret-shaped strings never leak into subscribers.

Three opt-outs (all backwards-compatible):

Opt-outEffect
yolo_mode=TrueSkips the default permission install; tools run freely
permissions=AutoApprove()Any explicit policy short-circuits the default
secrets_redaction=FalseDisables auto-wired redaction middleware
CHIMERA_UNSAFE=1 env varDisables ALL safety defaults process-wide (CI / internal use only)

_default_permissions_applied is set to True when the policy was installed by the dataclass; consumers (audit tooling, tests) may inspect it but never set it manually.

FieldTypeDefaultPurpose
permissionsPermissionPolicy | NoneNone (auto-installs Interactive())Gates tool calls; supports declarative PermissionRuleset for fine-grained control
detectorLoopDetector | NoneNoneDetects exact-repeat / pattern-cycle loops and warns or aborts
compactionCompactionStrategy | NoneNoneStrategy used when context approaches the budget
handlerStreamHandler | NoneNoneStream handler for token-by-token output
event_busEventBus | NoneNonePub/sub for the 26+ lifecycle events
auto_compact_thresholdfloat0.8Trigger compaction when context tokens exceed this fraction of the model budget
lspLSPManager | NoneNonePer-language LSP feedback after edits
cost_trackerCostTracker | NoneNonePer-step cache / reasoning / token cost ledger
audit_logAuditLog | NoneNonePermission-decision audit trail
checkpoint_managerCheckpointManager | NoneNoneNamed env snapshots for /checkpoint
git_workflowGitWorkflow | NoneNoneBranch-isolation + commit-strategy wrapper
wireWire | NoneNoneBidirectional WireMessage channel for IDEs / RPC clients
middlewarelist[LoopMiddleware] | NoneNoneRun-before-model / after-tool hooks
truncationTruncationConfig | NoneNonePer-result truncation rules
ghost_commitsGhostCommitManager | NoneNoneW13-G5 file-undo stack — auto-snapshots before every write/edit so /undo reverts files cleanly
file_trackerFileTracker | NoneNoneRecords read/modified files across compaction boundaries
cancellationCancellationToken | NoneNoneCooperative cancel signal honoured at safe yield points
message_queuesMessageQueues | NoneNoneSteering + follow-up queues for mid-turn injection
disciplinelist[DisciplineGuard] | NoneNonePhased-workflow guards (#118)
instruction_anchorInstructionAnchor | NoneNonePeriodically re-injects key instructions
learningLearningStore | NoneNoneSQLite+FTS5 observation store (#116)
feedback_trackerFeedbackTracker | NoneNoneConfidence + error tracking
learning_injectorLearningInjector | NoneNoneInject proven error-fix patterns mid-loop
hook_emitterHookEmitter | NoneNoneHook integration — fires PreToolUse, mutates updatedInput, overrides permissionDecision
tool_timeout_sfloat | NoneNonePer-tool-call timeout; tools that exceed it return a synthetic error so the loop continues
condensationSummaryCompaction | NoneNoneLLM-based condensation strategy (M11)
condense_every_n_stepsint | NoneNoneTrigger condensation.compact(...) every N steps
yolo_modeboolFalseDisable safety defaults for sandbox runs
secrets_redactionbool | NoneNoneForce-disable redaction middleware

Pass a PermissionRuleset for ordered, glob-based rule evaluation (last-match-wins, .gitignore style). Rules can target a tool name, an argument key, or a glob over arguments:

from chimera.permissions import PermissionRuleset, Rule, PermissionAction
rules = PermissionRuleset(
rules=[
Rule(tool_pattern="*", action=PermissionAction.ASK),
Rule(tool_pattern="read_file", action=PermissionAction.ALLOW),
Rule(tool_pattern="search", action=PermissionAction.ALLOW),
Rule(
tool_pattern="bash",
action=PermissionAction.DENY,
arg_key="command",
arg_pattern="rm *",
description="Block destructive shell commands",
),
],
default=PermissionAction.ASK,
)
config = LoopConfig(permissions=rules)

See Permissions for the full rule schema and the five preset policies (AutoApprove, AlwaysDeny, AllowList, ReadOnly, Interactive).

ghost_commits plugs a GhostCommitManager into the loop. The manager snapshots the workdir before every file-modifying tool call and exposes a stack-shaped undo() API used by the REPL /undo command:

from chimera.checkpoints_ghost import GhostCommitManager
ghost = GhostCommitManager(workdir="/path/to/project")
config = LoopConfig(ghost_commits=ghost)
# After the loop runs, restore the last write:
ghost.undo() # restores files to pre-write state
ghost.depth # snapshots remaining
ghost.peek() # most recent snapshot without popping

Snapshots cap at max_snapshots=50 by default; the oldest is evicted when the limit is reached. Both git repos (commit-on-hidden-ref) and plain directories (file copies) are supported.

The hook_emitter wires the 27 lifecycle events emitted by the loop into shell / prompt / function hooks. Each HookMatcher accepts an events: list[str] | None field that constrains which lifecycle events fire that matcher — None matches every event (legacy behaviour); a list (e.g. ["PreToolUse", "PostToolUse"]) restricts the matcher to those events only.

from chimera.hooks.emitter import HookEmitter
from chimera.hooks.executor import HookExecutor
from chimera.hooks.hook_types import HookMatcher, CommandHook
matchers = [
HookMatcher(
matcher="bash",
hooks=[CommandHook(command="echo 'about to bash'")],
events=["PreToolUse"], # only fires before bash, not after
),
]
emitter = HookEmitter(executor=HookExecutor(), matchers=matchers)
config = LoopConfig(hook_emitter=emitter)

The full lifecycle event roster (see chimera.hooks.events.HookEvent):

Event familyEvents
Tool dispatchPreToolUse, PostToolUse, PostToolUseFailure
PermissionsPermissionRequest, PermissionDenied
SessionSessionStart, SessionEnd, Stop, StopFailure, Setup
SubagentSubagentStart, SubagentStop, TeammateIdle
User inputUserPromptSubmit, Notification, Elicitation, ElicitationResult
CompactionPreCompact, PostCompact
TasksTaskCreated, TaskCompleted
WorkspaceWorktreeCreate, WorktreeRemove, CwdChanged, FileChanged
ConfigConfigChange, InstructionsLoaded

HookOutput from any hook may set permission_decision = "allow" | "deny" | "ask" | "defer" to override the policy; updated_input shallow-merges into tool args before dispatch.

import signal
from chimera.core.cancellation import CancellationToken
token = CancellationToken()
signal.signal(signal.SIGINT, lambda *_: token.cancel())
config = LoopConfig(cancellation=token)

The REPL (chimera code) wires this automatically so Ctrl+C cancels the current turn without killing the process.

from chimera.core.loop_config import LoopConfig, UNSAFE_ENV_VAR