diff --git a/.changeset/walm-55-occurred-at-openclaw.md b/.changeset/walm-55-occurred-at-openclaw.md new file mode 100644 index 00000000..e98519b1 --- /dev/null +++ b/.changeset/walm-55-occurred-at-openclaw.md @@ -0,0 +1,14 @@ +--- +"@mysten-incubation/oc-memwal": patch +--- + +Wire temporal anchoring through the agent-side memory tools. + +### Added + +- `memory_store` tool now accepts an optional `occurredAt` argument (RFC-3339 / ISO-8601 string) so agents can anchor recounted past events to the date they actually occurred. Description tells the LLM to omit it when unknown rather than guess. + +### Changed + +- Auto-capture hook (`agent_end`) now passes `new Date()` as `occurredAt` to `analyze()`. Every captured conversation now gets temporal anchoring automatically — the server extractor resolves in-turn relative references ("yesterday", "last Friday") into absolute dates inside the stored fact text. Facts captured by this version now carry resolved dates. +- SDK dependency bumped from published `^0.0.2` to `workspace:*` to consume the new `AnalyzeOptions` signature. diff --git a/.changeset/walm-55-occurred-at-sdk.md b/.changeset/walm-55-occurred-at-sdk.md new file mode 100644 index 00000000..4c034cbf --- /dev/null +++ b/.changeset/walm-55-occurred-at-sdk.md @@ -0,0 +1,11 @@ +--- +"@mysten-incubation/memwal": patch +--- + +Add optional `occurredAt` to `analyze()` and `analyzeAndWait()` for temporal anchoring of extracted facts. + +- New `AnalyzeOptions` overload: `analyze(text, { namespace, occurredAt })` accepts `Date` or RFC-3339 string. The legacy `analyze(text, namespace?)` signature still works unchanged. +- When `occurredAt` is supplied, the server resolves in-turn relative references ("last Friday", "yesterday") into absolute dates inside the extracted fact text before embedding and encryption. +- Wire format is RFC-3339 UTC with millisecond precision (e.g. `"2023-05-25T17:50:00.000Z"`). +- Invalid `Date` instances (constructed from malformed input) now throw a diagnostic `TypeError` from the SDK rather than an opaque `RangeError` from `toISOString()`. +- Field is omitted from the request body when not supplied — existing callers see byte-identical wire payloads. diff --git a/README.md b/README.md index 29e4cd99..52c05680 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # Walrus Memory -Privacy-first AI memory layer for storing encrypted memories on Walrus and -retrieving them with semantic search. +Portable agent memory — take your agent's memory anywhere. + +Walrus Memory enables AI agents to operate reliably across apps and sessions, +without losing context. Portable, verifiable, and fully controlled by you, it's +the memory layer that lets agents handle complex workflows and coordinate using +data they can trust. > Walrus Memory is currently in beta and actively evolving. While fully usable today, we continue to refine the developer experience and operational guidance. We welcome feedback from early builders as we continue to improve the product. @@ -96,7 +100,7 @@ For the full step-by-step setup guide, see: ## OpenClaw / NemoClaw Plugin -[`@mysten-incubation/oc-memwal`](packages/openclaw-memory-memwal) — a memory plugin for [OpenClaw](https://openclaw.ai) agents. It gives OpenClaw persistent, encrypted memory via Walrus Memory with automatic recall and capture hooks. +[`@mysten-incubation/oc-memwal`](packages/openclaw-memory-memwal) — a memory plugin for [OpenClaw](https://openclaw.ai) agents. It gives OpenClaw agents portable, verifiable memory through Walrus Memory with automatic recall and capture hooks. ```bash openclaw plugins install @mysten-incubation/oc-memwal diff --git a/SKILL.md b/SKILL.md index d2559dbc..f3ca26a4 100644 --- a/SKILL.md +++ b/SKILL.md @@ -2,14 +2,14 @@ name: memwal version: 0.0.1 description: | - Walrus Memory SDK for privacy-first AI memory on Sui blockchain with Walrus. + Walrus Memory SDK — portable agent memory that works across apps, sessions, and workflows. Use when users say: - "add memory to my app" - - "store encrypted memories" + - "portable agent memory" - "integrate Walrus Memory" - "AI agent memory" - - "persistent memory SDK" + - "memory across agents" - "Walrus memory storage" - "setup Walrus Memory" - "recall memories" @@ -19,7 +19,7 @@ keywords: - walrus memory - memory sdk - ai memory - - encrypted memory + - portable memory - walrus storage - sui blockchain - delegate key @@ -27,9 +27,9 @@ keywords: - vercel ai sdk --- -# Walrus Memory — Privacy-First AI Memory SDK +# Walrus Memory — Portable Agent Memory -Walrus Memory is a TypeScript SDK for persistent, encrypted AI memory. It stores memories on Walrus (decentralized storage), encrypts them with SEAL, enforces ownership onchain via Sui smart contracts, and retrieves them with semantic (vector) search. Memories are scoped by `owner + namespace` — each namespace is an isolated memory space. +Walrus Memory enables AI agents to operate reliably across apps and sessions, without losing context. It stores memories on Walrus (decentralized storage), encrypts them with SEAL, enforces ownership onchain via Sui smart contracts, and retrieves them with semantic (vector) search. Memory is portable by design — not tied to a single runtime or provider — and scoped by `owner + namespace` for isolation and coordination. --- @@ -37,12 +37,12 @@ Walrus Memory is a TypeScript SDK for persistent, encrypted AI memory. It stores Use Walrus Memory when your app or agent needs: -- **Persistent memory** across sessions, devices, or restarts -- **Encrypted storage** — end-to-end encryption, only the owner and authorized delegates can decrypt +- **Portable memory** — persists outside prompts and context windows, moves across agents, apps, and workflows +- **Full owner control** — programmable permissions and explicit ownership define how memory is shared and accessed +- **Agent coordination** — shared memory spaces help agents coordinate across long-running and multi-step workflows - **Semantic recall** — retrieve memories by meaning, not just keywords -- **Decentralized storage** — no single point of failure, stored on Walrus -- **Onchain ownership** — cryptographically enforced access control on Sui -- **Cross-app memory** — share memory between apps via delegate keys +- **Verifiable integrity** — memory integrity can be independently verified without centralized trust +- **Cross-app memory** — not tied to a single runtime or provider, share memory between apps via delegate keys --- @@ -146,7 +146,7 @@ const stored = await memwal.waitForRememberJob(accepted.job_id, { ## API Surface -### MemWal Methods +### Walrus Memory Methods | Method | Description | Returns | |---|---|---| diff --git a/apps/app/.env.example b/apps/app/.env.example index c608bfb7..c7030e66 100644 --- a/apps/app/.env.example +++ b/apps/app/.env.example @@ -16,6 +16,8 @@ VITE_MEMWAL_SERVER_URL=https://relayer.dev.memwal.ai # Docs URL (separate deployment) VITE_DOCS_URL=http://localhost:5174 +VITE_TERMS_OF_SERVICE_URL=https://docs.wal.app/docs/legal/walrus_general_tos +VITE_PRIVACY_POLICY_URL=https://docs.wal.app/docs/legal/privacy # Demo app links (comma-separated, format: Label|URL) # Example: VITE_DEMO_URLS=Chat Demo|https://chat.example.com,Agent Demo|https://agent.example.com diff --git a/apps/app/src/config.ts b/apps/app/src/config.ts index 0ef49285..52429f52 100644 --- a/apps/app/src/config.ts +++ b/apps/app/src/config.ts @@ -23,6 +23,10 @@ export const config = { .split(',').map(s => s.trim()).filter(Boolean) as string[], sidecarUrl: import.meta.env.VITE_SIDECAR_URL as string || 'http://localhost:9000', docsUrl: import.meta.env.VITE_DOCS_URL as string || '', + termsOfServiceUrl: import.meta.env.VITE_TERMS_OF_SERVICE_URL as string || + 'https://docs.wal.app/docs/legal/walrus_general_tos', + privacyPolicyUrl: import.meta.env.VITE_PRIVACY_POLICY_URL as string || + 'https://docs.wal.app/docs/legal/privacy', gtmContainerId: import.meta.env.VITE_GTM_CONTAINER_ID as string || '', gaMeasurementId: import.meta.env.VITE_GA_MEASUREMENT_ID as string || '', posthogProjectApiKey: ( diff --git a/apps/app/src/pages/Dashboard.tsx b/apps/app/src/pages/Dashboard.tsx index a48cd0ed..1547b740 100644 --- a/apps/app/src/pages/Dashboard.tsx +++ b/apps/app/src/pages/Dashboard.tsx @@ -132,6 +132,19 @@ type RemoveKeysConfirmState = { source: 'single' | 'selection' } +// sanitize a key label — strip HTML special chars and control characters +function sanitizeLabelInput(raw: string): string { + return raw + // Strip HTML special characters + .replace(/[<>&"'/]/g, '') + // Strip Unicode control characters. + .replace(/\p{Cc}/gu, '') +} + +function normalizeLabelForSubmit(raw: string): string { + return sanitizeLabelInput(raw).trim() +} + function bytesToHex(bytes: Uint8Array | number[]): string { return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('') } @@ -448,15 +461,6 @@ export default function Dashboard({ // Generate + add a new delegate key (via SDK) // ============================================================ - // sanitize a key label — strip HTML special chars and control characters - const sanitizeLabel = (raw: string): string => - raw - // Strip HTML special characters - .replace(/[<>&"'/]/g, '') - // Strip Unicode control characters. - .replace(/\p{Cc}/gu, '') - .trim() - const handleAddKey = useCallback(async () => { if (!walletSigner) return @@ -473,7 +477,7 @@ export default function Dashboard({ } // validate label before submitting on-chain - const trimmedLabel = sanitizeLabel(newKeyLabel) + const trimmedLabel = normalizeLabelForSubmit(newKeyLabel) if (!trimmedLabel) { setKeyError('key label cannot be empty') trackEvent('delegate_key_add_failed', { error_type: 'invalid_input' }) @@ -1120,8 +1124,8 @@ const result = await generateText({ value={newKeyLabel} maxLength={64} onChange={(e) => - // strip HTML special chars and control characters on every keystroke - setNewKeyLabel(sanitizeLabel(e.target.value)) + // strip unsafe characters while preserving whitespace during typing + setNewKeyLabel(sanitizeLabelInput(e.target.value)) } placeholder="New key" /> diff --git a/apps/app/src/pages/LandingPage.tsx b/apps/app/src/pages/LandingPage.tsx index 87815bc0..03019658 100644 --- a/apps/app/src/pages/LandingPage.tsx +++ b/apps/app/src/pages/LandingPage.tsx @@ -123,7 +123,7 @@ export default function LandingPage() {

- By continuing, you agree to our Terms of Service and Privacy Policy + By continuing, you agree to our Terms of Service and Privacy Policy