The previous mismatch text ('1 line has changed since last read. Use
the updated LINE#ID references shown below.') reads like a successful
edit followed by an informational note. Providers whose tool-result
plumbing does not surface `isError: true` to the model (e.g. Qwen via
the OpenAI-completions shim, where mitmproxy traces show the model only
receives the content text) treat the response as a success and proceed
with stale state.
Reword the message to start with 'Edit rejected:' and explicitly state
'The edit was NOT applied' so the failure is unambiguous in plaintext.
The structural payload (>>> markers, updated LINE#IDs) is unchanged.
Fixes#742
Mirror the existing `commands.enableClaudeUser`/`commands.enableClaudeProject`
schema entries so the OpenCode discovery provider exposes the same
user/project toggle surface as Claude. Default remains true to preserve
current behavior.
Fixes#661
The structured SQLite helper interpolates `where=` directly into SQL.
A crafted clause like `where=1=1 LIMIT 1000000 --` could comment out
the helper's bound `LIMIT ? OFFSET ?`, returning the full table in
violation of the documented pagination contract.
Validate where= at the selector boundary and reject SQL comments,
statement terminators, and pagination/attach/pragma keywords. Raw SQL
remains available via ?q=SELECT... for callers that need it.
Fixes#735
Extracts a single `formatMatchPath` helper used by both the
fast-glob/native code paths and the streaming onMatch callback so
relative paths, trailing-slash handling, and directory markers are
produced consistently. Also drops the retry-without-gitignore fallback
when the gitignored pass returns zero matches, so a broad hidden-file
pattern that is fully ignored stays fully ignored instead of silently
flipping gitignore off on the second attempt.
resolveMultiSearchPath now reports `exactFilePaths` when every token
resolves to a plain file (no globs, no suffix glob) and accepts a
single resolvable token so partially-missing lists still search the
resolvable subset. grep iterates those exact files individually
instead of collapsing them into a brace-union glob, which preserves
the user's explicit file set even when siblings share a basename.
Also adds a small `[grep] match lines use ':'; context lines use '-'`
banner when context lines are rendered, and splits the per-file
rendering helpers so files with no remaining matches no longer emit
empty headers.
Apply now recomputes per-file replacement counts from the actual apply
pass and compares them against the preview. If totals or per-file
counts drift (file changed between preview and apply, or apply matched
nothing), the tool returns an isError result explaining that the
preview is stale instead of silently claiming success with mismatched
numbers.
The resolve tool now preserves the underlying tool result's details on
ResolveToolDetails.sourceResultDetails instead of dropping them, and
the renderer distinguishes a failed apply ("Failed") from a user
discard ("Discard") so errored applies are no longer mislabelled.
tree-sitter-cpp parses `ns::doThing($ARG)` without a trailing
semicolon as declaration-like syntax, so ast_grep returns no matches.
Tell agents up-front to include the statement semicolon (or use a
looser `$CALLEE($ARG)` pattern) instead of debugging silently empty
results.
When a bash tool call requests a timeout outside the allowed 1-3600s
range, the effective clamped value and the originally requested value
are now emitted as a notice appended to the tool output and exposed on
BashToolDetails via requestedTimeoutSeconds. The renderer shows the
clamped+requested pair inline in the timeout badge.
- Reworked #prepareStopOutcome to collect stopped, terminated, and exited event waits before racing them.
- Attached noop rejection handlers to each pending wait promise to prevent unhandled rejection noise after the race settles.
- Returned Promise.race(promises) so stop outcome preparation still waits for the first relevant lifecycle event.
- Queued outbound JSON-RPC messages behind a per-client promise queue to serialize writes.
- Added a project-load gate for project-aware LSP operations before diagnostics and reference lookups.
- Retried declaration-only references with a short delay until project metadata is available, then proceeded with normal results.
- Relaxed chunk-mode parameter validation to accept `{path}`-only edits as valid delete operations.
- Updated the invalid-parameters help text to document accepted chunk delete payloads.
- Added a test that verifies a bare `{path}` edit removes the targeted chunk when null values are stripped.
- Added a streaming parser path for apply_patch envelopes that tolerates incomplete patch bodies.
- Updated apply patch preview expansion to return best-effort hunks when the renderer is in partial mode.
- Added a renderer test confirming streaming apply_patch input shows file paths without end-marker parse errors.
- Added built-in model entries for gpt-5.5 and gpt-image-2, including updated context windows, token limits, and pricing.
- Updated generated-model policy application to set or clear applyPatchToolType based on inferred GPT-5 freeform rules.
- Changed Spark edit-mode resolution to return apply_patch by default, honoring explicit replace and strict-mode overrides.
- Added tests for GPT-5 freeform policy inference and Spark edit-mode default, variant, and strict-mode behavior.
Slots a new "apply_patch" variant alongside the existing edit modes
(replace, patch, hashline, chunk, vim). The mode accepts a single input
string containing a Codex *** Begin Patch / *** End Patch envelope,
parses it with a new lenient parser (heredoc-tolerant), and fans each
file-op out to the existing executePatchSingle so LSP writethrough,
plan-mode guards, fs-cache invalidation and diagnostics are shared
with the patch mode.
Exposes both tool shapes from the spec: the JSON function-tool variant
(§1.2, {input: string}) and the OpenAI custom-tool / Lark-grammar
"freeform" variant (§1.1, raw patch string). The edit tool advertises
a Lark grammar via customFormat and a wire name via customWireName;
openai-responses emits it as a grammar-constrained custom tool when a
model opts in with applyPatchToolType: "freeform" in models.json.
custom_tool_call / custom_tool_call_output are plumbed end-to-end
through the shared responses code (emission, streaming, history
replay), and the agent-loop dispatcher matches tool calls by either
name or customWireName so returned calls route correctly.
Also threads preview/diff rendering for apply_patch through the TUI
(tool-execution + edit renderer) so streaming patches show per-file
diffs like the other edit modes.
Default edit mode is unchanged (hashline); opt in via edit.mode or
PI_EDIT_VARIANT=apply_patch.
Review-pass findings:
1. python.md (P3) — "Put workflow explanations in assistant message or
cell title" lost its **MUST** weight during compression. Agents were
observed embedding explanations as in-cell comments, polluting the
kernel and wasting tokens. Restored as "You **MUST** put workflow
explanations in the assistant message or cell title — never inside
cell code."
2. ast-grep.md + ast-edit.md (P3) — The variadic-capture warning that
$$$NAME (three dollars) is correct and $$NAME (two dollars) is
invalid was dropped during compression. LLMs trained on shell/regex
conventions where $$ is common are prone to this mistake; ast-grep
emits a generic parse error rather than a "bad metavariable"
diagnostic, so the negative example has direct reproduction value.
Added "Use $$$NAME, **NOT** $$NAME" to both files.
All 6/6 template tests pass; bun check clean.
Further compresses packages/coding-agent/src/prompts/system/system-prompt.md
from 21,530 B to 16,545 B (−23% on top of the prior compression;
−9,060 B / ~−2,265 tok per turn vs origin/main) while restoring
RFC 2119 weight on every inviolable rule and reorganizing the
pre-yield discipline.
system-prompt.md
RFC 2119 keyword restoration
All rules under `# Contract`, Procedure §§2/6, and the tail `<critical>`
block use `**MUST**` / `**MUST NOT**` keywords that the file's preamble
pins to RFC 2119. The previous draft had downgraded 29 of these to
`Do **NOT**`, which reads as below-MUST-NOT against the same preamble.
Restored sites:
- `# Contract` — all 7 inviolable bullets
- `<critical>` tail block — all 4 safety rules
- `## 6. Verification` — mock ban and no-proof yield rule
- Procedure §2 — "search for existing examples" rule regains the
PROHIBITED parallel-convention clause it lost
- `<dir-context>` — AGENTS.md read requirement regains MUST
Structural dedup
- `<source-of-truth>` and `<instruction-priority>` were two priority
lists with overlapping scope. Merged into a single
`<instruction-priority>` covering all 5 conflict-resolution levels.
- `<self-check>` and `<scope-check>` were two pre-yield checklists;
`## 6. Verification` also had a third numbered "Before yielding,
verify..." list with overlapping checks. Collapsed all three into
one `<pre-yield-check>` section (incl. "output format matches the
ask" from the old verification list). `## 6. Verification` now
focuses on verification discipline (test rigor, mocks, run-scope).
Signal restoration
Added four anchors to `<design-checklist>` that were dropped from the
earlier `<code-integrity>` block and had no home in the new structure:
- Adversarial-caller / tired-maintainer self-questioning
- Cost-of-easy-path framing
- Inhabit-the-call-site self-review
- Persistence on hard problems (do not punt half-solved work)
Handlebars fix
The `### Tool priority` header was emitted unconditionally but its
body was wrapped in `{{#ifAny python|bash}}`, producing a bare heading
with no content when neither tool was available. Header now sits
inside the conditional.
SECTION_SEPARATOR helper relocation
The `SECTION_SEPARATOR` Handlebars helper is a generic section-header
formatter that was registered in coding-agent's
`config/prompt-templates.ts`. This coupled every template consumer to
a side-effect import of that module; a prior fix added
`import "./config/prompt-templates"` to `system-prompt.ts` so the
`/system-prompt` sub-path export would register the helper, but
sibling consumers (`task/executor.ts`, `task/template.ts`) worked only
by accident because the parent agent happened to load
`system-prompt.ts` first.
- Moved `sectionSeparator` function and helper registration to
`packages/utils/src/prompt.ts` next to the other generic helpers
(`xml`, `codeblock`, `ifAny`, `includes`, `not`, `jsonStringify`).
- `packages/coding-agent/src/config/prompt-templates.ts` now
re-exports `sectionSeparator` from `@oh-my-pi/pi-utils/prompt` for
the test that imports it via the coding-agent path.
- Removed the side-effect import from
`packages/coding-agent/src/system-prompt.ts`.
Template typo fix
Also fixes a latent `SECTION_SEPERATOR` spelling (→ `SECTION_SEPARATOR`)
in the two subagent templates (`subagent-system-prompt.md`,
`subagent-user-prompt.md`). Before the move the typo was masked
because every call site and the helper shared the wrong spelling.
Post-move, the helper is canonical `SECTION_SEPARATOR` in pi-utils
and all templates match.
Verification
- `bun check` clean (TS + Rust, all 9 packages)
- `bun test packages/coding-agent/test/system-prompt-templates.test.ts`
6/6 pass (was 4/6 failing pre-existing before the infrastructure fix)
- `bun test packages/coding-agent/test/tools/task-template.test.ts`
4/4 pass (exercises the `sectionSeparator` re-export)
- `bun run format-prompts` clean
When a user models.yml declares a provider with auth: none (e.g. from
an older version), getApiKey() returns the literal 'N/A' without ever
consulting authStorage — even when valid OAuth credentials exist from
a successful /login.
Check authStorage.hasAuth() before returning kNoAuth. If real
credentials are present, fall through to the normal auth flow.
Fixes#749
Compiled Bun binaries unconditionally load bunfig.toml and .env from
the current working directory at runtime, before any application code
runs. Since omp is a coding agent that runs from arbitrary project
directories, it picks up foreign project configs -- most critically
preload directives that cause immediate crashes, but also a potential
security issue since preloads execute arbitrary code.
Add --no-compile-autoload-bunfig and --no-compile-autoload-dotenv to
the bun build --compile invocations (available since Bun v1.3.3).
Note: source installs via 'bun install -g' are still affected because
'bun run' has no equivalent flag. That remains an open issue.