Skip to content

Commit ef5bf7a

Browse files
author
Shaw
committed
fix(release): restore desktop release inputs
1 parent b07b79e commit ef5bf7a

12 files changed

Lines changed: 476 additions & 24 deletions

File tree

bun.lock

Lines changed: 236 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Desktop Release Regression Inventory
2+
3+
This inventory records desktop regression coverage that is intentionally outside
4+
the deterministic PR gate. The release matrix validator keeps these entries
5+
visible so they are reviewed before a production desktop release.
6+
7+
## Packaged and E2E Coverage
8+
9+
- gameOpenWindow — full round-trip with openGameWindow mock (needs canvas mock update)
10+
- Abnormal window position (off-screen) is corrected to safe defaults (e2e)
11+
- Deep link received while app is closed causes app to launch (e2e)
12+
- Deep link received while app is open does not launch second instance (e2e)
13+
- Shortcuts survive window focus changes (e2e)
14+
- App launches automatically after system restart (e2e)
15+
- Auto-launch survives app updates (e2e)
16+
- Keyboard shortcut Cmd+Q triggers quit (e2e)
17+
- Keyboard shortcut Cmd+R triggers reload (e2e)
18+
- Keyboard shortcut Cmd+Option+I opens devtools (e2e)
19+
- Gateway discovery sends gatewayDiscovery push event to renderer (integration)
20+
- Canvas window is sandboxed — cannot access main app origin (integration)
21+
- Canvas navigate blocks external URLs (integration)
22+
- Agent port is reachable via HTTP after status reaches 'running' (integration)
23+
- Agent crash triggers automatic restart (integration)
24+
- Stopping agent while starting does not leave zombie process (integration)
25+
- Check for updates contacts the release server (network)
26+
- Applying update relaunches the app (e2e)
27+
- Update check works on both canary and stable channels (network)
28+
- Tray icon persists after main window is closed (e2e)
29+
- Main window has native vibrancy effect on macOS (e2e)
30+
- Context menu closes when clicking elsewhere (e2e)
31+
32+
## Hardware and Manual Coverage
33+
34+
- Microphone input works after permission is granted (hardware)
35+
- Swabble fires 'wakeWordDetected' event when wake word is spoken (hardware)
36+
- Audio transcription produces non-empty text for clear speech (hardware)
37+
- Camera preview renders in the UI when stream is started (hardware)
38+
- Switching between front/rear camera works (hardware)
39+
- takeScreenshot returns a non-empty base64 PNG (hardware)
40+
- Frame capture mode streams frames at configured interval (hardware)
41+
- Left-clicking the tray icon opens the companion window (visual)
42+
- Right-clicking the tray icon shows the tray context menu (visual)
43+
- Window can be dragged by clicking the header region (visual)
44+
- Photo quality is acceptable at default settings (hardware)
45+
- Requesting accessibility opens System Preferences (OS interaction)
46+
- Permission status reflects actual system state (OS interaction)
47+
- Context menu appears at cursor position (visual)
48+
- Power state reflects actual battery status (hardware)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Desktop Release Regression Checklist
2+
3+
Complete these manual checks for desktop release candidates after packaged smoke
4+
tests pass on the target platform.
5+
6+
- [ ] Left-clicking the tray icon opens the companion window (visual)
7+
- [ ] Right-clicking the tray icon shows the tray context menu (visual)
8+
- [ ] Window can be dragged by clicking the header region (visual)
9+
- [ ] Photo quality is acceptable at default settings (hardware)
10+
- [ ] Requesting accessibility opens System Preferences (OS interaction)
11+
- [ ] Permission status reflects actual system state (OS interaction)
12+
- [ ] Context menu appears at cursor position (visual)
13+
- [ ] Power state reflects actual battery status (hardware)

packages/agent/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@
315315
"jsdom": "^29.0.0",
316316
"json5": "^2.2.3",
317317
"minimisted": "^2.0.0",
318+
"onnxruntime-web": "1.26.0",
318319
"pako": "^1.0.10",
319320
"pg": "^8.16.3",
320321
"pify": "^4.0.1",

