-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathconfig.example.json
More file actions
271 lines (270 loc) · 14.2 KB
/
Copy pathconfig.example.json
File metadata and controls
271 lines (270 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
{
"data_dir": ".remember",
"cooldowns": {
"save_seconds": 120,
"ndc_seconds": 3600,
"git_backup_seconds": 900,
"promo_seconds": 604800
},
"thresholds": {
"min_human_messages": 3,
"min_exchanges_without_human": 30,
"max_summary_failures": 3,
"delta_lines_trigger": 50,
"extract_max_bytes": 300000,
"consolidate_max_bytes": 600000,
"consolidate_timeout_seconds": 180,
"memory_inject_max_bytes": 200000,
"staging_warn_bytes": 2000000,
"autonomous_log_retention_days": 7,
"ndc_timeout_seconds": 180
},
"features": {
"ndc_compression": true,
"recovery": true,
"plugin_promos": true
},
"git_backup": {
"remote": "",
"branch": "",
"reject_notice_after": 3,
"commit_notice_after": 3,
"no_remote_notice_after": 10,
"gpg_sign": false,
"allow_remote_change": false
},
"hooks": {
"dispatch_timeout_seconds": 15,
"dispatch_timeout_detached_seconds": 120,
"dispatch_kill_grace_seconds": 5
},
"git_restore": {
"enabled": false,
"remote": "",
"branch": "",
"fetch_timeout_seconds": 20,
"diverged_notice_after": 3
},
"time_format": "24h",
"prompt_stamp": "full",
"model": "haiku",
"reject_pattern": "",
"handoff_mode": "single",
"session_start_slow_threshold_s": 5,
"haiku": {
"oauth_token": "",
"anthropic_api_key": "auto"
},
"_comments": {
"debug": [
"Verbose logging for cooldowns and locks. Until #176 this key was read",
"nowhere — setting it did nothing. Now: REMEMBER_DEBUG wins if set, then",
"this, then each script's own default (save-session verbose, git-backup",
"quiet). Remove the key entirely to keep those defaults."
],
"haiku.oauth_token": [
"OAuth token the plugin hands to the nested `claude -p` ONLY when the host",
"did not put CLAUDE_CODE_OAUTH_TOKEN in the hook subprocess environment.",
"Some desktop / Agent-SDK hosts withhold it from spawned children, so the",
"summarization call is unauthenticated and nothing ever saves (#129/#131).",
"Create one with `claude setup-token`. The plugin holds this credential and",
"passes it to the CLI — set it deliberately. A host-provided token always",
"wins; the REMEMBER_OAUTH_TOKEN env var overrides this. Blank = disabled.",
"A malformed value (whitespace, under 20 chars, not a string) is refused",
"and reported in the daily log — it is never passed to the CLI."
],
"consolidate_max_bytes": [
"Skip-guard cap (UTF-8 bytes) on the assembled consolidation prompt",
"(staging + recent.md + archive.md). Default 600000. If the prompt would",
"exceed this, consolidation SKIPS that run (staging + memory left intact)",
"rather than overflowing Haiku's window with 'Prompt is too long' and",
"stalling all saves. Unlike extract_max_bytes the input is NOT truncated,",
"because consolidation rewrites recent/archive and truncating would drop",
"archived memory. Set 0 to disable the guard.",
"Since #346 the same number caps the OUTPUT too: a model response larger",
"than this is refused instead of being written to recent.md/archive.md.",
"Capping only the input meant one oversized response became the permanent",
"record AND pushed the store past this cap, so every later run skipped and",
"the file could never shrink again.",
"Since #348 a store already past this cap gets out again: whichever file",
"is measurably why the round will not fit is rotated to a dated sibling",
"(archive-YYYY-MM-DD.md / recent-YYYY-MM-DD.md, -2 on a same-day repeat)",
"and a fresh one started. Nothing is deleted and the slices are named at",
"session start. If the past-day staging files are over the cap on their",
"own, nothing is rotated — no rotation would change the next round."
],
"consolidate_timeout_seconds": [
"Wall-clock budget (seconds) for the staging -> recent/archive",
"consolidation call. Default 180. Output length scales with input",
"length, so a large enough staging batch can genuinely need longer than",
"that to finish. On timeout the batch is left uncompressed and grows",
"further, so every later run is even more likely to time out too -- a",
"one-way ratchet (#806), the same shape #788/#792 fixed for the sibling",
"NDC now.md -> today-*.md path via ndc_timeout_seconds. Raise this if",
"the daily log shows repeated 'consolidation' ERROR lines mentioning a",
"timeout."
],
"staging_warn_bytes": [
"Byte threshold (UTF-8) for a one-time WARNING when a today-*.md staging",
"file grows past it (#349). Staging files are append-only by design --",
"only a SUCCESSFUL consolidation round retires one -- so sustained lock",
"contention, a full disk, or a stalled consolidation (features.ndc_",
"compression off, or the #346 skip-forever state) leaves the same kind of",
"span appended every round with nothing to bound it. This does NOT cap or",
"truncate the file; it only logs (report_error, reaching hook-errors.log,",
"surfaced by /remember:doctor) the first time the file crosses the line,",
"so the growth is visible instead of silent. Set 0 to disable.",
"Default 2000000 -- comfortably above a healthy day's worth of staged",
"entries and comfortably below consolidate_max_bytes, so a warning here",
"is a signal something is stuck well before that separate skip-guard."
],
"memory_inject_max_bytes": [
"A memory file larger than this (UTF-8 bytes) is NAMED with its size at",
"session start instead of being cat'd into context. Default 200000.",
"The bytes stay on disk and stay greppable — what stops is pouring them",
"into a context window that cannot hold them. A healthy memory file is",
"kilobytes, so this only fires on a store that is already broken; in #346",
"a 6.4GB recent.md froze every `claude` launch in that project. Set 0 to",
"disable."
],
"ndc_timeout_seconds": [
"Wall-clock budget (seconds) for the NDC now.md -> today-*.md compression",
"call. Default 180. Output length scales with input length, so a large",
"now.md can genuinely need longer than that to finish. On timeout now.md",
"is left untouched and grows further, so every later run is even more",
"likely to time out too -- a one-way ratchet (#788). Raise this if the",
"daily log shows repeated 'ndc' ERROR lines mentioning a timeout."
],
"prompt_stamp": [
"What the UserPromptSubmit hook injects into the model's context.",
" full (default, unchanged) -> [14:30 CEST — jack — 45%]",
" stable -> [jack]",
" off -> nothing at all",
"Two of the three fields in `full` change between turns for reasons that",
"have nothing to do with what you typed: the clock, and the context",
"percentage, which climbs on every prompt for anyone running the status",
"line. `stable` drops both and keeps the username. It KEEPS the >=95",
"context warning, which is threshold-gated and therefore only changes",
"bytes when it changes behaviour. `off` suppresses that warning too —",
"that is the difference between the two, and the reason both exist.",
"An unrecognised value is read as `full`."
],
"model": [
"Model for the summarize/consolidate calls (claude -p). Default 'haiku'.",
"Set 'sonnet' (or another tier) to improve salience + compression-cap",
"compliance; the call is backgrounded so there is no interactive cost.",
"An explicit REMEMBER_MODEL env var overrides this."
],
"reject_pattern": [
"Reject-gate regex keeping model refusals/clarifications out of memory.",
"'' -> the narrow built-in default (anchored refusal stems only).",
"'none' -> gate disabled (only the literal SKIP contract applies).",
"anything else -> a custom case-insensitive regex; invalid falls back to",
"the default. An explicit REMEMBER_REJECT_PATTERN env var overrides this."
],
"handoff_mode": [
"Two or more INTERACTIVE sessions share one project store by design",
"(_resolve_memory_project_dir, even across git worktrees, #56) -- but",
"the handoff note was always one fixed file, remember.md. The second",
"session's /remember silently overwrote the first's, which then",
"survived only in that session's own transcript, nowhere on disk (#363).",
"This is distinct from #221/#222: those protect a PENDING handoff from",
"a session that never writes one back; this is two sessions that each",
"DO write one, to the same name.",
"",
"'single' (default, unchanged) -> every session shares remember.md,",
" exactly as before. The clobber is not fixed for an install that",
" never sets this key -- that trade is deliberate, matching every",
" other behaviour-changing key in this file (data_dir, reject_pattern,",
" git_restore.enabled): a new layout never applies until asked for.",
"'per_session' -> the handoff becomes remember.<session_id>.md, keyed",
" by the SessionStart payload's own session_id. History, recent.md,",
" archive.md and consolidation are untouched -- only the single",
" handoff slot is namespaced.",
"",
"per_session needs a usable session_id to namespace anything. If none",
"reached the hook (the payload carried none, or it failed the #270",
"identifier sanitizer), the session falls back to the shared",
"remember.md -- the '=== HANDOFF ===' hint still points at it, since",
"that path is still correct (withholding the hint here broke external",
"storage mode, where it is the only real path), but a visible line",
"says the fallback happened so it does not read as isolation that",
"never applied. No pruning of stale remember.<session_id>.md files",
"ships with this key: the handoff is the user's own writing with no",
"copy anywhere else, and a delete keyed on \"the transcript looks gone\"",
"can misfire for reasons other than the transcript actually being",
"gone. Stale per-session handoffs accumulate until removed by hand."
],
"git_backup.allow_remote_change": [
"Guard against a swapped push remote. The backup hook records the remote",
"URL on the first push and aborts any later push whose URL differs — a",
"changed URL can mean a poisoned config.json pointing at an attacker's",
"host. false (default) keeps the guard on.",
"",
"Set true only to deliberately re-point at a new repo; the next push",
"records the new URL, so set it back to false afterwards.",
"",
"Note: before the jq-free config fallback landed, machines without jq",
"always read this as false regardless of configuration. It is now honoured",
"there too, so an existing true in your config takes effect on those",
"machines for the first time."
],
"hooks.dispatch_timeout_seconds": [
"How long ONE hooks.d/ listener may take before it is stopped. Nothing",
"bounded them until #286: dispatch() runs them in the foreground, so one",
"that blocks stalled the agent for as long as it blocked and logged",
"nothing, because nothing had failed.",
"",
"Two numbers, because two kinds of caller wait on this and only one of",
"them is a person. The interactive one covers after_post_tool,",
"after_user_prompt and both session_start events; the detached one covers",
"before/after_save and before/after_consolidate, dispatched from a nohup'd",
"process nobody is waiting on. Claude Code kills a hook at 60s by default,",
"so an interactive budget at or above that is the host killing the process",
"with no report from us.",
"",
"The shipped listeners take 0.17-0.58s in the foreground (their git I/O is",
"in a disowned subshell and does not count), so these are ~25x and ~200x",
"headroom. Set 0 to disable the bound.",
"",
"The kill is SIGTERM, then SIGKILL after dispatch_kill_grace_seconds, so a",
"listener that traps EXIT gets to release its lock. It is a courtesy, not",
"a veto: one that traps TERM and keeps going is killed anyway. The signal",
"goes to the listener's own PID, never its process group — a listener's",
"disowned background work is deliberately left running."
],
"session_start_slow_threshold_s": [
"#706: below this many whole seconds, the SessionStart hook's own",
"wall-clock duration is written ONLY to the daily log -- a line on",
"every start would be noise on a healthy host. At or above it, the",
"same number is also printed into the session under '=== SESSION-",
"START ===', so a slow HOST (not a slow plugin) is visible without",
"having to go looking for the daily log first. Default 5. Set 0 to",
"surface every start; there is no way to disable the daily-log line,",
"which costs one more log() call, no extra fork.",
"The plugin cannot diagnose a slow HOST -- see #660's own",
"investigation, a kernel-leak host that inflated every fork 3-10x --",
"and does not try to; it only says how long it took, which is what",
"lets a user go looking in the right place."
],
"data_dir": [
"Controls where memory data files are stored.",
"",
"Legacy default — project-relative (unchanged behaviour):",
" '.remember' → {project_dir}/.remember",
"",
"External mode — absolute or home-relative path:",
" '~/.remember/{slug}' → ~/.remember/<session-dir-slug>/",
" '/var/lib/remember/{slug}' → /var/lib/remember/<slug>/",
"",
"{slug} is replaced by the same slug Claude Code uses for",
"~/.claude/projects/<slug>/, so each project gets its own",
"isolated subtree. The .gitignore is NOT written in external",
"mode — manage the ~/.remember/ tree as your own private repo.",
"",
"User-global config lives at ~/.remember/config.json and is",
"merged on top of this file (per-project overrides user-global",
"which overrides these shipped defaults). See config.user.example.json."
]
}
}