Commit Graph

99 Commits

Author SHA1 Message Date
can1357 a42061d9ee feat(coding-agent): added self-update command with bundled templates
- Added `pi update` command for checking and installing CLI updates from GitHub releases or bun.
- Added cross-platform installer scripts (shell and PowerShell) with automatic PATH configuration.
- Changed HTML export to use compile-time bundled templates via Bun macros.
- Changed `exportToHtml` and `exportFromFile` functions to async operations.
2026-01-03 14:17:28 +01:00
can1357 4aaebfe5a8 fix: resolved race conditions and resource leaks with mutex locks
- Added mutex-based synchronization to prevent race conditions in LSP client, session context, and RPC mode.
- Fixed resource leaks by ensuring stream reader locks are released in try/finally blocks.
- Fixed duplicate processing issues by tracking in-flight operations in runner creation and message writes.
- Fixed event listener mutation during iteration by copying array before emitting.
- Added abort controller cleanup to prevent memory leaks in auto-compaction and retry logic.
2026-01-03 08:00:55 +01:00
can1357 0836b6d12d feat: added centralized winston logger with daily rotation for debugging
- Added centralized file logger with winston and daily rotation to ~/.pi/logs/ directory.
- Exposed logger property to hook and custom tool APIs for extension logging.
- Replaced silent error suppression with debug/warning logging across 20+ catch blocks.
- Removed separate Exa error logger in favor of centralized logging system.
2026-01-03 07:45:23 +01:00
can1357 9f9d5b1e76 feat(coding-agent): added auto-read for @filepath mentions in prompts
Files referenced with @path/to/file.ext syntax are automatically read
and injected as synthetic Read tool calls before the agent responds,
eliminating the need for manual file reads.
2026-01-03 05:52:40 +01:00
can1357 50ab6b8b1b style: removed .js extension from imports and added node: prefix to builtins 2026-01-03 05:12:25 +01:00
can1357 a099076a99 feat(coding-agent): added custom slash command loading from user and project directories
- Added custom TypeScript slash commands loaded from ~/.pi/commands/ and .pi/commands/ directories.
- Added support for loading markdown slash commands from Claude's ~/.claude/commands/ and .claude/commands/ directories.
- Added commands.enableClaudeUser and commands.enableClaudeProject settings to control Claude command directory loading.
- Implemented CustomCommandAPI with exec, typebox, and pi module injection for command execution context.
- Integrated custom commands into autocomplete provider for interactive mode discovery.
2026-01-03 04:31:07 +01:00
can1357 ac051daf90 feat(coding-agent): added clipboard export option to /export command 2026-01-03 04:05:00 +01:00
can1357 015f2c7e54 feat(agent): added interruptMode to control queued message timing
- Added interruptMode option to control when queued messages interrupt tool execution.
- Implemented "immediate" mode (default) to check queue after each tool and interrupt remaining tools.
- Implemented "wait" mode to defer queue processing until the entire turn completes.
- Exposed interruptMode configuration in interactive settings UI with immediate/wait options.
- Wired interrupt mode through AgentSession and SDK to enable runtime configuration and persistence.
2026-01-03 03:56:13 +01:00
can1357 003a19bdaa feat(coding-agent): added model roles for default and small model configuration
- Settings now use modelRoles map (e.g., { default: 'anthropic/claude-sonnet-4-5', small: 'anthropic/claude-haiku-4-5' })
- Session persistence uses 'provider/modelId' string format with optional role field
- Model selector: Enter sets default, S sets small, Escape closes
- Added role markers in selector: checkmark for default, lightning bolt for small
- Task tool supports pi/<role> aliases (e.g., model: pi/small, haiku, flash, mini)
- Title generation uses configured small model with auto-discovery fallback
- Explore agent now uses pi/small as primary model preference
2026-01-03 03:33:02 +01:00
can1357 2be354322d chore: renamed package scope to @oh-my-pi for consistent branding
- Renamed npm package scope from @mariozechner to @oh-my-pi across all packages for consistent branding.
- Removed packages/pods directory and all related pod management functionality.
- Updated import paths and module declarations throughout codebase to reflect new package names.
- Reformatted code with consistent trailing comma removal and ternary operator alignment.
2026-01-02 18:08:23 +01:00
Mario Zechner bfdfb96bf8 Add agent state methods to CustomToolContext and fix abort signature
CustomToolContext now has:
- isIdle() - check if agent is streaming
- hasQueuedMessages() - check if user has queued messages
- abort() - abort current operation (fire-and-forget)

Changed abort() signature from Promise<void> to void in both
HookContext and CustomToolContext. The abort is fire-and-forget:
it calls session.abort() without awaiting, so the abort signal
is set immediately while waitForIdle() runs in the background.

Fixes #388
2026-01-02 00:31:23 +01:00
Mario Zechner 321462bda7 Split HookContext and HookCommandContext to prevent deadlocks
HookContext (all events):
- isIdle() - read-only state check
- hasQueuedMessages() - read-only state check
- abort() - fire-and-forget, does not wait

HookCommandContext (slash commands only):
- waitForIdle() - waits for agent to finish
- newSession(options?) - create new session
- branch(entryId) - branch from entry
- navigateTree(targetId, options?) - navigate session tree

Session control methods moved from HookAPI (pi.*) to HookCommandContext (ctx.*)
because they can deadlock when called from event handlers that run inside
the agent loop (tool_call, tool_result, context events).
2026-01-02 00:24:58 +01:00
Mario Zechner cd7df64220 Add session management and agent state methods to hooks API
HookAPI additions:
- pi.newSession(options?) - create new session with optional setup callback
- pi.branch(entryId) - branch from a specific entry
- pi.navigateTree(targetId, options?) - navigate the session tree

HookContext additions:
- ctx.isIdle() - check if agent is streaming
- ctx.waitForIdle() - wait for agent to finish
- ctx.abort() - abort current operation
- ctx.hasQueuedMessages() - check for queued user messages

These enable hooks to programmatically manage sessions (handoff, templates)
and check agent state before showing interactive UI.

Fixes #388
2026-01-01 23:56:24 +01:00
Mario Zechner f107f980c9 Consolidate session events: remove session_before_new/session_new, add reason field to switch events
- Remove session_before_new and session_new hook events
- Add reason: 'new' | 'resume' to session_before_switch and session_switch events
- Remove 'new' reason from custom tool onSession (use 'switch' for both /new and /resume)
- Rename reset() to newSession(options?) in AgentSession
- Add NewSessionOptions with optional parentSession for lineage tracking
- Rename branchedFrom to parentSession in SessionHeader
- Rename RPC reset command to new_session with optional parentSession
- Update example hooks to use new event structure
- Update documentation and changelog

Based on discussion in #293
2026-01-01 23:31:26 +01:00
Mario Zechner 5bee4a7151 WIP: Rewrite export-html with tree sidebar, client-side rendering
- Add tree sidebar with search and filter (Default/All/Labels)
- Client-side markdown/syntax highlighting via vendored marked.js + highlight.js
- Base64 encode session data to avoid escaping issues
- Reuse theme.ts color tokens via getResolvedThemeColors()
- Sticky sidebar, responsive mobile layout with overlay
- Click tree node to scroll to message
- Keyboard shortcuts: Esc to reset, Ctrl/Cmd+F to search
2026-01-01 03:36:47 +01:00
Mario Zechner c583a31192 Change branch() to use entryId instead of entryIndex
- AgentSession.branch(entryId: string) now takes entry ID
- SessionBeforeBranchEvent.entryId replaces entryIndex
- getUserMessagesForBranching() returns entryId
- Update RPC types and client
- Update UserMessageSelectorComponent
- Update hook examples and tests
- Update docs (hooks.md, sdk.md)
2025-12-31 13:47:34 +01:00
Mario Zechner ea3d42907f Rework custom tools API with CustomToolContext
- CustomAgentTool renamed to CustomTool
- ToolAPI renamed to CustomToolAPI
- ToolContext renamed to CustomToolContext
- ToolSessionEvent renamed to CustomToolSessionEvent
- Added CustomToolContext parameter to execute() and onSession()
- CustomToolFactory now returns CustomTool<any, any> for type compatibility
- dispose() replaced with onSession({ reason: 'shutdown' })
- Added wrapCustomTool() to convert CustomTool to AgentTool
- Session exposes setToolUIContext() instead of leaking internals
- Fix ToolExecutionComponent to sync with toolOutputExpanded state
- Update all custom tool examples for new API
2025-12-31 12:05:24 +01:00
Mario Zechner 17cda9a1ea Replace custom tool dispose() with shutdown session event
Breaking change: CustomAgentTool.dispose() removed. Use onSession with
reason 'shutdown' instead for cleanup.

- Add 'shutdown' to SessionEvent.reason for custom tools
- Remove dispose() method from CustomAgentTool interface
- Make emitToolSessionEvent() public on AgentSession
- Emit shutdown event to tools in InteractiveMode.shutdown()
- Update custom-tools.md with new API and examples
2025-12-31 02:55:45 +01:00
Mario Zechner fa931ceed1 refactor(coding-agent): fix compaction for branched sessions, consolidate hook context types
Compaction API:
- prepareCompaction() now takes (pathEntries, settings) only
- CompactionPreparation restructured: removed cutPoint/messagesToKeep/boundaryStart, added turnPrefixMessages/isSplitTurn/previousSummary/fileOps/settings
- compact() now takes (preparation, model, apiKey, customInstructions?, signal?)
- Fixed token overflow by using getPath() instead of getEntries()

Hook types:
- HookEventContext renamed to HookContext
- HookCommandContext removed, RegisteredCommand.handler takes (args, ctx)
- HookContext now includes model field
- SessionBeforeCompactEvent: removed previousCompactions/model, added branchEntries
- SessionBeforeTreeEvent: removed model (use ctx.model)
- HookRunner.initialize() added for modes to set up callbacks
2025-12-31 02:24:24 +01:00
Mario Zechner 143a271872 Pass fromHook to appendCompaction for both manual and auto compaction 2025-12-30 22:42:24 +01:00
Mario Zechner 4beb61a6c9 Add fromHook field to CompactionEntry and BranchSummaryEntry
- fromHook: true = hook generated, skip file extraction
- fromHook: undefined/false = pi generated, extract files (backward compatible)
- branchWithSummary now accepts fromHook parameter
- File extraction only runs for !entry.fromHook entries
2025-12-30 22:42:24 +01:00
Mario Zechner 7d603b9bb1 Store file lists in BranchSummaryEntry.details for cumulative tracking
- BranchSummaryResult now returns readFiles and modifiedFiles separately
- BranchSummaryDetails type for details: { readFiles, modifiedFiles }
- branchWithSummary accepts optional details parameter
- Collect files from existing branch_summary.details when preparing entries
- Files accumulate across nested branch summaries
2025-12-30 22:42:24 +01:00
Mario Zechner 873ceab7f9 Use reserveTokens for branch summary (tokens left for prompt + response)
- tokenBudget = contextWindow - reserveTokens
- Default 16384, same as compaction
- Consistent naming with CompactionSettings.reserveTokens
2025-12-30 22:42:24 +01:00
Mario Zechner 299ff98c66 Use token-based maxTokens instead of fraction-based reserveFraction
- BranchSummarySettings.maxTokens (default 100000) instead of reserveFraction
- More intuitive and consistent with CompactionSettings.keepRecentTokens
2025-12-30 22:42:24 +01:00
Mario Zechner 9a4e8ddb71 Use AgentMessage in BranchPreparation and add BranchSummarySettings
- BranchPreparation now uses AgentMessage[] instead of custom type
- Reuse getMessageFromEntry pattern from compaction.ts
- Add BranchSummarySettings with reserveFraction to settings.json
- Add getBranchSummarySettings() to SettingsManager
- Use settings for reserveFraction instead of hardcoded value
2025-12-30 22:42:23 +01:00
Mario Zechner 7088fc2483 Add ReadonlySessionManager and refactor branch summarization
- Add ReadonlySessionManager interface to session-manager.ts
- Re-export from hooks/index.ts
- Add collectEntriesForBranchSummary() to extract entries for summarization
- Don't stop at compaction boundaries (include their summaries as context)
- Add token budget support to prepareBranchEntries()
- Walk entries newest-to-oldest to prioritize recent context
- Use options object for generateBranchSummary()
- Handle compaction entries as context summaries
- Export new types: CollectEntriesResult, GenerateBranchSummaryOptions
2025-12-30 22:42:23 +01:00
Mario Zechner d7a83cd806 Refactor: move compaction code to src/core/compaction/
- Move compaction.ts to src/core/compaction/compaction.ts
- Extract branch summarization to src/core/compaction/branch-summarization.ts
- Add index.ts to re-export all compaction utilities
- Update all imports across the codebase
2025-12-30 22:42:23 +01:00
Mario Zechner 37ec6633ef Add tree navigation tests and shared test utilities
- Add test/utilities.ts with shared helpers (API_KEY, userMsg, assistantMsg, createTestSession)
- Add agent-session-tree-navigation.test.ts with e2e tests for tree navigation
- Add getChildren() method to SessionManager
- Add summaryEntry to navigateTree return type
- Update existing tests to use shared utilities
2025-12-30 22:42:23 +01:00
Mario Zechner 854ada407b Fix branch summarization abort handling and tree navigation
- Check response.stopReason instead of catching errors for abort detection
- Return result object from _generateBranchSummary instead of throwing
- Fix summary attachment: attach to navigation target position, not old branch
- Support root-level summaries (parentId=null) in branchWithSummary
- Remove setTimeout hack for re-showing tree selector on abort
2025-12-30 22:42:23 +01:00
Mario Zechner 6700581430 WIP: Add branch summarization abort support with loader and escape handler 2025-12-30 22:42:23 +01:00
Mario Zechner 38df9e88ef feat(coding-agent): implement /tree command for session tree navigation
- Add TreeSelectorComponent with ASCII tree visualization
- Add AgentSession.navigateTree() for switching branches
- Add session_before_tree/session_tree hook events
- Add SessionManager.resetLeaf() for navigating to root
- Change leafId from string to string|null for consistency with parentId
- Support optional branch summarization when switching
- Update buildSessionContext() to handle null leafId
- Add /tree to slash commands in interactive mode
2025-12-30 22:42:22 +01:00
Mario Zechner 6632efe323 refactor(hooks): split session events into individual typed events
Major changes:
- Replace monolithic SessionEvent with reason discriminator with individual
  event types: session_start, session_before_switch, session_switch,
  session_before_new, session_new, session_before_branch, session_branch,
  session_before_compact, session_compact, session_shutdown
