Addresses the codex review comments on #1015 plus a sweep of adjacent
ACP conformance gaps surfaced while wiring them up.
Tool call + diff metadata
- acp-event-mapper: thread session cwd through and resolve every
`ToolCallLocation` (initial args, in-flight updates, result details)
to absolute paths against it; ACP requires absolute paths for
client-side file mapping.
- edit/modes/patch: emit the destination path for moves in the diff
result so post-edit "open file" actions land on the new file.
Permissions
- agent-session: pass cwd into `extractPermissionLocations` and resolve
raw `path`/`file`/etc. fields against it before sending
`session/request_permission`.
- agent-session: gate the permission wrapper on
`bridge.capabilities.requestPermission && bridge.requestPermission`,
matching the read/write/bash capability+method pattern.
acp-agent
- `authenticate`: validate `methodId` against the methods advertised by
`initialize` and reject anything else, so malformed clients fail fast.
- `setSessionConfigOption(MODE_CONFIG_ID)`: also emit
`current_mode_update` so clients tracking `modes.currentModeId` see
the same transition `session/set_mode` would produce.
- Pass `runtime.notifyConfigChanged` to builtins; emit
`available_commands_update` from a shared `reloadPlugins` helper
reused by `/reload-plugins`, `/marketplace`, and `/plugins`.
- prompt resource handling: route `resource` content with `image/*`
MIME into the `images` array instead of dropping it as an opaque
blob; non-image blobs still fall back to the URI placeholder.
- pass session cwd to the event mapper.
Builtins
- model: call `runtime.notifyConfigChanged()` after a successful
`setModel` so the ACP config selector reflects the new model
immediately.
- mcp: redact query strings and userinfo from MCP server URLs before
emitting them in `/mcp list` (prevents leaking `?exaApiKey=…` style
secrets); wire `manager.setAuthStorage(...)` before `prepareConfig`
in `/mcp test|resources|prompts` so OAuth servers can refresh tokens.
- ssh: reject non-integer `--port` values via a `^\d+$` guard instead
of silently coercing through `Number.parseInt`; list project hosts
first and dedupe user-scope duplicates to match capability-loader
precedence.
- export: reject clipboard aliases (`--copy`, `clipboard`, `copy`)
before passing them to `exportToHtml` as a filename.
- compact / force / move / browser: surface underlying failures via
`usage(errorMessage(...))` instead of letting them crash the command.
- session save|delete: route through the active SessionManager so the
persist writer is consulted and stale storage references are removed.
- marketplace / plugins / reload-plugins: call `runtime.reloadPlugins()`
on install/uninstall/upgrade and enable/disable so slash command
registries and command lists refresh consistently.
- shared.usage: make async and `await runtime.output(...)` so
`sessionUpdate` text is never dropped or reordered.
- types: document the new `reloadPlugins` and `notifyConfigChanged`
runtime hooks.
bash tool
- Use a shared `fireKill()` from the abort listener so `session/cancel`
terminates the remote command immediately instead of waiting for the
next `currentOutput()` round trip.
- Race `currentOutput()` against the abort signal so a stuck
`terminal/output` RPC cannot delay cancellation.
- Kill the terminal before reading final output on timeout so a slow
output read cannot let a timed-out command keep running past the
enforced timeout.
Tests
- acp-agent.test: extend the existing config-option assertions to
verify both `model` and `thinking_level` changes emit
`config_option_update` notifications scoped to the right session.
- acp-builtins.test: cover `/model` emitting both
`notifyTitleChanged` and `notifyConfigChanged`; lock in the parsed
`mcp add` / `ssh add` call shapes so future arg-parser regressions
fail the test instead of silently writing different configs; add a
`reloadPlugins` stub plus a typed `notifyConfigChanged` slot to the
shared test runtime factory.
- acp-stdout-hygiene.test: drain stderr in parallel and assert no
JSON-RPC frame leaks onto it; terminate the spawned process so the
stderr pump resolves deterministically.
CHANGELOG: itemize the above under `[Unreleased] > Fixed`.
CI
- bun run check: clean (TS + Rust)
- bun run test: 4128 pass / 689 skip / 0 fail (TS); 252 pass / 0 fail
(Rust nextest)
- bun run ci:test:smoke: --version / --help / `stats --help` all OK
- BashTool dispatches execution through the client bridge terminal channel when a bridge is present, falling back to local PTY otherwise
- ReadTool and WriteTool gate filesystem access through ACP permission checks
- Exports new tool wiring in the tools barrel
- Hardened `parseUrl` to decode each internal segment and reject empty, `.` or `..` segments.
- Added `AbortSignal` propagation through `ReadTool` into internal URL resolution to honor cancellation.
- Adjusted markdown rendering in `read` output so raw selector reads bypass markdown formatting.
- Aligned `conflict://` help text in `read`/`write` with URI read-path examples for scope conflicts.
- Removed one-shot eviction in openDb(), preventing cache rows from being purged before settings load.
- Moved hard-TTL enforcement to getOrFetchView() sweepIfDue() so configured retention applies per lookup.
- Extended github-cache tests to verify row persistence across reopen and expiry under stricter hardTtl.
- Updated event-controller read-tool streaming handling to wait for a parseable target before routing tool calls, avoiding early component binding for unresolved arguments.
- Allowed internal-URL read calls to bypass the regular read grouping path and fall through to direct tool execution.
- Adjusted read tool rendering to honor the computed `expanded` flag for completed output instead of forcing expanded output.
- Fixed `issue://owner/diff` and `pr://owner/diff` parsing so they resolve to issue and PR list outputs.
- Fixed `pr` short-form parsing by requiring `scheme==='pr'` and a numeric host before `diff` matching.
- Fixed PR unified-diff parsing to decode quoted header paths and count `----`/`++++` hunk lines as one deletion/addition.
- Fixed `read` error rendering to emit status blocks with cleaned, range-aware, tab-normalized lines.
- Stopped `github-cache` from chmod-ing existing parent directories, preserving pre-existing permission modes.
- Guarded session manager lookup in internal URL cwd resolution to handle missing managers safely.
- Replaced `Promise.finally` cleanup with explicit `then` handlers so default-repo in-flight entries are removed on both success and failure.
- Added readArgsTargetInternalUrl in the read tool group component to detect targets handled by InternalUrlRouter from path or file_path arguments.
- Updated event-controller and UI helper read rendering paths to skip grouping read tool calls when those arguments target internal URLs.
- Passed session cwd and settings into internal URL resolution in read.ts and added tests for internal versus non-internal target detection.
- Added issue:// and pr:// URL handlers for single lookups and list queries with query filters.
- Added a SQLite-backed GitHub cache with soft/hard TTLs, stale hits, and background stale refresh.
- Removed issue_view and pr_view tool operations, inputs, and docs, requiring reads via issue:// and pr:// URLs.
- Added github-cache and issue-pr-protocol tests with temporary cache DB setup and OMP_GITHUB_CACHE_DB teardown.
- Switched issue and PR search handlers to `gh api /search/issues` with `is:issue`/`is:pr` queries.
- Added REST search response models and mapped issue/code/commit/repo payloads to normalized results.
- Updated code, commit, and repo search parsing to read `{items}` envelopes and convert snake_case fields.
- Fixed merged-PR output state by deriving it from `pull_request.merged_at` in test fixtures.
- Replaced `with { type: "file" }` worker imports with `isCompiledBinary()` hybrid: literal string for `--compile` static analysis, `new URL(import.meta.url)` for dev portability.
- Added worker entrypoints as explicit `--compile` args in `build-binary.ts` so Bun emits them into bunfs.
- Added `smokeTestSyncWorker` and `omp --smoke-test` to catch silent worker-load failures in compiled binaries (fixes#1011, #1027).
- Added `isCompiledBinary()` utility to `@oh-my-pi/pi-utils` detecting bunfs path markers.
- Added `:conflicts` and `read conflict://<N>`/`read conflict://<N>/<scope>` support and rejected wildcard conflict reads.
- Added bulk conflict resolution via `write({ path: "conflict://*", ... })` across files with per-file grouping.
- Expanded conflict detection to scan files up to 10MB, track insertion-ordered history, and report full `X of Y` summaries.
- Reworked `spliceConflict` to match marker blocks by content, fixing shifted or stale block splicing.
- Added coverage for wildcard parsing, scoped reads, prepended-line splices, relocation, and warning assertions in detect/integration tests.
- Added `ConflictScope` parsing for `conflict://<N>/<scope>` with `ours`, `theirs`, and `base` validation.
- Added `read` support for full and scoped conflict URIs, rendering regions via `#readConflictRegion` with preserved formatting metadata.
- Added conflict-count and error handling in read results, including `Conflict #N not found` responses.
- Added `write`-path rejection for scoped conflict URIs, returning `ToolError` before lookup to enforce read-only behavior.
- Added unit and integration tests for scope parsing, conflict rendering, and read/write conflict error scenarios.
- Added conflictCount metadata and warning badge output to read results for files with unresolved conflicts.
- Added conflict detection parsing with strict marker matching and session-scoped conflict IDs.
- Added write-path conflict resolution for `conflict://N` using token expansion and marker validation before splicing.
- Added unit and integration tests for conflict scanning, history lifecycle, URI validation, and workflows.
- Introduced canonical `*** Cell` headers with `t:` and `rst` attributes in eval prompts, schema, and docs.
- Updated parser and grammar to parse `*** Cell` blocks, stop on `*** End`/next header/EOF, and handle invalid `rst` with errors.
- Added quote-aware attribute tokenizers and split HTML eval parsing into `Cell` and legacy `Begin` handlers with `py` defaults.
- Expanded parsing behavior and tests for `rst` booleans, title aliases, abort boundaries, and stray-line skips between cells.
- Documented why the tab worker entry must be imported with Bun's file attribute despite tsgo limitations.
- Added a @ts-expect-error on the tabWorkerEntryUrl import to suppress tsgo TS1192/TS5097 errors.
The Worker spawn in spawnTabWorker used `new Worker(new URL(...).href, ...)`
through a local variable, which Bun's --compile static analysis does not
recognize. As a result, tab-worker-entry.ts was never embedded in single-file
binaries (v14.5.13+), and the worker thread failed to load its entry at
/$bunfs/root/tab-worker-entry.ts, surfacing as the user-visible
"Timed out initializing browser tab worker".
Switch to a `with { type: "file" }` import of the entry module so the
bundler discovers and embeds it in both dev and compiled targets.
Regression test asserts `Bun.build` emits tab-worker-entry as an asset
alongside the tab-supervisor entry point — the same static-analysis pass
that powers `bun build --compile`.
- Added optional since/until/dateField parameters to issue, PR, commit, and repo search tools.
- Added parsing for relative and ISO date bounds and built inclusive/exclusive/range search qualifiers.
- Allowed issue/PR/commit/repo searches to proceed with only date ranges and no query string.
- Mapped tool-specific date fields (commits, repos) and rejected since/until for code search validation.
- Updated search tool docs and tests for date filter formats, mappings, and unsupported-code-search behavior.
- Added ownerId metadata to async jobs and to task/bash progress items from the session agent id.
- Extended async job registration and query methods with optional owner filters, and updated cancelAll to target matching owners.
- Updated session handoff and disposal so subagents inherit the parent manager, top-level sessions own it, and teardown cancels own jobs only.
- Added owner-aware async-job tests using hold/AbortSignal and scoped cancelAll assertions for running versus cancelled jobs.
- Added parent-to-subagent artifact manager adoption so subagents reuse the parent `ArtifactManager` and write artifacts into a shared directory with shared IDs.
- Passed the shared artifact manager through tool/session context into subagent executor startup and exposed it via `SessionManager` and `ToolSession` for lookup.
- Updated kernel environment and artifact-resolution paths to prefer `PI_ARTIFACTS_DIR`, falling back to existing session-file-based behavior when absent.
- Added process-wide singleton instances for InternalUrlRouter, AsyncJobManager, and MCPManager.
- Changed internal URL protocols to resolve through registered sessions and scan all active roots/datasets for matches.
- Refactored agent, artifact, memory, rule, skill, jobs, and mcp handlers to use shared manager and rule/skill state.
- Removed per-session protocol/tool wiring and switched tests to initialize and reset global singleton state.
- Added helpers to stringify and truncate display() JSON values before including them in text responses.
- Updated eval execution to append formatted display output text alongside stdout and emit images as dedicated content blocks.
- Adjusted no-output messaging for image-only runs and removed detail-level image payload duplication when content already includes image blocks.
- Updated bash, job, and task prompts to state that background results are delivered automatically when complete.
- Removed guidance encouraging repeated `jobs://` polling and clarified `job` with `poll` should be used only when a task is blocking.
- Changed the bash tool confirmation message to recommend doing other work while waiting for background jobs and polling only if needed.
- Removed the shared date, workspace, and critical-response block from now-prompt.md.
- Added the same instructional block to project-prompt.md so the project prompt now carries those system constraints.
Two-edge fix for a runtime circular-import TDZ that manifested as
`ReferenceError: Cannot access 'TaskTool' / 'SUBAGENT_WARNING_*' /
'MAX_OUTPUT_BYTES' before initialization.` whenever the executor module
graph and the task tool module graph were evaluated together (e.g.
`bun test executor-warnings.test.ts task-simple-mode.test.ts` in either
order, or the wider `task/ discovery/ task-simple-mode` combination).
The runtime cycle is
task/index.ts → ./executor → ../sdk → ./tools → ../task
closed by `tools/index.ts:285` eagerly dereferencing `TaskTool.create`
while the `task` module's body had not yet reached its `export class
TaskTool` declaration. The throw aborted `tools/index.ts`, which
propagated back through `sdk → executor`, leaving executor's body
suspended before its post-import `const`s (`SUBAGENT_WARNING_*`,
`MAX_OUTPUT_BYTES`) were initialized.
Two structural changes:
1. `tools/index.ts:285`: replace `task: TaskTool.create` with
`task: s => TaskTool.create(s)`. Defers the `TaskTool` binding
dereference to factory-call time, by which point the cycle has
fully unwound. Matches the lazy-factory shape every other
`BUILTIN_TOOLS` entry already uses.
2. `task/executor.ts:31`: split the `"../tools"` import. Source
`truncateTail` directly from its leaf module
`../session/streaming-output` (the barrel was just re-exporting
it). Keep `ContextFileEntry` as a type-only import — erased at
runtime, so no participation in the cycle.
Neither change touches tests, test runners, or removes the cycle in
source. They eliminate the eager dereferences that turned a benign
linker-level cycle into a TDZ at evaluation time.
Verified:
- `bun test executor-warnings.test.ts task-simple-mode.test.ts`
passes in both orderings (11/11).
- Wider `bun test packages/coding-agent/test/task/
packages/coding-agent/test/discovery/
packages/coding-agent/test/tools/task-simple-mode.test.ts` now
100/100 (was 14 fail / 1 error).
- `bun --cwd=packages/coding-agent run check` clean apart from the
pre-existing unrelated `anthropic.ts:1206 'stop_details'` error
from 4e0ca3c0e.
Co-Authored-By: omp <noreply@oh-my-pi.dev>
- Updated `irc` tool availability logic to require both `irc` settings and concurrency support.
- Imported `MAIN_AGENT_ID` and suppressed IRC access for the main agent when async mode is disabled, preventing sync-mode peer messaging.
- Replaced `===== ... =====` eval cell headers with `*** Begin ` / `*** End ` markers; legacy format remains renderable in HTML exports.
- Replaced hashline patch grammar with `*** Begin Patch` / `*** End Patch` envelope; old inputs without the envelope are still accepted.
- Extracted `sniffEvalLanguage` into a shared `sniff.ts` module reused by the parser and tool.
- Added `docs/ERRATA-GPT5-HARMONY.md` and `scripts/session-stats/harmony_backtest.py` documenting and backtesting the GPT-5 Harmony-header leak defect.
- Deleted `bash-normalize.ts` and its tests; output truncation is handled by the streaming tail buffer and artifact spillover.
- Removed `head`/`tail` schema fields from `bashSchema` and `BashToolInput`.
- Updated system prompt and bash tool prompt to forbid `| head`/`| tail` pipes and other anti-patterns, directing the agent to use dedicated tools instead.
- Added immutable metadata to protocol handlers and had the router copy each handler's setting onto resolved internal resources.
- Updated read and search tools to honor `resource.immutable`, and made search suppress hashline anchors only for immutable source paths while preserving them for mutable files.
- Expanded internal URL tests to cover mutable local resources and mixed immutable/mutable search input hashline behavior.
- Extended splitPathAndSel to detect compound selectors that combine a line range with `raw` in either order.
- Updated read selector parsing to support `:lines:raw` and `:raw:lines` selectors and propagate raw-mode handling through internal URL, archive, and notebook paths.
- Added tests covering compound selector syntax and confirming it returns verbatim content without anchors or line-number prefixes.
- Extended file-display resolution to accept an immutable flag and suppress hashline anchors when immutable sources are requested.
- Plumbed immutable propagation through ReadTool and SearchTool so internal URL reads and searches pass that context.
- Added a regression test confirming artifact:// search output no longer includes hashline anchors.
- Added `listWorkspace` native binding and API types, exporting bounded workspace trees with AGENTS.md candidates.
- Reworked `buildWorkspaceTree` and `buildDirectoryTree` to call `listWorkspace` with 5s timeout defaults.
- Replaced startup AGENTS.md discovery with workspace-tree-only scanning and removed legacy AgentsMdSearch session plumbing.
- Updated `WorkspaceTree` and system prompt context to expose `agentsMdFiles` and aligned tests/changelog expectations.
- Added `./hashline` package exports and redirected callers to the new hashline entrypoint.
- Moved hashline logic out of `edit/` to `src/hashline` and removed `edit/modes/hashline`/`edit/line-hash` paths.
- Added hashline parsers, anchors, types, and diff helpers with stricter input and mismatch validation.
- Implemented preflight and cache-recovery execution flows to reapply edits and handle stale anchor mismatches.
- Documented the hashline API relocation as breaking changes in `CHANGELOG.md`.
- Implemented hashline stale-anchor recovery using cached reads and a 3-way merge fallback.
- Updated hashline execution and preflight checks to retry mismatched anchors through cache recovery.
- Added file-read cache support with per-session LRU snapshots and contiguous/sparse record APIs.
- Integrated read and search tools with the shared cache to record candidate lines for recovery.
- Added tests for stale-anchor recovery flows and FileReadCache session, null, overlap, and eviction behavior.
- Expanded read tool range calculations to include optional leading and trailing context lines around user-requested offsets and limits.
- Added shared context range expansion logic so line-slice and streaming reads return anchor-safe windows while preserving existing truncation behavior.
- Updated read-tool tests to assert boundary-offset, limit, and combined offset-limit reads now include the expected ±3 lines of context.
- Added an intent function to EvalTool that generated a label from provided input.
- Parsed input with parseEvalInput and joined each cell title or language fallback into a newline string.
- Returned "evaluating" when input was missing or could not be parsed.
Subagents previously re-ran buildAgentsMdSearch and buildWorkspaceTree on
every spawn, repeating the slowest part of system-prompt construction for
each task tool invocation. On large/pathological repos those scans
exceeded the 5s preparation deadline and tripped the per-subagent
'system prompt preparation timed out' warning.
Forward the parent's already-resolved AgentsMdSearch and WorkspaceTree
through createAgentSession (alongside the existing contextFiles, skills,
and promptTemplates inheritance):
- Add agentsMdSearch and workspaceTree to CreateAgentSessionOptions;
createAgentSession short-circuits the parallel scan promises when
these are provided.
- Resolve them with contextFiles before constructing ToolSession; expose
on ToolSession so the task tool can read the parent's values.
- Thread them through ExecutorOptions (task/executor.ts) into the
subagent's createAgentSession call, and pass them from the task tool
(task/index.ts) on both the worktree-isolated and non-isolated paths.
- Updated the read tool docs to define URL selectors as `:50`, `:50-100`, and `:50+150` and to document the `https://host/:port` form for URLs with explicit ports.
- Changed URL selector parsing to use the file-style numeric range format, treated `raw` as a separate token, and updated the invalid zero-line message to direct users to `:1`.
- Refined embedded URL selector extraction to validate the base URL first and then match selectors against the new numeric range regex.
- Removed the read CLI argument and tool schema field so read requests no longer accept custom timeouts.
- Updated URL read handling to stop forwarding timeout values and execute URL reads without a timeout parameter.
- Standardized URL read fetching to a fixed 30-second timeout and dropped timeout metadata from URL call rendering.
- Added a readonly intent property to the write tool implementation.
- The intent now returned a contextual message using `args.path` when available, otherwise a generic "writing" label.