Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ updates:
directory: /
schedule:
interval: weekly
ignore:
# Pinned and patched speech runtime: upgrade deliberately, see docs/development.md.
- dependency-name: '@huggingface/transformers'
- dependency-name: onnxruntime-web
- dependency-name: onnxruntime-node
# typescript-eslint and astro check need TypeScript 6's JavaScript API;
# TypeScript 7 is installed separately as typescript-native.
- dependency-name: typescript
update-types: ['version-update:semver-major']
# Types track the oldest supported Node.js (22.18), not the newest.
- dependency-name: '@types/node'
update-types: ['version-update:semver-major']
groups:
npm:
patterns: ['*']
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to Echo are documented here. The format follows [Keep a Chan

## [Unreleased]

- Changed: the speech runtime is now Transformers.js 4.3.2 with ONNX Runtime 1.30 (native) and 1.31 (browser). Word timings and transcripts were checked against the previous runtime on real speech, and models still load only from their pinned versions, including offline.

## [1.0.0] - 2026-11-01

The first public release: Echo as an open-source, self-hosted product.
Expand Down
7 changes: 7 additions & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ Markdownlint uses `.markdownlint-cli2.jsonc` for root `*.md` and `docs/**/*.md`,

Use Node.js 22.18 or newer, which runs `.ts` files through built-in type stripping. Browser, extension, script, and test projects extend `tsconfig.base.json`; relative TypeScript imports include `.ts`. TypeScript 6.0.3 provides the JavaScript API for editors and tooling, while `typescript-native` pins 7.0.2 for `pnpm typecheck`; `pnpm typecheck:compat` checks compiler drift. Both scripts use explicit compiler paths because both packages publish a `tsc` binary. Both compilers require declarations for cross-project references, so solution builds emit declarations and build metadata under `node_modules/.cache/tsbuild/`; esbuild owns JavaScript assets.

**The speech runtime is pinned and patched.** `@huggingface/transformers` is pinned to 4.3.2, and `onnxruntime-web` and `onnxruntime-node` to the exact versions it depends on, so one copy of each runtime is installed and `scripts/copy-wasm.ts` ships the WebAssembly that matches the bundled JavaScript. Two 4.3.2 defects are worked around:

- `patches/@huggingface__transformers@4.3.2.patch` (applied by pnpm through `patchedDependencies` in `pnpm-workspace.yaml`) makes `pipeline()`, `AutoTokenizer` and the processor lookup pass `revision`, `cache_dir` and `local_files_only` to the files they probe. Unpatched, they probe the repository's `main` branch, which fetches unpinned files and breaks loading offline.
- `web/whisper-alignment.ts` restores 3.8.1's word timestamps: word mode does not predict timestamp tokens, and token times come from a port of 3.8.1's DTW, cropped to the real audio. 4.3.2's own word timestamps are one token late and hallucinate text at chunk boundaries.

Dependabot ignores these three packages. To upgrade them, drop or re-create the patch, then run real Whisper on real speech (in Node and in Chromium) with the current and new versions and compare the word timings before merging. Unit tests stub the models, so they cannot catch alignment regressions.

Tests are grouped into `tests/unit/`, `tests/integration/`, and `tests/e2e/`, with shared fixtures in `tests/helpers/`. Run one unit test with `node --test tests/unit/foo.test.ts`. Integration files run serially to isolate server and extension fixtures; `scripts/test-browser.ts` preserves the browser suite's defined order. Scripts and tests execute directly as TypeScript; generated browser bundle names remain unchanged.

## Design tokens, fonts and icons
Expand Down
2 changes: 2 additions & 0 deletions docs/licenses.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The runtime JavaScript dependencies:
| `onnxruntime-node` | MIT | The native runtime for Whisper Large V3 |
| `@scure/bip39` | MIT | The recovery phrase word list |

`web/whisper-alignment.ts` contains code ported from Transformers.js 3.8.1 (Copyright Hugging Face, Apache-2.0), and `patches/` holds a patch to Transformers.js 4.3.2 under the same licence.

Development-only packages (build tools, linters, test runners) are not shipped with Echo. CI runs `pnpm audit --prod` on every push.

## Fonts and icons
Expand Down
9 changes: 3 additions & 6 deletions extension/src/pairing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ async function showApprovalTab(link: Waiting): Promise<void> {
}
async function closeApprovalTab(requestId: string): Promise<void> {
const tab = (await chrome.storage.session.get('approvalTab'))['approvalTab'] as
| ApprovalTab
| undefined;
ApprovalTab | undefined;
if (tab?.requestId !== requestId) return;
await chrome.storage.session.remove('approvalTab');
const current = await chrome.tabs.get(tab.id).catch(() => undefined); // Closed by the user.
Expand Down Expand Up @@ -283,8 +282,7 @@ interface ConnectRequest {
}
export async function pendingConnect() {
const pending = (await chrome.storage.session.get('connectRequest'))['connectRequest'] as
| ConnectRequest
| undefined;
ConnectRequest | undefined;
if (!pending) return undefined;
const link = await readLink();
return {
Expand All @@ -295,8 +293,7 @@ export async function pendingConnect() {
}
export async function confirmConnect(id: string, previous?: string) {
const pending = (await chrome.storage.session.get('connectRequest'))['connectRequest'] as
| ConnectRequest
| undefined;
ConnectRequest | undefined;
const view = await pendingConnect();
if (!pending || !view || pending.id !== id || view.previous !== previous)
throw new Error('The connection request changed. Review it and try again.');
Expand Down
3 changes: 1 addition & 2 deletions extension/src/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ async function refreshState() {
if (state.recoveryError) showError(new Error(state.recoveryError));
if (!initialized) {
const saved = (await chrome.storage.local.get('onboardingChoices'))['onboardingChoices'] as
| Partial<typeof choices>
| undefined;
Partial<typeof choices> | undefined;
choices = { callOnly: saved?.callOnly === true };
initialized = true;
}
Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,26 @@
"lint:md": "markdownlint-cli2"
},
"dependencies": {
"@huggingface/transformers": "3.8.1",
"@scure/bip39": "2.2.0",
"onnxruntime-node": "1.21.0",
"onnxruntime-web": "1.22.0-dev.20250409-89f8206ba4"
"@huggingface/transformers": "4.3.2",
"@scure/bip39": "2.4.0",
"onnxruntime-node": "1.30.0",
"onnxruntime-web": "1.31.0-dev.20260914-8d85527a0"
},
"devDependencies": {
"@axe-core/playwright": "^4.13.0",
"@eslint/js": "10.0.1",
"@playwright/test": "^1.55.0",
"@types/chrome": "0.3.7",
"@types/node": "22.18.0",
"esbuild": "^0.25.0",
"@types/node": "22.20.5",
"esbuild": "^0.28.2",
"eslint": "10.14.0",
"eslint-config-prettier": "10.1.8",
"globals": "17.13.0",
"jiti": "2.7.0",
"knip": "6.44.0",
"lucide-static": "1.66.0",
"markdownlint-cli2": "0.24.0",
"prettier": "3.6.2",
"prettier": "3.9.10",
"stylelint": "17.16.0",
"stylelint-config-standard": "40.0.0",
"typescript": "6.0.3",
Expand Down
Loading
Loading