packages/agent/scripts/build-mobile-bundle.mjs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,33 @@ const nativeStubs = {
292292
"@types/react": path.join(stubsDir, "null-plugin.cjs"),
293293
"@types/react/jsx-runtime": path.join(stubsDir, "null-plugin.cjs"),
294294
"@types/react/jsx-dev-runtime": path.join(stubsDir, "null-plugin.cjs"),
295-
// node-llama-cpp and its @node-llama-cpp/<platform> prebuilds were
296-
// removed when plugin-local-inference migrated to llama-cpp-capacitor +
297-
// bun:ffi (the desktop FFI shim path). The mobile bundle no longer
298-
// needs the stubs because no source file imports them.
295+
// node-llama-cpp and its @node-llama-cpp/<platform> prebuilds stay stubbed
296+
// for mobile. plugin-local-inference resolves local inference through
297+
// llama-cpp-capacitor + bun:ffi on device, but node-llama-cpp's helper code
298+
// still contains dynamic imports for every desktop prebuild package.
299+
"node-llama-cpp": path.join(stubsDir, "null-plugin.cjs"),
300+
"@node-llama-cpp/linux-arm64": path.join(stubsDir, "null-plugin.cjs"),
301+
"@node-llama-cpp/linux-armv7l": path.join(stubsDir, "null-plugin.cjs"),
302+
"@node-llama-cpp/linux-x64": path.join(stubsDir, "null-plugin.cjs"),
303+
"@node-llama-cpp/linux-x64-cuda": path.join(stubsDir, "null-plugin.cjs"),
304+
"@node-llama-cpp/linux-x64-cuda-ext": path.join(
305+
stubsDir,
306+
"null-plugin.cjs",
307+
),
308+
"@node-llama-cpp/linux-x64-vulkan": path.join(
309+
stubsDir,
310+
"null-plugin.cjs",
311+
),
312+
"@node-llama-cpp/mac-arm64-metal": path.join(stubsDir, "null-plugin.cjs"),
313+
"@node-llama-cpp/mac-x64": path.join(stubsDir, "null-plugin.cjs"),
314+
"@node-llama-cpp/win-arm64": path.join(stubsDir, "null-plugin.cjs"),
315+
"@node-llama-cpp/win-x64": path.join(stubsDir, "null-plugin.cjs"),
316+
"@node-llama-cpp/win-x64-cuda": path.join(stubsDir, "null-plugin.cjs"),
317+
"@node-llama-cpp/win-x64-cuda-ext": path.join(
318+
stubsDir,
319+
"null-plugin.cjs",
320+
),
321+
"@node-llama-cpp/win-x64-vulkan": path.join(stubsDir, "null-plugin.cjs"),
299322
// llama-cpp-capacitor is the WebView-side JNI binding for the Capacitor
300323
// mobile build. The bun-side AOSP agent uses bun:ffi against libllama.so
301324
// directly via aosp-llama-adapter.ts, never this package — but Bun.build

packages/app-core/scripts/desktop-build.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const CORE_PACKAGE_DIR = resolveWorkspacePackageDir("core");
6363
const ELECTROBUN_CARROTS_PACKAGE_DIR =
6464
resolveWorkspacePackageDir("electrobun-carrots");
6565
const SHARED_PACKAGE_DIR = resolveWorkspacePackageDir("shared");
66+
const UI_PACKAGE_DIR = resolveWorkspacePackageDir("ui");
6667
const VAULT_PACKAGE_DIR = resolveWorkspacePackageDir("vault");
6768

6869
const argv = process.argv.slice(2);
@@ -598,6 +599,13 @@ function ensureWorkspaceRuntimePackagesBuilt() {
598599
ensureWorkspaceRuntimePackageBuilt("@elizaos/app-core", APP_CORE_PACKAGE_DIR);
599600
}
600601

602+
function ensureUiGeneratedAssets() {
603+
runBun(["run", "generate:css-strings"], {
604+
cwd: UI_PACKAGE_DIR,
605+
label: "Generating @elizaos/ui CSS string modules",
606+
});
607+
}
608+
601609
function stageDesktopBuild() {
602610
ensureAppDirs();
603611

@@ -648,6 +656,8 @@ function stageDesktopBuild() {
648656
allowFailure: true,
649657
});
650658

659+
ensureUiGeneratedAssets();
660+
651661
runPackageBinary("vite", ["build"], {
652662
cwd: APP_DIR,
653663
env: {

packages/app-core/scripts/run-mobile-build.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import {
5757
findForbiddenRuntimeImportGroups,
5858
findForbiddenRuntimeStrings,
5959
formatForbiddenRuntimeFindings,
60-
} from "../../bun-ios-runtime/scripts/ios-app-store-runtime-policy.mjs";
60+
} from "../../native/bun-runtime/scripts/ios-app-store-runtime-policy.mjs";
6161
import {
6262
loadAospVariantConfig,
6363
resolveAppConfigPath,

packages/scripts/audit-capability-router-live-ci.self-test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ const liveReportValidatorSelfTestSource = readFileSync(
2020
"packages/scripts/validate-capability-router-live-reports.self-test.ts",
2121
"utf8",
2222
);
23+
const githubLiveArtifactValidatorSource = readFileSync(
24+
"packages/scripts/validate-capability-router-github-live-artifacts.ts",
25+
"utf8",
26+
);
2327
const liveReportWriterSource = readFileSync(
2428
"packages/agent/src/services/remote-capability-live-report.ts",
2529
"utf8",
@@ -109,6 +113,30 @@ assertRootPackageFailure(
109113
),
110114
);
111115

116+
const githubArtifactValidatorFailure = assertFails(
117+
"GitHub live artifact validator downloads and validates reports",
118+
workflow,
119+
rootPackageJson,
120+
agentPackageJson,
121+
providerSmokeSource,
122+
liveReportValidatorSource,
123+
liveReportWriterSource,
124+
endpointConformanceSource,
125+
liveReportValidatorSelfTestSource,
126+
githubLiveArtifactValidatorSource.replace(
127+
'"remote-capability-provider-live-report"',
128+
'"remote-capability-provider-live-report-disabled"',
129+
),
130+
);
131+
if (
132+
githubArtifactValidatorFailure.sourcePath !==
133+
"packages/scripts/validate-capability-router-github-live-artifacts.ts"
134+
) {
135+
throw new Error(
136+
`GitHub artifact validator failure reported wrong source path: ${githubArtifactValidatorFailure.sourcePath}`,
137+
);
138+
}
139+
112140
const packageFailure = assertFails(
113141
"canonical remote capability suite covers live report writer",
114142
workflow,
@@ -604,10 +632,13 @@ function assertPasses(
604632
candidateLiveReportWriterSource = liveReportWriterSource,
605633
candidateEndpointConformanceSource = endpointConformanceSource,
606634
candidateLiveReportValidatorSelfTestSource = liveReportValidatorSelfTestSource,
635+
candidateGithubLiveArtifactValidatorSource = githubLiveArtifactValidatorSource,
607636
): void {
608637
const failures = validateCapabilityRouterLiveCi(candidate, {
609638
agentPackageJson: candidateAgentPackageJson,
610639
endpointConformanceSource: candidateEndpointConformanceSource,
640+
githubLiveArtifactValidatorSource:
641+
candidateGithubLiveArtifactValidatorSource,
611642
liveReportValidatorSelfTestSource:
612643
candidateLiveReportValidatorSelfTestSource,
613644
liveReportValidatorSource: candidateLiveReportValidatorSource,
@@ -635,10 +666,13 @@ function assertFails(
635666
candidateLiveReportWriterSource = liveReportWriterSource,
636667
candidateEndpointConformanceSource = endpointConformanceSource,
637668
candidateLiveReportValidatorSelfTestSource = liveReportValidatorSelfTestSource,
669+
candidateGithubLiveArtifactValidatorSource = githubLiveArtifactValidatorSource,
638670
): ReturnType<typeof validateCapabilityRouterLiveCi>[number] {
639671
const failures = validateCapabilityRouterLiveCi(candidate, {
640672
agentPackageJson: candidateAgentPackageJson,
641673
endpointConformanceSource: candidateEndpointConformanceSource,
674+
githubLiveArtifactValidatorSource:
675+
candidateGithubLiveArtifactValidatorSource,
642676
liveReportValidatorSelfTestSource:
643677
candidateLiveReportValidatorSelfTestSource,
644678
liveReportValidatorSource: candidateLiveReportValidatorSource,

packages/scripts/audit-capability-router-live-ci.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ const liveReportValidatorPath =
1313
"packages/scripts/validate-capability-router-live-reports.ts";
1414
const liveReportValidatorSelfTestPath =
1515
"packages/scripts/validate-capability-router-live-reports.self-test.ts";
16+
const githubLiveArtifactValidatorPath =
17+
"packages/scripts/validate-capability-router-github-live-artifacts.ts";
1618

1719
type Check = {
1820
name: string;
1921
pattern: RegExp;
2022
source?:
2123
| "agent-package"
2224
| "endpoint-conformance"
25+
| "github-live-artifact-validator"
2326
| "live-report-validator"
2427
| "live-report-validator-self-test"
2528
| "live-report-writer"
@@ -117,7 +120,7 @@ export const checks: Check[] = [
117120
{
118121
name: "GitHub live artifact validator downloads and validates reports",
119122
pattern:
120-
/gh[\s\S]*run[\s\S]*download[\s\S]*remote-capability-cloud-live-report[\s\S]*remote-capability-provider-live-report[\s\S]*test:remote-capabilities:validate-live-reports[\s\S]*--kind[\s\S]*cloud[\s\S]*test:remote-capabilities:validate-live-reports[\s\S]*--kind[\s\S]*provider[\s\S]*--require-providers[\s\S]*e2b,home-machine,mobile-companion/,
123+
/CLOUD_ARTIFACT\s*=\s*"remote-capability-cloud-live-report"[\s\S]*PROVIDER_ARTIFACT\s*=\s*"remote-capability-provider-live-report"[\s\S]*runCommand\("gh"[\s\S]*"run"[\s\S]*"download"[\s\S]*CLOUD_ARTIFACT[\s\S]*PROVIDER_ARTIFACT[\s\S]*test:remote-capabilities:validate-live-reports[\s\S]*--kind[\s\S]*cloud[\s\S]*test:remote-capabilities:validate-live-reports[\s\S]*--kind[\s\S]*provider[\s\S]*--require-providers[\s\S]*e2b,home-machine,mobile-companion/,
121124
source: "github-live-artifact-validator",
122125
message:
123126
"GitHub live artifact validation must download both artifacts and validate Cloud plus required provider report contents.",
@@ -381,6 +384,7 @@ export function validateCapabilityRouterLiveCi(
381384
options: {
382385
agentPackageJson?: string;
383386
endpointConformanceSource?: string;
387+
githubLiveArtifactValidatorSource?: string;
384388
liveReportValidatorSelfTestSource?: string;
385389
providerSmokeSource?: string;
386390
rootPackageJson?: string;
@@ -409,6 +413,7 @@ function getCheckContent(
409413
options: {
410414
agentPackageJson?: string;
411415
endpointConformanceSource?: string;
416+
githubLiveArtifactValidatorSource?: string;
412417
liveReportValidatorSelfTestSource?: string;
413418
providerSmokeSource?: string;
414419
rootPackageJson?: string;
@@ -420,6 +425,9 @@ function getCheckContent(
420425
if (check.source === "endpoint-conformance") {
421426
return options.endpointConformanceSource ?? "";
422427
}
428+
if (check.source === "github-live-artifact-validator") {
429+
return options.githubLiveArtifactValidatorSource ?? "";
430+
}
423431
if (check.source === "live-report-validator") {
424432
return options.liveReportValidatorSource ?? "";
425433
}
@@ -439,6 +447,9 @@ function getCheckContent(
439447
function getCheckSourcePath(check: Check, workflowPath: string): string {
440448
if (check.source === "agent-package") return agentPackagePath;
441449
if (check.source === "endpoint-conformance") return endpointConformancePath;
450+
if (check.source === "github-live-artifact-validator") {
451+
return githubLiveArtifactValidatorPath;
452+
}
442453
if (check.source === "live-report-validator") return liveReportValidatorPath;
443454
if (check.source === "live-report-validator-self-test") {
444455
return liveReportValidatorSelfTestPath;
@@ -462,11 +473,16 @@ if (import.meta.main) {
462473
liveReportValidatorSelfTestPath,
463474
"utf8",
464475
);
476+
const githubLiveArtifactValidatorSource = readFileSync(
477+
githubLiveArtifactValidatorPath,
478+
"utf8",
479+
);
465480
const liveReportWriterSource = readFileSync(liveReportWriterPath, "utf8");
466481
const providerSmokeSource = readFileSync(providerSmokePath, "utf8");
467482
const failures = validateCapabilityRouterLiveCi(workflow, {
468483
agentPackageJson,
469484
endpointConformanceSource,
485+
githubLiveArtifactValidatorSource,
470486
liveReportValidatorSelfTestSource,
471487
liveReportValidatorSource,
472488
liveReportWriterSource,

packages/scripts/patch-llama-cpp-capacitor.mjs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
* when already applied, which is treated as success.
2020
*/
2121

22-
import { execFileSync, spawnSync } from "node:child_process";
23-
import { existsSync, readFileSync, readdirSync } from "node:fs";
22+
import { spawnSync } from "node:child_process";
23+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2424
import { dirname, join, resolve } from "node:path";
2525
import { fileURLToPath } from "node:url";
2626

@@ -84,6 +84,8 @@ for (const { label, dir: pkgDir } of candidates) {
8484
try {
8585
const contents = readFileSync(buildGradle, "utf8");
8686
if (contents.includes("'arm64-v8a', 'riscv64'")) {
87+
ensureAndroidSkipJniLibsPatch(pkgDir);
88+
ensureAndroidSkipPatch(pkgDir);
8789
skipped++;
8890
continue;
8991
}
@@ -102,6 +104,8 @@ for (const { label, dir: pkgDir } of candidates) {
102104
);
103105

104106
if (result.status === 0 || result.status === 1) {
107+
ensureAndroidSkipJniLibsPatch(pkgDir);
108+
ensureAndroidSkipPatch(pkgDir);
105109
patched++;
106110
} else {
107111
failed++;
@@ -116,3 +120,52 @@ if (patched > 0 || skipped > 0 || failed > 0) {
116120
`[patch-llama-cpp-capacitor] patched=${patched} already-applied=${skipped} failed=${failed}`,
117121
);
118122
}
123+
124+
function ensureAndroidSkipJniLibsPatch(pkgDir) {
125+
const buildGradle = join(pkgDir, "android", "build.gradle");
126+
if (!existsSync(buildGradle)) return;
127+
const current = readFileSync(buildGradle, "utf8");
128+
if (current.includes("ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB")) return;
129+
const marker =
130+
" sourceSets {\n" +
131+
" main {\n" +
132+
" java {\n" +
133+
" srcDirs = ['src/main/java']\n" +
134+
" }\n" +
135+
" }\n";
136+
if (!current.includes(marker)) return;
137+
const skipBlock =
138+
" if (System.getenv('ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB') == '1') {\n" +
139+
" main {\n" +
140+
" jniLibs.srcDirs = []\n" +
141+
" }\n" +
142+
" }\n";
143+
writeFileSync(buildGradle, current.replace(marker, marker + skipBlock));
144+
}
145+
146+
function ensureAndroidSkipPatch(pkgDir) {
147+
const cmakeLists = join(pkgDir, "android", "src", "main", "CMakeLists.txt");
148+
if (!existsSync(cmakeLists)) return;
149+
const current = readFileSync(cmakeLists, "utf8");
150+
if (current.includes("ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB")) return;
151+
const marker =
152+
'if(NOT DEFINED ELIZA_REPO_ROOT)\n message(FATAL_ERROR "ELIZA_REPO_ROOT is required for the Eliza DFlash Android JNI bridge")\nendif()\n';
153+
if (!current.includes(marker)) return;
154+
const skipBlock =
155+
'if(DEFINED ENV{ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB} AND "$ENV{ELIZA_ANDROID_SKIP_FORK_LLAMA_LIB}" STREQUAL "1")\n' +
156+
' file(WRITE "${CMAKE_BINARY_DIR}/eliza-dflash-smoke-stub.cpp" "extern \\"C\\" int eliza_dflash_smoke_stub() { return 0; }\\n")\n' +
157+
' set(ELIZA_SMOKE_OUTPUT_NAME "llama-cpp-arm64")\n' +
158+
' if(ANDROID_ABI STREQUAL "riscv64")\n' +
159+
' set(ELIZA_SMOKE_OUTPUT_NAME "llama-cpp-riscv64")\n' +
160+
' endif()\n' +
161+
' add_library(${ELIZA_SMOKE_OUTPUT_NAME} SHARED "${CMAKE_BINARY_DIR}/eliza-dflash-smoke-stub.cpp")\n' +
162+
' set_target_properties(\n' +
163+
' ${ELIZA_SMOKE_OUTPUT_NAME}\n' +
164+
' PROPERTIES\n' +
165+
' OUTPUT_NAME "${ELIZA_SMOKE_OUTPUT_NAME}"\n' +
166+
' )\n' +
167+
' message(STATUS "Skipping Eliza DFlash JNI bridge for Android smoke build")\n' +
168+
' return()\n' +
169+
'endif()\n\n';
170+
writeFileSync(cmakeLists, current.replace(marker, skipBlock + marker));
171+
}

0 commit comments

Comments
 (0)