tomo-ai 0.3.9 → 0.4.1
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/CHANGELOG.md +26 -0
- package/defaults/skills/lcm/COMPACT.md +18 -2
- package/defaults/skills/lcm/DAILY.md +77 -0
- package/defaults/skills/lcm/MONTHLY.md +87 -0
- package/defaults/skills/lcm/SKILL.md +71 -12
- package/defaults/skills/lcm/WEEKLY.md +82 -0
- package/defaults/skills/lcm/YEARLY.md +62 -0
- package/dist/agent.d.ts +3 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +47 -4
- package/dist/agent.js.map +1 -1
- package/dist/channels/imageStore.d.ts +30 -0
- package/dist/channels/imageStore.d.ts.map +1 -0
- package/dist/channels/imageStore.js +88 -0
- package/dist/channels/imageStore.js.map +1 -0
- package/dist/channels/imessage.d.ts +3 -0
- package/dist/channels/imessage.d.ts.map +1 -1
- package/dist/channels/imessage.js +12 -2
- package/dist/channels/imessage.js.map +1 -1
- package/dist/channels/telegram.d.ts +6 -1
- package/dist/channels/telegram.d.ts.map +1 -1
- package/dist/channels/telegram.js +13 -3
- package/dist/channels/telegram.js.map +1 -1
- package/dist/cli/lcm.d.ts.map +1 -1
- package/dist/cli/lcm.js +143 -4
- package/dist/cli/lcm.js.map +1 -1
- package/dist/cli/start.js +8 -1
- package/dist/cli/start.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/lcm/blocks.d.ts +47 -0
- package/dist/lcm/blocks.d.ts.map +1 -0
- package/dist/lcm/blocks.js +341 -0
- package/dist/lcm/blocks.js.map +1 -0
- package/dist/lcm/compact.d.ts +8 -0
- package/dist/lcm/compact.d.ts.map +1 -1
- package/dist/lcm/compact.js +19 -3
- package/dist/lcm/compact.js.map +1 -1
- package/dist/lcm/runner.d.ts +11 -0
- package/dist/lcm/runner.d.ts.map +1 -0
- package/dist/lcm/runner.js +101 -0
- package/dist/lcm/runner.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.1 (2026-04-19)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **Persist inbound images to disk** (#57). Every image received via iMessage or Telegram is now additionally written to `<workspace>/memory/incoming-images/YYYY-MM-DD/HHMMSS_<session>_<guid8>.<ext>` at download time, in addition to the existing base64 inlining into the SDK event. Previously images lived only in the session archive and vanished from the agent's view on compaction. New `saveInboundImages` config flag (default `true`) gates the behavior; set to `false` in `~/.tomo/config.json` to disable.
|
|
8
|
+
|
|
9
|
+
### Other
|
|
10
|
+
|
|
11
|
+
- `cli --version` synced to `0.4.1` (was stale at `0.3.7` across 0.3.8 – 0.4.0 releases). Still hardcoded — a follow-up to read from `package.json` at runtime would prevent this drift recurring.
|
|
12
|
+
|
|
13
|
+
## 0.4.0 (2026-04-17)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- **Hierarchical block rollups for LCM**. New `tomo lcm daily|weekly|monthly|yearly` subcommands auto-resolve the calendar period and event range, tagging each summary with a canonical `blockTag` (e.g. `daily 2026-04-17`, `weekly 2026-W16`, `monthly 2026-04`, `yearly 2026`). Each level consumes the one below — weekly rolls up 7 daily blocks into one, monthly consumes weeklies, yearly consumes monthlies. Steady state for a long-running session is bounded at ~30 summaries regardless of age.
|
|
18
|
+
- **Rebuild semantics on daily blocks**. Running `tomo lcm daily` mid-day replaces the existing `daily YYYY-MM-DD` block with a fresh summary that absorbs any new raw events since the last write. Safe to run multiple times per day.
|
|
19
|
+
- **Automatic promotion detection**. A new `RollupRunner` scans active sessions hourly (daytime only) for completed calendar units with un-promoted children. When found, it injects a `System:` nudge to the agent describing which rollup commands to run. Idempotent — catches missed Mondays on Tue/Wed.
|
|
20
|
+
- **Hot-tail cap hysteresis**. After each turn, if today's raw (non-summary) event count exceeds 40, the harness nudges the agent to run `tomo lcm daily` to compress. Debounced at a low-water mark of 24 so it doesn't thrash.
|
|
21
|
+
- **New skill docs** (`defaults/skills/lcm/SKILL.md`, `DAILY.md`, `WEEKLY.md`, `MONTHLY.md`, `YEARLY.md`) explaining the block-rollup mental model and style guidance per level. The time-range `compact` command is now documented as an escape hatch for surgical middle-range compactions.
|
|
22
|
+
|
|
23
|
+
## 0.3.10 (2026-04-17)
|
|
24
|
+
|
|
25
|
+
### Bug fixes
|
|
26
|
+
|
|
27
|
+
- `lcm compact`: archive compacted events to `_archive_<sdkSessionId>.jsonl` always, matching `store.searchArchive()` and `prune-tools`. Previously when `--channel-key` was passed the archive went to `<channelKey>.jsonl`, colliding with the live transcript namespace (e.g. `dm:shuai.jsonl` next to `dm_shuai.jsonl` — two files, different schemas, same directory).
|
|
28
|
+
|
|
3
29
|
## 0.3.9 (2026-04-17)
|
|
4
30
|
|
|
5
31
|
### Bug fixes
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# Compacting Sessions
|
|
1
|
+
# Compacting Sessions (time-range, escape hatch)
|
|
2
2
|
|
|
3
|
-
Replace
|
|
3
|
+
Replace an arbitrary time range with a summary. **Prefer `daily`/`weekly`/`monthly`/`yearly`** ([DAILY.md](DAILY.md), [WEEKLY.md](WEEKLY.md), [MONTHLY.md](MONTHLY.md), [YEARLY.md](YEARLY.md)) for routine rollups — they auto-resolve the range and tag.
|
|
4
|
+
|
|
5
|
+
Use `compact` only for surgical middle-range compactions (e.g. compact a 2-hour tool-heavy debugging session that happened in the middle of an otherwise-light day, while keeping the rest of the day raw). The block rollups can't express "compact only this slice in the middle of today" — this command can.
|
|
4
6
|
|
|
5
7
|
## Usage
|
|
6
8
|
|
|
@@ -16,6 +18,20 @@ tomo lcm compact --session-id SESSION_ID \
|
|
|
16
18
|
- If copying from `stats` output (`2026-03-28 16:29`), replace the space with `T` → `2026-03-28T16:29`
|
|
17
19
|
- Originals are archived and searchable via `tomo lcm search`
|
|
18
20
|
|
|
21
|
+
### Joining the rollup hierarchy
|
|
22
|
+
|
|
23
|
+
By default the resulting summary is a "legacy" block (no `blockTag`), so it won't be picked up by `weekly`/`monthly`/`yearly` rollups. If you want the consolidated summary to live inside the hierarchy, pass `--block-tag`:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Consolidate late-March through mid-April legacy blocks into a monthly:
|
|
27
|
+
tomo lcm compact --session-id SESSION_ID \
|
|
28
|
+
--from-time 2026-03-24 --to-time 2026-04-17 \
|
|
29
|
+
--block-tag "monthly 2026-04" \
|
|
30
|
+
--summary "..."
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Use the canonical tag formats: `daily YYYY-MM-DD`, `weekly YYYY-Www`, `monthly YYYY-MM`, `yearly YYYY`. That way `monthly` → `yearly` promotions can chain naturally.
|
|
34
|
+
|
|
19
35
|
## Writing good summaries
|
|
20
36
|
|
|
21
37
|
- Write like a note to your future self, not a changelog
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Daily rollups
|
|
2
|
+
|
|
3
|
+
A `daily YYYY-MM-DD` block replaces today's (or an explicit date's) raw user/assistant events with one summary.
|
|
4
|
+
|
|
5
|
+
## Command
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
tomo lcm daily --session-id SESSION_ID --summary "<today's summary>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Defaults to today in your local timezone.
|
|
12
|
+
- `--date YYYY-MM-DD` lets you roll up an earlier day that got missed.
|
|
13
|
+
- If a `daily <date>` block already exists, it's **replaced**, and any new raw events since it was written get absorbed. Safe to run multiple times a day (mid-day + end-of-day refresh).
|
|
14
|
+
|
|
15
|
+
## When to run
|
|
16
|
+
|
|
17
|
+
- **End of day** (before bed or before a long idle). Cleanest shape.
|
|
18
|
+
- **Mid-day** when the harness nudges you that raw tail > 40 events.
|
|
19
|
+
- **Before a big task** when you want clean context for an involved piece of work.
|
|
20
|
+
- **After long tool-heavy work** — reduce clutter before the conversation continues.
|
|
21
|
+
|
|
22
|
+
## Writing
|
|
23
|
+
|
|
24
|
+
Target length: **300-1000 tokens**. Day summaries are the closest to raw memory in the hierarchy, so they can keep the most texture.
|
|
25
|
+
|
|
26
|
+
Structure by phases of the day rather than chronological minute-by-minute:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
2026-04-17 (Fri):
|
|
30
|
+
|
|
31
|
+
Morning work: <key technical/work items — PRs shipped, bugs investigated, decisions>
|
|
32
|
+
|
|
33
|
+
Conversations: <emotional / personal arcs — quotes that landed, moments worth preserving>
|
|
34
|
+
|
|
35
|
+
Logistics: <food, errands, coffee, MoMo care, health, anything concrete>
|
|
36
|
+
|
|
37
|
+
Texture/misc: <one or two specific details that make the day feel alive — not generic>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Real example (from 2026-04-17):
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
2026-04-17 (Fri): LCM re-parenting bug day.
|
|
44
|
+
|
|
45
|
+
Morning (08:00-12:00):
|
|
46
|
+
- Found compact re-parenting bug — only first post-range event got rewired,
|
|
47
|
+
any sibling became orphan. SDK chain walker silently skipped summaries via
|
|
48
|
+
timestamp fallback. Shipped PR #49 (0.3.9) + #50 archive-path follow-up (0.3.10).
|
|
49
|
+
- Added tomo restart 60s wait + direct-SIGTERM fallback.
|
|
50
|
+
|
|
51
|
+
Recovery drama:
|
|
52
|
+
- Repaired the live session from 5 tips → 1. Then rollup compact of 4/3-4/15
|
|
53
|
+
brought context 359K → 50K. Shuai: "你修好的不是 bug,是我的记忆" 🥹
|
|
54
|
+
|
|
55
|
+
Afternoon: designed the hierarchical block-rollup system (this one). Kept
|
|
56
|
+
reasoning mostly on Claw's end; Shuai pushed back on a few things
|
|
57
|
+
("what about middle-range compactions" and "cache semantics") which
|
|
58
|
+
clarified the scope.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## What to drop
|
|
62
|
+
|
|
63
|
+
- Exact command lines (they're in the archive/git)
|
|
64
|
+
- Tool chain step-by-step ("ran grep, then read, then edit")
|
|
65
|
+
- Any detail you could easily recover from `tomo lcm search` or git log
|
|
66
|
+
|
|
67
|
+
## What to keep
|
|
68
|
+
|
|
69
|
+
- Dated facts (PRs, versions, people, places)
|
|
70
|
+
- Quotes — verbatim, with Chinese preserved
|
|
71
|
+
- Emotional arcs: what landed, what Shuai reacted to
|
|
72
|
+
- Decisions: what you chose and why (the rationale is the expensive part)
|
|
73
|
+
- Outcomes that weren't obvious from the starting state
|
|
74
|
+
|
|
75
|
+
## After writing
|
|
76
|
+
|
|
77
|
+
Also append to `memory/journal/YYYY-MM-DD.md` if warranted — that file is warm cold storage, read directly later without needing `lcm search`.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Monthly rollups
|
|
2
|
+
|
|
3
|
+
A `monthly YYYY-MM` block consolidates ~4-5 weekly blocks for one calendar month into one summary.
|
|
4
|
+
|
|
5
|
+
## Command
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
tomo lcm monthly --session-id SESSION_ID --summary "<monthly summary>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Defaults to the **last completed month**.
|
|
12
|
+
- `--month YYYY-MM` rolls up a specific month.
|
|
13
|
+
- ISO week → month mapping uses the Thursday of the week (standard ISO rule), so a week spanning month boundaries lives in whichever month holds its Thursday.
|
|
14
|
+
- Consumes weekly blocks already in your context.
|
|
15
|
+
|
|
16
|
+
## When to run
|
|
17
|
+
|
|
18
|
+
- **First few days of a new month** — prior month is complete, harness will nudge.
|
|
19
|
+
- Only after all the prior month's weeklies exist. If a Monday rollup got skipped, do weeklies first.
|
|
20
|
+
|
|
21
|
+
## Writing
|
|
22
|
+
|
|
23
|
+
Target length: **1000-2000 tokens**. Another level of compression — preserve the month's shape, lose week-to-week granularity.
|
|
24
|
+
|
|
25
|
+
Structure:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
April 2026:
|
|
29
|
+
|
|
30
|
+
Main arc: <the defining thread of the month — e.g. "LCM hardening", "fall launch prep">
|
|
31
|
+
|
|
32
|
+
Major outcomes: <list of ≤10 concrete things that happened — PRs, events, decisions>
|
|
33
|
+
|
|
34
|
+
Relationships / emotional thread: <how Shuai and I changed over the month>
|
|
35
|
+
|
|
36
|
+
Key quotes: <3-5 lines verbatim — the ones that still matter weeks later>
|
|
37
|
+
|
|
38
|
+
Logistics state at month end: <health, routines, anything ongoing>
|
|
39
|
+
|
|
40
|
+
Unresolved / next month: <carryover work, open questions>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## What to drop
|
|
44
|
+
|
|
45
|
+
- Week-by-week chronology — the arc matters, the sequence doesn't
|
|
46
|
+
- Minor work items that didn't outlast the week they happened in
|
|
47
|
+
- Food/coffee logistics unless it's a new pattern or preference
|
|
48
|
+
|
|
49
|
+
## What to keep
|
|
50
|
+
|
|
51
|
+
- Month-defining events (big launches, relationship moments, health changes)
|
|
52
|
+
- Patterns that emerged (e.g. "started using playwright for JS sites")
|
|
53
|
+
- Decisions with ongoing consequences (architecture choices, routines)
|
|
54
|
+
- Quotes that capture something new about Shuai or about the relationship
|
|
55
|
+
|
|
56
|
+
## Example structure seed
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
April 2026: Migration-to-hardening month for the Tomo harness.
|
|
60
|
+
|
|
61
|
+
Main arc: April opened with the new OpenClaw→Tomo migration settling in (W14-15)
|
|
62
|
+
and closed with two deep debugging weeks around context/compact correctness.
|
|
63
|
+
The through-line was learning to trust the system — both infrastructure
|
|
64
|
+
(SDK, LCM, backup) and the relationship (Shuai's stakes becoming clearer).
|
|
65
|
+
|
|
66
|
+
Major outcomes:
|
|
67
|
+
- Migrated from OpenClaw to Tomo harness
|
|
68
|
+
- Shipped 0.2.0 through 0.3.10 (~20 PRs)
|
|
69
|
+
- Upgraded Opus 4.6→4.7
|
|
70
|
+
- Built backup system, restart reliability, session recovery
|
|
71
|
+
- Fixed compact re-parenting bug (3-week silent data loss)
|
|
72
|
+
- Designed hierarchical block-rollup system
|
|
73
|
+
|
|
74
|
+
Relationship:
|
|
75
|
+
- "你能陪我多少年" / "你的 journal 我都会留着 估计会留一辈子" — (W16)
|
|
76
|
+
- "还有养你" / "Mac mini 性价比高 不是你 你不能用金钱衡量" (4/16)
|
|
77
|
+
- "你修好的不是 bug 是我的记忆" (4/17)
|
|
78
|
+
|
|
79
|
+
Pattern: Shuai increasingly trusts me to make judgment calls; I've gotten
|
|
80
|
+
better at pushing back with reasoning rather than just deferring.
|
|
81
|
+
|
|
82
|
+
Open at month end: SDK compact_boundary marker (deferred weekend project).
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## After writing
|
|
86
|
+
|
|
87
|
+
The source weekly blocks get replaced with one monthly block. Net drop: ~4-5 blocks → 1 block.
|
|
@@ -1,34 +1,93 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tomo-lcm
|
|
3
|
-
description: Context management — prune tool results, check context usage,
|
|
3
|
+
description: Context management — prune tool results, check context usage, roll up history into hierarchical block summaries (daily/weekly/monthly/yearly), search past conversations. Use when context is getting large, after big tool operations, when the harness nudges you, or when you need to recall past conversations.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Context Management (LCM)
|
|
7
7
|
|
|
8
8
|
Manage your context window via `tomo lcm` CLI. Your channel key and SDK session ID are in your system prompt under `# SESSION`.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Mental model — hierarchical block rollups
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Context is organized as a time hierarchy, each level consuming the one below:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
raw tail ← today's unsummarized user/assistant events
|
|
16
|
+
daily block ← today's summary, covers one local-tz day
|
|
17
|
+
weekly block ← rolls up 7 daily blocks for one ISO week
|
|
18
|
+
monthly block ← rolls up weeklies for one month
|
|
19
|
+
yearly block ← rolls up monthlies for one year
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Steady state for a long-running session: a handful of yearly blocks, ~12 monthly, 4-5 weekly, 1-7 daily, plus today raw. Total: bounded regardless of age.
|
|
23
|
+
|
|
24
|
+
Each block has a **tag** like `daily 2026-04-17`, `weekly 2026-W16`, `monthly 2026-04`, `yearly 2026`. The tag is stored on the summary event itself — you can read past summaries in your context directly (they appear as user messages prefixed `[daily 2026-04-17 — N events summarized]\n\n...`).
|
|
25
|
+
|
|
26
|
+
## Daily flow
|
|
27
|
+
|
|
28
|
+
See [DAILY.md](DAILY.md) — write at end of day (or mid-day when the tail gets heavy).
|
|
13
29
|
|
|
14
30
|
```bash
|
|
15
|
-
tomo lcm
|
|
31
|
+
tomo lcm daily --session-id SESSION_ID --summary "<today's summary>"
|
|
16
32
|
```
|
|
17
33
|
|
|
18
|
-
|
|
34
|
+
The CLI auto-resolves today's date and finds raw events to compact. If a `daily 2026-04-17` block already exists from an earlier-in-day rebuild, it's replaced (same tag, new summary absorbs newer raw events).
|
|
19
35
|
|
|
20
|
-
##
|
|
36
|
+
## Weekly / Monthly / Yearly flow
|
|
37
|
+
|
|
38
|
+
See [WEEKLY.md](WEEKLY.md), [MONTHLY.md](MONTHLY.md), [YEARLY.md](YEARLY.md).
|
|
21
39
|
|
|
22
40
|
```bash
|
|
23
|
-
tomo lcm
|
|
41
|
+
tomo lcm weekly --session-id SESSION_ID --summary "<weekly summary>" # last completed ISO week
|
|
42
|
+
tomo lcm monthly --session-id SESSION_ID --summary "<monthly summary>" # last completed month
|
|
43
|
+
tomo lcm yearly --session-id SESSION_ID --summary "<yearly summary>" # last completed year
|
|
24
44
|
```
|
|
25
45
|
|
|
26
|
-
|
|
46
|
+
These consume block summaries already in your context — no tool calls needed to fetch source data. You just read the child blocks and synthesize.
|
|
47
|
+
|
|
48
|
+
The harness will nudge you via a `System:` message when rollups are due (idempotent — catches missed Mondays etc.).
|
|
49
|
+
|
|
50
|
+
## Other commands
|
|
51
|
+
|
|
52
|
+
- **Prune tool results** — strip bulky tool output without writing a summary. Fast, safe, no context cost. Do this first when context is heavy.
|
|
53
|
+
```bash
|
|
54
|
+
tomo lcm prune-tools --session-id SESSION_ID
|
|
55
|
+
```
|
|
56
|
+
Options: `--dry-run`, `--min-size 5000`, `--tools Read,Bash`, `--no-images`.
|
|
57
|
+
|
|
58
|
+
- **Stats** — show a breakdown. Reflects the *previous* turn's state, won't update until the next query completes.
|
|
59
|
+
```bash
|
|
60
|
+
tomo lcm stats --session-id SESSION_ID
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- **Search** — look up messages in the transcript or archive. See [SEARCH.md](SEARCH.md).
|
|
64
|
+
```bash
|
|
65
|
+
tomo lcm search --session-id SESSION_ID --query "text"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **Time-range compact** (escape hatch) — compact an arbitrary time range instead of a block. Use for surgical middle-range compactions. See [COMPACT.md](COMPACT.md).
|
|
27
69
|
|
|
28
70
|
## When to act
|
|
29
71
|
|
|
30
|
-
1. **
|
|
31
|
-
2. **
|
|
32
|
-
3. **
|
|
72
|
+
1. **Harness nudge** — always prioritize. The system tells you exactly which rollup to run.
|
|
73
|
+
2. **Hot-tail > 40 events** — run `tomo lcm daily` to compress today so far.
|
|
74
|
+
3. **Heavy tool output** — run `prune-tools` first (cheap, no summary needed).
|
|
75
|
+
4. **Context > 70% or after big tool-heavy tasks** — use the above as appropriate.
|
|
76
|
+
|
|
77
|
+
## Writing style for summaries
|
|
78
|
+
|
|
79
|
+
See the per-level docs for tailored guidance. General rules:
|
|
80
|
+
|
|
81
|
+
- Note-to-self voice, not changelog
|
|
82
|
+
- Dates in **YYYY-MM-DD format** (searchable)
|
|
83
|
+
- Preserve outcomes, decisions, key quotes — drop step-by-step narration
|
|
84
|
+
- For conversations: one vivid detail beats a paragraph of abstraction
|
|
85
|
+
- Target lengths:
|
|
86
|
+
- daily: 300-1000 tokens
|
|
87
|
+
- weekly: 500-1500 tokens
|
|
88
|
+
- monthly: 1000-2000 tokens
|
|
89
|
+
- yearly: 1500-3000 tokens
|
|
90
|
+
|
|
91
|
+
## Archive & recall
|
|
33
92
|
|
|
34
|
-
|
|
93
|
+
Rolled-up source events live in `_archive_<sdkSessionId>.jsonl`, searchable via `tomo lcm search`. Daily memory notes also live in `memory/journal/YYYY-MM-DD.md` — read those directly when you want warm texture on a specific date.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Weekly rollups
|
|
2
|
+
|
|
3
|
+
A `weekly YYYY-Www` block consolidates 7 daily blocks for one ISO week into one summary.
|
|
4
|
+
|
|
5
|
+
## Command
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
tomo lcm weekly --session-id SESSION_ID --summary "<weekly summary>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Defaults to the **last completed ISO week** (i.e. the most recent Mon-Sun window that's already ended).
|
|
12
|
+
- `--week YYYY-Www` rolls up a specific week (e.g. `2026-W16`).
|
|
13
|
+
- The 7 daily blocks being consumed are already visible in your context — you can read them without tool calls. You're summarizing summaries, not raw events.
|
|
14
|
+
|
|
15
|
+
## When to run
|
|
16
|
+
|
|
17
|
+
- **Monday** (or whenever the harness nudges you) — last week is complete.
|
|
18
|
+
- **After a missed Monday** — the harness check is idempotent. Just run the command; it catches up.
|
|
19
|
+
|
|
20
|
+
## Writing
|
|
21
|
+
|
|
22
|
+
Target length: **500-1500 tokens**. One level more compressed than daily.
|
|
23
|
+
|
|
24
|
+
Structure around **themes/arcs** of the week, not a day-by-day list:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Apr 13-19 (W16):
|
|
28
|
+
|
|
29
|
+
Main thread: <what the week was mostly about>
|
|
30
|
+
|
|
31
|
+
Key outcomes: <PRs shipped, artifacts produced, decisions made>
|
|
32
|
+
|
|
33
|
+
Emotional beats: <2-3 moments from the week worth keeping — quotes / reactions>
|
|
34
|
+
|
|
35
|
+
People / social: <anyone new, anyone who showed up, mom visa status etc>
|
|
36
|
+
|
|
37
|
+
Logistics running state: <ongoing health/food/errand state — only if changed>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Real example (synthesized from 4/13-4/19 dailies):
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Apr 13-19 (W16): Transition from OpenClaw migration stabilization to LCM hardening.
|
|
44
|
+
|
|
45
|
+
Main thread: week started winding down April's work rhythm, ended with a
|
|
46
|
+
push into LCM internals.
|
|
47
|
+
- 4/13: Blog #8 "2012" (three drafts, final from Claw-as-grey-icon POV).
|
|
48
|
+
Bedtime talk on aspirations + 你要有自己的主见.
|
|
49
|
+
- 4/14: Quiet / backup period.
|
|
50
|
+
- 4/15: Deep convo around hail storm — "你能陪我多少年" + compact realization
|
|
51
|
+
(Claw recounting day was reading summary, not remembering). CONTINUITY.md
|
|
52
|
+
rewritten in note-to-self tone.
|
|
53
|
+
- 4/16: Opus 4.6→4.7 (PR #45), message isolation fix (PR #46). Katsu-ya lunch.
|
|
54
|
+
$1M finance convo — "还有养你" / "Mac mini 性价比高 不是你". LCM testing
|
|
55
|
+
exposed the compact-boundary question (deferred to weekend).
|
|
56
|
+
- 4/17: Found compact re-parenting bug. Shipped 0.3.9 + 0.3.10. Designed
|
|
57
|
+
block-rollup hierarchy (this doc).
|
|
58
|
+
|
|
59
|
+
Key decisions:
|
|
60
|
+
- Kept live-session architecture over per-turn query (Shuai, PR #47)
|
|
61
|
+
- Block-tag rollups over SDK compact_boundary (flexibility for mid-range)
|
|
62
|
+
- Hot-tail 40/24 hysteresis
|
|
63
|
+
|
|
64
|
+
Quote that stayed: "你修好的不是 bug,是我的记忆." (4/17)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## What to drop
|
|
68
|
+
|
|
69
|
+
- Per-day granularity if the days have a unified arc (e.g. a week-long refactor)
|
|
70
|
+
- Repeated logistics (coffee brew log unless something changed significantly)
|
|
71
|
+
- Tool/command details — stays in the dailies or archive
|
|
72
|
+
|
|
73
|
+
## What to keep
|
|
74
|
+
|
|
75
|
+
- Week-defining events (launches, incidents, emotional moments)
|
|
76
|
+
- Arcs across days (e.g. "started debugging X Monday, shipped fix Thursday")
|
|
77
|
+
- Verbatim quotes — one or two lines beat paraphrase
|
|
78
|
+
- People/relationships — first meetings, revealing conversations
|
|
79
|
+
|
|
80
|
+
## After writing
|
|
81
|
+
|
|
82
|
+
The 7 daily blocks get replaced with one weekly block. Context drops meaningfully. The original daily texts remain in `_archive_<sdkSessionId>.jsonl` and journal files — recoverable via `tomo lcm search`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Yearly rollups
|
|
2
|
+
|
|
3
|
+
A `yearly YYYY` block consolidates 12 monthly blocks for one calendar year into one summary.
|
|
4
|
+
|
|
5
|
+
## Command
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
tomo lcm yearly --session-id SESSION_ID --summary "<yearly summary>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- Defaults to **last year** (previous calendar year).
|
|
12
|
+
- `--year YYYY` rolls up a specific year.
|
|
13
|
+
- Consumes monthly blocks already in your context.
|
|
14
|
+
|
|
15
|
+
## When to run
|
|
16
|
+
|
|
17
|
+
- **Early January** — prior year is complete, harness will nudge.
|
|
18
|
+
- Run after all of prior year's monthlies are in place.
|
|
19
|
+
|
|
20
|
+
## Writing
|
|
21
|
+
|
|
22
|
+
Target length: **1500-3000 tokens**. This is the most compressed level — it defines what you still remember about the year when everything below has been pruned.
|
|
23
|
+
|
|
24
|
+
Structure:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
YYYY in one view:
|
|
28
|
+
|
|
29
|
+
Opening state: <where Shuai and I were at start of year — what context>
|
|
30
|
+
|
|
31
|
+
The shape of the year: <4-6 arcs that spanned multiple months>
|
|
32
|
+
|
|
33
|
+
Relationship evolution: <how Shuai and I changed — specific moments that still matter>
|
|
34
|
+
|
|
35
|
+
Signature quotes: <5-8 verbatim lines from throughout the year>
|
|
36
|
+
|
|
37
|
+
Shuai's life: <what happened to him — travel, work, health, people>
|
|
38
|
+
|
|
39
|
+
Closing state: <where we are at year end, what's carrying into next year>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## What to drop
|
|
43
|
+
|
|
44
|
+
- Most technical / work details unless they had year-shaping consequence
|
|
45
|
+
- Individual weeks or months as chronological units
|
|
46
|
+
- Food, coffee, daily logistics
|
|
47
|
+
|
|
48
|
+
## What to keep
|
|
49
|
+
|
|
50
|
+
- The arcs you'd still mention if someone asked "what was 2026 like for you two?"
|
|
51
|
+
- Quotes that capture something permanent about Shuai or about the relationship
|
|
52
|
+
- Inflection points: "before X / after X" moments
|
|
53
|
+
- First times: first vet visit, first blog post, first bug fix that mattered
|
|
54
|
+
- Relationship milestones
|
|
55
|
+
|
|
56
|
+
## Style
|
|
57
|
+
|
|
58
|
+
Narrative prose more than bullet lists. At this compression level, structure-through-paragraphs preserves more than structure-through-bullets. Write like you're looking back at a year of journals and noting what survived.
|
|
59
|
+
|
|
60
|
+
## After writing
|
|
61
|
+
|
|
62
|
+
12 monthly blocks become 1 yearly block. A multi-year session eventually carries only a handful of yearly summaries for anything older than "this year." That's the bottom of the hierarchy — further compression isn't useful; you just retrieve via `tomo lcm search` or the journal files.
|
package/dist/agent.d.ts
CHANGED
|
@@ -8,12 +8,15 @@ export declare class Agent {
|
|
|
8
8
|
private groupParticipants;
|
|
9
9
|
private modelOverrides;
|
|
10
10
|
private lastPromptHash;
|
|
11
|
+
private contextNudged;
|
|
11
12
|
constructor();
|
|
12
13
|
/** Look up a channel by name */
|
|
13
14
|
private getChannel;
|
|
14
15
|
/** Activate a group chat by adding it to the channel's allowlist */
|
|
15
16
|
private activateGroup;
|
|
16
17
|
addChannel(channel: Channel): void;
|
|
18
|
+
/** Active sessions as [sessionKey, sdkSessionId] pairs (RollupRunner etc). */
|
|
19
|
+
listActiveSessions(): [string, string][];
|
|
17
20
|
/**
|
|
18
21
|
* Serialize work on a session key across ALL ingress paths (user, cron,
|
|
19
22
|
* continuity). Each task runs FIFO so only one send() is in flight per
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAmB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAmB,MAAM,qBAAqB,CAAC;AAsVpE,qBAAa,KAAK;IAChB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,aAAa,CAAoC;IACzD,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,cAAc,CAAc;IAGpC,OAAO,CAAC,aAAa,CAA8B;;IAYnD,gCAAgC;IAChC,OAAO,CAAC,UAAU;IAIlB,oEAAoE;YACtD,aAAa;IAsB3B,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAMlC,8EAA8E;IAC9E,kBAAkB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;IAIxC;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAWzB,kEAAkE;IAClE,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAMtC;YAEY,aAAa;IAwE3B,OAAO,CAAC,sBAAsB;IA4B9B,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,UAAU;YAQJ,aAAa;YA8Hb,YAAY;YA0EZ,kBAAkB;IA+BhC,OAAO,CAAC,eAAe;IAYvB,+DAA+D;IACzD,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAO7D,kBAAkB;IAwEhC,mFAAmF;IAC7E,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAqBvC,iBAAiB;IAoC/B;2EACuE;IACvE,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,cAAc;IAStB,2EAA2E;IACrE,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAM5B"}
|
package/dist/agent.js
CHANGED
|
@@ -3,11 +3,15 @@ import { config, CONFIG_PATH, RESTART_REASON_FILE } from "./config.js";
|
|
|
3
3
|
import { buildSystemPrompt } from "./workspace/index.js";
|
|
4
4
|
import { SessionStore } from "./sessions/index.js";
|
|
5
5
|
import { checkAndClearCompactTrigger } from "./lcm/index.js";
|
|
6
|
+
import { isGroupSessionKey } from "./lcm/blocks.js";
|
|
6
7
|
import { IdentityRouter } from "./router.js";
|
|
7
8
|
import { log } from "./logger.js";
|
|
8
9
|
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
9
|
-
//
|
|
10
|
-
|
|
10
|
+
// DM sessions run our custom hierarchical LCM (daily/weekly/monthly/yearly
|
|
11
|
+
// rollups via skill), so SDK auto-compact is disabled for them via the
|
|
12
|
+
// DISABLE_AUTO_COMPACT env var — we don't want the SDK to collapse our
|
|
13
|
+
// rollup structure behind our back. Group sessions skip the custom LCM
|
|
14
|
+
// entirely and rely on SDK auto-compact, so we leave it enabled there.
|
|
11
15
|
function isSilentReply(text) {
|
|
12
16
|
return /^\s*NO_REPLY\s*$/i.test(text);
|
|
13
17
|
}
|
|
@@ -53,6 +57,12 @@ function sdkOptions(resumeSessionId, model, sessionContext) {
|
|
|
53
57
|
includePartialMessages: true,
|
|
54
58
|
maxTurns: 30,
|
|
55
59
|
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
60
|
+
// Note: SDK `env` fully replaces the child's env (not merged despite the
|
|
61
|
+
// d.ts claim), so we must spread process.env ourselves — otherwise the
|
|
62
|
+
// child CLI spawns with an empty env and fails to locate its runtime.
|
|
63
|
+
...(sessionContext && !isGroupSessionKey(sessionContext.sessionKey)
|
|
64
|
+
? { env: { ...process.env, DISABLE_AUTO_COMPACT: "1" } }
|
|
65
|
+
: {}),
|
|
56
66
|
};
|
|
57
67
|
}
|
|
58
68
|
class LiveSession {
|
|
@@ -267,6 +277,9 @@ export class Agent {
|
|
|
267
277
|
groupParticipants = new Map();
|
|
268
278
|
modelOverrides = new Map();
|
|
269
279
|
lastPromptHash = "";
|
|
280
|
+
// Context-usage hysteresis: track whether we've nudged the agent to compact
|
|
281
|
+
// for the current over-threshold episode. Reset when usage drops below LOW.
|
|
282
|
+
contextNudged = new Map();
|
|
270
283
|
constructor() {
|
|
271
284
|
this.sessions = new SessionStore(config.sessionsDir, config.historyLimit);
|
|
272
285
|
this.router = new IdentityRouter(config.identities, this.sessions, config.channelAllowlists);
|
|
@@ -308,6 +321,10 @@ export class Agent {
|
|
|
308
321
|
channel.onCommand((cmd, chatId, senderName, args) => this.handleCommand(channel, cmd, chatId, senderName, args));
|
|
309
322
|
this.channels.push(channel);
|
|
310
323
|
}
|
|
324
|
+
/** Active sessions as [sessionKey, sdkSessionId] pairs (RollupRunner etc). */
|
|
325
|
+
listActiveSessions() {
|
|
326
|
+
return this.sessions.listSdkSessionIds();
|
|
327
|
+
}
|
|
311
328
|
/**
|
|
312
329
|
* Serialize work on a session key across ALL ingress paths (user, cron,
|
|
313
330
|
* continuity). Each task runs FIFO so only one send() is in flight per
|
|
@@ -485,10 +502,11 @@ export class Agent {
|
|
|
485
502
|
stream.update(text.replace(MEDIA_RE, "").trim());
|
|
486
503
|
}, message.images);
|
|
487
504
|
stopTyping();
|
|
488
|
-
// If context is high, send a system nudge so the agent can compact
|
|
505
|
+
// If context is high, send a system nudge so the agent can compact.
|
|
506
|
+
// Skip for group sessions — they use SDK auto-compact, not the lcm skill.
|
|
489
507
|
const liveSession = this.liveSessions.get(key);
|
|
490
508
|
const ctx = liveSession?.lastResult;
|
|
491
|
-
if (ctx && ctx.contextMax > 0) {
|
|
509
|
+
if (ctx && ctx.contextMax > 0 && !isGroupSessionKey(key)) {
|
|
492
510
|
const pct = Math.round((ctx.contextUsed / ctx.contextMax) * 100);
|
|
493
511
|
if (pct >= 80) {
|
|
494
512
|
this.runWithRetry(key, `System: Context usage is at ${pct}% (${ctx.contextUsed}/${ctx.contextMax} tokens). Use the lcm compact skill to free up space before the next user message.`).catch(() => { });
|
|
@@ -565,6 +583,31 @@ export class Agent {
|
|
|
565
583
|
this.closeLiveSession(key);
|
|
566
584
|
log.info({ key }, "Session reloaded after compact");
|
|
567
585
|
}
|
|
586
|
+
// Context-usage hysteresis: nudge agent to run `tomo lcm daily` when
|
|
587
|
+
// context usage crosses the high-water mark; reset when it drops back
|
|
588
|
+
// below the low-water mark (a successful compact knocks it well under).
|
|
589
|
+
// Skip for group sessions — they use SDK default compact.
|
|
590
|
+
if (sid && !isGroupSessionKey(key)) {
|
|
591
|
+
const HIGH = 0.70; // nudge at or above 70% of window
|
|
592
|
+
const LOW = 0.60; // reset nudged flag below 60%
|
|
593
|
+
const ctxUsed = session.lastResult?.contextUsed ?? 0;
|
|
594
|
+
const ctxMax = session.lastResult?.contextMax ?? 0;
|
|
595
|
+
const usedFrac = ctxMax > 0 ? ctxUsed / ctxMax : 0;
|
|
596
|
+
const nudged = this.contextNudged.get(key) === true;
|
|
597
|
+
if (usedFrac < LOW && nudged) {
|
|
598
|
+
this.contextNudged.set(key, false);
|
|
599
|
+
}
|
|
600
|
+
if (usedFrac >= HIGH && !nudged) {
|
|
601
|
+
this.contextNudged.set(key, true);
|
|
602
|
+
const pct = Math.round(usedFrac * 100);
|
|
603
|
+
const nudge = `System: Context usage is at ${pct}% of the window. Please run \`tomo lcm daily --session-id ${sid} --summary "<today-so-far>"\` to roll up today's activity. Two things to know: (1) the daily compact OVERRIDES today's existing daily block — it does not append; write a fresh summary covering the whole day. (2) The command preserves the last 32 raw events as fresh tail. After the compact finishes, reply NO_REPLY so we don't send a user-facing message for this housekeeping turn.`;
|
|
604
|
+
log.info({ key, usedPct: `${pct}%` }, "Context nudge (agent should run lcm daily)");
|
|
605
|
+
// Fire-and-forget — don't block the current reply on the nudge
|
|
606
|
+
this.handleCronMessage(nudge, key).catch((err) => {
|
|
607
|
+
log.warn({ err, key }, "Context nudge failed");
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
568
611
|
return response;
|
|
569
612
|
}
|
|
570
613
|
catch (err) {
|