Skip to content

Agent! 1.0.92.186

Choose a tag to compare

@macOS26 macOS26 released this 23 Aug 21:09
· 22 commits to main since this release

Agent! v1.0.92 (186)

Agentic Harness & Guards

  • Add HarnessGuardTests — eval suite for the agentic harness guards (e0b5aff)
    • 32 deterministic tests covering isToolFailure status-line semantics, toolCallFingerprint determinism + repeat exemptions, and routeStopReason (malformed tool call, max_tokens, open criteria, action claims, retry cap)
    • Also covers ToolErrorClassifier codes, GoalStateStore evidence gating, PlanStateStore surfacing rules, and critic-gate diff plumbing — all passing
  • Add critic review gate — opt-in one-shot LLM diff review before task_complete (2ce20ac)
    • New CriticGate.swift: when the toggle is on and the task edited files, completionGateBlocker runs a one-shot reviewer call (PASS/ISSUES) on git diff HEAD and blocks completion once with the found issues
    • Runs at most once per task (reset at task start in both main and tab loops) so it can never loop completion; toggle lives in Coding Preferences
  • Add ToolErrorClassifier — typed error codes + recovery hints on failing tool results (1d16827)
    • recordToolOutcomes appends a stable [error_code: ...] annotation with an actionable hint (old_string_not_found, file_not_found, permission_denied, build_failed, timeout, etc.)
    • The model gets structured errors instead of bare strings, directly on the failing tool_result content
  • Extend typed-error annotations to the tab-task path (55a8d19)
    • processTabResponseContent now runs the same ToolErrorClassifier as the main loop on each failing tool result
    • The [error_code: ...] + recovery hint is appended before the stuck-file and broken-record guards see the result
  • Add generic repeated-tool-call (broken record) guard (6dc2454)
    • StuckGuard.toolCallFingerprint() fingerprints every call (name + sorted-key JSON of input); polling/waiting tools are exempt via repeatExemptTools
    • Nudges on the 2nd identical call, hard-stops on the 3rd+; wired through ToolLoop.processTabResponseContent and TabTask
  • Nudge once before accepting a tool-less turn as completion (7bbb2eb)
    • A text-only turn is ambiguous (done, narrated plan, dropped malformed tool call, or truncation) but was silently reported as "✅ Completed"
    • First tool-less turn now appends a nudge to call task_complete or continue; a second consecutive one completes as before, so no ping-pong
  • Match guard tool results by tool_use_id instead of toolResults.last (5bcd81d)
    • toolResults.last is only correct for single-tool turns; on batched turns build failures were scored against unrelated output and edit failures attributed to the wrong file
    • Adds resultContent(for:in:) id-matched lookup plus an id-targeted appendNudge so rewind/stuck nudges land on the tool_result that actually triggered them
  • Route main-loop task_complete through the completion gates (c021bf3)
    • The four verification gates (goal criteria, auto-build, evidence, physical files) were unreachable from the main loop, which handles task_complete inline before dispatch
    • Extracted into completionGateBlocker() and called from both paths; when blocked, the refusal is fed back as that tool_use's tool_result and the loop continues
  • Fix false-positive stuck nudges on successful edits (77543c7)
    • appendStuckFileNudgeIfNeeded searched the ENTIRE tool output for "error:"/"failed"/"not found", so editing any file whose source contains those words logged phantom failures
    • Now checks only the first (status) line, matching FileTools.swift convention, and drops the bare "failed" substring
  • Fix the same false-positive stuck nudge in the overnight coding guard (025deed)
    • runOvernightCodingGuards had a byte-identical copy of the whole-output-scanning bug — and this is the live path that fired on every task
    • Same fix: status-line-only check, no bare "failed" substring; stops phantom 2-failure recovery nudges and 4-failure "stop editing this file" stuck-outs
  • Extract shared isToolFailure() helper (ac8ada4)
    • The same 8-line failure-detection block existed in two places, both with the identical bug needing the identical fix — a divergence risk on every future tweak
    • AgentViewModel.isToolFailure(output:) is now the single source of truth, called from both appendStuckFileNudgeIfNeeded and runOvernightCodingGuards
  • StuckGuard: unify give-up threshold with Guards.swift (6d51583)
    • The tab path gave up after 6 failures on a file while the main loop gave up at 4, despite a comment claiming the thresholds were unified
    • Both now give up at 4

Goal State & Planning

  • Feature #1: persistent goal state + self-verifying autonomy loop (57b5b1f)
    • GoalStateStore: file-backed goal + verification criteria that survive restarts, with a prompt block injected into Claude/Ollama system prompts
    • New goal_state tool (set/get/mark/clear) in all provider tool lists; task_complete bounces back while criteria remain unverified
  • Wire goal state prompt block into all LLM providers (835f427)
    • GoalStateStore.promptBlock was only injected for Claude/Ollama
    • OpenAI-compatible and Codex providers now also see the active goal and its success criteria, so goal-driven execution works on every backend
  • Require evidence when marking a goal criterion done (171b139)
    • The gate only checked criteria were marked done — pure self-reporting; a criterion could be checked off with zero proof
    • GoalCriterion gains an evidence field; goal_state(mark, done:true) rejects missing/blank evidence; task_complete blocks on unevidencedCriteria; 4 new tests
  • Add PlanStateStore — surface active plan checklist in every system prompt (6cda83f)
    • Injects the most recent non-stale plan's step status alongside the GoalStateStore/ToolOutcomeStore blocks
    • Works across Claude, OpenAI-compatible, Ollama, and Codex prompt builders — the model sees plan progress each turn without calling plan_mode(read)
  • Add self-verification pass + edit summary to task_complete (03a5dc1)
    • Physical evidence check: every file snapshotted this task must still exist and be non-empty, else completion is blocked with a rewind hint
    • task_complete output now appends taskEditSummary() so the blast radius of a task is always visible

