Skip to content

Conversation

@mschead
Copy link
Contributor

@mschead mschead commented Feb 6, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Agent-mode streaming now powered by Trigger.dev for improved reliability on long-running conversations.
    • Real-time streaming updates for AI responses and metadata events.
    • Enhanced monitoring of agent run status and progress.
  • Chores

    • Added Trigger.dev SDK integration and configuration.
    • Updated logging infrastructure.
    • Expanded test mocking for streaming components.

@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hackerai Error Error Feb 6, 2026 10:04pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Introduces Trigger.dev-based streaming for agent-mode conversations, replacing single-invocation serverless execution with a managed task. New task (agentStreamTask) orchestrates agent sessions with real-time streams (aiStream, metadataStream). The /api/agent route now triggers this task and returns a runId and access token. Frontend integrates Trigger.dev React hooks for stream consumption and progressive response rendering. Ask-mode chat remains unchanged.

Changes

Cohort / File(s) Summary
Trigger.dev Infrastructure
trigger.config.ts, src/trigger/agent-task.ts, src/trigger/streams.ts
New Trigger.dev configuration, agent streaming task orchestration with rate limiting, sandbox management, summarization, and persistence logic; typed stream definitions for AI output and metadata events.
API Routes & Handlers
app/api/agent/route.ts, app/api/agent-stream/route.ts, lib/api/prepare-agent-payload.ts
Agent route refactored to trigger Trigger.dev task and return runId/token; new agent-stream route for legacy streaming; shared payload preparation helper extracting pre-stream logic.
Frontend Agent Streaming
app/components/chat.tsx
Integrated Trigger.dev real-time stream hooks (useRealtimeStream, useRealtimeRun) for agent-mode conversations; added streaming state management, progressive message rendering, and synchronization guards with Convex; routes agent messages through new task-triggering flow.
Axiom Logging Refactor
lib/axiom/server.ts, lib/axiom/worker.ts, lib/api/chat-handler.ts, app/api/chat/[id]/stream/route.ts, instrumentation.ts, lib/utils/stream-cancellation.ts
Renamed server logger to nextJsAxiomLogger; introduced worker-scoped retry logger with lazy initialization and console fallback; updated all logging calls across API handlers and utilities.
Tool Utilities & Helpers
lib/ai/tools/utils/retry-with-backoff.ts, lib/ai/tools/utils/sandbox-health.ts
Introduced createRetryLogger for retry-with-backoff logic; integrated retry logger into sandbox health monitoring with conditional logging on final failures.
Testing & Configuration
__mocks__/trigger-dev-react-hooks.ts, __mocks__/trigger-dev-sdk.ts, jest.config.js, e2e/helpers/mock-handlers.ts
Added mocks for Trigger.dev hooks and SDK; extended Jest module mapping and transform patterns; updated e2e mock agent response to return runId/token instead of result/success.
Project Configuration & Dependencies
package.json, .env.local.example, tsconfig.json, .gitignore
Added Trigger.dev dependencies and dev script; added Trigger.dev environment variables; extended TypeScript include paths; added .trigger to gitignore.
Documentation
.cursor/plans/trigger.dev_agent_streaming_059bfd0f.plan.md, .cursor/rules/trigger.basic.mdc, .cursor/rules/convex_rules.mdc
Added AI plan for streaming workflow; introduced Trigger.dev Basic Tasks documentation; minor formatting fixes to Convex rules.

Sequence Diagram

sequenceDiagram
    actor Client
    participant AgentRoute as /api/agent<br/>(POST)
    participant PreparePayload as prepareAgentPayload
    participant TriggerSDK as Trigger.dev SDK
    participant AgentTask as agentStreamTask
    participant AIStream as aiStream
    participant MetadataStream as metadataStream
    participant Convex as Convex DB
    participant Frontend as Frontend/<br/>useRealtimeStream

    Client->>AgentRoute: POST /api/agent<br/>(chat, messages)
    AgentRoute->>PreparePayload: Prepare task payload<br/>(validate mode, fetch user,<br/>rate-limit, chat state)
    PreparePayload->>Convex: Fetch user, chat, messages
    PreparePayload->>AgentRoute: Return AgentTaskPayload
    AgentRoute->>TriggerSDK: Trigger agentStreamTask<br/>with payload
    TriggerSDK->>AgentTask: Execute task
    AgentRoute-->>Client: Return {runId,<br/>publicAccessToken}
    
    par Agent Streaming
        AgentTask->>AgentTask: Setup tools, sandbox,<br/>system prompt
        AgentTask->>MetadataStream: Emit data-title,<br/>data-upload-status, etc.
        AgentTask->>AIStream: Stream AI response<br/>chunks via aiStream
        AgentTask->>AgentTask: Handle summarization,<br/>memory updates, retries
        AgentTask->>Convex: Persist chat state,<br/>messages, usage
    end
    
    Frontend->>TriggerSDK: useRealtimeStream(runId,<br/>aiStream)
    TriggerSDK->>Frontend: Stream UIMessageChunk<br/>parts progressively
    Frontend->>Frontend: Render progressive<br/>assistant message
    
    Frontend->>TriggerSDK: useRealtimeRun(runId)
    TriggerSDK->>Frontend: Monitor run status
    Frontend->>Frontend: Update display status<br/>on completion
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Daily branch 2025 10 09 #52 — Directly modifies app/api/agent/route.ts to change the exported POST handler and maxDuration from the same route being refactored in this PR.
  • Feat/token bucket ratelimit #155 — Introduces the checkAgentRateLimit and deductAgentUsage rate-limiting functions that the new agentStreamTask and prepareAgentPayload depend on.
  • Daily branch 2025 09 19 #34 — Modifies streaming and chat infrastructure in app/components/chat.tsx and route handling to support resumable real-time streaming patterns similar to the Trigger.dev integration added here.

Poem

🐰 A task awaits in clouds above,
Streams of data, flows of love,
Agent whispers, chunk by chunk,
Real-time magic, no serverless funk!
Trigger hops through time and space,
UI renders at racing pace. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'WIP - feat: long agents' is vague and does not clearly convey the main change. It uses 'WIP' prefix and generic term 'long agents' without specifying that the PR introduces Trigger.dev-based streaming for agent-mode conversations. Consider revising the title to be more specific and descriptive of the core change, such as 'feat: integrate Trigger.dev streaming for agent-mode conversations' or similar, removing the WIP prefix if the PR is ready for review.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch agent-timeout

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

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

⚠️ Outside diff range comments (1)
app/components/chat.tsx (1)

