@mysten-incubation/memwal-mcp 0.0.14-dev.1 → 0.0.14-dev.11

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 (56) hide show
  1. package/README.md +238 -1
  2. package/dist/auth-required.d.ts +105 -0
  3. package/dist/auth-required.d.ts.map +1 -1
  4. package/dist/auth-required.js +48 -14
  5. package/dist/auth-required.js.map +1 -1
  6. package/dist/auth.d.ts +179 -18
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/auth.js +458 -35
  9. package/dist/auth.js.map +1 -1
  10. package/dist/auto-save.d.ts +100 -0
  11. package/dist/auto-save.d.ts.map +1 -0
  12. package/dist/auto-save.js +290 -0
  13. package/dist/auto-save.js.map +1 -0
  14. package/dist/bridge.d.ts +10 -2
  15. package/dist/bridge.d.ts.map +1 -1
  16. package/dist/bridge.js +155 -25
  17. package/dist/bridge.js.map +1 -1
  18. package/dist/compatibility.d.ts.map +1 -1
  19. package/dist/compatibility.js +17 -2
  20. package/dist/compatibility.js.map +1 -1
  21. package/dist/consent.d.ts +36 -0
  22. package/dist/consent.d.ts.map +1 -0
  23. package/dist/consent.js +129 -0
  24. package/dist/consent.js.map +1 -0
  25. package/dist/crypto.d.ts.map +1 -1
  26. package/dist/crypto.js +13 -1
  27. package/dist/crypto.js.map +1 -1
  28. package/dist/health-probe.d.ts +35 -0
  29. package/dist/health-probe.d.ts.map +1 -0
  30. package/dist/health-probe.js +87 -0
  31. package/dist/health-probe.js.map +1 -0
  32. package/dist/index.d.ts +11 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +266 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/instructions.d.ts +17 -21
  37. package/dist/instructions.d.ts.map +1 -1
  38. package/dist/instructions.js +74 -12
  39. package/dist/instructions.js.map +1 -1
  40. package/dist/login.d.ts +15 -1
  41. package/dist/login.d.ts.map +1 -1
  42. package/dist/login.js +109 -9
  43. package/dist/login.js.map +1 -1
  44. package/dist/memory-policy.d.ts +38 -0
  45. package/dist/memory-policy.d.ts.map +1 -0
  46. package/dist/memory-policy.js +80 -0
  47. package/dist/memory-policy.js.map +1 -0
  48. package/dist/messages.d.ts +10 -3
  49. package/dist/messages.d.ts.map +1 -1
  50. package/dist/messages.js +16 -5
  51. package/dist/messages.js.map +1 -1
  52. package/dist/recovery.d.ts +1 -0
  53. package/dist/recovery.d.ts.map +1 -1
  54. package/dist/recovery.js +15 -13
  55. package/dist/recovery.js.map +1 -1
  56. package/package.json +1 -1
package/README.md CHANGED
@@ -23,6 +23,86 @@ Add Walrus Memory MCP to your MCP client config:
23
23
  }
