Commit Graph

97 Commits

Author SHA1 Message Date
can1357 d29dfc869d Merge PR #4473: fix(session): guard legacy snapcompact frames on resume (@cexll) 2026-07-05 13:10:26 +02:00
can1357 a868a7d2d5 Merge PR #4471: fix(ai): separate Codex orchestration usage (@roboomp) 2026-07-05 13:03:08 +02:00
can1357 d4283d242c fix(session): preserve explicit sessions with consumed drafts 2026-07-05 12:44:15 +02:00
roboomp 170cf59cf7 fix(session): treated mode_change as draft-only metadata
`plan.defaultOnStartup` records a `mode_change` before the composer restores its draft; without this the draft-cleanup arm check treats the file as durable and the metadata-only JSONL leak reappears for default-plan sessions.

Added a regression case that drives a model_change + mode_change + draft-clear cycle and asserts the session file is dropped on close().

Fixes #4571
2026-07-04 23:18:28 +00:00
roboomp a3557222f1 fix(session): kept explicit empty sessions on close
Limit empty-session close cleanup to files whose draft sidecar lifecycle
materialized an otherwise startup-metadata-only session. Direct
ensureOnDisk() callers now remain discoverable even when they have no
user/assistant messages yet, and handoff custom_message entries survive
close before the next user turn.

Added regression coverage for resumed draft cleanup, ACP-style explicit
ensureOnDisk() records, and handoff custom messages.

Fixes #4571
2026-07-04 22:38:15 +00:00
roboomp 903900edc9 fix(session): dropped empty session file on close when no messages and no draft
`SessionManager.saveDraft(text)` calls `ensureOnDisk()` so the draft
sidecar has a parent JSONL. A follow-up `saveDraft("")` only unlinks
the sidecar — the session file was left behind, and `#shouldHaveSessionFile()`
could not prune it once the load path latched `#fileIsCurrent` and
`#forceFileCreation` to true. Each draft-then-clear-then-exit cycle
leaked a ~500–750 B zombie into `~/.omp/agent/sessions/<cwd>/`
containing only the title slot, session header, and a handful of
`model_change`/`mode_change`/`thinking_level_change` entries.

`close()` now calls `#dropIfEmptyAndNoDraft()` after draining the
writer: when the file exists, holds no user/assistant messages, and
no draft sidecar is present, it removes the session file and its
artifacts directory via `deleteSessionWithArtifacts`. Real conversations,
sessions with a saved draft still on disk (needed for `--resume`), and
never-materialized sessions are untouched.

Fixes #4571
2026-07-04 22:25:56 +00:00
ben 3fadaee37f fix(coding-agent): guard legacy snapcompact resume frames 2026-07-04 09:59:03 +08:00
roboomp a52ed682c7 fix(ai): separated codex orchestration usage
- Added a Usage.orchestration sidecar for provider-side service tokens so Responses/Codex totals and costs stay accurate without inflating visible prompt input/cache buckets.
- Updated Codex/WebSocket usage, session/status aggregates, and usage reporting to preserve orchestration-aware totals.
- Added regressions for OpenAI Responses accounting, Codex WebSocket terminal usage, cost calculation, and session aggregation.

Fixes #4469
2026-07-03 16:44:12 +00:00
roboomp 23d9f7c898 fix(session): discarded temp on EPERM guard-reject branches
FileSessionStorage.#replaceSessionFileAfterEpermSync now unlinks the staged temp file when commitGuard returns false in both fallback branches: the ENOENT-vanished-target path and the post-move-aside path (where the moved-aside backup is also restored). Honors the writeTextAtomic contract that a guard-rejected stage is discarded.

Regressions cover all three guard-reject exits: the direct rename pre-check, the ENOENT branch inside the EPERM fallback, and the move-aside branch that also restores the backup. Each asserts no orphan .tmp remains in the session dir.

Fixes #4338
2026-07-02 19:11:25 +00:00
roboomp 326a3406a8 fix(session): guarded atomic rewrite against flushSync overwrites
SessionStorage.writeTextAtomic now accepts a commitGuard the backend calls synchronously immediately before publishing the staged body. FileSessionStorage performs the guard check and rename in the same tick via fs.renameSync (both on the direct path and the EPERM move-aside fallback), so a concurrent #rewriteSynchronously (flushSync -> Ctrl+C / session exit) that bumps the disk epoch cannot be overwritten by the stale body serialized before it ran. MemorySessionStorage and IndexedSessionStorage honor the same guard.

