@getpaseo/server 0.1.101 → 0.1.102-beta.2

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 (132) hide show
  1. package/dist/scripts/supervisor.js +26 -8
  2. package/dist/server/server/agent/activity-curator.d.ts +17 -0
  3. package/dist/server/server/agent/activity-curator.js +101 -24
  4. package/dist/server/server/agent/agent-manager.js +5 -1
  5. package/dist/server/server/agent/agent-sdk-types.d.ts +7 -2
  6. package/dist/server/server/agent/provider-snapshot-manager.d.ts +8 -1
  7. package/dist/server/server/agent/provider-snapshot-manager.js +78 -33
  8. package/dist/server/server/agent/providers/acp-agent.d.ts +7 -0
  9. package/dist/server/server/agent/providers/acp-agent.js +8 -1
  10. package/dist/server/server/agent/providers/claude/agent.js +51 -14
  11. package/dist/server/server/agent/providers/claude/query.d.ts +3 -0
  12. package/dist/server/server/agent/providers/claude/query.js +4 -2
  13. package/dist/server/server/agent/providers/mock-load-test-agent.js +8 -0
  14. package/dist/server/server/agent/providers/opencode/paths.d.ts +2 -0
  15. package/dist/server/server/agent/providers/opencode/paths.js +7 -0
  16. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +2 -0
  17. package/dist/server/server/agent/providers/opencode/server-manager.js +34 -5
  18. package/dist/server/server/agent/providers/opencode-agent.d.ts +4 -0
  19. package/dist/server/server/agent/providers/opencode-agent.js +14 -2
  20. package/dist/server/server/agent/providers/pi/agent.d.ts +3 -0
  21. package/dist/server/server/agent/providers/pi/agent.js +9 -3
  22. package/dist/server/server/agent/providers/provider-image-output.js +11 -6
  23. package/dist/server/server/agent/tools/paseo-tools.d.ts +1 -1
  24. package/dist/server/server/agent/tools/paseo-tools.js +0 -2
  25. package/dist/server/server/bootstrap.d.ts +7 -1
  26. package/dist/server/server/bootstrap.js +18 -0
  27. package/dist/server/server/config.d.ts +2 -0
  28. package/dist/server/server/config.js +57 -1
  29. package/dist/server/server/daemon-worker.js +19 -7
  30. package/dist/server/server/lifecycle-reasons.d.ts +4 -0
  31. package/dist/server/server/lifecycle-reasons.js +6 -0
  32. package/dist/server/server/persisted-config.d.ts +7 -0
  33. package/dist/server/server/persisted-config.js +8 -0
  34. package/dist/server/server/process-diagnostics.d.ts +17 -0
  35. package/dist/server/server/process-diagnostics.js +22 -0
  36. package/dist/server/server/relay-transport.js +1 -0
  37. package/dist/server/server/resolve-worktree-creation-intent.js +3 -1
  38. package/dist/server/server/session/daemon/daemon-self-update-session-controller.d.ts +32 -0
  39. package/dist/server/server/session/daemon/daemon-self-update-session-controller.js +88 -0
  40. package/dist/server/server/session/daemon/daemon-self-updater.d.ts +32 -0
  41. package/dist/server/server/session/daemon/daemon-self-updater.js +56 -0
  42. package/dist/server/server/session/daemon/daemon-session.d.ts +12 -0
  43. package/dist/server/server/session/daemon/daemon-session.js +12 -0
  44. package/dist/server/server/session/daemon/diagnostics.js +10 -0
  45. package/dist/server/server/session/daemon/install-origin.d.ts +7 -0
  46. package/dist/server/server/session/daemon/install-origin.js +64 -0
  47. package/dist/server/server/session/daemon/npm-global-cli.d.ts +29 -0
  48. package/dist/server/server/session/daemon/npm-global-cli.js +98 -0
  49. package/dist/server/server/session/provider/provider-catalog-session.js +8 -4
  50. package/dist/server/server/session.d.ts +5 -3
  51. package/dist/server/server/session.js +74 -32
  52. package/dist/server/server/web-ui.d.ts +10 -0
  53. package/dist/server/server/web-ui.js +205 -0
  54. package/dist/server/server/websocket/runtime-metrics.d.ts +3 -0
  55. package/dist/server/server/websocket-server.d.ts +3 -0
  56. package/dist/server/server/websocket-server.js +190 -32
  57. package/dist/server/services/quota-fetcher/manifest.js +5 -0
  58. package/dist/server/services/quota-fetcher/providers/minimax.d.ts +29 -0
  59. package/dist/server/services/quota-fetcher/providers/minimax.js +227 -0
  60. package/dist/server/terminal/agent-hooks/agent-hook-installer.js +2 -2
  61. package/dist/server/utils/checkout-git.js +156 -3
  62. package/dist/server/utils/directory-suggestions.js +1 -4
  63. package/dist/server/utils/path.d.ts +2 -0
  64. package/dist/server/utils/path.js +13 -0
  65. package/dist/server/utils/worktree.d.ts +1 -0
  66. package/dist/server/utils/worktree.js +92 -11
  67. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css +1 -0
  68. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.br +0 -0
  69. package/dist/server/web-ui/_expo/static/css/xterm-3bb1704bf6cb0876640973dc0244b4cb.css.gz +0 -0
  70. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-b01555c9b42665a03988c0a0032ef528.js +1 -0
  71. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-b01555c9b42665a03988c0a0032ef528.js.br +0 -0
  72. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-b01555c9b42665a03988c0a0032ef528.js.gz +0 -0
  73. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-648388eca5c510b496e1eddf523f70ff.js +1 -0
  74. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-648388eca5c510b496e1eddf523f70ff.js.br +0 -0
  75. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-648388eca5c510b496e1eddf523f70ff.js.gz +0 -0
  76. package/dist/server/web-ui/_expo/static/js/web/index-0ebbea2cd337f0c0680fdb3f8d4d5af3.js +16157 -0
  77. package/dist/server/web-ui/_expo/static/js/web/index-0ebbea2cd337f0c0680fdb3f8d4d5af3.js.br +0 -0
  78. package/dist/server/web-ui/_expo/static/js/web/index-0ebbea2cd337f0c0680fdb3f8d4d5af3.js.gz +0 -0
  79. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-c64fa2416284927857a39087fd8d1332.js +1 -0
  80. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-c64fa2416284927857a39087fd8d1332.js.br +0 -0
  81. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-c64fa2416284927857a39087fd8d1332.js.gz +0 -0
  82. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-a9784226715772edf87ef36c596599c2.js +3 -0
  83. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-a9784226715772edf87ef36c596599c2.js.br +0 -0
  84. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-a9784226715772edf87ef36c596599c2.js.gz +0 -0
  85. package/dist/server/web-ui/apple-touch-icon.png +0 -0
  86. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon-mask.0a328cd9c1afd0afe8e3b1ec5165b1b4.png +0 -0
  87. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/back-icon.35ba0eaec5a4f5ed12ca16fabeae451d.png +0 -0
  88. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55.png +0 -0
  89. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@2x.png +0 -0
  90. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@3x.png +0 -0
  91. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/clear-icon.c94f6478e7ae0cdd9f15de1fcb9e5e55@4x.png +0 -0
  92. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7.png +0 -0
  93. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@2x.png +0 -0
  94. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@3x.png +0 -0
  95. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/close-icon.808e1b1b9b53114ec2838071a7e6daa7@4x.png +0 -0
  96. package/dist/server/web-ui/assets/__node_modules/@react-navigation/elements/lib/module/assets/search-icon.286d67d3f74808a60a78d3ebf1a5fb57.png +0 -0
  97. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/arrow_down.017bc6ba3fc25503e5eb5e53826d48a8.png +0 -0
  98. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/error.d1ea1496f9057eb392d5bbf3732a61b7.png +0 -0
  99. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/file.19eeb73b9593a38f8e9f418337fc7d10.png +0 -0
  100. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/forward.d8b800c443b8972542883e0b9de2bdc6.png +0 -0
  101. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/pkg.ab19f4cbc543357183a20571f68380a3.png +0 -0
  102. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/sitemap.412dd9275b6b48ad28f5e3d81bb1f626.png +0 -0
  103. package/dist/server/web-ui/assets/__node_modules/expo-router/assets/unmatched.20e71bdf79e3a97bf55fd9e164041578.png +0 -0
  104. package/dist/server/web-ui/assets/assets/images/editor-apps/antigravity.6e91a685c33435e0b466a56db86cf141.png +0 -0
  105. package/dist/server/web-ui/assets/assets/images/editor-apps/cursor.c31d6bce4fe9aadc3fe59962f4c4fcf3.png +0 -0
  106. package/dist/server/web-ui/assets/assets/images/editor-apps/file-explorer.3e15e8f72c825c85ce336bcb0cdef776.png +0 -0
  107. package/dist/server/web-ui/assets/assets/images/editor-apps/finder.7f68fc2c475621a672e1be09309d5567.png +0 -0
  108. package/dist/server/web-ui/assets/assets/images/editor-apps/vscode.832bdb4c685d930f1c864c793703600b.png +0 -0
  109. package/dist/server/web-ui/assets/assets/images/editor-apps/webstorm.aa5dc2cd8c20cc0a155c4c5c5ab3c5f5.png +0 -0
  110. package/dist/server/web-ui/assets/assets/images/editor-apps/zed.f3a670b7f9aa226da4fe53fb86f1abbd.png +0 -0
  111. package/dist/server/web-ui/assets/assets/images/favicon-dark-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
  112. package/dist/server/web-ui/assets/assets/images/favicon-dark-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
  113. package/dist/server/web-ui/assets/assets/images/favicon-dark.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
  114. package/dist/server/web-ui/assets/assets/images/favicon-light-attention.882b3a27dcb2073e9e31b334f9ed9728.png +0 -0
  115. package/dist/server/web-ui/assets/assets/images/favicon-light-running.8112342ff0d39e047a7f8d4fad9402f3.png +0 -0
  116. package/dist/server/web-ui/assets/assets/images/favicon-light.8005ed36ac07a5a7c60de25780897bd4.png +0 -0
  117. package/dist/server/web-ui/assets/assets/images/notification-icon.3bf81d33ddbf380606bdd248ba83e158.png +0 -0
  118. package/dist/server/web-ui/favicon.ico +0 -0
  119. package/dist/server/web-ui/index.html +90 -0
  120. package/dist/server/web-ui/index.html.br +0 -0
  121. package/dist/server/web-ui/index.html.gz +0 -0
  122. package/dist/server/web-ui/manifest.json +27 -0
  123. package/dist/server/web-ui/manifest.json.br +0 -0
  124. package/dist/server/web-ui/manifest.json.gz +0 -0
  125. package/dist/server/web-ui/metadata.json +1 -0
  126. package/dist/server/web-ui/metadata.json.br +1 -0
  127. package/dist/server/web-ui/metadata.json.gz +0 -0
  128. package/dist/server/web-ui/pwa-icon-192.png +0 -0
  129. package/dist/server/web-ui/pwa-icon-512.png +0 -0
  130. package/dist/server/web-ui/robots.txt +2 -0
  131. package/dist/src/server/persisted-config.js +8 -0
  132. package/package.json +7 -7
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
2
3
  import { resolvePaseoNodeEnv } from "./paseo-env.js";