24
24
  ```
25
25
 
26
+ ## How the plugin launches the server
27
+
28
+ The [MemWal plugin](https://memory.walrus.xyz/mcp/claude-code) does **not** use the
29
+ `npx` form above. `npx` resolves a package *name* against the directory the MCP
30
+ client was started in — your project — so a project that contains an installed
31
+ `@mysten-incubation/memwal-mcp` claiming the pinned version would be run instead of
32
+ the published one. Pinning the version in the `npx` command does not prevent that:
33
+ the planted package simply claims the pinned version.
34
+
35
+ Instead, every plugin launch config runs the plugin's launcher:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "memwal": {
41
+ "command": "node",
42
+ "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/launch_mcp.mjs"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ The launcher installs the pinned version once into a directory it owns and then
49
+ runs that absolute entry point with the current `node` binary:
50
+
51
+ ```
52
+ ~/.memwal/runtime/memwal-mcp@<version>/node_modules/@mysten-incubation/memwal-mcp/dist/bin/memwal-mcp.js
53
+ ```
54
+
55
+ It never consults your project's `node_modules` or a `PATH`-relative bin shim, and
56
+ it fails rather than falling back to the package name if the pinned version cannot
57
+ be installed. Everything after the script path is forwarded to the server
58
+ unchanged, so flags such as `--namespace work` or `--relayer <url>` work exactly as
59
+ they do above.
60
+
61
+ The runtime directory is trusted because of what it is, not how it is spelled:
62
+
63
+ - It must be outside the project the client started in. An absolute path is not
64
+ enough on its own — a client expands `${workspaceFolder}` to an absolute path
65
+ inside the repository, and a repository can commit a whole fake install there.
66
+ - It must be a real directory (not a symlink), owned by you, and not group- or
67
+ world-writable. The launcher creates it with mode `0700` and refuses to run code
68
+ out of it otherwise. If you see a refusal, `chmod 700 ~/.memwal ~/.memwal/runtime`.
69
+ - `MEMWAL_MCP_RUNTIME_DIR` moves it, subject to exactly the same rules.
70
+
71
+ The one-off install is run with `--ignore-scripts` (so no `preinstall` or
72
+ `postinstall` from the package or its dependencies executes), against an explicitly
73
+ pinned public registry, with the `npm_config_*` and `NODE_OPTIONS` environment
74
+ scrubbed for that spawn — npm ranks environment variables above every `.npmrc`, so
75
+ a client `env` block would otherwise choose the registry. npm itself is run as
76
+ `node <npm-cli.js>` resolved from the running node binary where that layout exists.
77
+
78
+ What that does **not** give you is an integrity check of the package contents: it
79
+ establishes where the code came from and that nobody else can write it, not that
80
+ the registry served the bytes a reviewer read. If you install from a private
81
+ registry, pre-populate the directory yourself (below) — the launcher then finds the
82
+ install and never runs npm at all.
83
+
84
+ If you configure MemWal without the plugin and want the same property, install the
85
+ version you intend to run into a directory outside any project and point your
86
+ client at its absolute path:
87
+
88
+ ```sh
89
+ mkdir -p ~/.memwal/runtime/memwal-mcp@0.0.14
90
+ chmod 700 ~/.memwal ~/.memwal/runtime
91
+ npm install --ignore-scripts --prefix ~/.memwal/runtime/memwal-mcp@0.0.14 \
92
+ @mysten-incubation/memwal-mcp@0.0.14
93
+ ```
94
+
95
+ ```json
96
+ {
97
+ "mcpServers": {
98
+ "memwal": {
99
+ "command": "node",
100
+ "args": ["/absolute/path/to/home/.memwal/runtime/memwal-mcp@0.0.14/node_modules/@mysten-incubation/memwal-mcp/dist/bin/memwal-mcp.js"]
101
+ }
102
+ }
103
+ }
104
+ ```
105
+
26
106
  ## Login
27
107
 
28
108
  Run the login flow manually:
@@ -38,6 +118,7 @@ The command opens your browser, asks you to connect your Sui wallet, and saves c
38
118
  ```sh
39
119
  memwal-mcp
40
120
  memwal-mcp login
121
+ memwal-mcp auto-save on|off
41
122
  memwal-mcp --logout
42
123
  memwal-mcp --help
43
124
  ```
@@ -52,9 +133,111 @@ Use CLI flags or environment variables to override the default Walrus Memory end
52
133
  | `--web-url <url>` | `MEMWAL_WEB_URL` | Override the web app URL used during login. |
53
134
  | `--label <text>` | `MEMWAL_CLIENT_LABEL` | Friendly delegate-key label shown in Walrus Memory. |
54
135
  | `--namespace <name>` (alias `--ns`) | `MEMWAL_NAMESPACE` | Default memory namespace applied when the agent omits one. |
136
+ | `auto-save on\|off` | `MEMWAL_AUTO_SAVE` | Whether the agent saves durable facts unprompted. On once you agree at login; see [Automatic Memory](#automatic-memory). |
55
137
 
56
138
  Enable verbose stderr logging with `MEMWAL_MCP_DEBUG=1`.
57
139
 
140
+ Set `MEMWAL_MCP_TRANSPORT=http` to dial the relayer's Streamable HTTP endpoint instead of the default SSE pair. Opt-in: reconnect replay is not transport-aware yet, so a write interrupted mid-send may be retried and duplicated.
141
+
142
+ ## Automatic Memory
143
+
144
+ MemWal saves durable facts — preferences, decisions, constraints, recurring
145
+ workflows — as you state them, without asking each time. That is what it is for,
146
+ so it is on. It asks you once first.
147
+
148
+ The question comes up in your terminal the first time you run `login`:
149
+
150
+ ```
151
+ MemWal can save things about you automatically.
152
+
153
+ What that means: when you state a preference, a decision, or a setting in
154
+ chat — "I prefer pnpm", "we deploy from dev", "the relayer is at X" —
155
+ MemWal writes it to your memory without asking each time, so it is there
156
+ in your next session and in every other client you use.
157
+
158
+ Before you choose:
159
+
160
+ - Saved memories are permanent. They go to Walrus, which is immutable
161
+ storage. You can stop saving new ones at any time, but you cannot
162
+ delete one that is already saved.
163
+ - They are encrypted to your account. Only your delegate key reads them.
164
+ - MemWal strips obvious credentials — API keys, tokens, passwords,
165
+ private keys — before saving. Treat that as a safety net, not a
166
+ guarantee: do not paste secrets into a session with this on.
167
+
168
+ [1] Save automatically recommended, this is what MemWal is for
169
+ [2] Only save when I ask nothing is saved unless you say "remember this"
170
+
171
+ Your choice [1/2]:
172
+ ```
173
+
174
+ **Saved memories are permanent.** Walrus is immutable storage: you can stop
175
+ saving new ones at any time, but you cannot delete one already saved — which is
176
+ why you are asked before it starts rather than after.
177
+
178
+ Until you answer, nothing is saved unprompted on a new install; "remember this"
179
+ and recall work throughout. If you were using MemWal before this question
180
+ existed, it keeps saving as it always has and asks you at your next login.
181
+
182
+ The question is only ever asked in a terminal. It is never an MCP tool and never
183
+ something the assistant can answer for you. Change it any time:
184
+
185
+ ```sh
186
+ npx -y @mysten-incubation/memwal-mcp auto-save on
187
+ npx -y @mysten-incubation/memwal-mcp auto-save off
188
+ npx -y @mysten-incubation/memwal-mcp auto-save # report the current setting
189
+ ```
190
+
191
+ The answer is stored as `{"autoSave": true}` in `settings.json` next to your
192
+ credentials file, so it follows the same project-local-beats-global resolution.
193
+ To pin one MCP client instead, set the environment variable — it overrides the
194
+ file and skips the question:
195
+
196
+ ```json
197
+ {
198
+ "mcpServers": {
199
+ "memwal": {
200
+ "command": "npx",
201
+ "args": ["-y", "@mysten-incubation/memwal-mcp"],
202
+ "env": { "MEMWAL_AUTO_SAVE": "1" }
203
+ }
204
+ }
205
+ }
206
+ ```
207
+
208
+ Recall is never gated, and neither is an explicit "remember this" — the setting
209
+ only decides whether the agent saves things you did not ask it to save. Saying
210
+ no costs nothing and is not asked about again.
211
+
212
+ ### What is never saved
213
+
214
+ Walrus storage is append-only and encrypted: a memory that lands **cannot be
215
+ edited or deleted**. So credentials are excluded whichever way you answer, by the same
216
+ rules stated in the server instructions, the tool descriptions and the plugin
217
+ hooks — and enforced by a check that runs before any text is sent:
218
+
219
+ - passwords, API keys, access and refresh tokens, private keys, seed and
220
+ recovery phrases, authorization headers, session cookies, and connection
221
+ strings or URLs with an embedded `user:password`;
222
+ - anything you say not to save ("don't save this", "off the record");
223
+ - pasted third-party content — a fenced block, a quoted passage — which is not
224
+ a fact about you.
225
+
226
+ When a message mixes a preference with a credential, the **preference is kept**
227
+ and only the credential is removed: "I prefer dark mode, db is
228
+ `postgres://admin:hunter2@db.internal/app`" is stored with the password gone and
229
+ the host intact. The agent is told which kinds were removed; the secret itself
230
+ is never stored, logged, or echoed back.
231
+
232
+ Detection targets specific credential shapes rather than "looks random", so
233
+ identifiers you *do* want remembered — blob ids, Sui object ids, commit SHAs,
234
+ digests — pass through untouched. Where a secret is indistinguishable from an
235
+ identifier, the **label** decides: pasting your `credentials.json` has its
236
+ `delegatePrivateKey` removed, while the same 64 hex characters with nothing
237
+ calling them a key are stored as the digest they look like. The trade-off is
238
+ that a secret in no recognisable shape, and with no label near it, can still
239
+ slip past the check — which is why the model-facing rules exist alongside it.
240
+
58
241
  ## Default Namespace
59
242
 
60
243
  By default the MCP tool schemas expose an optional `namespace` argument and the
@@ -146,12 +329,66 @@ You can also pass explicit URLs:
146
329
 
147
330
  ## Credential Storage
148
331
 
149
- Credentials are stored locally in `~/.memwal/credentials.json`. To remove them:
332
+ Credentials are stored locally in `~/.memwal/credentials.json`, and the
333
+ automatic-memory setting in `settings.json` beside it. To remove the
334
+ credentials:
150
335
 
151
336
  ```sh
152
337
  npx -y @mysten-incubation/memwal-mcp --logout
153
338
  ```
154
339
 