SessionManager.#rewriteAtomically threads a guard that returns false when the disk epoch changes, and re-checks the epoch after every writeTextAtomic before touching #fileIsCurrent / #rewriteRequired. #persistTitleChangeEntry's atomic fallback wires the same guard.

Added a regression that pauses the fake storage's writeTextAtomic mid-flight, appends a session_exit custom entry (which the fence records in memory), calls flushSync, releases the paused rewrite, and asserts the exit record is still on the JSONL path and the atomic publish was rejected by the guard.

Fixes #4338
2026-07-02 19:00:27 +00:00
can1357 c4c0331345 fix(coding-agent/session): prevented data loss in session serialization
- Persist signed message blocks (`text`, `thinking`, `toolCall`) and encrypted reasoning payloads verbatim during session serialization instead of clearing or truncating them.
- Preserve signature keys instead of replacing them with empty strings when they exceed persistence size limits.
- Exempt official first-party OpenAI and Anthropic API endpoints from the leaked-thinking stream healing wrapper to prevent misfires on legitimate visible text fences.
2026-07-02 03:58:10 +02:00
can1357 5ca07b1759 Merge PR #3759: Merge remote-tracking branch 'can1357/main' into fix/compaction-summary-chronological-position (@DarkPhilosophy)
# Conflicts:
#	packages/coding-agent/src/session/session-context.ts
2026-07-01 22:04:47 +02:00
roboomp a1a393cdc5 fix(session): linearized session path rebuilds
- Replaced leaf-to-root unshift path assembly with push plus one reverse in buildSessionContext and SessionEntryIndex.pathTo.
- Added regression coverage that keeps deep linear context and branch paths root-to-leaf without Array.unshift work.

Fixes #3961
2026-07-01 01:59:34 +00:00
can1357 fc24a70f5e Merge branch 'farm/ad81bc64/retry-anthropic-internal-server-error'
fix(compaction): cap snapcompact frame payloads (#3866)

Bound rebuilt snapcompact image payloads by a per-request base64 byte
budget so long sessions stop re-sending multi-megabyte standing image
archives on every provider request; auto-compaction falls back to
context-full summaries when snapcompact output is too large.

Resolved snapcompact.ts conflict against the main font-rendering refactor
by keeping both renderabilityProbeText and the frame-budget helpers.
Fixed historyBlocks to emit the omitted-frame notice before the kept
(newer) images, since the byte budget drops the oldest frames — keeping
reconstructed blocks oldest-to-newest (addresses Codex P2 review).

Fixes #3792
2026-06-30 09:59:03 +02:00
roboomp 39688620f8 fix(compaction): kept newest snapcompact frames
When legacy snapcompact archives exceed the per-request byte budget, retain frames from the newest end of the archived middle and restore oldest-to-newest order for the kept subset.
2026-06-30 05:50:12 +00:00
roboomp 88308f105f fix(compaction): capped snapcompact frame payloads
Bounded persisted snapcompact image archives by base64 byte size so large sessions stop re-sending multi-megabyte frame walls on every provider request.

Auto snapcompact now falls back to context-full summaries when rendered frame payloads exceed the byte budget, and legacy oversized archives omit over-budget frames during LLM context rebuilds.

Fixes #3792
2026-06-30 05:36:14 +00:00
roboomp cc2cf5c7d0 fix(session): scope compaction elision to active branch
Restrict the supersede sweep to compactions on the path from the current leaf so a newer compaction never rewrites a sibling branch's still-current summary or drops its preserveData. Streaming load now collects the active-branch ids before eliding instead of trampling sibling compactions encountered in file order.

Refs #3789
2026-06-29 06:22:04 +00:00
roboomp e8b2c5d058 fix(session): avoid ctrl-c rewrites for compacted sessions
Stream large session loads, elide superseded compaction payloads, skip synchronous rewrites when the append-only file is already current, and provide usable picker previews for developer-started forks.

Fixes #3789
2026-06-29 06:13:43 +00:00
DarkPhilosophy e52dbde6b8 fix(session): place compaction summary at chronological position in collapsed transcript
Commit 3bcbf1515 (fix #3258) moved the compaction summary to position 0
(top of transcript) as a side-effect of collapsing compacted history for
performance. This caused the divider to scroll into native scrollback where
Ctrl+O could no longer expand it — users got no visible feedback after /compact.

In collapsed transcript mode (display), emit kept messages first, then the
compaction summary, then post-compaction messages — restoring the chronological
position. Agent context (non-transcript) keeps summary-first ordering unchanged.
2026-06-29 01:29:07 +03:00
can1357 0ffe6c8e1f Merge PR #3675: keep moved sessions resumable (@riverpilot)
# Conflicts:
#	packages/coding-agent/src/modes/controllers/command-controller.ts
2026-06-28 18:46:19 +02:00
can1357 42981154fb feat(coding-agent): deduplicated thinking signatures during persistence
- Implement cleanup logic to drop `thinkingSignature` values from assistant thinking blocks during persistence.
- Identify and drop signatures only when the underlying reasoning data is already recoverable via the `providerPayload` items.
- Ensure orphaned signatures that cannot be reconstructed from the payload are preserved during serialization.
- Add comprehensive test coverage to verify deduplication safety and edge-case handling for missing payloads.
2026-06-28 17:01:04 +02:00
can1357 93f68b75a2 test(coding-agent/modes): updated controller test mocks
- Added missing `noteDisplayableThinkingContent` mock function to test fixtures.
- Included `markActivityStart` and `markActivityEnd` methods in status line mocks to match updated controller interfaces.
2026-06-28 16:53:16 +02:00
can1357 102d6d54ad feat: implemented v2 streaming remote compaction for model history state
- Introduced V2 streaming remote compaction for OpenAI-compatible models, enabling full conversation history forwarding and reducing data loss from local trimming.
- Added comprehensive support for sessionId, promptCacheKey, and automatic retry mechanisms to improve compaction reliability and accuracy.
- Updated agent, catalog, and configuration schemas to manage V2 streaming settings, model metadata, and model-specific context window constraints.
- Extended freeform tool patch support for Azure OpenAI and Codex models and refined assistant-side history preservation across providers.
2026-06-28 07:27:02 +02:00
can1357 46278588ea test(session): implemented persistent session management and testing
- Implemented mutable session titles with audit tracking, including storage persistence for SQL and Redis backends.
- Added comprehensive session management features such as idle recap triggers, incremental subagent yield submissions, and automated title refreshing.
- Enhanced task tracking in the TodoTool with progress prioritization and improved session cleanup logic.
- Introduced citation tag handling for OpenAI-compatible source markers and improved edit parsing.
2026-06-28 07:27:01 +02:00
Alexander Kirilin f209087cfd fix(omp): keep moved sessions resumable 2026-06-27 15:57:09 -04:00
can1357 74d7dfd2fa Merge PR #3354: fix: migrate coding-agent tests to removeWithRetries (@oldschoola) 2026-06-27 02:06:38 +02:00
can1357 3273d0de68 Fix /move session cleanup ownership 2026-06-27 02:04:50 +02:00
oldschoola 98656408d5 feat(move): interactive /move overlay with fresh session in target directory
Replaces the old /move (which relocated the current session file) with a
new flow that starts a fresh empty session in the target directory, leaving
the previous session resumable via /resume. With no argument, /move opens
a path autocomplete overlay (type to filter, Tab to accept, Enter to
confirm). If the target directory does not exist, a confirmation prompt
offers to create it. Empty move sessions are cleaned up on shutdown.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-24 12:08:07 -07:00
oldschoola a2854ba768 fix: migrate coding-agent tests from fs.rm to removeWithRetries
Migrate 203 test files (356 call sites) from fs.rm/fs.rmSync to
removeWithRetries/removeSyncWithRetries to reduce EBUSY test failures
on Windows. removeWithRetries is now exported from @oh-my-pi/pi-utils.

The migration uses a regex-based approach that:
- Replaces fs.rm(path, { recursive, force }) → removeWithRetries(path)
- Replaces fs.rmSync(path, { recursive, force }) → removeSyncWithRetries(path)
- Replaces fs.rm(path) → removeWithRetries(path) (no options)
- Skips fs.rm/fs.rmSync inside template literals (bun --eval scripts)
- Adds imports to existing @oh-my-pi/pi-utils import or creates new one
- Removes unused fs imports where fs.rm was the only fs usage (4 files)
2026-06-23 15:28:05 -07:00
can1357 e4a87fa3ce feat(coding-agent): added matplotlib rendering and image persistence across session reload
- Added Matplotlib figure PNG rendering and display tracking in Python runner to emit PNG output immediately when figures are displayed via display(fig).
- Extended session persistence to externalize oversized image payloads in both content and details.images, enabling tool result images to survive session reload.
- Enhanced session loader to resolve image data payloads and blob references across content and details.images during session reconstruction.
- Added image cache invalidation in TUI image component when image protocol, cell dimensions, or Kitty Unicode placeholder mode changes.
- Added comprehensive test coverage for Matplotlib display, image persistence across reload, and TUI image rendering with protocol and dimension changes.
2026-06-17 13:19:28 +02:00
can1357 409196bf2e fix(coding-agent/session): fixed context usage breakdown to prefer completed in-turn anchors
- Fixed context breakdown to anchor estimates on the latest completed assistant usage message after compaction.
- Adjusted pending-context usage selection to prefer an in-turn provider anchor when available at/after cutoff.
- Added a contextUsageRevision cache token so status-line context memo invalidates after snapshot clear.
2026-06-17 12:24:20 +02:00
can1357 6bbc573c6f fix(coding-agent/session): suppressed subagent breadcrumbs so --continue resumes the parent session
- Added a `suppressBreadcrumb` option to `SessionManager.open()` so headless opens skip writing the per-TTY `--continue` breadcrumb, and passed it from the subagent opens in `task/executor.ts` and the HTML export open in `export/html/index.ts`, which run in the parent's terminal and were clobbering the breadcrumb with their own artifact-dir session file.
- Added `resolveBreadcrumbToInteractiveRoot()` and applied it in `continueRecent()` so already-poisoned breadcrumbs pointing inside a parent's artifacts dir (`<parent>/<agentId>.jsonl`) resolve back up to the top-level interactive session.
- Added `subagent-breadcrumb.test.ts` covering both that a subagent open keeps `--continue` on the parent and that a stale subagent-pointing breadcrumb is recovered.
2026-06-16 16:05:50 +02:00
Gerben Meijer 4a6eec624b fix(coding-agent): preserve vLLM discovered context windows
Read vLLM max_model_len and OpenAI-compatible context_length metadata during model discovery, route providers.vllm.baseUrl into built-in discovery before cached models exist, and avoid sending local placeholder bearer tokens.

Scope the vLLM model cache to the discovery base URL so endpoint changes refetch immediately, and add focused regression coverage for configured and built-in vLLM discovery.
2026-06-14 17:50:16 +02:00
can1357 24c8bb24c6 feat(session): added modular session APIs and rebuilt listing/persistence behavior
- Added session-domain modules and exports for session-entries, context, listing, loader, and migrations.
- Changed persistence to async append writes plus writeTextAtomic, removing sync line APIs.
- Added compaction-aware session context rebuild with dangling tool-call cleanup.
- Added resumable session resolution with status inference, id/stem/suffix matching, and backup recovery.
2026-06-14 02:02:53 +02:00
can1357 0e78cac716 fix(coding-agent): removed abort marker and updated continue shortcut prompts
- Removed `<turn-aborted>` guidance injection from `transformMessages`, deleted `turn-aborted-guidance.md`, and dropped the synthetic abort note path for aborted/error turns.
- Updated `c`/`.` continue shortcuts to submit `manual-continue.md` as a hidden synthetic `developer` message via `session.prompt(..., { synthetic: true })` instead of sending an empty user turn.
- Updated tests and changelog notes in AI and coding-agent to reflect the revised abort-context and continue behavior.
2026-06-08 05:02:49 +02:00
can1357 71624ee47b fix(coding-agent): fixed stale settings and session-accent cache handling
- Cached setting path segments and memoized `Settings.get()` results, clearing caches on updates.
- Triggered session-name and session-accent callbacks only when effective values changed, with error-safe dispatch.
- Memoized status-line and interactive accent resolution with cache invalidation on settings/theme/session changes.
- Added regression tests for keybinding precedence, status-line, settings, and accent cache behavior.
2026-06-08 02:30:44 +02:00
can1357 ba4abb7e2f fix(coding-agent): excluded cwd-less legacy sessions from relocation guard
- Guard now skips sessions with empty cwd, not just undefined.
- Prevents path.resolve("") collision from hijacking a moved session.
2026-06-08 00:47:39 +02:00
bling cfa9a2d53e fix(coding-agent): resume sessions after a worktree move/rename
When a session's working directory is moved or renamed (e.g. `git worktree
move`), the session file stays under the old cwd-encoded bucket while the new
directory is empty. Resuming was lossy:

- `--continue` rejected the terminal breadcrumb purely on cwd mismatch and then
  found nothing in the new bucket, silently starting a fresh empty session.
- cross-project `--resume <id>` only offered to *fork* (duplicate) the session
  into the new directory, forcing manual id selection and leaving a stale copy.

Detect relocation via the strong, low-false-positive signal "recorded cwd no
longer exists on disk" and re-root in place with the existing `moveTo()`:

- `continueRecent` re-roots the terminal's last session into the current
  directory when its recorded cwd is gone and the new location has no sessions
  of its own (otherwise behavior is unchanged). `readTerminalBreadcrumb` is
  refactored into `readTerminalBreadcrumbEntry` returning the raw cwd +
  session file so callers can interpret a cwd mismatch.
- cross-project `--resume <id>` offers "Move (re-root)" instead of fork when the
  source directory is gone; a still-existing different project still forks.

Tests: continue-relocation (re-root on move, no-hijack on plain cd, prefer
local recent) and cross-project move-vs-fork routing.
2026-06-07 18:34:51 -04:00
can1357 b9c204c69e feat(session): added indexed async session backends and removed synchronous read APIs
- Added `IndexedSessionStorage` with `SessionStorageBackend` for index-based storage reads.
- Removed `readTextSync` from the public `SessionStorage` API and sync backends.
- Changed Redis and SQL backends to warm `{size, mtimeMs}` metadata and read via `readTextSlices`.
- Added per-path write queues and `drain()` to serialize operations and surface first failures.
2026-06-04 02:27:06 +02:00
can1357 e831c2c758 chore: reformat 2026-05-30 18:08:51 +02:00
can1357 420429df9c fix(session): extended dangling tool_use stripping to all mid-path assistant turns
- Previously only stripped dangling tool_use blocks from the trailing assistant turn; now scans all assistant turns on the resolved path.
- Builds a set of paired tool result IDs upfront to identify dangling calls anywhere in the message list.
- Adds a test covering a mid-path dangling turn alongside a correctly paired turn that must be preserved.
2026-05-30 03:52:55 +02:00
can1357 f6bf17d8cc fix(session): neutralized signed thinking blocks on dangling tool-use cleanup
- Stripped `redactedThinking` blocks (encrypted, no downgradeable plaintext) from trailing assistant turns during context rebuild.
- Cleared `thinkingSignature` on `thinking` blocks so the encoder downgrades them to plain text, avoiding Anthropic's "modified latest assistant message" rejection.
- Extended existing test to cover signed/redacted thinking alongside dangling tool calls.
2026-05-30 02:06:29 +02:00
can1357 3b75597aa6 fix(coding-agent): removed todo spinner and validated dangling tool_use stripping
- Removed todo spinner interval state and rendering hooks from InteractiveMode, and in-progress or active-matched todos now use the static running glyph.
- Removed spinner-driven matcher caching and render updates from todo list generation so running state is based on direct content matching.
- Added build-session context tests that remove dangling assistant `toolCall` entries and drop trailing assistant turns that contain only tool calls.
2026-05-30 01:24:58 +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
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
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
can1357 f1f6516056 refactor: reorganized exports and removed obsolete helper branches
- Removed export leakage by demoting many helper and const symbols to module-local scope.
- Renamed underscore-prefixed internals and cache fields, then updated related references and `satisfies never` checks.
- Deleted obsolete logic branches and helpers, including harmony-stream interruption flow and unused benchmark runtime helpers.
- Updated Biome config and manifests by broadening lint coverage and removing an unused `@napi-rs/cli` dev dependency.
- Adjusted tests and utilities to use renamed test helpers and remove redundant private test-only helpers/locals.
2026-05-14 04:36:19 +02:00
can1357 975941aba4 chore: remove garbage tests 2026-05-12 04:09:33 +02:00
can1357 c2a0253028 feat(coding-agent): added unsent editor draft persistence for resumed sessions
- Added session-draft persistence methods in SessionManager to write unsent editor text to an artifacts-sidecar draft file and delete it after single-shot consumption.
- Persisted editor text during interactive shutdown and restored that draft on resume when the editor was empty, enabling Ctrl+D draft recovery.
- Updated Ctrl+D handling in the editor/controller path and added tests covering draft round-trip, artifact cleanup, stale-draft eviction, and in-memory no-op behavior.
2026-05-04 02:20:29 +02:00
can1357 08403be71f fix(coding-agent): preserve explicit default model on session resume
buildSessionContext walked the entry path and unconditionally overwrote
models.default from every assistant message's reported model. Temporary
fallbacks (retry fallback, context promotion) and codex-side model
downgrades both produce assistant messages tagged with a different model
id, which clobbered the user's explicit /model pick on resume and made
the session silently revert to the older model.

Treat assistant-message inference as a legacy fallback that only fills
in models.default when no explicit `model_change` with role="default"
has been seen on the path.

Fixes #849
2026-04-30 04:51:23 +02:00