- Normalized agent `setSystemPrompt` to wrap string inputs into one-item arrays.
- Updated session creation to accept string `systemPrompt` values and normalize callback or direct results to string arrays.
- Adjusted extension result handling and test fixtures to accept string `systemPrompt` and missing `assistant_message` fields without crashing.
Deferred MCP discovery wrote 'Connecting to MCP servers: …' straight to process.stderr while the TUI owned the terminal, overdrawing the chat input box border. onMCPConnecting now emits McpConnectingEvent on the mcp:connecting channel; InteractiveMode subscribes and renders it via showStatus (status container), mirroring the LSP-startup pattern. New mcp/startup-events.ts holds the channel, type, and formatMCPConnectingMessage.
- agent-loop: raise repetition-detection floor to 180 chars and clear thinking
replay anchors when collapsing a detected loop.
- providers/google: ignore empty text parts, retain terminal thoughtSignatures,
and stop function-call signatures clobbering the prior block.
- autolearn: capture goal-mode at the turn boundary; harden managed-skill writes
against hard-links/symlinks (O_NOFOLLOW + nlink); refuse minting managed skills
whose name an authored skill already claims.
- eager tasks: thread agentKind through the session so a custom top-level agentId
still gets always-mode delegation; split Eager Tasks prompt into hard vs soft.
- title-generator: race the online title model against a local tiny-model fallback.
- eager-todo: keep the soft reminder aligned with the todo init schema.
- mcp/stdio: keep close() detaching the read loop instead of awaiting it.
- stream loop: fix collapsing and tool-call thought-signature handling.
- Added unified `omp setup speech` flow with JSON/check modes and model picker.
- Added local STT pipeline with sherpa workers, recorder/download flow, and streaming inference.
- Added local TTS pipeline with `omp say`, backend selection, and streaming vocalization.
- Replaced legacy speech settings with unified `speech`/`speechgen` configuration keys.
Grill findings on the auto-learn change-set — the controller nudge, the standing
guidance, and the actual tool availability could disagree:
- Guidance was rebuilt from live `autolearn.enabled`, so a mid-session enable (or
a subagent that filtered the tools out) injected guidance for tools the session
never built. `buildAutoLearnInstructions` now takes `{ manageSkill, learn }` and
is driven by the auto-learn BUILTINS that `createTools` actually built
(`builtInToolNames`) — provenance, so a same-named custom/extension tool can't
trigger it while auto-learn is off.
- The controller install reverted to gate on `autolearn.enabled && taskDepth === 0`:
the tool registry is built once at session start, so installing it while disabled
would nudge toward absent tools. The fire-time re-check still handles a
mid-session disable.
- Force-included auto-learn tools are now ACTIVATED for restricted top-level
sessions (mirroring the `yield` invariant), so a session with an explicit tool
whitelist actually exposes manage_skill/learn instead of building them inactive.
Tests: guidance gating by tool presence (none/manage-only/learn), restricted-session
activation via createAgentSession, and removal of the obsolete mid-session-enable
controller case.
The controller was only constructed when `autolearn.enabled` was true at
`createAgentSession` time. Because `newSession` reuses the session without
rerunning startup, enabling the setting via the UI mid-session never installed
the listener, so the feature stayed inert until the app was recreated. The
controller already re-checks the live flag at fire time, so install it for every
top-level session and let that check gate activation; when disabled it only
counts tool calls and returns.
Addresses review thread on PR #2542 (thread 18).
The `AutoLearnController` was constructed inside the fire-and-forget
`startMemoryStartupTask`, after `await memoryBackend.start()`. With a slow
backend (Mnemopi/Hindsight) a fast first turn could begin and finish before the
event listener subscribed, dropping that turn's tool events so no nudge fired.
Install the controller synchronously, before the backend task, so the listener
is registered before any turn can run.
Addresses review thread on PR #2542 (thread 10).
- Added a `snapcompact-savings.jsonl` append-only journal for per-tool savings records.
- Extended `SnapcompactInlineTransformer` to compute `savedTokens` and emit savings to an optional sink.
- Integrated `createSnapcompactSavingsRecorder` into `createAgentSession` for session-aware tool-result metrics.
- Skipped journaling entries without a session and deduped duplicate `toolCallId`s per recorder lifecycle.
- Added tests for savings-sink emissions and savings-journal read/write behavior.
Add a default-off "auto-learn" loop. When `autolearn.enabled` is set, after the
agent stops a session controller nudges it to capture reusable lessons: durable
facts go to long-term memory and repeatable procedures become "managed skills" —
SKILL.md files written to an isolated ~/.omp/agent/managed-skills directory that is
discovered and surfaced like authored skills but never overwrites them.
Two tools back this:
- `manage_skill` — create/update/delete managed skills.
- `learn` — record a lesson, optionally minting/enhancing a managed skill in the
same call (requires a hindsight/mnemopi memory backend).
The nudge is passive by default (a hidden reminder rides the next turn);
`autolearn.autoContinue` instead auto-runs one capture turn at stop, and
`autolearn.minToolCalls` (default 5) gates trivial turns. Plan/goal-mode turns and
subagents are never nudged, and the controller re-checks the live setting at fire
time so a mid-session opt-out takes effect.
Isolation & precedence: managed skills are a separate lowest-priority discovery
provider, so an authored skill of the same name wins across every provider and
custom directory regardless of third-party toggles; a disabled higher-priority
authored skill can never hide a managed one, and managed never masks an enabled
authored skill. Managed names and descriptions are sanitized on both write and
read (control/format chars, angle brackets, and Markdown fences) before they render
into the system prompt, and the SKILL.md byte cap is enforced on the final
serialized file.
Default off → zero footprint when disabled.
- Added session-domain modules and exports for session-entries, context, listing, loader, and migrations.
- Changed persistence to async append writes plus writeTextAtomic, removing sync line APIs.
- Added compaction-aware session context rebuild with dangling tool-call cleanup.
- Added resumable session resolution with status inference, id/stem/suffix matching, and backup recovery.
- Replaced async getApiKey probing with modelRegistry.hasConfiguredAuth during session model restore and fallback selection.
- Removed the per-provider key cache and avoided startup getApiKey/network work by checking configured auth synchronously.
- Deferred real key retrieval to the request-time resolver while preserving existing model selection flow.
- Added getActiveModel support to session/tool interfaces for propagating active model objects.
- Added model capability helpers to flag WebP-unfriendly Ollama backends for image resize options.
- Updated image normalization and loading to auto-disable/reencode WebP when model constraints require it.
- Recomputed `tools.discoveryMode: "auto"` in the deferred MCP closure in `sdk.ts` once the real tool count is known: a toolset crossing the threshold now flips discovery on, registers and activates `search_tool_bm25`, and skips `activateAll` instead of force-activating every MCP tool.
- Guarded the deferred MCP task against disposed sessions: added `AgentSession.isDisposed` and `enableMCPDiscovery()`, and the late connect now calls `disconnectAll()` instead of refreshing tools onto a dead session.
- Cleared `#fastPathKey`/`#fastPathItems` in `AssistantMessageComponent.invalidate()` so theme/symbol changes rebuild reused Markdown children instead of keeping stale captured themes.
- Memoized unusable read summaries as a `false` sentinel in `read.ts` so the per-session LRU no longer retains full sources of unsummarizable files.
- Broadened `HAS_REF_DEF` in `markdown.ts` to match backslash-escaped reference labels (`[a\]b]: x`) and cleared frozen stream-lex state on blank `setText()`.
- Added regression tests: deferred auto-discovery flip and mid-connect dispose (`sdk-mcp-auto-discovery.test.ts` + `many-tools-mcp.ts` fixture), fast-path child rebuild on invalidate, and escaped-ref-def incremental-lex equivalence.
- Added snapcompact.shape setting with auto and variant options in agent config.
- Implemented resolveShape support for forced variants, auto provider winners, and repricing.
- Threaded resolved shape into compaction and inline-image flows for pricing and rendering.
- Added a `personality` enum to `settings-schema.ts` and new `Personality` type alias.
- Replaced inline reply guidelines with a templated `<personality>` block in `system-prompt.md`.
- Added markdown personality specs and wired them into `system-prompt.ts` rendering.
- Updated `sdk.ts` and `selector-controller.ts` to apply personality changes and refresh prompts.
- Set sub-agent sessions to use `personality: none` to suppress personality rendering.
- Added `renderSnapcompactFrames()` and `snapcompactFrameCount()` to @oh-my-pi/snapcompact for paging arbitrary text into PNG image blocks without dim-marker bookkeeping.
- Widened the agent loop's `transformProviderContext` hook to `(context, model) => Context` so per-request transforms can gate on the dispatch model's capabilities.
- Added `SnapcompactInlineTransformer` rendering the system prompt and large historical tool results as snapcompact frames on vision models: vision gate, per-provider image budgets, 3k-token floor, savings-margin gate, skip-last rule, and hash-keyed render caches swept to live tool calls.
- Added default-off `snapcompact.systemPrompt` and `snapcompact.toolResults` settings under a new Context → Experimental group, composed after secret obfuscation in `sdk.ts` so frames are built per-request and never persisted to session.jsonl.
- Added prompt stubs (`snapcompact-system-stub.md`, `snapcompact-system-frames-note.md`, `snapcompact-toolresult-note.md`) and unit tests covering frame paging, no-mutate guarantees, budget caps, gates, and render caching.
Introduces AgentLifecycleManager: when the task executor adopts a finished subagent the manager arms a TTL timer on idle, parks the agent on expiry (disposes the live session, keeps the AgentRef + sessionFile), and revives it on demand via an injected reviver. Only this manager flips parked ↔ idle. AgentRegistry now annotates session: AgentRef["session"] as null exactly when parked/aborted, and sdk.ts wires the lifecycle dispose into main-session teardown, derives agentKind once, and gates ref unregistration on parking so a parked agent stays addressable (history://, revive).
Propagated explicit thinking-off state through the agent loop so provider requests receive disableReasoning instead of an undefined effort. Added Ollama and agent-session regressions for the :off path.\n\nFixes #2239
Converted provider-facing tool parameters to wire JSON Schema before redaction so live Zod instances are not deep-cloned into plain objects.
Fixes#2146
Redacted configured secrets across provider-facing system prompts, tool definitions, developer reminders, and assistant tool-call payloads before LLM requests.
Fixes#2146
- Added "available" status so recognized servers show under lazy mode without warmup.
- Rendered full welcome box as pre-TUI splash with fixed slot heights to avoid layout shift.
- Reported lazy servers as available in /status instead of omitting the section.
- Added an `lsp.lazy` boolean setting with default `true` so LSP servers start on first use.
- Updated session startup logic to run LSP warmup only when `enableLsp`, UI is enabled, and lazy startup is disabled.
- Documented the new lazy LSP initialization behavior and defaults in SDK and LSP tool docs.
Move bundled models, model cache/manager, thinking metadata, effort helpers,
provider descriptors/discovery, wire constants, and model identity utilities
into the new @oh-my-pi/pi-catalog package.
Update pi-ai to keep provider runtime/auth concerns, move catalog provider
metadata into CATALOG_PROVIDERS, and migrate coding-agent, agent, stats, docs,
and tests to import catalog values from pi-catalog.
Split coding-agent model registry helpers into discovery, roles, and models
config modules while preserving registry orchestration.
BREAKING CHANGE: @oh-my-pi/pi-ai no longer exports catalog subpaths such as
/models, /model-cache, /model-manager, /model-thinking, /effort,
/provider-models*, discovery helpers, and provider wire constants; use the
matching @oh-my-pi/pi-catalog subpaths instead.
- Fixed help rendering so `--help` no longer triggers unrelated command loaders.
- Fixed startup span logging to emit markers only with PI_DEBUG_STARTUP set.
- Fixed logger startup trace behavior for `:start`, `:done`, and `:fail` phases.
- Fixed prompt template processing with cached raw-template compilation and safer formatting.
- Optimized symbol and tag parsing in prompt templates via manual parsers.
Same shape of bug the reviewer flagged for custom tools: forwarding
`LoadExtensionsResult` from parent to subagent reused Extension instances
whose factories closed over the parent's `ExtensionAPI` — cwd, eventBus,
and runtime all pointed at the parent. Any tool/handler/command that
referenced `api.exec()`, `api.events`, or `api.runtime` still acted on the
parent session/worktree from inside an isolated subagent.
Forward only the path list; each session rebuilds extensions through
`loadExtensions` so factories see the right `ExtensionAPI`.
- `extensibility/extensions/loader.ts`: extract `discoverExtensionPaths`
(FS scan only) from `discoverAndLoadExtensions`. The combined helper now
composes the two. New export added to the package barrel.
- `sdk.ts`:
- Add `discoverSessionExtensionPaths()` (the `disableExtensionDiscovery`-aware
path-only counterpart of `loadSessionExtensions`).
- Add `preloadedExtensionPaths?: string[]` to `CreateAgentSessionOptions`.
Three loader branches: `preloadedExtensions` (CLI same-process reuse,
still shallow-cloned), `preloadedExtensionPaths` (subagent: skip scan,
reload locally), or full discovery.
- Document `preloadedExtensions` as same-process-only; subagent
forwarding MUST use `preloadedExtensionPaths`.
- `tools/index.ts`: `ToolSession.extensionsResult` → `extensionPaths:
string[]` for the same reason.
- `task/executor.ts` and `task/index.ts`: forward `extensionPaths`. Drop
the forward for the isolated `runSubprocess` branch — worktree cwd ≠
parent cwd, so the subagent re-discovers extensions against its own
tree.
- New `test/sdk-extensions-per-session-binding.test.ts` pins the contract:
two `loadExtensions` calls on the same path with different `cwd` and
different `EventBus` instances yield distinct Extension + runtime
objects whose factories close over the per-call bindings.
- Updated `executor-pass-through` and `sdk-preloaded-extensions-isolation`
tests for the new option name and comment context.
Refs PR review on #2193
Reviewer flagged that forwarding `LoadedCustomTool[]` from a parent session
to a subagent reused tool instances whose factories had closed over the
parent's `CustomToolAPI` — `cwd`, `exec`, `pushPendingAction`, and `ui` all
pointed at the parent. In isolated tasks the tool would `exec` against the
parent worktree and queue pending actions on the parent session.
Forward only the path list; let each session rebuild tools through
`loadCustomTools` so factories see the right `CustomToolAPI`.
- `extensibility/custom-tools/loader.ts`: extract `discoverCustomToolPaths`
(FS scan only) from `discoverAndLoadCustomTools`; export
`ToolPathWithSource`. The combined helper is now `discoverCustomToolPaths`
+ `loadCustomTools`.
- `sdk.ts`: replace `preloadedCustomTools` (`LoadedCustomTool[]`) with
`preloadedCustomToolPaths` (`ToolPathWithSource[]`). The custom-tools
block runs `loadCustomTools` unconditionally; only the path scan is
skipped when the caller pre-discovered it.
- `tools/index.ts`: `ToolSession.loadedCustomTools` →
`ToolSession.customToolPaths` for the same reason.
- `task/executor.ts` and `task/index.ts`: forward `customToolPaths`.
Drop the forward for isolated subagents — the worktree shifts `cwd`, so
the subagent re-discovers tools against its own working tree.
- New `test/sdk-custom-tools-per-session-binding.test.ts` pins the contract:
two `loadCustomTools` calls on the same path with different `cwd` and
different `pushPendingAction` callbacks yield distinct tool instances
whose factories see the per-call bindings.
- Updated `executor-pass-through` and `sdk-preloaded-extensions-isolation`
tests for the new option name and added a `ToolPathWithSource` fixture.
Refs PR review on #2193
Each `runSubprocess` call re-ran `loadCapability<Rule>()`,
`loadSessionExtensions()`, and `discoverAndLoadCustomTools()` because
`ExecutorOptions` and the `createAgentSession()` call inside the executor
omitted three pass-through fields the parent had already paid for. The
already-correct paths (skills, context files, workspace tree, MCP manager)
showed the intended pattern.
- Cache `rules`, `extensionsResult`, and `loadedCustomTools` on the
parent's `ToolSession`.
- Add `rules` / `preloadedExtensions` / `preloadedCustomTools` to
`ExecutorOptions`; forward them from both `runSubprocess` call sites
in `task/index.ts` and into the executor's `createAgentSession()`.
- Add `preloadedCustomTools` to `CreateAgentSessionOptions` and skip
`discoverAndLoadCustomTools()` when it is supplied.
- Shallow-clone `extensionsResult.extensions` when reusing
`preloadedExtensions`, so the per-session autoresearch + custom-tools
inline wrappers never leak back into the caller's array.
Fixes#2190
Deferred UI MCP discovery gated server-instruction inclusion on
`!deferMCPDiscoveryForUI`, so both `rebuildSystemPrompt` and the
`getMcpServerInstructions` signature callback omitted instructions for
the lifetime of every interactive session — even after background
discovery connected the servers. Server-provided instructions were
dropped permanently, contradicting the changelog's "next session" claim.
Both reads now depend only on `mcpManager.getServerInstructions()`,
which is empty before connect and populated after, so the post-discovery
`refreshMCPTools` rebuild folds them into the prompt. Corrects the
changelog and adds a home-isolated deferred end-to-end regression test
(stdio fixture server) that fails if the gate is reintroduced.
- Defer MCP server discovery off the first-paint critical path for UI
sessions; tools and slash commands stream in through the existing
live-refresh channel once each server connects (non-UI modes keep the
blocking path). ~290ms off first paint with MCP servers configured.
- Build the model catalog's canonical-equivalence index lazily on first
read instead of eagerly in the ModelRegistry constructor. A default
interactive launch never reads it pre-paint, moving the ~210ms build
(over ~3,200 models) off the critical path: ~244ms (~16%) off cold boot.
- Memoize per-session read summaries (tree-sitter parse) on the content
hash of the freshly-read bytes; the file is still read fresh each call
so results stay correct. Repeat same-file summary read 17ms -> 2.4ms.
- Reuse the Markdown subtree across streaming reveal ticks, memoize
grapheme counting, and stop re-highlighting finalized thinking blocks.
- Attribute the previously-unlabeled synchronous boot region in the
PI_TIMING table and add a bench:guard boot-regression target.
- Introduced filterInitialToolsForDiscoveryAll() to centralize tool filtering when discovery mode is "all", replacing inline logic in createAgentSession.
- Added forceActive parameter to ensure tools required by forced tool_choice features (e.g., eager todo) remain active in the request, preventing provider 400 errors.
- Updated eager todo enforcement to check active tool set instead of registry, ensuring it respects tool discovery hiding.
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
- Added `getRules()` to `TtsrManager` to expose registered TTSR rules in registration order.
- Updated `createAgentSession` to append `ttsrManager.getRules()` to active rules so TTSR rules can be resolved through `rule://`.
- Rewrote the rulebook matching docs to reflect that `rule://` now includes registered TTSR rules and why.
- Added a session-global file mutation counter and accessor methods to tool sessions.
- Updated the write tool to bump a file's mutation version after each write.
- Updated the edit tool to use session-wide mutation versions when checking stale deferred diagnostics.
- Added per-path edit versioning in `EditTool` to drop stale late diagnostics after later edits.
- Added deferred diagnostics queueing through `queueDeferredDiagnostics` and late-diagnostic yield batching.
- Updated `UiHelpers` to render late diagnostic file path and summary lines in the chat transcript.
- Added first-party-first provider priority defaults for model ranking.
- Consolidated model resolution to use getModelMatchPreferences from session settings.
- Prioritized providerPriorityRank ahead of usage rank when picking preferred models.
- Added second-pass fallback to default-model or API-key-valid matching order.
MCPTool and DeferredMCPTool now declare approval = 'write' instead of
implicitly defaulting to 'exec'. Without this, the approval system
requires user confirmation for every MCP tool call in non-yolo modes,
but the confirmation prompt never renders in the TUI while streaming,
causing the agent to hang indefinitely.
Also propagate the approval property through customToolToDefinition()
in sdk.ts, which was silently dropping it during CustomTool ->
ToolDefinition conversion.
- Added `/tan` slash command registration and interactive handling.
- Added TanCommandController validation and async task scheduling for `/tan` dispatch.
- Added session cloning that suppresses breadcrumbs, copies artifacts, and handles abort cleanup.
- Added `promptCacheKey` support in Agent and inherited `providerPromptCacheKey` in session creation.