Skip to content

fix(chat): keep steering, action and injected messages in the conversation - #4816

Open
ericallam wants to merge 7 commits into
mainfrom
fix/chat-agent-accumulator
Open

fix(chat): keep steering, action and injected messages in the conversation#4816
ericallam wants to merge 7 commits into
mainfrom
fix/chat-agent-accumulator

Conversation

@ericallam

@ericallam ericallam commented Aug 28, 2026

Copy link
Copy Markdown
Member

Summary

Four chat.agent fixes with one shape between them: a message reached the model or the browser without reaching the durable conversation, so the next turn contradicted what the user had just read.

  • Steering messages injected mid-turn never entered the accumulator. They reached the model and the screen, so nothing looked wrong, but uiMessages and newUIMessages never held them and an app persisting at turn complete lost them.
  • History rolled back in onAction was never persisted. The snapshot is written on the turn-complete path, and an action is not a turn — so an undo held while the worker stayed warm, then came back on the next continuation, minutes later and with no error.
  • A StreamTextResult returned from onAction was streamed and then dropped. Worst on regenerate: the user reads a new answer the model has no memory of, and the following turn carries on from the answer that was just replaced.
  • chat.inject() with a role: "system" message broke the turn outright. It went into messages, which ai@7 rejects for every provider (AI_InvalidPromptError from standardizePrompt, before any provider call). The turn ended in the app's error fallback and persisted an assistant message with no parts, so the agent looked like it had stopped answering. These now go to the instructions lane — where the model also treats them as trusted, which is the point of injecting context in the first place.

Fix

Each message is routed to the place it was already meant to reach. The two action fixes share a single snapshot write, so a regenerate that both rolls back history and streams a replacement writes once rather than twice, and that write leaves the resume cursor alone — an action has no turn cursor of its own, and writing an empty one would make the next boot replay from further back.

The first fix closes the durability half of the steering problem. The model-context half is unchanged and still covered by the expected-fail test added in #4795: on the managed path, an injected message still does not appear in the next turn's prompt.

Every fix has a test that fails without it. Each was also run end to end against a deployed agent twice, once with the fix present and once with only that fix reverted, so the tests are known to fail in its absence rather than merely to pass in its presence. A 46-scenario sweep of the surrounding chat surface came back clean.

The seed from payload.headStartMessages had no coverage for agents that do not
register hydrateMessages, and it reads unreachable: it sits inside
if (!hydrateMessages && couldHavePriorState), and couldHavePriorState is false on
a head-start run. It does fire, and this pins that.

Records the shape a persisting app has to handle, which is the part that actually
bites: by onTurnStart the accumulator is already ['user','assistant'], because
the warm route's partial is spliced in before the hook, so the incoming user
message is not the last one.
drainSteeringQueue used the injected uiMessage for span attributes, the
injection-confirmation chunk, the injected-ids set and onInjected — never the
accumulator. So the message reached the model and the browser, appeared in
neither uiMessages nor newUIMessages, and an app persisting from onTurnComplete
never learned it existed. The user steers, the agent obeys, the user reloads, and
their instruction is gone from the transcript and from every later turn's
context.

The asymmetry is the tell: a message that finds no step boundary falls back to
becoming its own turn and is accumulated normally. Only the path that worked lost
data.

Appended at injection time rather than turn end, so the order matches what
happened: after the message that started the turn, before the response that
answers it. Deduplicated by id, since a boundary can drain more than once.

The injection path had no test coverage at all — shouldInject appeared only in
ai.ts — because the harness had no way to deliver a message mid-turn. Adds
harness.sendPendingMessage() for that, which is also what a customer needs to
test steering in their own suite.
The snapshot is written on the turn-complete path, and an action is not a turn —
the block literally ends 'if (!isAction)'. So a chat.history mutation from
onAction lived only in the running worker's memory. Undo worked while that worker
stayed warm, then the next continuation booted from a snapshot still holding the
undone exchange and the messages came back. onAction is exactly where the docs
tell you to call rollbackTo, so this is the documented path silently not
persisting.

Writes the snapshot right after the action's override is applied, awaited for the
same reason as the turn-complete write: the agent may suspend straight after, and
in-flight promises do not reliably survive that.

