FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(session): fire system.transform before messages.transform by ualtinok · Pull Request #19961 · anomalyco/opencode · GitHub

fix(session): fire system.transform before messages.transform - #19961

Open
ualtinok wants to merge 2 commits into
anomalyco:devfrom
ualtinok:fix/plugin-hook-ordering
Open

fix(session): fire system.transform before messages.transform#19961
ualtinok wants to merge 2 commits into
anomalyco:devfrom
ualtinok:fix/plugin-hook-ordering

Conversation

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #19960

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Reorders the plugin hook execution so experimental.chat.system.transform fires before experimental.chat.messages.transform. Previously, messages.transform ran first in prompt.ts while system.transform ran later inside llm.ts — plugins couldn't inspect the final system prompt when mutating messages.

The system prompt assembly (agent prompt + env + skills + instructions + user.system) and the system.transform hook + header caching logic were extracted from llm.ts into shared helpers (LLM.buildSystem, LLM.transformSystem). These are now called in prompt.ts before messages.transform, and llm.ts receives the already-finalized system prompt.

Also adds sessionID and model to the messages.transform input (previously empty {}), so plugins can correlate transformations across hooks.

agent.ts's independent system.transform call (for agent generation) is unchanged — different code path.

How did you verify your code works?

  • Typecheck passes clean
  • Full test suite: 1625 pass, 0 fail, 8 skip (identical to dev baseline)
  • Verified LLM.buildSystem produces the same system prompt shape as the old inline assembly in llm.ts
  • Verified LLM.transformSystem preserves the header caching rejoin behavior

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Copy link
Copy Markdown
Collaborator

/review

Comment thread packages/opencode/src/session/prompt.ts Outdated
Comment thread packages/opencode/src/session/llm.ts Outdated

Copy link
Copy Markdown
Collaborator

/review

Copy link
Copy Markdown
Collaborator

