@getpaseo/server 0.1.101 → 0.1.102

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 (141) hide show
  1. package/.env.example +8 -0
  2. package/dist/scripts/supervisor.js +26 -8
  3. package/dist/server/server/agent/activity-curator.d.ts +17 -0
  4. package/dist/server/server/agent/activity-curator.js +101 -24
  5. package/dist/server/server/agent/agent-manager.js +5 -1
  6. package/dist/server/server/agent/agent-sdk-types.d.ts +7 -2
  7. package/dist/server/server/agent/provider-registry.js +12 -11
  8. package/dist/server/server/agent/provider-snapshot-manager.d.ts +8 -1
  9. package/dist/server/server/agent/provider-snapshot-manager.js +78 -33
  10. package/dist/server/server/agent/providers/acp-agent.d.ts +13 -0
  11. package/dist/server/server/agent/providers/acp-agent.js +37 -1
  12. package/dist/server/server/agent/providers/claude/agent.js +51 -14
  13. package/dist/server/server/agent/providers/claude/query.d.ts +3 -0
  14. package/dist/server/server/agent/providers/claude/query.js +4 -2
  15. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +2 -1
  16. package/dist/server/server/agent/providers/generic-acp-agent.js +2 -1
  17. package/dist/server/server/agent/providers/kiro-acp-agent.d.ts +17 -0
  18. package/dist/server/server/agent/providers/kiro-acp-agent.js +78 -0
  19. package/dist/server/server/agent/providers/mock-load-test-agent.js +8 -0
  20. package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
  21. package/dist/server/server/agent/providers/opencode/paths.js +7 -0
  22. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +2 -0
  23. package/dist/server/server/agent/providers/opencode/server-manager.js +34 -5
  24. package/dist/server/server/agent/providers/opencode-agent.d.ts +4 -0
  25. package/dist/server/server/agent/providers/opencode-agent.js +14 -2
  26. package/dist/server/server/agent/providers/pi/agent.d.ts +3 -0
  27. package/dist/server/server/agent/providers/pi/agent.js +9 -3
  28. package/dist/server/server/agent/providers/provider-image-output.js +11 -6
  29. package/dist/server/server/agent/tools/paseo-tools.d.ts +1 -1
  30. package/dist/server/server/agent/tools/paseo-tools.js +0 -2
  31. package/dist/server/server/bootstrap.d.ts +7 -1
  32. package/dist/server/server/bootstrap.js +18 -0
  33. package/dist/server/server/config.d.ts +2 -0
  34. package/dist/server/server/config.js +57 -1
  35. package/dist/server/server/daemon-worker.js +19 -7
  36. package/dist/server/server/lifecycle-reasons.d.ts +4 -0
  37. package/dist/server/server/lifecycle-reasons.js +6 -0
  38. package/dist/server/server/persisted-config.d.ts +13 -2
  39. package/dist/server/server/persisted-config.js +30 -11
  40. package/dist/server/server/process-diagnostics.d.ts +17 -0
  41. package/dist/server/server/process-diagnostics.js +22 -0
  42. package/dist/server/server/relay-transport.js +1 -0
  43. package/dist/server/server/resolve-worktree-creation-intent.js +3 -1
  44. package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
  45. package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
  46. package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
  47. package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
  48. package/dist/server/server/session/daemon/daemon-session.d.ts +12 -0
  49. package/dist/server/server/session/daemon/daemon-session.js +12 -0
  50. package/dist/server/server/session/daemon/diagnostics.js +10 -0
  51. package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
  52. package/dist/server/server/session/daemon/install-origin.js +64 -0
  53. package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
  54. package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
  55. package/dist/server/server/session/provider/provider-catalog-session.js +8 -4
  56. package/dist/server/server/session.d.ts +5 -3
  57. package/dist/server/server/session.js +74 -32
  58. package/dist/server/server/speech/providers/openai/config.d.ts +0 -2
  59. package/dist/server/server/speech/providers/openai/config.js +71 -34
  60. package/dist/server/server/speech/providers/openai/runtime.js +4 -4
  61. package/dist/server/server/web-ui.d.ts +10 -0
  62. package/dist/server/server/web-ui.js +205 -0
  63. package/dist/server/server/websocket/runtime-metrics.d.ts +3 -0
  64. package/dist/server/server/websocket-server.d.ts +3 -0
  65. package/dist/server/server/websocket-server.js +190 -32
  66. package/dist/server/services/quota-fetcher/manifest.js +5 -0
  67. package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
  68. package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
  69. package/dist/server/terminal/agent-hooks/agent-hook-installer.js +2 -2
  70. package/dist/server/utils/checkout-git.js +156 -3
  71. package/dist/server/utils/directory-suggestions.js +1 -4
  72. package/dist/server/utils/path.d.ts +2 -0
  73. package/dist/server/utils/path.js +13 -0
  74. package/dist/server/utils/worktree.d.ts +1 -0
  75. package/dist/server/utils/worktree.js +92 -11
  76. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
  77. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
  78. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
  79. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-5fa9347afb28c7d61bbf73684f3bdafa.js +1 -0
  80. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-5fa9347afb28c7d61bbf73684f3bdafa.js.br +0 -0
  81. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-5fa9347afb28c7d61bbf73684f3bdafa.js.gz +0 -0
  82. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-35159ed50cf218b741c5dab5a6f4a889.js +1 -0
  83. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-35159ed50cf218b741c5dab5a6f4a889.js.br +0 -0
  84. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-35159ed50cf218b741c5dab5a6f4a889.js.gz +0 -0
  85. package/dist/server/web-ui/_expo/static/js/web/index-cf63ba3f4de4164d2d9c3738e1b791b2.js +16157 -0
  86. package/dist/server/web-ui/_expo/static/js/web/index-cf63ba3f4de4164d2d9c3738e1b791b2.js.br +0 -0
  87. package/dist/server/web-ui/_expo/static/js/web/index-cf63ba3f4de4164d2d9c3738e1b791b2.js.gz +0 -0
  88. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-6a6cfdd9f06d4fd2b05adc1a8771c58c.js +1 -0
  89. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-6a6cfdd9f06d4fd2b05adc1a8771c58c.js.br +0 -0
  90. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-6a6cfdd9f06d4fd2b05adc1a8771c58c.js.gz +0 -0
  91. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-b31888f9a7de4f0c52d6a58fcddbcb20.js +3 -0
  92. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-b31888f9a7de4f0c52d6a58fcddbcb20.js.br +0 -0
  93. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-b31888f9a7de4f0c52d6a58fcddbcb20.js.gz +0 -0
  94. package/dist/server/web-ui/apple-touch-icon.png +0 -0
  95. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  96. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  97. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  98. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  99. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  100. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  101. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  102. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  103. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  104. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  105. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  106. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  107. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  108. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  109. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  110. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  111. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  112. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  113. package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
  114. package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
  115. package/dist/server/web-ui/assets/assets/images/editor-apps/file-explorer.3e15e8f72c825c85ce336bcb0cdef776.png +0 -0
  116. package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
  117. package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
  118. package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
  119. package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
  120. package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
  121. package/dist/server/web-ui/assets/assets/images/favicon-dark-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
  122. package/dist/server/web-ui/assets/assets/images/favicon-dark.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
  123. package/dist/server/web-ui/assets/assets/images/favicon-light-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
  124. package/dist/server/web-ui/assets/assets/images/favicon-light-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
  125. package/dist/server/web-ui/assets/assets/images/favicon-light.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
  126. package/dist/server/web-ui/assets/assets/images/notification-icon.3bf81d33ddbf380606bdd248ba83e158.png +0 -0
  127. package/dist/server/web-ui/favicon.ico +0 -0
  128. package/dist/server/web-ui/index.html +90 -0
  129. package/dist/server/web-ui/index.html.br +0 -0
  130. package/dist/server/web-ui/index.html.gz +0 -0
  131. package/dist/server/web-ui/manifest.json +27 -0
  132. package/dist/server/web-ui/manifest.json.br +0 -0
  133. package/dist/server/web-ui/manifest.json.gz +0 -0
  134. package/dist/server/web-ui/metadata.json +1 -0
  135. package/dist/server/web-ui/metadata.json.br +1 -0
  136. package/dist/server/web-ui/metadata.json.gz +0 -0
  137. package/dist/server/web-ui/pwa-icon-192.png +0 -0
  138. package/dist/server/web-ui/pwa-icon-512.png +0 -0
  139. package/dist/server/web-ui/robots.txt +2 -0
  140. package/dist/src/server/persisted-config.js +30 -11
  141. package/package.json +7 -7
