@elizaos/app-core 2.0.3-beta.5 → 2.0.3-beta.7

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 (40) hide show
  1. package/package.json +37 -36
  2. package/platforms/electrobun/electrobun.config.ts +31 -1
  3. package/platforms/electrobun/package.json +10 -10
  4. package/platforms/electrobun/remotes/fs/README.md +0 -1
  5. package/platforms/electrobun/remotes/fs/package.json +0 -1
  6. package/platforms/electrobun/remotes/git/README.md +1 -2
  7. package/platforms/electrobun/remotes/git/package.json +0 -1
  8. package/platforms/electrobun/remotes/local-model/README.md +0 -1
  9. package/platforms/electrobun/remotes/local-model/package.json +0 -1
  10. package/platforms/electrobun/remotes/pty/README.md +1 -2
  11. package/platforms/electrobun/remotes/pty/package.json +0 -1
  12. package/platforms/electrobun/remotes/runtime/README.md +1 -5
  13. package/platforms/electrobun/remotes/runtime/package.json +0 -1
  14. package/platforms/electrobun/remotes/surface/README.md +1 -2
  15. package/platforms/electrobun/remotes/surface/package.json +0 -1
  16. package/platforms/electrobun/scripts/sync-web-assets.mjs +31 -2
  17. package/platforms/electrobun/src/api-base.ts +11 -2
  18. package/platforms/electrobun/src/bridge/electrobun-direct-rpc.ts +15 -1
  19. package/platforms/electrobun/src/index.ts +25 -28
  20. package/platforms/electrobun/src/lifecycle/api-base-owner.test.ts +67 -5
  21. package/platforms/electrobun/src/lifecycle/api-base-owner.ts +36 -6
  22. package/platforms/electrobun/src/native/desktop.ts +1 -1
  23. package/platforms/electrobun/src/onboarding-overlay-window.ts +7 -8
  24. package/platforms/electrobun/src/preload.js +1 -1
  25. package/platforms/electrobun/src/rpc-schema.ts +5 -1
  26. package/runtime/voice-warmup.d.ts.map +1 -1
  27. package/runtime/voice-warmup.js +8 -2
  28. package/scripts/benchmark-preflight.mjs +31 -2
  29. package/scripts/build-image.sh +5 -2
  30. package/scripts/bun-riscv64/build.sh +12 -6
  31. package/scripts/bun-riscv64/run-build.sh +10 -1
  32. package/scripts/dev-platform.mjs +19 -10
  33. package/scripts/ios-xcframework/README.md +15 -9
  34. package/scripts/ios-xcframework/run-physical-device-smoke.mjs +5 -3
  35. package/scripts/playwright-ui-live-stack.ts +32 -8
  36. package/scripts/run-mobile-build.mjs +11 -14
  37. package/platforms/electrobun/src/desktop-pill-config.test.ts +0 -27
  38. package/platforms/electrobun/src/desktop-pill-config.ts +0 -40
  39. package/platforms/electrobun/src/pill-window.test.ts +0 -91
  40. package/platforms/electrobun/src/pill-window.ts +0 -99
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/app-core",
3
- "version": "2.0.3-beta.5",
3
+ "version": "2.0.3-beta.7",
4
4
  "description": "Shared application core for elizaOS white-label agent apps.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -155,22 +155,22 @@
155
155
  "three": "^0.184.0"
156
156
  },
