Repository navigation
Commit a88f406
committed
fix: hoist <system-reminder> to system role for non-Anthropic models + warning false-positive
Two related bugs the trace finally revealed, after the qwen.txt theory in
b07da4e didn't actually fix the refusal in production:
1. **Root cause of GPT-5.x refusals**: prompt.ts:2049 attaches PROMPT_PLAN
(wrapped in `<system-reminder>...</system-reminder>` tags) as a text
part on the latest user message. Claude is trained to recognize that
tag as authoritative system-level instructions even when delivered in
a user-role message. OpenAI / Google / etc. models are NOT — they see
a user message containing a `<system-reminder>` block with aggressive
constraint language (STRICTLY FORBIDDEN / ZERO exceptions / ABSOLUTE
CONSTRAINT / MUST) and pattern-match it as a prompt-injection attempt.
GPT-5.x via `altimate-backend/altimate-default` reliably returned
"I'm sorry, but I cannot assist with that request." for benign
developer requests like "plan a feature to add a button…". Confirmed
by reading the actual LLM payload via temporary debug instrumentation
in `llm.ts` (since removed).
Fix in `llm.ts`: for non-Anthropic-like models, walk
`input.messages` and extract any `<system-reminder>`-wrapped text
parts from user-role messages, then append them to the system array
so they arrive as proper `role:"system"` messages. User messages that
become empty after extraction are dropped. Anthropic-like models keep
the existing behavior.
Detection of Anthropic-like uses providerID + family + api.npm + the
existing api.id substring patterns, matching the heuristics already
used in `provider/transform.ts:284-295` for cache-control handling.
2. **Counter scope bug in plan-no-tool warning**: `sessionToolCallsMade`
in `processor.ts:57` was claimed (comment line 207) to be session-
scoped, but `SessionProcessor.create()` is called per-step by
`loop()` in `prompt.ts:485`, so the counter resets each step. A
multi-step plan session (read → grep → read → … → final text)
false-positives on the last text-only step. The warning fires *after*
the agent has successfully explored the codebase and produced a
plan — confusing the user into thinking the session failed.
Fix: at warning-check time, also scan `streamInput.messages` for any
prior assistant tool-call content. The session has used tools iff
either the per-step counter is non-zero OR the history contains a
tool-call. Regression test added in `processor.test.ts`.
The earlier `system.ts` family-based routing (commit b07da4e) stays.
It was based on a wrong root-cause theory, but the change itself is
correct: a model with `family: "openai"` should not fall through to
qwen.txt (an Anthropic-style fallback). Worth keeping as defense in
depth even though it wasn't the load-bearing fix.
Touches:
- `packages/opencode/src/session/llm.ts`: system-reminder hoist.
- `packages/opencode/src/session/processor.ts`: warning false-positive
fix + comment block explaining the per-step scoping.
- `packages/opencode/test/session/processor.test.ts`: regression test
for the warning false-positive.
Typecheck clean. 124 affected tests pass.
This is the actual fix for #887 / AI-6957.1 parent b07da4e commit a88f406
3 files changed
Lines changed: 91 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
67 | 109 | | |
68 | 110 | | |
69 | 111 | | |
| |||
72 | 114 | | |
73 | 115 | | |
74 | 116 | | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
75 | 121 | | |
76 | 122 | | |
77 | 123 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
348 | 348 | | |
349 | 349 | | |
350 | 350 | | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
351 | 368 | | |
352 | 369 | | |
353 | 370 | | |
354 | | - | |
| 371 | + | |
355 | 372 | | |
356 | 373 | | |
357 | 374 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
474 | 474 | | |
475 | 475 | | |
476 | 476 | | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
477 | 484 | | |
| 485 | + | |
478 | 486 | | |
479 | 487 | | |
480 | 488 | | |
481 | | - | |
| 489 | + | |
482 | 490 | | |
483 | 491 | | |
484 | 492 | | |
| |||
535 | 543 | | |
536 | 544 | | |
537 | 545 | | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
538 | 564 | | |
539 | 565 | | |
540 | 566 | | |
| |||
0 commit comments