@getpaseo/server 0.1.87 → 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 (69) 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 +137 -63
  9. package/dist/server/server/agent/mcp-shared.d.ts +1 -0
  10. package/dist/server/server/agent/providers/pi/agent.js +13 -0
  11. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
  12. package/dist/server/server/agent/timeline-projection.d.ts +17 -1
  13. package/dist/server/server/agent/timeline-projection.js +82 -17
  14. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +1 -0
  15. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +6 -1
  16. package/dist/server/server/bootstrap.d.ts +7 -2
  17. package/dist/server/server/bootstrap.js +152 -115
  18. package/dist/server/server/config.js +41 -0
  19. package/dist/server/server/loop-service.d.ts +22 -22
  20. package/dist/server/server/package-version.d.ts +2 -2
  21. package/dist/server/server/paseo-worktree-archive-service.d.ts +2 -0
  22. package/dist/server/server/paseo-worktree-archive-service.js +28 -9
  23. package/dist/server/server/persisted-config.d.ts +89 -33
  24. package/dist/server/server/persisted-config.js +17 -0
  25. package/dist/server/server/pid-lock.d.ts +2 -2
  26. package/dist/server/server/schedule/cron.js +52 -5
  27. package/dist/server/server/script-health-monitor.d.ts +4 -4
  28. package/dist/server/server/script-health-monitor.js +6 -6
  29. package/dist/server/server/script-proxy.d.ts +2 -39
  30. package/dist/server/server/script-proxy.js +1 -244
  31. package/dist/server/server/script-route-branch-handler.d.ts +2 -2
  32. package/dist/server/server/script-route-branch-handler.js +3 -37
  33. package/dist/server/server/script-status-projection.d.ts +6 -4
  34. package/dist/server/server/script-status-projection.js +85 -37
  35. package/dist/server/server/service-proxy.d.ts +237 -0
  36. package/dist/server/server/service-proxy.js +714 -0
  37. package/dist/server/server/session.d.ts +11 -4
  38. package/dist/server/server/session.js +96 -99
  39. package/dist/server/server/websocket-server.d.ts +7 -4
  40. package/dist/server/server/websocket-server.js +9 -4
  41. package/dist/server/server/workspace-directory.js +4 -0
  42. package/dist/server/server/workspace-git-service.d.ts +3 -0
  43. package/dist/server/server/workspace-git-service.js +53 -12
  44. package/dist/server/server/workspace-registry.d.ts +2 -2
  45. package/dist/server/server/workspace-service-env.d.ts +1 -0
  46. package/dist/server/server/workspace-service-env.js +23 -18
  47. package/dist/server/server/worktree/commands.d.ts +2 -0
  48. package/dist/server/server/worktree/commands.js +4 -1
  49. package/dist/server/server/worktree-bootstrap.d.ts +4 -3
  50. package/dist/server/server/worktree-bootstrap.js +14 -13
  51. package/dist/server/server/worktree-core.d.ts +1 -0
  52. package/dist/server/server/worktree-core.js +2 -0
  53. package/dist/server/server/worktree-session.d.ts +6 -2
  54. package/dist/server/server/worktree-session.js +3 -0
  55. package/dist/server/services/github-service.d.ts +1 -0
  56. package/dist/server/services/github-service.js +7 -1
  57. package/dist/server/terminal/terminal-manager.js +11 -1
  58. package/dist/server/terminal/terminal-session-controller.d.ts +3 -1
  59. package/dist/server/terminal/terminal-session-controller.js +22 -12
  60. package/dist/server/terminal/terminal.d.ts +1 -0
  61. package/dist/server/terminal/terminal.js +34 -0
  62. package/dist/server/utils/checkout-git.d.ts +6 -2
  63. package/dist/server/utils/checkout-git.js +136 -54
  64. package/dist/server/utils/worktree.d.ts +17 -12
  65. package/dist/server/utils/worktree.js +39 -22
  66. package/dist/src/server/persisted-config.js +17 -0
  67. package/package.json +5 -5
  68. package/dist/server/utils/script-hostname.d.ts +0 -8
  69. 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;
