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.
Files changed (46) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/defaults/skills/lcm/COMPACT.md +18 -2
  3. package/defaults/skills/lcm/DAILY.md +77 -0
  4. package/defaults/skills/lcm/MONTHLY.md +87 -0
  5. package/defaults/skills/lcm/SKILL.md +71 -12
  6. package/defaults/skills/lcm/WEEKLY.md +82 -0
  7. package/defaults/skills/lcm/YEARLY.md +62 -0
  8. package/dist/agent.d.ts +3 -0
  9. package/dist/agent.d.ts.map +1 -1
  10. package/dist/agent.js +47 -4
  11. package/dist/agent.js.map +1 -1
  12. package/dist/channels/imageStore.d.ts +30 -0
  13. package/dist/channels/imageStore.d.ts.map +1 -0
  14. package/dist/channels/imageStore.js +88 -0
  15. package/dist/channels/imageStore.js.map +1 -0
  16. package/dist/channels/imessage.d.ts +3 -0
  17. package/dist/channels/imessage.d.ts.map +1 -1
  18. package/dist/channels/imessage.js +12 -2
  19. package/dist/channels/imessage.js.map +1 -1
  20. package/dist/channels/telegram.d.ts +6 -1
  21. package/dist/channels/telegram.d.ts.map +1 -1
  22. package/dist/channels/telegram.js +13 -3
  23. package/dist/channels/telegram.js.map +1 -1
  24. package/dist/cli/lcm.d.ts.map +1 -1
  25. package/dist/cli/lcm.js +143 -4
  26. package/dist/cli/lcm.js.map +1 -1
  27. package/dist/cli/start.js +8 -1
  28. package/dist/cli/start.js.map +1 -1
  29. package/dist/cli.js +1 -1
  30. package/dist/config.d.ts +2 -0
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js +1 -0
  33. package/dist/config.js.map +1 -1
  34. package/dist/lcm/blocks.d.ts +47 -0
  35. package/dist/lcm/blocks.d.ts.map +1 -0
  36. package/dist/lcm/blocks.js +341 -0
  37. package/dist/lcm/blocks.js.map +1 -0
  38. package/dist/lcm/compact.d.ts +8 -0
  39. package/dist/lcm/compact.d.ts.map +1 -1
  40. package/dist/lcm/compact.js +19 -3
  41. package/dist/lcm/compact.js.map +1 -1
  42. package/dist/lcm/runner.d.ts +11 -0
  43. package/dist/lcm/runner.d.ts.map +1 -0
  44. package/dist/lcm/runner.js +101 -0
  45. package/dist/lcm/runner.js.map +1 -0
  46. 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 a heavy section of conversation with a summary. Use after `prune-tools` if context is still too high.
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, compact heavy sections, search past conversations. Use when context is getting large, after big tool operations, or when you need to recall past conversations.
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
- ## Prune tool results and images (fastest)
10
+ ## Mental model hierarchical block rollups
11
11
 
12
- Strip bulky tool output and base64 images without removing events. No summary needed.
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 prune-tools --session-id SESSION_ID
31
+ tomo lcm daily --session-id SESSION_ID --summary "<today's summary>"
16
32
  ```
17
33
 
18
- Options: `--dry-run` to preview, `--min-size 5000` for only large results, `--tools Read,Bash` to target specific tools, `--no-images` to skip image pruning.
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
- ## Check context
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 stats --session-id SESSION_ID
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
- Stats reflect the *previous* query's statewon't update until the next query completes.
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. **First:** `prune-tools`fast, no summary needed
31
- 2. **Then if still high:** `compact` replaces a section with a summary. See [COMPACT.md](COMPACT.md)
32
- 3. **To recall old conversations:** `search`. See [SEARCH.md](SEARCH.md)
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
- Trigger when context is above 70%, after big tool-heavy tasks, or when the harness warns you.
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
@@ -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;AA4UpE,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;;IAYpC,gCAAgC;IAChC,OAAO,CAAC,UAAU;IAIlB,oEAAoE;YACtD,aAAa;IAsB3B,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAMlC;;;;;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;YA6Hb,YAAY;YA8CZ,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"}
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
- // Disable SDK auto-compaction we manage context ourselves via LCM
10
- process.env.DISABLE_AUTO_COMPACT = "1";
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) {