Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions packages/cloud/src/CloudService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di
return this._retryQueue
}

private _isCloudAgent = false

public get isCloudAgent() {
return this._isCloudAgent
}

private constructor(context: ExtensionContext, log?: (...args: unknown[]) => void) {
super()

Expand Down Expand Up @@ -117,6 +123,7 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di

if (cloudToken && cloudToken.length > 0) {
this._authService = new StaticTokenAuthService(this.context, cloudToken, this.log)
this._isCloudAgent = true
} else {
this._authService = new WebAuthService(this.context, this.log)
}
Expand All @@ -141,19 +148,19 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di

this._cloudAPI = new CloudAPI(this._authService, this.log)

// Initialize retry queue with auth header provider
// Initialize retry queue with auth header provider.
this._retryQueue = new RetryQueue(
this.context,
undefined, // Use default config
undefined, // Use default config.
this.log,
() => {
// Provide fresh auth headers for retries
// Provide fresh auth headers for retries.
const sessionToken = this._authService?.getSessionToken()

if (sessionToken) {
return {
Authorization: `Bearer ${sessionToken}`,
}
return { Authorization: `Bearer ${sessionToken}` }
}

return undefined
},
)
Expand Down
3 changes: 3 additions & 0 deletions packages/cloud/src/bridge/BaseChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface BaseChannelOptions {
instanceId: string
appProperties: StaticAppProperties
gitProperties?: GitProperties
isCloudAgent: boolean
}

/**
Expand All @@ -22,11 +23,13 @@ export abstract class BaseChannel<TCommand = unknown, TEventName extends string
protected readonly instanceId: string
protected readonly appProperties: StaticAppProperties
protected readonly gitProperties?: GitProperties
protected readonly isCloudAgent: boolean

constructor(options: BaseChannelOptions) {
this.instanceId = options.instanceId
this.appProperties = options.appProperties
this.gitProperties = options.gitProperties
this.isCloudAgent = options.isCloudAgent
}

/**
Expand Down
8 changes: 7 additions & 1 deletion packages/cloud/src/bridge/BridgeOrchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export interface BridgeOrchestratorOptions {
socketBridgeUrl: string
token: string
provider: TaskProviderLike
sessionId?: string
sessionId: string
isCloudAgent: boolean
}

/**
Expand All @@ -44,6 +45,7 @@ export class BridgeOrchestrator {
private readonly instanceId: string
private readonly appProperties: StaticAppProperties
private readonly gitProperties?: GitProperties
private readonly isCloudAgent?: boolean
Copy link

Choose a reason for hiding this comment

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

[P0] Type mismatch can break build: isCloudAgent is declared as optional on the class (boolean | undefined) but passed to ExtensionChannel/TaskChannel which require a boolean. This allows undefined to flow into a non-optional parameter and will fail type-checking under strictNullChecks. Recommend aligning the class property with the options type: make it non-optional.

Suggested change
private readonly isCloudAgent?: boolean
private readonly isCloudAgent: boolean


// Components
private socketTransport: SocketTransport
Expand Down Expand Up @@ -96,6 +98,7 @@ export class BridgeOrchestrator {
if (!instance) {
try {
console.log(`[BridgeOrchestrator#connectOrDisconnect] Connecting...`)

// Populate telemetry properties before registering the instance.
await options.provider.getTelemetryProperties()

Expand Down Expand Up @@ -174,6 +177,7 @@ export class BridgeOrchestrator {
this.instanceId = options.sessionId || crypto.randomUUID()
Copy link

Choose a reason for hiding this comment

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

[P3] Minor cleanup: sessionId is now required in BridgeOrchestratorOptions, but the constructor still falls back to crypto.randomUUID(). Since callers must provide sessionId, the fallback is redundant. Consider simplifying to direct assignment for clarity.

Suggested change
this.instanceId = options.sessionId || crypto.randomUUID()
this.instanceId = options.sessionId

this.appProperties = { ...options.provider.appProperties, hostname: os.hostname() }
this.gitProperties = options.provider.gitProperties
this.isCloudAgent = options.isCloudAgent

this.socketTransport = new SocketTransport({
url: this.socketBridgeUrl,
Expand All @@ -200,12 +204,14 @@ export class BridgeOrchestrator {
gitProperties: this.gitProperties,
userId: this.userId,
provider: this.provider,
isCloudAgent: this.isCloudAgent,
})

this.taskChannel = new TaskChannel({
instanceId: this.instanceId,
appProperties: this.appProperties,
gitProperties: this.gitProperties,
isCloudAgent: this.isCloudAgent,
})
}

Expand Down
2 changes: 2 additions & 0 deletions packages/cloud/src/bridge/ExtensionChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class ExtensionChannel extends BaseChannel<
instanceId: options.instanceId,
appProperties: options.appProperties,
gitProperties: options.gitProperties,
isCloudAgent: options.isCloudAgent,
})

this.userId = options.userId
Expand All @@ -55,6 +56,7 @@ export class ExtensionChannel extends BaseChannel<
lastHeartbeat: Date.now(),
task: { taskId: "", taskStatus: TaskStatus.None },
taskHistory: [],
isCloudAgent: this.isCloudAgent,
Copy link

Choose a reason for hiding this comment

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

[P3] Durability: you set isCloudAgent on initial registration, but updateInstance relies on spreading the prior object to retain it. To make it future‑proof against refactors that rebuild the object, consider explicitly setting isCloudAgent: this.isCloudAgent inside updateInstance as well.

}

this.setupListeners()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ describe("ExtensionChannel", () => {
appProperties,
userId,
provider: mockProvider,
isCloudAgent: false,
})
})

Expand Down Expand Up @@ -176,6 +177,7 @@ describe("ExtensionChannel", () => {
appProperties,
userId,
provider: mockProvider,
isCloudAgent: false,
})

// Each event should have exactly 2 listeners (one from each channel)
Expand Down
1 change: 1 addition & 0 deletions packages/cloud/src/bridge/__tests__/TaskChannel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe("TaskChannel", () => {
taskChannel = new TaskChannel({
instanceId,
appProperties,
isCloudAgent: false,
})
})

Expand Down
2 changes: 1 addition & 1 deletion packages/types/npm/package.metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@roo-code/types",
"version": "1.82.0",
"version": "1.83.0",
"description": "TypeScript type definitions for Roo Code.",
"publishConfig": {
"access": "public",
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ export const extensionInstanceSchema = z.object({
modes: z.array(z.object({ slug: z.string(), name: z.string() })).optional(),
providerProfile: z.string().optional(),
providerProfiles: z.array(z.object({ name: z.string(), provider: z.string().optional() })).optional(),
isCloudAgent: z.boolean().optional(),
Copy link

Choose a reason for hiding this comment

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

[P2] Schema default improves robustness: if older clients omit this field, downstream code can always rely on a concrete boolean. Suggest defaulting to false so the property is present after parsing even when not sent.

Suggested change
isCloudAgent: z.boolean().optional(),
isCloudAgent: z.boolean().optional().default(false),

})

export type ExtensionInstance = z.infer<typeof extensionInstanceSchema>
Expand Down
1 change: 1 addition & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2343,6 +2343,7 @@ export class ClineProvider
...config,
provider: this,
sessionId: vscode.env.sessionId,
isCloudAgent: CloudService.instance.isCloudAgent,
})

const bridge = BridgeOrchestrator.getInstance()
Expand Down