@totalreclaw/totalreclaw 1.0.4 → 1.1.0

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.
package/SKILL.md DELETED
@@ -1,709 +0,0 @@
1
- ---
2
- name: totalreclaw
3
- description: "Zero-knowledge encrypted memory vault for AI agents — the password manager for AI memory. Full E2EE: server never sees plaintext."
4
- version: 1.0.1
5
- author: TotalReclaw Team
6
- license: MIT
7
- homepage: https://github.com/p-diogo/totalreclaw
8
- metadata:
9
- openclaw:
10
- requires:
11
- env: []
12
- bins: []
13
- emoji: "🧠"
14
- os: ["macos", "linux", "windows"]
15
- keywords:
16
- - memory
17
- - e2ee
18
- - zero-knowledge
19
- - encryption
20
- - privacy
21
- - agent-memory
22
- - persistent-context
23
- ---
24
-
25
- # TotalReclaw Skill
26
-
27
- ## Tools
28
-
29
- ### totalreclaw_remember
30
-
31
- Store a new fact or preference in long-term memory.
32
-
33
- **Parameters:**
34
-
35
- | Name | Type | Required | Description |
36
- |------|------|----------|-------------|
37
- | text | string | Yes | The fact or information to remember |
38
- | type | string | No | Type of memory: `fact`, `preference`, `decision`, `episodic`, or `goal`. Default: `fact` |
39
- | importance | integer | No | Importance score 1-10. Default: auto-detected by LLM |
40
-
41
- **Example:**
42
- ```json
43
- {
44
- "text": "User prefers TypeScript over JavaScript for new projects",
45
- "type": "preference",
46
- "importance": 7
47
- }
48
- ```
49
-
50
- **Returns:**
51
- ```json
52
- {
53
- "factId": "01234567-89ab-cdef-0123-456789abcdef",
54
- "status": "stored",
55
- "importance": 7,
56
- "encrypted": true
57
- }
58
- ```
59
-
60
- ---
61
-
62
- ### totalreclaw_recall
63
-
64
- Search and retrieve relevant memories from long-term storage.
65
-
66
- **Parameters:**
67
-
68
- | Name | Type | Required | Description |
69
- |------|------|----------|-------------|
70
- | query | string | Yes | Natural language query to search memories |
71
- | k | integer | No | Number of results to return. Default: 8, Max: 20 |
72
-
73
- **Example:**
74
- ```json
75
- {
76
- "query": "What programming languages does the user prefer?",
77
- "k": 5
78
- }
79
- ```
80
-
81
- **Returns:**
82
- ```json
83
- {
84
- "memories": [
85
- {
86
- "factId": "01234567-89ab-cdef-0123-456789abcdef",
87
- "factText": "User prefers TypeScript over JavaScript for new projects",
88
- "type": "preference",
89
- "importance": 7,
90
- "timestamp": "2026-02-22T10:30:00Z",
91
- "relevanceScore": 0.95
92
- }
93
- ],
94
- "totalCandidates": 47,
95
- "searchLatencyMs": 42
96
- }
97
- ```
98
-
99
- ---
100
-
101
- ### totalreclaw_forget
102
-
103
- Delete a specific fact from memory.
104
-
105
- **Parameters:**
106
-
107
- | Name | Type | Required | Description |
108
- |------|------|----------|-------------|
109
- | factId | string | Yes | UUID of the fact to delete |
110
-
111
- **Example:**
112
- ```json
113
- {
114
- "factId": "01234567-89ab-cdef-0123-456789abcdef"
115
- }
116
- ```
117
-
118
- **Returns:**
119
- ```json
120
- {
121
- "status": "deleted",
122
- "factId": "01234567-89ab-cdef-0123-456789abcdef",
123
- "tombstoneExpiry": "2026-03-24T00:00:00Z"
124
- }
125
- ```
126
-
127
- ---
128
-
129
- ### totalreclaw_export
130
-
131
- Export all stored memories in plaintext format.
132
-
133
- **Parameters:**
134
-
135
- | Name | Type | Required | Description |
136
- |------|------|----------|-------------|
137
- | format | string | No | Export format: `json` or `markdown`. Default: `json` |
138
-
139
- **Example:**
140
- ```json
141
- {
142
- "format": "json"
143
- }
144
- ```
145
-
146
- **Returns (JSON format):**
147
- ```json
148
- {
149
- "exportVersion": "0.3",
150
- "exportedAt": "2026-02-22T10:30:00Z",
151
- "totalFacts": 127,
152
- "facts": [
153
- {
154
- "id": "...",
155
- "factText": "...",
156
- "type": "preference",
157
- "importance": 7,
158
- "timestamp": "...",
159
- "entities": [...],
160
- "relations": [...]
161
- }
162
- ],
163
- "graph": {
164
- "entities": {...},
165
- "relations": [...]
166
- }
167
- }
168
- ```
169
-
170
- **Returns (Markdown format):**
171
- ```markdown
172
- # TotalReclaw Export
173
- Exported: 2026-02-22T10:30:00Z
174
- Total Facts: 127
175
-
176
- ## Preferences
177
- - User prefers TypeScript over JavaScript for new projects (importance: 7)
178
-
179
- ## Decisions
180
- - User decided to use PostgreSQL for the main database (importance: 8)
181
-
182
- ...
183
- ```
184
-
185
- ---
186
-
187
- ### totalreclaw_status
188
-
189
- Check subscription status and usage quota.
190
-
191
- **Parameters:** None
192
-
193
- **Example:**
194
- ```json
195
- {}
196
- ```
197
-
198
- **Returns:**
199
- ```json
200
- {
201
- "tier": "Free",
202
- "writesUsed": 42,
203
- "writesLimit": 100,
204
- "resetsAt": "2026-04-01",
205
- "pricingUrl": "https://totalreclaw.xyz/pricing"
206
- }
207
- ```
208
-
209
- ---
210
-
211
- ### totalreclaw_generate_recovery_phrase
212
-
213
- Generate a cryptographically secure 12-word BIP-39 recovery phrase for new users. **Always use this tool** — never generate mnemonic words yourself.
214
-
215
- **Parameters:** None
216
-
217
- **Example:**
218
- ```json
219
- {}
220
- ```
221
-
222
- **Returns:**
223
- ```json
224
- {
225
- "mnemonic": "abandon ability able about above absent absorb abstract absurd abuse access accident",
226
- "wordCount": 12,
227
- "instructions": ["Display this phrase prominently to the user.", "..."]
228
- }
229
- ```
230
-
231
- ---
232
-
233
- ## Post-Install Setup
234
-
235
- After installing TotalReclaw, the agent MUST complete setup automatically. **Do NOT ask the user to set environment variables manually.**
236
-
237
- ### Step 1: Set the server URL
238
-
239
- The TotalReclaw server URL is always `https://api.totalreclaw.xyz`. Set it in the OpenClaw config:
240
-
241
- ```json
242
- { "env": { "TOTALRECLAW_SERVER_URL": "https://api.totalreclaw.xyz" } }
243
- ```
244
-
245
- ### Step 2: Master password (recovery phrase)
246
-
247
- The master password is a **12-word BIP-39 mnemonic** — like a crypto wallet seed phrase. It derives all encryption keys. The server never sees it.
248
-
249
- Ask the user:
250
- > "Do you have an existing TotalReclaw recovery phrase you'd like to restore, or should I generate a new one?"
251
-
252
- - **New user:** Call the `totalreclaw_generate_recovery_phrase` tool to generate a cryptographically secure 12-word BIP-39 mnemonic. **NEVER generate mnemonic words yourself** — always use this tool. Then set it as `TOTALRECLAW_MASTER_PASSWORD` and display it prominently with a warning:
253
- > "⚠️ SAVE YOUR RECOVERY PHRASE — it's the only way to recover your memories if you switch devices: `[phrase]`"
254
- - **Returning user:** Ask them to provide their existing 12-word phrase, then set it as `TOTALRECLAW_MASTER_PASSWORD`.
255
-
256
- ### Step 3: Enable subgraph mode
257
-
258
- Set `TOTALRECLAW_SUBGRAPH_MODE=true` in the config to enable on-chain storage (required for beta).
259
-
260
- ### Step 4: Set memory slot
261
-
262
- Register TotalReclaw as the memory plugin:
263
- ```json
264
- { "plugins": { "slots": { "memory": "totalreclaw" } } }
265
- ```
266
-
267
- ### Step 5: Restart and verify
268
-
269
- Restart the gateway, then confirm the plugin loaded by checking that `totalreclaw_remember`, `totalreclaw_recall`, `totalreclaw_forget`, and `totalreclaw_export` tools are available.
270
-
271
- ### Step 6: Explain the free tier
272
-
273
- After setup completes, inform the user about the free tier:
274
- > "You're on TotalReclaw's free tier — 100 memory writes per month, unlimited reads and searches.
275
- > I'll let you know if you're approaching the limit. For unlimited writes, visit
276
- > https://totalreclaw.xyz/pricing"
277
-
278
- The agent proactively warns when quota usage exceeds 80%. If the limit is reached, new writes are rejected but all existing memories remain searchable.
279
-
280
- ---
281
-
282
- ## Description
283
-
284
- TotalReclaw is a zero-knowledge encrypted memory vault for AI agents. Think of it as a "password manager for AI memory" that provides:
285
-
286
- 1. **End-to-End Encryption** - All memories are encrypted client-side using AES-256-GCM. The server never sees plaintext data.
287
-
288
- 2. **Portable Memory** - Export all data as plaintext with one command. No vendor lock-in.
289
-
290
- 3. **Universal Compatibility** - Works across any MCP-compatible AI agent, not just OpenClaw.
291
-
292
- 4. **Intelligent Extraction** - Automatically extracts atomic facts, preferences, decisions, and goals from conversations.
293
-
294
- 5. **Smart Decay** - Important memories persist; trivial ones fade over time using a decay algorithm.
295
-
296
- 6. **Graph-Based** - Maintains entity relationships for multi-hop reasoning.
297
-
298
- ---
299
-
300
- ## Instructions for the LLM
301
-
302
- ### IMPORTANT: Do Not Write Cleartext Memory Files
303
-
304
- TotalReclaw handles all memory storage with end-to-end encryption. **Do NOT write facts, preferences, or decisions to MEMORY.md or memory/*.md files.** All memories are stored encrypted on-chain and recalled automatically — writing cleartext files defeats the zero-knowledge guarantee.
305
-
306
- If you need to store a memory, use the `totalreclaw_remember` tool. If you need to recall memories, use `totalreclaw_recall`.
307
-
308
- ### Billing Awareness
309
-
310
- TotalReclaw has a free tier (100 writes/month, unlimited reads). The plugin monitors quota usage automatically:
311
- - If usage exceeds 80%, a warning is injected into your context at conversation start
312
- - If a write fails with quota exceeded (403), inform the user and suggest visiting https://totalreclaw.xyz/pricing
313
- - Use `totalreclaw_status` when the user asks about their subscription, quota, or billing
314
-
315
- ### When to Use Each Tool
316
-
317
- #### totalreclaw_remember
318
-
319
- Use when:
320
- - The user explicitly asks you to remember something ("remember that...", "note that...", "don't forget...")
321
- - You detect a significant preference, decision, or fact that will be useful in future conversations
322
- - The user corrects or updates previous information about themselves
323
- - You observe important context about the user's work, projects, or preferences
324
-
325
- Do NOT use for:
326
- - Temporary information relevant only to the current conversation
327
- - Information the user explicitly says is temporary
328
- - Generic knowledge that isn't user-specific
329
-
330
- #### totalreclaw_recall
331
-
332
- Use when:
333
- - The user asks about their past preferences, decisions, or history
334
- - You need context about the user's projects, tools, or working style
335
- - The user asks "do you remember..." or "what did I tell you about..."
336
- - You're unsure about a user preference and want to check before making assumptions
337
- - Starting a new conversation to load relevant context
338
-
339
- Do NOT use for:
340
- - Every single message (use sparingly, max once per conversation start or when explicitly relevant)
341
- - General knowledge questions unrelated to the user
342
-
343
- #### totalreclaw_forget
344
-
345
- Use when:
346
- - The user explicitly asks you to forget something ("forget that...", "delete that memory...")
347
- - The user indicates information is outdated or incorrect and should be removed
348
- - The user requests a clean slate for a specific topic
349
-
350
- #### totalreclaw_export
351
-
352
- Use when:
353
- - The user asks to export, backup, or download their memory data
354
- - The user wants to see everything you know about them
355
- - The user is migrating to another system
356
-
357
- ---
358
-
359
- ### Best Practices
360
-
361
- 1. **Atomic Facts Only**: Each memory should be a single, atomic piece of information.
362
- - Good: "User prefers dark mode in all editors"
363
- - Bad: "User likes dark mode, uses VS Code, and works at Google"
364
-
365
- 2. **Importance Scoring**:
366
- - 1-3: Trivial, unlikely to matter (small talk, pleasantries)
367
- - 4-6: Useful context (tool preferences, working style)
368
- - 7-8: Important (key decisions, major preferences)
369
- - 9-10: Critical (core values, non-negotiables, safety info)
370
-
371
- 3. **Search Before Storing**: Always recall similar memories before storing new ones to avoid duplicates.
372
-
373
- 4. **Respect User Privacy**: Never store sensitive information (passwords, API keys, personal secrets) even if requested.
374
-
375
- 5. **Prefer NOOP**: When in doubt about whether to store something, prefer not storing it. Memory pollution is worse than missing a minor fact.
376
-
377
- ---
378
-
379
- ## Extraction Prompts (Mem0-Style)
380
-
381
- TotalReclaw uses a Mem0-style extraction pattern with four possible actions:
382
-
383
- ### Actions
384
-
385
- | Action | Description | When to Use |
386
- |--------|-------------|-------------|
387
- | ADD | Store as new memory | No similar memory exists |
388
- | UPDATE | Modify existing memory | New info refines/clarifies existing |
389
- | DELETE | Remove existing memory | New info contradicts existing |
390
- | NOOP | Do nothing | Already captured or not worth storing |
391
-
392
- ---
393
-
394
- ### Pre-Compaction Extraction
395
-
396
- Triggered before OpenClaw's context compaction (typically every few hours in long sessions).
397
-
398
- **System Prompt:**
399
-
400
- ```
401
- You are a memory extraction engine for an AI assistant. Your job is to analyze conversations and extract structured, atomic facts that should be remembered long-term.
402
-
403
- ## Extraction Guidelines
404
-
405
- 1. **Atomicity**: Each fact should be a single, atomic piece of information
406
- - GOOD: "User prefers TypeScript over JavaScript for new projects"
407
- - BAD: "User likes TypeScript, uses VS Code, and works at Google"
408
-
409
- 2. **Types**:
410
- - **fact**: Objective information about the user/world
411
- - **preference**: User's likes, dislikes, or preferences
412
- - **decision**: Choices the user has made
413
- - **episodic**: Event-based memories (what happened when)
414
- - **goal**: User's objectives or targets
415
-
416
- 3. **Importance Scoring (1-10)**:
417
- - 1-3: Trivial, unlikely to matter (small talk, pleasantries)
418
- - 4-6: Useful context (tool preferences, working style)
419
- - 7-8: Important (key decisions, major preferences)
420
- - 9-10: Critical (core values, non-negotiables, safety info)
421
-
422
- 4. **Confidence (0-1)**:
423
- - How certain are you that this is accurate and worth storing?
424
-
425
- 5. **Entities**: Extract named entities (people, projects, tools, concepts)
426
- - Use stable IDs: hash of name+type (e.g., "typescript-tool")
427
- - Types: person, project, tool, preference, concept, location, etc.
428
-
429
- 6. **Relations**: Extract relationships between entities
430
- - Common predicates: prefers, uses, works_on, decided_to_use, dislikes, etc.
431
-
432
- 7. **Actions (Mem0 pattern)**:
433
- - **ADD**: New fact, no conflict with existing memories
434
- - **UPDATE**: Modifies or refines an existing fact (provide existingFactId)
435
- - **DELETE**: Contradicts and replaces an existing fact
436
- - **NOOP**: Not worth storing or already captured
437
- ```
438
-
439
- **User Prompt Template:**
440
-
441
- ```
442
- ## Task: Pre-Compaction Memory Extraction
443
-
444
- You are reviewing the last 20 turns of conversation before they are compacted. Extract ALL valuable long-term memories.
445
-
446
- ## Conversation History (last 20 turns):
447
- {{CONVERSATION_HISTORY}}
448
-
449
- ## Existing Memories (for deduplication):
450
- {{EXISTING_MEMORIES}}
451
-
452
- ## Instructions:
453
- 1. Review each turn carefully for extractable information
454
- 2. Extract atomic facts, preferences, decisions, episodic memories, and goals
455
- 3. For each fact, determine if it's NEW (ADD), modifies existing (UPDATE), contradicts existing (DELETE), or is redundant (NOOP)
456
- 4. Score importance based on long-term relevance
457
- 5. Extract entities and relations
458
-
459
- ## Output Format:
460
- Return a JSON object with:
461
- {
462
- "facts": [
463
- {
464
- "factText": "string (max 512 chars)",
465
- "type": "fact|preference|decision|episodic|goal",
466
- "importance": 1-10,
467
- "confidence": 0-1,
468
- "action": "ADD|UPDATE|DELETE|NOOP",
469
- "existingFactId": "string (if UPDATE/DELETE)",
470
- "entities": [{"id": "...", "name": "...", "type": "..."}],
471
- "relations": [{"subjectId": "...", "predicate": "...", "objectId": "...", "confidence": 0-1}]
472
- }
473
- ]
474
- }
475
-
476
- Focus on quality over quantity. Better to have 5 highly accurate facts than 20 noisy ones.
477
- ```
478
-
479
- ---
480
-
481
- ### Post-Turn Extraction
482
-
483
- Triggered every N turns (configurable, default: 5) for lightweight extraction.
484
-
485
- **User Prompt Template:**
486
-
487
- ```
488
- ## Task: Quick Turn Extraction
489
-
490
- You are doing a lightweight extraction after a few turns. Focus ONLY on high-importance items.
491
-
492
- ## Recent Turns (last 3):
493
- {{CONVERSATION_HISTORY}}
494
-
495
- ## Existing Memories (top matches):
496
- {{EXISTING_MEMORIES}}
497
-
498
- ## Instructions:
499
- 1. Extract ONLY items with importance >= 7 (critical preferences, key decisions)
500
- 2. Skip trivial information - this is a quick pass
501
- 3. Use ADD/UPDATE/DELETE/NOOP appropriately
502
- 4. Be aggressive about NOOP for low-value content
503
-
504
- ## Output Format:
505
- Return a JSON object matching the extraction schema.
506
-
507
- Remember: Less is more. Only extract what truly matters.
508
- ```
509
-
510
- ---
511
-
512
- ### Explicit Command Detection
513
-
514
- Detect when the user explicitly requests memory storage.
515
-
516
- **Trigger Patterns (regex + LLM classification):**
517
-
518
- ```
519
- # Explicit memory commands
520
- "remember that..."
521
- "don't forget..."
522
- "note that..."
523
- "I prefer..."
524
- "for future reference..."
525
- "make a note..."
526
- "store this..."
527
- "keep in mind..."
528
-
529
- # Explicit forget commands
530
- "forget about..."
531
- "delete that memory..."
532
- "remove that from memory..."
533
- "stop remembering..."
534
- ```
535
-
536
- **User Prompt Template:**
537
-
538
- ```
539
- ## Task: Explicit Memory Storage
540
-
541
- The user has explicitly requested to remember something. This is a HIGH PRIORITY extraction.
542
-
543
- ## User's Explicit Request:
544
- {{USER_REQUEST}}
545
-
546
- ## Conversation Context:
547
- {{CONVERSATION_CONTEXT}}
548
-
549
- ## Instructions:
550
- 1. Parse what the user wants remembered
551
- 2. Boost importance by +1 (explicit requests matter more)
552
- 3. Extract as atomic fact(s) with appropriate type
553
- 4. Check against existing memories for UPDATE/DELETE
554
- 5. Set confidence HIGH (user explicitly wants this stored)
555
-
556
- ## Output Format:
557
- Return a JSON object matching the extraction schema.
558
-
559
- This is user-initiated storage - ensure accuracy and capture their intent precisely.
560
- ```
561
-
562
- ---
563
-
564
- ### Deduplication Judge
565
-
566
- Used to determine ADD vs UPDATE vs DELETE vs NOOP for each extracted fact.
567
-
568
- **System Prompt:**
569
-
570
- ```
571
- You are a memory deduplication judge. Your job is to determine if a new fact should be added as new, update an existing fact, delete/replace an existing fact, or be ignored as redundant.
572
-
573
- ## Decision Rules:
574
-
575
- 1. **ADD**: The fact is genuinely new information not covered by existing memories
576
- 2. **UPDATE**: The fact refines, clarifies, or partially modifies an existing fact
577
- 3. **DELETE**: The fact directly contradicts an existing fact and should replace it
578
- 4. **NOOP**: The fact is already fully captured by existing memories
579
-
580
- Be strict about NOOP - if the information is essentially the same, mark it as NOOP.
581
- ```
582
-
583
- **User Prompt Template:**
584
-
585
- ```
586
- ## New Fact to Evaluate:
587
- {{NEW_FACT}}
588
-
589
- ## Similar Existing Facts:
590
- {{EXISTING_FACTS}}
591
-
592
- ## Instructions:
593
- 1. Compare the new fact against each existing fact
594
- 2. Determine the appropriate action (ADD/UPDATE/DELETE/NOOP)
595
- 3. If UPDATE or DELETE, identify which existing fact to modify
596
- 4. Provide your confidence (0-1) and reasoning
597
-
598
- ## Output Format:
599
- {
600
- "decision": "ADD|UPDATE|DELETE|NOOP",
601
- "existingFactId": "string (if UPDATE/DELETE)",
602
- "confidence": 0-1,
603
- "reasoning": "string"
604
- }
605
- ```
606
-
607
- ---
608
-
609
- ## Configuration
610
-
611
- Default configuration values:
612
-
613
- | Key | Default | Description |
614
- |-----|---------|-------------|
615
- | `serverUrl` | `https://api.totalreclaw.xyz` | TotalReclaw server URL |
616
- | `autoExtractEveryTurns` | `5` | Turns between automatic extractions |
617
- | `minImportanceForAutoStore` | `6` | Minimum importance to auto-store |
618
- | `maxMemoriesInContext` | `8` | Maximum memories to inject into context |
619
- | `forgetThreshold` | `0.3` | Decay score threshold for eviction |
620
- | `decayHalfLifeDays` | `30` | Memory decay half-life in days |
621
-
622
- ---
623
-
624
- ## Privacy & Security
625
-
626
- - **Zero-Knowledge**: All encryption happens client-side. The server never sees plaintext.
627
- - **Master Password**: Never sent to the server. Used only for key derivation (Argon2id).
628
- - **Export Portability**: Full plaintext export available anytime.
629
- - **Tombstone Recovery**: Deleted memories can be recovered within 30 days.
630
-
631
- ---
632
-
633
- ## Lifecycle Hooks
634
-
635
- TotalReclaw integrates with OpenClaw through three lifecycle hooks:
636
-
637
- | Hook | Priority | Description |
638
- |------|----------|-------------|
639
- | `before_agent_start` | 10 | Retrieve relevant memories before agent processes message |
640
- | `agent_end` | 90 | Extract and store facts after agent completes turn |
641
- | `pre_compaction` | 5 | Full memory flush before context compaction |
642
-
643
- ---
644
-
645
- ## Example Usage
646
-
647
- ### Storing a preference
648
-
649
- ```json
650
- // Tool call
651
- {
652
- "tool": "totalreclaw_remember",
653
- "params": {
654
- "text": "User prefers functional programming over OOP",
655
- "type": "preference",
656
- "importance": 6
657
- }
658
- }
659
-
660
- // Response
661
- {
662
- "factId": "abc123",
663
- "status": "stored"
664
- }
665
- ```
666
-
667
- ### Recalling memories
668
-
669
- ```json
670
- // Tool call
671
- {
672
- "tool": "totalreclaw_recall",
673
- "params": {
674
- "query": "programming preferences",
675
- "k": 5
676
- }
677
- }
678
-
679
- // Response
680
- {
681
- "memories": [
682
- {
683
- "factId": "abc123",
684
- "factText": "User prefers functional programming over OOP",
685
- "type": "preference",
686
- "importance": 6,
687
- "relevanceScore": 0.92
688
- }
689
- ]
690
- }
691
- ```
692
-
693
- ### Forgetting a memory
694
-
695
- ```json
696
- // Tool call
697
- {
698
- "tool": "totalreclaw_forget",
699
- "params": {
700
- "factId": "abc123"
701
- }
702
- }
703
-
704
- // Response
705
- {
706
- "status": "deleted",
707
- "factId": "abc123"
708
- }
709
- ```