- Imported buildSessionContext from session-manager module.
- Replaced inline session context object with buildSessionContext factory call in test assertion.
The manual /compact command path in executeCompaction() called
rebuildChatFromMessages() but never added the compactionSummary
message to the chat. The auto-compaction path in event-controller
explicitly adds it, but the manual path was missing this step.
Capture the CompactionResult returned by session.compact() and
render the summary via addMessageToChat(), matching the behavior
of the auto-compaction flow.
Co-authored-by: Miroslav Drbal <miroslav.drbal@gendigital.com>
- Reapplied #applyHardcodedModelPolicies method to enforce gpt-5.4 context window policy across all model loading paths.
- Updated model registry to apply hardcoded policies after both initial load and dynamic discovery.
- Adjusted test expectations to reflect the restored policy enforcement behavior.
- Extracted service tier validation into dedicated `isSpecialServiceTier()` type guard function.
- Replaced inline undefined checks with centralized validation across four provider modules.
- Consolidated service tier logic to improve maintainability and reduce duplication.
When a user switches to a model that doesn't support thinking (e.g.,
Gemini 1.5 Flash) while a thinking level is still set from a previous
model, the app crashes with an uncaught exception from
requireSupportedEffort().
Add model.reasoning guards in mapOptionsForApi() for all provider paths
(Anthropic, Google, Google Gemini CLI, Google Vertex, OpenAI) so that
thinking is gracefully disabled instead of throwing. The Bedrock path
already had this guard.
- Added Tavily web search provider with API key authentication and credential discovery from environment or database.
- Integrated Tavily as highest-priority search provider in fallback chain with structured response mapping and error handling.
- Added Tavily OAuth login flow in CLI and auth-storage with manual API key input and validation.
- Added comprehensive test suite for Tavily provider covering registration, response mapping, error handling, and credential validation.
Fixes#313
- Removed Kagi Universal Summarizer integration from fetch tool and YouTube scraper.
- Removed `fetch.useKagiSummarizer` configuration setting from settings schema.
- Simplified renderHtmlToText() and renderUrl() functions by removing Kagi summarization fallback logic.
- Fixed indentation inconsistencies in test files from tabs to spaces.
findAnthropicAuth() only checked OAuth credentials in agent.db, missing
api_key type credentials entirely. Users authenticated via stored API key
(not env var, not OAuth) got null from isAvailable(), causing the provider
chain to skip Anthropic.
Added tier 4 (api_key in agent.db) between OAuth check and env var
fallback. Refactored store lifecycle so tiers 3-4 share one instance.
Also fixed ExaProvider.isAvailable() which unconditionally returned true,
ignoring exa.enabled/exa.enableSearch settings and never checking for
credentials. It now respects both settings and requires an actual API key.
* fix(session): bypass user-prompt pipeline in handoff
handoff() was calling #promptWithMessage, which gates on an API key
check before reaching this.agent.prompt(). That gate is appropriate for
user-facing prompts but has no place in an internal document-generation
call: it blocked the test spy on agent.prompt and required callers to
carry real credentials just to run the handoff path.
Fix: call #promptAgentWithIdleRetry directly (preserving the
busy-wait behaviour and #promptInFlightCount tracking) and skip the
user-prompt pipeline (API key validation, bash/python flushes, file
mention expansion, plan messages, extension events) entirely. handoff
creates a fresh session immediately after, so none of that setup
applies.
Tests now reach agent.prompt with no stub on modelRegistry.getApiKey.
* fix(patch): HASHLINE_PREFIX_RE strips comment lines with word: pattern
The regex used [0-9a-zA-Z]{1,16} for the hash ID segment, which matched
common comment patterns like '# Note:', '# TODO:', '# FIXME:'. When a
single-line replacement contained such a comment, nonEmpty===1 and
hashPrefixCount===1, triggering stripping and eating the comment prefix.
Actual hashline IDs are always exactly 2 chars from ZPMQVRWSNKTXJBYH.
Constrain the regex to that exact alphabet so no English word can match.
Also update tests that used fake IDs (AB, CD, EF) not in the real alphabet.
* Revert "fix(patch): HASHLINE_PREFIX_RE strips comment lines with word: pattern"
This reverts commit 112ad083de956d4ed8b78a7e6e9af2c061befbd5.
---------
Co-authored-by: Miroslav Drbal <miroslav.drbal@gendigital.com>
- Added idle-iterator utility to enforce maximum gaps between stream events, preventing indefinite hangs when providers stop sending mid-response. Applied watchdog to OpenAI completions, responses, Azure OpenAI responses, and Codex SSE streams with configurable timeout via PI_OPENAI_STREAM_IDLE_TIMEOUT_MS.
- Added separate first-event timeout for Codex websocket connections to quickly fall back to SSE when a prewarmed socket connects but never starts the response stream. Codex websocket now validates auth headers match before reusing connections.
- Fixed OAuth token refresh and usage lookups to respect request timeouts instead of waiting indefinitely during credential selection or rotation.
- Resolved symlinked paths before passing to brush shell to keep `pwd` output aligned with canonical Git worktree paths.
- Added `resolveShellCwd` helper that safely resolves symlinks and falls back to original path on error.
- Added test case verifying symlinked directories are canonicalized before execution.
* fix: strip invalid thinking signatures from aborted/errored messages
When a stream is interrupted mid-response, thinking blocks may have
empty or partial cryptographic signatures. These get persisted to
session history and sent on the next API call, causing:
'Invalid signature in thinking block'
transformMessages() now detects aborted/errored assistant messages and
clears thinkingSignature fields so they are treated as unsigned thinking
(converted to text by the serializer).
Also protect truncateForPersistence from corrupting signatures — clear
them entirely instead of truncating, since a partial signature is always
invalid.
* fix: disable thinking when tool_choice forces tool use on Bedrock
Bedrock rejects requests that combine extended thinking with forced
tool_choice (any or specific tool). The Anthropic provider already had
a guard (disableThinkingIfToolChoiceForced) but the Bedrock provider
was missing the equivalent check.
Also fix thinking block serialization: when a thinking block has no
valid signature (e.g., from an aborted stream), convert it to plain
text instead of sending it as reasoningContent without a signature.
The API requires the signature field on all reasoning blocks for models
that support it.
Add thinking block diagnostics to error messages for signature/thinking
related failures to aid debugging.
* fix(patch): HASHLINE_PREFIX_RE strips comment lines with word: pattern
The regex used [0-9a-zA-Z]{1,16} for the hash ID segment, which matched
common comment patterns like '# Note:', '# TODO:', '# FIXME:'. When a
single-line replacement contained such a comment, nonEmpty===1 and
hashPrefixCount===1, triggering stripping and eating the comment prefix.
Actual hashline IDs are always exactly 2 chars from ZPMQVRWSNKTXJBYH.
Constrain the regex to that exact alphabet so no English word can match.
Also update tests that used fake IDs (AB, CD, EF) not in the real alphabet.
* test(patch): add regression tests for comment line prefix stripping bug
Three new tests in hashlineParseContent describe block:
- hashlineParseText preserves '# Word:' comment lines (unit)
- full pipeline: replacing '# Note:' comment line preserves prefix
- full pipeline: replacing '# TODO:' comment line preserves prefix
These would have caught the HASHLINE_PREFIX_RE bug where [0-9a-zA-Z]{1,16}
matched comment words, causing stripNewLinePrefixes to eat the '# Note:'
prefix when a single comment line was the sole replacement entry.
---------
Co-authored-by: Miroslav Drbal <miroslav.drbal@gendigital.com>
#applyHardcodedModelPolicies forced contextWindow=1_000_000 on any model
with id 'gpt-5.4' regardless of provider. This was wrong in every case:
- github-copilot/gpt-5.4: inflated from bundled 400K (and overwrote the
~274K that Copilot's live /models discovery returns via capabilities.limits)
- openai/gpt-5.4: deflated from bundled 1_050_000 to 1_000_000
- openai-codex, opencode, opencode-zen: same downgrade from 1_050_000
The method was called twice — after static load and after runtime discovery —
so it reliably clobbered the correct provider-specific value both times.
No documented rationale exists for the override. The bundled models.json
values are already correct. Remove the method and its two call sites.
fixes#332
Co-authored-by: Miroslav Drbal <miroslav.drbal@gendigital.com>
- Deleted tsconfig.build.json and tsconfig.check.json files that only extended the base tsconfig.json without adding configuration.
- Updated check:ts script to remove the unused packages/stats/tsconfig.client.json reference.
- Replaced vi.spyOn fetch mocking pattern with hookFetch utility across 5 test files.
- Migrated fetch mocks to use resource management with 'using' keyword for automatic cleanup.
- Removed @ts-expect-error comments related to fetch.preconnect type issues.
- Updated ts-hook-fetch rule documentation with expanded patterns and clearer lifecycle guidance.
- Fixed API key credential matching to compare by type and identity key instead of object identity.
- Fixed credential deduplication to preserve existing credentials when replacement key matches.
- Fixed auth schema version preservation by conditionally writing schema version only on initial setup.
- Added test coverage for API key reuse, email deduplication, and schema version preservation.
- Extracted fetch mocking logic into reusable `hookFetch()` utility function with middleware-style handler pattern.
- Replaced manual `globalThis.fetch` assignment and restoration across 10 test files with `hookFetch()` calls using `using` statement for automatic cleanup.
- Implemented Disposable pattern with Symbol.dispose for fetch hook resource management, eliminating try-finally blocks.
- Exported `hookFetch` from utils public API to enable consistent fetch mocking across packages.
- Added `identity_key` column to auth credentials storage for improved credential deduplication.
- Added schema versioning system to auth credentials database for safer migrations.
- Changed credential deduplication logic to use single identity key instead of multiple identifiers for better performance.
- Fixed credential deduplication to correctly handle OAuth accounts with matching emails but different account IDs.
- Implemented automatic backfilling of identity keys during database schema migrations.
- Added 5 test cases for schema migration and identity key backfilling scenarios.
- Extracted OAuth identifier logic into public functions extractOAuthCredentialIdentifiers and extractOAuthTokenIdentifiers.
- Replaced single credentialIdentity string with multi-identifier resolveCredentialIdentifiers returning string[] for flexible matching.
- Changed credential deduplication from email-based to accountId-based matching in replaceAuthCredentialsForProvider.
- Updated auth-storage tests to verify accountId-prioritized deduplication behavior across soft-disable and hard-delete scenarios.
- Added documentation comments in coding-agent modules explaining partial JSON preservation for streaming tool previews.
- Documented streaming tool preview requirements and render paths in AGENTS.md.
- Added `env` parameter to bash tool for safe environment variable passing without shell re-parsing.
- Added support for rendering partial environment variable assignments in command preview during streaming.
- Updated bash tool prompt to recommend `env` parameter for multiline, quote-heavy, and untrusted values.
- Refactored tool execution component to conditionally merge partial JSON arguments during streaming.
- Added helper functions for environment variable normalization, escaping, and formatting.
- Fixed WebSocket stream fallback logic to safely replay buffered output over SSE when WebSocket fails after partial content has been streamed.
- Added tracking flag to prevent unsafe replays of tool calls and terminal events during fallback transitions.
- Enhanced error recovery to reset output state when replaying buffered content over SSE connection.
- Added docs.rs scraper for extracting Rust crate documentation from rustdoc JSON, supporting modules, functions, structs, traits, enums, and other Rust items with intelligent caching.
- Implemented rustdoc JSON parsing with type rendering for complex Rust types including generics, lifetimes, trait bounds, and qualified paths.
- Added caching layer for rustdoc JSON with date-based versioning for 'latest' releases to reduce repeated fetches.