Skip to content

Promote dev → main: account recovery and SDK fixes - #1302

Merged
ducnmm merged 26 commits into
mainfrom
dev
Oct 26, 2026
Merged

ducnmm merged 26 commits into
mainfrom
dev

Conversation

@ducnmm

@ducnmm ducnmm commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Summary (external facing)

Promote dev to main, including the dashboard recovery fix for inactive accounts after affected-key cleanup, Python SDK correctness fixes, improved chatbot memory relevance, and updated public documentation.

Changes

This PR follows the current dev branch.

Release prerequisites before merging into main:

Test plan

  • Dashboard recovery fix: app suite passed (22 files, 148 tests), app TypeScript check passed, SDK build passed.
  • Reviewed main...dev commit list and changed-file scope.
  • Promotion PR code/build/E2E CI passed on a5411f8a; live benchmark rerun also passed after dev rollout verification.

Changelog

  • Release note needed. Areas: app, python-sdk, chatbot, researcher, relayer, docs

Dev rollout validation

  • Deployed exact dev source a5411f8ae42a503a4c7cfdbae75210cb83d4d71a manually because relayer dev auto-deploy is disabled. Final deployment: 7d050e66-2762-4eb4-82a9-b3e44da99a10.

  • Public health confirms that commit with status=ok and write_ready=true. Python 0.2.1.dev0 publication and TS dev publication succeeded.

  • Startup hit an existing database migration advisory lock (872122/22) held by an idle pooled session. Read-only checks confirmed no index build and isolated dev/production databases; resetting only the idle dev session restored startup. Monitor the same startup phase during production rollout.

  • Live benchmark rerun (attempt 2) passed against the verified new dev deployment: https://github.com/MystenLabs/walrus-memory/actions/runs/40306869895

harrymove-ctrl and others added 21 commits October 23, 2026 17:03
* docs: remove docs/handover agent notes from the published docs tree

docs/handover/ held internal handover notes (security sweep reconcile and a
dependency review summary). Because the whole docs/ directory is published
to docs.wal.app/walrus-memory, these were exposed publicly. They were never
listed in docs.json navigation and nothing else in the repo links to them.

* chore(status): remove stale HANDOFF.md agent note

apps/status/HANDOFF.md was a WALM-99 session handoff (Linear ids, assignee
names, branch and commit state, 'merge still pending'). The status app is
already on dev, so the note is stale, and nothing references it.
Merge main into dev so dev contains main's history. main has no content dev
lacks, so the tree is unchanged.
docs.wal.app imports docs/, so operator runbooks, CI setup, migration
ceremony, and Console integration notes were publicly reachable even though
most are not in docs.json navigation.

Move nine pages to docs-internal/ (relayer/benchmark-ci-setup,
relayer/synthetic-seal-cross-account, relayer/runbook-gas-pool,
migration/ceremony-environments, ops/security-rc-workflow,
ops/migration-completion-artifact, reference/console-identity-link,
reference/enoki-connect-requirements, api/owner-token-auth), drop
synthetic-seal-cross-account from the Relayer nav, and update the CI path
filter, script and Rust comments, and the one public page that pointed at
owner-token-auth.

Also remove the private Notion PRD link, the production benchmark account
row, and the private Linear link on WALM-451 from public pages.
…et ids from public prose

The repository is public, so moving the nine internal pages to docs-internal/
only hid them from docs.wal.app. Delete them and the folder, and update the
benchmark-smoke path filter and the comments that pointed at them.

Remove WALM-451, WALM-319, WALM-439 and BEDU-612 from reference prose in
docs/, SKILL.md and the troubleshooting source note. Changelogs and code
comments keep their ticket ids.
The owner-token-auth page was deleted as an internal page, so the freshness
check no longer found documentation for the registered /v1/owner-tokens route.
Console is the only caller, so list it in undocumentedByDesign.
* docs: unpublish internal operator pages (WALM-877)

docs.wal.app imports every file under docs/, including operator notes
that are not in the public nav. Move those pages to internal/ and drop
the benchmark account ids from the repo.

WALM-877

* ci: point benchmark triggers at the moved setup doc

