From 8499cc6d585024232ba4aa99722a5253eddad13b Mon Sep 17 00:00:00 2001 From: Khaliq Date: Mon, 29 Jun 2026 15:30:38 -0700 Subject: [PATCH 1/4] use cast.agentrelay.com --- evals/harness.ts | 3 +- package-lock.json | 782 ++++++++++++++-------- package.json | 19 +- src/main/broker.ts | 3 +- src/main/cloud-agent.ts | 3 +- src/main/integration-event-bridge.ts | 6 +- src/main/integration-mounts.ts | 3 +- src/main/integrations.ts | 5 +- src/main/relay-service-urls.test.ts | 48 ++ src/main/relay-service-urls.ts | 62 ++ src/main/relay-workspace.ts | 9 +- src/main/relayfile-mount-launcher.test.ts | 22 + src/main/relayfile-mount-launcher.ts | 3 +- 13 files changed, 649 insertions(+), 319 deletions(-) create mode 100644 src/main/relay-service-urls.test.ts create mode 100644 src/main/relay-service-urls.ts diff --git a/evals/harness.ts b/evals/harness.ts index a69c5f2b..5afc0e75 100644 --- a/evals/harness.ts +++ b/evals/harness.ts @@ -12,6 +12,7 @@ import { HarnessDriverClient } from '@agent-relay/harness-driver' import type { BrokerEvent, AgentExitInfo } from '@agent-relay/harness-driver' import { RelaycastSetup } from '@relaycast/sdk' +import { RELAYCAST_BASE_URL } from '../src/main/relay-service-urls' const AGENT_EXIT_TIMEOUT_MS = 180_000 // 3 min max per run const AGENT_IDLE_THRESHOLD_SECS = 30 // release after 30s idle @@ -41,7 +42,7 @@ async function ensureWorkspaceKey(): Promise { _cachedWorkspaceKey = process.env.RELAY_API_KEY.trim() return _cachedWorkspaceKey } - const setup = new RelaycastSetup() + const setup = new RelaycastSetup({ baseUrl: process.env.RELAY_BASE_URL || RELAYCAST_BASE_URL }) const ws = await setup.createWorkspace({ name: `pear-eval-${Date.now().toString(36)}` }) _cachedWorkspaceKey = ws.apiKey return _cachedWorkspaceKey diff --git a/package-lock.json b/package-lock.json index c26b1e9b..23159024 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,20 +8,21 @@ "name": "pear-by-agent-relay", "version": "1.0.0", "dependencies": { - "@agent-relay/cloud": "^8.7.2", - "@agent-relay/factory": "^0.1.1", - "@agent-relay/harness-driver": "^8.3.7", - "@agent-relay/integration-prompts": "^8.8.4", - "@agent-relay/sdk": "^8.3.7", + "@agent-relay/cloud": "^9.1.6", + "@agent-relay/factory": "^0.1.13", + "@agent-relay/harness-driver": "^9.1.6", + "@agent-relay/integration-prompts": "^9.1.6", + "@agent-relay/sdk": "^9.1.6", "@agentworkforce/deploy": "^4.0.2", - "@relayburn/sdk": "^3.3.0", - "@relayfile/sdk": "^0.8.29", + "@relayburn/sdk": "^4.0.0", + "@relaycast/sdk": "^5.0.7", + "@relayfile/sdk": "^0.10.15", "@tanstack/react-virtual": "^3.14.2", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", - "agent-relay": "^8.3.7", + "agent-relay": "^9.1.6", "agentworkforce": "^4.0.2", "ai-hist": "^0.2.3", "allotment": "^1.0.9", @@ -40,7 +41,7 @@ "pear": "bin/pear.mjs" }, "devDependencies": { - "@agent-relay/evals": "^8.8.4", + "@agent-relay/evals": "^9.1.6", "@playwright/test": "^1.57.0", "@tailwindcss/vite": "^4.0.0", "@testing-library/react": "^16.3.2", @@ -153,9 +154,9 @@ "integrity": "sha512-h6YkyIl0DZSIeVTqgPZGgN+E2I0COcF7XjljVKGzZGLUKzmBKhLROeyiy9efz/YIpApat3xjLZ4Ac20+EsydTA==" }, "node_modules/@agent-relay/broker-darwin-arm64": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-arm64/-/broker-darwin-arm64-8.8.1.tgz", - "integrity": "sha512-RzJuWK+ajn09usi6Fq+Zkq+pkYajBwy9IQI+kPRc7DeimJbcVW8P4smo7zYmHKX4YJ7jghn7X8ltwMv/k09wLg==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-arm64/-/broker-darwin-arm64-9.1.6.tgz", + "integrity": "sha512-BqiyMpdjM2CAhg4ZdNYPYpxmSNVQZu0Vss/1TPPe7OJ9tQAdyrq0QLWBOgqeS/fuvyVz6NK5z8N3GYa8wxk6LA==", "cpu": [ "arm64" ], @@ -166,9 +167,9 @@ ] }, "node_modules/@agent-relay/broker-darwin-x64": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-x64/-/broker-darwin-x64-8.8.1.tgz", - "integrity": "sha512-Iwc92XZJ6ABWr8gol41FDLk/mgP/dMzBmLpUR0gag78WlzSpxf5vHhr1mSUfk8LuRXka14FRGEJ7JwCOQ4P9Og==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-x64/-/broker-darwin-x64-9.1.6.tgz", + "integrity": "sha512-D+5lfcUK5IW0D3FbBLpCIsCbnS0IMY3MVQqZOpYEL3Cq8pbf0hWII+xlQZvolFvol/CKHYk43lmoJebPIdeaFQ==", "cpu": [ "x64" ], @@ -179,9 +180,9 @@ ] }, "node_modules/@agent-relay/broker-linux-arm64": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-arm64/-/broker-linux-arm64-8.8.1.tgz", - "integrity": "sha512-3edxTajEmL8mDrltVeF0XjSijRiF6+F/dxC2KRFFIqos22sniulUxiKzoD46/hY1qFWhqHSRjNAhUnONFKtmZQ==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-arm64/-/broker-linux-arm64-9.1.6.tgz", + "integrity": "sha512-guKf/+dQfX1YP2v69SnVNgfnoWXPSZ8MgPoRv/4X8WUQPKou/WZk64SMUodH5LYvuj1uqFoYqWYZoxogrAQ45Q==", "cpu": [ "arm64" ], @@ -192,9 +193,9 @@ ] }, "node_modules/@agent-relay/broker-linux-x64": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-x64/-/broker-linux-x64-8.8.1.tgz", - "integrity": "sha512-GOguIz/rxFOxLDIrPV7UqRs3sHRpD93rf06Px60TC7tVDuKVnUEXo7Se/QK6bywgFA6XeJc4COY6zVRedJILsA==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-x64/-/broker-linux-x64-9.1.6.tgz", + "integrity": "sha512-HACxKqgX7thqgmyXpmUt6hZgncyxwQwNW7UJ7JzNp4h6eMcjin28RwK/5CCmefKBXthdAeSYazOPwZsC7BZcEQ==", "cpu": [ "x64" ], @@ -205,9 +206,9 @@ ] }, "node_modules/@agent-relay/broker-win32-x64": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-win32-x64/-/broker-win32-x64-8.8.1.tgz", - "integrity": "sha512-KNYCcer1C/QT6zFi5bx5SfQZow/Q9vNPPe8aPI+XgLG+L3SJ8JCW0hudwOEhse8qyIo9PWHR+gMn3AvJJEw31w==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-win32-x64/-/broker-win32-x64-9.1.6.tgz", + "integrity": "sha512-TCC01e2PQB0LDbr5eP412vOVkk+nnPcrYDU8ba8SqDojRI1RhQ2fOthabkJLw2X5FydYQtrgmOyo5q8rjzdFgg==", "cpu": [ "x64" ], @@ -218,11 +219,11 @@ ] }, "node_modules/@agent-relay/cloud": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/cloud/-/cloud-8.8.1.tgz", - "integrity": "sha512-tH1WfX1hdPa4egCNqfkKREQdxmjzlk5VexPXq0aM4KxAnpEIVEHxBDBijDrnq1Vo/RxyWoJ2+dESfOONRv0s8A==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/cloud/-/cloud-9.1.6.tgz", + "integrity": "sha512-1F2yHEs3f6t18CmXDVpcmIee6/ANBNMoarzPukC246bL4BfaRKpqTS/DoqtaJYY8mv2yBVt86f/cy88R0qO55Q==", "dependencies": { - "@agent-relay/config": "8.8.1", + "@agent-relay/config": "9.1.6", "@aws-sdk/client-s3": "3.1020.0", "ignore": "^7.0.5", "tar": "^7.5.10" @@ -232,121 +233,23 @@ } }, "node_modules/@agent-relay/config": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/config/-/config-8.8.1.tgz", - "integrity": "sha512-CzxxJGH/QnoT6qEMeL7hLNeQG/wHF60NODddGMOs0fMwKUKMhpKMq3G0fswN1LRNJqnjg7J/ZNZMIM/OL6uZLg==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/config/-/config-9.1.6.tgz", + "integrity": "sha512-ssBZR8DvvKiXO9TYC32DaoW+YGx4Anjz2WbRia+NH7fxRz2TXuAaD29Eb+/nOjVNrfgOtEMzHR9Zqz4dWmSU6w==", "dependencies": { "zod": "^3.23.8", "zod-to-json-schema": "^3.23.1" } }, "node_modules/@agent-relay/evals": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/evals/-/evals-8.8.4.tgz", - "integrity": "sha512-ruoDdV4Z2rKb5SeRe5lbLjUdt8t3jZB/y4KXPujwJRPNQuV6u/n615tHdabhAY0WenqoAMTSOPRhWfnVhJxIew==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/evals/-/evals-9.1.6.tgz", + "integrity": "sha512-zc/Am+ck+9tbzglChqrZQlqkD8MEFWUfy0RRJOPRkpoRUTbV/Z9MloRSmjafwp0/jQaY7KUbC3QTXGmQ4jxCoA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@agent-relay/harness-driver": "8.8.4", - "@agent-relay/integration-prompts": "8.8.4" - } - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/broker-darwin-arm64": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-arm64/-/broker-darwin-arm64-8.8.4.tgz", - "integrity": "sha512-xyF4C/0KeTaAC3QGNcPGwkJM9hzfCXBF1LTx9C1MK3oyHXrK+f21b2IucK7t0e1s7k8B+IQ12RMJ0TtC0UOftg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/broker-darwin-x64": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-x64/-/broker-darwin-x64-8.8.4.tgz", - "integrity": "sha512-TrB4M3Cm0bAjOGjIJz7rWOgGwEKz3u/YqIfkGD44wH1ByUHqCepwNH+N4DUDgPYhlq0mu7Gdvu2wNmcAxAlFsA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/broker-linux-arm64": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-arm64/-/broker-linux-arm64-8.8.4.tgz", - "integrity": "sha512-BI5SA+JXp0kItLjdBMkH0e91CrnACK+RB+N2h9K+ccSYpqEzTAlkXDmROdlt+4dFJuyGKvRGdCA9cnfR7DTxtg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/broker-linux-x64": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-x64/-/broker-linux-x64-8.8.4.tgz", - "integrity": "sha512-ZIhO0padIEV4P4M19aPrRsU7V773YicaerGBQEiCEGeBKR3fUwi8jYnGaFdsm/eGLqpw3tp4A/vWsVDsmQMlng==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/broker-win32-x64": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/broker-win32-x64/-/broker-win32-x64-8.8.4.tgz", - "integrity": "sha512-TMTlPCsp9CkF5EPewQteuH4BylF/G0jBU61r4qJZjImtZqsj6heb0kvhFvBAzD1gO46S+QKFniE6a0Xd0htJNg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/harness-driver": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/harness-driver/-/harness-driver-8.8.4.tgz", - "integrity": "sha512-fb3nv2vmaKaMjujoyk2c9Iputs4AAE06Cd5ZF9f6lOiL4nrxzfthZ0fd56KIUT2RTMbarpYjxCOs0yTUuRwm0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@agent-relay/sdk": "8.8.4", - "ws": "^8.18.3", - "zod": "^3.23.8" - }, - "optionalDependencies": { - "@agent-relay/broker-darwin-arm64": "8.8.4", - "@agent-relay/broker-darwin-x64": "8.8.4", - "@agent-relay/broker-linux-arm64": "8.8.4", - "@agent-relay/broker-linux-x64": "8.8.4", - "@agent-relay/broker-win32-x64": "8.8.4" - } - }, - "node_modules/@agent-relay/evals/node_modules/@agent-relay/sdk": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/sdk/-/sdk-8.8.4.tgz", - "integrity": "sha512-ATW6DZbTIHCakOjTmj+VsACRGzYBupqLVbkojfbdo+DSuNaWzp2EVMa0Wr/4NFKXZx8zZyT8HeElkPvY6sf5cQ==", - "dev": true, - "dependencies": { - "@relaycast/sdk": "^4.0.0" + "@agent-relay/harness-driver": "9.1.6", + "@agent-relay/integration-prompts": "9.1.6" } }, "node_modules/@agent-relay/events": { @@ -389,15 +292,17 @@ } }, "node_modules/@agent-relay/factory": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@agent-relay/factory/-/factory-0.1.1.tgz", - "integrity": "sha512-Qzdk5VbOVV+1nVn+ISiz/+icC+YyGNMOO61E9PIw8w+wQ2ufM/+f40qO2SNv1WSI3/upC0FSSc3IB62EgYlUvQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@agent-relay/factory/-/factory-0.1.13.tgz", + "integrity": "sha512-mdoC/5mqWNsrtWUWpanp1FMo5EhShH8113bpZAl9lw0Xj2TEKHtYlI7Y6rn1I2TqWaV89vuAhHJ8S5lCe+Clig==", "license": "Apache-2.0", "dependencies": { - "@agent-relay/cloud": "^8.7.2", - "@agent-relay/harness-driver": "^8.3.7", - "@relayfile/sdk": "^0.8.29", - "agent-relay": "^8.8.1", + "@agent-relay/cloud": "^9.0.2", + "@agent-relay/fleet": "^9.0.2", + "@agent-relay/harness-driver": "^9.0.2", + "@agent-relay/integration-prompts": "^9.0.2", + "@relayfile/sdk": "^0.10.9", + "agent-relay": "^9.0.1", "zod": "^3.25.76" }, "bin": { @@ -408,65 +313,65 @@ } }, "node_modules/@agent-relay/fleet": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/fleet/-/fleet-8.8.1.tgz", - "integrity": "sha512-zP8eUPQ5ikOMRrq59wRkYmj5wEOpLmDnchNt4vGRmrm1eNozDTZCmCJHEYnlYLk2xf4QZDaJ+5IbnMNzgOVxsg==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/fleet/-/fleet-9.1.6.tgz", + "integrity": "sha512-wAEaJuL+hYlk9zvkE89wZ/AtqxzVYGtETpUDvnXAYvLKCFp5wGFDaGnUmeVLdWNRBYdV+RYD7ggpnpxHkw0jsA==", "license": "Apache-2.0", "dependencies": { - "@agent-relay/harness-driver": "8.8.1", - "@agent-relay/harnesses": "8.8.1", - "@agent-relay/sdk": "8.8.1", + "@agent-relay/harness-driver": "9.1.6", + "@agent-relay/harnesses": "9.1.6", + "@agent-relay/sdk": "9.1.6", "zod": "^3.23.8" } }, "node_modules/@agent-relay/harness-driver": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/harness-driver/-/harness-driver-8.8.1.tgz", - "integrity": "sha512-hUYNx1RsK/fiLfkUcG2/oTHIViHYHrYFZ/rPCCJuj+3pzAlQqj815wKRp/iD8extMWLpzyiTm9kU5klcyBXpZw==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/harness-driver/-/harness-driver-9.1.6.tgz", + "integrity": "sha512-3lZKbkeFLm9nQ4jxrTOG1vmpMTKoCLhLLh4jrhJNO5PzWw2CJ218gUGB4U+2EWpnB+vVT8CtPzardWlqgUsZ4g==", "license": "Apache-2.0", "dependencies": { - "@agent-relay/sdk": "8.8.1", + "@agent-relay/sdk": "9.1.6", "ws": "^8.18.3", "zod": "^3.23.8" }, "optionalDependencies": { - "@agent-relay/broker-darwin-arm64": "8.8.1", - "@agent-relay/broker-darwin-x64": "8.8.1", - "@agent-relay/broker-linux-arm64": "8.8.1", - "@agent-relay/broker-linux-x64": "8.8.1", - "@agent-relay/broker-win32-x64": "8.8.1" + "@agent-relay/broker-darwin-arm64": "9.1.6", + "@agent-relay/broker-darwin-x64": "9.1.6", + "@agent-relay/broker-linux-arm64": "9.1.6", + "@agent-relay/broker-linux-x64": "9.1.6", + "@agent-relay/broker-win32-x64": "9.1.6" } }, "node_modules/@agent-relay/harnesses": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/harnesses/-/harnesses-8.8.1.tgz", - "integrity": "sha512-FAAmSPV7NnDz5rd56Xf+GNsgpnTabC4rqAO9Zvi1p+Su5lRN1iQGJFmSun6PlmiBvXCCn3N2iuGgC54ZBGZNsw==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/harnesses/-/harnesses-9.1.6.tgz", + "integrity": "sha512-ZZh1cRoWUZLQinhKk/VlLUJzvy5IM6PTD+z3d4uUG3rRImWLSJF+iCQxiFfTzGsnz4yU03hJYNTJeL8W9QmMTw==", "license": "Apache-2.0", "dependencies": { - "@agent-relay/harness-driver": "8.8.1", - "@agent-relay/sdk": "8.8.1" + "@agent-relay/harness-driver": "9.1.6", + "@agent-relay/sdk": "9.1.6" } }, "node_modules/@agent-relay/integration-prompts": { - "version": "8.8.4", - "resolved": "https://registry.npmjs.org/@agent-relay/integration-prompts/-/integration-prompts-8.8.4.tgz", - "integrity": "sha512-+S5hL3DscnxYZPxlOEISTeFC6fMf7TQT0H30mY6HvDmt7AV0rF+HqwPbtpoUkgJwP6iC+e/xti43JcSN132deg==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/integration-prompts/-/integration-prompts-9.1.6.tgz", + "integrity": "sha512-iboLPz3ZBd231DfL93nGPk3eQw5epn0cnhPKxit3epevpBQGNY8h+jBpv1pjTVqwMyQ9vLUK5xATKIhvRtf9sg==", "license": "Apache-2.0" }, "node_modules/@agent-relay/sdk": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/sdk/-/sdk-8.8.1.tgz", - "integrity": "sha512-UTy2RmM0Zwatq8hw+W6TmL2Wh/fvmXKmz+KEKrUnOifDDqymw4Z/Oz/RKYJp+3UoS14/E5MNVQWKLDWLPjY3og==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/sdk/-/sdk-9.1.6.tgz", + "integrity": "sha512-70VRkM/6KIyMIXR3NETDM9u4R1MWKRyg1V69EJQVBYSLM0+rOr6796ZZhSdlmriv+YiGBvsew5vet0ZT2NmRsw==", "dependencies": { - "@relaycast/sdk": "^4.0.0" + "@relaycast/sdk": "^5.0.5" } }, "node_modules/@agent-relay/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@agent-relay/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-YE4UJ0ue1V2xYtMKDImFED3WJCxgyxOR7NpfDcy+WrHj7NRnJXTsEFeehPh3cSXUsfRcQ7Y7W2tFA1yLraFCfA==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/@agent-relay/utils/-/utils-9.1.6.tgz", + "integrity": "sha512-zh0G6hdQQf7+vb5cxEbKftu1N5qCLpifnfZkIdbmIj21sGOHXHeY6CQEDVhPo9+NXO8sXr/B+vxNHtbkRVxt2w==", "dependencies": { - "@agent-relay/config": "8.8.1", + "@agent-relay/config": "9.1.6", "compare-versions": "^6.1.1" } }, @@ -4832,38 +4737,38 @@ } }, "node_modules/@posthog/core": { - "version": "1.30.5", - "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.30.5.tgz", - "integrity": "sha512-NGBRhqyMOpiSKd8xtg8dckUxh+kB02LdtnxKYW7ngycJmgM4GBGzSDaATbMQDKaNS9m2FJ9QRZ8nJyelGCoqNQ==", + "version": "1.38.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.38.0.tgz", + "integrity": "sha512-QLrJh0hMVpEJXHNyiJR9YhvIO5tzLDedw4UHdvB+ub4fXHMtHCA8H44qgl11HWR3ajpjxtJ8hs3HyOGVF3Zc1w==", "license": "MIT", "dependencies": { - "@posthog/types": "1.379.2" + "@posthog/types": "^1.391.1" } }, "node_modules/@posthog/types": { - "version": "1.379.2", - "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.379.2.tgz", - "integrity": "sha512-quGXOeNewmaGqTxE/eH8MgM/niW1jfgCyzh4bwpXFpDwuQPvWA8WkCFo6k1oKGYV03Go0udw49aGUz6s58cRsA==", + "version": "1.391.1", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.391.1.tgz", + "integrity": "sha512-ASwd7Nf4pViqdYRYaNRyPYRVKWa1CcHUAUWR0XeQJLGdNnsWACBwe0sSieb/cHnKsRXjRwO/23KIY83lm/Ccpw==", "license": "MIT" }, "node_modules/@relayburn/sdk": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@relayburn/sdk/-/sdk-3.3.0.tgz", - "integrity": "sha512-9Ccl/jcFdcGJu0Sl5m5XxJjhngRefevS5TQtdAQl1RBX+HpIdb8JCn/RfmM/tnrmD+ae03Je+4RnivHZ7YA7KA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@relayburn/sdk/-/sdk-4.0.0.tgz", + "integrity": "sha512-DBf2xE2hoYIqDrL7rH22R6whsP+1+BQPc3CZUPYfy82Z08S4zd7eQEkNkKLD6oyd2Dm97gl16yOZNZnDNs/0tg==", "engines": { "node": ">=22" }, "optionalDependencies": { - "@relayburn/sdk-darwin-arm64": "3.3.0", - "@relayburn/sdk-darwin-x64": "3.3.0", - "@relayburn/sdk-linux-arm64-gnu": "3.3.0", - "@relayburn/sdk-linux-x64-gnu": "3.3.0" + "@relayburn/sdk-darwin-arm64": "4.0.0", + "@relayburn/sdk-darwin-x64": "4.0.0", + "@relayburn/sdk-linux-arm64-gnu": "4.0.0", + "@relayburn/sdk-linux-x64-gnu": "4.0.0" } }, "node_modules/@relayburn/sdk-darwin-arm64": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@relayburn/sdk-darwin-arm64/-/sdk-darwin-arm64-3.3.0.tgz", - "integrity": "sha512-KqjqsxG+FT267hwubbWidyg8OhZr+C331RlXJoxCLIUHseCd2qDqmhk85g23qr4moMXYzkER8Fl9UoblOQDRQw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@relayburn/sdk-darwin-arm64/-/sdk-darwin-arm64-4.0.0.tgz", + "integrity": "sha512-l2E0SDcAkE+zd1apraRrVjIomBQS9x3Bl49CY7VfR6ZJF0EWQwQpD7zoAke2FkdpfHWEL/oJuleoGoojem+GOQ==", "cpu": [ "arm64" ], @@ -4876,9 +4781,9 @@ } }, "node_modules/@relayburn/sdk-darwin-x64": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@relayburn/sdk-darwin-x64/-/sdk-darwin-x64-3.3.0.tgz", - "integrity": "sha512-VI/JEPaLERlZRR6lVNkyhO1ZgnC4S726/SvMlxEihUJ2yzZCm6QM+oEcsh9U6r/jzQgAg8ozj+sFSVfJkKn9UA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@relayburn/sdk-darwin-x64/-/sdk-darwin-x64-4.0.0.tgz", + "integrity": "sha512-IjfsojAG+Gv84J8a8gNpHabmZpVkXY/sy5r7fimvaaKL3iSW5IxXYfHvCwWUIOAbybebn7BqwIqKwhecpjBENw==", "cpu": [ "x64" ], @@ -4891,9 +4796,9 @@ } }, "node_modules/@relayburn/sdk-linux-arm64-gnu": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@relayburn/sdk-linux-arm64-gnu/-/sdk-linux-arm64-gnu-3.3.0.tgz", - "integrity": "sha512-i+Zmb3YO1OoFQaYv6lzLTjVj9n36emYzQV2x98wJAPO/dJpr9rBdoS1d2Eo/YRtyll/f7MtoW9BylOexW8uq4A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@relayburn/sdk-linux-arm64-gnu/-/sdk-linux-arm64-gnu-4.0.0.tgz", + "integrity": "sha512-fFGHoM5LVO/P5MOgwYzJQpEt+FyGHkh9JK2kS8upv+xaLlq4T1zRbtqhO/nZcFq4RFh7H/QSyBthJ85VCvaBnw==", "cpu": [ "arm64" ], @@ -4906,9 +4811,9 @@ } }, "node_modules/@relayburn/sdk-linux-x64-gnu": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@relayburn/sdk-linux-x64-gnu/-/sdk-linux-x64-gnu-3.3.0.tgz", - "integrity": "sha512-9DEe62UlziEnk7P3bxtZ4B1LEI8+Cw99/WPEptigFVipQ4ruiPpUpPi098xUkjfm4Q1mCjHpROqOJ/4VVJYEOA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@relayburn/sdk-linux-x64-gnu/-/sdk-linux-x64-gnu-4.0.0.tgz", + "integrity": "sha512-2NIqNNuHjIGzu/mDTv+1wOLbouipFhsjAnqMFIJL6yOi4hE1YH7sEdGBazDKPlrR3RjliBnbjUN4EzVNvIxohA==", "cpu": [ "x64" ], @@ -4921,11 +4826,11 @@ } }, "node_modules/@relaycast/sdk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.0.0.tgz", - "integrity": "sha512-x6aHC0w2tIorE/gtU5iBc6bHUj0vmYySIye8NFg4aC8jbFwELRoy1xKtlYgiwmSK23eZ/zZ9xxMeI33i7OI+7Q==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-5.0.7.tgz", + "integrity": "sha512-Ob4qt4MU0UOQGYRMyN8GmYOhgs733EAg6nBzuGAgKAyJaxRP9coYZ/Bp8fEJYahwwnOZarSooqh+hv3k/grQeQ==", "dependencies": { - "@relaycast/types": "4.0.0", + "@relaycast/types": "5.0.7", "zod": "^4.3.6" } }, @@ -4939,9 +4844,9 @@ } }, "node_modules/@relaycast/types": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.0.0.tgz", - "integrity": "sha512-ag9GYFe/1jsWhDpWdUW8gZwdr0wJ9/jj/aq4oNHQVOFmjJERN3gM0BX0o2c/iWnl0VS4lWjEnC04fH9/QbxZQQ==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-5.0.7.tgz", + "integrity": "sha512-La4NKQ6b9cK2OZS7j6ingT3JgHlGrhE6rjKwqoA4YWTuNU7PfD3ey+5Ki4yNcE7QOmK3yIwmVxdTGHZtoLSubw==", "dependencies": { "zod": "^4.3.6" } @@ -4977,10 +4882,10 @@ } }, "node_modules/@relayfile/core": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.8.29.tgz", - "integrity": "sha512-r+C2wcApAmhEJ5/eUanybLo+KYMqMbJMSdbDNB/VKptiAmABKx4X1DmwYbXAgZX14tmeEG4uTkJOJ1jZXZB0mA==", - "license": "MIT", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.10.15.tgz", + "integrity": "sha512-WSWGyRw+frNyIB7JcjPBdyJvC7SP8OkWP9jhnsDgN+XRkEZ0VxYRhFtsEgob5sCaKNbEJscylnEYNgtBkY8zKQ==", + "license": "Apache-2.0", "engines": { "node": ">=18" } @@ -4999,64 +4904,64 @@ } }, "node_modules/@relayfile/mount-darwin-arm64": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.8.29.tgz", - "integrity": "sha512-BuxmPfMz/kTGPrb5dblwHf7Q3w08t2edbtNpfKjd7DvNOLiltRz8Dek5hw0ogeWU3GeQUQYmgGFE38XvpsAKCA==", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.10.15.tgz", + "integrity": "sha512-OEMtA05gOh8aabH1kA83gQyIUHaFoaXdpTNgCy7pwDoWFG8+iWDi4rLrnmk+itmnP2sKnQsf0ZlW2AEmt7rGyw==", "cpu": [ "arm64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "darwin" ] }, "node_modules/@relayfile/mount-darwin-x64": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.8.29.tgz", - "integrity": "sha512-hC8Ym8Izwur1Et0of2J+AxiVxEHzoXvL01oMhLwCQC/EVZwCjy5AcyrU4d0jc7pOck7rRQ4ga5W/gXE9DM9sYQ==", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.10.15.tgz", + "integrity": "sha512-zGad6wHmpqPCXEL8fqvhWUq4LdllUdpO9J2xpGQ5KYtupPdH7E7YzDtSD8baiOpTtGvewIZsA/mXS21w7g/5xA==", "cpu": [ "x64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "darwin" ] }, "node_modules/@relayfile/mount-linux-arm64": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.8.29.tgz", - "integrity": "sha512-Rm+r1LCcwNR7o8Hj12xXIjDjw2lLsFBk6XNpfSG/fu0kgihQibpEvMp6ZCkli4xn6XM6T9uZT/e7s6XKv2e4mQ==", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.10.15.tgz", + "integrity": "sha512-wPkBWr9fAw2n2pFoiHcAAspcQKWYqMEZiMKEdxPHLtZ1MV/sz+2tRz68dLXqEoRreMTUEJyWkEvl+7hnA7rfcw==", "cpu": [ "arm64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ] }, "node_modules/@relayfile/mount-linux-x64": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.8.29.tgz", - "integrity": "sha512-tiWdfM2/jIRsL/mMmfsjJLKZl1SSLSANglQJUXMa8eANTwB/ItxJ5mwUz7ryuWoBSB+43beWK8Euj2zHDDlicQ==", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.10.15.tgz", + "integrity": "sha512-O/CraCwlTaZuOD1ls4uoHAz3cqMKjJ2jJQKcc29+1HULyxre0a4FALqByU1OiiIdMScRS3Mx9tfI2EMmcJK5bQ==", "cpu": [ "x64" ], - "license": "MIT", + "license": "Apache-2.0", "optional": true, "os": [ "linux" ] }, "node_modules/@relayfile/sdk": { - "version": "0.8.29", - "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.8.29.tgz", - "integrity": "sha512-6iXUtr2muhLm1b5sRSe1Qu2hVe49omv0v9Xj57kadKIdY/353IRAy8VjbWAK/8aHWzdGQuMJqtR4VeA4f42+gg==", - "license": "MIT", + "version": "0.10.15", + "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.10.15.tgz", + "integrity": "sha512-kZX8CN0h+whK9ivG1K9u1+f1KQq+kUHPOjo92B4cf4WxwkV2L5/5xN8lw3jM/+ljTFkexMCwU5qJm0Bp+H9gdQ==", + "license": "Apache-2.0", "dependencies": { - "@relayfile/core": "0.8.29", + "@relayfile/core": "0.10.15", "ignore": "^7.0.5", "tar": "^7.5.10" }, @@ -5064,16 +4969,16 @@ "node": ">=18" }, "optionalDependencies": { - "@relayfile/mount-darwin-arm64": "0.8.29", - "@relayfile/mount-darwin-x64": "0.8.29", - "@relayfile/mount-linux-arm64": "0.8.29", - "@relayfile/mount-linux-x64": "0.8.29" + "@relayfile/mount-darwin-arm64": "0.10.15", + "@relayfile/mount-darwin-x64": "0.10.15", + "@relayfile/mount-linux-arm64": "0.10.15", + "@relayfile/mount-linux-x64": "0.10.15" } }, "node_modules/@relayflows/browser-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@relayflows/browser-primitive/-/browser-primitive-1.0.1.tgz", - "integrity": "sha512-GHjIWI0i2SdgrE6cxn0xAJWUTwSxeYp4nm6whxSZ1an8DrF5kvGnyokuuaLRV86LGwiLo9hBmOyt+Ml5r7fbBA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@relayflows/browser-primitive/-/browser-primitive-1.0.4.tgz", + "integrity": "sha512-ZR8xGIeodYeEdnlMwZchQgIhcJxDTMCoy2FylpMgyJkhNmraSL/OTGHQyE5IgYngGMFCaujftxzlAufI7rGVvA==", "dependencies": { "@agent-relay/sdk": "^8.2.0", "playwright": "^1.51.1" @@ -5082,12 +4987,46 @@ "agent-relay-browser-mcp": "dist/mcp-server.js" } }, + "node_modules/@relayflows/browser-primitive/node_modules/@agent-relay/sdk": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/sdk/-/sdk-8.9.2.tgz", + "integrity": "sha512-jd/X+1NKS7yWxyZun1UtKuAZMk5LvGZjJ4U1nIhZ0e9mi+5LxZa31BIHv2DgXc8PUJMO2OsWAcdX/hDDK30CHQ==", + "dependencies": { + "@relaycast/sdk": "^4.1.2" + } + }, + "node_modules/@relayflows/browser-primitive/node_modules/@relaycast/sdk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.2.0.tgz", + "integrity": "sha512-nqYJ4ZY/SJWOpd2fI/Tottz/c2y8DzvHGmLGH+speEN3z7sjRaaJB4C90favKFCFIbbpGRChs2KrvujOnHccdQ==", + "dependencies": { + "@relaycast/types": "4.2.0", + "zod": "^4.3.6" + } + }, + "node_modules/@relayflows/browser-primitive/node_modules/@relaycast/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.2.0.tgz", + "integrity": "sha512-3SA7Igs1H3fTrgxOZWJmD0timj3WeDsjqpmKiJazhX75CGcyjmvpKYaxFupyoFO687HRwi8VpBVq3v+dNhSrOQ==", + "dependencies": { + "zod": "^4.3.6" + } + }, + "node_modules/@relayflows/browser-primitive/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@relayflows/cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@relayflows/cli/-/cli-1.0.1.tgz", - "integrity": "sha512-qc14FODCSP5jCs5GsWTYyJ8qPDtM3Ax4qF8nfPB7ixpoFRk+h3BnlklGSo3Aba5T5oxY+FDRnJzbl/olbwn6EA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@relayflows/cli/-/cli-1.0.4.tgz", + "integrity": "sha512-HmZdZJBauSBMCywh0W3z3Oal1Tvg3ZhVMqWn7oDyWC8fwmA0gPNTKnsKo2weak/6iOHx0lY/orapMsgv2ecJOg==", "dependencies": { - "@relayflows/core": "1.0.1", + "@relayflows/core": "1.0.4", "commander": "^12.1.0" }, "bin": { @@ -5095,9 +5034,9 @@ } }, "node_modules/@relayflows/core": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@relayflows/core/-/core-1.0.1.tgz", - "integrity": "sha512-AqlzwmiirwjmB96tZLQQApjNwHV10OemJPWoHj4YIWe9vR7cD9kT3UHaQ8qTFMrHlfbcM+qujErUBYwtKMM9Rg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@relayflows/core/-/core-1.0.4.tgz", + "integrity": "sha512-MTeJEqh57qxtWgjAQDwhIqM9kqhaJ6FBOEgg3TB93lXzYysdh0XHDHQMaz6X1gn6aqd00sp7gE6vzi1Wh1vUeg==", "dependencies": { "@agent-relay/cloud": "^8.2.0", "@agent-relay/config": "^8.2.0", @@ -5106,9 +5045,9 @@ "@agent-relay/sdk": "^8.2.0", "@relaycast/sdk": "^1.1.0", "@relayfile/sdk": "^0.8.0", - "@relayflows/browser-primitive": "1.0.1", - "@relayflows/github-primitive": "1.0.1", - "@relayflows/slack-primitive": "1.0.1", + "@relayflows/browser-primitive": "1.0.4", + "@relayflows/github-primitive": "1.0.4", + "@relayflows/slack-primitive": "1.0.4", "@sinclair/typebox": "^0.34.48", "agent-trajectories": "^0.6.0", "chalk": "^4.1.2", @@ -5119,6 +5058,156 @@ "zod": "^3.23.8" } }, + "node_modules/@relayflows/core/node_modules/@agent-relay/broker-darwin-arm64": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-arm64/-/broker-darwin-arm64-8.9.2.tgz", + "integrity": "sha512-N6NkQ9PafZge+YpIOChg+0hJ8ALVHGoqwZrWwO9BmH83ORz9JiTSAG+F1ygdnJVGJtjTabvPxigwQGE82vBzjQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/broker-darwin-x64": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-darwin-x64/-/broker-darwin-x64-8.9.2.tgz", + "integrity": "sha512-uHKwnh+fTkWobGSOdSeLJdUIDAvkyOjwCeK3niqOK5duh7bbVY27OY10VjUX+sI3JvYMWhKM8KJhtY7pmuWGiw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/broker-linux-arm64": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-arm64/-/broker-linux-arm64-8.9.2.tgz", + "integrity": "sha512-gqklbEYJxrJdl94U5PAGSRHUB9st7Qy1scv3NfmUy28d2+WG6YlbdKn+Y/1+lCbgRDMbcuPeFG5ibzYj7a1Phg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/broker-linux-x64": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-linux-x64/-/broker-linux-x64-8.9.2.tgz", + "integrity": "sha512-L35sbjP6sx1KpOq7ZG9TmhmqUELE09jY2PHMkETg1B+AN9Eai43P3hpUHpS9U/AdZS00NihdJ3ell3Tlv88tXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/broker-win32-x64": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/broker-win32-x64/-/broker-win32-x64-8.9.2.tgz", + "integrity": "sha512-+an1a8fhtlKYYKUk6HIe3YqkCDGUJZDO0vt7Bh4mXWuo+oInIyDH1oM1p7RABfDGnypTpIGt9nyLbiagkQsW2Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/cloud": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/cloud/-/cloud-8.9.2.tgz", + "integrity": "sha512-yvuf8OZ+JuTLDNL5XEhVPtFgxgTvsSOSsCBV9UkZ/WgoA/zmYIWppGVla5F49882aLmBdqpHPyhx8sL7PmYlrg==", + "dependencies": { + "@agent-relay/config": "8.9.2", + "@aws-sdk/client-s3": "3.1020.0", + "ignore": "^7.0.5", + "tar": "^7.5.10" + }, + "optionalDependencies": { + "ssh2": "^1.17.0" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/config": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/config/-/config-8.9.2.tgz", + "integrity": "sha512-6jE9ItFCMzk2gJmX3djYadXc/E7Hsvz20EKJNZ5yZZLz2+1D5ihOa+AxVtGJCUB/Nnh3DtJbLS0rsJ7Gm6aiwQ==", + "dependencies": { + "zod": "^3.23.8", + "zod-to-json-schema": "^3.23.1" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/harness-driver": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/harness-driver/-/harness-driver-8.9.2.tgz", + "integrity": "sha512-yCwQqcsNM4VAIohNlVfkLoZdoclKT2tyom1QDlnyqo057E7vaSMJenpY+hZnt+vEiVNK7iIPcUvca+eD/np6vw==", + "license": "Apache-2.0", + "dependencies": { + "@agent-relay/sdk": "8.9.2", + "ws": "^8.18.3", + "zod": "^3.23.8" + }, + "optionalDependencies": { + "@agent-relay/broker-darwin-arm64": "8.9.2", + "@agent-relay/broker-darwin-x64": "8.9.2", + "@agent-relay/broker-linux-arm64": "8.9.2", + "@agent-relay/broker-linux-x64": "8.9.2", + "@agent-relay/broker-win32-x64": "8.9.2" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/harnesses": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/harnesses/-/harnesses-8.9.2.tgz", + "integrity": "sha512-7EmDzXeC60qswNtTijbS2NU51AtxuKgPrjLJ/yIRo1JZ7nqEFdDEjH3s69RXMnb0T/wqlzGY+E0ccd808/fCVA==", + "license": "Apache-2.0", + "dependencies": { + "@agent-relay/harness-driver": "8.9.2", + "@agent-relay/sdk": "8.9.2" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/sdk": { + "version": "8.9.2", + "resolved": "https://registry.npmjs.org/@agent-relay/sdk/-/sdk-8.9.2.tgz", + "integrity": "sha512-jd/X+1NKS7yWxyZun1UtKuAZMk5LvGZjJ4U1nIhZ0e9mi+5LxZa31BIHv2DgXc8PUJMO2OsWAcdX/hDDK30CHQ==", + "dependencies": { + "@relaycast/sdk": "^4.1.2" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/sdk/node_modules/@relaycast/sdk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-4.2.0.tgz", + "integrity": "sha512-nqYJ4ZY/SJWOpd2fI/Tottz/c2y8DzvHGmLGH+speEN3z7sjRaaJB4C90favKFCFIbbpGRChs2KrvujOnHccdQ==", + "dependencies": { + "@relaycast/types": "4.2.0", + "zod": "^4.3.6" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/sdk/node_modules/@relaycast/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@relaycast/types/-/types-4.2.0.tgz", + "integrity": "sha512-3SA7Igs1H3fTrgxOZWJmD0timj3WeDsjqpmKiJazhX75CGcyjmvpKYaxFupyoFO687HRwi8VpBVq3v+dNhSrOQ==", + "dependencies": { + "zod": "^4.3.6" + } + }, + "node_modules/@relayflows/core/node_modules/@agent-relay/sdk/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@relayflows/core/node_modules/@relaycast/sdk": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@relaycast/sdk/-/sdk-1.2.0.tgz", @@ -5154,15 +5243,96 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/@relayflows/core/node_modules/@relayfile/core": { + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@relayfile/core/-/core-0.8.30.tgz", + "integrity": "sha512-2jvy1uj5594Yje2ASBKpTH2MHcYOz02oEDicSqnXNt7bf5HJ7Q/3I/D5DNBkp5pQS3s13+LK/vxqCmClUQSAOA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@relayflows/core/node_modules/@relayfile/mount-darwin-arm64": { + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-arm64/-/mount-darwin-arm64-0.8.30.tgz", + "integrity": "sha512-XYIJ1aiay6u9QdpGeDoWNA+Q0jboHbsOjCi37kBHpEapbuUXpqSW9HH5vAmpIPxzaFvsmYvTkTkjHPS9RLlnUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@relayflows/core/node_modules/@relayfile/mount-darwin-x64": { + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@relayfile/mount-darwin-x64/-/mount-darwin-x64-0.8.30.tgz", + "integrity": "sha512-i8SJtwzUeZGSSghmHD8CFe3AK+TIPez7DnpMm7XvYJi7qFqdlPb3Xz8YMo7ANBKD0PrANexvxW71NeYzpZ4IEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@relayflows/core/node_modules/@relayfile/mount-linux-arm64": { + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-arm64/-/mount-linux-arm64-0.8.30.tgz", + "integrity": "sha512-PkBSEqKSak1dBLnZE9iBpsefNlaBVI83DjhXP9VAjnRBDN2jqEy8QrcztbmimgbRp3h960PONVtobLGAqQ90tg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@relayflows/core/node_modules/@relayfile/mount-linux-x64": { + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@relayfile/mount-linux-x64/-/mount-linux-x64-0.8.30.tgz", + "integrity": "sha512-rjcETwjE80U29CueveHsW/VXvEW68+W8ePqq9IG3OTQZomgYSbXaYb/JS/SWGn0ywUeZaZi0wPlcNEnVNtflfQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@relayflows/core/node_modules/@relayfile/sdk": { + "version": "0.8.30", + "resolved": "https://registry.npmjs.org/@relayfile/sdk/-/sdk-0.8.30.tgz", + "integrity": "sha512-uXWxeEWrtnU27nQlCVLeBF7xtHOyfjQ5S+ogbkI7qnPt9PnZvsmO0SHgBSbI9cUmm63Jse6k7ssrdBu8SmDlOA==", + "license": "MIT", + "dependencies": { + "@relayfile/core": "0.8.30", + "ignore": "^7.0.5", + "tar": "^7.5.10" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@relayfile/mount-darwin-arm64": "0.8.30", + "@relayfile/mount-darwin-x64": "0.8.30", + "@relayfile/mount-linux-arm64": "0.8.30", + "@relayfile/mount-linux-x64": "0.8.30" + } + }, "node_modules/@relayflows/github-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@relayflows/github-primitive/-/github-primitive-1.0.1.tgz", - "integrity": "sha512-kWOXeZUiVrqooOYNGZaRExPU5tW4DlnG5eOvVKhjdQ71a0c8G+8AqC7GqYlXDeHyzjY89Owubg7ii1wakQdDUg==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@relayflows/github-primitive/-/github-primitive-1.0.4.tgz", + "integrity": "sha512-QFU7yv5qPNTLmkKcwSwk/DZfNpOwgMT90kFBhJ4dl7Hjm2Q/NwkG/BM+OtbnLocYSwm2eSY8ADbOs4SRqQFqEA==" }, "node_modules/@relayflows/slack-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@relayflows/slack-primitive/-/slack-primitive-1.0.1.tgz", - "integrity": "sha512-OWoj7CR8xRGhjmIL4hbkw9NQf8wCdEElfYApDWMt1N18U0646k1Uac8SQ2GB6bEVMS3YygI692D9SMvN8x7kbw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@relayflows/slack-primitive/-/slack-primitive-1.0.4.tgz", + "integrity": "sha512-2jm5UwJY7wgoeMnpOvDx23oO0MvG4WcMv5oeRN8UDeEw1tPC691SjK2lbURVeiKhVIo0Gq5bz2NSP28K5dTErg==", "dependencies": { "@slack/web-api": "^7.16.0" } @@ -5698,9 +5868,9 @@ } }, "node_modules/@slack/web-api": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.16.0.tgz", - "integrity": "sha512-68SAV77uuGKuhyyaRytX8UijVnqSLsTSKslGXw17cjQYXn+jtNl7gbaEjHgC5x2rhCuFdahBrEC2VCLppbzReg==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.17.0.tgz", + "integrity": "sha512-jejr34a8B4L5AS713wOAx1LAqNkW16HVMDEa6sYBvFDc/llUBl8hXaiI4BwF+Al+Sug19Vn2O7iokTVIhVvZ1Q==", "license": "MIT", "dependencies": { "@slack/logger": "^4.0.1", @@ -7434,19 +7604,19 @@ } }, "node_modules/agent-relay": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/agent-relay/-/agent-relay-8.8.1.tgz", - "integrity": "sha512-DzSO0g4cRpzKLBso8b36c/QL8Xg3i7ZKvUJUDQwHtWx0QMG/5tNqI2l7GdXD1yrUiRUZVSrs/K5ZCVEH4ixukA==", + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/agent-relay/-/agent-relay-9.1.6.tgz", + "integrity": "sha512-S5lZS1Rn4RkDsKFso1rf/X5LSjjWbSMd+oMc6/YBPCQ4mM2fH9JVMI0jcggLUCpvuuR8FB3Wghl0c33BLAR4dw==", "license": "Apache-2.0", "dependencies": { - "@agent-relay/cloud": "8.8.1", - "@agent-relay/config": "8.8.1", - "@agent-relay/fleet": "8.8.1", - "@agent-relay/harness-driver": "8.8.1", - "@agent-relay/sdk": "8.8.1", - "@agent-relay/utils": "8.8.1", + "@agent-relay/cloud": "9.1.6", + "@agent-relay/config": "9.1.6", + "@agent-relay/fleet": "9.1.6", + "@agent-relay/harness-driver": "9.1.6", + "@agent-relay/sdk": "9.1.6", + "@agent-relay/utils": "9.1.6", "@modelcontextprotocol/sdk": "^1.0.0", - "@relaycast/sdk": "^4.0.0", + "@relaycast/sdk": "^5.0.5", "@relayflows/cli": "^1.0.1", "@xterm/headless": "^6.0.0", "commander": "^12.1.0", @@ -7980,20 +8150,20 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -8003,6 +8173,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/body-parser/node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -10886,9 +11069,9 @@ } }, "node_modules/hono": { - "version": "4.12.23", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", - "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", + "version": "4.12.27", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz", + "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -11839,9 +12022,9 @@ } }, "node_modules/listr2": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.1.tgz", - "integrity": "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.2.tgz", + "integrity": "sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==", "license": "MIT", "dependencies": { "cli-truncate": "^5.2.0", @@ -13151,12 +13334,12 @@ } }, "node_modules/posthog-node": { - "version": "5.35.14", - "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.35.14.tgz", - "integrity": "sha512-vUO4/MQ+edqSua4jAQY+H8wvVMFOZNoBt72dqnxGkqgES0awW0NPFdDSfEhGhS6BSkydNFkN/4crnOksbCnFlQ==", + "version": "5.38.6", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.38.6.tgz", + "integrity": "sha512-Sm2mCAa9/lTTYppnKyy0AhQrriq8fOd8B2vwd3EE/9uihyIx9qkJ1xGYbvADxQJlF7HqM1/TIFCKsI0JvF8gjQ==", "license": "MIT", "dependencies": { - "@posthog/core": "1.30.5" + "@posthog/core": "^1.38.0" }, "engines": { "node": "^20.20.0 || >=22.22.0" @@ -13391,12 +13574,13 @@ } }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -13439,12 +13623,16 @@ } }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { @@ -14044,14 +14232,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, diff --git a/package.json b/package.json index 02f76ae8..53b2d0ae 100644 --- a/package.json +++ b/package.json @@ -35,20 +35,21 @@ "lint": "eslint ." }, "dependencies": { - "@agent-relay/cloud": "^8.7.2", - "@agent-relay/factory": "^0.1.1", - "@agent-relay/harness-driver": "^8.3.7", - "@agent-relay/integration-prompts": "^8.8.4", - "@agent-relay/sdk": "^8.3.7", + "@agent-relay/cloud": "^9.1.6", + "@agent-relay/factory": "^0.1.13", + "@agent-relay/harness-driver": "^9.1.6", + "@agent-relay/integration-prompts": "^9.1.6", + "@agent-relay/sdk": "^9.1.6", "@agentworkforce/deploy": "^4.0.2", - "@relayburn/sdk": "^3.3.0", - "@relayfile/sdk": "^0.8.29", + "@relayburn/sdk": "^4.0.0", + "@relaycast/sdk": "^5.0.7", + "@relayfile/sdk": "^0.10.15", "@tanstack/react-virtual": "^3.14.2", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", - "agent-relay": "^8.3.7", + "agent-relay": "^9.1.6", "agentworkforce": "^4.0.2", "ai-hist": "^0.2.3", "allotment": "^1.0.9", @@ -67,7 +68,7 @@ "protobufjs": "8.5.0" }, "devDependencies": { - "@agent-relay/evals": "^8.8.4", + "@agent-relay/evals": "^9.1.6", "@playwright/test": "^1.57.0", "@tailwindcss/vite": "^4.0.0", "@testing-library/react": "^16.3.2", diff --git a/src/main/broker.ts b/src/main/broker.ts index bf85dbe6..9e0c2794 100644 --- a/src/main/broker.ts +++ b/src/main/broker.ts @@ -33,6 +33,7 @@ import { } from './broker-event-utils' import { PtyInputStreamManager } from './pty-input-stream' import { PtyChunkDeduper } from './pty-dedup' +import { RELAYCAST_BASE_URL } from './relay-service-urls' import { SpawnCoordinator, spawnRequestKey, @@ -2170,7 +2171,7 @@ export class BrokerManager { const relay = new AgentRelay({ workspaceKey, - ...(process.env.RELAY_BASE_URL ? { baseUrl: process.env.RELAY_BASE_URL } : {}) + baseUrl: process.env.RELAY_BASE_URL || RELAYCAST_BASE_URL }) const limit = normalizeReconcileLimit(input.limit) const messages = input.kind === 'channel' diff --git a/src/main/cloud-agent.ts b/src/main/cloud-agent.ts index fbc7d359..6b4ad95e 100644 --- a/src/main/cloud-agent.ts +++ b/src/main/cloud-agent.ts @@ -19,6 +19,7 @@ import { accountWorkspaceReadyRetryOptions, getAccountWorkspaceId, resolveCloudA import { brokerManager } from './broker' import { getRelayWorkspaceManager } from './relay-workspace' import { createPearMountLauncher } from './relayfile-mount-launcher' +import { normalizeRelayServiceEnv } from './relay-service-urls' import { loadStore, saveStore, type Project, type ProjectCloudAgent } from './store' import type { CloudAgentBinding, @@ -1276,7 +1277,7 @@ export class CloudAgentManager { return launcher.start({ ...input, env: { - ...input.env, + ...normalizeRelayServiceEnv(input.env), RELAYFILE_MOUNT_LOCAL_LAYOUT: 'exact', RELAYFILE_MOUNT_SYNC_MODE: 'mirror', RELAYFILE_CONFLICT_POLICY: policy diff --git a/src/main/integration-event-bridge.ts b/src/main/integration-event-bridge.ts index cbd0d3c7..2e20842e 100644 --- a/src/main/integration-event-bridge.ts +++ b/src/main/integration-event-bridge.ts @@ -37,6 +37,8 @@ type ChangeEvent = Omit & { import type { ConnectedIntegration } from './integrations' // @ts-expect-error Node's strip-types test runner requires the explicit .ts extension. import { isSlackWritebackCommandRoot, slackWritebackCommandMountPathFor } from './slack-writeback-command-roots.ts' +// @ts-expect-error Node's strip-types test runner requires the explicit .ts extension. +import { normalizeRelayWorkspaceInfo } from './relay-service-urls.ts' import type { IntegrationEventTelemetryCounters, IntegrationEventTelemetrySnapshot @@ -3702,10 +3704,10 @@ export class IntegrationEventBridge { cloudApiUrl: auth.apiUrl, accessToken: tokenProvider }) - return setup.joinWorkspace(accountWorkspaceId, { + return normalizeRelayWorkspaceInfo(await setup.joinWorkspace(accountWorkspaceId, { agentName: INTEGRATION_EVENT_AGENT_NAME, scopes: INTEGRATION_EVENT_SCOPES - }) + })) } let joined: Awaited> diff --git a/src/main/integration-mounts.ts b/src/main/integration-mounts.ts index 60e6fd38..e94b249b 100644 --- a/src/main/integration-mounts.ts +++ b/src/main/integration-mounts.ts @@ -11,6 +11,7 @@ import { import { accountWorkspaceReadyRetryOptions, getAccountWorkspaceId, refreshCloudAuth, resolveCloudAuth } from './auth' import { createPearMountLauncher } from './relayfile-mount-launcher' import { forgetMountPid, killMountPid, reapOrphanedMountPids, recordMountPid } from './relayfile-mount-pids' +import { normalizeRelayServiceEnv } from './relay-service-urls' const MOUNT_READY_TIMEOUT_MS = 60_000 const MOUNT_SYNC_TIMEOUT = '180s' @@ -810,7 +811,7 @@ export class IntegrationMountManager { start: (input: MountLauncherStart) => launcher.start({ ...input, env: { - ...input.env, + ...normalizeRelayServiceEnv(input.env), RELAYFILE_MOUNT_LOCAL_LAYOUT: spec.localLayout, RELAYFILE_MOUNT_SYNC_MODE: spec.syncMode, RELAYFILE_MOUNT_TIMEOUT: input.env.RELAYFILE_MOUNT_TIMEOUT || diff --git a/src/main/integrations.ts b/src/main/integrations.ts index 0f551e03..d3568938 100644 --- a/src/main/integrations.ts +++ b/src/main/integrations.ts @@ -38,6 +38,7 @@ import { import { INTEGRATIONS_CATALOG } from './integrations.catalog' import { slackWritebackCommandMountPathFor } from './slack-writeback-command-roots' import { getRelayWorkspaceManager } from './relay-workspace' +import { normalizeRelayWorkspaceInfo } from './relay-service-urls' import { loadStore, saveStore, type ProjectIntegration } from './store' export type IntegrationAuthMethod = 'oauth' | 'token' | 'apikey' @@ -1711,10 +1712,10 @@ export class IntegrationsManager { cloudApiUrl: auth.apiUrl, accessToken: () => auth.accessToken }) - const handle = await setup.joinWorkspace(workspaceId, { + const handle = normalizeRelayWorkspaceInfo(await setup.joinWorkspace(workspaceId, { agentName: mode === 'read' ? 'pear-integrations-reader' : 'pear-integrations-writer', scopes: mode === 'read' ? ['relayfile:fs:read:/**'] : ['relayfile:fs:read:/**', 'relayfile:fs:write:/**'] - }) + })) if (mode === 'read') this.integrationRemoteReaderHandle = handle else this.integrationRemoteWriterHandle = handle return handle diff --git a/src/main/relay-service-urls.test.ts b/src/main/relay-service-urls.test.ts new file mode 100644 index 00000000..e6d23bee --- /dev/null +++ b/src/main/relay-service-urls.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest' +import { + RELAYCAST_BASE_URL, + RELAYFILE_BASE_URL, + normalizeRelayServiceEnv, + normalizeRelayWorkspaceInfo, + normalizeRelaycastBaseUrl, + normalizeRelayfileBaseUrl +} from './relay-service-urls' + +describe('relay service URL normalization', () => { + it('canonicalizes hosted relayfile and relaycast URLs', () => { + expect(normalizeRelayfileBaseUrl('https://api.relayfile.dev/')).toBe(RELAYFILE_BASE_URL) + expect(normalizeRelayfileBaseUrl('https://file.agentrelay.com/v1')).toBe(RELAYFILE_BASE_URL) + expect(normalizeRelaycastBaseUrl('https://api.relaycast.dev')).toBe(RELAYCAST_BASE_URL) + expect(normalizeRelaycastBaseUrl('https://cast.agentrelay.com/')).toBe(RELAYCAST_BASE_URL) + }) + + it('leaves custom self-hosted URLs intact', () => { + expect(normalizeRelayfileBaseUrl('https://relayfile.internal.example/')).toBe('https://relayfile.internal.example') + expect(normalizeRelaycastBaseUrl('https://relaycast.internal.example/api')).toBe('https://relaycast.internal.example/api') + }) + + it('normalizes relay mount env aliases together', () => { + expect(normalizeRelayServiceEnv({ + RELAYFILE_BASE_URL: 'https://api.relayfile.dev', + RELAYCAST_BASE_URL: 'https://api.relaycast.dev' + })).toMatchObject({ + RELAYFILE_BASE_URL, + RELAYCAST_BASE_URL, + RELAY_BASE_URL: RELAYCAST_BASE_URL + }) + }) + + it('normalizes workspace handle info in place', () => { + const handle = { + info: { + relayfileUrl: 'https://api.relayfile.dev', + relaycastBaseUrl: 'https://api.relaycast.dev' + } + } + + expect(normalizeRelayWorkspaceInfo(handle).info).toEqual({ + relayfileUrl: RELAYFILE_BASE_URL, + relaycastBaseUrl: RELAYCAST_BASE_URL + }) + }) +}) diff --git a/src/main/relay-service-urls.ts b/src/main/relay-service-urls.ts new file mode 100644 index 00000000..fd6b83fc --- /dev/null +++ b/src/main/relay-service-urls.ts @@ -0,0 +1,62 @@ +export const RELAYFILE_BASE_URL = 'https://file.agentrelay.com' +export const RELAYCAST_BASE_URL = 'https://cast.agentrelay.com' + +const LEGACY_RELAYFILE_HOSTS = new Set([ + 'api.relayfile.dev', + 'relayfile.dev' +]) + +const LEGACY_RELAYCAST_HOSTS = new Set([ + 'api.relaycast.dev', + 'relaycast.dev' +]) + +export function normalizeRelayfileBaseUrl(value: string | null | undefined): string { + return normalizeHostedServiceUrl(value, RELAYFILE_BASE_URL, LEGACY_RELAYFILE_HOSTS) +} + +export function normalizeRelaycastBaseUrl(value: string | null | undefined): string { + return normalizeHostedServiceUrl(value, RELAYCAST_BASE_URL, LEGACY_RELAYCAST_HOSTS) +} + +export function normalizeRelayServiceEnv(env: Record): Record { + return { + ...env, + RELAYFILE_BASE_URL: normalizeRelayfileBaseUrl(env.RELAYFILE_BASE_URL), + RELAYCAST_BASE_URL: normalizeRelaycastBaseUrl(env.RELAYCAST_BASE_URL || env.RELAY_BASE_URL), + RELAY_BASE_URL: normalizeRelaycastBaseUrl(env.RELAY_BASE_URL || env.RELAYCAST_BASE_URL) + } +} + +export function normalizeRelayWorkspaceInfo(handle: T): T { + if (handle.info) { + handle.info.relayfileUrl = normalizeRelayfileBaseUrl(handle.info.relayfileUrl) + handle.info.relaycastBaseUrl = normalizeRelaycastBaseUrl(handle.info.relaycastBaseUrl) + } + return handle +} + +function normalizeHostedServiceUrl( + value: string | null | undefined, + canonical: string, + legacyHosts: Set +): string { + const trimmed = value?.trim() + if (!trimmed) return canonical + + try { + const url = new URL(trimmed) + if (url.hostname === new URL(canonical).hostname || legacyHosts.has(url.hostname)) { + return canonical + } + } catch { + return trimmed + } + + return trimmed.replace(/\/+$/, '') +} diff --git a/src/main/relay-workspace.ts b/src/main/relay-workspace.ts index a461ef18..551d9404 100644 --- a/src/main/relay-workspace.ts +++ b/src/main/relay-workspace.ts @@ -3,6 +3,7 @@ import { RelayfileSetup, type WorkspaceHandle } from '@relayfile/sdk' import { resolveCloudAuth } from './auth' import { loadStore, saveStore } from './store' import type { RelayWorkspaceManagerLike, RelayWorkspaceRecord } from './relay-workspace.types' +import { normalizeRelayWorkspaceInfo } from './relay-service-urls' const ACCOUNT_AGENT_NAME = 'pear-account' // Scope grammar is `plane:resource:action:path`; the cloud API only accepts the @@ -172,10 +173,10 @@ export class RelayWorkspaceManager { clearPersistedWorkspace(existing) } else { try { - const handle = await setup.joinWorkspace(existing.id, { + const handle = normalizeRelayWorkspaceInfo(await setup.joinWorkspace(existing.id, { agentName: this.agentName, scopes: this.scopes - }) + })) return { handle } } catch (err) { if (!isWorkspaceMissing(err)) throw err @@ -184,10 +185,10 @@ export class RelayWorkspaceManager { } } - const handle = await setup.createWorkspace({ + const handle = normalizeRelayWorkspaceInfo(await setup.createWorkspace({ agentName: this.agentName, scopes: this.scopes - }) + })) return { handle, persist: { diff --git a/src/main/relayfile-mount-launcher.test.ts b/src/main/relayfile-mount-launcher.test.ts index 12862dbc..1846fe1a 100644 --- a/src/main/relayfile-mount-launcher.test.ts +++ b/src/main/relayfile-mount-launcher.test.ts @@ -21,6 +21,7 @@ vi.mock('@relayfile/sdk/mount-launcher', () => ({ })) import { createPearMountLauncher } from './relayfile-mount-launcher' +import { RELAYCAST_BASE_URL, RELAYFILE_BASE_URL } from './relay-service-urls' describe('createPearMountLauncher', () => { let tempDir: string @@ -97,4 +98,25 @@ describe('createPearMountLauncher', () => { }) } }) + + it('canonicalizes hosted relay service URLs before launching the mount', async () => { + const launcher = createPearMountLauncher() + + await launcher.start({ + env: { + RELAYFILE_BASE_URL: 'https://api.relayfile.dev', + RELAYCAST_BASE_URL: 'https://api.relaycast.dev', + RELAYFILE_LOCAL_DIR: join(tempDir, 'canonical-hosts') + }, + readyTimeoutMs: 5_000 + }) + + expect(mock.start).toHaveBeenCalledWith(expect.objectContaining({ + env: expect.objectContaining({ + RELAYFILE_BASE_URL, + RELAYCAST_BASE_URL, + RELAY_BASE_URL: RELAYCAST_BASE_URL + }) + })) + }) }) diff --git a/src/main/relayfile-mount-launcher.ts b/src/main/relayfile-mount-launcher.ts index a465e5a1..6cb6ce79 100644 --- a/src/main/relayfile-mount-launcher.ts +++ b/src/main/relayfile-mount-launcher.ts @@ -4,6 +4,7 @@ import { join, resolve } from 'node:path' import { app } from 'electron' import { type MountLauncher, type MountLauncherStart } from '@relayfile/sdk' import { createDefaultMountLauncher } from '@relayfile/sdk/mount-launcher' +import { normalizeRelayServiceEnv } from './relay-service-urls' function canExecute(filePath: string | undefined): filePath is string { if (!filePath) return false @@ -117,7 +118,7 @@ export function createPearMountLauncher(options: { onEvent?: MountLauncherStart[ return launcher.start({ ...input, env: { - ...input.env, + ...normalizeRelayServiceEnv(input.env), RELAYFILE_MOUNT_BIN: binary, ...(credsPath ? { RELAYFILE_MOUNT_CREDS_FILE: credsPath } : {}) }, From faf235a7250a3d4e855c71d3f4ebba9294e9360e Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 30 Jun 2026 12:35:09 -0700 Subject: [PATCH 2/4] Harden channel chat reconciliation --- .../hooks/use-message-reconciliation.test.ts | 198 ++++++++++++++++++ .../src/hooks/use-message-reconciliation.ts | 152 ++++++++++++-- 2 files changed, 330 insertions(+), 20 deletions(-) diff --git a/src/renderer/src/hooks/use-message-reconciliation.test.ts b/src/renderer/src/hooks/use-message-reconciliation.test.ts index 56fa6302..4ebccc1e 100644 --- a/src/renderer/src/hooks/use-message-reconciliation.test.ts +++ b/src/renderer/src/hooks/use-message-reconciliation.test.ts @@ -36,6 +36,15 @@ const channelMessage: ChatMessage = { projectId: 'project-1' } +function channelRequest(channelName: string): MessageReconciliationRequest { + return { + projectId: 'project-1', + kind: 'channel', + channelName, + limit: 50 + } +} + function deferredMessages(): { promise: Promise resolve: (messages: ChatMessage[]) => void @@ -96,6 +105,50 @@ describe('getActiveMessageReconciliationRequest', () => { })).toBeNull() }) + it('builds a channel reconciliation request from broker channel message events', () => { + expect(hooks.getBrokerEventMessageReconciliationRequest({ + event: { + kind: 'relaycast_published', + event_id: 'evt-1', + to: 'general', + target_type: 'channel', + projectId: 'project-1' + }, + knownChannelNames: ['general'] + })).toEqual({ + projectId: 'project-1', + kind: 'channel', + channelName: 'general', + limit: 50 + }) + + expect(hooks.getBrokerEventMessageReconciliationRequest({ + event: { + kind: 'message_delivery_confirmed', + event_id: 'evt-2', + to: '#general' + }, + fallbackProjectId: 'project-1' + })).toEqual({ + projectId: 'project-1', + kind: 'channel', + channelName: 'general', + limit: 50 + }) + }) + + it('does not treat agent-targeted delivery events as channel updates', () => { + expect(hooks.getBrokerEventMessageReconciliationRequest({ + event: { + kind: 'message_delivery_confirmed', + event_id: 'evt-3', + to: 'codex-1', + projectId: 'project-1' + }, + knownChannelNames: ['general'] + })).toBeNull() + }) + it('schedules reconciliation after a human message send timestamp changes', () => { const reconciler = { schedule: vi.fn() } @@ -209,6 +262,151 @@ describe('createMessageReconciler', () => { expect(mergeMessages).toHaveBeenNthCalledWith(2, [secondMessage]) }) + it('coalesces duplicate targeted channel event reconciliations by room', async () => { + vi.useFakeTimers() + const request = channelRequest('general') + const reconcileMessages = vi.fn(async () => [channelMessage]) + const mergeMessages = vi.fn() + const debug = vi.fn() + const reconciler = hooks.createMessageReconciler({ + getRequest: () => null, + reconcileMessages, + mergeMessages, + setTimeout: setTimeout as unknown as typeof window.setTimeout, + clearTimeout: clearTimeout as unknown as typeof window.clearTimeout, + debounceMs: 10, + now: () => 123, + debug + }) + + reconciler.scheduleRequest(request, 'broker-event:relaycast_published') + reconciler.scheduleRequest(request, 'broker-event:message_delivery_confirmed') + await vi.advanceTimersByTimeAsync(9) + + expect(reconcileMessages).not.toHaveBeenCalled() + + await vi.advanceTimersByTimeAsync(1) + + expect(reconcileMessages).toHaveBeenCalledTimes(1) + expect(reconcileMessages).toHaveBeenCalledWith(request) + expect(mergeMessages).toHaveBeenCalledTimes(1) + expect(mergeMessages).toHaveBeenCalledWith([channelMessage]) + expect(debug).toHaveBeenCalledWith(expect.objectContaining({ + kind: 'merged', + reason: 'broker-event:message_delivery_confirmed', + messageCount: 1, + timestamp: 123 + })) + }) + + it('reruns dirty in-flight targeted reconciliations independently per channel', async () => { + vi.useFakeTimers() + const generalRequest = channelRequest('general') + const triageRequest = channelRequest('triage') + const generalFirst = deferredMessages() + const generalSecond = deferredMessages() + const triageFirst = deferredMessages() + const triageSecond = deferredMessages() + const reconcileMessages = vi.fn((request: MessageReconciliationRequest) => { + if (request.kind === 'channel' && request.channelName === 'general') { + return reconcileMessages.mock.calls.filter(([candidate]) => + candidate.kind === 'channel' && candidate.channelName === 'general' + ).length === 1 + ? generalFirst.promise + : generalSecond.promise + } + return reconcileMessages.mock.calls.filter(([candidate]) => + candidate.kind === 'channel' && candidate.channelName === 'triage' + ).length === 1 + ? triageFirst.promise + : triageSecond.promise + }) + const mergeMessages = vi.fn() + const reconciler = hooks.createMessageReconciler({ + getRequest: () => null, + reconcileMessages, + mergeMessages, + setTimeout: setTimeout as unknown as typeof window.setTimeout, + clearTimeout: clearTimeout as unknown as typeof window.clearTimeout, + debounceMs: 1 + }) + + reconciler.scheduleRequest(generalRequest, 'broker-event:general:first') + reconciler.scheduleRequest(triageRequest, 'broker-event:triage:first') + await vi.advanceTimersByTimeAsync(1) + + expect(reconcileMessages).toHaveBeenCalledTimes(2) + expect(reconcileMessages).toHaveBeenNthCalledWith(1, generalRequest) + expect(reconcileMessages).toHaveBeenNthCalledWith(2, triageRequest) + + reconciler.scheduleRequest(generalRequest, 'broker-event:general:second') + reconciler.scheduleRequest(triageRequest, 'broker-event:triage:second') + await vi.advanceTimersByTimeAsync(1) + + expect(reconcileMessages).toHaveBeenCalledTimes(2) + + generalFirst.resolve([{ ...channelMessage, id: 'general-1', body: 'general first' }]) + await Promise.resolve() + await Promise.resolve() + + expect(reconcileMessages).toHaveBeenCalledTimes(3) + expect(reconcileMessages).toHaveBeenNthCalledWith(3, generalRequest) + + triageFirst.resolve([{ ...channelMessage, id: 'triage-1', to: '#triage', body: 'triage first' }]) + await Promise.resolve() + await Promise.resolve() + + expect(reconcileMessages).toHaveBeenCalledTimes(4) + expect(reconcileMessages).toHaveBeenNthCalledWith(4, triageRequest) + + generalSecond.resolve([{ ...channelMessage, id: 'general-2', body: 'general second' }]) + triageSecond.resolve([{ ...channelMessage, id: 'triage-2', to: '#triage', body: 'triage second' }]) + await Promise.resolve() + await Promise.resolve() + + expect(mergeMessages).toHaveBeenCalledTimes(4) + expect(mergeMessages).toHaveBeenNthCalledWith(3, [{ ...channelMessage, id: 'general-2', body: 'general second' }]) + expect(mergeMessages).toHaveBeenNthCalledWith(4, [{ ...channelMessage, id: 'triage-2', to: '#triage', body: 'triage second' }]) + }) + + it('cleans up keyed timers and suppresses in-flight merges after dispose', async () => { + vi.useFakeTimers() + const generalRequest = channelRequest('general') + const triageRequest = channelRequest('triage') + const inFlight = deferredMessages() + const clearTimeout = vi.fn((handle: number) => globalThis.clearTimeout(handle)) + const reconcileMessages = vi.fn(() => inFlight.promise) + const mergeMessages = vi.fn() + const reconciler = hooks.createMessageReconciler({ + getRequest: () => null, + reconcileMessages, + mergeMessages, + setTimeout: setTimeout as unknown as typeof window.setTimeout, + clearTimeout, + debounceMs: 10 + }) + + void reconciler.runRequestNow(generalRequest, 'broker-event:general:first') + void reconciler.runRequestNow(generalRequest, 'broker-event:general:second') + reconciler.scheduleRequest(generalRequest, 'broker-event:general:timer') + reconciler.scheduleRequest(triageRequest, 'broker-event:triage:timer') + + expect(reconcileMessages).toHaveBeenCalledTimes(1) + + reconciler.dispose() + expect(clearTimeout).toHaveBeenCalledTimes(2) + + await vi.advanceTimersByTimeAsync(10) + expect(reconcileMessages).toHaveBeenCalledTimes(1) + + inFlight.resolve([channelMessage]) + await Promise.resolve() + await Promise.resolve() + + expect(mergeMessages).not.toHaveBeenCalled() + expect(reconcileMessages).toHaveBeenCalledTimes(1) + }) + it('skips fetches when no active chat room can be reconciled', async () => { vi.useFakeTimers() const reconcileMessages = vi.fn(async () => [channelMessage]) diff --git a/src/renderer/src/hooks/use-message-reconciliation.ts b/src/renderer/src/hooks/use-message-reconciliation.ts index a7d90e63..bd100364 100644 --- a/src/renderer/src/hooks/use-message-reconciliation.ts +++ b/src/renderer/src/hooks/use-message-reconciliation.ts @@ -2,7 +2,7 @@ import { useEffect, useMemo } from 'react' import { getDirectMessageRoomId } from '@/lib/direct-messages' import { pear } from '@/lib/ipc' import { useAgentStore, type ChatMessage } from '@/stores/agent-store' -import { useProjectStore } from '@/stores/project-store' +import { normalizeChannelName as normalizeProjectChannelName, useProjectStore } from '@/stores/project-store' import { type AppTab, useUIStore } from '@/stores/ui-store' import type { BrokerEventStreamDiagnostic, @@ -26,6 +26,14 @@ const EVENT_STREAM_RECONCILED_STATUSES = new Set void } +type BrokerEventLike = Record & { + kind?: string + projectId?: string +} + export interface MessageReconciliationDebugEvent { kind: 'scheduled' | 'started' | 'skipped' | 'merged' | 'failed' reason: string @@ -56,6 +69,8 @@ export interface MessageReconciliationDebugEvent { export interface MessageReconciler { schedule: (reason: string) => void runNow: (reason: string) => Promise + scheduleRequest: (request: MessageReconciliationRequest, reason: string) => void + runRequestNow: (request: MessageReconciliationRequest, reason: string) => Promise dispose: () => void } @@ -73,6 +88,58 @@ function normalizeChannelName(value: string | undefined): string | null { return normalized || null } +function brokerEventString(event: BrokerEventLike, key: string): string | undefined { + const value = event[key] + return typeof value === 'string' && value.trim() ? value : undefined +} + +function messageReconciliationRequestKey(request: MessageReconciliationRequest): string { + return JSON.stringify([ + request.projectId, + request.kind, + request.kind === 'channel' ? request.channelName : request.conversationId + ]) +} + +export function getBrokerEventMessageReconciliationRequest(input: { + event: BrokerEventLike + fallbackProjectId?: string | null + knownChannelNames?: string[] + limit?: number +}): MessageReconciliationRequest | null { + const kind = input.event.kind + if (!kind || !CHANNEL_MESSAGE_EVENT_KINDS.has(kind)) return null + + const projectId = input.event.projectId || input.fallbackProjectId || '' + if (!projectId.trim()) return null + + const rawTarget = brokerEventString(input.event, 'target') || brokerEventString(input.event, 'to') + if (!rawTarget) return null + + const channelName = normalizeChannelName(rawTarget) + if (!channelName) return null + + const normalizedKnownChannels = new Set( + (input.knownChannelNames || []) + .map((channel) => normalizeProjectChannelName(channel)) + .filter(Boolean) + ) + const targetType = brokerEventString(input.event, 'target_type')?.toLowerCase() + const isChannelTarget = + rawTarget.trim().startsWith('#') || + targetType === 'channel' || + normalizedKnownChannels.has(channelName) + + if (!isChannelTarget) return null + + return { + projectId, + kind: 'channel', + channelName, + limit: input.limit ?? DEFAULT_RECONCILE_LIMIT + } +} + function getActiveTab(tabs: AppTab[], activeTabId: string): AppTab | undefined { return tabs.find((tab) => tab.id === activeTabId) } @@ -115,26 +182,23 @@ export function createMessageReconciler(deps: MessageReconcilerDeps): MessageRec const debounceMs = deps.debounceMs ?? DEFAULT_RECONCILE_DEBOUNCE_MS const now = deps.now ?? (() => Date.now()) let timer: number | null = null + const requestTimers = new Map() + const requestInFlight = new Map>() + const pendingRequestReruns = new Map() let disposed = false - let inFlight: Promise | null = null - let pendingRerun: string | null = null const debug = (event: Omit): void => { deps.debug?.({ ...event, timestamp: now() }) } - const runNow = async (reason: string): Promise => { + const runRequestNow = async (request: MessageReconciliationRequest, reason: string): Promise => { if (disposed) return - if (inFlight) { - pendingRerun = reason + const requestKey = messageReconciliationRequestKey(request) + const existingRun = requestInFlight.get(requestKey) + if (existingRun) { + pendingRequestReruns.set(requestKey, { request, reason }) debug({ kind: 'scheduled', reason }) - return inFlight - } - - const request = deps.getRequest() - if (!request) { - debug({ kind: 'skipped', reason }) - return + return existingRun } const currentRun = (async () => { @@ -154,22 +218,47 @@ export function createMessageReconciler(deps: MessageReconcilerDeps): MessageRec }) } })() - inFlight = currentRun + requestInFlight.set(requestKey, currentRun) try { await currentRun } finally { - if (inFlight === currentRun) { - inFlight = null + if (requestInFlight.get(requestKey) === currentRun) { + requestInFlight.delete(requestKey) } - const rerunReason = pendingRerun - pendingRerun = null - if (rerunReason && !disposed) { - await runNow(rerunReason) + const rerun = pendingRequestReruns.get(requestKey) + pendingRequestReruns.delete(requestKey) + if (rerun && !disposed) { + await runRequestNow(rerun.request, rerun.reason) } } } + const runNow = async (reason: string): Promise => { + if (disposed) return + const request = deps.getRequest() + if (!request) { + debug({ kind: 'skipped', reason }) + return + } + await runRequestNow(request, reason) + } + + const scheduleRequest = (request: MessageReconciliationRequest, reason: string): void => { + if (disposed) return + const requestKey = messageReconciliationRequestKey(request) + const existingTimer = requestTimers.get(requestKey) + if (existingTimer) { + deps.clearTimeout(existingTimer) + } + debug({ kind: 'scheduled', reason }) + const nextTimer = deps.setTimeout(() => { + requestTimers.delete(requestKey) + void runRequestNow(request, reason) + }, debounceMs) + requestTimers.set(requestKey, nextTimer) + } + const schedule = (reason: string): void => { if (disposed) return if (timer) { @@ -185,12 +274,19 @@ export function createMessageReconciler(deps: MessageReconcilerDeps): MessageRec return { schedule, runNow, + scheduleRequest, + runRequestNow, dispose: () => { disposed = true if (timer) { deps.clearTimeout(timer) timer = null } + for (const requestTimer of requestTimers.values()) { + deps.clearTimeout(requestTimer) + } + requestTimers.clear() + pendingRequestReruns.clear() } } } @@ -305,4 +401,20 @@ export function useMessageReconciliation(): void { } }) }, [reconciler]) + + useEffect(() => { + return pear?.broker?.onEvent?.((event) => { + const brokerEvent = event as BrokerEventLike + const project = useProjectStore.getState() + const projectId = brokerEvent.projectId || project.activeProjectId + const knownChannelNames = project.projects.find((entry) => entry.id === projectId)?.channels || [] + const request = getBrokerEventMessageReconciliationRequest({ + event: brokerEvent, + fallbackProjectId: project.activeProjectId, + knownChannelNames + }) + if (!request) return + reconciler.scheduleRequest(request, `broker-event:${brokerEvent.kind}`) + }) + }, [reconciler]) } From b4775cda3e8cbe4590148809f06ecaed5e70c5ba Mon Sep 17 00:00:00 2001 From: "agent-relay-code[bot]" Date: Tue, 30 Jun 2026 19:42:55 +0000 Subject: [PATCH 3/4] chore: apply pr-reviewer fixes for #386 --- electron-builder.mcp-resources.yml | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/electron-builder.mcp-resources.yml b/electron-builder.mcp-resources.yml index 0a4c3feb..069dfdba 100644 --- a/electron-builder.mcp-resources.yml +++ b/electron-builder.mcp-resources.yml @@ -83,19 +83,36 @@ extraResources: - '@relaycast/sdk/node_modules/zod/**' - '@relaycast/types/**' - '@relaycast/types/node_modules/zod/**' - - '@relayfile/core/**' - - '@relayfile/mount-darwin-arm64/**' - - '@relayfile/mount-darwin-x64/**' - - '@relayfile/mount-linux-arm64/**' - - '@relayfile/mount-linux-x64/**' - - '@relayfile/sdk/**' - '@relayflows/browser-primitive/**' + - '@relayflows/browser-primitive/node_modules/@agent-relay/sdk/**' + - '@relayflows/browser-primitive/node_modules/@relaycast/sdk/**' + - '@relayflows/browser-primitive/node_modules/@relaycast/types/**' + - '@relayflows/browser-primitive/node_modules/zod/**' - '@relayflows/cli/**' - '@relayflows/core/**' + - '@relayflows/core/node_modules/@agent-relay/broker-darwin-arm64/**' + - '@relayflows/core/node_modules/@agent-relay/broker-darwin-x64/**' + - '@relayflows/core/node_modules/@agent-relay/broker-linux-arm64/**' + - '@relayflows/core/node_modules/@agent-relay/broker-linux-x64/**' + - '@relayflows/core/node_modules/@agent-relay/broker-win32-x64/**' + - '@relayflows/core/node_modules/@agent-relay/cloud/**' + - '@relayflows/core/node_modules/@agent-relay/config/**' + - '@relayflows/core/node_modules/@agent-relay/harness-driver/**' + - '@relayflows/core/node_modules/@agent-relay/harnesses/**' + - '@relayflows/core/node_modules/@agent-relay/sdk/**' + - '@relayflows/core/node_modules/@agent-relay/sdk/node_modules/@relaycast/sdk/**' + - '@relayflows/core/node_modules/@agent-relay/sdk/node_modules/@relaycast/types/**' + - '@relayflows/core/node_modules/@agent-relay/sdk/node_modules/zod/**' - '@relayflows/core/node_modules/@relaycast/sdk/**' - '@relayflows/core/node_modules/@relaycast/sdk/node_modules/zod/**' - '@relayflows/core/node_modules/@relaycast/types/**' - '@relayflows/core/node_modules/@relaycast/types/node_modules/zod/**' + - '@relayflows/core/node_modules/@relayfile/core/**' + - '@relayflows/core/node_modules/@relayfile/mount-darwin-arm64/**' + - '@relayflows/core/node_modules/@relayfile/mount-darwin-x64/**' + - '@relayflows/core/node_modules/@relayfile/mount-linux-arm64/**' + - '@relayflows/core/node_modules/@relayfile/mount-linux-x64/**' + - '@relayflows/core/node_modules/@relayfile/sdk/**' - '@relayflows/github-primitive/**' - '@relayflows/slack-primitive/**' - '@sinclair/typebox/**' @@ -160,6 +177,7 @@ extraResources: - 'axios/**' - 'bcrypt-pbkdf/**' - 'body-parser/**' + - 'body-parser/node_modules/content-type/**' - 'body-parser/node_modules/iconv-lite/**' - 'bowser/**' - 'buildcheck/**' From 078d99a3f7180583d44ccf7dc1ec6761fa19628d Mon Sep 17 00:00:00 2001 From: Khaliq Date: Tue, 30 Jun 2026 12:50:35 -0700 Subject: [PATCH 4/4] Address chat reconciliation review feedback --- src/main/broker.ts | 4 +- src/main/relay-service-urls.test.ts | 19 +++++++- src/main/relay-service-urls.ts | 25 +++++++---- .../hooks/use-message-reconciliation.test.ts | 43 +++++++++++++++++++ .../src/hooks/use-message-reconciliation.ts | 10 ++++- 5 files changed, 86 insertions(+), 15 deletions(-) diff --git a/src/main/broker.ts b/src/main/broker.ts index 9e0c2794..51db7ebe 100644 --- a/src/main/broker.ts +++ b/src/main/broker.ts @@ -33,7 +33,7 @@ import { } from './broker-event-utils' import { PtyInputStreamManager } from './pty-input-stream' import { PtyChunkDeduper } from './pty-dedup' -import { RELAYCAST_BASE_URL } from './relay-service-urls' +import { normalizeRelaycastBaseUrl } from './relay-service-urls' import { SpawnCoordinator, spawnRequestKey, @@ -2171,7 +2171,7 @@ export class BrokerManager { const relay = new AgentRelay({ workspaceKey, - baseUrl: process.env.RELAY_BASE_URL || RELAYCAST_BASE_URL + baseUrl: normalizeRelaycastBaseUrl(process.env.RELAYCAST_BASE_URL || process.env.RELAY_BASE_URL) }) const limit = normalizeReconcileLimit(input.limit) const messages = input.kind === 'channel' diff --git a/src/main/relay-service-urls.test.ts b/src/main/relay-service-urls.test.ts index e6d23bee..d1507b36 100644 --- a/src/main/relay-service-urls.test.ts +++ b/src/main/relay-service-urls.test.ts @@ -32,7 +32,15 @@ describe('relay service URL normalization', () => { }) }) - it('normalizes workspace handle info in place', () => { + it('normalizes nullish relay mount env to hosted defaults', () => { + expect(normalizeRelayServiceEnv(null)).toMatchObject({ + RELAYFILE_BASE_URL, + RELAYCAST_BASE_URL, + RELAY_BASE_URL: RELAYCAST_BASE_URL + }) + }) + + it('normalizes workspace handle info without mutating the input', () => { const handle = { info: { relayfileUrl: 'https://api.relayfile.dev', @@ -40,9 +48,16 @@ describe('relay service URL normalization', () => { } } - expect(normalizeRelayWorkspaceInfo(handle).info).toEqual({ + const normalized = normalizeRelayWorkspaceInfo(handle) + + expect(normalized.info).toEqual({ relayfileUrl: RELAYFILE_BASE_URL, relaycastBaseUrl: RELAYCAST_BASE_URL }) + expect(handle.info).toEqual({ + relayfileUrl: 'https://api.relayfile.dev', + relaycastBaseUrl: 'https://api.relaycast.dev' + }) + expect(normalized).not.toBe(handle) }) }) diff --git a/src/main/relay-service-urls.ts b/src/main/relay-service-urls.ts index fd6b83fc..9ff2f5c1 100644 --- a/src/main/relay-service-urls.ts +++ b/src/main/relay-service-urls.ts @@ -19,12 +19,13 @@ export function normalizeRelaycastBaseUrl(value: string | null | undefined): str return normalizeHostedServiceUrl(value, RELAYCAST_BASE_URL, LEGACY_RELAYCAST_HOSTS) } -export function normalizeRelayServiceEnv(env: Record): Record { +export function normalizeRelayServiceEnv(env?: Record | null): Record { + const safeEnv = env || {} return { - ...env, - RELAYFILE_BASE_URL: normalizeRelayfileBaseUrl(env.RELAYFILE_BASE_URL), - RELAYCAST_BASE_URL: normalizeRelaycastBaseUrl(env.RELAYCAST_BASE_URL || env.RELAY_BASE_URL), - RELAY_BASE_URL: normalizeRelaycastBaseUrl(env.RELAY_BASE_URL || env.RELAYCAST_BASE_URL) + ...safeEnv, + RELAYFILE_BASE_URL: normalizeRelayfileBaseUrl(safeEnv.RELAYFILE_BASE_URL), + RELAYCAST_BASE_URL: normalizeRelaycastBaseUrl(safeEnv.RELAYCAST_BASE_URL || safeEnv.RELAY_BASE_URL), + RELAY_BASE_URL: normalizeRelaycastBaseUrl(safeEnv.RELAY_BASE_URL || safeEnv.RELAYCAST_BASE_URL) } } @@ -34,11 +35,17 @@ export function normalizeRelayWorkspaceInfo(handle: T): T { - if (handle.info) { - handle.info.relayfileUrl = normalizeRelayfileBaseUrl(handle.info.relayfileUrl) - handle.info.relaycastBaseUrl = normalizeRelaycastBaseUrl(handle.info.relaycastBaseUrl) + if (!handle.info) { + return handle + } + return { + ...handle, + info: { + ...handle.info, + relayfileUrl: normalizeRelayfileBaseUrl(handle.info.relayfileUrl), + relaycastBaseUrl: normalizeRelaycastBaseUrl(handle.info.relaycastBaseUrl) + } } - return handle } function normalizeHostedServiceUrl( diff --git a/src/renderer/src/hooks/use-message-reconciliation.test.ts b/src/renderer/src/hooks/use-message-reconciliation.test.ts index 4ebccc1e..82a1d32c 100644 --- a/src/renderer/src/hooks/use-message-reconciliation.test.ts +++ b/src/renderer/src/hooks/use-message-reconciliation.test.ts @@ -369,6 +369,49 @@ describe('createMessageReconciler', () => { expect(mergeMessages).toHaveBeenNthCalledWith(4, [{ ...channelMessage, id: 'triage-2', to: '#triage', body: 'triage second' }]) }) + it('awaits a queued dirty rerun when runRequestNow joins an in-flight request', async () => { + const request = channelRequest('general') + const firstFetch = deferredMessages() + const secondFetch = deferredMessages() + const reconcileMessages = vi.fn() + .mockReturnValueOnce(firstFetch.promise) + .mockReturnValueOnce(secondFetch.promise) + const mergeMessages = vi.fn() + const reconciler = hooks.createMessageReconciler({ + getRequest: () => null, + reconcileMessages, + mergeMessages, + setTimeout: setTimeout as unknown as typeof window.setTimeout, + clearTimeout: clearTimeout as unknown as typeof window.clearTimeout, + debounceMs: 1 + }) + let secondRunSettled = false + + void reconciler.runRequestNow(request, 'broker-event:first') + await Promise.resolve() + + const secondRun = reconciler.runRequestNow(request, 'broker-event:second') + secondRun.then(() => { + secondRunSettled = true + }) + await Promise.resolve() + + expect(reconcileMessages).toHaveBeenCalledTimes(1) + + firstFetch.resolve([{ ...channelMessage, id: 'general-1', body: 'general first' }]) + await Promise.resolve() + await Promise.resolve() + + expect(reconcileMessages).toHaveBeenCalledTimes(2) + expect(secondRunSettled).toBe(false) + + secondFetch.resolve([{ ...channelMessage, id: 'general-2', body: 'general second' }]) + await secondRun + + expect(secondRunSettled).toBe(true) + expect(mergeMessages).toHaveBeenCalledTimes(2) + }) + it('cleans up keyed timers and suppresses in-flight merges after dispose', async () => { vi.useFakeTimers() const generalRequest = channelRequest('general') diff --git a/src/renderer/src/hooks/use-message-reconciliation.ts b/src/renderer/src/hooks/use-message-reconciliation.ts index bd100364..8fd831d4 100644 --- a/src/renderer/src/hooks/use-message-reconciliation.ts +++ b/src/renderer/src/hooks/use-message-reconciliation.ts @@ -198,7 +198,12 @@ export function createMessageReconciler(deps: MessageReconcilerDeps): MessageRec if (existingRun) { pendingRequestReruns.set(requestKey, { request, reason }) debug({ kind: 'scheduled', reason }) - return existingRun + await existingRun + const followUpRun = requestInFlight.get(requestKey) + if (followUpRun && followUpRun !== existingRun) { + await followUpRun + } + return } const currentRun = (async () => { @@ -404,10 +409,11 @@ export function useMessageReconciliation(): void { useEffect(() => { return pear?.broker?.onEvent?.((event) => { + if (!event) return const brokerEvent = event as BrokerEventLike const project = useProjectStore.getState() const projectId = brokerEvent.projectId || project.activeProjectId - const knownChannelNames = project.projects.find((entry) => entry.id === projectId)?.channels || [] + const knownChannelNames = project.projects?.find((entry) => entry.id === projectId)?.channels || [] const request = getBrokerEventMessageReconciliationRequest({ event: brokerEvent, fallbackProjectId: project.activeProjectId,