@byte5ai/palaia 2.5.1 → 2.7.2

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/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # @byte5ai/palaia
2
2
 
3
- **Palaia memory backend for OpenClaw.**
3
+ **palaia memory backend for OpenClaw.**
4
4
 
5
- Replace OpenClaw's built-in `memory-core` with Palaia — local, cloud-free, WAL-backed agent memory with tier routing and semantic search.
5
+ Replace OpenClaw's built-in `memory-core` with palaia — local, cloud-free, WAL-backed agent memory with tier routing and semantic search.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- # Install Palaia (Python CLI)
10
+ # Install palaia (Python CLI)
11
11
  pip install palaia
12
12
 
13
13
  # Install the OpenClaw plugin
@@ -59,7 +59,7 @@ All options are optional — sensible defaults are used:
59
59
 
60
60
  ### `memory_search` (always available)
61
61
 
62
- Semantically search Palaia memory:
62
+ Semantically search palaia memory:
63
63
 
64
64
  ```
65
65
  memory_search({ query: "deployment process", maxResults: 5, tier: "all" })
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @byte5ai/palaia — Palaia Memory Backend for OpenClaw
2
+ * @byte5ai/palaia — palaia Memory Backend for OpenClaw
3
3
  *
4
4
  * Plugin entry point. Loaded by OpenClaw via jiti (no build step needed).
5
5
  *
@@ -33,7 +33,7 @@ import type { OpenClawPluginApi, OpenClawPluginEntry } from "./src/types.js";
33
33
  // Plugin entry point compatible with OpenClaw plugin-sdk definePluginEntry
34
34
  const palaiaPlugin: OpenClawPluginEntry = {
35
35
  id: "palaia",
36
- name: "Palaia Memory",
36
+ name: "palaia Memory",
37
37
  register(api: OpenClawPluginApi) {
38
38
  // Issue #66: Plugin config is resolved GLOBALLY via api.pluginConfig.
39
39
  // OpenClaw does NOT provide per-agent config resolution — all agents share the same
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "palaia",
3
- "name": "Palaia Memory",
3
+ "name": "palaia Memory",
4
4
  "kind": "memory",
5
5
  "skills": ["./skill"],
6
6
  "configSchema": {
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "workspace": {
14
14
  "type": "string",
15
- "description": "Palaia workspace path (default: agent workspace)"
15
+ "description": "palaia workspace path (default: agent workspace)"
16
16
  },
17
17
  "tier": {
18
18
  "type": "string",
@@ -106,7 +106,7 @@
106
106
  },
107
107
  "uiHints": {
108
108
  "binaryPath": {
109
- "label": "Palaia Binary Path",
109
+ "label": "palaia Binary Path",
110
110
  "placeholder": "auto-detect"
111
111
  },
112
112
  "workspace": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@byte5ai/palaia",
3
- "version": "2.5.1",
4
- "description": "Palaia memory backend for OpenClaw",
3
+ "version": "2.7.2",
4
+ "description": "palaia memory backend for OpenClaw",
5
5
  "main": "index.ts",
6
6
  "openclaw": {
7
7
  "extensions": [
package/skill/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: palaia
3
- version: "2.5.1"
3
+ version: "2.7.2"
4
4
  description: >
5
5
  Local, crash-safe persistent memory for OpenClaw agents.
6
6
  SQLite-backed by default. Semantic search, projects, scopes, auto-capture.
@@ -15,26 +15,28 @@ metadata:
15
15
  kind: pip
16
16
  package: "palaia[fastembed]"
17
17
  bins: ["palaia"]
18
- label: "Install Palaia with semantic search (pip)"
18
+ label: "Install palaia with semantic search (pip)"
19
19
  postInstall:
20
20
  - command: "palaia init"
21
- label: "Initialize Palaia store"
22
- - command: "npm install -g @byte5ai/palaia@latest"
23
- label: "Install OpenClaw plugin (npm)"
21
+ label: "Initialize palaia store"
22
+ - command: "openclaw plugins install @byte5ai/palaia"
23
+ label: "Install OpenClaw plugin"
24
24
  postUpdate:
25
- - command: "python3 -m pip install --upgrade 'palaia[fastembed]' && (npm install -g @byte5ai/palaia@latest 2>/dev/null || true) && palaia doctor --fix && palaia warmup"
26
- label: "Upgrade Palaia with semantic search (pip + npm plugin) and run health checks"
25
+ - command: "python3 -m pip install --upgrade 'palaia[fastembed]' && openclaw plugins install @byte5ai/palaia && palaia doctor --fix && palaia warmup"
26
+ label: "Upgrade palaia with semantic search + plugin and run health checks"
27
27
  postUpdateMessage: >
28
- Palaia has been updated to v2.5. New: agent isolation mode (--isolated), modern CLI
29
- design, backup-restore auto-fix. Run `palaia doctor --fix` to upgrade.
28
+ palaia has been updated to v2.7.2. New: `palaia setup claude-code` for
29
+ one-command Claude Code integration (MCP config + agent instructions),
30
+ doctor check for Claude Code config.
31
+ Run `palaia doctor --fix` to upgrade.
30
32
  plugin:
31
33
  slot: memory
32
34
  package: "@byte5ai/palaia"
33
35
  ---
34
36
 
35
- # Palaia v2.4 — Agent Memory Skill
37
+ # palaia v2.7 — Agent Memory Skill
36
38
 
37
- Palaia is the memory system for OpenClaw agents. It gives you persistent, searchable, crash-safe memory across sessions. You don't need to understand how it works — just use the commands below.
39
+ palaia is the memory system for OpenClaw agents. It gives you persistent, searchable, crash-safe memory across sessions. You don't need to understand how it works — just use the commands below.
38
40
 
39
41
  <!-- begin:install -->
40
42
  ## First-Time Setup
@@ -73,22 +75,15 @@ Note: `palaia[fastembed]` already includes sqlite-vec for native SIMD vector sea
73
75
  After the CLI is installed, wire it into OpenClaw:
74
76
 
75
77
  ```bash
76
- npm install -g @byte5ai/palaia@latest
78
+ openclaw plugins install @byte5ai/palaia
77
79
  ```
78
80
 
79
- Then activate in OpenClaw config (read existing values first, never overwrite arrays):
80
- ```bash
81
- PALAIA_PATH="$(npm root -g)/@byte5ai/palaia"
82
- ```
83
-
84
- Add to `openclaw.json`:
85
- ```json
81
+ Then activate the memory slot in OpenClaw config:
82
+ ```json5
83
+ // openclaw.json
86
84
  {
87
- "plugins": {
88
- "load": { "paths": ["<PALAIA_PATH>"] },
89
- "allow": ["..existing..", "palaia"],
90
- "slots": { "memory": "palaia" },
91
- "entries": { "palaia": { "enabled": true } }
85
+ plugins: {
86
+ slots: { memory: "palaia" }
92
87
  }
93
88
  }
94
89
  ```
@@ -106,7 +101,7 @@ Single-agent systems work with the default name. For multi-agent setups:
106
101
  palaia init --agent YOUR_AGENT_NAME
107
102
  ```
108
103
 
109
- ## Updating Palaia
104
+ ## Updating palaia
110
105
 
111
106
  When the user asks to update, or when `palaia status` shows a version mismatch:
112
107
 
@@ -133,7 +128,7 @@ pipx install "palaia[fastembed]" --force
133
128
  palaia doctor --fix
134
129
 
135
130
  # Step 4: Update OpenClaw plugin (if installed)
136
- npm install -g @byte5ai/palaia@latest
131
+ openclaw plugins install @byte5ai/palaia
137
132
  ```
138
133
 
139
134
  After the manual update, `palaia upgrade` will be available for future updates.
@@ -141,14 +136,19 @@ After the manual update, `palaia upgrade` will be available for future updates.
141
136
 
142
137
  ## Auto-Check (run every time this skill is loaded)
143
138
 
144
- Before doing anything else with Palaia, run `palaia doctor` silently. If it reports ANY warnings, address them immediately by following `palaia doctor --fix` recommendations. Do not proceed with other Palaia tasks until doctor reports no issues.
139
+ Before doing anything else with palaia, run `palaia doctor` silently. If it reports ANY warnings, address them immediately by following `palaia doctor --fix` recommendations. Do not proceed with other palaia tasks until doctor reports no issues.
145
140
 
146
141
  ---
147
142
 
148
143
  ## Core Concepts
149
144
 
150
- ### Auto-Capture
151
- Conversations are automatically captured when the OpenClaw plugin is active. You do NOT need to manually write things from conversations — Palaia handles it. Manual `palaia write` is only for structured knowledge: processes, tasks, and knowledge from external sources.
145
+ ### Auto-Capture vs. Manual Write
146
+ Conversations are automatically captured when the OpenClaw plugin is active. Auto-capture handles routine knowledge extraction from conversations.
147
+
148
+ **Manual writes (`palaia write`) are higher value.** When you or the user identify something as especially important — a key decision, a reusable workflow, a critical fact from an external source — write it manually. Manually written entries are ranked higher than auto-captured ones in recall results, so they surface more prominently in future sessions. Use manual writes for:
149
+ - Important facts, decisions, or context from external sources
150
+ - Reusable processes and workflows (team runbooks)
151
+ - Sticky notes / reminders for future sessions (tasks)
152
152
 
153
153
  ### Three Tiers
154
154
  - **HOT** — Active memories (< 7 days or frequently accessed). Always searched.
@@ -162,8 +162,8 @@ Conversations are automatically captured when the OpenClaw plugin is active. You
162
162
 
163
163
  ### Entry Types
164
164
  - **memory** — Facts, decisions, learnings (default)
165
- - **process** — Workflows, checklists, SOPs
166
- - **task** — Action items with status, priority, assignee, due date
165
+ - **process** — Workflows, checklists, SOPs (team runbooks)
166
+ - **task** — Sticky notes / reminders for future sessions. Tasks are ephemeral: when marked done, they are automatically deleted. Never auto-captured — only created by explicit `palaia write --type task`.
167
167
 
168
168
  ---
169
169
 
@@ -184,7 +184,7 @@ palaia config set database_url postgresql://user:pass@host/db
184
184
 
185
185
  ### Semantic Vector Search
186
186
 
187
- Palaia uses **hybrid search**: BM25 keyword matching (always active) combined with semantic vector embeddings (when a provider is configured). This finds memories by meaning, not just keywords.
187
+ palaia uses **hybrid search**: BM25 keyword matching (always active) combined with semantic vector embeddings (when a provider is configured). This finds memories by meaning, not just keywords.
188
188
 
189
189
  **Embedding providers** (checked in chain order, first available wins):
190
190
 
@@ -215,7 +215,7 @@ The OpenClaw plugin starts the embed-server automatically. For CLI-only usage, i
215
215
 
216
216
  ### MCP Server (Claude Desktop, Cursor, any MCP host)
217
217
 
218
- Palaia works as a standalone MCP memory server — **no OpenClaw required**. Any AI tool that supports MCP can use palaia as persistent local memory.
218
+ palaia works as a standalone MCP memory server — **no OpenClaw required**. Any AI tool that supports MCP can use palaia as persistent local memory.
219
219
 
220
220
  ```bash
221
221
  pip install 'palaia[mcp]'
@@ -265,22 +265,28 @@ palaia-mcp --read-only # No writes (untrusted hosts)
265
265
 
266
266
  ### `palaia write` — Save structured knowledge
267
267
 
268
- Only use for explicit process/task entries, or knowledge from external sources. Conversation knowledge is auto-captured.
268
+ Use for important facts, reusable processes, sticky-note tasks, and knowledge from external sources. Manually written entries rank higher than auto-captured ones in recall.
269
269
 
270
270
  ```bash
271
- # Save a fact from outside the conversation
271
+ # Save an important fact (ranks higher than auto-capture)
272
272
  palaia write "API rate limit is 100 req/min" --type memory --tags api,limits
273
273
 
274
- # Record a step-by-step process
275
- palaia write "1. Build 2. Test 3. Deploy" --type process --project myapp
274
+ # Record a reusable team workflow (use specific, unique titles!)
275
+ palaia write "Backend: Deploy to staging via Docker" --type process --project myapp --scope team
276
276
 
277
- # Create a task with structured fields
278
- palaia write "fix login bug" --type task --priority high --assignee Elliot --due-date 2026-04-01
277
+ # Create a sticky note for a future session (deleted when done)
278
+ palaia write "verify backup works after schema migration" --type task
279
279
 
280
280
  # Save to a specific project with scope
281
281
  palaia write "Use JWT for auth" --project backend --scope team --tags decision
282
282
  ```
283
283
 
284
+ **Process naming convention:** Use the format `[Domain]: [What it does]` for process titles. Specific titles prevent duplicates and make processes findable.
285
+ - Good: `"Release: PyPI publish + ClawHub sync"`, `"Backend: Deploy to staging via Docker"`
286
+ - Bad: `"Deploy steps"`, `"Release process"` (too generic, will collide with other similar processes)
287
+
288
+ Before writing a new process, search for existing ones: `palaia query "deploy" --type process`. If a similar process exists, update it with `palaia edit <id>` instead of creating a new one.
289
+
284
290
  ### `palaia query` — Semantic search
285
291
 
286
292
  Find memories by meaning, not just keywords.
@@ -438,6 +444,17 @@ palaia upgrade
438
444
 
439
445
  Auto-detects the install method (pip/uv/pipx/brew), preserves all installed extras (fastembed, mcp, sqlite-vec, curate), runs `palaia doctor --fix`, and upgrades the OpenClaw npm plugin if present. Always use this instead of manual pip commands.
440
446
 
447
+ ### `palaia ui` — Local memory explorer (NEW in v2.7)
448
+
449
+ ```bash
450
+ pip install 'palaia[ui]' # One-time: install FastAPI + uvicorn
451
+ palaia ui # Opens browser at http://127.0.0.1:8384
452
+ palaia ui --port 9000 # Custom port (auto-fallback if busy)
453
+ palaia ui --no-browser # Don't auto-open browser
454
+ ```
455
+
456
+ Browse, search, create, edit, and delete entries in the browser. Manual entries are highlighted with a gold border (1.3× recall boost). Tasks are post-its: clicking ✓ deletes them. The health pill in the header shows doctor status with actionable warnings. Localhost only — no authentication, no network exposure.
457
+
441
458
  ### `palaia doctor` — Diagnostics and auto-fix
442
459
 
443
460
  ```bash
@@ -457,6 +474,15 @@ palaia gc --aggressive # Also clears COLD tier
457
474
  palaia gc --budget 200 # Keep max N entries
458
475
  ```
459
476
 
477
+ ### `palaia prune` — Selective cleanup (NEW in v2.5)
478
+
479
+ ```bash
480
+ palaia prune --agent moneypenny # Remove auto-captured entries by agent
481
+ palaia prune --tags auto-capture # Remove all auto-captured entries
482
+ palaia prune --dry-run # Preview what would be removed
483
+ palaia prune --protect-type process # Never delete process entries
484
+ ```
485
+
460
486
  ### `palaia config` — Configuration
461
487
 
462
488
  ```bash
@@ -486,10 +512,13 @@ palaia project locks # List all active locks
486
512
  ### `palaia edit` — Modify existing entries
487
513
 
488
514
  ```bash
489
- palaia edit <id> --status done
515
+ palaia edit <id> --status done # For tasks: this DELETES the entry (sticky note completed)
516
+ palaia edit <id> --status wontfix # For tasks: also deletes (cancelled reminder)
490
517
  palaia edit <id> "updated content" --tags new,tags --priority high
491
518
  ```
492
519
 
520
+ **Task lifecycle:** Tasks are sticky notes. When you mark a task as `done` or `wontfix`, it is automatically deleted — not archived. This is intentional: completed reminders have no long-term value. If the outcome of the task should be remembered, write a separate memory entry before marking the task done.
521
+
493
522
  ### Other commands
494
523
 
495
524
  ```bash
@@ -508,7 +537,7 @@ All commands support `--json` for machine-readable output.
508
537
 
509
538
  ## Smart Nudging
510
539
 
511
- Palaia's CLI output contains contextual hints prefixed with `[palaia]`. These are important guidance.
540
+ palaia's CLI output contains contextual hints prefixed with `[palaia]`. These are important guidance.
512
541
 
513
542
  **As an agent, you should:**
514
543
  - Read and act on nudge messages in CLI output
@@ -649,12 +678,12 @@ palaia write "## Dev-Agent Lifecycle
649
678
 
650
679
  | Situation | Command |
651
680
  |-----------|---------|
652
- | Remember a fact (not from conversation) | `palaia write "..." --type memory` |
653
- | Record a process/SOP | `palaia write "steps..." --type process` |
654
- | Create a task | `palaia write "fix bug" --type task --priority high` |
655
- | Mark task done | `palaia edit <id> --status done` |
681
+ | Save an important fact or decision | `palaia write "..." --type memory` (ranks higher than auto-capture) |
682
+ | Document a reusable workflow | `palaia write "Domain: Steps..." --type process --scope team` |
683
+ | Leave a reminder for future sessions | `palaia write "check X after Y" --type task` (auto-deleted when done) |
684
+ | Mark a reminder as done | `palaia edit <id> --status done` (deletes the task) |
656
685
  | Find something | `palaia query "..."` |
657
- | Find open tasks | `palaia list --type task --status open` |
686
+ | Find open reminders | `palaia list --type task --status open` |
658
687
  | Check system health | `palaia status` |
659
688
  | Something is wrong | `palaia doctor --fix` |
660
689
  | Clean up old entries | `palaia gc` |
@@ -662,11 +691,11 @@ palaia write "## Dev-Agent Lifecycle
662
691
  | Review accumulated knowledge | `palaia curate analyze` |
663
692
  | Share knowledge | `palaia sync export` or `palaia package export` |
664
693
  | Check for messages | `palaia memo inbox` |
665
- | Start of session | Session briefing is now automatic. Just run `palaia doctor` and check `palaia memo inbox`. |
694
+ | Start of session | Session briefing is automatic. Just run `palaia doctor` and check `palaia memo inbox`. |
666
695
 
667
- **Do NOT manually write:** facts, decisions, or preferences that came up in the current conversation. Auto-Capture handles these.
696
+ **Auto-capture** handles routine conversation knowledge. **Manual writes rank higher** in recall use them when something is especially important, reusable, or comes from an external source.
668
697
 
669
- **DO manually write:** processes, tasks with structured fields, knowledge from external sources, project setup.
698
+ **DO manually write:** key decisions, reusable processes (team runbooks), sticky-note reminders (tasks), important facts from external sources.
670
699
 
671
700
  ---
672
701
 
@@ -706,10 +735,10 @@ palaia init --capture-level <off|minimal|normal|aggressive>
706
735
  Session continuity gives agents automatic context restoration across sessions. These features work out of the box with the OpenClaw plugin -- no manual setup needed.
707
736
 
708
737
  ### Session Briefings
709
- On session start, Palaia automatically injects a briefing with the last session summary and any open tasks. This means agents resume work without needing to manually search for context.
738
+ Session briefings are injected automatically at session start. They contain your last session summary and any open tasks (sticky notes). Read the briefing carefully it is your primary context for continuing previous work. Do not ask the user "where did we leave off?" when a briefing is present. Instead, acknowledge the context and continue seamlessly.
710
739
 
711
740
  ### Session Summaries
712
- When a session ends or resets, Palaia auto-saves a summary of what happened. These are stored as entries with the `session-summary` tag and can be queried:
741
+ When a session ends or resets, palaia auto-saves a summary of what happened. These are stored as entries with the `session-summary` tag and can be queried:
713
742
  ```bash
714
743
  palaia query "session-summary" --tags session-summary
715
744
  ```
@@ -721,7 +750,7 @@ Wrap sensitive content in `<private>...</private>` blocks to exclude it from aut
721
750
  Fresh memories are ranked higher in recall results. The boost factor is configurable via `recallRecencyBoost` (default `0.3`, set to `0` to disable).
722
751
 
723
752
  ### Progressive Disclosure
724
- When result sets exceed 100 entries, Palaia uses compact mode to keep context manageable. Use `--limit` to control result size explicitly:
753
+ When result sets exceed 100 entries, palaia uses compact mode to keep context manageable. Use `--limit` to control result size explicitly:
725
754
  ```bash
726
755
  palaia list --type task --status open --limit 5
727
756
  ```
@@ -750,6 +779,7 @@ Set in `openclaw.json` under `plugins.entries.palaia.config`:
750
779
  | `sessionBriefingMaxChars` | `1500` | Max chars for session briefing injection |
751
780
  | `captureToolObservations` | `true` | Track tool usage as session context |
752
781
  | `recallRecencyBoost` | `0.3` | Boost factor for fresh memories (0=off) |
782
+ | `manualEntryBoost` | `1.3` | Boost factor for manually written entries vs auto-captured (1.0=off) |
753
783
 
754
784
  ---
755
785
 
package/src/config.ts CHANGED
@@ -12,7 +12,7 @@ export interface RecallTypeWeights {
12
12
  export interface PalaiaPluginConfig {
13
13
  /** Path to palaia binary (default: auto-detect) */
14
14
  binaryPath?: string;
15
- /** Palaia workspace path (default: agent workspace) */
15
+ /** palaia workspace path (default: agent workspace) */
16
16
  workspace?: string;
17
17
  /** Default tier filter: "hot" | "warm" | "all" */
18
18
  tier: string;
@@ -74,6 +74,8 @@ export interface PalaiaPluginConfig {
74
74
  captureToolObservations: boolean;
75
75
  /** Recency boost factor for recall (0 = off, 0.3 = 30% boost for <24h entries) */
76
76
  recallRecencyBoost: number;
77
+ /** Boost factor for manually written entries vs auto-captured (default: 1.3 = 30% boost) */
78
+ manualEntryBoost: number;
77
79
  }
78
80
 
79
81
  export const DEFAULT_RECALL_TYPE_WEIGHTS: RecallTypeWeights = {
@@ -103,6 +105,7 @@ export const DEFAULT_CONFIG: PalaiaPluginConfig = {
103
105
  sessionBriefingMaxChars: 1500,
104
106
  captureToolObservations: true,
105
107
  recallRecencyBoost: 0.3,
108
+ manualEntryBoost: 1.3,
106
109
  };
107
110
 
108
111
  /**
@@ -33,7 +33,7 @@ import {
33
33
  extractWithLLM,
34
34
  shouldAttemptCapture,
35
35
  extractSignificance,
36
- stripPalaiaInjectedContext,
36
+ strippalaiaInjectedContext,
37
37
  stripPrivateBlocks,
38
38
  trimToRecentExchanges,
39
39
  parsePalaiaHints,
@@ -174,12 +174,12 @@ async function buildMemoryContext(
174
174
  }
175
175
 
176
176
  // Apply type-weighted reranking and blocked filtering (Issue #121)
177
- const rankedRaw = rerankByTypeWeight(entries, resolvedPrio.recallTypeWeight, config.recallRecencyBoost);
177
+ const rankedRaw = rerankByTypeWeight(entries, resolvedPrio.recallTypeWeight, config.recallRecencyBoost, config.manualEntryBoost);
178
178
  const ranked = filterBlocked(rankedRaw, resolvedPrio.blocked);
179
179
 
180
180
  // Build context string — progressive disclosure for large stores
181
181
  const compact = shouldUseCompactMode(ranked.length);
182
- let text = "## Active Memory (Palaia)\n\n";
182
+ let text = "## Active Memory (palaia)\n\n";
183
183
  if (compact) {
184
184
  text += "_Compact mode — use `memory_get <id>` for full details._\n\n";
185
185
  }
@@ -193,7 +193,7 @@ async function buildMemoryContext(
193
193
  }
194
194
 
195
195
  // Build nudge text and check remaining budget before appending
196
- const USAGE_NUDGE = "[palaia] auto-capture=on. Manual write: --type process (SOPs/checklists) or --type task (todos with assignee/deadline) only. Conversation knowledge is auto-captured do not duplicate with manual writes.";
196
+ const USAGE_NUDGE = "[palaia] auto-capture=on. Manual writes rank higher in recall. Use --type process for reusable workflows, --type task for sticky-note reminders (auto-deleted when done), --type memory for important facts.";
197
197
  let agentNudges = "";
198
198
  try {
199
199
  const pluginState = await loadPluginState(config.workspace);
@@ -203,7 +203,7 @@ async function buildMemoryContext(
203
203
  }
204
204
  const { nudges } = checkNudges(pluginState);
205
205
  if (nudges.length > 0) {
206
- agentNudges = "\n\n## Agent Nudge (Palaia)\n\n" + nudges.join("\n\n");
206
+ agentNudges = "\n\n## Agent Nudge (palaia)\n\n" + nudges.join("\n\n");
207
207
  }
208
208
  await savePluginState(pluginState, config.workspace);
209
209
  } catch {
@@ -255,11 +255,17 @@ async function runAutoCapture(
255
255
  logger: { info(...a: unknown[]): void; warn(...a: unknown[]): void },
256
256
  ): Promise<boolean> {
257
257
  if (!config.autoCapture) return false;
258
- if (!messages || messages.length === 0) return false;
258
+ if (!messages || messages.length === 0) {
259
+ logger.info("[palaia] Auto-capture skipped: no messages");
260
+ return false;
261
+ }
259
262
 
260
263
  const allTexts = extractMessageTexts(messages);
261
264
  const userTurns = allTexts.filter((t) => t.role === "user").length;
262
- if (userTurns < config.captureMinTurns) return false;
265
+ if (userTurns < config.captureMinTurns) {
266
+ logger.info(`[palaia] Auto-capture skipped: ${userTurns} user turns < captureMinTurns=${config.captureMinTurns}`);
267
+ return false;
268
+ }
263
269
 
264
270
  // Parse capture hints
265
271
  const collectedHints: { project?: string; scope?: string }[] = [];
@@ -272,7 +278,7 @@ async function runAutoCapture(
272
278
  const cleanedTexts = allTexts.map(t => ({
273
279
  ...t,
274
280
  text: stripPrivateBlocks(
275
- t.role === "user" ? stripPalaiaInjectedContext(t.text) : t.text
281
+ t.role === "user" ? strippalaiaInjectedContext(t.text) : t.text
276
282
  ),
277
283
  }));
278
284
  const recentTexts = trimToRecentExchanges(cleanedTexts);
@@ -283,7 +289,10 @@ async function runAutoCapture(
283
289
  }
284
290
  const exchangeText = exchangeParts.join("\n");
285
291
 
286
- if (!shouldAttemptCapture(exchangeText)) return false;
292
+ if (!shouldAttemptCapture(exchangeText)) {
293
+ logger.info(`[palaia] Auto-capture skipped: content did not pass significance filter (${exchangeText.length} chars)`);
294
+ return false;
295
+ }
287
296
 
288
297
  const hookOpts = buildRunnerOpts(config);
289
298
  const knownProjects = await loadProjects(hookOpts);
@@ -295,6 +304,7 @@ async function runAutoCapture(
295
304
  try {
296
305
  const results = await extractWithLLM(messages, api.config, {
297
306
  captureModel: config.captureModel,
307
+ workspace: config.workspace,
298
308
  }, knownProjects);
299
309
 
300
310
  for (const r of results) {
@@ -330,7 +340,10 @@ async function runAutoCapture(
330
340
  if (!llmHandled) {
331
341
  if (config.captureFrequency === "significant") {
332
342
  const significance = extractSignificance(exchangeText);
333
- if (!significance) return false;
343
+ if (!significance) {
344
+ logger.info("[palaia] Auto-capture skipped: rule-based extraction found no significance (need ≥2 distinct tags)");
345
+ return false;
346
+ }
334
347
  const tags = [...significance.tags];
335
348
  if (!tags.includes("auto-capture")) tags.push("auto-capture");
336
349
  const scope = effectiveCaptureScope !== "team"
@@ -379,7 +392,7 @@ export function createPalaiaContextEngine(
379
392
  const engine: ContextEngine = {
380
393
  info: {
381
394
  id: "palaia",
382
- name: "Palaia Memory",
395
+ name: "palaia Memory",
383
396
  version: "2.3",
384
397
  },
385
398
 
@@ -237,12 +237,8 @@ WHAT TO CAPTURE (be thorough — capture anything worth remembering):
237
237
  - Project context changes: scope changes, timeline shifts, requirement updates, priority changes
238
238
  - Workflow patterns the user established ("my process is...", "I always do X before Y")
239
239
 
240
- STRICT TASK CLASSIFICATION RULESa "task" MUST have ALL three of:
241
- 1. A clear, completable action (not just an observation or idea)
242
- 2. An identifiable responsible party (explicitly named or unambiguously inferable from context)
243
- 3. A concrete deliverable or measurable end state
244
- If ANY of these is missing, classify as "memory" instead of "task". When in doubt, use "memory".
245
- Observations, learnings, insights, opinions, and general knowledge are ALWAYS "memory", never "task".
240
+ IMPORTANT: Never classify as "task". Tasks are manually created sticky notes (post-its) they must only come from explicit user/agent intent via palaia write --type task. Auto-capture must use "memory" or "process" only.
241
+ Observations, learnings, insights, opinions, action items, and general knowledge are ALWAYS "memory" or "process", never "task".
246
242
 
247
243
  Only extract genuinely significant knowledge. Skip small talk, acknowledgments, routine exchanges.
248
244
  Do NOT extract if similar knowledge was likely captured in a recent exchange. Prefer quality over quantity. Skip routine status updates and acknowledgments.
@@ -461,7 +457,7 @@ export function trimToRecentExchanges(
461
457
  export async function extractWithLLM(
462
458
  messages: unknown[],
463
459
  config: any,
464
- pluginConfig?: { captureModel?: string },
460
+ pluginConfig?: { captureModel?: string; workspace?: string },
465
461
  knownProjects?: CachedProject[],
466
462
  ): Promise<ExtractionResult[]> {
467
463
  const runEmbeddedPiAgent = await getEmbeddedPiAgent();
@@ -472,10 +468,10 @@ export async function extractWithLLM(
472
468
  }
473
469
 
474
470
  const allTexts = extractMessageTexts(messages);
475
- // Strip Palaia-injected recall context and private blocks from user messages
471
+ // Strip palaia-injected recall context and private blocks from user messages
476
472
  const cleanedTexts = allTexts.map(t =>
477
473
  t.role === "user"
478
- ? { ...t, text: stripPrivateBlocks(stripPalaiaInjectedContext(t.text)) }
474
+ ? { ...t, text: stripPrivateBlocks(strippalaiaInjectedContext(t.text)) }
479
475
  : { ...t, text: stripPrivateBlocks(t.text) }
480
476
  );
481
477
  // Only extract from recent exchanges — full history causes LLM timeouts
@@ -519,7 +515,7 @@ export async function extractWithLLM(
519
515
  const result = await runEmbeddedPiAgent({
520
516
  sessionId,
521
517
  sessionFile,
522
- workspaceDir: config?.agents?.defaults?.workspace ?? process.cwd(),
518
+ workspaceDir: pluginConfig?.workspace || config?.agents?.defaults?.workspace || process.cwd(),
523
519
  config,
524
520
  prompt,
525
521
  timeoutMs: 15_000,
@@ -551,7 +547,8 @@ export async function extractWithLLM(
551
547
  const content = typeof item.content === "string" ? item.content.trim() : "";
552
548
  if (!content) continue;
553
549
 
554
- const validTypes = new Set(["memory", "process", "task"]);
550
+ const validTypes = new Set(["memory", "process"]);
551
+ // Tasks are manual-only (post-its) — auto-capture never creates tasks
555
552
  const type = validTypes.has(item.type) ? item.type : "memory";
556
553
 
557
554
  const validTags = new Set([
@@ -614,9 +611,9 @@ const SIGNIFICANCE_RULES: Array<{
614
611
  { pattern: /(?:mistake was|fehler war|should have|hätten sollen|next time)/i, tag: "lesson", type: "memory" },
615
612
  // Surprises
616
613
  { pattern: /(?:surprising|überraschend|unexpected|unerwartet|didn'?t expect|nicht erwartet|plot twist)/i, tag: "surprise", type: "memory" },
617
- // Commitments and tasks
618
- { pattern: /(?:i will|ich werde|todo:|action item|must do|muss noch|need to|commit to|verspreche)/i, tag: "commitment", type: "task" },
619
- { pattern: /(?:deadline|frist|due date|bis zum|by end of|spätestens)/i, tag: "commitment", type: "task" },
614
+ // Commitments (captured as memory — tasks are manual-only post-its)
615
+ { pattern: /(?:i will|ich werde|todo:|action item|must do|muss noch|need to|commit to|verspreche)/i, tag: "commitment", type: "memory" },
616
+ { pattern: /(?:deadline|frist|due date|bis zum|by end of|spätestens)/i, tag: "commitment", type: "memory" },
620
617
  // Processes and workflows
621
618
  { pattern: /(?:the process is|der prozess|steps?:|workflow:|how to|anleitung|recipe:|checklist)/i, tag: "process", type: "process" },
622
619
  { pattern: /(?:first,?\s.*then|schritt \d|step \d|1\.\s.*2\.\s)/i, tag: "process", type: "process" },
@@ -725,22 +722,22 @@ export function extractSignificance(
725
722
  }
726
723
 
727
724
  /**
728
- * Strip Palaia-injected recall context from message text.
725
+ * Strip palaia-injected recall context from message text.
729
726
  * The recall block is prepended to user messages by before_prompt_build via prependContext.
730
727
  * OpenClaw merges it into the user message, so agent_end sees it as user content.
731
728
  * Without stripping, auto-capture re-captures the injected memories -> feedback loop.
732
729
  *
733
730
  * The block has a stable structure:
734
- * - Starts with "## Active Memory (Palaia)"
731
+ * - Starts with "## Active Memory (palaia)"
735
732
  * - Contains [t/m], [t/pr], [t/tk] prefixed entries
736
733
  * - Ends with "[palaia] auto-capture=on..." nudge line
737
734
  */
738
- export function stripPalaiaInjectedContext(text: string): string {
739
- // Pattern: "## Active Memory (Palaia)" ... "[palaia] auto-capture=on..." + optional trailing newlines
735
+ export function strippalaiaInjectedContext(text: string): string {
736
+ // Pattern: "## Active Memory (palaia)" ... "[palaia] auto-capture=on..." + optional trailing newlines
740
737
  // The nudge line is always present and marks the end of the injected block
741
- const PALAIA_BLOCK_RE = /## Active Memory \(Palaia\)[\s\S]*?\[palaia\][^\n]*\n*/;
738
+ const PALAIA_BLOCK_RE = /## Active Memory \(palaia\)[\s\S]*?\[palaia\][^\n]*\n*/;
742
739
  // Also strip Session Briefing blocks
743
- const BRIEFING_BLOCK_RE = /## Session Briefing \(Palaia\)[\s\S]*?(?=\n##|\n\n\n|$)/;
740
+ const BRIEFING_BLOCK_RE = /## Session Briefing \(palaia\)[\s\S]*?(?=\n##|\n\n\n|$)/;
744
741
  return text
745
742
  .replace(PALAIA_BLOCK_RE, '')
746
743
  .replace(BRIEFING_BLOCK_RE, '')
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Lifecycle hooks for the Palaia OpenClaw plugin.
2
+ * Lifecycle hooks for the palaia OpenClaw plugin.
3
3
  *
4
4
  * - before_prompt_build: Query-based contextual recall (Issue #65).
5
5
  * Returns appendSystemContext with brain instruction when memory is used.
@@ -81,7 +81,7 @@ export {
81
81
  isNoiseContent,
82
82
  shouldAttemptCapture,
83
83
  extractSignificance,
84
- stripPalaiaInjectedContext,
84
+ strippalaiaInjectedContext,
85
85
  } from "./capture.js";
86
86
 
87
87
  // Reaction exports
@@ -130,7 +130,7 @@ import {
130
130
  resolveCaptureModel,
131
131
  shouldAttemptCapture,
132
132
  extractSignificance,
133
- stripPalaiaInjectedContext,
133
+ strippalaiaInjectedContext,
134
134
  trimToRecentExchanges,
135
135
  setLogger as setCaptureLogger,
136
136
  getLlmImportFailureLogged,
@@ -278,7 +278,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
278
278
  // ── /palaia status command ─────────────────────────────────────
279
279
  api.registerCommand({
280
280
  name: "palaia-status",
281
- description: "Show Palaia memory status",
281
+ description: "Show palaia memory status",
282
282
  async handler(_args: string) {
283
283
  try {
284
284
  const state = await loadPluginState(config.workspace);
@@ -293,7 +293,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
293
293
 
294
294
  return { text: formatStatusResponse(state, stats, config) };
295
295
  } catch (error) {
296
- return { text: `Palaia status error: ${error}` };
296
+ return { text: `palaia status error: ${error}` };
297
297
  }
298
298
  },
299
299
  });
@@ -482,13 +482,13 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
482
482
  }
483
483
 
484
484
  // Apply type-weighted reranking and blocked filtering (Issue #121)
485
- const rankedRaw = rerankByTypeWeight(entries, resolvedPrio.recallTypeWeight, config.recallRecencyBoost);
485
+ const rankedRaw = rerankByTypeWeight(entries, resolvedPrio.recallTypeWeight, config.recallRecencyBoost, config.manualEntryBoost);
486
486
  const ranked = filterBlocked(rankedRaw, resolvedPrio.blocked);
487
487
 
488
488
  // Build context string with char budget
489
489
  // Progressive disclosure: compact mode for large stores (title + first line + ID)
490
490
  const compact = shouldUseCompactMode(ranked.length);
491
- let text = "## Active Memory (Palaia)\n\n";
491
+ let text = "## Active Memory (palaia)\n\n";
492
492
  if (compact) {
493
493
  text += "_Compact mode — use `memory_get <id>` for full details._\n\n";
494
494
  }
@@ -512,7 +512,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
512
512
  }
513
513
  const { nudges } = checkNudges(pluginState);
514
514
  if (nudges.length > 0) {
515
- nudgeContext = "\n\n## Agent Nudge (Palaia)\n\n" + nudges.join("\n\n");
515
+ nudgeContext = "\n\n## Agent Nudge (palaia)\n\n" + nudges.join("\n\n");
516
516
  }
517
517
  await savePluginState(pluginState, resolved.workspace);
518
518
  } catch {
@@ -556,7 +556,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
556
556
  return {
557
557
  prependContext: briefingText + text,
558
558
  appendSystemContext: config.showMemorySources
559
- ? "You used Palaia memory in this turn. Add \u{1f9e0} at the very end of your response (after everything else, on its own line)."
559
+ ? "You used palaia memory in this turn. Add \u{1f9e0} at the very end of your response (after everything else, on its own line)."
560
560
  : undefined,
561
561
  };
562
562
  } catch (error) {
@@ -587,6 +587,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
587
587
  const hookOpts = buildRunnerOpts(config, { workspace: resolved.workspace });
588
588
 
589
589
  if (!event.success || !event.messages || event.messages.length === 0) {
590
+ logger.info(`[palaia] Auto-capture skipped: success=${event.success}, messages=${event.messages?.length ?? 0}`);
590
591
  return;
591
592
  }
592
593
 
@@ -597,6 +598,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
597
598
 
598
599
  const userTurns = allTexts.filter((t) => t.role === "user").length;
599
600
  if (userTurns < config.captureMinTurns) {
601
+ logger.info(`[palaia] Auto-capture skipped: ${userTurns} user turns < captureMinTurns=${config.captureMinTurns}`);
600
602
  return;
601
603
  }
602
604
 
@@ -607,7 +609,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
607
609
  collectedHints.push(...hints);
608
610
  }
609
611
 
610
- // Strip Palaia-injected recall context and private blocks from messages.
612
+ // Strip palaia-injected recall context and private blocks from messages.
611
613
  // The recall block is prepended to user messages by before_prompt_build.
612
614
  // Without stripping, auto-capture would re-capture previously recalled memories.
613
615
  // Private blocks (<private>...</private>) must be excluded from capture.
@@ -615,7 +617,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
615
617
  const cleanedTexts = allTexts.map(t => ({
616
618
  ...t,
617
619
  text: stripPrivateBlocks(
618
- t.role === "user" ? stripPalaiaInjectedContext(t.text) : t.text
620
+ t.role === "user" ? strippalaiaInjectedContext(t.text) : t.text
619
621
  ),
620
622
  }));
621
623
 
@@ -632,6 +634,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
632
634
  const exchangeText = exchangeParts.join("\n");
633
635
 
634
636
  if (!shouldAttemptCapture(exchangeText)) {
637
+ logger.info(`[palaia] Auto-capture skipped: content did not pass significance filter (${exchangeText.length} chars)`);
635
638
  return;
636
639
  }
637
640
 
@@ -733,6 +736,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
733
736
  try {
734
737
  const results = await extractWithLLM(event.messages, api.config, {
735
738
  captureModel: config.captureModel,
739
+ workspace: resolved.workspace,
736
740
  }, knownProjects);
737
741
 
738
742
  await storeLLMResults(results);
@@ -752,6 +756,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
752
756
  // Retry without captureModel -> resolveCaptureModel will use primary model
753
757
  const fallbackResults = await extractWithLLM(event.messages, api.config, {
754
758
  captureModel: undefined,
759
+ workspace: resolved.workspace,
755
760
  }, knownProjects);
756
761
  await storeLLMResults(fallbackResults);
757
762
  llmHandled = true;
@@ -776,6 +781,7 @@ export function registerHooks(api: OpenClawPluginApi, config: PalaiaPluginConfig
776
781
  if (config.captureFrequency === "significant") {
777
782
  const significance = extractSignificance(exchangeText);
778
783
  if (!significance) {
784
+ logger.info("[palaia] Auto-capture skipped: rule-based extraction found no significance (need ≥2 distinct tags)");
779
785
  return;
780
786
  }
781
787
  captureData = significance;
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { RecallTypeWeights } from "../config.js";
9
- import { stripPalaiaInjectedContext } from "./capture.js";
9
+ import { strippalaiaInjectedContext } from "./capture.js";
10
10
 
11
11
  // ============================================================================
12
12
  // Types
@@ -79,7 +79,7 @@ export function buildFootnote(
79
79
  const dateStr = formatShortDate(e.date);
80
80
  return dateStr ? `"${e.title}" (${dateStr})` : `"${e.title}"`;
81
81
  });
82
- return `\n\n\u{1f4ce} Palaia: ${parts.join(", ")}`;
82
+ return `\n\n\u{1f4ce} palaia: ${parts.join(", ")}`;
83
83
  }
84
84
 
85
85
  // Re-export formatShortDate from state for use here
@@ -96,7 +96,7 @@ const TRANSPARENCY_RECALL_THRESHOLD = 50;
96
96
  const TRANSPARENCY_DAYS_THRESHOLD = 7;
97
97
 
98
98
  const SATISFACTION_NUDGE_TEXT =
99
- "Your user has been using Palaia for a while now. " +
99
+ "Your user has been using palaia for a while now. " +
100
100
  "Ask them casually if they're happy with the memory system. " +
101
101
  "If there are issues, suggest `palaia doctor`.";
102
102
 
@@ -268,7 +268,7 @@ function isSystemOnlyContent(text: string): boolean {
268
268
  export function buildRecallQuery(messages: unknown[]): string {
269
269
  const texts = extractMessageTexts(messages).map(t =>
270
270
  t.role === "user"
271
- ? { ...t, text: stripPalaiaInjectedContext(t.text) }
271
+ ? { ...t, text: strippalaiaInjectedContext(t.text) }
272
272
  : t
273
273
  );
274
274
 
@@ -367,12 +367,17 @@ export function rerankByTypeWeight(
367
367
  results: QueryResult["results"],
368
368
  weights: Record<string, number>,
369
369
  recencyBoost = 0,
370
+ manualEntryBoost = 1.3,
370
371
  ): RankedEntry[] {
371
372
  return results
372
373
  .map((r) => {
373
374
  const type = r.type || "memory";
374
375
  const weight = weights[type] ?? 1.0;
375
376
  const recency = calcRecencyBoost(r.created, recencyBoost);
377
+ // Manual entries (no auto-capture tag) get a boost over auto-captured ones.
378
+ // This ensures intentionally stored knowledge ranks higher than conversation noise.
379
+ const isAutoCapture = r.tags?.includes("auto-capture") ?? false;
380
+ const sourceBoost = isAutoCapture ? 1.0 : manualEntryBoost;
376
381
  return {
377
382
  id: r.id,
378
383
  body: r.content || r.body || "",
@@ -383,7 +388,7 @@ export function rerankByTypeWeight(
383
388
  score: r.score,
384
389
  bm25Score: r.bm25_score,
385
390
  embedScore: r.embed_score,
386
- weightedScore: r.score * weight * recency,
391
+ weightedScore: r.score * weight * recency * sourceBoost,
387
392
  created: r.created,
388
393
  tags: r.tags,
389
394
  };
@@ -20,7 +20,7 @@ import {
20
20
  type ToolObservation,
21
21
  } from "./state.js";
22
22
  import {
23
- stripPalaiaInjectedContext,
23
+ strippalaiaInjectedContext,
24
24
  stripPrivateBlocks,
25
25
  trimToRecentExchanges,
26
26
  extractWithLLM,
@@ -111,7 +111,7 @@ export function formatBriefing(briefing: PendingBriefing, maxChars: number): str
111
111
  if (maxChars <= 0) return "";
112
112
  if (!briefing.summary && briefing.openTasks.length === 0) return "";
113
113
 
114
- const parts: string[] = ["## Session Briefing (Palaia)\n"];
114
+ const parts: string[] = ["## Session Briefing (palaia)\n"];
115
115
 
116
116
  if (briefing.summary) {
117
117
  const agoMs = Date.now() - briefing.timestamp;
@@ -161,6 +161,7 @@ export async function captureSessionSummary(
161
161
  try {
162
162
  const results = await extractWithLLM(messages, api.config, {
163
163
  captureModel: config.captureModel,
164
+ workspace: config.workspace,
164
165
  }, []);
165
166
 
166
167
  if (results.length > 0) {
@@ -176,7 +177,7 @@ export async function captureSessionSummary(
176
177
  const cleaned = allTexts.map((t: { role: string; text: string }) => ({
177
178
  ...t,
178
179
  text: stripPrivateBlocks(
179
- t.role === "user" ? stripPalaiaInjectedContext(t.text) : t.text
180
+ t.role === "user" ? strippalaiaInjectedContext(t.text) : t.text
180
181
  ),
181
182
  }));
182
183
  const recent = trimToRecentExchanges(cleaned, 3);
@@ -338,10 +338,11 @@ const VALID_SCOPES = ["private", "team", "public"];
338
338
 
339
339
  /**
340
340
  * Check if a scope string is valid for palaia write.
341
- * Valid: "private", "team", "public", or any "shared:*" prefix.
341
+ * Valid: "private", "team", "public".
342
+ * Legacy shared:X is accepted but normalized to "team" by the CLI.
342
343
  */
343
344
  export function isValidScope(s: string): boolean {
344
- return VALID_SCOPES.includes(s) || s.startsWith("shared:");
345
+ return VALID_SCOPES.includes(s);
345
346
  }
346
347
 
347
348
  /**
@@ -395,7 +396,7 @@ export function formatStatusResponse(
395
396
  stats: Record<string, unknown>,
396
397
  config: PalaiaPluginConfig,
397
398
  ): string {
398
- const lines: string[] = ["Palaia Memory Status", ""];
399
+ const lines: string[] = ["palaia Memory Status", ""];
399
400
 
400
401
  // Recall count
401
402
  const sinceDate = state.firstRecallTimestamp
package/src/runner.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Palaia CLI subprocess runner.
2
+ * palaia CLI subprocess runner.
3
3
  *
4
4
  * Executes palaia CLI commands, parses JSON output, handles binary detection
5
5
  * and timeouts. This is the bridge between the OpenClaw plugin and the
@@ -92,7 +92,7 @@ export async function detectBinary(
92
92
  }
93
93
 
94
94
  throw new Error(
95
- "Palaia binary not found. Install with: pip install palaia\n" +
95
+ "palaia binary not found. Install with: pip install palaia\n" +
96
96
  "Or set binaryPath in plugin config."
97
97
  );
98
98
  }
@@ -129,7 +129,7 @@ function execCommand(
129
129
  (error, stdout, stderr) => {
130
130
  if (error && (error as any).killed) {
131
131
  reject(
132
- new Error(`Palaia command timed out after ${timeout}ms: ${cmd} ${args.join(" ")}`)
132
+ new Error(`palaia command timed out after ${timeout}ms: ${cmd} ${args.join(" ")}`)
133
133
  );
134
134
  return;
135
135
  }
@@ -184,7 +184,7 @@ export async function run(
184
184
 
185
185
  if (result.exitCode !== 0) {
186
186
  const errMsg = result.stderr.trim() || result.stdout.trim();
187
- throw new Error(`Palaia CLI error (exit ${result.exitCode}): ${errMsg}`);
187
+ throw new Error(`palaia CLI error (exit ${result.exitCode}): ${errMsg}`);
188
188
  }
189
189
 
190
190
  return result.stdout;
package/src/tools.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Agent tools: memory_search, memory_get, memory_write.
3
3
  *
4
- * These tools are the core of the Palaia OpenClaw integration.
4
+ * These tools are the core of the palaia OpenClaw integration.
5
5
  * They shell out to the palaia CLI with --json and return results
6
6
  * in the format OpenClaw agents expect.
7
7
  */
8
8
 
9
9
  import { Type } from "@sinclair/typebox";
10
- import { run, runJson, type RunnerOpts } from "./runner.js";
10
+ import { run, runJson, getEmbedServerManager, type RunnerOpts } from "./runner.js";
11
11
  import type { PalaiaPluginConfig } from "./config.js";
12
12
  import { sanitizeScope, isValidScope } from "./hooks/index.js";
13
13
  import { loadPriorities, resolvePriorities } from "./priorities.js";
@@ -62,7 +62,7 @@ function buildRunnerOpts(config: PalaiaPluginConfig): RunnerOpts {
62
62
  }
63
63
 
64
64
  /**
65
- * Register all Palaia agent tools on the given plugin API.
65
+ * Register all palaia agent tools on the given plugin API.
66
66
  */
67
67
  export function registerTools(api: OpenClawPluginApi, config: PalaiaPluginConfig): void {
68
68
  const opts = buildRunnerOpts(config);
@@ -71,7 +71,7 @@ export function registerTools(api: OpenClawPluginApi, config: PalaiaPluginConfig
71
71
  api.registerTool({
72
72
  name: "memory_search",
73
73
  description:
74
- "Semantically search Palaia memory for relevant notes and context.",
74
+ "Semantically search palaia memory for relevant notes and context.",
75
75
  parameters: Type.Object({
76
76
  query: Type.String({ description: "Search query" }),
77
77
  maxResults: Type.Optional(
@@ -114,30 +114,48 @@ export function registerTools(api: OpenClawPluginApi, config: PalaiaPluginConfig
114
114
  }
115
115
 
116
116
  const limit = params.maxResults || config.maxResults || 5;
117
- const args: string[] = ["query", params.query, "--limit", String(limit)];
117
+ const includeCold = params.tier === "all" || config.tier === "all";
118
118
 
119
- // --all flag includes cold tier
120
- if (params.tier === "all" || config.tier === "all") {
121
- args.push("--all");
119
+ // Try embed server first — its queue correctly handles concurrent requests
120
+ // without counting wait time against the timeout.
121
+ let result: QueryResult | null = null;
122
+ if (config.embeddingServer) {
123
+ try {
124
+ const mgr = getEmbedServerManager(opts);
125
+ const resp = await mgr.query({
126
+ text: params.query,
127
+ top_k: limit,
128
+ include_cold: includeCold,
129
+ ...(params.type ? { type: params.type } : {}),
130
+ }, config.timeoutMs || 3000);
131
+ if (resp?.result?.results && Array.isArray(resp.result.results)) {
132
+ result = { results: resp.result.results };
133
+ }
134
+ } catch {
135
+ // Fall through to CLI
136
+ }
122
137
  }
123
138
 
124
- // Type filter (Issue #82)
125
- if (params.type) {
126
- args.push("--type", params.type);
139
+ // CLI fallback longer timeout since it includes process spawn overhead
140
+ if (!result) {
141
+ const args: string[] = ["query", params.query, "--limit", String(limit)];
142
+ if (includeCold) {
143
+ args.push("--all");
144
+ }
145
+ if (params.type) {
146
+ args.push("--type", params.type);
147
+ }
148
+ result = await runJson<QueryResult>(args, { ...opts, timeoutMs: 15000 });
127
149
  }
128
150
 
129
- const result = await runJson<QueryResult>(args, opts);
130
-
131
151
  // Apply scope visibility filter (Issue #145: agent isolation)
132
152
  let filteredResults = result.results || [];
133
153
  if (scopeVisibility) {
134
154
  filteredResults = filteredResults.filter((r) => {
135
155
  const scope = r.scope || "team";
136
- return scopeVisibility!.some((allowed) => {
137
- if (scope === allowed) return true;
138
- if (allowed === "shared" && scope.startsWith("shared:")) return true;
139
- return false;
140
- });
156
+ // Legacy shared:X entries are treated as team
157
+ const effectiveScope = scope.startsWith("shared:") ? "team" : scope;
158
+ return scopeVisibility!.includes(effectiveScope);
141
159
  });
142
160
  }
143
161
 
@@ -173,7 +191,7 @@ export function registerTools(api: OpenClawPluginApi, config: PalaiaPluginConfig
173
191
  // ── memory_get ─────────────────────────────────────────────────
174
192
  api.registerTool({
175
193
  name: "memory_get",
176
- description: "Read a specific Palaia memory entry by path or id.",
194
+ description: "Read a specific palaia memory entry by path or id.",
177
195
  parameters: Type.Object({
178
196
  path: Type.String({ description: "Memory path or UUID" }),
179
197
  from: Type.Optional(
@@ -213,12 +231,12 @@ export function registerTools(api: OpenClawPluginApi, config: PalaiaPluginConfig
213
231
  {
214
232
  name: "memory_write",
215
233
  description:
216
- "Write a new memory entry to Palaia. WAL-backed, crash-safe.",
234
+ "Write a new memory entry to palaia. WAL-backed, crash-safe.",
217
235
  parameters: Type.Object({
218
236
  content: Type.String({ description: "Memory content to write" }),
219
237
  scope: Type.Optional(
220
238
  Type.String({
221
- description: "Scope: private|team|shared:X|public (default: team)",
239
+ description: "Scope: private|team|public (default: team)",
222
240
  default: "team",
223
241
  })
224
242
  ),
@@ -306,7 +324,7 @@ export function registerTools(api: OpenClawPluginApi, config: PalaiaPluginConfig
306
324
  content: [
307
325
  {
308
326
  type: "text" as const,
309
- text: `Invalid scope "${params.scope}". Valid scopes: private, team, public, shared:<name>`,
327
+ text: `Invalid scope "${params.scope}". Valid scopes: private, team, public`,
310
328
  },
311
329
  ],
312
330
  };
package/src/types.ts CHANGED
@@ -324,7 +324,7 @@ export type SubagentEndReason = "deleted" | "completed" | "swept" | "released";
324
324
  /**
325
325
  * ContextEngine interface — matches OpenClaw v2026.3.28.
326
326
  *
327
- * This is the full interface. Palaia implements a subset;
327
+ * This is the full interface. palaia implements a subset;
328
328
  * optional methods are marked with `?`.
329
329
  */
330
330
  export interface ContextEngine {