Commit Graph

189 Commits

Author SHA1 Message Date
Kyle McCleary 8e5f619502 fix(coding-agent): harden Agent Hub lifecycle and persistence 2026-08-04 16:29:15 -07:00
Kyle McCleary 91467c2f27 fix(coding-agent): restore Agent Hub lineage metrics 2026-08-03 19:42:11 -07:00
can1357 1a8caad23e chore: update docs + rename reset to clear 2026-08-03 18:37:23 +02:00
can1357 a418920ec1 feat: made /reset semantically different
Closes #4447
2026-08-03 15:46:46 +02:00
can1357 c53a47f97d Merge PR #7287: fix(coding-agent): prune archived session stats (@alphastorm)
# Conflicts:
#	packages/coding-agent/src/session/session-manager.ts
2026-08-03 15:15:36 +02:00
Aleksandr Khaustov efe640a161 fix(session): preserve title when branching 2026-08-03 13:27:01 +04:00
Oleg Pulatov 6c84a8bb99 fix(coding-agent): keep completed session entries durable across crashes
Completed transcript entries now write through to the OS page cache on
append instead of microtask-batching, supersede in-flight atomic rewrites
with a synchronous full-body publish, and land on the live moveTo path
(source pre-rename, destination post-rename) so a software crash no longer
drops finished user/assistant/tool events. Streaming text remains durable
only at message_end; no fsync/power-loss guarantee is claimed.
2026-08-03 04:14:58 +02:00
Sunil Srivatsa 980ab4fd0a fix(coding-agent): harden archived stats cleanup 2026-08-01 17:17:46 -04:00
can1357 539e7277d0 fix(session): fence title updates during moves 2026-08-01 20:13:39 +02:00
roboomp dd0972456d docs(session): clarify move flushSync durability caveat
The move fence intentionally defers a flushSync landing in the rename window; full synchronous durability there is incompatible with orphan-avoidance for a rename-based, Windows-safe move. Document the caveat honestly instead of implying parity with the in-place rewrite path.

Refs #7270
2026-08-01 14:07:41 +00:00
roboomp 8f4e58edd2 fix(session): fence session moves without canceling queued rewrites
The move fence bumped #diskEpoch, which no-oped any disk task already queued at the prior epoch (e.g. a header-only ensureOnDisk materializing rewrite), losing explicitly materialized ACP/draft sessions. Gate the append hot path on #sessionFileRelocating instead of a fresh epoch, so prior disk work still drains.

Fixes #7270
2026-08-01 13:56:30 +00:00
roboomp f3fda5139d fix(session): fence flushSync during session moves
A fenced append followed by a Ctrl+C flushSync in the post-rename, pre-repoint window rewrote the full body to the old path, recreating the orphan. Sync rewrites now defer while the session file is relocating.

Fixes #7270
2026-08-01 13:47:58 +00:00
roboomp ea265df009 fix(session): fenced appends during session moves
Prevented synchronous session appends from reopening the vacated source path while moveTo relocates and repoints the journal.

Fixes #7270
2026-08-01 13:39:51 +00:00
Wolfie 3492f45ac1 fix(session): drain batches from flushSync 2026-07-30 16:09:32 -07:00
can1357 6b4efa896f feat(coding-agent): implemented oauth credential pin persistence and seeding
- Added hashing utilities and session entry definitions for OAuth credential pins.
- Added session manager methods to append and retrieve credential pins with backdated timestamp support.
- Added credential pin recording after assistant turns and seeding during session restoration.
- Added comprehensive unit tests covering credential pin recording, persistence, and seeding.
2026-07-30 07:21:08 +02:00
can1357 f11641d5a8 feat(coding-agent): enabled importing foreign sessions from claude and codex
- Implemented session stores and metadata converters to import Claude and Codex sessions into OMP.
- Added `--from-claude` and `--from-codex` CLI flags and `/resume` command arguments for foreign session resolution.
- Updated session selector components and controllers to support listing and picking external agent sessions.
- Added comprehensive unit tests and documentation covering foreign session import functionality.
2026-07-30 00:28:40 +02:00
roboomp cb63ebd9f5 fix(session): preserved compaction data for rewinds
Kept superseded summaries and preserveData durable while deriving forward transcript elision from the active compaction.

Added branch and rewind coverage for snapcompact archives and OpenAI remote replacement history.

Fixes #4090
2026-07-23 19:53:05 +00:00
can1357 da1056226e Merge PR #5464 port: persist vibe sessions across restarts (@roboomp) 2026-07-23 18:07:22 +02:00
can1357 5d66eb7f2a Merge PR #5464: fix(coding-agent): persist vibe sessions across restarts (@roboomp) 2026-07-23 18:06:11 +02:00
can1357 2ffb67e3c7 fix: reconciled merged tests and dead code with current main structure
- warp completion test updated to event-taking notification signature
- hindsight test config gained required timeout fields
- dropped orphaned parseBillingConfig and advisor secret-collection dupes
- deduped fixture key; formatter pass on merged files
2026-07-23 18:02:31 +02:00
can1357 bcd23ee7c1 fix: kept seeded workspace roots lazy until the session file is durable
Seeding additionalDirectories at launch (via --add-dir or the
workspace.additionalDirectories setting) called #rewriteAtomically on a
brand-new session manager, which materialized a header-only JSONL and a
fresh breadcrumb before any assistant output. Launching and exiting with
configured roots therefore created an empty resumable session that
--continue picked over the previous conversation.

Gated all three workspace-directory mutators behind the existing
#shouldHaveSessionFile() lazy-persistence gate (one shared helper), and
made setAdditionalDirectories a no-op when the normalized list is
unchanged so resuming large sessions no longer rewrites the whole JSONL
on every startup. Roots set before the gate is crossed land in the
header with the first durable write; added a regression test.
2026-07-23 17:30:34 +02:00
maatheusgois-dd 5c312e23ff Fix biome lint: import wrapping, import sorting, formatting
Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 11:42:48 -03:00
maatheusgois-dd c24cb606a8 Normalize additionalDirectories in #resetToNewSession through normalizeSessionWorkspace
Relative paths in workspace.additionalDirectories settings (e.g.
'../shared') were stored raw in the new-session header instead of
being expanded to absolute. Now all new-session roots go through the
same normalizer used at startup.

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 02:25:03 -03:00
maatheusgois-dd 33e1857a1d Centralize additional-root seeding in SessionManager.newSession
- Add additionalDirectories to NewSessionOptions
- #resetToNewSession now seeds #additionalDirectories from options,
  so all new-session transitions (handoff, branch, /new) get the roots
- AgentSession.newSession passes settings dirs via options instead of
  calling setAdditionalDirectories after the fact

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 02:14:51 -03:00
maatheusgois-dd bff14d121d Copy additionalDirectories in createBranchedSession and normalize fork roots against target cwd
- createBranchedSession (/branch, /btw) now copies additionalDirectories
  to the new header so branches preserve multi-root state
- forkFrom filters source additionalDirectories against the target cwd
  so the new cwd is never also listed as an additional root

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 02:04:05 -03:00
maatheusgois-dd 1704e3fd5a Normalize additional dirs on /move and discover nested AGENTS.md from added roots
- Filter #additionalDirectories when moveTo changes cwd so the new cwd
  is never also listed as an additional root (session-manager.ts)
- Build workspace tree for each additional root to discover nested
  AGENTS.md files under added roots, merging agentsMdFiles into the
  primary set (system-prompt.ts)

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 01:47:46 -03:00
maatheusgois-dd d3777e778d Restore additionalDirectories on failed session switch rollback
restoreState() now syncs #additionalDirectories from the captured
snapshot header, so a failed switchSession (e.g. from a session_switch
hook or model-restore error) doesn't leave the original session with
the target's workspace roots.

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 01:20:48 -03:00
maatheusgois-dd ab0c25e058 Fix 2nd-round AI review: context files for all roots, merge on resume, fork copies roots
- Always augment context files with additional root context, even when
  createAgentSession passes preloaded contextFiles (system-prompt.ts)
- Merge configured dirs with existing restored roots on resume instead
  of replacing them (sdk.ts)
- Copy additionalDirectories from source header in forkFrom so forks
  preserve the multi-root set (session-manager.ts)
