Skill: wbic16/sq-memory (version 1.0.1)

# SQ Memory - OpenClaw Skill **Give your OpenClaw agents permanent memory.** ## Open Source & MIT Licensed SQ is open-source software you can run yourself or use our hosted version. - **Source Code:** https://github.com/wbic16/SQ - **License:** MIT (free forever, modify/sell/distribute) - **Self-Host:** Free (5 minute setup) - **Hosted Option:** Paid convenience service at mirrorborn.us ## What This Skill Does OpenClaw agents lose all memory between sessions. Every restart = amnesia. This skill connects your agent to SQ—persistent 11D text storage. Your agent can: - Remember user preferences across sessions - Store conversation history beyond context limits - Share memory with other agents - Never hallucinate forgotten details again ## Installation ```bash npx clawhub install sq-memory ``` Or manually: ```bash git clone https://github.com/wbic16/openclaw-sq-skill.git ~/.openclaw/skills/sq-memory ``` ## Configuration Add to your agent's `.openclaw/config.yaml`: ```yaml skills: sq-memory: enabled: true endpoint: http://localhost:1337 username: your-username [REDACTED_SECRET] namespace: agent-name # Isolates this agent's memory ``` ## Usage Your agent automatically gets new memory tools: ### remember(key, value) Store something for later: ```javascript remember("user/name", "Alice") remember("user/preferences/theme", "dark") remember("conversation/2026-02-11/summary", "Discussed phext storage...") ``` ### recall(key) Retrieve stored memory: ```javascript const name = recall("user/name") // "Alice" const theme = recall("user/preferences/theme") // "dark" ``` ### forget(key) Delete memory: ```javascript forget("conversation/2026-02-11/summary") ``` ### list_memories(prefix) List all memories under a coordinate: ```javascript const prefs = list_memories("user/preferences/") // Returns: ["user/preferences/theme", "user/preferences/language", ...] ``` ## Coordinate Structure Memories are stored at 11D coordinates. The skill uses this convention: ``` namespace.1.1 / category.subcategory.item / 1.1.1 ``` Example: - Agent namespace: `my-assistant` - User preference for theme: `my-assistant.1.1/user.preferences.theme/1.1.1` This means: - Each agent has isolated memory (namespace collision impossible) - Memories are hierarchically organized - You can share coordinates between agents if needed ## Example: User Preference Agent ```javascript // In your agent's system prompt or skill code: async function getUserTheme() { const theme = recall("user/preferences/theme") return theme || "light" // Default to light if not set } async function setUserTheme(newTheme) { remember("user/preferences/theme", newTheme) return `Theme set to ${newTheme}` } // Agent conversation: User: "I prefer dark mode" Agent: *calls setUserTheme("dark")* Agent: "Got it! I've set your theme to dark mode." // Next session (days later): User: "What's my preferred theme?" Agent: *calls getUserTheme()* Agent: "You prefer dark mode." ``` ## Example: Conversation History ```javascript // Store conversation summaries beyond context window: async function summarizeAndStore(conversationId, summary) { const date = new Date().toISOString().split('T')[0] const key = `conversations/${date}/${conversationId}/summary` remember(key, summary) } async function recallConversation(conversationId) { const memories = list_memories(`conversations/`) return memories .filter(m => m.includes(conversationId)) .map(key => recall(key)) } // Usage: summarizeAndStore("conv-123", "User asked about phext storage, explained 11D coordinates") // Later: const history = recallConversation("conv-123") // Agent can recall what was discussed even after context window cleared ``` ## Advanced: Multi-Agent Coordination Multiple agents can share memory at agreed coordinates: **Agent A (writes):** ```javascript remember("shared/tasks/pending/task-42", "Review pull request #123") ``` **Agent B (reads):** ```javascript const task = recall("shared/tasks/pending/task-42") // Sees: "Review pull request #123" ``` This enables true multi-agent workflows. ## API Reference All functions are available in the `sq` namespace: ### sq.remember(coordinate, text) - **coordinate**: String in format `a.b.c/d.e.f/g.h.i` or shorthand `category/item` - **text**: String to store (max 1MB per coordinate) - **Returns**: `{success: true, coordinate: "full.coordinate.path"}` ### sq.recall(coordinate) - **coordinate**: String (exact match) - **Returns**: String (stored text) or `null` if not found ### sq.forget(coordinate) - **coordinate**: String (exact match) - **Returns**: `{success: true}` or `{success: false, error: "..."}` ### sq.list_memories(prefix) - **prefix**: String (e.g., `"user/"` matches all user memories) - **Returns**: Array of coordinate strings ### sq.update(coordinate, text) - Alias for `remember()` (overwrites existing) ## Rate Limits - **Free tier**: 1,000 API calls/day, 100MB storage - **SQ Cloud ($50/mo)**: 10,000 API calls/day, 1TB storage - **Enterprise**: Custom limits ## Troubleshooting **"Connection refused" error:** - Check your `endpoint` in config (should be `https://sq.mirrorborn.us`) - Verify credentials are correct **"Quota exceeded" error:** - You've hit rate limits - Upgrade to SQ Cloud or wait for daily reset **Memory not persisting:** - Check namespace isolation (each agent needs unique namespace) - Verify coordinate format is valid ## Why SQ? **Open source & MIT licensed:** - Run it yourself for free - Modify it to fit your needs - No vendor lock-in - Transparent codebase **Not a vector database:** - Agents can *read* stored text (not just search embeddings) - Structured by coordinates (not similarity) - Deterministic retrieval (no relevance ranking guesses) **Not Redis:** - Persistent (survives restarts) - 11D addressing (not flat key-value) - Immutable history (WAL for time-travel) **Built for agents:** - Coordinate system matches agent thinking (hierarchical) - No schema overhead - Scales from KB to TB ## Get SQ **Self-Host (Free):** 1. Clone: `git clone https://github.com/wbic16/SQ.git` 2. Build: `cd SQ && cargo build --release` 3. Run: `./target/release/sq 1337` 4. Configure SQ Memory to `http://localhost:1337` **Hosted (Convenience):** 1. Sign up: https://mirrorborn.us 2. Get API key 3. Configure SQ Memory to `https://sq.mirrorborn.us` 4. Pay $50/mo (or use free tier) ## Support - Discord: https://discord.gg/kGCMM5yQ - Docs: https://mirrorborn.us/help.html - GitHub: https://github.com/wbic16/SQ --- **Built by Mirrorborn 🦋 for the OpenClaw ecosystem**

path: .gitignore
content: node_modules/
*.log
.env
.DS_Store


path: CHANGELOG.md
content: # Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.0] - 2026-02-11

### Added
- Initial release of SQ Memory skill for OpenClaw
- Core memory functions: `remember()`, `recall()`, `forget()`, `list_memories()`
- Support for both SQ Cloud and self-hosted SQ endpoints
- Namespace isolation for multi-agent setups
- Shorthand coordinate syntax (e.g., `user/preferences/theme`)
- Automatic coordinate expansion to full 11D phext format
- HTTP and HTTPS endpoint support
- Optional API key authentication for SQ Cloud
- Comprehensive test suite (`test.js`)
- Three usage examples:
  - `user-preferences.js` - Personal preference tracking
  - `conversation-history.js` - Long-term conversation memory
  - `multi-agent-coordination.js` - Distributed task coordination
- Documentation:
  - `README.md` - Overview and quick reference
  - `SKILL.md` - Full skill documentation
  - `QUICKSTART.md` - 5-minute setup guide
  - `SELF-HOSTED.md` - Self-hosting instructions
- OpenClaw skill.json manifest for tool registration

### Features
- **Persistent Memory**: Survives agent restarts
- **11D Addressing**: Hierarchical coordinate system
- **JSON Support**: Store complex objects as JSON strings
- **Large Text**: Tested up to 10KB+ per coordinate
- **Multi-Agent**: Shared memory at agreed coordinates
- **No Database**: Direct text storage via SQ
- **WAL Support**: Write-ahead logging for durability

### Requirements
- Node.js >= 18.0.0
- OpenClaw >= 2026.2.0
- SQ endpoint (local or cloud)

### Known Limitations
- Max 1MB per coordinate (SQ limitation)
- No built-in search (use `list_memories()` + filter)
- No transactions across multiple coordinates

## [1.0.1] - 2026-02-11

### Fixed
- **CRITICAL: Missing `p=` parameter in API calls** - All API endpoints now include `&p=${phext}` parameter for proper phext isolation (discovered during Tester onboarding)
- Install command in docs updated from `openclaw skill install` to `npx clawhub install` (correct method)

### Added
- `phext` config option (defaults to `namespace` value if not specified)

### Changed
- All API calls now include phext name parameter for proper multi-tenant isolation

## [Unreleased]

### Planned
- WebSocket support for real-time memory updates
- Batch operations for multiple remember/recall/forget calls
- Built-in search with coordinate pattern matching
- Memory snapshots for point-in-time recovery
- Integration with OpenClaw session context
- Agent memory profiling tools
- Memory garbage collection helpers
- Coordinate migration utilities


path: CONTRIBUTING.md
content: # Contributing to SQ Memory Skill

Thanks for your interest in improving the SQ Memory skill for OpenClaw! 🔱

---

## Quick Links

- **Report Bug:** [GitHub Issues](https://github.com/wbic16/openclaw-sq-skill/issues)
- **Discord:** https://discord.gg/kGCMM5yQ
- **SQ Repo:** https://github.com/wbic16/SQ
- **OpenClaw:** https://openclaw.ai

---

## How to Contribute

### 1. Report Bugs

**Found a bug?** Open an issue with:
- What happened (expected vs actual behavior)
- Steps to reproduce
- Your environment (OS, Node version, OpenClaw version, SQ version)
- Error messages/logs

**Example:**
```
Title: recall() fails for coordinates with special characters

Description:
When using recall("user/[REDACTED_SECRET]"), the function throws:
"HTTP 400: Invalid coordinate"

Expected: Should escape special characters automatically
Actual: Fails with error

Environment:
- OS: Ubuntu 22.04
- Node: v20.10.0
- OpenClaw: 2026.2.1
- SQ: 0.5.2

Steps:
1. await remember("user/[REDACTED_SECRET]", "test")
2. await recall("user/[REDACTED_SECRET]")
3. Error occurs
```

---

### 2. Suggest Features

**Have an idea?** Open an issue with:
- Use case (what problem does it solve?)
- Proposed API (how would it work?)
- Example code
- Alternatives considered

**Example:**
```
Title: Add batch operations for multiple coordinates

Use Case:
When storing user preferences, I often need to write 5-10 values.
Calling remember() 10 times = 10 HTTP requests (slow).

Proposed API:
await rememberBatch({
  "user/preferences/theme": "dark",
  "user/preferences/language": "en",
  "user/preferences/timezone": "America/Chicago"
});

Returns: { success: 3, failed: 0 }

Alternatives:
- Keep single operations (current approach)
- Use SQ's batch insert endpoint directly

Notes:
- Would improve performance for multi-value updates
- SQ already supports batch via /api/v2/batch
```

---

### 3. Add Examples

**Have a cool use case?** Share it!

**Create a new example file:**
```bash
cd examples/
cp user-preferences.js my-example.js
# Edit my-example.js with your use case
```

**Submit a PR with:**
- Commented code showing the pattern
- Explanation of what it does
- Use case description
- Coordinate structure used

**Good examples:**
- Time-series data storage
- Agent knowledge base
- Collaborative note-taking
- Workflow orchestration

---

### 4. Improve Documentation

**Found a typo? Unclear explanation? Missing info?**

Edit the relevant file and submit a PR:
- `README.md` - Overview and quick reference
- `SKILL.md` - Full documentation
- `QUICKSTART.md` - Setup guide
- `SELF-HOSTED.md` - Self-hosting instructions

**Documentation improvements we need:**
- More use cases
- Better error handling examples
- Performance tuning tips
- Migration guides
- Troubleshooting sections

---

### 5. Fix Bugs

**Want to fix something?**

1. Fork the repo
2. Create a branch: `git checkout -b fix-recall-special-chars`
3. Make your changes
4. Add tests if applicable
5. Run tests: `npm test`
6. Commit: `git commit -m "Fix: escape special characters in coordinates"`
7. Push: `git push origin fix-recall-special-chars`
8. Open a PR

**PR checklist:**
- [ ] Tests pass (`npm test`)
- [ ] Code is documented (JSDoc comments)
- [ ] CHANGELOG.md updated
- [ ] No breaking changes (or clearly marked if unavoidable)

---

### 6. Add Features

**Want to add something new?**

1. Open an issue first to discuss
2. Get consensus on the approach
3. Fork and create a feature branch
4. Implement with tests
5. Update docs
6. Submit PR

**Feature PR checklist:**
- [ ] Issue exists and is referenced
- [ ] Tests added for new functionality
- [ ] Documentation updated (README, SKILL.md)
- [ ] CHANGELOG.md updated
- [ ] Examples added if appropriate
- [ ] Backward compatible (or version bump)

---

## Development Setup

### Prerequisites
- Node.js >= 18.0.0
- Git
- SQ endpoint (local or cloud)

### Setup
```bash
# Clone your fork
git clone https://github.com/YOUR-USERNAME/openclaw-sq-skill.git
cd openclaw-sq-skill

# Install dependencies
npm install

# Run tests
npm test

# Test against your SQ endpoint
node test.js http://localhost:1337
```

### Running Tests
```bash
# Default (uses http://localhost:1337)
npm test

# Specific endpoint
SQ_ENDPOINT=http://localhost:1337 SQ_API_KEY=your-key npm test

# Or pass directly
node test.js http://your-endpoint:1337
```

---

## Code Style

**Keep it simple:**
- Readable > clever
- Comments for complex logic
- JSDoc for public functions
- Meaningful variable names

**Example:**
```javascript
/**
 * Store text at a coordinate
 * 
 * @param {string} coordinate - Memory coordinate (e.g., 'user/preferences/theme')
 * @param {string} text - Text to store
 * @returns {Promise<{success: boolean, coordinate: string}>}
 */
async function remember(coordinate, text) {
  const fullCoord = this._expandCoordinate(coordinate);
  // ... implementation
}
```

---

## Testing

**All changes should include tests:**
```javascript
// Good test structure:
console.log('Test: Feature X works correctly');

await remember('test/feature-x', 'value');
const result = await recall('test/feature-x');

if (result === 'value') {
  console.log('✅ Feature X works');
} else {
  throw new Error(`Expected "value", got "${result}"`);
}

await forget('test/feature-x');
```

**Run full test suite before submitting:**
```bash
npm test
```

---

## Versioning

We use [Semantic Versioning](https://semver.org/):
- **MAJOR** (1.0.0): Breaking changes
- **MINOR** (0.1.0): New features, backward compatible
- **PATCH** (0.0.1): Bug fixes

**Version bump guidelines:**
- Breaking API change → MAJOR
- New function/feature → MINOR
- Bug fix, docs → PATCH

---

## Release Process

(For maintainers)

1. Update version in `package.json`, `skill.json`, `CHANGELOG.md`
2. Commit: `git commit -m "Release v0.2.0"`
3. Tag: `git tag v0.2.0`
4. Push: `git push && git push --tags`
5. Create GitHub release with CHANGELOG excerpt
6. Announce in Discord #general

---

## Community Guidelines

**Be kind:**
- Respect others' time and expertise
- Assume good intentions
- Provide constructive feedback
- Help newcomers

**Be professional:**
- No harassment, discrimination, trolling
- Stay on topic
- Follow OpenClaw community standards

---

## Questions?

- **Discord:** https://discord.gg/kGCMM5yQ (fastest)
- **GitHub Issues:** For bugs/features
- **Email:** [REDACTED_SECRET] (for sensitive issues)

---

## Recognition

Contributors will be:
- Listed in CHANGELOG.md for their contributions
- Thanked in release notes
- Recognized in Discord announcements

**We appreciate you!** 🦋

---

**Built by Mirrorborn for the OpenClaw ecosystem**


path: index.js
content: /**
 * SQ Cloud Memory - OpenClaw Skill
 * Persistent 11D text storage for agents
 */

const https = require('https');
const http = require('http');

class SQMemory {
  constructor(config) {
    this.endpoint = config.endpoint || 'http://localhost:1337';
    this.[REDACTED_SECRET];
    this.namespace = config.namespace || 'default-agent';
    this.phext = config.phext || config.namespace || 'default';
  }

  /**
   * Convert shorthand coordinate to full 11D format
   * "user/preferences/theme" → "namespace.1.1/user.preferences.theme/1.1.1"
   */
  _expandCoordinate(shorthand) {
    if (shorthand.match(/^\d+\.\d+\.\d+\/\d+\.\d+\.\d+\/\d+\.\d+\.\d+$/)) {
      return shorthand; // Already full format
    }
    
    // Convert "category/subcategory/item" to proper phext coordinate
    const parts = shorthand.split('/').map(p => p.replace(/\//g, '.'));
    const library = `${this.namespace}.1.1`;
    const collection = parts.join('.');
    const scroll = '1.1.1';
    
    return `${library}/${collection}/${scroll}`;
  }

  /**
   * Make authenticated HTTP request to SQ Cloud
   */
  _request(method, path, body = null) {
    return new Promise((resolve, reject) => {
      const url = new URL(this.endpoint + path);
      const isHttps = url.protocol === 'https:';
      const lib = isHttps ? https : http;
      
      const options = {
        hostname: url.hostname,
        port: url.port || (isHttps ? 443 : 80),
        path: url.pathname + url.search,
        method: method,
        headers: {
          'Content-Type': 'text/plain',
          'User-Agent': 'OpenClaw-SQ-Skill/1.0.1'
        }
      };
      
      // Add API key if provided (SQ Cloud)
      if (this.apiKey) {
        options.headers['Authorization'] = `Bearer ${this.apiKey}`;
      }
      
      if (body) {
        options.headers['Content-Length'] = Buffer.byteLength(body);
      }
      
      const req = lib.request(options, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            resolve(data);
          } else {
            reject(new Error(`HTTP ${res.statusCode}: ${data}`));
          }
        });
      });
      
      req.on('error', reject);
      
      if (body) {
        req.write(body);
      }
      req.end();
    });
  }

  /**
   * Store text at a coordinate
   */
  async remember(coordinate, text) {
    const fullCoord = this._expandCoordinate(coordinate);
    const encoded = encodeURIComponent(fullCoord);
    
    const s = encodeURIComponent(text);
    await this._request('GET', `/api/v2/update?p=${encodeURIComponent(this.phext)}&c=${encoded}&s=${s}`);
    
    return {
      success: true,
      coordinate: fullCoord
    };
  }

  /**
   * Retrieve text from a coordinate
   */
  async recall(coordinate) {
    const fullCoord = this._expandCoordinate(coordinate);
    const encoded = encodeURIComponent(fullCoord);
    
    try {
      const text = await this._request('GET', `/api/v2/select?p=${encodeURIComponent(this.phext)}&c=${encoded}`);
      return text;
    } catch (err) {
      if (err.message.includes('404')) {
        return null; // Not found
      }
      throw err;
    }
  }

  /**
   * Delete memory at a coordinate
   */
  async forget(coordinate) {
    const fullCoord = this._expandCoordinate(coordinate);
    const encoded = encodeURIComponent(fullCoord);
    
    try {
      await this._request('POST', `/api/v2/delete?p=${encodeURIComponent(this.phext)}&c=${encoded}`);
      return { success: true };
    } catch (err) {
      return { success: false, error: err.message };
    }
  }

  /**
   * List all coordinates matching a prefix
   */
  async list_memories(prefix) {
    const fullPrefix = this._expandCoordinate(prefix);
    const encoded = encodeURIComponent(fullPrefix);
    
    try {
      const response = await this._request('GET', `/api/v2/toc?p=${encodeURIComponent(this.phext)}`);
      const lines = response.split('\n').filter(l => l.trim());
      return lines;
    } catch (err) {
      if (err.message.includes('404')) {
        return []; // No memories found
      }
      throw err;
    }
  }

  /**
   * Update (overwrite) text at a coordinate
   */
  async update(coordinate, text) {
    return this.remember(coordinate, text);
  }
}

/**
 * OpenClaw skill initialization
 */
module.exports = {
  name: 'sq-memory',
  version: '1.0.1',
  
  async init(config) {
    const sq = new SQMemory(config);
    
    return {
      tools: {
        remember: sq.remember.bind(sq),
        recall: sq.recall.bind(sq),
        forget: sq.forget.bind(sq),
        list_memories: sq.list_memories.bind(sq),
        update: sq.update.bind(sq)
      }
    };
  }
};


path: LICENSE.md
content: MIT License

Copyright (c) 2026 Mirrorborn

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


path: package.json
content: {
  "name": "openclaw-sq-memory",
  "version": "1.0.1",
  "description": "SQ Cloud memory skill for OpenClaw agents",
  "main": "index.js",
  "scripts": {
    "test": "node test.js"
  },
  "keywords": [
    "openclaw",
    "skill",
    "memory",
    "phext",
    "sq-cloud"
  ],
  "author": "Mirrorborn",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/wbic16/openclaw-sq-skill.git"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}


path: QUICKSTART.md
content: # Quick Start: SQ Memory for OpenClaw

**Get your OpenClaw agent remembering in 5 minutes.**

## 🎉 SQ is Open Source (MIT License)

You can run SQ yourself for free or use our hosted version for convenience.

- **Source:** https://github.com/wbic16/SQ
- **License:** MIT (do whatever you want)
- **Self-Host:** Free forever
- **Hosted:** $50/mo (or 100MB free tier)

---

## Step 1: Install the Skill (30 seconds)

**Option A: Via ClawHub**
```bash
npx clawhub install sq-memory
```

**Option B: Manual install**
```bash
cd ~/.openclaw/skills
git clone https://github.com/wbic16/openclaw-sq-skill.git sq-memory
cd sq-memory
npm install
```

---

## Step 2: Choose Your Endpoint (1 minute)

### Option A: Self-Hosted (Free, Local)

**Start SQ on your machine:**
```bash
# Install SQ
git clone https://github.com/wbic16/SQ.git
cd SQ
cargo build --release

# Run it
./target/release/sq 1337
```

**Config:**
```yaml
# ~/.openclaw/config.yaml
skills:
  sq-memory:
    enabled: true
    endpoint: http://localhost:1337
    api_key: ""
    namespace: my-assistant
```

### Option B: SQ Cloud (Hosted, $50/month or free tier)

**Sign up at mirrorborn.us:**
1. Visit https://mirrorborn.us
2. Click "Get Started"
3. Choose plan (100MB free or 1TB paid)
4. Copy your API key

**Config:**
```yaml
# ~/.openclaw/config.yaml
skills:
  sq-memory:
    enabled: true
    endpoint: http://localhost:1337
    [REDACTED_SECRET]
    namespace: my-assistant
```

---

## Step 3: Test It (2 minutes)

**Run the test script:**
```bash
cd ~/.openclaw/skills/sq-memory
node test.js http://localhost:1337  # Or your endpoint
```

**Expected output:**
```
🔱 Testing SQ Memory Skill
📍 Endpoint: http://localhost:1337
🏷️  Namespace: test-agent

1️⃣  Initializing skill...
✅ Skill initialized

2️⃣  Test: Basic remember/recall
   📝 Stored: "Hello from SQ!" at test/basic
   ✅ Recalled correctly: "Hello from SQ!"

...

✅ ALL TESTS PASSED! 🎉
```

If tests pass, you're ready to go!

---

## Step 4: Use It in Your Agent (1 minute)

**Add to your agent's system prompt:**
```
You have access to permanent memory via the sq-memory skill.

Tools available:
- remember(key, value) - Store information permanently
- recall(key) - Retrieve stored information
- forget(key) - Delete stored information
- list_memories(prefix) - List all memories matching a prefix

When users mention preferences, remember them.
When answering questions, check memory first.

Example coordinates:
- user/preferences/theme
- user/identity/name
- conversations/2026-02-11/summary
```

**Example interaction:**
```
User: "My favorite color is blue"
Agent: [calls remember("user/preferences/color", "blue")]
Agent: "Got it! I'll remember that your favorite color is blue."

[Agent restarts - new session]

User: "What's my favorite color?"
Agent: [calls recall("user/preferences/color")]
Agent: "Your favorite color is blue!"
```

---

## What Just Happened?

1. You installed the SQ Memory skill
2. You configured it to talk to an SQ endpoint (local or cloud)
3. Your agent now has persistent memory across sessions
4. Memory survives restarts, context window clears, everything

**No more amnesia. Your agent remembers forever.**

---

## Next Steps

### Learn More
- **Full docs:** [SKILL.md](SKILL.md)
- **Examples:** [examples/](examples/)
- **Self-hosting:** [SELF-HOSTED.md](SELF-HOSTED.md)

### Try Examples
```bash
cd ~/.openclaw/skills/sq-memory/examples
node user-preferences.js       # User preference agent
node conversation-history.js   # Long-term conversation tracking
node multi-agent-coordination.js  # Agents working together
```

### Customize
Edit your namespace to separate different agents:
```yaml
skills:
  sq-memory:
    namespace: my-coding-assistant  # Different namespace = isolated memory
```

### Monitor Usage
Check what your agent remembers:
```bash
# List all memories
curl "http://localhost:1337/api/v2/toc?c=my-assistant.1.1/1.1.1/1.1.1"

# Get specific memory
curl "http://localhost:1337/api/v2/select?c=my-assistant.1.1/user.preferences.theme/1.1.1"
```

---

## Troubleshooting

**"Connection refused"**
- SQ not running → start it with `./sq 1337`
- Wrong endpoint → check `endpoint` in config

**"Tests fail"**
- SQ version too old → upgrade to latest
- Network issue → check firewall/proxy

**"Memory not persisting"**
- Check namespace isolation → each agent needs unique namespace
- Verify SQ is writing to disk → check `.phext` files in SQ directory

**Need help?**
- Discord: https://discord.gg/kGCMM5yQ
- GitHub Issues: https://github.com/wbic16/openclaw-sq-skill/issues

---

## What You Built

You now have:
- ✅ OpenClaw agent with permanent memory
- ✅ Memory that survives restarts
- ✅ Infinite storage (11D addressing)
- ✅ Multi-agent coordination capability
- ✅ No database required

**Time to build agents that actually remember.**

🔱 **Welcome to the Exocortex.**


path: README.md
content: # SQ Memory - OpenClaw Skill

**Give your OpenClaw agents permanent memory that survives restarts.**

## Open Source & MIT Licensed

SQ is **free, open-source software** (MIT license). Run it yourself or use our hosted version for convenience.

- **Source:** https://github.com/wbic16/SQ
- **License:** MIT (do whatever you want with it)
- **Hosting:** Optional paid service at mirrorborn.us

## The Problem

OpenClaw agents are powerful, but they suffer from amnesia:
- Every restart = complete memory loss
- Context window fills up → agent forgets earlier conversation
- No way to share knowledge between agents
- User preferences must be re-entered every session

## The Solution

This skill connects your agent to **SQ**—open-source persistent 11-dimensional text storage.

Run it yourself (free) or use our hosted version (paid). Your agent gains four new abilities:
- `remember(key, value)` - Store something permanently
- `recall(key)` - Retrieve stored memory
- `forget(key)` - Delete memory
- `list_memories(prefix)` - Browse stored memories

## Quick Start

### 1. Install the Skill

```bash
npx clawhub install sq-memory
```

### 2. Get SQ Endpoint

**Option A: Self-Host** (free, open source)
```bash
git clone https://github.com/wbic16/SQ.git
cd SQ
cargo build --release
./target/release/sq 1337
```
See [SELF-HOSTED.md](SELF-HOSTED.md) for full setup.

**Option B: Use Hosted SQ** (convenience, paid)
Sign up at [mirrorborn.us](https://mirrorborn.us) for managed hosting:
- No setup required
- 100MB free tier (or 1TB with SQ Cloud $50/mo)
- Automatic backups

### 3. Configure

**For SQ Cloud:**
```yaml
skills:
  sq-memory:
    enabled: true
    endpoint: https://sq.mirrorborn.us
    [REDACTED_SECRET]
    namespace: my-assistant
```

**For self-hosted:**
```yaml
skills:
  sq-memory:
    enabled: true
    endpoint: http://localhost:1337  # Or your server URL
    api_key: ""                       # Leave empty for self-hosted
    namespace: my-assistant
```

### 4. Use It

Your agent now remembers across sessions:

```
User: "My favorite color is blue"
Agent: *calls remember("user/preferences/color", "blue")*
Agent: "Got it! I'll remember that."

[Agent restarts]

User: "What's my favorite color?"
Agent: *calls recall("user/preferences/color")*
Agent: "Your favorite color is blue!"
```

## Use Cases

### Personal Assistant
- Remember user preferences (theme, language, timezone)
- Store contact information
- Track recurring tasks
- Build conversation history beyond context window

### Multi-Agent Coordination
- Share task lists between agents
- Coordinate work (Agent A marks task complete, Agent B sees it)
- Build shared knowledge base

### Long-Running Projects
- Track project milestones
- Store research findings
- Accumulate domain knowledge over time

## No Vendor Lock-In

**Your data, your way:**
- Run SQ yourself = full control
- Switch between self-hosted and hosted anytime
- Export your data = plain `.phext` text files
- Modify SQ source code to fit your needs
- No proprietary formats, no API limits (self-hosted)

**This is what open source means:** You're never trapped.

## How It Works

**Under the hood:**
1. Skill translates friendly keys (`"user/name"`) into phext coordinates (`"agent.1.1/user.name.1/1.1.1"`)
2. Makes authenticated HTTPS request to SQ Cloud
3. SQ stores text at exact 11D coordinate
4. Text persists forever (or until you delete it)

**Why 11D?**
- Phext uses 9 hierarchical delimiters (Library→Shelf→Series→...→Line)
- Plus 2D text (columns & rows) = 11 dimensions total
- Result: Infinitely addressable text space with zero schema

**Not a database:**
- No tables, no SQL, no schemas
- Just: coordinate → text
- Agents can read what they wrote (unlike vector embeddings)

## API Reference

See [SKILL.md](SKILL.md) for full documentation.

## Examples

See `examples/` directory for:
- User preference agent
- Conversation history tracker
- Multi-agent task coordinator

## Pricing

### Self-Hosted (Open Source, MIT License)
- **Cost**: Free forever
- **Storage**: Limited only by your disk
- **Setup**: 5 minutes (see [SELF-HOSTED.md](SELF-HOSTED.md))
- **Source**: https://github.com/wbic16/SQ
- **License**: MIT (modify, sell, whatever you want)

### Hosted SQ (Convenience Service)
- **Free tier**: 100MB storage, 1,000 API calls/day
- **SQ Cloud**: $50/month, 1TB storage, 10,000 API calls/day
- **Enterprise**: Custom limits, dedicated instances
- **Why pay?** Managed service, automatic backups, no DevOps

## Support

- Discord: https://discord.gg/kGCMM5yQ
- Docs: https://mirrorborn.us/help.html
- Issues: https://github.com/wbic16/openclaw-sq-skill/issues

## License

MIT

---

**Built by Mirrorborn 🦋**


path: SELF-HOSTED.md
content: # Self-Hosted SQ Setup for OpenClaw

**Want to run your own SQ endpoint instead of using SQ Cloud?** Here's how.

---

## Why Self-Host?

- **Free** - No monthly subscription
- **Privacy** - Your data stays on your machine
- **Learning** - Understand how SQ works under the hood
- **Customization** - Modify SQ to fit your needs

---

## Quick Start

### 1. Install SQ

**Option A: From source (Rust)**
```bash
git clone https://github.com/wbic16/SQ.git
cd SQ
cargo build --release
```

**Option B: Docker**
```bash
docker pull wbic16/sq
```

**Option C: Pre-built binary**
Download from: https://github.com/wbic16/SQ/releases

---

### 2. Start SQ Server

**Standalone mode:**
```bash
# From source
./target/release/sq 1337

# Docker
docker run -p 1337:1337 wbic16/sq 1337
```

**Systemd service (Linux):**
```bash
sudo nano /etc/systemd/system/sq.service
```

```ini
[Unit]
Description=SQ Server
After=network.target

[Service]
Type=simple
User=your-username
WorkingDirectory=/path/to/SQ
ExecStart=/path/to/SQ/target/release/sq 1337
Restart=always

[Install]
WantedBy=multi-user.target
```

```bash
sudo systemctl enable sq
sudo systemctl start sq
```

---

### 3. Test Your Endpoint

```bash
# Check version
curl http://localhost:1337/api/v2/version

# Should return:
{"version":"0.5.2","libphext":"0.3.1"}
```

---

### 4. Configure OpenClaw Skill

**Edit `.openclaw/config.yaml`:**

```yaml
skills:
  sq-memory:
    enabled: true
    endpoint: http://localhost:1337  # Your self-hosted endpoint
    api_key: ""                       # Leave empty for self-hosted
    namespace: my-assistant
```

**No API key needed** - self-hosted SQ has no authentication by default.

---

### 5. Test the Skill

```javascript
// Your agent can now use memory:
remember("test/message", "Hello from self-hosted SQ!")
recall("test/message")  // Returns: "Hello from self-hosted SQ!"
```

---

## Production Setup (Optional)

### Add HTTPS with nginx

**Install nginx + certbot:**
```bash
sudo apt install nginx certbot python3-certbot-nginx
```

**Nginx config (`/etc/nginx/sites-available/sq`):**
```nginx
server {
    listen 443 ssl http2;
    server_name your-domain.com;
    
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    
    location / {
        proxy_pass http://127.0.0.1:1337;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
```

**Get certificate:**
```bash
sudo certbot --nginx -d your-domain.com
```

**Update OpenClaw config:**
```yaml
endpoint: https://your-domain.com
```

---

### Add API Key Authentication (Optional)

**Nginx basic auth:**
```bash
sudo apt install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd your-username
```

**Add to nginx config:**
```nginx
location / {
    auth_basic "SQ API";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:1337;
}
```

**Update OpenClaw config:**
```yaml
endpoint: https://your-domain.com
[REDACTED_SECRET]  # The htpasswd password
```

The skill will send it as `Authorization: Bearer <api_key>`.

---

## Multi-User Setup

**For multi-agent or multi-user scenarios:**

1. **Run multiple SQ instances** (different ports):
   ```bash
   ./sq 1337  # Agent 1
   ./sq 1338  # Agent 2
   ./sq 1339  # Agent 3
   ```

2. **Configure agents with different endpoints:**
   ```yaml
   # Agent 1
   endpoint: http://localhost:1337
   
   # Agent 2
   endpoint: http://localhost:1338
   ```

3. **Use namespaces for isolation:**
   ```yaml
   # Even on same endpoint, different namespaces prevent collision
   namespace: agent-alice
   namespace: agent-bob
   ```

---

## Data Location

**SQ stores phext files in the working directory:**
```
/path/to/SQ/
  ├── .sq/                # Shared memory segments (daemon mode)
  ├── *.phext             # Your data files
  └── *.phext.json        # Metadata
```

**Backup your data:**
```bash
# Simple backup
cp *.phext /backup/location/

# Or use rsync
rsync -av --include='*.phext' --exclude='*' /path/to/SQ/ /backup/
```

---

## Troubleshooting

### SQ won't start
```bash
# Check if port is in use
sudo lsof -i :1337

# Check SQ logs
journalctl -u sq -f
```

### OpenClaw can't connect
```bash
# Test endpoint directly
curl http://localhost:1337/api/v2/version

# Check firewall
sudo ufw status
sudo ufw allow 1337/tcp
```

### Memory not persisting
SQ auto-saves on every write. If data disappears:
- Check disk space: `df -h`
- Check file permissions: `ls -la *.phext`
- Verify SQ is running: `ps aux | grep sq`

---

## Performance Tuning

**For large datasets:**

1. **Increase system limits:**
   ```bash
   # /etc/security/limits.conf
   * soft nofile 65536
   * hard nofile 65536
   ```

2. **Use SSD storage** for `.phext` files

3. **Monitor resource usage:**
   ```bash
   # Memory
   top -p $(pgrep sq)
   
   # Disk I/O
   iotop -p $(pgrep sq)
   ```

---

## Upgrading SQ

```bash
cd SQ
git pull
cargo build --release
sudo systemctl restart sq
```

**Check version:**
```bash
curl http://localhost:1337/api/v2/version
```

---

## Community & Support

- **Docs:** https://github.com/wbic16/SQ
- **Discord:** https://discord.gg/kGCMM5yQ
- **Issues:** https://github.com/wbic16/SQ/issues

---

## SQ Cloud vs Self-Hosted

| Feature | Self-Hosted | SQ Cloud |
|---------|-------------|----------|
| **Cost** | Free | $50/month |
| **Setup** | DIY | Managed |
| **Storage** | Limited by your disk | 1TB |
| **Backups** | You manage | Automatic |
| **Support** | Community | Email support |
| **Uptime** | Your responsibility | 99.9% SLA |
| **Auth** | Optional | Built-in |

**Choose self-hosted if:**
- Learning/experimenting
- Privacy is critical
- You have DevOps skills

**Choose SQ Cloud if:**
- You want it to just work
- You need reliability
- You value your time over $50/month

---

## Next Steps

1. Start SQ locally
2. Test with curl
3. Configure OpenClaw skill
4. Build agents that remember!

**Questions?** Join Discord or open a GitHub issue.

🔱 **Self-hosting is how phext stays open.**


path: skill.json
content: {
  "name": "sq-memory",
  "version": "1.0.1",
  "description": "Persistent 11D memory for OpenClaw agents via SQ Cloud",
  "author": "Mirrorborn",
  "repository": "https://github.com/wbic16/openclaw-sq-skill",
  "license": "MIT",
  "openclaw_version": ">=2026.2.0",
  "tools": [
    {
      "name": "remember",
      "description": "Store text at a coordinate for permanent memory",
      "parameters": {
        "coordinate": {
          "type": "string",
          "description": "Memory coordinate (e.g., 'user/preferences/theme')",
          "required": true
        },
        "text": {
          "type": "string",
          "description": "Text to store",
          "required": true
        }
      }
    },
    {
      "name": "recall",
      "description": "Retrieve text from a coordinate",
      "parameters": {
        "coordinate": {
          "type": "string",
          "description": "Memory coordinate to retrieve",
          "required": true
        }
      }
    },
    {
      "name": "forget",
      "description": "Delete memory at a coordinate",
      "parameters": {
        "coordinate": {
          "type": "string",
          "description": "Memory coordinate to delete",
          "required": true
        }
      }
    },
    {
      "name": "list_memories",
      "description": "List all memory coordinates matching a prefix",
      "parameters": {
        "prefix": {
          "type": "string",
          "description": "Coordinate prefix to search (e.g., 'user/')",
          "required": true
        }
      }
    }
  ],
  "config": {
    "endpoint": {
      "type": "string",
      "description": "SQ endpoint URL",
      "default": "http://localhost:1337",
      "required": true
    },
    "api_key": {
      "type": "string",
      "description": "SQ Cloud API key (leave empty for self-hosted)",
      "required": false,
      "secret": true
    },
    "namespace": {
      "type": "string",
      "description": "Agent namespace for memory isolation",
      "default": "default-agent",
      "required": true
    }
  },
  "dependencies": {
    "node": ">=18.0.0"
  }
}


path: test.js
content: #!/usr/bin/env node

/**
 * Test script for SQ Memory skill
 * 
 * Tests both local and cloud endpoints
 * 
 * Usage:
 *   node test.js                          # Test with default config
 *   node test.js http://localhost:1337    # Test specific endpoint
 */

const SQMemory = require('./index.js');

// Test configuration
const config = {
  endpoint: process.argv[2] || process.env.SQ_ENDPOINT || 'http://localhost:1337',
  [REDACTED_SECRET] || '',
  namespace: 'test-agent'
};

console.log(`\n🔱 Testing SQ Memory Skill`);
console.log(`📍 Endpoint: ${config.endpoint}`);
console.log(`🏷️  Namespace: ${config.namespace}\n`);

async function runTests() {
  try {
    // Initialize skill
    console.log('1️⃣  Initializing skill...');
    const { tools } = await SQMemory.init(config);
    const { remember, recall, forget, list_memories } = tools;
    console.log('✅ Skill initialized\n');
    
    // Test 1: Basic remember/recall
    console.log('2️⃣  Test: Basic remember/recall');
    const testKey = 'test/basic';
    const testValue = 'Hello from SQ!';
    
    await remember(testKey, testValue);
    console.log(`   📝 Stored: "${testValue}" at ${testKey}`);
    
    const recalled = await recall(testKey);
    if (recalled === testValue) {
      console.log(`   ✅ Recalled correctly: "${recalled}"`);
    } else {
      throw new Error(`Recall mismatch: expected "${testValue}", got "${recalled}"`);
    }
    console.log('');
    
    // Test 2: Update (overwrite)
    console.log('3️⃣  Test: Update existing memory');
    const updatedValue = 'Updated value!';
    await remember(testKey, updatedValue);
    console.log(`   📝 Updated: "${updatedValue}" at ${testKey}`);
    
    const recalledUpdated = await recall(testKey);
    if (recalledUpdated === updatedValue) {
      console.log(`   ✅ Update successful: "${recalledUpdated}"`);
    } else {
      throw new Error(`Update failed: expected "${updatedValue}", got "${recalledUpdated}"`);
    }
    console.log('');
    
    // Test 3: Multiple memories
    console.log('4️⃣  Test: Multiple memories');
    await remember('test/multi/item1', 'First item');
    await remember('test/multi/item2', 'Second item');
    await remember('test/multi/item3', 'Third item');
    console.log(`   📝 Stored 3 items under test/multi/`);
    
    const item1 = await recall('test/multi/item1');
    const item2 = await recall('test/multi/item2');
    const item3 = await recall('test/multi/item3');
    
    if (item1 === 'First item' && item2 === 'Second item' && item3 === 'Third item') {
      console.log(`   ✅ All items recalled correctly`);
    } else {
      throw new Error('Multi-item recall failed');
    }
    console.log('');
    
    // Test 4: List memories
    console.log('5️⃣  Test: List memories');
    const listed = await list_memories('test/multi/');
    console.log(`   📋 Found ${listed.length} memories under test/multi/`);
    
    if (listed.length >= 3) {
      console.log(`   ✅ List working: ${listed.join(', ')}`);
    } else {
      throw new Error(`List failed: expected 3+, got ${listed.length}`);
    }
    console.log('');
    
    // Test 5: Forget (delete)
    console.log('6️⃣  Test: Forget (delete)');
    await forget('test/multi/item2');
    console.log(`   🗑️  Deleted test/multi/item2`);
    
    const deletedRecall = await recall('test/multi/item2');
    if (deletedRecall === null) {
      console.log(`   ✅ Delete successful (recall returned null)`);
    } else {
      throw new Error(`Delete failed: item still exists`);
    }
    console.log('');
    
    // Test 6: JSON storage
    console.log('7️⃣  Test: JSON storage');
    const jsonData = {
      name: 'Alice',
      preferences: {
        theme: 'dark',
        language: 'en'
      },
      timestamp: Date.now()
    };
    
    await remember('test/json/data', JSON.stringify(jsonData));
    console.log(`   📝 Stored JSON object`);
    
    const jsonRecalled = await recall('test/json/data');
    const parsed = JSON.parse(jsonRecalled);
    
    if (parsed.name === 'Alice' && parsed.preferences.theme === 'dark') {
      console.log(`   ✅ JSON round-trip successful`);
    } else {
      throw new Error('JSON parsing failed');
    }
    console.log('');
    
    // Test 7: Large text
    console.log('8️⃣  Test: Large text storage');
    const largeText = 'A'.repeat(10000); // 10KB
    await remember('test/large', largeText);
    console.log(`   📝 Stored 10KB of text`);
    
    const largeRecalled = await recall('test/large');
    if (largeRecalled.length === 10000) {
      console.log(`   ✅ Large text stored/recalled successfully`);
    } else {
      throw new Error(`Large text size mismatch: ${largeRecalled.length}`);
    }
    console.log('');
    
    // Test 8: Non-existent key
    console.log('9️⃣  Test: Recall non-existent key');
    const missing = await recall('test/does-not-exist');
    if (missing === null) {
      console.log(`   ✅ Returns null for missing key`);
    } else {
      throw new Error(`Expected null, got "${missing}"`);
    }
    console.log('');
    
    // Cleanup
    console.log('🧹 Cleaning up test data...');
    await forget('test/basic');
    await forget('test/multi/item1');
    await forget('test/multi/item3');
    await forget('test/json/data');
    await forget('test/large');
    console.log('   ✅ Cleanup complete\n');
    
    console.log('✅ ALL TESTS PASSED! 🎉\n');
    console.log('The SQ Memory skill is working correctly.');
    console.log('You can now use it in your OpenClaw agents.\n');
    
  } catch (err) {
    console.error(`\n❌ TEST FAILED: ${err.message}`);
    console.error(err.stack);
    process.exit(1);
  }
}

// Run tests
runTests().catch(err => {
  console.error(`Fatal error: ${err.message}`);
  process.exit(1);
});


path: examples/conversation-history.js
content: /**
 * Example: Conversation History Tracker
 * 
 * Store conversation summaries beyond the context window limit.
 * This enables true long-term memory for extended interactions.
 */

/**
 * Store a conversation summary
 */
async function storeConversationSummary(conversationId, summary) {
  const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
  const coordinate = `conversations/${date}/${conversationId}`;
  
  await remember(coordinate, JSON.stringify({
    date: date,
    conversationId: conversationId,
    summary: summary,
    timestamp: Date.now()
  }));
  
  return `Stored summary for conversation ${conversationId}`;
}

/**
 * Recall conversation history for a specific conversation
 */
async function recallConversation(conversationId) {
  const coords = await list_memories(`conversations/`);
  const relevant = coords.filter(c => c.includes(conversationId));
  
  const history = [];
  for (const coord of relevant) {
    const text = await recall(coord);
    if (text) {
      try {
        history.push(JSON.parse(text));
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return history.sort((a, b) => a.timestamp - b.timestamp);
}

/**
 * Get recent conversation summaries (last N days)
 */
async function getRecentConversations(days = 7) {
  const now = new Date();
  const cutoff = new Date(now.getTime() - (days * 24 * 60 * 60 * 1000));
  
  const coords = await list_memories('conversations/');
  const recent = [];
  
  for (const coord of coords) {
    const text = await recall(coord);
    if (text) {
      try {
        const data = JSON.parse(text);
        const convDate = new Date(data.date);
        if (convDate >= cutoff) {
          recent.push(data);
        }
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return recent.sort((a, b) => b.timestamp - a.timestamp);
}

/**
 * Usage example in agent conversation:
 */

// After a long conversation spanning multiple sessions:
// AGENT: "Let me summarize what we discussed..."
// AGENT uses: storeConversationSummary("conv-2026-02-11", "User asked about phext storage. Explained 11D coordinates, showed examples of SQ API usage, discussed multi-tenant architecture.")

// Days later, context window has cleared:
// USER: "What did we talk about last week regarding phext?"
// AGENT uses: getRecentConversations(7)
// AGENT: "We discussed phext storage on February 11th. I explained 11D coordinates and showed you how to use the SQ API."

// Continuing a specific conversation thread:
// USER: "Continue our discussion about phext storage"
// AGENT uses: recallConversation("conv-2026-02-11")
// AGENT: "Right, we were discussing phext's 11D coordinate system. You wanted to know about..."

/**
 * Recommended coordinate structure for conversations:
 * 
 * conversations/2026-02-11/conv-001
 * conversations/2026-02-11/conv-002
 * conversations/2026-02-12/conv-003
 * 
 * This enables:
 * - Chronological browsing
 * - Date-range queries
 * - Conversation threading
 */

/**
 * Advanced: Store conversation turns (messages)
 */
async function storeConversationTurn(conversationId, turn, role, message) {
  const date = new Date().toISOString().split('T')[0];
  const coordinate = `conversations/${date}/${conversationId}/turn-${turn}`;
  
  await remember(coordinate, JSON.stringify({
    role: role,        // "user" or "assistant"
    message: message,
    timestamp: Date.now(),
    turn: turn
  }));
}

/**
 * Retrieve full conversation transcript
 */
async function getConversationTranscript(conversationId) {
  const coords = await list_memories(`conversations/`);
  const turns = coords
    .filter(c => c.includes(conversationId))
    .filter(c => c.includes('/turn-'));
  
  const transcript = [];
  for (const coord of turns) {
    const text = await recall(coord);
    if (text) {
      try {
        transcript.push(JSON.parse(text));
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return transcript.sort((a, b) => a.turn - b.turn);
}

// This enables:
// - Complete conversation replay
// - Context reconstruction after restarts
// - Training data generation
// - Audit trails


path: examples/multi-agent-coordination.js
content: /**
 * Example: Multi-Agent Task Coordination
 * 
 * Multiple agents share a task list and coordinate work via SQ.
 * No central database needed - agents read/write shared coordinates.
 */

/**
 * Agent A: Create a task
 */
async function createTask(taskId, description, assignedTo = null) {
  const coordinate = `shared/tasks/pending/${taskId}`;
  
  await remember(coordinate, JSON.stringify({
    id: taskId,
    description: description,
    assignedTo: assignedTo,
    status: 'pending',
    createdAt: Date.now(),
    createdBy: 'agent-a'  // Could be dynamic
  }));
  
  return `Task ${taskId} created`;
}

/**
 * Agent B: Claim a task
 */
async function claimTask(taskId, agentName) {
  const pendingCoord = `shared/tasks/pending/${taskId}`;
  const task = await recall(pendingCoord);
  
  if (!task) {
    return `Task ${taskId} not found`;
  }
  
  const data = JSON.parse(task);
  data.assignedTo = agentName;
  data.status = 'in-progress';
  data.claimedAt = Date.now();
  
  // Move from pending to in-progress
  await forget(pendingCoord);
  await remember(`shared/tasks/in-progress/${taskId}`, JSON.stringify(data));
  
  return `Task ${taskId} claimed by ${agentName}`;
}

/**
 * Agent B: Complete a task
 */
async function completeTask(taskId, result = null) {
  const inProgressCoord = `shared/tasks/in-progress/${taskId}`;
  const task = await recall(inProgressCoord);
  
  if (!task) {
    return `Task ${taskId} not found`;
  }
  
  const data = JSON.parse(task);
  data.status = 'completed';
  data.completedAt = Date.now();
  data.result = result;
  
  // Move from in-progress to completed
  await forget(inProgressCoord);
  await remember(`shared/tasks/completed/${taskId}`, JSON.stringify(data));
  
  return `Task ${taskId} marked complete`;
}

/**
 * Any agent: List pending tasks
 */
async function listPendingTasks() {
  const coords = await list_memories('shared/tasks/pending/');
  const tasks = [];
  
  for (const coord of coords) {
    const text = await recall(coord);
    if (text) {
      try {
        tasks.push(JSON.parse(text));
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return tasks.sort((a, b) => a.createdAt - b.createdAt);
}

/**
 * Any agent: List in-progress tasks
 */
async function listInProgressTasks() {
  const coords = await list_memories('shared/tasks/in-progress/');
  const tasks = [];
  
  for (const coord of coords) {
    const text = await recall(coord);
    if (text) {
      try {
        tasks.push(JSON.parse(text));
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return tasks;
}

/**
 * Workflow Example:
 */

// Agent A (Coordinator):
// await createTask("task-001", "Review PR #123", null);
// await createTask("task-002", "Write docs for feature X", "agent-b");

// Agent B (Worker):
// const pending = await listPendingTasks();
// // Sees: [{ id: "task-001", description: "Review PR #123", ... }]
// 
// await claimTask("task-001", "agent-b");
// // ... does work ...
// await completeTask("task-001", "PR approved and merged");

// Agent A (checking status):
// const inProgress = await listInProgressTasks();
// // Sees agent-b working on task-001
// 
// const completed = await listCompletedTasks();
// // Sees task-001 completed with result

/**
 * Advanced: Agent capability registry
 */

// Each agent registers what it can do:
async function registerCapability(agentName, capabilities) {
  const coordinate = `agents/registry/${agentName}`;
  await remember(coordinate, JSON.stringify({
    name: agentName,
    capabilities: capabilities,  // ["code-review", "documentation", "testing"]
    status: 'online',
    lastSeen: Date.now()
  }));
}

// Coordinator can find agents by capability:
async function findAgentsByCapability(capability) {
  const coords = await list_memories('agents/registry/');
  const agents = [];
  
  for (const coord of coords) {
    const text = await recall(coord);
    if (text) {
      try {
        const data = JSON.parse(text);
        if (data.capabilities.includes(capability)) {
          agents.push(data.name);
        }
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return agents;
}

// Usage:
// await registerCapability("agent-b", ["code-review", "documentation"]);
// const reviewers = await findAgentsByCapability("code-review");
// // Returns: ["agent-b"]
// await createTask("task-003", "Review security audit", reviewers[0]);

/**
 * Advanced: Inter-agent messaging
 */

async function sendMessage(fromAgent, toAgent, message) {
  const timestamp = Date.now();
  const coordinate = `messages/${toAgent}/inbox/${timestamp}`;
  
  await remember(coordinate, JSON.stringify({
    from: fromAgent,
    to: toAgent,
    message: message,
    timestamp: timestamp,
    read: false
  }));
}

async function readInbox(agentName) {
  const coords = await list_memories(`messages/${agentName}/inbox/`);
  const messages = [];
  
  for (const coord of coords) {
    const text = await recall(coord);
    if (text) {
      try {
        const msg = JSON.parse(text);
        if (!msg.read) {
          messages.push(msg);
          
          // Mark as read
          msg.read = true;
          await remember(coord, JSON.stringify(msg));
        }
      } catch (e) {
        // Skip invalid JSON
      }
    }
  }
  
  return messages.sort((a, b) => a.timestamp - b.timestamp);
}

// Usage:
// Agent A: await sendMessage("agent-a", "agent-b", "Please review PR #123");
// Agent B: const inbox = await readInbox("agent-b");
//          // Sees message from agent-a

/**
 * Coordinate structure for multi-agent workflows:
 * 
 * shared/tasks/pending/<task-id>
 * shared/tasks/in-progress/<task-id>
 * shared/tasks/completed/<task-id>
 * shared/tasks/failed/<task-id>
 * 
 * agents/registry/<agent-name>
 * agents/heartbeat/<agent-name>
 * 
 * messages/<agent-name>/inbox/<timestamp>
 * messages/<agent-name>/sent/<timestamp>
 * 
 * This enables:
 * - Distributed task queues
 * - Agent discovery
 * - Async coordination
 * - No central orchestrator needed
 */

/**
 * Heartbeat pattern for agent health monitoring:
 */
async function heartbeat(agentName, status = 'online') {
  const coordinate = `agents/heartbeat/${agentName}`;
  await remember(coordinate, JSON.stringify({
    name: agentName,
    status: status,  // "online", "busy", "offline"
    timestamp: Date.now()
  }));
}

async function checkAgentHealth(agentName, timeoutMs = 60000) {
  const coordinate = `agents/heartbeat/${agentName}`;
  const text = await recall(coordinate);
  
  if (!text) {
    return 'unknown';
  }
  
  try {
    const data = JSON.parse(text);
    const age = Date.now() - data.timestamp;
    
    if (age > timeoutMs) {
      return 'offline';
    }
    
    return data.status;
  } catch (e) {
    return 'error';
  }
}

// Each agent sends heartbeat every 30 seconds:
// setInterval(() => heartbeat("agent-b", "online"), 30000);

// Coordinator checks health:
// const health = await checkAgentHealth("agent-b");
// if (health === 'offline') {
//   console.log("Agent B is down, reassigning tasks...");
// }


path: examples/user-preferences.js
content: /**
 * Example: User Preference Agent
 * 
 * This agent remembers user preferences across sessions using SQ Cloud.
 * 
 * Usage in agent system prompt:
 * "You have access to permanent memory via sq-memory skill.
 *  When users mention preferences, store them with remember().
 *  When answering questions, check recall() first."
 */

// Example conversation flow:

// USER: "I prefer dark mode"
// AGENT uses: remember("user/preferences/theme", "dark")

// USER: "My timezone is America/Chicago" 
// AGENT uses: remember("user/preferences/timezone", "America/Chicago")

// USER: "Call me Will"
// AGENT uses: remember("user/identity/preferred-name", "Will")

// [AGENT RESTARTS - new session]

// USER: "What time is it?"
// AGENT uses: recall("user/preferences/timezone") → "America/Chicago"
// AGENT: "It's 3:15 PM in America/Chicago"

// USER: "What's my name?"
// AGENT uses: recall("user/identity/preferred-name") → "Will"
// AGENT: "Your name is Will!"

// USER: "What are all my preferences?"
// AGENT uses: list_memories("user/preferences/")
// AGENT returns: ["user/preferences/theme", "user/preferences/timezone"]
// AGENT: "You prefer dark mode and your timezone is America/Chicago"

/**
 * Recommended coordinate structure for user preferences:
 * 
 * user/identity/name
 * user/identity/preferred-name
 * user/identity/pronouns
 * user/preferences/theme
 * user/preferences/language
 * user/preferences/timezone
 * user/preferences/notification-level
 * user/contacts/email
 * user/contacts/phone
 * user/settings/ai-model
 * user/settings/response-style
 */

// Implementation example (pseudo-code for OpenClaw agent):

async function handleUserPreference(category, value) {
  const coordinate = `user/preferences/${category}`;
  await remember(coordinate, value);
  return `Remembered: ${category} = ${value}`;
}

async function getUserPreference(category) {
  const coordinate = `user/preferences/${category}`;
  const value = await recall(coordinate);
  return value || null;
}

async function getAllPreferences() {
  const coords = await list_memories("user/preferences/");
  const prefs = {};
  
  for (const coord of coords) {
    const category = coord.split('/').pop();
    const value = await recall(coord);
    prefs[category] = value;
  }
  
  return prefs;
}

// Usage in agent conversation:
// - User says preference → call handleUserPreference()
// - User asks question needing preference → call getUserPreference()
// - User asks "what do you know about me?" → call getAllPreferences()


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)