- Deleted the 'Bad' example that incorrectly demonstrated guessing indentation as spaces instead of using actual tabs.
- Kept the 'Good' example that correctly shows using real tab characters in JSON to match file indentation.
- Fixed tool argument coercion to handle malformed JSON with trailing wrapper braces by parsing leading JSON containers.
- Relaxed JSON detection heuristics to check only leading braces instead of requiring matching closing braces.
- Added test case for array strings with trailing wrapper braces from nested JSON malformation.
- Added auto-inclusion of ast_grep and ast_edit tools when their text-based counterparts are enabled.
- Made AST tool recommendations conditional in bash tool prompt based on session availability.
- Replaced timeout-based cancellation with AbortSignal-based cancellation in ask tool.
- Added auto-correction of escaped tab indentation in edits via PI_HASHLINE_AUTOCORRECT_ESCAPED_TABS environment variable.
- Added warning detection for suspicious Unicode escape placeholder in edit content.
- Corrected variable initialization order in formatPromptContent to prevent stale trimmedStart value when ASCII symbol replacement occurs.
- Updated bash.md documentation to use ellipsis (...) instead of three dots (...) for consistency with ASCII symbol replacement behavior.
- Added test case verifying table row compaction and content deduplication during pre-render with ASCII symbol replacement.
- Replaced timeout-based cancellation with AbortSignal-based cancellation in ask tool for unified abort handling.
- Added signal parameter to UIContext select() and input() methods to support external cancellation propagation.
- Fixed race condition in dialog overlay handling by replacing direct resolve() calls with settled flag wrapper.
- Added comprehensive test coverage for ask tool cancellation behavior across user-initiated and timeout scenarios.
- Enforced tool decision in plan mode--agent now requires calling either `ask` or `exit_plan_mode` when a turn ends without a required tool call.
- Fixed cancellation behavior of `ask` tool to abort the current turn instead of returning a normal cancelled selection, while timeout-driven auto-cancel still returns without aborting.
- Added plan-mode-tool-decision-reminder system prompt to guide agent when required tools are not called.
- Improved agent_end event handling to use fallback assistant message when #lastAssistantMessage is unavailable.
- Added auto-correction for escaped tab indentation in edits via PI_HASHLINE_AUTOCORRECT_ESCAPED_TABS environment variable.
- Added warning detection for suspicious Unicode escape placeholder \uDDDD in edit content.
- Clarified hashline documentation that \t in JSON represents real tab characters, not literal backslash-t strings.
- Added comprehensive test coverage for escaped tab auto-correction and Unicode escape detection.
- Clarified plan mode constraints and tool invocation patterns with template variables for dynamic tool names.
- Improved readability of critical directives by converting bullet points to parallel grammatical structure.
- Replaced hardcoded tool names (ask, exit_plan_mode) with template variables ({{askToolName}}, {{exitToolName}}) for flexibility.
- Added five new test cases covering viewport consistency across repeated width reflows, aggressive resize storms, height-only resizes with cursor drift, streaming content under resize, and forced renders with cursor relocation.
- Added test for scrollback boundedness during mixed width/height resize storms on static content.
- Added test for scrollback integrity when appending lines during aggressive resize operations.
- Added checkpoint and rewind tools to create context checkpoints before exploratory work and rewind to replace exploration messages with concise reports.
- Added checkpoint.enabled setting to control availability of checkpoint and rewind tools in agent sessions.
- Added getCheckpointState() and setCheckpointState() methods to agent session API for checkpoint state management.
- Implemented checkpoint state tracking with message count, entry ID, and timestamp to enable context cost optimization during investigations.
- Added render_mermaid tool to convert Mermaid diagrams to ASCII output with configurable rendering options.
- Added renderMermaid.enabled setting to control availability of the render_mermaid tool.
- Changed Mermaid rendering from PNG terminal graphics to ASCII text format across theme and TUI components.
- Migrated mermaid utilities from pi-tui to pi-utils package with new ASCII rendering functions.
- Removed PNG-based mermaid rendering APIs (getMermaidImage, renderMermaidToPng) in favor of ASCII alternatives.
- Added transcript export option to debug menu that writes visible TUI conversation to a temporary text file.
- Implemented handleDebugTranscriptCommand in CommandController to render chat messages, strip ANSI codes, and write to temp directory.
- Updated InteractiveModeContext interface and InteractiveMode to expose the new debug transcript command handler.
- Added `hlinejsonref` Handlebars helper for embedding hashline references inside JSON blocks in prompts.
- Enhanced hashline tool documentation with explicit prohibition on formatting-only edits.
- Added mandatory rule requiring indentation in `lines` to match surrounding context exactly from `read` output.
- Updated all hashline examples to use `hlinejsonref` instead of `hlineref` for JSON-safe references.
- Added librarian and oracle agents for library research and technical diagnostics.
- Expanded tool support across explore, plan, and reviewer agents with lsp, fetch, web_search, and ast_grep.
- Added dependencies and risks output fields to explore agent for enhanced analysis.
- Renamed explore agent output field from query to summary with expanded description.
- Simplified ast-edit tool documentation by removing preview/resolve workflow references and clarifying output behavior.
- Added critical guidance to hashline tool documentation on proper line deletion to prevent duplication.
PendingAction.apply() and reject() now receive the reason string that was
passed to resolve(). This lets custom tools surface the agent's rationale
in their apply/discard output or use it for logging.
- PendingAction interface: apply(reason) and reject?(reason)
- CustomToolPendingAction: same signatures, reject is optional
- CustomToolLoader: threads reject through when building PendingAction
- AstEditTool: accepts _reason (unused, reserved for future tracing)
- resolve.test: covers reason forwarding on apply and reject paths,
and verifies reject return value replaces the default discard message
- docs/resolve-tool-runtime.md: updated interface table, built-in
producer description, usage example, and developer guidance
- Introduce `deferrable?: boolean` on AgentTool, CustomTool, and ToolDefinition.
AstEditTool sets it to true; resolve is now injected only when at least one
active tool is deferrable (previously unconditional).
- Replace single-slot PendingActionStore (set/get/clear) with a LIFO stack
(push/peek/pop/clear). Multiple deferrable tools can stage independent
preview actions; resolve always consumes the topmost one first.
- Wire pendingActionStore through discoverAndLoadCustomTools / loadCustomTools /
CustomToolLoader so custom tools can call pushPendingAction(action) to
register a resolve-compatible pending action with label, apply callback,
optional details, and optional sourceToolName.
- Export HIDDEN_TOOLS and ResolveTool from the SDK for manual tool composition.
- Add CustomToolPendingAction type and pushPendingAction to CustomToolAPI.
- Update createAgentSession to re-inject or remove resolve after the deferrable
audit, consistent with createTools behavior.
- Add LIFO resolve test, update existing tests (set -> push, get -> peek).
- Add docs/resolve-tool-runtime.md covering PendingActionStore internals,
built-in producer example, and custom tool usage guide.
- Simplified resolve tool output rendering from boxed layout to inline highlighted format for cleaner display.
- Updated resolve tool to parse source tool name from label using colon separator instead of separate metadata field.
- Replaced CachedOutputBlock with direct line rendering using padToWidth and inverse styling.
- Removed actionBadge helper function and consolidated action/state logic into single rendering path.
- Updated 1 test to verify inline rendering behavior and label parsing.
- ast_grep: drop include_meta (always true now); update prompt examples
- ast_edit: dry_run -> preview; max_replacements -> limit; max_files
removed from schema, always read from $PI_MAX_AST_FILES (default 1000)
- cap parse errors at PARSE_ERRORS_LIMIT (20) in both text output and
TUI; show 'N / total' summary when over limit
- export PARSE_ERRORS_LIMIT from render-utils, no hardcoded magic numbers
- Removed normativeRewrite setting and buildNormativeUpdateInput() function from public API.
- Removed $normative property and TNormative generic parameter from ToolResultMessage and AgentToolResult interfaces.
- Deleted normative.ts patch normalization module and related helper functions for diff anchor processing.
- Removed rewriteAssistantToolCallArgs() and #rewriteToolCallArgs() methods that modified tool call arguments.
Remove the non-empty validation on `out` in both the TypeScript tool
layer and the Rust native layer. An empty replacement template now
deletes the matched AST node, which is the natural semantics.
Also remove .trim() calls on pattern and rewrite strings — trimming
silently destroys intentional whitespace and serves no purpose since
ast-grep matches on AST structure, not raw text.
providers/google-gemini-cli:
- parseGeminiCliCredentials() handles legacy, alias (project_id/refresh/expires),
and enriched credential JSON formats
- shouldRefreshGeminiCliCredentials() + refreshGeminiCliCredentialsIfNeeded()
proactively refresh OAuth tokens 60s before expiry for both providers
- normalizeAntigravityTools() converts parametersJsonSchema -> parameters
in function declarations for Antigravity compatibility
- VALIDATED tool calling config applied for Antigravity + Claude model combos
- maxOutputTokens removed from generation config for Antigravity non-Claude models
- Antigravity system instruction injection scoped to Claude + gemini-3-pro-high models
- Antigravity session ID: signed decimal int63 derived from SHA-256 of first user
message (or random bounded int63), replacing truncated hex hash
- Antigravity requestId uses agent-{uuid}; non-Antigravity requests omit
requestId/userAgent/requestType from payload
- ANTIGRAVITY_DAILY_ENDPOINT corrected to daily-cloudcode-pa.googleapis.com;
sandbox kept as fallback
- ANTIGRAVITY_SYSTEM_INSTRUCTION exported
oauth/google-antigravity:
- PKCE removed from OAuth flow (no code_challenge)
- loadCodeAssist metadata ideType changed to ANTIGRAVITY
- discoverProject uses single production endpoint; falls back to onboardUser LRO
(up to 5 retries, 2s interval) instead of hardcoded default project ID
- ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA exported
oauth/google-gemini-cli:
- PKCE removed from OAuth flow
oauth/index:
- getOAuthApiKey includes refreshToken, expiresAt, email, accountId in
Gemini/Antigravity JSON payload for proactive refresh support
discovery/antigravity:
- Tries production daily endpoint first, sandbox as fallback
- Removed recommended/agentModelSorts filter; applies denylist instead
- ANTIGRAVITY_DISCOVERY_DENYLIST filters low-quality/internal models
- Request body no longer includes project field
coding-agent:
- gemini_image: corrected responseModalities to uppercase IMAGE/TEXT
- Gemini web search: endpoint fallback (daily->sandbox) with retry on 429/5xx,
aligned Antigravity request metadata, ANTIGRAVITY_SYSTEM_INSTRUCTION injection
- buildGeminiRequestTools() helper for composable googleSearch/codeExecution/urlContext
- Web search schema: expose max_tokens, temperature, num_search_results params
- Web search: explicit provider falls back to auto chain when unavailable
Tests: google-antigravity-auth, google-gemini-cli-alignment, web-search-gemini
providers/anthropic:
- Bump claudeCodeVersion to 2.1.63; system instruction identifies as Claude Agent SDK
- X-Stainless-Os and X-Stainless-Arch now runtime-computed via mapStainlessOs/mapStainlessArch
- Remove X-Stainless-Helper-Method; update package version to 0.74.0, runtime to v24.3.0
- Remove fine-grained-tool-streaming-2025-05-14 from default beta set; add
context-management-2025-06-27 and prompt-caching-scope-2026-01-05
- Accept-Encoding updated to 'gzip, deflate, br, zstd'
- Inject x-anthropic-billing-header block (SHA-256 payload fingerprint) and
Claude Agent SDK identity block with ephemeral 1h cache-control for OAuth requests
- Auto-generate cloaking user IDs for OAuth metadata.user_id when absent/invalid
- applyClaudeToolPrefix / stripClaudeToolPrefix skip Anthropic built-in tool names
- buildClaudeCodeTlsFetchOptions attaches SNI + default TLS ciphers for api.anthropic.com
- Non-Anthropic base URLs now use Bearer auth regardless of OAuth status
- Prompt-caching no longer strips then re-applies; skips if blocks already have cache_control
oauth/anthropic:
- Token URL changed from platform.claude.com to api.anthropic.com
- OAuth scopes trimmed to org:create_api_key user:profile user:inference
- Code exchange strips URL fragment from callback code (fragment used as state override)
- AnthropicOAuthFlow exported
- OAuth callback server timeout extended from 2 min to 5 min
usage/claude:
- user-agent updated to claude-cli/2.1.63 (external, cli)
- anthropic-beta header extended with full production beta set
coding-agent web search:
- Anthropic provider uses buildAnthropicSearchHeaders instead of buildAnthropicHeaders
Tests: anthropic-alignment, anthropic-oauth, claude-usage-headers, web-search-anthropic
BREAKING CHANGE: ast_find parameter 'pattern' (string) is replaced by
'patterns' (string[]). ast_replace parameters 'pattern' + 'rewrite' are
replaced by 'ops: Array<{ pat: string; out: string }>'.
Native (pi-natives / crates/pi-natives):
- astFind accepts patterns[] array; all patterns run per file, results
merged and sorted by path/line/column before offset+limit are applied
- astReplace accepts rewrites Record<string,string>; all patterns compiled
once upfront and applied per file in a single pass
- Deterministic result ordering via BTreeSet/BTreeMap
Coding-agent tools:
- ast_find: multi-pattern deduplication, '>>' prefix on match-start lines,
padded line numbers, directory-tree grouping (# dir / ## └─ file headers),
scopePath/files/fileMatches in tool details
- ast_replace: ops[] interface with duplicate-pattern rejection, diff-style
(-before/+after) previews grouped by directory, parse errors shown on
zero-replacement path, fileReplacements in tool details
- Tool prompts updated to document new interfaces with multi-pattern examples
- Task item id maxLength raised from 32 to 48 characters
- Added ast_replace tool test suite
- Add hasUnrepresentableStrictObjectMap() pre-flight in tryEnforceStrictSchema;
schemas using patternProperties or schema-valued additionalProperties now
degrade to non-strict mode instead of throwing during enforcement
- Add test coverage for patternProperties graceful degradation
- Extend ANTHROPIC_OAUTH_BETA in openai-compat route with
interleaved-thinking-2025-05-14, context-management-2025-06-27, and
prompt-caching-scope-2026-01-05 beta flags
- Add gemini, codex, and synthetic to providers.webSearch setting enum
/plan without args still toggles plan mode on/off as before.
/plan <prompt> enters plan mode and immediately starts planning
with the given prompt, skipping the extra enter-then-type step.
## New: schema compatibility validation API
Add `validateSchemaCompatibility(schema, provider)` in
`packages/ai/src/utils/schema/compatibility.ts` that performs a static
audit of a JSON Schema against three provider targets:
- `openai-strict`: checks forbidden keys, required/properties symmetry,
additionalProperties constraint, and that every node declares a type,
combinator, or $ref
- `google`: checks unsupported keyword set and array-valued type
- `cloud-code-assist-claude`: checks forbidden keywords, array type,
null type, nullable keyword, and combiner presence; also validates via
AJV 2020 draft
Add `validateStrictSchemaEnforcement(original, result)` to assert the
fail-open contract: when strict enforcement succeeds the output must pass
openai-strict validation; when it fails the output must be the original
schema object (same reference).
Export both functions and their types from `./utils/schema/index.ts`.
## New: shared constants in fields.ts
Extract `COMBINATOR_KEYS` (`anyOf`, `allOf`, `oneOf`) and add
`CCA_UNSUPPORTED_SCHEMA_FIELDS` as exported constants, eliminating the
local duplicate in `strict-mode.ts` and providing a canonical field set
for Cloud Code Assist (much narrower than the Google set — CCA supports
validation keywords like `additionalProperties`, `minLength`,
`pattern`, etc.).
## Fix: cycle detection in all recursive schema traversals
All recursive walkers now carry a `WeakSet<object>` guard. Previously any
schema with a reference cycle (or a schema object that appears at two
nodes in the tree) would cause an infinite loop or a stack overflow:
- `sanitizeSchemaForStrictMode` / `enforceStrictSchema`
- `normalizeSchemaForCloudCodeAssistClaude`
- `normalizeNullablePropertiesForCloudCodeAssist`
- `stripResidualCombiners`
- `sanitizeSchemaImpl` (Google sanitizer)
- `hasResidualCloudCodeAssistIncompatibilities`
`hasResidualCloudCodeAssistIncompatibilities` previously returned `true`
for already-visited nodes, producing false positives that forced the CCA
fallback schema on valid (but multiply-referenced) schemas. It now
correctly returns `false`.
## Fix: stripResidualCombiners iterates to fixpoint
The previous single-pass approach missed chained combiner reductions
where one collapsed variant exposed another reducible combiner. The
rewriter now loops until no further reduction occurs.
## Fix: mergeObjectCombinerVariants required-field computation
The merged object schema now takes the intersection of all variants'
`required` arrays, then unions in own-level required properties that
exist in the merged schema. Previously the `required` field was silently
dropped from the flattened schema, making all properties effectively
optional.
## Fix: sanitizeSchemaForGoogle improvements
- Type inference for const-collapsed enums: type is derived from all
variants (must unanimously agree), falling back to inference from enum
values; mixed null/non-null infers the non-null scalar type and sets
`nullable: true`
- Const→enum deduplication now uses deep structural equality instead of
`Object.is`
- Recursion spreads the full options object so new fields (`unsupportedFields`,
`seen`) are not silently dropped when descending into sub-schemas
- Array-valued `type` is filtered to strings before processing
- Removed incorrect stripping of `additionalProperties: false` (the
field is valid and should be preserved)
- Parameterized `unsupportedFields` in `SanitizeSchemaOptions` enables
code reuse between the Google and CCA sanitizers
## Fix: sanitizeSchemaForStrictMode / enforceStrictSchema
- `nullable: true` is now stripped during sanitization and expanded into
`anyOf: [schema, {type: "null"}]` in the enforcer output, matching
what OpenAI strict mode requires
- Type inference: `type: "array"` is inferred when `items` is present;
a scalar type is inferred from uniform `enum` values
- Const→enum merge uses deep equality to avoid duplicate entries when
both `const` and `enum` exist with the same value
- `additionalProperties` is now dropped unconditionally in sanitization
(previously only object-valued `additionalProperties` was recursed;
non-object values were passed through)
- `enforceStrictSchema` recurses into `$defs` and `definitions` blocks
- `enforceStrictSchema` handles tuple-style `items` arrays
- `enforceStrictSchema` skips double-wrapping: optional properties
already expressed as `anyOf: [..., {type: "null"}]` are not wrapped again
- `tryEnforceStrictSchema` now caches results in a `WeakMap` keyed on
the input schema object to avoid redundant work on repeated calls
## Fix: mergeCompatibleEnumSchemas deep equality
Uses `areJsonValuesEqual` instead of `Object.is` when deduplicating
enum members, so structurally equal objects are not duplicated.
## New: test coverage
- `packages/ai/test/schema-normalization.test.ts`: comprehensive unit
tests for strict mode, Google, and Cloud Code Assist normalization
- `packages/ai/test/schema-compatibility.test.ts`: unit tests for all
three provider targets in the new compatibility validator
- `packages/coding-agent/test/tools/provider-schema-compatibility.test.ts`:
integration test that instantiates every builtin and hidden tool, runs
their parameter schemas through all three provider pipelines, and
asserts zero compatibility violations
- Extracted schema utilities from typebox-helpers and google-shared into new modular utils/schema package with 17 exported functions.
- Consolidated OpenAI strict mode schema enforcement across codex, completions, and responses providers using unified adaptSchemaForStrict() helper.
- Refactored credential ranking from hardcoded Codex-specific logic to pluggable CredentialRankingStrategy pattern with provider implementations.
- Migrated 500+ lines of Google schema sanitization and normalization logic from google-shared.ts to dedicated utils/schema modules with expanded functionality.
Rust (crates/pi-natives/src/ast.rs):
- Expand supported languages from 7 to all 25 ast-grep tree-sitter
languages: Bash, C, C++, C#, CSS, Elixir, Go, Haskell, HCL, HTML,
Java, JavaScript, JSON, Kotlin, Lua, Nix, PHP, Python, Ruby, Rust,
Scala, Solidity, Swift, TSX, TypeScript, YAML.
- Pre-filter file candidates by language-resolvable extension before
reading or parsing. Non-code files (.md, .zip, .bin, .gitignore, etc.)
are silently skipped instead of producing misleading parse errors.
- Make canonical_lang_name exhaustive over SupportLang so the compiler
flags new variants on upstream upgrades.
TypeScript (ast-find.ts, ast-replace.ts):
- Format match output with hashline tags (LINE#HASH:content) when
hashline mode is active, consistent with read/grep tool output.
Falls back to plain line numbers when hashline mode is off.
- ast_find now emits all lines of a multiline match instead of a
single truncated preview line.
Prompts:
- Add AST pattern syntax reference (metavariables, wildcards, variadics)
to the system prompt.
- Add examples and scoping guidance to ast-find.md and ast-replace.md.
COLORFGBG reflects the actual terminal profile colors, while
detectMacOSAppearance() reports the OS-level dark/light setting.
When a user runs a dark terminal on macOS light mode (or vice versa),
COLORFGBG gives the correct answer. The previous ordering caused omp
to load the light theme in dark terminals when macOS was in light mode.
Detection priority is now:
1. Mode 2031 (terminal DSR) - most authoritative
2. COLORFGBG env var - reflects terminal profile
3. macOS system appearance - OS-level fallback
4. Default to dark