From 667319adb48ab7c22754b7080890b040e6153922 Mon Sep 17 00:00:00 2001 From: can1357 Date: Sat, 27 Jun 2026 02:10:41 +0200 Subject: [PATCH] fix(types): add os import, drop invalid id fields in tests after #3416/#3510 drop --- packages/agent/CHANGELOG.md | 18 +-- packages/ai/CHANGELOG.md | 139 ++++++++---------- packages/catalog/CHANGELOG.md | 50 +++---- packages/coding-agent/CHANGELOG.md | 81 +++------- .../src/slash-commands/builtin-registry.ts | 1 + .../test/collab/guest-subagent-badge.test.ts | 2 +- .../modes/controllers/usage-command.test.ts | 5 +- packages/coding-agent/test/usage-cli.test.ts | 4 +- packages/collab-web/CHANGELOG.md | 3 +- packages/hashline/CHANGELOG.md | 3 +- packages/tui/CHANGELOG.md | 14 +- packages/utils/CHANGELOG.md | 13 +- 12 files changed, 129 insertions(+), 204 deletions(-) diff --git a/packages/agent/CHANGELOG.md b/packages/agent/CHANGELOG.md index f34aaf104..de5e1776b 100644 --- a/packages/agent/CHANGELOG.md +++ b/packages/agent/CHANGELOG.md @@ -2,10 +2,16 @@ ## [Unreleased] +### Added + +- Added an optional `cwdResolver` to `Agent` (and a `getCwd` per-call resolver on `AgentLoopConfig`) that is read once per LLM call to resolve the working directory, overriding the static `cwd` (falling back to it when the resolver returns `undefined`). Lets a host reflect a session move into provider options without reconstructing the agent — workspace-scoped provider discovery (e.g. GitLab Duo Agent namespace/project) now follows the live directory instead of the directory captured at construction. + ### Fixed - Fixed API-level provider refusals being replayed as assistant dialogue on later requests, which could anchor repeated refusals after a single blocked turn. ([#3592](https://github.com/can1357/oh-my-pi/issues/3592)) - Fixed `streamProxy` leaking internal `partialJson` streaming state onto the final `AssistantMessage` when the stream ended without a `toolcall_end` event. The field is now accumulated in a side-channel `Map` (eliminating `as any` casts on the accumulation path), written onto the content object via a typed `ToolCall & { partialJson: string }` intersection so downstream renderers can still read it during streaming, and scrubbed from all content blocks at `toolcall_end`, `done`, and `error` — guaranteeing it never appears on the final message. +- Fixed `Agent` forwarding the working directory (`cwd`) into provider stream options so the GitLab Duo Agent provider can scope local tool execution to the workspace. +- Allowed configured custom OpenAI-compatible providers to use native remote compaction instead of falling back to local summarization. ([#3104](https://github.com/can1357/oh-my-pi/issues/3104)) ## [16.1.23] - 2026-06-26 @@ -28,9 +34,6 @@ ### Fixed - Hardened the agent-loop cooperative yield against backward wall-clock jumps. A stale future timestamp left in the shared yield gate (NTP step, or a fake-timer test mocking `Date.now`) could make `yieldIfDue()` gate forever and stop yielding to the event loop; the gate now treats a backward clock delta as due and re-anchors. The gate is exposed as an injectable `YieldGate` (with `yieldIfDue()` retained as the shared singleton) so it can be exercised without mocking process-global timers. -### Added - -- Added an optional `cwdResolver` to `Agent` (and a `getCwd` per-call resolver on `AgentLoopConfig`) that is read once per LLM call to resolve the working directory, overriding the static `cwd` (falling back to it when the resolver returns `undefined`). Lets a host reflect a session move into provider options without reconstructing the agent — workspace-scoped provider discovery (e.g. GitLab Duo Agent namespace/project) now follows the live directory instead of the directory captured at construction. ## [16.1.16] - 2026-06-23 @@ -43,10 +46,6 @@ - Updated `buildSideRequestContext` to allow pinning custom system prompts -### Fixed - -- Fixed `Agent` forwarding the working directory (`cwd`) into provider stream options so the GitLab Duo Agent provider can scope local tool execution to the workspace. - ## [16.1.10] - 2026-06-21 ### Fixed @@ -68,10 +67,6 @@ - Exported helper functions `normalizeMessagesForProvider` and `resolveOwnedDialectFromEnv` from `packages/agent/src/agent-loop.ts`. -### Fixed - -- Allowed configured custom OpenAI-compatible providers to use native remote compaction instead of falling back to local summarization. ([#3104](https://github.com/can1357/oh-my-pi/issues/3104)) - ## [16.1.5] - 2026-06-19 ### Fixed @@ -169,6 +164,7 @@ ### Fixed - Fixed `pruneToolOutputs` blanking tiny tool results during overflow pruning: results below `50` tokens (`MIN_PRUNE_TOKENS`) are no longer replaced with the `[Output truncated - N tokens]` placeholder, which cost more tokens than the result itself and churned the prompt cache for zero savings. + ## [15.13.2] - 2026-06-15 ### Breaking Changes diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 21cf48d63..9e17def00 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [Unreleased] + ### Breaking Changes - Removed the `@oh-my-pi/pi-ai/utils/json-parse` module. The JSON repair/parse helpers (`repairJson`, `parseJsonWithRepair`, `parseStreamingJson`, `parseStreamingJsonThrottled`) now live in `@oh-my-pi/pi-utils` so the SSE reader and other utilities can share one parser; import them from `@oh-my-pi/pi-utils`. @@ -9,12 +10,71 @@ - Added runaway detection for Gemini models to interrupt streams stuck in excessive planning steps - Added a per-provider in-flight request limiter for LLM streams, shared across local OMP processes and configurable by callers with `maxInFlightRequests`. +- Added `credits` field (array of `UsageResetCreditDetail` with `grantedAt`, `expiresAt`, `status`) to `UsageResetCredits`, so display layers can show when banked rate-limit resets expire. The OpenAI Codex usage provider now calls `listCodexResetCredits` to populate individual credit details when `availableCount > 0` ([#3339](https://github.com/can1357/oh-my-pi/issues/3339)). +- Added the GitLab Duo Agent provider (id `gitlab-duo-agent`, API id `gitlab-duo-agent`), registry metadata, and the built-in implementation. The id mirrors GitLab's official "GitLab Duo Agent Platform" naming; the existing AI Gateway proxy provider is renamed to display "GitLab Duo Non-Agentic" (id `gitlab-duo` unchanged) to match GitLab's "Non-Agentic" classification. +- Added GitLab Duo Workflow provider protocol helpers, stream routing, WebSocket action response handling, and provider-level contract tests. +- Added GitLab Duo Workflow OAuth login using GitLab's official VS Code OAuth application, with paste-code instructions for the `vscode://gitlab.gitlab-workflow/authentication` callback. +- Added GitLab Duo Workflow project auto-discovery: the inline `ambient` flow requires a GitLab project server-side but OMP has no project of its own, so when no project is configured the provider discovers an accessible one (preferring a project under the resolved namespace group, then any membership project) and scopes `direct_access`, workflow creation, and WebSocket routing to it. +- Added GitLab Duo Workflow login-time namespace Duo settings enablement: before the first run each session the provider checks the resolved root namespace's Duo settings and, when any of `duo_agent_platform_enabled` / `duo_workflow_mcp_enabled` / `experiment_features_enabled` is off, sends a minimal group `PUT` to turn on exactly those three flags the inline MCP-only ambient flow requires. The enable is best-effort (a permission failure for non-owners/maintainers is logged and never blocks the run) and runs at most once per session. + +### Changed + +- Changed the GitLab Duo Agent rendered-`goal` soft overflow threshold from 1.25 MB to 1 MB (`1_048_576`), so a goal at or above 1 MB enters the jitter zone and an `There was an error` failure there is re-labeled as a context overflow to trigger auto-compaction earlier. The 1.25 MB ceiling almost never fired in practice; lowering it makes auto-compaction engage before the goal grows into the high-failure band. The hard (necessary-fail) threshold stays at 2 MB. +- Changed the GitLab Duo Agent `goal` transcript to drop bytes the model never reads: tool-call ids and tool-result ids are omitted (the inline ambient flow issues one tool call per turn and the result rides the very next turn, so call→result pair by adjacency, not by UUID), the OMP-internal per-call intent (`i`) field is stripped from replayed tool-call arguments, and the tool-call JSON is no longer `<`/`>`-escaped (the goal is plain transcript text, not an HTML/script context). On a real long session this trims roughly 7% of the rendered goal with no semantic change; live tool dispatch is unaffected because it never reads the replayed transcript. +- Changed GitLab Duo Agent to bound the rendered `goal` by two byte thresholds and treat oversized goals as context overflow so the session auto-compacts instead of hard-failing. Empirically the DWS/Workhorse transport has no fixed token wall but its failure probability climbs with the rendered-goal byte size (≤~1.25 MB basically always succeeds, ~1.4–1.7 MB is a jitter band, ≥~2 MB basically always fails, 4 MB is the DWS gRPC `MAX_MESSAGE_SIZE` hard cap). The soft threshold is the measured last-guaranteed-success ceiling (1.25 MB) and the hard threshold is the necessary-fail floor (2 MB): a goal in `[soft, hard)` is still attempted once (it can succeed) and only re-labeled as an `OVERFLOW_PATTERNS`-matching `prompt is too long: …` error if the run actually fails; a goal at or above the hard threshold is not sent at all — the provider proactively ends the stream with the overflow error (stopping the just-created server-side workflow) so no quota is spent on a near-certain failure. The goal body itself is never truncated, and a goal below the soft threshold that errors surfaces its raw server message verbatim so genuine transient faults are not misclassified. +- Changed GitLab Duo Agent to register OMP's MCP tools under their bare names (`read`, `bash`, …) instead of the `mcp__omp__`-prefixed form. The server does not strip prefixes — it binds the model's tool schema and matches incoming tool calls under the exact wire name (`sanitize_llm_name` only replaces illegal characters) — so a prefixed wire name only forced the model to learn `mcp__omp__read` while OMP's own tool docs refer to `read`, with no namespacing benefit. Registering the bare name aligns the name the model sees, the toolset key it is matched against, and OMP's docs. The inbound parser still strips a leading `mcp__omp__` defensively in case a model or server echoes a prefixed name. +- Changed the GitLab Duo Agent `goal` transcript to render replayed tool calls as past-tense `{args}` records and tool results as `` / ``, replacing the prior `{"name":…,"arguments":…}` / `` markers. The model was mimicking the old `{name,arguments}` shape as its own emittable text instead of using the structured tool-use channel, because the historical-record markers were byte-identical to a plausible live-call grammar. The new form reads as a past record (a call that already ran), drops the `{name,arguments}` wrapper (the tool name moves onto the tag, args ride the body), and omits the tool name from the result header (call→result pair by adjacency). This also trims ~48 bytes per call/result pair in the worked example. +- Changed the GitLab Duo Agent inline flow's system prompt to append a short history-note whenever the `goal` is a multi-turn ChatML transcript, telling the model the transcript's `<|im_start|>`/``/`` markers are a past record of already-executed turns and tool calls — not a syntax to emit — and to call tools only through its structured tool-use channel. Reframing the markers to past tense reduced but did not eliminate the model copying them as its own output; the explicit instruction closes the remaining gap. The note rides the system slot (not the goal), is appended only for multi-turn transcripts (a lone bare-text prompt has no markers), and lives in a static `.md` file imported as text. +- Changed GitLab Duo Workflow provider to run an inline custom `ambient` flow (`flowConfig` over the WebSocket, schema `v1`) instead of the built-in `chat` flow, with MCP-only agent privileges so GitLab native tools stay hidden and OMP MCP tools receive `runMCPTool` actions. The inline flow supplies OMP's own system prompt (no server-side jinja wrapper or GitLab project/namespace metadata reaches the agent) and opts into `on_agent_reasoning`, and uses the `DUO_AGENT_PLATFORM` unit primitive. +- Changed GitLab Duo Workflow start requests to restore GitLab's official routing/model metadata while leaving `additional_context` empty because custom client-context items can make namespace-only chat workflows open the WebSocket without replying. +- Changed GitLab Duo Workflow to carry OMP's system prompt in the inline flow's `prompt_template.system` slot and render the conversation as a flat ChatML transcript in the `goal` (no `` wrapper, no privileged ``). Every turn is equal-weight so a mid-task reminder or IRC wake no longer outranks the actual task, and each assistant turn replays its tool calls (`` name + args) paired with the next `tool` turn so the call→result chain stays intact. `additional_context` stays empty. +- Changed GitLab Duo Agent namespace auto-discovery to cache the discovered root namespace per account (a non-reversible fingerprint of the credential plus base URL) and reuse it as the first choice on every later turn and session, instead of re-running discovery on each request. The namespace is a function of the GitLab credential, not of the conversation/cwd, and the inline ambient flow never exposes it to the model, so a stable per-account cache is correct; a cached namespace is only re-discovered (once) if its dependent `direct_access`/workflow-create calls later fail. Explicit `rootNamespaceId`/`namespaceId`/project configuration (option or env) bypasses the cache and stays authoritative. +- Changed GitLab Duo Workflow tool-call streaming to finalize one assistant message per `runMCPTool` action and resume on the same WebSocket with that single tool result. The DWS inline ambient flow dispatches MCP tool calls serially — its `ToolNode` runs `for tool_call ...: await tool.ainvoke(...)`, and each MCP `ainvoke` blocks until the client returns the matching `actionResponse` before the next action is dispatched — so there is no parallel burst to merge. Each tool call is therefore its own assistant message (one `done`, one usage row), matching the server's actual one-action-per-turn wire behavior. ### Fixed - Fixed llama.cpp OpenAI-compatible capture follow-ups sending named forced `tool_choice` as an object; the chat-completions encoder now downgrades that shape to string `"required"` for llama.cpp so its parser no longer falls back with `type must be string, but is object`. ([#3593](https://github.com/can1357/oh-my-pi/issues/3593)) - Fixed `omp usage` silently omitting Ollama and Ollama Cloud accounts by registering placeholder usage providers for both until an upstream quota endpoint is available. ([#3555](https://github.com/can1357/oh-my-pi/issues/3555)) - Fixed Gemini reasoning-runaway detection to expose a dedicated thought-summary header guard for streams that keep emitting fresh planning titles without making a tool call, so higher layers can interrupt that shape without reusing the generic silent retry marker. +- Fixed GitLab Duo Workflow `direct_access` errors dropping the HTTP status when GitLab returned a JSON error body (e.g. a 401 `{"message":"Unauthorized"}` from an expired OAuth token, or a 429 quota body). The thrown error now embeds `HTTP ` alongside the body message so the streaming auth-retry path (`extractStatusFromAssistantError` → `extractHttpStatusFromError`) can recover the status and refresh/rotate the parked broker credential instead of surfacing a hard failure. +- Fixed `AuthStorage.login` always synthesizing a default manual-code paste prompt, which made the loopback `OAuthCallbackFlow` race a readline prompt against the HTTP callback for normal (non-paste-code) OAuth providers and could leave that prompt dangling — a dirty/blocked terminal — when the browser callback won. The default prompt is now synthesized only for `pasteCodeFlow` providers (`PASTE_CODE_LOGIN_PROVIDERS`); loopback providers get no manual-code race unless a caller explicitly supplies `onManualCodeInput`. This is the authoritative gate covering every caller (not just the auth-broker CLI). +- Fixed GitLab Duo Agent checkpoint deduplication swallowing a legitimate later agent message whose text equalled an earlier turn (e.g. two turns that both say "Done", or repeated reasoning). The content-signature fallback that suppresses replayed text across renamed `message_id`s is now scoped to the message's turn position (boundary count within the snapshot) instead of global text equality, so a replayed message reappearing at the same turn is still deduped while a genuinely new message at a later turn emits. +- Fixed GitLab Duo Agent re-issuing the same tool call in an infinite loop when a resumed inline-flow workflow stopped advancing server-side. The earlier stall guard compared the checkpoint `ui_chat_log` message count, but that count is an incremental-streaming slice window capped at ~2 even on a healthy `FINISHED` run, so it could not discriminate a real loop. Detection now compares the server checkpoint's byte length across consecutive tool-call boundaries of the same workflow: a healthy turn emits checkpoints whose size progresses, while a stalled workflow re-emits a byte-identical checkpoint. When two consecutive boundaries carry byte-identical checkpoints the provider settles `stalled` and restarts on a fresh workflow (bounded by `GITLAB_DUO_WORKFLOW_MAX_STALL_RESTARTS`) instead of running the doomed tool call; a single boundary is never falsely flagged. +- Fixed GitLab Duo Agent sending a full project path as the WebSocket `project_id` when `projectId`/`GITLAB_DUO_PROJECT_ID` was configured as a `group/project` path (a form namespace discovery already accepts). A path-valued `projectId` is now routed through the same numeric-id resolution as `projectPath`, so the socket receives the numeric project id project-scoped routing requires instead of the raw path string. +- Fixed GitLab Duo Agent dropping the resolved namespace/root namespace from the WebSocket route whenever no numeric project id was available (a configured project path that could not be resolved, or auto-discovery finding no project). The socket then opened with no scope and could route or fail outside the selected namespace; the namespace/root are now always sent regardless of whether a project id resolved. +- Fixed GitLab Duo Workflow `direct_access` failures to surface sanitized GitLab quota/auth details instead of a bare HTTP status. +- Fixed GitLab Duo Agent repeatedly re-calling the same tool until the user interrupts and re-sends a message. When a resumed turn held pending `runMCPTool` actions whose `requestID` could not be paired with a persisted tool result (id mismatch), the provider silently created a fresh workflow while leaving the previous one running server-side. The stranded workflow's LangGraph still treated the tool call as in-flight, so the model never saw the result and re-issued the same call in a loop; only a user interrupt broke it. The unresolvable-batch path now follows the same cleanup as a mid-batch steer — it stops the stranded workflow server-side and drops the resumable session before seeding the fresh workflow. +- Hardened GitLab Duo Agent action handling to require the server-assigned `requestID` on each `runMCPTool` action frame instead of synthesizing a random one when it appears absent. A fabricated non-empty id is silently discarded by the DWS executor outbox (it misses the awaiting-futures map), which would strand the tool call; DWS always assigns every action a non-empty `requestID`, so a genuinely missing id now fails the turn fast with diagnostics rather than stalling. +- Fixed GitLab Duo Agent treating the server's per-workflow step (graph-recursion) limit as a fatal error. A long but healthy OMP tool-call loop legitimately overruns the cap and surfaced as `FAILED`; the provider now transparently starts a fresh workflow that continues the same conversation (the accumulated context replays through the goal envelope) instead of failing the turn, bounded so a perpetually-overrunning task degrades to a graceful stop. Genuine `FAILED`/`STOPPED` statuses still surface as errors. +- Fixed GitLab Duo Workflow `direct_access` to send `root_namespace_id` in GitLab's GraphQL GID form, avoiding `404 Namespace Not Found` responses when OAuth credentials require canonical namespace metadata. +- Fixed GitLab Duo Workflow runtime namespace resolution so Workflow startup no longer requires `aiChatAvailableModels` to return a selectable model before sending the prompt. +- Fixed GitLab Duo Workflow create to use the discovered namespace path when no project is configured, avoiding `404 Namespace Not Found` responses with OAuth credentials. +- Fixed GitLab Duo Workflow project-path runs to resolve the numeric project id for WebSocket routing while keeping REST `direct_access` and workflow creation scoped to the original project path. +- Fixed GitLab Duo Workflow runtime model selection to honor GitLab `pinnedModel` metadata in both WebSocket routing and start request metadata when available. +- Fixed GitLab Duo Workflow runtime namespace selection to ignore stale model metadata and discover the namespace for the current OAuth credential unless an explicit namespace is configured. +- Fixed GitLab Duo Workflow action handlers so class-based OMP bridge methods keep their receiver when executing `runMCPTool` and native action callbacks. +- Fixed GitLab Duo Workflow action replay IDs so provider-driven `runMCPTool` results can be paired with synthetic assistant tool-call blocks instead of persisting standalone tool results after a stopped assistant. +- Fixed GitLab Duo Workflow checkpoint streaming to process `ui_chat_log` entries in order, preserving tool boundaries and per-entry agent deltas instead of only rendering the last agent message. +- Fixed GitLab Duo Workflow checkpoint streaming to map `ui_chat_log` agent entries tagged `message_sub_type: "reasoning"` (the inline flow's `on_agent_reasoning` pre-tool-call commentary) to thinking blocks, and other agent text to assistant text, matching the chain-of-thought the official Duo CLI surfaces. +- Fixed GitLab Duo Workflow checkpoint streaming to ignore same-key non-prefix checkpoint rewrites instead of appending the rewritten text as a duplicate continuation. +- Fixed GitLab Duo Workflow goal instructions to treat `workflowMetadata`, `additional_context`, `mcpTools`, preapprovals, namespace/project IDs, and `mcp__omp__*` names as GitLab transport metadata instead of user-visible OMP tool policy. +- Fixed GitLab Duo Workflow WebSocket transport errors to report sanitized event fields (`type`, `message`, `error`, `code`, `reason`) instead of a bare `[object ErrorEvent]`. +- Fixed GitLab Duo Workflow tracing so trace-file directory/append failures can never raise an unhandled rejection that crashes the host process. +- Fixed GitLab Duo Workflow server-side tool steps (GitLab native `gitlab_*` tools the agent runs without a client `runMCPTool` action) to emit a `pause_turn` stop at each checkpoint tool boundary, so multi-step server-side reasoning resumes on the same WebSocket and renders as separate independent assistant messages instead of one collapsed block. +- Fixed GitLab Duo Workflow usage reporting to map each checkpoint's per-agent `agent_context_usage` (`total_tokens`/`max_tokens`, preferring the `Chat Agent` then `context_builder` entry) onto the assistant message's `usage.input`/`totalTokens` so the per-message usage row reflects the real server-side context occupancy, without inflating output or cost. +- Fixed GitLab Duo Workflow to stop redacting credential-like substrings from goals, conversation history, and tool results before sending them to GitLab; content redaction is not a provider responsibility, and the over-broad marker was clobbering ordinary prose. Content is now forwarded verbatim. +- Fixed GitLab Duo Workflow runs hanging indefinitely when the WebSocket silently goes half-open (a proxy/LB drops the TCP link without delivering `close`/`error`); the socket now aborts after a 90s idle deadline and recovers by starting a FRESH workflow that replays the conversation through the goal transcript. Same-`workflowID` reconnect is not used because a second connection on an inline flow re-compiles the flow from the live `flowConfig` and the server's checkpoint replay rejects the rebuilt graph topology, so the recovery mirrors the step-limit restart. +- Fixed GitLab Duo Workflow leaving the remote workflow running and the resumable session pointing at a dead socket when a turn ends abnormally — either a user abort or the WebSocket rejecting (e.g. `onerror`). The stop `PATCH` previously got the request's already-aborted signal and only ran when the socket loop returned normally. It now runs from a `finally` path with a fresh signal whenever the turn aborts or the socket loop throws, and drops `providerSessionState.active` so the next turn does not reuse the closed socket. +- Fixed GitLab Duo Workflow dropping a paused session when a tool-result resume crosses a server-side tool boundary: the post-action resume now preserves `providerSessionState.active` on a `pause` result instead of clearing it, so the buffered continuation replays on the next turn instead of starting a new workflow. +- Fixed GitLab Duo Workflow resume turns hanging when a preserved socket, replayed for a tool result or a paused continuation, returned a non-terminal result (`closed`/`approval`/`timeout`) for which the socket emits no `done` event: both resume paths now share the fresh-workflow finalizer, which drops the resumable session and pushes a terminal `done` for every non-`action`/non-`pause`/non-`terminal` result so the assistant stream always closes instead of waiting forever. +- Fixed GitLab Duo Workflow turning normal streaming into a `pause_turn` per checkpoint: since GitLab checkpoints are full `ui_chat_log` snapshots, a later frame replays the earlier `request`/`tool` boundary before the new agent delta. Pause now fires only on a boundary that follows a delta emitted in the current checkpoint, so a stale replayed boundary no longer pauses. +- Fixed GitLab Duo Workflow dropping a user steer issued mid-tool-loop: when a new user/developer message lands after the pending batch's tool results, returning those results on the live socket would silently discard the steer. The provider now detects the mid-batch steer, stops the live workflow, and re-seeds a FRESH workflow whose goal transcript includes the steer so it reaches the model. +- Fixed GitLab Duo Workflow treating the server's de-identified catch-all `FAILED` as fatal. It wraps transient upstream faults (model 5xx that exhausted retries, AgentStuckError, etc.), so the provider now retries once on a FRESH workflow by replaying the conversation through the goal transcript, and only surfaces the error if the retry also fails. Genuine non-generic `FAILED`/`STOPPED` statuses still surface immediately. +- Fixed GitLab Duo Workflow API URL construction dropping a self-managed GitLab relative install base path: building request/WebSocket URLs with a leading-slash path against `https://host/gitlab` discarded `/gitlab`. `direct_access`, workflow creation, `aiChatAvailableModels`, project discovery/lookup, and the non-`serviceEndpoint` WebSocket URL now append onto the normalized base so the install path is preserved. + +### Removed + +- Removed the GitLab Duo Workflow legacy `chat`/`software_development` flow paths and the non-MCP action bridge. The inline custom `ambient` flow (MCP-only) is now the sole code path, so the server-side flow-registry branch, the `chat`/`software_development` workflow definitions, and the action-to-OMP-tool mappers for native GitLab actions (plus the `GitLabHttpResponse` action-response shape) are gone; only `runMCPTool`/`run_mcp_tool` actions remain. ## [16.1.23] - 2026-06-26 @@ -51,9 +111,6 @@ - Fixed Ollama/Ollama Cloud native chat responses that finish with `done_reason: "length"` and no assistant content surfacing as a normal empty stop; they now become a context-window error instead of entering empty-stop retry recovery. ([#3464](https://github.com/can1357/oh-my-pi/issues/3464)) - Fixed direct Anthropic Claude Sonnet/Haiku 4.5 requests serializing `output_config.effort`. The catalog classification (`packages/catalog/src/model-thinking.ts`) drove the `anthropic-budget-effort` branch in `buildParams`, which Anthropic's first-party Messages API rejects on Sonnet/Haiku 4.5 with HTTP 400 `This model does not support the effort parameter.` Sonnet/Haiku 4.5 now use plain `thinking.budget_tokens`; Opus 4.5 still emits `output_config.effort` because Anthropic supports it there. ([#3497](https://github.com/can1357/oh-my-pi/issues/3497)) -### Changed - -- Changed the GitLab Duo Agent rendered-`goal` soft overflow threshold from 1.25 MB to 1 MB (`1_048_576`), so a goal at or above 1 MB enters the jitter zone and an `There was an error` failure there is re-labeled as a context overflow to trigger auto-compaction earlier. The 1.25 MB ceiling almost never fired in practice; lowering it makes auto-compaction engage before the goal grows into the high-failure band. The hard (necessary-fail) threshold stays at 2 MB. ## [16.1.19] - 2026-06-25 @@ -99,26 +156,6 @@ - Fixed OpenRouter Anthropic models on the Responses path omitting `cache_control`, so prompt caching engages without forcing Chat Completions. ([#3397](https://github.com/can1357/oh-my-pi/issues/3397)) - Fixed OpenRouter Anthropic Responses follow-up requests replaying prior reasoning items with stale signatures, which caused HTTP 400 `Invalid signature in thinking block` errors after a thinking turn. ([#3399](https://github.com/can1357/oh-my-pi/issues/3399)) - Fixed OpenRouter Anthropic models on the Responses path omitting `cache_control`, so prompt caching engages without forcing Chat Completions. `cacheRetention: "long"` now upgrades the breakpoint to `ttl: "1h"`. ([#3397](https://github.com/can1357/oh-my-pi/issues/3397)) -### Fixed - -- Fixed GitLab Duo Workflow `direct_access` errors dropping the HTTP status when GitLab returned a JSON error body (e.g. a 401 `{"message":"Unauthorized"}` from an expired OAuth token, or a 429 quota body). The thrown error now embeds `HTTP ` alongside the body message so the streaming auth-retry path (`extractStatusFromAssistantError` → `extractHttpStatusFromError`) can recover the status and refresh/rotate the parked broker credential instead of surfacing a hard failure. -- Fixed `AuthStorage.login` always synthesizing a default manual-code paste prompt, which made the loopback `OAuthCallbackFlow` race a readline prompt against the HTTP callback for normal (non-paste-code) OAuth providers and could leave that prompt dangling — a dirty/blocked terminal — when the browser callback won. The default prompt is now synthesized only for `pasteCodeFlow` providers (`PASTE_CODE_LOGIN_PROVIDERS`); loopback providers get no manual-code race unless a caller explicitly supplies `onManualCodeInput`. This is the authoritative gate covering every caller (not just the auth-broker CLI). -- Fixed GitLab Duo Agent checkpoint deduplication swallowing a legitimate later agent message whose text equalled an earlier turn (e.g. two turns that both say "Done", or repeated reasoning). The content-signature fallback that suppresses replayed text across renamed `message_id`s is now scoped to the message's turn position (boundary count within the snapshot) instead of global text equality, so a replayed message reappearing at the same turn is still deduped while a genuinely new message at a later turn emits. -- Fixed GitLab Duo Agent re-issuing the same tool call in an infinite loop when a resumed inline-flow workflow stopped advancing server-side. The earlier stall guard compared the checkpoint `ui_chat_log` message count, but that count is an incremental-streaming slice window capped at ~2 even on a healthy `FINISHED` run, so it could not discriminate a real loop. Detection now compares the server checkpoint's byte length across consecutive tool-call boundaries of the same workflow: a healthy turn emits checkpoints whose size progresses, while a stalled workflow re-emits a byte-identical checkpoint. When two consecutive boundaries carry byte-identical checkpoints the provider settles `stalled` and restarts on a fresh workflow (bounded by `GITLAB_DUO_WORKFLOW_MAX_STALL_RESTARTS`) instead of running the doomed tool call; a single boundary is never falsely flagged. -- Fixed GitLab Duo Agent sending a full project path as the WebSocket `project_id` when `projectId`/`GITLAB_DUO_PROJECT_ID` was configured as a `group/project` path (a form namespace discovery already accepts). A path-valued `projectId` is now routed through the same numeric-id resolution as `projectPath`, so the socket receives the numeric project id project-scoped routing requires instead of the raw path string. -- Fixed GitLab Duo Agent dropping the resolved namespace/root namespace from the WebSocket route whenever no numeric project id was available (a configured project path that could not be resolved, or auto-discovery finding no project). The socket then opened with no scope and could route or fail outside the selected namespace; the namespace/root are now always sent regardless of whether a project id resolved. - -### Changed - -- Changed the GitLab Duo Agent `goal` transcript to drop bytes the model never reads: tool-call ids and tool-result ids are omitted (the inline ambient flow issues one tool call per turn and the result rides the very next turn, so call→result pair by adjacency, not by UUID), the OMP-internal per-call intent (`i`) field is stripped from replayed tool-call arguments, and the tool-call JSON is no longer `<`/`>`-escaped (the goal is plain transcript text, not an HTML/script context). On a real long session this trims roughly 7% of the rendered goal with no semantic change; live tool dispatch is unaffected because it never reads the replayed transcript. -- Changed GitLab Duo Agent to bound the rendered `goal` by two byte thresholds and treat oversized goals as context overflow so the session auto-compacts instead of hard-failing. Empirically the DWS/Workhorse transport has no fixed token wall but its failure probability climbs with the rendered-goal byte size (≤~1.25 MB basically always succeeds, ~1.4–1.7 MB is a jitter band, ≥~2 MB basically always fails, 4 MB is the DWS gRPC `MAX_MESSAGE_SIZE` hard cap). The soft threshold is the measured last-guaranteed-success ceiling (1.25 MB) and the hard threshold is the necessary-fail floor (2 MB): a goal in `[soft, hard)` is still attempted once (it can succeed) and only re-labeled as an `OVERFLOW_PATTERNS`-matching `prompt is too long: …` error if the run actually fails; a goal at or above the hard threshold is not sent at all — the provider proactively ends the stream with the overflow error (stopping the just-created server-side workflow) so no quota is spent on a near-certain failure. The goal body itself is never truncated, and a goal below the soft threshold that errors surfaces its raw server message verbatim so genuine transient faults are not misclassified. -- Changed GitLab Duo Agent to register OMP's MCP tools under their bare names (`read`, `bash`, …) instead of the `mcp__omp__`-prefixed form. The server does not strip prefixes — it binds the model's tool schema and matches incoming tool calls under the exact wire name (`sanitize_llm_name` only replaces illegal characters) — so a prefixed wire name only forced the model to learn `mcp__omp__read` while OMP's own tool docs refer to `read`, with no namespacing benefit. Registering the bare name aligns the name the model sees, the toolset key it is matched against, and OMP's docs. The inbound parser still strips a leading `mcp__omp__` defensively in case a model or server echoes a prefixed name. -- Changed the GitLab Duo Agent `goal` transcript to render replayed tool calls as past-tense `{args}` records and tool results as `` / ``, replacing the prior `{"name":…,"arguments":…}` / `` markers. The model was mimicking the old `{name,arguments}` shape as its own emittable text instead of using the structured tool-use channel, because the historical-record markers were byte-identical to a plausible live-call grammar. The new form reads as a past record (a call that already ran), drops the `{name,arguments}` wrapper (the tool name moves onto the tag, args ride the body), and omits the tool name from the result header (call→result pair by adjacency). This also trims ~48 bytes per call/result pair in the worked example. -- Changed the GitLab Duo Agent inline flow's system prompt to append a short history-note whenever the `goal` is a multi-turn ChatML transcript, telling the model the transcript's `<|im_start|>`/``/`` markers are a past record of already-executed turns and tool calls — not a syntax to emit — and to call tools only through its structured tool-use channel. Reframing the markers to past tense reduced but did not eliminate the model copying them as its own output; the explicit instruction closes the remaining gap. The note rides the system slot (not the goal), is appended only for multi-turn transcripts (a lone bare-text prompt has no markers), and lives in a static `.md` file imported as text. - -### Added - -- Added `credits` field (array of `UsageResetCreditDetail` with `grantedAt`, `expiresAt`, `status`) to `UsageResetCredits`, so display layers can show when banked rate-limit resets expire. The OpenAI Codex usage provider now calls `listCodexResetCredits` to populate individual credit details when `availableCount > 0` ([#3339](https://github.com/can1357/oh-my-pi/issues/3339)). ## [16.1.16] - 2026-06-23 @@ -126,57 +163,6 @@ - Fixed Anthropic-compatible thinking requests sending replayed thinking blocks without `context_management.keep: "all"`, preserving multi-turn reasoning context for API-key providers. API-key requests now also advertise the required `context-management-2025-06-27` beta header so the field is honored instead of rejected. Injected SDK clients, GitHub Copilot's Anthropic proxy, and Vertex rawPredict are excluded because this code path cannot add the beta to caller-owned clients, Copilot strips Anthropic betas and demotes thinking blocks to text upstream, and Vertex expects betas in the JSON body rather than the Anthropic HTTP beta header. ([#3288](https://github.com/can1357/oh-my-pi/issues/3288)) - Fixed OpenRouter Responses native history replay leaking Gemini reasoning item `format` metadata back into follow-up requests, which caused HTTP 400 rejections while preserving encrypted reasoning replay. -### Added - -- Added the GitLab Duo Agent provider (id `gitlab-duo-agent`, API id `gitlab-duo-agent`), registry metadata, and the built-in implementation. The id mirrors GitLab's official "GitLab Duo Agent Platform" naming; the existing AI Gateway proxy provider is renamed to display "GitLab Duo Non-Agentic" (id `gitlab-duo` unchanged) to match GitLab's "Non-Agentic" classification. -- Added GitLab Duo Workflow provider protocol helpers, stream routing, WebSocket action response handling, and provider-level contract tests. -- Added GitLab Duo Workflow OAuth login using GitLab's official VS Code OAuth application, with paste-code instructions for the `vscode://gitlab.gitlab-workflow/authentication` callback. -- Added GitLab Duo Workflow project auto-discovery: the inline `ambient` flow requires a GitLab project server-side but OMP has no project of its own, so when no project is configured the provider discovers an accessible one (preferring a project under the resolved namespace group, then any membership project) and scopes `direct_access`, workflow creation, and WebSocket routing to it. -- Added GitLab Duo Workflow login-time namespace Duo settings enablement: before the first run each session the provider checks the resolved root namespace's Duo settings and, when any of `duo_agent_platform_enabled` / `duo_workflow_mcp_enabled` / `experiment_features_enabled` is off, sends a minimal group `PUT` to turn on exactly those three flags the inline MCP-only ambient flow requires. The enable is best-effort (a permission failure for non-owners/maintainers is logged and never blocks the run) and runs at most once per session. - -### Changed - -- Changed GitLab Duo Workflow provider to run an inline custom `ambient` flow (`flowConfig` over the WebSocket, schema `v1`) instead of the built-in `chat` flow, with MCP-only agent privileges so GitLab native tools stay hidden and OMP MCP tools receive `runMCPTool` actions. The inline flow supplies OMP's own system prompt (no server-side jinja wrapper or GitLab project/namespace metadata reaches the agent) and opts into `on_agent_reasoning`, and uses the `DUO_AGENT_PLATFORM` unit primitive. -- Changed GitLab Duo Workflow start requests to restore GitLab's official routing/model metadata while leaving `additional_context` empty because custom client-context items can make namespace-only chat workflows open the WebSocket without replying. -- Changed GitLab Duo Workflow to carry OMP's system prompt in the inline flow's `prompt_template.system` slot and render the conversation as a flat ChatML transcript in the `goal` (no `` wrapper, no privileged ``). Every turn is equal-weight so a mid-task reminder or IRC wake no longer outranks the actual task, and each assistant turn replays its tool calls (`` name + args) paired with the next `tool` turn so the call→result chain stays intact. `additional_context` stays empty. -- Changed GitLab Duo Agent namespace auto-discovery to cache the discovered root namespace per account (a non-reversible fingerprint of the credential plus base URL) and reuse it as the first choice on every later turn and session, instead of re-running discovery on each request. The namespace is a function of the GitLab credential, not of the conversation/cwd, and the inline ambient flow never exposes it to the model, so a stable per-account cache is correct; a cached namespace is only re-discovered (once) if its dependent `direct_access`/workflow-create calls later fail. Explicit `rootNamespaceId`/`namespaceId`/project configuration (option or env) bypasses the cache and stays authoritative. -- Changed GitLab Duo Workflow tool-call streaming to finalize one assistant message per `runMCPTool` action and resume on the same WebSocket with that single tool result. The DWS inline ambient flow dispatches MCP tool calls serially — its `ToolNode` runs `for tool_call ...: await tool.ainvoke(...)`, and each MCP `ainvoke` blocks until the client returns the matching `actionResponse` before the next action is dispatched — so there is no parallel burst to merge. Each tool call is therefore its own assistant message (one `done`, one usage row), matching the server's actual one-action-per-turn wire behavior. - -### Fixed - -- Fixed GitLab Duo Workflow `direct_access` failures to surface sanitized GitLab quota/auth details instead of a bare HTTP status. -- Fixed GitLab Duo Agent repeatedly re-calling the same tool until the user interrupts and re-sends a message. When a resumed turn held pending `runMCPTool` actions whose `requestID` could not be paired with a persisted tool result (id mismatch), the provider silently created a fresh workflow while leaving the previous one running server-side. The stranded workflow's LangGraph still treated the tool call as in-flight, so the model never saw the result and re-issued the same call in a loop; only a user interrupt broke it. The unresolvable-batch path now follows the same cleanup as a mid-batch steer — it stops the stranded workflow server-side and drops the resumable session before seeding the fresh workflow. -- Hardened GitLab Duo Agent action handling to require the server-assigned `requestID` on each `runMCPTool` action frame instead of synthesizing a random one when it appears absent. A fabricated non-empty id is silently discarded by the DWS executor outbox (it misses the awaiting-futures map), which would strand the tool call; DWS always assigns every action a non-empty `requestID`, so a genuinely missing id now fails the turn fast with diagnostics rather than stalling. -- Fixed GitLab Duo Agent treating the server's per-workflow step (graph-recursion) limit as a fatal error. A long but healthy OMP tool-call loop legitimately overruns the cap and surfaced as `FAILED`; the provider now transparently starts a fresh workflow that continues the same conversation (the accumulated context replays through the goal envelope) instead of failing the turn, bounded so a perpetually-overrunning task degrades to a graceful stop. Genuine `FAILED`/`STOPPED` statuses still surface as errors. -- Fixed GitLab Duo Workflow `direct_access` to send `root_namespace_id` in GitLab's GraphQL GID form, avoiding `404 Namespace Not Found` responses when OAuth credentials require canonical namespace metadata. -- Fixed GitLab Duo Workflow runtime namespace resolution so Workflow startup no longer requires `aiChatAvailableModels` to return a selectable model before sending the prompt. -- Fixed GitLab Duo Workflow create to use the discovered namespace path when no project is configured, avoiding `404 Namespace Not Found` responses with OAuth credentials. -- Fixed GitLab Duo Workflow project-path runs to resolve the numeric project id for WebSocket routing while keeping REST `direct_access` and workflow creation scoped to the original project path. -- Fixed GitLab Duo Workflow runtime model selection to honor GitLab `pinnedModel` metadata in both WebSocket routing and start request metadata when available. -- Fixed GitLab Duo Workflow runtime namespace selection to ignore stale model metadata and discover the namespace for the current OAuth credential unless an explicit namespace is configured. -- Fixed GitLab Duo Workflow action handlers so class-based OMP bridge methods keep their receiver when executing `runMCPTool` and native action callbacks. -- Fixed GitLab Duo Workflow action replay IDs so provider-driven `runMCPTool` results can be paired with synthetic assistant tool-call blocks instead of persisting standalone tool results after a stopped assistant. -- Fixed GitLab Duo Workflow checkpoint streaming to process `ui_chat_log` entries in order, preserving tool boundaries and per-entry agent deltas instead of only rendering the last agent message. -- Fixed GitLab Duo Workflow checkpoint streaming to map `ui_chat_log` agent entries tagged `message_sub_type: "reasoning"` (the inline flow's `on_agent_reasoning` pre-tool-call commentary) to thinking blocks, and other agent text to assistant text, matching the chain-of-thought the official Duo CLI surfaces. -- Fixed GitLab Duo Workflow checkpoint streaming to ignore same-key non-prefix checkpoint rewrites instead of appending the rewritten text as a duplicate continuation. -- Fixed GitLab Duo Workflow goal instructions to treat `workflowMetadata`, `additional_context`, `mcpTools`, preapprovals, namespace/project IDs, and `mcp__omp__*` names as GitLab transport metadata instead of user-visible OMP tool policy. -- Fixed GitLab Duo Workflow WebSocket transport errors to report sanitized event fields (`type`, `message`, `error`, `code`, `reason`) instead of a bare `[object ErrorEvent]`. -- Fixed GitLab Duo Workflow tracing so trace-file directory/append failures can never raise an unhandled rejection that crashes the host process. -- Fixed GitLab Duo Workflow server-side tool steps (GitLab native `gitlab_*` tools the agent runs without a client `runMCPTool` action) to emit a `pause_turn` stop at each checkpoint tool boundary, so multi-step server-side reasoning resumes on the same WebSocket and renders as separate independent assistant messages instead of one collapsed block. -- Fixed GitLab Duo Workflow usage reporting to map each checkpoint's per-agent `agent_context_usage` (`total_tokens`/`max_tokens`, preferring the `Chat Agent` then `context_builder` entry) onto the assistant message's `usage.input`/`totalTokens` so the per-message usage row reflects the real server-side context occupancy, without inflating output or cost. -- Fixed GitLab Duo Workflow to stop redacting credential-like substrings from goals, conversation history, and tool results before sending them to GitLab; content redaction is not a provider responsibility, and the over-broad marker was clobbering ordinary prose. Content is now forwarded verbatim. -- Fixed GitLab Duo Workflow runs hanging indefinitely when the WebSocket silently goes half-open (a proxy/LB drops the TCP link without delivering `close`/`error`); the socket now aborts after a 90s idle deadline and recovers by starting a FRESH workflow that replays the conversation through the goal transcript. Same-`workflowID` reconnect is not used because a second connection on an inline flow re-compiles the flow from the live `flowConfig` and the server's checkpoint replay rejects the rebuilt graph topology, so the recovery mirrors the step-limit restart. -- Fixed GitLab Duo Workflow leaving the remote workflow running and the resumable session pointing at a dead socket when a turn ends abnormally — either a user abort or the WebSocket rejecting (e.g. `onerror`). The stop `PATCH` previously got the request's already-aborted signal and only ran when the socket loop returned normally. It now runs from a `finally` path with a fresh signal whenever the turn aborts or the socket loop throws, and drops `providerSessionState.active` so the next turn does not reuse the closed socket. -- Fixed GitLab Duo Workflow dropping a paused session when a tool-result resume crosses a server-side tool boundary: the post-action resume now preserves `providerSessionState.active` on a `pause` result instead of clearing it, so the buffered continuation replays on the next turn instead of starting a new workflow. -- Fixed GitLab Duo Workflow resume turns hanging when a preserved socket, replayed for a tool result or a paused continuation, returned a non-terminal result (`closed`/`approval`/`timeout`) for which the socket emits no `done` event: both resume paths now share the fresh-workflow finalizer, which drops the resumable session and pushes a terminal `done` for every non-`action`/non-`pause`/non-`terminal` result so the assistant stream always closes instead of waiting forever. -- Fixed GitLab Duo Workflow turning normal streaming into a `pause_turn` per checkpoint: since GitLab checkpoints are full `ui_chat_log` snapshots, a later frame replays the earlier `request`/`tool` boundary before the new agent delta. Pause now fires only on a boundary that follows a delta emitted in the current checkpoint, so a stale replayed boundary no longer pauses. -- Fixed GitLab Duo Workflow dropping a user steer issued mid-tool-loop: when a new user/developer message lands after the pending batch's tool results, returning those results on the live socket would silently discard the steer. The provider now detects the mid-batch steer, stops the live workflow, and re-seeds a FRESH workflow whose goal transcript includes the steer so it reaches the model. -- Fixed GitLab Duo Workflow treating the server's de-identified catch-all `FAILED` as fatal. It wraps transient upstream faults (model 5xx that exhausted retries, AgentStuckError, etc.), so the provider now retries once on a FRESH workflow by replaying the conversation through the goal transcript, and only surfaces the error if the retry also fails. Genuine non-generic `FAILED`/`STOPPED` statuses still surface immediately. -- Fixed GitLab Duo Workflow API URL construction dropping a self-managed GitLab relative install base path: building request/WebSocket URLs with a leading-slash path against `https://host/gitlab` discarded `/gitlab`. `direct_access`, workflow creation, `aiChatAvailableModels`, project discovery/lookup, and the non-`serviceEndpoint` WebSocket URL now append onto the normalized base so the install path is preserved. - -### Removed - -- Removed the GitLab Duo Workflow legacy `chat`/`software_development` flow paths and the non-MCP action bridge. The inline custom `ambient` flow (MCP-only) is now the sole code path, so the server-side flow-registry branch, the `chat`/`software_development` workflow definitions, and the action-to-OMP-tool mappers for native GitLab actions (plus the `GitLabHttpResponse` action-response shape) are gone; only `runMCPTool`/`run_mcp_tool` actions remain. ## [16.1.15] - 2026-06-22 @@ -219,7 +205,6 @@ - Fixed OpenAI Responses native history replay dropping failed/incomplete image generation calls instead of resending their transient `ig_...` item IDs, preventing follow-up requests from failing with `404 Item with id ... not found`. ([#3225](https://github.com/can1357/oh-my-pi/issues/3225)) - Fixed `/login fireworks` rejecting valid `fw_…` keys with `Fireworks API key validation failed (500): Error listing deployed models`. The validator pinged `/inference/v1/models`, which Fireworks serves from the per-account deployment registry and 500s for accounts without active deployments. Login now hits the static control-plane `List Models` catalog (`GET /v1/accounts/fireworks/models?filter=supports_serverless=true&pageSize=1`) — the same endpoint discovery already uses — so authentication no longer depends on the caller's deployment state. ([#3219](https://github.com/can1357/oh-my-pi/issues/3219)) - ## [16.1.11] - 2026-06-21 ### Fixed @@ -294,6 +279,7 @@ ### Fixed - Fixed the Antigravity (`google-antigravity`) request builder dropping `labels.model_enum` when the wire profile does not declare one. Required for Claude 4.6 ids whose `AntigravityModelWireProfile` carries only `maxOutputTokens` (no captured `model_enum`); the label is now emitted only when the catalog defines it. ([#3067](https://github.com/can1357/oh-my-pi/issues/3067)) + ## [16.1.3] - 2026-06-19 ### Added @@ -556,6 +542,7 @@ - Dropped nameless native `toolCall` events so they no longer appear as surfaced tool calls in owned-mode streams - Fixed truncated Gemini and Gemma tool blocks from being emitted as plain text during streaming - Fixed Gemini/Gemma in-band tool-call parsing around Python comments, raw/unicode string literals, and Gemma close-token text inside string values. + ## [15.13.2] - 2026-06-15 ### Added @@ -4241,4 +4228,4 @@ _Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_ ## [0.9.4] - 2025-11-26 -Initial release with multi-provider LLM support. \ No newline at end of file +Initial release with multi-provider LLM support. diff --git a/packages/catalog/CHANGELOG.md b/packages/catalog/CHANGELOG.md index 7c3c411b6..b08937026 100644 --- a/packages/catalog/CHANGELOG.md +++ b/packages/catalog/CHANGELOG.md @@ -5,6 +5,26 @@ ### Added - Added `OpenAICompat.supportsNamedToolChoice` so string-only OpenAI-compatible chat servers can keep forced tool use without emitting the named function-object `tool_choice` shape. ([#3593](https://github.com/can1357/oh-my-pi/issues/3593)) +- Added GitLab Duo Agent catalog discovery for `gitlab-duo-agent`, including namespace selection and live `aiChatAvailableModels` model mapping. +- Added model metadata for provider-native remote compaction and compaction-only model selection. ([#3104](https://github.com/can1357/oh-my-pi/issues/3104)) + +### Changed + +- Changed GitLab Duo Agent model specs to `reasoning: false`. The Duo Agent Platform path exposes no client-controllable thinking knob (the underlying Anthropic model params are server-fixed), so OMP no longer shows a thinking-effort selector for these models. + +### Fixed + +- Fixed built-in LiteLLM discovery to prefer rich proxy metadata from LiteLLM management endpoints before falling back to /models, with a versioned cache namespace so stale bare-id cache rows do not hide capability data. +- Fixed the bundled catalog omitting the GitLab Duo Agent provider so a fresh install (before any credentialed dynamic discovery populates the cache) could not surface its default model. The generator now seeds the `gitlab-duo-agent` fallback model (`claude_sonnet_4_6_vertex`) into `models.json`, deduped behind live `aiChatAvailableModels` discovery when generation has credentials. +- Fixed GitLab Duo Agent namespace discovery only inspecting the first page of top-level groups, so a token belonging to more than 100 top-level groups could miss a usable Duo namespace on a later page and fail or select the wrong group. Discovery now follows GitLab's `x-next-page` pagination (bounded) and validates candidates across all pages. +- Fixed GitLab Duo Agent namespace discovery rejecting a workspace SSH remote whose port differs from the configured web `baseUrl` (self-managed GitLab commonly exposes SSH on a dedicated port, e.g. `ssh://git@host:2222/group/project.git` against `https://host`). SSH and SCP-style remotes now compare on the bare hostname; HTTP(S) remotes still compare host:port strictly so a different service on the same host is not adopted. +- Fixed GitLab Duo Workflow runtime namespace discovery so agent startup can resolve a root namespace without requiring live `aiChatAvailableModels` results. +- Fixed GitLab Duo Workflow runtime namespace discovery to preserve namespace paths for Workflow creation, including numeric/GID namespace overrides that must be resolved through GitLab group metadata. +- Fixed GitLab Duo Workflow project namespace discovery to fall back to the GraphQL `rootAncestor` query whenever a REST project payload exposes no explicit root (the normal payload only carries the immediate `namespace`), including numeric `projectId`/`GITLAB_DUO_PROJECT_ID` values: the fallback now keys off the project's `path_with_namespace` from the REST payload instead of being blocked by the missing slash, so a numeric id pinning a leaf subgroup project resolves the correct root namespace instead of falling through to remotes or top-level groups. +- Fixed GitLab Duo Workflow model specs to resolve a static `contextWindow` from the model ref family (Claude/Gemini → 1,000,000, default 200,000) instead of leaving it null, so OMP's context panel, usage percentage, and long-context auto-compaction work; GitLab exposes the real window only at runtime in each checkpoint's `agent_context_usage`, which the catalog ModelSpec cannot backfill. +- Fixed GitLab Duo Workflow catalog discovery ignoring `GITLAB_DUO_PROJECT_PATH`: namespace discovery now resolves the configured project from a `projectPath` config field and the `GITLAB_DUO_PROJECT_PATH` env var (in addition to `projectId`/`GITLAB_DUO_PROJECT_ID`), so workspaces that pin a project by path no longer fall through to the wrong group or fail before runtime project handling applies. +- Fixed GitLab Duo Workflow remote project discovery missing the current GitLab project in linked Git worktree checkouts: a worktree's `.git` points at `.git/worktrees/`, whose own `config` holds no remotes — those live in the common directory named by the gitdir's `commondir` file. Discovery now follows `commondir` to read the common `config`, so workspaces in a worktree resolve the correct namespace instead of falling back to top-level group candidates. +- Fixed GitLab Duo Workflow remote project discovery on self-managed GitLab installed under a relative path (e.g. `https://host/gitlab`): HTTPS remotes look like `https://host/gitlab/group/project.git` but project full paths stay `group/project`, so discovery previously queried `/api/v4/projects/gitlab%2Fgroup%2Fproject` and missed the project. The parser now strips the install base path from the remote before deriving the project full path. ## [16.1.23] - 2026-06-26 @@ -18,9 +38,6 @@ ### Added - Added `OpenAICompat.replayReasoningContent` — auto-enabled for the built-in local OpenAI-compatible providers (`llama.cpp`, `lm-studio`, `vllm`, `ollama` on `openai-completions`) and for any provider pointed at a loopback / RFC1918 / `*.local` baseUrl. NOT gated on `spec.reasoning`: the runtime discovery paths for `llama.cpp` / `lm-studio` / `openai-models-list` hardcode `reasoning: false` because the upstream `/models` endpoints don't advertise the capability, while the stream parser still records incoming `reasoning_content` deltas as thinking blocks — gating on the spec flag would leave every discovered local Qwen / DeepSeek model re-triggering #3528. The encoder only writes `reasoning_content` when a thinking block actually exists on the turn, so the flag is a no-op on pure-text histories. Built-in proxy providers (currently `litellm`) are excluded from both checks because they forward to an unrelated upstream that gains no KV-cache benefit and may 400 on the extra field; users running a custom proxy in front of a llama.cpp-style backend can opt in via the sparse `compat.replayReasoningContent: true` override. Signals to the `openai-completions` encoder that preserved `thinking` blocks must be re-emitted as `reasoning_content` on every assistant turn so chat templates that reconstruct `…` from the field (Qwen3, DeepSeek-R1, GLM-5.x) keep the prior turn's tokens byte-stable and llama.cpp's prefix KV cache survives. ([#3528](https://github.com/can1357/oh-my-pi/issues/3528)) -### Fixed - -- Fixed built-in LiteLLM discovery to prefer rich proxy metadata from LiteLLM management endpoints before falling back to /models, with a versioned cache namespace so stale bare-id cache rows do not hide capability data. ## [16.1.20] - 2026-06-25 @@ -35,11 +52,6 @@ - Fixed the Umans GLM-5.2 thinking-level picker collapsing to a single `high` tier after dynamic discovery: the `max` upstream level now resolves to the internal `xhigh` effort, the picker shows both `high` and `xhigh`, and the metadata maps `xhigh` back to Umans's native `max` wire tier. ([#3192](https://github.com/can1357/oh-my-pi/issues/3192)) - Fixed GitHub Copilot business and enterprise endpoints accepting image inputs that they reject with `400 vision is not supported`. The Copilot `/models` response advertises `capabilities.supports.vision = true` for Claude/GPT chat models on every host, but only the canonical personal endpoint (`https://api.githubcopilot.com`) actually serves them; `githubCopilotModelManagerOptions` now forces `input: ["text"]` whenever discovery resolves to a non-personal base URL, and `mergeDynamicModel` honours the dynamic value (instead of OR-upgrading) when the merged endpoint differs from the bundled reference. ([#3387](https://github.com/can1357/oh-my-pi/issues/3387)) - Fixed OpenRouter Anthropic compat to strip Responses reasoning history during replay so signed thinking blocks are not sent back to routed Anthropic providers. ([#3399](https://github.com/can1357/oh-my-pi/issues/3399)) -### Fixed - -- Fixed the bundled catalog omitting the GitLab Duo Agent provider so a fresh install (before any credentialed dynamic discovery populates the cache) could not surface its default model. The generator now seeds the `gitlab-duo-agent` fallback model (`claude_sonnet_4_6_vertex`) into `models.json`, deduped behind live `aiChatAvailableModels` discovery when generation has credentials. -- Fixed GitLab Duo Agent namespace discovery only inspecting the first page of top-level groups, so a token belonging to more than 100 top-level groups could miss a usable Duo namespace on a later page and fail or select the wrong group. Discovery now follows GitLab's `x-next-page` pagination (bounded) and validates candidates across all pages. -- Fixed GitLab Duo Agent namespace discovery rejecting a workspace SSH remote whose port differs from the configured web `baseUrl` (self-managed GitLab commonly exposes SSH on a dedicated port, e.g. `ssh://git@host:2222/group/project.git` against `https://host`). SSH and SCP-style remotes now compare on the bare hostname; HTTP(S) remotes still compare host:port strictly so a different service on the same host is not adopted. ## [16.1.14] - 2026-06-22 @@ -87,27 +99,6 @@ - Fixed Fireworks-hosted Qwen turns (e.g. `fireworks/qwen3.7-plus`) failing with `400 Extra inputs are not permitted, field: 'enable_thinking'`. Fireworks serves Qwen3 with controllable thinking via OpenAI-style `reasoning_effort` and rejects the top-level `enable_thinking` boolean that Alibaba DashScope speaks; `buildOpenAICompat` was selecting `thinkingFormat: "qwen"` from the `qwen` id pattern regardless of host. Fireworks-hosted Qwen models now resolve to `thinkingFormat: "openai"`. - Fixed MiMo models on OpenAI-compatible gateways to expose only accepted `low`, `medium`, and `high` reasoning tiers and map unsupported raw `minimal`/`xhigh` requests to safe wire values. ([#2864](https://github.com/can1357/oh-my-pi/issues/2864)) -### Added - -- Added GitLab Duo Agent catalog discovery for `gitlab-duo-agent`, including namespace selection and live `aiChatAvailableModels` model mapping. - -### Changed - -- Changed GitLab Duo Agent model specs to `reasoning: false`. The Duo Agent Platform path exposes no client-controllable thinking knob (the underlying Anthropic model params are server-fixed), so OMP no longer shows a thinking-effort selector for these models. - -### Fixed - -- Fixed GitLab Duo Workflow runtime namespace discovery so agent startup can resolve a root namespace without requiring live `aiChatAvailableModels` results. -- Fixed GitLab Duo Workflow runtime namespace discovery to preserve namespace paths for Workflow creation, including numeric/GID namespace overrides that must be resolved through GitLab group metadata. -- Fixed GitLab Duo Workflow project namespace discovery to fall back to the GraphQL `rootAncestor` query whenever a REST project payload exposes no explicit root (the normal payload only carries the immediate `namespace`), including numeric `projectId`/`GITLAB_DUO_PROJECT_ID` values: the fallback now keys off the project's `path_with_namespace` from the REST payload instead of being blocked by the missing slash, so a numeric id pinning a leaf subgroup project resolves the correct root namespace instead of falling through to remotes or top-level groups. -- Fixed GitLab Duo Workflow model specs to resolve a static `contextWindow` from the model ref family (Claude/Gemini → 1,000,000, default 200,000) instead of leaving it null, so OMP's context panel, usage percentage, and long-context auto-compaction work; GitLab exposes the real window only at runtime in each checkpoint's `agent_context_usage`, which the catalog ModelSpec cannot backfill. -- Fixed GitLab Duo Workflow catalog discovery ignoring `GITLAB_DUO_PROJECT_PATH`: namespace discovery now resolves the configured project from a `projectPath` config field and the `GITLAB_DUO_PROJECT_PATH` env var (in addition to `projectId`/`GITLAB_DUO_PROJECT_ID`), so workspaces that pin a project by path no longer fall through to the wrong group or fail before runtime project handling applies. -- Fixed GitLab Duo Workflow remote project discovery missing the current GitLab project in linked Git worktree checkouts: a worktree's `.git` points at `.git/worktrees/`, whose own `config` holds no remotes — those live in the common directory named by the gitdir's `commondir` file. Discovery now follows `commondir` to read the common `config`, so workspaces in a worktree resolve the correct namespace instead of falling back to top-level group candidates. -- Fixed GitLab Duo Workflow remote project discovery on self-managed GitLab installed under a relative path (e.g. `https://host/gitlab`): HTTPS remotes look like `https://host/gitlab/group/project.git` but project full paths stay `group/project`, so discovery previously queried `/api/v4/projects/gitlab%2Fgroup%2Fproject` and missed the project. The parser now strips the install base path from the remote before deriving the project full path. - -### Added - -- Added model metadata for provider-native remote compaction and compaction-only model selection. ([#3104](https://github.com/can1357/oh-my-pi/issues/3104)) ## [16.1.7] - 2026-06-20 @@ -259,6 +250,7 @@ - Folded the `azure-openai-responses` API into the OpenAI Responses thinking-inference branches so Azure reasoning models (o-series, GPT-5, Codex) resolve the discrete effort vocabulary (including `xhigh`) and effort-control mode instead of falling through to generic defaults. - Fixed `ollama-cloud` discovery inheriting an unsafe cross-provider `contextWindow`/`maxTokens` when `/api/show` returns no size metadata; it now falls back to the safe 128K context / 8K output caps. - Dropped internal Fireworks control-plane resource ids (`accounts/fireworks/{models,routers}/…`) from the bundle; only the public request ids ship. + ## [15.13.2] - 2026-06-15 ### Added diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 4c69756eb..f748174e3 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -1,12 +1,20 @@ # Changelog ## [Unreleased] + ### Added - Added Loop Guard "Tool-Call Reminder" to automatically interrupt Gemini reasoning loops that generate excessive planning headers without acting - Added support for file deletion and moving within file editing operations - Added `providers.maxInFlightRequests` to cap concurrent LLM requests per provider across local OMP processes from settings. - +- The `/usage` display (TUI, ACP text, and `omp usage` CLI) now shows expiry dates for banked Codex rate-limit resets, so users can plan when to redeem them before they expire ([#3339](https://github.com/can1357/oh-my-pi/issues/3339)). +- Added `ssh://host/path` support to `read`, `search`, and `write` for single text files on pre-configured SSH hosts (or `~/.ssh/config` aliases); POSIX remotes only (Linux/macOS/BSD; Windows hosts are rejected — use the `ssh` tool), UTF-8 text only, up to 1 MiB. Requires the same approval as the `ssh` tool, rejects argument-injecting hosts/usernames (leading `-`), validates the entire file as UTF-8, peels read selectors so `write` targets the same file `read` does, and replaces (rather than writes through) a non-directory symlinked write destination via a uniquely named remote temp. `read` also lists an `ssh://` directory one level deep (dotfiles included, directories first; `ssh://host/` lists the remote root), while `search` refuses an `ssh://` directory and `write` refuses to overwrite one. Bare `ssh://` lists the configured hosts, which are also offered as autocompletions after typing `ssh://`. +- Added an interactive `/move` overlay: typing `/move` with no argument opens a path autocomplete picker (type to filter, ↑↓ to navigate, Tab to accept, Enter to confirm). `/move ` still works for direct invocation. The command now starts a fresh empty session in the target directory instead of relocating the current session file, leaving the previous session resumable via `/resume`. If the target directory does not exist, a confirmation prompt offers to create it. Empty move sessions (no user/assistant messages) are automatically cleaned up on shutdown so they don't accumulate. +- Added Tab completion for `/move` destination directories. Typing `/move ` now offers directory suggestions relative to the current working directory, including support for `~`, absolute, and relative paths. +- Added TinyFish, DuckDuckGo, xAI, and Firecrawl web_search providers. +- Added `models.yml` `remoteCompaction` and `compactionModel` config so custom providers can opt into provider-native compaction and run compaction on a separate model without changing the active session model. ([#3104](https://github.com/can1357/oh-my-pi/issues/3104)) +- Added project/user/plugin `dap.json` and `dap.yaml` support for defining or overriding debugger adapters used by the `debug` tool. ([#2999](https://github.com/can1357/oh-my-pi/issues/2999)) +- Added an Appearance setting for OSC 9;4 native terminal progress indicators during active agent turns and context maintenance. ### Changed @@ -16,22 +24,9 @@ - Automatically migrated existing user settings for `search` and `find` to `grep` and `glob` configs - Changed the `inlineToolDescriptors` setting ("Inline Tool Descriptors") from a boolean to a three-way enum (`auto` | `on` | `off`), defaulting to `auto`. `auto` inlines tool descriptors into the system prompt (and strips them from provider tool schemas) only for Gemini models, leaving them in the schemas otherwise; `on`/`off` force the behavior regardless of model. Existing `true`/`false` configs migrate to `on`/`off`. - Replaced `as string | undefined` inline casts with `typeof` guards in the TUI usage renderer's account identity resolution (`formatAccountLabel`, `formatUnlimitedReportLabel`, reset-credits label, and unlimited-plan tier), so empty-string metadata values fall through to the next fallback instead of being displayed - -### Added - -- The `/usage` display (TUI, ACP text, and `omp usage` CLI) now shows expiry dates for banked Codex rate-limit resets, so users can plan when to redeem them before they expire ([#3339](https://github.com/can1357/oh-my-pi/issues/3339)). - -### Added - -- Added `ssh://host/path` support to `read`, `search`, and `write` for single text files on pre-configured SSH hosts (or `~/.ssh/config` aliases); POSIX remotes only (Linux/macOS/BSD; Windows hosts are rejected — use the `ssh` tool), UTF-8 text only, up to 1 MiB. Requires the same approval as the `ssh` tool, rejects argument-injecting hosts/usernames (leading `-`), validates the entire file as UTF-8, peels read selectors so `write` targets the same file `read` does, and replaces (rather than writes through) a non-directory symlinked write destination via a uniquely named remote temp. `read` also lists an `ssh://` directory one level deep (dotfiles included, directories first; `ssh://host/` lists the remote root), while `search` refuses an `ssh://` directory and `write` refuses to overwrite one. Bare `ssh://` lists the configured hosts, which are also offered as autocompletions after typing `ssh://`. - -### Added - -- Added an interactive `/move` overlay: typing `/move` with no argument opens a path autocomplete picker (type to filter, ↑↓ to navigate, Tab to accept, Enter to confirm). `/move ` still works for direct invocation. The command now starts a fresh empty session in the target directory instead of relocating the current session file, leaving the previous session resumable via `/resume`. If the target directory does not exist, a confirmation prompt offers to create it. Empty move sessions (no user/assistant messages) are automatically cleaned up on shutdown so they don't accumulate. - -### Added - -- Added Tab completion for `/move` destination directories. Typing `/move ` now offers directory suggestions relative to the current working directory, including support for `~`, absolute, and relative paths. +- Reused shared TUI mouse-routing helpers across fullscreen overlay selectors. +- Cached successful document conversions so repeated reads of unchanged PDFs, Office documents, and EPUBs reuse converted markdown instead of rerunning markit conversion. +- Hardened the document conversion cache: random-suffixed temp filenames, orphaned `.tmp` sweeping during prune, and regression coverage for the sweep. ### Fixed @@ -62,12 +57,20 @@ - `ssh://` now strips IPv6 URL brackets before invoking OpenSSH (so `ssh://[::1]/path` targets `::1` instead of failing to resolve), rejects a malformed or out-of-range port (`ssh://host:abc`, `ssh://host:65536`) before connecting instead of treating the bad authority as an opaque default-port host, and `search` no longer drains a remote directory listing only to reject it (it requests directory metadata via `skipDirectoryListing`). - `ssh://` `read`/`search`/`write` now reject a URL query string or fragment (e.g. `ssh://host/tmp/a?draft`, `ssh://host/tmp/a#draft`) instead of silently operating on the path truncated before the `?`/`#`; a literal `?`/`#` in a remote filename must be percent-encoded (`%3F`/`%23`). - `ssh://` `read`/`search`/`write` now restrict transfers to remotes whose login shell is `sh`, `bash`, or `zsh`. A non-POSIX login shell (fish, csh/tcsh) can't parse the POSIX transfer snippets — and csh/tcsh apply `!` history expansion to the command line — so it is refused with a clear error (use the `ssh` tool) instead of running a broken remote command; host-shell detection no longer misclassifies `fish`/`csh`/`tcsh` as `sh`. +- Added models.yml discovery.type: litellm so custom LiteLLM gateways can discover context windows, output caps, vision, and reasoning metadata from LiteLLM before falling back to OpenAI-compatible /models. +- Fixed the main TUI status line hiding live subagents unless users already enabled the `subagents` segment; running subagents now force a hub-key badge into the editor border, and advisor rows in Agent Hub are labeled read-only. ([#3499](https://github.com/can1357/oh-my-pi/issues/3499)) +- Fixed Ctrl+Z hanging the terminal after any tool call had run: the TUI tore down (`ui.stop()`) but the process kept running in `Sl+` state, leaving the user with a dead terminal recoverable only via `kill -9`. The embedded `brush-core` shell behind every bash tool call installs a tokio SIGTSTP listener on `Process::wait` (`crates/brush-core-vendored/src/sys/unix/signal.rs::tstp_signal_listener` → `tokio::signal::unix::signal(SIGTSTP)`); per tokio's contract, the first call for a SignalKind permanently replaces the kernel-default handler for the lifetime of the process. So the first bash invocation — even `/usr/bin/true` — silently overrode SIGTSTP's "stop" default, and `InputController.handleCtrlZ`'s subsequent `process.kill(0, "SIGTSTP")` was swallowed by tokio. The handler now sends `SIGSTOP` (uncatchable, unblockable, unignorable) to the foreground process group, so the kernel parks omp regardless of installed handlers and the shell sees the whole job stop even when omp runs behind a wrapper (`npx`, `pnpm exec`, `bunx`, …) or as one stage of a pipeline. MCP stdio servers now spawn detached into their own session — they're insulated both from terminal job-control signals (which used to stop their process trees and leave the JSONL read loop blocked on silent pipes) and from the new pgid=0 suspend itself ([#3461](https://github.com/can1357/oh-my-pi/issues/3461)). +- Fixed streaming Esc handling so the first Esc arms a 2s cancel hint and only a second Esc in that window aborts the active response ([#3493](https://github.com/can1357/oh-my-pi/issues/3493)). +- Migrated 203 test files from `fs.rm`/`fs.rmSync` to `removeWithRetries`/`removeSyncWithRetries` (356 call sites) to reduce EBUSY test failures on Windows. `removeWithRetries` is now exported from `@oh-my-pi/pi-utils` +- Fixed GitLab Duo Agent namespace/project discovery reading the original repo's git remote after a `/move`. The session's working directory is now resolved live (per LLM call) from the `SessionManager` instead of being captured when the agent was constructed, so moving the session re-scopes Duo workspace discovery to the new repository. +- Fixed `omp auth-broker login gitlab-duo-agent` (and `--via`) hanging until timeout: the provider uses GitLab's fixed `vscode://` OAuth redirect, which never reaches the broker's local callback server, and `runLocalLogin` supplied no `onManualCodeInput` fallback. The broker login now offers the same paste-the-redirect-URL prompt the interactive sign-in uses, so credentials can be saved. +- Fixed the persistent todo HUD above the editor reading as ambient status text rather than an anchored panel — the small "Todos" block now sits inside dim horizontal rules (matching `BtwPanel` / `OmfgPanel`) and the header inlines progress and the active-phase pointer (`Todos · 2/7 done · I/III Foundation`), so the list stays self-describing without scrolling back to the tool-result block in chat ([#3213](https://github.com/can1357/oh-my-pi/issues/3213)). +- Fixed advisor and status-line context when the session cwd is a parent of a single child git repo, so nested-repo work is surfaced before missing parent-cwd paths are treated as destroyed. ([#3130](https://github.com/can1357/oh-my-pi/issues/3130)) ## [16.1.23] - 2026-06-26 ### Added -- Added TinyFish, DuckDuckGo, xAI, and Firecrawl web_search providers. - Added `compaction.midTurnEnabled` for mid-turn threshold auto-compaction before the next tool-loop provider request. ([#3525](https://github.com/can1357/oh-my-pi/issues/3525)) - Added `grep -q`/`--quiet`/`--silent` and `-x`/`--line-regexp` to the in-process `grep` builtin used by the bash tool. `-q` suppresses all stdout and exits 0 on the first match (short-circuiting, with match status taking precedence over read errors per GNU); `-x` anchors each pattern to whole lines. Unblocks shell conditionals such as `grep -qx "$applet" <(strings bin)`. - Added plan-mode guidance (hashline edit mode only) steering the agent to revise the plan file section-by-section with `SWAP.BLK`/`DEL.BLK`/`INS.BLK.POST` anchored on markdown headings — a heading resolves its whole section (through nested deeper headings), so the agent can rewrite, drop, or append sections without rewriting the file. @@ -108,16 +111,12 @@ - Fixed compiled-binary validation for legacy `pi.extensions` packages whose source imports worker-only coding-agent subpaths or extension-local package subpaths such as `typebox/value`; `omp install @charmland/pi-hyper-provider`, `omp plugin doctor`, and runtime provider discovery now use a main-thread-safe load path. ([#3508](https://github.com/can1357/oh-my-pi/issues/3508)) - Fixed repeated todo updates in one TUI turn stacking full todo panels; superseded todo snapshots now stay live until the next todo update replaces them or the turn ends. ([#3516](https://github.com/can1357/oh-my-pi/issues/3516)) - Fixed MCP OAuth authorization failing with `Authorization failed: An unexpected error occurred` against authorization servers (Plane is the live example) that reject redundant fallback `resource` indicators. OMP now drops same-origin resources only when it synthesized them from the server URL fallback (e.g. `https://mcp.plane.so/http/mcp`). Provider-advertised resources from OAuth/protected-resource discovery or an embedded authorization-URL `resource` query parameter are preserved even when they are same-origin or origin-only, so gateway-hosted MCP services can still request the audience they advertised. The refresh-token path uses the same policy, filtered against the authorization-server origin persisted on the credential as `authorizationUrl`, with `tokenUrl`'s origin as the legacy fallback when that field is absent. ([#3502](https://github.com/can1357/oh-my-pi/issues/3502)) -- Added models.yml discovery.type: litellm so custom LiteLLM gateways can discover context windows, output caps, vision, and reasoning metadata from LiteLLM before falling back to OpenAI-compatible /models. ## [16.1.20] - 2026-06-25 ### Fixed - Fixed Ctrl+Z hanging the terminal after any tool call had run: the TUI tore down (`ui.stop()`) but the process kept running in `Sl+` state, leaving the user with a dead terminal recoverable only via `kill -9`. The embedded `brush-core` shell behind every bash tool call installs a tokio SIGTSTP listener on `Process::wait` (`crates/vendor/brush-core/src/sys/unix/signal.rs::tstp_signal_listener` → `tokio::signal::unix::signal(SIGTSTP)`); per tokio's contract, the first call for a SignalKind permanently replaces the kernel-default handler for the lifetime of the process. So the first bash invocation — even `/usr/bin/true` — silently overrode SIGTSTP's "stop" default, and `InputController.handleCtrlZ`'s subsequent `process.kill(0, "SIGTSTP")` was swallowed by tokio. The handler now sends `SIGSTOP` (uncatchable, unblockable, unignorable) to the foreground process group, so the kernel parks omp regardless of installed handlers and the shell sees the whole job stop even when omp runs behind a wrapper (`npx`, `pnpm exec`, `bunx`, …) or as one stage of a pipeline. MCP stdio servers now spawn detached into their own session — they're insulated both from terminal job-control signals (which used to stop their process trees and leave the JSONL read loop blocked on silent pipes) and from the new pgid=0 suspend itself ([#3461](https://github.com/can1357/oh-my-pi/issues/3461)). -- Fixed the main TUI status line hiding live subagents unless users already enabled the `subagents` segment; running subagents now force a hub-key badge into the editor border, and advisor rows in Agent Hub are labeled read-only. ([#3499](https://github.com/can1357/oh-my-pi/issues/3499)) - -- Fixed Ctrl+Z hanging the terminal after any tool call had run: the TUI tore down (`ui.stop()`) but the process kept running in `Sl+` state, leaving the user with a dead terminal recoverable only via `kill -9`. The embedded `brush-core` shell behind every bash tool call installs a tokio SIGTSTP listener on `Process::wait` (`crates/brush-core-vendored/src/sys/unix/signal.rs::tstp_signal_listener` → `tokio::signal::unix::signal(SIGTSTP)`); per tokio's contract, the first call for a SignalKind permanently replaces the kernel-default handler for the lifetime of the process. So the first bash invocation — even `/usr/bin/true` — silently overrode SIGTSTP's "stop" default, and `InputController.handleCtrlZ`'s subsequent `process.kill(0, "SIGTSTP")` was swallowed by tokio. The handler now sends `SIGSTOP` (uncatchable, unblockable, unignorable) to the foreground process group, so the kernel parks omp regardless of installed handlers and the shell sees the whole job stop even when omp runs behind a wrapper (`npx`, `pnpm exec`, `bunx`, …) or as one stage of a pipeline. MCP stdio servers now spawn detached into their own session — they're insulated both from terminal job-control signals (which used to stop their process trees and leave the JSONL read loop blocked on silent pipes) and from the new pgid=0 suspend itself ([#3461](https://github.com/can1357/oh-my-pi/issues/3461)). - Fixed image-only composer submissions while the agent is streaming being treated as empty input, which dropped the image or aborted the active turn when another message was queued. Pending pasted images now count as submit content for Enter and Ctrl+Enter follow-ups. ([#3467](https://github.com/can1357/oh-my-pi/issues/3467)) - Fixed `omp gallery --state` accepting lifecycle tokens that did not match displayed state labels and rendering unknown state values as `· undefined`; displayed labels now work as aliases, invalid values fail with a valid-token list, and failed gallery fixtures visibly render failures. ([#3473](https://github.com/can1357/oh-my-pi/issues/3473)) - Fixed the bash tool's snapshotted `mise()` shell function dying with `command: command not found:` because `$__MISE_EXE` was empty in the replay shell. `generateSnapshotScript` captured the function via `declare -f`/`typeset -f` but only ever re-exported `PATH`, so every other env var the rc file set (notably the `*_EXE` sidecar `mise activate` exports) was lost; the function body then expanded `command "$__MISE_EXE" "$@"` to `command "" …` and died with exit 127. The snapshot script now scans captured function bodies for `$VAR` / `${VAR…}` references and re-emits `export NAME='value'` for each referenced var that is currently set (with a denylist for shell-internal names like `PATH`/`HOME`/`BASH_*`/`LC_*` plus a likely-secret denylist for `*TOKEN*`/`*SECRET*`/`*API_KEY*`/`*PASSWORD*`/`*PRIVATE_KEY*`/`*ACCESS_KEY*`/`*CREDENTIAL*`/`*SESSION_KEY*`), the snapshot script `umask 077`s itself and the JS caller chmods the snapshot file/dir to `0600`/`0700` so the new export pass can't leak secrets into a shared tmp dir. Fixes mise, asdf shims, direnv-style helpers, and other activation idioms that pair a function with a helper env var. `getShellConfigFile` now also honours `env.HOME` (falling back to `os.homedir()`) so sandboxed callers can target a non-default rc. ([#3470](https://github.com/can1357/oh-my-pi/issues/3470)) @@ -128,10 +127,6 @@ - Fixed a background-task spawn slot leaking from the `task.maxConcurrency` limiter when progress reporting threw between acquiring the slot and entering the guarded run: `markRunning`/`reportProgress` now run inside the try whose `finally` releases the semaphore, so a failed progress report can no longer permanently shrink subagent concurrency. ([#3464](https://github.com/can1357/oh-my-pi/issues/3464)) - Fixed active goal runs that successfully call `yield` and then receive a trailing empty assistant `stop` skipping threshold compaction; post-yield empty-stop suppression now still anchors active-goal compaction on the yield-bearing assistant turn, so long-running tasks continue after maintenance instead of settling early. -### Fixed - -- Fixed streaming Esc handling so the first Esc arms a 2s cancel hint and only a second Esc in that window aborts the active response ([#3493](https://github.com/can1357/oh-my-pi/issues/3493)). - ## [16.1.19] - 2026-06-25 ### Fixed @@ -172,7 +167,6 @@ - Fixed the `ask` tool's "Other (type your own)" free-text editor (prompt-style `HookEditorComponent`) ignoring Ctrl+Q and Ctrl+Enter, so Windows Terminal users who learned the `app.message.followUp` chord from the main editor (#1903 / fixed by #1905) got zero feedback on submit. The hook-style and main-editor surfaces honored `matchesAppFollowUp`; the prompt-style handler did not, leaving plain Enter as the sole submit path and Ctrl+Enter falling through to Editor as a newline (silently swallowed by WT). `#handlePromptStyleInput` now checks `matchesAppFollowUp` first — mirroring `#handleHookStyleInput` — and the hint reads `enter or ctrl+q submit` so the chord is discoverable. ([#3353](https://github.com/can1357/oh-my-pi/issues/3353)) - Fixed the TUI freezing when a tool approval prompt fires while `/settings` (or the Extensions/Agents dashboard) is open. The fullscreen overlay's close handler restored focus to the editor it had captured at open time, but `ExtensionUiController` had since swapped the editor out of the editor slot for the approval prompt — so on exit the visible prompt sat unreachable while keystrokes routed to the now-unmounted editor (no Enter/Up/Down/Esc response, only Ctrl+C escaped). `SelectorController` now restores focus to whatever currently owns the editor slot via a `focusActiveEditorArea()` helper, applied to settings, extensions dashboard, and agents dashboard close paths. ([#3349](https://github.com/can1357/oh-my-pi/issues/3349)) - Fixed `/settings` coercing enum/text values to display strings before handing them to the TUI list, preventing YAML numeric enum values from reaching native truncation ([#3338](https://github.com/can1357/oh-my-pi/issues/3338)). -- Migrated 203 test files from `fs.rm`/`fs.rmSync` to `removeWithRetries`/`removeSyncWithRetries` (356 call sites) to reduce EBUSY test failures on Windows. `removeWithRetries` is now exported from `@oh-my-pi/pi-utils` - Fixed all extension loading silently failing on the cross-compiled `omp-darwin-arm64` release binary (downloaded directly or via a Homebrew tap wrapper) because `__computeBunfsPackageRoot` mis-handled `import.meta.dir = "//root/omp-darwin-arm64"`. Bun 1.3.14 reports `/` for the compiled entry's `import.meta.dir`, but the pre-fix function joined `metaDir + "packages"` and produced `/root/omp-darwin-arm64/packages` — the binary basename was baked into every bunfs path, so the TypeBox/legacy-pi shims and every `@oh-my-pi/pi-*` package-root override failed `existsSync` validation and `resolveCanonicalPiSpecifier` fell through to a bunfs `Bun.resolveSync` that also could not find the module. The function now detects the bunfs-root + binary-basename shape (`path.basename(path.dirname(metaDir)) === "root"`) and strips the trailing binary segment by slicing the original `metaDir`; the production bunfs shim join path also preserves Bun's bunfs-native `//root` / `B:\~BUN\root` prefix that `path.join` would otherwise collapse. ([#3329](https://github.com/can1357/oh-my-pi/issues/3329)) - Fixed llama.cpp discovery to prefer per-model `/v1/models` `meta.n_ctx`/`meta.n_ctx_train` values, refresh selected models after lazy load, and bypass fresh-cache reuse so server restarts update context windows. ([#3310](https://github.com/can1357/oh-my-pi/issues/3310)) - Fixed `task.maxConcurrency: 0` serializing subagent spawns instead of running them unbounded. The settings UI labels `0` as "Unlimited", but the session-scoped spawn `Semaphore` clamped `max` via `Math.max(1, max)`, so the second subagent body in a batch always waited for the first to release the seat. The constructor now treats `max <= 0` (and any non-finite input) as unbounded via `Number.POSITIVE_INFINITY`, matching the eval `parallel()`/`pipeline()` worker-pool semantics ([#3305](https://github.com/can1357/oh-my-pi/issues/3305)). @@ -190,13 +184,6 @@ - Fixed extension `tool_call`/`tool_result` events for hashline `edit` calls to expose `event.input.path` for single-file edits and `event.input.paths` for every parsed target, so planning-mode gates can allow one markdown plan edit but still block multi-file hashline calls that cannot be represented by one path ([#1678](https://github.com/can1357/oh-my-pi/issues/1678)). - Fixed scripted `eval` `agent()` subagents continuing after a successful `yield` when a trailing empty assistant `stop` arrived after the executor's yield-triggered abort. The session's `agent_end` maintenance compared `#assistantEndedWithSuccessfulYield(msg)` against the trailing empty-stop message — not the prior yield-bearing one — so the empty-stop recovery path appended a retry reminder and scheduled `agent.continue()`, reviving the already-yielded child. The yield handler now sets a sticky `#yieldTerminationPending` flag (cleared on the next `prompt()`) that short-circuits empty-stop / unexpected-stop / compaction continuations for the rest of the run, so a successful yield is terminal regardless of trailing stops ([#3389](https://github.com/can1357/oh-my-pi/issues/3389)). - Fixed snapcompact rasterizing transcript frames into requests bound for GitHub Copilot business and enterprise endpoints, which then rejected the session permanently with `400 vision is not supported`. The snapcompact vision gate now also short-circuits whenever `model.provider === "github-copilot"` and the resolved `baseUrl` is not the canonical personal-Copilot host, protecting cached/stale Model specs that still advertise `["text","image"]` on a non-personal endpoint. ([#3387](https://github.com/can1357/oh-my-pi/issues/3387)) -- Fixed GitLab Duo Agent namespace/project discovery reading the original repo's git remote after a `/move`. The session's working directory is now resolved live (per LLM call) from the `SessionManager` instead of being captured when the agent was constructed, so moving the session re-scopes Duo workspace discovery to the new repository. -- Fixed `omp auth-broker login gitlab-duo-agent` (and `--via`) hanging until timeout: the provider uses GitLab's fixed `vscode://` OAuth redirect, which never reaches the broker's local callback server, and `runLocalLogin` supplied no `onManualCodeInput` fallback. The broker login now offers the same paste-the-redirect-URL prompt the interactive sign-in uses, so credentials can be saved. - -### Changed - -- Reused shared TUI mouse-routing helpers across fullscreen overlay selectors. - ## [16.1.16] - 2026-06-23 @@ -266,10 +253,6 @@ - Fixed configured model discovery caches to refresh when `models.yml`/`models.json` is newer than the cached row, so updated local model metadata is not shadowed by fresh `models.db` entries. ([#3242](https://github.com/can1357/oh-my-pi/issues/3242)) - Fixed hide-secrets handling so advisor session updates are redacted before the advisor model sees them and opaque assistant thinking blocks are no longer deobfuscated. - Filtered alias definitions brush's whitespace-only expander cannot execute (`(`, `)`, `|`, `&`, `;`, `<`, `>`, `` ` ``) from the bash-tool shell snapshot, so user rc-files containing compound aliases like Fedora's default `which='(alias; declare -f) | /usr/bin/which …'` no longer poison the brush session with `error: command not found: (alias;` ([#3234](https://github.com/can1357/oh-my-pi/issues/3234)). -### Changed - -- Cached successful document conversions so repeated reads of unchanged PDFs, Office documents, and EPUBs reuse converted markdown instead of rerunning markit conversion. -- Hardened the document conversion cache: random-suffixed temp filenames, orphaned `.tmp` sweeping during prune, and regression coverage for the sweep. ## [16.1.14] - 2026-06-22 @@ -312,11 +295,6 @@ - Fixed secret obfuscation corrupting Codex image reads (and other provider requests) with `Invalid 'input[N].content[].image_url'. Expected a base64-encoded data URL ... but got an invalid base64-encoded value`. The obfuscator deep-walked every string in the outbound request — including inline image base64 and opaque provider replay/signature fields — so a configured secret that happened to be a substring of the base64 (or of an ordinary word like `response`) injected `#HASH#` placeholders mid-payload. Obfuscation is now opt-in and fully typed: only user messages, tool-result messages, and user-attributed developer messages (`@file` mentions) are redacted; system prompts and tool schemas pass through untouched; image bytes and signature/encrypted-reasoning fields are never rewritten; and tool-call arguments are the only JSON walked. Configured plain secrets and regex matches shorter than 8 characters are now ignored to stop false matches on short words. - Fixed RPC/ACP startup clobbering explicit caller/project/global configuration for `task.isolation.{mode,merge,commits}`, `task.eager`, `task.batch`, `task.maxConcurrency`, `task.maxRecursionDepth`, `task.disabledAgents`, `task.agentModelOverrides`, `memory.backend`, `memories.enabled`, `advisor.{enabled,subagents,syncBacklog,immuneTurns}`, plus the RPC-only `async.{enabled,maxJobs}` and `bash.autoBackground.{enabled,thresholdMs}`. `applyDefaultSettingOverrides` re-asserted the schema default as a runtime override after settings load, regressing the `isConfigured()` guard added for #2598 and ignoring every explicit value the embedder, project, `--config` overlay, or global config had set. The guard is restored, so the host default now only fills holes ([#3207](https://github.com/can1357/oh-my-pi/issues/3207)). - -### Fixed - -- Fixed the persistent todo HUD above the editor reading as ambient status text rather than an anchored panel — the small "Todos" block now sits inside dim horizontal rules (matching `BtwPanel` / `OmfgPanel`) and the header inlines progress and the active-phase pointer (`Todos · 2/7 done · I/III Foundation`), so the list stays self-describing without scrolling back to the tool-result block in chat ([#3213](https://github.com/can1357/oh-my-pi/issues/3213)). - ## [16.1.11] - 2026-06-21 ### Added @@ -388,10 +366,6 @@ - Removed `/debug dump-next-request` command - Removed Wafer Pass from CLI credential help; Wafer Serverless remains available. -### Fixed - -- Fixed advisor and status-line context when the session cwd is a parent of a single child git repo, so nested-repo work is surfaced before missing parent-cwd paths are treated as destroyed. ([#3130](https://github.com/can1357/oh-my-pi/issues/3130)) - ## [16.1.8] - 2026-06-20 ### Added @@ -438,10 +412,6 @@ - Fixed auto-compaction being suppressed when a `before_provider_request` extension shrinks the outgoing request below the real stored conversation (e.g. a context-compression proxy such as Headroom, or an aggressive obfuscator). The provider then reports deflated prompt tokens, so the threshold check never fired and the stored history grew unbounded until it overflowed the context window and could no longer be compacted at all. The compaction decision (both the pre-prompt and post-response paths) now floors the provider-reported context tokens by the agent's own local estimate of the stored conversation, so on-wire compression can no longer hide a too-large history from the auto-compactor. Context display and cost accounting still use the exact provider usage; only the compaction trigger takes the floor. - Fixed mnemopi proactive linking being configurable only through the `MNEMOPI_PROACTIVE_LINKING` environment variable, unlike the sibling `mnemopi.polyphonicRecall` / `mnemopi.enhancedRecall` settings: added a `mnemopi.proactiveLinking` config.yml setting (off by default, `/settings` → Memory → Mnemopi) that ingests new memories into the episodic graph as they are stored, linking them to related entities and memories; `MNEMOPI_PROACTIVE_LINKING` still overrides the configured value when set ([#2440](https://github.com/can1357/oh-my-pi/issues/2440)). -### Added - -- Added `models.yml` `remoteCompaction` and `compactionModel` config so custom providers can opt into provider-native compaction and run compaction on a separate model without changing the active session model. ([#3104](https://github.com/can1357/oh-my-pi/issues/3104)) - ## [16.1.7] - 2026-06-20 ### Fixed @@ -454,9 +424,6 @@ - Enabled inline prompts with `/loop` commands (e.g., `/loop 10 fix the bug`) - Added support for compound duration formats in `/loop` (e.g., `1h30m`) -### Added - -- Added project/user/plugin `dap.json` and `dap.yaml` support for defining or overriding debugger adapters used by the `debug` tool. ([#2999](https://github.com/can1357/oh-my-pi/issues/2999)) ## [16.1.5] - 2026-06-19 @@ -478,10 +445,6 @@ - Fixed image paste placeholders falling through to terminal hyperlink settings before `Settings.init()`, so early editor rendering falls back to plain text instead of crashing. ([#3064](https://github.com/can1357/oh-my-pi/issues/3064)) - Fixed `omp plugin install github:owner/repo` silently keeping the user on a stale commit when re-run on an already-installed GitHub plugin. `bun install ` respects the existing `bun.lock` pin when the spec is unchanged and never re-resolves the remote ref, so the manager now follows a git re-install with `bun update ` to refresh the lockfile pin against the upstream. The install transaction also snapshots `bun.lock` up front and routes feature validation, extension validation, and runtime-config save through one rollback path so a failed install can never leave the rejected commit pinned in the active tree or lockfile. First-time installs are unaffected. ([#3063](https://github.com/can1357/oh-my-pi/issues/3063)) -### Added - -- Added an Appearance setting for OSC 9;4 native terminal progress indicators during active agent turns and context maintenance. - ## [16.1.3] - 2026-06-19 ### Changed @@ -12608,4 +12571,4 @@ Initial public release. ## [0.7.6] - 2025-11-13 -Previous releases did not maintain a changelog. \ No newline at end of file +Previous releases did not maintain a changelog. diff --git a/packages/coding-agent/src/slash-commands/builtin-registry.ts b/packages/coding-agent/src/slash-commands/builtin-registry.ts index 315586223..83cce6db9 100644 --- a/packages/coding-agent/src/slash-commands/builtin-registry.ts +++ b/packages/coding-agent/src/slash-commands/builtin-registry.ts @@ -1,4 +1,5 @@ import * as fs from "node:fs/promises"; +import * as os from "node:os"; import * as path from "node:path"; import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth"; import { type AutocompleteItem, Spacer } from "@oh-my-pi/pi-tui"; diff --git a/packages/coding-agent/test/collab/guest-subagent-badge.test.ts b/packages/coding-agent/test/collab/guest-subagent-badge.test.ts index c63f54a55..1683ea778 100644 --- a/packages/coding-agent/test/collab/guest-subagent-badge.test.ts +++ b/packages/coding-agent/test/collab/guest-subagent-badge.test.ts @@ -120,7 +120,7 @@ function makeState(): Extract["state"] { queuedMessageCount: 0, sessionName: "host session", cwd: "/tmp", - participants: [{ id: "host", name: "Host", role: "host" }], + participants: [{ name: "Host", role: "host" }], }; } diff --git a/packages/coding-agent/test/modes/controllers/usage-command.test.ts b/packages/coding-agent/test/modes/controllers/usage-command.test.ts index 7769ed268..c6af0c95f 100644 --- a/packages/coding-agent/test/modes/controllers/usage-command.test.ts +++ b/packages/coding-agent/test/modes/controllers/usage-command.test.ts @@ -88,10 +88,7 @@ describe("CommandController /usage", () => { metadata: { email: "user@example.com" }, resetCredits: { availableCount: 2, - credits: [ - { id: "future-reset", expiresAt: futureIso }, - { id: "expired-reset", expiresAt: expiredIso }, - ], + credits: [{ expiresAt: futureIso }, { expiresAt: expiredIso }], }, }, ]; diff --git a/packages/coding-agent/test/usage-cli.test.ts b/packages/coding-agent/test/usage-cli.test.ts index 5d8cb1cd6..113ea1dc0 100644 --- a/packages/coding-agent/test/usage-cli.test.ts +++ b/packages/coding-agent/test/usage-cli.test.ts @@ -223,7 +223,7 @@ describe("formatUsageBreakdown", () => { metadata: { email: "future@example.test" }, resetCredits: { availableCount: 1, - credits: [{ id: "future-reset", expiresAt: "2026-01-03T00:00:00.000Z" }], + credits: [{ expiresAt: "2026-01-03T00:00:00.000Z" }], }, }, { @@ -233,7 +233,7 @@ describe("formatUsageBreakdown", () => { metadata: { email: "expired@example.test" }, resetCredits: { availableCount: 1, - credits: [{ id: "expired-reset", expiresAt: "2025-12-30T00:00:00.000Z" }], + credits: [{ expiresAt: "2025-12-30T00:00:00.000Z" }], }, }, ]; diff --git a/packages/collab-web/CHANGELOG.md b/packages/collab-web/CHANGELOG.md index 4854505f3..f30352b91 100644 --- a/packages/collab-web/CHANGELOG.md +++ b/packages/collab-web/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [Unreleased] + ### Added - Added dedicated renderers for `glob` and `grep` tools to improve result readability @@ -142,4 +143,4 @@ ### Security -- Hardened transcript Markdown rendering by escaping embedded HTML and allowing only safe link schemes \ No newline at end of file +- Hardened transcript Markdown rendering by escaping embedded HTML and allowing only safe link schemes diff --git a/packages/hashline/CHANGELOG.md b/packages/hashline/CHANGELOG.md index f678c84d7..df00706ee 100644 --- a/packages/hashline/CHANGELOG.md +++ b/packages/hashline/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [Unreleased] + ### Added - Added `REM` section op to delete files @@ -317,4 +318,4 @@ All notable changes to this package will be documented in this file. - Fixed repeated patch application mutating cached `after_anchor` edits between target snapshots - Fixed multi-section patching to preflight write policies and reject duplicate canonical targets before any section is committed -- Fixed mixed line-ending restoration to preserve the first newline style instead of rewriting ties to LF \ No newline at end of file +- Fixed mixed line-ending restoration to preserve the first newline style instead of rewriting ties to LF diff --git a/packages/tui/CHANGELOG.md b/packages/tui/CHANGELOG.md index 373cf641d..c78cc5eca 100644 --- a/packages/tui/CHANGELOG.md +++ b/packages/tui/CHANGELOG.md @@ -7,6 +7,8 @@ - Added support for rendering HTML `` tags as theme-styled inline code blocks - Added support for rendering HTML `
` tags as horizontal rules - Added support for rendering HTML `
` tags with appropriate quote styling +- Recognized Warp (`TERM_PROGRAM=WarpTerminal`) as a first-class terminal. Inline images now negotiate the Kitty graphics protocol on macOS/Linux (direct placement — Warp has no Unicode-placeholder support); the protocol is dropped on Windows, where Warp ships without Kitty support and the APC sequences would render as visible garbage. True color is enabled. OSC 8 hyperlinks stay off by default because Warp's renderer prints the escape as literal text rather than a clickable link (opt in with `PI_FORCE_HYPERLINKS=1` once Warp lands real support), and synchronized output remains gated on the runtime DECRQM probe ([#3471](https://github.com/can1357/oh-my-pi/issues/3471)). +- Added shared SGR mouse input routing helpers and `SelectList.routeMouse()` support for fullscreen overlay hit-testing. ### Fixed @@ -14,6 +16,7 @@ - Fixed stray or unmatched HTML tags leaking into rendered output - Improved layout consistency by correctly handling HTML block-level tags in various contexts - Markdown renderer now handles inline `…` (rendered as themed inline code, identical to a backtick codespan, with HTML entities like `&` decoded), block `
` (rendered as a horizontal rule), and balanced single-line `
…
` (rendered with the quote border) instead of leaking the raw tags as literal text. Applies to the transcript renderer, table cells, list items, and the inline `renderInlineMarkdown` helper used for option labels; fenced code blocks keep such markup verbatim. +- Fixed ordinary render scheduling to yield behind already-queued terminal input, preventing delayed Esc delivery during heavy streaming paints ([#3493](https://github.com/can1357/oh-my-pi/issues/3493)). ## [16.1.20] - 2026-06-25 @@ -22,14 +25,6 @@ - Recognized Warp (`TERM_PROGRAM=WarpTerminal`) as a first-class terminal, enabling Kitty inline images on macOS/Linux while keeping Warp's unsafe OSC 8 hyperlinks and Windows Kitty graphics disabled ([#3471](https://github.com/can1357/oh-my-pi/issues/3471)). - Kept queued interrupt keys ahead of ordinary repaints so a slow long-transcript frame cannot consume the Ctrl+C/Esc double-press window before the second key is handled. -### Added - -- Recognized Warp (`TERM_PROGRAM=WarpTerminal`) as a first-class terminal. Inline images now negotiate the Kitty graphics protocol on macOS/Linux (direct placement — Warp has no Unicode-placeholder support); the protocol is dropped on Windows, where Warp ships without Kitty support and the APC sequences would render as visible garbage. True color is enabled. OSC 8 hyperlinks stay off by default because Warp's renderer prints the escape as literal text rather than a clickable link (opt in with `PI_FORCE_HYPERLINKS=1` once Warp lands real support), and synchronized output remains gated on the runtime DECRQM probe ([#3471](https://github.com/can1357/oh-my-pi/issues/3471)). - -### Fixed - -- Fixed ordinary render scheduling to yield behind already-queued terminal input, preventing delayed Esc delivery during heavy streaming paints ([#3493](https://github.com/can1357/oh-my-pi/issues/3493)). - ## [16.1.19] - 2026-06-25 ### Fixed @@ -49,9 +44,6 @@ - Fixed `@`-path autocomplete failing on Windows for paths outside the cwd. Windows absolute paths (e.g. `C:\\Users\\...`) were not detected as absolute — only `/` was checked — so they were incorrectly joined with the base directory, producing invalid search paths and empty suggestions. Path-join calls also introduced backslashes into suggestion values, breaking round-trip insertion. Absolute path detection now uses `path.isAbsolute()` (handles drive letters) and suggestion paths are normalized to forward slashes (valid on all platforms). - Fixed settings rows crashing native text truncation when a malformed config value reaches the renderer as a non-string ([#3338](https://github.com/can1357/oh-my-pi/issues/3338)). - Fixed desktop notifications being silently lost under tmux on the common stack of tmux + kitty/ghostty/wezterm/iTerm2. `TERMINAL_ID` resolves to the inner terminal (whose markers leak into the tmux session env), which maps to `NotifyProtocol.Osc9` / `NotifyProtocol.Osc99`, and `sendNotification()` wrote that raw OSC straight to stdout — tmux dropped it on the floor and `monitor-bell` / `monitor-activity` never fired, so a backgrounded omp pane had no way to flag completion or `ask` blockage. Under `TMUX`, OSC-protocol notifications are now wrapped in tmux's `\x1bPtmux;…\x1b\\` DCS passthrough envelope (so users with `set -g allow-passthrough on` still get the real toast on the outer terminal) and followed by a `\x07` BEL (so `set -g monitor-bell on` reliably flags the window otherwise). The OSC 99 capability probe in `terminal.ts` is wrapped the same way so rich notifications keep working across tmux. `NotifyProtocol.Bell` paths are unchanged. ([#3395](https://github.com/can1357/oh-my-pi/issues/3395)) -### Added - -- Added shared SGR mouse input routing helpers and `SelectList.routeMouse()` support for fullscreen overlay hit-testing. ## [16.1.10] - 2026-06-21 diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index efc24003d..95d5fa88c 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -1,25 +1,20 @@ # Changelog ## [Unreleased] + ### Added - Added a relaxed JSON parser that supports single-quoted strings, unquoted keys, and comments - Added `parseStreamingJson` for robust parsing of truncated or malformed streaming JSON - Added `parseStreamingJsonThrottled` for efficient processing of incremental streaming updates +- Added an XDG-aware document conversion cache directory helper for coding-agent document reads. +- Exported `removeWithRetries()` as a standalone async function so tests with async cleanup hooks (`afterEach(async () => …)`) can use the same retry-on-EBUSY cleanup logic as `TempDir.remove()`. Previously only the sync variant was exported, forcing async tests to use `fs.rm` directly — which fails with EBUSY on Windows when SQLite database files are still locked. ### Changed - Reworked streaming SSE JSON (`readSseJson`) to recover a truncated or lightly malformed final event through the shared streaming JSON parser (relocated here from `@oh-my-pi/pi-ai`), ending the stream cleanly on a cut-off tail instead of throwing. Non-container final events (provider error text, bare scalars) still surface as a `SyntaxError`. - Increased the EBUSY retry delay from 25ms to 50ms (40 retries × 50ms = 2s total window, up from 1s). Windows can hold file locks on SQLite databases for up to ~1.5s after `close()`, and the previous 1-second window was too short for some test cleanup scenarios — `settings-manager.test.ts` and `sdk-credential-disabled-bridge.test.ts` still failed with EBUSY even when using `removeSyncWithRetries`. -### Added - -- Added an XDG-aware document conversion cache directory helper for coding-agent document reads. - -### Added - -- Exported `removeWithRetries()` as a standalone async function so tests with async cleanup hooks (`afterEach(async () => …)`) can use the same retry-on-EBUSY cleanup logic as `TempDir.remove()`. Previously only the sync variant was exported, forcing async tests to use `fs.rm` directly — which fails with EBUSY on Windows when SQLite database files are still locked. - ## [16.1.8] - 2026-06-20 ### Added @@ -210,4 +205,4 @@ ### Added -- Added an XDG-aware tiny-title model cache directory helper for coding-agent local title models. \ No newline at end of file +- Added an XDG-aware tiny-title model cache directory helper for coding-agent local title models.