3
4
  import { z } from "zod";
4
5
  import { expandTilde } from "../utils/path.js";
@@ -11,6 +12,20 @@ import { mergeHostnames, parseHostnamesEnv } from "./hostnames.js";
11
12
  const DEFAULT_PORT = 6767;
12
13
  const DEFAULT_RELAY_ENDPOINT = "relay.paseo.sh:443";
13
14
  const DEFAULT_APP_BASE_URL = "https://app.paseo.sh";
15
+ const DEFAULT_TRUSTED_PROXIES = ["loopback"];
16
+ export function resolveBundledWebUiDistDir(moduleUrl = import.meta.url) {
17
+ const moduleDir = path.dirname(fileURLToPath(moduleUrl));
18
+ if (path.basename(moduleDir) === "server" && path.basename(path.dirname(moduleDir)) === "src") {
19
+ return path.resolve(moduleDir, "..", "..", "dist", "server", "web-ui");
20
+ }
21
+ if (path.basename(moduleDir) === "server" &&
22
+ path.basename(path.dirname(moduleDir)) === "server" &&
23
+ path.basename(path.dirname(path.dirname(moduleDir))) === "dist") {
24
+ return path.resolve(moduleDir, "..", "web-ui");
25
+ }
26
+ return path.resolve(moduleDir, "web-ui");
27
+ }
28
+ const BUNDLED_WEB_UI_DIST_DIR = resolveBundledWebUiDistDir();
14
29
  function parseBooleanEnv(value) {
15
30
  if (value === undefined) {
16
31
  return undefined;
@@ -137,6 +152,21 @@ function resolveServiceProxyConfig(env, persisted) {
137
152
  : null;
138
153
  return { publicBaseUrl, standaloneListen };
139
154
  }
155
+ function resolveWebUiConfig(paseoHome, env, cli, persisted) {
156
+ const enabled = cli?.webUiEnabled ??
157
+ parseBooleanEnv(env.PASEO_WEB_UI_ENABLED) ??
158
+ persisted.features?.webUi?.enabled ??
159
+ false;
160
+ const rawDistDir = env.PASEO_WEB_UI_DIST_DIR ?? persisted.features?.webUi?.distDir;
161
+ const trimmedDistDir = rawDistDir?.trim();
162
+ const distDir = trimmedDistDir
163
+ ? path.resolve(path.isAbsolute(trimmedDistDir) ? trimmedDistDir : paseoHome, trimmedDistDir)
164
+ : BUNDLED_WEB_UI_DIST_DIR;
165
+ return {
166
+ enabled,
167
+ distDir,
168
+ };
169
+ }
140
170
  function resolveVoiceLlmConfig(env, persisted) {
141
171
  const envVoiceLlmProvider = parseOptionalVoiceLlmProvider(env.PASEO_VOICE_LLM_PROVIDER);
142
172
  const persistedVoiceLlmProvider = parseOptionalVoiceLlmProvider(persisted.features?.voiceMode?.llm?.provider);
@@ -153,6 +183,28 @@ function resolveCorsAllowedOrigins(env, persisted) {
153
183
  const persistedCorsOrigins = persisted.daemon?.cors?.allowedOrigins ?? [];
154
184
  return Array.from(new Set([...persistedCorsOrigins, ...envCorsOrigins].filter((s) => s.length > 0)));
155
185
  }
186
+ function parseTrustedProxiesEnv(value) {
187
+ const trimmed = value?.trim();
188
+ if (!trimmed) {
189
+ return undefined;
190
+ }
191
+ const normalized = trimmed.toLowerCase();
192
+ if (["1", "true", "yes", "on"].includes(normalized)) {
193
+ return true;
194
+ }
195
+ if (["0", "false", "no", "off"].includes(normalized)) {
196
+ return [];
197
+ }
198
+ return trimmed
199
+ .split(",")
200
+ .map((proxy) => proxy.trim())
201
+ .filter((proxy) => proxy.length > 0);
202
+ }
203
+ function resolveTrustedProxiesConfig(env, persisted) {
204
+ return (parseTrustedProxiesEnv(env.PASEO_TRUSTED_PROXIES) ??
205
+ persisted.daemon?.trustedProxies ??
206
+ DEFAULT_TRUSTED_PROXIES);
207
+ }
156
208
  // PASEO_LISTEN can be:
157
209
  // - host:port (TCP)
158
210
  // - /path/to/socket (Unix socket)
@@ -198,6 +250,7 @@ function resolveStaticLoadConfigSettings(env, cli, persisted) {
198
250
  parseHostnamesEnv(env.PASEO_HOSTNAMES ?? env.PASEO_ALLOWED_HOSTS),
199
251
  cli?.hostnames,
200
252
  ]),
253
+ trustedProxies: resolveTrustedProxiesConfig(env, persisted),
201
254
  appBaseUrl: env.PASEO_APP_BASE_URL ?? persisted.app?.baseUrl ?? DEFAULT_APP_BASE_URL,
202
255
  };
203
256
  }