The smoke workflow still listed docs/relayer/benchmark-ci-setup.md, so an edit to the moved page would not run it.
dev already moved the nine operator pages to internal/ (#1297), so keep that
layout and drop this branch's own moves, deletions and path edits. What
remains here is the ticket-id cleanup in public prose.
docs: drop internal ticket ids from public reference prose
account::create_account needs the AccountRegistry shared object, but
/config only reported packageId, so builders had to scrape the dashboard
bundle for it. Return the configured registry_id as registryId next to
packageId. Building the response moves into ConfigResponse::from_config
so it can be tested without an AppState.

Fixes #1152
WALM-835
getMemWalModel passed minRelevance: 0 to withMemWal, which disables the
SDK's 0.3 relevance filter, so all maxMemories hits were injected into
every prompt however unrelated they were. Drop the override so the SDK
default applies, and turn on middleware debug logging only outside
production.

Fixes #1099
WALM-813
.gitignore listed env files one by one, so .env.staging, .env.dev,
.env.development and .env.ci were not ignored. Ignore .env and .env.* at
any depth and keep the !.env.example exception.

Fixes #971
WALM-674
…taging/main (#683)

* ci(openclaw): run the plugin test suite in CI

The plugin has two test files and neither ran anywhere. #668 added
test/security.test.mjs for the GH #639/#640 injection work and #662 added
test/plugin.test.mjs, but `test.yml` never referenced oc-memwal, so both
only ran if someone typed the pnpm filter by hand. The regression tests
guarding a security fix were not guarding anything.

Adds an OpenClaw Plugin / Unit tests job in the same shape as the Noter one:
install, build the SDK because the plugin depends on it through the
workspace, then run the package's own test script.

That script is `tsc && node --test`, so a type error fails the job too, and
the 30 cases now hold the line on every defect found by running the plugin
against a real gateway: the manifest must not mark credential fields
required or `plugins install` deadlocks, the agent tools must stay declared
in contracts.tools or neither registers, relayer calls must keep their
deadline, and the injection filter must not regress in either direction.

* test(openclaw): commit the E2E harnesses and gate them to staging and main

The E2E work lived in a gitignored scratch directory, so nobody but me could
run it and CI could not run it at all. Moves it into the package as two
suites split by what they cost.

mock-relayer.test.mjs serves a broken relayer from the test process: rate
limiting, 5xx, and a socket held open with no reply. No credentials, no
network, no cost. The hang case is the point of it. Before the request
deadline existed a quiet relayer left the recall hook pending forever and the
turn never completed, and an unreachable hostname does not reproduce that
because DNS fails fast.

live-relayer.test.mjs runs against a real relayer and skips itself unless
credentials are present, so it stays safe locally and in any job without
secrets. Writes are separately opt-in behind MEMWAL_E2E_WRITE because Walrus
is append-only with no per-blob delete: every write is permanent and costs gas
plus storage. They land in a throwaway e2e-<timestamp> namespace so they can
never touch `default`. Authorisation is checked with a signed recall rather
than /health, which is unauthenticated and answers even for a revoked key.

CI gets an OpenClaw Plugin / E2E job restricted to pushes on staging and main,
never per PR, and pointed at the staging relayer even on a main merge since
there is no reason to leave test data on mainnet. Missing secrets degrade to
skips rather than failures, so the job can land before the secrets exist.

The unit glob narrows to test/*.test.mjs so the per-PR job does not pick up
E2E, with test:e2e covering test/e2e/*.test.mjs.

One detail worth recording: the hang case first took 301s despite the deadline
firing in 1.5s. withTimeout races the request rather than aborting it, since
recall() builds its own AbortController and takes no external signal, so the
abandoned fetch held its socket and server.close() waited on it. The mock now
force-closes connections. That socket leak is real in production too, and
fixing it properly means threading an AbortSignal through the SDK.

* review: fail the mock relayer on /api, not on the version preflight

The SDK calls GET /version before recall and analyze. The mock now serves that metadata and records only /api hits. E2E secrets stay on the E2E step.

---------

Co-authored-by: Harry Phan <phanhoangvinhhien@gmail.com>
…client (#1269)

The default MemWal client imports @mysten/seal and @mysten/sui on
recall() to build the Seal session, and package.json lists both as
required peers, but the quick starts and the API reference said they
were only needed for MemWalManual. With pnpm, yarn or legacy-peer-deps
that meant recall() failed with "Cannot find package '@mysten/seal'".
Install both with the SDK and keep @mysten/walrus as the manual-only
extra.

Fixes #993
WALM-689
docs/contract/overview.md, self-hosting.md and apps/app/.env.example
already list the current mainnet package 0xe7c16f... and registry
0x8bf82c..., but llms-full.txt still gave the retired mainnet pair (and
the retired testnet pair), so an account created from it lands under a
package the production relayer does not serve. Copy the IDs from
docs/contract/overview.md.

Fixes #1032
WALM-714
…LM-701) (#1265)

* fix(python-sdk): do not rewrite an empty namespace to the default

Every call resolved its namespace with namespace or self._namespace, so
namespace="" was written to the default partition, while the TypeScript
SDK sends "" and the relayer rejects it. Use the client default only for
None and raise ValueError("namespace cannot be empty") for an empty or
blank string, before anything is signed. MemWalMock.recall gets the same
check; its store path already had it.

Fixes #1012
WALM-701

* fix(python-sdk): stop rejecting whitespace namespaces

The relayer's validate_namespace refuses only "", a NUL byte and over-long
names, and keeps " ", tab, newline and CR namespaces readable because they
are valid Postgres text. The .strip() check in _resolve_namespace and the
mock blocked partitions the TypeScript SDK (namespace ?? default) can still
read and write. Only "" is rejected client-side now, with the same message
the relayer and the TypeScript mock use; None still means the client default.

Test doubles now accept the full _signed_request signature, the capture
double answers remember and bulk-remember so every call path is covered, and
new tests assert whitespace namespaces are sent unchanged.

WALM-701
…ing (WALM-854) (#1264)

* fix(python-sdk): reject non-finite vectors and bad limits before signing

remember_manual/recall_manual passed vectors straight to json.dumps, which
writes bare NaN/Infinity tokens that are not JSON, so the relayer failed
with a parse error. recall, recall_manual, restore and list_namespaces
also sent limit=NaN, -5 or ?limit=nan. Raise ValueError naming the
argument for a vector that is not a non-empty list of finite numbers and
for a limit that is not a positive integer, and sign bodies with
allow_nan=False as a backstop.

Fixes #1218
WALM-854

* fix(python-sdk): validate list_namespaces limit before owner lookup and in the mock

list_namespaces resolved the owner (a signed POST /api/stats on a fresh
client) before checking the limit, so a bad limit signed and sent a request
and could surface as a transport error instead of ValueError. Check it
first. MemWalMock.recall, restore and list_namespaces now use the same
positive-integer check as the real client, so 0, True and negative limits
no longer behave differently under the mock.

Test doubles now accept the full _signed_request signature, and a new
double keeps the real _resolve_owner so the test fails if signing happens
before the ValueError.

WALM-854
…s (WALM-860) (#1253)

* fix(sdk): name malformed 2xx response shapes instead of raw TypeErrors

value's shape was still trusted, so valid JSON of the wrong shape
escaped as a raw TypeError:

- getRememberStatus / waitForRememberJob: a primitive or null body
  crashed the `in` check; it now degrades to not_found like an object
  without status already did.
- waitForRememberJobs: a bulk-status body without a results array now
  raises a named, non-retried error instead of 'not iterable'.
- recall, analyzeAndWait, listMemories: a missing results / job_ids /
  memories array raises a named error regardless of maxDistance,
  maxTokens, or namespace options.

Fixes #1247

* chore(sdk): prepare 0.1.10 release

SDK 0.1.9 is already published on npm, so a promotion without a new version skips the stable publish. Bump packages/sdk to 0.1.10, add the 0.1.10 section to both SDK changelogs, and move the TypeScript pin in the release verifier.
#1252)

* fix(sdk): reject non-finite scoring weights in recallManual

#1228 guarded recall() against NaN/Infinity scoring weights, but
MemWal.recallManual and MemWalManual.recallManual serialize the same
option unchecked, so JSON.stringify sent {"semantic": null} in the
signed body. Hoist the finiteness check into a shared
assertFiniteScoringWeights helper and call it from all three sites.

Fixes #1246

* chore(sdk): prepare 0.1.10 release

SDK 0.1.9 is already published on npm, so a promotion without a new version skips the stable publish. Bump packages/sdk to 0.1.10, add the 0.1.10 section to both SDK changelogs, and move the TypeScript pin in the release verifier.
@railway-app
railway-app Bot temporarily deployed to Walrus Memory / dev October 26, 2026 09:17 Inactive
…lankText (Fixes #1156) (#1177)

* fix(sdk): reject zero-width space and format characters in assertNonBlankText (Fixes #1156)

* review: reject zero-width remember text in the mock

MemWalMock.remember("\u200B") used to succeed. It now fails with the same empty-text error as blank text.

* docs(sdk): stop claiming the relayer rejects format-only text

trim already rejects U+FEFF. The relayer still accepts U+200B, U+200C, U+200D, and U+2060.

* chore(sdk): cut TypeScript 0.1.10 for the zero-width text check

dev and main already publish 0.1.9, so a runtime validation change under
that version would skip stable publication. Bump the SDK to 0.1.10, add the
0.1.10 notes to both changelogs, and move the verifier pin to match.

---------

Co-authored-by: harrymove-ctrl <267878750+harrymove-ctrl@users.noreply.github.com>
@railway-app
railway-app Bot temporarily deployed to Walrus Memory / dev October 26, 2026 09:18 Inactive
@ducnmm
ducnmm deployed to benchmark-dev October 26, 2026 09:25 — with GitHub Actions Active
@HoangDucBach
HoangDucBach deployed to benchmark-dev October 26, 2026 09:43 — with GitHub Actions Active
#1240)

* fix(relayer): rebuild a register cancelled by shared-object congestion

A prepared register stays in the 60s success flight. When Sui cancels
that digest, the step loop treats the failure as a rebuild and then
rejoins the cached bytes, so every retry replays the dead transaction
until the step budget is gone. Drop that prepare flight once the digest
is finalized as a failure or its sponsorship is dead.

* fix(relayer): let a congestion rebuild use a new nonce and step

* test(relayer): drop the step-budget check that never runs the loop

* fix(relayer): treat only a congestion cancel as a dead register

A finalized register failure now clears the journal only when Sui reports
CongestedObjects. Every other failure keeps the prepared bytes so the next
attempt finds the digest instead of signing a second register. The rebuild
mixes the cancelled digest into the address-balance nonce so the replacement
is a different transaction.

* test(relayer): assert the congestion rebuild request carries the dead digest
@ducnmm
ducnmm deployed to benchmark-dev October 26, 2026 10:17 — with GitHub Actions Active
@ducnmm
ducnmm deployed to benchmark-dev October 26, 2026 10:17 — with GitHub Actions Active
@railway-app
railway-app Bot temporarily deployed to Walrus Memory / dev October 26, 2026 10:17 Inactive
)

* fix(python-sdk): preserve save jobs when confirmation is cancelled

* chore(python-sdk): prepare 0.2.1 release with save confirmation fixes

* test(python-sdk): make cancellation assertion explicit
@ducnmm
ducnmm deployed to benchmark-dev October 26, 2026 10:22 — with GitHub Actions Active
@railway-app
railway-app Bot temporarily deployed to Walrus Memory / dev October 26, 2026 10:22 Inactive
@ducnmm
ducnmm deployed to benchmark-dev October 26, 2026 10:23 — with GitHub Actions Active
@ducnmm
ducnmm deployed to benchmark-dev October 26, 2026 10:45 — with GitHub Actions Active
@ducnmm
ducnmm merged commit 5bf3a2f into main Oct 26, 2026
66 checks passed

This branch was successfully deployed

1 active and 1 inactive deployments
benchmark-dev — a5411f8a Deployed Oct 26, 2026 by ducnmm via Memory API Latency #498
Walrus Memory / dev — a5411f8a Deployed Oct 26, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants