Skip to content

feat(v3): allow triggerOptions in ai.tool and fix shadowing bug - #4465

Closed
deepshekhardas wants to merge 7 commits into
triggerdotdev:mainfrom
deepshekhardas:fix/1510-dots-escaping
Closed

feat(v3): allow triggerOptions in ai.tool and fix shadowing bug#4465
deepshekhardas wants to merge 7 commits into
triggerdotdev:mainfrom
deepshekhardas:fix/1510-dots-escaping

Conversation

@deepshekhardas

Copy link
Copy Markdown

Fixes #1510 - dots escaping. See original PR.

@changeset-bot

changeset-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d397a4b

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

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

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Hi @deepshekhardas, thanks for your interest in contributing!

This project requires that pull request authors are vouched, and you are not in the list of vouched users.

This PR will be closed automatically. See https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md for more details.

@github-actions github-actions Bot closed this Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 142fbed1-d8f4-4ca2-9535-9ea7f3a0208d

📥 Commits

Reviewing files that changed from the base of the PR and between 14824b0 and d397a4b.

📒 Files selected for processing (19)
  • .changeset/fix-cli-ui-bugs.md
  • apps/webapp/app/components/RuntimeIcon.tsx
  • apps/webapp/app/routes/admin.api.v1.runs-replication.create.ts
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx
  • apps/webapp/app/services/clickhouseInstance.server.ts
  • apps/webapp/app/services/emailAuth.server.tsx
  • apps/webapp/app/services/runsReplicationInstance.server.ts
  • internal-packages/clickhouse/Dockerfile
  • packages/build/src/extensions/playwright.ts
  • packages/cli-v3/src/deploy/buildImage.ts
  • packages/core/src/v3/apiClient/index.ts
  • packages/core/src/v3/apiClient/runStream.ts
  • packages/core/src/v3/build/runtime.ts
  • packages/core/src/v3/schemas/api.ts
  • packages/core/src/v3/utils/flattenAttributes.ts
  • packages/trigger-sdk/src/v3/ai.test.ts
  • packages/trigger-sdk/src/v3/ai.ts
  • repro_1510.ts
  • test-flatten.ts

Walkthrough

The changes fix several CLI and webapp issues. They update runtime fallback rendering, GitHub and email authentication behavior, and ClickHouse URL handling. They revise ClickHouse container startup, Playwright metadata parsing, and ECR authentication. Core packages improve stream payload handling, Bun path resolution, nullable deployment fields, and escaped attribute paths. The trigger SDK adds AI tool trigger options and related tests.

✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/1510-dots-escaping
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Trivy (0.72.0)

Trivy execution failed: 2026-08-02T03:24:23Z FATAL Fatal error run error: fs scan error: scan error: scan failed: failed analysis: post analysis error: post analysis error: ansible scan error: fs filter error: fs filter error: walk error range error: stat packages/build/doctor.config.json: no such file or directory: range error: stat packages/build/doctor.config.json: no such file or directory


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.

Warning

⚠️ This pull request shows signs of AI-generated slop (redundant_comments, description_diff_mismatch). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 10 potential issues.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +5 to +17
WORKDIR /app
COPY ./schema ./schema
COPY ./cmd ./cmd
COPY ./migrate.sh ./migrate.sh

RUN go build -o /usr/local/bin/transform ./cmd/transform/main.go
RUN chmod +x ./migrate.sh

ENV GOOSE_DRIVER=clickhouse
ENV GOOSE_DBSTRING="tcp://default:password@clickhouse:9000"
ENV GOOSE_MIGRATION_DIR=./schema
CMD ["goose", "up"]

ENTRYPOINT ["./migrate.sh"]

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.

🔴 ClickHouse migration container fails to build because it copies files that don't exist

The migration image build copies a helper script and a source folder (COPY ./cmd ./cmd / COPY ./migrate.sh ./migrate.sh at internal-packages/clickhouse/Dockerfile:7-8) that are not present in the package, so the image can never be built.
Impact: Anyone starting local services or CI that builds the ClickHouse migration image gets a hard build failure and no migrations run.

Missing files referenced by the Dockerfile

A directory listing of internal-packages/clickhouse/ shows only Dockerfile, README.md, package.json, schema/, src/, tsconfigs and vitest.config.ts. There is no cmd/transform/main.go and no migrate.sh. Therefore:

  • COPY ./cmd ./cmd fails immediately ("/cmd": not found).
  • Even if the COPY were skipped, RUN go build -o /usr/local/bin/transform ./cmd/transform/main.go and ENTRYPOINT ["./migrate.sh"] (internal-packages/clickhouse/Dockerfile:10,17) would fail.

