Jelajahi Sumber

Task metadata (#7092)

Chris Estreich 4 bulan lalu
induk
melakukan
c56b95bc60

+ 8 - 2
package.json

@@ -24,15 +24,20 @@
 		"knip": "knip --include files",
 		"update-contributors": "node scripts/update-contributors.js",
 		"evals": "dotenvx run -f packages/evals/.env.development packages/evals/.env.local -- docker compose -f packages/evals/docker-compose.yml --profile server --profile runner up --build --scale runner=0",
-		"link-workspace-packages": "node scripts/link-packages.js",
-		"unlink-workspace-packages": "node scripts/link-packages.js --unlink"
+		"npm:publish:types": "pnpm --filter @roo-code/types npm:publish",
+		"link-workspace-packages": "tsx scripts/link-packages.ts",
+		"unlink-workspace-packages": "tsx scripts/link-packages.ts --unlink"
 	},
 	"devDependencies": {
 		"@changesets/cli": "^2.27.10",
 		"@dotenvx/dotenvx": "^1.34.0",
+		"@roo-code/config-typescript": "workspace:^",
+		"@types/glob": "^9.0.0",
+		"@types/node": "^24.1.0",
 		"@vscode/vsce": "3.3.2",
 		"esbuild": "^0.25.0",
 		"eslint": "^9.27.0",
+		"glob": "^11.0.3",
 		"husky": "^9.1.7",
 		"knip": "^5.44.4",
 		"lint-staged": "^16.0.0",
@@ -41,6 +46,7 @@
 		"ovsx": "0.10.4",
 		"prettier": "^3.4.2",
 		"rimraf": "^6.0.1",
+		"tsx": "^4.19.3",
 		"turbo": "^2.5.3",
 		"typescript": "^5.4.5"
 	},

+ 2 - 0
packages/types/.gitignore

@@ -0,0 +1,2 @@
+dist
+npm/package.json

+ 17 - 1
packages/types/eslint.config.mjs

@@ -1,4 +1,20 @@
 import { config } from "@roo-code/config-eslint/base"
+import globals from "globals"
 
 /** @type {import("eslint").Linter.Config} */
-export default [...config]
+export default [
+	...config,
+	{
+		files: ["**/*.cjs"],
+		languageOptions: {
+			globals: {
+				...globals.node,
+				...globals.commonjs,
+			},
+			sourceType: "commonjs",
+		},
+		rules: {
+			"@typescript-eslint/no-require-imports": "off",
+		},
+	},
+]

+ 3 - 25
packages/types/npm/package.json → packages/types/npm/package.metadata.json

@@ -1,6 +1,6 @@
 {
 	"name": "@roo-code/types",
-	"version": "1.46.0",
+	"version": "1.48.0",
 	"description": "TypeScript type definitions for Roo Code.",
 	"publishConfig": {
 		"access": "public",
@@ -17,28 +17,6 @@
 		"url": "https://github.com/RooCodeInc/Roo-Code/issues"
 	},
 	"homepage": "https://roocode.com",
-	"keywords": [
-		"roo",
-		"roo-code",
-		"ai"
-	],
-	"main": "./dist/index.cjs",
-	"module": "./dist/index.js",
-	"types": "./dist/index.d.ts",
-	"exports": {
-		".": {
-			"types": "./dist/index.d.ts",
-			"import": "./dist/index.js",
-			"require": {
-				"types": "./dist/index.d.cts",
-				"default": "./dist/index.cjs"
-			}
-		}
-	},
-	"files": [
-		"dist"
-	],
-	"dependencies": {
-		"zod": "^3.25.61"
-	}
+	"keywords": ["roo", "roo-code", "ai"],
+	"files": ["dist"]
 }

+ 5 - 3
packages/types/package.json

@@ -18,8 +18,9 @@
 		"check-types": "tsc --noEmit",
 		"test": "vitest run",
 		"build": "tsup",
