77a68b1070
Two pathologies surfaced in the same captured failure (#2081): a subagent spent 16 minutes hammering 205 `edit` calls (182 byte-identical no-ops) against a file that already matched its payload, while a sibling bash invocation persisted 7.6MB of PowerShell rich-object metadata to `~/.omp/agent/artifacts/<id>.bash.log` from what was intended as a small tail. Both are addressed independently here: - Hashline executor now consults a per-ToolSession `noopLoopGuard` that hashes the raw patch input and tracks consecutive no-ops per canonical path. After NOOP_HARD_LIMIT (3) repeats of the same payload the soft "byte-identical" hint escalates to a thrown ToolError, which the agent loop surfaces as a tool failure rather than success-with-text — far more effective at breaking the loop than the soft hint alone. A non-noop commit (or any variant payload) resets the counter; state is isolated per ToolSession so subagents cannot inherit each other's history. - OutputSink artifact-on-disk writes are now bounded by `artifactMaxBytes` (default 4 MiB = 3 MiB head + 1 MiB rolling tail). Once the head budget is exhausted, subsequent chunks divert into a fixed-size tail ring; `dump()` replays the ring behind a single `[ARTIFACT TRUNCATED: kept first … + last … of …; … elided from the middle]` notice before closing the sink. Setting `artifactMaxBytes: 0` restores the historical unbounded behavior. Sized comfortably above anything a model would reasonably scroll through via the artifact URL scheme while preventing the captured 7.6MB spray from sitting on disk. The terminal-typing lag the reporter observed has multiple compounding causes (transcript-render freezing is disabled on win32; the bash result renderer lacks the per-render cache that the eval renderer already has). Those land in a follow-up — the loop guard + artifact cap address the root pathologies that turned the session into a multi-MB transcript in the first place. Fixes #2081