An action has no turn cursor, so the write reuses the last one rather than
writing undefined — that would drop the resume point and make the next boot
replay from further back to rebuild what it could have read.
…ation

Returning a StreamTextResult from onAction piped it to the browser and stopped
there. The accumulator never saw it, no snapshot recorded it, and actions fire no
onTurnComplete — so the user read a good answer that the model had no memory of,
and the next turn carried on from the answer regenerate had just replaced. The
disagreement between the screen and the conversation was invisible until that
next turn contradicted it.

The action branch now captures what it pipes, using the pipeChatAndCapture that
already existed for exactly this, and appends the message to the accumulator.
Persistence beyond the snapshot is still the app's job, since an action fires no
turn hook — pipeAndCapture hands back the same message for that.

Also folds the snapshot write added for rolled-back history into one helper used
by both action paths, so a regenerate that both rolls back and answers writes
once rather than twice, and the cursor-preservation rule lives in one place.

The two fixes needed each other: with the rollback persisted but the response
dropped, a regenerate left the snapshot empty rather than stale — still wrong,
just differently.
chat.inject with role 'system' put the message into the conversation, which ai@7
rejects for every provider: standardizePrompt throws before any provider is
called. The next turn died with an error chunk reading 'An error occurred.' and
persisted an assistant message with no parts, so from the app's side the agent
had simply stopped answering.

The error message names the fix — use the instructions option — and Instructions
is string | SystemModelMessage | Array<SystemModelMessage>, so an injected system
block has a correct home. It is appended after the base prompt, which keeps the
prompt's position for caching and reads as a later amendment.

This makes the documented examples right rather than rewriting them to a
workaround. It also answers whether trusted mid-conversation context is
supportable: it is, and only this way. A message injected as 'user' is untrusted
by construction, and a well-aligned model says so and re-derives the answer from
tools instead. The docs now state which lane to use for facts and which for
directives.

A new instruction block changes the cached prefix, so the first call carrying it
misses the prompt cache. Only turns that actually injected pay it.
@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 02c2e6b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 27 packages
Name Type
@trigger.dev/sdk Patch
@trigger.dev/python Patch
@internal/dashboard-agent Patch
@trigger.dev/build Patch
@trigger.dev/core Patch
@trigger.dev/react-hooks Patch
@trigger.dev/redis-worker Patch
@trigger.dev/rsc Patch
@trigger.dev/schema-to-json Patch
@trigger.dev/database Patch
@trigger.dev/otlp-importer Patch
@trigger.dev/rbac Patch
@trigger.dev/sso Patch
trigger.dev Patch
@internal/clickhouse Patch
@internal/llm-model-catalog Patch
@internal/metrics-pipeline Patch
@internal/redis Patch
@internal/replication Patch
@internal/run-engine Patch
@internal/run-store Patch
@internal/schedule-engine Patch
@internal/tracing Patch
@internal/webhook-engine Patch
@internal/webhook-sources Patch
@internal/testcontainers Patch
@internal/cache Patch

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

@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The SDK routes system injections into model instructions and keeps other injected messages in the conversation. Steering messages enter accumulated UI history and onTurnComplete.newUIMessages. Action responses and history mutations persist in snapshots while retaining the output cursor. The changes add documentation, patch changesets, harness support, handover coverage, and regression tests.

Merge Risk: 🟡 Moderate · up to 02c2e

This change improves durable chat history, but steering messages may still be omitted from later raw turns, while cancellation or error recovery can lose an action-driven history change or replay already represented output. These bounded continuation and durability risks should be fixed or explicitly accepted before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 8 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary change: preserving steering, action, and injected messages in the conversation.
Description check ✅ Passed The description is detailed and directly explains all four fixes, implementation approach, testing, and regression coverage. It does not include the template's issue-closing line, checklist, or screen…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description is detailed and directly explains all four fixes, implementation approach, testing, and regression coverage. It does not include the template's issue-closing line, checklist, or screenshots section, but the core required information is present.

Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 15 functions across 8 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/chat-agent-accumulator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ericallam ericallam changed the title fix(chat): keep steered, injected and action-produced messages in the conversation fix(chat): keep steering, action and injected messages in the conversation Aug 28, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 6


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 968af3c6-2942-4744-9a5d-0540b1d34454