157
157
  "optionalDependencies": {
158
- "@elizaos/capacitor-appblocker": "2.0.3-beta.5",
159
- "@elizaos/capacitor-bun-runtime": "2.0.3-beta.5",
160
- "@elizaos/capacitor-camera": "2.0.3-beta.5",
161
- "@elizaos/capacitor-canvas": "2.0.3-beta.5",
162
- "@elizaos/capacitor-contacts": "2.0.3-beta.5",
163
- "@elizaos/capacitor-gateway": "2.0.3-beta.5",
164
- "@elizaos/capacitor-location": "2.0.3-beta.5",
165
- "@elizaos/capacitor-messages": "2.0.3-beta.5",
166
- "@elizaos/capacitor-mobile-agent-bridge": "2.0.3-beta.5",
167
- "@elizaos/capacitor-mobile-signals": "2.0.3-beta.5",
168
- "@elizaos/capacitor-phone": "2.0.3-beta.5",
169
- "@elizaos/capacitor-screencapture": "2.0.3-beta.5",
170
- "@elizaos/capacitor-swabble": "2.0.3-beta.5",
171
- "@elizaos/capacitor-system": "2.0.3-beta.5",
172
- "@elizaos/capacitor-talkmode": "2.0.3-beta.5",
173
- "@elizaos/capacitor-websiteblocker": "2.0.3-beta.5",
158
+ "@elizaos/capacitor-appblocker": "2.0.3-beta.7",
159
+ "@elizaos/capacitor-bun-runtime": "2.0.3-beta.7",
160
+ "@elizaos/capacitor-camera": "2.0.3-beta.7",
161
+ "@elizaos/capacitor-canvas": "2.0.3-beta.7",
162
+ "@elizaos/capacitor-contacts": "2.0.3-beta.7",
163
+ "@elizaos/capacitor-gateway": "2.0.3-beta.7",
164
+ "@elizaos/capacitor-location": "2.0.3-beta.7",
165
+ "@elizaos/capacitor-messages": "2.0.3-beta.7",
166
+ "@elizaos/capacitor-mobile-agent-bridge": "2.0.3-beta.7",
167
+ "@elizaos/capacitor-mobile-signals": "2.0.3-beta.7",
168
+ "@elizaos/capacitor-phone": "2.0.3-beta.7",
169
+ "@elizaos/capacitor-screencapture": "2.0.3-beta.7",
170
+ "@elizaos/capacitor-swabble": "2.0.3-beta.7",
171
+ "@elizaos/capacitor-system": "2.0.3-beta.7",
172
+ "@elizaos/capacitor-talkmode": "2.0.3-beta.7",
173
+ "@elizaos/capacitor-websiteblocker": "2.0.3-beta.7",
174
174
  "sharp": "^0.34.5"
175
175
  },
176
176
  "dependencies": {
@@ -184,24 +184,25 @@
184
184
  "@capacitor/preferences": "^8.0.1",
185
185
  "@capacitor/push-notifications": "^8.0.0",
186
186
  "@clack/prompts": "^1.0.0",
187
- "@elizaos/core": "2.0.3-beta.5",
188
- "@elizaos/plugin-anthropic": "2.0.3-beta.5",
189
- "@elizaos/plugin-browser": "2.0.3-beta.5",
190
- "@elizaos/plugin-edge-tts": "2.0.3-beta.5",
191
- "@elizaos/plugin-elizacloud": "2.0.3-beta.5",
192
- "@elizaos/plugin-groq": "2.0.3-beta.5",
193
- "@elizaos/plugin-health": "2.0.3-beta.5",
194
- "@elizaos/plugin-local-inference": "2.0.3-beta.5",
195
- "@elizaos/plugin-openai": "2.0.3-beta.5",
196
- "@elizaos/plugin-registry": "2.0.3-beta.5",
197
- "@elizaos/plugin-sql": "2.0.3-beta.5",
198
- "@elizaos/plugin-wechat": "2.0.3-beta.5",
199
- "@elizaos/plugin-workflow": "2.0.3-beta.5",
200
- "@elizaos/registry": "2.0.3-beta.5",
201
- "@elizaos/shared": "2.0.3-beta.5",
202
- "@elizaos/skills": "2.0.3-beta.5",
203
- "@elizaos/ui": "2.0.3-beta.5",
204
- "@elizaos/vault": "2.0.3-beta.5",
187
+ "@elizaos/agent": "2.0.3-beta.7",
188
+ "@elizaos/core": "2.0.3-beta.7",
189
+ "@elizaos/plugin-anthropic": "2.0.3-beta.7",
190
+ "@elizaos/plugin-browser": "2.0.3-beta.7",
191
+ "@elizaos/plugin-edge-tts": "2.0.3-beta.7",
192
+ "@elizaos/plugin-elizacloud": "2.0.3-beta.7",
193
+ "@elizaos/plugin-groq": "2.0.3-beta.7",
194
+ "@elizaos/plugin-health": "2.0.3-beta.7",
195
+ "@elizaos/plugin-local-inference": "2.0.3-beta.7",
196
+ "@elizaos/plugin-openai": "2.0.3-beta.7",
197
+ "@elizaos/plugin-registry": "2.0.3-beta.7",
198
+ "@elizaos/plugin-sql": "2.0.3-beta.7",
199
+ "@elizaos/plugin-wechat": "2.0.3-beta.7",
200
+ "@elizaos/plugin-workflow": "2.0.3-beta.7",
201
+ "@elizaos/registry": "2.0.3-beta.7",
202
+ "@elizaos/shared": "2.0.3-beta.7",
203
+ "@elizaos/skills": "2.0.3-beta.7",
204
+ "@elizaos/ui": "2.0.3-beta.7",
205
+ "@elizaos/vault": "2.0.3-beta.7",
205
206
  "@node-rs/argon2": "^2.0.2",
206
207
  "@upstash/redis": "^1.37.0",
207
208
  "chalk": "^5.3.0",
@@ -222,5 +223,5 @@
222
223
  },
