Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
# -----------------------------------------------------------------------------

# AGENTMEMORY_AUTO_COMPRESS=true # Run LLM compression on every observation batch (requires a provider key). Default off — synthetic compression handles most cases.
# VIEWER_LANGUAGE=en # Viewer UI language. Built-in: en, de, zh. Falls back to en for missing keys. New languages ship via PR (src/viewer/locales/<lang>.json) — see CONTRIBUTING.md.
# AGENTMEMORY_INJECT_CONTEXT=true # Inject recalled memories back into agent prompts (#143). Default off — hooks capture observations but do not modify conversation.
# CONSOLIDATION_ENABLED=true # Run the 4-tier consolidation pipeline (memories → semantic → procedural). Default off — opt in once you've measured the LLM cost.
# CONSOLIDATION_DECAY_DAYS=30 # Age (days) after which non-reinforced memories decay during consolidation
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

### Added

- Viewer i18n: `VIEWER_LANGUAGE` env switches viewer UI between shipped locales (`en`, `de`, `zh`). New locales ship via PR with JSON files under `src/viewer/locales/`. Closes #483.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align changelog updates with the documented release policy.

Line 11 adds a CHANGELOG entry in a feature PR, while the repo contribution policy says CHANGELOG edits should land in release PRs only. Please move this note to the release PR flow (or update the policy if practice has changed).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 11, The CHANGELOG entry "Viewer i18n: `VIEWER_LANGUAGE`
env switches viewer UI..." was added in a feature PR but per the repo
contribution policy changelog edits must land in release PRs; remove this line
from the current PR (revert the CHANGELOG.md addition) and instead add the entry
to the upcoming release PR's changelog section (or update the contribution
policy if you intend to change practice), ensuring the same text is used when
moved.


## [0.9.22] — 2026-05-26

Stability + ecosystem wave. Three install-broken bugs (`npm install` ERESOLVE, non-OpenAI base URLs, broken Claude bridge path) closed. Six runtime bugs from active users fixed end-to-end. Three new agent integrations (Qwen Code, Antigravity, Kiro). New `AGENT_ID` scope for multi-agent setups. Port mapping documented.
Expand Down
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ If it's a feature: describe the user problem before the implementation. "I could
- Address review feedback in new commits (do not force-push to the same branch). Maintainers may squash on merge.
- A maintainer will merge when tests pass, CodeRabbit is green, and any review comments are addressed.

## Contributing a translation

1. Copy `src/viewer/locales/en.json` to `src/viewer/locales/<lang>.json` (e.g. `fr.json`, `zh.json`, `ja.json`).
2. Translate the values. Keep the keys, nesting, and `{placeholder}` markers identical to `en.json`.
3. `npm test` — the structural parity test in `test/viewer-i18n.test.ts` checks that your file has every nested leaf key from `en.json`.
4. Open a PR with `Closes` or `Refs` to any related i18n issue. Sign-off required (DCO).

Missing nested keys at runtime fall back to English, so partial translations are valid — but please aim for completeness.

## Developer Certificate of Origin

Every commit must carry a `Signed-off-by` trailer stating you have the right to submit the contribution under Apache-2.0. The full text of the DCO is at <https://developercertificate.org>.
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,17 @@ open http://localhost:3113

The viewer server binds to `127.0.0.1` by default. The REST-served `/agentmemory/viewer` endpoint follows the normal `AGENTMEMORY_SECRET` bearer-token rules. CSP headers use a per-response script nonce and disable inline handler attributes (`script-src-attr 'none'`).

### Viewer language

The viewer UI defaults to English. Switch it via `.env`:

```bash
# .env
VIEWER_LANGUAGE=zh
```

Restart agentmemory to apply. Built-in locales: `en`, `de`, `zh`. Missing keys fall back to English. New locales ship via PR against this repo — there is no drop-in location in packaged installs. To contribute, work from a source checkout: copy `src/viewer/locales/en.json` to `<lang>.json`, translate the values, and open a PR — no code changes required. See [Contributing a translation](CONTRIBUTING.md#contributing-a-translation).

---

<h2 id="iii-console"><picture><source media="(prefers-color-scheme: dark)" srcset="assets/tags/light/section-viewer.svg"><img src="assets/tags/section-viewer.svg" alt="iii Console" height="32" /></picture></h2>
Expand Down Expand Up @@ -1304,6 +1315,9 @@ Create `~/.agentmemory/.env`:
# CLAUDE_MEMORY_BRIDGE=false
# SNAPSHOT_ENABLED=false

# Viewer language
# VIEWER_LANGUAGE=en # Viewer UI language. Built-in: en, de, zh. Falls back to en for missing keys. New languages via PR (src/viewer/locales/<lang>.json), see CONTRIBUTING.md.

# Team
# TEAM_ID=
# USER_ID=
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"agentmemory": "dist/cli.mjs"
},
"scripts": {
"build": "tsdown && (cp iii-config.yaml dist/ 2>/dev/null || true) && (cp iii-config.docker.yaml dist/ 2>/dev/null || true) && (cp docker-compose.yml dist/ 2>/dev/null || true) && (cp .env.example dist/ 2>/dev/null || true) && mkdir -p dist/viewer && cp src/viewer/index.html dist/viewer/ && cp src/viewer/favicon.svg dist/viewer/",
"build": "tsdown && (cp iii-config.yaml dist/ 2>/dev/null || true) && (cp iii-config.docker.yaml dist/ 2>/dev/null || true) && (cp docker-compose.yml dist/ 2>/dev/null || true) && (cp .env.example dist/ 2>/dev/null || true) && mkdir -p dist/viewer/locales && cp src/viewer/index.html dist/viewer/ && cp src/viewer/favicon.svg dist/viewer/ && cp src/viewer/locales/*.json dist/viewer/locales/",
"dev": "tsx src/index.ts",
"start": "node dist/cli.mjs",
"migrate": "node dist/functions/migrate.js",
Expand Down
1 change: 1 addition & 0 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { timingSafeEqual, createHmac, randomBytes } from "node:crypto";

const hmacKey = randomBytes(32);
export const VIEWER_NONCE_PLACEHOLDER = "__AGENTMEMORY_VIEWER_NONCE__";
export const VIEWER_LOCALE_PLACEHOLDER = "__AGENTMEMORY_LOCALE__";

export function timingSafeCompare(a: string, b: string): boolean {
const hmacA = createHmac("sha256", hmacKey).update(a).digest();
Expand Down
11 changes: 10 additions & 1 deletion src/viewer/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import {
VIEWER_NONCE_PLACEHOLDER,
VIEWER_LOCALE_PLACEHOLDER,
createViewerNonce,
buildViewerCsp,
} from "../auth.js";
import { VERSION } from "../version.js";
import { buildLocaleBundle, resolveViewerLanguage } from "./locales.js";

const VIEWER_VERSION_PLACEHOLDER = "__AGENTMEMORY_VERSION__";

Expand Down Expand Up @@ -34,9 +36,16 @@ export function renderViewerDocument():
}

const nonce = createViewerNonce();
const bundle = buildLocaleBundle(resolveViewerLanguage());
// Escape < so a translation containing "</script" cannot break out
// of the inline <script>.
const localeJson = JSON.stringify(bundle).replace(/</g, "\\u003c");

const html = template
.replaceAll(VIEWER_NONCE_PLACEHOLDER, nonce)
.replaceAll(VIEWER_VERSION_PLACEHOLDER, VERSION);
.replaceAll(VIEWER_VERSION_PLACEHOLDER, VERSION)
.replaceAll(VIEWER_LOCALE_PLACEHOLDER, localeJson);

return {
found: true,
html,
Expand Down
Loading