- Each event has dedicated result type (SessionBeforeSwitchResult, etc.)
- HookHandler type now allows bare return statements (void in return type)
- HookAPI.on() has proper overloads for each event with correct typing

Additional fixes:
- AgentSession now always subscribes to agent in constructor (was only
  subscribing when external subscribe() called, breaking internal handlers)
- Standardize on undefined over null throughout codebase
- HookUIContext methods return undefined instead of null
- SessionManager methods return undefined instead of null
- Simplify hook exports to 'export type * from types.js'
- Add detailed JSDoc for skipConversationRestore vs cancel
- Fix createBranchedSession to rebuild index in persist mode
- newSession() now returns the session file path

Updated all example hooks, tests, and emission sites to use new event types.
2025-12-30 22:42:22 +01:00
Mario Zechner ca1ccfc7e2 Support multiple messages in agent.prompt() and agentLoop
- agentLoop now accepts AgentMessage[] instead of single message
- agent.prompt() accepts AgentMessage | AgentMessage[]
- Emits message_start/end for each message in the array
- AgentSession.prompt() builds array with hook message + user message
- TUI now receives events for before_agent_start injected messages
2025-12-30 22:42:22 +01:00
Mario Zechner b65b59f35f Implement before_agent_start hook event
- Add BeforeAgentStartEvent and BeforeAgentStartEventResult types
- Add emitBeforeAgentStart to HookRunner
- Call in AgentSession.prompt() before agent.prompt()
- Hook can return a message to inject into context (persisted + visible)
- Add test hook demonstrating custom message rendering and before_agent_start
2025-12-30 22:42:22 +01:00
Mario Zechner f5f9dab09d Use exhaustive switch on message.role throughout coding-agent
- addMessageToChat: exhaustive switch for all AgentMessage roles
- renderSessionContext: delegates to addMessageToChat, special handling for assistant tool calls and tool results
- export-html formatMessage: exhaustive switch for all AgentMessage roles
- Removed isHookMessage, isBashExecutionMessage type guards in favor of role checks
- Fixed imports and removed unused getLatestCompactionEntry
2025-12-30 22:42:21 +01:00
Mario Zechner e8947026a8 WIP: Major cleanup - move Attachment to consumers, simplify agent API
- Removed Attachment from agent package (now in web-ui/coding-agent)
- Agent.prompt now takes (text, images?: ImageContent[])
- Removed transports from web-ui (duplicate of agent package)
- Updated coding-agent to use local message types
- Updated mom package for new agent API

Remaining: Fix AgentInterface.ts to compose UserMessageWithAttachments
2025-12-30 22:42:20 +01:00
Mario Zechner 6be6cad52c WIP: Refactor agent package - not compiling
- Renamed AppMessage to AgentMessage throughout
- New agent-loop.ts with AgentLoopContext, AgentLoopConfig
- Removed transport abstraction, Agent now takes streamFn directly
- Extracted streamProxy to proxy.ts utility
- Removed agent-loop from pi-ai (now in agent package)
- Updated consumers (coding-agent, mom) for AgentMessage rename
- Tests updated but some consumers still need migration