223
224
  "module": "./index.js",
224
225
  "types": "./index.d.ts",
225
- "gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
226
+ "gitHead": "61094f10458d11055c75b3dd0bae374e3f66bac5"
226
227
  }
@@ -1,3 +1,4 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import fs from "node:fs";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
@@ -178,6 +179,13 @@ const workspacePackagesRoot = fs.existsSync(
178
179
  )
179
180
  ? path.join(repoRoot, "packages")
180
181
  : path.join(repoRoot, "eliza", "packages");
182
+ const elizaWorkspaceRoot = path.dirname(workspacePackagesRoot);
183
+ const rmPathRecursiveScript = path.join(
184
+ elizaWorkspaceRoot,
185
+ "packages",
186
+ "scripts",
187
+ "rm-path-recursive.mjs",
188
+ );
181
189
  const sharedSourceDir = path.join(workspacePackagesRoot, "shared", "src");
182
190
  const coreNodeEntry = fs.existsSync(
183
191
  path.join(workspacePackagesRoot, "core", "dist", "node", "index.node.js"),
@@ -216,7 +224,7 @@ function resolveRuntimeBundleSourcePath(rootDir: string): string {
216
224
  ".generated",
217
225
  "runtime-dist",
218
226
  );
219
- fs.rmSync(sanitizedDistPath, { recursive: true, force: true });
227
+ rmRecursive(sanitizedDistPath);
220
228
  fs.mkdirSync(sanitizedDistPath, { recursive: true });
221
229
  for (const entry of fs.readdirSync(runtimeDistPath)) {
222
230
  if (entry === "node_modules") continue;
@@ -232,6 +240,28 @@ function resolveRuntimeBundleSourcePath(rootDir: string): string {
232
240
  return sanitizedDistPath;
233
241
  }
234
242
 
243
+ function rmRecursive(pathToRemove: string): void {
244
+ const result = spawnSync(
245
+ process.execPath,
246
+ [rmPathRecursiveScript, path.resolve(pathToRemove)],
247
+ {
248
+ cwd: elizaWorkspaceRoot,
249
+ encoding: "utf8",
250
+ stdio: ["ignore", "pipe", "pipe"],
251
+ },
252
+ );
253
+ if (result.status !== 0) {
254
+ const reason =
255
+ result.stderr?.trim() ||
256
+ result.stdout?.trim() ||
257
+ result.error?.message ||
258
+ `exit status ${String(result.status)}`;
259
+ throw new Error(
260
+ `Failed to recursively remove sanitized Electrobun runtime dist ${pathToRemove}: ${reason}`,
261
+ );
262
+ }
263
+ }
264
+
235
265
  const runtimeBundleSourcePath = resolveRuntimeBundleSourcePath(repoRoot);
236
266
  const runtimeBundleDistDir = path.relative(
237
267
  electrobunDir,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/electrobun",
3
- "version": "2.0.3-beta.5",
3
+ "version": "2.0.3-beta.7",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "assets",
@@ -25,14 +25,14 @@
25
25
  "build:native-effects": "bash scripts/build-macos-effects.sh"
26
26
  },
27
27
  "dependencies": {
28
- "@elizaos/agent": "2.0.3-beta.5",
29
- "@elizaos/app-core": "2.0.3-beta.5",
30
- "@elizaos/plugin-browser": "2.0.3-beta.5",
31
- "@elizaos/plugin-capacitor-bridge": "2.0.3-beta.5",
32
- "@elizaos/plugin-registry": "2.0.3-beta.5",
33
- "@elizaos/plugin-remote-manifest": "2.0.3-beta.5",
34
- "@elizaos/plugin-streaming": "2.0.3-beta.5",
35
- "@elizaos/shared": "2.0.3-beta.5",
28
+ "@elizaos/agent": "2.0.3-beta.7",
29
+ "@elizaos/app-core": "2.0.3-beta.7",
30
+ "@elizaos/plugin-browser": "2.0.3-beta.7",
31
+ "@elizaos/plugin-capacitor-bridge": "2.0.3-beta.7",
32
+ "@elizaos/plugin-registry": "2.0.3-beta.7",
33
+ "@elizaos/plugin-remote-manifest": "2.0.3-beta.7",
34
+ "@elizaos/plugin-streaming": "2.0.3-beta.7",
35
+ "@elizaos/shared": "2.0.3-beta.7",
36
36
  "bonjour-service": "1.4.2",
37
37
  "electrobun": "^1.18.1"
38
38
  },
@@ -46,5 +46,5 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
49
+ "gitHead": "61094f10458d11055c75b3dd0bae374e3f66bac5"
50
50
  }
@@ -61,7 +61,6 @@ Environment overrides:
61
61
  ## Commands
62
62
 
63
63
  ```sh
64
- bun run --cwd elizalaunch/remotes/fs build
65
64
  bun run --cwd elizalaunch/remotes/fs smoke
66
65
  bun run --cwd elizalaunch/remotes/fs smoke:phase5
67
66
  bun run --cwd elizalaunch/remotes/fs typecheck
@@ -4,7 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "bun build src/bun/worker.ts src/dev/phase5-smoke.ts --outdir dist --target bun",
8
7
  "smoke": "bun run src/dev/phase5-smoke.ts",
9
8
  "smoke:phase5": "bun run src/dev/phase5-smoke.ts",
10
9
  "typecheck": "tsc --ignoreConfig --noEmit --target ES2022 --module ESNext --moduleResolution Bundler --lib ES2022,DOM --types bun-types --allowImportingTsExtensions src/bun/protocol.ts src/bun/errors.ts src/bun/file-limits.ts src/bun/path-guard.ts src/bun/fs-service.ts src/bun/worker.ts src/dev/phase5-smoke.ts"
@@ -57,11 +57,10 @@ Each operation includes:
57
57
  - `ELIZA_GIT_COMMAND_TIMEOUT_MS` defaults to `120000`
58
58
  - `ELIZA_GIT_MAX_OPERATIONS` defaults to `200`
59
59
 
60
- ## Build And Smoke
60
+ ## Typecheck And Smoke
61
61
 
62
62
  ```sh
63
63
  bun run --cwd elizalaunch/remotes/git typecheck
64
- bun run --cwd elizalaunch/remotes/git build
65
64
  bun run --cwd elizalaunch/remotes/git smoke
66
65
  bun run --cwd elizalaunch/remotes/git smoke:phase7
67
66
  ```
@@ -4,7 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "bun build src/bun/worker.ts src/dev/phase7-smoke.ts --outdir dist --target bun",
8
7
  "smoke": "bun run src/dev/phase7-smoke.ts",
9
8
  "smoke:phase7": "bun run src/dev/phase7-smoke.ts",
10
9
  "typecheck": "tsc --ignoreConfig --noEmit --target ES2022 --module ESNext --moduleResolution Bundler --lib ES2022,DOM --types bun-types --allowImportingTsExtensions src/bun/protocol.ts src/bun/errors.ts src/bun/operation-history.ts src/bun/git-command.ts src/bun/git-service.ts src/bun/worker.ts src/dev/phase7-smoke.ts"
@@ -94,7 +94,6 @@ The current route file does not expose direct local generation or direct embeddi
94
94
 
95
95
  ```sh
96
96
  bun run --cwd elizalaunch/remotes/local-model typecheck
97
- bun run --cwd elizalaunch/remotes/local-model build
98
97
  bun run --cwd elizalaunch/remotes/local-model smoke
99
98
  bun run --cwd elizalaunch/remotes/local-model smoke:phase8
100
99
  ```
@@ -4,7 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "bun build src/bun/worker.ts src/dev/phase8-smoke.ts --outdir dist --target bun",
8
7
  "smoke": "bun run src/dev/phase8-smoke.ts",
9
8
  "smoke:phase8": "bun run src/dev/phase8-smoke.ts",
10
9
  "typecheck": "tsc --ignoreConfig --noEmit --target ES2022 --module ESNext --moduleResolution Bundler --lib ES2022,DOM --types bun-types --allowImportingTsExtensions src/bun/protocol.ts src/bun/errors.ts src/bun/eliza1-catalog.ts src/bun/hf-eliza1-client.ts src/bun/local-inference-api-client.ts src/bun/download-state.ts src/bun/model-service.ts src/bun/worker.ts src/dev/phase8-smoke.ts"
@@ -52,10 +52,9 @@ Output polling through `pty.session.output.tail` is sufficient for Phase 6 when
52
52
 
53
53
  Session environment inherits `process.env` and merges per-session env overrides.
54
54
 
55
- ## Build And Smoke
55
+ ## Smoke
56
56
 
57
57
  ```sh
58
- bun run --cwd elizalaunch/remotes/pty build
59
58
  bun run --cwd elizalaunch/remotes/pty smoke
60
59
  bun run --cwd elizalaunch/remotes/pty smoke:phase6
61
60
  ```
@@ -4,7 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "bun build src/bun/worker.ts src/dev/phase6-smoke.ts --outdir dist --target bun",
8
7
  "smoke": "bun run src/dev/phase6-smoke.ts",
9
8
  "smoke:phase6": "bun run src/dev/phase6-smoke.ts",
10
9
  "typecheck": "tsc --ignoreConfig --noEmit --target ES2022 --module ESNext --moduleResolution Bundler --lib ES2022,DOM --types bun-types --allowImportingTsExtensions src/bun/protocol.ts src/bun/errors.ts src/bun/output-buffer.ts src/bun/pty-service.ts src/bun/worker.ts src/dev/phase6-smoke.ts"
@@ -206,11 +206,7 @@ Git:
206
206
 
207
207
  ## Run
208
208
 
209
- Build:
210
-
211
- ```sh
212
- bun run --cwd elizalaunch/remotes/runtime build
213
- ```
209
+ Remotes run from source (the runtime loads `src/bun/worker.ts` / `src/web/index.html` directly); there is no build step.
214
210
 
215
211
  Phase 1 smoke:
216
212
 
@@ -4,7 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "bun build src/bun/worker.ts src/dev/phase1-smoke.ts src/dev/phase2-smoke.ts src/dev/phase3-smoke.ts --outdir dist --target bun",
8
7
  "smoke": "bun src/dev/phase1-smoke.ts",
9
8
  "smoke:phase1": "bun src/dev/phase1-smoke.ts",
10
9
  "smoke:phase2": "bun src/dev/phase2-smoke.ts",
@@ -106,10 +106,9 @@ It consumes:
106
106
  - `agent.message.stream.done`
107
107
  - `agent.message.stream.cancelled`
108
108
 
109
- ## Build and Smoke
109
+ ## Smoke
110
110
 
111
111
  ```sh
112
- bun run --cwd elizalaunch/remotes/surface build
113
112
  bun run --cwd elizalaunch/remotes/surface smoke
114
113
  bun run --cwd elizalaunch/remotes/surface smoke:phase4
115
114
  ```
@@ -4,7 +4,6 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "bun build src/bun/worker.ts src/dev/phase4-smoke.ts --outdir dist --target bun && bun build src/web/index.ts --outdir dist/web --target browser",
8
7
  "smoke": "bun run src/dev/phase4-smoke.ts",
9
8
  "smoke:phase4": "bun run src/dev/phase4-smoke.ts",
10
9
  "typecheck": "tsc --ignoreConfig --noEmit --target ES2022 --module ESNext --moduleResolution Bundler --lib ES2022,DOM,DOM.Iterable --types bun-types --allowImportingTsExtensions src/protocol/event-types.ts src/protocol/runtime-client.ts src/web/state.ts src/web/render.ts src/web/app.ts src/web/index.ts src/bun/worker.ts src/dev/phase4-smoke.ts"
@@ -9,7 +9,8 @@
9
9
  * Resolves the host app via the standard elizaOS layout; run after
10
10
  * `bun run build:web` (or equivalent).
11
11
  */
12
- import { cp, mkdir, rm, stat } from "node:fs/promises";
12
+ import { spawnSync } from "node:child_process";
13
+ import { cp, mkdir, stat } from "node:fs/promises";
13
14
  import path from "node:path";
14
15
  import {
15
16
  resolveElectrobunDir,
@@ -22,6 +23,12 @@ const appRoot = resolveMainAppDir(repoRoot, "app");
22
23
  const electrobunRoot = resolveElectrobunDir(repoRoot);
23
24
  const sourceDir = path.join(appRoot, "dist");
24
25
  const targetDir = path.join(electrobunRoot, "app");
26
+ const rmPathRecursiveScript = path.join(
27
+ repoRoot,
28
+ "packages",
29
+ "scripts",
30
+ "rm-path-recursive.mjs",
31
+ );
25
32
  const LOG_PREFIX = "[Electrobun]";
26
33
 
27
34
  async function ensureDirExists(dir) {
@@ -33,6 +40,28 @@ async function ensureDirExists(dir) {
33
40
  }
34
41
  }
35
42
 
43
+ function rmRecursive(pathToRemove) {
44
+ const result = spawnSync(
45
+ process.execPath,
46
+ [rmPathRecursiveScript, path.resolve(pathToRemove)],
47
+ {
48
+ cwd: repoRoot,
49
+ encoding: "utf8",
50
+ stdio: ["ignore", "pipe", "pipe"],
51
+ },
52
+ );
53
+ if (result.status !== 0) {
54
+ const reason =
55
+ result.stderr?.trim() ||
56
+ result.stdout?.trim() ||
57
+ result.error?.message ||
58
+ `exit status ${String(result.status)}`;
59
+ throw new Error(
60
+ `${LOG_PREFIX} failed to recursively remove ${pathToRemove}: ${reason}`,
61
+ );
62
+ }
63
+ }
64
+
36
65
  if (!(await ensureDirExists(sourceDir))) {
37
66
  console.error(`${LOG_PREFIX} Web build output not found: ${sourceDir}`);
38
67
  console.error(
@@ -41,7 +70,7 @@ if (!(await ensureDirExists(sourceDir))) {
41
70
  process.exit(1);
42
71
  }
43
72
 
44
- await rm(targetDir, { recursive: true, force: true });
73
+ rmRecursive(targetDir);
45
74
  await mkdir(targetDir, { recursive: true });
46
75
  await cp(sourceDir, targetDir, { recursive: true, force: true });
47
76
 
@@ -256,7 +256,11 @@ export function resolveRendererFacingApiBase(
256
256
  */
257
257
  type ApiBaseUpdateRpc = {
258
258
  send?: {
259
- apiBaseUpdate?: (payload: { base: string; token?: string }) => void;
259
+ apiBaseUpdate?: (payload: {
260
+ base: string;
261
+ token?: string;
262
+ externalApiBase?: string | null;
263
+ }) => void;
260
264
  };
261
265
  };
262
266
 
@@ -264,9 +268,14 @@ export function pushApiBaseToRenderer(
264
268
  win: { webview: { rpc?: unknown } },
265
269
  base: string,
266
270
  apiToken?: string,
271
+ externalApiBase?: string | null,
267
272
  ): void {
268
273
  const trimmedToken = apiToken?.trim();
269
- const payload = { base, token: trimmedToken || undefined };
274
+ const payload = {
275
+ base,
276
+ token: trimmedToken || undefined,
277
+ externalApiBase: externalApiBase ?? null,
278
+ };
270
279
  try {
271
280
  const rpcSend = (win.webview.rpc as ApiBaseUpdateRpc | undefined)?.send;
272
281
  rpcSend?.apiBaseUpdate?.(payload);
@@ -63,8 +63,21 @@ ensureElectrobunGlobal();
63
63
 
64
64
  function dispatchMessage(messageName: string, payload: unknown): void {
65
65
  if (messageName === "apiBaseUpdate") {
66
- const apiBaseUpdate = payload as { base: string; token?: string };
66
+ const apiBaseUpdate = payload as {
67
+ base: string;
68
+ token?: string;
69
+ externalApiBase?: string | null;
70
+ };
67
71
  window.__ELIZA_API_BASE__ = apiBaseUpdate.base;
72
+ if (
73
+ typeof apiBaseUpdate.externalApiBase === "string" &&
74
+ apiBaseUpdate.externalApiBase.trim()
75
+ ) {
76
+ window.__ELIZA_DESKTOP_EXTERNAL_API_BASE__ =
77
+ apiBaseUpdate.externalApiBase.trim();
78
+ } else {
79
+ Reflect.deleteProperty(window, "__ELIZA_DESKTOP_EXTERNAL_API_BASE__");
80
+ }
68
81
  if (apiBaseUpdate.token) {
69
82
  Object.defineProperty(window, "__ELIZA_API_TOKEN__", {
70
83
  value: apiBaseUpdate.token,
@@ -196,6 +209,7 @@ declare global {
196
209
  interface Window {
197
210
  __ELIZA_API_BASE__?: string;
198
211
  __ELIZA_API_TOKEN__: string;
212
+ __ELIZA_DESKTOP_EXTERNAL_API_BASE__?: string;
199
213
  __ELIZA_ELECTROBUN_RPC__?: typeof electrobunRpc;
200
214
  }
201
215
  }
@@ -34,7 +34,6 @@ import { getBrandConfig } from "./brand-config";
34
34
  import { startBrowserWorkspaceBridgeServer } from "./browser-workspace-bridge-server";
35
35
  import { readNavigationEventUrl } from "./cloud-auth-window";
36
36
  import { readOpenUrlEventUrl } from "./desktop-deep-link-events";
37
- import { shouldCreateDesktopPill } from "./desktop-pill-config";
38
37
  import { startDesktopTestBridgeServer } from "./desktop-test-bridge-server";
39
38
  import {
40
39
  shouldCreateDesktopTray,
@@ -96,7 +95,6 @@ import {
96
95
  getOnboardingOverlayWindow,
97
96
  } from "./onboarding-overlay-window";
98
97
  import { getPersistedDeployment } from "./persisted-deployment";
99
- import { createPillWindow, getPillWindow } from "./pill-window";
100
98
  import { printElectrobunDevSettingsBanner } from "./print-electrobun-dev-settings-banner";
101
99
  import {
102
100
  createRendererApiProxyRequestInit,
@@ -944,11 +942,32 @@ async function resolveRendererUrl(): Promise<string> {
944
942
  return rendererUrl;
945
943
  }
946
944
 
945
+ function appendApiBaseParam(rendererUrl: string, apiBase: string): string {
946
+ try {
947
+ const url = new URL(rendererUrl);
948
+ if (!url.searchParams.has("apiBase")) {
949
+ url.searchParams.set("apiBase", apiBase);
950
+ }
951
+ return url.toString();
952
+ } catch {
953
+ return rendererUrl;
954
+ }
955
+ }
956
+
957
+ async function resolveRendererUrlForCurrentRuntime(): Promise<string> {
958
+ const rendererUrl = await resolveRendererUrl();
959
+ const runtime = resolveDesktopRuntime();
960
+ if (runtime.mode === "external" && runtime.externalApi.base) {
961
+ return appendApiBaseParam(rendererUrl, runtime.externalApi.base);
962
+ }
963
+ return rendererUrl;
964
+ }
965
+
947
966
  async function createMainWindow(rpc: ElizaDesktopRpc): Promise<BrowserWindow> {
948
967
  const kiosk = isKioskShellMode();
949
968
  const rendererUrl = kiosk
950
- ? appendKioskShellModeParam(await resolveRendererUrl())
951
- : await resolveRendererUrl();
969
+ ? appendKioskShellModeParam(await resolveRendererUrlForCurrentRuntime())
970
+ : await resolveRendererUrlForCurrentRuntime();
952
971
  const buildInfo = await BuildConfig.get();
953
972
  const mainWindowPartition = resolveMainWindowPartition(process.env, {
954
973
  platform: process.platform,
@@ -1251,7 +1270,7 @@ async function openOnboardingOverlayWindow(): Promise<BrowserWindow> {
1251
1270
  return existing;
1252
1271
  }
1253
1272
  const { rpc } = createDesktopRpc("onboarding-overlay");
1254
- const rendererUrl = await resolveRendererUrl();
1273
+ const rendererUrl = await resolveRendererUrlForCurrentRuntime();
1255
1274
  let preload = "";
1256
1275
  try {
1257
1276
  preload = readResolvedPreloadScript(import.meta.dir);
@@ -1692,11 +1711,6 @@ function injectApiBaseIntoOpenRendererWindows(): void {
1692
1711
  injectApiBase(currentWindow);
1693
1712
  }
1694
1713
 
1695
- const pillWindow = getPillWindow();
1696
- if (pillWindow && pillWindow !== currentWindow) {
1697
- injectApiBase(pillWindow);
1698
- }
1699
-
1700
1714
  surfaceWindowManager?.forEachWindow((w) => {
1701
1715
  injectApiBase(w as BrowserWindow);
1702
1716
  });
@@ -1712,10 +1726,6 @@ function collectOpenRendererWindows(): BrowserWindow[] {
1712
1726
  if (currentWindow) {
1713
1727
  windows.push(currentWindow);
1714
1728
  }
1715
- const pillWindow = getPillWindow();
1716
- if (pillWindow && pillWindow !== currentWindow) {
1717
- windows.push(pillWindow);
1718
- }
1719
1729
  surfaceWindowManager?.forEachWindow((w) => {
1720
1730
  windows.push(w as BrowserWindow);
1721
1731
  });
@@ -2479,7 +2489,7 @@ async function main(): Promise<void> {
2479
2489
  // The agent rebound to a different loopback port (or recovered from a
2480
2490
  // crash) — the cookies we installed during _startAgent were scoped to
2481
2491
  // the old origin. Re-prime so every renderer's next /api request stays
2482
- // authenticated, including the OS-level pill window.
2492
+ // authenticated, including any open secondary renderer windows.
2483
2493
  markDesktopSessionStale();
2484
2494
  const apiBase = `http://127.0.0.1:${status.port}`;
2485
2495
  const rendererBase = resolveRendererFacingApiBase(
@@ -2617,19 +2627,6 @@ async function main(): Promise<void> {
2617
2627
  /* non-fatal */
2618
2628
  }
2619
2629
  getFloatingChatManager().configure(url, preload);
2620
- // In kiosk mode the chat pill lives in-canvas on the KioskShell, so we
2621
- // never spawn the separate native pill toplevel. Outside kiosk, the pill
2622
- // loads the same renderer in chat-overlay shell mode so the assistant
2623
- // lives in its own OS-level window instead of inside the app.
2624
- if (!isKioskShellMode() && shouldCreateDesktopPill()) {
2625
- try {
2626
- createPillWindow({ rendererUrl: url, preload });
2627
- } catch (err) {
2628
- logger.warn(
2629
- `[Main] Failed to spawn pill window: ${err instanceof Error ? err.message : String(err)}`,
2630
- );
2631
- }
2632
- }
2633
2630
  });
