Skip to content

fix(agent): back off context and cwd#1600

Merged
zerob13 merged 3 commits intodevfrom
fix/maxtoken-limit
May 9, 2026
Merged

fix(agent): back off context and cwd#1600
zerob13 merged 3 commits intodevfrom
fix/maxtoken-limit

Conversation

@zerob13
Copy link
Copy Markdown
Collaborator

@zerob13 zerob13 commented May 9, 2026

Summary by CodeRabbit

  • New Features

    • Context-pressure preflight with a 256-token safety margin, automatic recovery/compaction when output capacity would drop below threshold, and safety-adjusted tool-output fitting (reports zero effective output when nothing fits)
    • Cross-platform shell fallback and robust shell resolution for background exec
  • Bug Fixes

    • Richer spawn error messages and stricter working-directory validation to avoid failing spawns
    • Provider-options sanitization to drop invalid fields before sending to providers
  • Documentation

    • Added plans, specs, and task checklists for context-budget and shell-fallback behavior
  • Tests

    • New unit tests covering context budgeting, recovery, shell fallback, spawn handling, and provider-option sanitization

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 11d8c25f-5473-4263-b500-e1a7a193936b

📥 Commits

Reviewing files that changed from the base of the PR and between 37f1f2e and cb6d542.

📒 Files selected for processing (5)
  • docs/issues/background-exec-shell-fallback/plan.md
  • docs/issues/background-exec-shell-fallback/spec.md
  • docs/issues/background-exec-shell-fallback/tasks.md
  • src/main/lib/agentRuntime/shellEnvHelper.ts
  • test/main/lib/agentRuntime/shellEnvHelper.test.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/issues/background-exec-shell-fallback/plan.md
  • docs/issues/background-exec-shell-fallback/tasks.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/main/lib/agentRuntime/shellEnvHelper.test.ts
  • src/main/lib/agentRuntime/shellEnvHelper.ts

📝 Walkthrough

Walkthrough

This PR adds a provider-call preflight for token budgeting with a 256-token safety margin and context-pressure recovery (compaction/trim) when output capacity would drop below 4000 tokens, and centralizes POSIX shell fallback plus spawn-cwd validation with enriched spawn error messages and tests.

Changes

Agent Tool Context Budget - Second Increment

Layer / File(s) Summary
Planning & Specification
docs/issues/agent-tool-context-budget/plan.md, spec.md, tasks.md
New "Second Increment" docs: provider-call preflight, 256-token safety margin, context-pressure detection at 4000-token threshold, internal recovery/compaction or transient trimming, tool-output safety-adjusted fitting, and zero-effective-output reporting when unfittable.
Context Budget Primitives
src/main/presenter/agentRuntimePresenter/contextBudget.ts
Adds AGENT_CONTEXT_SAFETY_MARGIN_TOKENS, AGENT_CONTEXT_PRESSURE_MIN_OUTPUT_TOKENS, RequestContextPreflightResult, and getUsableContextLength() which reserves safety margin and enforces a minimum effective-output floor.
Preflight API & Tool Sanitization
src/main/presenter/agentRuntimePresenter/contextBudget.ts
New preflightRequestContext() that caps requestedMaxTokens, estimates tool-reserve tokens, sanitizes tool continuations, fits messages into the usable context window, computes effectiveMaxTokens, and sets fitsWithinContext, shrunkByContextPressure, and requiresContextPressureRecovery.
Message Fitting & Output Guard
src/main/presenter/agentRuntimePresenter/contextBudget.ts, src/main/presenter/agentRuntimePresenter/toolOutputGuard.ts
Updates message-fitting and effective-max-tokens math to use getUsableContextLength(...); hasContextBudget() and tool-output fitting use the safety-adjusted context length.
Compaction Service Recovery
src/main/presenter/agentRuntimePresenter/compactionService.ts
Adds prepareForContextPressureRecovery() to force compaction intent and updates prepareCompaction() to accept projectedMessages: ChatMessage[] and an optional force flag to bypass trigger checks.
Runtime Presenter Wiring
src/main/presenter/agentRuntimePresenter/index.ts
Replaces prior "budget + fit" flow with preflightRequestContext() and an optional recoverRequestContextPressure() compaction/trim recovery step that may replace the leading system prompt in-place, re-runs preflight, and streams with recovered messages and effectiveMaxTokens.
Message Mapper Sanitization
src/main/presenter/llmProviderPresenter/aiSdk/messageMapper.ts
Adds isPlainObject/sanitizeProviderOptions() to drop invalid provider option values before attaching providerOptions to mapped AI SDK messages across reasoning parts, text parts, content, tool calls, and tool results.
Context Budget Tests
test/main/presenter/agentRuntimePresenter/contextBudget.test.ts, toolOutputGuard.test.ts, aiSdkMessageMapper.test.ts, agentRuntimePresenter.test.ts
Adds deterministic token mocks and tests for usable-context sizing, shrinking under pressure, recovery triggering, zero-fit reporting, orphaned tool filtering, compaction/trim recovery behaviors, and tool-output guard thresholds.