Known issues:
- AgentTool, AgentToolResult not exported from pi-ai
- Attachment not exported from pi-agent-core
- ProviderTransport removed but still referenced
- messageTransformer -> convertToLlm migration incomplete
- CustomMessages declaration merging not working properly
2025-12-30 22:42:20 +01:00
Mario Zechner 6a877bb501 Cleanup: unify HookMessage naming and simplify SessionContext
- Rename HookAppMessage to HookMessage, isHookAppMessage to isHookMessage
- Remove entries array from SessionContext (use isHookMessage type guard instead)
- HookMessage.content now accepts string directly (not just array)
- Fix streamMessage type in AgentState (AppMessage, not Message)
- Rename CustomMessageComponent to HookMessageComponent
- Fix test hook to use pi.sendMessage
2025-12-30 22:42:20 +01:00
Mario Zechner d1ecb8aee3 Move exec to HookAPI, sessionManager/modelRegistry to HookEventContext
Breaking changes:
- HookEventContext now has sessionManager and modelRegistry (moved from SessionEventBase)
- HookAPI now has exec() method (moved from HookEventContext/HookCommandContext)
- HookRunner constructor takes sessionManager and modelRegistry as required params
- Session events no longer include sessionManager/modelRegistry fields

Hook code migration:
- event.sessionManager -> ctx.sessionManager
- event.modelRegistry -> ctx.modelRegistry
- ctx.exec() -> pi.exec()