2634
2631
 
2635
2632
  // Per-window RPC tracking: surface windows each get their own typed
@@ -1,10 +1,23 @@
1
1
  import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
- import { getCurrent, injectIntoHtml, setCurrent } from "./api-base-owner";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
+ import {
6
+ getCurrent,
7
+ injectIntoHtml,
8
+ pushToWindow,
9
+ setCurrent,
10
+ } from "./api-base-owner";
6
11
 
7
12
  const ENV_KEYS = [
13
+ "ELIZA_API_BASE",
14
+ "ELIZA_API_BASE_URL",
15
+ "ELIZA_DESKTOP_API_BASE",
16
+ "ELIZA_DESKTOP_CLOUD_AGENT_BASE",
17
+ "ELIZA_DESKTOP_CLOUD_ONLY",
18
+ "ELIZA_DESKTOP_RUNTIME_MODE",
19
+ "ELIZA_DESKTOP_SKIP_EMBEDDED_AGENT",
20
+ "ELIZA_DESKTOP_TEST_API_BASE",
8
21
  "ELIZA_STARTUP_SESSION_ID",
9
22
  "ELIZA_STARTUP_STATE_FILE",
10
23
  "ELIZA_STARTUP_EVENTS_FILE",
@@ -17,12 +30,10 @@ let tempHome: string | null = null;
17
30
  beforeEach(() => {
18
31
  for (const key of ENV_KEYS) {
19
32
  originalEnv.set(key, process.env[key]);
33
+ if (key !== "HOME") delete process.env[key];
20
34
  }
21
35
  tempHome = fs.mkdtempSync(path.join(os.tmpdir(), "eliza-api-base-owner-"));
22
36
  process.env.HOME = tempHome;
23
- delete process.env.ELIZA_STARTUP_SESSION_ID;
24
- delete process.env.ELIZA_STARTUP_STATE_FILE;
25
- delete process.env.ELIZA_STARTUP_EVENTS_FILE;
26
37
  setCurrent(null);
27
38
  });
28
39
 
@@ -80,4 +91,55 @@ describe("api-base-owner", () => {
80
91
  expect(injected).toContain('"__ELIZA_API_TOKEN__"');
81
92
  expect(injected).toContain("elizaos.app.boot-config");
82
93
  });
94
+
95
+ it("marks a non-loopback current API base as an external desktop API base", () => {
96
+ setCurrent("https://agent.example.com", "cloud-token");
97
+
98
+ const injected = injectIntoHtml("<html><head></head><body></body></html>");
99
+
100
+ expect(injected).toContain(
101
+ 'window.__ELIZA_API_BASE__="https://agent.example.com";',
102
+ );
103
+ expect(injected).toContain(
104
+ 'window.__ELIZA_DESKTOP_EXTERNAL_API_BASE__="https://agent.example.com";',
105
+ );
106
+ });
107
+
108
+ it("pushes the external desktop API base to already-open renderer windows", () => {
109
+ setCurrent("https://agent.example.com", "cloud-token");
110
+ const apiBaseUpdate = vi.fn();
111
+
112
+ pushToWindow({
113
+ webview: {
114
+ rpc: {
115
+ send: { apiBaseUpdate },
116
+ },
117
+ },
118
+ });
119
+
120
+ expect(apiBaseUpdate).toHaveBeenCalledWith({
121
+ base: "https://agent.example.com",
122
+ token: "cloud-token",
123
+ externalApiBase: "https://agent.example.com",
124
+ });
125
+ });
126
+
127
+ it("does not mark loopback API bases as external", () => {
128
+ setCurrent("http://127.0.0.1:31337", "dev-token");
129
+ const apiBaseUpdate = vi.fn();
130
+
131
+ pushToWindow({
132
+ webview: {
133
+ rpc: {
134
+ send: { apiBaseUpdate },
135
+ },
136
+ },
137
+ });
138
+
139
+ expect(apiBaseUpdate).toHaveBeenCalledWith({
140
+ base: "http://127.0.0.1:31337",
141
+ token: "dev-token",
142
+ externalApiBase: null,
143
+ });
144
+ });
83
145
  });