Context & Compaction

  • Scale the compaction threshold to the model's real context window (b97e336)
    • CompactionState ignored its contextWindow argument and always used a hardcoded 30K threshold — far too late for a 4K Foundation Models session, far too early (cache-destroying) for 1M Claude
    • Threshold is now ~55% of the window clamped to 2K–400K; the provider→window mapping moved into AgentViewModel.contextWindow(for:) so the token meter and compactor share one source of truth
  • Always run structural compaction; only AI summarization respects the toggle (bcc2827)
    • With "Token Compression" off there was no truncation path at all: the conversation grew append-only until the provider rejected it, and context-length recovery was a no-op
    • Structural recovery now always runs; the toggle gates only Tier 1 Apple Intelligence summarization; adds a cheap chars/4 pre-check and demotes orphaned tool_result blocks so pruning can't cause an Anthropic 400
  • Prompt-cache-stable context + stop_reason-driven loop control (8fda92f)
    • Tier 1: messages are append-only between compaction events — the per-turn sliding window that rewrote the conversation middle (defeating prompt caching) is gone; the newest user message carries a cache_control breakpoint; PrefixStabilityTests locks in byte-identical prefixes
    • Tier 2: routeStopReason routes turns on the API-reported stop_reason — malformed tool calls re-issue, max_tokens continues instead of completing, end_turn with open goal criteria gets a specific nudge
  • Add ToolResultCache: disk spill for tool results before compaction (b258882)
    • Compaction rewrites old tool_result blocks to a 3-line preview, so the agent's own earlier reads vanished mid-task and it re-read the same files
    • Full text is written to .agent/toolcache/.txt before truncation so it stays recoverable
  • Compression: spill tool results to disk before truncating (0ed2557)
    • Both lossy sites (compressMessages 3-line preview, microcompact [cleared]) now write the full content to ToolResultCache first
    • Compaction becomes recoverable rather than destructive
  • Add restore_tool_result tool (04f42ee)
    • Exposes the spill cache to the model: pass the tool_use_id from a truncated block to get the full text back, instead of re-reading a file
    • Schema registered in ClaudeService.tools(), which also points ToolResultCache at the current project folder
  • Compression: key summary cache on SHA-256 instead of hashValue (2bd7452)
    • _summaryCache was keyed on content.hashValue — per-process seeded and collision-prone
    • A collision served another tool result's summary as if it were this one; SHA-256 keys eliminate the silent wrong-context risk

LLM & Sub-agents

  • Extended thinking for Claude + reasoning effort pass-through (0b39191)
    • New persisted Reasoning setting (off/low/medium/high); ClaudeService maps it to an extended-thinking budget (2K/8K/16K), sends the interleaved-thinking beta flag, drops temperature, and floors max_tokens above the budget
    • Streams thinking/redacted_thinking blocks (with signatures) so they pass back unmodified across tool turns; OpenAI-compatible providers get reasoning_effort opt-in; retry paths preserve thinking blocks and compaction never splits a message
  • Sub-agent upgrades: model override, file-based results, tiered caps (f7bb1bb)
    • spawn_agent accepts an optional model id from the active provider's list, so cheap/fast models can run search agents while the parent keeps its own — honored across Claude, LM Studio, and both Ollama paths
    • Findings longer than the notification cap spill to {project}/.agent/subagents/.md with the path in the notification; write-capable agents stay capped at 3 concurrent, read-only research agents fan out to 6
  • Tool-outcome learning: in-task advisories + chronic-failure flags (7753e40)
    • ToolOutcomeStore tracks per-tool success/failure per task and persists per-project counts to {project}/.agent/tool_outcomes.json; after 3 same-tool failures in one task a one-shot advisory tells the LLM to change approach
    • Tools with 5+ persisted failures and no successes are listed in a CHRONICALLY FAILING TOOLS system-prompt block, frozen at task start for prompt-cache stability; any success clears chronic status