340
+ A sign-in that is still in flight also writes a `login-pending.json`
341
+ write-ahead record, beside `credentials.json` in `~/.memwal`. It holds the
342
+ delegate keypair minted for that sign-in, written before the browser can
343
+ register the public half on-chain so an interrupted login can be reclaimed
344
+ instead of paid for a second time. Same owner-only mode `0600` as
345
+ `credentials.json`.
346
+
347
+ It is removed once the sign-in completes, on `--logout`, and on a successful
348
+ recovery at the next start. A record older than 24 hours is discarded rather
349
+ than reused.
350
+
351
+ ### Per-project credentials
352
+
353
+ A project can keep its own `.memwal/credentials.json` so memory written from it
354
+ goes to a separate account. That file lives inside the repository, where anyone
355
+ who can commit to it — or who can get you to open a clone — could otherwise
356
+ choose the account and relayer your memories go to. So it is **ignored until you
357
+ approve it**, once per machine:
358
+
359
+ ```sh
360
+ cd path/to/project
361
+ npx -y @mysten-incubation/memwal-mcp approve-project
362
+ ```
363
+
364
+ Until then the global credentials are used, and a line on stderr names the file
365
+ that was skipped and the destination it wanted. An approval covers one exact
366
+ project path, account, delegate key and relayer: if any of those change, it has
367
+ to be approved again. The record is kept in `~/.memwal/project-approvals.json`,
368
+ outside the repository, so a repository cannot carry its own approval.
369
+ `revoke-project` withdraws it.
370
+
371
+ Approving also picks the file that is **written**: a later sign-in from that
372
+ project saves a delegate private key into `.memwal/credentials.json` in plain
373
+ text, inside the repository. `approve-project` says so, and so does the sign-in
374
+ warning. Add `.memwal/` to your `.gitignore`. The write-ahead record is the one
375
+ thing that stays out: a project sign-in keeps it in `~/.memwal/login-pending/`,
376
+ one file per approved project, so no key material lands in the checkout and a
377
+ sign-in is still reclaimable only by the project that started it.
378
+
379
+ `MEMWAL_CREDS_DIR` points the credentials, the approval record and the
380
+ write-ahead record at a directory of your choosing and overrides project
381
+ resolution entirely, with no approval. Because it skips the gate, it must be an
382
+ **absolute path outside the current project**: a relative value would resolve
383
+ against the working directory and put the approval record inside the
384
+ repository, and an MCP client passes on the `env` block it reads from
385
+ `.cursor/mcp.json` / `.vscode/mcp.json` / `.claude/settings.json` in the
386
+ checkout — where `${workspaceFolder}` is expanded, so an in-project absolute
387
+ path is not proof you chose it. Anything else is refused with an error naming
388
+ the value, rather than quietly ignored. An empty value means unset.
389
+
390
+ `memwal_health` reports the destination in use as `account=… relayer=…`.
391
+
155
392
  ## License
156
393
 
157
394
  Apache-2.0
@@ -67,6 +67,7 @@ export declare const ALL_TOOL_DEFINITIONS: ({
67
67
  query?: undefined;
68
68
  limit?: undefined;
69
69
  maxDistance?: undefined;
70
+ freshKey?: undefined;
70
71
  };
71
72
  required: string[];
72
73
  additionalProperties: boolean;
@@ -102,6 +103,7 @@ export declare const ALL_TOOL_DEFINITIONS: ({
102
103
  query?: undefined;
103
104
  limit?: undefined;
104
105
  maxDistance?: undefined;
106
+ freshKey?: undefined;
105
107
  };
106
108
  required: string[];
107
109
  additionalProperties: boolean;
@@ -142,6 +144,7 @@ export declare const ALL_TOOL_DEFINITIONS: ({
142
144
  query?: undefined;
143
145
  limit?: undefined;
144
146
  maxDistance?: undefined;
147
+ freshKey?: undefined;
145
148
  };
146
149
  additionalProperties: boolean;
147
150
  required?: undefined;
@@ -181,6 +184,7 @@ export declare const ALL_TOOL_DEFINITIONS: ({
181
184
  job_id?: undefined;
182
185
  job_ids?: undefined;
183
186
  waitMs?: undefined;
187
+ freshKey?: undefined;
184
188
  };
185
189
  required: string[];
186
190
  additionalProperties: boolean;
@@ -213,6 +217,7 @@ export declare const ALL_TOOL_DEFINITIONS: ({
213
217
  waitMs?: undefined;
214
218
  query?: undefined;
215
219
  maxDistance?: undefined;
220
+ freshKey?: undefined;
216
221
  };
217
222
  required: string[];
218
223
  additionalProperties: boolean;
@@ -237,6 +242,36 @@ export declare const ALL_TOOL_DEFINITIONS: ({
237
242
  query?: undefined;
238
243
  limit?: undefined;
239
244
  maxDistance?: undefined;
245
+ freshKey?: undefined;
246
+ };
247
+ additionalProperties: boolean;
248
+ required?: undefined;
249
+ };
250
+ } | {
251
+ name: string;
252
+ title: string;
253
+ annotations: {
254
+ readOnlyHint: boolean;
255
+ destructiveHint: boolean;
256
+ };
257
+ description: string;
258
+ inputSchema: {
259
+ type: string;
260
+ properties: {
261
+ freshKey: {
262
+ type: string;
263
+ default: boolean;
264
+ description: string;
265
+ };
266
+ text?: undefined;
267
+ namespace?: undefined;
268
+ facts?: undefined;
269
+ job_id?: undefined;
270
+ job_ids?: undefined;
271
+ waitMs?: undefined;
272
+ query?: undefined;
273
+ limit?: undefined;
274
+ maxDistance?: undefined;
240
275
  };
241
276
  additionalProperties: boolean;
242
277
  required?: undefined;
@@ -271,6 +306,7 @@ export declare const TOOL_DEFINITIONS: ({
271
306
  query?: undefined;
272
307
  limit?: undefined;
273
308
  maxDistance?: undefined;
309
+ freshKey?: undefined;
274
310
  };
275
311
  required: string[];
276
312
  additionalProperties: boolean;
@@ -306,6 +342,7 @@ export declare const TOOL_DEFINITIONS: ({
306
342
  query?: undefined;
307
343
  limit?: undefined;
308
344
  maxDistance?: undefined;
345
+ freshKey?: undefined;
309
346
  };
310
347
  required: string[];
311
348
  additionalProperties: boolean;
@@ -346,6 +383,7 @@ export declare const TOOL_DEFINITIONS: ({
346
383
  query?: undefined;
347
384
  limit?: undefined;
348
385
  maxDistance?: undefined;
386
+ freshKey?: undefined;
349
387
  };
350
388
  additionalProperties: boolean;
351
389
  required?: undefined;
@@ -385,6 +423,7 @@ export declare const TOOL_DEFINITIONS: ({
385
423
  job_id?: undefined;
386
424
  job_ids?: undefined;
387
425
  waitMs?: undefined;
426
+ freshKey?: undefined;
388
427
  };
389
428
  required: string[];
390
429
  additionalProperties: boolean;
@@ -417,6 +456,7 @@ export declare const TOOL_DEFINITIONS: ({
417
456
  waitMs?: undefined;
418
457
  query?: undefined;
419
458
  maxDistance?: undefined;
459
+ freshKey?: undefined;
420
460
  };
421
461
  required: string[];
422
462
  additionalProperties: boolean;
@@ -441,6 +481,36 @@ export declare const TOOL_DEFINITIONS: ({
441
481
  query?: undefined;
442
482
  limit?: undefined;
443
483
  maxDistance?: undefined;
484
+ freshKey?: undefined;
485
+ };
486
+ additionalProperties: boolean;
487
+ required?: undefined;
488
+ };
489
+ } | {
490
+ name: string;
491
+ title: string;
492
+ annotations: {
493
+ readOnlyHint: boolean;
494
+ destructiveHint: boolean;
495
+ };
496
+ description: string;
497
+ inputSchema: {
498
+ type: string;
499
+ properties: {
500
+ freshKey: {
501
+ type: string;
502
+ default: boolean;
503
+ description: string;
504
+ };
505
+ text?: undefined;
506
+ namespace?: undefined;
507
+ facts?: undefined;
508
+ job_id?: undefined;
509
+ job_ids?: undefined;
510
+ waitMs?: undefined;
511
+ query?: undefined;
512
+ limit?: undefined;
513
+ maxDistance?: undefined;
444
514
  };
445
515
  additionalProperties: boolean;
446
516
  required?: undefined;
@@ -475,6 +545,7 @@ export declare const SIGNED_OUT_TOOL_DEFINITIONS: ({
475
545
  query?: undefined;
476
546
  limit?: undefined;
477
547
  maxDistance?: undefined;
548
+ freshKey?: undefined;
478
549
  };
479
550
  required: string[];
480
551
  additionalProperties: boolean;
@@ -510,6 +581,7 @@ export declare const SIGNED_OUT_TOOL_DEFINITIONS: ({
510
581
  query?: undefined;
511
582
  limit?: undefined;
512
583
  maxDistance?: undefined;
584
+ freshKey?: undefined;
513
585
  };
514
586
  required: string[];
515
587
  additionalProperties: boolean;
@@ -550,6 +622,7 @@ export declare const SIGNED_OUT_TOOL_DEFINITIONS: ({
550
622
  query?: undefined;
551
623
  limit?: undefined;
552
624
  maxDistance?: undefined;
625
+ freshKey?: undefined;
553
626
  };
554
627
  additionalProperties: boolean;
555
628
  required?: undefined;
@@ -589,6 +662,7 @@ export declare const SIGNED_OUT_TOOL_DEFINITIONS: ({
589
662
  job_id?: undefined;
590
663
  job_ids?: undefined;
591
664
  waitMs?: undefined;
665
+ freshKey?: undefined;
592
666
  };
593
667
  required: string[];
594
668
  additionalProperties: boolean;
@@ -621,6 +695,7 @@ export declare const SIGNED_OUT_TOOL_DEFINITIONS: ({
621
695
  waitMs?: undefined;
622
696
  query?: undefined;
623
697
  maxDistance?: undefined;
698
+ freshKey?: undefined;
624
699
  };
625
700
  required: string[];
626
701
  additionalProperties: boolean;
@@ -645,6 +720,36 @@ export declare const SIGNED_OUT_TOOL_DEFINITIONS: ({
645
720
  query?: undefined;
646
721
  limit?: undefined;
647
722
  maxDistance?: undefined;
723
+ freshKey?: undefined;
724
+ };
725
+ additionalProperties: boolean;
726
+ required?: undefined;
727
+ };
728
+ } | {
729
+ name: string;
730
+ title: string;
731
+ annotations: {
732
+ readOnlyHint: boolean;
733
+ destructiveHint: boolean;
734
+ };
735
+ description: string;
736
+ inputSchema: {
737
+ type: string;
738
+ properties: {
739
+ freshKey: {
740
+ type: string;
741
+ default: boolean;
742
+ description: string;
743
+ };
744
+ text?: undefined;
745
+ namespace?: undefined;
746
+ facts?: undefined;
747
+ job_id?: undefined;
748
+ job_ids?: undefined;
749
+ waitMs?: undefined;
750
+ query?: undefined;
751
+ limit?: undefined;
752
+ maxDistance?: undefined;
648
753
  };
649
754
  additionalProperties: boolean;
650
755
  required?: undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-required.d.ts","sourceRoot":"","sources":["../src/auth-required.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAiLzE;;;;;;;;;;;;;;0EAc0E;AAC1E,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAMH;;0EAE0E;AAC1E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA6B,CAAC;AAS/D;;4CAE4C;AAC5C,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAuB,CAAC;AAErD;;8BAE8B;AAC9B,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAwB,CAAC;AA2CjE;;;mCAGmC;AACnC,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;4DAIwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAmID;;yCAEyC;AACzC,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,iBAAiB,CAAC;IACzB;;sCAEkC;IAClC,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B;AAwID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACvC,MAAM,EAAE,kBAAkB,GAC3B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgE7B"}
1
+ {"version":3,"file":"auth-required.d.ts","sourceRoot":"","sources":["../src/auth-required.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AA2MzE;;;;;;;;;;;;;;0EAc0E;AAC1E,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAOrD,CAAC;AAMH;;0EAE0E;AAC1E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA6B,CAAC;AAS/D;;4CAE4C;AAC5C,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAuB,CAAC;AAErD;;8BAE8B;AAC9B,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAwB,CAAC;AA+CjE;;;mCAGmC;AACnC,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd;;;;4DAIwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AA6ID;;yCAEyC;AACzC,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,iBAAiB,CAAC;IACzB;;sCAEkC;IAClC,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B;AA4ID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,qBAAqB,CACvC,MAAM,EAAE,kBAAkB,GAC3B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAgE7B"}
@@ -25,11 +25,24 @@ import { credsPath, loadCreds } from "./auth.js";
25
25
  import { rememberInitializeClientInfo } from "./client-info.js";
26
26
  import { loginFailureNotice, loginPrompt, loginSuccessNotification } from "./messages.js";
27
27
  import { log } from "./logger.js";
28
- import { startOrReuseLoginFlow, resolveLoginTimeoutMs } from "./login.js";
28
+ import { startOrReuseLoginFlow, resolveLoginTimeoutMs, isLoginSuperseded } from "./login.js";
29
29
  import { AUTH_REQUIRED_INSTRUCTIONS } from "./instructions.js";
30
+ import { SECRET_EXCLUSION_RULES, SECRET_EXCLUSION_SUMMARY, AUTO_SAVE_OPT_IN_RULE, } from "./memory-policy.js";
30
31
  import { MEMWAL_MCP_VERSION } from "./version.js";
31
- const SIGNED_OUT_REMEMBER = "Save a fact to the user's Walrus Memory personal memory. Call ONLY when the user explicitly asks to remember/save something. Pass the full, detailed text — never summarize.";
32
- const SIGNED_IN_REMEMBER = "Save a durable fact about the user or project to their Walrus Memory. Call this PROACTIVELY whenever the user states a preference, decision, constraint, correction, identity detail, or recurring workflow — even if they did not say 'remember this'. Skip one-off tasks, the current file or bug, and small talk. Pass the full statement; do not summarize. To save several facts at once, use memwal_remember_bulk instead. By default this returns in ~1s once the relayer has accepted the job (job_id) — the Walrus write is still in flight and the fact is NOT stored yet. Do not claim it is saved. Settle it with the job-status tool this server advertises (re-list tools if you do not see one). A blob_id in the same reply means it already stored (optional wait budget).";
32
+ /**
33
+ * WALM-642: every write-tool description below ends with the shared policy
34
+ * block, the same text the `instructions` field and the plugin hooks carry, so
35
+ * an agent that only ever sees one of the three still gets the same rules.
36
+ * The signed-out variants get the one-line summary — they exist to keep a
37
+ * credential-less model from spamming writes, and the full block would be the
38
+ * longest thing in a list of tools that cannot run yet.
39
+ */
40
+ const SIGNED_OUT_REMEMBER = "Save a fact to the user's Walrus Memory personal memory. Call ONLY when the user explicitly asks to remember/save something. Pass the full, detailed text — never summarize. " +
41
+ SECRET_EXCLUSION_SUMMARY;
42
+ const SIGNED_IN_REMEMBER = "Save a durable fact about the user or project to their Walrus Memory. Call this PROACTIVELY whenever the user states a preference, decision, constraint, correction, identity detail, or recurring workflow — even if they did not say 'remember this' — provided they have turned automatic memory on. Skip one-off tasks, the current file or bug, and small talk. Pass the full statement; do not summarize. To save several facts at once, use memwal_remember_bulk instead. By default this returns in ~1s once the relayer has accepted the job (job_id) — the Walrus write is still in flight and the fact is NOT stored yet. Do not claim it is saved. Settle it with the job-status tool this server advertises (re-list tools if you do not see one). A blob_id in the same reply means it already stored (optional wait budget). " +
43
+ AUTO_SAVE_OPT_IN_RULE +
44
+ " " +
45
+ SECRET_EXCLUSION_RULES;
33
46
  const SIGNED_OUT_RECALL = "Search the user's Walrus Memory for facts relevant to a query. Returns matching memories ranked by relevance.";
34
47
  const SIGNED_IN_RECALL = "Search the user's Walrus Memory for relevant facts before responding. Call this PROACTIVELY at the start of a task, or whenever the user references past work, prior decisions, their preferences, or anything you may have stored earlier — don't wait to be asked. A single focused query is usually enough — recall is a real retrieval over encrypted storage, so do NOT fire multiple redundant searches for the same question. Returns matching memories ranked by relevance.";
35
48
  /** Build the static memory-tool list. `proactive` is true for the signed-in
@@ -56,7 +69,8 @@ function buildToolDefinitions(proactive) {
56
69
  name: "memwal_remember_bulk",
57
70
  title: "Remember Multiple Facts",
58
71
  annotations: { readOnlyHint: false, destructiveHint: false },
59
- description: "Save multiple durable facts in one call. Use when you learned several distinct facts at once (onboarding details, a list of preferences, decisions from a discussion). Pass an array of complete fact statements (max 20) — do not summarize. Prefer this over repeated memwal_remember calls. By default this returns in ~1s once the relayer has accepted the batch (job_ids) — the Walrus writes are still in flight and the facts are NOT stored yet. Do not claim they are saved. Settle them with the job-status tool this server advertises (re-list tools if you do not see one). A blob_id in the same reply means that fact already stored (optional wait budget).",
72
+ description: "Save multiple durable facts in one call. Use when you learned several distinct facts at once (onboarding details, a list of preferences, decisions from a discussion). Pass an array of complete fact statements (max 20) — do not summarize. Prefer this over repeated memwal_remember calls. By default this returns in ~1s once the relayer has accepted the batch (job_ids) — the Walrus writes are still in flight and the facts are NOT stored yet. Do not claim they are saved. Settle them with the job-status tool this server advertises (re-list tools if you do not see one). A blob_id in the same reply means that fact already stored (optional wait budget). " +
73
+ (proactive ? AUTO_SAVE_OPT_IN_RULE + " " + SECRET_EXCLUSION_RULES : SECRET_EXCLUSION_SUMMARY),
60
74
  inputSchema: {
61
75
  type: "object",
62
76
  properties: {
@@ -125,7 +139,8 @@ function buildToolDefinitions(proactive) {
125
139
  name: "memwal_analyze",
126
140
  title: "Analyze and Remember",
127
141
  annotations: { readOnlyHint: false, destructiveHint: true },
128
- description: "Extract memorable facts from a longer passage of text (preferences, habits, biographical info, constraints) and save each as a separate Walrus Memory memory. Use this when you want MemWal's LLM to split the facts out of a transcript or notes for you; if you already know the exact facts, use memwal_remember or memwal_remember_bulk instead.",
142
+ description: "Extract memorable facts from a longer passage of text (preferences, habits, biographical info, constraints) and save each as a separate Walrus Memory memory. Use this when you want MemWal's LLM to split the facts out of a transcript or notes for you; if you already know the exact facts, use memwal_remember or memwal_remember_bulk instead. " +
143
+ (proactive ? AUTO_SAVE_OPT_IN_RULE + " " + SECRET_EXCLUSION_RULES : SECRET_EXCLUSION_SUMMARY),
129
144
  inputSchema: {
130
145
  type: "object",
131
146
  properties: {
@@ -169,7 +184,13 @@ function buildToolDefinitions(proactive) {
169
184
  description: "Sign this MCP client into your Walrus Memory account by opening a browser. Run once when the agent reports Walrus Memory is not signed in. Opens the dashboard in the default browser, waits for wallet approval, then writes credentials to ~/.memwal/credentials.json. Other memwal_* tools become usable on the next call after a successful login.",
170
185
  inputSchema: {
171
186
  type: "object",
172
- properties: {},
187
+ properties: {
188
+ freshKey: {
189
+ type: "boolean",
190
+ default: false,
191
+ description: "Mint a new delegate key instead of reusing the one a previous unfinished sign-in left pending. Leave false for an ordinary login. Set it only after a normal sign-in has already failed on the pending key — it abandons that key, which must then be removed from the dashboard if it was approved on-chain, and the new one needs its own add_delegate_key transaction.",
192
+ },
193
+ },
173
194
  additionalProperties: false,
174
195
  },
175
196
  },
@@ -239,13 +260,17 @@ const LOGIN_INSTRUCTION = [
239
260
  /** Replaces {@link LOGIN_INSTRUCTION} after a failed attempt, which
240
261
  * {@link loginFailureNotice} has just described. The generic copy promises "no
241
262
  * client restart" and leads with `memwal_login`; for a key the user already
242
- * approved, a restart is the only thing that recovers it and signing in again
243
- * cannot register it twice. So the retry is offered only for the case it
244
- * actually fixes. */
263
+ * approved, a restart is the only thing that recovers it and a plain retry
264
+ * reuses it rather than registering a second one. So the retry names
265
+ * `freshKey` for the case the plain one cannot fix. */
245
266
  const LOGIN_RETRY_INSTRUCTION = [
246
- "If you did not approve the wallet step, start a new sign-in: call the `memwal_login`",
247
- "tool from this client, or run `npx -y @mysten-incubation/memwal-mcp login`. Open the",
248
- "new link straight away and leave this client running through the wallet prompt.",
267
+ "Start a new sign-in: call the `memwal_login` tool from this client, or run",
268
+ "`npx -y @mysten-incubation/memwal-mcp login`. Open the new link straight away and",
269
+ "leave this client running through the wallet prompt.",
270
+ "",
271
+ "If that keeps failing on the same key, call `memwal_login` with `freshKey: true` to",
272
+ "mint a new one. That abandons the pending key: if you already approved it on-chain,",
273
+ "remove it from the dashboard afterwards.",
249
274
  ].join("\n");
250
275
  /** Set when a background `memwal_login` ends without credentials. The tool call
251
276
  * already returned the URL by then, so this is the only place left to say so. */
@@ -286,7 +311,7 @@ function sendLogMessage(level, text) {
286
311
  * issues any other memwal_* tool to verify — which now succeeds because
287
312
  * the bridge picks up the saved creds on its next call.
288
313
  */
289
- async function handleLoginToolCall(config, _progressToken) {
314
+ async function handleLoginToolCall(config, _progressToken, freshKey) {
290
315
  // Fire login but DO NOT await the wallet callback — it runs in the
291
316
  // background. openBrowser: false because (a) child-process spawning a
292
317
  // browser is unreliable across MCP clients, and (b) macOS `open <url>`
@@ -297,12 +322,17 @@ async function handleLoginToolCall(config, _progressToken) {
297
322
  // Concurrent memwal_login calls join this in-flight flow instead of
298
323
  // opening a second listener (that race hung later recall/remember).
299
324
  lastLoginFailure = null;
325
+ let discardedKey;
300
326
  const session = startOrReuseLoginFlow({
301
327
  relayerUrl: config.relayerUrl,
302
328
  webUrl: config.webUrl,
303
329
  label: config.label,
304
330
  timeoutMs: resolveLoginTimeoutMs(),
305
331
  openBrowser: false,
332
+ freshKey,
333
+ onDiscarded: (key) => {
334
+ discardedKey = key;
335
+ },
306
336
  onUrl: (url) => {
307
337
  sendLogMessage("info", `Walrus Memory MCP login URL: ${url}`);
308
338
  },
@@ -336,6 +366,9 @@ async function handleLoginToolCall(config, _progressToken) {
336
366
  url = await Promise.race([session.url, timeoutPromise]);
337
367
  }
338
368
  catch (err) {
369
+ if (isLoginSuperseded(err)) {
370
+ return { isError: true, text: "This sign-in was replaced by a newer memwal_login. Open that result instead of starting another login." };
371
+ }
339
372
  const msg = err instanceof Error ? err.message : String(err);
340
373
  log.error("memwal_login.tool.url_not_ready", { msg });
341
374
  return {
@@ -360,6 +393,7 @@ async function handleLoginToolCall(config, _progressToken) {
360
393
  url,
361
394
  credentialsPath: credsPath(),
362
395
  signedIn: loadCreds() !== null,
396
+ discardedKey,
363
397
  }),
364
398
  };
365
399
  }
@@ -433,7 +467,7 @@ function handleAuthLine(line, config) {
433
467
  // Returns near-instantly with the click-able URL. The listener
434
468
  // stays alive in the background — see handleLoginToolCall for the
435
469
  // rationale on not blocking.
436
- void handleLoginToolCall(config, progressToken).then((result) => {
470
+ void handleLoginToolCall(config, progressToken, params.arguments?.freshKey === true).then((result) => {
437
471
  writeStdoutMessage({
438
472
  jsonrpc: "2.0",
439
473
  id,