@@ -489,7 +493,10 @@ export declare class Session {
489
493
  private handleWorkspaceSetupStatusRequest;
490
494
  private handleArchiveWorkspaceRequest;
491
495
  private handleFetchAgent;
492
- private loadProjectedTimelineWindow;
496
+ private shouldUseFullTimelineForProjectedPage;
497
+ private selectCanonicalTimelineProjection;
498
+ private selectProjectedTimelineProjection;
499
+ private selectTimelineProjection;
493
500
  private handleFetchAgentTimelineRequest;
494
501
  private handleSendAgentMessageRequest;
495
502
  private handleWaitForFinish;
@@ -38,7 +38,7 @@ import { createAgentCommand } from "./agent/create-agent/create.js";
38
38
  import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, setAgentModeCommand, updateAgentCommand, } from "./agent/lifecycle-command.js";
39
39
  import { buildStoredAgentPayload, resolveEffectiveThinkingOptionId, resolveStoredAgentPayloadUpdatedAt, toAgentPayload, } from "./agent/agent-projections.js";
40
40
  import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from "./agent/timeline-append.js";
41
- import { projectTimelineRows, selectTimelineWindowByProjectedLimit, } from "./agent/timeline-projection.js";
41
+ import { projectTimelineRows, selectProjectedTimelinePage, } from "./agent/timeline-projection.js";
42
42
  import { StructuredAgentFallbackError, StructuredAgentResponseError, generateStructuredAgentResponseWithFallback, } from "./agent/agent-response-loop.js";
43
43
  import { resolveStructuredGenerationProviders, } from "./agent/structured-generation-providers.js";
44
44
  import { getAgentStreamEventTurnId, } from "./agent/agent-sdk-types.js";
@@ -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,
@@ -370,9 +371,11 @@ export class Session {
370
371
  hasBinaryChannel: () => this.onBinaryMessage !== null,
371
372
  isPathWithinRoot: (rootPath, candidatePath) => this.isPathWithinRoot(rootPath, candidatePath),
372
373
  sessionLogger: this.sessionLogger,
374
+ clientSupportsWrapReflow: () => this.clientCapabilities.has(CLIENT_CAPS.terminalReflowableSnapshot),
373
375
  });
