Commit Graph
444 Commits
Author SHA1 Message Date
can1357 cfabeeb17c feat(web): added Codex and Gemini web search providers with shared AgentStorage flow
- Added OpenAI Codex and Gemini web search provider options with updated setup/auth descriptions.
- Updated Codex OAuth flow to refresh near-expiry tokens during web_search and persist the refreshed credentials.
- Plumbed AgentStorage through search orchestrator, scrapers, and fetch paths so providers share session credentials.
- Refactored web provider and credential helpers to accept caller-provided AgentStorage and resolve keys synchronously.
2026-05-25 21:21:13 +02:00
Can BölükandGitHub d201442a16 Merge pull request #1372 from can1357/farm/e4c67c73/fix-subagent-session-start-busy
fix(agent): prevent subagent session_start busy race
2026-05-25 21:59:38 +03:00
roboomp 1217091557 fix(agent): prevented subagent session_start busy race
Queued extension-delivered user messages when deliverAs is set and waited for session_start extension message sends before prompting subagents.

Fixes #1343
2026-05-25 18:48:06 +00:00
can1357 80186e341c feat(agent): threaded intentTracing option through append-only context
- Exported `normalizeTools` so `AppendOnlyContext` uses the same tool normalization as the agent loop.
- Added `BuildOptions.intentTracing` to `build()`/`reset()`/`takeSnapshot()` so intent injection is consistent and included in the prefix fingerprint.
- Improved `#computeDigest` to cover tool_calls, tool_call_id, name, and id fields to catch in-place mutations.
- Fixed `#unsubscribeAppendOnly` leak and added no-op guard in `#syncAppendOnlyContext`.
2026-05-25 14:06:44 +02:00
can1357 79f6bf4f76 fix(session-manager): added orphaned backup recovery after EPERM rename
- Added `recoverOrphanedBackups` to promote `.jsonl..bak` files back to their primary path when the primary is missing, preventing data loss after a mid-rename crash.
- Changed backup filename from dot-prefixed to plain `..bak` so the shared `*.bak` glob can find it on both real and in-memory storage backends.
- Surfaced the original EPERM as the error `cause` and included both original and retry messages when rollback also fails.
2026-05-25 14:05:41 +02:00
can1357 3801b4ee32 fix(coding-agent): added configurable retry delay cap and surfaced rate-limit failure state
- Added `retry.maxDelayMs` to the settings schema and interfaces, with a default cap for provider backoff delays.
- Updated session auto-retry logic to fail fast when a requested wait exceeds the cap without fallback, emitting terminal auto-retry failure state.
- Propagated retry state and failure data into task progress and rendering so children show retry/wait details and reminder prompts stop after terminal errors.
2026-05-25 12:37:32 +02:00
Can BölükandGitHub 98603c669b Merge branch 'main' into farm/7f8acce1/loop-mode-can-auto-submit-again-before-t 2026-05-25 12:58:57 +03:00
Can BölükandGitHub 2305690a03 Merge pull request #1339 from can1357/farm/fec0dca1/session-compaction-rewrite-can-fail-with
fix(session): handle EPERM during session rewrite
2026-05-25 12:57:05 +03:00
Brit c93126ee3f fix: re-evaluate append-only mode on setting changes (not just model switch) 2026-05-24 22:48:12 +02:00
Brit e71d2ff49e fix: detect content rewrites in syncMessages, reset append-only cache on model switch 2026-05-24 22:39:20 +02:00
Brit 648bbdc163 fix(agent): passed AbortSignal to transformContext and re-evaluated append-only on model switch 2026-05-24 22:28:38 +02:00
roboomp 84eb837b00 fix(session): handled eperm during session rewrite
Replaced overwrite-style session rewrites with an EPERM fallback that moves the old session file aside before retrying and restores it if the retry fails.

Added regression coverage for active-session rewrite recovery so the session remains writable after the fallback.

Fixes #1337
2026-05-24 18:00:42 +00:00
roboomp 5573270e5e fix(loop-mode): block auto-submit while post-prompt background work is pending
When session.prompt() returns, idle-flush tasks for async-job result
deliveries are scheduled via #schedulePostPromptTask (1ms delay) and
added to #postPromptTasks immediately.  The 800ms loop timer could fire
in that window before isStreaming became true, causing the loop to
submit the next prompt while the delivery turn was still pending.  The
delivery then hit AgentBusyError and the job result was silently dropped.

