Support Perplexity web search via session cookies extracted from
the desktop app (Electron/AppImage). Accepts either a raw cookie
string or a bare session token value.
Auth resolution order: PERPLEXITY_COOKIES > agent.db OAuth > PERPLEXITY_API_KEY
The #getGitStatus() method fires an async `git status --porcelain` but
only updates the TTL timestamp after the async completes. In large repos
where git status takes seconds, every TUI render cycle during that window
spawns a new process — cascading into hundreds of concurrent git processes,
exhausting file descriptors and creating .git/index.lock contention.
Add a #gitStatusInFlight boolean guard that ensures exactly one git status
process runs at a time. Use a finally block to unconditionally reset the
guard and refresh the TTL to completion time.
Request contents.summary from Exa /search API and combine up to
3 non-empty summaries into SearchResponse.answer, replacing the
previous 'No answer text returned' output.
Changes:
- Add contents.summary to every Exa search request body
- Add summary field to ExaSearchResult interface
- Export synthesizeAnswer() to build combined answer from summaries
- Export buildExaRequestBody() for testability
- Export normalizeSearchType() (was private)
- Prefer summary over text/highlights for snippet field (falsy check)
- Only synthesize answer from results that have a URL (mirrors sources filter)
Tests: 37 new tests covering normalizeSearchType, buildExaRequestBody,
synthesizeAnswer (pure unit tests) and searchExa (mocked fetch)
The disabledServers mechanism introduced in 4bfa3b0c (Merge branch
'pr-82', 2026-02-16) was defeated by a level guard added after merge:
servers with _source.level === "user" were exempt from the disabled
check. This meant servers discovered from ~/.claude.json (which the
Claude provider tags as level "user") were never actually filtered out,
even when present in disabledServers.
Remove the level guard so disabledServers applies unconditionally. This
is safe because the /mcp disable command already uses updateMCPServer
(setting enabled: false inline) for servers defined in omp own configs;
the disabledServers path only fires for third-party discovered servers.
Also fix the /mcp list display to cross-filter discovered servers
against the disabled list, preventing a server from appearing both as
"connected" and "disabled" when the MCP manager has stale state.
The submit_result tool marked 'data' as optional (Type.Optional) in its
JSON schema, but runtime validation requires it when status is 'success'.
Models that follow the schema literally omit the data parameter, causing
every call to fail with 'data is required when status is success (got
null/undefined)'. The agent then retries indefinitely.
Change data from Type.Optional(dataSchema) to dataSchema so the schema
matches runtime requirements. Models are now forced to include the data
object when calling submit_result with success, resolving the loop.
For aborted status, models can pass data: {}; validation is skipped.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Added a shared CHANGELOG_CATEGORIES constant and reused it across changelog tooling.
- Validated changelog entry and deletion payloads to require arrays and flag invalid categories.
- Exported the changelog tool schema for generator validation and referenced it during parsing.
Co-authored-by: Can Bölük <can1357@users.noreply.github.com>
- Guard Container.render() to ensure width >= 1 for ALL components
- Guard TwoColumnBody.rightWidth with Math.max(0, ...)
- Validate description type before passing to native function
- Fall back to truncation when width <= 2
Fixes crash when opening extensions in narrow terminals.
The Exa MCP endpoint at https://mcp.exa.ai/mcp is publicly accessible
without an API key. Previously, all Exa tools bailed early with
'EXA_API_KEY not found' when no key was set, even though the basic
tools (web_search_exa, company_research_exa, get_code_context_exa)
work fine unauthenticated.
Changes:
- fetchExaTools/callExaTool: accept null apiKey, omit exaApiKey param
- MCPWrappedTool.execute: only require key for Websets tools
- createExaTool (factory): remove early-return guard on missing key
Fixes#151
The non-interactive environment variables (pager, editor, and prompt
suppression) were only applied in the PTY (interactive) bash path.
The non-PTY executeBash path had no such defaults, so commands could
block on pagers or credential prompts.
- Extract NO_PAGER_ENV from bash-interactive.ts into shared
non-interactive-env.ts module (renamed to NON_INTERACTIVE_ENV)
- Apply it in executeBash (non-PTY) with user overrides taking
precedence
- Fix ordering in interactive PTY path so user env overrides
the defaults (was reversed)
- Add test verifying defaults are applied alongside user env
- Standardized prompt format, removing XML-like tags.
- Emphasized RFC 2119 keywords using bolding across all prompts.
- Rewrote the core system prompt with enhanced structure and directives.
- Consolidated @oh-my-pi/pi-utils subpath imports into single package root import across 100+ files.
- Moved tryParseJson utility from local web scrapers module to @oh-my-pi/pi-utils package for centralized JSON parsing.
- Renamed loadSkillsFromDir to scanSkillsFromDir and refactored skill discovery to use fs.promises.readdir instead of glob-based approach.
- Replaced custom parseJSON with tryParseJson across discovery modules for consistent error handling.
- Removed emitCustomToolSessionEvent method and cleanupSshResources function, consolidating shutdown logic into dispose method.
- Updated glob pattern construction to use GlobBuilder with literal_separator(true) for improved path handling.
- Added fallback empty string for undefined tool descriptions across all AI providers to prevent runtime errors.
- Made `description` field required in CustomTool interface and normalized tool descriptions in agent-loop.
- Refactored tool normalization in agent-loop by renaming `injectIntentIntoTools()` to `normalizeTools()` with conditional intent injection.
- Added comprehensive description metadata to todo-write tool schema fields for improved clarity.
- Replaced hardcoded tab width update with a generic mechanism to trigger all registered setting hooks.
- Ensures all settings with associated hooks are properly configured during initialization.
- Introduced mechanism to suppress future delivery attempts for acknowledged jobs.
- The AwaitTool acknowledges deliveries for jobs it finished awaiting.
- Prevents unnecessary retries and clears delivery queue for handled jobs.
- Added a `display.tabWidth` setting to control default tab rendering.
- Integrated `.editorconfig` resolution for c ontext-aware tab indentation.
- Updated native text processing to use the resolved tab width for calculations.
- Improved tab visualization in diffs and other UI elements for consistency.
The {{#if contextFiles.length}} block (line 10) was never closed with
{{/if}}, causing Handlebars to throw 'if doesn't match ifAny' at
runtime when the template is compiled.
Additionally, the git version control section (git.isRepo guard with
branch, status, and commit history) was missing entirely, even though
the outer {{#ifAny contextFiles.length git.isRepo}} guard references it.
This fix:
- Adds the missing {{/if}} to close the contextFiles.length block
- Restores the git.isRepo conditional section for version control info
- Ensures </project> and {{/ifAny}} are properly positioned
- Removed `timeout` parameter from await tool--tool now waits indefinitely until jobs complete or the call is aborted.
- Renamed `job_ids` parameter to `jobs` in await tool schema.
- Removed `timedOut` field from await tool result details.
- Simplified timeout logic by eliminating sleep-based polling in favor of indefinite waiting.
- Restructured task tool documentation for clarity, moving parameter definitions into a dedicated section and consolidating guidance on context, assignments, and parallelization.
- Reformatted system prompt template to use markdown headings instead of XML tags for skills, preloaded skills, and rules sections.
Move agent__intent description from per-tool JSON schema injection (repeated
once per tool) into a single conditional block in the system prompt template.
Token savings scale with tool count.
- agent-loop: remove description from intent schema injection
- system-prompt.md: add intentTracing/intentField template block
- sdk: compute intentField once, pass through to prompt builder
- system-prompt.ts: thread intentField option into template context
- Renamed `file` parameter to `path` in edit tool schemas and all test cases for consistency.
- Removed `file` property fallback from EditRenderArgs interface and path resolution methods.
- Refactored browser viewport schema to use snake_case `device_scale_factor` with explicit camelCase mapping for Puppeteer API.
- Added hashline edit rule requiring `end` tag to include closing braces/brackets when replacing blocks.
- Refactored INTENT_FIELD injection logic to conditionally reorder schema properties and update required array.
- Added getTodoPhases() and setTodoPhases() methods to ToolSession API for in-memory todo phase management.
- Added getLatestTodoPhasesFromEntries() export to retrieve todo phases from session history entries.
- Changed todo state management from file-based (todos.json) to in-memory session cache with automatic persistence.
- Changed todo phases to sync from session branch history during branching and rewriting operations.
- Removed file-based todo loading logic and replaced with session-based todo phase retrieval throughout codebase.
- Removed try-catch wrapper around URL resolution in expandInternalUrls, allowing errors to propagate to caller.
- Simplified template formatting in subagent-user-prompt.md by collapsing context block to single line.