The previous CMD ["goose", "up"] worked with just the schema directory.

Prompt for agents
The Dockerfile in internal-packages/clickhouse now depends on a `cmd/transform/main.go` Go program and a `migrate.sh` entrypoint script, but neither file exists in the package. The image build will fail at the COPY steps. Either add these files to the package (the transform program and the migrate.sh wrapper that runs goose with up/down arguments), or revert the Dockerfile to the previous `CMD ["goose", "up"]` form that only requires the schema directory.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +489 to 503
let credentials;
if (cloudRegistryHost.endsWith("amazonaws.com")) {
const [credentialsError, result] = await tryCatch(
getDockerUsernameAndPassword(apiClient, deploymentId)
);

if (credentialsError) {
return {
ok: false as const,
error: `Failed to get docker credentials: ${credentialsError.message}`,
logs: "",
};
if (credentialsError) {
return {
ok: false as const,
error: `Failed to get docker credentials: ${credentialsError.message}`,
logs: "",
};
}
credentials = result;
}

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.

🔴 Docker pushes to non-AWS registries no longer log in, causing deploy failures

Registry credentials are now fetched only when the target registry address ends in amazonaws.com (cloudRegistryHost.endsWith("amazonaws.com") at packages/cli-v3/src/deploy/buildImage.ts:490), so pushes to any other registry silently skip authentication.
Impact: Self-hosted users doing local builds against a non-AWS registry now get "unauthorized" push failures during deploy instead of being logged in automatically.

How the credential path is reached

authenticateToRegistry is set from options.localBuild in packages/cli-v3/src/commands/deploy.ts:441,574, so for every local build that pushes, the CLI previously called getDockerUsernameAndPassword (packages/cli-v3/src/deploy/buildImage.ts:1004-1021), which returns either TRIGGER_DOCKER_USERNAME/TRIGGER_DOCKER_PASSWORD env credentials or credentials generated by the platform via apiClient.generateRegistryCredentials(deploymentId). Those credentials are not AWS-specific — self-hosted installs point TRIGGER_DOCKER_REGISTRY at their own registry.

With the new guard, credentials stays undefined for those hosts and the code takes the else branch that only logs a debug message, so docker buildx build --push runs unauthenticated.

Prompt for agents
In packages/cli-v3/src/deploy/buildImage.ts the registry login is now gated on the registry host ending with `amazonaws.com`. This breaks self-hosted deployments where the platform (or TRIGGER_DOCKER_USERNAME/TRIGGER_DOCKER_PASSWORD) supplies credentials for a non-AWS registry. Consider always attempting to obtain credentials when `authenticateToRegistry` is set and only skipping login when credentials cannot be obtained (e.g. treat a credential fetch failure as 'assume the user is already logged in') rather than keying off the registry hostname.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +300 to +303
chunk:
typeof parsedBody.data === "string"
? safeParseJSON(parsedBody.data)
: parsedBody.data,

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.

🔴 Realtime stream text chunks get silently converted into numbers, booleans or objects

Every string value received on a realtime stream is now run through a JSON parse (safeParseJSON(parsedBody.data) at packages/core/src/v3/apiClient/runStream.ts:300-303), so a chunk whose text happens to look like JSON is handed to the subscriber as a different type.
Impact: Applications streaming text (for example LLM tokens) receive numbers, booleans or objects instead of the strings that were sent, breaking string concatenation and comparisons.

Why the double parse is wrong for the writer path

For v2 streams written by the SDK, packages/core/src/v3/realtimeStreams/streamsWriterV2.ts:144 sends JSON.stringify({ data: chunk, id }), so parsedBody.data already carries the original, correctly typed chunk. If the chunk was the string "5", "true" or '{"a":1}', the extra safeParseJSON turns it into 5, true, or an object.

The raw-string case this change targets comes from the append endpoint, where apps/webapp/app/services/realtime/s2realtimeStreams.server.ts:108-115 stores the request body text verbatim as data. Fixing that by parsing all strings on the client also corrupts the writer path; the two producers should be made consistent instead (e.g. have the append endpoint/appendToStream always store a JSON-encoded value).

Prompt for agents
packages/core/src/v3/apiClient/runStream.ts now JSON-parses any string `parsedBody.data`. This corrupts legitimate string chunks produced by streamsWriterV2 (which already stores the typed chunk inside `{data, id}`) whenever the string is itself valid JSON ("5", "true", "[1]"). The real inconsistency is the append path: apps/webapp/app/services/realtime/s2realtimeStreams.server.ts stores the raw request text as `data`, while the writer stores a decoded value. Make the producers consistent (e.g. JSON-encode the part in ApiClient.appendToStream / on the server before storing) instead of guessing on the consumer side.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

target="_blank"
rel="noreferrer noopener"
to={`https://github.com/settings/installations/${selectedInstallation?.appInstallationId}`}
to={`https://github.com/apps/trigger-dev-app/installations/${selectedInstallation?.appInstallationId}`}

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.

🟡 Repository access link points at trigger.dev's hosted GitHub app for every installation

The "configure repository access" link is now built with a hard-coded application name (https://github.com/apps/trigger-dev-app/installations/... at apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx:517) instead of the app that is actually configured, so self-hosted installs send users to the wrong page.
Impact: Self-hosted users clicking the link land on an unrelated GitHub app page and cannot manage their repository access.

Configured app slug is available

The GitHub app slug is configurable via GITHUB_APP_SLUG (apps/webapp/app/env.server.ts:20) and is used elsewhere, e.g. apps/webapp/app/services/gitHubSession.server.ts:37. The component should receive the configured slug (via the resource loader) rather than embedding trigger-dev-app.

Prompt for agents
The hint link hardcodes the GitHub app slug `trigger-dev-app`. The slug is configurable through GITHUB_APP_SLUG (apps/webapp/app/env.server.ts) and should be surfaced from the github resource route loader and used to build the installation URL, so self-hosted deployments with their own GitHub App get a working link.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +2 to +6
"@trigger.dev/build": patch
"trigger.dev": patch
"@trigger.dev/core": patch
"@internal/clickhouse": patch
"webapp": patch

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.

🟡 Release tooling will error because the change note lists a package that is excluded from versioning

The new release note lists webapp (.changeset/fix-cli-ui-bugs.md:6) even though that package is explicitly excluded from versioning, so the versioning step fails when it sees excluded and non-excluded packages in the same note.
Impact: The release/versioning job errors out until the note is corrected.

Changesets ignore list

.changeset/config.json contains "ignore": ["webapp", "coordinator", "docker-provider", "kubernetes-provider", "supervisor"]. Changesets fails with "The following changesets contain both ignored and not ignored packages" in that situation. Per CONTRIBUTING.md/CLAUDE.md, server-only changes belong in a .server-changes/ file, not in a changeset. @internal/clickhouse is also a private, unpublished package.

Suggested change
"@trigger.dev/build": patch
"trigger.dev": patch
"@trigger.dev/core": patch
"@internal/clickhouse": patch
"webapp": patch
---
"@trigger.dev/build": patch
"trigger.dev": patch
"@trigger.dev/core": patch
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +66 to +79
execute: async (input, toolCallMetadata) => {
const serializedOptions = toolCallMetadata ? JSON.parse(JSON.stringify(toolCallMetadata)) : undefined;

return await task
.triggerAndWait(input as inferSchemaIn<TTaskSchema>, {
...options?.triggerOptions,
metadata: {
[METADATA_KEY]: serializedOptions,
...options?.triggerOptions?.metadata,
},
})
.unwrap();
},
...options,

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.

🟡 Several edited files are committed with formatting that the repository's formatter rejects

Multiple touched files were re-indented by hand instead of with the project's formatter (see the misaligned block at packages/trigger-sdk/src/v3/ai.ts:66-79), which the repository requires before committing.
Impact: The format check fails and unrelated lines show up as noise in the diff.

Affected locations

AGENTS.md states: "Formatting is enforced using Prettier. Run pnpm run format before committing."

Badly formatted / gratuitously reformatted hunks:

  • packages/trigger-sdk/src/v3/ai.ts:66-79 (stray leading spaces, trailing whitespace, mis-indented ...options).
  • packages/core/src/v3/apiClient/runStream.ts:22-52, :104-107, :396 () { }), :755-759 — type literals and boolean chains de-indented.
  • packages/core/src/v3/utils/flattenAttributes.ts:57 () { }).
  • packages/cli-v3/src/deploy/buildImage.ts:563-568 (cache args array de-indented, template literal broken across lines).
  • packages/core/src/v3/apiClient/index.ts:1377-1382.