-		"npm:publish": "cd npm && npm version minor && cd - && tsup --outDir npm/dist && cd npm && npm publish",
-		"clean": "rimraf dist npm/dist .turbo"
+		"build:watch": "tsup --watch --outDir npm/dist --onSuccess 'echo ✅ Types rebuilt to npm/dist'",
+		"npm:publish": "node scripts/publish-npm.cjs",
+		"clean": "rimraf dist .turbo"
 	},
 	"dependencies": {
 		"zod": "^3.25.61"
@@ -27,7 +28,8 @@
 	"devDependencies": {
 		"@roo-code/config-eslint": "workspace:^",
 		"@roo-code/config-typescript": "workspace:^",
-		"@types/node": "20.x",
+		"@types/node": "^24.1.0",
+		"globals": "^16.3.0",
 		"tsup": "^8.3.5",
 		"vitest": "^3.2.3"
 	}

+ 351 - 0
packages/types/scripts/publish-npm.cjs

@@ -0,0 +1,351 @@
+/* eslint-env node */
+
+const fs = require("fs")
+const path = require("path")
+const { execSync } = require("child_process")
+const readline = require("readline")
+
+const PACKAGE_NAME = "@roo-code/types"
+const BRANCH_NAME = "roo-code-types-v"
+
+const rootDir = path.join(__dirname, "..")
+const npmDir = path.join(rootDir, "npm")
+const monorepoPackagePath = path.join(rootDir, "package.json")
+const npmMetadataPath = path.join(npmDir, "package.metadata.json")
+const npmPackagePath = path.join(npmDir, "package.json")
+
+const args = process.argv.slice(2)
+const publishOnly = args.includes("--publish-only")
+
+async function confirmPublish() {
+	const rl = readline.createInterface({
+		input: process.stdin,
+		output: process.stdout,
+	})
+
+	return new Promise((resolve) => {
+		rl.question("\n⚠️  Are you sure you want to publish to npm? (y/n): ", (answer) => {
+			rl.close()
+			resolve(answer.toLowerCase() === "y")
+		})
+	})
+}
+
+function updatePackageVersion(filePath, version) {
+	try {
+		const packageContent = JSON.parse(fs.readFileSync(filePath, "utf8"))
+		const oldVersion = packageContent.version
+		packageContent.version = version
+		fs.writeFileSync(filePath, JSON.stringify(packageContent, null, 2) + "\n")
+
+		try {
+			execSync(`npx prettier --write "${filePath}"`, { stdio: "pipe" })
+			console.log(`✨ Formatted ${path.basename(filePath)} with prettier`)
+		} catch (prettierError) {
+			console.warn(`⚠️  Could not format with prettier:`, prettierError.message)
+		}
+
+		const fileName = path.basename(filePath)
+		console.log(`✅ Updated ${fileName} version: ${oldVersion} → ${version}`)
+		return oldVersion
+	} catch (error) {
+		throw new Error(`Failed to update version in ${path.basename(filePath)}: ${error.message}`)
+	}
+}
+
+function syncVersionToMetadata(version) {
+	console.log("  📝 Syncing version to package.metadata.json...")
+	updatePackageVersion(npmMetadataPath, version)
+}
+
+function commitVersionChanges(version) {
+	try {
+		console.log("  📝 Committing version changes to git...")
+
+		try {
+			const status = execSync("git status --porcelain", { encoding: "utf8" })
+			const relevantChanges = status.split("\n").filter((line) => line.includes("packages/sdk/npm/package"))
+
+			if (relevantChanges.length === 0) {
+				console.log("  ⚠️  No version changes to commit")
+				return
+			}
+		} catch (error) {
+			console.warn("  ⚠️  Could not check git status:", error.message)
+		}
+
+		execSync("git add .", { stdio: "pipe" })
+		const commitMessage = `chore: bump version to v${version}`
+		execSync(`git commit -m "${commitMessage}"`, { stdio: "pipe" })
+		console.log(`  ✅ Committed: ${commitMessage}`)
+	} catch (error) {
+		console.warn("  ⚠️  Could not commit version changes:", error.message)
+		console.log("     You may need to commit these changes manually.")
+	}
+}
+
+function checkGitHubCLI() {
+	try {
+		execSync("gh --version", { stdio: "pipe" })
+		execSync("gh auth status", { stdio: "pipe" })
+		return true
+	} catch (_error) {
+		return false
+	}
+}
+
+function createPullRequest(branchName, baseBranch, version) {
+	try {
+		console.log(`  🔄 Creating pull request...`)
+
+		if (!checkGitHubCLI()) {
+			console.warn("  ⚠️  GitHub CLI not found or not authenticated")
+			console.log("     Install gh CLI and run: gh auth login")
+			console.log("     Then manually create PR with: gh pr create")
+			return
+		}
+
+		const title = `Release: v${version}`
+		const body = `## 🚀 Release v${version}
+
+This PR contains the version bump for the SDK release v${version}.
+
+### Changes
+- Bumped version from previous to v${version}
+- Published to npm as ${PACKAGE_NAME}@${version}
+
+### Checklist
+- [x] Version bumped
+- [x] Package published to npm
+- [ ] Changelog updated (if applicable)
+- [ ] Documentation updated (if applicable)
+
+---
+*This PR was automatically created by the npm publish script.*`
+
+		try {
+			// Create the pull request
+			const prUrl = execSync(
+				`gh pr create --base "${baseBranch}" --head "${branchName}" --title "${title}" --body "${body}"`,
+				{ encoding: "utf8", stdio: "pipe" },
+			).trim()
+
+			console.log(`  ✅ Pull request created: ${prUrl}`)
+		} catch (error) {
+			if (error.message.includes("already exists")) {
+				console.log("  ℹ️  Pull request already exists for this branch")
+			} else {
+				throw error
+			}
+		}
+	} catch (error) {
+		console.error("  ❌ Failed to create pull request:", error.message)
+		console.log("     You can manually create a PR with:")
+		console.log(`     gh pr create --base "${baseBranch}" --head "${branchName}"`)
+	}
+}
+
+function createVersionBranchAndCommit(version) {
+	try {
+		const branchName = `${BRANCH_NAME}${version}`
+		console.log(`  🌿 Creating version branch: ${branchName}...`)
+
+		let currentBranch
+
+		try {
+			currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
+				encoding: "utf8",
+			}).trim()
+		} catch (_error) {
+			console.warn("  ⚠️  Could not determine current branch")
+			currentBranch = "main"
+		}
+
+		execSync(`git checkout -b ${branchName}`, { stdio: "pipe" })
+		console.log(`  ✅ Created branch: ${branchName}`)
+		commitVersionChanges(version)
+		execSync(`git push --set-upstream origin ${branchName}`, { stdio: "pipe" })
+		console.log(`  ✅ Pushed branch to origin with upstream tracking`)
+		createPullRequest(branchName, currentBranch, version)
+
+		if (currentBranch) {
+			execSync(`git checkout ${currentBranch}`, { stdio: "pipe" })
+			console.log(`  ✅ Returned to branch: ${currentBranch}`)
+		}
+
+		console.log(`  🎯 Version branch created with commits: ${branchName}`)
+	} catch (error) {
+		console.error("  ❌ Failed to create version branch:", error.message)
+		console.log("     You may need to create the branch manually.")
+	}
+}
+
+function generateNpmPackage() {
+	try {
+		console.log("  📖 Reading monorepo package.json...")
+
+		const monorepoPackageContent = fs.readFileSync(monorepoPackagePath, "utf8")
+		const monorepoPackage = JSON.parse(monorepoPackageContent)
+
+		console.log("  📖 Reading npm package metadata...")
+
+		const npmMetadataContent = fs.readFileSync(npmMetadataPath, "utf8")
+		const npmMetadata = JSON.parse(npmMetadataContent)
+
+		console.log("  🔨 Generating npm package.json...")
+
+		const npmPackage = {
+			...npmMetadata,
+			dependencies: monorepoPackage.dependencies || {},
+			main: "./dist/index.cjs",
+			module: "./dist/index.js",
+			types: "./dist/index.d.ts",
+			exports: {
+				".": {
+					types: "./dist/index.d.ts",
+					import: "./dist/index.js",
+					require: {
+						types: "./dist/index.d.cts",
+						default: "./dist/index.cjs",
+					},
+				},
+			},
+			files: ["dist"],
+		}
+
+		const outputContent = JSON.stringify(npmPackage, null, 2) + "\n"
+		fs.writeFileSync(npmPackagePath, outputContent)
+
+		console.log("  ✅ npm/package.json generated successfully")
+		console.log(`  📦 Package name: ${npmPackage.name}`)
+		console.log(`  📌 Version: ${npmPackage.version}`)
+		console.log(`  📚 Dependencies: ${Object.keys(npmPackage.dependencies).length}`)
+	} catch (error) {
+		throw new Error(`Failed to generate npm package.json: ${error.message}`)
+	}
+}
+
+async function publish() {
+	try {
+		console.log("\n🚀 NPM PUBLISH WORKFLOW")
+		if (publishOnly) {
+			console.log("📌 Mode: Publish only (no git operations)")
+		}
+		console.log("=".repeat(60))
+
+		console.log("\n📦 Step 1: Generating npm package.json...")
+		generateNpmPackage()
+
+		const npmPackage = JSON.parse(fs.readFileSync(npmPackagePath, "utf8"))
+		const originalVersion = npmPackage.version // Save original version
+		console.log(`\n📌 Current version: ${npmPackage.version}`)
+		console.log(`📦 Package name: ${npmPackage.name}`)
+
+		console.log("\n📈 Step 2: Bumping version (minor)...")
+
+		try {
+			execSync("npm version minor --no-git-tag-version", {
+				cwd: npmDir,
+				stdio: "inherit",
+			})
+		} catch (error) {
+			console.error("❌ Failed to bump version:", error.message)
+			throw error
+		}
+
+		const updatedPackage = JSON.parse(fs.readFileSync(npmPackagePath, "utf8"))
+		console.log(`✅ New version: ${updatedPackage.version}`)
+
+		console.log("\n🔨 Step 3: Building production bundle...")
+		console.log("  This may take a moment...")
+
+		try {
+			execSync("NODE_ENV=production pnpm tsup --outDir npm/dist", {
+				cwd: rootDir,
+				stdio: "inherit",
+			})
+
+			console.log("✅ Production build complete")
+		} catch (error) {
+			console.error("❌ Build failed:", error.message)
+			throw error
+		}
+
+		console.log("\n" + "=".repeat(60))
+		console.log("📋 PUBLISH SUMMARY:")
+		console.log(`   Package: ${updatedPackage.name}`)
+		console.log(`   Version: ${updatedPackage.version}`)
+		console.log(`   Registry: ${updatedPackage.publishConfig?.registry || "https://registry.npmjs.org/"}`)
+		console.log(`   Access: ${updatedPackage.publishConfig?.access || "public"}`)
+		console.log("=".repeat(60))
+
+		const confirmed = await confirmPublish()
+
+		if (!confirmed) {
+			console.log("\n❌ Publishing cancelled by user")
+			console.log("🔙 Reverting version change...")
+
+			try {
+				updatePackageVersion(npmPackagePath, originalVersion)
+			} catch (revertError) {
+				console.error("⚠️  Could not revert version:", revertError.message)
+				console.log(`   You may need to manually change version back to ${originalVersion}`)
+			}
+
+			process.exit(0)
+		}
+
+		console.log("\n💾 Step 4: Syncing version to metadata...")
+		syncVersionToMetadata(updatedPackage.version)
+
+		console.log("\n🚀 Step 5: Publishing to npm...")
+
+		try {
+			execSync("npm publish", {
+				cwd: npmDir,
+				stdio: "inherit",
+			})
+		} catch (error) {
+			console.error("❌ Publish failed:", error.message)
+			console.error("💡 The package was built but not published.")
+			console.error("   You can try publishing manually from the npm directory.")
+
+			throw error
+		}
+
+		if (!publishOnly) {
+			console.log("\n🌿 Step 6: Creating version branch, committing, and opening PR...")
+			createVersionBranchAndCommit(updatedPackage.version)
+		} else {
+			console.log("\n📝 Step 6: Skipping version branch creation (--publish-only mode)")
+		}
+
+		console.log("\n" + "=".repeat(60))
+		console.log("✅ Successfully published to npm!")
+		console.log(`🎉 ${updatedPackage.name}@${updatedPackage.version} is now live`)
+		console.log(`📦 View at: https://www.npmjs.com/package/${updatedPackage.name}`)
+
+		if (!publishOnly) {
+			console.log(`🌿 Version branch: ${BRANCH_NAME}${updatedPackage.version}`)
+		}
+
+		console.log("=".repeat(60) + "\n")
+	} catch (error) {
+		console.error("\n❌ Error during publish process:", error.message)
+		console.error("\n💡 Troubleshooting tips:")
+		console.error("   1. Ensure you are logged in to npm: npm whoami")
+		console.error("   2. Check your npm permissions for this package")
+		console.error("   3. Verify the package name is not already taken")
+		console.error("   4. Make sure all dependencies are installed: pnpm install")
+		process.exit(1)
+	}
+}
+
+async function main() {
+	await publish()
+}
+
+main().catch((error) => {
+	console.error("Unexpected error:", error)
+	process.exit(1)
+})