This looks like a lot more changes than simply changing the orders just at a glance

})
const finalizeFailedSummary = (error: unknown) =>
Effect.gen(function* () {
msg.error = MessageV2.fromError(error, { providerID: model.providerID })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Suggestion for the human to consider: this summary failure path persists msg.error, but it does not set msg.finish. Since failed compaction summaries are later returned as the last assistant message, a subsequent loop can see an errored summary with no finish and attempt the compaction path again instead of treating the turn as complete. Consider setting msg.finish = "error" before updateMessage, matching the existing compact-result error path below.


const finalizeFailedAssistant = (error: unknown) =>
Effect.gen(function* () {
msg.error = MessageV2.fromError(error, { providerID: model.providerID })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Suggestion for the human to consider: this new pre-stream failure path records msg.error, but it leaves msg.finish unset. The prompt loop only exits early when the latest assistant has a non-tool finish, so a later loop() call on the same session can treat this errored assistant as unfinished and retry the same user turn. This probably should mark the assistant as finished, consistent with other error paths that use finish = "error".

Copy link
Copy Markdown
Contributor Author

This looks like a lot more changes than simply changing the orders just at a glance

I will explain the reasoning when I get back home.

ualtinok marked this pull request as draft May 16, 2026 12:54
ualtinok marked this pull request as ready for review May 19, 2026 05:24

Copy link
Copy Markdown
Contributor Author

Hey @rekram1-node, I made some changes after your first review. Let me first explain why it looks more changes than needed:

The original plan was just reordering the 2 hooks but as LLM.stream contract has direct callers, we needed to preserve backwards compatibility to make the least changes. That was wrong call, we now removed the backward compatibility and all callers including compaction route uses the same contract.

I've been testing this PR on my local since the implementation and there's no problems observed. If you need longer explanation of the surface, I can give more detailed answer. I believe this PR is ready to ship.

elmagow commented May 26, 2026

Copy link
Copy Markdown

Can we have this reviewed? thanks

ualtinok force-pushed the fix/plugin-hook-ordering branch 5 times, most recently from 8c371a9 to 1cb9c3e Compare June 6, 2026 09:45
ualtinok force-pushed the fix/plugin-hook-ordering branch 3 times, most recently from c97494d to 01faef0 Compare June 14, 2026 05:03
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Jun 17, 2026
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Jun 17, 2026
ualtinok force-pushed the fix/plugin-hook-ordering branch from 16d3672 to 26b60dc Compare June 28, 2026 10:23
randomvariable added a commit to randomvariable/opencode that referenced this pull request Jun 30, 2026
Keep the volatile current date out of the cached system prefix for DeepSeek
models (both the V2 runner and V1 path) and instead append it to the
trailing user turn, add SystemContext.omit, and rework DeepSeek reasoning
handling so reasoning_content is only replayed across assistant tool turns
(and caching hints are skipped) — maximizing DeepSeek prompt-prefix cache
reuse.

Ported from anomalyco#31867 (adapted for the Effect layer; the prepare() date test
passes an explicit system prompt since anomalyco#19961 moved system assembly to
callers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
randomvariable added a commit to randomvariable/opencode that referenced this pull request Jun 30, 2026
Add an experimental subagent-interrupt capability (behind
OPENCODE_EXPERIMENTAL_SUBAGENT_INTERRUPT): an Interrupt service with
pending/terminal state, task_steer/task_cancel/task_abort tools (gated by a
new `interrupt` permission), a POST /session/:id/interrupt endpoint, and a
turn-boundary consume in the prompt loop that injects an escaped steer/cancel
frame plus a visible transcript marker. Cancel records a terminal reason and
force-breaks after a grace window; abort writes a marker, records the
terminal, and cancels the BackgroundJob immediately. The TUI gains an
esc-with-reason flow, footer button, and distinct interrupt markers.

Ported from anomalyco#32425 (adapted for the Effect layer; merged with the prior
task.ts/registry.ts/prompt.ts changes from anomalyco#29447/anomalyco#32122/anomalyco#12520/anomalyco#32192/anomalyco#19961).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ualtinok force-pushed the fix/plugin-hook-ordering branch from 26b60dc to 6e7412f Compare July 2, 2026 08:15
ualtinok force-pushed the fix/plugin-hook-ordering branch from db13429 to 43afdf2 Compare July 7, 2026 08:56
randomvariable added a commit to randomvariable/opencode that referenced this pull request Jul 10, 2026
Keep the volatile current date out of the cached system prefix for DeepSeek
models (both the V2 runner and V1 path) and instead append it to the
trailing user turn, add SystemContext.omit, and rework DeepSeek reasoning
handling so reasoning_content is only replayed across assistant tool turns
(and caching hints are skipped) — maximizing DeepSeek prompt-prefix cache
reuse.

Ported from anomalyco#31867 (adapted for the Effect layer; the prepare() date test
passes an explicit system prompt since anomalyco#19961 moved system assembly to
callers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
randomvariable added a commit to randomvariable/opencode that referenced this pull request Jul 10, 2026
Add an experimental subagent-interrupt capability (behind
OPENCODE_EXPERIMENTAL_SUBAGENT_INTERRUPT): an Interrupt service with
pending/terminal state, task_steer/task_cancel/task_abort tools (gated by a
new `interrupt` permission), a POST /session/:id/interrupt endpoint, and a
turn-boundary consume in the prompt loop that injects an escaped steer/cancel
frame plus a visible transcript marker. Cancel records a terminal reason and
force-breaks after a grace window; abort writes a marker, records the
terminal, and cancels the BackgroundJob immediately. The TUI gains an
esc-with-reason flow, footer button, and distinct interrupt markers.

Ported from anomalyco#32425 (adapted for the Effect layer; merged with the prior
task.ts/registry.ts/prompt.ts changes from anomalyco#29447/anomalyco#32122/anomalyco#12520/anomalyco#32192/anomalyco#19961).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ualtinok force-pushed the fix/plugin-hook-ordering branch from 43afdf2 to 5207442 Compare July 15, 2026 07:18
randomvariable added a commit to randomvariable/opencode that referenced this pull request Jul 17, 2026
Keep the volatile current date out of the cached system prefix for DeepSeek
models (both the V2 runner and V1 path) and instead append it to the
trailing user turn, add SystemContext.omit, and rework DeepSeek reasoning
handling so reasoning_content is only replayed across assistant tool turns
(and caching hints are skipped) — maximizing DeepSeek prompt-prefix cache
reuse.

Ported from anomalyco#31867 (adapted for the Effect layer; the prepare() date test
passes an explicit system prompt since anomalyco#19961 moved system assembly to
callers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
randomvariable added a commit to randomvariable/opencode that referenced this pull request Jul 17, 2026
Add an experimental subagent-interrupt capability (behind
OPENCODE_EXPERIMENTAL_SUBAGENT_INTERRUPT): an Interrupt service with
pending/terminal state, task_steer/task_cancel/task_abort tools (gated by a
new `interrupt` permission), a POST /session/:id/interrupt endpoint, and a
turn-boundary consume in the prompt loop that injects an escaped steer/cancel
frame plus a visible transcript marker. Cancel records a terminal reason and
force-breaks after a grace window; abort writes a marker, records the
terminal, and cancels the BackgroundJob immediately. The TUI gains an
esc-with-reason flow, footer button, and distinct interrupt markers.

Ported from anomalyco#32425 (adapted for the Effect layer; merged with the prior
task.ts/registry.ts/prompt.ts changes from anomalyco#29447/anomalyco#32122/anomalyco#12520/anomalyco#32192/anomalyco#19961).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Jul 25, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Jul 25, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Jul 26, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Jul 26, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 2, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
randomvariable added a commit to randomvariable/opencode that referenced this pull request Aug 3, 2026
Keep the volatile current date out of the cached system prefix for DeepSeek
models (both the V2 runner and V1 path) and instead append it to the
trailing user turn, add SystemContext.omit, and rework DeepSeek reasoning
handling so reasoning_content is only replayed across assistant tool turns
(and caching hints are skipped) — maximizing DeepSeek prompt-prefix cache
reuse.

Ported from anomalyco#31867 (adapted for the Effect layer; the prepare() date test
passes an explicit system prompt since anomalyco#19961 moved system assembly to
callers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
randomvariable added a commit to randomvariable/opencode that referenced this pull request Aug 3, 2026
Add an experimental subagent-interrupt capability (behind
OPENCODE_EXPERIMENTAL_SUBAGENT_INTERRUPT): an Interrupt service with
pending/terminal state, task_steer/task_cancel/task_abort tools (gated by a
new `interrupt` permission), a POST /session/:id/interrupt endpoint, and a
turn-boundary consume in the prompt loop that injects an escaped steer/cancel
frame plus a visible transcript marker. Cancel records a terminal reason and
force-breaks after a grace window; abort writes a marker, records the
terminal, and cancels the BackgroundJob immediately. The TUI gains an
esc-with-reason flow, footer button, and distinct interrupt markers.

Ported from anomalyco#32425 (adapted for the Effect layer; merged with the prior
task.ts/registry.ts/prompt.ts changes from anomalyco#29447/anomalyco#32122/anomalyco#12520/anomalyco#32192/anomalyco#19961).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 8, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 15, 2026
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 19, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 22, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
Qiiks added a commit to Qiiks/opencode that referenced this pull request Aug 23, 2026
…quest prep

The Aug-15 upstream merge dropped both firings of this hook: ualtinok's
prompt.ts-side trigger from fcb023e (port of anomalyco#19961) conflicted away,
and upstream's request.ts-side block never landed in the resolution.
Every plugin registering the hook (magic-context guidance injection +
prompt-hash cache-flush coordination, aft-opencode hints) has been
silently inert since.

Restore upstream v1.18.18 semantics: fire after system assembly with
{sessionID, model}, then rejoin a mutated multi-part system into
header+rest so prompt-prefix caching stays stable. Regression test
proves prepare() now delivers plugin mutations into prepared.system.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 28, 2026
PR anomalyco#19961 moves system assembly out of LLMRequestPrep.prepare into the
callers, so a caller passing raw parts now loses the agent prompt. This
fixture predates the PR's base, so it was not updated with the others.
The recorded fixtures are unchanged and still assert the agent prompt is
present in the request body.
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 29, 2026
Upstream PR anomalyco#19961 by @ualtinok, ported onto current dev. The design and
the original implementation are theirs; this commit adapts them to dev's
current session and runtime structure and updates the recorded-fixture
caller that the reordering affects.

Co-authored-by: ualtinok <ufukaltinok@gmail.com>
iceteaSA added a commit to iceteaSA/opencode that referenced this pull request Aug 29, 2026
Carries five third-party upstream PRs, one commit each, with original
authorship and Co-authored-by trailers preserved:
  anomalyco#32203 @ualtinok      stabilize duplicate skill discovery
  anomalyco#31859 @ualtinok      reject bootstrap client reentry
  anomalyco#19961 @ualtinok      fire system transform before messages transform
  anomalyco#39930 @brauliobo     bound compacted history hydration
  anomalyco#31867 @ChangedenCZD  improve deepseek prompt cache reuse

Reconciles anomalyco#19961's hook reordering with the fork's sparse-context and
instruction-audience handling: sparse still branches before skills/MCP are
built, and modelMsgs is computed after messages.transform so plugin
mutations are picked up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

system.transform fires after messages.transform — plugins can't coordinate system+message mutations

3 participants


Back | FazBrowse Home | New Git URL