From 303df4b269c2f5834e869a8e4071355ee942ba2d Mon Sep 17 00:00:00 2001 From: Daniel Blignaut Date: Sun, 2 Aug 2026 20:50:11 +0200 Subject: [PATCH 1/6] feat(sentry-bot): consume typed ChatKit signals --- code-review-bot/app/api/code-review/route.ts | 6 +- code-review-bot/package.json | 3 +- code-review-bot/pnpm-lock.yaml | 11 +- .../app/api/sentry-remediation/route.ts | 136 +++++++++++------- .../lib/remediation/signal.ts | 68 ++++----- sentry-remediation-bot/package.json | 4 +- sentry-remediation-bot/pnpm-lock.yaml | 11 +- 7 files changed, 134 insertions(+), 105 deletions(-) diff --git a/code-review-bot/app/api/code-review/route.ts b/code-review-bot/app/api/code-review/route.ts index ca45b85..5c96b87 100644 --- a/code-review-bot/app/api/code-review/route.ts +++ b/code-review-bot/app/api/code-review/route.ts @@ -24,6 +24,7 @@ const REQUEST_TIMEOUT_MS = 285_000; export const POST = chatKitEndpoint({ client: tilde, webhookSigningKey: env.TILDE_WEBHOOK_SIGNING_KEY, + requestTimeoutMs: REQUEST_TIMEOUT_MS, async handler(request, context) { const github = context.github; if (!github) { @@ -32,10 +33,7 @@ export const POST = chatKitEndpoint({ if (!github.owner || !github.repo || !github.pull_number) { throw new Error("The GitHub message must identify a pull request."); } - const signal = AbortSignal.any([ - request.signal, - AbortSignal.timeout(REQUEST_TIMEOUT_MS), - ]); + const signal = request.signal; const history = await context.session.history(); const messages = await convertToAiSdkMessages({ messages: [...history.items, ...context.messages], diff --git a/code-review-bot/package.json b/code-review-bot/package.json index 6cb1f92..d6aa340 100644 --- a/code-review-bot/package.json +++ b/code-review-bot/package.json @@ -14,6 +14,7 @@ "sharp": "0.35.3" }, "onlyBuiltDependencies": [ + "@trytilde/harness-sdk-vercel-ai-node", "cbor-extract", "esbuild", "protobufjs", @@ -32,7 +33,7 @@ "@ai-sdk/mcp": "1.0.59", "@ai-sdk/openai": "3.0.81", "@trytilde/harness-sdk": "0.1.2", - "@trytilde/harness-sdk-vercel-ai-node": "0.1.2", + "@trytilde/harness-sdk-vercel-ai-node": "git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node", "ai": "6.0.220", "modal": "0.9.0", "next": "16.2.12", diff --git a/code-review-bot/pnpm-lock.yaml b/code-review-bot/pnpm-lock.yaml index 17df6bb..eed593b 100644 --- a/code-review-bot/pnpm-lock.yaml +++ b/code-review-bot/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: specifier: 0.1.2 version: 0.1.2 '@trytilde/harness-sdk-vercel-ai-node': - specifier: 0.1.2 - version: 0.1.2(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) + specifier: git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node + version: git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) ai: specifier: 6.0.220 version: 6.0.220(zod@4.4.3) @@ -576,8 +576,9 @@ packages: '@trytilde/api-client@0.1.2': resolution: {integrity: sha512-kpwS1CgRqVrTrcYJ5ZrZm+iA1gamx5CO9YRzTT9rEdn5bIOTsrbHyTCvbBTH9XrBD3Q5K3dFenW0H2Ku7NMScA==} - '@trytilde/harness-sdk-vercel-ai-node@0.1.2': - resolution: {integrity: sha512-gk/1KH6Rb6kPUpUhtvkJqB5rHz5eBu6WDewcs0FbWJIZZ2A+uf0kr11p0WqklVgbrjn7tJjNZN6Un/Gg01lC2w==} + '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node': + resolution: {commit: b658d48e9f4760899d26e7fa473dc13e4a3ce3cb, path: packages/vercel-ai-node, repo: git@github.com:trytilde/harness-sdk.git, type: git} + version: 0.1.2 peerDependencies: '@ai-sdk/mcp': ^1.0.46 '@trytilde/harness-sdk': ^0.1.2 @@ -2525,7 +2526,7 @@ snapshots: '@trytilde/api-client@0.1.2': {} - '@trytilde/harness-sdk-vercel-ai-node@0.1.2(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': + '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': dependencies: '@ai-sdk/mcp': 1.0.59(zod@4.4.3) '@trytilde/harness-sdk': 0.1.2 diff --git a/sentry-remediation-bot/app/api/sentry-remediation/route.ts b/sentry-remediation-bot/app/api/sentry-remediation/route.ts index f82aa0f..0bf90c8 100644 --- a/sentry-remediation-bot/app/api/sentry-remediation/route.ts +++ b/sentry-remediation-bot/app/api/sentry-remediation/route.ts @@ -1,6 +1,15 @@ -import { chatKitEndpoint, convertToAiSdkMessages, createMCPClient } from "@trytilde/harness-sdk-vercel-ai-node"; +import { + chatKitEndpoint, + convertToAiSdkMessages, + createMCPClient, +} from "@trytilde/harness-sdk-vercel-ai-node"; import { openai } from "@ai-sdk/openai"; -import { consumeStream, convertToModelMessages, stepCountIs, streamText } from "ai"; +import { + consumeStream, + convertToModelMessages, + stepCountIs, + streamText, +} from "ai"; import { env } from "@/lib/env"; import { remediationPrompt } from "@/lib/remediation/prompt"; import { @@ -8,7 +17,7 @@ import { createRemediationSandbox, type RemediationSandbox, } from "@/lib/remediation/sandbox"; -import { latestSentrySignalPrompt } from "@/lib/remediation/signal"; +import { sentryIssueCreatedMessage } from "@/lib/remediation/signal"; import { tilde } from "@/lib/tilde"; export const maxDuration = 300; @@ -17,63 +26,88 @@ const REQUEST_TIMEOUT_MS = 285_000; export const POST = chatKitEndpoint({ client: tilde, webhookSigningKey: env.TILDE_WEBHOOK_SIGNING_KEY, + requestTimeoutMs: REQUEST_TIMEOUT_MS, async handler(request, context) { - const signal = AbortSignal.any([request.signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)]); - const messages = await convertToAiSdkMessages({ messages: context.messages, chatkit: context.chatkit }); - const signalPrompt = await waitForSentrySignalPrompt(context.sessionId, signal); - const { mcp, closeMcp } = await createMCPClient({ client: tilde, serverId: env.TILDE_MCP_SERVER_ID }); + const history = await context.session.history(); + let handledSentrySignal = false; + const messages = await convertToAiSdkMessages({ + messages: [...history.items, ...context.messages], + chatkit: context.chatkit, + onUnprocessed: { + sentry: { + "sentry.issue.created": (signal) => { + handledSentrySignal = true; + return sentryIssueCreatedMessage(signal); + }, + }, + }, + }); + if (!handledSentrySignal) { + throw new Error( + "The remediation session does not contain a valid Sentry issue-created signal", + ); + } + const { mcp, closeMcp } = await createMCPClient({ + client: tilde, + serverId: env.TILDE_MCP_SERVER_ID, + }); let sandbox: RemediationSandbox | undefined; - async function closeResources() { for (const result of await Promise.allSettled([sandbox?.close(), closeMcp()])) if (result.status === "rejected") console.error("Could not clean up remediation resource.", result.reason); } + async function closeResources() { + for (const result of await Promise.allSettled([ + sandbox?.close(), + closeMcp(), + ])) { + if (result.status === "rejected") { + console.error( + "Could not clean up remediation resource.", + result.reason, + ); + } + } + } try { const remoteTools = await mcp.tools(); - sandbox = await createRemediationSandbox(env, tilde, remoteTools, signal); + sandbox = await createRemediationSandbox( + env, + tilde, + remoteTools, + request.signal, + ); const activeSandbox = sandbox; - signal.addEventListener("abort", () => void activeSandbox.close(), { once: true }); + request.signal.addEventListener( + "abort", + () => void activeSandbox.close(), + { once: true }, + ); const result = streamText({ - abortSignal: signal, - messages: [ - ...(await convertToModelMessages(messages)), - { role: "user", content: signalPrompt }, - ], + abortSignal: request.signal, + messages: await convertToModelMessages(messages), model: openai(env.OPENAI_MODEL), stopWhen: stepCountIs(60), - system: remediationPrompt(activeSandbox.id, activeSandbox.repositoryPath, env.GITHUB_REPOSITORY), + system: remediationPrompt( + activeSandbox.id, + activeSandbox.repositoryPath, + env.GITHUB_REPOSITORY, + ), tools: agentRemediationTools(remoteTools), - async onError({ error }) { console.error("Sentry remediation failed.", error); await closeResources(); }, - async onAbort() { await closeResources(); }, - async onFinish() { await closeResources(); }, + async onError({ error }) { + console.error("Sentry remediation failed.", error); + await closeResources(); + }, + async onAbort() { + await closeResources(); + }, + async onFinish() { + await closeResources(); + }, + }); + return result.toUIMessageStreamResponse({ + consumeSseStream: consumeStream, + originalMessages: messages, }); - return result.toUIMessageStreamResponse({ consumeSseStream: consumeStream, originalMessages: messages }); - } catch (error) { await closeResources(); throw error; } - }, -}); - -async function waitForSentrySignalPrompt(sessionId: string, signal: AbortSignal): Promise { - let lastError: unknown; - for (let attempt = 0; attempt < 20; attempt += 1) { - try { - const rawMessages = await tilde.chatkit.listMessageHistory({ sessionId, pageSize: 100 }); - return latestSentrySignalPrompt(rawMessages.items); } catch (error) { - lastError = error; - if (attempt === 19) break; - await abortableDelay(250, signal); + await closeResources(); + throw error; } - } - throw lastError; -} - -function abortableDelay(delayMs: number, signal: AbortSignal): Promise { - if (signal.aborted) return Promise.reject(signal.reason); - return new Promise((resolve, reject) => { - const onAbort = () => { - clearTimeout(timeout); - reject(signal.reason); - }; - const timeout = setTimeout(() => { - signal.removeEventListener("abort", onAbort); - resolve(); - }, delayMs); - signal.addEventListener("abort", onAbort, { once: true }); - }); -} + }, +}); diff --git a/sentry-remediation-bot/lib/remediation/signal.ts b/sentry-remediation-bot/lib/remediation/signal.ts index 61d72e2..96438ee 100644 --- a/sentry-remediation-bot/lib/remediation/signal.ts +++ b/sentry-remediation-bot/lib/remediation/signal.ts @@ -1,40 +1,34 @@ -import { z } from "zod"; +import type { SentrySignalByType } from "@trytilde/harness-sdk-vercel-ai-node"; +import type { UIMessage } from "ai"; -const sentrySignalMessageSchema = z.object({ - data: z.object({ - data: z.object({ - event: z.object({ eventID: z.string().min(1) }).passthrough(), - issue: z - .object({ - id: z.string().min(1), - permalink: z.string().url(), - shortId: z.string().min(1), - title: z.string().min(1), - }) - .passthrough(), - }), - }), - metadata: z.object({ signal_type: z.literal("sentry.issue.created") }).passthrough(), - role: z.literal("system"), - summary: z.string().min(1), - type: z.literal("signal"), -}); +type SentryIssueCreatedSignal = + SentrySignalByType["sentry.issue.created"]; -/** Convert the newest raw Sentry signal message into the explicit task given to the model. */ -export function latestSentrySignalPrompt(messages: unknown[]): string { - for (const message of messages.toReversed()) { - const parsed = sentrySignalMessageSchema.safeParse(message); - if (!parsed.success) continue; - const { event, issue } = parsed.data.data.data; - return [ - "A new Sentry issue signal triggered this remediation run.", - `Summary: ${parsed.data.summary}`, - `Issue: ${issue.shortId} (${issue.id})`, - `URL: ${issue.permalink}`, - `Event ID: ${event.eventID}`, - `Title: ${issue.title}`, - "Investigate this exact issue and complete the remediation workflow described in the system prompt.", - ].join("\n"); - } - throw new Error("The remediation session does not contain a valid Sentry issue-created signal"); +/** Convert a typed Sentry issue-created signal into the model's remediation task. */ +export function sentryIssueCreatedMessage( + signal: SentryIssueCreatedSignal, +): UIMessage { + const { event, issue } = signal.data.data; + const eventId = + typeof event === "object" && + event !== null && + "eventID" in event && + typeof event.eventID === "string" + ? event.eventID + : "not provided"; + const prompt = [ + "A new Sentry issue signal triggered this remediation run.", + `Summary: ${signal.summary ?? issue.title}`, + `Issue: ${issue.shortId ?? issue.id} (${issue.id})`, + `URL: ${issue.permalink ?? "not provided"}`, + `Event ID: ${eventId}`, + `Title: ${issue.title}`, + "Investigate this exact issue and complete the remediation workflow described in the system prompt.", + ].join("\n"); + + return { + id: signal.id, + role: "user", + parts: [{ type: "text", text: prompt }], + }; } diff --git a/sentry-remediation-bot/package.json b/sentry-remediation-bot/package.json index 54c623a..74bb543 100644 --- a/sentry-remediation-bot/package.json +++ b/sentry-remediation-bot/package.json @@ -8,11 +8,11 @@ "packageManager": "pnpm@10.33.1", "pnpm": { "overrides": { "postcss": "8.5.25", "sharp": "0.35.3" }, - "onlyBuiltDependencies": ["cbor-extract", "esbuild", "protobufjs", "sharp", "unrs-resolver"] + "onlyBuiltDependencies": ["@trytilde/harness-sdk-vercel-ai-node", "cbor-extract", "esbuild", "protobufjs", "sharp", "unrs-resolver"] }, "scripts": { "dev": "next dev --webpack", "build": "next build --webpack", "start": "next start", "lint": "eslint .", "typecheck": "tsc --noEmit", "test": "node --test lib/remediation/issue-session-key.test.mjs" }, "dependencies": { - "@ai-sdk/mcp": "1.0.59", "@ai-sdk/openai": "3.0.81", "@trytilde/harness-sdk": "0.1.2", "@trytilde/harness-sdk-vercel-ai-node": "0.1.2", "ai": "6.0.220", "next": "16.2.12", "react": "19.2.8", "react-dom": "19.2.8", "zod": "4.4.3" + "@ai-sdk/mcp": "1.0.59", "@ai-sdk/openai": "3.0.81", "@trytilde/harness-sdk": "0.1.2", "@trytilde/harness-sdk-vercel-ai-node": "git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node", "ai": "6.0.220", "next": "16.2.12", "react": "19.2.8", "react-dom": "19.2.8", "zod": "4.4.3" }, "devDependencies": { "@types/node": "24.10.0", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", "eslint": "9.39.1", "eslint-config-next": "16.2.12", "typescript": "5.9.3" } } diff --git a/sentry-remediation-bot/pnpm-lock.yaml b/sentry-remediation-bot/pnpm-lock.yaml index 6929538..ef89504 100644 --- a/sentry-remediation-bot/pnpm-lock.yaml +++ b/sentry-remediation-bot/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: specifier: 0.1.2 version: 0.1.2 '@trytilde/harness-sdk-vercel-ai-node': - specifier: 0.1.2 - version: 0.1.2(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) + specifier: git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node + version: git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) ai: specifier: 6.0.220 version: 6.0.220(zod@4.4.3) @@ -540,8 +540,9 @@ packages: '@trytilde/api-client@0.1.2': resolution: {integrity: sha512-kpwS1CgRqVrTrcYJ5ZrZm+iA1gamx5CO9YRzTT9rEdn5bIOTsrbHyTCvbBTH9XrBD3Q5K3dFenW0H2Ku7NMScA==} - '@trytilde/harness-sdk-vercel-ai-node@0.1.2': - resolution: {integrity: sha512-gk/1KH6Rb6kPUpUhtvkJqB5rHz5eBu6WDewcs0FbWJIZZ2A+uf0kr11p0WqklVgbrjn7tJjNZN6Un/Gg01lC2w==} + '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node': + resolution: {commit: b658d48e9f4760899d26e7fa473dc13e4a3ce3cb, path: packages/vercel-ai-node, repo: git@github.com:trytilde/harness-sdk.git, type: git} + version: 0.1.2 peerDependencies: '@ai-sdk/mcp': ^1.0.46 '@trytilde/harness-sdk': ^0.1.2 @@ -2441,7 +2442,7 @@ snapshots: '@trytilde/api-client@0.1.2': {} - '@trytilde/harness-sdk-vercel-ai-node@0.1.2(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': + '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': dependencies: '@ai-sdk/mcp': 1.0.59(zod@4.4.3) '@trytilde/harness-sdk': 0.1.2 From 85176ac37f39fbc589612b4c8015e1f7d456e133 Mon Sep 17 00:00:00 2001 From: Daniel Blignaut Date: Sun, 2 Aug 2026 20:56:39 +0200 Subject: [PATCH 2/6] build: vendor typed-signal SDK prerelease --- code-review-bot/package.json | 3 +-- code-review-bot/pnpm-lock.yaml | 12 ++++++------ sentry-remediation-bot/package.json | 4 ++-- sentry-remediation-bot/pnpm-lock.yaml | 12 ++++++------ ...dk-vercel-ai-node-0.2.0-sentry-signals.0.tgz | Bin 0 -> 32922 bytes 5 files changed, 15 insertions(+), 16 deletions(-) create mode 100644 vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz diff --git a/code-review-bot/package.json b/code-review-bot/package.json index d6aa340..058e86f 100644 --- a/code-review-bot/package.json +++ b/code-review-bot/package.json @@ -14,7 +14,6 @@ "sharp": "0.35.3" }, "onlyBuiltDependencies": [ - "@trytilde/harness-sdk-vercel-ai-node", "cbor-extract", "esbuild", "protobufjs", @@ -33,7 +32,7 @@ "@ai-sdk/mcp": "1.0.59", "@ai-sdk/openai": "3.0.81", "@trytilde/harness-sdk": "0.1.2", - "@trytilde/harness-sdk-vercel-ai-node": "git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node", + "@trytilde/harness-sdk-vercel-ai-node": "file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz", "ai": "6.0.220", "modal": "0.9.0", "next": "16.2.12", diff --git a/code-review-bot/pnpm-lock.yaml b/code-review-bot/pnpm-lock.yaml index eed593b..f1790c2 100644 --- a/code-review-bot/pnpm-lock.yaml +++ b/code-review-bot/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: specifier: 0.1.2 version: 0.1.2 '@trytilde/harness-sdk-vercel-ai-node': - specifier: git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node - version: git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) + specifier: file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz + version: file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) ai: specifier: 6.0.220 version: 6.0.220(zod@4.4.3) @@ -576,9 +576,9 @@ packages: '@trytilde/api-client@0.1.2': resolution: {integrity: sha512-kpwS1CgRqVrTrcYJ5ZrZm+iA1gamx5CO9YRzTT9rEdn5bIOTsrbHyTCvbBTH9XrBD3Q5K3dFenW0H2Ku7NMScA==} - '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node': - resolution: {commit: b658d48e9f4760899d26e7fa473dc13e4a3ce3cb, path: packages/vercel-ai-node, repo: git@github.com:trytilde/harness-sdk.git, type: git} - version: 0.1.2 + '@trytilde/harness-sdk-vercel-ai-node@file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz': + resolution: {integrity: sha512-zuSSJAqUVMzVgbFUT4tHYBGItDRcA9/OLc0rpH6Z4YzCuNstFb1Q3qB7JUiB/eo8Tt2Qsc7tpeDGPINRXmRq2A==, tarball: file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz} + version: 0.2.0-sentry-signals.0 peerDependencies: '@ai-sdk/mcp': ^1.0.46 '@trytilde/harness-sdk': ^0.1.2 @@ -2526,7 +2526,7 @@ snapshots: '@trytilde/api-client@0.1.2': {} - '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': + '@trytilde/harness-sdk-vercel-ai-node@file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': dependencies: '@ai-sdk/mcp': 1.0.59(zod@4.4.3) '@trytilde/harness-sdk': 0.1.2 diff --git a/sentry-remediation-bot/package.json b/sentry-remediation-bot/package.json index 74bb543..c705383 100644 --- a/sentry-remediation-bot/package.json +++ b/sentry-remediation-bot/package.json @@ -8,11 +8,11 @@ "packageManager": "pnpm@10.33.1", "pnpm": { "overrides": { "postcss": "8.5.25", "sharp": "0.35.3" }, - "onlyBuiltDependencies": ["@trytilde/harness-sdk-vercel-ai-node", "cbor-extract", "esbuild", "protobufjs", "sharp", "unrs-resolver"] + "onlyBuiltDependencies": ["cbor-extract", "esbuild", "protobufjs", "sharp", "unrs-resolver"] }, "scripts": { "dev": "next dev --webpack", "build": "next build --webpack", "start": "next start", "lint": "eslint .", "typecheck": "tsc --noEmit", "test": "node --test lib/remediation/issue-session-key.test.mjs" }, "dependencies": { - "@ai-sdk/mcp": "1.0.59", "@ai-sdk/openai": "3.0.81", "@trytilde/harness-sdk": "0.1.2", "@trytilde/harness-sdk-vercel-ai-node": "git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node", "ai": "6.0.220", "next": "16.2.12", "react": "19.2.8", "react-dom": "19.2.8", "zod": "4.4.3" + "@ai-sdk/mcp": "1.0.59", "@ai-sdk/openai": "3.0.81", "@trytilde/harness-sdk": "0.1.2", "@trytilde/harness-sdk-vercel-ai-node": "file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz", "ai": "6.0.220", "next": "16.2.12", "react": "19.2.8", "react-dom": "19.2.8", "zod": "4.4.3" }, "devDependencies": { "@types/node": "24.10.0", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", "eslint": "9.39.1", "eslint-config-next": "16.2.12", "typescript": "5.9.3" } } diff --git a/sentry-remediation-bot/pnpm-lock.yaml b/sentry-remediation-bot/pnpm-lock.yaml index ef89504..2574c59 100644 --- a/sentry-remediation-bot/pnpm-lock.yaml +++ b/sentry-remediation-bot/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: specifier: 0.1.2 version: 0.1.2 '@trytilde/harness-sdk-vercel-ai-node': - specifier: git+https://github.com/trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node - version: git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) + specifier: file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz + version: file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3)) ai: specifier: 6.0.220 version: 6.0.220(zod@4.4.3) @@ -540,9 +540,9 @@ packages: '@trytilde/api-client@0.1.2': resolution: {integrity: sha512-kpwS1CgRqVrTrcYJ5ZrZm+iA1gamx5CO9YRzTT9rEdn5bIOTsrbHyTCvbBTH9XrBD3Q5K3dFenW0H2Ku7NMScA==} - '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node': - resolution: {commit: b658d48e9f4760899d26e7fa473dc13e4a3ce3cb, path: packages/vercel-ai-node, repo: git@github.com:trytilde/harness-sdk.git, type: git} - version: 0.1.2 + '@trytilde/harness-sdk-vercel-ai-node@file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz': + resolution: {integrity: sha512-zuSSJAqUVMzVgbFUT4tHYBGItDRcA9/OLc0rpH6Z4YzCuNstFb1Q3qB7JUiB/eo8Tt2Qsc7tpeDGPINRXmRq2A==, tarball: file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz} + version: 0.2.0-sentry-signals.0 peerDependencies: '@ai-sdk/mcp': ^1.0.46 '@trytilde/harness-sdk': ^0.1.2 @@ -2442,7 +2442,7 @@ snapshots: '@trytilde/api-client@0.1.2': {} - '@trytilde/harness-sdk-vercel-ai-node@git+https://git@github.com:trytilde/harness-sdk.git#b658d48e9f4760899d26e7fa473dc13e4a3ce3cb&path:packages/vercel-ai-node(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': + '@trytilde/harness-sdk-vercel-ai-node@file:../vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz(@ai-sdk/mcp@1.0.59(zod@4.4.3))(@trytilde/harness-sdk@0.1.2)(ai@6.0.220(zod@4.4.3))': dependencies: '@ai-sdk/mcp': 1.0.59(zod@4.4.3) '@trytilde/harness-sdk': 0.1.2 diff --git a/vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz b/vendor/trytilde-harness-sdk-vercel-ai-node-0.2.0-sentry-signals.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c714bb2e41b6030c023d6903d463fb40e43b79da GIT binary patch literal 32922 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