+ 11 - 0
packages/types/src/task.ts

@@ -1,3 +1,5 @@
+import { z } from "zod"
+
 import { RooCodeEventName } from "./events.js"
 import { type ClineMessage, type BlockingAsk, type TokenUsage } from "./message.js"
 import { type ToolUsage, type ToolName } from "./tool.js"
@@ -61,10 +63,19 @@ export type TaskProviderEvents = {
  * TaskLike
  */
 
+export const taskMetadataSchema = z.object({
+	taskId: z.string(),
+	task: z.string().optional(),
+	images: z.array(z.string()).optional(),
+})
+
+export type TaskMetadata = z.infer<typeof taskMetadataSchema>
+
 export interface TaskLike {
 	readonly taskId: string
 	readonly rootTask?: TaskLike
 	readonly blockingAsk?: BlockingAsk
+	readonly metadata: TaskMetadata
 
 	on<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this
 	off<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this

+ 1 - 1
packages/types/tsconfig.json

@@ -4,6 +4,6 @@
 		"types": ["vitest/globals"],
 		"outDir": "dist"
 	},
-	"include": ["src"],
+	"include": ["src", "scripts", "*.config.ts"],
 	"exclude": ["node_modules"]
 }

+ 126 - 61
pnpm-lock.yaml

@@ -22,6 +22,15 @@ importers:
       '@dotenvx/dotenvx':
         specifier: ^1.34.0
         version: 1.44.2
+      '@roo-code/config-typescript':
+        specifier: workspace:^
+        version: link:packages/config-typescript
+      '@types/glob':
+        specifier: ^9.0.0
+        version: 9.0.0
+      '@types/node':
+        specifier: ^24.1.0
+        version: 24.2.1
       '@vscode/vsce':
         specifier: 3.3.2
         version: 3.3.2
@@ -31,12 +40,15 @@ importers:
       eslint:
         specifier: ^9.27.0
         version: 9.28.0([email protected])
+      glob:
+        specifier: ^11.0.3
+        version: 11.0.3
       husky:
         specifier: ^9.1.7
         version: 9.1.7
       knip:
         specifier: ^5.44.4
-        version: 5.60.2(@types/[email protected]5.29)([email protected])
+        version: 5.60.2(@types/node@24.2.1)([email protected])
       lint-staged:
         specifier: ^16.0.0
         version: 16.1.2
@@ -55,6 +67,9 @@ importers:
       rimraf:
         specifier: ^6.0.1
         version: 6.0.1
+      tsx:
+        specifier: ^4.19.3
+        version: 4.19.4
       turbo:
         specifier: ^2.5.3
         version: 2.5.4
@@ -235,7 +250,7 @@ importers:
         version: 4.1.6
       vitest:
         specifier: ^3.2.3
-        version: 3.2.4(@types/[email protected])(@types/[email protected]5.29)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
+        version: 3.2.4(@types/[email protected])(@types/node@24.2.1)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
 
   apps/web-roo-code:
     dependencies:
@@ -521,14 +536,17 @@ importers:
         specifier: workspace:^
         version: link:../config-typescript
       '@types/node':
-        specifier: 20.x
-        version: 20.17.57
+        specifier: ^24.1.0
+        version: 24.2.1
+      globals:
+        specifier: ^16.3.0
+        version: 16.3.0
       tsup:
         specifier: ^8.3.5
         version: 8.5.0([email protected])([email protected])([email protected])([email protected])([email protected])
       vitest:
         specifier: ^3.2.3
-        version: 3.2.4(@types/[email protected])(@types/node@20.17.57)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
+        version: 3.2.4(@types/[email protected])(@types/node@24.2.1)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
 
   src:
     dependencies:
@@ -563,8 +581,8 @@ importers:
         specifier: ^1.14.0
         version: 1.14.0([email protected])
       '@roo-code/cloud':
-        specifier: ^0.13.0
-        version: 0.13.0
+        specifier: ^0.14.0
+        version: 0.14.0
       '@roo-code/ipc':
         specifier: workspace:^
         version: link:../packages/ipc
@@ -703,6 +721,9 @@ importers:
       simple-git:
         specifier: ^3.27.0
         version: 3.27.0
+      socket.io-client:
+        specifier: ^4.8.1
+        version: 4.8.1
       sound-play:
         specifier: ^1.1.0
         version: 1.1.0
@@ -1936,6 +1957,14 @@ packages:
   '@ioredis/[email protected]':
     resolution: {integrity: sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==}
 
+  '@isaacs/[email protected]':
+    resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==}
+    engines: {node: 20 || >=22}
+
+  '@isaacs/[email protected]':
+    resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==}
+    engines: {node: 20 || >=22}
+
   '@isaacs/[email protected]':
     resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
     engines: {node: '>=12'}
@@ -3065,11 +3094,11 @@ packages:
     cpu: [x64]
     os: [win32]
 
-  '@roo-code/[email protected]3.0':
-    resolution: {integrity: sha512-pIrfMpuHSk3LPMG3SLmsfr/IYvL5x0k0iUXQ51dgZ2fP7TlzWg2erxclNl79O7pRMDYFnA/8YoQTAnv1N8VXnQ==}
+  '@roo-code/[email protected]4.0':
+    resolution: {integrity: sha512-EKFw7sLSJcOpXQDjCMFL4EnE7OH3RhO00joteaysNYNpNe+js6kMFrBZY61rj1tZAXV/943RWeQptG4sSAfPFQ==}
 
-  '@roo-code/[email protected]5.0':
-    resolution: {integrity: sha512-+rFJ9HcukDl59X9pI6lgZDtJHtKqHg6BIdS4LG759X+5MV7wvlNb4S+bQ3qKqIlBsQhH17bzIJCfsWZwjwxecw==}
+  '@roo-code/[email protected]8.0':
+    resolution: {integrity: sha512-DJkA/310sLcs4vM4YLhJhc5A1JFYmdcOt+hRQMUPq+wF02yW9rc7ZZzHNDOOUZL+kk5naUd2nBRxLiJoh6o0Ng==}
 
   '@sec-ant/[email protected]':
     resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
@@ -3801,6 +3830,10 @@ packages:
   '@types/[email protected]':
     resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==}
 
+  '@types/[email protected]':
+    resolution: {integrity: sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==}
+    deprecated: This is a stub types definition. glob provides its own type definitions, so you do not need this installed.
+
   '@types/[email protected]':
     resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
 
@@ -3871,14 +3904,8 @@ packages:
   '@types/[email protected]':
     resolution: {integrity: sha512-f3T4y6VU4fVQDKVqJV4Uppy8c1p/sVvS3peyqxyWnzkqXFJLRU7Y1Bl7rMS1Qe9z0v4M6McY0Fp9yBsgHJUsWQ==}
 
-  '@types/[email protected]':
-    resolution: {integrity: sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==}
-
-  '@types/[email protected]':
-    resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==}
-
-  '@types/[email protected]':
-    resolution: {integrity: sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==}
+  '@types/[email protected]':
+    resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==}
 
   '@types/[email protected]':
     resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
@@ -6000,6 +6027,11 @@ packages:
     engines: {node: 20 || >=22}
     hasBin: true
 
+  [email protected]:
+    resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==}
+    engines: {node: 20 || >=22}
+    hasBin: true
+
   [email protected]:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
     deprecated: Glob versions prior to v9 are no longer supported
@@ -6020,6 +6052,10 @@ packages:
     resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==}
     engines: {node: '>=18'}
 
+  [email protected]:
+    resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==}
+    engines: {node: '>=18'}
+
   [email protected]:
     resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
     engines: {node: '>= 0.4'}
@@ -6571,6 +6607,10 @@ packages:
     resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==}
     engines: {node: 20 || >=22}
 
+  [email protected]:
+    resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==}
+    engines: {node: 20 || >=22}
+
   [email protected]:
     resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -7333,6 +7373,10 @@ packages:
     resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
     engines: {node: 20 || >=22}
 
+  [email protected]:
+    resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==}
+    engines: {node: 20 || >=22}
+
   [email protected]:
     resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
 
@@ -8675,6 +8719,7 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
     engines: {node: '>= 8'}
+    deprecated: The work that was done in this beta branch won't be included in future versions
 
   [email protected]:
     resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==}
@@ -9249,8 +9294,8 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
 
-  undici-types@6.21.0:
-    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+  undici-types@7.10.0:
+    resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==}
 
   [email protected]:
     resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==}
@@ -11133,6 +11178,12 @@ snapshots:
 
   '@ioredis/[email protected]': {}
 
+  '@isaacs/[email protected]': {}
+
+  '@isaacs/[email protected]':
+    dependencies:
+      '@isaacs/balanced-match': 4.0.1
+
   '@isaacs/[email protected]':
     dependencies:
       string-width: 5.1.2
@@ -11161,7 +11212,7 @@ snapshots:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.6
       '@types/istanbul-reports': 3.0.4
-      '@types/node': 20.17.57
+      '@types/node': 24.2.1
       '@types/yargs': 17.0.33
       chalk: 4.1.2
 
@@ -12234,9 +12285,9 @@ snapshots:
   '@rollup/[email protected]':
     optional: true
 
-  '@roo-code/[email protected]3.0':
+  '@roo-code/[email protected]4.0':
     dependencies:
-      '@roo-code/types': 1.45.0
+      '@roo-code/types': 1.48.0
       ioredis: 5.6.1
       p-wait-for: 5.0.2
       socket.io-client: 4.8.1
@@ -12246,7 +12297,7 @@ snapshots:
       - supports-color
       - utf-8-validate
 
-  '@roo-code/[email protected]5.0':
+  '@roo-code/[email protected]8.0':
     dependencies:
       zod: 3.25.76
 
@@ -13159,7 +13210,11 @@ snapshots:
   '@types/[email protected]':
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.17.57
+      '@types/node': 24.2.1
+
+  '@types/[email protected]':
+    dependencies:
+      glob: 11.0.3
 
   '@types/[email protected]':
     dependencies:
@@ -13212,12 +13267,12 @@ snapshots:
 
   '@types/[email protected]':
     dependencies:
-      '@types/node': 20.17.57
+      '@types/node': 24.2.1
       form-data: 4.0.4
 
   '@types/[email protected]':
     dependencies:
-      '@types/node': 20.17.57
+      '@types/node': 24.2.1
 
   '@types/[email protected]': {}
 
@@ -13235,18 +13290,9 @@ snapshots:
     dependencies:
       undici-types: 6.19.8
 
-  '@types/[email protected]':
-    dependencies:
-      undici-types: 6.21.0
-
-  '@types/[email protected]':
-    dependencies:
-      undici-types: 6.21.0
-    optional: true
-
-  '@types/[email protected]':
+  '@types/[email protected]':
     dependencies:
-      undici-types: 6.21.0
+      undici-types: 7.10.0
 
   '@types/[email protected]': {}
 
@@ -13277,11 +13323,11 @@ snapshots:
 
   '@types/[email protected]':
     dependencies:
-      '@types/node': 20.19.1
+      '@types/node': 24.2.1
 
   '@types/[email protected]':
     dependencies:
-      '@types/node': 20.19.1
+      '@types/node': 24.2.1
       '@types/stream-chain': 2.1.0
 
   '@types/[email protected]': {}
@@ -13307,7 +13353,7 @@ snapshots:
 
   '@types/[email protected]':
     dependencies:
-      '@types/node': 20.19.9
+      '@types/node': 24.2.1
     optional: true
 
   '@types/[email protected]': {}
@@ -13318,7 +13364,7 @@ snapshots:
 
   '@types/[email protected]':
     dependencies:
-      '@types/node': 20.17.50
+      '@types/node': 24.2.1
     optional: true
 
   '@typescript-eslint/[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])':
@@ -13443,13 +13489,13 @@ snapshots:
     optionalDependencies:
       vite: 6.3.5(@types/[email protected])([email protected])([email protected])([email protected])([email protected])
 
-  '@vitest/[email protected]([email protected](@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected]))':
+  '@vitest/[email protected]([email protected](@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected]))':
     dependencies:
       '@vitest/spy': 3.2.4
       estree-walker: 3.0.3
       magic-string: 0.30.17
     optionalDependencies:
-      vite: 6.3.5(@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected])
+      vite: 6.3.5(@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected])
 
   '@vitest/[email protected]':
     dependencies:
@@ -13480,7 +13526,7 @@ snapshots:
       sirv: 3.0.1
       tinyglobby: 0.2.14
       tinyrainbow: 2.0.0
-      vitest: 3.2.4(@types/[email protected])(@types/[email protected]5.29)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
+      vitest: 3.2.4(@types/[email protected])(@types/node@24.2.1)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
 
   '@vitest/[email protected]':
     dependencies:
@@ -13561,7 +13607,7 @@ snapshots:
       cockatiel: 3.2.1
       commander: 12.1.0
       form-data: 4.0.4
-      glob: 11.0.2
+      glob: 11.0.3
       hosted-git-info: 4.1.0
       jsonc-parser: 3.3.1
       leven: 3.1.0
@@ -15691,6 +15737,15 @@ snapshots:
       package-json-from-dist: 1.0.1
       path-scurry: 2.0.0
 
+  [email protected]:
+    dependencies:
+      foreground-child: 3.3.1
+      jackspeak: 4.1.1
+      minimatch: 10.0.3
+      minipass: 7.1.2
+      package-json-from-dist: 1.0.1
+      path-scurry: 2.0.0
+
   [email protected]:
     dependencies:
       fs.realpath: 1.0.0
@@ -15708,6 +15763,8 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]:
     dependencies:
       define-properties: 1.2.1
@@ -16297,6 +16354,10 @@ snapshots:
     dependencies:
       '@isaacs/cliui': 8.0.2
 
+  [email protected]:
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+
   [email protected]:
     dependencies:
       chalk: 4.1.2