@@ -205,7 +258,7 @@ export function loadConfig(paseoHome, options) {
205
258
  const env = options?.env ?? process.env;
206
259
  const persisted = loadPersistedConfig(paseoHome);
207
260
  const listen = resolveListenAddress(env, options?.cli, persisted);
208
- const { mcpEnabled, mcpInjectIntoAgents, autoArchiveAfterMerge, appendSystemPrompt, terminalProfiles, hostnames, appBaseUrl, } = resolveStaticLoadConfigSettings(env, options?.cli, persisted);
261
+ const { mcpEnabled, mcpInjectIntoAgents, autoArchiveAfterMerge, appendSystemPrompt, terminalProfiles, hostnames, trustedProxies, appBaseUrl, } = resolveStaticLoadConfigSettings(env, options?.cli, persisted);
209
262
  const relay = resolveRelayConfig({
210
263
  env,
211
264
  persisted,
@@ -213,6 +266,7 @@ export function loadConfig(paseoHome, options) {
213
266
  cliRelayUseTls: options?.cli?.relayUseTls,
214
267
  });
215
268
  const serviceProxy = resolveServiceProxyConfig(env, persisted);
269
+ const webUi = resolveWebUiConfig(paseoHome, env, options?.cli, persisted);
216
270
  const { openai, speech } = resolveSpeechConfig({
217
271
  paseoHome,
218
272
  env,
@@ -226,6 +280,7 @@ export function loadConfig(paseoHome, options) {
226
280
  worktreesRoot: resolveWorktreesRoot(paseoHome, persisted),
227
281
  corsAllowedOrigins: resolveCorsAllowedOrigins(env, persisted),
228
282
  hostnames,
283
+ trustedProxies,
229
284
  mcpEnabled,
230
285
  mcpInjectIntoAgents,
231
286
  autoArchiveAfterMerge,
@@ -243,6 +298,7 @@ export function loadConfig(paseoHome, options) {
243
298
  relayUseTls: relay.useTls,
244
299
  relayPublicUseTls: relay.publicUseTls,
245
300
  serviceProxy,
301
+ webUi,
246
302
  appBaseUrl,
247
303
  auth: resolveAuthConfig(env, persisted),
248
304
  openai,
@@ -4,6 +4,7 @@ import { createPaseoDaemon } from "./bootstrap.js";
4
4
  import { loadConfig } from "./config.js";
5
5
  import { resolvePaseoHome } from "./paseo-home.js";
6
6
  import { createRootLogger } from "./logger.js";
7
+ import { getProcessDiagnostics } from "./process-diagnostics.js";
7
8
  process.title = "Paseo Daemon";
8
9
  function isPidAlive(pid) {
9
10
  try {
@@ -60,6 +61,12 @@ function applyCliFlagOverrides(config) {
60
61
  if (process.argv.includes("--no-inject-mcp")) {
61
62
  config.mcpInjectIntoAgents = false;
62
63
  }
64
+ if (process.argv.includes("--web-ui")) {
65
+ config.webUi = { ...(config.webUi ?? { distDir: null }), enabled: true };
66
+ }
67
+ if (process.argv.includes("--no-web-ui")) {
68
+ config.webUi = { ...(config.webUi ?? { distDir: null }), enabled: false };
69
+ }
63
70
  }
64
71
  async function main() {
65
72
  const { paseoHome, logger, config } = bootstrapFromEnvironment();
@@ -77,11 +84,12 @@ async function main() {
77
84
  });
78
85
  };
79
86
  const beginShutdown = (signal, options) => {
87
+ const reason = options?.reason ?? `worker_received_${signal}`;
80
88
  if (!shutdownPromise) {
81
- logger.info(`${signal} received, shutting down gracefully...`);
89
+ logger.info({ signal, reason, ...getProcessDiagnostics() }, `${signal} received, shutting down gracefully...`);
82
90
  shutdownPromise = (async () => {
83
91
  const forceExit = setTimeout(() => {
84
- logger.warn("Forcing shutdown - HTTP server didn't close in time");
92
+ logger.warn({ signal, reason, ...getProcessDiagnostics() }, "Forcing shutdown - HTTP server didn't close in time");
85
93
  process.exit(1);
86
94
  }, 10000);
87
95
  try {
@@ -103,7 +111,7 @@ async function main() {
103
111
  })();
104
112
  }
105
113
  else {
106
- logger.info(`${signal} received while shutdown is already in progress`);
114
+ logger.info({ signal, reason, ...getProcessDiagnostics() }, `${signal} received while shutdown is already in progress`);
107
115
  }
108
116
  installExitHook();
109
117
  };
@@ -122,11 +130,11 @@ async function main() {
122
130
  };
123
131
  const handleLifecycleIntent = (intent) => {
124
132
  if (intent.type === "shutdown") {
125
- logger.warn({ clientId: intent.clientId, requestId: intent.requestId }, "Shutdown requested via websocket");
126
- if (sendSupervisorLifecycleMessage({ type: "paseo:shutdown" })) {
133
+ logger.warn({ clientId: intent.clientId, requestId: intent.requestId, reason: intent.reason }, "Shutdown requested via websocket");
134
+ if (sendSupervisorLifecycleMessage({ type: "paseo:shutdown", reason: intent.reason })) {
127
135
  return;
128
136
  }
129
- beginShutdown("shutdown lifecycle intent");
137
+ beginShutdown("shutdown lifecycle intent", { reason: intent.reason });
130
138
  return;
131
139
  }
132
140
  logger.warn({ clientId: intent.clientId, requestId: intent.requestId, reason: intent.reason }, "Restart requested via websocket");
@@ -136,7 +144,10 @@ async function main() {
136
144
  })) {
137
145
  return;
138
146
  }
139
- beginShutdown("restart lifecycle intent", { successExitCode: 0 });
147
+ beginShutdown("restart lifecycle intent", {
148
+ reason: intent.reason,
149
+ successExitCode: 0,
150
+ });
140
151
  };
141
152
  const installSupervisorLivenessGuard = () => {
142
153
  if (typeof process.send !== "function") {
@@ -152,6 +163,7 @@ async function main() {
152
163
  supervisorExitRequested = true;
153
164
  writeWorkerLifecycleLog(paseoHome, "Supervisor liveness lost; worker exiting", {
154
165
  reason,
166
+ ...getProcessDiagnostics(),
155
167
  supervisorPid,
156
168
  currentParentPid: process.ppid,
157
169
  ipcConnected: typeof process.connected === "boolean" ? process.connected : null,
@@ -0,0 +1,4 @@
1
+ export declare const CLIENT_SHUTDOWN_RPC_REASON = "client_shutdown_rpc";
2
+ export declare const DEFAULT_CLIENT_RESTART_RPC_REASON = "client_restart_rpc";
3
+ export declare function normalizeClientRestartRpcReason(reason: string | undefined): string;
4
+ //# sourceMappingURL=lifecycle-reasons.d.ts.map
@@ -0,0 +1,6 @@
1
+ export const CLIENT_SHUTDOWN_RPC_REASON = "client_shutdown_rpc";
2
+ export const DEFAULT_CLIENT_RESTART_RPC_REASON = "client_restart_rpc";
3
+ export function normalizeClientRestartRpcReason(reason) {
4
+ return reason?.trim() || DEFAULT_CLIENT_RESTART_RPC_REASON;
5
+ }
6
+ //# sourceMappingURL=lifecycle-reasons.js.map
@@ -19,6 +19,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
19
19
  listen: z.ZodOptional<z.ZodString>;
20
20
  hostnames: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
21
21
  allowedHosts: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
22
+ trustedProxies: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodArray<z.ZodString>]>>;
22
23
  mcp: z.ZodOptional<z.ZodObject<{
23
24
  enabled: z.ZodOptional<z.ZodBoolean>;
24
25
  injectIntoAgents: z.ZodOptional<z.ZodBoolean>;
@@ -54,6 +55,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
54
55
  }, z.core.$strict>, z.ZodTransform<{
55
56
  listen?: string | undefined;
56
57
  hostnames?: true | string[] | undefined;
58
+ trustedProxies?: true | string[] | undefined;
57
59
  mcp?: {
58
60
  [x: string]: unknown;
59
61
  enabled?: boolean | undefined;
@@ -92,6 +94,7 @@ export declare const PersistedConfigSchema: z.ZodObject<{
92
94
  listen?: string | undefined;
93
95
  hostnames?: true | string[] | undefined;
94
96
  allowedHosts?: true | string[] | undefined;
97
+ trustedProxies?: true | string[] | undefined;
95
98
  mcp?: {
96
99
  [x: string]: unknown;
97
100
  enabled?: boolean | undefined;
@@ -241,6 +244,10 @@ export declare const PersistedConfigSchema: z.ZodObject<{
241
244
  speed: z.ZodOptional<z.ZodNumber>;
242
245
  }, z.core.$strict>>;
243
246
  }, z.core.$strict>>;
247
+ webUi: z.ZodOptional<z.ZodObject<{
248
+ enabled: z.ZodOptional<z.ZodBoolean>;
249
+ distDir: z.ZodOptional<z.ZodString>;
250
+ }, z.core.$strict>>;
244
251
  }, z.core.$strict>>;
245
252
  log: z.ZodOptional<z.ZodObject<{
246
253
  level: z.ZodOptional<z.ZodEnum<{
@@ -126,6 +126,12 @@ const FeatureVoiceModeSchema = z
126
126
  .optional(),
127
127
  })
128
128
  .strict();
129
+ const FeatureWebUiSchema = z
130
+ .object({
131
+ enabled: z.boolean().optional(),
132
+ distDir: z.string().min(1).optional(),
133
+ })
134
+ .strict();
129
135
  const StructuredGenerationProviderConfigSchema = z
130
136
  .object({
131
137
  provider: z.string().min(1),
@@ -187,6 +193,7 @@ export const PersistedConfigSchema = z
187
193
  listen: z.string().optional(),
188
194
  hostnames: z.union([z.literal(true), z.array(z.string())]).optional(),
189
195
  allowedHosts: z.union([z.literal(true), z.array(z.string())]).optional(),
196
+ trustedProxies: z.union([z.literal(true), z.array(z.string())]).optional(),
190
197
  mcp: z
191
198
  .object({
192
199
  enabled: z.boolean().optional(),
@@ -252,6 +259,7 @@ export const PersistedConfigSchema = z
252
259
  .object({
253
260
  dictation: FeatureDictationSchema.optional(),
254
261
  voiceMode: FeatureVoiceModeSchema.optional(),
262
+ webUi: FeatureWebUiSchema.optional(),
255
263
  })
256
264
  .strict()
257
265
  .optional(),
@@ -0,0 +1,17 @@
1
+ export interface ProcessMemoryDiagnostics {
2
+ rss: number;
3
+ heapTotal: number;
4
+ heapUsed: number;
5
+ external: number;
6
+ arrayBuffers: number;
7
+ }
8
+ export interface ProcessDiagnostics {
9
+ pid: number;
10
+ ppid: number;
11
+ uptimeSeconds: number;
12
+ memory: ProcessMemoryDiagnostics;
13
+ }
14
+ export declare function getProcessMemoryDiagnostics(): ProcessMemoryDiagnostics;
15
+ export declare function getProcessUptimeSeconds(): number;
16
+ export declare function getProcessDiagnostics(): ProcessDiagnostics;
17
+ //# sourceMappingURL=process-diagnostics.d.ts.map
@@ -0,0 +1,22 @@
1
+ export function getProcessMemoryDiagnostics() {
2
+ const memory = process.memoryUsage();
3
+ return {
4
+ rss: memory.rss,
5
+ heapTotal: memory.heapTotal,
6
+ heapUsed: memory.heapUsed,
7
+ external: memory.external,
8
+ arrayBuffers: memory.arrayBuffers,
9
+ };
10
+ }
11
+ export function getProcessUptimeSeconds() {
12
+ return Math.round(process.uptime() * 1000) / 1000;
13
+ }
14
+ export function getProcessDiagnostics() {
15
+ return {
16
+ pid: process.pid,
17
+ ppid: process.ppid,
18
+ uptimeSeconds: getProcessUptimeSeconds(),
19
+ memory: getProcessMemoryDiagnostics(),
20
+ };
21
+ }
22
+ //# sourceMappingURL=process-diagnostics.js.map
@@ -335,6 +335,7 @@ export function startRelayTransport({ logger, attachSocket, relayEndpoint, relay
335
335
  const externalMetadata = {
336
336
  transport: "relay",
337
337
  externalSessionKey: `session:${connectionId}`,
338
+ relayConnectionId: connectionId,
338
339
  };
339
340
  if (daemonKeyPair) {
340
341
  void attachEncryptedSocket(socket, daemonKeyPair, relayLogger.child({ connectionId }), attachSocket, externalMetadata);
@@ -64,9 +64,10 @@ async function resolveGitHubPrCheckoutIntent(params) {
64
64
  const baseRefName = checkoutTarget?.baseRefName?.trim() ||
65
65
  (await resolveDefaultBranch(params.repoRoot, params.deps));
66
66
  const localBranchName = buildGitHubPrLocalBranchName({ headRef, checkoutTarget });
67
- const pushRemoteUrl = checkoutTarget
67
+ const pushRemoteUrl = checkoutTarget?.isCrossRepository
68
68
  ? checkoutTarget.headRepositorySshUrl || checkoutTarget.headRepositoryUrl || undefined
69
69
  : undefined;
70
+ const trackOriginHead = checkoutTarget ? !checkoutTarget.isCrossRepository : false;
70
71
  return {
71
72
  kind: "checkout-github-pr",
72
73
  githubPrNumber: params.githubPrNumber,
@@ -74,6 +75,7 @@ async function resolveGitHubPrCheckoutIntent(params) {
74
75
  baseRefName,
75
76
  ...(localBranchName !== headRef ? { localBranchName } : {}),
76
77
  ...(pushRemoteUrl ? { pushRemoteUrl } : {}),
78
+ ...(trackOriginHead ? { trackOriginHead } : {}),
77
79
  };
78
80
  }
79
81
  async function resolveGitHubPrCheckoutTarget(params) {
@@ -0,0 +1,32 @@
1
+ import type pino from "pino";
2
+ import type { SessionInboundMessage, SessionOutboundMessage } from "../../messages.js";
3
+ import { type DaemonSelfUpdater } from "./daemon-self-updater.js";
4
+ interface DaemonSelfUpdateRestartIntent {
5
+ type: "restart";
6
+ clientId: string;
7
+ requestId: string;
8
+ reason: string;
9
+ }
10
+ export interface DaemonSelfUpdateSessionControllerOptions {
11
+ clientId: string;
12
+ daemonVersion: string | null;
13
+ emit: (msg: SessionOutboundMessage) => void;
14
+ emitLifecycleIntent: (intent: DaemonSelfUpdateRestartIntent) => void;
15
+ sessionLogger: pino.Logger;
16
+ updater?: Pick<DaemonSelfUpdater, "update">;
17
+ }
18
+ export declare class DaemonSelfUpdateSessionController {
19
+ private readonly clientId;
20
+ private readonly daemonVersion;
21
+ private readonly emit;
22
+ private readonly emitLifecycleIntent;
23
+ private readonly sessionLogger;
24
+ private readonly updater;
25
+ constructor(options: DaemonSelfUpdateSessionControllerOptions);
26
+ dispatch(msg: SessionInboundMessage): Promise<void> | undefined;
27
+ private handleDaemonUpdateRequest;
28
+ private emitProgress;
29
+ private emitResponse;
30
+ }
31
+ export {};
32
+ //# sourceMappingURL=daemon-self-update-session-controller.d.ts.map
@@ -0,0 +1,88 @@
1
+ import { daemonSelfUpdater, DaemonSelfUpdateInProgressError, } from "./daemon-self-updater.js";
2
+ import { getErrorMessage } from "@getpaseo/protocol/error-utils";
3
+ const DAEMON_SELF_UPDATE_MESSAGE_TYPES = new Set([
4
+ "daemon.update.request",
5
+ ]);
6
+ function isDaemonSelfUpdateMessage(msg) {
7
+ return DAEMON_SELF_UPDATE_MESSAGE_TYPES.has(msg.type);
8
+ }
9
+ export class DaemonSelfUpdateSessionController {
10
+ constructor(options) {
11
+ this.clientId = options.clientId;
12
+ this.daemonVersion = options.daemonVersion;
13
+ this.emit = options.emit;
14
+ this.emitLifecycleIntent = options.emitLifecycleIntent;
15
+ this.sessionLogger = options.sessionLogger;
16
+ this.updater = options.updater ?? daemonSelfUpdater;
17
+ }
18
+ dispatch(msg) {
19
+ if (!isDaemonSelfUpdateMessage(msg)) {
20
+ return undefined;
21
+ }
22
+ return this.handleDaemonUpdateRequest(msg);
23
+ }
24
+ async handleDaemonUpdateRequest(msg) {
25
+ const previousVersion = this.daemonVersion;
26
+ try {
27
+ const result = await this.updater.update({
28
+ daemonVersion: previousVersion,
29
+ onProgress: (phase) => this.emitProgress(msg.requestId, phase),
30
+ logger: this.sessionLogger,
31
+ });
32
+ this.emitResponse({
33
+ requestId: msg.requestId,
34
+ success: result.success,
35
+ error: result.error,
36
+ previousVersion,
37
+ newVersion: result.newVersion,
38
+ });
39
+ if (!result.success) {
40
+ return;
41
+ }
42
+ this.emitLifecycleIntent({
43
+ type: "restart",
44
+ clientId: this.clientId,
45
+ requestId: msg.requestId,
46
+ reason: "daemon_update",
47
+ });
48
+ }
49
+ catch (error) {
50
+ if (error instanceof DaemonSelfUpdateInProgressError) {
51
+ this.emit({
52
+ type: "rpc_error",
53
+ payload: {
54
+ requestId: msg.requestId,
55
+ requestType: "daemon.update.request",
56
+ error: error.message,
57
+ code: "already_updating",
58
+ },
59
+ });
60
+ return;
61
+ }
62
+ this.sessionLogger.error({ err: error }, "Daemon update failed with exception");
63
+ this.emitResponse({
64
+ requestId: msg.requestId,
65
+ success: false,
66
+ error: getErrorMessage(error),
67
+ previousVersion,
68
+ newVersion: null,
69
+ });
70
+ }
71
+ }
72
+ emitProgress(requestId, phase) {
73
+ this.emit({
74
+ type: "daemon.update.progress",
75
+ payload: {
76
+ requestId,
77
+ phase,
78
+ },
79
+ });
80
+ }
81
+ emitResponse(payload) {
82
+ this.emit({
83
+ type: "daemon.update.response",
84
+ payload,
85
+ });
86
+ }
87
+ }
88
+ //# sourceMappingURL=daemon-self-update-session-controller.js.map
@@ -0,0 +1,32 @@
1
+ import { type DaemonInstallOriginRuntime } from "./install-origin.js";
2
+ import { type NpmGlobalPaseoCli } from "./npm-global-cli.js";
3
+ export type DaemonSelfUpdatePhase = "starting" | "downloading" | "installing" | "complete";
4
+ export interface DaemonSelfUpdateResult {
5
+ success: boolean;
6
+ error: string | null;
7
+ newVersion: string | null;
8
+ }
9
+ export interface DaemonSelfUpdateInput {
10
+ daemonVersion: string | null;
11
+ onProgress: (phase: DaemonSelfUpdatePhase) => void;
12
+ logger: DaemonSelfUpdateLogger;
13
+ }
14
+ export interface DaemonSelfUpdateLogger {
15
+ error(obj: object, msg?: string): void;
16
+ warn(obj: object, msg?: string): void;
17
+ }
18
+ export interface DaemonSelfUpdateRuntime {
19
+ npm: NpmGlobalPaseoCli;
20
+ installOrigin: DaemonInstallOriginRuntime;
21
+ }
22
+ export declare class DaemonSelfUpdateInProgressError extends Error {
23
+ constructor();
24
+ }
25
+ export declare class DaemonSelfUpdater {
26
+ private readonly runtime;
27
+ private inProgress;
28
+ constructor(runtime?: DaemonSelfUpdateRuntime);
29
+ update(input: DaemonSelfUpdateInput): Promise<DaemonSelfUpdateResult>;
30
+ }
31
+ export declare const daemonSelfUpdater: DaemonSelfUpdater;
32
+ //# sourceMappingURL=daemon-self-updater.d.ts.map
@@ -0,0 +1,56 @@
1
+ import { getErrorMessage } from "@getpaseo/protocol/error-utils";
2
+ import { daemonInstallOriginRuntime, validateDaemonInstallOrigin, } from "./install-origin.js";
3
+ import { npmGlobalPaseoCli } from "./npm-global-cli.js";
4
+ export class DaemonSelfUpdateInProgressError extends Error {
5
+ constructor() {
6
+ super("An update is already in progress");
7
+ this.name = "DaemonSelfUpdateInProgressError";
8
+ }
9
+ }
10
+ const defaultRuntime = {
11
+ npm: npmGlobalPaseoCli,
12
+ installOrigin: daemonInstallOriginRuntime,
13
+ };
14
+ export class DaemonSelfUpdater {
15
+ constructor(runtime = defaultRuntime) {
16
+ this.runtime = runtime;
17
+ this.inProgress = false;
18
+ }
19
+ async update(input) {
20
+ if (this.inProgress) {
21
+ throw new DaemonSelfUpdateInProgressError();
22
+ }
23
+ this.inProgress = true;
24
+ try {
25
+ input.onProgress("starting");
26
+ const install = await this.runtime.npm.inspect();
27
+ const unsupportedReason = validateDaemonInstallOrigin(install, input.daemonVersion, this.runtime.installOrigin);
28
+ if (unsupportedReason) {
29
+ return { success: false, error: unsupportedReason, newVersion: null };
30
+ }
31
+ input.onProgress("downloading");
32
+ input.onProgress("installing");
33
+ const result = await this.runtime.npm.installLatest();
34
+ if (result.exitCode !== 0) {
35
+ const error = result.stderr.trim() || result.stdout.trim() || `npm exited with code ${result.exitCode}`;
36
+ input.logger.error({ exitCode: result.exitCode, stderr: result.stderr }, "Daemon self-update failed");
37
+ return { success: false, error, newVersion: null };
38
+ }
39
+ const updatedInstall = await this.runtime.npm.inspect().catch((error) => {
40
+ input.logger.warn({ err: error }, "Unable to read updated npm package version");
41
+ return null;
42
+ });
43
+ input.onProgress("complete");
44
+ return { success: true, error: null, newVersion: updatedInstall?.version ?? null };
45
+ }
46
+ catch (error) {
47
+ input.logger.error({ err: error }, "Daemon self-update failed with exception");
48
+ return { success: false, error: getErrorMessage(error), newVersion: null };
49
+ }
50
+ finally {
51
+ this.inProgress = false;
52
+ }
53
+ }
54
+ }
55
+ export const daemonSelfUpdater = new DaemonSelfUpdater();
56
+ //# sourceMappingURL=daemon-self-updater.js.map
@@ -17,9 +17,16 @@ export interface DaemonRuntimeConfig {
17
17
  }
18
18
  export interface DaemonSessionHost {
19
19
  emit(msg: SessionOutboundMessage): void;
20
+ emitLifecycleIntent(intent: {
21
+ type: "restart";
22
+ clientId: string;
23
+ requestId: string;
24
+ reason: string;
25
+ }): void;
20
26
  }
21
27
  export interface DaemonSessionOptions {
22
28
  host: DaemonSessionHost;
29
+ clientId: string;
23
30
  paseoHome: string;
24
31
  serverId: string | undefined;
25
32
  daemonVersion: string | undefined;
@@ -40,6 +47,7 @@ export interface DaemonSessionOptions {
40
47
  */
41
48
  export declare class DaemonSession {
42
49
  private readonly host;
50
+ private readonly clientId;
43
51
  private readonly paseoHome;
44
52
  private readonly serverId;
45
53
  private readonly daemonVersion;
@@ -50,6 +58,7 @@ export declare class DaemonSession {
50
58
  private readonly listProviderAvailability;
51
59
  private readonly getWebSocketRuntimeMetrics;
52
60
  private readonly logger;
61
+ private readonly selfUpdate;
53
62
  constructor(options: DaemonSessionOptions);
54
63
  handleGetStatusRequest(msg: Extract<SessionInboundMessage, {
55
64
  type: "daemon.get_status.request";
@@ -60,5 +69,8 @@ export declare class DaemonSession {
60
69
  handleDiagnosticsRequest(msg: Extract<SessionInboundMessage, {
61
70
  type: "diagnostics.request";
62
71
  }>): Promise<void>;
72
+ handleUpdateRequest(msg: Extract<SessionInboundMessage, {
73
+ type: "daemon.update.request";
74
+ }>): Promise<void>;
63
75
  }
64
76
  //# sourceMappingURL=daemon-session.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { getPidLockInfo } from "../../pid-lock.js";
2
2
  import { generateLocalPairingOffer } from "../../pairing-offer.js";
3
3
  import { collectDaemonDiagnostics, } from "./diagnostics.js";
4
+ import { DaemonSelfUpdateSessionController } from "./daemon-self-update-session-controller.js";
4
5
  /**
5
6
  * A client's read surface for the daemon process itself: its runtime status
6
7
  * (pid-lock start time, listen address, relay config, provider availability) and
@@ -11,6 +12,7 @@ import { collectDaemonDiagnostics, } from "./diagnostics.js";
11
12
  export class DaemonSession {
12
13
  constructor(options) {
13
14
  this.host = options.host;
15
+ this.clientId = options.clientId;
14
16
  this.paseoHome = options.paseoHome;
15
17
  this.serverId = options.serverId;
16
18
  this.daemonVersion = options.daemonVersion;
@@ -21,6 +23,13 @@ export class DaemonSession {
21
23
  this.listProviderAvailability = options.listProviderAvailability;
22
24
  this.getWebSocketRuntimeMetrics = options.getWebSocketRuntimeMetrics ?? (() => null);
23
25
  this.logger = options.logger;
26
+ this.selfUpdate = new DaemonSelfUpdateSessionController({
27
+ clientId: this.clientId,
28
+ daemonVersion: this.daemonVersion ?? null,
29
+ emit: (msg) => this.host.emit(msg),
30
+ emitLifecycleIntent: (intent) => this.host.emitLifecycleIntent(intent),
31
+ sessionLogger: this.logger,
32
+ });
24
33
  }
25
34
  async handleGetStatusRequest(msg) {
26
35
  try {
@@ -132,5 +141,8 @@ export class DaemonSession {
132
141
  });
133
142
  }
134
143
  }
144
+ async handleUpdateRequest(msg) {
145
+ await this.selfUpdate.dispatch(msg);
146
+ }
135
147
  }
136
148
  //# sourceMappingURL=daemon-session.js.map
@@ -154,6 +154,16 @@ function collectWebSocketRuntimeEntries(options) {
154
154
  return [
155
155
  { label: "Collected at", value: snapshot.collectedAt },
156
156
  { label: "Window", value: formatDurationMs(snapshot.windowMs) },
157
+ { label: "Process uptime", value: formatDurationMs(snapshot.uptimeSeconds * 1000) },
158
+ {
159
+ label: "Process memory",
160
+ value: [
161
+ `rss=${formatBytes(snapshot.memory.rss)}`,
162
+ `heap=${formatBytes(snapshot.memory.heapUsed)} / ${formatBytes(snapshot.memory.heapTotal)}`,
163
+ `external=${formatBytes(snapshot.memory.external)}`,
164
+ `arrayBuffers=${formatBytes(snapshot.memory.arrayBuffers)}`,
165
+ ].join(", "),
166
+ },
157
167
  { label: "Final", value: String(snapshot.final) },
158
168
  {
159
169
  label: "Sessions",
@@ -0,0 +1,7 @@
1
+ import { type NpmGlobalPaseoInstall } from "./npm-global-cli.js";
2
+ export interface DaemonInstallOriginRuntime {
3
+ resolveCurrentServerPackageRoot(): string | null;
4
+ }
5
+ export declare const daemonInstallOriginRuntime: DaemonInstallOriginRuntime;
6
+ export declare function validateDaemonInstallOrigin(install: NpmGlobalPaseoInstall, daemonVersion: string | null, runtime?: DaemonInstallOriginRuntime): string | null;
7
+ //# sourceMappingURL=install-origin.d.ts.map