feat: add agents.defaults.compaction.notifyUser config option (default: false) [Fix #54249] (#54251)

Merged via squash.

Prepared head SHA: 6fd4cdb7c3
Co-authored-by: oguricap0327 <266246182+oguricap0327@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
Oguri Cap
2026-04-02 03:29:17 +08:00
committed by GitHub
parent 5b73108e58
commit 1f99c87a44
10 changed files with 260 additions and 34 deletions

View File

@@ -6,6 +6,8 @@ Docs: https://docs.openclaw.ai
### Changes
- Agents/compaction: add `agents.defaults.compaction.notifyUser` so the `🧹 Compacting context...` start notice is opt-in instead of always being shown. (#54251) Thanks @oguricap0327.
### Fixes
- Matrix/multi-account: keep room-level `account` scoping, inherited room overrides, and implicit account selection consistent across top-level default auth, named accounts, and cached-credential env setups. (#58449) thanks @Daanvdplas and @gumadeiras.

View File

@@ -2200,6 +2200,16 @@
"tags": [],
"hasChildren": false
},
{
"path": "agents.defaults.memorySearch.notifyUser",
"kind": "core",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "agents.defaults.memorySearch.outputDimensionality",
"kind": "core",
@@ -10199,7 +10209,10 @@
{
"path": "channels.discord.accounts.*.allowFrom.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -10449,7 +10462,10 @@
{
"path": "channels.discord.accounts.*.dm.allowFrom.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -10479,7 +10495,10 @@
{
"path": "channels.discord.accounts.*.dm.groupChannels.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -10701,7 +10720,10 @@
{
"path": "channels.discord.accounts.*.execApprovals.approvers.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -10925,7 +10947,10 @@
{
"path": "channels.discord.accounts.*.guilds.*.channels.*.roles.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -11125,7 +11150,10 @@
{
"path": "channels.discord.accounts.*.guilds.*.channels.*.users.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -11181,7 +11209,10 @@
{
"path": "channels.discord.accounts.*.guilds.*.roles.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -11361,7 +11392,10 @@
{
"path": "channels.discord.accounts.*.guilds.*.users.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -12601,7 +12635,10 @@
{
"path": "channels.discord.allowFrom.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -12909,7 +12946,10 @@
{
"path": "channels.discord.dm.allowFrom.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -12939,7 +12979,10 @@
{
"path": "channels.discord.dm.groupChannels.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -13207,7 +13250,10 @@
{
"path": "channels.discord.execApprovals.approvers.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -13431,7 +13477,10 @@
{
"path": "channels.discord.guilds.*.channels.*.roles.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -13631,7 +13680,10 @@
{
"path": "channels.discord.guilds.*.channels.*.users.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -13687,7 +13739,10 @@
{
"path": "channels.discord.guilds.*.roles.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,
@@ -13867,7 +13922,10 @@
{
"path": "channels.discord.guilds.*.users.*",
"kind": "channel",
"type": "string",
"type": [
"number",
"string"
],
"required": false,
"deprecated": false,
"sensitive": false,

View File

@@ -1,4 +1,4 @@
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5765}
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5766}
{"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true}
{"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true}
{"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -185,6 +185,7 @@
{"recordType":"path","path":"agents.defaults.memorySearch.multimodal.maxFileBytes","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage"],"label":"Memory Search Multimodal Max File Bytes","help":"Sets the maximum bytes allowed per multimodal file before it is skipped during memory indexing. Use this to cap upload cost and indexing latency, or raise it for short high-quality audio clips.","hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.multimodal.modalities","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search Multimodal Modalities","help":"Selects which multimodal file types are indexed from extraPaths: \"image\", \"audio\", or \"all\". Keep this narrow to avoid indexing large binary corpora unintentionally.","hasChildren":true}
{"recordType":"path","path":"agents.defaults.memorySearch.multimodal.modalities.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.notifyUser","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.outputDimensionality","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search Output Dimensionality","help":"Gemini embedding-2 only: chooses the output vector size for memory embeddings. Use 768, 1536, or 3072 (default), and expect a full reindex when you change it because stored vector dimensions must stay consistent.","hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search Provider","help":"Selects the embedding backend used to build/query memory vectors: \"openai\", \"gemini\", \"voyage\", \"mistral\", \"ollama\", or \"local\". Keep your most reliable provider here and configure fallback for resilience.","hasChildren":false}
{"recordType":"path","path":"agents.defaults.memorySearch.qmd","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Memory Search QMD Collections","help":"Use this when one agent should query another agent's transcript collections; QMD-specific extra collections let you opt into cross-agent memory search without flattening everything into one shared namespace.","hasChildren":true}
@@ -895,7 +896,7 @@
{"recordType":"path","path":"channels.discord.accounts.*.agentComponents.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.allowBots","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.autoPresence","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.autoPresence.degradedText","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.autoPresence.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -919,10 +920,10 @@
{"recordType":"path","path":"channels.discord.accounts.*.defaultTo","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.dm.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.dm.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupChannels","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupChannels.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupChannels.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm.groupEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dm.policy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -943,7 +944,7 @@
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.approvers.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.cleanupAfterResolve","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -963,7 +964,7 @@
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.includeThreadStarter","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.skills","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.skills.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -983,12 +984,12 @@
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.channels.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.ignoreOtherMentions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.reactionNotifications","kind":"channel","type":"string","required":false,"enumValues":["off","own","all","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.slug","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.tools","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.tools.allow","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1006,7 +1007,7 @@
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.guilds.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.accounts.*.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.accounts.*.heartbeat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1120,7 +1121,7 @@
{"recordType":"path","path":"channels.discord.agentComponents.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.allowBots","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["access","channels","network"],"label":"Discord Allow Bot Messages","help":"Allow bot-authored messages to trigger Discord replies (default: false). Set \"mentions\" to only accept bot messages that mention the bot.","hasChildren":false}
{"recordType":"path","path":"channels.discord.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.autoPresence","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.autoPresence.degradedText","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Auto Presence Degraded Text","help":"Optional custom status text while runtime/model availability is degraded or unknown (idle).","hasChildren":false}
{"recordType":"path","path":"channels.discord.autoPresence.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Discord Auto Presence Enabled","help":"Enable automatic Discord bot presence updates based on runtime/model availability signals. When enabled: healthy=>online, degraded/unknown=>idle, exhausted/unavailable=>dnd.","hasChildren":false}
@@ -1145,10 +1146,10 @@
{"recordType":"path","path":"channels.discord.defaultTo","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.dm.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.dm.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm.groupChannels","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.dm.groupChannels.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm.groupChannels.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm.groupEnabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.dm.policy","kind":"channel","type":"string","required":false,"enumValues":["pairing","allowlist","open","disabled"],"deprecated":false,"sensitive":false,"tags":["access","channels","network"],"label":"Discord DM Policy","help":"Direct message access control (\"pairing\" recommended). \"open\" requires channels.discord.allowFrom=[\"*\"] (legacy: channels.discord.dm.allowFrom).","hasChildren":false}
{"recordType":"path","path":"channels.discord.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1169,7 +1170,7 @@
{"recordType":"path","path":"channels.discord.execApprovals.agentFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.execApprovals.agentFilter.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.execApprovals.approvers","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.execApprovals.approvers.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.execApprovals.approvers.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.execApprovals.cleanupAfterResolve","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.execApprovals.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.execApprovals.sessionFilter","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1189,7 +1190,7 @@
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.includeThreadStarter","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.skills","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.skills.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1209,12 +1210,12 @@
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.channels.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.ignoreOtherMentions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.reactionNotifications","kind":"channel","type":"string","required":false,"enumValues":["off","own","all","allowlist"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.roles","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.roles.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.roles.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.slug","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.tools","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.tools.allow","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1232,7 +1233,7 @@
{"recordType":"path","path":"channels.discord.guilds.*.toolsBySender.*.deny","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.toolsBySender.*.deny.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.users","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.guilds.*.users.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.guilds.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.healthMonitor","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.discord.healthMonitor.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.discord.heartbeat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}

View File

@@ -116,6 +116,10 @@ type FallbackRunnerParams = {
type EmbeddedAgentParams = {
onToolResult?: (payload: { text?: string; mediaUrls?: string[] }) => Promise<void> | void;
onAgentEvent?: (payload: {
stream: string;
data: { phase?: string; completed?: boolean };
}) => Promise<void> | void;
};
function createMockTypingSignaler(): TypingSignaler {
@@ -229,6 +233,145 @@ describe("runAgentTurnWithFallback", () => {
expect(onToolResult.mock.calls[0]?.[0]?.text).toBeUndefined();
});
it("keeps compaction start notices silent by default", async () => {
const onBlockReply = vi.fn();
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: EmbeddedAgentParams) => {
await params.onAgentEvent?.({ stream: "compaction", data: { phase: "start" } });
return { payloads: [{ text: "final" }], meta: {} };
});
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
const result = await runAgentTurnWithFallback({
commandBody: "hello",
followupRun: createFollowupRun(),
sessionCtx: {
Provider: "whatsapp",
MessageSid: "msg",
} as unknown as TemplateContext,
opts: { onBlockReply },
typingSignals: createMockTypingSignaler(),
blockReplyPipeline: null,
blockStreamingEnabled: false,
resolvedBlockStreamingBreak: "message_end",
applyReplyToMode: (payload) => payload,
shouldEmitToolResult: () => true,
shouldEmitToolOutput: () => false,
pendingToolTasks: new Set(),
resetSessionAfterCompactionFailure: async () => false,
resetSessionAfterRoleOrderingConflict: async () => false,
isHeartbeat: false,
sessionKey: "main",
getActiveSessionEntry: () => undefined,
resolvedVerboseLevel: "off",
});
expect(result.kind).toBe("success");
expect(onBlockReply).not.toHaveBeenCalled();
});
it("keeps compaction callbacks active when notices are silent by default", async () => {
const onBlockReply = vi.fn();
const onCompactionStart = vi.fn();
const onCompactionEnd = vi.fn();
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: EmbeddedAgentParams) => {
await params.onAgentEvent?.({ stream: "compaction", data: { phase: "start" } });
await params.onAgentEvent?.({
stream: "compaction",
data: { phase: "end", completed: true },
});
return { payloads: [{ text: "final" }], meta: {} };
});
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
const result = await runAgentTurnWithFallback({
commandBody: "hello",
followupRun: createFollowupRun(),
sessionCtx: {
Provider: "whatsapp",
MessageSid: "msg",
} as unknown as TemplateContext,
opts: {
onBlockReply,
onCompactionStart,
onCompactionEnd,
},
typingSignals: createMockTypingSignaler(),
blockReplyPipeline: null,
blockStreamingEnabled: false,
resolvedBlockStreamingBreak: "message_end",
applyReplyToMode: (payload) => payload,
shouldEmitToolResult: () => true,
shouldEmitToolOutput: () => false,
pendingToolTasks: new Set(),
resetSessionAfterCompactionFailure: async () => false,
resetSessionAfterRoleOrderingConflict: async () => false,
isHeartbeat: false,
sessionKey: "main",
getActiveSessionEntry: () => undefined,
resolvedVerboseLevel: "off",
});
expect(result.kind).toBe("success");
expect(onCompactionStart).toHaveBeenCalledTimes(1);
expect(onCompactionEnd).toHaveBeenCalledTimes(1);
expect(onBlockReply).not.toHaveBeenCalled();
});
it("emits a compaction start notice when notifyUser is enabled", async () => {
const onBlockReply = vi.fn();
state.runEmbeddedPiAgentMock.mockImplementationOnce(async (params: EmbeddedAgentParams) => {
await params.onAgentEvent?.({ stream: "compaction", data: { phase: "start" } });
return { payloads: [{ text: "final" }], meta: {} };
});
const followupRun = createFollowupRun();
followupRun.run.config = {
agents: {
defaults: {
compaction: {
notifyUser: true,
},
},
},
};
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
const result = await runAgentTurnWithFallback({
commandBody: "hello",
followupRun,
sessionCtx: {
Provider: "whatsapp",
MessageSid: "msg",
} as unknown as TemplateContext,
opts: { onBlockReply },
typingSignals: createMockTypingSignaler(),
blockReplyPipeline: null,
blockStreamingEnabled: false,
resolvedBlockStreamingBreak: "message_end",
applyReplyToMode: (payload) => payload,
shouldEmitToolResult: () => true,
shouldEmitToolOutput: () => false,
pendingToolTasks: new Set(),
resetSessionAfterCompactionFailure: async () => false,
resetSessionAfterRoleOrderingConflict: async () => false,
isHeartbeat: false,
sessionKey: "main",
getActiveSessionEntry: () => undefined,
resolvedVerboseLevel: "off",
});
expect(result.kind).toBe("success");
expect(onBlockReply).toHaveBeenCalledTimes(1);
expect(onBlockReply).toHaveBeenCalledWith(
expect.objectContaining({
text: "🧹 Compacting context...",
replyToId: "msg",
replyToCurrent: true,
isCompactionNotice: true,
}),
);
});
it("does not show a rate-limit countdown for mixed-cause fallback exhaustion", async () => {
state.runWithModelFallbackMock.mockRejectedValueOnce(
Object.assign(

View File

@@ -495,9 +495,14 @@ export async function runAgentTurnWithFallback(params: {
if (evt.stream === "compaction") {
const phase = typeof evt.data.phase === "string" ? evt.data.phase : "";
if (phase === "start") {
// Keep custom compaction callbacks active, but gate the
// fallback user-facing notice behind explicit opt-in.
const notifyUser =
params.followupRun.run.config.agents?.defaults?.compaction?.notifyUser ===
true;
if (params.opts?.onCompactionStart) {
await params.opts.onCompactionStart();
} else if (params.opts?.onBlockReply) {
} else if (notifyUser && params.opts?.onBlockReply) {
// Send directly via opts.onBlockReply (bypassing the
// pipeline) so the notice does not cause final payloads
// to be discarded on non-streaming model paths.

View File

@@ -2491,6 +2491,9 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
},
additionalProperties: false,
},
notifyUser: {
type: "boolean",
},
},
additionalProperties: false,
},
@@ -13863,6 +13866,11 @@ export const GENERATED_BASE_CONFIG_SCHEMA = {
help: "When enabled, rewrites the session JSONL file after compaction to remove entries that were summarized. Prevents unbounded file growth in long-running sessions with many compaction cycles. Default: false.",
tags: ["advanced"],
},
"agents.defaults.compaction.notifyUser": {
label: "Compaction Notify User",
help: "When enabled, sends a brief compaction notice to the user (e.g. '🧹 Compacting context...') when compaction starts. Disabled by default to keep compaction silent and non-intrusive.",
tags: ["advanced"],
},
"agents.defaults.compaction.memoryFlush": {
label: "Compaction Memory Flush",
help: "Pre-compaction memory flush settings that run an agentic memory write before heavy compaction. Keep enabled for long sessions so salient context is persisted before aggressive trimming.",

View File

@@ -1116,6 +1116,8 @@ export const FIELD_HELP: Record<string, string> = {
"Optional provider/model override used only for compaction summarization. Set this when you want compaction to run on a different model than the session default, and leave it unset to keep using the primary agent model.",
"agents.defaults.compaction.truncateAfterCompaction":
"When enabled, rewrites the session JSONL file after compaction to remove entries that were summarized. Prevents unbounded file growth in long-running sessions with many compaction cycles. Default: false.",
"agents.defaults.compaction.notifyUser":
"When enabled, sends a brief compaction notice to the user (e.g. '🧹 Compacting context...') when compaction starts. Disabled by default to keep compaction silent and non-intrusive.",
"agents.defaults.compaction.memoryFlush":
"Pre-compaction memory flush settings that run an agentic memory write before heavy compaction. Keep enabled for long sessions so salient context is persisted before aggressive trimming.",
"agents.defaults.compaction.memoryFlush.enabled":

View File

@@ -508,6 +508,7 @@ export const FIELD_LABELS: Record<string, string> = {
"agents.defaults.compaction.timeoutSeconds": "Compaction Timeout (Seconds)",
"agents.defaults.compaction.model": "Compaction Model Override",
"agents.defaults.compaction.truncateAfterCompaction": "Truncate After Compaction",
"agents.defaults.compaction.notifyUser": "Compaction Notify User",
"agents.defaults.compaction.memoryFlush": "Compaction Memory Flush",
"agents.defaults.compaction.memoryFlush.enabled": "Compaction Memory Flush Enabled",
"agents.defaults.compaction.memoryFlush.softThresholdTokens":

View File

@@ -354,6 +354,11 @@ export type AgentCompactionConfig = {
* Default: false (existing behavior preserved).
*/
truncateAfterCompaction?: boolean;
/**
* Send a "🧹 Compacting context..." notice to the user when compaction starts.
* Default: false (silent by default).
*/
notifyUser?: boolean;
};
export type AgentCompactionMemoryFlushConfig = {

View File

@@ -141,6 +141,7 @@ export const AgentDefaultsSchema = z
})
.strict()
.optional(),
notifyUser: z.boolean().optional(),
})
.strict()
.optional(),