738-769: ⚠️ Potential issue | 🟠 Major

Potential permanent sync block if the assistant message never appears in Convex.

After the 3-second skipPaginatedSyncUntilRef window expires, the lastTriggerAssistantIdRef check (Lines 754-769) still gates Convex sync on finding the trigger assistant message. If the backend save fails or is delayed beyond expectations, this ref is never cleared and Convex sync is blocked indefinitely for this chat session.

Consider adding a timeout-based fallback that clears lastTriggerAssistantIdRef after a reasonable period (e.g., 10–15 seconds), allowing normal sync to resume.

Sketch of a fix

Track the timestamp when lastTriggerAssistantIdRef was set, and clear it if more than N seconds have elapsed:

+  const lastTriggerAssistantSetAtRef = useRef<number>(0);
+
   // When trigger run completes, sync messages and clear trigger state
   useEffect(() => {
     if (!triggerRun || triggerStatus !== "ready") return;
     ...
     if (assistantMsg) {
       lastTriggerAssistantIdRef.current = assistantMsg.id;
+      lastTriggerAssistantSetAtRef.current = Date.now();
     }
     ...
   }, [...]);

Then in the sync effect:

       if (lastTriggerId) {
+        // Safety valve: clear ref if Convex hasn't caught up within 15s
+        if (Date.now() - lastTriggerAssistantSetAtRef.current > 15_000) {
+          lastTriggerAssistantIdRef.current = null;
+          setMessages(uiMessages);
+          return;
+        }
         const hasAssistantMessage = uiMessages.some(
🤖 Fix all issues with AI agents
In @.cursor/rules/convex_rules.mdc:
- Line 172: Update the grammatical error in the documentation string "page
(contains an array of documents that you fetches)" by replacing "that you
fetches" with a correct form such as "that you fetch" or "that are fetched" so
the line reads e.g. "page (contains an array of documents that you fetch)";
locate the exact text in the .cursor/rules/convex_rules.mdc file and apply the
change.

In @.cursor/rules/trigger.basic.mdc:
- Around line 168-170: The example uses a hardcoded past date in the wait.until
call (await wait.until({ date: new Date("2024-12-25") })), which can mislead
readers; update the example to use a future/relative date instead—either replace
the literal with a clearly-future ISO date string (e.g., next year) or compute a
relative date dynamically (e.g., new Date(Date.now() + 7*24*60*60*1000)) so the
wait.until example always demonstrates waiting for a future date; keep the
reference to the wait.until call and the new Date constructor to locate the
change.

In `@app/api/agent-stream/route.ts`:
- Line 5: The POST route in route.ts calls
createChatHandler("/api/agent-stream") but createChatHandler only accepts the
union "/api/chat" | "/api/agent", causing TS2345; fix by either changing the
call in route.ts (the POST export) to use the existing endpoint string
"/api/agent" if the stream should mirror that handler, or widen the accepted
union in createChatHandler (function createChatHandler) to include
"/api/agent-stream" so the new route compiles—update the type definition in
lib/api/chat-handler.ts accordingly.

In `@app/api/agent/route.ts`:
- Around line 9-34: The POST handler currently deducts usage via
prepareAgentPayload (which calls checkRateLimit) but does not refund if
tasks.trigger("agent-stream", payload) or auth.createPublicToken fails; update
POST to instantiate and use the same UsageRefundTracker pattern (as used in
chat-handler.ts) around the call to tasks.trigger and auth.createPublicToken so
that on any non-ChatSDKError exception you call the tracker to refund the
deducted credits before rethrowing, and ensure ChatSDKError paths still use
error.toResponse() without double-refund; reference prepareAgentPayload,
tasks.trigger, auth.createPublicToken, UsageRefundTracker, agentStreamTask, and
the POST function while making this change.
- Around line 18-23: Update the public access token creation to use a longer
expiration to provide a safety buffer: change the expirationTime passed to
auth.createPublicToken when creating publicAccessToken from "1hr" to "2hr",
ensuring the token outlives tasks that approach the Trigger.dev maxDuration;
locate the call to auth.createPublicToken that constructs publicAccessToken and
modify its expirationTime string accordingly.

In `@app/components/chat.tsx`:
- Around line 517-579: The effect currently rebuilds a ReadableStream from the
entire aiParts array on every aiParts update, causing O(n) re-processing; modify
the logic to only append new chunks by tracking the last-processed index (e.g.,
add a ref like lastProcessedIndexRef) and when the effect runs (in the block
that creates the ReadableStream and calls readUIMessageStream) only enqueue
aiParts.slice(lastProcessedIndexRef.current) (or otherwise feed only new
elements) and update lastProcessedIndexRef.current after processing; keep
existing throttling/flush behavior and continue to use aiPartsGenerationRef,
triggerRun, setTriggerAssistantMessage, and readUIMessageStream as-is so
behavior is unchanged except for processing only new chunks.
- Around line 451-462: The trigger metadata branch handling
data-rate-limit-warning must match the normalization, dedup, and localStorage
behavior used in the useChat onData path: instead of blindly spreading rawData
and casting to RateLimitWarningData, call the same shared handler (or inline the
same logic) that normalizes warning types ("sliding-window",
"extra-usage-active", "token-bucket"), converts resetTime strings to Date,
enforces the hasUserDismissedWarningRef current check, and applies the
extra-usage-active localStorage dedup/flag logic before calling
setRateLimitWarning; update the code around hasUserDismissedWarningRef,
setRateLimitWarning, and RateLimitWarningData to use that shared normalization
so both paths behave identically.
- Around line 965-974: The wrappedRegenerate function can start
triggerRegenerate even when a trigger run is already active; mirror the guard
used in wrappedSendMessage by checking the triggerRun state (e.g., ensure
triggerRun === null) before calling triggerRegenerate when chatMode === "agent"
&& !temporaryChatsEnabled, and return early if a run is in progress so you don't
start concurrent trigger runs that could write to the same streams.
- Around line 529-531: The forEach callback on (aiParts as Chunk[]).forEach
currently uses a concise arrow that implicitly returns the value from
controller.enqueue, triggering a lint warning; change the callback to a
block-bodied arrow that explicitly calls controller.enqueue without returning
(e.g., (chunk: Chunk) => { controller.enqueue(chunk); }) so the intent is clear
and the lint warning is silenced—update the call site where aiParts, Chunk, and
controller.enqueue are used accordingly.
- Line 8: The client component imports the server-only API (import { runs } from
"@trigger.dev/sdk/v3") and calls runs.cancel(triggerRun.runId); move that cancel
logic into a server action or API route that imports runs and calls
runs.cancel(runId), then remove the server SDK import from the client and invoke
the server action from the component (e.g., fetch the API or call the server
action) passing triggerRun.runId; ensure the new server function validates the
runId and returns success/error so the client can update UI.

In `@lib/api/prepare-agent-payload.ts`:
- Around line 108-256: prepareAgentPayload currently calls checkRateLimit (which
deducts credits) and then proceeds, but there is no UsageRefundTracker so
credits cannot be refunded on later failure; modify prepareAgentPayload to
surface the rate limit data so the caller can construct a UsageRefundTracker:
return the raw rateLimitInfo (or an object containing it) alongside the existing
payload (i.e., add a rateLimitInfoRaw or rateLimitForRefund field to the
returned object) and keep serializeRateLimitInfo(rateLimitInfo) for the response
field; this ensures callers (like app/api/agent/route.ts) can create a
UsageRefundTracker with the exact rateLimitInfo before any further actions that
might fail.
- Around line 191-208: Remove the redundant subscription check around
extraUsageConfig: because earlier code already throws for subscription ===
"free", delete the outer if (subscription !== "free") block and unnest the inner
logic so that extraUsageEnabled is read and, if true,
getExtraUsageBalance(userId) is awaited and used to populate extraUsageConfig;
update references to userCustomization?.extra_usage_enabled,
getExtraUsageBalance, and the extraUsageConfig object creation (keep same fields
enabled, hasBalance, balanceDollars, autoReloadEnabled) so behavior and types
remain unchanged.
- Around line 253-254: AgentTaskPayload types don't match the values returned:
transform sandboxFiles and fileTokens before assigning them to AgentTaskPayload
— map each SandboxFile (from wherever sandboxFiles is produced) into the shape
AgentTaskPayload expects ({ path: string; content: string; language?: string })
using SandboxFile.url/localPath/content fields, and convert fileTokens (returned
by getMessagesByChatId as Record<Id<"files">, number>) into a single number
(e.g., sum of the record's values or pick the relevant id) before assigning to
AgentTaskPayload.fileTokens; update the assignment site where sandboxFiles and
fileTokens are set or adjust AgentTaskPayload only if you intentionally want to
accept the original types.

In `@src/trigger/agent-task.ts`:
- Around line 75-86: The object returned by createMetadataWriter is being
directly asserted to UIMessageStreamWriter causing a TypeScript error; change
the final cast to go through unknown (e.g., return { write(...) { ... }, merge:
() => { ... } } as unknown as UIMessageStreamWriter) so the partial
implementation (write and merge) compiles; keep the existing write, merge,
MetadataEvent creation, and metadataStream.append usage intact and only change
the cast to "as unknown as UIMessageStreamWriter".
- Around line 266-275: AgentTaskPayload.fileTokens is a number but
checkAndSummarizeIfNeeded expects Record<Id<"files">, number>, causing a type
mismatch; fix by making the payload and its (de)serialization preserve the
original map shape or by broadening checkAndSummarizeIfNeeded to accept the
serialized form. Specifically either (A) change AgentTaskPayload.fileTokens back
to Record<Id<"files">, number> in prepare-agent-payload.ts and adjust any
serialization/deserialization so the file token map is sent/parsed as an object,
or (B) update the checkAndSummarizeIfNeeded signature in
lib/utils/message-summarization.ts to accept number | Record<Id<"files">,
number> and add logic to handle the numeric total (convert to a single-entry map
or treat as aggregate) so the call in agent-task.ts compiles without type
errors.
- Around line 182-189: The payload type mismatch occurs because
processMessageFiles/processChatMessages returns SandboxFile[] with shape { url:
string; localPath: string } but AgentTaskPayload.sandboxFiles is typed as
Array<{ path: string; content: string; language?: string }>; update
AgentTaskPayload.sandboxFiles to accept the actual SandboxFile[] shape (or
define and import a shared SandboxFile interface and use it) so assignments in
prepare-agent-payload (where sandboxFiles from processChatMessages is assigned)
type-check; ensure any usages of AgentTaskPayload.sandboxFiles (e.g.,
uploadSandboxFiles, ensureSandbox) are updated to the new shape or adapted via a
clear conversion function if content/path shape must be preserved.
- Line 229: The declaration of summarizationParts uses UIMessagePart<unknown,
unknown>[] which is inconsistent with other code and the return type of
createSummarizationCompletedPart(); change the type to UIMessagePart<any, any>[]
so summarizationParts matches createSummarizationCompletedPart() and other
usages (search for summarizationParts, UIMessagePart, and
createSummarizationCompletedPart to locate the spot).

In `@trigger.config.ts`:
- Line 4: The project field currently uses a non-null assertion
(process.env.TRIGGER_PROJECT!) which prevents the nullish fallback from ever
running; remove the `!` so the runtime fallback works (i.e., use
process.env.TRIGGER_PROJECT ?? "proj_umgzqxmfrucveonxqdmt") and ensure any
callers expecting a definite string handle possible undefined if needed; update
the expression associated with the project property in trigger.config.ts
accordingly.
🧹 Nitpick comments (6)
.gitignore (1)

61-62: Consider organizing Trigger.dev ignores separately with documentation.

The .trigger entry is correctly added but is placed under the "# Desktop (Tauri)" section, which may cause confusion since it relates to Trigger.dev, not Tauri. For better maintainability, consider adding a dedicated section with a comment explaining the purpose.

📁 Suggested organization
 # Desktop (Tauri)
 packages/desktop/node_modules/
 packages/desktop/src-tauri/target/
 
+# Trigger.dev
 .trigger
tsconfig.json (1)

32-33: Redundant include entry.

trigger.config.ts is already matched by the **/*.ts glob on Line 29. The explicit entry is harmless but unnecessary.

package.json (1)

10-10: npx trigger.dev@latest dev may diverge from pinned SDK version.

The dependencies pin @trigger.dev/sdk at 4.3.3, but this script pulls the @latest CLI. A major CLI update could introduce incompatibilities with the pinned SDK. Consider pinning the CLI version to match:

Proposed fix
-    "dev:trigger": "npx trigger.dev@latest dev",
+    "dev:trigger": "npx trigger.dev@4.3.3 dev",
lib/axiom/worker.ts (1)

21-42: Repeated initialization attempts when env vars are missing.

If AXIOM_TOKEN/AXIOM_DATASET are absent, getWorkerLogger() returns null without caching that result, so every log call re-checks env vars and potentially re-enters the try/catch. Since this is on a logging path (not perf-critical), it's minor — but a simple sentinel would avoid the repeated work.

Optional fix
 let _workerLogger: Logger | null = null;
+let _workerLoggerInitialized = false;
 
 function getWorkerLogger(): Logger | null {
-  if (_workerLogger) return _workerLogger;
+  if (_workerLoggerInitialized) return _workerLogger;
+  _workerLoggerInitialized = true;
   const token = process.env.AXIOM_TOKEN;
lib/ai/tools/utils/retry-with-backoff.ts (1)

68-68: Abort logging bypasses the caller-provided logger.

Lines 77-79 and 122-124 use the hardcoded retryLogger instead of the configurable logger option (used at lines 90, 99, 111). This means callers that supply a custom logger (e.g., sandbox-health.ts which filters for "failed after") won't see or control abort log output.

If this is intentional (abort events should always be logged), a brief comment would clarify the design. Otherwise, consider using logger consistently:

Proposed fix for consistency
     if (signal?.aborted) {
-      retryLogger(
+      logger(
         `Retry aborted before attempt ${attempt + 1}/${maxRetries} (reason: signal_already_aborted)`,
       );
-            retryLogger(
+            logger(
               `Retry aborted during backoff delay (attempt ${attempt + 1}/${maxRetries}, delayMs: ${delayMs}, reason: signal_aborted_during_delay)`,
             );

Also applies to: 77-79, 122-124

app/components/chat.tsx (1)

1037-1042: Duplicate array construction for trigger display messages.

The same [...triggerBaseAndUserMessages, ...(triggerAssistantMessage ? [triggerAssistantMessage] : [])] expression is repeated in three places (Lines 588, 1037, 1093). Consider extracting it into a useMemo to avoid redundant allocations and ensure referential stability for downstream components.

Also applies to: 1093-1098

- page (contains an array of documents that you fetches)
- isDone (a boolean that represents whether or not this is the last page of documents)
- continueCursor (a string that represents the cursor to use to fetch the next page of documents)
- page (contains an array of documents that you fetches)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix grammatical error in documentation.

The phrase "that you fetches" should be "that you fetch" or "that is fetched" for correct grammar.

📝 Proposed fix
-- page (contains an array of documents that you fetches)
+- page (contains an array of documents that you fetch)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- page (contains an array of documents that you fetches)
- page (contains an array of documents that you fetch)
🤖 Prompt for AI Agents
In @.cursor/rules/convex_rules.mdc at line 172, Update the grammatical error in
the documentation string "page (contains an array of documents that you
fetches)" by replacing "that you fetches" with a correct form such as "that you
fetch" or "that are fetched" so the line reads e.g. "page (contains an array of
documents that you fetch)"; locate the exact text in the
.cursor/rules/convex_rules.mdc file and apply the change.

Comment on lines +168 to +170
// Wait until specific date
await wait.until({ date: new Date("2024-12-25") });

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use a future/relative date in the wait.until example.

The current example date is already in the past, which can confuse readers and imply an immediate resolution. Consider using a future date or a relative example.

📝 Suggested edit
-    await wait.until({ date: new Date("2024-12-25") });
+    await wait.until({ date: new Date("2026-12-25") });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Wait until specific date
await wait.until({ date: new Date("2024-12-25") });
// Wait until specific date
await wait.until({ date: new Date("2026-12-25") });
🤖 Prompt for AI Agents
In @.cursor/rules/trigger.basic.mdc around lines 168 - 170, The example uses a
hardcoded past date in the wait.until call (await wait.until({ date: new
Date("2024-12-25") })), which can mislead readers; update the example to use a
future/relative date instead—either replace the literal with a clearly-future
ISO date string (e.g., next year) or compute a relative date dynamically (e.g.,
new Date(Date.now() + 7*24*60*60*1000)) so the wait.until example always
demonstrates waiting for a future date; keep the reference to the wait.until
call and the new Date constructor to locate the change.


export const maxDuration = 800;

export const POST = createChatHandler("/api/agent-stream");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Build failure: "/api/agent-stream" is not assignable to the endpoint parameter type.

The createChatHandler function (in lib/api/chat-handler.ts, Line 86) restricts its endpoint parameter to "/api/chat" | "/api/agent". Passing "/api/agent-stream" causes TS2345, breaking CI.

Either widen the union in createChatHandler:

Option A: Widen the union type in chat-handler.ts
 export const createChatHandler = (
-  endpoint: "/api/chat" | "/api/agent" = "/api/chat",
+  endpoint: "/api/chat" | "/api/agent" | "/api/agent-stream" = "/api/chat",
 ) => {

Or, if this route is meant to be a long-running fallback that mirrors /api/agent behavior, consider whether /api/agent-stream truly needs its own endpoint string or can reuse "/api/agent".

🧰 Tools
🪛 GitHub Actions: Run Tests

[error] 5-5: TypeScript error TS2345: Argument of type '"/api/agent-stream"' is not assignable to parameter of type '"/api/chat" | "/api/agent" | undefined'.

🪛 GitHub Check: test (20.x)

[failure] 5-5:
Argument of type '"/api/agent-stream"' is not assignable to parameter of type '"/api/chat" | "/api/agent" | undefined'.

🤖 Prompt for AI Agents
In `@app/api/agent-stream/route.ts` at line 5, The POST route in route.ts calls
createChatHandler("/api/agent-stream") but createChatHandler only accepts the
union "/api/chat" | "/api/agent", causing TS2345; fix by either changing the
call in route.ts (the POST export) to use the existing endpoint string
"/api/agent" if the stream should mirror that handler, or widen the accepted
union in createChatHandler (function createChatHandler) to include
"/api/agent-stream" so the new route compiles—update the type definition in
lib/api/chat-handler.ts accordingly.

Comment on lines +9 to +34
export async function POST(req: NextRequest) {
try {
const payload = await prepareAgentPayload(req);

const handle = await tasks.trigger<typeof agentStreamTask>(
"agent-stream",
payload,
);

const publicAccessToken = await auth.createPublicToken({
scopes: {
read: { runs: [handle.id] },
},
expirationTime: "1hr",
});

return Response.json({
runId: handle.id,
publicAccessToken,
});
} catch (error) {
if (error instanceof ChatSDKError) {
return error.toResponse();
}
throw error;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

No usage refund if tasks.trigger or auth.createPublicToken fails.

prepareAgentPayload calls checkRateLimit which deducts usage credits. If the subsequent tasks.trigger or auth.createPublicToken call throws a non-ChatSDKError exception, the error is rethrown without refunding the deducted credits. The existing chat-handler.ts uses a UsageRefundTracker for this purpose.

Suggested fix: add usage refund on error
+import { UsageRefundTracker } from "@/lib/rate-limit";
+
 export async function POST(req: NextRequest) {
+  const usageRefundTracker = new UsageRefundTracker();
   try {
     const payload = await prepareAgentPayload(req);
+    // Track deductions for potential refund
+    usageRefundTracker.setUser(payload.userId, payload.subscription);
 
     const handle = await tasks.trigger<typeof agentStreamTask>(
       "agent-stream",
       payload,
     );
     // ... rest of handler
   } catch (error) {
+    await usageRefundTracker.refund();
     if (error instanceof ChatSDKError) {
       return error.toResponse();
     }
     throw error;
   }
 }
🤖 Prompt for AI Agents
In `@app/api/agent/route.ts` around lines 9 - 34, The POST handler currently
deducts usage via prepareAgentPayload (which calls checkRateLimit) but does not
refund if tasks.trigger("agent-stream", payload) or auth.createPublicToken
fails; update POST to instantiate and use the same UsageRefundTracker pattern
(as used in chat-handler.ts) around the call to tasks.trigger and
auth.createPublicToken so that on any non-ChatSDKError exception you call the
tracker to refund the deducted credits before rethrowing, and ensure
ChatSDKError paths still use error.toResponse() without double-refund; reference
prepareAgentPayload, tasks.trigger, auth.createPublicToken, UsageRefundTracker,
agentStreamTask, and the POST function while making this change.

Comment on lines +18 to +23
const publicAccessToken = await auth.createPublicToken({
scopes: {
read: { runs: [handle.id] },
},
expirationTime: "1hr",
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Trigger.dev SDK auth.createPublicToken expirationTime parameter documentation

💡 Result:

In the Trigger.dev SDK, auth.createPublicToken() accepts an expirationTime option that controls when the Public Access Token expires.

  • Default: Public Access Tokens expire after 15 minutes if you don’t specify expirationTime. [1]
  • Accepted types / meaning: [1]
    • string → treated as a time span (e.g. "1hr", "15m")
    • number → treated as a Unix timestamp
    • Date → treated as an absolute date/time
  • Time-span string format: uses the jose time-span format: a number + unit (supports seconds/minutes/hours/days/weeks/years; no months). You can also use "ago", a leading -, or "from now". [1]

Example:

import { auth } from "@trigger.dev/sdk";

const publicToken = await auth.createPublicToken({
  expirationTime: "1hr",
});

[1]

Sources
[1] Trigger.dev Docs – Realtime authentication (Public Access Token expiration): https://trigger.dev/docs/frontend/overview


🏁 Script executed:

# Check the maxDuration value in the route file
fd "route.ts" --path "*/api/agent/*" -x cat -n {}

Repository: hackerai-tech/hackerai

Length of output: 281


🏁 Script executed:

# Find app/api/agent/route.ts and display it with line numbers
find . -name "route.ts" -path "*/api/agent/*" -type f -exec cat -n {} \;

Repository: hackerai-tech/hackerai

Length of output: 1208


🏁 Script executed:

# Find the agentStreamTask definition to check its actual maxDuration
find . -name "*agent*task*" -type f | head -20

Repository: hackerai-tech/hackerai

Length of output: 93


🏁 Script executed:

# Also search for agentStreamTask definition
rg "agentStreamTask" -A 10 --type ts --type tsx

Repository: hackerai-tech/hackerai

Length of output: 93


🏁 Script executed:

# Check the agent-task.ts file to see the actual task definition
cat -n ./src/trigger/agent-task.ts

Repository: hackerai-tech/hackerai

Length of output: 17451


🏁 Script executed:

# Search for agentStreamTask definition with correct file type
rg "agentStreamTask|maxDuration" -A 5 --type ts

Repository: hackerai-tech/hackerai

Length of output: 3434


Add safety buffer to public access token expiration.

The expirationTime: "1hr" aligns exactly with the Trigger.dev task's maxDuration: 3600 (from trigger.config.ts). If a task runs near the limit, the token could expire while the frontend is still streaming the response. Increase to "2hr" to add a safety margin.

Suggested fix
     const publicAccessToken = await auth.createPublicToken({
       scopes: {
         read: { runs: [handle.id] },
       },
-      expirationTime: "1hr",
+      expirationTime: "2hr",
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const publicAccessToken = await auth.createPublicToken({
scopes: {
read: { runs: [handle.id] },
},
expirationTime: "1hr",
});
const publicAccessToken = await auth.createPublicToken({
scopes: {
read: { runs: [handle.id] },
},
expirationTime: "2hr",
});
🤖 Prompt for AI Agents
In `@app/api/agent/route.ts` around lines 18 - 23, Update the public access token
creation to use a longer expiration to provide a safety buffer: change the
expirationTime passed to auth.createPublicToken when creating publicAccessToken
from "1hr" to "2hr", ensuring the token outlives tasks that approach the
Trigger.dev maxDuration; locate the call to auth.createPublicToken that
constructs publicAccessToken and modify its expirationTime string accordingly.

Comment on lines +75 to +86
function createMetadataWriter(): UIMessageStreamWriter {
return {
write(part: { type: string; data?: unknown }) {
if (!part.type.startsWith("data-")) return;
const event = { type: part.type, data: part.data } as MetadataEvent;
void metadataStream.append(event);
},
merge: () => {
// No-op: we pipe LLM stream separately to aiStream
},
} as UIMessageStreamWriter;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

TypeScript build failure: unsafe type assertion for UIMessageStreamWriter.

The CI reports that this cast is invalid because the partial object (write + no-op merge) doesn't overlap with the full UIMessageStreamWriter type. Cast through unknown to fix the build.

Proposed fix
-  } as UIMessageStreamWriter;
+  } as unknown as UIMessageStreamWriter;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function createMetadataWriter(): UIMessageStreamWriter {
return {
write(part: { type: string; data?: unknown }) {
if (!part.type.startsWith("data-")) return;
const event = { type: part.type, data: part.data } as MetadataEvent;
void metadataStream.append(event);
},
merge: () => {
// No-op: we pipe LLM stream separately to aiStream
},
} as UIMessageStreamWriter;
}
function createMetadataWriter(): UIMessageStreamWriter {
return {
write(part: { type: string; data?: unknown }) {
if (!part.type.startsWith("data-")) return;
const event = { type: part.type, data: part.data } as MetadataEvent;
void metadataStream.append(event);
},
merge: () => {
// No-op: we pipe LLM stream separately to aiStream
},
} as unknown as UIMessageStreamWriter;
}
🧰 Tools
🪛 GitHub Check: test (20.x)

[failure] 76-76:
Conversion of type '{ write(part: { type: string; data?: unknown; }): void; merge: () => void; }' to type 'UIMessageStreamWriter<UIMessage<unknown, UIDataTypes, UITools>>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

🤖 Prompt for AI Agents
In `@src/trigger/agent-task.ts` around lines 75 - 86, The object returned by
createMetadataWriter is being directly asserted to UIMessageStreamWriter causing
a TypeScript error; change the final cast to go through unknown (e.g., return {
write(...) { ... }, merge: () => { ... } } as unknown as UIMessageStreamWriter)
so the partial implementation (write and merge) compiles; keep the existing
write, merge, MetadataEvent creation, and metadataStream.append usage intact and
only change the cast to "as unknown as UIMessageStreamWriter".

Comment on lines +182 to +189
if (mode === "agent" && sandboxFiles && sandboxFiles.length > 0) {
writeUploadStartStatus(metadataWriter);
try {
await uploadSandboxFiles(sandboxFiles, ensureSandbox);
} finally {
writeUploadCompleteStatus(metadataWriter);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check the SandboxFile type definition
rg -n "SandboxFile" --type ts -A 5 -B 2 | head -100

Repository: hackerai-tech/hackerai

Length of output: 6023


🏁 Script executed:

# Check the uploadSandboxFiles function signature
rg -n "uploadSandboxFiles" --type ts -A 8 | head -100

Repository: hackerai-tech/hackerai

Length of output: 2877


🏁 Script executed:

# Check the file that serializes sandboxFiles (prepare-agent-payload.ts)
fd "prepare-agent-payload.ts" --type f

Repository: hackerai-tech/hackerai

Length of output: 98


🏁 Script executed:

# Check AgentTaskPayload type definition
rg -n "AgentTaskPayload" --type ts -A 10 -B 2 | head -150

Repository: hackerai-tech/hackerai

Length of output: 3070


🏁 Script executed:

# Get the complete AgentTaskPayload type definition
rg -n "export type AgentTaskPayload" --type ts -A 30 lib/api/prepare-agent-payload.ts

Repository: hackerai-tech/hackerai

Length of output: 1054


🏁 Script executed:

# Check how sandboxFiles is set in prepare-agent-payload.ts
rg -n "sandboxFiles" --type ts -B 3 -A 3 lib/api/prepare-agent-payload.ts

Repository: hackerai-tech/hackerai

Length of output: 715


🏁 Script executed:

# Check the complete prepare-agent-payload function implementation
cat -n lib/api/prepare-agent-payload.ts | head -200

Repository: hackerai-tech/hackerai

Length of output: 6992


🏁 Script executed:

# Verify there's no type casting or mapping before the uploadSandboxFiles call
sed -n '170,190p' src/trigger/agent-task.ts

Repository: hackerai-tech/hackerai

Length of output: 731


🏁 Script executed:

# Check if processChatMessages returns already-transformed sandboxFiles
rg -n "processChatMessages" --type ts -A 15 lib/chat/chat-processor.ts | head -80

Repository: hackerai-tech/hackerai

Length of output: 673


🏁 Script executed:

# Get the full return type of processChatMessages
rg -n "processChatMessages" --type ts -A 50 lib/chat/chat-processor.ts | head -120

Repository: hackerai-tech/hackerai

Length of output: 2467


🏁 Script executed:

# Check for any transformation of sandboxFiles in chat-processor
rg -n "sandboxFiles" --type ts -B 3 -A 3 lib/chat/chat-processor.ts

Repository: hackerai-tech/hackerai

Length of output: 404


🏁 Script executed:

# Find the processMessageFiles function and its return type
rg -n "export.*processMessageFiles|function processMessageFiles" --type ts -A 60 lib/utils/file-transform-utils.ts | head -150

Repository: hackerai-tech/hackerai

Length of output: 1799


TypeScript compilation error: sandboxFiles type mismatch in AgentTaskPayload.

processMessageFiles() returns SandboxFile[] with shape { url: string; localPath: string }, but AgentTaskPayload.sandboxFiles is typed as Array<{ path: string; content: string; language?: string }>. When sandboxFiles from processChatMessages() is assigned to the payload at prepare-agent-payload.ts:253, TypeScript will fail to compile. Align the AgentTaskPayload.sandboxFiles type definition with the actual SandboxFile[] returned by processMessageFiles().

🧰 Tools
🪛 GitHub Check: test (20.x)

[failure] 185-185:
Argument of type '{ path: string; content: string; language?: string | undefined; }[]' is not assignable to parameter of type 'SandboxFile[]'.

🤖 Prompt for AI Agents
In `@src/trigger/agent-task.ts` around lines 182 - 189, The payload type mismatch
occurs because processMessageFiles/processChatMessages returns SandboxFile[]
with shape { url: string; localPath: string } but AgentTaskPayload.sandboxFiles
is typed as Array<{ path: string; content: string; language?: string }>; update
AgentTaskPayload.sandboxFiles to accept the actual SandboxFile[] shape (or
define and import a shared SandboxFile interface and use it) so assignments in
prepare-agent-payload (where sandboxFiles from processChatMessages is assigned)
type-check; ensure any usages of AgentTaskPayload.sandboxFiles (e.g.,
uploadSandboxFiles, ensureSandbox) are updated to the new shape or adapted via a
clear conversion function if content/path shape must be preserved.

let finalMessages = processedMessages;
let hasSummarized = false;
const isReasoningModel = mode === "agent";
const summarizationParts: UIMessagePart<unknown, unknown>[] = [];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the file structure and verify the file exists
find . -type f -name "agent-task.ts" | head -5

Repository: hackerai-tech/hackerai

Length of output: 93


🏁 Script executed:

# Search for UIMessagePart type definition
rg "type UIMessagePart|interface UIMessagePart" -A 5

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Search for UIDataTypes definition
rg "type UIDataTypes|interface UIDataTypes" -A 10

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Read the file to find the actual line 229 and see imports
wc -l ./src/trigger/agent-task.ts

Repository: hackerai-tech/hackerai

Length of output: 97


🏁 Script executed:

# Read around line 229 with context
sed -n '220,240p' ./src/trigger/agent-task.ts

Repository: hackerai-tech/hackerai

Length of output: 800


🏁 Script executed:

# Search for all UIMessagePart usage
rg "UIMessagePart" -i

Repository: hackerai-tech/hackerai

Length of output: 1087


🏁 Script executed:

# Find the import statement for UIMessagePart in agent-task.ts
head -50 ./src/trigger/agent-task.ts | grep -A 2 -B 2 "UIMessagePart"

Repository: hackerai-tech/hackerai

Length of output: 169


🏁 Script executed:

# Check if there's a tsconfig.json
find . -name "tsconfig.json" -type f | head -5

Repository: hackerai-tech/hackerai

Length of output: 299


🏁 Script executed:

# Search for any TypeScript error output or build scripts
rg "UIDataTypes" -i

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Find package.json and check "ai" package version
rg '"ai"' -A 1 package.json | head -10

Repository: hackerai-tech/hackerai

Length of output: 115


🏁 Script executed:

# Search for UIMessagePart type definition in node_modules
find ./node_modules -path "*/ai/dist/*" -name "*.d.ts" | head -5

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Look for type definitions related to UIMessagePart
rg "UIMessagePart" ./node_modules/.pnpm/*/node_modules/ai --type typescript -A 3 2>/dev/null | head -50

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Check if there's a way to see the actual AI SDK types
find ./node_modules -path "*ai*" -name "index.d.ts" -o -name "*.d.ts" | grep -E "(^./node_modules/ai/|^./node_modules/.pnpm/ai)" | head -10

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Try to find the actual types file for the ai package
ls -la ./node_modules/ai/ 2>/dev/null || ls -la ./node_modules/.pnpm/ | grep "^d" | grep "ai" | head -5

Repository: hackerai-tech/hackerai

Length of output: 417


🏁 Script executed:

# Search for build logs or error output
find . -type f -name "*.log" -o -name "tsbuildinfo" | head -10

Repository: hackerai-tech/hackerai

Length of output: 374


🏁 Script executed:

# Check the actual type annotations in the codebase for other UIMessagePart usages
rg "UIMessagePart<" --type typescript -B 2 -A 2

Repository: hackerai-tech/hackerai

Length of output: 100


🏁 Script executed:

# Search for UIMessagePart generic type usage with correct regex
rg "UIMessagePart<" -A 2

Repository: hackerai-tech/hackerai

Length of output: 1403


🏁 Script executed:

# Look at what other files use for UIMessagePart generics
rg "UIMessagePart<" --type ts --type tsx

Repository: hackerai-tech/hackerai

Length of output: 93


🏁 Script executed:

# Check the stream-writer-utils to see how createSummarizationCompletedPart is defined
cat ./lib/utils/stream-writer-utils.ts | head -100

Repository: hackerai-tech/hackerai

Length of output: 2510


🏁 Script executed:

# Check if there are any build artifacts or error logs showing TS errors
find . -name "*.tsbuildinfo" -o -name "dist" -type d | head -5

Repository: hackerai-tech/hackerai

Length of output: 408


🏁 Script executed:

# Look for any GitHub Actions or CI logs that might show build errors
find . -path "*/.github/workflows/*" -name "*.yml" -o -name "*.yaml" | head -5

Repository: hackerai-tech/hackerai

Length of output: 220


🏁 Script executed:

# Check if the project builds successfully by looking for package.json build scripts
cat package.json | grep -A 5 '"scripts"'

Repository: hackerai-tech/hackerai

Length of output: 303


🏁 Script executed:

# Check the test workflow to see if TypeScript checks are run
cat ./.github/workflows/test.yml

Repository: hackerai-tech/hackerai

Length of output: 839


🏁 Script executed:

# Try to find package.json to see if there's a type-check script
rg '"type-check"|"typecheck"|"tsc"' package.json

Repository: hackerai-tech/hackerai

Length of output: 98


🏁 Script executed:

# Check if there's a next.config.js or similar that might have TypeScript settings
find . -maxdepth 2 -name "next.config.*" -o -name "tsconfig*.json" | grep -v node_modules

Repository: hackerai-tech/hackerai

Length of output: 98


🏁 Script executed:

# Read the tsconfig.json to understand type checking settings
cat ./tsconfig.json

Repository: hackerai-tech/hackerai

Length of output: 883


🏁 Script executed:

# Check if the project actually passes type checks or if there are known issues
ls -la | grep -i "tsc\|error\|build"

Repository: hackerai-tech/hackerai

Length of output: 131


🏁 Script executed:

# Try to locate ai package types in node_modules
find ./node_modules -path "*/ai/*" -name "index.d.ts" 2>/dev/null | head -5

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Look for ai package type definitions another way
ls -la ./node_modules/.pnpm/ | grep "^d.*ai@"

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Search more directly in pnpm for ai package
ls -la ./node_modules/.pnpm/ | grep "ai@" | head -10

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Try to access the ai package directly
cat ./node_modules/ai/package.json 2>/dev/null | head -30

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Search for the dist/index.d.ts or similar in ai package
find ./node_modules -path "*node_modules/ai*" -type f -name "*.d.ts" 2>/dev/null | head -10

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Look for UIMessagePart in node_modules more broadly
rg "export.*UIMessagePart\|type UIMessagePart\|interface UIMessagePart" ./node_modules --type-list

Repository: hackerai-tech/hackerai

Length of output: 455


🏁 Script executed:

# Search for any TypeScript definition files related to UIMessagePart
rg "UIMessagePart" ./node_modules -l 2>/dev/null | head -10

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Check the actual node_modules/ai structure
ls -la ./node_modules/ai 2>/dev/null || echo "ai not in node_modules root"

Repository: hackerai-tech/hackerai

Length of output: 93


🏁 Script executed:

# Search through pnpm structure for ai package
find ./node_modules/.pnpm -name "package.json" -path "*/ai@*" 2>/dev/null | head -3

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Let's try a different approach - check what the actual TypeScript error would be
# by examining how the code is used
rg "summarizationParts" -B 3 -A 3

Repository: hackerai-tech/hackerai

Length of output: 4099


🏁 Script executed:

# Check if there's any documentation or comments about UIDataTypes in the codebase
rg "UIDataTypes" -i

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Let's see if we can get more info about the ai package version and its types
cat ./package.json | grep -A 5 -B 5 '"ai"'

Repository: hackerai-tech/hackerai

Length of output: 410


🏁 Script executed:

# Try to understand the consistency issue better - check all UIMessagePart usages
rg "UIMessagePart\[" -A 1 -B 1

Repository: hackerai-tech/hackerai

Length of output: 48


🏁 Script executed:

# Check if the line has been recently modified
git log -1 --format="%h %s" -- src/trigger/agent-task.ts 2>/dev/null || echo "Git not available"

Repository: hackerai-tech/hackerai

Length of output: 91


🏁 Script executed:

# Let's verify the actual TypeScript compilation by checking if there are any error reports
# Look for evidence that the code actually compiles or fails
git status 2>/dev/null | head -20 || echo "Git status not available"

Repository: hackerai-tech/hackerai

Length of output: 333


🏁 Script executed:

# Check the exact imports and see if there are any type guards or narrowing
head -100 ./src/trigger/agent-task.ts | tail -80

Repository: hackerai-tech/hackerai

Length of output: 2528


🏁 Script executed:

# Verify what function is being pushed into the array
rg "createSummarizationCompletedPart" -B 2 -A 5 ./lib/utils/stream-writer-utils.ts

Repository: hackerai-tech/hackerai

Length of output: 226


Fix type inconsistency: use any generics like all other code paths.

UIMessagePart<unknown, unknown>[] is inconsistent with the return type of createSummarizationCompletedPart() which returns UIMessagePart<any, any>, and differs from all other usages in the codebase (lib/utils.ts, lib/api/chat-handler.ts, etc.). Change to UIMessagePart<any, any>[] for consistency.

Proposed fix
-  const summarizationParts: UIMessagePart<unknown, unknown>[] = [];
+  const summarizationParts: UIMessagePart<any, any>[] = [];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const summarizationParts: UIMessagePart<unknown, unknown>[] = [];
const summarizationParts: UIMessagePart<any, any>[] = [];
🧰 Tools
🪛 GitHub Check: test (20.x)

[failure] 229-229:
Type 'unknown' does not satisfy the constraint 'UIDataTypes'.

🤖 Prompt for AI Agents
In `@src/trigger/agent-task.ts` at line 229, The declaration of summarizationParts
uses UIMessagePart<unknown, unknown>[] which is inconsistent with other code and
the return type of createSummarizationCompletedPart(); change the type to
UIMessagePart<any, any>[] so summarizationParts matches
createSummarizationCompletedPart() and other usages (search for
summarizationParts, UIMessagePart, and createSummarizationCompletedPart to
locate the spot).

Comment on lines +266 to +275
const { needsSummarization, summarizedMessages } =
await checkAndSummarizeIfNeeded(
finalMessages,
subscription,
trackedProvider.languageModel(summarizationModelName),
mode,
metadataWriter,
chatId,
fileTokens,
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find AgentTaskPayload definition
rg -n "AgentTaskPayload" --type=ts -A 10 | head -50

Repository: hackerai-tech/hackerai

Length of output: 2602


🏁 Script executed:

#!/bin/bash
# Check imports and type declarations in agent-task.ts
head -50 src/trigger/agent-task.ts

Repository: hackerai-tech/hackerai

Length of output: 1656


🏁 Script executed:

#!/bin/bash
# Find checkAndSummarizeIfNeeded function definition
rg -n "checkAndSummarizeIfNeeded" --type=ts -A 5 | head -100

Repository: hackerai-tech/hackerai

Length of output: 2217


🏁 Script executed:

#!/bin/bash
# Get full checkAndSummarizeIfNeeded function signature
rg -n "checkAndSummarizeIfNeeded" -A 15 --type=ts lib/utils/message-summarization.ts

Repository: hackerai-tech/hackerai

Length of output: 644


🏁 Script executed:

#!/bin/bash
# Get full AgentTaskPayload type definition
rg -n "export type AgentTaskPayload" -A 30 --type=ts lib/api/prepare-agent-payload.ts

Repository: hackerai-tech/hackerai

Length of output: 1054


🏁 Script executed:

#!/bin/bash
# Check what fileTokens is in the context at line 274
sed -n '260,280p' src/trigger/agent-task.ts

Repository: hackerai-tech/hackerai

Length of output: 972


TypeScript type mismatch: fileTokens parameter incompatibility.

AgentTaskPayload.fileTokens is typed as number (lib/api/prepare-agent-payload.ts:69), but checkAndSummarizeIfNeeded expects Record<Id<"files">, number> (lib/utils/message-summarization.ts:79). The payload serialization has flattened the token map to a simple number. Either preserve the Record<Id<"files">, number> shape during serialization/deserialization, or modify checkAndSummarizeIfNeeded to accept the serialized number format.

🧰 Tools
🪛 GitHub Check: test (20.x)

[failure] 274-274:
Argument of type 'number' is not assignable to parameter of type 'Record<Id<"files">, number>'.

🤖 Prompt for AI Agents
In `@src/trigger/agent-task.ts` around lines 266 - 275,
AgentTaskPayload.fileTokens is a number but checkAndSummarizeIfNeeded expects
Record<Id<"files">, number>, causing a type mismatch; fix by making the payload
and its (de)serialization preserve the original map shape or by broadening
checkAndSummarizeIfNeeded to accept the serialized form. Specifically either (A)
change AgentTaskPayload.fileTokens back to Record<Id<"files">, number> in
prepare-agent-payload.ts and adjust any serialization/deserialization so the
file token map is sent/parsed as an object, or (B) update the
checkAndSummarizeIfNeeded signature in lib/utils/message-summarization.ts to
accept number | Record<Id<"files">, number> and add logic to handle the numeric
total (convert to a single-entry map or treat as aggregate) so the call in
agent-task.ts compiles without type errors.

import { defineConfig } from "@trigger.dev/sdk/v3";

export default defineConfig({
project: process.env.TRIGGER_PROJECT! ?? "proj_umgzqxmfrucveonxqdmt",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Non-null assertion (!) makes the ?? fallback unreachable.

process.env.TRIGGER_PROJECT! tells TypeScript the value is never null/undefined, so the nullish coalescing operator is dead code. Remove the ! to let the fallback work at runtime:

Proposed fix
-  project: process.env.TRIGGER_PROJECT! ?? "proj_umgzqxmfrucveonxqdmt",
+  project: process.env.TRIGGER_PROJECT ?? "proj_umgzqxmfrucveonxqdmt",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
project: process.env.TRIGGER_PROJECT! ?? "proj_umgzqxmfrucveonxqdmt",
project: process.env.TRIGGER_PROJECT ?? "proj_umgzqxmfrucveonxqdmt",
🤖 Prompt for AI Agents
In `@trigger.config.ts` at line 4, The project field currently uses a non-null
assertion (process.env.TRIGGER_PROJECT!) which prevents the nullish fallback
from ever running; remove the `!` so the runtime fallback works (i.e., use
process.env.TRIGGER_PROJECT ?? "proj_umgzqxmfrucveonxqdmt") and ensure any
callers expecting a definite string handle possible undefined if needed; update
the expression associated with the project property in trigger.config.ts
accordingly.

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