Background Exec Shell Fallback

Layer / File(s) Summary
Planning & Specification
docs/issues/background-exec-shell-fallback/plan.md, spec.md, tasks.md
Documents centralized getUserShell() fallback, platform-specific POSIX candidate lists, PATH+default-path resolution, pre-spawn working-directory validation, and error-reporting expectations.
Spawn Guard Utilities
src/main/lib/agentRuntime/spawnGuard.ts
New helpers: resolveUsableSpawnCwd(cwd) to normalize and validate cwd (exists & directory) with remediation hints, and describeSpawnFailure(error, context) to format spawn failure messages with optional ENOENT guidance.
Shell Resolution & Fallback
src/main/lib/agentRuntime/shellEnvHelper.ts
Adds fs-based availability/executability checks, POSIX fallback candidate lists (macOS/Linux/generic), isAvailableShell(), resolveShellPath() (absolute or via PATH+defaults), getFallbackShellCandidates(), and reworked getUserShell() defaulting to /bin/sh with ['-c'] when needed.
Background Session Manager
src/main/lib/agentRuntime/backgroundExecSessionManager.ts
Computes spawnCwd via resolveUsableSpawnCwd() in start(), persists cwd/shell onto the session, and uses describeSpawnFailure() to build enriched child error messages and logging.
Agent Bash Handler
src/main/presenter/toolPresenter/agentTools/agentBashHandler.ts
Uses resolveUsableSpawnCwd(cwd) for detached/foreground spawns so the spawned process receives a validated working directory.
Background Exec Tests
test/main/lib/agentRuntime/backgroundExecSessionManager.test.ts, shellEnvHelper.test.ts, agentBashHandlerEncoding.test.ts, test/setup.ts
Extends and centralizes fs mocks (statSync/accessSync/constants.X_OK), preserves/restores SHELL env per test, asserts /bin/sh fallback bootstrap uses ['-c'], asserts rejection when cwd is missing/inaccessible, and loosens cwd assertions to accept path-ending matches.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I count the tokens, tuck a margin tight,
If space grows thin I compact through the night.
When shells are lost, I hunt PATH till one shows,
Validate the cwd so the spawn safely goes—
Hoppy fixes keep the runtime light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(agent): back off context and cwd' accurately reflects the main changes in this PR, which implement context-pressure backoff for agent tool execution and shell working directory resolution.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/maxtoken-limit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
test/main/presenter/agentRuntimePresenter/toolOutputGuard.test.ts (1)

4-27: ⚡ Quick win

The tokenx mock is not exercised by this test.

hasContextBudget() only calls approximateTokenSize() for toolDefinitions, but this case passes toolDefinitions: []. The 3744/3745 boundary is therefore still tied to estimateMessagesTokens(), so the test can drift when the message estimator changes.

