fix: silence AI SDK "system message in messages" warning in act/extract/observe#2359
Conversation
…ct/observe PR #2305 fixed this for agent.execute()'s outer loop (v3AgentHandler.ts) but missed act()/extract()/observe(), which build the same system-role-in-messages shape and route through a separate, unpatched generateObject/generateText call in AISdkClient.createChatCompletion() (both the internal default client and the public BYOC external client). Since the hybrid/DOM agent's own act/extract/observe tools call these primitives on every step, agent.execute() runs kept re-triggering the warning even with the earlier fix applied. Add allowSystemInMessages: true to the remaining 4 call sites, same pattern as the original fix.
There was a problem hiding this comment.
All reported issues were addressed across 3 files
Confidence score: 5/5
- Safe to merge after the addressed issues were fixed.
Architecture diagram
sequenceDiagram
participant Client as External Caller
participant Agent as Hybrid Agent
participant ActTool as act() tool
participant ExtractTool as extract() tool
participant ObserveTool as observe() tool
participant AISdkClient as AISdkClient (llm/aisdk.ts)
participant BYOCClient as BYOC AISdkClient (external_clients/aisdk.ts)
participant OpenAI as AI SDK / LLM Backend
Note over Client,OpenAI: System prompt is built as { role: "system", ... } message inside messages array
Client->>Agent: agent.execute() (multi-step loop)
Agent->>ActTool: internal tool call (click/type)
ActTool->>AISdkClient: createChatCompletion() with system message
Note over AISdkClient: allowSystemInMessages: true (generation call)
AISdkClient->>OpenAI: generateText() with system in messages
Agent->>ExtractTool: internal tool call
ExtractTool->>AISdkClient: createChatCompletion() with system message
Note over AISdkClient: allowSystemInMessages: true (generation call)
AISdkClient->>OpenAI: generateObject() with system in messages
Agent->>ObserveTool: internal tool call
ObserveTool->>AISdkClient: createChatCompletion() with system message
Note over AISdkClient: allowSystemInMessages: true (generation call)
AISdkClient->>OpenAI: generateText() with system in messages
Note over AISdkClient: Two call sites: generateObject (response_model) and generateText (tools)
alt External caller uses BYOC (Bring Your Own Client)
Client->>BYOCClient: createChatCompletion() with system message
Note over BYOCClient: allowSystemInMessages: true (generation call)
BYOCClient->>OpenAI: generateObject() with system in messages
Client->>BYOCClient: createChatCompletion() with system message
Note over BYOCClient: allowSystemInMessages: true (generation call)
BYOCClient->>OpenAI: generateText() with system in messages
Note over BYOCClient: Two call sites: generateObject (response_model) and generateText (tools)
end
Note over Client,OpenAI: Total 4 call sites patched (2 in llm/aisdk.ts, 2 in external_clients/aisdk.ts)
Note over OpenAI: AI SDK v5 warning suppressed for all system-in-messages calls
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…xt calls Addresses cubic review feedback on #2359: add focused regression coverage so a future refactor can't silently drop allowSystemInMessages and reintroduce the AI SDK warning, for both the internal default client (llm/aisdk.ts) and the public BYOC client (external_clients/aisdk.ts).
🦋 Changeset detectedLatest commit: d001bd5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
pirate
left a comment
There was a problem hiding this comment.
Approved assuming the warning is actually spurious.
If we are indeed repeating the system prompt in the message that seems like it could be wasting tokens / context window unnecessarily?
|
@pirate good question, but no duplication here — the system prompt is only ever passed once per call. It's never passed as a separate top-level
On the token cost question: every multi-step agent call is a fresh, stateless completion request, so the system prompt has to be resent on every step regardless of whether you use the The reason it's deliberately kept as a |
## Why The `stagehand-server-v3` SEA binary release workflow (`.github/workflows/stagehand-server-v3-release.yml`) only cuts a new tag/binary build when a changeset added since the last `stagehand-server-v3/v*` tag explicitly lists the `@browserbasehq/stagehand-server-v3` package. It doesn't look at `package.json` versions. The last such changeset landed on 2026-06-09 (v3.7.2, #2217). Since then, 39 changesets have merged that touch `packages/core`/`packages/server-v3` — including Gemini 3.5 Flash CUA support (#2273) and an AI SDK warning fix affecting act/extract/observe (#2359) — and none of them included the `stagehand-server-v3` package line. So the release-detect job has returned `release=false` on every push for over a month, even though `updateInternalDependencies: patch` cosmetically bumps server-v3's `package.json`/CHANGELOG on every Version Packages PR, making it look like a release happened when it didn't. This changeset is a one-time catch-up: it doesn't change any code, it just gives the release workflow a qualifying trigger so it cuts a binary build containing everything already merged to `packages/core` since v3.7.2. Related: a user asked about this gap on Discord, referencing #2333 (Gemini 3.5 Flash support request). ## What changed - Added `.changeset/stagehand-server-v3-catchup-release.md` bumping `@browserbasehq/stagehand-server-v3` (patch, 3.7.2 → 3.7.3). ## E2E Test Matrix | Command / flow | Observed output | Confidence / sufficiency | | --- | --- | --- | | Ran the exact front-matter regex from `stagehand-server-v3-release.yml`'s `detect` job against the new changeset file | `Parsed: @browserbasehq/stagehand-server-v3 patch matches target package: true` | Proves this changeset satisfies the workflow's own detection logic and will set `release=true` on merge to main, advancing the tag from `v3.7.2` to `v3.7.3`. | | Verified all 13 historical `stagehand-server-v3` tags against their triggering commit's changeset | 13/13 correlate exactly with a changeset explicitly bumping `@browserbasehq/stagehand-server-v3` | Confirms the detection mechanism is real and consistent — this isn't a guess about how the pipeline works. | | Scanned all 39 changesets added to main since the `v3.7.2` tag commit | 0/39 include a `stagehand-server-v3` line | Confirms the gap is total (not partial) and this PR is the correct/only trigger needed. | Linear: [STG-2587](https://linear.app/browserbase/issue/STG-2587/cut-a-stagehand-server-v3-release-to-catch-up-on-binary-drift-since) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Triggers a catch-up SEA binary release for `@browserbasehq/stagehand-server-v3` by adding a changeset, so all core changes since v3.7.2 ship (including Gemini 3.5 Flash computer-use support). Addresses Linear STG-2587 by closing the drift between version bumps and actual binary releases. <sup>Written for commit efb5f99. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/browserbase/stagehand/pull/2367?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
What & why
#2305 silenced the AI SDK v5 "system message in messages" warning, but only for
agent.execute()'s outer loop (v3AgentHandler.ts).act(),extract(), andobserve()build their system prompt the same way — a{ role: "system", ... }message inside themessagesarray — and route through a separate call path that was never patched:AISdkClient.createChatCompletion()inpackages/core/lib/v3/llm/aisdk.ts(the default client for any"provider/model"string), plus the identical pattern in the public BYOC clientpackages/core/lib/v3/external_clients/aisdk.ts.Since the hybrid/DOM agent's own
act/extract/observetools call these primitives internally on every step, anagent.execute()run that clicks/types repeatedly still spammed the warning even after upgrading past the first fix — a customer reported exactly this.Fix
Add
allowSystemInMessages: trueto the 4 remaininggenerateObject/generateTextcall sites (2 inllm/aisdk.ts, 2 inexternal_clients/aisdk.ts), same pattern as the original fix.E2E Test Matrix
@browserbasehq/stagehand@alpha(already includes #2305's fix),act()→extract()→observe()chain on a real Browserbase sessionAI SDK system-in-messages warnings emitted: YES (4)act()→extract()→observe()chain, same model, real Browserbase sessionAI SDK system-in-messages warnings emitted: NO (0)act()+extract()+observe()+ hybridagent.execute()(multi-step form fill, real Browserbase session)AI SDK system-in-messages warnings emitted: NO (0 total)agent.execute()outer loop, and that the agent's internal act/extract/observe tool calls are also silenced end-to-end.pnpm turbo run build --filter @browserbasehq/stagehandTasks: 3 successful, 3 totaleslint+prettier --checkon changed filesA/B model:
anthropic/claude-haiku-4-5-20251001,experimental: true, hybrid agent mode — same harness shape as #2305's own E2E matrix.Closes STG-2573.
Summary by cubic
Silences the noisy "system message in messages" warning in
act(),extract(), andobserve(), including internal tool calls. SetsallowSystemInMessages: trueat 4generateObject/generateTextsites and adds unit tests for both AISDK clients; closes STG-2573.Written for commit d001bd5. Summary will update on new commits.