@@ -16328,7 +16389,7 @@ snapshots:
   [email protected]:
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.17.57
+      '@types/node': 24.2.1
       chalk: 4.1.2
       ci-info: 3.9.0
       graceful-fs: 4.2.11
@@ -16488,10 +16549,10 @@ snapshots:
 
   [email protected]: {}
 
-  [email protected](@types/[email protected]5.29)([email protected]):
+  [email protected](@types/node@24.2.1)([email protected]):
     dependencies:
       '@nodelib/fs.walk': 1.2.8
-      '@types/node': 22.15.29
+      '@types/node': 24.2.1
       fast-glob: 3.3.3
       formatly: 0.2.4
       jiti: 2.4.2
@@ -17322,6 +17383,10 @@ snapshots:
     dependencies:
       brace-expansion: 4.0.1
 
+  [email protected]:
+    dependencies:
+      '@isaacs/brace-expansion': 5.0.0
+
   [email protected]:
     dependencies:
       brace-expansion: 4.0.1
@@ -18544,7 +18609,7 @@ snapshots:
 
   [email protected]:
     dependencies:
-      glob: 11.0.2
+      glob: 11.0.3
       package-json-from-dist: 1.0.1
 
   [email protected]: {}
@@ -19531,7 +19596,7 @@ snapshots:
 
   [email protected]: {}
 
-  undici-types@6.21.0: {}
+  undici-types@7.10.0: {}
 
   [email protected]: {}
 
@@ -19806,13 +19871,13 @@ snapshots:
       - tsx
       - yaml
 
-  [email protected](@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected]):
+  [email protected](@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected]):
     dependencies:
       cac: 6.7.14
       debug: 4.4.1([email protected])
       es-module-lexer: 1.7.0
       pathe: 2.0.3
-      vite: 6.3.5(@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected])
+      vite: 6.3.5(@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected])
     transitivePeerDependencies:
       - '@types/node'
       - jiti
@@ -19859,7 +19924,7 @@ snapshots:
       tsx: 4.19.4
       yaml: 2.8.0
 
-  [email protected](@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected]):
+  [email protected](@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected]):
     dependencies:
       esbuild: 0.25.5
       fdir: 6.4.4([email protected])
@@ -19868,7 +19933,7 @@ snapshots:
       rollup: 4.40.2
       tinyglobby: 0.2.13
     optionalDependencies:
-      '@types/node': 22.15.29
+      '@types/node': 24.2.1
       fsevents: 2.3.3
       jiti: 2.4.2
       lightningcss: 1.30.1
@@ -19963,11 +20028,11 @@ snapshots:
       - tsx
       - yaml
 
-  [email protected](@types/[email protected])(@types/[email protected]5.29)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]):
+  [email protected](@types/[email protected])(@types/node@24.2.1)(@vitest/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]):
     dependencies:
       '@types/chai': 5.2.2
       '@vitest/expect': 3.2.4
-      '@vitest/mocker': 3.2.4([email protected](@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected]))
+      '@vitest/mocker': 3.2.4([email protected](@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected]))
       '@vitest/pretty-format': 3.2.4
       '@vitest/runner': 3.2.4
       '@vitest/snapshot': 3.2.4
@@ -19985,12 +20050,12 @@ snapshots:
       tinyglobby: 0.2.14
       tinypool: 1.1.1
       tinyrainbow: 2.0.0
-      vite: 6.3.5(@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected])
-      vite-node: 3.2.4(@types/[email protected]5.29)([email protected])([email protected])([email protected])([email protected])
+      vite: 6.3.5(@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected])
+      vite-node: 3.2.4(@types/node@24.2.1)([email protected])([email protected])([email protected])([email protected])
       why-is-node-running: 2.3.0
     optionalDependencies:
       '@types/debug': 4.1.12
-      '@types/node': 22.15.29
+      '@types/node': 24.2.1
       '@vitest/ui': 3.2.4([email protected])
       jsdom: 26.1.0
     transitivePeerDependencies:

+ 0 - 158
scripts/link-packages.js

@@ -1,158 +0,0 @@
-#!/usr/bin/env node
-
-const { spawn, execSync } = require("child_process")
-const path = require("path")
-const fs = require("fs")
-
-// Package configuration - Add new packages here as needed.
-const config = {
-	packages: [
-		{
-			name: "@roo-code/cloud",
-			sourcePath: "../Roo-Code-Cloud/packages/sdk",
-			targetPath: "src/node_modules/@roo-code/cloud",
-			npmPath: "npm",
-			watchCommand: "pnpm build:development:watch",
-		},
-	],
-}
-
-const args = process.argv.slice(2)
-const packageName = args.find((arg) => !arg.startsWith("--"))
-const watch = !args.includes("--no-watch")
-const unlink = args.includes("--unlink")
-
-const packages = packageName ? config.packages.filter((p) => p.name === packageName) : config.packages
-
-if (!packages.length) {
-	console.error(`Package '${packageName}' not found`)
-	process.exit(1)
-}
-
-packages.forEach(unlink ? unlinkPackage : linkPackage)
-
-// After unlinking, restore npm packages with a single pnpm install.
-if (unlink && packages.length > 0) {
-	const srcPath = path.resolve(__dirname, "..", "src")
-	console.log("\nRestoring npm packages...")
-
-	try {
-		execSync("pnpm install", { cwd: srcPath, stdio: "inherit" })
-		console.log("Successfully restored npm packages")
-	} catch (error) {
-		console.error(`Failed to restore packages: ${error.message}`)
-		console.log("You may need to run 'pnpm install' manually in the src directory")
-	}
-}
-
-if (!unlink && watch) {
-	const watchers = packages.filter((pkg) => pkg.watchCommand).map(startWatch)
-
-	if (watchers.length) {
-		process.on("SIGINT", () => {
-			console.log("\nStopping...")
-			watchers.forEach((w) => w.kill())
-			process.exit(0)
-		})
-		console.log("\nWatching for changes. Press Ctrl+C to stop.\n")
-	}
-}
-
-function generateNpmPackageJson(sourcePath, npmPath) {
-	const npmDir = path.join(sourcePath, npmPath)
-	const npmPackagePath = path.join(npmDir, "package.json")
-	const npmMetadataPath = path.join(npmDir, "package.metadata.json")
-	const monorepoPackagePath = path.join(sourcePath, "package.json")
-
-	if (fs.existsSync(npmPackagePath)) {
-		console.log(`  ✓ npm/package.json already exists`)
-		return
-	}
-
-	if (!fs.existsSync(npmMetadataPath)) {
-		console.log(`  ⚠ No package.metadata.json found, skipping npm package.json generation`)
-		return
-	}
-
-	try {
-		console.log(`  📦 Generating npm/package.json...`)
-		const monorepoPackage = JSON.parse(fs.readFileSync(monorepoPackagePath, "utf8"))
-		const npmMetadata = JSON.parse(fs.readFileSync(npmMetadataPath, "utf8"))
-
-		const npmPackage = {
-			...npmMetadata,
-			type: "module",
-			dependencies: monorepoPackage.dependencies || {},
-			main: "./dist/index.cjs",
-			module: "./dist/index.js",
-			types: "./dist/index.d.ts",
-			exports: {
-				".": {
-					types: "./dist/index.d.ts",
-					import: "./dist/index.js",
-					require: {
-						types: "./dist/index.d.cts",
-						default: "./dist/index.cjs",
-					},
-				},
-			},
-			files: ["dist"],
-		}
-
-		fs.writeFileSync(npmPackagePath, JSON.stringify(npmPackage, null, 2) + "\n")
-		console.log(`  ✓ Generated npm/package.json for ${npmPackage.name}`)
-	} catch (error) {
-		console.error(`  ✗ Failed to generate npm/package.json: ${error.message}`)
-	}
-}
-
-function linkPackage(pkg) {
-	const sourcePath = path.resolve(__dirname, "..", pkg.sourcePath)
-	const targetPath = path.resolve(__dirname, "..", pkg.targetPath)
-
-	if (!fs.existsSync(sourcePath)) {
-		console.error(`Source not found: ${sourcePath}`)
-		process.exit(1)
-	}
-
-	// Install dependencies if needed.
-	if (!fs.existsSync(path.join(sourcePath, "node_modules"))) {
-		console.log(`Installing dependencies for ${pkg.name}...`)
-
-		try {
-			execSync("pnpm install", { cwd: sourcePath, stdio: "inherit" })
-		} catch (e) {
-			execSync("pnpm install --no-frozen-lockfile", { cwd: sourcePath, stdio: "inherit" })
-		}
-	}
-
-	// If npmPath is specified, check if we need to generate package.json.
-	if (pkg.npmPath) {
-		generateNpmPackageJson(sourcePath, pkg.npmPath)
-	}
-
-	// Create symlink.
-	fs.rmSync(targetPath, { recursive: true, force: true })
-	fs.mkdirSync(path.dirname(targetPath), { recursive: true })
-	const linkSource = pkg.npmPath ? path.join(sourcePath, pkg.npmPath) : sourcePath
-	fs.symlinkSync(linkSource, targetPath, "dir")
-	console.log(`Linked ${pkg.name}`)
-}
-
-function unlinkPackage(pkg) {
-	const targetPath = path.resolve(__dirname, "..", pkg.targetPath)
-	if (fs.existsSync(targetPath)) {
-		fs.rmSync(targetPath, { recursive: true, force: true })
-		console.log(`Unlinked ${pkg.name}`)
-	}
-}
-
-function startWatch(pkg) {
-	console.log(`Watching ${pkg.name}...`)
-	const [cmd, ...args] = pkg.watchCommand.split(" ")
-	return spawn(cmd, args, {
-		cwd: path.resolve(__dirname, "..", pkg.sourcePath),
-		stdio: "inherit",
-		shell: true,
-	})
-}

+ 340 - 0
scripts/link-packages.ts

@@ -0,0 +1,340 @@
+import { spawn, execSync, type ChildProcess } from "child_process"
+import * as path from "path"
+import * as fs from "fs"
+import { fileURLToPath } from "url"
+import { glob } from "glob"
+
+// @ts-expect-error - TS1470: We only run this script with tsx so it will never
+// compile to CJS and it's safe to ignore this tsc error.
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
+
+interface PackageConfig {
+	readonly name: string
+	readonly sourcePath: string
+	readonly targetPaths: readonly string[]
+	readonly replacePath?: string
+	readonly npmPath: string
+	readonly watchCommand?: string
+	readonly watchOutput?: {
+		readonly start: string[]
+		readonly stop: string[]
+	}
+}
+
+interface Config {
+	readonly packages: readonly PackageConfig[]
+}
+
+interface WatcherResult {
+	child: ChildProcess
+}
+
+interface NpmPackage {
+	name?: string
+	version?: string
+	type: "module"
+	dependencies: Record<string, string>
+	main: string
+	module: string
+	types: string
+	exports: {
+		".": {
+			types: string
+			import: string
+			require: {
+				types: string
+				default: string
+			}
+		}
+	}
+	files: string[]
+}
+
+const config: Config = {
+	packages: [
+		{
+			name: "@roo-code/cloud",
+			sourcePath: "../Roo-Code-Cloud/packages/sdk",
+			targetPaths: ["src/node_modules/@roo-code/cloud"] as const,
+			replacePath: "node_modules/.pnpm/@roo-code+cloud*",
+			npmPath: "npm",
+			watchCommand: "pnpm build:development:watch",
+			watchOutput: {
+				start: ["CLI Building", "CLI Change detected"],
+				stop: ["DTS ⚡️ Build success"],
+			},
+		},
+	],
+} as const
+
+const args = process.argv.slice(2)
+const packageName = args.find((arg) => !arg.startsWith("--"))
+const watchMode = !args.includes("--no-watch")
+const unlink = args.includes("--unlink")
+
+const packages: readonly PackageConfig[] = packageName
+	? config.packages.filter((p) => p.name === packageName)
+	: config.packages
+
+if (!packages.length) {
+	console.error(`Package '${packageName}' not found`)
+	process.exit(1)
+}
+
+function pathExists(filePath: string): boolean {
+	try {
+		fs.accessSync(filePath)
+		return true
+	} catch {
+		return false
+	}
+}
+
+function copyRecursiveSync(src: string, dest: string): void {
+	const exists = pathExists(src)
+
+	if (!exists) {
+		return
+	}
+
+	const stats = fs.statSync(src)
+	const isDirectory = stats.isDirectory()
+
+	if (isDirectory) {
+		if (!pathExists(dest)) {
+			fs.mkdirSync(dest, { recursive: true })
+		}
+
+		const children = fs.readdirSync(src)
+
+		children.forEach((childItemName) => {
+			copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName))
+		})
+	} else {
+		fs.copyFileSync(src, dest)
+	}
+}
+
+function generateNpmPackageJson(sourcePath: string, npmPath: string): string {
+	const npmDir = path.join(sourcePath, npmPath)
+	const npmPackagePath = path.join(npmDir, "package.json")
+	const npmMetadataPath = path.join(npmDir, "package.metadata.json")
+	const monorepoPackagePath = path.join(sourcePath, "package.json")
+
+	if (pathExists(npmPackagePath)) {
+		return npmPackagePath
+	}
+
+	if (!pathExists(npmMetadataPath)) {
+		throw new Error(`No package.metadata.json found in ${npmDir}`)
+	}
+
+	const monorepoPackageContent = fs.readFileSync(monorepoPackagePath, "utf8")
+
+	const monorepoPackage = JSON.parse(monorepoPackageContent) as {
+		dependencies?: Record<string, string>
+	}
+
+	const npmMetadataContent = fs.readFileSync(npmMetadataPath, "utf8")
+	const npmMetadata = JSON.parse(npmMetadataContent) as Partial<NpmPackage>
+
+	const npmPackage: NpmPackage = {
+		...npmMetadata,
+		type: "module",
+		dependencies: monorepoPackage.dependencies || {},
+		main: "./dist/index.cjs",
+		module: "./dist/index.js",
+		types: "./dist/index.d.ts",
+		exports: {
+			".": {
+				types: "./dist/index.d.ts",
+				import: "./dist/index.js",
+				require: {
+					types: "./dist/index.d.cts",
+					default: "./dist/index.cjs",
+				},
+			},
+		},
+		files: ["dist"],
+	}
+
+	fs.writeFileSync(npmPackagePath, JSON.stringify(npmPackage, null, 2) + "\n")
+
+	return npmPackagePath
+}
+
+function linkPackage(pkg: PackageConfig): void {
+	const sourcePath = path.resolve(__dirname, "..", pkg.sourcePath)
+
+	if (!pathExists(sourcePath)) {
+		console.error(`❌ Source not found: ${sourcePath}`)
+		process.exit(1)
+	}
+
+	generateNpmPackageJson(sourcePath, pkg.npmPath)
+
+	for (const currentTargetPath of pkg.targetPaths) {
+		const targetPath = path.resolve(__dirname, "..", currentTargetPath)
+
+		if (pathExists(targetPath)) {
+			fs.rmSync(targetPath, { recursive: true, force: true })
+		}
+
+		const parentDir = path.dirname(targetPath)
+		fs.mkdirSync(parentDir, { recursive: true })
+
+		const linkSource = pkg.npmPath ? path.join(sourcePath, pkg.npmPath) : sourcePath
+		copyRecursiveSync(linkSource, targetPath)
+	}
+}
+
+function unlinkPackage(pkg: PackageConfig): void {
+	for (const currentTargetPath of pkg.targetPaths) {
+		const targetPath = path.resolve(__dirname, "..", currentTargetPath)
+
+		if (pathExists(targetPath)) {
+			fs.rmSync(targetPath, { recursive: true, force: true })
+			console.log(`🗑️  Removed ${pkg.name} from ${currentTargetPath}`)
+		}
+	}
+}
+
+function startWatch(pkg: PackageConfig): WatcherResult {
+	if (!pkg.watchCommand) {
+		throw new Error(`Package ${pkg.name} has no watch command configured`)
+	}
+
+	const commandParts = pkg.watchCommand.split(" ")
+	const [cmd, ...args] = commandParts
+
+	if (!cmd) {
+		throw new Error(`Invalid watch command for ${pkg.name}`)
+	}
+
+	console.log(`Watching for changes to ${pkg.sourcePath} with ${cmd} ${args.join(" ")}`)
+
+	const child = spawn(cmd, args, {
+		cwd: path.resolve(__dirname, "..", pkg.sourcePath),
+		stdio: "pipe",
+		shell: true,
+	})
+
+	let debounceTimer: NodeJS.Timeout | null = null
+
+	const DEBOUNCE_DELAY = 500
+
+	if (child.stdout) {
+		child.stdout.on("data", (data: Buffer) => {
+			const output = data.toString()
+
+			const isStarting = pkg.watchOutput?.start.some((start) => output.includes(start))
+
+			const isDone = pkg.watchOutput?.stop.some((stop) => output.includes(stop))
+
+			if (isStarting) {
+				console.log(`🔨 Building ${pkg.name}...`)
+
+				if (debounceTimer) {
+					clearTimeout(debounceTimer)
+					debounceTimer = null
+				}
+			}
+
+			if (isDone) {
+				console.log(`✅ Built ${pkg.name}`)
+
+				if (debounceTimer) {
+					clearTimeout(debounceTimer)
+				}
+
+				debounceTimer = setTimeout(() => {
+					linkPackage(pkg)
+
+					console.log(`📋 Copied ${pkg.name} to ${pkg.targetPaths.length} paths\n`)
+
+					debounceTimer = null
+				}, DEBOUNCE_DELAY)
+			}
+		})
+	}
+
+	if (child.stderr) {
+		child.stderr.on("data", (data: Buffer) => {
+			console.log(`❌ "${data.toString()}"`)
+		})
+	}
+
+	return { child }
+}
+
+function main(): void {
+	if (unlink) {
+		packages.forEach(unlinkPackage)
+
+		console.log("\n📦 Restoring npm packages...")
+
+		try {
+			execSync("pnpm install", { cwd: __dirname, stdio: "ignore" })
+			console.log("✅ npm packages restored")
+		} catch (error) {
+			console.error(`❌ Failed to restore packages: ${error instanceof Error ? error.message : String(error)}`)
+
+			console.log("   Run 'pnpm install' manually if needed")
+		}
+	} else {
+		packages.forEach((pkg) => {
+			linkPackage(pkg)
+
+			if (pkg.replacePath) {
+				const replacePattern = path.resolve(__dirname, "..", pkg.replacePath)
+
+				try {
+					const matchedPaths = glob.sync(replacePattern)
+
+					if (matchedPaths.length > 0) {
+						matchedPaths.forEach((matchedPath: string) => {
+							if (pathExists(matchedPath)) {
+								fs.rmSync(matchedPath, { recursive: true, force: true })
+								console.log(`🗑️  Removed ${pkg.name} from ${matchedPath}`)
+							}
+						})
+					} else {
+						if (pathExists(replacePattern)) {
+							fs.rmSync(replacePattern, { recursive: true, force: true })
+							console.log(`🗑️  Removed ${pkg.name} from ${replacePattern}`)
+						}
+					}
+				} catch (error) {
+					console.error(
+						`❌ Error processing replace path: ${error instanceof Error ? error.message : String(error)}`,
+					)
+				}
+			}
+		})
+
+		if (watchMode) {
+			const packagesWithWatch = packages.filter(
+				(pkg): pkg is PackageConfig & { watchCommand: string } => pkg.watchCommand !== undefined,
+			)
+
+			const watchers = packagesWithWatch.map(startWatch)
+
+			if (watchers.length > 0) {
+				process.on("SIGINT", () => {
+					console.log("\n👋 Stopping watchers...")
+
+					watchers.forEach((w) => {
+						if (w.child) {
+							w.child.kill()
+						}
+					})
+
+					process.exit(0)
+				})
+			}
+		}
+	}
+}
+
+main()

+ 14 - 5
src/core/task/Task.ts

@@ -11,6 +11,7 @@ import { serializeError } from "serialize-error"
 
 import {
 	type TaskLike,
+	type TaskMetadata,
 	type TaskEvents,
 	type ProviderSettings,
 	type TokenUsage,
@@ -32,7 +33,7 @@ import {
 	isBlockingAsk,
 } from "@roo-code/types"
 import { TelemetryService } from "@roo-code/telemetry"
-import { CloudService, UnifiedBridgeService } from "@roo-code/cloud"
+import { CloudService, ExtensionBridgeService } from "@roo-code/cloud"
 
 // api
 import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api"
@@ -122,9 +123,11 @@ export type TaskOptions = {
 }
 
 export class Task extends EventEmitter<TaskEvents> implements TaskLike {
-	todoList?: TodoItem[]
 	readonly taskId: string
 	readonly instanceId: string
+	readonly metadata: TaskMetadata
+
+	todoList?: TodoItem[]
 
 	readonly rootTask: Task | undefined = undefined
 	readonly parentTask: Task | undefined = undefined
@@ -240,7 +243,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 	// Task Bridge
 	enableTaskBridge: boolean
-	bridgeService: UnifiedBridgeService | null = null
+	bridgeService: ExtensionBridgeService | null = null
 
 	// Streaming
 	isWaitingForFirstChunk = false
@@ -285,6 +288,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 		this.taskId = historyItem ? historyItem.id : crypto.randomUUID()
 
+		this.metadata = {
+			taskId: this.taskId,
+			task: historyItem ? historyItem.task : task,
+			images: historyItem ? [] : images,
+		}
+
 		// Normal use-case is usually retry similar history task with new workspace.
 		this.workspacePath = parentTask
 			? parentTask.workspacePath
@@ -981,7 +990,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 	private async startTask(task?: string, images?: string[]): Promise<void> {
 		if (this.enableTaskBridge) {
 			try {
-				this.bridgeService = this.bridgeService || UnifiedBridgeService.getInstance()
+				this.bridgeService = this.bridgeService || ExtensionBridgeService.getInstance()
 
 				if (this.bridgeService) {
 					await this.bridgeService.subscribeToTask(this)
@@ -1046,7 +1055,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 	private async resumeTaskFromHistory() {
 		if (this.enableTaskBridge) {
 			try {
-				this.bridgeService = this.bridgeService || UnifiedBridgeService.getInstance()
+				this.bridgeService = this.bridgeService || ExtensionBridgeService.getInstance()
 
 				if (this.bridgeService) {
 					await this.bridgeService.subscribeToTask(this)

+ 20 - 31
src/core/webview/ClineProvider.ts

@@ -560,18 +560,16 @@ export class ClineProvider
 		this.log("Resolving webview view")
 
 		this.view = webviewView
-
-		// Set panel reference according to webview type
 		const inTabMode = "onDidChangeViewState" in webviewView
+
 		if (inTabMode) {
-			// Tag page type
 			setPanel(webviewView, "tab")
 		} else if ("onDidChangeVisibility" in webviewView) {
-			// Sidebar Type
 			setPanel(webviewView, "sidebar")
 		}
 
-		// Initialize out-of-scope variables that need to receive persistent global state values
+		// Initialize out-of-scope variables that need to receive persistent
+		// global state values.
 		this.getState().then(
 			({
 				terminalShellIntegrationTimeout = Terminal.defaultShellIntegrationTimeout,
@@ -594,18 +592,15 @@ export class ClineProvider
 			},
 		)
 
-		// Initialize tts enabled state
 		this.getState().then(({ ttsEnabled }) => {
 			setTtsEnabled(ttsEnabled ?? false)
 		})
 
-		// Initialize tts speed state
 		this.getState().then(({ ttsSpeed }) => {
 			setTtsSpeed(ttsSpeed ?? 1)
 		})
 
 		webviewView.webview.options = {
-			// Allow scripts in the webview
 			enableScripts: true,
 			localResourceRoots: [this.contextProxy.extensionUri],
 		}
@@ -616,32 +611,31 @@ export class ClineProvider
 				: this.getHtmlContent(webviewView.webview)
 
 		// Sets up an event listener to listen for messages passed from the webview view context
-		// and executes code based on the message that is received
+		// and executes code based on the message that is received.
 		this.setWebviewMessageListener(webviewView.webview)
 
-		// Initialize code index status subscription for the current workspace
+		// Initialize code index status subscription for the current workspace.
 		this.updateCodeIndexStatusSubscription()
 
-		// Listen for active editor changes to update code index status for the current workspace
+		// Listen for active editor changes to update code index status for the
+		// current workspace.
 		const activeEditorSubscription = vscode.window.onDidChangeActiveTextEditor(() => {
-			// Update subscription when workspace might have changed
+			// Update subscription when workspace might have changed.
 			this.updateCodeIndexStatusSubscription()
 		})
 		this.webviewDisposables.push(activeEditorSubscription)
 
-		// Logs show up in bottom panel > Debug Console
-		//console.log("registering listener")
-
-		// Listen for when the panel becomes visible
+		// Listen for when the panel becomes visible.
 		// https://github.com/microsoft/vscode-discussions/discussions/840
 		if ("onDidChangeViewState" in webviewView) {
-			// WebviewView and WebviewPanel have all the same properties except for this visibility listener
-			// panel
+			// WebviewView and WebviewPanel have all the same properties except
+			// for this visibility listener panel.
 			const viewStateDisposable = webviewView.onDidChangeViewState(() => {
 				if (this.view?.visible) {
 					this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
 				}
 			})
+
 			this.webviewDisposables.push(viewStateDisposable)
 		} else if ("onDidChangeVisibility" in webviewView) {
 			// sidebar
@@ -650,6 +644,7 @@ export class ClineProvider
 					this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
 				}
 			})
+
 			this.webviewDisposables.push(visibilityDisposable)
 		}
 
@@ -838,8 +833,8 @@ export class ClineProvider
 	}
 
 	private async getHMRHtmlContent(webview: vscode.Webview): Promise<string> {
-		// Try to read the port from the file
-		let localPort = "5173" // Default fallback
+		let localPort = "5173"
+
 		try {
 			const fs = require("fs")
 			const path = require("path")
@@ -855,7 +850,6 @@ export class ClineProvider
 			}
 		} catch (err) {
 			console.error("[ClineProvider:Vite] Failed to read Vite port file:", err)
-			// Continue with default port if file reading fails
 		}
 
 		const localServerUrl = `localhost:${localPort}`
@@ -865,7 +859,6 @@ export class ClineProvider
 			await axios.get(`http://${localServerUrl}`)
 		} catch (error) {
 			vscode.window.showErrorMessage(t("common:errors.hmr_not_running"))
-
 			return this.getHtmlContent(webview)
 		}
 
@@ -2130,12 +2123,8 @@ export class ClineProvider
 		return true
 	}
 
-	/**
-	 * Handle remote control enabled/disabled state changes
-	 * Manages UnifiedBridgeService lifecycle
-	 */
 	public async handleRemoteControlToggle(enabled: boolean) {
-		const { CloudService: CloudServiceImport, UnifiedBridgeService } = await import("@roo-code/cloud")
+		const { CloudService: CloudServiceImport, ExtensionBridgeService } = await import("@roo-code/cloud")
 
 		const userInfo = CloudServiceImport.instance.getUserInfo()
 
@@ -2146,10 +2135,10 @@ export class ClineProvider
 			return
 		}
 
-		await UnifiedBridgeService.handleRemoteControlState(
+		await ExtensionBridgeService.handleRemoteControlState(
 			userInfo,
 			enabled,
-			{ ...bridgeConfig, provider: this },
+			{ ...bridgeConfig, provider: this, sessionId: vscode.env.sessionId },
 			(message: string) => this.log(message),
 		)
 
@@ -2158,7 +2147,7 @@ export class ClineProvider
 
 			if (currentTask && !currentTask.bridgeService) {
 				try {
-					currentTask.bridgeService = UnifiedBridgeService.getInstance()
+					currentTask.bridgeService = ExtensionBridgeService.getInstance()
 
 					if (currentTask.bridgeService) {
 						await currentTask.bridgeService.subscribeToTask(currentTask)
@@ -2183,7 +2172,7 @@ export class ClineProvider
 				}
 			}
 
-			UnifiedBridgeService.resetInstance()
+			ExtensionBridgeService.resetInstance()
 		}
 	}
 

+ 4 - 4
src/extension.ts

@@ -12,7 +12,7 @@ try {
 	console.warn("Failed to load environment variables:", e)
 }
 
-import { CloudService, UnifiedBridgeService } from "@roo-code/cloud"
+import { CloudService, ExtensionBridgeService } from "@roo-code/cloud"
 import { TelemetryService, PostHogTelemetryClient } from "@roo-code/telemetry"
 
 import "./utils/path" // Necessary to have access to String.prototype.toPosix.
@@ -141,10 +141,10 @@ export async function activate(context: vscode.ExtensionContext) {
 			return
 		}
 
-		UnifiedBridgeService.handleRemoteControlState(
+		ExtensionBridgeService.handleRemoteControlState(
 			userInfo,
 			contextProxy.getValue("remoteControlEnabled"),
-			{ ...bridgeConfig, provider },
+			{ ...bridgeConfig, provider, sessionId: vscode.env.sessionId },
 			(message: string) => outputChannel.appendLine(message),
 		)
 	})
@@ -280,7 +280,7 @@ export async function activate(context: vscode.ExtensionContext) {
 export async function deactivate() {
 	outputChannel.appendLine(`${Package.name} extension deactivated`)
 
-	const bridgeService = UnifiedBridgeService.getInstance()
+	const bridgeService = ExtensionBridgeService.getInstance()
 
 	if (bridgeService) {
 		await bridgeService.disconnect()

+ 2 - 1
src/package.json

@@ -427,7 +427,7 @@
 		"@mistralai/mistralai": "^1.3.6",
 		"@modelcontextprotocol/sdk": "^1.9.0",
 		"@qdrant/js-client-rest": "^1.14.0",
-		"@roo-code/cloud": "^0.13.0",
+		"@roo-code/cloud": "^0.14.0",
 		"@roo-code/ipc": "workspace:^",
 		"@roo-code/telemetry": "workspace:^",
 		"@roo-code/types": "workspace:^",
@@ -474,6 +474,7 @@
 		"say": "^0.16.0",
 		"serialize-error": "^12.0.0",
 		"simple-git": "^3.27.0",
+		"socket.io-client": "^4.8.1",
 		"sound-play": "^1.1.0",
 		"stream-json": "^1.8.0",
 		"string-similarity": "^4.0.4",

+ 8 - 0
tsconfig.json

@@ -0,0 +1,8 @@
+{
+	"extends": "@roo-code/config-typescript/base.json",
+	"compilerOptions": {
+		"types": ["node"]
+	},
+	"exclude": ["node_modules"],
+	"include": ["scripts/*.ts"]
+}