Either add a non-empty toolDefinitions fixture so the mock participates, or remove the mock and derive the boundary from the same message-token estimator the production code uses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/main/presenter/agentRuntimePresenter/toolOutputGuard.test.ts` around
lines 4 - 27, The test's tokenx mock never runs because hasContextBudget() only
calls approximateTokenSize() for toolDefinitions and the test passes
toolDefinitions: []; update the test (in toolOutputGuard.test.ts) to include a
small non-empty toolDefinitions fixture (e.g., one ToolDefinition object with a
predictable description or prompt text) so that approximateTokenSize (the
vi.mock for 'tokenx') is exercised, and adjust the message lengths (the
'x'.repeat(...) values) as needed to preserve the same true boundary when the
mocked approximateTokenSize is applied; ensure references to ToolOutputGuard and
hasContextBudget remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/lib/agentRuntime/shellEnvHelper.ts`:
- Around line 266-292: The current isAvailableShell/resolveShellPath logic only
checks existence via fs.existsSync which can return true for non-executable
files; update isAvailableShell to verify executability as well by using
fs.statSync and fs.accessSync(shellPath, fs.constants.X_OK) (or equivalent
permission checks) and return false when access or stat fails; keep
resolveShellPath behavior but call the new isAvailableShell so non-executable
candidates are skipped and the fallback search continues.

In `@src/main/presenter/agentRuntimePresenter/contextBudget.ts`:
- Around line 163-171: The code reports a positive effectiveMaxTokens even when
fitsWithinContext is false (remainingOutputTokens <= 0); change the logic so
effectiveMaxTokens is zero whenever the request does not fit. Concretely, update
the computation of effectiveMaxTokens (alongside fitsWithinContext) so that when
hasFiniteContext and remainingOutputTokens <= 0 you set effectiveMaxTokens = 0;
otherwise keep the existing Math.max(Math.min(requestedMaxTokens,
remainingOutputTokens), AGENT_MIN_EFFECTIVE_OUTPUT_TOKENS) behavior, and ensure
totalRequestTokens uses that adjusted effectiveMaxTokens. This ensures
effectiveMaxTokens is never positive when fitsWithinContext is false.

In `@src/main/presenter/agentRuntimePresenter/index.ts`:
- Around line 1877-1901: The recovery path currently only patches the leading
system prompt via replaceLeadingSystemPromptInPlace, leaving requestMessages
(the mutable array later reused) unchanged when recoverContextPressure returns a
trimmed/compacted list; update requestMessages in-place whenever
recovered.messages exists (replace its contents with recovered.messages) so
subsequent loops and provider.coreStream see the persisted, trimmed message
list, and ensure requestPreflight (from preflightRequestContext) remains
consistent with that mutated requestMessages; perform this update alongside the
existing recovered.systemPrompt handling after recoverContextPressure returns.

In `@src/main/presenter/llmProviderPresenter/aiSdk/messageMapper.ts`:
- Around line 20-35: The sanitizeProviderOptions function currently only checks
typeof === 'object' and !Array.isArray, letting Date/URL/class instances
through; update the filter in sanitizeProviderOptions to only accept plain
object payloads by using a robust "is plain object" check (e.g., ensure
Object.prototype.toString.call(value) === '[object Object]' or
Object.getPrototypeOf(value) === Object.prototype) in the entry type guard for
the Object.entries filter so exotic objects (Date, URL, class instances) are
rejected and the function returns undefined for non-plain values.

In `@test/main/lib/agentRuntime/backgroundExecSessionManager.test.ts`:
- Line 55: The test currently calls
vi.mocked(fs.existsSync).mockReturnValue(...), but vi.mocked is only a TS type
helper and not a mock creator; replace those calls with spies: use vi.spyOn(fs,
'existsSync').mockReturnValue(...) for the occurrences around where existsSync
is used (refer to the test file and the symbol fs.existsSync), and apply the
same change to the other two occurrences flagged in the comment (the calls at
the other test locations). Ensure you create the spy before invoking the mock
behavior and restore the spy after the test if necessary.