Updated:
- src/core/hooks/types.ts - type changes
- src/core/hooks/runner.ts - constructor, createContext
- src/core/hooks/loader.ts - add exec to HookAPI
- src/core/sdk.ts - pass sessionManager/modelRegistry to HookRunner
- src/core/agent-session.ts - remove sessionManager/modelRegistry from events
- src/modes/* - remove setSessionFile calls, update events
- examples/hooks/* - update to new API
2025-12-30 22:42:19 +01:00
Mario Zechner c3134e4991 Update plan: HookCommandContext without sendMessage (use pi closure) 2025-12-30 22:42:19 +01:00
Mario Zechner a91f29f1b0 Add Agent.prompt(AppMessage) overload for custom message types
Instead of using continue() which validates roles, prompt() now accepts
an AppMessage directly. This allows hook messages with role: 'hookMessage'
to trigger proper agent loop with message events.

- Add overloads: prompt(AppMessage) and prompt(string, attachments?)
- sendHookMessage uses prompt(appMessage) instead of appendMessage+continue
2025-12-30 22:42:19 +01:00
Mario Zechner 5c3ce3ecc5 Use proper HookAppMessage type instead of _hookData marker
Following the same pattern as BashExecutionMessage:
- HookAppMessage has role: 'hookMessage' with customType, content, display, details
- isHookAppMessage() type guard for checking message type
- messageTransformer converts to user message for LLM context
- TUI checks isHookAppMessage() for rendering as CustomMessageComponent

This makes the API clean for anyone building on AgentSession - they can
use the type guard instead of knowing about internal marker fields.
2025-12-30 22:42:19 +01:00
Mario Zechner 40ecc08a5e Add emitLastMessage flag to agent.continue()
When calling continue() with emitLastMessage=true, the agent loop
emits message_start/message_end events for the last message in context.
This allows messages added outside the loop (e.g., hook messages via
sendHookMessage) to trigger proper TUI rendering.

Changes across packages:
- packages/ai: agentLoopContinue() accepts emitLastMessage parameter
- packages/agent: Agent.continue(), transports updated to pass flag
- packages/coding-agent: sendHookMessage passes true when triggerTurn
2025-12-30 22:42:19 +01:00
Mario Zechner 091e97170b Hook commands: remove string return, use sendMessage() for prompting
- Command handler now returns Promise<void> instead of Promise<string | undefined>
- To trigger LLM response, use sendMessage() with triggerTurn: true
- Simplify _tryExecuteHookCommand to return boolean

Added example hook and slash command in .pi/:
- .pi/hooks/test-command.ts - /greet command using sendMessage
- .pi/commands/review.md - file-based /review command
2025-12-30 22:42:18 +01:00
Mario Zechner a68f85fc1c Move hook command execution to AgentSession.prompt()
Hook commands registered via pi.registerCommand() are now handled in
AgentSession.prompt() alongside file-based slash commands. This:

- Removes duplicate tryHandleHookCommand from interactive-mode and rpc-mode
- All modes (interactive, RPC, print) share the same command handling logic
- AgentSession._tryExecuteHookCommand() builds CommandContext using:
  - UI context from hookRunner (set by mode)
  - sessionManager, modelRegistry from AgentSession
  - sendMessage via sendHookMessage
  - exec via exported execCommand
- Handler returning string uses it as prompt, undefined returns early

Also:
- Export execCommand from hooks/runner.ts
- Add getUIContext() and getHasUI() to HookRunner
- Make HookRunner.emitError() public for error reporting
2025-12-30 22:42:18 +01:00
Mario Zechner e568ef6bb0 Hook API: replace send() with sendMessage(), add appendEntry() and registerCommand()
Breaking changes to Hook API:
- pi.send(text, attachments?) replaced with pi.sendMessage(message, triggerTurn?)
  - Creates CustomMessageEntry instead of user messages
  - Properly handles queuing during streaming via agent loop
  - Supports optional turn triggering when idle
- New pi.appendEntry(customType, data?) for hook state persistence
- New pi.registerCommand(name, options) for custom slash commands
- Handler types renamed: SendHandler -> SendMessageHandler, new AppendEntryHandler

Implementation:
- AgentSession.sendHookMessage() handles all three cases:
  - Streaming: queues message with _hookData marker, agent loop processes it
  - Not streaming + triggerTurn: appends to state/session, calls agent.continue()
  - Not streaming + no trigger: appends to state/session only
- message_end handler routes based on _hookData presence to correct persistence
- HookRunner gains getRegisteredCommands() and getCommand() methods

New types: HookMessage<T>, RegisteredCommand, CommandContext
2025-12-30 22:42:18 +01:00
Mario Zechner f6e784bc9a Refactor SessionEventBase to pass sessionManager and modelRegistry
Breaking changes to hook types:
- SessionEventBase now passes sessionManager and modelRegistry directly
- before_compact: passes preparation, previousCompactions (newest first)
- before_switch: has targetSessionFile; switch: has previousSessionFile
- Removed resolveApiKey (use modelRegistry.getApiKey())
- getSessionFile() returns string | undefined for in-memory sessions

Updated:
- All session event emissions in agent-session.ts
- Hook examples (custom-compaction.ts, auto-commit-on-exit.ts, confirm-destructive.ts)
- Tests (compaction-hooks.test.ts, compaction-hooks-example.test.ts)
- export-html.ts guards for in-memory sessions
2025-12-30 22:42:18 +01:00
Mario Zechner 1e1d473b45 Make CompactionEntry and CompactionResult generic with details field
- CompactionEntry<T> and CompactionResult<T> now have optional details?: T
- appendCompaction() accepts optional details parameter
- Hooks can return compaction.details to store custom data
- Enables structured compaction with ArtifactIndex (see #314)
- Fix CompactionResult export location (now from compaction.ts)
- Update plan with remaining compaction refactor items
2025-12-30 22:42:18 +01:00
Mario Zechner 56ef0e63f3 Session tree: simplify types, add branching API, comprehensive tests
Types:
- SessionEntryBase with type field, extended by all entry types
- CustomEntry for hooks (type: 'custom', customType, data)
- Remove XXXContent types and TreeNode (redundant)

API:
- Rename saveXXX to appendXXX with JSDoc explaining tree semantics
- Rename branchInPlace to branch() with better docs
- Add createBranchedSession(leafId) replacing index-based version
- Add getTree() returning SessionTreeNode[] for tree traversal
- Add appendCustomEntry(customType, data) for hooks

Tests:
- tree-traversal.test.ts: 28 tests covering append, getPath, getTree,
  branch, branchWithSummary, createBranchedSession
- save-entry.test.ts: custom entry integration

Docs:
- Class-level JSDoc explaining append-only tree model
- Method docs explaining leaf advancement and branching
- CHANGELOG.md entry for all changes
2025-12-30 22:42:18 +01:00
Mario Zechner 7bb808da52 Fix SessionEntry type to exclude SessionHeader
- SessionEntry now only contains conversation entries (messages, compaction, etc.)
- SessionHeader is separate, not part of SessionEntry
- FileEntry = SessionHeader | SessionEntry (for file storage)
- getEntries() filters out header, returns SessionEntry[]
- Added getHeader() for accessing session metadata
- Updated compaction and tests to not expect header in entries
- Updated mom package to use FileEntry for internal storage
2025-12-30 22:42:17 +01:00