Hooks, Rewind & Backups

  • Wire post-tool hooks into tool dispatch (96b3857)
    • runPostToolHooks was defined in HooksService but never called anywhere
    • finishStep() now feeds each appended tool result through the postToolUse hooks and substitutes the transformed output, so hooks can react to / rewrite results without model tokens
  • Fire taskStart and taskComplete event hooks (6a566af)
    • runEventHooks was dead code
    • The tab task loop now emits .taskStart at the beginning of a run and .taskComplete on task_complete, passing prompt/summary/tab/projectFolder as context
  • Fire buildFailure event hook on failed xcode_build (d05e2ac)
    • Completes the hook wiring: .buildFailure fires with projectPath, projectFolder and the tail of the build output
    • Hooks can auto-react to a broken build without spending model tokens
  • Route file edit backups through FileBackupService.snapshot (87a9ae5)
    • snapshot() was dead code — the live write/edit paths called backup() directly, so per-task snapshot grouping never happened
    • The three call sites (FileManager.swift write_file/edit_file, File.swift) now use snapshot(), giving task-scoped rewind real data
  • Add task-scoped rewind on top of FileBackupService (2664587)
    • snapshot() recorded per-task versions but nothing consumed them; a failed multi-file attempt could only be undone one edit at a time
    • New FileBackupService.snapshottedFiles()/rewindTask() roll every file touched this task back to its version-1 snapshot as a unit; exposed as rewind_task / task_edits tool actions and file(action:"rewind"|"task_edits") aliases
  • Offer task rewind after 3 consecutive build failures (022d0b2)
    • rewind_task existed but only fired if the model chose to call it
    • consecutiveBuildFailures was already tracked, so the rewind offer now hooks in at 3 failures — before the hard auto-stop at 5

Xcode & Tooling

  • Xcode build: verify status before reporting success (c16b0a8)
    • buildProject only polled completed — a cancelled, failed, or errored build with zero collectible issues was reported as "Build succeeded"
    • Now requires status == .succeeded; adds an isXcodeRunning() guard so ScriptingBridge can't cold-launch Xcode mid-build, waits for workspace.loaded before build(), and includes status in the timeout message
  • Infer build target from path in xcode add_file (fdfded1)
    • addFileToProject always inserted into the FIRST PBXSourcesBuildPhase (the app target), so files under AgentTests/ broke the build with "unable to resolve module dependency: 'Testing'"
    • New sourcesPhaseID(for:in:) matches the file's parent directory against each PBXNativeTarget name and returns that target's Sources phase, falling back to first-phase for non-target-shaped paths
  • Stop logging git/xcode tool output twice (d19a938)
    • Tools shelling out through executeViaUserAgent streamed output live into the activity log, then dispatchTool logged the returned string a second time — every commit block appeared twice
    • executeViaUserAgent records what it streamed in lastStreamedOutput and both logging sites skip the log when the return value matches
  • ToolBatch: make read_file parallel and actually consume pre-executed results (d935d30)
    • precomputedResults was write-only: the TaskGroup pre-executed read-only tools, then the dispatch loop re-ran every tool anyway — every parallel batch executed twice
    • The read_file dedup guards are now nonisolated (lock-protected), so read_file joins the parallel group; multi-file reads — the agent's most common operation — run concurrently
  • Tab tasks: run the same overnight-coding guard battery as the main loop (e436bda)
    • runTabTask is called from 7 UI sites but ran exactly one guard, while the main loop runs the full runOvernightCodingGuards battery (build enforcement, edit-cycle detection, build-failure budget)
    • processTabResponseContent now takes the three guard counters as inout params, calls the full battery per tool result, and ends the task cleanly when the error budget trips

Tests

  • Add unit tests for the agentic core (cb8e401)
    • GoalStateStoreTests: criteria trimming/filtering, the allCriteriaDone gate, empty-criteria semantics, case-insensitive lookup, persistence across instances, clear() reopening the gate, promptBlock rendering
    • StuckGuardFingerprintTests: fingerprint determinism, key-order normalization, changed value/name/extra-key detection, and the repeatExemptTools membership contract
  • Fixture-replay tests for the task loop's decision layer (919ffd0)
    • Extracts turnDecision (pure completion detection) so it and routeStopReason are testable without UI or stores
    • LoopReplayTests drives scripted fixtures through the real decision order: happy path, malformed-tool recovery, max_tokens truncation, premature end_turn against open criteria; also locks in that sanitizeMessagesForContinuation leaves no orphaned tool_use blocks
  • Add regression tests for isToolFailure() (e36891c)
    • Failure detection had zero coverage despite being pure string logic that was wrong twice — both times from scanning the whole output instead of the status line
    • Pins that a successful edit echoing "error:"/"failed"/"not found" in its body is NOT a failure, real status-line failures still are, and bare "failed" is not a trigger
  • Gate live-network web automation tests behind an env var (b21cc4b)
    • WebAutomationTests drives real Safari against Google/LinkedIn/GitHub; results depend on network, login state and third-party markup, so the suite could never exit clean
    • Opt in with AGENT_RUN_NETWORK_TESTS=1; default xcodebuild test now passes: 57 tests, 6 suites, 0 failures
  • Make the test target compile so the suite can actually run (96dc0f1)
    • DiffToolsTests.swift had 35 missing-try errors on throwing helpers; AgenticCoreTests needed @mainactor for main-actor-isolated members
    • xcodebuild test now runs to completion: 26 tests across GoalStateStoreTests, StuckGuardFingerprintTests and DiffTools all pass

Full Changelog: v1.0.90.184...v1.0.92.186