374
376
  this.createAgentLifecycleDispatch = new CreateAgentLifecycleDispatch({
375
377
  paseoHome: this.paseoHome,
378
+ worktreesRoot: this.worktreesRoot,
376
379
  agentManager: this.agentManager,
377
380
  agentStorage: this.agentStorage,
378
381
  github: this.github,
@@ -397,12 +400,13 @@ export class Session {
397
400
  logger: this.sessionLogger,
398
401
  });
399
402
  this.providerSnapshotManager = providerSnapshotManager;
400
- this.scriptRouteStore = scriptRouteStore ?? null;
403
+ this.serviceProxy = serviceProxy ?? null;
401
404
  this.scriptRuntimeStore = scriptRuntimeStore ?? null;
402
405
  this.workspaceSetupSnapshots = workspaceSetupSnapshots ?? new Map();
403
406
  this.onBranchChanged = onBranchChanged;
404
407
  this.getDaemonTcpPort = getDaemonTcpPort ?? null;
405
408
  this.getDaemonTcpHost = getDaemonTcpHost ?? null;
409
+ this.serviceProxyPublicBaseUrl = serviceProxyPublicBaseUrl ?? null;
406
410
  this.resolveScriptHealth = resolveScriptHealth ?? null;
407
411
  this.sttLanguage = sttLanguage ?? "en";
408
412
  this.subscribeToOptionalManagers();
@@ -2093,6 +2097,7 @@ export class Session {
2093
2097
  agentStorage: this.agentStorage,
2094
2098
  logger: this.sessionLogger,
2095
2099
  paseoHome: this.paseoHome,
2100
+ worktreesRoot: this.worktreesRoot,
2096
2101
  workspaceGitService: this.workspaceGitService,
2097
2102
  providerSnapshotManager: this.providerSnapshotManager,
2098
2103
  daemonConfig: this.readStructuredGenerationDaemonConfig(),
@@ -2427,6 +2432,7 @@ export class Session {
2427
2432
  async buildAgentSessionConfig(config, gitOptions, legacyWorktreeName, firstAgentContext) {
2428
2433
  return buildWorktreeAgentSessionConfig({
2429
2434
  paseoHome: this.paseoHome,
2435
+ worktreesRoot: this.worktreesRoot,
2430
2436
  sessionLogger: this.sessionLogger,
2431
2437
  workspaceGitService: this.workspaceGitService,
2432
2438
  createPaseoWorktree: (input, serviceOptions) => this.createPaseoWorktreeWorkflow(input, {
@@ -3872,7 +3878,7 @@ export class Session {
3872
3878
  const mutatedCwd = await mergeToBase(cwd, {
3873
3879
  baseRef,
3874
3880
  mode: msg.strategy === "squash" ? "squash" : "merge",
3875
- }, { paseoHome: this.paseoHome });
3881
+ }, { paseoHome: this.paseoHome, worktreesRoot: this.worktreesRoot });
3876
3882
  await Promise.all([
3877
3883
  this.notifyGitMutation(mutatedCwd, "merge-to-base", { invalidateGithub: true }),
3878
3884
  ...(mutatedCwd !== cwd ? [this.notifyGitMutation(cwd, "merge-to-base")] : []),
@@ -4292,6 +4298,7 @@ export class Session {
4292
4298
  async handlePaseoWorktreeArchiveRequest(msg) {
4293
4299
  return handleWorktreeArchiveRequest({
4294
4300
  paseoHome: this.paseoHome,
4301
+ worktreesRoot: this.worktreesRoot,
4295
4302
  github: this.github,
4296
4303
  workspaceGitService: this.workspaceGitService,
4297
4304
  agentManager: this.agentManager,
@@ -4729,14 +4736,15 @@ export class Session {
4729
4736
  status: "done",
4730
4737
  activityAt: null,
4731
4738
  diffStat,
4732
- scripts: this.scriptRouteStore && this.scriptRuntimeStore
4739
+ scripts: this.serviceProxy && this.scriptRuntimeStore
4733
4740
  ? buildWorkspaceScriptPayloads({
4734
4741
  workspaceId: workspace.workspaceId,
4735
4742
  workspaceDirectory: workspace.cwd,
4736
4743
  paseoConfig: readPaseoConfigForProjection(workspace.cwd, this.sessionLogger),
4737
- routeStore: this.scriptRouteStore,
4744
+ serviceProxy: this.serviceProxy,
4738
4745
  runtimeStore: this.scriptRuntimeStore,
4739
4746
  daemonPort: this.getDaemonTcpPort?.() ?? null,
4747
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
4740
4748
  gitMetadata: this.resolveWorkspaceScriptGitMetadata(workspace.cwd),
4741
4749
  resolveHealth: this.resolveScriptHealth ?? undefined,
4742
4750
  })
@@ -5398,16 +5406,17 @@ export class Session {
5398
5406
  }
5399
5407
  }
5400
5408
  buildWorkspaceScriptPayloadSnapshot(workspaceId, workspaceDirectory) {
5401
- if (!this.scriptRouteStore || !this.scriptRuntimeStore) {
5409
+ if (!this.serviceProxy || !this.scriptRuntimeStore) {
5402
5410
  return [];
5403
5411
  }
5404
5412
  return buildWorkspaceScriptPayloads({
5405
5413
  workspaceId,
5406
5414
  workspaceDirectory,
5407
5415
  paseoConfig: readPaseoConfigForProjection(workspaceDirectory, this.sessionLogger),
5408
- routeStore: this.scriptRouteStore,
5416
+ serviceProxy: this.serviceProxy,
5409
5417
  runtimeStore: this.scriptRuntimeStore,
5410
5418
  daemonPort: this.getDaemonTcpPort?.() ?? null,
5419
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
5411
5420
  gitMetadata: this.resolveWorkspaceScriptGitMetadata(workspaceDirectory),
5412
5421
  resolveHealth: this.resolveScriptHealth ?? undefined,
5413
5422
  });
@@ -5442,7 +5451,7 @@ export class Session {
5442
5451
  }
5443
5452
  async handleStartWorkspaceScriptRequest(request) {
5444
5453
  try {
5445
- if (!this.terminalManager || !this.scriptRouteStore || !this.scriptRuntimeStore) {
5454
+ if (!this.terminalManager || !this.serviceProxy || !this.scriptRuntimeStore) {
5446
5455
  throw new Error("Workspace scripts are not available on this daemon");
5447
5456
  }
5448
5457
  const workspace = await this.workspaceRegistry.get(request.workspaceId);
@@ -5458,7 +5467,8 @@ export class Session {
5458
5467
  scriptName: request.scriptName,
5459
5468
  daemonPort: this.getDaemonTcpPort?.() ?? null,
5460
5469
  daemonListenHost: this.getDaemonTcpHost?.() ?? null,
5461
- routeStore: this.scriptRouteStore,
5470
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
5471
+ serviceProxy: this.serviceProxy,
5462
5472
  runtimeStore: this.scriptRuntimeStore,
5463
5473
  terminalManager: this.terminalManager,
5464
5474
  logger: this.sessionLogger,
@@ -5553,6 +5563,7 @@ export class Session {
5553
5563
  async handleCreatePaseoWorktreeRequest(request) {
5554
5564
  return handleCreateWorktreeRequest({
5555
5565
  paseoHome: this.paseoHome,
5566
+ worktreesRoot: this.worktreesRoot,
5556
5567
  describeWorkspaceRecord: (result) => this.describeCreatedWorktreeWorkspace(result),
5557
5568
  emit: (message) => this.emit(message),
5558
5569
  sessionLogger: this.sessionLogger,
@@ -5562,6 +5573,7 @@ export class Session {
5562
5573
  async createPaseoWorktreeWorkflow(input, options) {
5563
5574
  return createWorktreeWorkflow({
5564
5575
  paseoHome: this.paseoHome,
5576
+ worktreesRoot: this.worktreesRoot,
5565
5577
  createPaseoWorktree: (workflowInput, serviceOptions) => this.createPaseoWorktree(workflowInput, serviceOptions),
5566
5578
  warmWorkspaceGitData: (workspace) => this.warmWorkspaceGitDataForWorkspace(workspace),
5567
5579
  autoNameWorkspaceBranchForFirstAgent: (autoNameInput) => this.scheduleAutoNameWorkspaceBranchForFirstAgent(autoNameInput),
@@ -5573,10 +5585,11 @@ export class Session {
5573
5585
  sessionLogger: this.sessionLogger,
5574
5586
  terminalManager: this.terminalManager,
5575
5587
  archiveWorkspaceRecord: (workspaceId) => this.archiveWorkspaceRecord(workspaceId),
5576
- scriptRouteStore: this.scriptRouteStore,
5588
+ serviceProxy: this.serviceProxy,
5577
5589
  scriptRuntimeStore: this.scriptRuntimeStore,
5578
5590
  getDaemonTcpPort: this.getDaemonTcpPort,
5579
5591
  getDaemonTcpHost: this.getDaemonTcpHost,
5592
+ serviceProxyPublicBaseUrl: this.serviceProxyPublicBaseUrl,
5580
5593
  onScriptsChanged: (workspaceId, workspaceDirectory) => {
5581
5594
  this.emitWorkspaceScriptStatusUpdate(workspaceId, workspaceDirectory);
5582
5595
  },
@@ -5652,62 +5665,63 @@ export class Session {
5652
5665
  payload: { requestId, agent, project, error: null },
5653
5666
  });
5654
5667
  }
5655
- loadProjectedTimelineWindow(params) {
5656
- const { agentId, direction, cursor, requestedLimit } = params;
5657
- let timeline = params.timeline;
5658
- const projectedLimit = Math.max(1, Math.floor(requestedLimit));
5659
- let fetchLimit = projectedLimit;
5660
- let projectedWindow = selectTimelineWindowByProjectedLimit({
5668
+ shouldUseFullTimelineForProjectedPage(input) {
5669
+ const { timeline } = input;
5670
+ if (timeline.reset || timeline.rows.length === 0 || !timeline.hasOlder) {
5671
+ return false;
5672
+ }
5673
+ const firstRow = timeline.rows[0];
5674
+ if (firstRow?.item.type === "assistant_message" ||
5675
+ firstRow?.item.type === "reasoning" ||
5676
+ firstRow?.item.type === "tool_call") {
5677
+ return true;
5678
+ }
5679
+ return timeline.rows.some((row) => row.item.type === "tool_call");
5680
+ }
5681
+ selectCanonicalTimelineProjection(input) {
5682
+ const entries = projectTimelineRows({ rows: input.timeline.rows, mode: "canonical" });
5683
+ return {
5684
+ timeline: input.timeline,
5685
+ entries,
5686
+ startSeq: entries[0]?.seqStart ?? null,
5687
+ endSeq: entries[entries.length - 1]?.seqEnd ?? null,
5688
+ hasOlder: input.timeline.hasOlder,
5689
+ hasNewer: input.timeline.hasNewer,
5690
+ };
5691
+ }
5692
+ selectProjectedTimelineProjection(input) {
5693
+ const timeline = this.shouldUseFullTimelineForProjectedPage({
5694
+ timeline: input.controlTimeline,
5695
+ })
5696
+ ? this.agentManager.fetchTimeline(input.agentId, { direction: "tail", limit: 0 })
5697
+ : input.controlTimeline;
5698
+ const page = selectProjectedTimelinePage({
5661
5699
  rows: timeline.rows,
5662
- direction,
5663
- limit: projectedLimit,
5664
- collapseToolLifecycle: false,
5700
+ bounds: timeline.window,
5701
+ direction: input.controlTimeline.reset ? "tail" : input.direction,
5702
+ ...(input.cursor ? { cursorSeq: input.cursor.seq } : {}),
5703
+ limit: input.pageLimit,
5665
5704
  });
5666
- while (timeline.hasOlder) {
5667
- const needsMoreProjectedEntries = projectedWindow.projectedEntries.length < projectedLimit;
5668
- const firstLoadedRow = timeline.rows[0];
5669
- const firstSelectedRow = projectedWindow.selectedRows[0];
5670
- const startsAtLoadedBoundary = firstLoadedRow != null &&
5671
- firstSelectedRow != null &&
5672
- firstSelectedRow.seq === firstLoadedRow.seq;
5673
- const boundaryIsAssistantChunk = startsAtLoadedBoundary && firstLoadedRow.item.type === "assistant_message";
5674
- if (!needsMoreProjectedEntries && !boundaryIsAssistantChunk) {
5675
- break;
5676
- }
5677
- const maxRows = Math.max(0, timeline.window.maxSeq - timeline.window.minSeq + 1);
5678
- const nextFetchLimit = Math.min(maxRows, fetchLimit * 2);
5679
- if (nextFetchLimit <= fetchLimit) {
5680
- break;
5681
- }
5682
- fetchLimit = nextFetchLimit;
5683
- timeline = this.agentManager.fetchTimeline(agentId, {
5684
- direction,
5685
- cursor,
5686
- limit: fetchLimit,
5687
- });
5688
- projectedWindow = selectTimelineWindowByProjectedLimit({
5689
- rows: timeline.rows,
5690
- direction,
5691
- limit: projectedLimit,
5692
- collapseToolLifecycle: false,
5693
- });
5694
- }
5695
5705
  return {
5696
5706
  timeline,
5697
- selectedRows: projectedWindow.selectedRows,
5698
- minSeq: projectedWindow.minSeq,
5699
- maxSeq: projectedWindow.maxSeq,
5707
+ entries: page.entries,
5708
+ startSeq: page.startSeq,
5709
+ endSeq: page.endSeq,
5710
+ hasOlder: page.hasOlder || (page.startSeq !== null && page.startSeq > timeline.window.minSeq),
5711
+ hasNewer: page.hasNewer,
5700
5712
  };
5701
5713
  }
5714
+ selectTimelineProjection(input) {
5715
+ if (input.projection === "canonical") {
5716
+ return this.selectCanonicalTimelineProjection({ timeline: input.controlTimeline });
5717
+ }
5718
+ return this.selectProjectedTimelineProjection(input);
5719
+ }
5702
5720
  async handleFetchAgentTimelineRequest(msg) {
5703
5721
  const direction = msg.direction ?? (msg.cursor ? "after" : "tail");
5704
5722
  const projection = msg.projection ?? "projected";
5705
5723
  const requestedLimit = msg.limit;
5706
- const limit = requestedLimit ?? (direction === "after" ? 0 : undefined);
5707
- const shouldLimitByProjectedWindow = projection === "canonical" &&
5708
- direction === "tail" &&
5709
- typeof requestedLimit === "number" &&
5710
- requestedLimit > 0;
5724
+ const pageLimit = requestedLimit ?? (direction === "after" ? 0 : 200);
5711
5725
  const cursor = msg.cursor
5712
5726
  ? {
5713
5727
  epoch: msg.cursor.epoch,
@@ -5721,42 +5735,25 @@ export class Session {
5721
5735
  logger: this.sessionLogger,
5722
5736
  });
5723
5737
  const agentPayload = await this.buildAgentPayload(snapshot);
5724
- let timeline = this.agentManager.fetchTimeline(msg.agentId, {
5738
+ const controlTimeline = this.agentManager.fetchTimeline(msg.agentId, {
5725
5739
  direction,
5726
5740
  cursor,
5727
- limit: shouldLimitByProjectedWindow && typeof requestedLimit === "number"
5728
- ? Math.max(1, Math.floor(requestedLimit))
5729
- : limit,
5730
- });
5731
- let hasOlder = timeline.hasOlder;
5732
- let hasNewer = timeline.hasNewer;
5733
- let startCursor = null;
5734
- let endCursor = null;
5735
- let entries;
5736
- if (shouldLimitByProjectedWindow) {
5737
- const projectedResult = this.loadProjectedTimelineWindow({
5738
- agentId: msg.agentId,
5739
- direction,
5740
- cursor,
5741
- requestedLimit,
5742
- timeline,
5743
- });
5744
- timeline = projectedResult.timeline;
5745
- entries = projectTimelineRows({ rows: projectedResult.selectedRows, mode: projection });
5746
- if (projectedResult.minSeq !== null && projectedResult.maxSeq !== null) {
5747
- startCursor = { epoch: timeline.epoch, seq: projectedResult.minSeq };
5748
- endCursor = { epoch: timeline.epoch, seq: projectedResult.maxSeq };
5749
- hasOlder = projectedResult.minSeq > timeline.window.minSeq;
5750
- hasNewer = false;
5751
- }
5752
- }
5753
- else {
5754
- const firstRow = timeline.rows[0];
5755
- const lastRow = timeline.rows[timeline.rows.length - 1];
5756
- startCursor = firstRow ? { epoch: timeline.epoch, seq: firstRow.seq } : null;
5757
- endCursor = lastRow ? { epoch: timeline.epoch, seq: lastRow.seq } : null;
5758
- entries = projectTimelineRows({ rows: timeline.rows, mode: projection });
5759
- }
5741
+ limit: pageLimit,
5742
+ });
5743
+ const selectedTimeline = this.selectTimelineProjection({
5744
+ agentId: msg.agentId,
5745
+ projection,
5746
+ controlTimeline,
5747
+ direction,
5748
+ ...(cursor ? { cursor } : {}),
5749
+ pageLimit,
5750
+ });
5751
+ const startCursor = selectedTimeline.startSeq !== null
5752
+ ? { epoch: selectedTimeline.timeline.epoch, seq: selectedTimeline.startSeq }
5753
+ : null;
5754
+ const endCursor = selectedTimeline.endSeq !== null
5755
+ ? { epoch: selectedTimeline.timeline.epoch, seq: selectedTimeline.endSeq }
5756
+ : null;
5760
5757
  this.emit({
5761
5758
  type: "fetch_agent_timeline_response",
5762
5759
  payload: {
@@ -5765,16 +5762,16 @@ export class Session {
5765
5762
  agent: agentPayload,
5766
5763
  direction,
5767
5764
  projection,
5768
- epoch: timeline.epoch,
5769
- reset: timeline.reset,
5770
- staleCursor: timeline.staleCursor,
5771
- gap: timeline.gap,
5772
- window: timeline.window,
5765
+ epoch: selectedTimeline.timeline.epoch,
5766
+ reset: controlTimeline.reset,
5767
+ staleCursor: controlTimeline.staleCursor,
5768
+ gap: controlTimeline.gap,
5769
+ window: selectedTimeline.timeline.window,
5773
5770
  startCursor,
5774
5771
  endCursor,
5775
- hasOlder,
5776
- hasNewer,
5777
- entries: entries.map((entry) => ({
5772
+ hasOlder: selectedTimeline.hasOlder,
5773
+ hasNewer: selectedTimeline.hasNewer,
5774
+ entries: selectedTimeline.entries.map((entry) => ({
5778
5775
  provider: snapshot.provider,
5779
5776
  item: entry.item,
5780
5777
  timestamp: entry.timestamp,
@@ -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;