@@ -18,11 +18,13 @@ import { createWorkspaceScriptsService, } from "./session/workspace-scripts/work
18
18
  import { getErrorMessage, getErrorMessageOr } from "@getpaseo/protocol/error-utils";
19
19
  import { getAgentStatusPriority } from "@getpaseo/protocol/agent-state-bucket";
20
20
  import { getParentAgentIdFromLabels } from "@getpaseo/protocol/agent-labels";
21
+ import { CLIENT_SHUTDOWN_RPC_REASON, normalizeClientRestartRpcReason, } from "./lifecycle-reasons.js";
21
22
  import { createAgentCommand } from "./agent/create-agent/create.js";
22
23
  import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, detachAgentCommand, setAgentModeCommand, updateAgentCommand, } from "./agent/lifecycle-command.js";
23
24
  import { buildStoredAgentPayload, resolveStoredAgentPayloadUpdatedAt, toAgentPayload, } from "./agent/agent-projections.js";
24
25
  import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from "./agent/timeline-append.js";
25
26
  import { projectTimelineRows, selectProjectedTimelinePage, } from "./agent/timeline-projection.js";
27
+ import { buildAgentForkContextAttachment } from "./agent/activity-curator.js";
26
28
  import { getAgentStreamEventTurnId, } from "./agent/agent-sdk-types.js";
27
29
  import { ImportSessionsRequestError, importProviderSession, listImportableProviderSessions, normalizeImportAgentRequest, } from "./agent/import-sessions.js";
