mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-22 03:03:42 +00:00
mom: add context usage to thread summary, update docs
- Usage summary now shows context tokens vs model context window - Updated CHANGELOG.md with all recent changes - Updated README.md with new file structure (log.jsonl/context.jsonl)
This commit is contained in:
parent
99fe4802ef
commit
71b776e290
4 changed files with 87 additions and 30 deletions
|
|
@ -2,6 +2,31 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Complete rewrite of message handling architecture
|
||||||
|
- `log.jsonl` is now the source of truth for all channel messages
|
||||||
|
- `context.jsonl` stores LLM context (messages sent to Claude)
|
||||||
|
- Sync mechanism ensures context.jsonl stays in sync with log.jsonl at run start
|
||||||
|
- Session header written immediately on new session creation (not lazily)
|
||||||
|
|
||||||
|
- Backfill improvements
|
||||||
|
- Only backfills channels that already have a `log.jsonl` file
|
||||||
|
- Strips @mentions from backfilled messages (consistent with live messages)
|
||||||
|
- Uses largest timestamp in log for efficient incremental backfill
|
||||||
|
- Fetches DM channels in addition to public/private channels
|
||||||
|
|
||||||
|
- Message handling improvements
|
||||||
|
- Channel chatter (messages without @mention) logged but doesn't trigger processing
|
||||||
|
- Messages sent while mom is busy are logged and synced on next run
|
||||||
|
- Pre-startup messages (replayed by Slack on reconnect) logged but not auto-processed
|
||||||
|
- Stop command executes immediately (not queued), can interrupt running tasks
|
||||||
|
- Channel @mentions no longer double-logged (was firing both app_mention and message events)
|
||||||
|
|
||||||
|
- Usage summary now includes context window usage
|
||||||
|
- Shows current context tokens vs model's context window
|
||||||
|
- Example: `Context: 4.2k / 200k (2.1%)`
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Slack API errors (msg_too_long) no longer crash the process
|
- Slack API errors (msg_too_long) no longer crash the process
|
||||||
|
|
@ -13,20 +38,12 @@
|
||||||
- Private channel messages not being logged
|
- Private channel messages not being logged
|
||||||
- Added `message.groups` to required bot events in README
|
- Added `message.groups` to required bot events in README
|
||||||
- Added `groups:history` and `groups:read` to required scopes in README
|
- Added `groups:history` and `groups:read` to required scopes in README
|
||||||
- `app_mention` handler now logs messages directly instead of relying on `message` event
|
|
||||||
- Added deduplication in `ChannelStore.logMessage()` to prevent double-logging
|
- Stop command now updates "Stopping..." to "Stopped" instead of posting two messages
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Port truncation logic from coding-agent: bash and read tools now use consistent 2000 lines OR 50KB limits with actionable notices
|
- Port truncation logic from coding-agent: bash and read tools now use consistent 2000 lines OR 50KB limits with actionable notices
|
||||||
- Remove redundant context history truncation (tools already provide truncation with actionable hints)
|
|
||||||
|
|
||||||
- Message backfill on startup (#103)
|
|
||||||
- Fetches missed messages from Slack using `conversations.history` API when mom restarts
|
|
||||||
- Backfills up to 3 pages (3000 messages) per channel since last logged timestamp
|
|
||||||
- Includes mom's own responses and user messages (excludes other bots)
|
|
||||||
- Downloads attachments from backfilled messages
|
|
||||||
- Logs progress: channel count, per-channel message counts, total with duration
|
|
||||||
|
|
||||||
## [0.10.2] - 2025-11-27
|
## [0.10.2] - 2025-11-27
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,24 +145,27 @@ You provide mom with a **data directory** (e.g., `./data`) as her workspace. Whi
|
||||||
```
|
```
|
||||||
./data/ # Your host directory
|
./data/ # Your host directory
|
||||||
├── MEMORY.md # Global memory (shared across channels)
|
├── MEMORY.md # Global memory (shared across channels)
|
||||||
|
├── settings.json # Global settings (compaction, retry, etc.)
|
||||||
├── skills/ # Global custom CLI tools mom creates
|
├── skills/ # Global custom CLI tools mom creates
|
||||||
├── C123ABC/ # Each Slack channel gets a directory
|
├── C123ABC/ # Each Slack channel gets a directory
|
||||||
│ ├── MEMORY.md # Channel-specific memory
|
│ ├── MEMORY.md # Channel-specific memory
|
||||||
│ ├── log.jsonl # Full conversation history
|
│ ├── log.jsonl # Full message history (source of truth)
|
||||||
|
│ ├── context.jsonl # LLM context (synced from log.jsonl)
|
||||||
│ ├── attachments/ # Files users shared
|
│ ├── attachments/ # Files users shared
|
||||||
│ ├── scratch/ # Mom's working directory
|
│ ├── scratch/ # Mom's working directory
|
||||||
│ └── skills/ # Channel-specific CLI tools
|
│ └── skills/ # Channel-specific CLI tools
|
||||||
└── C456DEF/ # Another channel
|
└── D456DEF/ # DM channels also get directories
|
||||||
└── ...
|
└── ...
|
||||||
```
|
```
|
||||||
|
|
||||||
**What's stored here:**
|
**What's stored here:**
|
||||||
- Conversation logs and Slack attachments. These are automatically stored by mom
|
- `log.jsonl`: All channel messages (user messages, bot responses). Source of truth.
|
||||||
- Memory files. Context mom remembers across sessions
|
- `context.jsonl`: Messages sent to Claude. Synced from log.jsonl at each run start.
|
||||||
|
- Memory files: Context mom remembers across sessions
|
||||||
- Custom tools/scripts mom creates (aka "skills")
|
- Custom tools/scripts mom creates (aka "skills")
|
||||||
- Working files, cloned repos, generated output
|
- Working files, cloned repos, generated output
|
||||||
|
|
||||||
This is also where mom efficiently greps channel log files for conversation history, giving her essentially infinite context.
|
Mom efficiently greps `log.jsonl` for conversation history, giving her essentially infinite context beyond what's in `context.jsonl`.
|
||||||
|
|
||||||
### Memory
|
### Memory
|
||||||
|
|
||||||
|
|
@ -230,33 +233,41 @@ Mom will read the `SKILL.md` file before using a skill, and reuse stored credent
|
||||||
|
|
||||||
Update mom anytime with `npm install -g @mariozechner/pi-mom`. This only updates the Node.js app on your host. Anything mom installed inside the Docker container remains unchanged.
|
Update mom anytime with `npm install -g @mariozechner/pi-mom`. This only updates the Node.js app on your host. Anything mom installed inside the Docker container remains unchanged.
|
||||||
|
|
||||||
## Message History (log.jsonl)
|
## Message History
|
||||||
|
|
||||||
Each channel's `log.jsonl` contains the full conversation history. Every message, tool call, and result. Format: one JSON object per line with ISO 8601 timestamps:
|
Mom uses two files per channel to manage messages:
|
||||||
|
|
||||||
|
### log.jsonl (Source of Truth)
|
||||||
|
|
||||||
|
All channel messages are stored here. This includes user messages, channel chatter (messages without @mention), and bot responses. Format: one JSON object per line with ISO 8601 timestamps:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface LoggedMessage {
|
interface LoggedMessage {
|
||||||
date: string; // ISO 8601 (e.g., "2025-11-26T10:44:00.000Z")
|
date: string; // ISO 8601 (e.g., "2025-11-26T10:44:00.000Z")
|
||||||
ts: string; // Slack timestamp or epoch ms
|
ts: string; // Slack timestamp (seconds.microseconds)
|
||||||
user: string; // User ID or "bot"
|
user: string; // User ID or "bot"
|
||||||
userName?: string; // Handle (e.g., "mario")
|
userName?: string; // Handle (e.g., "mario")
|
||||||
displayName?: string; // Display name (e.g., "Mario Zechner")
|
displayName?: string; // Display name (e.g., "Mario Zechner")
|
||||||
text: string; // Message text
|
text: string; // Message text (@mentions stripped)
|
||||||
attachments: Array<{
|
attachments: string[]; // Filenames of attachments
|
||||||
original: string; // Original filename
|
|
||||||
local: string; // Path relative to data dir
|
|
||||||
}>;
|
|
||||||
isBot: boolean;
|
isBot: boolean;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
```json
|
```json
|
||||||
{"date":"2025-11-26T10:44:00.123Z","ts":"1732619040.123456","user":"U123ABC","userName":"mario","text":"@mom hello","attachments":[],"isBot":false}
|
{"date":"2025-11-26T10:44:00.123Z","ts":"1732619040.123456","user":"U123ABC","userName":"mario","text":"hello","attachments":[],"isBot":false}
|
||||||
{"date":"2025-11-26T10:44:05.456Z","ts":"1732619045456","user":"bot","text":"Hi! How can I help?","attachments":[],"isBot":true}
|
{"date":"2025-11-26T10:44:05.456Z","ts":"1732619045456","user":"bot","text":"Hi! How can I help?","attachments":[],"isBot":true}
|
||||||
```
|
```
|
||||||
|
|
||||||
Mom knows how to query these logs efficiently (see [her system prompt](src/agent.ts)) to avoid context overflow when searching conversation history.
|
### context.jsonl (LLM Context)
|
||||||
|
|
||||||
|
Messages sent to Claude are stored here. This is synced from `log.jsonl` at the start of each run to ensure:
|
||||||
|
- Backfilled messages (from Slack API on startup) are included
|
||||||
|
- Channel chatter between @mentions is included
|
||||||
|
- Messages sent while mom was busy are included
|
||||||
|
|
||||||
|
Mom knows how to query `log.jsonl` efficiently (see [her system prompt](src/agent.ts)) for older history beyond what's in context.
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
|
|
@ -339,9 +350,10 @@ mom --sandbox=docker:mom-exec ./data-exec
|
||||||
|
|
||||||
### Code Structure
|
### Code Structure
|
||||||
|
|
||||||
- `src/main.ts`: Entry point, CLI arg parsing, message routing
|
- `src/main.ts`: Entry point, CLI arg parsing, handler setup, SlackContext adapter
|
||||||
- `src/agent.ts`: Agent runner, event handling, tool execution
|
- `src/agent.ts`: Agent runner, event handling, tool execution, session management
|
||||||
- `src/slack.ts`: Slack integration, context management, message posting
|
- `src/slack.ts`: Slack integration (Socket Mode), backfill, message logging
|
||||||
|
- `src/context.ts`: Session manager (context.jsonl), log-to-context sync
|
||||||
- `src/store.ts`: Channel data persistence, attachment downloads
|
- `src/store.ts`: Channel data persistence, attachment downloads
|
||||||
- `src/log.ts`: Centralized logging (console output)
|
- `src/log.ts`: Centralized logging (console output)
|
||||||
- `src/sandbox.ts`: Docker/host sandbox execution
|
- `src/sandbox.ts`: Docker/host sandbox execution
|
||||||
|
|
|
||||||
|
|
@ -617,9 +617,24 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log usage summary
|
// Log usage summary with context info
|
||||||
if (runState.totalUsage.cost.total > 0) {
|
if (runState.totalUsage.cost.total > 0) {
|
||||||
const summary = log.logUsageSummary(runState.logCtx!, runState.totalUsage);
|
// Get last non-aborted assistant message for context calculation
|
||||||
|
const messages = session.messages;
|
||||||
|
const lastAssistantMessage = messages
|
||||||
|
.slice()
|
||||||
|
.reverse()
|
||||||
|
.find((m) => m.role === "assistant" && (m as any).stopReason !== "aborted") as any;
|
||||||
|
|
||||||
|
const contextTokens = lastAssistantMessage
|
||||||
|
? lastAssistantMessage.usage.input +
|
||||||
|
lastAssistantMessage.usage.output +
|
||||||
|
lastAssistantMessage.usage.cacheRead +
|
||||||
|
lastAssistantMessage.usage.cacheWrite
|
||||||
|
: 0;
|
||||||
|
const contextWindow = model.contextWindow || 200000;
|
||||||
|
|
||||||
|
const summary = log.logUsageSummary(runState.logCtx!, runState.totalUsage, contextTokens, contextWindow);
|
||||||
runState.queue.enqueue(() => ctx.respondInThread(summary), "usage summary");
|
runState.queue.enqueue(() => ctx.respondInThread(summary), "usage summary");
|
||||||
await queueChain;
|
await queueChain;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,13 +195,26 @@ export function logUsageSummary(
|
||||||
cacheWrite: number;
|
cacheWrite: number;
|
||||||
cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
|
cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
|
||||||
},
|
},
|
||||||
|
contextTokens?: number,
|
||||||
|
contextWindow?: number,
|
||||||
): string {
|
): string {
|
||||||
|
const formatTokens = (count: number): string => {
|
||||||
|
if (count < 1000) return count.toString();
|
||||||
|
if (count < 10000) return (count / 1000).toFixed(1) + "k";
|
||||||
|
if (count < 1000000) return Math.round(count / 1000) + "k";
|
||||||
|
return (count / 1000000).toFixed(1) + "M";
|
||||||
|
};
|
||||||
|
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
lines.push("*Usage Summary*");
|
lines.push("*Usage Summary*");
|
||||||
lines.push(`Tokens: ${usage.input.toLocaleString()} in, ${usage.output.toLocaleString()} out`);
|
lines.push(`Tokens: ${usage.input.toLocaleString()} in, ${usage.output.toLocaleString()} out`);
|
||||||
if (usage.cacheRead > 0 || usage.cacheWrite > 0) {
|
if (usage.cacheRead > 0 || usage.cacheWrite > 0) {
|
||||||
lines.push(`Cache: ${usage.cacheRead.toLocaleString()} read, ${usage.cacheWrite.toLocaleString()} write`);
|
lines.push(`Cache: ${usage.cacheRead.toLocaleString()} read, ${usage.cacheWrite.toLocaleString()} write`);
|
||||||
}
|
}
|
||||||
|
if (contextTokens && contextWindow) {
|
||||||
|
const contextPercent = ((contextTokens / contextWindow) * 100).toFixed(1);
|
||||||
|
lines.push(`Context: ${formatTokens(contextTokens)} / ${formatTokens(contextWindow)} (${contextPercent}%)`);
|
||||||
|
}
|
||||||
lines.push(
|
lines.push(
|
||||||
`Cost: $${usage.cost.input.toFixed(4)} in, $${usage.cost.output.toFixed(4)} out` +
|
`Cost: $${usage.cost.input.toFixed(4)} in, $${usage.cost.output.toFixed(4)} out` +
|
||||||
(usage.cacheRead > 0 || usage.cacheWrite > 0
|
(usage.cacheRead > 0 || usage.cacheWrite > 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue