Skip to content

feat: session activity history and fix ANSI in session logs#116

Merged
jcanizalez merged 2 commits intomainfrom
feat/session-events-96
Mar 26, 2026
Merged

feat: session activity history and fix ANSI in session logs#116
jcanizalez merged 2 commits intomainfrom
feat/session-events-96

Conversation

@jcanizalez
Copy link
Copy Markdown
Owner

Summary

  • Add session_events table for persistent session lifecycle event logging across all sessions (not just task-linked)
  • Log 6 event types: created, exited, task_linked, renamed, archived, unarchived
  • Add sessionEvent:list and sessionEvent:listBySession RPC methods + list_session_events MCP tool
  • Fix garbled text in session logs by stripping ANSI escape codes before storage (extracted to shared ansi-strip.ts utility)

Closes #96

Test plan

  • yarn build passes
  • yarn test — 433 tests pass
  • Launch a session, rename it, exit it → query sessionEvent:listBySession via MCP to verify events logged
  • Start a task-linked session → verify SessionActivityLog shows clean text (no ANSI escape codes)
  • Archive/unarchive a session → verify corresponding events recorded

Add persistent session lifecycle event logging for all sessions —
not just task-linked ones — enabling post-mortem analysis, metrics,
and multi-agent coordination.

- Add session_events table (id, session_id, event_type, timestamp, metadata)
- Log created, exited, task_linked, renamed, archived, unarchived events
- Add sessionEvent:list and sessionEvent:listBySession RPC methods
- Add list_session_events MCP tool for agent coordination
- Extract ANSI stripping to shared utility (ansi-strip.ts)
- Strip ANSI escape codes from session log output before storage
Copilot AI review requested due to automatic review settings March 26, 2026 18:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces persistent session lifecycle event history across all sessions (not only task-linked) and centralizes ANSI stripping so stored session output is readable/plain text.

Changes:

  • Added shared SessionEventType / SessionEvent types and new RPC method definitions for listing session events.
  • Added session_events SQLite table with insert/list helpers, and server-side logging hooks for lifecycle events (created/exited/linked/renamed/archived/unarchived).
  • Replaced inline ANSI-stripping logic with a stripAnsi() utility and applied it before storing/buffering session output; added an MCP tool to query session events.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/shared/src/types.ts Adds SessionEventType/SessionEvent and new IPC method constants for session event listing.
packages/shared/src/protocol.ts Adds typed RPC method entries for sessionEvent:list and sessionEvent:listBySession.
packages/server/src/register-methods.ts Logs session lifecycle events and strips ANSI codes before buffering session output for storage; registers new RPC methods.
packages/server/src/pty-manager.ts Uses shared stripAnsi() utility when building the rolling output buffer.
packages/server/src/database.ts Creates session_events table + indexes and implements insert/list query helpers (with per-session pruning).
packages/server/src/ansi-strip.ts Adds reusable ANSI stripping helper used by server components.
packages/mcp/src/tools/sessions.ts Adds list_session_events MCP tool that calls the new RPC methods.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


server.tool(
'list_session_events',
'List session lifecycle events (created, exited, renamed, archived, task_linked). Use for post-mortem analysis and multi-agent coordination.',
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tool description lists event types but omits unarchived, which is also supported/logged. Update the description so it matches the actual event set.

Suggested change
'List session lifecycle events (created, exited, renamed, archived, task_linked). Use for post-mortem analysis and multi-agent coordination.',
'List session lifecycle events (created, exited, renamed, archived, unarchived, task_linked). Use for post-mortem analysis and multi-agent coordination.',

Copilot uses AI. Check for mistakes.
Comment on lines +293 to +299
event_type: z
.string()
.max(50)
.optional()
.describe(
'Filter by event type (created, exited, task_linked, renamed, archived, unarchived)'
),
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event_type is currently validated as an arbitrary string. Since the supported values are a fixed set, consider validating with an enum (or reusing the shared SessionEventType union) to give immediate feedback on typos and keep the MCP tool contract tight.

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +142
'sessionEvent:list': {
params: { eventType?: string; limit?: number }
result: SessionEvent[]
}
'sessionEvent:listBySession': {
params: { sessionId: string; limit?: number }
result: SessionEvent[]
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sessionEvent:list / sessionEvent:listBySession params currently type eventType as string, even though SessionEventType exists in shared types. This weakens type-safety for RPC consumers and makes it easier to pass invalid event types. Consider importing SessionEventType and typing eventType?: SessionEventType (and keeping the same wire format).

Copilot uses AI. Check for mistakes.
metadata TEXT
);

CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session_events queries sort by timestamp (e.g., ORDER BY timestamp DESC) but the schema only indexes session_id and event_type. This can cause full scans / temp sorts as the table grows. Consider adding an index that supports the common access patterns, e.g. (session_id, timestamp) and/or (event_type, timestamp), or at least an index on timestamp.

Suggested change
CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id);
CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id);
CREATE INDEX IF NOT EXISTS idx_session_events_session_timestamp ON session_events(session_id, timestamp DESC);

Copilot uses AI. Check for mistakes.
Comment on lines +1826 to +1833
export function listSessionEvents(eventType?: string, limit = 100): SessionEvent[] {
const d = getDb()
let rows: Array<Record<string, unknown>>
if (eventType) {
rows = d
.prepare('SELECT * FROM session_events WHERE event_type = ? ORDER BY timestamp DESC LIMIT ?')
.all(eventType, limit) as Array<Record<string, unknown>>
} else {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listSessionEvents accepts eventType?: string even though the shared type defines SessionEventType. Using the union type here (and in the RPC method handler) would prevent accidental logging/querying of unsupported event types and keep the API consistent end-to-end.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +7
// Strip ANSI escape sequences so stored output is plain text.
// eslint-disable-next-line no-control-regex
const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]|\x1b\].*?(?:\x07|\x1b\\)|\x1b[()][0-2B]|\x1b[=>]/g

export function stripAnsi(data: string): string {
return data.replace(ANSI_RE, '').replace(/\r/g, '')
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says ANSI stripping was extracted to a shared ansi-strip.ts utility, but this utility was added under packages/server/src/ansi-strip.ts and is only used server-side. Either move it into the shared package (and import from there) if it’s intended to be reused across packages, or update the PR description to match the current location/scope.

Copilot uses AI. Check for mistakes.
@jcanizalez jcanizalez merged commit 3d8d128 into main Mar 26, 2026
1 check passed
@jcanizalez jcanizalez deleted the feat/session-events-96 branch March 26, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Log session activity history (like workflow runs) for all sessions

2 participants