28
30
  import { checkoutLiteFromGitSnapshot, deriveWorkspaceDisplayName, } from "./workspace-registry-model.js";
@@ -355,7 +357,9 @@ export class Session {
355
357
  this.daemonSession = new DaemonSession({
356
358
  host: {
357
359
  emit: (msg) => this.emit(msg),
360
+ emitLifecycleIntent: (intent) => this.emitLifecycleIntent(intent),
358
361
  },
362
+ clientId: this.clientId,
359
363
  paseoHome: this.paseoHome,
360
364
  serverId,
361
365
  daemonVersion,
@@ -746,12 +750,6 @@ export class Session {
746
750
  };
747
751
  }
748
752
  async buildProjectPlacementForWorkspaceId(workspaceId) {
749
- const workspace = await this.workspaceRegistry.get(workspaceId);
750
- if (!workspace)
751
- return null;
752
- return this.buildProjectPlacementForWorkspace(workspace);
753
- }
754
- async buildProjectPlacementForExistingWorkspaceProject(workspaceId) {
755
753
  const workspace = await this.workspaceRegistry.get(workspaceId);
756
754
  if (!workspace)
757
755
  return null;
@@ -815,6 +813,7 @@ export class Session {
815
813
  const promise = this.dispatchVoiceAndControlMessage(msg) ??
816
814
  this.dispatchAgentRewindMessage(msg) ??
817
815
  this.dispatchAgentRelationshipMessage(msg) ??
816
+ this.dispatchAgentTimelineMessage(msg) ??
818
817
  this.dispatchAgentLifecycleMessage(msg) ??
819
818
  this.dispatchAgentConfigMessage(msg) ??
820
819
  this.dispatchCheckoutMessage(msg) ??
@@ -891,6 +890,16 @@ export class Session {
891
890
  return undefined;
892
891
  }
893
892
  }
893
+ dispatchAgentTimelineMessage(msg) {
894
+ switch (msg.type) {
895
+ case "fetch_agent_timeline_request":
896
+ return this.handleFetchAgentTimelineRequest(msg);
897
+ case "agent.fork_context.request":
898
+ return this.handleAgentForkContextRequest(msg);
899
+ default:
900
+ return undefined;
901
+ }
902
+ }
894
903
  dispatchAgentLifecycleMessage(msg) {
895
904
  switch (msg.type) {
896
905
  case "fetch_agents_request":
@@ -925,8 +934,6 @@ export class Session {
925
934
  return this.handleRefreshAgentRequest(msg);
926
935
  case "cancel_agent_request":
927
936
  return this.handleCancelAgentRequest(msg.agentId, msg.requestId);
928
- case "fetch_agent_timeline_request":
929
- return this.handleFetchAgentTimelineRequest(msg);
930
937
  case "agent_permission_response":
931
938
  return this.handleAgentPermissionResponse(msg.agentId, msg.requestId, msg.response);
932
939
  case "clear_agent_attention":
@@ -957,6 +964,8 @@ export class Session {
957
964
  return this.daemonSession.handleGetPairingOfferRequest(msg);
958
965
  case "diagnostics.request":
959
966
  return this.daemonSession.handleDiagnosticsRequest(msg);
967
+ case "daemon.update.request":
968
+ return this.daemonSession.handleUpdateRequest(msg);
960
969
  case "set_daemon_config_request":
961
970
  this.emit({
962
971
  type: "set_daemon_config_response",
@@ -1164,6 +1173,9 @@ export class Session {
1164
1173
  resetPeakInflight() {
1165
1174
  this.peakInflightRequests = this.inflightRequests;
1166
1175
  }
1176
+ getSessionId() {
1177
+ return this.sessionId;
1178
+ }
1167
1179
  async handleBinaryFrame(binaryFrame) {
1168
1180
  if (binaryFrame.kind === "file_transfer") {
1169
1181
  await this.workspaceFilesSession.handleFileTransferFrame(binaryFrame.frame);
@@ -1172,6 +1184,7 @@ export class Session {
1172
1184
  this.terminalController.handleBinaryFrame(binaryFrame.frame);
1173
1185
  }
1174
1186
  async handleRestartServerRequest(requestId, reason) {
1187
+ const lifecycleReason = normalizeClientRestartRpcReason(reason);
1175
1188
  const payload = {
1176
1189
  status: "restart_requested",
1177
1190
  clientId: this.clientId,
@@ -1180,7 +1193,7 @@ export class Session {
1180
1193
  payload.reason = reason;
1181
1194
  }
1182
1195
  payload.requestId = requestId;
1183
- this.sessionLogger.warn({ reason }, "Restart requested via websocket");
1196
+ this.sessionLogger.warn({ reason: lifecycleReason }, "Restart requested via websocket");
1184
1197
  this.emit({
1185
1198
  type: "status",
1186
1199
  payload,
@@ -1189,11 +1202,12 @@ export class Session {
1189
1202
  type: "restart",
1190
1203
  clientId: this.clientId,
1191
1204
  requestId,
1192
- ...(reason ? { reason } : {}),
1205
+ reason: lifecycleReason,
1193
1206
  });
1194
1207
  }
1195
1208
  async handleShutdownServerRequest(requestId) {
1196
- this.sessionLogger.warn("Shutdown requested via websocket");
1209
+ const reason = CLIENT_SHUTDOWN_RPC_REASON;
1210
+ this.sessionLogger.warn({ reason }, "Shutdown requested via websocket");
1197
1211
  this.emit({
1198
1212
  type: "status",
1199
1213
  payload: {
@@ -1206,6 +1220,7 @@ export class Session {
1206
1220
  type: "shutdown",
1207
1221
  clientId: this.clientId,
1208
1222
  requestId,
1223
+ reason,
1209
1224
  });
1210
1225
  }
1211
1226
  emitLifecycleIntent(intent) {
@@ -1691,6 +1706,7 @@ export class Session {
1691
1706
  cwd: createAgentConfig.cwd,
1692
1707
  initialTitle: workspacePromptTitle,
1693
1708
  });
1709
+ const createdDirectoryWorkspaceForAgent = !createdWorktree && !msg.workspaceId;
1694
1710
  const { snapshot, liveSnapshot } = await createAgentCommand({
1695
1711
  agentManager: this.agentManager,
1696
1712
  agentStorage: this.agentStorage,
@@ -1716,11 +1732,8 @@ export class Session {
1716
1732
  buildSessionConfig: (sessionConfig, gitOptions, legacyWorktreeName, ctx) => this.buildAgentSessionConfig(sessionConfig, gitOptions, legacyWorktreeName, ctx),
1717
1733
  });
1718
1734
  createdAgentId = snapshot.id;
1719
- if (!createdWorktree && msg.workspaceId) {
1720
- await this.writeInitialWorkspaceTitleIfUntitled(workspaceId, workspacePromptTitle);
1721
- }
1722
1735
  await this.agentUpdates.forwardLiveAgent(snapshot);
1723
- if (!createdWorktree && trimmedPrompt) {
1736
+ if (createdDirectoryWorkspaceForAgent && trimmedPrompt) {
1724
1737
  await this.scheduleAutoNameLocalWorkspaceTitleForFirstAgent({
1725
1738
  workspaceId,
1726
1739
  cwd: createAgentConfig.cwd,
@@ -2118,20 +2131,6 @@ export class Session {
2118
2131
  updatedAt: new Date().toISOString(),
2119
2132
  });
2120
2133
  }
2121
- async writeInitialWorkspaceTitleIfUntitled(workspaceId, title) {
2122
- if (!title) {
2123
- return;
2124
- }
2125
- const current = await this.workspaceRegistry.get(workspaceId);
2126
- if (!current || current.title) {
2127
- return;
2128
- }
2129
- await this.workspaceRegistry.upsert({
2130
- ...current,
2131
- title,
2132
- updatedAt: new Date().toISOString(),
2133
- });
2134
- }
2135
2134
  // Wraps the injected workspace-name generator for a directory workspace.
2136
2135
  async generateWorkspaceTitleFromContext(input) {
2137
2136
  return this.generateWorkspaceName({
@@ -2624,9 +2623,7 @@ export class Session {
2624
2623
  if (existing) {
2625
2624
  return existing;
2626
2625
  }
2627
- const placementPromise = request.type === "fetch_agent_history_request"
2628
- ? this.buildProjectPlacementForExistingWorkspaceProject(workspaceId)
2629
- : this.buildProjectPlacementForWorkspaceId(workspaceId);
2626
+ const placementPromise = this.buildProjectPlacementForWorkspaceId(workspaceId);
2630
2627
  placementByWorkspaceId.set(workspaceId, placementPromise);
2631
2628
  return placementPromise;
2632
2629
  };
@@ -4038,6 +4035,51 @@ export class Session {
4038
4035
  });
4039
4036
  }
4040
4037
  }
4038
+ async handleAgentForkContextRequest(msg) {
4039
+ try {
4040
+ const snapshot = await ensureAgentLoaded(msg.agentId, {
4041
+ agentManager: this.agentManager,
4042
+ agentStorage: this.agentStorage,
4043
+ logger: this.sessionLogger,
4044
+ });
4045
+ const agentPayload = await this.buildAgentPayload(snapshot);
4046
+ const rows = this.agentManager.fetchTimeline(msg.agentId, {
4047
+ direction: "tail",
4048
+ limit: 0,
4049
+ }).rows;
4050
+ const forkContext = buildAgentForkContextAttachment({
4051
+ rows,
4052
+ boundaryMessageId: msg.boundaryMessageId,
4053
+ agentTitle: agentPayload.title,
4054
+ cwd: snapshot.cwd,
4055
+ });
4056
+ this.emit({
4057
+ type: "agent.fork_context.response",
4058
+ payload: {
4059
+ requestId: msg.requestId,
4060
+ agentId: msg.agentId,
4061
+ attachment: forkContext.attachment,
4062
+ itemCount: forkContext.itemCount,
4063
+ boundaryMessageId: forkContext.boundaryMessageId,
4064
+ error: null,
4065
+ },
4066
+ });
4067
+ }
4068
+ catch (error) {
4069
+ this.sessionLogger.error({ err: error, agentId: msg.agentId }, "Failed to handle agent.fork_context.request");
4070
+ this.emit({
4071
+ type: "agent.fork_context.response",
4072
+ payload: {
4073
+ requestId: msg.requestId,
4074
+ agentId: msg.agentId,
4075
+ attachment: null,
4076
+ itemCount: 0,
4077
+ boundaryMessageId: msg.boundaryMessageId ?? null,
4078
+ error: error instanceof Error ? error.message : String(error),
4079
+ },
4080
+ });
4081
+ }
4082
+ }
4041
4083
  async handleSendAgentMessageRequest(msg) {
4042
4084
  const resolved = await this.resolveAgentIdentifier(msg.agentId);
4043
4085
  if (!resolved.ok) {
@@ -4,8 +4,6 @@ import type { STTConfig } from "./stt.js";
4
4
  import type { TTSConfig } from "./tts.js";
5
5
  export declare const DEFAULT_OPENAI_TTS_MODEL = "tts-1";
6
6
  export interface OpenAiSpeechProviderConfig {
7
- apiKey?: string;
8
- baseUrl?: string;
9
7
  stt?: Partial<STTConfig> & {
10
8
  apiKey?: string;
11
9
  };
@@ -9,11 +9,21 @@ const OptionalTrimmedStringSchema = z
9
9
  .trim()
10
10
  .optional()
11
11
  .transform((value) => (value && value.length > 0 ? value : undefined));
12
- const OpenAiSpeechResolutionSchema = z.object({
13
- apiKey: OptionalTrimmedStringSchema,
14
- baseUrl: OptionalTrimmedStringSchema,
12
+ // Endpoint credentials only — plain trimmed strings, so this never throws on a
13
+ // malformed value. The STT/TTS option groups parse separately and only for the
14
+ // endpoint that is actually configured, so a stale env var for an unused endpoint
15
+ // (e.g. a leftover TTS_VOICE in an STT-only setup) can't break the other one.
16
+ const OpenAiEndpointKeysSchema = z.object({
17
+ sttApiKey: OptionalTrimmedStringSchema,
18
+ sttBaseUrl: OptionalTrimmedStringSchema,
19
+ ttsApiKey: OptionalTrimmedStringSchema,
20
+ ttsBaseUrl: OptionalTrimmedStringSchema,
21
+ });
22
+ const OpenAiSttOptionsSchema = z.object({
15
23
  sttConfidenceThreshold: OptionalFiniteNumberSchema,
16
24
  sttModel: OptionalTrimmedStringSchema,
25
+ });
26
+ const OpenAiTtsOptionsSchema = z.object({
17
27
  ttsVoice: z.string().trim().toLowerCase().pipe(OpenAiTtsVoiceSchema).default("alloy"),
18
28
  ttsModel: z
19
29
  .string()
@@ -30,9 +40,15 @@ function pickIfOpenAi(provider, value) {
30
40
  }
31
41
  function firstDefined(values) {
32
42
  for (const value of values) {
33
- if (value !== undefined && value !== null) {
34
- return value;
43
+ if (value === undefined || value === null) {
44
+ continue;
45
+ }
46
+ // Empty/whitespace env vars (e.g. a copied .env.example with OPENAI_STT_API_KEY=)
47
+ // must not shadow a later fallback such as OPENAI_API_KEY.
48
+ if (typeof value === "string" && value.trim().length === 0) {
49
+ continue;
35
50
  }
51
+ return value;
36
52
  }
37
53
  return undefined;
38
54
  }
@@ -66,46 +82,67 @@ function buildOpenAiTtsInput(params) {
66
82
  };
67
83
  }
68
84
  function buildOpenAiResolutionInput(params) {
85
+ const { env } = params;
86
+ const openai = params.persisted.providers?.openai;
69
87
  return {
70
- apiKey: firstDefined([
71
- params.persisted.providers?.openai?.voice?.apiKey,
72
- params.env.OPENAI_VOICE_API_KEY,
73
- params.persisted.providers?.openai?.apiKey,
74
- params.env.OPENAI_API_KEY,
88
+ sttApiKey: firstDefined([
89
+ openai?.stt?.apiKey,
90
+ env.OPENAI_STT_API_KEY,
91
+ openai?.apiKey,
92
+ env.OPENAI_API_KEY,
75
93
  ]),
76
- baseUrl: firstDefined([
77
- params.persisted.providers?.openai?.voice?.baseUrl,
78
- params.env.OPENAI_VOICE_BASE_URL,
79
- params.persisted.providers?.openai?.baseUrl,
80
- params.env.OPENAI_BASE_URL,
94
+ sttBaseUrl: firstDefined([
95
+ openai?.stt?.baseUrl,
96
+ env.OPENAI_STT_BASE_URL,
97
+ openai?.baseUrl,
98
+ env.OPENAI_BASE_URL,
99
+ ]),
100
+ ttsApiKey: firstDefined([
101
+ openai?.tts?.apiKey,
102
+ env.OPENAI_TTS_API_KEY,
103
+ openai?.apiKey,
104
+ env.OPENAI_API_KEY,
105
+ ]),
106
+ ttsBaseUrl: firstDefined([
107
+ openai?.tts?.baseUrl,
108
+ env.OPENAI_TTS_BASE_URL,
109
+ openai?.baseUrl,
110
+ env.OPENAI_BASE_URL,
81
111
  ]),
82
112
  ...buildOpenAiSttInput(params),
83
113
  ...buildOpenAiTtsInput(params),
84
114
  };
85
115
  }
86
116
  export function resolveOpenAiSpeechConfig(params) {
87
- const parsed = OpenAiSpeechResolutionSchema.parse(buildOpenAiResolutionInput(params));
88
- if (!parsed.apiKey) {
117
+ const input = buildOpenAiResolutionInput(params);
118
+ const keys = OpenAiEndpointKeysSchema.parse(input);
119
+ if (!keys.sttApiKey && !keys.ttsApiKey) {
89
120
  return undefined;
90
121
  }
91
122
  return {
92
- apiKey: parsed.apiKey,
93
- ...(parsed.baseUrl ? { baseUrl: parsed.baseUrl } : {}),
94
- stt: {
95
- apiKey: parsed.apiKey,
96
- ...(parsed.baseUrl ? { baseUrl: parsed.baseUrl } : {}),
97
- ...(parsed.sttConfidenceThreshold !== undefined
98
- ? { confidenceThreshold: parsed.sttConfidenceThreshold }
99
- : {}),
100
- ...(parsed.sttModel ? { model: parsed.sttModel } : {}),
101
- },
102
- tts: {
103
- apiKey: parsed.apiKey,
104
- ...(parsed.baseUrl ? { baseUrl: parsed.baseUrl } : {}),
105
- voice: parsed.ttsVoice,
106
- model: parsed.ttsModel,
107
- responseFormat: "pcm",
108
- },
123
+ ...(keys.sttApiKey ? { stt: buildSttConfig(keys.sttApiKey, keys.sttBaseUrl, input) } : {}),
124
+ ...(keys.ttsApiKey ? { tts: buildTtsConfig(keys.ttsApiKey, keys.ttsBaseUrl, input) } : {}),
125
+ };
126
+ }
127
+ function buildSttConfig(apiKey, baseUrl, input) {
128
+ const options = OpenAiSttOptionsSchema.parse(input);
129
+ return {
130
+ apiKey,
131
+ ...(baseUrl ? { baseUrl } : {}),
132
+ ...(options.sttConfidenceThreshold !== undefined
133
+ ? { confidenceThreshold: options.sttConfidenceThreshold }
134
+ : {}),
135
+ ...(options.sttModel ? { model: options.sttModel } : {}),
136
+ };
137
+ }
138
+ function buildTtsConfig(apiKey, baseUrl, input) {
139
+ const options = OpenAiTtsOptionsSchema.parse(input);
140
+ return {
141
+ apiKey,
142
+ ...(baseUrl ? { baseUrl } : {}),
143
+ voice: options.ttsVoice,
144
+ model: options.ttsModel,
145
+ responseFormat: "pcm",
109
146
  };
110
147
  }
111
148
  //# sourceMappingURL=config.js.map
@@ -2,11 +2,11 @@ import { DEFAULT_OPENAI_TTS_MODEL } from "./config.js";
2
2
  import { OpenAISTT } from "./stt.js";
3
3
  import { OpenAITTS } from "./tts.js";
4
4
  function resolveOpenAiCredentials(openaiConfig) {
5
- const openaiApiKey = openaiConfig?.apiKey;
5
+ const sttApiKey = openaiConfig?.stt?.apiKey;
6
6
  return {
7
- openaiSttApiKey: openaiConfig?.stt?.apiKey ?? openaiApiKey,
8
- openaiTtsApiKey: openaiConfig?.tts?.apiKey ?? openaiApiKey,
9
- openaiDictationApiKey: openaiApiKey,
7
+ openaiSttApiKey: sttApiKey,
8
+ openaiTtsApiKey: openaiConfig?.tts?.apiKey,
9
+ openaiDictationApiKey: sttApiKey,
10
10
  };
11
11
  }
12
12
  export function getOpenAiSpeechAvailability(openaiConfig) {
@@ -0,0 +1,10 @@
1
+ import type { RequestHandler } from "express";
2
+ import type { Logger } from "pino";
3
+ export interface WebUiMiddlewareOptions {
4
+ enabled: boolean;
5
+ distDir: string | null;
6
+ label: string;
7
+ logger: Logger;
8
+ }
9
+ export declare function createWebUiMiddleware(options: WebUiMiddlewareOptions): RequestHandler;
10
+ //# sourceMappingURL=web-ui.d.ts.map
@@ -0,0 +1,205 @@
1
+ import { createReadStream, readFileSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ const EXCLUDED_PATH_PREFIXES = ["/api/", "/mcp/", "/public/"];
4
+ const EXCLUDED_PATHS = new Set(["/api", "/mcp", "/public"]);
5
+ function isExcludedPath(requestPath) {
6
+ for (const prefix of EXCLUDED_PATH_PREFIXES) {
7
+ if (requestPath.startsWith(prefix)) {
8
+ return true;
9
+ }
10
+ }
11
+ return EXCLUDED_PATHS.has(requestPath);
12
+ }
13
+ const CONTENT_TYPES = {
14
+ ".html": "text/html; charset=utf-8",
15
+ ".js": "application/javascript; charset=utf-8",
16
+ ".mjs": "application/javascript; charset=utf-8",
17
+ ".css": "text/css; charset=utf-8",
18
+ ".json": "application/json; charset=utf-8",
19
+ ".png": "image/png",
20
+ ".jpg": "image/jpeg",
21
+ ".jpeg": "image/jpeg",
22
+ ".gif": "image/gif",
23
+ ".svg": "image/svg+xml",
24
+ ".ico": "image/x-icon",
25
+ ".woff": "font/woff",
26
+ ".woff2": "font/woff2",
27
+ ".ttf": "font/ttf",
28
+ ".otf": "font/otf",
29
+ ".eot": "application/vnd.ms-fontobject",
30
+ ".map": "application/json",
31
+ };
32
+ function getContentType(filePath) {
33
+ const ext = path.extname(filePath).toLowerCase();
34
+ return CONTENT_TYPES[ext] ?? "application/octet-stream";
35
+ }
36
+ function selectEncoding(acceptEncoding) {
37
+ if (!acceptEncoding) {
38
+ return null;
39
+ }
40
+ const normalized = acceptEncoding.toLowerCase();
41
+ if (normalized.includes("br")) {
42
+ return "br";
43
+ }
44
+ if (normalized.includes("gzip")) {
45
+ return "gzip";
46
+ }
47
+ return null;
48
+ }
49
+ function isHashedAsset(filePath) {
50
+ const base = path.basename(filePath);
51
+ // Match content hashes like index-abc123def456.js or main.abc123def456.css.
52
+ return /[-.][0-9a-f]{16,}[-.]/i.test(base);
53
+ }
54
+ function isInsideDir(targetPath, dirPath) {
55
+ const resolvedDir = path.resolve(dirPath);
56
+ const resolvedTarget = path.resolve(targetPath);
57
+ return resolvedTarget === resolvedDir || resolvedTarget.startsWith(resolvedDir + path.sep);
58
+ }
59
+ function resolveTargetFile(distDir, requestPath) {
60
+ const safePath = path.normalize(requestPath).replace(/^(\.\.[/\\])+/, "");
61
+ let filePath = path.join(distDir, safePath);
62
+ const stat = safeStat(filePath);
63
+ if (stat?.isDirectory()) {
64
+ filePath = path.join(filePath, "index.html");
65
+ }
66
+ const finalStat = safeStat(filePath);
67
+ if (!finalStat?.isFile()) {
68
+ filePath = path.join(distDir, "index.html");
69
+ const fallbackStat = safeStat(filePath);
70
+ if (!fallbackStat?.isFile()) {
71
+ return null;
72
+ }
73
+ }
74
+ if (!isInsideDir(filePath, distDir)) {
75
+ return null;
76
+ }
77
+ const resolvedFile = path.resolve(filePath);
78
+ const isIndexHtml = path.basename(resolvedFile).toLowerCase() === "index.html";
79
+ return { resolvedFile, isIndexHtml };
80
+ }
81
+ function safeStat(filePath) {
82
+ try {
83
+ return statSync(filePath);
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ function resolveContentEncoding(resolvedFile, acceptEncoding) {
90
+ const encoding = selectEncoding(acceptEncoding);
91
+ if (!encoding) {
92
+ return { finalFile: resolvedFile, contentEncoding: null };
93
+ }
94
+ const compressedFile = `${resolvedFile}.${encoding === "br" ? "br" : "gz"}`;
95
+ const compressedStat = safeStat(compressedFile);
96
+ if (compressedStat?.isFile()) {
97
+ return { finalFile: compressedFile, contentEncoding: encoding };
98
+ }
99
+ return { finalFile: resolvedFile, contentEncoding: null };
100
+ }
101
+ function setResponseCacheHeaders(res, isIndexHtml, resolvedFile) {
102
+ if (isIndexHtml) {
103
+ res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate");
104
+ res.setHeader("Pragma", "no-cache");
105
+ res.setHeader("Expires", "0");
106
+ }
107
+ else if (isHashedAsset(resolvedFile)) {
108
+ res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
109
+ }
110
+ else {
111
+ res.setHeader("Cache-Control", "no-cache");
112
+ }
113
+ }
114
+ export function createWebUiMiddleware(options) {
115
+ const { enabled, distDir, label, logger } = options;
116
+ const childLogger = logger.child({ module: "web-ui" });
117
+ if (!enabled || !distDir) {
118
+ childLogger.info({ enabled, hasDistDir: !!distDir }, "Daemon web UI disabled or missing dist directory");
119
+ }
120
+ else {
121
+ childLogger.info({ distDir }, "Daemon web UI mounted");
122
+ }
123
+ return (req, res, next) => {
124
+ if (req.method !== "GET" && req.method !== "HEAD") {
125
+ next();
126
+ return;
127
+ }
128
+ if (isExcludedPath(req.path)) {
129
+ next();
130
+ return;
131
+ }
132
+ if (!enabled || !distDir) {
133
+ res.status(404).end();
134
+ return;
135
+ }
136
+ serveWebUiFile({ distDir, requestPath: req.path, label, req, res });
137
+ };
138
+ }
139
+ function serveWebUiFile(options) {
140
+ const { distDir, requestPath, label, req, res } = options;
141
+ const target = resolveTargetFile(distDir, requestPath);
142
+ if (!target) {
143
+ res.status(404).end();
144
+ return;
145
+ }
146
+ const { resolvedFile, isIndexHtml } = target;
147
+ const acceptEncoding = isIndexHtml ? undefined : req.headers["accept-encoding"];
148
+ const { finalFile, contentEncoding } = resolveContentEncoding(resolvedFile, acceptEncoding);
149
+ res.setHeader("Content-Type", getContentType(resolvedFile));
150
+ if (contentEncoding) {
151
+ res.setHeader("Content-Encoding", contentEncoding);
152
+ res.setHeader("Vary", "Accept-Encoding");
153
+ }
154
+ setResponseCacheHeaders(res, isIndexHtml, resolvedFile);
155
+ if (req.method === "HEAD") {
156
+ res.status(200).end();
157
+ return;
158
+ }
159
+ if (isIndexHtml) {
160
+ sendIndexHtml(res, finalFile, req, label);
161
+ return;
162
+ }
163
+ const stream = createReadStream(finalFile);
164
+ stream.on("error", () => {
165
+ if (!res.headersSent) {
166
+ res.status(500).end();
167
+ }
168
+ else {
169
+ res.end();
170
+ }
171
+ });
172
+ stream.pipe(res);
173
+ }
174
+ function sendIndexHtml(res, filePath, req, label) {
175
+ try {
176
+ const html = readFileSync(filePath, "utf-8");
177
+ const injected = injectConnectionHint(html, req, label);
178
+ res.status(200).send(injected);
179
+ }
180
+ catch {
181
+ res.status(500).end();
182
+ }
183
+ }
184
+ function serializeInlineScriptJson(value) {
185
+ return JSON.stringify(value)
186
+ .replace(/</g, "\\u003C")
187
+ .replace(/>/g, "\\u003E")
188
+ .replace(/&/g, "\\u0026");
189
+ }
190
+ function injectConnectionHint(html, req, label) {
191
+ const host = typeof req.headers.host === "string" ? req.headers.host : "";
192
+ const useTls = req.protocol === "https";
193
+ const hint = {
194
+ listen: host,
195
+ useTls,
196
+ label,
197
+ };
198
+ const script = `<script>window.__PASEO_INITIAL_DAEMON_CONNECTION__=${serializeInlineScriptJson(hint)}</script>`;
199
+ const headClose = /<\/head>/i;
200
+ if (headClose.test(html)) {
201
+ return html.replace(headClose, `${script}</head>`);
202
+ }
203
+ return script + html;
204
+ }
205
+ //# sourceMappingURL=web-ui.js.map
@@ -1,4 +1,5 @@
1
1
  import type { WSOutboundMessage } from "../messages.js";
2
+ import type { ProcessMemoryDiagnostics } from "../process-diagnostics.js";
2
3
  export interface WebSocketRuntimeCounters {
3
4
  connectedAwaitingHello: number;
4
5
  helloResumed: number;
@@ -56,6 +57,8 @@ export interface WebSocketRuntimeDiagnosticSnapshot<TRuntime = unknown, TAgents
56
57
  p99Ms: number;
57
58
  maxMs: number;
58
59
  } | null;
60
+ uptimeSeconds: number;
61
+ memory: ProcessMemoryDiagnostics;
59
62
  runtime: TRuntime;
60
63
  agents: TAgents;
61
64
  }
@@ -26,6 +26,7 @@ import { type DaemonAuthConfig } from "./auth.js";
26
26
  export interface ExternalSocketMetadata {
27
27
  transport: "relay";
28
28
  externalSessionKey?: string;
29
+ relayConnectionId?: string;
29
30
  }
30
31
  interface WebSocketServerConfig {
31
32
  allowedOrigins: Set<string>;
@@ -50,6 +51,7 @@ export declare class VoiceAssistantWebSocketServer {
50
51
  private readonly wss;
51
52
  private readonly pendingConnections;
52
53
  private readonly sessions;
54
+ private readonly socketIdentities;
53
55
  private readonly externalSessionsByKey;
54
56
  private readonly serverId;
55
57
  private readonly daemonVersion;
@@ -156,5 +158,6 @@ export declare class VoiceAssistantWebSocketServer {
156
158
  private broadcastAgentAttention;
157
159
  private broadcastTerminalAttention;
158
160
  }
161
+ export declare function isWebSocketSameOrigin(origin: string | undefined, requestHost: string | null): boolean;
159
162
  export {};
160
163
  //# sourceMappingURL=websocket-server.d.ts.map