Add AgentSession.hasPostPromptWork (= #postPromptTasks.size > 0) and
include it in #isLoopAutoSubmitBlocked() alongside isStreaming and
isCompacting.  Add a regression test that verifies the loop defers when
hasPostPromptWork is true and fires once it becomes false.

Fixes #1294
2026-05-22 13:41:17 +00:00
can1357andCan Bölük 796f963da9 feat(coding-agent): added coding-agent follow-up queue with onBeforeYield
- Added optional `onBeforeYield` configuration and `setOnBeforeYield` in Agent, executed before follow-up checks.
- Added `YieldQueue` to `AgentSession`, with setup/teardown and streaming/idle flush via `setOnBeforeYield`.
- Replaced immediate async-result follow-up dispatch with queued batch entries, including stale-state suppression.
- Added MCP follow-up queueing in SDK, deduplicating updates by `serverName` and `uri`.
- Added changelog entries for `onBeforeYield`, async-result batching, MCP dedupe, and `display.shimmer` modes.
- Added yield queue unit tests for streaming emission, debounced idle batches, stale filtering, and error isolation.
2026-05-22 13:08:51 +09:00
roboomp 1b6ef3b5dc fix(coding-agent): respected google retry hints
Parsed provider retry hint text before falling back to quota cooldowns so Google per-minute token limits use the server-provided delay.

Fixes #1253
2026-05-21 09:38:43 +00:00
can1357 3d96fd0d9e fix(coding-agent): fixed compaction to prefer active session model over role default
- Added the active session model to compaction candidate selection before role-based candidates.
- Updated compaction routing so role-based models are only considered after the current chat model.
- Added a regression test proving an Anthropic session prefers its active model over `modelRoles.default` on OpenAI.
2026-05-21 16:37:44 +09:00
can1357andCan Bölük fd42c8da26 fix(coding-agent): stop mutating todo state via timer-based autoclear
#scheduleTodoAutoClear / #runTodoAutoClear used to splice completed and
abandoned tasks out of #todoPhases on a 60s (later 30min) timer. The
mutation made earlier completions vanish from phase counts ("5 tasks"
dropped to 4) and contradicted the model's own claim of progress.

The autoclear path is removed entirely. Canonical #todoPhases is only
mutated by explicit todo_write calls. formatSummary's denominator
(`current.tasks.length`) now stays stable across tool calls, so phase
counts include completed tasks until the model explicitly removes them.

Leaves the `tasks.todoClearDelay` setting in place (inert) to avoid
changing the schema in this patch.
2026-05-21 15:22:46 +09:00
can1357andCan Bölük d30dc78409 fix(coding-agent): clean up ephemeral irc reply turn
Three coordinated tweaks in runEphemeralTurn and the supporting
#buildEphemeralSnapshot so IRC reply text stops leaking tool-call
markup, duplicating verbatim, and breaking DeepSeek-class encoders:

- Drop the recipient's tools array entirely instead of relying on
  toolChoice:"none" (not every backend enforces it). The model now has
  no tool surface to emit so leaked function_call / DSML markup stops.
- Preserve thinking content blocks when snapshotting the in-flight
  streaming assistant message so the openai-completions encoder can
  re-emit reasoning_content for DeepSeek-routed recipients (10 reports
  of HTTP 400 "'reasoning_content' in thinking mode must be passed
  back").
- Collapse consecutive duplicate sentences in replyText and cap reply
  length so a looping recipient does not spam the IRC channel with the
  same line repeated N times.
2026-05-19 19:24:16 +09:00
can1357andCan Bölük 965bd095a5 fix(coding-agent): keep completed todo tasks visible for the full turn
The 60s autoclear was mutating canonical #todoPhases via setTimeout, so
earlier completions vanished from the model's view of phase progress.
Default delay bumped well above any plausible turn duration and a
dedup helper added so the canonical list remains intact until the next
explicit prompt boundary.
2026-05-19 19:24:16 +09:00
can1357andCan Bölük 3e567b1852 fix(coding-agent): stop dropping rewind checkpoint on every aborted message
The unconditional clear of #checkpointState on stopReason==="aborted"
fired on user interrupts, TTSR rule injection, streaming-edit guards,
plan-compact, and auto-compaction, silently dropping the user's
checkpoint with no signal to the model. Downstream #applyRewind already
tolerates message-count drift via its safeCount clamp, so the clear is
safe to remove. Accounts for 100% of rewind tool grievances.
2026-05-19 19:24:16 +09:00
Can Bölük 899983859e Merge remote-tracking branch 'origin/main' 2026-05-19 18:18:34 +09:00
can1357 94803877bd fix(coding-agent): fixed Anthropic fast-mode detection and scoped fast-mode icon
- Expanded `isAnthropicFastModeUnsupportedError` to treat 429 `rate_limit_error` responses mentioning fast mode as unsupported alongside 400 `invalid_request_error` speed-rejection cases.
- Added tests for unsupported-fast-mode detection covering 400, 429, and unrelated error payloads.
- Added `AgentSession.isFastModeActive()` with provider-scoped resolution and switched status-line rendering to use it for the fast-mode icon.
2026-05-19 18:18:26 +09:00
Can BölükandGitHub d80b9ca084 Merge branch 'main' into fix/acp-bash-permission-shape 2026-05-19 18:13:40 +09:00
can1357 7e93d9b79c feat(ai,coding-agent): add scoped service tiers (claude-only, openai-only)
Two new `ServiceTier` values let users target priority/fast mode at one
provider family without paying premium costs on the other when switching
models mid-session:

- `"openai-only"` → resolves to `"priority"` on `openai` and
  `openai-codex`; `undefined` everywhere else.
- `"claude-only"` → resolves to `"priority"` on direct `anthropic`;
  `undefined` on Bedrock/Vertex Claude and elsewhere.

Implementation centers on a new `resolveServiceTier(serviceTier, provider)`
helper exported from `@oh-my-pi/pi-ai`. The three OpenAI providers and the
Anthropic provider all route through it, replacing the previous
`shouldSendServiceTier` type-guard pattern (which couldn't survive scoped
values — the input variable's literal type stops matching the wire type
once scopes are introduced). `shouldSendServiceTier` is kept as a plain
boolean for external callers but no longer narrows the input.

`getPriorityPremiumRequests` is reworked: it now counts Anthropic +
`"priority"` (fast mode) as one premium request — the original PR
introduced the realization but didn't update billing — and continues to
ignore providers that silently drop the field on the wire.

User-facing:
- `serviceTier` setting enum gains `"openai-only"` and `"claude-only"`
  with clear UI descriptions.
- `/fast on` still sets the unscoped `"priority"`, but `/fast status`
  and `isFastModeEnabled()` now report `on` for any priority-granting
  tier (including scoped values). `/fast off` clears to `undefined`
  regardless of scope.
- The Anthropic auto-fallback listener and re-arm clearing both cover
  `"priority"` and `"claude-only"` (the two values that grant priority
  on Anthropic). `"openai-only"` doesn't trigger the anthropic
  fallback even if the user is on an Anthropic model — by design.

Tests cover all four resolver branches (unscoped passthrough, openai-only
match/miss, claude-only match/miss), Anthropic provider's wire `speed`
field under each scope, and updated premium accounting.
2026-05-19 18:06:00 +09:00
can1357 250e55283e refactor(ai,coding-agent): unify fast mode under serviceTier
Replaces the parallel `speed` knob with the existing `serviceTier`
concept. The anthropic-messages provider now realizes
`serviceTier: "priority"` by setting `speed: "fast"` on the wire and
appending the `fast-mode-2026-02-01` beta header; other providers
continue to pass `service_tier` through natively or ignore it.

User-facing impact:
- `/fast` no longer dispatches on model.api. It just toggles
  `serviceTier: "priority"`. Anthropic-specific translation lives
  entirely in the provider.
- Anthropic auto-fallback marker is now the generic `"priority"`
  identifier in `AssistantMessage.disabledFeatures` instead of
  `"anthropic.fast_mode"`.
- New `clearAnthropicFastModeFallback(providerSessionState)` export is
  invoked from `AgentSession.setServiceTier` when transitioning into
  `"priority"`, so re-running `/fast on` after the provider
  auto-disabled fast mode actually re-arms the next request instead of
  silently no-oping.

Provider-side cleanups:
- Tightened cast site (`ParamsWithSpeed` alias) for the typed
  `speed: "fast"` injection.
- Widened the rejection matcher (`\bspeed\b` + `not support`) so
  phrasing drift ("is not supported" vs "does not support", quoted vs
  backticked) doesn't break the fallback.

Dropped from the PR:
- `Agent.speed` / `AgentOptions.speed` / `SimpleStreamOptions.speed`
  fields.
- `SpeedChangeEntry` and `appendSpeedChange` from the session entry
  schema; service-tier change entries already cover this.
- `AgentSession.setSpeed` / `.speed` and the previousSpeed
  capture/restore in `switchSession` — collapsed back into
  `setServiceTier` + previousServiceTier, which now covers the rollback
  too.
2026-05-19 17:55:44 +09:00
jiwangyihao 0820982b6a fix(acp): include execute metadata in bash permissions 2026-05-19 16:41:05 +08:00
Lucas Szwarcberg d0efcea5ad feat(ai,coding-agent): added Anthropic fast mode with auto-fallback
Wires `speed: "fast"` and the `fast-mode-2026-02-01` beta into the
Anthropic provider, plumbs a matching `speed` option through
`SimpleStreamOptions` and the Agent, and teaches `/fast` to dispatch
on the active model's API (Anthropic -> speed=fast, OpenAI -> existing
serviceTier=priority path). Server is the authority on which models
support fast mode.

When the server rejects an unsupported model, the provider mirrors the
strict-tools fallback: drops the field, retries the same turn
transparently, persists the disable via `providerSessionState`, and
surfaces the action through the new `AssistantMessage.disabledFeatures`
marker so the session can sync the toggle off and warn the user.
2026-05-18 11:48:40 -07:00
can1357 bfae4d46c3 fix(coding-agent): tracked acp tool args by session for replay
- Tracked ACP tool-call inputs per session and replayed them via `toolArgsById`/`getToolArgs` plumbing.
- Merged ACP tool execution end content from start and result events so command output replay preserves original args.
- Scoped ACP async-job draining by session `ownerId` and `agentId` with in-flight tracking and permission-gated deferred turns.
- Refactored compaction telemetry and async tests with per-test telemetry setup and asynchronous teardown resets.
2026-05-17 13:15:07 +02:00
Can BölükandGitHub 34ce96e42f Merge pull request #1138 from jiwangyihao/acp-autonomous-continuation
fix(coding-agent): keep ACP async continuations owned
2026-05-17 13:12:15 +02:00
Can BölükandGitHub b39c8c8460 Merge pull request #1134 from jiwangyihao/fix/acp-zed-permission-gate
fix(coding-agent): repair ACP permission flow for file edits
2026-05-17 13:12:07 +02:00
jiwangyihao 05e1e702dd fix(coding-agent): keep ACP async continuations owned 2026-05-17 15:30:09 +08:00
can1357 8f204539b8 fix(coding-agent): deferred agent_end emission until prompt unwinds
- Held wire-level agent_end until #promptInFlightCount drops to 0, preventing AgentBusyError when subscribers fire the next prompt synchronously from agent_end.
- Added #pendingAgentEndEmit field and #flushPendingAgentEnd(), called from #endInFlight and #resetInFlight.
- Added regression test covering re-entrant prompt() from agent_end listener.
2026-05-17 08:53:41 +02:00
jiwangyihao cb7d30a437 Merge remote-tracking branch 'origin/main' into fix/acp-zed-permission-gate 2026-05-17 11:44:41 +08:00
can1357 2155b8e020 perf: replaced WeakMap caches with symbol-keyed properties
- Migrated per-object caches (chat/tool starts, model fingerprints, validation contexts, provider indexes, render IDs) from WeakMap to Symbol-keyed properties on the objects themselves.
- Rewrote SSE debug tee as a single-pass inline parser, eliminating the body.tee() + readSseEvents re-parse pipeline.
- Refactored MockModel from a factory function + external WeakMap state into a self-contained class.
- Added FIFO memoization caches for heuristic candidate expansion and namespace suffix lookups.
2026-05-17 03:47:45 +02:00
jiwangyihao ef2d536b96 fix(coding-agent): 修复 ACP 文件编辑权限请求 2026-05-17 09:40:50 +08:00
can1357 484fca9c01 feat: added auth-gateway usage cache with single-flight 15s ttl fallback
- Added AbortSignal propagation and timeout-race handling for broker health, usage, refresh, and snapshot calls.
- Added single-flight usage-report caching with 15s TTL, per-caller abort races, and null-on-fail fallback.
- Expanded provider schemas and parse/build logic for cache metadata, headers, stop controls, and image/file content.
- Hardened auth flows by rejecting refresh sentinels and using timing-safe bearer-token comparisons.
2026-05-17 01:10:25 +02:00
can1357 c3f5a60c22 feat(auth): added auth-broker for remote credential vault
- Added `AuthBrokerClient`, `RemoteAuthCredentialStore`, `AuthBrokerRefresher`, and `startAuthBroker` server in `packages/ai/src/auth-broker`.
- Renamed `AuthCredentialStore` class to `SqliteAuthCredentialStore`; extracted `AuthCredentialStore` as a persistence interface.
- Added `exportSnapshot`, `forceRefreshCredentialById`, `disableCredentialById`, and `upsertCredential` to `AuthStorage` for broker wire protocol.
- Added `omp auth-broker` CLI subcommand (serve, token, login, logout, import, status) and `discoverAuthStorage` broker-mode path keyed on `OMP_AUTH_BROKER_URL`.
2026-05-16 20:44:07 +02:00
can1357 7ea9e16408 feat(coding-agent): changed TTSR non-interrupt tool matches to fold into toolResult
- Non-interrupting tool-source TTSR matches now prepend a system-reminder to the matched tool's `toolResult` content instead of queuing a loop-wide deferred follow-up turn.
- Text/thinking source matches retain the previous deferred-injection behavior.
- Added deduplication so one rule attaches to exactly one sibling tool call per batch.
- Stale per-tool injections are cleared on abort/error before tools produce results.
2026-05-16 20:15:53 +02:00
can1357 39f34ada70 feat: added pure-JS sanitizeText
- Migrated sanitizeText from pi-natives to pi-utils as a pure-JS implementation, removing the native dependency across all call sites.
2026-05-16 20:12:26 +02:00
can1357 64fcdc308f refactor(coding-agent)!: removed StringEnum helper and shortened tool schema descriptions
- Replaced all StringEnum(...) usages with z.enum([...]) across tools, examples, and tests.
- Removed StringEnum re-export from @oh-my-pi/pi-coding-agent public API.
- Condensed verbose tool parameter descriptions to minimal lowercase phrases.
- Renamed AuthCredentialStore to SqliteAuthCredentialStore at usage sites.
2026-05-16 19:26:32 +02:00
can1357 32453aaff0 feat(agent): added AgentTelemetry across compaction and branch-summary
- Added optional AgentTelemetry to summary, handoff, branch-summary, and compact option types.
- Replaced one-shot `completeSimple` usage with `instrumentedCompleteSimple` across compaction, summary, and branch-summary calls and passed `oneshotKind`.
- Added `PiGenAIAttr.OneshotKind`, `InstrumentedChatSpanOptions`, and response-header forwarding in telemetry span lifecycle.
- Added `resolveTelemetry` propagation in coding-agent session and inspect-image paths to pass request-scoped telemetry.
- Added compaction telemetry test harness and span assertions for success, no-telemetry, and error cases.
2026-05-16 18:03:07 +02:00
Gerben Meijer 694f5e9a54 Refresh SSH hosts without restart 2026-05-16 00:20:00 +02:00
can1357 8b1364d4c2 feat(coding-agent): implemented one-shot generateHandoff in coding-agent
- Replaced event-driven handoff with one-shot `generateHandoff(...)` via `completeSimple`.
- Added cancellable `/handoff` command handling with a loader and Escape-to-abort flow.
- Removed legacy `compaction/handoff.ts` exports and added `generateHandoff(messages, model, apiKey, options)`.
- Fixed pre-cancelled handoff behavior to return `Handoff cancelled` and propagate abort signals.
- Updated handoff tests/mocks to assert `generateHandoff` invocation details and `AgentSession.handoff()` options.
2026-05-15 18:49:53 +02:00
can1357 e1aaf78874 refactor(compaction): moved compaction APIs to @oh-my-pi/pi-agent-core
- Relocated compaction, branch-summarization, pruning, and utils from coding-agent to packages/agent/src/compaction.
- Moved OpenAI remote compaction helpers from packages/ai to the new compaction module.
- Added handoff.ts with extractHandoffDocument, createHandoffContext, and renderHandoffPrompt helpers.
- Exposed new entries.ts with standalone SessionEntry types so coding-agent no longer owns them.
2026-05-15 18:31:12 +02:00
can1357 9ca04c9e8c fix(ai): preserved custom tool calls and threaded abort signal in remote compaction
- buildOpenAiNativeHistory now emits custom_tool_call / custom_tool_call_output for blocks with customWireName (apply_patch and other freeform tools), matching the normal Responses replay path; previously demoted to function_call which broke remote-compaction replay or mismatched the original call.
- requestOpenAiRemoteCompaction and requestRemoteCompaction accept an optional AbortSignal; the coding-agent compaction caller forwards the existing signal so cancellation now terminates the in-flight fetch instead of stranding the session in the compacting state until the server replies.
2026-05-15 17:47:32 +02:00
can1357 35beac2972 fix(coding-agent): defer persist when writer is mid-close
`SessionManager.close()` queues `#closePersistWriterInternal()` on the
persist chain. The task awaits `#persistWriter.close()`, which flips
`#closing = true` synchronously before yielding on its inner writer
`close()`. A concurrent `appendMessage()` landing in that yield window
hit the hot path, got the still-cached (but closing) writer back from
`#ensurePersistWriter()`, and threw `Error("Writer closed")` from
`writeSync`. The throw was stashed into `#persistError`; the next async
caller (`flush()` or a later `appendMessage()`) re-threw it as an
unhandled rejection with the original line-1282 stack.

Expose `NdjsonFileWriter.isOpen()` and treat a mid-close cached writer
as a miss in `#ensurePersistWriter()`. `_persist` now falls back to the
async `#rewriteFile()` cold path so the entry — already in
`#fileEntries` — still lands on disk once the close drains.
2026-05-15 17:00:18 +02:00
can1357 ea76fae05f feat: added OpenAI remote-compaction provider endpoint gating support
- Added OpenAI remote-compaction API support with provider-specific endpoint gating.
- Added buildOpenAiNativeHistory, token-budget estimation, and message trimming for remote-compaction.
- Added helpers to preserve and validate remote-compaction metadata in request/response handling.
- Refactored coding-agent compaction to consume pi-ai remote-compaction helpers with converted message history.
- Exported remote-compaction from ai index and documented the new APIs in CHANGELOG.
2026-05-15 14:46:54 +02:00
can1357 2a1052ea9f fix(coding-agent): aligned output counters after sink replacement
- Adjusted OutputSink to disable head retention after replace(), resetting counters so later pushes append to the tail and do not trigger stale middle-elision in dump().
- Refined artifact link emission to insert a newline separator only when the minimized output lacked one.
- Added a regression test for replace-plus-push ordering that verifies no elision marker and aligned byte counts.
2026-05-15 14:46:54 +02:00
can1357 933058a241 feat(goals): added per-session goal mode with token budget tracking
- Added GoalRuntime with wall-clock and token accounting, budget steering, and lifecycle operations (create, pause, resume, drop, complete).
- Exposed goal tool as a hidden agent tool, activated only when goal mode is enabled.
- Integrated goal continuation loop in InteractiveMode with auto-submit between turns.
- Added status line segment and theme icons for goal mode state.
2026-05-14 06:40:41 +02:00
can1357 71f3997afa fix(session): resolved session persistence flow to use sync write/read APIs
- Added sync truncation helpers to recursively prepare session entries and externalize image data.
- Reworked session persistence to use synchronous preparation plus `writeSync` with close-state checks.
- Added synchronous session-storage APIs and rerouted write paths to `writeLineSync`/`readTextSync`.
- Added `BlobStore.putSync`, migrated hashing to `Bun.SHA256`, and updated hash tests accordingly.
2026-05-14 06:12:41 +02:00