📥 Commits

Reviewing files that changed from the base of the PR and between 2e24c01 and fd2caf2.

📒 Files selected for processing (14)
  • .changeset/action-stream-into-conversation.md
  • .changeset/inject-instructions-shape.md
  • .changeset/inject-system-to-instructions.md
  • .changeset/persist-action-history-mutations.md
  • .changeset/steering-messages-accumulator.md
  • docs/ai-chat/background-injection.mdx
  • packages/trigger-sdk/src/v3/ai.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (29)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: internal / 🧪 Unit Tests: Internal
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
🧰 Additional context used
📓 Path-based instructions (11)
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` or deprecated `client.defineJob`.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
Add crumbs as you write code — not just when debugging. Mark lines with

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/ai-chat/background-injection.mdx
  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` (deprecated path alias)

📄 CodeRabbit inference engine (packages/trigger-sdk/CLAUDE.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
Use vitest for all tests in the Trigger.dev repository

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
Use function declarations instead of default exports

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format

📄 CodeRabbit inference engine (docs/CLAUDE.md)

Files:

  • docs/ai-chat/background-injection.mdx
Use types over interfaces for TypeScript

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

Files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
  • packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
  • packages/trigger-sdk/src/v3/ai.ts
🧠 Learnings (3)
📚 Learning: 2026-08-16T18:36:58.179Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4537
File: packages/trigger-sdk/test/normalizeKeyString.test.ts:1-2
Timestamp: 2026-08-16T18:36:58.179Z
Learning: For related SDK `chat.agent` tests in the Trigger.dev repository—including chat channels, handover, snapshot, and transport-event coverage—keep new test files under `packages/trigger-sdk/test/` rather than colocating them with the `packages/trigger-sdk/src/v3/` source files.

Applied to files:

  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/action-snapshot-cursor.test.ts
📚 Learning: 2026-06-16T09:19:47.637Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3960
File: apps/webapp/test/prismaInfrastructureErrorCapture.test.ts:0-0
Timestamp: 2026-06-16T09:19:47.637Z
Learning: In this repo’s Vitest setup, `vitest.config.ts` uses `globals: true`, so identifiers like `vi`, `describe`, `it`, and `expect` are available as globals in Vitest test files. During code review, do not flag missing `vi`/`describe`/`it`/`expect` imports as a runtime error or correctness issue when they’re used in `*.test.ts/tsx` or `*.spec.ts/tsx` files. Explicit imports are still preferred for consistency, but they’re not required for runtime behavior.

Applied to files:

  • packages/trigger-sdk/test/chatHandover.test.ts
📚 Learning: 2026-05-19T22:37:47.286Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3671
File: packages/trigger-sdk/test/recovery-boot.test.ts:456-457
Timestamp: 2026-05-19T22:37:47.286Z
Learning: In `packages/trigger-sdk` (Trigger.dev SDK), `logger.warn` (and other SDK logger methods) should route to the Trigger.dev structured logger sink, not to `console.warn`. In SDK tests, `vi.spyOn(console, "warn")` (or similar console spies) should only be used to suppress stray console output; reviewers should not suggest asserting on `console.warn` spies to verify SDK-internal warning/fallback log behavior. Use the SDK’s structured-logger outputs/capture approach instead of console spies.

Applied to files:

  • packages/trigger-sdk/src/v3/ai.ts
🪛 ast-grep (0.45.2)
packages/trigger-sdk/src/v3/ai.ts

[warning] 6675-6682: Avoid logging sensitive data
Context: logger.warn(
"chat.agent: snapshot write outside a turn failed; the change may not survive a continuation",
{
error: error instanceof Error ? error.message : String(error),
sessionId: sessionIdForSnapshot,
reason,
}
)
Note: [CWE-532] Insertion of Sensitive Information into Log File.

(log-sensitive-data-typescript)

🔇 Additional comments (1)
packages/trigger-sdk/test/chatHandover.test.ts (1)

647-647: 🎯 Functional Correctness

No duplicate declaration exists. The file contains one captured declaration at line 647.

Comment thread packages/trigger-sdk/src/v3/ai.ts
Comment thread packages/trigger-sdk/src/v3/ai.ts
Comment thread packages/trigger-sdk/src/v3/ai.ts Outdated
Comment on lines +10019 to +10034
const systemBlocks = messages.filter(
(message): message is SystemModelMessage => message.role === "system"
);
const conversational = messages.filter((message) => message.role !== "system");

if (systemBlocks.length > 0) {
const instructions = locals.get(chatInjectedInstructionsKey) ?? [];
instructions.push(...systemBlocks);
locals.set(chatInjectedInstructionsKey, instructions);
}

if (conversational.length > 0) {
const queue = locals.get(chatBackgroundQueueKey) ?? [];
queue.push(...conversational);
locals.set(chatBackgroundQueueKey, queue);
}

@coderabbitai coderabbitai Bot Aug 28, 2026

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.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep system injections available to direct streamText() callers.

This code removes system messages from chatBackgroundQueueKey. Only toStreamTextOptions() reads chatInjectedInstructionsKey. A supported run() implementation that calls streamText({ model, messages, abortSignal }) directly now receives neither the injected system message nor a system option.

Do not remove the automatic delivery path until the managed agent can supply the instruction lane to every supported run() path. Add a regression test that injects system context without calling chat.toStreamTextOptions().

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This one is real and I am leaving it open rather than resolving it, because the right answer is a judgement call rather than a quick fix.

Confirming the mechanism you describe: before this change, a system block went into chatBackgroundQueueKey, and the turn loop drains that queue into accumulatedMessages independently of toStreamTextOptions() — so a run() calling streamText({ model, messages, abortSignal }) directly did receive the injected content, as a system message inside messages. That is precisely the shape ai@7 rejects, so on ai@7 that path was already broken (AI_InvalidPromptError from standardizePrompt, before any provider call). On ai@6 it worked. After this change that caller gets nothing, so for ai@6 direct callers this trades a working delivery for a silent one.

The reason I am not simply restoring the old path: there is no way to put a system option on a streamText() call the SDK does not build. toStreamTextOptions() is the only seam. The options are (a) document that system-role injection requires spreading chat.toStreamTextOptions(), and warn when instructions are left unconsumed at turn end — now detectable, since the lane drains on apply as of ca7f086; (b) add a system field to the run() arguments so a direct caller can forward it; (c) keep automatic delivery for ai@6 and route to instructions only on ai@7, which needs a runtime version check this package deliberately avoids (import.meta.url is illegal in its CommonJS output, and a bundled task may have no resolvable ai).

My preference is (a) plus (b) as a follow-up, not in this PR — (b) is an API addition and this PR is a set of fixes. Flagging for a maintainer to choose before merge.

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.

@ericallam, thank you for the detailed confirmation.

Your analysis identifies the compatibility boundary correctly. The change fixes the ai@7 invalid prompt path, but ai@6 direct streamText() callers no longer receive injected system context.

I agree that restoring the previous queue behavior is not a safe fix. It would retain the ai@7 failure mode. The proposed follow-up is appropriate:

  1. Document that system-role chat.inject() requires chat.toStreamTextOptions().
  2. Warn when a system instruction remains unconsumed at turn end.
  3. Add a system field to the managed run() payload so direct callers can forward it.

Would you like me to open a GitHub issue for that follow-up?


✏️ Learnings added
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4816
File: packages/trigger-sdk/src/v3/ai.ts:10031-10046
Timestamp: 2026-08-28T14:16:29.734Z
Learning: In `packages/trigger-sdk/src/v3/ai.ts`, system-role `chat.inject()` context is delivered through `chat.toStreamTextOptions()` because only that helper can supply `streamText`'s `system` option. Direct `streamText({ model, messages, abortSignal })` callers received system messages through `messages` on ai6, but that shape fails on ai7 because AI SDK 7 rejects system messages in `messages`. The intended follow-up is to document the `chat.toStreamTextOptions()` requirement, warn for unconsumed injected instructions, and consider a `system` field on managed `chat.agent` `run()` payloads.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4816
File: packages/trigger-sdk/test/inject-system-instructions.test.ts:3-8
Timestamp: 2026-08-28T14:16:14.265Z
Learning: In `packages/trigger-sdk` chat unit tests, `MockLanguageModelV3`, `simulateReadableStream`, and the `mockChatAgent` harness are approved deterministic test utilities. The AGENTS.md “never mock, use testcontainers” guidance applies to integration tests that use real infrastructure such as Redis or PostgreSQL, not fake language models. Existing chat coverage, including `chat.test.ts`, `mockChatAgent.test.ts`, and `chatHandover.test.ts`, uses this same pattern. Real deployed-agent verification can provide separate provider and transport integration evidence.

You are interacting with an AI system.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Half of it is now in, in 02c2e6b: the docs page states that the instructions lane is delivered by chat.toStreamTextOptions(), so a run() that calls streamText({ model, messages, abortSignal }) without spreading it will not receive a role: "system" injection — and that the conversational lane has no such requirement. It also documents that an injection applies to the next inference call only, which is the drain behaviour from the previous commit.

Not in, and deliberately: the unconsumed-instruction warning and the system field on run()'s arguments. The warning is harder than it looks — the canonical pattern injects from onTurnComplete, so instructions pending at the end of a turn are the normal case, and a naive check would fire on the documented usage. Distinguishing that from a genuine loss needs per-turn tracking of whether toStreamTextOptions() was called, which is more machinery than I want to add to the turn loop in a fix PR. The system field is an API addition.

On opening an issue for the follow-up: leaving that to a maintainer rather than deciding it here.

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.

@ericallam, thanks for the clarification.

The documentation makes the new system-instruction contract explicit. It also distinguishes the conversational lane correctly.

I agree that an end-of-turn warning needs per-turn consumption tracking. A naive warning would report the documented onTurnComplete injection flow incorrectly.

The system field remains a separate public API change. I will keep this finding open for maintainer follow-up rather than treat the documentation change as a complete fix.

🐇


🧠 Learnings used
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4816
File: packages/trigger-sdk/src/v3/ai.ts:10031-10046
Timestamp: 2026-08-28T14:16:32.762Z
Learning: In `packages/trigger-sdk/src/v3/ai.ts`, system-role `chat.inject()` context is delivered through `chat.toStreamTextOptions()` because only that helper can supply `streamText`'s `system` option. Direct `streamText({ model, messages, abortSignal })` callers received system messages through `messages` on ai6, but that shape fails on ai7 because AI SDK 7 rejects system messages in `messages`. The intended follow-up is to document the `chat.toStreamTextOptions()` requirement, warn for unconsumed injected instructions, and consider a `system` field on managed `chat.agent` `run()` payloads.

Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4816
File: packages/trigger-sdk/test/inject-system-instructions.test.ts:3-8
Timestamp: 2026-08-28T14:16:14.265Z
Learning: In `packages/trigger-sdk` chat unit tests, `MockLanguageModelV3`, `simulateReadableStream`, and the `mockChatAgent` harness are approved deterministic test utilities. The AGENTS.md “never mock, use testcontainers” guidance applies to integration tests that use real infrastructure such as Redis or PostgreSQL, not fake language models. Existing chat coverage, including `chat.test.ts`, `mockChatAgent.test.ts`, and `chatHandover.test.ts`, uses this same pattern. Real deployed-agent verification can provide separate provider and transport integration evidence.

You are interacting with an AI system.

Comment thread packages/trigger-sdk/src/v3/test/mock-chat-agent.ts
Comment thread packages/trigger-sdk/test/inject-system-instructions.test.ts
… paths

Record only the messages a steering drain actually claimed. The loop used the
offered batch, so a record another consumer took while shouldInject() awaited
was written into the accumulator for a turn it was never part of.

Drain the injected instructions once applied, matching the conversational
lane. Left in place they were re-applied by every later toStreamTextOptions()
call in the run, growing the prompt and changing its cached prefix each turn.

Clean a stopped action's partial response before it is committed, and skip
committing at all once the run is cancelled.
@pkg-pr-new

pkg-pr-new Bot commented Aug 28, 2026

Copy link
Copy Markdown

Open in StackBlitz

@trigger.dev/build

npm i https://pkg.pr.new/@trigger.dev/build@02c2e6b

trigger.dev

npm i https://pkg.pr.new/trigger.dev@02c2e6b

@trigger.dev/core

npm i https://pkg.pr.new/@trigger.dev/core@02c2e6b

@trigger.dev/python

npm i https://pkg.pr.new/@trigger.dev/python@02c2e6b

@trigger.dev/react-hooks

npm i https://pkg.pr.new/@trigger.dev/react-hooks@02c2e6b

@trigger.dev/redis-worker

npm i https://pkg.pr.new/@trigger.dev/redis-worker@02c2e6b

@trigger.dev/rsc

npm i https://pkg.pr.new/@trigger.dev/rsc@02c2e6b

@trigger.dev/schema-to-json

npm i https://pkg.pr.new/@trigger.dev/schema-to-json@02c2e6b

@trigger.dev/sdk

npm i https://pkg.pr.new/@trigger.dev/sdk@02c2e6b

commit: 02c2e6b

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/trigger-sdk/src/v3/ai.ts (2)

4267-4276: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep the model accumulator synchronized with injected steering messages.

Lines 4269-4276 update only chatCurrentUIMessagesKey. They do not update accumulatedMessages or turnNewModelMessages. After this response, the next turn in the same worker uses accumulatedMessages, so it omits steering that influenced the prior response. onTurnComplete.messages also omits that message.

Return the claimed UI and model messages from drainSteeringQueue(). Update both accumulator representations in each caller. Add a regression test that sends a steering message, completes the turn, then verifies the next run() call receives that message.


4267-4276: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add required development crumbs.

Add approved crumb instrumentation for these new behavior paths. If no approved namespace fits, ask before adding one.

  • packages/trigger-sdk/src/v3/ai.ts#L4267-L4276: add crumbs for steering claim and accumulator updates.
  • packages/trigger-sdk/test/inject-system-instructions.test.ts#L179-L234: add crumbs for the one-turn instruction-drain scenario.

As per coding guidelines, “Add crumbs as you write code — not just when debugging.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f069755c-6da5-41bc-8e27-c3b1350588ff

📥 Commits

Reviewing files that changed from the base of the PR and between fd2caf2 and ca7f086.

📒 Files selected for processing (6)
  • packages/trigger-sdk/src/v3/ai.ts
  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/test/chatHandover.test.ts
  • packages/trigger-sdk/test/action-snapshot.test.ts
  • packages/trigger-sdk/test/steering-accumulator.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (43)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Node.js 20.20 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Node.js 24.18 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: internal / 🧪 Unit Tests: Internal
  • GitHub Check: sdk-compat / Node.js 26.4 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Node.js 22.23 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: fk-cascade-guard / fk-cascade-guard
  • GitHub Check: runops-guard / runops-guard
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: code-quality / code-quality
  • GitHub Check: Build and publish previews
🧰 Additional context used
📓 Path-based instructions (10)
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` or deprecated `client.defineJob`.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Add crumbs as you write code — not just when debugging. Mark lines with

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` (deprecated path alias)

📄 CodeRabbit inference engine (packages/trigger-sdk/CLAUDE.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Use vitest for all tests in the Trigger.dev repository

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
Use function declarations instead of default exports

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Use types over interfaces for TypeScript

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

Files:

  • packages/trigger-sdk/test/inject-system-instructions.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
🔇 Additional comments (1)
packages/trigger-sdk/test/inject-system-instructions.test.ts (1)

11-27: Replace mock-based chat coverage with the approved test strategy.

This test uses simulateReadableStream and MockLanguageModelV3. Use the repository Testcontainers-backed fixture instead.

As per coding guidelines, “We use vitest exclusively. Never mock anything - use testcontainers instead.”

Also applies to: 179-188

Source: Coding guidelines

@ericallam
ericallam marked this pull request as ready for review August 28, 2026 14:30
devin-ai-integration[bot]

This comment was marked as resolved.

…finished

pipeChatAndCapture returns a stream failure rather than throwing it, so a
mid-stream failure in a response returned from onAction was committed as a
complete answer, snapshotted, and followed by a normal turn-complete with no
error — the browser saw the stream stop and the next turn built on the
truncated text. The partial is still kept; the failure is now surfaced with it.

Document that the instructions lane is delivered by chat.toStreamTextOptions(),
and that an injection applies to the next inference call only.

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/trigger-sdk/src/v3/ai.ts (2)

4267-4276: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Persist claimed steering messages in raw accumulator paths.

drainSteeringQueue() updates only chatCurrentUIMessagesKey and chatTurnNewUIMessagesKey. ChatMessageAccumulator.prepareStep() and ChatTurn.prepareStep() pass their own queue into this helper, but neither raw accumulator is connected to these locals.

After a successful injection, the current inference sees the steering message. The next raw turn and caller-managed persistence do not see it because uiMessages and modelMessages were not updated.

Return the claimed UI and model messages, or add a callback that updates each raw accumulator after a successful claim. Deduplicate by message ID.


8036-8087: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add crumb markers for this new state transition.

Add // @Crumbs markers, or wrap this action stream capture and error path in a `// `#region` `@crumbs block. This path changes persistence order and error propagation.

As per coding guidelines, “Add crumbs as you write code — not just when debugging.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d29e0d9-c508-4a05-bf16-072254964879

📥 Commits

Reviewing files that changed from the base of the PR and between ca7f086 and 02c2e6b.

📒 Files selected for processing (3)
  • docs/ai-chat/background-injection.mdx
  • packages/trigger-sdk/src/v3/ai.ts
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (42)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
  • GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
  • GitHub Check: sdk-compat / Node.js 24.18 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
  • GitHub Check: sdk-compat / Node.js 26.4 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: sdk-compat / Node.js 20.20 (warp-ubuntu-latest-x64-4x)
  • GitHub Check: internal / 🧪 Unit Tests: Internal
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: fk-cascade-guard / fk-cascade-guard
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: runops-guard / runops-guard
  • GitHub Check: typecheck / typecheck
  • GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
  • GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
  • GitHub Check: code-quality / code-quality
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Build and publish previews
🧰 Additional context used
📓 Path-based instructions (11)
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` or deprecated `client.defineJob`.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Add crumbs as you write code — not just when debugging. Mark lines with

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • docs/ai-chat/background-injection.mdx
  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` (deprecated path alias)

📄 CodeRabbit inference engine (packages/trigger-sdk/CLAUDE.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
Use vitest for all tests in the Trigger.dev repository

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
Use function declarations instead of default exports

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format

📄 CodeRabbit inference engine (docs/CLAUDE.md)

Files:

  • docs/ai-chat/background-injection.mdx
Use types over interfaces for TypeScript

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs

📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)

Files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
🧠 Learnings (2)
📚 Learning: 2026-06-16T09:19:47.637Z
Learnt from: d-cs
Repo: triggerdotdev/trigger.dev PR: 3960
File: apps/webapp/test/prismaInfrastructureErrorCapture.test.ts:0-0
Timestamp: 2026-06-16T09:19:47.637Z
Learning: In this repo’s Vitest setup, `vitest.config.ts` uses `globals: true`, so identifiers like `vi`, `describe`, `it`, and `expect` are available as globals in Vitest test files. During code review, do not flag missing `vi`/`describe`/`it`/`expect` imports as a runtime error or correctness issue when they’re used in `*.test.ts/tsx` or `*.spec.ts/tsx` files. Explicit imports are still preferred for consistency, but they’re not required for runtime behavior.

Applied to files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
📚 Learning: 2026-08-16T18:36:58.179Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 4537
File: packages/trigger-sdk/test/normalizeKeyString.test.ts:1-2
Timestamp: 2026-08-16T18:36:58.179Z
Learning: For related SDK `chat.agent` tests in the Trigger.dev repository—including chat channels, handover, snapshot, and transport-event coverage—keep new test files under `packages/trigger-sdk/test/` rather than colocating them with the `packages/trigger-sdk/src/v3/` source files.

Applied to files:

  • packages/trigger-sdk/test/action-stream-accumulator.test.ts
🔇 Additional comments (2)
docs/ai-chat/background-injection.mdx (1)

211-233: LGTM!

packages/trigger-sdk/test/action-stream-accumulator.test.ts (1)

99-171: LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant