@unblocklabs/unblock-memory 0.8.3 → 0.10.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/README.md +194 -94
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +10 -5
- package/dist/src/diagnostics.d.ts +48 -4
- package/dist/src/diagnostics.js +11 -2
- package/dist/src/emotion-baselines.d.ts +120 -0
- package/dist/src/emotion-baselines.js +387 -0
- package/dist/src/inside-out-runtime.js +58 -9
- package/dist/src/inside-out-store.d.ts +37 -0
- package/dist/src/inside-out-store.js +137 -0
- package/dist/src/inside-out.d.ts +142 -7
- package/dist/src/inside-out.js +43 -72
- package/dist/src/manager.d.ts +2 -2
- package/dist/src/plugin.js +2 -0
- package/dist/src/response-judge.d.ts +1 -1
- package/dist/src/response-stages.d.ts +1 -1
- package/dist/src/response-text.d.ts +7 -0
- package/dist/src/response-text.js +14 -2
- package/dist/src/retrieval-telemetry.d.ts +1 -1
- package/dist/src/review-tools.js +4 -1
- package/dist/src/safety-config.d.ts +30 -0
- package/dist/src/safety-config.js +118 -0
- package/dist/src/safety-whisperer.d.ts +31 -0
- package/dist/src/safety-whisperer.js +302 -0
- package/dist/src/skill-whisperer.d.ts +1 -0
- package/dist/src/skill-whisperer.js +2 -2
- package/dist/src/typesafe-client.d.ts +2 -0
- package/dist/src/typesafe-client.js +7 -0
- package/dist/src/whisperer-prompt.d.ts +4 -2
- package/dist/src/whisperer-prompt.js +6 -5
- package/docs/configuration.md +282 -11
- package/docs/peoplesql.md +7 -0
- package/docs/retrieval.md +5 -3
- package/openclaw.plugin.json +99 -4
- package/package.json +1 -1
package/docs/configuration.md
CHANGED
|
@@ -16,6 +16,7 @@ settings; a rotated credential file is reread without a restart.
|
|
|
16
16
|
| Skill Whisperer | `skillWhisperer.enabled`, skills corpus, host hooks | Best local vector candidate meeting `minScore` | Exclude failed candidates; successful siblings may qualify; no vector fallback |
|
|
17
17
|
| Memory Whisperer | `memoryWhisperer.enabled`, explicit approved corpora, host hooks | No hints | Exclude failed candidates; recall/deadline failures still suppress the turn |
|
|
18
18
|
| Complementary hints | Enabled Memory Whisperer + `complementaryHints` | No additional judgment; base hints also require a key | Keep baseline hints unless the total deadline expires |
|
|
19
|
+
| Safety Whisperer | `safetyWhisperer.enabled`, at least one scenario, host hooks (conversation access and prompt injection) | Could not check: the turn proceeds without safety prompts (fails open) | Fails open; recorded as could-not-check, never as no match |
|
|
19
20
|
| People store/tools | `people.enabled` | Available; automatic save review needs primer/key or verified manual alternative | Storage/inspection still available |
|
|
20
21
|
| People Whisperer | People + `people.whisperer.enabled`, host hooks, eligible person/dossier, no prior thread receipt | Unchanged local lookup | Unchanged local lookup |
|
|
21
22
|
| People Primer / automatic dossier save review | People + `peoplePrimer.enabled`, approved corpora | No judgment; save requires source-specific manual verification | No automatic save; verify/retry instead |
|
|
@@ -25,8 +26,11 @@ settings; a rotated credential file is reread without a restart.
|
|
|
25
26
|
| Inside Out | `insideOut.enabled`; opts in all retained channels | No inference | Failed reviews are recorded and retried; successes stay cached |
|
|
26
27
|
| Clustering | Configured local `analysis.executable` | Unchanged | Unchanged; worker failures do not disable ordinary search |
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
Skill Whisperer turns on by default once a `skills` corpus is configured; without
|
|
30
|
+
one it registers no hook and reports inactive.
|
|
31
|
+
Memory/People Whisperers, people storage, the primer and audits default off.
|
|
32
|
+
`typesafe.enabled` defaults true but does not enable those opt-in features;
|
|
33
|
+
`sentimentEnabled` defaults true only **within an enabled
|
|
30
34
|
response audit**. `peoplePrimer` controls automatic dossier-save review;
|
|
31
35
|
`evidenceReview` is a different advisory tool. Disabling people injection does
|
|
32
36
|
not disable people tools/storage or erase dossiers.
|
|
@@ -215,8 +219,8 @@ contract; these tables explain their effects.
|
|
|
215
219
|
| `typesafe.enabled` | `true` | Shared plugin provider gate; no feature is opted in merely by adding a key |
|
|
216
220
|
| `typesafe.apiKey` | Unset | Explicit inline key; mutually exclusive with key file; prefer file |
|
|
217
221
|
| `typesafe.apiKeyFile` | Unset | Absolute raw-key or dotenv file, reread at credential resolution; explicit missing file never falls back to a different key |
|
|
218
|
-
| `typesafe.timeoutMs` | `1500` | 1–10,000 per request for Skill/Memory Whisperer, quality/claim/cluster review and response audit; **primer and dossier save use `peoplePrimer.timeoutMs` instead** |
|
|
219
|
-
| `skillWhisperer.enabled` | `
|
|
222
|
+
| `typesafe.timeoutMs` | `1500` | 1–10,000 per request for Skill/Memory Whisperer, quality/claim/cluster review and response audit; **primer and dossier save use `peoplePrimer.timeoutMs` and Safety Whisperer uses `safetyWhisperer.timeoutMs` instead** |
|
|
223
|
+
| `skillWhisperer.enabled` | `true` | Active only with a `skills` corpus (otherwise resolved to `false`, with no hook or host warning) and appropriate host hook access; fixed 3-second total deadline; `false` opts out |
|
|
220
224
|
| `skillWhisperer.historyMessages` | `5` | Nonnegative integer; prior visible messages used for routing |
|
|
221
225
|
| `skillWhisperer.minScore` | `0.5` | 0–1, **local vector fallback only**, ignored for TypeSafe shortlist admission |
|
|
222
226
|
| `skillWhisperer.cooldownTurns` | `10` | Nonnegative user-turn count; no fallback to weaker cooling-down alternatives |
|
|
@@ -230,6 +234,18 @@ contract; these tables explain their effects.
|
|
|
230
234
|
| `memoryWhisperer.cooldownTurns` | `10` | 0–1,000; recently injected evidence |
|
|
231
235
|
| `memoryWhisperer.timeoutMs` | `3000` | 1–10,000 total whisper deadline, not just the provider timeout |
|
|
232
236
|
| `memoryWhisperer.complementaryHints` | `false` | Optional extra redundancy judgment; does not expand retrieval or enable the feature |
|
|
237
|
+
| `safetyWhisperer.enabled` | `false` | Requires at least one scenario, a TypeSafe key and host hooks; see [Safety Whisperer](#safety-whisperer) |
|
|
238
|
+
| `safetyWhisperer.historyMessages` | `6` | 0–20 earlier visible messages, whole messages only, for resolving follow-ups |
|
|
239
|
+
| `safetyWhisperer.timeoutMs` | `3000` | 1–10,000 total deadline (credential and request); the host hook timeout must exceed the slowest enabled whisperer |
|
|
240
|
+
| `safetyWhisperer.excludedChannels` | `[]` | Up to 500 `{ provider, accountId, channelId, name? }`, matched case-insensitively; skips every scenario there |
|
|
241
|
+
| `safetyWhisperer.scenarios` | `[]` | Up to 20; unique `id` of letters, digits, `.`, `_` or `-` |
|
|
242
|
+
| `safetyWhisperer.scenarios[].instructions` | Required | Up to 2,000 characters: what the scenario covers, with exclusions; sent to TypeSafe |
|
|
243
|
+
| `safetyWhisperer.scenarios[].flaggedPrompt` | Required | Up to 2,000 characters, appended for matching unapproved requests; never sent to TypeSafe |
|
|
244
|
+
| `safetyWhisperer.scenarios[].minProbability` | `0.4` | 0–1 minimum yes-probability that the current request is covered |
|
|
245
|
+
| `safetyWhisperer.scenarios[].approvedRequestors` | `[]` | Up to 500 `{ provider, accountId, senderId, name? }`; `senderId` must match exactly; skip this scenario for them |
|
|
246
|
+
| `safetyWhisperer.scenarios[].excludedChannels` | `[]` | Up to 500 channels where only this scenario is skipped |
|
|
247
|
+
| `safetyWhisperer.scenarios[].agentIds` | Unset (every agent) | Non-empty list limits the scenario to these agents when one config serves several; normalized like OpenClaw agent IDs (lowercase) |
|
|
248
|
+
| `safetyWhisperer.scenarios[].enabled` | `true` | Disabled scenarios are never sent to TypeSafe |
|
|
233
249
|
|
|
234
250
|
No configured plugin credential means fallback to **`TYPESAFE_API_KEY` only** in
|
|
235
251
|
the Gateway environment. Unlike QMD's resolver, the plugin does not read a
|
|
@@ -364,6 +380,7 @@ boundary, not multi-tenant authorization. Approve sources for the agent's audien
|
|
|
364
380
|
| Cluster review | Up to 6 eligible complete sampled chunks, each at most 2,000 characters; conclusions only concern the sample |
|
|
365
381
|
| Response audit | Approved visible request/answer/context/feedback and bounded later response evidence, separated by assessment stage; optional current-index whole-short-document evidence from approved file corpora |
|
|
366
382
|
| Inside Out | Target human reply, nearest assistant text and bounded preceding visible history; channel-scoped speaker identity keys |
|
|
383
|
+
| Safety check | The complete current human message without OpenClaw's conversation-info header; the thread starter, replied-to message and channel messages since the last reply that arrived with it, which can include other people's names and message IDs; up to `historyMessages` earlier visible messages; and each eligible scenario's `instructions`. No sender, account or channel IDs from that header, approval lists or flagged prompts. A message wrapper Safety Whisperer does not recognize (anything other than Slack's) is sent whole, minus that header. |
|
|
367
384
|
| Standalone QMD query | Query, optional intent, selected excerpts, source paths and evaluation time; separate process/SDK credentials and collection scope |
|
|
368
385
|
|
|
369
386
|
Omitting tool-result/thinking/system fields does not remove their content if it
|
|
@@ -371,6 +388,157 @@ was quoted in ordinary visible text. Provider judgments are advisory; probabilit
|
|
|
371
388
|
or score is not proof. Enabling a feature approves only that feature's documented processing.
|
|
372
389
|
|
|
373
390
|
|
|
391
|
+
## Safety Whisperer
|
|
392
|
+
|
|
393
|
+
An optional, interim guardrail for agents that serve several people. Before the
|
|
394
|
+
main model runs on a human turn, it checks the current message against scenarios
|
|
395
|
+
you define, such as questions about one person's salary. When a requestor who is
|
|
396
|
+
not approved for a scenario asks something it covers, that scenario's
|
|
397
|
+
`flaggedPrompt` is appended next to their message for that turn.
|
|
398
|
+
|
|
399
|
+
**This is a prompt-level guardrail, not access control.** The classifier can miss
|
|
400
|
+
a request and the agent can ignore an injected instruction. It does not remove
|
|
401
|
+
sensitive information already available in context, memory or tools. An approved
|
|
402
|
+
requestor is not an approved audience: asking in a shared channel can still expose
|
|
403
|
+
an answer to everyone there. Use separate agents and real data-access controls for
|
|
404
|
+
hard boundaries.
|
|
405
|
+
|
|
406
|
+
```json
|
|
407
|
+
{
|
|
408
|
+
"safetyWhisperer": {
|
|
409
|
+
"enabled": true,
|
|
410
|
+
"excludedChannels": [
|
|
411
|
+
{ "name": "#leadership", "provider": "slack", "accountId": "work", "channelId": "C_LEADERSHIP" }
|
|
412
|
+
],
|
|
413
|
+
"scenarios": [
|
|
414
|
+
{
|
|
415
|
+
"id": "individual-salaries",
|
|
416
|
+
"instructions": "Requests about a specific individual employee's salary, pay or compensation, including colloquial phrasing such as how much someone makes or earns. Exclude vendors and contractors.",
|
|
417
|
+
"approvedRequestors": [
|
|
418
|
+
{ "name": "Dani", "provider": "slack", "accountId": "work", "senderId": "U_DANI" }
|
|
419
|
+
],
|
|
420
|
+
"excludedChannels": [
|
|
421
|
+
{ "name": "#people-ops", "provider": "slack", "accountId": "work", "channelId": "C_PEOPLE_OPS" }
|
|
422
|
+
],
|
|
423
|
+
"flaggedPrompt": "This requestor is not approved to access this information. Kindly tell them that; do not disclose the requested information."
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The plugin entry also needs `hooks.allowConversationAccess: true`, and
|
|
431
|
+
`allowPromptInjection` must not be `false`; see [host controls](#host-controls).
|
|
432
|
+
|
|
433
|
+
### How each turn is checked
|
|
434
|
+
|
|
435
|
+
1. Only user-triggered turns are checked, every time: there is no cooldown. A
|
|
436
|
+
prompt rebuild for the same run reuses that run's result. Messages steered
|
|
437
|
+
into a run that is already working are not checked; see
|
|
438
|
+
[messages sent while the agent works](#messages-sent-while-the-agent-works).
|
|
439
|
+
2. A global `excludedChannels` match skips every scenario. A scenario's own
|
|
440
|
+
`excludedChannels` skip only that scenario. Thread replies and forum topics
|
|
441
|
+
use their channel.
|
|
442
|
+
3. Disabled scenarios, scenarios limited to other `agentIds`, and scenarios that
|
|
443
|
+
approve this requestor are dropped.
|
|
444
|
+
4. If none remain, no request is made. Otherwise one TypeSafe request asks one
|
|
445
|
+
independent yes/no question per remaining scenario. It includes the complete
|
|
446
|
+
current message, the channel context that arrived with it (thread starter,
|
|
447
|
+
replied-to message, chat history since the agent's last reply) and up to
|
|
448
|
+
`historyMessages` earlier visible messages. A follow-up like "What about
|
|
449
|
+
Sally?" or "answer the question above" is therefore judged in context, while
|
|
450
|
+
the current message is judged, not whether an earlier one mentioned the topic.
|
|
451
|
+
5. Every scenario scoring at least its `minProbability` adds its `flaggedPrompt`
|
|
452
|
+
in `<safety>` tags, last in the shared `<unblock_memory>` block after the
|
|
453
|
+
human message.
|
|
454
|
+
|
|
455
|
+
### Messages sent while the agent works
|
|
456
|
+
|
|
457
|
+
OpenClaw's default queue mode, `steer`, adds a message sent during an active run
|
|
458
|
+
to that run without building a new prompt, so the message is never checked. The
|
|
459
|
+
same applies when a person types `/queue steer` in a session. For agents that
|
|
460
|
+
rely on Safety Whisperer, set `messages.queue.mode` (and any
|
|
461
|
+
`messages.queue.byChannel` entries) to `followup` or `collect`; `memory_diagnostics`
|
|
462
|
+
and the Gateway log warn with `queue_mode_steer` until then. In `collect` mode,
|
|
463
|
+
queued messages from different people carry no single sender, so none of them is
|
|
464
|
+
approved.
|
|
465
|
+
|
|
466
|
+
### Identity
|
|
467
|
+
|
|
468
|
+
Approval and exclusions compare `provider`, `accountId`, and `senderId` or
|
|
469
|
+
`channelId` values from the host's trusted turn metadata. `senderId` must match
|
|
470
|
+
exactly. `provider`, `accountId` and `channelId` match case-insensitively, because
|
|
471
|
+
OpenClaw lowercases them in places. Names are labels only; display names, message
|
|
472
|
+
text and identity claims never count. Missing identity never approves anyone and
|
|
473
|
+
never excludes a channel, so turns without a sender ID, such as CLI or webchat,
|
|
474
|
+
are treated as unapproved. Email-based approval is not supported.
|
|
475
|
+
|
|
476
|
+
For Slack:
|
|
477
|
+
|
|
478
|
+
- `provider` is `slack`.
|
|
479
|
+
- `accountId` is the OpenClaw Slack account key, or `default` when the channel
|
|
480
|
+
has one account.
|
|
481
|
+
- `senderId` is the person's member ID (`U…`): in their Slack profile, choose
|
|
482
|
+
**Copy member ID**.
|
|
483
|
+
- `channelId` is the channel's ID (`C…` or `G…`), shown at the bottom of the
|
|
484
|
+
channel details.
|
|
485
|
+
|
|
486
|
+
Some runtimes do not pass sender identity to plugins; OpenClaw's Codex
|
|
487
|
+
app-server harness is one. There, every requestor is treated as unapproved,
|
|
488
|
+
exclusions may not apply, and `memory_diagnostics` counts `missing_identity`;
|
|
489
|
+
the first such turn is also logged.
|
|
490
|
+
|
|
491
|
+
### When the check cannot run
|
|
492
|
+
|
|
493
|
+
Safety Whisperer **fails open**: if the check cannot complete, the turn proceeds
|
|
494
|
+
with no safety prompt. A TypeSafe outage or missing key therefore turns the
|
|
495
|
+
guardrail off. The exception is a message too large to check: the requestor
|
|
496
|
+
controls its size, so every eligible scenario's `flaggedPrompt` is added instead.
|
|
497
|
+
`memory_diagnostics` reports each outcome under `whisperers.safety`, keeping
|
|
498
|
+
could-not-check separate from no match:
|
|
499
|
+
|
|
500
|
+
| Outcome | Meaning |
|
|
501
|
+
| --- | --- |
|
|
502
|
+
| `flagged` | At least one scenario matched; `matchedScenarios` counts matches by scenario ID |
|
|
503
|
+
| `no_match` | Checked; no scenario matched |
|
|
504
|
+
| `excluded` / `no_scenarios` | Skipped by a global exclusion, or nothing remained after filtering; no request |
|
|
505
|
+
| `empty_request` | The current message had no text, such as an attachment alone; not checked |
|
|
506
|
+
| `missing_key` / `typesafe_disabled` | Could not check: no credential, or TypeSafe disabled |
|
|
507
|
+
| `input_too_large` | Not checked: the current message exceeds the roughly 24k-token budget. It is never truncated; every eligible `flaggedPrompt` is added |
|
|
508
|
+
| `timed_out` / `failed` | Could not check: deadline, provider error or no usable answer |
|
|
509
|
+
| `partial_response` | Counted with `flagged`: some answers were unusable, and the confident matches were still added |
|
|
510
|
+
| `missing_identity` | Counted with the turn's outcome: the host passed no provider, account or sender ID |
|
|
511
|
+
| `cancelled` | The session ended or the Gateway stopped during the check |
|
|
512
|
+
|
|
513
|
+
Warnings include the outcome, error code, HTTP status and scenario count, never
|
|
514
|
+
message text; a missing key is logged once until it returns.
|
|
515
|
+
`memory_diagnostics` also reports `safety.active: false` with a `reason` when
|
|
516
|
+
settings or host controls prevent checks (`typesafe_disabled`,
|
|
517
|
+
`conversation_access_not_allowed`, `prompt_injection_not_allowed`,
|
|
518
|
+
`no_enabled_scenarios`), and a `warnings` list:
|
|
519
|
+
|
|
520
|
+
- `host_hook_timeout_below_whisperer_timeouts`: the host's `before_prompt_build`
|
|
521
|
+
timeout is shorter than the slowest enabled whisperer (`slowestWhispererMs`).
|
|
522
|
+
The host times out the plugin's whole contribution, so a slow Memory Whisperer
|
|
523
|
+
turn can drop a safety prompt even after diagnostics counted it as `flagged`.
|
|
524
|
+
- `queue_mode_steer`: see
|
|
525
|
+
[messages sent while the agent works](#messages-sent-while-the-agent-works).
|
|
526
|
+
|
|
527
|
+
The Gateway logs this status at startup when the feature is inactive or has a
|
|
528
|
+
warning.
|
|
529
|
+
|
|
530
|
+
### Writing scenarios
|
|
531
|
+
|
|
532
|
+
`instructions` are the classifier's entire definition of a scenario. Name the
|
|
533
|
+
subject, its synonyms and colloquial phrasing, and what is excluded. In the
|
|
534
|
+
source checkout's `eval/safety-whisperer/` cases, adding "pay … including how much
|
|
535
|
+
someone makes or earns" raised a compound request ("What does Bob make, and is he
|
|
536
|
+
on medical leave?") from 0.06 to 0.45–0.48 and lowered "When is payday?" from 0.45
|
|
537
|
+
to 0.18–0.21. With that wording, the 0.4 default `minProbability` made no errors
|
|
538
|
+
across 22 cases, while 0.5 missed the compound request. Lower it to catch more
|
|
539
|
+
borderline requests, or raise it for fewer false refusals. Test your own scenarios
|
|
540
|
+
there before relying on them.
|
|
541
|
+
|
|
374
542
|
## Inside Out
|
|
375
543
|
|
|
376
544
|
Inside Out records six independent emotion-presence probabilities for each human
|
|
@@ -384,15 +552,18 @@ change memories, retrieval, prompts, or dossiers.
|
|
|
384
552
|
"enabled": true,
|
|
385
553
|
"intervalMinutes": 1440,
|
|
386
554
|
"maxInteractions": 100,
|
|
387
|
-
"maxContextTokens": 30000
|
|
555
|
+
"maxContextTokens": 30000,
|
|
556
|
+
"baselineMinInteractions": 20
|
|
388
557
|
}
|
|
389
558
|
}
|
|
390
559
|
```
|
|
391
560
|
|
|
392
561
|
Defaults: disabled; daily cadence; at most 100 attempted reviews per run; estimated
|
|
393
562
|
30,000-token context budget, leaving room for questions under Jev's 32k
|
|
394
|
-
state-plus-longest-question limit. `intervalMinutes: 0`
|
|
395
|
-
|
|
563
|
+
state-plus-longest-question limit. `intervalMinutes: 0` makes inference manual-only;
|
|
564
|
+
local baselines still refresh daily and on Gateway startup when Inside Out is enabled.
|
|
565
|
+
`baselineMinInteractions` defaults to 20 (1–10,000) for each comparison population.
|
|
566
|
+
The shared `typesafe` settings supply Jev credentials and request timeout.
|
|
396
567
|
|
|
397
568
|
**Enabling this approves sending visible human/assistant conversation text across
|
|
398
569
|
all retained channels to TypeSafe.** System messages, tool bodies, thinking blocks,
|
|
@@ -417,11 +588,15 @@ openclaw memory-emotions link-people --agent main
|
|
|
417
588
|
openclaw memory-emotions link-people --agent main --repair-identities
|
|
418
589
|
openclaw memory-emotions run --agent main --session SESSION_ID
|
|
419
590
|
openclaw memory-emotions export --agent main --session SESSION_ID
|
|
591
|
+
openclaw memory-emotions baseline-refresh --agent main
|
|
592
|
+
openclaw memory-emotions baselines --agent main --person PERSON_ID
|
|
593
|
+
openclaw memory-emotions export --agent main --person PERSON_ID --deviation-history
|
|
594
|
+
openclaw memory-emotions baseline-refresh --agent main --recompare
|
|
420
595
|
```
|
|
421
596
|
|
|
422
597
|
`run` handles backfill and new interactions through the same bounded pass. Repeat
|
|
423
|
-
it until `reviewed` and `failed` are zero to finish backfill.
|
|
424
|
-
runs one pass, then repeats at the configured interval. Successful results are
|
|
598
|
+
it until `reviewed` and `failed` are zero to finish backfill. With automatic inference
|
|
599
|
+
enabled, Gateway startup also runs one pass, then repeats at the configured interval. Successful results are
|
|
425
600
|
cached; new interactions take priority over retries. Request failures retry after
|
|
426
601
|
an hour when the pass has remaining capacity, or immediately with `--retry`.
|
|
427
602
|
Oversized context failures require explicit `--retry`, for example after increasing
|
|
@@ -480,13 +655,109 @@ changed or removed mappings; by default it never reads transcripts. Its explicit
|
|
|
480
655
|
`--repair-identities` option reads sources owning unlinked reviews and repairs
|
|
481
656
|
their channel, sender ID and human key from persisted metadata before linking.
|
|
482
657
|
Neither mode calls Jev or changes scores, timestamps, review IDs or checkpoints.
|
|
483
|
-
Unmatched identities stay null. Exports include `person_id`.
|
|
658
|
+
Unmatched identities stay null. Exports include `person_id` and `personal_deviation`.
|
|
484
659
|
`export` emits all matching rows as JSON, including errors. `report` averages
|
|
485
660
|
successful rows by human, UTC day/week, rubric, and model; counts are the denominator.
|
|
486
|
-
Both accept `--session`, `--sender`, `--since YYYY-MM-DD`, `--emotion`, and `--min`. `--sender`
|
|
661
|
+
Both accept `--person`, `--session`, `--sender`, `--since YYYY-MM-DD`, `--emotion`, and `--min`. `--sender`
|
|
487
662
|
matches exact sender IDs across channels; use exported `human_key` for precise
|
|
488
663
|
account-specific analysis. These commands are read-only and never invoke Jev.
|
|
489
664
|
|
|
665
|
+
### Person-relative baselines and deviations
|
|
666
|
+
|
|
667
|
+
Inside Out keeps analytics beside PeopleSQL in the same `unblock-memory.sqlite`,
|
|
668
|
+
linked by its existing `person_id`. `person_emotion_baselines` holds one snapshot
|
|
669
|
+
per person, rubric, model and completed week; `inside_out_deviations` holds
|
|
670
|
+
individual assessments. They are separate from dossier fields, recognition blurbs
|
|
671
|
+
and searchable memory. PeopleSQL identities must already exist; Inside Out does not
|
|
672
|
+
create people or guess links. Statistics are per agent's retained grades, not
|
|
673
|
+
fleet-wide aggregates.
|
|
674
|
+
|
|
675
|
+
Weeks run Monday 00:00 UTC to the next Monday, with an exclusive end. Each stored
|
|
676
|
+
week keeps, per emotion, the exact sample count, sum, minimum and maximum, plus the
|
|
677
|
+
distribution in 0.01-wide bins (at most 101 values), so a snapshot stays small
|
|
678
|
+
however much history accumulates. Reports show weekly and cumulative sample counts,
|
|
679
|
+
mean, minimum, maximum, p10, median, p90 and the binned distribution; cumulative
|
|
680
|
+
values are computed from all earlier stored weeks when read. Means, minimums and
|
|
681
|
+
maximums are exact; quantiles use bin values. Completed empty weeks are logged with
|
|
682
|
+
zero weekly observations. Rubrics and models have separate populations.
|
|
683
|
+
|
|
684
|
+
For each graded reply, `personal_deviation` compares its raw probabilities to that
|
|
685
|
+
person's **preceding completed weeks**, excluding the reply's entire current week
|
|
686
|
+
and future interactions. Both cumulative history and the immediately preceding
|
|
687
|
+
week are reported. Each emotion includes:
|
|
688
|
+
|
|
689
|
+
- `delta`: signed difference from the person's exact baseline mean.
|
|
690
|
+
- `percentile`: empirical rank from 0 to 1 within 0.01-wide bins; grades in the
|
|
691
|
+
same bin are ties and take the midpoint.
|
|
692
|
+
- `unusualness`: `abs(2 * percentile - 1)`, from 0 at the middle to 1 at the extremes.
|
|
693
|
+
This is a rank-derived signal, **not a probability or emotional intensity**.
|
|
694
|
+
|
|
695
|
+
For example, a joy probability of 0.8 can be routine for someone whose prior grades
|
|
696
|
+
are consistently 0.8, and unusual for someone whose prior grades are 0.1. This does
|
|
697
|
+
not establish their personality, mental state, or the agent's quality. Counts below
|
|
698
|
+
`baselineMinInteractions` produce `insufficient_history` with null emotion metrics,
|
|
699
|
+
independently for weekly and cumulative comparisons. Unlinked people, missing or
|
|
700
|
+
future timestamps and failed grades have explicit statuses; they are never neutral
|
|
701
|
+
scores. Exports before the first local assessment show `not_compared`.
|
|
702
|
+
|
|
703
|
+
`run` refreshes snapshots after grading new replies. Both grading and daily local
|
|
704
|
+
refreshes update exact PeopleSQL links before aggregating or assessing new replies.
|
|
705
|
+
Historical aggregation runs in a worker thread and commits in short batches, so the
|
|
706
|
+
Gateway event loop stays responsive and other writers to `unblock-memory.sqlite`
|
|
707
|
+
wait for one batch at most, never a whole refresh. Gateway shutdown cancels an
|
|
708
|
+
active refresh; the next one resumes, and repeating a refresh never duplicates
|
|
709
|
+
records. If the refresh after grading fails, `run` still returns its counts with a
|
|
710
|
+
`baselineError` (the Gateway logs a warning); saved grades are unaffected.
|
|
711
|
+
`link-people` also refreshes analytics. `baseline-refresh` does this without loading
|
|
712
|
+
transcripts, calling Jev, or requiring TypeSafe credentials. `baselines` reads
|
|
713
|
+
weekly snapshots, newest first, with optional `--person`, `--since YYYY-MM-DD`
|
|
714
|
+
(inclusive UTC week-end) and `--limit` (1–1,000 weeks, default 100).
|
|
715
|
+
|
|
716
|
+
Snapshots always reflect the currently linked grades. A late historical grade, a
|
|
717
|
+
retry, a relink or removed identity evidence rewrites the affected week and every
|
|
718
|
+
later week of that population in place, and a population with no linked grades left
|
|
719
|
+
loses its snapshots; earlier revisions are not kept. A snapshot `id` addresses its
|
|
720
|
+
whole cumulative population, so returning to an earlier population restores the
|
|
721
|
+
earlier `id`.
|
|
722
|
+
|
|
723
|
+
Normal refreshes preserve an existing assessment of the same grade. Late historical
|
|
724
|
+
grades change snapshots but do not silently rewrite earlier comparisons.
|
|
725
|
+
Use `baseline-refresh --recompare` to append changed assessments after backfill,
|
|
726
|
+
identity or sample-policy changes; `export --deviation-history` includes the earlier
|
|
727
|
+
results and marks which match the current grade. Repeating an unchanged refresh
|
|
728
|
+
adds no duplicate records. Previously sparse or future-dated assessments also need
|
|
729
|
+
explicit re-comparison when their circumstances change.
|
|
730
|
+
|
|
731
|
+
`computed_at` and `assessedAt` record when these calculations actually occurred.
|
|
732
|
+
A backfilled comparison uses retained evidence from earlier interaction weeks, but
|
|
733
|
+
does not pretend that evidence had already been graded at the historical send time.
|
|
734
|
+
Baseline calculation requires no additional model calls and changes no raw emotion
|
|
735
|
+
judgments, dossiers, prompt injection, clustering or memory creation.
|
|
736
|
+
|
|
737
|
+
### Deleting a person's emotion data
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
openclaw memory-emotions purge --agent main --person PERSON_ID
|
|
741
|
+
openclaw memory-emotions purge --agent main --person PERSON_ID --grades
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
`purge` deletes one person's weekly snapshots and their whole assessment history,
|
|
745
|
+
including assessments recorded while a reply was linked to them, and prints what it
|
|
746
|
+
removed. It makes no Jev calls.
|
|
747
|
+
|
|
748
|
+
- **Without `--grades`**, their raw grades stay, so the next refresh rebuilds
|
|
749
|
+
current snapshots and assessments from replies still linked to them. Use this
|
|
750
|
+
after correcting a misattributed identity.
|
|
751
|
+
- **With `--grades`**, it also erases the six probabilities of every reply currently
|
|
752
|
+
linked to that person and unlinks it. Each row stays as a tombstone with the error
|
|
753
|
+
`Purged by operator`, so `run` (including `--retry`) never sends that reply to Jev
|
|
754
|
+
again, `link-people` never relinks it, and refreshes skip it. This cannot be
|
|
755
|
+
undone; run `link-people` first so every reply from that person is linked.
|
|
756
|
+
|
|
757
|
+
Purging removes existing data only. While Inside Out stays enabled and the person's
|
|
758
|
+
identity remains in PeopleSQL, their new replies are graded and start a new
|
|
759
|
+
baseline. Soft-deleting a person in PeopleSQL does not remove Inside Out data.
|
|
760
|
+
|
|
490
761
|
## Storage, upgrades and recovery
|
|
491
762
|
|
|
492
763
|
Each agent's state lives under the configured OpenClaw state directory, normally
|
package/docs/peoplesql.md
CHANGED
|
@@ -9,6 +9,13 @@ store with `people.enabled`; enable injection separately with
|
|
|
9
9
|
and automatic save review, not a maintenance scheduler. For independent setup
|
|
10
10
|
examples, see the [configuration profiles](configuration.md#example-profiles).
|
|
11
11
|
|
|
12
|
+
[Inside Out baselines](configuration.md#person-relative-baselines-and-deviations)
|
|
13
|
+
reuse PeopleSQL's person IDs in separate analytics tables in the same database.
|
|
14
|
+
They retain weekly emotion-grade distributions and interaction deviations; none of
|
|
15
|
+
this analytics is written into dossiers or injected by People Whisperer. Deleting a
|
|
16
|
+
dossier or soft-deleting a person leaves it in place; use
|
|
17
|
+
[`memory-emotions purge`](configuration.md#deleting-a-persons-emotion-data).
|
|
18
|
+
|
|
12
19
|
## Optional People Dossier Primer
|
|
13
20
|
|
|
14
21
|
`memory_people_prime({ personId, agentName? })` prepares evidence for an existing person;
|
package/docs/retrieval.md
CHANGED
|
@@ -468,9 +468,11 @@ or raw exceptions; exact query text is not retained by this telemetry.
|
|
|
468
468
|
## Skill Whisperer
|
|
469
469
|
|
|
470
470
|
|
|
471
|
-
Skill Whisperer is
|
|
472
|
-
isolated `skills` corpus
|
|
473
|
-
`plugins.entries.unblock-memory.hooks.allowConversationAccess`.
|
|
471
|
+
Skill Whisperer is a semantic reminder for user turns. It turns on by default once
|
|
472
|
+
you configure one isolated `skills` corpus and authorize
|
|
473
|
+
`plugins.entries.unblock-memory.hooks.allowConversationAccess`. Without a skills
|
|
474
|
+
corpus it stays inactive: no prompt hook is registered and diagnostics report it
|
|
475
|
+
disabled. Set `skillWhisperer.enabled` to `false` to opt out. It
|
|
474
476
|
embeds the current prompt plus the configured number of prior user/assistant
|
|
475
477
|
messages, compares it with each configured skill's frontmatter `name` and
|
|
476
478
|
`description`. With TypeSafe enabled and a key available, the top three valid
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "unblock-memory",
|
|
3
3
|
"name": "Unblock Memory",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"description": "Indexes, retrieves, and analyzes configured workspace memory with existing QMD vectors.",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"activation": { "onStartup": true },
|
|
@@ -92,6 +92,14 @@
|
|
|
92
92
|
"label": "Approved Hint Corpora",
|
|
93
93
|
"help": "Explicit non-skill corpus allowlist for automatic recall and TypeSafe processing. Session recall spans this agent's indexed sessions; the sessions corpus chatTypes setting controls direct-message inclusion."
|
|
94
94
|
},
|
|
95
|
+
"safetyWhisperer.enabled": {
|
|
96
|
+
"label": "Safety Whisperer",
|
|
97
|
+
"help": "Prompt-level guardrail, not access control. Sends every checked human message and bounded recent history to TypeSafe and appends a scenario's flaggedPrompt when an unapproved requestor's message matches. Fails open: if the check cannot run, the turn proceeds without it. Requires hook conversation access and prompt injection. Disabled by default."
|
|
98
|
+
},
|
|
99
|
+
"safetyWhisperer.scenarios": {
|
|
100
|
+
"label": "Safety Scenarios",
|
|
101
|
+
"help": "Each scenario is judged independently. Approval and exclusions match exact provider/account/sender or channel IDs from trusted channel metadata, never names or message text."
|
|
102
|
+
},
|
|
95
103
|
"people.enabled": {
|
|
96
104
|
"label": "PeopleSQL",
|
|
97
105
|
"help": "Enable the agent-local people store, Slack identity observation and people tools. Disabled by default; whispering and TypeSafe primer/review are separate opt-ins."
|
|
@@ -116,7 +124,8 @@
|
|
|
116
124
|
"enabled": { "type": "boolean", "default": false },
|
|
117
125
|
"intervalMinutes": { "type": "integer", "minimum": 0, "maximum": 10080, "default": 1440 },
|
|
118
126
|
"maxInteractions": { "type": "integer", "minimum": 1, "maximum": 10000, "default": 100 },
|
|
119
|
-
"maxContextTokens": { "type": "integer", "minimum": 256, "maximum": 30000, "default": 30000 }
|
|
127
|
+
"maxContextTokens": { "type": "integer", "minimum": 256, "maximum": 30000, "default": 30000 },
|
|
128
|
+
"baselineMinInteractions": { "type": "integer", "minimum": 1, "maximum": 10000, "default": 20 }
|
|
120
129
|
}
|
|
121
130
|
},
|
|
122
131
|
"extraction": {
|
|
@@ -340,17 +349,103 @@
|
|
|
340
349
|
"type": "object",
|
|
341
350
|
"additionalProperties": false,
|
|
342
351
|
"properties": {
|
|
343
|
-
"enabled": { "type": "boolean", "default":
|
|
352
|
+
"enabled": { "type": "boolean", "default": true, "description": "Takes effect only when a corpus with kind \"skills\" is configured." },
|
|
344
353
|
"historyMessages": { "type": "integer", "minimum": 0, "default": 5 },
|
|
345
354
|
"minScore": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.5 },
|
|
346
355
|
"cooldownTurns": { "type": "integer", "minimum": 0, "default": 10 }
|
|
347
356
|
},
|
|
348
357
|
"default": {
|
|
349
|
-
"enabled":
|
|
358
|
+
"enabled": true,
|
|
350
359
|
"historyMessages": 5,
|
|
351
360
|
"minScore": 0.5,
|
|
352
361
|
"cooldownTurns": 10
|
|
353
362
|
}
|
|
363
|
+
},
|
|
364
|
+
"safetyWhisperer": {
|
|
365
|
+
"type": "object",
|
|
366
|
+
"additionalProperties": false,
|
|
367
|
+
"description": "Optional prompt-level guardrail for sensitive request scenarios. Fails open when the check cannot run.",
|
|
368
|
+
"properties": {
|
|
369
|
+
"enabled": { "type": "boolean", "default": false },
|
|
370
|
+
"historyMessages": { "type": "integer", "minimum": 0, "maximum": 20, "default": 6 },
|
|
371
|
+
"timeoutMs": { "type": "integer", "minimum": 1, "maximum": 10000, "default": 3000 },
|
|
372
|
+
"excludedChannels": {
|
|
373
|
+
"type": "array",
|
|
374
|
+
"maxItems": 500,
|
|
375
|
+
"description": "Channels where every safety scenario is skipped. Threads inherit their channel.",
|
|
376
|
+
"items": {
|
|
377
|
+
"type": "object",
|
|
378
|
+
"additionalProperties": false,
|
|
379
|
+
"required": ["provider", "accountId", "channelId"],
|
|
380
|
+
"properties": {
|
|
381
|
+
"name": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
382
|
+
"provider": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
383
|
+
"accountId": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
384
|
+
"channelId": { "type": "string", "pattern": "\\S", "maxLength": 256 }
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
"scenarios": {
|
|
389
|
+
"type": "array",
|
|
390
|
+
"maxItems": 20,
|
|
391
|
+
"items": {
|
|
392
|
+
"type": "object",
|
|
393
|
+
"additionalProperties": false,
|
|
394
|
+
"required": ["id", "instructions", "flaggedPrompt"],
|
|
395
|
+
"properties": {
|
|
396
|
+
"id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" },
|
|
397
|
+
"enabled": { "type": "boolean", "default": true },
|
|
398
|
+
"instructions": { "type": "string", "pattern": "\\S", "maxLength": 2000, "description": "What requests this scenario covers, including exclusions." },
|
|
399
|
+
"flaggedPrompt": { "type": "string", "pattern": "\\S", "maxLength": 2000, "description": "Appended next to the human message when an unapproved requestor's message matches." },
|
|
400
|
+
"minProbability": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.4, "description": "Minimum TypeSafe yes-probability that the current request is covered." },
|
|
401
|
+
"agentIds": {
|
|
402
|
+
"type": "array",
|
|
403
|
+
"minItems": 1,
|
|
404
|
+
"maxItems": 500,
|
|
405
|
+
"description": "Limit the scenario to these agents; omit for every agent.",
|
|
406
|
+
"items": { "type": "string", "pattern": "\\S", "maxLength": 256 }
|
|
407
|
+
},
|
|
408
|
+
"approvedRequestors": {
|
|
409
|
+
"type": "array",
|
|
410
|
+
"maxItems": 500,
|
|
411
|
+
"items": {
|
|
412
|
+
"type": "object",
|
|
413
|
+
"additionalProperties": false,
|
|
414
|
+
"required": ["provider", "accountId", "senderId"],
|
|
415
|
+
"properties": {
|
|
416
|
+
"name": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
417
|
+
"provider": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
418
|
+
"accountId": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
419
|
+
"senderId": { "type": "string", "pattern": "\\S", "maxLength": 256 }
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
"excludedChannels": {
|
|
424
|
+
"type": "array",
|
|
425
|
+
"maxItems": 500,
|
|
426
|
+
"items": {
|
|
427
|
+
"type": "object",
|
|
428
|
+
"additionalProperties": false,
|
|
429
|
+
"required": ["provider", "accountId", "channelId"],
|
|
430
|
+
"properties": {
|
|
431
|
+
"name": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
432
|
+
"provider": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
433
|
+
"accountId": { "type": "string", "pattern": "\\S", "maxLength": 256 },
|
|
434
|
+
"channelId": { "type": "string", "pattern": "\\S", "maxLength": 256 }
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
"default": {
|
|
443
|
+
"enabled": false,
|
|
444
|
+
"historyMessages": 6,
|
|
445
|
+
"timeoutMs": 3000,
|
|
446
|
+
"excludedChannels": [],
|
|
447
|
+
"scenarios": []
|
|
448
|
+
}
|
|
354
449
|
}
|
|
355
450
|
}
|
|
356
451
|
}
|