- Add test for forkFrom preserving additionalDirectories

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 01:14:13 -03:00
maatheusgois-dd 48381f0e98 Fix AI review findings: settings on initial launch, /new root leak, persisted roots on resume, consistent ~ normalization, dead exports
- Seed workspace.additionalDirectories settings on initial launch session,
  not just /new (sdk.ts)
- Always call setAdditionalDirectories on /new, even with empty list, to
  clear stale roots from the previous session (agent-session.ts)
- Make setAdditionalDirectories async and trigger atomic rewrite when a
  session file already exists, so --continue --add-dir persists (session-manager.ts)
- Route addWorkspaceDirectory/removeWorkspaceDirectory through
  normalizeWorkspaceDirectory for consistent ~ expansion (session-manager.ts)
- Drop dead exports: workspaceRootForPath (no production callers),
  getWorkspace (no production callers), and unused SessionWorkspace type
  import from session-manager.ts (session-workspace.ts, session-manager.ts)
- Remove unnecessary as SettingPath / as string[] casts (agent-session.ts)
- Update tests: add ~ expansion coverage, root-clearing on /new,
  persistence on resumed sessions, fix header line parsing

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-22 00:58:02 -03:00
maatheusgois-dd 32d8b84e26 Add dynamic multi-root workspace context (#2569)
A session now carries an ordered list of workspace directories beyond cwd,
managed live from the terminal. New /add-dir, /remove-dir, and /dirs slash
commands let you add and remove folders mid-session; the repeatable --add-dir
CLI flag seeds them at launch, and the workspace.additionalDirectories
setting persists defaults per project. Additional roots are persisted in the
session header, survive reopen/fork/move, and are surfaced to the agent in the
system prompt so it knows they exist and can read/grep/glob them by absolute
path. Design aligns with the endorsed community implementation on
feature/session-workspace.

Co-authored-by: oh-my-pi <https://omp.sh>
2026-07-21 23:38:32 -03:00
can1357 4577f064cb Merge PR #5736: fix(session): persist /new boundary so autoResume does not resume pre-/new transcript (@roboomp) 2026-07-20 22:50:05 +02:00
vmcall d944879f21 feat(task): unified structured subagent execution
- Added per-invocation task schemas with strict and permissive validation.
- Shared task and eval agent policy, artifacts, isolation, and lifecycle handling.
- Enabled host-restricted plan-mode eval agents and persisted their capability clamp.

Fixes #5279
2026-07-17 17:36:59 +02:00
roboomp e6de8142fe fix(session): retained bash ownership across session/branch transitions
Late user-initiated bash results and minimized-output artifacts were
recorded against whichever session or branch was active when execution
finished, not the one that started it. executeBash() awaited the result
then wrote through the mutable live sessionManager, while pending bash
messages carried no session/branch ownership and the minimized-output
callback used the live manager. A session change during execution
redirected transcript entries and artifacts to the replacement session
or branch, and a dropped session could be recreated by a straggler.

Capture a bash ownership target when execution starts and transition it
whenever the active session or transcript leaf changes. Late results and
minimized artifacts route to the originating session/branch (via a
detached clone when the file changed, or an off-leaf branch append when
the leaf moved), are discarded for an intentional drop, and ownership is
released on failed transitions. RPC dispatch concurrency and immediate
abort_bash behavior are unchanged.

Fixes #5743
2026-07-16 19:56:27 +00:00
roboomp 447eb51f29 fix(session): persist /new boundary so autoResume does not resume pre-/new transcript
New-session persistence is lazy: after `/new`, the JSONL is not created
until assistant output exists. Exiting before any assistant message left
the per-terminal breadcrumb pointing at a not-yet-materialized file, which
`readTerminalBreadcrumbEntry` rejected (missing target), so `continueRecent`
fell back to `findMostRecentSession` and resurrected the pre-`/new` transcript.

`/new` now records a durable `fresh` breadcrumb boundary. The reader returns a
fresh breadcrumb even when its target is absent (with `exists:false`), and
`continueRecent` honors it by starting fresh instead of falling back. The crumb
is re-stamped non-fresh once the session materializes, so a genuinely
stale/deleted breadcrumb still falls back to the most-recent session. The
breadcrumb write is now synchronous so the fresh->materialized re-stamp cannot
reorder.

Fixes #5730
2026-07-16 18:01:14 +00:00
can1357 5ff277349c refactor(coding-agent): consolidated tool surface onto xd:// devices and hub
- Added the `xd://` virtual device protocol (`internal-urls/xd-protocol.ts`, `tools/xdev.ts`): tools declaring `loadMode: "discoverable"` are unmounted from the request tools array and driven via `read xd://` (list/docs+schema) and `write xd://<tool>` (execute), gated by the `tools.xdev` setting (default on) and inlined into the system prompt.
- Merged the `irc`, `job`, and `launch` tools into a single `hub` tool (`tools/hub/`, `async/job-manager.ts`): messaging keeps `send`/`inbox`/`list`, job control maps to `wait`/`cancel`/`jobs`, process supervision keeps `start`/`logs`/`stop`/`restart`/`describe` with `ps`, and the unified `wait` races background jobs against peer messages; SDK `IrcTool`/`JobTool`/`LaunchTool` are replaced by `HubTool`.
- Removed the hidden `resolve` tool in favor of the `xd://resolve`/`xd://reject`/`xd://propose` resolution devices, auto-including `write` whenever a deferrable tool or plan mode is present.
- Removed the BM25 tool-discovery system: the `search_tool_bm25` tool, the `tool-discovery` module, the `tools.discoveryMode`/`mcp.discoveryMode`/`mcp.discoveryDefaultServers`/`tools.essentialOverride` settings, per-tool MCP selection, and the `mcp_tool_selection` message type.
- Unified tool presentation on `ToolLoadMode` (`essential`|`discoverable`), replacing the custom-tool `xdev?: boolean` opt-out; custom, extension, MCP, RPC host, image-generation, and TTS tools now default to `discoverable`, and added a `satisfies` predicate to `SoftToolRequirement`.
- Removed the standalone `ssh` command tool and `ssh/ssh-executor` (the `ssh://` read/write/search protocol stays), and made `--tools` address hidden built-ins.
- Updated collab-web to render `xd://` dispatches and `hub` op families, dropped the `search_tool_bm25`/`ssh`/`report-finding` renderers, refreshed tool docs and prompts, and migrated the affected tests and changelogs.
2026-07-15 15:16:29 +02:00
roboomp 4bc68b45e2 fix(coding-agent): persisted vibe sessions across restarts
Vibe worker roster lived only in a process-local Map, so a resumed parent
session started with an empty registry and vibe_send failed with
"Unknown vibe session". Persist a versioned, parent-scoped lifecycle
journal (spawn/turn/tombstone events), rehydrate validated idle workers
through the persisted-subagent reviver on resume, and gate the flow with
generation/CAS protection so stale finalizers cannot clobber a
replacement worker. Killed transcripts stay readable but non-revivable;
mode-exit commits tombstones atomically with the mode change and rolls
back cleanly on storage failure.

Ported from @mastertyko's fork branch fix/vibe-session-persistence.

Fixes #5303
2026-07-14 17:58:14 +00:00
can1357 531880c620 feat: improved json serialization for bigint values
- Introduced `stringifyJson` helper to preserve bigint precision by serializing them as decimal strings.
- Replaced native `JSON.stringify` across compaction and session management modules to prevent serialization errors when handling bigint values in tool arguments.
- Added regression tests in `agent` and `coding-agent` packages to ensure bigint tool arguments remain intact through compaction and persistence flows.
2026-07-11 00:12:29 +02:00
roboomp 7fa2c3f42d fix(coding-agent): preserved fork prompt cache affinity
- Persisted an inherited provider prompt-cache key on full session forks while keeping the child OMP session id independent.

- Added --prompt-cache-key and SDK startup inheritance so explicit cache affinity is separate from provider session routing.

- Cleared automatic inherited keys when model, thinking, system prompt, or tool schema inputs change.

Fixes #5035
2026-07-10 07:22:28 +00:00
can1357 8ec34a7662 Merge PR #4349: fix(session): handle malformed custom messages (@roboomp) 2026-07-05 13:25:24 +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
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 01ab7e26d7 fix(session): kept newer fence owner on stale rewrite unwind
SessionManager.#runFencedAtomicRewrite's finally now only clears #atomicRewriteFenceEpoch when it still matches the unwinding task's epoch. When flushSync supersedes an in-flight rewrite (bumping #diskEpoch and resetting #diskTail), a fresh atomic task scheduled at the new epoch can take ownership of the fence before the stale rewrite finally settles; the previous unconditional clear stranded the newer rewrite's bookkeeping so subsequent sync appends took the hot writer path and were then detached by the newer publish.

Regression: SequencedRewriteStorage pauses the first N writeTextAtomic calls on per-call gates. Test schedules a stale rewrite, forces flushSync to bump the epoch via a fenced append, schedules a newer rewrite that parks at pauses[1], releases the stale gate (stale unwinds and guard-rejects), then appends a custom entry — asserts writerOpens does not grow (fence preserved) and the fenced entry lands in the newer publish's body. Without the fix, writerOpens grows from 1 to 2.

Fixes #4338
2026-07-02 21:20:53 +00:00
roboomp 549b4c13a8 fix(session): relaxed fence once flushSync superseded the atomic
Replaced the boolean #atomicRewriteActive flag with #atomicRewriteFenceEpoch: number | null. The fence branch in #appendToSessionFile now applies only while the pending atomic rewrite's epoch still matches #diskEpoch. Once flushSync -> #rewriteSynchronously bumps the epoch, the in-flight writeTextAtomic is guaranteed to abandon via its commitGuard, so subsequent sync appends can (and must) take the hot path against the freshly-published body instead of being stranded in memory when close() returns without another rewrite.

New regression: pauses writeTextAtomic mid-flight, appends a fenced custom entry, calls flushSync (which captures it into the durable body), then appends a message + custom entry after the epoch bump. Reads the current JSONL BEFORE releasing the paused atomic and asserts both post-flushSync entries are already on disk; then releases the atomic (commitGuard rejects) and closes the session and asserts nothing is lost.

Fixes #4338
2026-07-02 20:54:24 +00:00
roboomp 8da17ba3b5 fix(session): handled malformed custom messages
Normalized extension custom-message payloads before session state or persistence, including bare string sendMessage shorthands. Skipped legacy bare custom_message entries during context rebuilds and dropped malformed custom/hook messages before LLM conversion. Added regression coverage for the poisoned-session resume crash.\n\nFixes #4345
2026-07-02 20:34:15 +00:00
roboomp babb731cf5 fix(session): looped title fallback + drained backend on close
SessionManager.#persistTitleChangeEntry's catch fallback previously did a single-shot atomic rewrite: any prompt/tool appended while it awaited was fenced with #atomicRewriteDirty=true but never re-serialized. Extracted the fenced-rewrite do-while loop into #runFencedAtomicRewrite and used it from both #rewriteAtomically and #persistTitleChangeEntry, so fenced entries during either path are captured before the task resolves.

Added SessionStorage.drain(): for FileSessionStorage and MemorySessionStorage it is a no-op; IndexedSessionStorage already had one and now conforms to the interface. SessionManager.flush() and close() await it so a graceful shutdown does not exit while a fire-and-forget writeTextSync publish (queued by flushSync on an indexed backend) is still on the wire — reducing the residual publish-window race for Redis/SQL where the backend cannot be aborted mid-flight.

Regression covers the title fallback loop: TitleFallbackPausingStorage forces updateSessionTitle to throw, pauses the fallback's writeTextAtomic, appends a message and a custom entry during the pause, and asserts (a) both fenced entries land on the current JSONL, (b) the final title is applied, and (c) writeTextAtomicCalls >= 2 proving the loop iterated.

Fixes #4338
2026-07-02 20:32:27 +00:00
roboomp 24c6b3a9f9 fix(session): fenced writer close-yield inside atomic rewrite
SessionManager.#rewriteAtomically now enables #atomicRewriteActive before #closeWriterHandle() and keeps it set until the rewrite task exits, so a sync append landing in the close-yield window is fenced and cannot open a fresh writer that the pending writeTextAtomic would then detach from the current JSONL path. Same pattern applied to the #persistTitleChangeEntry atomic fallback.

Added a regression that pauses the fake storage's writer.close() gate, appends a message and a custom entry during the pause, and asserts (1) no new writer opens (writerOpens counter unchanged) and (2) the fenced entries land on the current JSONL path after the rewrite completes.

Fixes #4338
2026-07-02 19:07:36 +00:00