Suggested change
execute: async (input, toolCallMetadata) => {
const serializedOptions = toolCallMetadata ? JSON.parse(JSON.stringify(toolCallMetadata)) : undefined;
return await task
.triggerAndWait(input as inferSchemaIn<TTaskSchema>, {
...options?.triggerOptions,
metadata: {
[METADATA_KEY]: serializedOptions,
...options?.triggerOptions?.metadata,
},
})
.unwrap();
},
...options,
execute: async (input, toolCallMetadata) => {
const serializedOptions = toolCallMetadata
? JSON.parse(JSON.stringify(toolCallMetadata))
: undefined;
return await task
.triggerAndWait(input as inferSchemaIn<TTaskSchema>, {
...options?.triggerOptions,
metadata: {
[METADATA_KEY]: serializedOptions,
...options?.triggerOptions?.metadata,
},
})
.unwrap();
},
...options,
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +16 to +19
const triggerAndWaitSpy = vi.spyOn(myTask, "triggerAndWait");
triggerAndWaitSpy.mockReturnValue({
unwrap: () => Promise.resolve("Hello world"),
} as any);

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.

🟡 New test relies on stubbing internals, which the repository forbids

The added test replaces the task's trigger behaviour with a stub (vi.spyOn(myTask, "triggerAndWait") at packages/trigger-sdk/src/v3/ai.test.ts:16-19), which the project's testing guidelines explicitly disallow.
Impact: The test violates the repo's testing policy and only asserts against a fake, so it can pass while the real path is broken.