In `@test/main/lib/agentRuntime/shellEnvHelper.test.ts`:
- Line 68: Tests call vi.mocked(fs.existsSync) but the 'fs' module is never
mocked or spied on, causing runtime failures; fix by either adding a
module-level mock with vi.mock('fs') (consistent with other mocks like
'child_process' and 'electron') or by creating spies in the test setup (e.g., in
beforeEach call vi.spyOn(fs, 'existsSync') and then use .mockReturnValue(...) /
.mockImplementation(...) for each test), and update all usages of
vi.mocked(fs.existsSync) (including the other occurrences) to operate on the
mocked/spied function.

In
`@test/main/presenter/toolPresenter/agentTools/agentBashHandlerEncoding.test.ts`:
- Line 68: The test currently uses vi.mocked(fs.existsSync) which doesn't create
runtime stubs and leaves resolveUsableSpawnCwd() calling the real filesystem;
replace that with runtime spies/stubs for both fs.existsSync and fs.statSync
(the functions used by resolveUsableSpawnCwd in spawnGuard.ts) — e.g., spy on
fs.existsSync to return true and spy on fs.statSync to return a Stats-like
object whose isDirectory() returns true, and ensure you restore the spies after
the test so other tests are unaffected.

---

Nitpick comments:
In `@test/main/presenter/agentRuntimePresenter/toolOutputGuard.test.ts`:
- Around line 4-27: The test's tokenx mock never runs because hasContextBudget()
only calls approximateTokenSize() for toolDefinitions and the test passes
toolDefinitions: []; update the test (in toolOutputGuard.test.ts) to include a
small non-empty toolDefinitions fixture (e.g., one ToolDefinition object with a
predictable description or prompt text) so that approximateTokenSize (the
vi.mock for 'tokenx') is exercised, and adjust the message lengths (the
'x'.repeat(...) values) as needed to preserve the same true boundary when the
mocked approximateTokenSize is applied; ensure references to ToolOutputGuard and
hasContextBudget remain unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 15826431-0492-44b8-a7f8-c2aa409fe82f

📥 Commits

Reviewing files that changed from the base of the PR and between f723237 and 1dbfbc3.

📒 Files selected for processing (22)
  • docs/issues/agent-tool-context-budget/plan.md
  • docs/issues/agent-tool-context-budget/spec.md
  • docs/issues/agent-tool-context-budget/tasks.md
  • docs/issues/background-exec-shell-fallback/plan.md
  • docs/issues/background-exec-shell-fallback/spec.md
  • docs/issues/background-exec-shell-fallback/tasks.md
  • src/main/lib/agentRuntime/backgroundExecSessionManager.ts
  • src/main/lib/agentRuntime/shellEnvHelper.ts
  • src/main/lib/agentRuntime/spawnGuard.ts
  • src/main/presenter/agentRuntimePresenter/compactionService.ts
  • src/main/presenter/agentRuntimePresenter/contextBudget.ts
  • src/main/presenter/agentRuntimePresenter/index.ts
  • src/main/presenter/agentRuntimePresenter/toolOutputGuard.ts
  • src/main/presenter/llmProviderPresenter/aiSdk/messageMapper.ts
  • src/main/presenter/toolPresenter/agentTools/agentBashHandler.ts
  • test/main/lib/agentRuntime/backgroundExecSessionManager.test.ts
  • test/main/lib/agentRuntime/shellEnvHelper.test.ts
  • test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts
  • test/main/presenter/agentRuntimePresenter/contextBudget.test.ts
  • test/main/presenter/agentRuntimePresenter/toolOutputGuard.test.ts
  • test/main/presenter/llmProviderPresenter/aiSdkMessageMapper.test.ts
  • test/main/presenter/toolPresenter/agentTools/agentBashHandlerEncoding.test.ts

Comment thread src/main/lib/agentRuntime/shellEnvHelper.ts
Comment thread src/main/presenter/agentRuntimePresenter/contextBudget.ts
Comment thread src/main/presenter/agentRuntimePresenter/index.ts
Comment thread src/main/presenter/llmProviderPresenter/aiSdk/messageMapper.ts
Comment thread test/main/lib/agentRuntime/backgroundExecSessionManager.test.ts Outdated
Comment thread test/main/lib/agentRuntime/shellEnvHelper.test.ts Outdated
Comment thread test/main/presenter/toolPresenter/agentTools/agentBashHandlerEncoding.test.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/lib/agentRuntime/shellEnvHelper.ts`:
- Around line 318-323: The current selection for variable shell may return
fallbackShells[0] even if resolveShellPath rejected it; update the logic in
shellEnvHelper (around getFallbackShellCandidates and resolveShellPath usage
that computes shell) so that you only accept a candidate if
resolveShellPath(candidate) returns a truthy/valid path, otherwise fall back to
a guaranteed-safe default like '/bin/sh'; in practice remove the unconditional
fallbackShells[0] branch and change the expression to use the first resolved
candidate from fallbackShells (from fallbackShells.map(...).find(Boolean)) or
'/bin/sh' so spawn() won’t receive an unchecked/previously-rejected path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f25af7a0-5a7e-4554-9eb4-6fbf0723d7f6

📥 Commits

Reviewing files that changed from the base of the PR and between 1dbfbc3 and 37f1f2e.

📒 Files selected for processing (18)
  • docs/issues/agent-tool-context-budget/plan.md
  • docs/issues/agent-tool-context-budget/spec.md
  • docs/issues/agent-tool-context-budget/tasks.md
  • docs/issues/background-exec-shell-fallback/plan.md
  • docs/issues/background-exec-shell-fallback/spec.md
  • docs/issues/background-exec-shell-fallback/tasks.md
  • src/main/lib/agentRuntime/shellEnvHelper.ts
  • src/main/presenter/agentRuntimePresenter/contextBudget.ts
  • src/main/presenter/agentRuntimePresenter/index.ts
  • src/main/presenter/llmProviderPresenter/aiSdk/messageMapper.ts
  • test/main/lib/agentRuntime/backgroundExecSessionManager.test.ts
  • test/main/lib/agentRuntime/shellEnvHelper.test.ts
  • test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts
  • test/main/presenter/agentRuntimePresenter/contextBudget.test.ts
  • test/main/presenter/agentRuntimePresenter/toolOutputGuard.test.ts
  • test/main/presenter/llmProviderPresenter/aiSdkMessageMapper.test.ts
  • test/main/presenter/toolPresenter/agentTools/agentBashHandlerEncoding.test.ts
  • test/setup.ts
✅ Files skipped from review due to trivial changes (4)
  • docs/issues/background-exec-shell-fallback/tasks.md
  • docs/issues/agent-tool-context-budget/spec.md
  • docs/issues/background-exec-shell-fallback/plan.md
  • docs/issues/agent-tool-context-budget/tasks.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • test/main/presenter/agentRuntimePresenter/toolOutputGuard.test.ts
  • test/main/presenter/agentRuntimePresenter/contextBudget.test.ts
  • test/main/presenter/toolPresenter/agentTools/agentBashHandlerEncoding.test.ts
  • src/main/presenter/agentRuntimePresenter/contextBudget.ts
  • src/main/presenter/llmProviderPresenter/aiSdk/messageMapper.ts
  • docs/issues/agent-tool-context-budget/plan.md
  • test/main/lib/agentRuntime/backgroundExecSessionManager.test.ts
  • test/main/presenter/llmProviderPresenter/aiSdkMessageMapper.test.ts
  • src/main/presenter/agentRuntimePresenter/index.ts
  • test/main/presenter/agentRuntimePresenter/agentRuntimePresenter.test.ts

Comment thread src/main/lib/agentRuntime/shellEnvHelper.ts
@zerob13 zerob13 merged commit 2f1458c into dev May 9, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant