@getpaseo/server 0.1.88 → 0.1.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/server/server/agent/agent-manager.js +4 -1
  2. package/dist/server/server/agent/agent-storage.d.ts +22 -22
  3. package/dist/server/server/agent/create-agent/create.d.ts +2 -0
  4. package/dist/server/server/agent/create-agent/create.js +16 -5
  5. package/dist/server/server/agent/create-agent-lifecycle-dispatch.d.ts +1 -0
  6. package/dist/server/server/agent/create-agent-lifecycle-dispatch.js +4 -0
  7. package/dist/server/server/agent/mcp-server.d.ts +1 -0
  8. package/dist/server/server/agent/mcp-server.js +113 -70
  9. package/dist/server/server/agent/providers/pi/agent.js +13 -0
  10. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
  11. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +1 -0
  12. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +6 -1
  13. package/dist/server/server/bootstrap.d.ts +7 -2
  14. package/dist/server/server/bootstrap.js +152 -115
  15. package/dist/server/server/config.js +41 -0
  16. package/dist/server/server/loop-service.d.ts +22 -22
  17. package/dist/server/server/package-version.d.ts +2 -2
  18. package/dist/server/server/paseo-worktree-archive-service.d.ts +2 -0
  19. package/dist/server/server/paseo-worktree-archive-service.js +28 -9
  20. package/dist/server/server/persisted-config.d.ts +84 -28
  21. package/dist/server/server/persisted-config.js +17 -0
  22. package/dist/server/server/pid-lock.d.ts +2 -2
  23. package/dist/server/server/script-health-monitor.d.ts +4 -4
  24. package/dist/server/server/script-health-monitor.js +6 -6
  25. package/dist/server/server/script-proxy.d.ts +2 -39
  26. package/dist/server/server/script-proxy.js +1 -244
  27. package/dist/server/server/script-route-branch-handler.d.ts +2 -2
  28. package/dist/server/server/script-route-branch-handler.js +3 -37
  29. package/dist/server/server/script-status-projection.d.ts +6 -4
  30. package/dist/server/server/script-status-projection.js +85 -37
  31. package/dist/server/server/service-proxy.d.ts +237 -0
  32. package/dist/server/server/service-proxy.js +714 -0
  33. package/dist/server/server/session.d.ts +7 -3
  34. package/dist/server/server/session.js +22 -10
  35. package/dist/server/server/websocket-server.d.ts +7 -4
  36. package/dist/server/server/websocket-server.js +9 -4
  37. package/dist/server/server/workspace-directory.js +4 -0
  38. package/dist/server/server/workspace-git-service.d.ts +3 -0
  39. package/dist/server/server/workspace-git-service.js +53 -12
  40. package/dist/server/server/workspace-registry.d.ts +2 -2
  41. package/dist/server/server/workspace-service-env.d.ts +1 -0
  42. package/dist/server/server/workspace-service-env.js +23 -18
  43. package/dist/server/server/worktree/commands.d.ts +2 -0
  44. package/dist/server/server/worktree/commands.js +4 -1
  45. package/dist/server/server/worktree-bootstrap.d.ts +4 -3
  46. package/dist/server/server/worktree-bootstrap.js +14 -13
  47. package/dist/server/server/worktree-core.d.ts +1 -0
  48. package/dist/server/server/worktree-core.js +2 -0
  49. package/dist/server/server/worktree-session.d.ts +6 -2
  50. package/dist/server/server/worktree-session.js +3 -0
  51. package/dist/server/services/github-service.d.ts +1 -0
  52. package/dist/server/services/github-service.js +7 -1
  53. package/dist/server/utils/checkout-git.d.ts +6 -2
  54. package/dist/server/utils/checkout-git.js +17 -7
  55. package/dist/server/utils/worktree.d.ts +17 -12
  56. package/dist/server/utils/worktree.js +39 -22
  57. package/dist/src/server/persisted-config.js +17 -0
  58. package/package.json +5 -5
  59. package/dist/server/utils/script-hostname.d.ts +0 -8
  60. package/dist/server/utils/script-hostname.js +0 -14
@@ -15,7 +15,7 @@ import type { AgentStorage } from "./agent/agent-storage.js";
15
15
  import { type PersistedWorkspaceRecord, type ProjectRegistry, type WorkspaceRegistry } from "./workspace-registry.js";
16
16
  import { DownloadTokenStore } from "./file-download/token-store.js";
17
17
  import { PushTokenStore } from "./push/token-store.js";
18
- import type { ScriptRouteStore } from "./script-proxy.js";
18
+ import type { ServiceProxySubsystem } from "./service-proxy.js";
19
19
  import { CheckoutDiffManager } from "./checkout-diff-manager.js";
20
20
  import { type Resolvable } from "./speech/provider-resolver.js";
21
21
  import type { SpeechReadinessSnapshot } from "./speech/speech-runtime.js";
@@ -46,6 +46,7 @@ export interface SessionOptions {
46
46
  downloadTokenStore: DownloadTokenStore;
47
47
  pushTokenStore: PushTokenStore;
48
48
  paseoHome: string;
49
+ worktreesRoot?: string;
49
50
  agentManager: AgentManager;
50
51
  agentStorage: AgentStorage;
51
52
  projectRegistry: ProjectRegistry;
@@ -64,12 +65,13 @@ export interface SessionOptions {
64
65
  tts: Resolvable<TextToSpeechProvider | null>;
65
66
  terminalManager: TerminalManager | null;
66
67
  providerSnapshotManager: ProviderSnapshotManager;
67
- scriptRouteStore?: ScriptRouteStore;
68
+ serviceProxy?: ServiceProxySubsystem;
68
69
  scriptRuntimeStore?: WorkspaceScriptRuntimeStore;
69
70
  workspaceSetupSnapshots?: Map<string, WorkspaceSetupSnapshot>;
70
71
  onBranchChanged?: (workspaceId: string, oldBranch: string | null, newBranch: string | null) => void;
71
72
  getDaemonTcpPort?: () => number | null;
72
73
  getDaemonTcpHost?: () => string | null;
74
+ serviceProxyPublicBaseUrl?: string | null;
73
75
  resolveScriptHealth?: (hostname: string) => ScriptHealthState | null;
74
76
  voice?: {
75
77
  turnDetection?: Resolvable<TurnDetectionProvider | null>;
@@ -124,6 +126,7 @@ export declare class Session {
124
126
  private readonly onLifecycleIntent;
125
127
  private readonly sessionLogger;
126
128
  private readonly paseoHome;
129
+ private readonly worktreesRoot;
127
130
  private abortController;
128
131
  private processingPhase;
129
132
  private isVoiceMode;
@@ -163,11 +166,12 @@ export declare class Session {
163
166
  private readonly terminalManager;
164
167
  private readonly providerSnapshotManager;
165
168
  private unsubscribeProviderSnapshotEvents;
166
- private readonly scriptRouteStore;
169
+ private readonly serviceProxy;
167
170
  private readonly scriptRuntimeStore;
168
171
  private readonly onBranchChanged?;
169
172
  private readonly getDaemonTcpPort;
170
173
  private readonly getDaemonTcpHost;
174
+ private readonly serviceProxyPublicBaseUrl;
171
175
  private readonly resolveScriptHealth;
172
176
  private readonly terminalController;
173
177
  private inflightRequests;
@@ -334,7 +334,7 @@ export class Session {
334
334
  }
335
335
  },
336
336
  });
337
- const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, agentManager, agentStorage, projectRegistry, workspaceRegistry, chatService, scheduleService, loopService, checkoutDiffManager, github, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, sttLanguage, tts, terminalManager, providerSnapshotManager, scriptRouteStore, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, resolveScriptHealth, voice, voiceBridge, dictation, serverId, daemonVersion, daemonRuntimeConfig, } = options;
337
+ const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, worktreesRoot, agentManager, agentStorage, projectRegistry, workspaceRegistry, chatService, scheduleService, loopService, checkoutDiffManager, github, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, sttLanguage, tts, terminalManager, providerSnapshotManager, serviceProxy, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, serviceProxyPublicBaseUrl, resolveScriptHealth, voice, voiceBridge, dictation, serverId, daemonVersion, daemonRuntimeConfig, } = options;
338
338
  this.clientId = clientId;
339
339
  this.appVersion = appVersion ?? null;
340
340
  this.clientCapabilities = parseClientCapabilities(clientCapabilities);
@@ -345,6 +345,7 @@ export class Session {
345
345
  this.downloadTokenStore = downloadTokenStore;
346
346
  this.pushTokenStore = pushTokenStore;
347
347
  this.paseoHome = paseoHome;
348
+ this.worktreesRoot = worktreesRoot;
348
349
  this.sessionLogger = logger.child({
349
350
  module: "session",
350
351
  clientId: this.clientId,
@@ -374,6 +375,7 @@ export class Session {
374
375
  });
375
376
  this.createAgentLifecycleDispatch = new CreateAgentLifecycleDispatch({
376
377
  paseoHome: this.paseoHome,
378
+ worktreesRoot: this.worktreesRoot,
377
379
  agentManager: this.agentManager,
378
380
  agentStorage: this.agentStorage,
379
381
  github: this.github,
@@ -398,12 +400,13 @@ export class Session {
398
400
  logger: this.sessionLogger,
399
401
  });
400
402
  this.providerSnapshotManager = providerSnapshotManager;
401
- this.scriptRouteStore = scriptRouteStore ?? null;
403
+ this.serviceProxy = serviceProxy ?? null;
402
404
  this.scriptRuntimeStore = scriptRuntimeStore ?? null;
403
405
  this.workspaceSetupSnapshots = workspaceSetupSnapshots ?? new Map();
404
406
  this.onBranchChanged = onBranchChanged;
405
407
  this.getDaemonTcpPort = getDaemonTcpPort ?? null;
406
408
  this.getDaemonTcpHost = getDaemonTcpHost ?? null;
409
+ this.serviceProxyPublicBaseUrl = serviceProxyPublicBaseUrl ?? null;
407
410
  this.resolveScriptHealth = resolveScriptHealth ?? null;
408
411
  this.sttLanguage = sttLanguage ?? "en";
409
412
  this.subscribeToOptionalManagers();
@@ -2094,6 +2097,7 @@ export class Session {
2094
2097
  agentStorage: this.agentStorage,
2095
2098
  logger: this.sessionLogger,
2096
2099
  paseoHome: this.paseoHome,
2100
+ worktreesRoot: this.worktreesRoot,
2097
2101
  workspaceGitService: this.workspaceGitService,
2098
2102
  providerSnapshotManager: this.providerSnapshotManager,
2099
2103
  daemonConfig: this.readStructuredGenerationDaemonConfig(),
@@ -2428,6 +2432,7 @@ export class Session {
2428
2432
  async buildAgentSessionConfig(config, gitOptions, legacyWorktreeName, firstAgentContext) {
2429
2433
  return buildWorktreeAgentSessionConfig({
2430
2434
  paseoHome: this.paseoHome,
2435
+ worktreesRoot: this.worktreesRoot,
2431
2436
  sessionLogger: this.sessionLogger,
2432
2437
  workspaceGitService: this.workspaceGitService,
2433
2438
  createPaseoWorktree: (input, serviceOptions) => this.createPaseoWorktreeWorkflow(input, {
@@ -3873,7 +3878,7 @@ export class Session {
3873
3878
  const mutatedCwd = await mergeToBase(cwd, {
3874
3879
  baseRef,
3875
3880
  mode: msg.strategy === "squash" ? "squash" : "merge",
3876
- }, { paseoHome: this.paseoHome });
3881
+ }, { paseoHome: this.paseoHome, worktreesRoot: this.worktreesRoot });
3877
3882
  await Promise.all([
3878
3883
  this.notifyGitMutation(mutatedCwd, "merge-to-base", { invalidateGithub: true }),
3879
3884
  ...(mutatedCwd !== cwd ? [this.notifyGitMutation(cwd, "merge-to-base")] : []),
@@ -4293,6 +4298,7 @@ export class Session {
4293
4298
  async handlePaseoWorktreeArchiveRequest(msg) {
4294
4299
  return handleWorktreeArchiveRequest({
4295
4300
  paseoHome: this.paseoHome,
4301
+ worktreesRoot: this.worktreesRoot,
4296
4302
  github: this.github,
4297
4303
  workspaceGitService: this.workspaceGitService,
4298
4304
  agentManager: this.agentManager,
@@ -4730,14 +4736,15 @@ export class Session {
4730
4736
  status: "done",
4731
4737
  activityAt: null,
4732
4738
  diffStat,
4733
- scripts: this.scriptRouteStore && this.scriptRuntimeStore
4739
+ scripts: this.serviceProxy && this.scriptRuntimeStore
4734
4740
  ? buildWorkspaceScriptPayloads({
4735
4741
  workspaceId: workspace.workspaceId,
4736
4742
  workspaceDirectory: workspace.cwd,
4737
4743
  paseoConfig: readPaseoConfigForProjection(workspace.cwd, this.sessionLogger),
4738
- routeStore: this.scriptRouteStore,
4744
+ serviceProxy: this.serviceProxy,
4739
4745
  runtimeStore: this.scriptRuntimeStore,
4740
4746
  daemonPort: this.getDaemonTcpPort?.() ?? null,
4747
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
4741
4748
  gitMetadata: this.resolveWorkspaceScriptGitMetadata(workspace.cwd),
4742
4749
  resolveHealth: this.resolveScriptHealth ?? undefined,
4743
4750
  })
@@ -5399,16 +5406,17 @@ export class Session {
5399
5406
  }
5400
5407
  }
5401
5408
  buildWorkspaceScriptPayloadSnapshot(workspaceId, workspaceDirectory) {
5402
- if (!this.scriptRouteStore || !this.scriptRuntimeStore) {
5409
+ if (!this.serviceProxy || !this.scriptRuntimeStore) {
5403
5410
  return [];
5404
5411
  }
5405
5412
  return buildWorkspaceScriptPayloads({
5406
5413
  workspaceId,
5407
5414
  workspaceDirectory,
5408
5415
  paseoConfig: readPaseoConfigForProjection(workspaceDirectory, this.sessionLogger),
5409
- routeStore: this.scriptRouteStore,
5416
+ serviceProxy: this.serviceProxy,
5410
5417
  runtimeStore: this.scriptRuntimeStore,
5411
5418
  daemonPort: this.getDaemonTcpPort?.() ?? null,
5419
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
5412
5420
  gitMetadata: this.resolveWorkspaceScriptGitMetadata(workspaceDirectory),
5413
5421
  resolveHealth: this.resolveScriptHealth ?? undefined,
5414
5422
  });
@@ -5443,7 +5451,7 @@ export class Session {
5443
5451
  }
5444
5452
  async handleStartWorkspaceScriptRequest(request) {
5445
5453
  try {
5446
- if (!this.terminalManager || !this.scriptRouteStore || !this.scriptRuntimeStore) {
5454
+ if (!this.terminalManager || !this.serviceProxy || !this.scriptRuntimeStore) {
5447
5455
  throw new Error("Workspace scripts are not available on this daemon");
5448
5456
  }
5449
5457
  const workspace = await this.workspaceRegistry.get(request.workspaceId);
@@ -5459,7 +5467,8 @@ export class Session {
5459
5467
  scriptName: request.scriptName,
5460
5468
  daemonPort: this.getDaemonTcpPort?.() ?? null,
5461
5469
  daemonListenHost: this.getDaemonTcpHost?.() ?? null,
5462
- routeStore: this.scriptRouteStore,
5470
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
5471
+ serviceProxy: this.serviceProxy,
5463
5472
  runtimeStore: this.scriptRuntimeStore,
5464
5473
  terminalManager: this.terminalManager,
5465
5474
  logger: this.sessionLogger,
@@ -5554,6 +5563,7 @@ export class Session {
5554
5563
  async handleCreatePaseoWorktreeRequest(request) {
5555
5564
  return handleCreateWorktreeRequest({
5556
5565
  paseoHome: this.paseoHome,
5566
+ worktreesRoot: this.worktreesRoot,
5557
5567
  describeWorkspaceRecord: (result) => this.describeCreatedWorktreeWorkspace(result),
5558
5568
  emit: (message) => this.emit(message),
5559
5569
  sessionLogger: this.sessionLogger,
@@ -5563,6 +5573,7 @@ export class Session {
5563
5573
  async createPaseoWorktreeWorkflow(input, options) {
5564
5574
  return createWorktreeWorkflow({
5565
5575
  paseoHome: this.paseoHome,
5576
+ worktreesRoot: this.worktreesRoot,
5566
5577
  createPaseoWorktree: (workflowInput, serviceOptions) => this.createPaseoWorktree(workflowInput, serviceOptions),
5567
5578
  warmWorkspaceGitData: (workspace) => this.warmWorkspaceGitDataForWorkspace(workspace),
5568
5579
  autoNameWorkspaceBranchForFirstAgent: (autoNameInput) => this.scheduleAutoNameWorkspaceBranchForFirstAgent(autoNameInput),
@@ -5574,10 +5585,11 @@ export class Session {
5574
5585
  sessionLogger: this.sessionLogger,
5575
5586
  terminalManager: this.terminalManager,
5576
5587
  archiveWorkspaceRecord: (workspaceId) => this.archiveWorkspaceRecord(workspaceId),
5577
- scriptRouteStore: this.scriptRouteStore,
5588
+ serviceProxy: this.serviceProxy,
5578
5589
  scriptRuntimeStore: this.scriptRuntimeStore,
5579
5590
  getDaemonTcpPort: this.getDaemonTcpPort,
5580
5591
  getDaemonTcpHost: this.getDaemonTcpHost,
5592
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
5581
5593
  onScriptsChanged: (workspaceId, workspaceDirectory) => {
5582
5594
  this.emitWorkspaceScriptStatusUpdate(workspaceId, workspaceDirectory);
5583
5595
  },
@@ -17,7 +17,7 @@ import { ProviderSnapshotManager } from "./agent/provider-snapshot-manager.js";
17
17
  import type { WorkspaceGitService } from "./workspace-git-service.js";
18
18
  import { type PushNotificationSender } from "./push/notifications.js";
19
19
  import type { ScriptHealthState } from "./script-health-monitor.js";
20
- import type { ScriptRouteStore } from "./script-proxy.js";
20
+ import type { ServiceProxySubsystem } from "./service-proxy.js";
21
21
  import type { WorkspaceScriptRuntimeStore } from "./workspace-script-runtime-store.js";
22
22
  import type { SpeechReadinessSnapshot, SpeechService } from "./speech/speech-runtime.js";
23
23
  import type { VoiceCallerContext, VoiceSpeakHandler } from "./voice-types.js";
@@ -66,16 +66,18 @@ export declare class VoiceAssistantWebSocketServer {
66
66
  private readonly workspaceGitService;
67
67
  private readonly downloadTokenStore;
68
68
  private readonly paseoHome;
69
+ private readonly worktreesRoot;
69
70
  private readonly daemonConfigStore;
70
71
  private readonly pushTokenStore;
71
72
  private readonly pushNotificationSender;
72
73
  private readonly mcpBaseUrl;
73
74
  private speech;
74
75
  private terminalManager;
75
- private scriptRouteStore;
76
+ private serviceProxy;
76
77
  private scriptRuntimeStore;
77
78
  private getDaemonTcpPort;
78
79
  private getDaemonTcpHost;
80
+ private serviceProxyPublicBaseUrl;
79
81
  private resolveScriptHealth;
80
82
  private dictation;
81
83
  private readonly voiceSpeakHandlers;
@@ -91,8 +93,9 @@ export declare class VoiceAssistantWebSocketServer {
91
93
  private unsubscribeDaemonConfigChange;
92
94
  constructor(server: HTTPServer, logger: pino.Logger, serverId: string, agentManager: AgentManager, agentStorage: AgentStorage, downloadTokenStore: DownloadTokenStore, paseoHome: string, daemonConfigStore: DaemonConfigStore, mcpBaseUrl: string | null, wsConfig: WebSocketServerConfig, auth?: DaemonAuthConfig, speech?: SpeechService | null, terminalManager?: TerminalManager | null, dictation?: {
93
95
  finalTimeoutMs?: number;
94
- }, daemonVersion?: string, onLifecycleIntent?: (intent: SessionLifecycleIntent) => void, projectRegistry?: ProjectRegistry, workspaceRegistry?: WorkspaceRegistry, chatService?: FileBackedChatService, loopService?: LoopService, scheduleService?: ScheduleService, checkoutDiffManager?: CheckoutDiffManager, scriptRouteStore?: ScriptRouteStore | null, scriptRuntimeStore?: WorkspaceScriptRuntimeStore | null, onBranchChanged?: (workspaceId: string, oldBranch: string | null, newBranch: string | null) => void, getDaemonTcpPort?: () => number | null, getDaemonTcpHost?: () => string | null, resolveScriptHealth?: (hostname: string) => ScriptHealthState | null, workspaceGitService?: WorkspaceGitService, github?: GitHubService, pushNotificationSender?: PushNotificationSender, providerSnapshotManager?: ProviderSnapshotManager, daemonRuntimeConfig?: {
96
+ }, daemonVersion?: string, onLifecycleIntent?: (intent: SessionLifecycleIntent) => void, projectRegistry?: ProjectRegistry, workspaceRegistry?: WorkspaceRegistry, chatService?: FileBackedChatService, loopService?: LoopService, scheduleService?: ScheduleService, checkoutDiffManager?: CheckoutDiffManager, serviceProxy?: ServiceProxySubsystem | null, scriptRuntimeStore?: WorkspaceScriptRuntimeStore | null, onBranchChanged?: (workspaceId: string, oldBranch: string | null, newBranch: string | null) => void, getDaemonTcpPort?: () => number | null, getDaemonTcpHost?: () => string | null, resolveScriptHealth?: (hostname: string) => ScriptHealthState | null, workspaceGitService?: WorkspaceGitService, github?: GitHubService, pushNotificationSender?: PushNotificationSender, providerSnapshotManager?: ProviderSnapshotManager, daemonRuntimeConfig?: {
95
97
  listen: string | null;
98
+ worktreesRoot?: string;
96
99
  relay: {
97
100
  enabled: boolean;
98
101
  endpoint: string;
@@ -100,7 +103,7 @@ export declare class VoiceAssistantWebSocketServer {
100
103
  useTls: boolean;
101
104
  publicUseTls: boolean;
102
105
  };
103
- });
106
+ }, serviceProxyPublicBaseUrl?: string | null);
104
107
  private assignOptionalServices;
105
108
  private createWebSocketServer;
106
109
  private startRuntimeMetricsInterval;
@@ -202,7 +202,7 @@ function requireWebSocketServices(params) {
202
202
  * WebSocket server that only accepts sockets + parses/forwards messages to the session layer.
203
203
  */
204
204
  export class VoiceAssistantWebSocketServer {
205
- constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, paseoHome, daemonConfigStore, mcpBaseUrl, wsConfig, auth, speech, terminalManager, dictation, daemonVersion, onLifecycleIntent, projectRegistry, workspaceRegistry, chatService, loopService, scheduleService, checkoutDiffManager, scriptRouteStore, scriptRuntimeStore, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, resolveScriptHealth, workspaceGitService, github, pushNotificationSender, providerSnapshotManager, daemonRuntimeConfig) {
205
+ constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, paseoHome, daemonConfigStore, mcpBaseUrl, wsConfig, auth, speech, terminalManager, dictation, daemonVersion, onLifecycleIntent, projectRegistry, workspaceRegistry, chatService, loopService, scheduleService, checkoutDiffManager, serviceProxy, scriptRuntimeStore, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, resolveScriptHealth, workspaceGitService, github, pushNotificationSender, providerSnapshotManager, daemonRuntimeConfig, serviceProxyPublicBaseUrl) {
206
206
  this.pendingConnections = new Map();
207
207
  this.sessions = new Map();
208
208
  this.externalSessionsByKey = new Map();
@@ -238,6 +238,7 @@ export class VoiceAssistantWebSocketServer {
238
238
  this.workspaceGitService = workspaceGitService ?? createFallbackWorkspaceGitService();
239
239
  this.downloadTokenStore = downloadTokenStore;
240
240
  this.paseoHome = paseoHome;
241
+ this.worktreesRoot = daemonRuntimeConfig?.worktreesRoot;
241
242
  this.daemonConfigStore = daemonConfigStore;
242
243
  this.mcpBaseUrl = mcpBaseUrl;
243
244
  this.assignOptionalServices({
@@ -245,11 +246,12 @@ export class VoiceAssistantWebSocketServer {
245
246
  terminalManager,
246
247
  dictation,
247
248
  onLifecycleIntent,
248
- scriptRouteStore,
249
+ serviceProxy,
249
250
  scriptRuntimeStore,
250
251
  onBranchChanged,
251
252
  getDaemonTcpPort,
252
253
  getDaemonTcpHost,
254
+ serviceProxyPublicBaseUrl,
253
255
  resolveScriptHealth,
254
256
  });
255
257
  if (!providerSnapshotManager) {
@@ -286,11 +288,12 @@ export class VoiceAssistantWebSocketServer {
286
288
  this.terminalManager = params.terminalManager ?? null;
287
289
  this.dictation = params.dictation ?? null;
288
290
  this.onLifecycleIntent = params.onLifecycleIntent ?? null;
289
- this.scriptRouteStore = params.scriptRouteStore ?? null;
291
+ this.serviceProxy = params.serviceProxy ?? null;
290
292
  this.scriptRuntimeStore = params.scriptRuntimeStore ?? null;
291
293
  this.onBranchChanged = params.onBranchChanged ?? null;
292
294
  this.getDaemonTcpPort = params.getDaemonTcpPort ?? null;
293
295
  this.getDaemonTcpHost = params.getDaemonTcpHost ?? null;
296
+ this.serviceProxyPublicBaseUrl = params.serviceProxyPublicBaseUrl ?? null;
294
297
  this.resolveScriptHealth = params.resolveScriptHealth ?? null;
295
298
  }
296
299
  createWebSocketServer(server, wsConfig, auth) {
@@ -550,6 +553,7 @@ export class VoiceAssistantWebSocketServer {
550
553
  downloadTokenStore: this.downloadTokenStore,
551
554
  pushTokenStore: this.pushTokenStore,
552
555
  paseoHome: this.paseoHome,
556
+ worktreesRoot: this.worktreesRoot,
553
557
  agentManager: this.agentManager,
554
558
  agentStorage: this.agentStorage,
555
559
  projectRegistry: this.projectRegistry,
@@ -567,12 +571,13 @@ export class VoiceAssistantWebSocketServer {
567
571
  tts: () => this.speech?.resolveTts() ?? null,
568
572
  terminalManager: this.terminalManager,
569
573
  providerSnapshotManager: this.providerSnapshotManager,
570
- scriptRouteStore: this.scriptRouteStore ?? undefined,
574
+ serviceProxy: this.serviceProxy ?? undefined,
571
575
  scriptRuntimeStore: this.scriptRuntimeStore ?? undefined,
572
576
  workspaceSetupSnapshots: this.workspaceSetupSnapshots,
573
577
  onBranchChanged: this.onBranchChanged ?? undefined,
574
578
  getDaemonTcpPort: this.getDaemonTcpPort ?? undefined,
575
579
  getDaemonTcpHost: this.getDaemonTcpHost ?? undefined,
580
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
576
581
  resolveScriptHealth: this.resolveScriptHealth ?? undefined,
577
582
  voice: {
578
583
  turnDetection: () => this.speech?.resolveTurnDetection() ?? null,
@@ -1,6 +1,7 @@
1
1
  import { homedir } from "node:os";
2
2
  import { sep } from "node:path";
3
3
  import { deriveAgentStateBucket, getWorkspaceStateBucketPriority, } from "@getpaseo/protocol/agent-state-bucket";
4
+ import { isDelegatedAgent } from "@getpaseo/protocol/agent-labels";
4
5
  import { SortablePager } from "./pagination/sortable-pager.js";
5
6
  import { normalizeWorkspaceId } from "./workspace-registry-model.js";
6
7
  const FETCH_WORKSPACES_SORT_KEYS = [
@@ -99,6 +100,9 @@ export class WorkspaceDirectory {
99
100
  if (!this.deps.isProviderVisibleToClient(agent.provider)) {
100
101
  continue;
101
102
  }
103
+ if (isDelegatedAgent(agent)) {
104
+ continue;
105
+ }
102
106
  const workspaceId = workspaceIdsByDirectory.get(normalizeWorkspaceId(agent.cwd));
103
107
  if (workspaceId === undefined) {
104
108
  continue;
@@ -150,11 +150,13 @@ interface WorkspaceGitServiceDependencies {
150
150
  interface WorkspaceGitServiceOptions {
151
151
  logger: pino.Logger;
152
152
  paseoHome: string;
153
+ worktreesRoot?: string;
153
154
  deps?: Partial<WorkspaceGitServiceDependencies>;
154
155
  }
155
156
  export declare class WorkspaceGitServiceImpl implements WorkspaceGitService {
156
157
  private readonly logger;
157
158
  private readonly paseoHome;
159
+ private readonly worktreesRoot;
158
160
  private readonly deps;
159
161
  private readonly snapshotUpdatedListeners;
160
162
  private readonly workspaceTargets;
@@ -219,6 +221,7 @@ export declare class WorkspaceGitServiceImpl implements WorkspaceGitService {
219
221
  private scheduleWorkspaceRefresh;
220
222
  private startWorkspaceSubscriptionTimers;
221
223
  private updateGitHubPollForTarget;
224
+ private resolveGitHubPollTarget;
222
225
  private stopGitHubPollForTarget;
223
226
  private addWorkingTreeWatcher;
224
227
  private ensureLinuxRepoTreeWatchers;
@@ -71,6 +71,7 @@ export class WorkspaceGitServiceImpl {
71
71
  this.checkoutDiffCache = new LRUCache({ max: WORKSPACE_GIT_CHECKOUT_DIFF_CACHE_MAX });
72
72
  this.logger = options.logger.child({ module: "workspace-git-service" });
73
73
  this.paseoHome = options.paseoHome;
74
+ this.worktreesRoot = options.worktreesRoot;
74
75
  this.deps = resolveWorkspaceGitServiceDeps(options.deps);
75
76
  }
76
77
  registerWorkspace(params, listener) {
@@ -112,6 +113,7 @@ export class WorkspaceGitServiceImpl {
112
113
  try {
113
114
  const status = await this.deps.getCheckoutStatus(normalizedCwd, {
114
115
  paseoHome: this.paseoHome,
116
+ worktreesRoot: this.worktreesRoot,
115
117
  logger: this.logger,
116
118
  });
117
119
  if (!status.isGit) {
@@ -152,7 +154,10 @@ export class WorkspaceGitServiceImpl {
152
154
  const normalizedCwd = normalizeWorkspaceId(cwd);
153
155
  const normalizedOptions = this.normalizeCheckoutDiffOptions(options);
154
156
  const key = this.buildCheckoutDiffCacheKey(normalizedCwd, normalizedOptions);
155
- return this.readAuxiliaryCache(this.checkoutDiffCache, key, readOptions, () => this.deps.getCheckoutDiff(normalizedCwd, normalizedOptions, { paseoHome: this.paseoHome }));
157
+ return this.readAuxiliaryCache(this.checkoutDiffCache, key, readOptions, () => this.deps.getCheckoutDiff(normalizedCwd, normalizedOptions, {
158
+ paseoHome: this.paseoHome,
159
+ worktreesRoot: this.worktreesRoot,
160
+ }));
156
161
  }
157
162
  normalizeCheckoutDiffOptions(options) {
158
163
  return {
@@ -221,6 +226,7 @@ export class WorkspaceGitServiceImpl {
221
226
  return this.readAuxiliaryCache(this.worktreeListCache, key, options, () => this.deps.listPaseoWorktrees({
222
227
  cwd: repoRoot,
223
228
  paseoHome: this.paseoHome,
229
+ worktreesRoot: this.worktreesRoot,
224
230
  }));
225
231
  }
226
232
  async resolveRepoRoot(cwd, options) {
@@ -380,7 +386,7 @@ export class WorkspaceGitServiceImpl {
380
386
  debounceTimer: null,
381
387
  selfHealTimer: null,
382
388
  githubPollSubscription: null,
383
- githubPollHeadRef: null,
389
+ githubPollKey: null,
384
390
  refreshState: { status: "idle" },
385
391
  latestGit: null,
386
392
  latestGitLoadedAtMs: null,
@@ -676,21 +682,26 @@ export class WorkspaceGitServiceImpl {
676
682
  this.stopGitHubPollForTarget(target);
677
683
  return;
678
684
  }
679
- const headRef = git.currentBranch;
680
- const hasGitHubRemote = target.cachedGitHubRemote?.remoteUrl === git.remoteUrl &&
685
+ const pollTarget = this.resolveGitHubPollTarget(target);
686
+ const remoteUrl = git.remoteUrl;
687
+ const hasGitHubRemote = target.cachedGitHubRemote?.remoteUrl === remoteUrl &&
681
688
  target.cachedGitHubRemote.identity !== null;
682
- if (!headRef || !hasGitHubRemote) {
689
+ if (!pollTarget || remoteUrl === null || !hasGitHubRemote) {
683
690
  this.stopGitHubPollForTarget(target);
684
691
  return;
685
692
  }
686
- if (target.githubPollHeadRef === headRef && target.githubPollSubscription) {
693
+ const pollKey = buildWorkspaceGitHubPollKey(remoteUrl, pollTarget);
694
+ if (target.githubPollKey === pollKey && target.githubPollSubscription) {
687
695
  return;
688
696
  }
689
697
  this.stopGitHubPollForTarget(target);
690
- target.githubPollHeadRef = headRef;
698
+ target.githubPollKey = pollKey;
691
699
  target.githubPollSubscription = this.deps.github.retainCurrentPullRequestStatusPoll({
692
700
  cwd: target.cwd,
693
- headRef,
701
+ headRef: pollTarget.headRef,
702
+ ...(pollTarget.headRepositoryOwner
703
+ ? { headRepositoryOwner: pollTarget.headRepositoryOwner }
704
+ : {}),
694
705
  onStatus: (status) => {
695
706
  if (!this.isActiveObservedWorkspaceTarget(target)) {
696
707
  return;
@@ -700,14 +711,33 @@ export class WorkspaceGitServiceImpl {
700
711
  });
701
712
  },
702
713
  onError: (error) => {
703
- this.logger.warn({ err: error, cwd: target.cwd, headRef, reason: "self-heal-github" }, "Failed to run GitHub self-heal refresh");
714
+ this.logger.warn({
715
+ err: error,
716
+ cwd: target.cwd,
717
+ headRef: pollTarget.headRef,
718
+ headRepositoryOwner: pollTarget.headRepositoryOwner,
719
+ reason: "self-heal-github",
720
+ }, "Failed to run GitHub self-heal refresh");
704
721
  },
705
722
  });
706
723
  }
724
+ resolveGitHubPollTarget(target) {
725
+ const git = target.latestGit;
726
+ if (!git?.currentBranch) {
727
+ return null;
728
+ }
729
+ const lookupTarget = target.latestFacts?.isGit && target.latestFacts.currentBranch === git.currentBranch
730
+ ? target.latestFacts.pullRequestLookupTarget
731
+ : null;
732
+ if (lookupTarget) {
733
+ return lookupTarget;
734
+ }
735
+ return { headRef: git.currentBranch };
736
+ }
707
737
  stopGitHubPollForTarget(target) {
708
738
  target.githubPollSubscription?.unsubscribe();
709
739
  target.githubPollSubscription = null;
710
- target.githubPollHeadRef = null;
740
+ target.githubPollKey = null;
711
741
  }
712
742
  addWorkingTreeWatcher(target, watchPath, shouldTryRecursive) {
713
743
  if (target.watchedPaths.has(watchPath)) {
@@ -998,7 +1028,11 @@ export class WorkspaceGitServiceImpl {
998
1028
  target.lastShellOutAtMs = now.getTime();
999
1029
  const cwd = target.cwd;
1000
1030
  const previousGitHubPollKey = this.getGitHubPollKey(target);
1001
- const baseContext = { paseoHome: this.paseoHome, logger: this.logger };
1031
+ const baseContext = {
1032
+ paseoHome: this.paseoHome,
1033
+ worktreesRoot: this.worktreesRoot,
1034
+ logger: this.logger,
1035
+ };
1002
1036
  const facts = await this.loadCheckoutFacts(target, {
1003
1037
  ...baseContext,
1004
1038
  allowRecent: !request.force,
@@ -1075,7 +1109,11 @@ export class WorkspaceGitServiceImpl {
1075
1109
  if (!githubRemote || githubRemote.remoteUrl !== git.remoteUrl || !githubRemote.identity) {
1076
1110
  return null;
1077
1111
  }
1078
- return JSON.stringify([git.remoteUrl, git.currentBranch]);
1112
+ const pollTarget = this.resolveGitHubPollTarget(target);
1113
+ if (!pollTarget) {
1114
+ return null;
1115
+ }
1116
+ return buildWorkspaceGitHubPollKey(git.remoteUrl, pollTarget);
1079
1117
  }
1080
1118
  rememberGitHubSnapshot(target, github, options) {
1081
1119
  if (target.closed || this.workspaceTargets.get(target.cwd) !== target) {
@@ -1317,6 +1355,9 @@ function buildGitHubSnapshotFromStatus(status) {
1317
1355
  error: null,
1318
1356
  };
1319
1357
  }
1358
+ function buildWorkspaceGitHubPollKey(remoteUrl, target) {
1359
+ return JSON.stringify([remoteUrl, target.headRef, target.headRepositoryOwner ?? null]);
1360
+ }
1320
1361
  async function runGitFetch(cwd) {
1321
1362
  await runGitCommand(["fetch", "origin", "--prune"], {
1322
1363
  cwd,
@@ -39,6 +39,7 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
39
39
  updatedAt: z.ZodString;
40
40
  archivedAt: z.ZodNullable<z.ZodString>;
41
41
  }, "strip", z.ZodTypeAny, {
42
+ workspaceId: string;
42
43
  cwd: string;
43
44
  createdAt: string;
44
45
  updatedAt: string;
@@ -46,8 +47,8 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
46
47
  kind: "worktree" | "local_checkout" | "directory";
47
48
  projectId: string;
48
49
  displayName: string;
49
- workspaceId: string;
50
50
  }, {
51
+ workspaceId: string;
51
52
  cwd: string;
52
53
  createdAt: string;
53
54
  updatedAt: string;
@@ -55,7 +56,6 @@ declare const PersistedWorkspaceRecordSchema: z.ZodObject<{
55
56
  kind: "worktree" | "local_checkout" | "directory";
56
57
  projectId: string;
57
58
  displayName: string;
58
- workspaceId: string;
59
59
  }>;
60
60
  export type PersistedProjectRecord = z.infer<typeof PersistedProjectRecordSchema>;
61
61
  export type PersistedWorkspaceRecord = z.infer<typeof PersistedWorkspaceRecordSchema>;
@@ -8,6 +8,7 @@ export interface BuildWorkspaceServiceEnvOptions {
8
8
  branchName: string | null;
9
9
  daemonPort: number | null | undefined;
10
10
  daemonListenHost: string | null | undefined;
11
+ serviceProxyPublicBaseUrl?: string | null;
11
12
  peers: readonly WorkspaceServicePeer[];
12
13
  }
13
14
  export declare function normalizeServiceEnvName(scriptName: string): string;
@@ -1,4 +1,4 @@
1
- import { buildScriptHostname } from "../utils/script-hostname.js";
1
+ import { projectServiceProxyUrls } from "./service-proxy.js";
2
2
  export function normalizeServiceEnvName(scriptName) {
3
3
  return scriptName
4
4
  .toUpperCase()
@@ -16,24 +16,28 @@ export function buildWorkspaceServiceEnv(options) {
16
16
  HOST: resolveServiceBindHost(options.daemonListenHost),
17
17
  PASEO_PORT: String(selfPeer.port),
18
18
  };
19
- if (options.daemonPort !== null && options.daemonPort !== undefined) {
20
- env.PASEO_URL = buildServiceProxyUrl({
21
- projectSlug: options.projectSlug,
22
- branchName: options.branchName,
23
- scriptName: options.scriptName,
24
- daemonPort: options.daemonPort,
25
- });
19
+ const selfProxyUrl = buildServiceProxyUrl({
20
+ projectSlug: options.projectSlug,
21
+ branchName: options.branchName,
22
+ scriptName: options.scriptName,
23
+ daemonPort: options.daemonPort,
24
+ serviceProxyPublicBaseUrl: options.serviceProxyPublicBaseUrl,
25
+ });
26
+ if (selfProxyUrl) {
27
+ env.PASEO_URL = selfProxyUrl;
26
28
  }
27
29
  for (const peer of options.peers) {
28
30
  const envName = normalizeServiceEnvName(peer.scriptName);
29
31
  env[`PASEO_SERVICE_${envName}_PORT`] = String(peer.port);
30
- if (options.daemonPort !== null && options.daemonPort !== undefined) {
31
- env[`PASEO_SERVICE_${envName}_URL`] = buildServiceProxyUrl({
32
- projectSlug: options.projectSlug,
33
- branchName: options.branchName,
34
- scriptName: peer.scriptName,
35
- daemonPort: options.daemonPort,
36
- });
32
+ const peerProxyUrl = buildServiceProxyUrl({
33
+ projectSlug: options.projectSlug,
34
+ branchName: options.branchName,
35
+ scriptName: peer.scriptName,
36
+ daemonPort: options.daemonPort,
37
+ serviceProxyPublicBaseUrl: options.serviceProxyPublicBaseUrl,
38
+ });
39
+ if (peerProxyUrl) {
40
+ env[`PASEO_SERVICE_${envName}_URL`] = peerProxyUrl;
37
41
  }
38
42
  }
39
43
  return env;
@@ -42,12 +46,13 @@ export function resolveServiceBindHost(daemonListenHost) {
42
46
  return isLoopbackListenHost(daemonListenHost) ? "127.0.0.1" : "0.0.0.0";
43
47
  }
44
48
  function buildServiceProxyUrl(options) {
45
- const hostname = buildScriptHostname({
49
+ return projectServiceProxyUrls({
46
50
  projectSlug: options.projectSlug,
47
51
  branchName: options.branchName,
48
52
  scriptName: options.scriptName,
49
- });
50
- return `http://${hostname}:${options.daemonPort}`;
53
+ daemonPort: options.daemonPort,
54
+ publicBaseUrl: options.serviceProxyPublicBaseUrl,
55
+ }).proxyUrl;
51
56
  }
52
57
  function isLoopbackListenHost(host) {
53
58
  if (!host) {
@@ -13,10 +13,12 @@ export declare function listPaseoWorktreesCommand(dependencies: ListPaseoWorktre
13
13
  type CreatePaseoWorktreeWorkflow<Result extends CreatePaseoWorktreeResult> = (input: CreatePaseoWorktreeInput) => Promise<Result>;
14
14
  export interface CreatePaseoWorktreeCommandDependencies<Result extends CreatePaseoWorktreeResult = CreatePaseoWorktreeResult> {
15
15
  paseoHome?: string;
16
+ worktreesRoot?: string;
16
17
  createPaseoWorktreeWorkflow?: CreatePaseoWorktreeWorkflow<Result>;
17
18
  }
18
19
  export type CreatePaseoWorktreeCommandInput = Omit<CreatePaseoWorktreeInput, "paseoHome" | "runSetup"> & {
19
20
  paseoHome?: string;
21
+ worktreesRoot?: string;
20
22
  };
21
23
  export type CreatePaseoWorktreeCommandResult<Result extends CreatePaseoWorktreeResult> = {
22
24
  ok: true;