Rule reference

AGENTS.md: "Tests should avoid mocks or stubs and use the helpers from @internal/testcontainers when Redis or Postgres are needed." CLAUDE.md: "We use vitest exclusively. Never mock anything - use testcontainers instead."

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

})
.unwrap();
},
...options,

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.

🟡 Internal trigger settings leak into the AI tool definition

The whole options object, including the new trigger settings, is copied onto the tool definition (...options at packages/trigger-sdk/src/v3/ai.ts:79), so triggerOptions ends up as an extra property of the tool handed to the AI SDK.
Impact: The AI SDK receives an unexpected field on the tool object, which may be forwarded or serialised in unintended ways.

Mechanism

ToolOptions previously contained only experimental_toToolResultContent, so ...options was safe to spread into dynamicTool(...). Now it also carries triggerOptions (packages/trigger-sdk/src/v3/ai.ts:31), which is purely an SDK-side concern. triggerOptions should be destructured out before spreading the remaining tool options.

Prompt for agents
In packages/trigger-sdk/src/v3/ai.ts, `...options` is spread into `dynamicTool(...)` and now includes the newly added `triggerOptions`, which is not a valid tool property. Destructure `triggerOptions` out of `options` (e.g. `const { triggerOptions, ...toolOptions } = options ?? {}`) and only spread the remaining tool-level options into `dynamicTool`.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +8 to +36
function escapeKey(key: string): string {
return key.replace(/\\/g, "\\\\").replace(/\./g, "\\.");
}

function unescapeKey(key: string): string {
return key.replace(/\\\./g, ".").replace(/\\\\/g, "\\");
}

function splitKey(key: string): string[] {
const parts: string[] = [];
let currentPart = "";
for (let i = 0; i < key.length; i++) {
if (key[i] === "\\" && i + 1 < key.length) {
if (key[i + 1] === "." || key[i + 1] === "\\") {
currentPart += key[i + 1];
i++;
} else {
currentPart += key[i];
}
} else if (key[i] === ".") {
parts.push(currentPart);
currentPart = "";
} else {
currentPart += key[i];
}
}
parts.push(currentPart);
return parts;
}

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.

🔍 Key escaping changes stored attribute names and is only backwards compatible for keys without backslashes

escapeKey now rewrites object/Map keys containing . or \ before they become OTEL attribute names, and unflattenAttributes uses splitKey to reverse it. Round-tripping is correct for new data, but note two consequences: (1) stored spans/events written by older SDK versions whose keys contain literal backslashes will now be un-escaped differently on read (\\ collapses to \); (2) attribute names visible in traces/ClickHouse for keys with dots change shape ("a.b" -> a\.b), so any dashboard/query that matches attribute keys literally will see new names. Worth confirming with existing packages/core/test/flattenAttributes.test.ts expectations, which could not be executed here because workspace deps are not installed.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

secret,
callbackURL: "/magic",
sessionMagicLinkKey: "triggerdotdev:magiclink",
validateSession: false,

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.

🔍 Magic-link option name may not match the strategy's API

remix-auth-email-link@2.0.2 exposes the option validateSessionMagicLink (default false); there is no validateSession option. If the intent was to relax the same-browser check, the option name is wrong and the line is a no-op; if the strategy version does accept it, disabling session validation weakens the magic-link flow. Worth confirming against the installed package's typings.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

[TRI-4123] Logging objects with keys with periods in doesn't render properly in the UI

1 participant